blob: 91da0d0be0692146256fa64708cffd83a7b2d84b [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.
// @dart = 2.9
library fasta.source_library_builder;
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:front_end/src/fasta/dill/dill_library_builder.dart'
show DillLibraryBuilder;
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/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/unresolved_type.dart';
import '../builder/void_type_declaration_builder.dart';
import '../combinator.dart' show Combinator;
import '../configuration.dart' show Configuration;
import '../export.dart' show Export;
import '../fasta_codes.dart';
import '../identifiers.dart' show QualifiedName, flattenName;
import '../import.dart' show Import;
import '../kernel/internal_ast.dart';
import '../kernel/kernel_builder.dart'
show
ImplicitFieldType,
LoadLibraryBuilder,
compareProcedures,
toKernelCombinators;
import '../kernel/type_algorithms.dart'
show
NonSimplicityIssue,
calculateBounds,
computeTypeVariableBuilderVariance,
findUnaliasedGenericFunctionTypes,
getInboundReferenceIssuesInType,
getNonSimplicityIssuesForDeclaration,
getNonSimplicityIssuesForTypeVariables,
pendingVariance;
import '../loader.dart' show Loader;
import '../modifier.dart'
show
abstractMask,
constMask,
finalMask,
declaresConstConstructorMask,
hasInitializerMask,
initializingFormalMask,
lateMask,
mixinDeclarationMask,
namedMixinApplicationMask,
staticMask;
import '../names.dart' show indexSetName;
import '../problems.dart' show unexpected, unhandled;
import '../scope.dart';
import '../type_inference/type_inferrer.dart' show TypeInferrerImpl;
import 'source_class_builder.dart' show SourceClassBuilder;
import 'source_extension_builder.dart' show SourceExtensionBuilder;
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";
final SourceLoader loader;
final TypeParameterScopeBuilder libraryDeclaration;
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;
final Uri fileUri;
final Uri _packageUri;
Uri get packageUriForTesting => _packageUri;
final List<Object> accessors = <Object>[];
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 actualOrigin;
final List<FunctionBuilder> nativeMethods = <FunctionBuilder>[];
final List<TypeVariableBuilder> boundlessTypeVariables =
<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;
final Library _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;
SourceLibraryBuilder.internal(
SourceLoader loader,
Uri fileUri,
Uri packageUri,
LanguageVersion packageLanguageVersion,
Scope scope,
SourceLibraryBuilder actualOrigin,
Library library,
Library nameOrigin,
Library referencesFrom,
bool referenceIsPartOwner)
: this.fromScopes(
loader,
fileUri,
packageUri,
packageLanguageVersion,
new TypeParameterScopeBuilder.library(),
scope ?? new Scope.top(),
actualOrigin,
library,
nameOrigin,
referencesFrom);
SourceLibraryBuilder.fromScopes(
this.loader,
this.fileUri,
this._packageUri,
this.packageLanguageVersion,
this.libraryDeclaration,
this.importScope,
this.actualOrigin,
this.library,
this._nameOrigin,
this.referencesFrom)
: _languageVersion = packageLanguageVersion,
currentTypeParameterScopeBuilder = libraryDeclaration,
referencesFromIndexed =
referencesFrom == null ? null : new IndexedLibrary(referencesFrom),
super(
fileUri, libraryDeclaration.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}'.");
}
bool _enableConstFunctionsInLibrary;
bool _enableVarianceInLibrary;
bool _enableNonfunctionTypeAliasesInLibrary;
bool _enableNonNullableInLibrary;
Version _enableNonNullableVersionInLibrary;
Version _enableConstructorTearoffsVersionInLibrary;
bool _enableTripleShiftInLibrary;
bool _enableExtensionMethodsInLibrary;
bool _enableGenericMetadataInLibrary;
bool _enableExtensionTypesInLibrary;
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) &&
!isOptOutTest(library.importUri);
Version get enableNonNullableVersionInLibrary =>
_enableNonNullableVersionInLibrary ??= loader.target
.getExperimentEnabledVersionInLibrary(
ExperimentalFlag.nonNullable, _packageUri ?? importUri);
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);
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(
Uri uri,
Uri fileUri,
Uri packageUri,
LanguageVersion packageLanguageVersion,
Loader loader,
SourceLibraryBuilder actualOrigin,
{Scope scope,
Library target,
Library nameOrigin,
Library referencesFrom,
bool referenceIsPartOwner})
: this.internal(
loader,
fileUri,
packageUri,
packageLanguageVersion,
scope,
actualOrigin,
target ??
(actualOrigin?.library ??
new Library(uri,
fileUri: fileUri,
reference: referenceIsPartOwner == true
? null
: referencesFrom?.reference)
..setLanguageVersion(packageLanguageVersion.version)),
nameOrigin,
referencesFrom,
referenceIsPartOwner);
@override
bool get isPart => partOfName != null || partOfUri != null;
List<UnresolvedType> get types => libraryDeclaration.types;
@override
bool get isSynthetic => accessProblem != null;
TypeBuilder addType(TypeBuilder type, int charOffset) {
currentTypeParameterScopeBuilder
.addType(new UnresolvedType(type, charOffset, fileUri));
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;
static bool isOptOutTest(Uri uri) {
String path = uri.path;
for (String testDir in ['/tests/', '/generated_tests/']) {
int start = path.indexOf(testDir);
if (start == -1) continue;
String rest = path.substring(start + testDir.length);
return optOutTestPaths.any(rest.startsWith);
}
return false;
}
static const List<String> optOutTestPaths = [
'co19_2/',
'corelib_2/',
'web_2/',
'ffi_2',
'language_2/',
'lib_2/',
'samples_2/',
'service_2/',
'standalone_2/',
'vm/dart_2/', // in runtime/tests
];
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;
suffix = name.name;
} else {
prefix = name;
suffix = null;
}
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<Combinator> 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.builders[new Uri(scheme: "dart", path: dottedName)];
if (imported == null) {
LibraryBuilder coreLibrary = loader.read(
resolve(this.importUri,
new Uri(scheme: "dart", path: "core").toString(), -1),
-1,
accessor: loader.first);
imported = coreLibrary
.loader.builders[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<Combinator> 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(messageDeprecateDartExt, 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);
LibraryBuilder library = loader.read(partOfUri, uriOffset,
fileUri: newFileUri, accessor: this);
if (loader.first == this) {
// This is a part, and it was the first input. Let the loader know
// about that.
loader.first = library;
}
}
}
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 == libraryDeclaration) {
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 == libraryDeclaration);
}
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);
}
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,
{bool checkForInstanceVsStaticConflict,
bool checkForMethodVsSetterConflict}) {
assert(checkForInstanceVsStaticConflict != null);
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 (MemberBuilderImpl currentSetter = setter;
currentSetter != null;
currentSetter = currentSetter.next) {
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}) {
checkMemberConflicts(this, scope,
checkForInstanceVsStaticConflict: false,
checkForMethodVsSetterConflict: true);
Iterator<Builder> iterator = this.iterator;
while (iterator.moveNext()) {
buildBuilder(iterator.current, coreLibrary);
}
if (modifyTarget == false) 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 getterReference =
referencesFromIndexed?.lookupGetterReference(fieldName);
library.addField(new Field.immutable(fieldName,
initializer: new StringLiteral(jsonEncode(unserializableExports)),
isStatic: true,
isConst: true,
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 (SourceLibraryBuilder 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);
}
}
}
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);
}
}
types.addAll(part.types);
constructorReferences.addAll(part.constructorReferences);
part.partOfLibrary = this;
part.scope.becomePartOf(scope);
// TODO(ahe): Include metadata from part?
nativeMethods.addAll(part.nativeMethods);
boundlessTypeVariables.addAll(part.boundlessTypeVariables);
// 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>[];
library.problemsAsJson.addAll(part.library.problemsAsJson);
}
List<FieldBuilder> partImplicitlyTypedFields =
part.takeImplicitlyTypedFields();
if (partImplicitlyTypedFields != null) {
if (_implicitlyTypedFields == null) {
_implicitlyTypedFields = partImplicitlyTypedFields;
} else {
_implicitlyTypedFields.addAll(partImplicitlyTypedFields);
}
}
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() {
NameIterator iterator = nameIterator;
while (iterator.moveNext()) {
addToExportScope(iterator.name, iterator.current);
}
}
void addImportsToScope() {
bool explicitCoreImport = this == loader.coreLibrary;
for (Import import in imports) {
if (import.imported == loader.coreLibrary) {
explicitCoreImport = true;
}
if (import.imported?.isPart ?? false) {
addProblem(
templatePartOfInLibrary.withArguments(import.imported.fileUri),
import.charOffset,
noLength,
fileUri);
}
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.message;
} 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);
}
}
/// Resolves all unresolved types in [types]. The list of types is cleared
/// when done.
int resolveTypes() {
int typeCount = types.length;
for (UnresolvedType t in types) {
t.resolveIn(scope, this);
t.checkType(this);
}
types.clear();
return typeCount;
}
@override
int resolveConstructors(_) {
int count = 0;
Iterator<Builder> iterator = this.iterator;
while (iterator.moveNext()) {
count += iterator.current.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];
int charOffset = accessors[i + 1];
int length = accessors[i + 2];
addProblem(message, charOffset, length, accessor);
}
accessProblem = message;
}
}
@override
SourceLibraryBuilder get origin => actualOrigin ?? this;
Uri get importUri => library.importUri;
void addSyntheticDeclarationOfDynamic() {
addBuilder("dynamic",
new DynamicTypeDeclarationBuilder(const DynamicType(), this, -1), -1);
}
void addSyntheticDeclarationOfNever() {
addBuilder(
"Never",
new NeverTypeDeclarationBuilder(
const NeverType.nonNullable(), this, -1),
-1);
}
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) {
return addType(
new NamedTypeBuilder(
name, nullabilityBuilder, arguments, fileUri, charOffset),
charOffset);
}
TypeBuilder addMixinApplication(
TypeBuilder supertype, List<TypeBuilder> mixins, int charOffset) {
return addType(
new MixinApplicationBuilder(supertype, mixins, fileUri, charOffset),
charOffset);
}
TypeBuilder addVoidType(int charOffset) {
// 'void' is always nullable.
return addNamedType(
"void", const NullabilityBuilder.nullable(), null, charOffset)
..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) {
_addClass(
TypeParameterScopeKind.classDeclaration,
metadata,
modifiers,
className,
typeVariables,
supertype,
interfaces,
startOffset,
nameOffset,
endOffset,
supertypeOffset);
}
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);
}
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) {
_checkBadFunctionDeclUse(className, kind, nameOffset);
_checkBadFunctionParameter(typeVariables);
// Nested declaration began in `OutlineBuilder.beginClassDeclaration`.
TypeParameterScopeBuilder declaration =
endNestedDeclaration(kind, className)
..resolveTypes(typeVariables, this);
assert(declaration.parent == libraryDeclaration);
Map<String, MemberBuilder> 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;
}
Class referencesFromClass;
if (referencesFrom != null) {
referencesFromClass = referencesFromIndexed.lookupClass(className);
assert(referencesFromClass == null ||
_currentClassReferencesFromIndexed != null);
}
ClassBuilder classBuilder = new SourceClassBuilder(
metadata,
modifiers,
className,
typeVariables,
applyMixins(supertype, startOffset, nameOffset, endOffset, className,
isMixinDeclaration,
typeVariables: typeVariables),
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,
referencesFromClass,
_currentClassReferencesFromIndexed,
isMixinDeclaration: isMixinDeclaration);
constructorReferences.clear();
Map<String, TypeVariableBuilder> typeVariablesByName =
checkTypeVariables(typeVariables, classBuilder);
void setParent(String name, MemberBuilder member) {
while (member != null) {
member.parent = classBuilder;
member = member.next;
}
}
void setParentAndCheckConflicts(String name, MemberBuilder 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);
}
members.forEach(setParentAndCheckConflicts);
constructors.forEach(setParentAndCheckConflicts);
setters.forEach(setParentAndCheckConflicts);
addBuilder(className, classBuilder, nameOffset,
getterReference: referencesFromClass?.reference);
}
Map<String, TypeVariableBuilder> checkTypeVariables(
List<TypeVariableBuilder> typeVariables, Builder owner) {
if (typeVariables?.isEmpty ?? true) 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,
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)
..resolveTypes(typeVariables, this);
assert(declaration.parent == libraryDeclaration);
Map<String, MemberBuilder> 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,
classScope,
this,
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;
}
}
void setParentAndCheckConflicts(String name, MemberBuilder 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);
}
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,
List<TypeBuilder> interfaces}) {
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<TypeBuilder> newTypes = <TypeBuilder>[];
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 (TypeBuilder newType in newTypes) {
currentTypeParameterScopeBuilder
.addType(new UnresolvedType(newType, -1, null));
}
TypeParameterScopeBuilder mixinDeclaration = this
.endNestedDeclaration(
TypeParameterScopeKind.unnamedMixinApplication,
"mixin application");
mixinDeclaration.resolveTypes(applicationTypeVariables, this);
applicationTypeArguments = <TypeBuilder>[];
for (TypeVariableBuilder typeVariable in typeVariables) {
applicationTypeArguments.add(addNamedType(typeVariable.name,
const NullabilityBuilder.omitted(), null, charOffset)
..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) == null
? startCharOffset
: metadata.first.charOffset;
Class referencesFromClass;
IndexedClass referencesFromIndexedClass;
if (referencesFrom != null) {
referencesFromClass = referencesFromIndexed.lookupClass(fullname);
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,
referencesFromClass,
referencesFromIndexedClass,
mixedInTypeBuilder: isMixinDeclaration ? null : mixin,
);
// 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: referencesFromClass?.reference);
supertype = addNamedType(fullname, const NullabilityBuilder.omitted(),
applicationTypeArguments, charOffset);
}
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) {
// Nested declaration began in `OutlineBuilder.beginNamedMixinApplication`.
endNestedDeclaration(TypeParameterScopeKind.namedMixinApplication, name)
.resolveTypes(typeVariables, this);
NamedTypeBuilder supertype = applyMixins(mixinApplication, startCharOffset,
charOffset, charEndOffset, name, false,
metadata: metadata,
name: name,
typeVariables: typeVariables,
modifiers: modifiers,
interfaces: interfaces);
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;
}
final bool fieldIsLateWithLowering = (modifiers & lateMask) != 0 &&
loader.target.backendTarget.isLateFieldLoweringEnabled(
hasInitializer: hasInitializer,
isFinal: (modifiers & finalMask) != 0,
isStatic: isTopLevel || (modifiers & staticMask) != 0);
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 fieldGetterReference;
Reference fieldSetterReference;
Reference lateIsSetGetterReference;
Reference lateIsSetSetterReference;
Reference lateGetterReference;
Reference lateSetterReference;
FieldNameScheme fieldNameScheme = new FieldNameScheme(
isInstanceMember: isInstanceMember,
className: className,
isExtensionMember: isExtensionMember,
extensionName: extensionName,
libraryReference: referencesFrom?.reference ?? library.reference);
if (referencesFrom != null) {
IndexedContainer indexedContainer =
_currentClassReferencesFromIndexed ?? referencesFromIndexed;
Name nameToLookupName = fieldNameScheme.getName(FieldNameType.Field, name,
isSynthesized: fieldIsLateWithLowering);
fieldGetterReference =
indexedContainer.lookupGetterReference(nameToLookupName);
fieldSetterReference =
indexedContainer.lookupSetterReference(nameToLookupName);
if (fieldIsLateWithLowering) {
Name lateIsSetNameName = fieldNameScheme.getName(
FieldNameType.IsSetField, name,
isSynthesized: fieldIsLateWithLowering);
lateIsSetGetterReference =
indexedContainer.lookupGetterReference(lateIsSetNameName);
lateIsSetSetterReference =
indexedContainer.lookupSetterReference(lateIsSetNameName);
lateGetterReference = indexedContainer.lookupGetterReference(
fieldNameScheme.getName(FieldNameType.Getter, name,
isSynthesized: fieldIsLateWithLowering));
lateSetterReference = indexedContainer.lookupSetterReference(
fieldNameScheme.getName(FieldNameType.Setter, name,
isSynthesized: fieldIsLateWithLowering));
}
}
SourceFieldBuilder fieldBuilder = new SourceFieldBuilder(
metadata,
type,
name,
modifiers,
isTopLevel,
this,
charOffset,
charEndOffset,
fieldNameScheme,
isInstanceMember: isInstanceMember,
fieldGetterReference: fieldGetterReference,
fieldSetterReference: fieldSetterReference,
lateIsSetGetterReference: lateIsSetGetterReference,
lateIsSetSetterReference: lateIsSetSetterReference,
lateGetterReference: lateGetterReference,
lateSetterReference: lateSetterReference);
fieldBuilder.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}) {
Member referenceFrom;
if (_currentClassReferencesFromIndexed != null) {
referenceFrom = _currentClassReferencesFromIndexed.lookupConstructor(
new Name(
constructorName, _currentClassReferencesFromIndexed.library));
}
ConstructorBuilder constructorBuilder = new ConstructorBuilderImpl(
metadata,
modifiers & ~abstractMask,
returnType,
constructorName,
typeVariables,
formals,
this,
startCharOffset,
charOffset,
charOpenParenOffset,
charEndOffset,
referenceFrom,
nativeMethodName);
checkTypeVariables(typeVariables, constructorBuilder);
addBuilder(constructorName, constructorBuilder, charOffset,
getterReference: referenceFrom?.reference);
if (nativeMethodName != null) {
addNativeMethod(constructorBuilder);
}
if (constructorBuilder.isConst) {
currentTypeParameterScopeBuilder?.declaresConstConstructor = true;
// const constructors will have their initializers compiled and written
// into the outline.
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,
{bool isInstanceMember,
bool isExtensionMember}) {
assert(isInstanceMember != null);
assert(isExtensionMember != null);
assert(!isExtensionMember ||
currentTypeParameterScopeBuilder.kind ==
TypeParameterScopeKind.extensionDeclaration);
String extensionName =
isExtensionMember ? currentTypeParameterScopeBuilder.name : null;
ProcedureNameScheme procedureNameScheme = new ProcedureNameScheme(
isExtensionMember: isExtensionMember,
extensionName: extensionName,
isStatic: !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 = procedureNameScheme.getName(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(
procedureNameScheme.getName(ProcedureKind.Getter, name));
}
}
}
ProcedureBuilder procedureBuilder = new SourceProcedureBuilder(
metadata,
modifiers,
returnType,
name,
typeVariables,
formals,
kind,
this,
startCharOffset,
charOffset,
charOpenParenOffset,
charEndOffset,
procedureReference,
tearOffReference,
asyncModifier,
procedureNameScheme,
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);
// 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;
}
ProcedureNameScheme procedureNameScheme = new ProcedureNameScheme(
isExtensionMember: false,
extensionName: null,
isStatic: true,
libraryReference: referencesFrom != null
? (_currentClassReferencesFromIndexed ?? referencesFromIndexed)
.library
.reference
: library.reference);
Reference reference = _currentClassReferencesFromIndexed
?.lookupConstructor(
new Name(procedureName, _currentClassReferencesFromIndexed.library))
?.reference;
ProcedureBuilder procedureBuilder;
if (redirectionTarget != null) {
procedureBuilder = new RedirectingFactoryBuilder(
metadata,
staticMask | modifiers,
returnType,
procedureName,
copyTypeVariables(
currentTypeParameterScopeBuilder.typeVariables ??
const <TypeVariableBuilder>[],
factoryDeclaration),
formals,
this,
startCharOffset,
charOffset,
charOpenParenOffset,
charEndOffset,
reference,
procedureNameScheme,
nativeMethodName,
redirectionTarget);
} else {
procedureBuilder = new SourceProcedureBuilder(
metadata,
staticMask | modifiers,
returnType,
procedureName,
copyTypeVariables(
currentTypeParameterScopeBuilder.typeVariables ??
const <TypeVariableBuilder>[],
factoryDeclaration),
formals,
ProcedureKind.Factory,
this,
startCharOffset,
charOffset,
charOpenParenOffset,
charEndOffset,
reference,
null,
asyncModifier,
procedureNameScheme,
isExtensionMember: false,
isInstanceMember: false,
nativeMethodName: nativeMethodName);
}
TypeParameterScopeBuilder savedDeclaration =
currentTypeParameterScopeBuilder;
currentTypeParameterScopeBuilder = factoryDeclaration;
for (TypeVariableBuilder tv in procedureBuilder.typeVariables) {
NamedTypeBuilder t = procedureBuilder.returnType;
t.arguments.add(addNamedType(tv.name, const NullabilityBuilder.omitted(),
null, procedureBuilder.charOffset));
}
currentTypeParameterScopeBuilder = savedDeclaration;
factoryDeclaration.resolveTypes(procedureBuilder.typeVariables, this);
addBuilder(procedureName, procedureBuilder, charOffset,
getterReference: reference);
if (nativeMethodName != null) {
addNativeMethod(procedureBuilder);
}
}
void addEnum(
List<MetadataBuilder> metadata,
String name,
List<EnumConstantInfo> enumConstantInfos,
int startCharOffset,
int charOffset,
int charEndOffset) {
Class referencesFromClass;
IndexedClass referencesFromIndexedClass;
if (referencesFrom != null) {
referencesFromClass = referencesFromIndexed.lookupClass(name);
referencesFromIndexedClass =
referencesFromIndexed.lookupIndexedClass(name);
}
EnumBuilder builder = new EnumBuilder(
metadata,
name,
enumConstantInfos,
this,
startCharOffset,
charOffset,
charEndOffset,
referencesFromClass,
referencesFromIndexedClass);
addBuilder(name, builder, charOffset,
getterReference: referencesFromClass?.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")
.resolveTypes(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")
.resolveTypes(typeVariables, this);
return addType(builder, charOffset);
}
FormalParameterBuilder addFormalParameter(
List<MetadataBuilder> metadata,
int modifiers,
TypeBuilder type,
String name,
bool hasThis,
int charOffset,
Token initializerToken) {
if (hasThis) {
modifiers |= initializingFormalMask;
}
FormalParameterBuilder formal = new FormalParameterBuilder(
metadata, modifiers, type, name, this, charOffset,
fileUri: fileUri)
..initializerToken = initializerToken
..hasDeclaredInitializer = (initializerToken != null);
return formal;
}
TypeVariableBuilder addTypeVariable(List<MetadataBuilder> metadata,
String name, TypeBuilder bound, int charOffset, Uri fileUri) {
TypeVariableBuilder builder = new TypeVariableBuilder(
name, this, charOffset, fileUri,
bound: bound, metadata: metadata);
boundlessTypeVariables.add(builder);
return builder;
}
@override
void buildOutlineExpressions() {
MetadataBuilder.buildAnnotations(
library, metadata, this, null, null, fileUri);
}
/// Builds the core AST structures for [declaration] needed for the outline.
void buildBuilder(Builder declaration, LibraryBuilder coreLibrary) {
String findDuplicateSuffix(Builder declaration) {
if (declaration.next != null) {
int count = 0;
Builder current = declaration.next;
while (current != null) {
count++;
current = current.next;
}
return "#$count";
}
return "";
}
if (declaration is SourceClassBuilder) {
Class cls = declaration.build(this, coreLibrary);
if (!declaration.isPatch) {
cls.name += findDuplicateSuffix(declaration);
library.addClass(cls);
}
} else if (declaration is SourceExtensionBuilder) {
Extension extension = declaration.build(this, coreLibrary,
addMembersToLibrary: !declaration.isDuplicate);
if (!declaration.isPatch && !declaration.isDuplicate) {
library.addExtension(extension);
}
} else if (declaration is MemberBuilderImpl) {
declaration.buildMembers(this,
(Member member, BuiltMemberKind memberKind) {
if (member is Field) {
member.isStatic = true;
if (!declaration.isPatch && !declaration.isDuplicate) {
library.addField(member);
}
} else if (member is Procedure) {
member.isStatic = true;
if (!declaration.isPatch &&
!declaration.isDuplicate &&
!declaration.isConflictingSetter) {
library.addProcedure(member);
}
} else {
unhandled("${member.runtimeType}:${memberKind}", "buildBuilder",
declaration.charOffset, declaration.fileUri);
}
});
} else if (declaration is SourceTypeAliasBuilder) {
Typedef typedef = declaration.build(this);
if (!declaration.isPatch && !declaration.isDuplicate) {
library.addTypedef(typedef);
}
} else if (declaration is EnumBuilder) {
Class cls = declaration.build(this, coreLibrary);
if (!declaration.isPatch) {
cls.name += findDuplicateSuffix(declaration);
library.addClass(cls);
}
} else if (declaration is PrefixBuilder) {
// Ignored. Kernel doesn't represent prefixes.
return;
} else if (declaration is BuiltinTypeDeclarationBuilder) {
// Nothing needed.
return;
} else {
unhandled("${declaration.runtimeType}", "buildBuilder",
declaration.charOffset, declaration.fileUri);
}
}
void addNativeDependency(String nativeImportPath) {
MemberBuilder constructor = loader.getNativeAnnotation();
Arguments arguments =
new Arguments(<Expression>[new StringLiteral(nativeImportPath)]);
Expression annotation;
if (constructor.isConstructor) {
annotation = new ConstructorInvocation(constructor.member, arguments)
..isConst = true;
} else {
annotation = new StaticInvocation(constructor.member, arguments)
..isConst = true;
}
library.addAnnotation(annotation);
}
void addDependencies(Library library, Set<SourceLibraryBuilder> seen) {
if (!seen.add(this)) {
return;
}
// Merge import and export lists to have the dependencies in source order.
// This is required for the DietListener to correctly match up metadata.
int importIndex = 0;
int exportIndex = 0;
while (importIndex < imports.length || exportIndex < exports.length) {
if (exportIndex >= exports.length ||
(importIndex < imports.length &&
imports[importIndex].charOffset <
exports[exportIndex].charOffset)) {
// Add import
Import import = imports[importIndex++];
// Rather than add a LibraryDependency, we attach an annotation.
if (import.nativeImportPath != null) {
addNativeDependency(import.nativeImportPath);
continue;
}
if (import.deferred && import.prefixBuilder?.dependency != null) {
library.addDependency(import.prefixBuilder.dependency);
} else {
library.addDependency(new LibraryDependency.import(
import.imported.library,
name: import.prefix,
combinators: toKernelCombinators(import.combinators))
..fileOffset = import.charOffset);
}
} else {
// Add export
Export export = exports[exportIndex++];
library.addDependency(new LibraryDependency.export(
export.exported.library,
combinators: toKernelCombinators(export.combinators))
..fileOffset = export.charOffset);
}
}
for (LibraryBuilder part in parts) {
if (part is SourceLibraryBuilder) {
part.addDependencies(library, seen);
}
}
}
@override
Builder computeAmbiguousDeclaration(
String name, Builder declaration, Builder other, int charOffset,
{bool isExport: false, bool isImport: false}) {
// TODO(ahe): Can I move this to Scope or Prefix?
if (declaration == other) return declaration;
if (declaration is InvalidTypeDeclarationBuilder) return declaration;
if (other is InvalidTypeDeclarationBuilder) return other;
if (declaration is AccessErrorBuilder) {
AccessErrorBuilder error = declaration;
declaration = error.builder;
}
if (other is AccessErrorBuilder) {
AccessErrorBuilder error = other;
other = error.builder;
}
Builder preferred;
Uri uri;
Uri otherUri;
if (scope.lookupLocalMember(name, setter: false) == declaration) {
preferred = declaration;
} else {
uri = computeLibraryUri(declaration);
otherUri = computeLibraryUri(other);
if (declaration is LoadLibraryBuilder) {
preferred = declaration;
} else if (other is LoadLibraryBuilder) {
preferred = other;
} else if (otherUri?.scheme == "dart" && uri?.scheme != "dart") {
preferred = declaration;
} else if (uri?.scheme == "dart" && otherUri?.scheme != "dart") {
preferred = other;
}
}
if (preferred != null) {
return preferred;
}
if (declaration.next == null && other.next == null) {
if (isImport && declaration is PrefixBuilder && other is PrefixBuilder) {
// Handles the case where the same prefix is used for different
// imports.
return declaration
..exportScope.merge(other.exportScope,
(String name, Builder existing, Builder member) {
return computeAmbiguousDeclaration(
name, existing, member, charOffset,
isExport: isExport, isImport: isImport);
});
}
}
if (isExport) {
Template<Message Function(String name, Uri uri, Uri uri2)> template =
templateDuplicatedExport;
Message message = template.withArguments(name, uri, otherUri);
addProblem(message, charOffset, noLength, fileUri);
}
Template<Message Function(String name, Uri uri, Uri uri2)> builderTemplate =
isExport
? templateDuplicatedExportInType
: templateDuplicatedImportInType;
Message message = builderTemplate.withArguments(
name,
// TODO(ahe): We should probably use a context object here
// instead of including URIs in this message.
uri,
otherUri);
// We report the error lazily (setting suppressMessage to false) because the
// spec 18.1 states that 'It is not an error if N is introduced by two or
// more imports but never referred to.'
return new InvalidTypeDeclarationBuilder(
name, message.withLocation(fileUri, charOffset, name.length),
suppressMessage: false);
}
int finishDeferredLoadTearoffs() {
int total = 0;
for (Import import in imports) {
if (import.deferred) {
Procedure tearoff = import.prefixBuilder.loadLibraryBuilder.tearoff;
if (tearoff != null) library.addProcedure(tearoff);
total++;
}
}
return total;
}
int finishForwarders() {
int count = 0;
CloneVisitorNotMembers cloner = new CloneVisitorNotMembers();
for (int i = 0; i < forwardersOrigins.length; i += 2) {
Procedure forwarder = forwardersOrigins[i];
Procedure origin = forwardersOrigins[i + 1];
int positionalCount = origin.function.positionalParameters.length;
if (forwarder.function.positionalParameters.length != positionalCount) {
return unexpected(
"$positionalCount",
"${forwarder.function.positionalParameters.length}",
origin.fileOffset,
origin.fileUri);
}
for (int j = 0; j < positionalCount; ++j) {
VariableDeclaration forwarderParameter =
forwarder.function.positionalParameters[j];
VariableDeclaration originParameter =
origin.function.positionalParameters[j];
if (originParameter.initializer != null) {
forwarderParameter.initializer =
cloner.clone(originParameter.initializer);
forwarderParameter.initializer.parent = forwarderParameter;
}
}
Map<String, VariableDeclaration> originNamedMap =
<String, VariableDeclaration>{};
for (VariableDeclaration originNamed in origin.function.namedParameters) {
originNamedMap[originNamed.name] = originNamed;
}
for (VariableDeclaration forwarderNamed
in forwarder.function.namedParameters) {
VariableDeclaration originNamed = originNamedMap[forwarderNamed.name];
if (originNamed == null) {
return unhandled(
"null", forwarder.name.text, origin.fileOffset, origin.fileUri);
}
if (originNamed.initializer == null) continue;
forwarderNamed.initializer = cloner.clone(originNamed.initializer);
forwarderNamed.initializer.parent = forwarderNamed;
}
++count;
}
forwardersOrigins.clear();
return count;
}
void addNativeMethod(FunctionBuilder method) {
nativeMethods.add(method);
}
int finishNativeMethods() {
for (FunctionBuilder method in nativeMethods) {
method.becomeNative(loader);
}
return nativeMethods.length;
}
/// Creates a copy of [original] into the scope of [declaration].
///
/// This is used for adding copies of class type parameters to factory
/// methods and unnamed mixin applications, and for adding copies of
/// extension type parameters to extension instance methods.
///
/// If [synthesizeTypeParameterNames] is `true` the names of the
/// [TypeParameter] are prefix with '#' to indicate that their synthesized.
List<TypeVariableBuilder>