| // 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: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 extends BaseNameSpaceLookupScope { |
| @override |
| final NameSpace _nameSpace; |
| |
| @override |
| final LookupScope? _parent; |
| |
| NameSpaceLookupScope(this._nameSpace, {LookupScope? parent}) |
| : _parent = parent; |
| } |
| |
| abstract class AbstractTypeParameterScope implements LookupScope { |
| final LookupScope _parent; |
| |
| AbstractTypeParameterScope(this._parent); |
| |
| 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 extends AbstractTypeParameterScope { |
| final Map<String, TypeParameterBuilder> _typeParameters; |
| |
| TypeParameterScope(super._parent, this._typeParameters); |
| |
| @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 extends BaseNameSpaceLookupScope { |
| final SourceCompilationUnit _compilationUnit; |
| final NameSpace _importNameSpace; |
| |
| CompilationUnitImportScope(this._compilationUnit, this._importNameSpace); |
| |
| @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 extends BaseNameSpaceLookupScope { |
| final SourceCompilationUnit _compilationUnit; |
| |
| @override |
| final LookupScope? _parent; |
| |
| CompilationUnitScope(this._compilationUnit, {LookupScope? parent}) |
| : _parent = parent; |
| |
| @override |
| NameSpace get _nameSpace => _compilationUnit.libraryBuilder.libraryNameSpace; |
| } |
| |
| /// The scope containing the prefixes imported into a compilation unit. |
| class CompilationUnitPrefixScope extends BaseNameSpaceLookupScope { |
| @override |
| final ComputedNameSpace _nameSpace; |
| |
| @override |
| final LookupScope? _parent; |
| |
| CompilationUnitPrefixScope(this._nameSpace, {required LookupScope? parent}) |
| : _parent = parent; |
| } |
| |
| class DeclarationBuilderScope extends BaseNameSpaceLookupScope { |
| DeclarationBuilder? _declarationBuilder; |
| |
| @override |
| final LookupScope? _parent; |
| |
| DeclarationBuilderScope(this._parent); |
| |
| @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 = codeDuplicatedImport.withArgumentsOld( |
| name, |
| // TODO(ahe): We should probably use a context object here |
| // instead of including URIs in this message. |
| firstUri, |
| 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; |
| |
| LookupResultIterator(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> implements Iterator<T> { |
| final Iterator<NamedBuilder> _iterator; |
| final bool includeDuplicates; |
| |
| FilteredIterator(this._iterator, {required this.includeDuplicates}); |
| |
| 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; |
| |
| IteratorSequence(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; |
| } |
| } |