blob: b7551de78cccbbbeb5180dbe9a8697e78dbe6971 [file] [log] [blame]
// 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)