| // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| import 'package:front_end/src/codes/diagnostic.dart' as diag; | |
| import 'package:kernel/ast.dart'; | |
| import 'package:kernel/class_hierarchy.dart'; | |
| import 'package:kernel/type_environment.dart'; | |
| import '../api_prototype/experimental_flags.dart'; | |
| import '../builder/builder.dart'; | |
| import '../builder/compilation_unit.dart'; | |
| import '../builder/declaration_builders.dart'; | |
| import '../builder/member_builder.dart'; | |
| import '../builder/metadata_builder.dart'; | |
| import '../builder/prefix_builder.dart'; | |
| import '../kernel/hierarchy/class_member.dart' show ClassMember; | |
| import '../kernel/kernel_helper.dart'; | |
| import '../kernel/load_library_builder.dart'; | |
| import '../kernel/type_algorithms.dart'; | |
| import '../source/source_class_builder.dart'; | |
| import '../source/source_library_builder.dart'; | |
| import '../source/source_member_builder.dart'; | |
| import 'lookup_result.dart'; | |
| import 'messages.dart'; | |
| import 'name_space.dart'; | |
| import 'uri_offset.dart'; | |
| abstract class LookupScope { | |
| LookupResult? lookup(String name); | |
| } | |
| /// A [LookupScope] based directly on a [NameSpace]. | |
| abstract class BaseNameSpaceLookupScope implements LookupScope { | |
| NameSpace get _nameSpace; | |
| LookupScope? get _parent; | |
| @override | |
| LookupResult? lookup(String name) { | |
| return _nameSpace.lookup(name) ?? _parent?.lookup(name); | |
| } | |
| @override | |
| String toString() => "$runtimeType()"; | |
| } | |
| class NameSpaceLookupScope( | |
| @override final NameSpace _nameSpace, { | |
| @override final LookupScope? _parent, | |
| }) extends BaseNameSpaceLookupScope; | |
| abstract class AbstractTypeParameterScope(final LookupScope _parent) | |
| implements LookupScope { | |
| TypeParameterBuilder? getTypeParameter(String name); | |
| @override | |
| LookupResult? lookup(String name) { | |
| LookupResult? result = getTypeParameter(name); | |
| return result ?? _parent.lookup(name); | |
| } | |
| @override | |
| String toString() => "$runtimeType(type parameter)"; | |
| } | |
| class TypeParameterScope( | |
| super._parent, | |
| final Map<String, TypeParameterBuilder> _typeParameters, | |
| ) extends AbstractTypeParameterScope { | |
| @override | |
| TypeParameterBuilder? getTypeParameter(String name) => _typeParameters[name]; | |
| static LookupScope fromList( | |
| LookupScope parent, | |
| List<TypeParameterBuilder>? typeParameterBuilders, | |
| ) { | |
| if (typeParameterBuilders == null) return parent; | |
| Map<String, TypeParameterBuilder> map = {}; | |
| for (TypeParameterBuilder typeParameterBuilder in typeParameterBuilders) { | |
| if (typeParameterBuilder.isWildcard) continue; | |
| map[typeParameterBuilder.name] = typeParameterBuilder; | |
| } | |
| return new TypeParameterScope(parent, map); | |
| } | |
| } | |
| /// The import scope of a compilation unit. | |
| /// | |
| /// This includes all declaration available through imports in this compilation | |
| /// unit. Its parent scope is the prefix scope of the parent compilation unit. | |
| /// If the compilation unit has no parent, the | |
| /// [SourceLibraryBuilder.parentScope] is used as the parent. This is not a | |
| /// normal Dart scope, but instead a synthesized scope used for expression | |
| /// compilation. | |
| class CompilationUnitImportScope( | |
| final SourceCompilationUnit _compilationUnit, | |
| final NameSpace _importNameSpace, | |
| ) extends BaseNameSpaceLookupScope { | |
| @override | |
| NameSpace get _nameSpace => _importNameSpace; | |
| @override | |
| LookupScope? get _parent => | |
| _compilationUnit.parentCompilationUnit?.prefixScope ?? | |
| _compilationUnit.libraryBuilder.parentScope; | |
| } | |
| /// The scope of a compilation unit. | |
| /// | |
| /// This is the enclosing scope for all declarations within the compilation | |
| /// unit. It gives access to all declarations in the library the compilation | |
| /// unit is part of. Its parent scope is the prefix scope, which contains all | |
| /// imports with prefixes declared in this compilation unit. The grand parent | |
| /// scope is the import scope of the compilation unit implemented through | |
| /// [CompilationUnitImportScope]. | |
| class CompilationUnitScope( | |
| final SourceCompilationUnit _compilationUnit, { | |
| @override final LookupScope? _parent, | |
| }) extends BaseNameSpaceLookupScope { | |
| @override | |
| NameSpace get _nameSpace => _compilationUnit.libraryBuilder.libraryNameSpace; | |
| } | |
| /// The scope containing the prefixes imported into a compilation unit. | |
| class CompilationUnitPrefixScope( | |
| @override final ComputedNameSpace _nameSpace, { | |
| @override required final CompilationUnitImportScope? _parent, | |
| }) extends BaseNameSpaceLookupScope; | |
| class DeclarationBuilderScope(@override final LookupScope? _parent) | |
| extends BaseNameSpaceLookupScope { | |
| DeclarationBuilder? _declarationBuilder; | |
| @override | |
| NameSpace get _nameSpace { | |
| assert(_declarationBuilder != null, "declarationBuilder has not been set."); | |
| return _declarationBuilder!.nameSpace; | |
| } | |
| void set declarationBuilder(DeclarationBuilder value) { | |
| assert( | |
| _declarationBuilder == null, | |
| "declarationBuilder has already been set.", | |
| ); | |
| _declarationBuilder = value; | |
| } | |
| } | |
| /// Computes a builder for the import collision between [declaration] and | |
| /// [other]. | |
| NamedBuilder computeAmbiguousDeclarationForImport( | |
| ProblemReporting problemReporting, | |
| String name, | |
| NamedBuilder declaration, | |
| NamedBuilder other, { | |
| required UriOffset uriOffset, | |
| }) { | |
| // Prefix fragments are merged to singular prefix builders when computing the | |
| // import scope. | |
| assert( | |
| !(declaration is PrefixBuilder && other is PrefixBuilder), | |
| "Unexpected prefix builders $declaration and $other.", | |
| ); | |
| // TODO(ahe): Can I move this to Scope or Prefix? | |
| if (declaration == other) return declaration; | |
| if (declaration is InvalidBuilder) return declaration; | |
| if (other is InvalidBuilder) return other; | |
| NamedBuilder? preferred; | |
| Uri uri = computeLibraryUri(declaration); | |
| Uri otherUri = computeLibraryUri(other); | |
| if (declaration is LoadLibraryBuilder) { | |
| preferred = declaration; | |
| } else if (other is LoadLibraryBuilder) { | |
| preferred = other; | |
| } else if (otherUri.isScheme("dart") && !uri.isScheme("dart")) { | |
| preferred = declaration; | |
| } else if (uri.isScheme("dart") && !otherUri.isScheme("dart")) { | |
| preferred = other; | |
| } | |
| if (preferred != null) { | |
| return preferred; | |
| } | |
| Uri firstUri = uri; | |
| Uri secondUri = otherUri; | |
| if (firstUri.toString().compareTo(secondUri.toString()) > 0) { | |
| firstUri = secondUri; | |
| secondUri = uri; | |
| } | |
| Message message = diag.duplicatedImport.withArguments( | |
| name: name, | |
| // TODO(ahe): We should probably use a context object here | |
| // instead of including URIs in this message. | |
| uri: firstUri, | |
| uri2: secondUri, | |
| ); | |
| // We report the error lazily (setting errorHasBeenReported to false) because | |
| // the spec 18.1 states that 'It is not an error if N is introduced by two or | |
| // more imports but never referred to.' | |
| return new InvalidBuilder( | |
| name, | |
| message.withLocation(uriOffset.fileUri, uriOffset.fileOffset, name.length), | |
| errorHasBeenReported: false, | |
| ); | |
| } | |
| mixin ErroneousMemberBuilderMixin implements SourceMemberBuilder { | |
| @override | |
| // Coverage-ignore(suite): Not run. | |
| MemberDataForTesting? get dataForTesting => null; | |
| @override | |
| // Coverage-ignore(suite): Not run. | |
| Iterable<MetadataBuilder>? get metadataForTesting => null; | |
| @override | |
| // Coverage-ignore(suite): Not run. | |
| Name get memberName => throw new UnsupportedError('$runtimeType.memberName'); | |
| @override | |
| // Coverage-ignore(suite): Not run. | |
| Member? get readTarget => null; | |
| @override | |
| // Coverage-ignore(suite): Not run. | |
| Reference? get readTargetReference => null; | |
| @override | |
| // Coverage-ignore(suite): Not run. | |
| Member? get writeTarget => null; | |
| @override | |
| // Coverage-ignore(suite): Not run. | |
| Reference? get writeTargetReference => null; | |
| @override | |
| // Coverage-ignore(suite): Not run. | |
| Member? get invokeTarget => null; | |
| @override | |
| // Coverage-ignore(suite): Not run. | |
| Reference? get invokeTargetReference => null; | |
| @override | |
| // Coverage-ignore(suite): Not run. | |
| Iterable<Reference> get exportedMemberReferences => const []; | |
| @override | |
| // Coverage-ignore(suite): Not run. | |
| bool get isProperty => throw new UnsupportedError("$runtimeType.isProperty"); | |
| @override | |
| // Coverage-ignore(suite): Not run. | |
| bool get isFinal => false; | |
| @override | |
| // Coverage-ignore(suite): Not run. | |
| bool get isSynthesized => false; | |
| @override | |
| DeclarationBuilder get declarationBuilder { | |
| throw new UnsupportedError('$runtimeType.declarationBuilder'); | |
| } | |
| @override | |
| ClassBuilder get classBuilder { | |
| throw new UnsupportedError('$runtimeType.classBuilder'); | |
| } | |
| @override | |
| SourceLibraryBuilder get libraryBuilder { | |
| throw new UnsupportedError('$runtimeType.library'); | |
| } | |
| @override | |
| void buildOutlineExpressions( | |
| ClassHierarchy classHierarchy, | |
| List<DelayedDefaultValueCloner> delayedDefaultValueCloners, | |
| ) { | |
| throw new UnsupportedError('$runtimeType.buildOutlineExpressions'); | |
| } | |
| @override | |
| // Coverage-ignore(suite): Not run. | |
| void buildOutlineNodes(BuildNodesCallback f) { | |
| assert(false, "Unexpected call to $runtimeType.buildOutlineNodes."); | |
| } | |
| @override | |
| // Coverage-ignore(suite): Not run. | |
| int buildBodyNodes(BuildNodesCallback f) { | |
| assert(false, "Unexpected call to $runtimeType.buildBodyNodes."); | |
| return 0; | |
| } | |
| @override | |
| // Coverage-ignore(suite): Not run. | |
| int computeDefaultTypes( | |
| ComputeDefaultTypeContext context, { | |
| required bool inErrorRecovery, | |
| }) { | |
| assert(false, "Unexpected call to $runtimeType.computeDefaultTypes."); | |
| return 0; | |
| } | |
| @override | |
| // Coverage-ignore(suite): Not run. | |
| List<ClassMember> get localMembers => const <ClassMember>[]; | |
| @override | |
| // Coverage-ignore(suite): Not run. | |
| List<ClassMember> get localSetters => const <ClassMember>[]; | |
| @override | |
| // Coverage-ignore(suite): Not run. | |
| void checkVariance( | |
| SourceClassBuilder sourceClassBuilder, | |
| TypeEnvironment typeEnvironment, | |
| ) { | |
| assert(false, "Unexpected call to $runtimeType.checkVariance."); | |
| } | |
| @override | |
| // Coverage-ignore(suite): Not run. | |
| void checkTypes( | |
| ProblemReporting problemReporting, | |
| LibraryFeatures libraryFeatures, | |
| NameSpace nameSpace, | |
| TypeEnvironment typeEnvironment, | |
| ) { | |
| assert(false, "Unexpected call to $runtimeType.checkVariance."); | |
| } | |
| @override | |
| MemberBuilder get getable; | |
| } | |
| class LookupResultIterator implements Iterator<NamedBuilder> { | |
| Iterator<LookupResult>? _lookupResultIterator; | |
| LookupResult? _currentLookupResult; | |
| NamedBuilder? _currentBuilder; | |
| new(this._lookupResultIterator); | |
| @override | |
| bool moveNext() { | |
| NamedBuilder? next = _currentBuilder?.next; | |
| if (next != null) { | |
| // Coverage-ignore-block(suite): Not run. | |
| _currentBuilder = next; | |
| return true; | |
| } | |
| next = _currentLookupResult?.setable; | |
| if (next != null) { | |
| _currentLookupResult = null; | |
| _currentBuilder = next; | |
| return true; | |
| } | |
| if (_lookupResultIterator != null) { | |
| if (_lookupResultIterator!.moveNext()) { | |
| LookupResult result = _lookupResultIterator!.current; | |
| if (result is NamedBuilder) { | |
| _currentBuilder = result as NamedBuilder; | |
| return true; | |
| } else { | |
| next = result.getable; | |
| if (next != null) { | |
| _currentBuilder = next; | |
| _currentLookupResult = result; | |
| return true; | |
| } | |
| // Coverage-ignore-block(suite): Not run. | |
| next = result.setable; | |
| if (next != null) { | |
| _currentBuilder = next; | |
| return true; | |
| } | |
| } | |
| } else { | |
| _lookupResultIterator = null; | |
| } | |
| } | |
| return false; | |
| } | |
| @override | |
| NamedBuilder get current { | |
| return _currentBuilder ?? // Coverage-ignore(suite): Not run. | |
| (throw new StateError('No element')); | |
| } | |
| } | |
| /// Filtered builder [Iterator]. | |
| class FilteredIterator<T extends NamedBuilder>( | |
| final Iterator<NamedBuilder> _iterator, { | |
| required final bool includeDuplicates, | |
| }) implements Iterator<T> { | |
| bool _include(NamedBuilder element) { | |
| if (!includeDuplicates && (element.isDuplicate)) { | |
| return false; | |
| } | |
| return element is T; | |
| } | |
| @override | |
| T get current => _iterator.current as T; | |
| @override | |
| bool moveNext() { | |
| while (_iterator.moveNext()) { | |
| NamedBuilder candidate = _iterator.current; | |
| if (_include(candidate)) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| } | |
| extension IteratorExtension<T extends NamedBuilder> on Iterator<T> { | |
| void forEach(void Function(T) f) { | |
| while (moveNext()) { | |
| f(current); | |
| } | |
| } | |
| List<T> toList() { | |
| List<T> list = []; | |
| while (moveNext()) { | |
| list.add(current); | |
| } | |
| return list; | |
| } | |
| Iterator<T> join(Iterator<T> other) { | |
| return new IteratorSequence<T>([this, other]); | |
| } | |
| } | |
| class IteratorSequence<T> implements Iterator<T> { | |
| Iterator<Iterator<T>> _iterators; | |
| Iterator<T>? _current; | |
| new(Iterable<Iterator<T>> iterators) : _iterators = iterators.iterator; | |
| @override | |
| T get current { | |
| if (_current != null) { | |
| return _current!.current; | |
| } | |
| // Coverage-ignore-block(suite): Not run. | |
| throw new StateError("No current element"); | |
| } | |
| @override | |
| bool moveNext() { | |
| if (_current != null) { | |
| if (_current!.moveNext()) { | |
| return true; | |
| } | |
| _current = null; | |
| } | |
| while (_iterators.moveNext()) { | |
| _current = _iterators.current; | |
| if (_current!.moveNext()) { | |
| return true; | |
| } | |
| _current = null; | |
| } | |
| return false; | |
| } | |
| } |