| // 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. |
| |
| library fasta.scope; |
| |
| import 'package:kernel/ast.dart'; |
| import 'package:kernel/class_hierarchy.dart'; |
| import 'package:kernel/type_environment.dart'; |
| |
| import '../builder/builder.dart'; |
| import '../builder/declaration_builders.dart'; |
| import '../builder/library_builder.dart'; |
| import '../builder/member_builder.dart'; |
| import '../builder/metadata_builder.dart'; |
| import '../builder/name_iterator.dart'; |
| import '../builder/prefix_builder.dart'; |
| //import '../kernel/body_builder.dart' show JumpTarget; |
| import '../kernel/body_builder_context.dart'; |
| import '../kernel/hierarchy/class_member.dart' show ClassMember; |
| import '../kernel/kernel_helper.dart'; |
| import '../kernel/load_library_builder.dart'; |
| import '../source/source_class_builder.dart'; |
| import '../source/source_extension_builder.dart'; |
| import '../source/source_extension_type_declaration_builder.dart'; |
| import '../source/source_function_builder.dart'; |
| import '../source/source_library_builder.dart'; |
| import '../source/source_member_builder.dart'; |
| import 'messages.dart'; |
| import 'name_space.dart'; |
| import 'uri_offset.dart'; |
| |
| enum ScopeKind { |
| /// Scope of pattern switch-case statements |
| /// |
| /// These scopes receive special treatment in that they are end-points of the |
| /// scope stack in presence of multiple heads for the same case, but can have |
| /// nested scopes if it's just a single head. In that latter possibility the |
| /// body of the case is nested into the scope of the case head. And for switch |
| /// expressions that scope includes both the head and the case expression. |
| caseHead, |
| |
| /// The declaration-level scope for classes, enums, and similar declarations |
| declaration, |
| |
| /// Scope where the formal parameters of a function are declared |
| formals, |
| |
| /// Scope of a `for` statement |
| forStatement, |
| |
| /// Scope of a function body |
| functionBody, |
| |
| /// Scope of the head of the if-case statement |
| ifCaseHead, |
| |
| /// Scope of an if-element in a collection |
| ifElement, |
| |
| /// Scope for the initializers of generative constructors |
| initializers, |
| |
| /// Scope where the joint variables of a switch case are declared |
| jointVariables, |
| |
| /// Scope where labels of labelled statements are declared |
| labels, |
| |
| /// Top-level scope of a library |
| library, |
| |
| /// The special scope of the named function expression |
| /// |
| /// This scope is treated separately because the named function expressions |
| /// are allowed to be recursive, and the name of that function expression |
| /// should be visible in the scope of the function itself. |
| namedFunctionExpression, |
| |
| /// The scope of the RHS of a binary-or pattern |
| /// |
| /// It is utilized for separating the branch-local variables from the joint |
| /// variables of the overall binary-or pattern. |
| orPatternRight, |
| |
| /// The scope of a pattern |
| /// |
| /// It contains the variables associated with pattern variable declarations. |
| pattern, |
| |
| /// Local scope of a statement, such as the body of a while loop |
| statementLocalScope, |
| |
| /// Local scope of a switch block |
| switchBlock, |
| |
| /// Scope for switch cases |
| /// |
| /// This scope kind is used in assertion checks. |
| switchCase, |
| |
| /// Scope for switch case bodies |
| /// |
| /// This is used to handle local variables of switch cases. |
| switchCaseBody, |
| |
| /// Scope for type parameters of declarations |
| typeParameters, |
| } |
| |
| abstract class LookupScope { |
| ScopeKind get kind; |
| Builder? lookupGetable(String name, int charOffset, Uri fileUri); |
| Builder? lookupSetable(String name, int charOffset, Uri fileUri); |
| // TODO(johnniwinther): Should this be moved to an outer scope interface? |
| void forEachExtension(void Function(ExtensionBuilder) f); |
| } |
| |
| /// Returns the correct value of the [getable] and [setable] found as a lookup |
| /// of [name]. |
| /// |
| /// If [isSetter] is `true`, the lookup intends to find a setable. Otherwise it |
| /// intends to find a getable. |
| /// |
| /// This ensures that an [AmbiguousBuilder] is returned if the found builder is |
| /// a duplicate. |
| /// |
| /// If [forStaticAccess] is `true`, `null` is returned if the found builder is |
| /// an instance member. |
| Builder? normalizeLookup( |
| {required Builder? getable, |
| required Builder? setable, |
| required String name, |
| required int charOffset, |
| required Uri fileUri, |
| required String classNameOrDebugName, |
| required bool isSetter, |
| bool forStaticAccess = false}) { |
| Builder? thisBuilder; |
| Builder? otherBuilder; |
| if (isSetter) { |
| thisBuilder = setable; |
| otherBuilder = getable; |
| } else { |
| thisBuilder = getable; |
| otherBuilder = setable; |
| } |
| Builder? builder = _normalizeBuilderLookup(thisBuilder, |
| name: name, |
| charOffset: charOffset, |
| fileUri: fileUri, |
| classNameOrDebugName: classNameOrDebugName, |
| forStaticAccess: forStaticAccess); |
| if (builder != null) { |
| return builder; |
| } |
| builder = _normalizeCrossLookup( |
| _normalizeBuilderLookup(otherBuilder, |
| name: name, |
| charOffset: charOffset, |
| fileUri: fileUri, |
| classNameOrDebugName: classNameOrDebugName, |
| forStaticAccess: forStaticAccess), |
| name: name, |
| charOffset: charOffset, |
| fileUri: fileUri); |
| return builder; |
| } |
| |
| /// Returns the correct value of [builder] found as a lookup of [name]. |
| /// |
| /// This ensures that an [AmbiguousBuilder] is returned if the found builder is |
| /// a duplicate. |
| /// |
| /// If [forStaticAccess] is `true`, `null` is returned if the found builder is |
| /// an instance member. |
| Builder? _normalizeBuilderLookup(Builder? builder, |
| {required String name, |
| required int charOffset, |
| required Uri fileUri, |
| required String classNameOrDebugName, |
| required bool forStaticAccess}) { |
| if (builder == null) return null; |
| if (builder.next != null) { |
| return new AmbiguousBuilder( |
| name.isEmpty |
| ? |
| // Coverage-ignore(suite): Not run. |
| classNameOrDebugName |
| : name, |
| builder, |
| charOffset, |
| fileUri); |
| } else if (forStaticAccess && builder.isDeclarationInstanceMember) { |
| return null; |
| } else if (builder is MemberBuilder && builder.isConflictingSetter) { |
| // TODO(johnniwinther): Use a variant of [AmbiguousBuilder] for this case. |
| return null; |
| } else { |
| return builder; |
| } |
| } |
| |
| /// Returns the correct value of [builder] found as a lookup of [name] where |
| /// [builder] is found as a setable in search of a getable or as a getable in |
| /// search of a setable. |
| /// |
| /// This ensures that an [AccessErrorBuilder] is returned if a non-problem |
| /// builder was found. |
| Builder? _normalizeCrossLookup(Builder? builder, |
| {required String name, required int charOffset, required Uri fileUri}) { |
| if (builder != null && !builder.hasProblem) { |
| return new AccessErrorBuilder(name, builder, charOffset, fileUri); |
| } |
| return builder; |
| } |
| |
| mixin LookupScopeMixin implements LookupScope { |
| String get classNameOrDebugName; |
| |
| Builder? lookupGetableIn( |
| String name, int charOffset, Uri fileUri, Map<String, Builder> getables) { |
| return normalizeLookup( |
| getable: getables[name], |
| setable: null, |
| name: name, |
| charOffset: charOffset, |
| fileUri: fileUri, |
| classNameOrDebugName: classNameOrDebugName, |
| isSetter: false); |
| } |
| |
| Builder? lookupSetableIn(String name, int charOffset, Uri fileUri, |
| Map<String, Builder>? getables) { |
| return normalizeLookup( |
| getable: getables?[name], |
| setable: null, |
| name: name, |
| charOffset: charOffset, |
| fileUri: fileUri, |
| classNameOrDebugName: classNameOrDebugName, |
| isSetter: true); |
| } |
| } |
| |
| /// A [LookupScope] based directly on a [NameSpace]. |
| abstract class BaseNameSpaceLookupScope implements LookupScope { |
| @override |
| final ScopeKind kind; |
| |
| final String classNameOrDebugName; |
| |
| BaseNameSpaceLookupScope(this.kind, this.classNameOrDebugName); |
| |
| NameSpace get _nameSpace; |
| |
| LookupScope? get _parent; |
| |
| @override |
| Builder? lookupGetable(String name, int charOffset, Uri fileUri) { |
| Builder? builder = normalizeLookup( |
| getable: _nameSpace.lookupLocalMember(name, setter: false), |
| setable: _nameSpace.lookupLocalMember(name, setter: true), |
| name: name, |
| charOffset: charOffset, |
| fileUri: fileUri, |
| classNameOrDebugName: classNameOrDebugName, |
| isSetter: false); |
| return builder ?? _parent?.lookupGetable(name, charOffset, fileUri); |
| } |
| |
| @override |
| Builder? lookupSetable(String name, int charOffset, Uri fileUri) { |
| Builder? builder = normalizeLookup( |
| getable: _nameSpace.lookupLocalMember(name, setter: false), |
| setable: _nameSpace.lookupLocalMember(name, setter: true), |
| name: name, |
| charOffset: charOffset, |
| fileUri: fileUri, |
| classNameOrDebugName: classNameOrDebugName, |
| isSetter: true); |
| return builder ?? _parent?.lookupSetable(name, charOffset, fileUri); |
| } |
| |
| @override |
| void forEachExtension(void Function(ExtensionBuilder) f) { |
| _nameSpace.forEachLocalExtension(f); |
| _parent?.forEachExtension(f); |
| } |
| } |
| |
| class NameSpaceLookupScope extends BaseNameSpaceLookupScope { |
| @override |
| final NameSpace _nameSpace; |
| |
| @override |
| final LookupScope? _parent; |
| |
| NameSpaceLookupScope(this._nameSpace, super.kind, super.classNameOrDebugName, |
| {LookupScope? parent}) |
| : _parent = parent; |
| } |
| |
| class TypeParameterScope with LookupScopeMixin { |
| final LookupScope _parent; |
| final Map<String, Builder> _typeParameters; |
| |
| TypeParameterScope(this._parent, this._typeParameters); |
| |
| @override |
| ScopeKind get kind => ScopeKind.typeParameters; |
| |
| @override |
| Builder? lookupGetable(String name, int charOffset, Uri fileUri) { |
| return lookupGetableIn(name, charOffset, fileUri, _typeParameters) ?? |
| _parent.lookupGetable(name, charOffset, fileUri); |
| } |
| |
| @override |
| Builder? lookupSetable(String name, int charOffset, Uri fileUri) { |
| Builder? builder = |
| lookupSetableIn(name, charOffset, fileUri, _typeParameters); |
| return builder ?? _parent.lookupSetable(name, charOffset, fileUri); |
| } |
| |
| @override |
| String get classNameOrDebugName => "type parameter"; |
| |
| static LookupScope fromList( |
| LookupScope parent, List<TypeVariableBuilder>? typeVariableBuilders) { |
| if (typeVariableBuilders == null) return parent; |
| Map<String, Builder> map = {}; |
| for (TypeVariableBuilder typeVariableBuilder in typeVariableBuilders) { |
| if (typeVariableBuilder.isWildcard) continue; |
| map[typeVariableBuilder.name] = typeVariableBuilder; |
| } |
| return new TypeParameterScope(parent, map); |
| } |
| |
| @override |
| void forEachExtension(void Function(ExtensionBuilder) f) { |
| _parent.forEachExtension(f); |
| } |
| } |
| |
| class FixedLookupScope implements LookupScope { |
| final LookupScope? _parent; |
| @override |
| final ScopeKind kind; |
| final String classNameOrDebugName; |
| final Map<String, Builder>? _getables; |
| final Map<String, Builder>? _setables; |
| |
| FixedLookupScope(this.kind, this.classNameOrDebugName, |
| {Map<String, Builder>? getables, |
| Map<String, Builder>? setables, |
| LookupScope? parent}) |
| : this._getables = getables, |
| this._setables = setables, |
| this._parent = parent; |
| |
| @override |
| Builder? lookupGetable(String name, int charOffset, Uri fileUri) { |
| Builder? builder = normalizeLookup( |
| getable: _getables?[name], |
| setable: _setables?[name], |
| name: name, |
| charOffset: charOffset, |
| fileUri: fileUri, |
| classNameOrDebugName: classNameOrDebugName, |
| isSetter: false); |
| return builder ?? _parent?.lookupGetable(name, charOffset, fileUri); |
| } |
| |
| @override |
| Builder? lookupSetable(String name, int charOffset, Uri fileUri) { |
| Builder? builder = normalizeLookup( |
| getable: _getables?[name], |
| setable: _setables?[name], |
| name: name, |
| charOffset: charOffset, |
| fileUri: fileUri, |
| classNameOrDebugName: classNameOrDebugName, |
| isSetter: true); |
| return builder ?? _parent?.lookupSetable(name, charOffset, fileUri); |
| } |
| |
| @override |
| void forEachExtension(void Function(ExtensionBuilder) f) { |
| _parent?.forEachExtension(f); |
| } |
| } |
| |
| // TODO(johnniwinther): Use this instead of [SourceLibraryBuilderScope]. |
| class CompilationUnitScope extends BaseNameSpaceLookupScope { |
| final CompilationUnit _compilationUnit; |
| |
| @override |
| final LookupScope? _parent; |
| |
| CompilationUnitScope( |
| this._compilationUnit, super.kind, super.classNameOrDebugName, |
| {LookupScope? parent}) |
| : _parent = parent; |
| |
| @override |
| NameSpace get _nameSpace => _compilationUnit.libraryBuilder.nameSpace; |
| } |
| |
| class SourceLibraryBuilderScope extends BaseNameSpaceLookupScope { |
| final SourceLibraryBuilder _libraryBuilder; |
| |
| SourceLibraryBuilderScope( |
| this._libraryBuilder, super.kind, super.classNameOrDebugName); |
| |
| @override |
| NameSpace get _nameSpace => _libraryBuilder.nameSpace; |
| |
| @override |
| LookupScope? get _parent => _libraryBuilder.importScope; |
| } |
| |
| class ConstructorScope { |
| /// Constructors declared in this scope. |
| final Map<String, MemberBuilder> _local; |
| |
| final String className; |
| |
| ConstructorScope(this.className, this._local); |
| |
| MemberBuilder? lookup(String name, int charOffset, Uri fileUri) { |
| MemberBuilder? builder = _local[name]; |
| if (builder == null) return null; |
| if (builder.next != null) { |
| return new AmbiguousMemberBuilder( |
| name.isEmpty ? className : name, builder, charOffset, fileUri); |
| } else { |
| return builder; |
| } |
| } |
| |
| MemberBuilder? lookupLocalMember(String name) { |
| return _local[name]; |
| } |
| |
| void addLocalMember(String name, MemberBuilder builder) { |
| _local[name] = builder; |
| } |
| |
| void addLocalMembers(Map<String, MemberBuilder> map) { |
| _local.addAll(map); |
| } |
| |
| /// Returns an iterator of all constructors mapped in this scope, |
| /// including duplicate constructors mapped to the same name. |
| Iterator<MemberBuilder> get unfilteredIterator => |
| new ConstructorScopeIterator(this); |
| |
| /// Returns an iterator of all constructors mapped in this scope, |
| /// including duplicate constructors mapped to the same name. |
| /// |
| /// Compared to [unfilteredIterator] this iterator also gives access to the |
| /// name that the builders are mapped to. |
| NameIterator<MemberBuilder> get unfilteredNameIterator => |
| new ConstructorScopeNameIterator(this); |
| |
| /// Returns a filtered iterator of constructors mapped in this scope. |
| /// |
| /// Only members of type [T] are included. If [parent] is provided, on members |
| /// declared in [parent] are included. If [includeDuplicates] is `true`, all |
| /// duplicates of the same name are included, otherwise, only the first |
| /// declared member is included. If [includeAugmentations] is `true`, both |
| /// original and augmenting/patching members are included, otherwise, only |
| /// original members are included. |
| Iterator<T> filteredIterator<T extends MemberBuilder>( |
| {Builder? parent, |
| required bool includeDuplicates, |
| required bool includeAugmentations}) { |
| return new FilteredIterator<T>(unfilteredIterator, |
| parent: parent, |
| includeDuplicates: includeDuplicates, |
| includeAugmentations: includeAugmentations); |
| } |
| |
| /// Returns a filtered iterator of constructors mapped in this scope. |
| /// |
| /// Only members of type [T] are included. If [parent] is provided, on members |
| /// declared in [parent] are included. If [includeDuplicates] is `true`, all |
| /// duplicates of the same name are included, otherwise, only the first |
| /// declared member is included. If [includeAugmentations] is `true`, both |
| /// original and augmenting/patching members are included, otherwise, only |
| /// original members are included. |
| /// |
| /// Compared to [filteredIterator] this iterator also gives access to the |
| /// name that the builders are mapped to. |
| NameIterator<T> filteredNameIterator<T extends MemberBuilder>( |
| {Builder? parent, |
| required bool includeDuplicates, |
| required bool includeAugmentations}) { |
| return new FilteredNameIterator<T>(unfilteredNameIterator, |
| parent: parent, |
| includeDuplicates: includeDuplicates, |
| includeAugmentations: includeAugmentations); |
| } |
| |
| @override |
| String toString() => "ConstructorScope($className, ${_local.keys})"; |
| } |
| |
| /// Computes a builder for the import/export collision between [declaration] and |
| /// [other] and adds it to [nameSpace]. |
| Builder computeAmbiguousDeclarationForScope(ProblemReporting problemReporting, |
| NameSpace nameSpace, String name, Builder declaration, Builder other, |
| {required UriOffset uriOffset, |
| bool isExport = false, |
| bool isImport = false}) { |
| // TODO(ahe): Can I move this to Scope or Prefix? |
| if (declaration == other) return declaration; |
| if (declaration is InvalidTypeDeclarationBuilder) return declaration; |
| if (other is InvalidTypeDeclarationBuilder) return other; |
| if (declaration is AccessErrorBuilder) { |
| // Coverage-ignore-block(suite): Not run. |
| AccessErrorBuilder error = declaration; |
| declaration = error.builder; |
| } |
| if (other is AccessErrorBuilder) { |
| // Coverage-ignore-block(suite): Not run. |
| AccessErrorBuilder error = other; |
| other = error.builder; |
| } |
| Builder? preferred; |
| Uri? uri; |
| Uri? otherUri; |
| if (nameSpace.lookupLocalMember(name, setter: false) == declaration) { |
| preferred = declaration; |
| } else { |
| uri = computeLibraryUri(declaration); |
| 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; |
| } |
| if (declaration.next == null && other.next == null) { |
| if (isImport && |
| declaration is PrefixBuilder && |
| // Coverage-ignore(suite): Not run. |
| other is PrefixBuilder) { |
| // Coverage-ignore-block(suite): Not run. |
| // Handles the case where the same prefix is used for different |
| // imports. |
| declaration.mergeScopes(other, problemReporting, nameSpace, |
| uriOffset: uriOffset, isImport: isImport, isExport: isExport); |
| return declaration; |
| } |
| } |
| Uri firstUri = uri!; |
| Uri secondUri = otherUri!; |
| if (firstUri.toString().compareTo(secondUri.toString()) > 0) { |
| firstUri = secondUri; |
| secondUri = uri; |
| } |
| if (isExport) { |
| Template<Message Function(String name, Uri uri, Uri uri2)> template = |
| templateDuplicatedExport; |
| Message message = template.withArguments(name, firstUri, secondUri); |
| problemReporting.addProblem( |
| message, uriOffset.fileOffset, noLength, uriOffset.uri); |
| } |
| Template<Message Function(String name, Uri uri, Uri uri2)> builderTemplate = |
| isExport |
| ? templateDuplicatedExportInType |
| : templateDuplicatedImportInType; |
| Message message = builderTemplate.withArguments( |
| 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 suppressMessage 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 InvalidTypeDeclarationBuilder(name, |
| message.withLocation(uriOffset.uri, uriOffset.fileOffset, name.length), |
| suppressMessage: false); |
| } |
| |
| abstract class ProblemBuilder extends BuilderImpl { |
| final String name; |
| |
| final Builder builder; |
| |
| @override |
| final int charOffset; |
| |
| @override |
| final Uri fileUri; |
| |
| ProblemBuilder(this.name, this.builder, this.charOffset, this.fileUri); |
| |
| @override |
| bool get hasProblem => true; |
| |
| Message get message; |
| |
| @override |
| String get fullNameForErrors => name; |
| } |
| |
| /// Represents a [builder] that's being accessed incorrectly. For example, an |
| /// attempt to write to a final field, or to read from a setter. |
| class AccessErrorBuilder extends ProblemBuilder { |
| AccessErrorBuilder(String name, Builder builder, int charOffset, Uri fileUri) |
| : super(name, builder, charOffset, fileUri); |
| |
| @override |
| Builder? get parent => builder.parent; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| bool get isFinal => builder.isFinal; |
| |
| @override |
| bool get isField => builder.isField; |
| |
| @override |
| bool get isRegularMethod => builder.isRegularMethod; |
| |
| @override |
| bool get isGetter => !builder.isGetter; |
| |
| @override |
| bool get isSetter => !builder.isSetter; |
| |
| @override |
| bool get isDeclarationInstanceMember => builder.isDeclarationInstanceMember; |
| |
| @override |
| bool get isClassInstanceMember => builder.isClassInstanceMember; |
| |
| @override |
| bool get isExtensionInstanceMember => builder.isExtensionInstanceMember; |
| |
| @override |
| bool get isExtensionTypeInstanceMember => |
| builder.isExtensionTypeInstanceMember; |
| |
| @override |
| bool get isStatic => builder.isStatic; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| bool get isTopLevel => builder.isTopLevel; |
| |
| @override |
| bool get isTypeDeclaration => builder.isTypeDeclaration; |
| |
| @override |
| bool get isLocal => builder.isLocal; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Message get message => templateAccessError.withArguments(name); |
| } |
| |
| class AmbiguousBuilder extends ProblemBuilder { |
| AmbiguousBuilder(String name, Builder builder, int charOffset, Uri fileUri) |
| : super(name, builder, charOffset, fileUri); |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Builder? get parent => null; |
| |
| @override |
| Message get message => templateDuplicatedDeclarationUse.withArguments(name); |
| |
| // Coverage-ignore(suite): Not run. |
| // TODO(ahe): Also provide context. |
| |
| Builder getFirstDeclaration() { |
| Builder declaration = builder; |
| while (declaration.next != null) { |
| declaration = declaration.next!; |
| } |
| return declaration; |
| } |
| } |
| |
| mixin ErroneousMemberBuilderMixin implements SourceMemberBuilder { |
| @override |
| // Coverage-ignore(suite): Not run. |
| MemberDataForTesting? get dataForTesting => null; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Member get member => throw new UnsupportedError('$runtimeType.member'); |
| |
| @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. |
| Member? get writeTarget => null; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Member? get invokeTarget => null; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Iterable<Member> get exportedMembers => const []; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| bool get isAssignable => false; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| bool get isExternal => false; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| bool get isAbstract => false; |
| |
| @override |
| bool get isConflictingSetter => false; |
| |
| @override |
| bool get isConflictingAugmentationMember => false; |
| |
| @override |
| void set isConflictingAugmentationMember(bool value) { |
| throw new UnsupportedError('$runtimeType.isConflictingAugmentationMember='); |
| } |
| |
| @override |
| void set parent(Builder? value) { |
| throw new UnsupportedError('$runtimeType.parent='); |
| } |
| |
| @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'); |
| } |
| |
| // TODO(johnniwinther): Remove this and create a [ProcedureBuilder] interface. |
| @override |
| // Coverage-ignore(suite): Not run. |
| ProcedureKind? get kind => null; |
| |
| @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. |
| 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( |
| SourceLibraryBuilder library, TypeEnvironment typeEnvironment) { |
| assert(false, "Unexpected call to $runtimeType.checkVariance."); |
| } |
| |
| @override |
| bool get isAugmentation { |
| throw new UnsupportedError('$runtimeType.isAugmentation'); |
| } |
| |
| @override |
| AugmentSuperTarget? get augmentSuperTarget { |
| throw new UnsupportedError('$runtimeType.augmentSuperTarget}'); |
| } |
| |
| @override |
| BodyBuilderContext createBodyBuilderContext( |
| {required bool inOutlineBuildingPhase, |
| required bool inMetadata, |
| required bool inConstFields}) { |
| throw new UnsupportedError( |
| '$runtimeType.bodyBuilderContextForAnnotations}'); |
| } |
| |
| @override |
| Iterable<Annotatable> get annotatables { |
| throw new UnsupportedError('$runtimeType.annotatables}'); |
| } |
| } |
| |
| class AmbiguousMemberBuilder extends AmbiguousBuilder |
| with ErroneousMemberBuilderMixin { |
| AmbiguousMemberBuilder( |
| String name, Builder builder, int charOffset, Uri fileUri) |
| : super(name, builder, charOffset, fileUri); |
| } |
| |
| /// Iterator over builders mapped in a [Scope], including duplicates for each |
| /// directly mapped builder. |
| class ScopeIterator implements Iterator<Builder> { |
| Iterator<Builder>? local; |
| Iterator<Builder>? setters; |
| Iterator<Builder>? extensions; |
| |
| Builder? _current; |
| |
| ScopeIterator(this.local, this.setters, this.extensions); |
| |
| @override |
| bool moveNext() { |
| Builder? next = _current?.next; |
| if (next != null) { |
| _current = next; |
| return true; |
| } |
| if (local != null) { |
| if (local!.moveNext()) { |
| _current = local!.current; |
| return true; |
| } |
| local = null; |
| } |
| if (setters != null) { |
| if (setters!.moveNext()) { |
| _current = setters!.current; |
| return true; |
| } |
| setters = null; |
| } |
| if (extensions != null) { |
| while (extensions!.moveNext()) { |
| Builder extension = extensions!.current; |
| // Named extensions have already been included throw [local] so we skip |
| // them here. |
| if (extension is SourceExtensionBuilder && |
| extension.isUnnamedExtension) { |
| _current = extension; |
| return true; |
| } |
| } |
| extensions = null; |
| } |
| _current = null; |
| return false; |
| } |
| |
| @override |
| Builder get current { |
| return _current ?? // Coverage-ignore(suite): Not run. |
| (throw new StateError('No element')); |
| } |
| } |
| |
| /// Iterator over builders mapped in a [Scope], including duplicates for each |
| /// directly mapped builder. |
| /// |
| /// Compared to [ScopeIterator] this iterator also gives |
| /// access to the name that the builders are mapped to. |
| class ScopeNameIterator extends ScopeIterator implements NameIterator<Builder> { |
| Iterator<String>? localNames; |
| Iterator<String>? setterNames; |
| |
| String? _name; |
| |
| ScopeNameIterator(Map<String, Builder>? getables, |
| Map<String, Builder>? setables, Iterator<Builder>? extensions) |
| : localNames = getables?.keys.iterator, |
| setterNames = setables?.keys.iterator, |
| super(getables?.values.iterator, setables?.values.iterator, extensions); |
| |
| @override |
| bool moveNext() { |
| Builder? next = _current?.next; |
| if (next != null) { |
| _current = next; |
| return true; |
| } |
| if (local != null) { |
| if (local!.moveNext()) { |
| localNames!.moveNext(); |
| _current = local!.current; |
| _name = localNames!.current; |
| return true; |
| } |
| local = null; |
| localNames = null; |
| } |
| if (setters != null) { |
| if (setters!.moveNext()) { |
| setterNames!.moveNext(); |
| _current = setters!.current; |
| _name = setterNames!.current; |
| return true; |
| } |
| setters = null; |
| setterNames = null; |
| } |
| if (extensions != null) { |
| while (extensions!.moveNext()) { |
| Builder extension = extensions!.current; |
| // Named extensions have already been included throw [local] so we skip |
| // them here. |
| if (extension is SourceExtensionBuilder && |
| extension.isUnnamedExtension) { |
| _current = extension; |
| _name = extension.name; |
| return true; |
| } |
| } |
| extensions = null; |
| } |
| _current = null; |
| _name = null; |
| return false; |
| } |
| |
| @override |
| String get name { |
| return _name ?? // Coverage-ignore(suite): Not run. |
| (throw new StateError('No element')); |
| } |
| } |
| |
| /// Iterator over builders mapped in a [ConstructorScope], including duplicates |
| /// for each directly mapped builder. |
| class ConstructorScopeIterator implements Iterator<MemberBuilder> { |
| Iterator<MemberBuilder> local; |
| |
| MemberBuilder? _current; |
| |
| ConstructorScopeIterator(ConstructorScope scope) |
| : local = scope._local.values.iterator; |
| |
| @override |
| bool moveNext() { |
| MemberBuilder? next = _current?.next as MemberBuilder?; |
| if (next != null) { |
| _current = next; |
| return true; |
| } |
| if (local.moveNext()) { |
| _current = local.current; |
| return true; |
| } |
| return false; |
| } |
| |
| @override |
| MemberBuilder get current { |
| return _current ?? // Coverage-ignore(suite): Not run. |
| (throw new StateError('No element')); |
| } |
| } |
| |
| /// Iterator over builders mapped in a [ConstructorScope], including duplicates |
| /// for each directly mapped builder. |
| /// |
| /// Compared to [ConstructorScopeIterator] this iterator also gives |
| /// access to the name that the builders are mapped to. |
| class ConstructorScopeNameIterator extends ConstructorScopeIterator |
| implements NameIterator<MemberBuilder> { |
| final Iterator<String> localNames; |
| |
| String? _name; |
| |
| ConstructorScopeNameIterator(ConstructorScope scope) |
| : localNames = scope._local.keys.iterator, |
| super(scope); |
| |
| @override |
| bool moveNext() { |
| MemberBuilder? next = _current?.next as MemberBuilder?; |
| if (next != null) { |
| _current = next; |
| return true; |
| } |
| if (local.moveNext()) { |
| localNames.moveNext(); |
| _current = local.current; |
| _name = localNames.current; |
| return true; |
| } |
| _current = null; |
| _name = null; |
| return false; |
| } |
| |
| @override |
| String get name { |
| return _name ?? // Coverage-ignore(suite): Not run. |
| (throw new StateError('No element')); |
| } |
| } |
| |
| /// Filtered builder [Iterator]. |
| class FilteredIterator<T extends Builder> implements Iterator<T> { |
| final Iterator<Builder> _iterator; |
| final Builder? parent; |
| final bool includeDuplicates; |
| final bool includeAugmentations; |
| |
| FilteredIterator(this._iterator, |
| {required this.parent, |
| required this.includeDuplicates, |
| required this.includeAugmentations}); |
| |
| bool _include(Builder element) { |
| if (parent != null && element.parent != parent) return false; |
| if (!includeDuplicates && |
| (element.isDuplicate || element.isConflictingAugmentationMember)) { |
| return false; |
| } |
| if (!includeAugmentations && element.isAugmenting) return false; |
| return element is T; |
| } |
| |
| @override |
| T get current => _iterator.current as T; |
| |
| @override |
| bool moveNext() { |
| while (_iterator.moveNext()) { |
| Builder candidate = _iterator.current; |
| if (_include(candidate)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| } |
| |
| /// Filtered [NameIterator]. |
| /// |
| /// Compared to [FilteredIterator] this iterator also gives |
| /// access to the name that the builders are mapped to. |
| class FilteredNameIterator<T extends Builder> implements NameIterator<T> { |
| final NameIterator<Builder> _iterator; |
| final Builder? parent; |
| final bool includeDuplicates; |
| final bool includeAugmentations; |
| |
| FilteredNameIterator(this._iterator, |
| {required this.parent, |
| required this.includeDuplicates, |
| required this.includeAugmentations}); |
| |
| bool _include(Builder element) { |
| if (parent != null && element.parent != parent) return false; |
| if (!includeDuplicates && |
| (element.isDuplicate || element.isConflictingAugmentationMember)) { |
| return false; |
| } |
| if (!includeAugmentations && element.isAugmenting) return false; |
| return element is T; |
| } |
| |
| @override |
| T get current => _iterator.current as T; |
| |
| @override |
| String get name => _iterator.name; |
| |
| @override |
| bool moveNext() { |
| while (_iterator.moveNext()) { |
| Builder candidate = _iterator.current; |
| if (_include(candidate)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| } |
| |
| extension IteratorExtension<T extends Builder> on Iterator<T> { |
| void forEach(void Function(T) f) { |
| while (moveNext()) { |
| f(current); |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| 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]); |
| } |
| } |
| |
| extension NameIteratorExtension<T extends Builder> on NameIterator<T> { |
| void forEach(void Function(String, T) f) { |
| while (moveNext()) { |
| f(name, current); |
| } |
| } |
| } |
| |
| abstract class MergedScope<T extends Builder> { |
| final T _origin; |
| final NameSpace _originNameSpace; |
| Map<T, NameSpace> _augmentationNameSpaces = {}; |
| |
| MergedScope(this._origin, this._originNameSpace); |
| |
| SourceLibraryBuilder get originLibrary; |
| |
| void _addBuilderToMergedScope( |
| String name, Builder newBuilder, Builder? existingBuilder, |
| {required bool setter, required bool inPatchLibrary}) { |
| bool isAugmentationBuilder = inPatchLibrary |
| ? newBuilder.hasPatchAnnotation |
| : newBuilder.isAugmentation; |
| if (existingBuilder != null) { |
| if (isAugmentationBuilder) { |
| existingBuilder.applyAugmentation(newBuilder); |
| } else { |
| newBuilder.isConflictingAugmentationMember = true; |
| Message message; |
| Message context; |
| if (newBuilder is SourceMemberBuilder && |
| existingBuilder is SourceMemberBuilder) { |
| if (_origin is SourceLibraryBuilder) { |
| message = inPatchLibrary |
| ? templateNonPatchLibraryMemberConflict.withArguments(name) |
| : templateNonAugmentationLibraryMemberConflict |
| .withArguments(name); |
| } else { |
| message = inPatchLibrary |
| ? templateNonPatchClassMemberConflict.withArguments(name) |
| : templateNonAugmentationClassMemberConflict |
| .withArguments(name); |
| } |
| context = messageNonAugmentationMemberConflictCause; |
| } else if (newBuilder is SourceClassBuilder && |
| existingBuilder is SourceClassBuilder) { |
| message = inPatchLibrary |
| ? templateNonPatchClassConflict.withArguments(name) |
| : templateNonAugmentationClassConflict.withArguments(name); |
| context = messageNonAugmentationClassConflictCause; |
| } else { |
| if (_origin is SourceLibraryBuilder) { |
| message = inPatchLibrary |
| ? templateNonPatchLibraryConflict.withArguments(name) |
| : templateNonAugmentationLibraryConflict.withArguments(name); |
| } else { |
| // Coverage-ignore-block(suite): Not run. |
| message = inPatchLibrary |
| ? templateNonPatchClassMemberConflict.withArguments(name) |
| : templateNonAugmentationClassMemberConflict |
| .withArguments(name); |
| } |
| context = messageNonAugmentationMemberConflictCause; |
| } |
| originLibrary.addProblem( |
| message, newBuilder.charOffset, name.length, newBuilder.fileUri, |
| context: [ |
| context.withLocation(existingBuilder.fileUri!, |
| existingBuilder.charOffset, name.length) |
| ]); |
| } |
| } else { |
| if (isAugmentationBuilder) { |
| Message message; |
| if (newBuilder is SourceMemberBuilder) { |
| if (_origin is SourceLibraryBuilder) { |
| message = inPatchLibrary |
| ? templateUnmatchedPatchLibraryMember.withArguments(name) |
| : templateUnmatchedAugmentationLibraryMember |
| .withArguments(name); |
| } else { |
| message = inPatchLibrary |
| ? templateUnmatchedPatchClassMember.withArguments(name) |
| : templateUnmatchedAugmentationClassMember.withArguments(name); |
| } |
| } else if (newBuilder is SourceClassBuilder) { |
| message = inPatchLibrary |
| ? templateUnmatchedPatchClass.withArguments(name) |
| : templateUnmatchedAugmentationClass.withArguments(name); |
| } else { |
| message = inPatchLibrary |
| ? templateUnmatchedPatchDeclaration.withArguments(name) |
| : |
| // Coverage-ignore(suite): Not run. |
| templateUnmatchedAugmentationDeclaration.withArguments(name); |
| } |
| originLibrary.addProblem( |
| message, newBuilder.charOffset, name.length, newBuilder.fileUri); |
| } else { |
| if (inPatchLibrary && |
| !name.startsWith('_') && |
| !_allowInjectedPublicMember(newBuilder)) { |
| originLibrary.addProblem( |
| templatePatchInjectionFailed.withArguments( |
| name, originLibrary.importUri), |
| newBuilder.charOffset, |
| noLength, |
| newBuilder.fileUri); |
| } |
| _originNameSpace.addLocalMember(name, newBuilder, setter: setter); |
| if (newBuilder is ExtensionBuilder) { |
| _originNameSpace.addExtension(newBuilder); |
| } |
| for (NameSpace augmentationNameSpace |
| in _augmentationNameSpaces.values) { |
| _addBuilderToAugmentationNameSpace( |
| augmentationNameSpace, name, newBuilder, |
| setter: setter); |
| } |
| } |
| } |
| } |
| |
| void _addBuilderToAugmentationNameSpace( |
| NameSpace augmentationNameSpace, String name, Builder member, |
| {required bool setter}) { |
| Builder? augmentationMember = |
| augmentationNameSpace.lookupLocalMember(name, setter: setter); |
| if (augmentationMember == null) { |
| augmentationNameSpace.addLocalMember(name, member, setter: setter); |
| if (member is ExtensionBuilder) { |
| augmentationNameSpace.addExtension(member); |
| } |
| } |
| } |
| |
| void _addAugmentationScope(T parentBuilder, NameSpace nameSpace, |
| {required Map<String, List<Builder>>? augmentations, |
| required Map<String, List<Builder>>? setterAugmentations, |
| required bool inPatchLibrary}) { |
| // TODO(johnniwinther): Use `scope.filteredNameIterator` instead of |
| // `scope.forEachLocalMember`/`scope.forEachLocalSetter`. |
| |
| // Include all augmentation scope members to the origin scope. |
| nameSpace.forEachLocalMember((String name, Builder member) { |
| // In case of duplicates we use the first declaration. |
| while (member.isDuplicate) { |
| member = member.next!; |
| } |
| _addBuilderToMergedScope( |
| name, member, _originNameSpace.lookupLocalMember(name, setter: false), |
| setter: false, inPatchLibrary: inPatchLibrary); |
| }); |
| if (augmentations != null) { |
| for (String augmentedName in augmentations.keys) { |
| for (Builder augmentation in augmentations[augmentedName]!) { |
| _addBuilderToMergedScope(augmentedName, augmentation, |
| _originNameSpace.lookupLocalMember(augmentedName, setter: false), |
| setter: false, inPatchLibrary: inPatchLibrary); |
| } |
| } |
| } |
| nameSpace.forEachLocalSetter((String name, Builder member) { |
| // In case of duplicates we use the first declaration. |
| while (member.isDuplicate) { |
| member = member.next!; |
| } |
| _addBuilderToMergedScope( |
| name, member, _originNameSpace.lookupLocalMember(name, setter: true), |
| setter: true, inPatchLibrary: inPatchLibrary); |
| }); |
| if (setterAugmentations != null) { |
| for (String augmentedName in setterAugmentations.keys) { |
| for (Builder augmentation in setterAugmentations[augmentedName]!) { |
| _addBuilderToMergedScope(augmentedName, augmentation, |
| _originNameSpace.lookupLocalMember(augmentedName, setter: true), |
| setter: true, inPatchLibrary: inPatchLibrary); |
| } |
| } |
| } |
| nameSpace.forEachLocalExtension((ExtensionBuilder extensionBuilder) { |
| if (extensionBuilder is SourceExtensionBuilder && |
| extensionBuilder.isUnnamedExtension) { |
| _originNameSpace.addExtension(extensionBuilder); |
| for (NameSpace augmentationNameSpace |
| in _augmentationNameSpaces.values) { |
| augmentationNameSpace.addExtension(extensionBuilder); |
| } |
| } |
| }); |
| |
| // Include all origin scope members in the augmentation scope. |
| _originNameSpace.forEachLocalMember((String name, Builder originMember) { |
| _addBuilderToAugmentationNameSpace(nameSpace, name, originMember, |
| setter: false); |
| }); |
| _originNameSpace.forEachLocalSetter((String name, Builder originMember) { |
| _addBuilderToAugmentationNameSpace(nameSpace, name, originMember, |
| setter: true); |
| }); |
| _originNameSpace.forEachLocalExtension((ExtensionBuilder extensionBuilder) { |
| if (extensionBuilder is SourceExtensionBuilder && |
| extensionBuilder.isUnnamedExtension) { |
| nameSpace.addExtension(extensionBuilder); |
| } |
| }); |
| |
| _augmentationNameSpaces[parentBuilder] = nameSpace; |
| } |
| |
| bool _allowInjectedPublicMember(Builder newBuilder); |
| } |
| |
| class MergedLibraryScope extends MergedScope<SourceLibraryBuilder> { |
| MergedLibraryScope(SourceLibraryBuilder origin) |
| : super(origin, origin.nameSpace); |
| |
| @override |
| SourceLibraryBuilder get originLibrary => _origin; |
| |
| void addAugmentationScope(SourceLibraryBuilder builder) { |
| _addAugmentationScope(builder, builder.nameSpace, |
| augmentations: builder.augmentations, |
| setterAugmentations: builder.setterAugmentations, |
| inPatchLibrary: builder.isPatchLibrary); |
| } |
| |
| @override |
| bool _allowInjectedPublicMember(Builder newBuilder) { |
| return originLibrary.importUri.isScheme("dart") && |
| originLibrary.importUri.path.startsWith("_"); |
| } |
| } |
| |
| class MergedClassMemberScope extends MergedScope<SourceClassBuilder> { |
| final ConstructorScope _originConstructorScope; |
| Map<SourceClassBuilder, ConstructorScope> _augmentationConstructorScopes = {}; |
| |
| MergedClassMemberScope(SourceClassBuilder origin) |
| : _originConstructorScope = origin.constructorScope, |
| super(origin, origin.nameSpace); |
| |
| @override |
| SourceLibraryBuilder get originLibrary => _origin.libraryBuilder; |
| |
| void _addAugmentationConstructorScope(ConstructorScope constructorScope, |
| {required bool inPatchLibrary}) { |
| constructorScope._local |
| .forEach((String name, MemberBuilder newConstructor) { |
| MemberBuilder? existingConstructor = |
| _originConstructorScope.lookupLocalMember(name); |
| bool isAugmentationBuilder = inPatchLibrary |
| ? newConstructor.hasPatchAnnotation |
| : newConstructor.isAugmentation; |
| if (existingConstructor != null) { |
| if (isAugmentationBuilder) { |
| existingConstructor.applyAugmentation(newConstructor); |
| } else { |
| newConstructor.isConflictingAugmentationMember = true; |
| originLibrary.addProblem( |
| inPatchLibrary |
| ? templateNonPatchConstructorConflict |
| .withArguments(newConstructor.fullNameForErrors) |
| : |
| // Coverage-ignore(suite): Not run. |
| templateNonAugmentationConstructorConflict |
| .withArguments(newConstructor.fullNameForErrors), |
| newConstructor.charOffset, |
| noLength, |
| newConstructor.fileUri, |
| context: [ |
| messageNonAugmentationConstructorConflictCause.withLocation( |
| existingConstructor.fileUri!, |
| existingConstructor.charOffset, |
| noLength) |
| ]); |
| } |
| } else { |
| if (isAugmentationBuilder) { |
| originLibrary.addProblem( |
| inPatchLibrary |
| ? templateUnmatchedPatchConstructor |
| .withArguments(newConstructor.fullNameForErrors) |
| : |
| // Coverage-ignore(suite): Not run. |
| templateUnmatchedAugmentationConstructor |
| .withArguments(newConstructor.fullNameForErrors), |
| newConstructor.charOffset, |
| noLength, |
| newConstructor.fileUri); |
| } else { |
| _originConstructorScope.addLocalMember(name, newConstructor); |
| for (ConstructorScope augmentationConstructorScope |
| in _augmentationConstructorScopes.values) { |
| // Coverage-ignore-block(suite): Not run. |
| _addConstructorToAugmentationScope( |
| augmentationConstructorScope, name, newConstructor); |
| } |
| } |
| if (inPatchLibrary && |
| !name.startsWith('_') && |
| !_allowInjectedPublicMember(newConstructor)) { |
| // Coverage-ignore-block(suite): Not run. |
| originLibrary.addProblem( |
| templatePatchInjectionFailed.withArguments( |
| name, originLibrary.importUri), |
| newConstructor.charOffset, |
| noLength, |
| newConstructor.fileUri); |
| } |
| } |
| }); |
| _originConstructorScope._local |
| .forEach((String name, MemberBuilder originConstructor) { |
| _addConstructorToAugmentationScope( |
| constructorScope, name, originConstructor); |
| }); |
| } |
| |
| void _addConstructorToAugmentationScope( |
| ConstructorScope augmentationConstructorScope, |
| String name, |
| MemberBuilder constructor) { |
| Builder? augmentationConstructor = |
| augmentationConstructorScope.lookupLocalMember(name); |
| if (augmentationConstructor == null) { |
| augmentationConstructorScope.addLocalMember(name, constructor); |
| } |
| } |
| |
| // TODO(johnniwinther): Check for conflicts between constructors and class |
| // members. |
| void addAugmentationScope(SourceClassBuilder builder) { |
| _addAugmentationScope(builder, builder.nameSpace, |
| augmentations: null, |
| setterAugmentations: null, |
| inPatchLibrary: builder.libraryBuilder.isPatchLibrary); |
| _addAugmentationConstructorScope(builder.constructorScope, |
| inPatchLibrary: builder.libraryBuilder.isPatchLibrary); |
| } |
| |
| @override |
| bool _allowInjectedPublicMember(Builder newBuilder) { |
| if (originLibrary.importUri.isScheme("dart") && |
| originLibrary.importUri.path.startsWith("_")) { |
| return true; |
| } |
| if (newBuilder.isStatic) { |
| // Coverage-ignore-block(suite): Not run. |
| return _origin.name.startsWith('_'); |
| } |
| // TODO(johnniwinther): Restrict the use of injected public class members. |
| return true; |
| } |
| } |
| |
| extension on Builder { |
| bool get isAugmentation { |
| Builder self = this; |
| if (self is SourceLibraryBuilder) { |
| // Coverage-ignore-block(suite): Not run. |
| return self.isAugmentationLibrary; |
| } else if (self is SourceClassBuilder) { |
| return self.isAugmentation; |
| } else if (self is SourceMemberBuilder) { |
| return self.isAugmentation; |
| } else { |
| // TODO(johnniwinther): Handle all cases here. |
| return false; |
| } |
| } |
| |
| bool get isConflictingAugmentationMember { |
| Builder self = this; |
| if (self is SourceMemberBuilder) { |
| return self.isConflictingAugmentationMember; |
| } else if (self is SourceClassBuilder) { |
| return self.isConflictingAugmentationMember; |
| } |
| // TODO(johnniwinther): Handle all cases here. |
| return false; |
| } |
| |
| void set isConflictingAugmentationMember(bool value) { |
| Builder self = this; |
| if (self is SourceMemberBuilder) { |
| self.isConflictingAugmentationMember = value; |
| } else if (self is SourceClassBuilder) { |
| self.isConflictingAugmentationMember = value; |
| } |
| // TODO(johnniwinther): Handle all cases here. |
| } |
| |
| bool _hasPatchAnnotation(List<MetadataBuilder>? metadata) { |
| if (metadata == null) { |
| return false; |
| } |
| for (MetadataBuilder metadataBuilder in metadata) { |
| if (metadataBuilder.hasPatch) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool get hasPatchAnnotation { |
| Builder self = this; |
| if (self is SourceFunctionBuilder) { |
| return _hasPatchAnnotation(self.metadata); |
| } else if (self is SourceClassBuilder) { |
| return _hasPatchAnnotation(self.metadata); |
| } else if (self is SourceExtensionBuilder) { |
| return _hasPatchAnnotation(self.metadata); |
| } else if (self is SourceExtensionTypeDeclarationBuilder) { |
| // Coverage-ignore-block(suite): Not run. |
| return _hasPatchAnnotation(self.metadata); |
| } |
| return false; |
| } |
| } |
| |
| 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; |
| } |
| } |