| // 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.source_library_builder; |
| |
| import 'dart:collection'; |
| import 'dart:convert' show jsonEncode; |
| |
| import 'package:_fe_analyzer_shared/src/scanner/token.dart' show Token; |
| |
| import 'package:_fe_analyzer_shared/src/util/resolve_relative_uri.dart' |
| show resolveRelativeUri; |
| |
| import 'package:kernel/ast.dart' hide Combinator, MapLiteralEntry; |
| |
| import 'package:kernel/class_hierarchy.dart' show ClassHierarchy; |
| |
| import 'package:kernel/clone.dart' show CloneVisitorNotMembers; |
| |
| import 'package:kernel/reference_from_index.dart' |
| show IndexedClass, IndexedContainer, IndexedLibrary; |
| |
| import 'package:kernel/src/bounds_checks.dart' |
| show |
| TypeArgumentIssue, |
| findTypeArgumentIssues, |
| findTypeArgumentIssuesForInvocation, |
| getGenericTypeName; |
| |
| import 'package:kernel/type_algebra.dart' show Substitution, substitute; |
| |
| import 'package:kernel/type_environment.dart' |
| show SubtypeCheckMode, TypeEnvironment; |
| |
| import '../../api_prototype/experimental_flags.dart'; |
| import '../../base/nnbd_mode.dart'; |
| |
| import '../builder/builder.dart'; |
| import '../builder/builtin_type_declaration_builder.dart'; |
| import '../builder/class_builder.dart'; |
| import '../builder/constructor_builder.dart'; |
| import '../builder/constructor_reference_builder.dart'; |
| import '../builder/dynamic_type_declaration_builder.dart'; |
| import '../builder/enum_builder.dart'; |
| import '../builder/extension_builder.dart'; |
| import '../builder/factory_builder.dart'; |
| import '../builder/field_builder.dart'; |
| import '../builder/formal_parameter_builder.dart'; |
| import '../builder/function_builder.dart'; |
| import '../builder/function_type_builder.dart'; |
| import '../builder/invalid_type_declaration_builder.dart'; |
| import '../builder/library_builder.dart'; |
| import '../builder/member_builder.dart'; |
| import '../builder/metadata_builder.dart'; |
| import '../builder/mixin_application_builder.dart'; |
| import '../builder/name_iterator.dart'; |
| import '../builder/named_type_builder.dart'; |
| import '../builder/never_type_declaration_builder.dart'; |
| import '../builder/nullability_builder.dart'; |
| import '../builder/prefix_builder.dart'; |
| import '../builder/procedure_builder.dart'; |
| import '../builder/type_alias_builder.dart'; |
| import '../builder/type_builder.dart'; |
| import '../builder/type_declaration_builder.dart'; |
| import '../builder/type_variable_builder.dart'; |
| import '../builder/void_type_declaration_builder.dart'; |
| |
| import '../combinator.dart' show CombinatorBuilder; |
| |
| import '../configuration.dart' show Configuration; |
| |
| import '../dill/dill_library_builder.dart' show DillLibraryBuilder; |
| |
| import '../export.dart' show Export; |
| |
| import '../fasta_codes.dart'; |
| |
| import '../identifiers.dart' show QualifiedName, flattenName; |
| |
| import '../import.dart' show Import; |
| |
| import '../kernel/hierarchy/members_builder.dart'; |
| import '../kernel/constructor_tearoff_lowering.dart'; |
| import '../kernel/implicit_field_type.dart'; |
| import '../kernel/internal_ast.dart'; |
| import '../kernel/kernel_helper.dart'; |
| import '../kernel/load_library_builder.dart'; |
| import '../kernel/type_algorithms.dart' |
| show |
| NonSimplicityIssue, |
| calculateBounds, |
| computeTypeVariableBuilderVariance, |
| findUnaliasedGenericFunctionTypes, |
| getInboundReferenceIssuesInType, |
| getNonSimplicityIssuesForDeclaration, |
| getNonSimplicityIssuesForTypeVariables, |
| pendingVariance; |
| import '../kernel/utils.dart' show compareProcedures, toKernelCombinators; |
| |
| import '../modifier.dart' |
| show |
| abstractMask, |
| constMask, |
| externalMask, |
| finalMask, |
| declaresConstConstructorMask, |
| hasInitializerMask, |
| initializingFormalMask, |
| superInitializingFormalMask, |
| lateMask, |
| mixinDeclarationMask, |
| namedMixinApplicationMask, |
| staticMask; |
| |
| import '../names.dart' show indexSetName; |
| |
| import '../operator.dart'; |
| |
| import '../problems.dart' show unexpected, unhandled; |
| |
| import '../scope.dart'; |
| |
| import '../type_inference/type_inferrer.dart' show TypeInferrerImpl; |
| |
| import '../util/helpers.dart'; |
| import 'name_scheme.dart'; |
| import 'source_class_builder.dart' show SourceClassBuilder; |
| import 'source_extension_builder.dart'; |
| import 'source_loader.dart' show SourceLoader; |
| import 'source_type_alias_builder.dart'; |
| |
| class SourceLibraryBuilder extends LibraryBuilderImpl { |
| static const String MALFORMED_URI_SCHEME = "org-dartlang-malformed-uri"; |
| |
| @override |
| final SourceLoader loader; |
| |
| final TypeParameterScopeBuilder _libraryTypeParameterScopeBuilder; |
| |
| final List<ConstructorReferenceBuilder> constructorReferences = |
| <ConstructorReferenceBuilder>[]; |
| |
| final List<LibraryBuilder> parts = <LibraryBuilder>[]; |
| |
| // Can I use library.parts instead? See SourceLibraryBuilder.addPart. |
| final List<int> partOffsets = <int>[]; |
| |
| final List<Import> imports = <Import>[]; |
| |
| final List<Export> exports = <Export>[]; |
| |
| final Scope importScope; |
| |
| @override |
| final Uri fileUri; |
| |
| final Uri? _packageUri; |
| |
| Uri? get packageUriForTesting => _packageUri; |
| |
| final List<Object> accessors = <Object>[]; |
| |
| @override |
| String? name; |
| |
| String? partOfName; |
| |
| Uri? partOfUri; |
| |
| List<MetadataBuilder>? metadata; |
| |
| /// The current declaration that is being built. When we start parsing a |
| /// declaration (class, method, and so on), we don't have enough information |
| /// to create a builder and this object records its members and types until, |
| /// for example, [addClass] is called. |
| TypeParameterScopeBuilder currentTypeParameterScopeBuilder; |
| |
| /// Non-null if this library causes an error upon access, that is, there was |
| /// an error reading its source. |
| Message? accessProblem; |
| |
| @override |
| final Library library; |
| |
| final SourceLibraryBuilder? _origin; |
| |
| final List<SourceFunctionBuilder> nativeMethods = <SourceFunctionBuilder>[]; |
| |
| final List<TypeVariableBuilder> unboundTypeVariables = |
| <TypeVariableBuilder>[]; |
| |
| final List<UncheckedTypedefType> uncheckedTypedefTypes = |
| <UncheckedTypedefType>[]; |
| |
| // A list of alternating forwarders and the procedures they were generated |
| // for. Note that it may not include a forwarder-origin pair in cases when |
| // the former does not need to be updated after the body of the latter was |
| // built. |
| final List<Procedure> forwardersOrigins = <Procedure>[]; |
| |
| // List of types inferred in the outline. Errors in these should be reported |
| // differently than for specified types. |
| // TODO(dmitryas): Find a way to mark inferred types. |
| final Set<DartType> inferredTypes = new Set<DartType>.identity(); |
| |
| // While the bounds of type parameters aren't compiled yet, we can't tell the |
| // default nullability of the corresponding type-parameter types. This list |
| // is used to collect such type-parameter types in order to set the |
| // nullability after the bounds are built. |
| final List<PendingNullability> _pendingNullabilities = <PendingNullability>[]; |
| |
| // A library to use for Names generated when compiling code in this library. |
| // This allows code generated in one library to use the private namespace of |
| // another, for example during expression compilation (debugging). |
| Library get nameOrigin => _nameOrigin?.library ?? library; |
| @override |
| LibraryBuilder get nameOriginBuilder => _nameOrigin ?? this; |
| final LibraryBuilder? _nameOrigin; |
| |
| final Library? referencesFrom; |
| final IndexedLibrary? referencesFromIndexed; |
| IndexedClass? _currentClassReferencesFromIndexed; |
| |
| /// Exports that can't be serialized. |
| /// |
| /// The key is the name of the exported member. |
| /// |
| /// If the name is `dynamic` or `void`, this library reexports the |
| /// corresponding type from `dart:core`, and the value is null. |
| /// |
| /// Otherwise, this represents an error (an ambiguous export). In this case, |
| /// the error message is the corresponding value in the map. |
| Map<String, String?>? unserializableExports; |
| |
| List<FieldBuilder>? _implicitlyTypedFields; |
| |
| /// The language version of this library as defined by the language version |
| /// of the package it belongs to, if present, or the current language version |
| /// otherwise. |
| /// |
| /// This language version we be used as the language version for the library |
| /// if the library does not contain an explicit @dart= annotation. |
| final LanguageVersion packageLanguageVersion; |
| |
| /// The actual language version of this library. This is initially the |
| /// [packageLanguageVersion] but will be updated if the library contains |
| /// an explicit @dart= language version annotation. |
| LanguageVersion _languageVersion; |
| |
| bool postponedProblemsIssued = false; |
| List<PostponedProblem>? postponedProblems; |
| |
| /// List of [PrefixBuilder]s for imports with prefixes. |
| List<PrefixBuilder>? _prefixBuilders; |
| |
| /// Set of extension declarations in scope. This is computed lazily in |
| /// [forEachExtensionInScope]. |
| Set<ExtensionBuilder>? _extensionsInScope; |
| |
| List<SourceLibraryBuilder>? _patchLibraries; |
| |
| SourceLibraryBuilder.internal( |
| SourceLoader loader, |
| Uri fileUri, |
| Uri? packageUri, |
| LanguageVersion packageLanguageVersion, |
| Scope? scope, |
| SourceLibraryBuilder? origin, |
| Library library, |
| LibraryBuilder? nameOrigin, |
| Library? referencesFrom, |
| bool? referenceIsPartOwner) |
| : this.fromScopes( |
| loader, |
| fileUri, |
| packageUri, |
| packageLanguageVersion, |
| new TypeParameterScopeBuilder.library(), |
| scope ?? new Scope.top(), |
| origin, |
| library, |
| nameOrigin, |
| referencesFrom); |
| |
| SourceLibraryBuilder.fromScopes( |
| this.loader, |
| this.fileUri, |
| this._packageUri, |
| this.packageLanguageVersion, |
| this._libraryTypeParameterScopeBuilder, |
| this.importScope, |
| SourceLibraryBuilder? origin, |
| this.library, |
| this._nameOrigin, |
| this.referencesFrom) |
| : _languageVersion = packageLanguageVersion, |
| currentTypeParameterScopeBuilder = _libraryTypeParameterScopeBuilder, |
| referencesFromIndexed = |
| referencesFrom == null ? null : new IndexedLibrary(referencesFrom), |
| _origin = origin, |
| super(fileUri, _libraryTypeParameterScopeBuilder.toScope(importScope), |
| new Scope.top()) { |
| assert( |
| _packageUri == null || |
| importUri.scheme != 'package' || |
| importUri.path.startsWith(_packageUri!.path), |
| "Foreign package uri '$_packageUri' set on library with import uri " |
| "'${importUri}'."); |
| assert( |
| importUri.scheme != 'dart' || _packageUri == null, |
| "Package uri '$_packageUri' set on dart: library with import uri " |
| "'${importUri}'."); |
| } |
| |
| TypeParameterScopeBuilder get libraryTypeParameterScopeBuilderForTesting => |
| _libraryTypeParameterScopeBuilder; |
| |
| bool? _enableConstFunctionsInLibrary; |
| bool? _enableVarianceInLibrary; |
| bool? _enableNonfunctionTypeAliasesInLibrary; |
| bool? _enableNonNullableInLibrary; |
| Version? _enableNonNullableVersionInLibrary; |
| Version? _enableConstructorTearoffsVersionInLibrary; |
| Version? _enableExtensionTypesVersionInLibrary; |
| Version? _enableNamedArgumentsAnywhereVersionInLibrary; |
| Version? _enableSuperParametersVersionInLibrary; |
| Version? _enableEnhancedEnumsVersionInLibrary; |
| Version? _enableMacrosVersionInLibrary; |
| bool? _enableTripleShiftInLibrary; |
| bool? _enableExtensionMethodsInLibrary; |
| bool? _enableGenericMetadataInLibrary; |
| bool? _enableExtensionTypesInLibrary; |
| bool? _enableEnhancedEnumsInLibrary; |
| bool? _enableConstructorTearOffsInLibrary; |
| bool? _enableNamedArgumentsAnywhereInLibrary; |
| bool? _enableSuperParametersInLibrary; |
| bool? _enableMacrosInLibrary; |
| |
| bool get enableConstFunctionsInLibrary => _enableConstFunctionsInLibrary ??= |
| loader.target.isExperimentEnabledInLibraryByVersion( |
| ExperimentalFlag.constFunctions, |
| _packageUri ?? importUri, |
| languageVersion.version); |
| |
| bool get enableVarianceInLibrary => _enableVarianceInLibrary ??= loader.target |
| .isExperimentEnabledInLibraryByVersion(ExperimentalFlag.variance, |
| _packageUri ?? importUri, languageVersion.version); |
| |
| bool get enableNonfunctionTypeAliasesInLibrary => |
| _enableNonfunctionTypeAliasesInLibrary ??= loader.target |
| .isExperimentEnabledInLibraryByVersion( |
| ExperimentalFlag.nonfunctionTypeAliases, |
| _packageUri ?? importUri, |
| languageVersion.version); |
| |
| /// Returns `true` if the 'non-nullable' experiment is enabled for this |
| /// library. |
| /// |
| /// Note that the library might still opt out of the experiment by having |
| /// a version that is too low for opting in to the experiment. |
| bool get enableNonNullableInLibrary => _enableNonNullableInLibrary ??= |
| loader.target.isExperimentEnabledInLibrary( |
| ExperimentalFlag.nonNullable, _packageUri ?? importUri); |
| |
| Version get enableNonNullableVersionInLibrary => |
| _enableNonNullableVersionInLibrary ??= loader.target |
| .getExperimentEnabledVersionInLibrary( |
| ExperimentalFlag.nonNullable, _packageUri ?? importUri); |
| |
| bool get enableConstructorTearOffsInLibrary => |
| _enableConstructorTearOffsInLibrary ??= loader.target |
| .isExperimentEnabledInLibraryByVersion( |
| ExperimentalFlag.constructorTearoffs, |
| _packageUri ?? importUri, |
| languageVersion.version); |
| |
| Version get enableConstructorTearOffsVersionInLibrary => |
| _enableConstructorTearoffsVersionInLibrary ??= loader.target |
| .getExperimentEnabledVersionInLibrary( |
| ExperimentalFlag.constructorTearoffs, _packageUri ?? importUri); |
| |
| bool get enableTripleShiftInLibrary => _enableTripleShiftInLibrary ??= |
| loader.target.isExperimentEnabledInLibraryByVersion( |
| ExperimentalFlag.tripleShift, |
| _packageUri ?? importUri, |
| languageVersion.version); |
| |
| bool get enableExtensionMethodsInLibrary => |
| _enableExtensionMethodsInLibrary ??= loader.target |
| .isExperimentEnabledInLibraryByVersion( |
| ExperimentalFlag.extensionMethods, |
| _packageUri ?? importUri, |
| languageVersion.version); |
| |
| bool get enableGenericMetadataInLibrary => _enableGenericMetadataInLibrary ??= |
| loader.target.isExperimentEnabledInLibraryByVersion( |
| ExperimentalFlag.genericMetadata, |
| _packageUri ?? importUri, |
| languageVersion.version); |
| |
| bool get enableExtensionTypesInLibrary => _enableExtensionTypesInLibrary ??= |
| loader.target.isExperimentEnabledInLibraryByVersion( |
| ExperimentalFlag.extensionTypes, |
| _packageUri ?? importUri, |
| languageVersion.version); |
| |
| Version get enableExtensionTypesVersionInLibrary => |
| _enableExtensionTypesVersionInLibrary ??= loader.target |
| .getExperimentEnabledVersionInLibrary( |
| ExperimentalFlag.extensionTypes, _packageUri ?? importUri); |
| |
| bool get enableNamedArgumentsAnywhereInLibrary => |
| _enableNamedArgumentsAnywhereInLibrary ??= loader.target |
| .isExperimentEnabledInLibraryByVersion( |
| ExperimentalFlag.namedArgumentsAnywhere, |
| _packageUri ?? importUri, |
| languageVersion.version); |
| |
| Version get enableNamedArgumentsAnywhereVersionInLibrary => |
| _enableNamedArgumentsAnywhereVersionInLibrary ??= loader.target |
| .getExperimentEnabledVersionInLibrary( |
| ExperimentalFlag.namedArgumentsAnywhere, |
| _packageUri ?? importUri); |
| |
| bool get enableSuperParametersInLibrary => _enableSuperParametersInLibrary ??= |
| loader.target.isExperimentEnabledInLibraryByVersion( |
| ExperimentalFlag.superParameters, |
| _packageUri ?? importUri, |
| languageVersion.version); |
| |
| Version get enableSuperParametersVersionInLibrary => |
| _enableSuperParametersVersionInLibrary ??= loader.target |
| .getExperimentEnabledVersionInLibrary( |
| ExperimentalFlag.superParameters, _packageUri ?? importUri); |
| |
| bool get enableEnhancedEnumsInLibrary => _enableEnhancedEnumsInLibrary ??= |
| loader.target.isExperimentEnabledInLibraryByVersion( |
| ExperimentalFlag.enhancedEnums, |
| _packageUri ?? importUri, |
| languageVersion.version); |
| |
| Version get enableEnhancedEnumsVersionInLibrary => |
| _enableEnhancedEnumsVersionInLibrary ??= loader.target |
| .getExperimentEnabledVersionInLibrary( |
| ExperimentalFlag.enhancedEnums, _packageUri ?? importUri); |
| |
| bool get enableMacrosInLibrary => _enableMacrosInLibrary ??= loader.target |
| .isExperimentEnabledInLibraryByVersion(ExperimentalFlag.macros, |
| _packageUri ?? importUri, languageVersion.version); |
| |
| Version get enableMacrosVersionInLibrary => _enableMacrosVersionInLibrary ??= |
| loader.target.getExperimentEnabledVersionInLibrary( |
| ExperimentalFlag.macros, _packageUri ?? importUri); |
| |
| void _updateLibraryNNBDSettings() { |
| library.isNonNullableByDefault = isNonNullableByDefault; |
| switch (loader.nnbdMode) { |
| case NnbdMode.Weak: |
| library.nonNullableByDefaultCompiledMode = |
| NonNullableByDefaultCompiledMode.Weak; |
| break; |
| case NnbdMode.Strong: |
| library.nonNullableByDefaultCompiledMode = |
| NonNullableByDefaultCompiledMode.Strong; |
| break; |
| case NnbdMode.Agnostic: |
| library.nonNullableByDefaultCompiledMode = |
| NonNullableByDefaultCompiledMode.Agnostic; |
| break; |
| } |
| } |
| |
| SourceLibraryBuilder( |
| {required Uri importUri, |
| required Uri fileUri, |
| Uri? packageUri, |
| required LanguageVersion packageLanguageVersion, |
| required SourceLoader loader, |
| SourceLibraryBuilder? origin, |
| Scope? scope, |
| Library? target, |
| LibraryBuilder? nameOrigin, |
| Library? referencesFrom, |
| bool? referenceIsPartOwner}) |
| : this.internal( |
| loader, |
| fileUri, |
| packageUri, |
| packageLanguageVersion, |
| scope, |
| origin, |
| target ?? |
| (origin?.library ?? |
| new Library(importUri, |
| fileUri: fileUri, |
| reference: referenceIsPartOwner == true |
| ? null |
| : referencesFrom?.reference) |
| ..setLanguageVersion(packageLanguageVersion.version)), |
| nameOrigin, |
| referencesFrom, |
| referenceIsPartOwner); |
| |
| @override |
| bool get isPart => partOfName != null || partOfUri != null; |
| |
| // TODO(johnniwinther): Can avoid using this from outside this class? |
| Iterable<SourceLibraryBuilder>? get patchLibraries => _patchLibraries; |
| |
| void addPatchLibrary(SourceLibraryBuilder patchLibrary) { |
| assert(patchLibrary.isPatch, |
| "Library ${patchLibrary} must be a patch library."); |
| assert(!patchLibrary.isPart, |
| "Patch library ${patchLibrary} cannot be a part ."); |
| (_patchLibraries ??= []).add(patchLibrary); |
| } |
| |
| List<NamedTypeBuilder> get unresolvedNamedTypes => |
| _libraryTypeParameterScopeBuilder.unresolvedNamedTypes; |
| |
| @override |
| bool get isSynthetic => accessProblem != null; |
| |
| NamedTypeBuilder registerUnresolvedNamedType(NamedTypeBuilder type) { |
| currentTypeParameterScopeBuilder.registerUnresolvedNamedType(type); |
| return type; |
| } |
| |
| bool? _isNonNullableByDefault; |
| |
| @override |
| bool get isNonNullableByDefault { |
| assert( |
| _isNonNullableByDefault == null || |
| _isNonNullableByDefault == _computeIsNonNullableByDefault(), |
| "Unstable isNonNullableByDefault property, changed " |
| "from ${_isNonNullableByDefault} to " |
| "${_computeIsNonNullableByDefault()}"); |
| return _ensureIsNonNullableByDefault(); |
| } |
| |
| bool _ensureIsNonNullableByDefault() { |
| if (_isNonNullableByDefault == null) { |
| _isNonNullableByDefault = _computeIsNonNullableByDefault(); |
| _updateLibraryNNBDSettings(); |
| } |
| return _isNonNullableByDefault!; |
| } |
| |
| bool _computeIsNonNullableByDefault() => |
| enableNonNullableInLibrary && |
| languageVersion.version >= enableNonNullableVersionInLibrary; |
| |
| LanguageVersion get languageVersion { |
| assert( |
| _languageVersion.isFinal, |
| "Attempting to read the language version of ${this} before has been " |
| "finalized."); |
| return _languageVersion; |
| } |
| |
| void markLanguageVersionFinal() { |
| _languageVersion.isFinal = true; |
| _ensureIsNonNullableByDefault(); |
| if (!isNonNullableByDefault && |
| (loader.nnbdMode == NnbdMode.Strong || |
| loader.nnbdMode == NnbdMode.Agnostic)) { |
| // In strong and agnostic mode, the language version is not allowed to |
| // opt a library out of nnbd. |
| if (_languageVersion.isExplicit) { |
| addPostponedProblem(messageStrongModeNNBDButOptOut, |
| _languageVersion.charOffset, _languageVersion.charCount, fileUri); |
| } else { |
| loader.registerStrongOptOutLibrary(this); |
| } |
| library.nonNullableByDefaultCompiledMode = |
| NonNullableByDefaultCompiledMode.Invalid; |
| loader.hasInvalidNnbdModeLibrary = true; |
| } |
| } |
| |
| /// Set the language version to an explicit major and minor version. |
| /// |
| /// The default language version specified by the .packages file is passed |
| /// to the constructor, but the library can have source code that specifies |
| /// another one which should be supported. |
| /// |
| /// Only the first registered language version is used. |
| /// |
| /// [offset] and [length] refers to the offset and length of the source code |
| /// specifying the language version. |
| void registerExplicitLanguageVersion(Version version, |
| {int offset: 0, int length: noLength}) { |
| if (_languageVersion.isExplicit) { |
| // If more than once language version exists we use the first. |
| return; |
| } |
| assert(!_languageVersion.isFinal); |
| |
| if (version > loader.target.currentSdkVersion) { |
| // If trying to set a language version that is higher than the current sdk |
| // version it's an error. |
| addPostponedProblem( |
| templateLanguageVersionTooHigh.withArguments( |
| loader.target.currentSdkVersion.major, |
| loader.target.currentSdkVersion.minor), |
| offset, |
| length, |
| fileUri); |
| // If the package set an OK version, but the file set an invalid version |
| // we want to use the package version. |
| _languageVersion = new InvalidLanguageVersion( |
| fileUri, offset, length, packageLanguageVersion.version, true); |
| } else { |
| _languageVersion = new LanguageVersion(version, fileUri, offset, length); |
| } |
| library.setLanguageVersion(_languageVersion.version); |
| _languageVersion.isFinal = true; |
| } |
| |
| ConstructorReferenceBuilder addConstructorReference(Object name, |
| List<TypeBuilder>? typeArguments, String? suffix, int charOffset) { |
| ConstructorReferenceBuilder ref = new ConstructorReferenceBuilder( |
| name, typeArguments, suffix, this, charOffset); |
| constructorReferences.add(ref); |
| return ref; |
| } |
| |
| void beginNestedDeclaration(TypeParameterScopeKind kind, String name, |
| {bool hasMembers: true}) { |
| currentTypeParameterScopeBuilder = |
| currentTypeParameterScopeBuilder.createNested(kind, name, hasMembers); |
| } |
| |
| TypeParameterScopeBuilder endNestedDeclaration( |
| TypeParameterScopeKind kind, String? name) { |
| assert( |
| currentTypeParameterScopeBuilder.kind == kind, |
| "Unexpected declaration. " |
| "Trying to end a ${currentTypeParameterScopeBuilder.kind} as a $kind."); |
| assert( |
| (name?.startsWith(currentTypeParameterScopeBuilder.name) ?? |
| (name == currentTypeParameterScopeBuilder.name)) || |
| currentTypeParameterScopeBuilder.name == "operator" || |
| identical(name, "<syntax-error>"), |
| "${name} != ${currentTypeParameterScopeBuilder.name}"); |
| TypeParameterScopeBuilder previous = currentTypeParameterScopeBuilder; |
| currentTypeParameterScopeBuilder = currentTypeParameterScopeBuilder.parent!; |
| return previous; |
| } |
| |
| bool uriIsValid(Uri uri) => uri.scheme != MALFORMED_URI_SCHEME; |
| |
| Uri resolve(Uri baseUri, String? uri, int uriOffset, {isPart: false}) { |
| if (uri == null) { |
| addProblem(messageExpectedUri, uriOffset, noLength, fileUri); |
| return new Uri(scheme: MALFORMED_URI_SCHEME); |
| } |
| Uri parsedUri; |
| try { |
| parsedUri = Uri.parse(uri); |
| } on FormatException catch (e) { |
| // Point to position in string indicated by the exception, |
| // or to the initial quote if no position is given. |
| // (Assumes the directive is using a single-line string.) |
| addProblem(templateCouldNotParseUri.withArguments(uri, e.message), |
| uriOffset + 1 + (e.offset ?? -1), 1, fileUri); |
| return new Uri( |
| scheme: MALFORMED_URI_SCHEME, query: Uri.encodeQueryComponent(uri)); |
| } |
| if (isPart && baseUri.scheme == "dart") { |
| // Resolve using special rules for dart: URIs |
| return resolveRelativeUri(baseUri, parsedUri); |
| } else { |
| return baseUri.resolveUri(parsedUri); |
| } |
| } |
| |
| String? computeAndValidateConstructorName(Object? name, int charOffset, |
| {isFactory: false}) { |
| String className = currentTypeParameterScopeBuilder.name; |
| String prefix; |
| String? suffix; |
| if (name is QualifiedName) { |
| prefix = name.qualifier as String; |
| suffix = name.name; |
| } else { |
| prefix = name as String; |
| suffix = null; |
| } |
| if (enableConstructorTearOffsInLibrary) { |
| suffix = suffix == "new" ? "" : suffix; |
| } |
| if (prefix == className) { |
| return suffix ?? ""; |
| } |
| if (suffix == null && !isFactory) { |
| // A legal name for a regular method, but not for a constructor. |
| return null; |
| } |
| |
| addProblem( |
| messageConstructorWithWrongName, charOffset, prefix.length, fileUri, |
| context: [ |
| templateConstructorWithWrongNameContext |
| .withArguments(currentTypeParameterScopeBuilder.name) |
| .withLocation( |
| importUri, |
| currentTypeParameterScopeBuilder.charOffset, |
| currentTypeParameterScopeBuilder.name.length) |
| ]); |
| |
| return suffix; |
| } |
| |
| void addExport( |
| List<MetadataBuilder>? metadata, |
| String uri, |
| List<Configuration>? configurations, |
| List<CombinatorBuilder>? combinators, |
| int charOffset, |
| int uriOffset) { |
| if (configurations != null) { |
| for (Configuration config in configurations) { |
| if (lookupImportCondition(config.dottedName) == config.condition) { |
| uri = config.importUri; |
| break; |
| } |
| } |
| } |
| |
| LibraryBuilder exportedLibrary = loader.read( |
| resolve(this.importUri, uri, uriOffset), charOffset, |
| accessor: this); |
| exportedLibrary.addExporter(this, combinators, charOffset); |
| exports.add(new Export(this, exportedLibrary, combinators, charOffset)); |
| } |
| |
| String lookupImportCondition(String dottedName) { |
| const String prefix = "dart.library."; |
| if (!dottedName.startsWith(prefix)) return ""; |
| dottedName = dottedName.substring(prefix.length); |
| if (!loader.target.uriTranslator.isLibrarySupported(dottedName)) return ""; |
| |
| LibraryBuilder? imported = |
| loader.lookupLibraryBuilder(new Uri(scheme: "dart", path: dottedName)); |
| |
| if (imported == null) { |
| LibraryBuilder coreLibrary = loader.readAsEntryPoint(resolve( |
| this.importUri, |
| new Uri(scheme: "dart", path: "core").toString(), |
| -1)); |
| imported = coreLibrary.loader |
| .lookupLibraryBuilder(new Uri(scheme: 'dart', path: dottedName)); |
| } |
| return imported != null && !imported.isSynthetic ? "true" : ""; |
| } |
| |
| void addImport( |
| List<MetadataBuilder>? metadata, |
| String uri, |
| List<Configuration>? configurations, |
| String? prefix, |
| List<CombinatorBuilder>? combinators, |
| bool deferred, |
| int charOffset, |
| int prefixCharOffset, |
| int uriOffset, |
| int importIndex) { |
| if (configurations != null) { |
| for (Configuration config in configurations) { |
| if (lookupImportCondition(config.dottedName) == config.condition) { |
| uri = config.importUri; |
| break; |
| } |
| } |
| } |
| |
| LibraryBuilder? builder = null; |
| Uri? resolvedUri; |
| String? nativePath; |
| const String nativeExtensionScheme = "dart-ext:"; |
| if (uri.startsWith(nativeExtensionScheme)) { |
| addProblem(messageUnsupportedDartExt, charOffset, noLength, fileUri); |
| String strippedUri = uri.substring(nativeExtensionScheme.length); |
| if (strippedUri.startsWith("package")) { |
| resolvedUri = resolve(this.importUri, strippedUri, |
| uriOffset + nativeExtensionScheme.length); |
| resolvedUri = loader.target.translateUri(resolvedUri); |
| nativePath = resolvedUri.toString(); |
| } else { |
| resolvedUri = new Uri(scheme: "dart-ext", pathSegments: [uri]); |
| nativePath = uri; |
| } |
| } else { |
| resolvedUri = resolve(this.importUri, uri, uriOffset); |
| builder = loader.read(resolvedUri, uriOffset, accessor: this); |
| } |
| |
| imports.add(new Import(this, builder, deferred, prefix, combinators, |
| configurations, charOffset, prefixCharOffset, importIndex, |
| nativeImportPath: nativePath)); |
| } |
| |
| void addPart(List<MetadataBuilder>? metadata, String uri, int charOffset) { |
| Uri resolvedUri; |
| Uri newFileUri; |
| resolvedUri = resolve(this.importUri, uri, charOffset, isPart: true); |
| newFileUri = resolve(fileUri, uri, charOffset); |
| // TODO(johnniwinther): Add a LibraryPartBuilder instead of using |
| // [LibraryBuilder] to represent both libraries and parts. |
| parts.add(loader.read(resolvedUri, charOffset, |
| fileUri: newFileUri, accessor: this)); |
| partOffsets.add(charOffset); |
| |
| // TODO(ahe): [metadata] should be stored, evaluated, and added to [part]. |
| LibraryPart part = new LibraryPart(<Expression>[], uri) |
| ..fileOffset = charOffset; |
| library.addPart(part); |
| } |
| |
| void addPartOf(List<MetadataBuilder>? metadata, String? name, String? uri, |
| int uriOffset) { |
| partOfName = name; |
| if (uri != null) { |
| partOfUri = resolve(this.importUri, uri, uriOffset); |
| Uri newFileUri = resolve(fileUri, uri, uriOffset); |
| loader.read(partOfUri!, uriOffset, fileUri: newFileUri, accessor: this); |
| } |
| } |
| |
| void addFields(List<MetadataBuilder>? metadata, int modifiers, |
| bool isTopLevel, TypeBuilder? type, List<FieldInfo> fieldInfos) { |
| for (FieldInfo info in fieldInfos) { |
| bool isConst = modifiers & constMask != 0; |
| bool isFinal = modifiers & finalMask != 0; |
| bool potentiallyNeedInitializerInOutline = isConst || isFinal; |
| Token? startToken; |
| if (potentiallyNeedInitializerInOutline || type == null) { |
| startToken = info.initializerToken; |
| } |
| if (startToken != null) { |
| // Extract only the tokens for the initializer expression from the |
| // token stream. |
| Token endToken = info.beforeLast!; |
| endToken.setNext(new Token.eof(endToken.next!.offset)); |
| new Token.eof(startToken.previous!.offset).setNext(startToken); |
| } |
| bool hasInitializer = info.initializerToken != null; |
| addField(metadata, modifiers, isTopLevel, type, info.name, |
| info.charOffset, info.charEndOffset, startToken, hasInitializer, |
| constInitializerToken: |
| potentiallyNeedInitializerInOutline ? startToken : null); |
| } |
| } |
| |
| @override |
| Builder? addBuilder(String? name, Builder declaration, int charOffset, |
| {Reference? getterReference, Reference? setterReference}) { |
| // TODO(ahe): Set the parent correctly here. Could then change the |
| // implementation of MemberBuilder.isTopLevel to test explicitly for a |
| // LibraryBuilder. |
| if (name == null) { |
| unhandled("null", "name", charOffset, fileUri); |
| } |
| if (getterReference != null) { |
| loader.buildersCreatedWithReferences[getterReference] = declaration; |
| } |
| if (setterReference != null) { |
| loader.buildersCreatedWithReferences[setterReference] = declaration; |
| } |
| if (currentTypeParameterScopeBuilder == _libraryTypeParameterScopeBuilder) { |
| if (declaration is MemberBuilder) { |
| declaration.parent = this; |
| } else if (declaration is TypeDeclarationBuilder) { |
| declaration.parent = this; |
| } else if (declaration is PrefixBuilder) { |
| assert(declaration.parent == this); |
| } else { |
| return unhandled( |
| "${declaration.runtimeType}", "addBuilder", charOffset, fileUri); |
| } |
| } else { |
| assert(currentTypeParameterScopeBuilder.parent == |
| _libraryTypeParameterScopeBuilder); |
| } |
| bool isConstructor = declaration is FunctionBuilder && |
| (declaration.isConstructor || declaration.isFactory); |
| if (!isConstructor && name == currentTypeParameterScopeBuilder.name) { |
| addProblem( |
| messageMemberWithSameNameAsClass, charOffset, noLength, fileUri); |
| } |
| Map<String, Builder> members = isConstructor |
| ? currentTypeParameterScopeBuilder.constructors! |
| : (declaration.isSetter |
| ? currentTypeParameterScopeBuilder.setters! |
| : currentTypeParameterScopeBuilder.members!); |
| Builder? existing = members[name]; |
| |
| if (existing == declaration) return existing; |
| |
| if (declaration.next != null && declaration.next != existing) { |
| unexpected( |
| "${declaration.next!.fileUri}@${declaration.next!.charOffset}", |
| "${existing?.fileUri}@${existing?.charOffset}", |
| declaration.charOffset, |
| declaration.fileUri); |
| } |
| declaration.next = existing; |
| if (declaration is PrefixBuilder && existing is PrefixBuilder) { |
| assert(existing.next is! PrefixBuilder); |
| Builder? deferred; |
| Builder? other; |
| if (declaration.deferred) { |
| deferred = declaration; |
| other = existing; |
| } else if (existing.deferred) { |
| deferred = existing; |
| other = declaration; |
| } |
| if (deferred != null) { |
| addProblem(templateDeferredPrefixDuplicated.withArguments(name), |
| deferred.charOffset, noLength, fileUri, |
| context: [ |
| templateDeferredPrefixDuplicatedCause |
| .withArguments(name) |
| .withLocation(fileUri, other!.charOffset, noLength) |
| ]); |
| } |
| return existing |
| ..exportScope.merge(declaration.exportScope, |
| (String name, Builder existing, Builder member) { |
| return computeAmbiguousDeclaration( |
| name, existing, member, charOffset); |
| }); |
| } else if (isDuplicatedDeclaration(existing, declaration)) { |
| String fullName = name; |
| if (isConstructor) { |
| if (name.isEmpty) { |
| fullName = currentTypeParameterScopeBuilder.name; |
| } else { |
| fullName = "${currentTypeParameterScopeBuilder.name}.$name"; |
| } |
| } |
| addProblem(templateDuplicatedDeclaration.withArguments(fullName), |
| charOffset, fullName.length, declaration.fileUri!, |
| context: <LocatedMessage>[ |
| templateDuplicatedDeclarationCause |
| .withArguments(fullName) |
| .withLocation( |
| existing!.fileUri!, existing.charOffset, fullName.length) |
| ]); |
| } else if (declaration.isExtension) { |
| // We add the extension declaration to the extension scope only if its |
| // name is unique. Only the first of duplicate extensions is accessible |
| // by name or by resolution and the remaining are dropped for the output. |
| currentTypeParameterScopeBuilder.extensions! |
| .add(declaration as SourceExtensionBuilder); |
| } |
| if (declaration is PrefixBuilder) { |
| _prefixBuilders ??= <PrefixBuilder>[]; |
| _prefixBuilders!.add(declaration); |
| } |
| return members[name] = declaration; |
| } |
| |
| bool isDuplicatedDeclaration(Builder? existing, Builder other) { |
| if (existing == null) return false; |
| Builder? next = existing.next; |
| if (next == null) { |
| if (existing.isGetter && other.isSetter) return false; |
| if (existing.isSetter && other.isGetter) return false; |
| } else { |
| if (next is ClassBuilder && !next.isMixinApplication) return true; |
| } |
| if (existing is ClassBuilder && other is ClassBuilder) { |
| // We allow multiple mixin applications with the same name. An |
| // alternative is to share these mixin applications. This situation can |
| // happen if you have `class A extends Object with Mixin {}` and `class B |
| // extends Object with Mixin {}` in the same library. |
| return !existing.isMixinApplication || !other.isMixinApplication; |
| } |
| return true; |
| } |
| |
| /// Checks [scope] for conflicts between setters and non-setters and reports |
| /// them in [sourceLibraryBuilder]. |
| /// |
| /// If [checkForInstanceVsStaticConflict] is `true`, conflicts between |
| /// instance and static members of the same name are reported. |
| /// |
| /// If [checkForMethodVsSetterConflict] is `true`, conflicts between |
| /// methods and setters of the same name are reported. |
| static void checkMemberConflicts( |
| SourceLibraryBuilder sourceLibraryBuilder, Scope scope, |
| {required bool checkForInstanceVsStaticConflict, |
| required bool checkForMethodVsSetterConflict}) { |
| // ignore: unnecessary_null_comparison |
| assert(checkForInstanceVsStaticConflict != null); |
| // ignore: unnecessary_null_comparison |
| assert(checkForMethodVsSetterConflict != null); |
| |
| scope.forEachLocalSetter((String name, MemberBuilder setter) { |
| Builder? getable = scope.lookupLocalMember(name, setter: false); |
| if (getable == null) { |
| // Setter without getter. |
| return; |
| } |
| |
| bool isConflictingSetter = false; |
| Set<Builder> conflictingGetables = {}; |
| for (Builder? currentGetable = getable; |
| currentGetable != null; |
| currentGetable = currentGetable.next) { |
| if (currentGetable is FieldBuilder) { |
| if (currentGetable.isAssignable) { |
| // Setter with writable field. |
| isConflictingSetter = true; |
| conflictingGetables.add(currentGetable); |
| } |
| } else if (checkForMethodVsSetterConflict && !currentGetable.isGetter) { |
| // Setter with method. |
| conflictingGetables.add(currentGetable); |
| } |
| } |
| for (SourceMemberBuilderImpl? currentSetter = |
| setter as SourceMemberBuilderImpl?; |
| currentSetter != null; |
| currentSetter = currentSetter.next as SourceMemberBuilderImpl?) { |
| bool conflict = conflictingGetables.isNotEmpty; |
| for (Builder? currentGetable = getable; |
| currentGetable != null; |
| currentGetable = currentGetable.next) { |
| if (checkForInstanceVsStaticConflict && |
| currentGetable.isDeclarationInstanceMember != |
| currentSetter.isDeclarationInstanceMember) { |
| conflict = true; |
| conflictingGetables.add(currentGetable); |
| } |
| } |
| if (isConflictingSetter) { |
| currentSetter.isConflictingSetter = true; |
| } |
| if (conflict) { |
| if (currentSetter.isConflictingSetter) { |
| sourceLibraryBuilder.addProblem( |
| templateConflictsWithImplicitSetter.withArguments(name), |
| currentSetter.charOffset, |
| noLength, |
| currentSetter.fileUri); |
| } else { |
| sourceLibraryBuilder.addProblem( |
| templateConflictsWithMember.withArguments(name), |
| currentSetter.charOffset, |
| noLength, |
| currentSetter.fileUri); |
| } |
| } |
| } |
| for (Builder conflictingGetable in conflictingGetables) { |
| // TODO(ahe): Context argument to previous message? |
| sourceLibraryBuilder.addProblem( |
| templateConflictsWithSetter.withArguments(name), |
| conflictingGetable.charOffset, |
| noLength, |
| conflictingGetable.fileUri!); |
| } |
| }); |
| } |
| |
| /// Builds the core AST structure of this library as needed for the outline. |
| Library build(LibraryBuilder coreLibrary, {bool modifyTarget: true}) { |
| // TODO(johnniwinther): Avoid the need to process patch libraries before |
| // the origin. Currently, settings performed by the patch are overridden |
| // by the origin. For instance, the `Map` class is abstract in the origin |
| // but (unintentionally) concrete in the patch. By processing the origin |
| // last the `isAbstract` property set by the patch is corrected by the |
| // origin. |
| Iterable<SourceLibraryBuilder>? patches = this.patchLibraries; |
| if (patches != null) { |
| for (SourceLibraryBuilder patchLibrary in patches) { |
| patchLibrary.build(coreLibrary, modifyTarget: modifyTarget); |
| } |
| } |
| |
| checkMemberConflicts(this, scope, |
| checkForInstanceVsStaticConflict: false, |
| checkForMethodVsSetterConflict: true); |
| |
| Iterator<Builder> iterator = this.iterator; |
| while (iterator.moveNext()) { |
| buildBuilder(iterator.current, coreLibrary); |
| } |
| |
| if (!modifyTarget) return library; |
| |
| library.isSynthetic = isSynthetic; |
| addDependencies(library, new Set<SourceLibraryBuilder>()); |
| |
| library.name = name; |
| library.procedures.sort(compareProcedures); |
| |
| if (unserializableExports != null) { |
| Name fieldName = new Name("_exports#", library); |
| Reference? fieldReference = |
| referencesFromIndexed?.lookupFieldReference(fieldName); |
| Reference? getterReference = |
| referencesFromIndexed?.lookupGetterReference(fieldName); |
| library.addField(new Field.immutable(fieldName, |
| initializer: new StringLiteral(jsonEncode(unserializableExports)), |
| isStatic: true, |
| isConst: true, |
| fieldReference: fieldReference, |
| getterReference: getterReference, |
| fileUri: library.fileUri)); |
| } |
| |
| return library; |
| } |
| |
| void validatePart(SourceLibraryBuilder? library, Set<Uri>? usedParts) { |
| if (library != null && parts.isNotEmpty) { |
| // If [library] is null, we have already reported a problem that this |
| // part is orphaned. |
| List<LocatedMessage> context = <LocatedMessage>[ |
| messagePartInPartLibraryContext.withLocation(library.fileUri, -1, 1), |
| ]; |
| for (int offset in partOffsets) { |
| addProblem(messagePartInPart, offset, noLength, fileUri, |
| context: context); |
| } |
| for (LibraryBuilder part in parts) { |
| // Mark this part as used so we don't report it as orphaned. |
| usedParts!.add(part.importUri); |
| } |
| } |
| parts.clear(); |
| if (exporters.isNotEmpty) { |
| List<LocatedMessage> context = <LocatedMessage>[ |
| messagePartExportContext.withLocation(fileUri, -1, 1), |
| ]; |
| for (Export export in exporters) { |
| export.exporter.addProblem( |
| messagePartExport, export.charOffset, "export".length, null, |
| context: context); |
| if (library != null) { |
| // Recovery: Export the main library instead. |
| export.exported = library; |
| SourceLibraryBuilder exporter = |
| export.exporter as SourceLibraryBuilder; |
| for (Export export2 in exporter.exports) { |
| if (export2.exported == this) { |
| export2.exported = library; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| void includeParts(Set<Uri> usedParts) { |
| Set<Uri> seenParts = new Set<Uri>(); |
| for (int i = 0; i < parts.length; i++) { |
| LibraryBuilder part = parts[i]; |
| int partOffset = partOffsets[i]; |
| if (part == this) { |
| addProblem(messagePartOfSelf, -1, noLength, fileUri); |
| } else if (seenParts.add(part.fileUri)) { |
| if (part.partOfLibrary != null) { |
| addProblem(messagePartOfTwoLibraries, -1, noLength, part.fileUri, |
| context: [ |
| messagePartOfTwoLibrariesContext.withLocation( |
| part.partOfLibrary!.fileUri, -1, noLength), |
| messagePartOfTwoLibrariesContext.withLocation( |
| this.fileUri, -1, noLength) |
| ]); |
| } else { |
| if (isPatch) { |
| usedParts.add(part.fileUri); |
| } else { |
| usedParts.add(part.importUri); |
| } |
| includePart(part, usedParts, partOffset); |
| } |
| } else { |
| addProblem(templatePartTwice.withArguments(part.fileUri), -1, noLength, |
| fileUri); |
| } |
| } |
| } |
| |
| bool includePart(LibraryBuilder part, Set<Uri> usedParts, int partOffset) { |
| if (part is SourceLibraryBuilder) { |
| if (part.partOfUri != null) { |
| if (uriIsValid(part.partOfUri!) && part.partOfUri != importUri) { |
| // This is an error, but the part is not removed from the list of |
| // parts, so that metadata annotations can be associated with it. |
| addProblem( |
| templatePartOfUriMismatch.withArguments( |
| part.fileUri, importUri, part.partOfUri!), |
| partOffset, |
| noLength, |
| fileUri); |
| return false; |
| } |
| } else if (part.partOfName != null) { |
| if (name != null) { |
| if (part.partOfName != name) { |
| // This is an error, but the part is not removed from the list of |
| // parts, so that metadata annotations can be associated with it. |
| addProblem( |
| templatePartOfLibraryNameMismatch.withArguments( |
| part.fileUri, name!, part.partOfName!), |
| partOffset, |
| noLength, |
| fileUri); |
| return false; |
| } |
| } else { |
| // This is an error, but the part is not removed from the list of |
| // parts, so that metadata annotations can be associated with it. |
| addProblem( |
| templatePartOfUseUri.withArguments( |
| part.fileUri, fileUri, part.partOfName!), |
| partOffset, |
| noLength, |
| fileUri); |
| return false; |
| } |
| } else { |
| // This is an error, but the part is not removed from the list of parts, |
| // so that metadata annotations can be associated with it. |
| assert(!part.isPart); |
| if (uriIsValid(part.fileUri)) { |
| addProblem(templateMissingPartOf.withArguments(part.fileUri), |
| partOffset, noLength, fileUri); |
| } |
| return false; |
| } |
| |
| // Language versions have to match. Except if (at least) one of them is |
| // invalid in which case we've already gotten an error about this. |
| if (languageVersion != part.languageVersion && |
| languageVersion.valid && |
| part.languageVersion.valid) { |
| // This is an error, but the part is not removed from the list of |
| // parts, so that metadata annotations can be associated with it. |
| List<LocatedMessage> context = <LocatedMessage>[]; |
| if (languageVersion.isExplicit) { |
| context.add(messageLanguageVersionLibraryContext.withLocation( |
| languageVersion.fileUri!, |
| languageVersion.charOffset, |
| languageVersion.charCount)); |
| } |
| if (part.languageVersion.isExplicit) { |
| context.add(messageLanguageVersionPartContext.withLocation( |
| part.languageVersion.fileUri!, |
| part.languageVersion.charOffset, |
| part.languageVersion.charCount)); |
| } |
| addProblem( |
| messageLanguageVersionMismatchInPart, partOffset, noLength, fileUri, |
| context: context); |
| } |
| |
| part.validatePart(this, usedParts); |
| NameIterator partDeclarations = part.nameIterator; |
| while (partDeclarations.moveNext()) { |
| String name = partDeclarations.name; |
| Builder declaration = partDeclarations.current; |
| |
| if (declaration.next != null) { |
| List<Builder> duplicated = <Builder>[]; |
| while (declaration.next != null) { |
| duplicated.add(declaration); |
| partDeclarations.moveNext(); |
| declaration = partDeclarations.current; |
| } |
| duplicated.add(declaration); |
| // Handle duplicated declarations in the part. |
| // |
| // Duplicated declarations are handled by creating a linked list using |
| // the `next` field. This is preferred over making all scope entries |
| // be a `List<Declaration>`. |
| // |
| // We maintain the linked list so that the last entry is easy to |
| // recognize (it's `next` field is null). This means that it is |
| // reversed with respect to source code order. Since kernel doesn't |
| // allow duplicated declarations, we ensure that we only add the first |
| // declaration to the kernel tree. |
| // |
| // Since the duplicated declarations are stored in reverse order, we |
| // iterate over them in reverse order as this is simpler and normally |
| // not a problem. However, in this case we need to call [addBuilder] |
| // in source order as it would otherwise create cycles. |
| // |
| // We also need to be careful preserving the order of the links. The |
| // part library still keeps these declarations in its scope so that |
| // DietListener can find them. |
| for (int i = duplicated.length; i > 0; i--) { |
| Builder declaration = duplicated[i - 1]; |
| // No reference: There should be no duplicates when using |
| // references. |
| addBuilder(name, declaration, declaration.charOffset); |
| } |
| } else { |
| // No reference: The part is in the same loader so the reference |
| // - if needed - was already added. |
| addBuilder(name, declaration, declaration.charOffset); |
| } |
| } |
| unresolvedNamedTypes.addAll(part.unresolvedNamedTypes); |
| constructorReferences.addAll(part.constructorReferences); |
| part.partOfLibrary = this; |
| part.scope.becomePartOf(scope); |
| // TODO(ahe): Include metadata from part? |
| |
| // Recovery: Take on all exporters (i.e. if a library has erroneously |
| // exported the part it has (in validatePart) been recovered to import the |
| // main library (this) instead --- to make it complete (and set up scopes |
| // correctly) the exporters in this has to be updated too). |
| exporters.addAll(part.exporters); |
| |
| nativeMethods.addAll(part.nativeMethods); |
| unboundTypeVariables.addAll(part.unboundTypeVariables); |
| // Check that the targets are different. This is not normally a problem |
| // but is for patch files. |
| if (library != part.library && part.library.problemsAsJson != null) { |
| (library.problemsAsJson ??= <String>[]) |
| .addAll(part.library.problemsAsJson!); |
| } |
| List<FieldBuilder> partImplicitlyTypedFields = []; |
| part.collectImplicitlyTypedFields(partImplicitlyTypedFields); |
| if (partImplicitlyTypedFields.isNotEmpty) { |
| if (_implicitlyTypedFields == null) { |
| _implicitlyTypedFields = partImplicitlyTypedFields; |
| } else { |
| _implicitlyTypedFields!.addAll(partImplicitlyTypedFields); |
| } |
| } |
| if (library != part.library) { |
| // Mark the part library as synthetic as it's not an actual library |
| // (anymore). |
| part.library.isSynthetic = true; |
| } |
| return true; |
| } else { |
| assert(part is DillLibraryBuilder); |
| // Trying to add a dill library builder as a part means that it exists |
| // as a stand-alone library in the dill file. |
| // This means, that it's not a part (if it had been it would be been |
| // "merged in" to the real library and thus not been a library on its own) |
| // so we behave like if it's a library with a missing "part of" |
| // declaration (i.e. as it was a SourceLibraryBuilder without a "part of" |
| // declaration). |
| if (uriIsValid(part.fileUri)) { |
| addProblem(templateMissingPartOf.withArguments(part.fileUri), |
| partOffset, noLength, fileUri); |
| } |
| return false; |
| } |
| } |
| |
| void buildInitialScopes() { |
| Iterable<SourceLibraryBuilder>? patches = this.patchLibraries; |
| if (patches != null) { |
| for (SourceLibraryBuilder patchLibrary in patches) { |
| patchLibrary.buildInitialScopes(); |
| } |
| } |
| |
| NameIterator iterator = nameIterator; |
| while (iterator.moveNext()) { |
| addToExportScope(iterator.name, iterator.current); |
| } |
| } |
| |
| void addImportsToScope() { |
| Iterable<SourceLibraryBuilder>? patches = this.patchLibraries; |
| if (patches != null) { |
| for (SourceLibraryBuilder patchLibrary in patches) { |
| patchLibrary.addImportsToScope(); |
| } |
| } |
| |
| bool explicitCoreImport = this == loader.coreLibrary; |
| for (Import import in imports) { |
| if (import.imported?.isPart ?? false) { |
| addProblem( |
| templatePartOfInLibrary.withArguments(import.imported!.fileUri), |
| import.charOffset, |
| noLength, |
| fileUri); |
| if (import.imported?.partOfLibrary != null) { |
| // Recovery: Rewrite to import the "part owner" library. |
| // Note that the part will not have a partOfLibrary if it claims to be |
| // a part, but isn't mentioned as a part by the (would-be) "parent". |
| import.imported = import.imported?.partOfLibrary; |
| } |
| } |
| if (import.imported == loader.coreLibrary) { |
| explicitCoreImport = true; |
| } |
| import.finalizeImports(this); |
| } |
| if (!explicitCoreImport) { |
| loader.coreLibrary.exportScope.forEach((String name, Builder member) { |
| addToScope(name, member, -1, true); |
| }); |
| } |
| |
| exportScope.forEach((String name, Builder member) { |
| if (member.parent != this) { |
| switch (name) { |
| case "dynamic": |
| case "void": |
| case "Never": |
| unserializableExports ??= <String, String?>{}; |
| unserializableExports![name] = null; |
| break; |
| |
| default: |
| if (member is InvalidTypeDeclarationBuilder) { |
| unserializableExports ??= <String, String>{}; |
| unserializableExports![name] = member.message.problemMessage; |
| } else { |
| // Eventually (in #buildBuilder) members aren't added to the |
| // library if the have 'next' pointers, so don't add them as |
| // additionalExports either. Add the last one only (the one that |
| // will eventually be added to the library). |
| Builder memberLast = member; |
| while (memberLast.next != null) { |
| memberLast = memberLast.next!; |
| } |
| if (memberLast is ClassBuilder) { |
| library.additionalExports.add(memberLast.cls.reference); |
| } else if (memberLast is TypeAliasBuilder) { |
| library.additionalExports.add(memberLast.typedef.reference); |
| } else if (memberLast is ExtensionBuilder) { |
| library.additionalExports.add(memberLast.extension.reference); |
| } else if (memberLast is MemberBuilder) { |
| for (Member member in memberLast.exportedMembers) { |
| if (member is Field) { |
| // For fields add both getter and setter references |
| // so replacing a field with a getter/setter pair still |
| // exports correctly. |
| library.additionalExports.add(member.getterReference); |
| if (member.hasSetter) { |
| library.additionalExports.add(member.setterReference!); |
| } |
| } else { |
| library.additionalExports.add(member.reference); |
| } |
| } |
| } else { |
| unhandled('member', 'exportScope', memberLast.charOffset, |
| memberLast.fileUri); |
| } |
| } |
| } |
| } |
| }); |
| } |
| |
| @override |
| void addToScope(String name, Builder member, int charOffset, bool isImport) { |
| Builder? existing = |
| importScope.lookupLocalMember(name, setter: member.isSetter); |
| if (existing != null) { |
| if (existing != member) { |
| importScope.addLocalMember( |
| name, |
| computeAmbiguousDeclaration(name, existing, member, charOffset, |
| isImport: isImport), |
| setter: member.isSetter); |
| } |
| } else { |
| importScope.addLocalMember(name, member, setter: member.isSetter); |
| } |
| if (member.isExtension) { |
| importScope.addExtension(member as ExtensionBuilder); |
| } |
| } |
| |
| /// Resolves all unresolved types in [unresolvedNamedTypes]. The list of types |
| /// is cleared when done. |
| int resolveTypes() { |
| int typeCount = 0; |
| |
| Iterable<SourceLibraryBuilder>? patches = this.patchLibraries; |
| if (patches != null) { |
| for (SourceLibraryBuilder patchLibrary in patches) { |
| typeCount += patchLibrary.resolveTypes(); |
| } |
| } |
| |
| typeCount += unresolvedNamedTypes.length; |
| for (NamedTypeBuilder namedType in unresolvedNamedTypes) { |
| namedType.resolveIn( |
| scope, namedType.charOffset!, namedType.fileUri!, this); |
| namedType.check(this, namedType.charOffset!, namedType.fileUri!); |
| } |
| unresolvedNamedTypes.clear(); |
| return typeCount; |
| } |
| |
| void installDefaultSupertypes( |
| ClassBuilder objectClassBuilder, Class objectClass) { |
| Iterable<SourceLibraryBuilder>? patches = this.patchLibraries; |
| if (patches != null) { |
| for (SourceLibraryBuilder patchLibrary in patches) { |
| patchLibrary.installDefaultSupertypes(objectClassBuilder, objectClass); |
| } |
| } |
| |
| Iterator<Builder> iterator = this.iterator; |
| while (iterator.moveNext()) { |
| Builder declaration = iterator.current; |
| if (declaration is SourceClassBuilder) { |
| Class cls = declaration.cls; |
| if (cls != objectClass) { |
| cls.supertype ??= objectClass.asRawSupertype; |
| declaration.supertypeBuilder ??= new NamedTypeBuilder( |
| "Object", |
| const NullabilityBuilder.omitted(), |
| /* arguments = */ null, |
| /* fileUri = */ null, |
| /* charOffset = */ null, |
| instanceTypeVariableAccess: |
| InstanceTypeVariableAccessState.Unexpected) |
| ..bind(objectClassBuilder); |
| } |
| if (declaration.isMixinApplication) { |
| cls.mixedInType = declaration.mixedInTypeBuilder!.buildMixedInType( |
| this, declaration.charOffset, declaration.fileUri); |
| } |
| } |
| } |
| } |
| |
| void collectSourceClasses(List<SourceClassBuilder> sourceClasses) { |
| Iterable<SourceLibraryBuilder>? patches = this.patchLibraries; |
| if (patches != null) { |
| for (SourceLibraryBuilder patchLibrary in patches) { |
| patchLibrary.collectSourceClasses(sourceClasses); |
| } |
| } |
| |
| Iterator<Builder> iterator = this.iterator; |
| while (iterator.moveNext()) { |
| Builder member = iterator.current; |
| if (member is SourceClassBuilder && !member.isPatch) { |
| sourceClasses.add(member); |
| } |
| } |
| } |
| |
| /// Resolve constructors (lookup names in scope) recorded in this builder and |
| /// return the number of constructors resolved. |
| int resolveConstructors() { |
| int count = 0; |
| |
| Iterable<SourceLibraryBuilder>? patches = this.patchLibraries; |
| if (patches != null) { |
| for (SourceLibraryBuilder patchLibrary in patches) { |
| count += patchLibrary.resolveConstructors(); |
| } |
| } |
| |
| Iterator<Builder> iterator = this.iterator; |
| while (iterator.moveNext()) { |
| Builder builder = iterator.current; |
| if (builder is SourceClassBuilder) { |
| count += builder.resolveConstructors(this); |
| } |
| } |
| return count; |
| } |
| |
| @override |
| String get fullNameForErrors { |
| // TODO(ahe): Consider if we should use relativizeUri here. The downside to |
| // doing that is that this URI may be used in an error message. Ideally, we |
| // should create a class that represents qualified names that we can |
| // relativize when printing a message, but still store the full URI in |
| // .dill files. |
| return name ?? "<library '$fileUri'>"; |
| } |
| |
| @override |
| void recordAccess(int charOffset, int length, Uri fileUri) { |
| accessors.add(fileUri); |
| accessors.add(charOffset); |
| accessors.add(length); |
| if (accessProblem != null) { |
| addProblem(accessProblem!, charOffset, length, fileUri); |
| } |
| } |
| |
| void addProblemAtAccessors(Message message) { |
| if (accessProblem == null) { |
| if (accessors.isEmpty && this == loader.first) { |
| // This is the entry point library, and nobody access it directly. So |
| // we need to report a problem. |
| loader.addProblem(message, -1, 1, null); |
| } |
| for (int i = 0; i < accessors.length; i += 3) { |
| Uri accessor = accessors[i] as Uri; |
| int charOffset = accessors[i + 1] as int; |
| int length = accessors[i + 2] as int; |
| addProblem(message, charOffset, length, accessor); |
| } |
| accessProblem = message; |
| } |
| } |
| |
| @override |
| SourceLibraryBuilder get origin => _origin ?? this; |
| |
| @override |
| Uri get importUri => library.importUri; |
| |
| @override |
| void addSyntheticDeclarationOfDynamic() { |
| addBuilder("dynamic", |
| new DynamicTypeDeclarationBuilder(const DynamicType(), this, -1), -1); |
| } |
| |
| @override |
| void addSyntheticDeclarationOfNever() { |
| addBuilder( |
| "Never", |
| new NeverTypeDeclarationBuilder( |
| const NeverType.nonNullable(), this, -1), |
| -1); |
| } |
| |
| @override |
| void addSyntheticDeclarationOfNull() { |
| // TODO(dmitryas): Uncomment the following when the Null class is removed |
| // from the SDK. |
| //addBuilder("Null", new NullTypeBuilder(const NullType(), this, -1), -1); |
| } |
| |
| TypeBuilder addNamedType(Object name, NullabilityBuilder nullabilityBuilder, |
| List<TypeBuilder>? arguments, int charOffset, |
| {required InstanceTypeVariableAccessState instanceTypeVariableAccess}) { |
| return registerUnresolvedNamedType(new NamedTypeBuilder( |
| name, nullabilityBuilder, arguments, fileUri, charOffset, |
| instanceTypeVariableAccess: instanceTypeVariableAccess)); |
| } |
| |
| TypeBuilder addMixinApplication( |
| TypeBuilder? supertype, List<TypeBuilder> mixins, int charOffset) { |
| return new MixinApplicationBuilder(supertype, mixins, fileUri, charOffset); |
| } |
| |
| TypeBuilder addVoidType(int charOffset) { |
| // 'void' is always nullable. |
| return addNamedType( |
| "void", const NullabilityBuilder.nullable(), null, charOffset, |
| instanceTypeVariableAccess: InstanceTypeVariableAccessState.Unexpected) |
| ..bind( |
| new VoidTypeDeclarationBuilder(const VoidType(), this, charOffset)); |
| } |
| |
| void _checkBadFunctionParameter(List<TypeVariableBuilder>? typeVariables) { |
| if (typeVariables == null || typeVariables.isEmpty) { |
| return; |
| } |
| |
| for (TypeVariableBuilder type in typeVariables) { |
| if (type.name == "Function") { |
| addProblem(messageFunctionAsTypeParameter, type.charOffset, |
| type.name.length, type.fileUri!); |
| } |
| } |
| } |
| |
| void _checkBadFunctionDeclUse( |
| String className, TypeParameterScopeKind kind, int charOffset) { |
| String? decType; |
| switch (kind) { |
| case TypeParameterScopeKind.classDeclaration: |
| decType = "class"; |
| break; |
| case TypeParameterScopeKind.mixinDeclaration: |
| decType = "mixin"; |
| break; |
| case TypeParameterScopeKind.extensionDeclaration: |
| decType = "extension"; |
| break; |
| default: |
| break; |
| } |
| if (className != "Function") { |
| return; |
| } |
| if (decType == "class" && importUri.scheme == "dart") { |
| // Allow declaration of class Function in the sdk. |
| return; |
| } |
| if (decType != null) { |
| addProblem(templateFunctionUsedAsDec.withArguments(decType), charOffset, |
| className.length, fileUri); |
| } |
| } |
| |
| /// Add a problem that might not be reported immediately. |
| /// |
| /// Problems will be issued after source information has been added. |
| /// Once the problems has been issued, adding a new "postponed" problem will |
| /// be issued immediately. |
| void addPostponedProblem( |
| Message message, int charOffset, int length, Uri fileUri) { |
| if (postponedProblemsIssued) { |
| addProblem(message, charOffset, length, fileUri); |
| } else { |
| postponedProblems ??= <PostponedProblem>[]; |
| postponedProblems! |
| .add(new PostponedProblem(message, charOffset, length, fileUri)); |
| } |
| } |
| |
| void issuePostponedProblems() { |
| postponedProblemsIssued = true; |
| if (postponedProblems == null) return; |
| for (int i = 0; i < postponedProblems!.length; ++i) { |
| PostponedProblem postponedProblem = postponedProblems![i]; |
| addProblem(postponedProblem.message, postponedProblem.charOffset, |
| postponedProblem.length, postponedProblem.fileUri); |
| } |
| postponedProblems = null; |
| } |
| |
| @override |
| FormattedMessage? addProblem( |
| Message message, int charOffset, int length, Uri? fileUri, |
| {bool wasHandled: false, |
| List<LocatedMessage>? context, |
| Severity? severity, |
| bool problemOnLibrary: false}) { |
| FormattedMessage? formattedMessage = super.addProblem( |
| message, charOffset, length, fileUri, |
| wasHandled: wasHandled, |
| context: context, |
| severity: severity, |
| problemOnLibrary: true); |
| if (formattedMessage != null) { |
| library.problemsAsJson ??= <String>[]; |
| library.problemsAsJson!.add(formattedMessage.toJsonString()); |
| } |
| return formattedMessage; |
| } |
| |
| void addClass( |
| List<MetadataBuilder>? metadata, |
| int modifiers, |
| String className, |
| List<TypeVariableBuilder>? typeVariables, |
| TypeBuilder? supertype, |
| List<TypeBuilder>? interfaces, |
| int startOffset, |
| int nameOffset, |
| int endOffset, |
| int supertypeOffset, |
| {required bool isMacro}) { |
| _addClass( |
| TypeParameterScopeKind.classDeclaration, |
| metadata, |
| modifiers, |
| className, |
| typeVariables, |
| supertype, |
| interfaces, |
| startOffset, |
| nameOffset, |
| endOffset, |
| supertypeOffset, |
| isMacro: isMacro); |
| } |
| |
| void addMixinDeclaration( |
| List<MetadataBuilder>? metadata, |
| int modifiers, |
| String className, |
| List<TypeVariableBuilder>? typeVariables, |
| TypeBuilder? supertype, |
| List<TypeBuilder>? interfaces, |
| int startOffset, |
| int nameOffset, |
| int endOffset, |
| int supertypeOffset) { |
| _addClass( |
| TypeParameterScopeKind.mixinDeclaration, |
| metadata, |
| modifiers, |
| className, |
| typeVariables, |
| supertype, |
| interfaces, |
| startOffset, |
| nameOffset, |
| endOffset, |
| supertypeOffset, |
| isMacro: false); |
| } |
| |
| void _addClass( |
| TypeParameterScopeKind kind, |
| List<MetadataBuilder>? metadata, |
| int modifiers, |
| String className, |
| List<TypeVariableBuilder>? typeVariables, |
| TypeBuilder? supertype, |
| List<TypeBuilder>? interfaces, |
| int startOffset, |
| int nameOffset, |
| int endOffset, |
| int supertypeOffset, |
| {required bool isMacro}) { |
| _checkBadFunctionDeclUse(className, kind, nameOffset); |
| _checkBadFunctionParameter(typeVariables); |
| // Nested declaration began in `OutlineBuilder.beginClassDeclaration`. |
| TypeParameterScopeBuilder declaration = |
| endNestedDeclaration(kind, className) |
| ..resolveNamedTypes(typeVariables, this); |
| assert(declaration.parent == _libraryTypeParameterScopeBuilder); |
| Map<String, Builder> members = declaration.members!; |
| Map<String, MemberBuilder> constructors = declaration.constructors!; |
| Map<String, MemberBuilder> setters = declaration.setters!; |
| |
| Scope classScope = new Scope( |
| local: members, |
| setters: setters, |
| parent: scope.withTypeVariables(typeVariables), |
| debugName: "class $className", |
| isModifiable: false); |
| |
| // When looking up a constructor, we don't consider type variables or the |
| // library scope. |
| ConstructorScope constructorScope = |
| new ConstructorScope(className, constructors); |
| bool isMixinDeclaration = false; |
| if (modifiers & mixinDeclarationMask != 0) { |
| isMixinDeclaration = true; |
| modifiers = (modifiers & ~mixinDeclarationMask) | abstractMask; |
| } |
| if (declaration.declaresConstConstructor) { |
| modifiers |= declaresConstConstructorMask; |
| } |
| ClassBuilder classBuilder = new SourceClassBuilder( |
| metadata, |
| modifiers, |
| className, |
| typeVariables, |
| applyMixins(supertype, startOffset, nameOffset, endOffset, className, |
| isMixinDeclaration, |
| typeVariables: typeVariables, isMacro: false), |
| interfaces, |
| // TODO(johnniwinther): Add the `on` clause types of a mixin declaration |
| // here. |
| null, |
| classScope, |
| constructorScope, |
| this, |
| new List<ConstructorReferenceBuilder>.from(constructorReferences), |
| startOffset, |
| nameOffset, |
| endOffset, |
| _currentClassReferencesFromIndexed, |
| isMixinDeclaration: isMixinDeclaration, |
| isMacro: isMacro); |
| |
| constructorReferences.clear(); |
| Map<String, TypeVariableBuilder>? typeVariablesByName = |
| checkTypeVariables(typeVariables, classBuilder); |
| void setParent(String name, MemberBuilder? member) { |
| while (member != null) { |
| member.parent = classBuilder; |
| member = member.next as MemberBuilder?; |
| } |
| } |
| |
| void setParentAndCheckConflicts(String name, Builder member) { |
| if (typeVariablesByName != null) { |
| TypeVariableBuilder? tv = typeVariablesByName[name]; |
| if (tv != null) { |
| classBuilder.addProblem( |
| templateConflictsWithTypeVariable.withArguments(name), |
| member.charOffset, |
| name.length, |
| context: [ |
| messageConflictsWithTypeVariableCause.withLocation( |
| tv.fileUri!, tv.charOffset, name.length) |
| ]); |
| } |
| } |
| setParent(name, member as MemberBuilder); |
| } |
| |
| members.forEach(setParentAndCheckConflicts); |
| constructors.forEach(setParentAndCheckConflicts); |
| setters.forEach(setParentAndCheckConflicts); |
| addBuilder(className, classBuilder, nameOffset, |
| getterReference: _currentClassReferencesFromIndexed?.cls.reference); |
| } |
| |
| Map<String, TypeVariableBuilder>? checkTypeVariables( |
| List<TypeVariableBuilder>? typeVariables, Builder? owner) { |
| if (typeVariables == null || typeVariables.isEmpty) return null; |
| Map<String, TypeVariableBuilder> typeVariablesByName = |
| <String, TypeVariableBuilder>{}; |
| for (TypeVariableBuilder tv in typeVariables) { |
| TypeVariableBuilder? existing = typeVariablesByName[tv.name]; |
| if (existing != null) { |
| if (existing.isExtensionTypeParameter) { |
| // The type parameter from the extension is shadowed by the type |
| // parameter from the member. Rename the shadowed type parameter. |
| existing.parameter.name = '#${existing.name}'; |
| typeVariablesByName[tv.name] = tv; |
| } else { |
| addProblem(messageTypeVariableDuplicatedName, tv.charOffset, |
| tv.name.length, fileUri, |
| context: [ |
| templateTypeVariableDuplicatedNameCause |
| .withArguments(tv.name) |
| .withLocation( |
| fileUri, existing.charOffset, existing.name.length) |
| ]); |
| } |
| } else { |
| typeVariablesByName[tv.name] = tv; |
| if (owner is ClassBuilder) { |
| // Only classes and type variables can't have the same name. See |
| // [#29555](https://github.com/dart-lang/sdk/issues/29555). |
| if (tv.name == owner.name) { |
| addProblem(messageTypeVariableSameNameAsEnclosing, tv.charOffset, |
| tv.name.length, fileUri); |
| } |
| } |
| } |
| } |
| return typeVariablesByName; |
| } |
| |
| void checkGetterSetterTypes(ProcedureBuilder getterBuilder, |
| ProcedureBuilder setterBuilder, TypeEnvironment typeEnvironment) { |
| DartType getterType; |
| List<TypeParameter>? getterExtensionTypeParameters; |
| if (getterBuilder.isExtensionInstanceMember) { |
| // An extension instance getter |
| // |
| // extension E<T> on A { |
| // T get property => ... |
| // } |
| // |
| // is encoded as a top level method |
| // |
| // T# E#get#property<T#>(A #this) => ... |
| // |
| Procedure procedure = getterBuilder.procedure; |
| getterType = procedure.function.returnType; |
| getterExtensionTypeParameters = procedure.function.typeParameters; |
| } else { |
| getterType = getterBuilder.procedure.getterType; |
| } |
| DartType setterType; |
| if (setterBuilder.isExtensionInstanceMember) { |
| // An extension instance setter |
| // |
| // extension E<T> on A { |
| // void set property(T value) { ... } |
| // } |
| // |
| // is encoded as a top level method |
| // |
| // void E#set#property<T#>(A #this, T# value) { ... } |
| // |
| Procedure procedure = setterBuilder.procedure; |
| setterType = procedure.function.positionalParameters[1].type; |
| if (getterExtensionTypeParameters != null && |
| getterExtensionTypeParameters.isNotEmpty) { |
| // We substitute the setter type parameters for the getter type |
| // parameters to check them below in a shared context. |
| List<TypeParameter> setterExtensionTypeParameters = |
| procedure.function.typeParameters; |
| assert(getterExtensionTypeParameters.length == |
| setterExtensionTypeParameters.length); |
| setterType = Substitution.fromPairs( |
| setterExtensionTypeParameters, |
| new List<DartType>.generate( |
| getterExtensionTypeParameters.length, |
| (int index) => new TypeParameterType.forAlphaRenaming( |
| setterExtensionTypeParameters[index], |
| getterExtensionTypeParameters![index]))) |
| .substituteType(setterType); |
| } |
| } else { |
| setterType = setterBuilder.procedure.setterType; |
| } |
| |
| if (getterType is InvalidType || setterType is InvalidType) { |
| // Don't report a problem as something else is wrong that has already |
| // been reported. |
| } else { |
| bool isValid = typeEnvironment.isSubtypeOf( |
| getterType, |
| setterType, |
| library.isNonNullableByDefault |
| ? SubtypeCheckMode.withNullabilities |
| : SubtypeCheckMode.ignoringNullabilities); |
| if (!isValid && !library.isNonNullableByDefault) { |
| // Allow assignability in legacy libraries. |
| isValid = typeEnvironment.isSubtypeOf( |
| setterType, getterType, SubtypeCheckMode.ignoringNullabilities); |
| } |
| if (!isValid) { |
| String getterMemberName = getterBuilder.fullNameForErrors; |
| String setterMemberName = setterBuilder.fullNameForErrors; |
| Template<Message Function(DartType, String, DartType, String, bool)> |
| template = library.isNonNullableByDefault |
| ? templateInvalidGetterSetterType |
| : templateInvalidGetterSetterTypeLegacy; |
| addProblem( |
| template.withArguments(getterType, getterMemberName, setterType, |
| setterMemberName, library.isNonNullableByDefault), |
| getterBuilder.charOffset, |
| getterBuilder.name.length, |
| getterBuilder.fileUri, |
| context: [ |
| templateInvalidGetterSetterTypeSetterContext |
| .withArguments(setterMemberName) |
| .withLocation(setterBuilder.fileUri!, |
| setterBuilder.charOffset, setterBuilder.name.length) |
| ]); |
| } |
| } |
| } |
| |
| void addExtensionDeclaration( |
| List<MetadataBuilder>? metadata, |
| int modifiers, |
| String extensionName, |
| List<TypeVariableBuilder>? typeVariables, |
| TypeBuilder type, |
| ExtensionTypeShowHideClauseBuilder extensionTypeShowHideClauseBuilder, |
| bool isExtensionTypeDeclaration, |
| int startOffset, |
| int nameOffset, |
| int endOffset) { |
| _checkBadFunctionDeclUse( |
| extensionName, TypeParameterScopeKind.extensionDeclaration, nameOffset); |
| _checkBadFunctionParameter(typeVariables); |
| // Nested declaration began in `OutlineBuilder.beginExtensionDeclaration`. |
| TypeParameterScopeBuilder declaration = endNestedDeclaration( |
| TypeParameterScopeKind.extensionDeclaration, extensionName) |
| ..resolveNamedTypes(typeVariables, this); |
| assert(declaration.parent == _libraryTypeParameterScopeBuilder); |
| Map<String, Builder> members = declaration.members!; |
| Map<String, MemberBuilder> constructors = declaration.constructors!; |
| Map<String, MemberBuilder> setters = declaration.setters!; |
| |
| Scope classScope = new Scope( |
| local: members, |
| setters: setters, |
| parent: scope.withTypeVariables(typeVariables), |
| debugName: "extension $extensionName", |
| isModifiable: false); |
| |
| Extension? referenceFrom = |
| referencesFromIndexed?.lookupExtension(extensionName); |
| |
| ExtensionBuilder extensionBuilder = new SourceExtensionBuilder( |
| metadata, |
| modifiers, |
| extensionName, |
| typeVariables, |
| type, |
| extensionTypeShowHideClauseBuilder, |
| classScope, |
| this, |
| isExtensionTypeDeclaration, |
| startOffset, |
| nameOffset, |
| endOffset, |
| referenceFrom); |
| constructorReferences.clear(); |
| Map<String, TypeVariableBuilder>? typeVariablesByName = |
| checkTypeVariables(typeVariables, extensionBuilder); |
| void setParent(String name, MemberBuilder? member) { |
| while (member != null) { |
| member.parent = extensionBuilder; |
| member = member.next as MemberBuilder?; |
| } |
| } |
| |
| void setParentAndCheckConflicts(String name, Builder member) { |
| if (typeVariablesByName != null) { |
| TypeVariableBuilder? tv = typeVariablesByName[name]; |
| if (tv != null) { |
| extensionBuilder.addProblem( |
| templateConflictsWithTypeVariable.withArguments(name), |
| member.charOffset, |
| name.length, |
| context: [ |
| messageConflictsWithTypeVariableCause.withLocation( |
| tv.fileUri!, tv.charOffset, name.length) |
| ]); |
| } |
| } |
| setParent(name, member as MemberBuilder); |
| } |
| |
| members.forEach(setParentAndCheckConflicts); |
| constructors.forEach(setParentAndCheckConflicts); |
| setters.forEach(setParentAndCheckConflicts); |
| addBuilder(extensionName, extensionBuilder, nameOffset, |
| getterReference: referenceFrom?.reference); |
| } |
| |
| TypeBuilder? applyMixins( |
| TypeBuilder? type, |
| int startCharOffset, |
| int charOffset, |
| int charEndOffset, |
| String subclassName, |
| bool isMixinDeclaration, |
| {List<MetadataBuilder>? metadata, |
| String? name, |
| List<TypeVariableBuilder>? typeVariables, |
| int modifiers: 0, |
| List<TypeBuilder>? interfaces, |
| required bool isMacro}) { |
| if (name == null) { |
| // The following parameters should only be used when building a named |
| // mixin application. |
| if (metadata != null) { |
| unhandled("metadata", "unnamed mixin application", charOffset, fileUri); |
| } else if (interfaces != null) { |
| unhandled( |
| "interfaces", "unnamed mixin application", charOffset, fileUri); |
| } |
| } |
| if (type is MixinApplicationBuilder) { |
| // Documentation below assumes the given mixin application is in one of |
| // these forms: |
| // |
| // class C extends S with M1, M2, M3; |
| // class Named = S with M1, M2, M3; |
| // |
| // When we refer to the subclass, we mean `C` or `Named`. |
| |
| /// The current supertype. |
| /// |
| /// Starts out having the value `S` and on each iteration of the loop |
| /// below, it will take on the value corresponding to: |
| /// |
| /// 1. `S with M1`. |
| /// 2. `(S with M1) with M2`. |
| /// 3. `((S with M1) with M2) with M3`. |
| TypeBuilder supertype = type.supertype ?? loader.target.objectType; |
| |
| /// The variable part of the mixin application's synthetic name. It |
| /// starts out as the name of the superclass, but is only used after it |
| /// has been combined with the name of the current mixin. In the examples |
| /// from above, it will take these values: |
| /// |
| /// 1. `S&M1` |
| /// 2. `S&M1&M2` |
| /// 3. `S&M1&M2&M3`. |
| /// |
| /// The full name of the mixin application is obtained by prepending the |
| /// name of the subclass (`C` or `Named` in the above examples) to the |
| /// running name. For the example `C`, that leads to these full names: |
| /// |
| /// 1. `_C&S&M1` |
| /// 2. `_C&S&M1&M2` |
| /// 3. `_C&S&M1&M2&M3`. |
| /// |
| /// For a named mixin application, the last name has been given by the |
| /// programmer, so for the example `Named` we see these full names: |
| /// |
| /// 1. `_Named&S&M1` |
| /// 2. `_Named&S&M1&M2` |
| /// 3. `Named`. |
| String runningName = extractName(supertype.name); |
| |
| /// True when we're building a named mixin application. Notice that for |
| /// the `Named` example above, this is only true on the last |
| /// iteration because only the full mixin application is named. |
| bool isNamedMixinApplication; |
| |
| /// The names of the type variables of the subclass. |
| Set<String>? typeVariableNames; |
| if (typeVariables != null) { |
| typeVariableNames = new Set<String>(); |
| for (TypeVariableBuilder typeVariable in typeVariables) { |
| typeVariableNames.add(typeVariable.name); |
| } |
| } |
| |
| /// Helper function that returns `true` if a type variable with a name |
| /// from [typeVariableNames] is referenced in [type]. |
| bool usesTypeVariables(TypeBuilder? type) { |
| if (type is NamedTypeBuilder) { |
| if (type.declaration is TypeVariableBuilder) { |
| return typeVariableNames!.contains(type.declaration!.name); |
| } |
| |
| List<TypeBuilder>? typeArguments = type.arguments; |
| if (typeArguments != null && typeVariables != null) { |
| for (TypeBuilder argument in typeArguments) { |
| if (usesTypeVariables(argument)) { |
| return true; |
| } |
| } |
| } |
| } else if (type is FunctionTypeBuilder) { |
| if (type.formals != null) { |
| for (FormalParameterBuilder formal in type.formals!) { |
| if (usesTypeVariables(formal.type)) { |
| return true; |
| } |
| } |
| } |
| List<TypeVariableBuilder>? typeVariables = type.typeVariables; |
| if (typeVariables != null) { |
| for (TypeVariableBuilder variable in typeVariables) { |
| if (usesTypeVariables(variable.bound)) { |
| return true; |
| } |
| } |
| } |
| return usesTypeVariables(type.returnType); |
| } |
| return false; |
| } |
| |
| /// Iterate over the mixins from left to right. At the end of each |
| /// iteration, a new [supertype] is computed that is the mixin |
| /// application of [supertype] with the current mixin. |
| for (int i = 0; i < type.mixins.length; i++) { |
| TypeBuilder mixin = type.mixins[i]; |
| isNamedMixinApplication = name != null && mixin == type.mixins.last; |
| bool isGeneric = false; |
| if (!isNamedMixinApplication) { |
| if (supertype is NamedTypeBuilder) { |
| isGeneric = isGeneric || usesTypeVariables(supertype); |
| } |
| if (mixin is NamedTypeBuilder) { |
| runningName += "&${extractName(mixin.name)}"; |
| isGeneric = isGeneric || usesTypeVariables(mixin); |
| } |
| } |
| String fullname = |
| isNamedMixinApplication ? name : "_$subclassName&$runningName"; |
| List<TypeVariableBuilder>? applicationTypeVariables; |
| List<TypeBuilder>? applicationTypeArguments; |
| if (isNamedMixinApplication) { |
| // If this is a named mixin application, it must be given all the |
| // declarated type variables. |
| applicationTypeVariables = typeVariables; |
| } else { |
| // Otherwise, we pass the fresh type variables to the mixin |
| // application in the same order as they're declared on the subclass. |
| if (isGeneric) { |
| this.beginNestedDeclaration( |
| TypeParameterScopeKind.unnamedMixinApplication, |
| "mixin application"); |
| |
| applicationTypeVariables = copyTypeVariables( |
| typeVariables!, currentTypeParameterScopeBuilder); |
| |
| List<NamedTypeBuilder> newTypes = <NamedTypeBuilder>[]; |
| if (supertype is NamedTypeBuilder && supertype.arguments != null) { |
| for (int i = 0; i < supertype.arguments!.length; ++i) { |
| supertype.arguments![i] = supertype.arguments![i] |
| .clone(newTypes, this, currentTypeParameterScopeBuilder); |
| } |
| } |
| if (mixin is NamedTypeBuilder && mixin.arguments != null) { |
| for (int i = 0; i < mixin.arguments!.length; ++i) { |
| mixin.arguments![i] = mixin.arguments![i] |
| .clone(newTypes, this, currentTypeParameterScopeBuilder); |
| } |
| } |
| for (NamedTypeBuilder newType in newTypes) { |
| currentTypeParameterScopeBuilder |
| .registerUnresolvedNamedType(newType); |
| } |
| |
| TypeParameterScopeBuilder mixinDeclaration = this |
| .endNestedDeclaration( |
| TypeParameterScopeKind.unnamedMixinApplication, |
| "mixin application"); |
| mixinDeclaration.resolveNamedTypes(applicationTypeVariables, this); |
| |
| applicationTypeArguments = <TypeBuilder>[]; |
| for (TypeVariableBuilder typeVariable in typeVariables) { |
| applicationTypeArguments.add(addNamedType(typeVariable.name, |
| const NullabilityBuilder.omitted(), null, charOffset, |
| instanceTypeVariableAccess: |
| InstanceTypeVariableAccessState.Allowed) |
| ..bind( |
| // The type variable types passed as arguments to the |
| // generic class representing the anonymous mixin |
| // application should refer back to the type variables of |
| // the class that extend the anonymous mixin application. |
| typeVariable)); |
| } |
| } |
| } |
| final int computedStartCharOffset = |
| !isNamedMixinApplication || metadata == null |
| ? startCharOffset |
| : metadata.first.charOffset; |
| |
| IndexedClass? referencesFromIndexedClass; |
| if (referencesFromIndexed != null) { |
| referencesFromIndexedClass = |
| referencesFromIndexed!.lookupIndexedClass(fullname); |
| } |
| |
| SourceClassBuilder application = new SourceClassBuilder( |
| isNamedMixinApplication ? metadata : null, |
| isNamedMixinApplication |
| ? modifiers | namedMixinApplicationMask |
| : abstractMask, |
| fullname, |
| applicationTypeVariables, |
| isMixinDeclaration ? null : supertype, |
| isNamedMixinApplication |
| ? interfaces |
| : isMixinDeclaration |
| ? [supertype, mixin] |
| : null, |
| null, // No `on` clause types. |
| new Scope( |
| local: <String, MemberBuilder>{}, |
| setters: <String, MemberBuilder>{}, |
| parent: scope.withTypeVariables(typeVariables), |
| debugName: "mixin $fullname ", |
| isModifiable: false), |
| new ConstructorScope(fullname, <String, MemberBuilder>{}), |
| this, |
| <ConstructorReferenceBuilder>[], |
| computedStartCharOffset, |
| charOffset, |
| charEndOffset, |
| referencesFromIndexedClass, |
| mixedInTypeBuilder: isMixinDeclaration ? null : mixin, |
| isMacro: isNamedMixinApplication && isMacro); |
| // TODO(ahe, kmillikin): Should always be true? |
| // pkg/analyzer/test/src/summary/resynthesize_kernel_test.dart can't |
| // handle that :( |
| application.cls.isAnonymousMixin = !isNamedMixinApplication; |
| addBuilder(fullname, application, charOffset, |
| getterReference: referencesFromIndexedClass?.cls.reference); |
| supertype = addNamedType(fullname, const NullabilityBuilder.omitted(), |
| applicationTypeArguments, charOffset, |
| instanceTypeVariableAccess: |
| InstanceTypeVariableAccessState.Allowed); |
| } |
| return supertype; |
| } else { |
| return type; |
| } |
| } |
| |
| void addNamedMixinApplication( |
| List<MetadataBuilder>? metadata, |
| String name, |
| List<TypeVariableBuilder>? typeVariables, |
| int modifiers, |
| TypeBuilder? mixinApplication, |
| List<TypeBuilder>? interfaces, |
| int startCharOffset, |
| int charOffset, |
| int charEndOffset, |
| {required bool isMacro}) { |
| // Nested declaration began in `OutlineBuilder.beginNamedMixinApplication`. |
| endNestedDeclaration(TypeParameterScopeKind.namedMixinApplication, name) |
| .resolveNamedTypes(typeVariables, this); |
| TypeBuilder supertype = applyMixins(mixinApplication, startCharOffset, |
| charOffset, charEndOffset, name, false, |
| metadata: metadata, |
| name: name, |
| typeVariables: typeVariables, |
| modifiers: modifiers, |
| interfaces: interfaces, |
| isMacro: isMacro)!; |
| checkTypeVariables(typeVariables, supertype.declaration); |
| } |
| |
| void addField( |
| List<MetadataBuilder>? metadata, |
| int modifiers, |
| bool isTopLevel, |
| TypeBuilder? type, |
| String name, |
| int charOffset, |
| int charEndOffset, |
| Token? initializerToken, |
| bool hasInitializer, |
| {Token? constInitializerToken}) { |
| if (hasInitializer) { |
| modifiers |= hasInitializerMask; |
| } |
| bool isLate = (modifiers & lateMask) != 0; |
| bool isFinal = (modifiers & finalMask) != 0; |
| bool isStatic = (modifiers & staticMask) != 0; |
| bool isExternal = (modifiers & externalMask) != 0; |
| final bool fieldIsLateWithLowering = isLate && |
| (loader.target.backendTarget.isLateFieldLoweringEnabled( |
| hasInitializer: hasInitializer, |
| isFinal: isFinal, |
| isStatic: isTopLevel || isStatic) || |
| (loader.target.backendTarget.useStaticFieldLowering && |
| (isStatic || isTopLevel))); |
| |
| final bool isInstanceMember = currentTypeParameterScopeBuilder.kind != |
| TypeParameterScopeKind.library && |
| (modifiers & staticMask) == 0; |
| String? className; |
| if (isInstanceMember) { |
| className = currentTypeParameterScopeBuilder.name; |
| } |
| final bool isExtensionMember = currentTypeParameterScopeBuilder.kind == |
| TypeParameterScopeKind.extensionDeclaration; |
| String? extensionName; |
| if (isExtensionMember) { |
| extensionName = currentTypeParameterScopeBuilder.name; |
| } |
| |
| Reference? fieldReference; |
| Reference? fieldGetterReference; |
| Reference? fieldSetterReference; |
| Reference? lateIsSetFieldReference; |
| Reference? lateIsSetGetterReference; |
| Reference? lateIsSetSetterReference; |
| Reference? lateGetterReference; |
| Reference? lateSetterReference; |
| |
| NameScheme nameScheme = new NameScheme( |
| isInstanceMember: isInstanceMember, |
| className: className, |
| isExtensionMember: isExtensionMember, |
| extensionName: extensionName, |
| libraryReference: referencesFrom?.reference ?? library.reference); |
| if (referencesFrom != null) { |
| IndexedContainer indexedContainer = |
| (_currentClassReferencesFromIndexed ?? referencesFromIndexed)!; |
| if (isExtensionMember && isInstanceMember && isExternal) { |
| /// An external extension instance field is special. It is treated |
| /// as an external getter/setter pair and is therefore encoded as a pair |
| /// of top level methods using the extension instance member naming |
| /// convention. |
| fieldGetterReference = indexedContainer.lookupGetterReference( |
| nameScheme.getProcedureName(ProcedureKind.Getter, name)); |
| fieldSetterReference = indexedContainer.lookupGetterReference( |
| nameScheme.getProcedureName(ProcedureKind.Setter, name)); |
| } else { |
| Name nameToLookup = nameScheme.getFieldName(FieldNameType.Field, name, |
| isSynthesized: fieldIsLateWithLowering); |
| fieldReference = indexedContainer.lookupFieldReference(nameToLookup); |
| fieldGetterReference = |
| indexedContainer.lookupGetterReference(nameToLookup); |
| fieldSetterReference = |
| indexedContainer.lookupSetterReference(nameToLookup); |
| } |
| |
| if (fieldIsLateWithLowering) { |
| Name lateIsSetName = nameScheme.getFieldName( |
| FieldNameType.IsSetField, name, |
| isSynthesized: fieldIsLateWithLowering); |
| lateIsSetFieldReference = |
| indexedContainer.lookupFieldReference(lateIsSetName); |
| lateIsSetGetterReference = |
| indexedContainer.lookupGetterReference(lateIsSetName); |
| lateIsSetSetterReference = |
| indexedContainer.lookupSetterReference(lateIsSetName); |
| lateGetterReference = indexedContainer.lookupGetterReference( |
| nameScheme.getFieldName(FieldNameType.Getter, name, |
| isSynthesized: fieldIsLateWithLowering)); |
| lateSetterReference = indexedContainer.lookupSetterReference( |
| nameScheme.getFieldName(FieldNameType.Setter, name, |
| isSynthesized: fieldIsLateWithLowering)); |
| } |
| } |
| |
| SourceFieldBuilder fieldBuilder = new SourceFieldBuilder( |
| metadata, |
| type, |
| name, |
| modifiers, |
| isTopLevel, |
| this, |
| charOffset, |
| charEndOffset, |
| nameScheme, |
| fieldReference: fieldReference, |
| fieldGetterReference: fieldGetterReference, |
| fieldSetterReference: fieldSetterReference, |
| lateIsSetFieldReference: lateIsSetFieldReference, |
| lateIsSetGetterReference: lateIsSetGetterReference, |
| lateIsSetSetterReference: lateIsSetSetterReference, |
| lateGetterReference: lateGetterReference, |
| lateSetterReference: lateSetterReference, |
| constInitializerToken: constInitializerToken); |
| addBuilder(name, fieldBuilder, charOffset, |
| getterReference: fieldGetterReference, |
| setterReference: fieldSetterReference); |
| if (type == null && fieldBuilder.next == null) { |
| // Only the first one (the last one in the linked list of next pointers) |
| // are added to the tree, had parent pointers and can infer correctly. |
| if (initializerToken == null && fieldBuilder.isStatic) { |
| // A static field without type and initializer will always be inferred |
| // to have type `dynamic`. |
| fieldBuilder.fieldType = const DynamicType(); |
| } else { |
| // A field with no type and initializer or an instance field without |
| // type and initializer need to have the type inferred. |
| fieldBuilder.fieldType = |
| new ImplicitFieldType(fieldBuilder, initializerToken); |
| registerImplicitlyTypedField(fieldBuilder); |
| } |
| } |
| } |
| |
| void addConstructor( |
| List<MetadataBuilder>? metadata, |
| int modifiers, |
| TypeBuilder? returnType, |
| final Object? name, |
| String constructorName, |
| List<TypeVariableBuilder>? typeVariables, |
| List<FormalParameterBuilder>? formals, |
| int startCharOffset, |
| int charOffset, |
| int charOpenParenOffset, |
| int charEndOffset, |
| String? nativeMethodName, |
| {Token? beginInitializers, |
| required bool forAbstractClass}) { |
| Reference? constructorReference; |
| Reference? tearOffReference; |
| if (_currentClassReferencesFromIndexed != null) { |
| constructorReference = _currentClassReferencesFromIndexed! |
| .lookupConstructorReference(new Name( |
| constructorName, _currentClassReferencesFromIndexed!.library)); |
| tearOffReference = _currentClassReferencesFromIndexed! |
| .lookupGetterReference(constructorTearOffName( |
| constructorName, _currentClassReferencesFromIndexed!.library)); |
| } |
| SourceConstructorBuilder constructorBuilder = new SourceConstructorBuilder( |
| metadata, |
| modifiers & ~abstractMask, |
| returnType, |
| constructorName, |
| typeVariables, |
| formals, |
| this, |
| startCharOffset, |
| charOffset, |
| charOpenParenOffset, |
| charEndOffset, |
| constructorReference, |
| tearOffReference, |
| nativeMethodName: nativeMethodName, |
| forAbstractClassOrEnum: forAbstractClass); |
| checkTypeVariables(typeVariables, constructorBuilder); |
| // TODO(johnniwinther): There is no way to pass the tear off reference here. |
| addBuilder(constructorName, constructorBuilder, charOffset, |
| getterReference: constructorReference); |
| if (nativeMethodName != null) { |
| addNativeMethod(constructorBuilder); |
| } |
| if (constructorBuilder.isConst) { |
| currentTypeParameterScopeBuilder.declaresConstConstructor = true; |
| } |
| if (constructorBuilder.isConst || enableSuperParametersInLibrary) { |
| // const constructors will have their initializers compiled and written |
| // into the outline. In case of super-parameters language feature, the |
| // super initializers are required to infer the types of super parameters. |
| constructorBuilder.beginInitializers = |
| beginInitializers ?? new Token.eof(-1); |
| } |
| } |
| |
| void addProcedure( |
| List<MetadataBuilder>? metadata, |
| int modifiers, |
| TypeBuilder? returnType, |
| String name, |
| List<TypeVariableBuilder>? typeVariables, |
| List<FormalParameterBuilder>? formals, |
| ProcedureKind kind, |
| int startCharOffset, |
| int charOffset, |
| int charOpenParenOffset, |
| int charEndOffset, |
| String? nativeMethodName, |
| AsyncMarker asyncModifier, |
| {required bool isInstanceMember, |
| required bool isExtensionMember}) { |
| // ignore: unnecessary_null_comparison |
| assert(isInstanceMember != null); |
| // ignore: unnecessary_null_comparison |
| assert(isExtensionMember != null); |
| assert(!isExtensionMember || |
| currentTypeParameterScopeBuilder.kind == |
| TypeParameterScopeKind.extensionDeclaration); |
| String? className = (isInstanceMember && !isExtensionMember) |
| ? currentTypeParameterScopeBuilder.name |
| : null; |
| String? extensionName = |
| isExtensionMember ? currentTypeParameterScopeBuilder.name : null; |
| NameScheme nameScheme = new NameScheme( |
| isExtensionMember: isExtensionMember, |
| className: className, |
| extensionName: extensionName, |
| isInstanceMember: isInstanceMember, |
| libraryReference: referencesFrom?.reference ?? library.reference); |
| |
| if (returnType == null) { |
| if (kind == ProcedureKind.Operator && |
| identical(name, indexSetName.text)) { |
| returnType = addVoidType(charOffset); |
| } else if (kind == ProcedureKind.Setter) { |
| returnType = addVoidType(charOffset); |
| } |
| } |
| Reference? procedureReference; |
| Reference? tearOffReference; |
| if (referencesFrom != null) { |
| Name nameToLookup = nameScheme.getProcedureName(kind, name); |
| if (_currentClassReferencesFromIndexed != null) { |
| if (kind == ProcedureKind.Setter) { |
| procedureReference = _currentClassReferencesFromIndexed! |
| .lookupSetterReference(nameToLookup); |
| } else { |
| procedureReference = _currentClassReferencesFromIndexed! |
| .lookupGetterReference(nameToLookup); |
| } |
| } else { |
| if (kind == ProcedureKind.Setter && |
| // Extension instance setters are encoded as methods. |
| !(isExtensionMember && isInstanceMember)) { |
| procedureReference = |
| referencesFromIndexed!.lookupSetterReference(nameToLookup); |
| } else { |
| procedureReference = |
| referencesFromIndexed!.lookupGetterReference(nameToLookup); |
| } |
| if (isExtensionMember && kind == ProcedureKind.Method) { |
| tearOffReference = referencesFromIndexed!.lookupGetterReference( |
| nameScheme.getProcedureName(ProcedureKind.Getter, name)); |
| } |
| } |
| } |
| SourceProcedureBuilder procedureBuilder = new SourceProcedureBuilder( |
| metadata, |
| modifiers, |
| returnType, |
| name, |
| typeVariables, |
| formals, |
| kind, |
| this, |
| startCharOffset, |
| charOffset, |
| charOpenParenOffset, |
| charEndOffset, |
| procedureReference, |
| tearOffReference, |
| asyncModifier, |
| nameScheme, |
| isExtensionMember: isExtensionMember, |
| isInstanceMember: isInstanceMember, |
| nativeMethodName: nativeMethodName); |
| checkTypeVariables(typeVariables, procedureBuilder); |
| addBuilder(name, procedureBuilder, charOffset, |
| getterReference: procedureReference); |
| if (nativeMethodName != null) { |
| addNativeMethod(procedureBuilder); |
| } |
| } |
| |
| void addFactoryMethod( |
| List<MetadataBuilder>? metadata, |
| int modifiers, |
| Object name, |
| List<FormalParameterBuilder>? formals, |
| ConstructorReferenceBuilder? redirectionTarget, |
| int startCharOffset, |
| int charOffset, |
| int charOpenParenOffset, |
| int charEndOffset, |
| String? nativeMethodName, |
| AsyncMarker asyncModifier) { |
| TypeBuilder returnType = addNamedType( |
| currentTypeParameterScopeBuilder.parent!.name, |
| const NullabilityBuilder.omitted(), |
| <TypeBuilder>[], |
| charOffset, |
| instanceTypeVariableAccess: InstanceTypeVariableAccessState.Allowed); |
| if (currentTypeParameterScopeBuilder.parent?.kind == |
| TypeParameterScopeKind.extensionDeclaration) { |
| // Make the synthesized return type invalid for extensions. |
| String name = currentTypeParameterScopeBuilder.parent!.name; |
| returnType.bind(new InvalidTypeDeclarationBuilder( |
| name, |
| messageExtensionDeclaresConstructor.withLocation( |
| fileUri, charOffset, name.length))); |
| } |
| // Nested declaration began in `OutlineBuilder.beginFactoryMethod`. |
| TypeParameterScopeBuilder factoryDeclaration = endNestedDeclaration( |
| TypeParameterScopeKind.factoryMethod, "#factory_method"); |
| |
| // Prepare the simple procedure name. |
| String procedureName; |
| String? constructorName = |
| computeAndValidateConstructorName(name, charOffset, isFactory: true); |
| if (constructorName != null) { |
| procedureName = constructorName; |
| } else { |
| procedureName = name as String; |
| } |
| |
| NameScheme procedureNameScheme = new NameScheme( |
| isExtensionMember: false, |
| className: null, |
| extensionName: null, |
| isInstanceMember: false, |
| libraryReference: referencesFrom != null |
| ? (_currentClassReferencesFromIndexed ?? referencesFromIndexed)! |
| .library |
| .reference |
| : library.reference); |
| |
| Reference? constructorReference; |
| Reference? tearOffReference; |
| if (_currentClassReferencesFromIndexed != null) { |
| constructorReference = _currentClassReferencesFromIndexed! |
| .lookupConstructorReference(new Name( |
| procedureName, _currentClassReferencesFromIndexed!.library)); |
| tearOffReference = _currentClassReferencesFromIndexed! |
| .lookupGetterReference(constructorTearOffName( |
| procedureName, _currentClassReferencesFromIndexed!.library)); |
| } |
| |
| SourceFactoryBuilder procedureBuilder; |
| if (redirectionTarget != null) { |
| procedureBuilder = new RedirectingFactoryBuilder( |
| metadata, |
| staticMask | modifiers, |
| returnType, |
| procedureName, |
| copyTypeVariables( |
| currentTypeParameterScopeBuilder.typeVariables ?? |
| const <TypeVariableBuilder>[], |
| factoryDeclaration), |
| formals, |
| this, |
| startCharOffset, |
| charOffset, |
| charOpenParenOffset, |
| charEndOffset, |
| constructorReference, |
| tearOffReference, |
| procedureNameScheme, |
| nativeMethodName, |
| redirectionTarget); |
| } else { |
| procedureBuilder = new SourceFactoryBuilder( |
| metadata, |
| staticMask | modifiers, |
| returnType, |
| procedureName, |
| copyTypeVariables( |
| currentTypeParameterScopeBuilder.typeVariables ?? |
| const <TypeVariableBuilder>[], |
| factoryDeclaration), |
| formals, |
| this, |
| startCharOffset, |
| charOffset, |
| charOpenParenOffset, |
| charEndOffset, |
| constructorReference, |
| tearOffReference, |
| asyncModifier, |
| procedureNameScheme, |
| nativeMethodName: nativeMethodName); |
| } |
| |
| TypeParameterScopeBuilder savedDeclaration = |
| currentTypeParameterScopeBuilder; |
| currentTypeParameterScopeBuilder = factoryDeclaration; |
| for (TypeVariableBuilder tv in procedureBuilder.typeVariables!) { |
| NamedTypeBuilder t = procedureBuilder.returnType as NamedTypeBuilder; |
| t.arguments!.add(addNamedType(tv.name, const NullabilityBuilder.omitted(), |
| null, procedureBuilder.charOffset, |
| instanceTypeVariableAccess: InstanceTypeVariableAccessState.Allowed)); |
| } |
| currentTypeParameterScopeBuilder = savedDeclaration; |
| |
| factoryDeclaration.resolveNamedTypes(procedureBuilder.typeVariables, this); |
| addBuilder(procedureName, procedureBuilder, charOffset, |
| getterReference: constructorReference); |
| if (nativeMethodName != null) { |
| addNativeMethod(procedureBuilder); |
| } |
| } |
| |
| void addEnum( |
| List<MetadataBuilder>? metadata, |
| String name, |
| List<TypeVariableBuilder>? typeVariables, |
| List<EnumConstantInfo?>? enumConstantInfos, |
| int startCharOffset, |
| int charOffset, |
| int charEndOffset) { |
| IndexedClass? referencesFromIndexedClass; |
| if (referencesFrom != null) { |
| referencesFromIndexedClass = |
| referencesFromIndexed!.lookupIndexedClass(name); |
| } |
| // Nested declaration began in `OutlineBuilder.beginEnum`. |
| // TODO(cstefantsova): Use actual type variables here. |
| TypeParameterScopeBuilder declaration = |
| endNestedDeclaration(TypeParameterScopeKind.enumDeclaration, name) |
| ..resolveNamedTypes([], this); |
| Map<String, Builder> members = declaration.members!; |
| Map<String, MemberBuilder> constructors = declaration.constructors!; |
| Map<String, MemberBuilder> setters = declaration.setters!; |
| |
| EnumBuilder enumBuilder = new EnumBuilder( |
| metadata, |
| name, |
| enumConstantInfos, |
| this, |
| startCharOffset, |
| charOffset, |
| charEndOffset, |
| referencesFromIndexedClass, |
| new Scope( |
| local: members, |
| setters: setters, |
| parent: scope.withTypeVariables(<TypeVariableBuilder>[]), |
| debugName: "enum $name", |
| isModifiable: false), |
| new ConstructorScope(name, constructors)); |
| |
| Map<String, TypeVariableBuilder>? typeVariablesByName = |
| checkTypeVariables(typeVariables, enumBuilder); |
| |
| void setParent(String name, MemberBuilder? member) { |
| while (member != null) { |
| member.parent = enumBuilder; |
| member = member.next as MemberBuilder?; |
| } |
| } |
| |
| void setParentAndCheckConflicts(String name, Builder member) { |
| if (typeVariablesByName != null) { |
| TypeVariableBuilder? tv = typeVariablesByName[name]; |
| if (tv != null) { |
| enumBuilder.addProblem( |
| templateConflictsWithTypeVariable.withArguments(name), |
| member.charOffset, |
| name.length, |
| context: [ |
| messageConflictsWithTypeVariableCause.withLocation( |
| tv.fileUri!, tv.charOffset, name.length) |
| ]); |
| } |
| } |
| setParent(name, member as MemberBuilder); |
| } |
| |
| members.forEach(setParentAndCheckConflicts); |
| constructors.forEach(setParentAndCheckConflicts); |
| setters.forEach(setParentAndCheckConflicts); |
| addBuilder(name, enumBuilder, charOffset, |
| getterReference: referencesFromIndexedClass?.cls.reference); |
| } |
| |
| void addFunctionTypeAlias( |
| List<MetadataBuilder>? metadata, |
| String name, |
| List<TypeVariableBuilder>? typeVariables, |
| TypeBuilder? type, |
| int charOffset) { |
| if (typeVariables != null) { |
| for (TypeVariableBuilder typeVariable in typeVariables) { |
| typeVariable.variance = pendingVariance; |
| } |
| } |
| Typedef? referenceFrom = referencesFromIndexed?.lookupTypedef(name); |
| TypeAliasBuilder typedefBuilder = new SourceTypeAliasBuilder( |
| metadata, name, typeVariables, type, this, charOffset, |
| referenceFrom: referenceFrom); |
| checkTypeVariables(typeVariables, typedefBuilder); |
| // Nested declaration began in `OutlineBuilder.beginFunctionTypeAlias`. |
| endNestedDeclaration(TypeParameterScopeKind.typedef, "#typedef") |
| .resolveNamedTypes(typeVariables, this); |
| addBuilder(name, typedefBuilder, charOffset, |
| getterReference: referenceFrom?.reference); |
| } |
| |
| FunctionTypeBuilder addFunctionType( |
| TypeBuilder? returnType, |
| List<TypeVariableBuilder>? typeVariables, |
| List<FormalParameterBuilder>? formals, |
| NullabilityBuilder nullabilityBuilder, |
| Uri fileUri, |
| int charOffset) { |
| FunctionTypeBuilder builder = new FunctionTypeBuilder(returnType, |
| typeVariables, formals, nullabilityBuilder, fileUri, charOffset); |
| checkTypeVariables(typeVariables, null); |
| if (typeVariables != null) { |
| for (TypeVariableBuilder builder in typeVariables) { |
| if (builder.metadata != null) { |
| if (!enableGenericMetadataInLibrary) { |
| addProblem(messageAnnotationOnFunctionTypeTypeVariable, |
| builder.charOffset, builder.name.length, builder.fileUri); |
| } |
| } |
| } |
| } |
| // Nested declaration began in `OutlineBuilder.beginFunctionType` or |
| // `OutlineBuilder.beginFunctionTypedFormalParameter`. |
| endNestedDeclaration(TypeParameterScopeKind.functionType, "#function_type") |
| .resolveNamedTypes(typeVariables, this); |
| return builder; |
| } |
| |
| FormalParameterBuilder addFormalParameter( |
| List<MetadataBuilder>? metadata, |
| int modifiers, |
| TypeBuilder? type, |
| String name, |
| bool hasThis, |
| bool hasSuper, |
| int charOffset, |
| Token? initializerToken) { |
| assert(!hasThis || !hasSuper, |
| "Formal parameter '${name}' has both 'this' and 'super' prefixes."); |
| if (hasThis) { |
| modifiers |= initializingFormalMask; |
| } |
| if (hasSuper) { |
| modifiers |= superInitializingFormalMask; |
| } |
| FormalParameterBuilder formal = new FormalParameterBuilder( |
| metadata, modifiers, type, name, this, charOffset, |
| fileUri: fileUri) |
|