blob: ea3cf9b7c4c007f133f9aaaf0e3df54ce928d53e [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_loader;
import 'dart:convert' show utf8;
import 'dart:typed_data' show Uint8List;
import 'package:_fe_analyzer_shared/src/parser/class_member_parser.dart'
show ClassMemberParser;
import 'package:_fe_analyzer_shared/src/parser/parser.dart'
show Parser, lengthForToken;
import 'package:_fe_analyzer_shared/src/scanner/scanner.dart'
show
ErrorToken,
LanguageVersionToken,
Scanner,
ScannerConfiguration,
ScannerResult,
Token,
scan;
import 'package:kernel/ast.dart'
show
Arguments,
AsyncMarker,
Class,
Component,
Constructor,
DartType,
Expression,
FunctionNode,
InterfaceType,
Library,
LibraryDependency,
Member,
NeverType,
Nullability,
Procedure,
ProcedureKind,
Reference,
Supertype,
TreeNode,
Version;
import 'package:kernel/class_hierarchy.dart'
show ClassHierarchy, HandleAmbiguousSupertypes;
import 'package:kernel/core_types.dart' show CoreTypes;
import 'package:kernel/reference_from_index.dart' show ReferenceFromIndex;
import 'package:kernel/type_environment.dart';
import 'package:package_config/package_config.dart';
import '../../api_prototype/experimental_flags.dart';
import '../../api_prototype/file_system.dart';
import '../../base/common.dart';
import '../../base/instrumentation.dart' show Instrumentation;
import '../../base/nnbd_mode.dart';
import '../denylisted_classes.dart'
show denylistedCoreClasses, denylistedTypedDataClasses;
import '../builder/builder.dart';
import '../builder/class_builder.dart';
import '../builder/constructor_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/invalid_type_declaration_builder.dart';
import '../builder/library_builder.dart';
import '../builder/member_builder.dart';
import '../builder/named_type_builder.dart';
import '../builder/never_type_declaration_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 '../export.dart' show Export;
import '../fasta_codes.dart';
import '../kernel/body_builder.dart' show BodyBuilder;
import '../kernel/kernel_builder.dart'
show ClassHierarchyBuilder, ClassMember, DelayedCheck;
import '../kernel/kernel_helper.dart'
show SynthesizedFunctionNode, TypeDependency;
import '../kernel/kernel_target.dart' show KernelTarget;
import '../kernel/transform_collections.dart' show CollectionTransformer;
import '../kernel/transform_set_literals.dart' show SetLiteralTransformer;
import '../kernel/type_builder_computer.dart' show TypeBuilderComputer;
import '../loader.dart' show Loader, untranslatableUriScheme;
import '../problems.dart' show internalProblem;
import '../source/stack_listener_impl.dart' show offsetForToken;
import '../type_inference/type_inference_engine.dart';
import '../type_inference/type_inferrer.dart';
import '../util/helpers.dart';
import 'diet_listener.dart' show DietListener;
import 'diet_parser.dart' show DietParser, useImplicitCreationExpressionInCfe;
import 'outline_builder.dart' show OutlineBuilder;
import 'source_class_builder.dart' show SourceClassBuilder;
import 'source_library_builder.dart' show SourceLibraryBuilder;
import 'source_type_alias_builder.dart';
class SourceLoader extends Loader {
/// The [FileSystem] which should be used to access files.
final FileSystem fileSystem;
/// Whether comments should be scanned and parsed.
final bool includeComments;
final Map<Uri, List<int>> sourceBytes = <Uri, List<int>>{};
ClassHierarchyBuilder? _builderHierarchy;
ReferenceFromIndex? referenceFromIndex;
/// Used when building directly to kernel.
ClassHierarchy? _hierarchy;
CoreTypes? _coreTypes;
TypeEnvironment? _typeEnvironment;
/// For builders created with a reference, this maps from that reference to
/// that builder. This is used for looking up source builders when finalizing
/// exports in dill builders.
Map<Reference, Builder> buildersCreatedWithReferences = {};
/// Used when checking whether a return type of an async function is valid.
///
/// The said return type is valid if it's a subtype of [futureOfBottom].
DartType? _futureOfBottom;
DartType get futureOfBottom => _futureOfBottom!;
/// Used when checking whether a return type of a sync* function is valid.
///
/// The said return type is valid if it's a subtype of [iterableOfBottom].
DartType? _iterableOfBottom;
DartType get iterableOfBottom => _iterableOfBottom!;
/// Used when checking whether a return type of an async* function is valid.
///
/// The said return type is valid if it's a subtype of [streamOfBottom].
DartType? _streamOfBottom;
DartType get streamOfBottom => _streamOfBottom!;
TypeInferenceEngineImpl? _typeInferenceEngine;
Instrumentation? instrumentation;
CollectionTransformer? collectionTransformer;
SetLiteralTransformer? setLiteralTransformer;
final SourceLoaderDataForTesting? dataForTesting;
SourceLoader(this.fileSystem, this.includeComments, KernelTarget target)
: dataForTesting =
retainDataForTesting ? new SourceLoaderDataForTesting() : null,
super(target);
NnbdMode get nnbdMode => target.context.options.nnbdMode;
CoreTypes get coreTypes {
assert(_coreTypes != null, "CoreTypes has not been computed.");
return _coreTypes!;
}
ClassHierarchy get hierarchy => _hierarchy!;
void set hierarchy(ClassHierarchy? value) {
if (_hierarchy != value) {
_hierarchy = value;
_typeEnvironment = null;
}
}
TypeEnvironment get typeEnvironment {
return _typeEnvironment ??= new TypeEnvironment(coreTypes, hierarchy);
}
TypeInferenceEngineImpl get typeInferenceEngine => _typeInferenceEngine!;
ClassHierarchyBuilder get builderHierarchy => _builderHierarchy!;
@override
Template<SummaryTemplate> get outlineSummaryTemplate =>
templateSourceOutlineSummary;
@override
bool get isSourceLoader => true;
Future<Token> tokenize(SourceLibraryBuilder library,
{bool suppressLexicalErrors: false}) async {
Uri fileUri = library.fileUri;
// Lookup the file URI in the cache.
List<int>? bytes = sourceBytes[fileUri];
if (bytes == null) {
// Error recovery.
if (fileUri.scheme == untranslatableUriScheme) {
Message message =
templateUntranslatableUri.withArguments(library.importUri);
library.addProblemAtAccessors(message);
bytes = synthesizeSourceForMissingFile(library.importUri, null);
} else if (!fileUri.hasScheme) {
return internalProblem(
templateInternalProblemUriMissingScheme.withArguments(fileUri),
-1,
library.importUri);
} else if (fileUri.scheme == SourceLibraryBuilder.MALFORMED_URI_SCHEME) {
library.addProblemAtAccessors(messageExpectedUri);
bytes = synthesizeSourceForMissingFile(library.importUri, null);
}
if (bytes != null) {
Uint8List zeroTerminatedBytes = new Uint8List(bytes.length + 1);
zeroTerminatedBytes.setRange(0, bytes.length, bytes);
bytes = zeroTerminatedBytes;
sourceBytes[fileUri] = bytes;
}
}
if (bytes == null) {
// If it isn't found in the cache, read the file read from the file
// system.
List<int> rawBytes;
try {
rawBytes = await fileSystem.entityForUri(fileUri).readAsBytes();
} on FileSystemException catch (e) {
Message message =
templateCantReadFile.withArguments(fileUri, e.message);
library.addProblemAtAccessors(message);
rawBytes = synthesizeSourceForMissingFile(library.importUri, message);
}
Uint8List zeroTerminatedBytes = new Uint8List(rawBytes.length + 1);
zeroTerminatedBytes.setRange(0, rawBytes.length, rawBytes);
bytes = zeroTerminatedBytes;
sourceBytes[fileUri] = bytes;
byteCount += rawBytes.length;
}
ScannerResult result = scan(bytes,
includeComments: includeComments,
configuration: new ScannerConfiguration(
enableTripleShift: target.isExperimentEnabledInLibraryByVersion(
ExperimentalFlag.tripleShift,
library.importUri,
library.packageLanguageVersion.version),
enableExtensionMethods:
target.isExperimentEnabledInLibraryByVersion(
ExperimentalFlag.extensionMethods,
library.importUri,
library.packageLanguageVersion.version),
enableNonNullable: target.isExperimentEnabledInLibraryByVersion(
ExperimentalFlag.nonNullable,
library.importUri,
library.packageLanguageVersion.version)),
languageVersionChanged:
(Scanner scanner, LanguageVersionToken version) {
if (!suppressLexicalErrors) {
library.registerExplicitLanguageVersion(
new Version(version.major, version.minor),
offset: version.offset,
length: version.length);
}
scanner.configuration = new ScannerConfiguration(
enableTripleShift: library.enableTripleShiftInLibrary,
enableExtensionMethods: library.enableExtensionMethodsInLibrary,
enableNonNullable: library.isNonNullableByDefault);
});
Token token = result.tokens;
if (!suppressLexicalErrors) {
List<int> source = getSource(bytes);
Uri importUri = library.importUri;
if (library.isPatch) {
// For patch files we create a "fake" import uri.
// We cannot use the import uri from the patched library because
// several different files would then have the same import uri,
// and the VM does not support that. Also, what would, for instance,
// setting a breakpoint on line 42 of some import uri mean, if the uri
// represented several files?
List<String> newPathSegments =
new List<String>.from(importUri.pathSegments);
newPathSegments.add(library.fileUri.pathSegments.last);
newPathSegments[0] = "${newPathSegments[0]}-patch";
importUri = importUri.replace(pathSegments: newPathSegments);
}
target.addSourceInformation(
importUri, library.fileUri, result.lineStarts, source);
}
library.issuePostponedProblems();
library.markLanguageVersionFinal();
while (token is ErrorToken) {
if (!suppressLexicalErrors) {
ErrorToken error = token;
library.addProblem(error.assertionMessage, offsetForToken(token),
lengthForToken(token), fileUri);
}
token = token.next!;
}
return token;
}
List<int> synthesizeSourceForMissingFile(Uri uri, Message? message) {
switch ("$uri") {
case "dart:core":
return utf8.encode(defaultDartCoreSource);
case "dart:async":
return utf8.encode(defaultDartAsyncSource);
case "dart:collection":
return utf8.encode(defaultDartCollectionSource);
case "dart:_internal":
return utf8.encode(defaultDartInternalSource);
case "dart:typed_data":
return utf8.encode(defaultDartTypedDataSource);
default:
return utf8.encode(message == null ? "" : "/* ${message.message} */");
}
}
Set<LibraryBuilder>? _strongOptOutLibraries;
void registerStrongOptOutLibrary(LibraryBuilder libraryBuilder) {
_strongOptOutLibraries ??= {};
_strongOptOutLibraries!.add(libraryBuilder);
hasInvalidNnbdModeLibrary = true;
}
bool hasInvalidNnbdModeLibrary = false;
Map<LibraryBuilder, Message>? _nnbdMismatchLibraries;
void registerNnbdMismatchLibrary(
LibraryBuilder libraryBuilder, Message message) {
_nnbdMismatchLibraries ??= {};
_nnbdMismatchLibraries![libraryBuilder] = message;
hasInvalidNnbdModeLibrary = true;
}
void registerConstructorToBeInferred(
Constructor constructor, ConstructorBuilder builder) {
_typeInferenceEngine!.toBeInferred[constructor] = builder;
}
void registerTypeDependency(Member member, TypeDependency typeDependency) {
_typeInferenceEngine!.typeDependencies[member] = typeDependency;
}
@override
Future<Null> buildOutlines() async {
await super.buildOutlines();
if (_strongOptOutLibraries != null) {
// We have libraries that are opted out in strong mode "non-explicitly",
// that is, either implicitly through the package version or loaded from
// .dill as opt out.
//
// To reduce the verbosity of the error messages we try to reduce the
// message to only include the package name once for packages that are
// opted out.
//
// We use the current package config to retrieve the package based
// language version to determine whether the package as a whole is opted
// out. If so, we only include the package name and not the library uri
// in the message. For package libraries with no corresponding package
// config we include each library uri in the message. For non-package
// libraries with no corresponding package config we generate a message
// per library.
giveCombinedErrorForNonStrongLibraries(_strongOptOutLibraries!,
emitNonPackageErrors: true);
_strongOptOutLibraries = null;
}
if (_nnbdMismatchLibraries != null) {
for (MapEntry<LibraryBuilder, Message> entry
in _nnbdMismatchLibraries!.entries) {
addProblem(entry.value, -1, noLength, entry.key.fileUri);
}
_nnbdMismatchLibraries = null;
}
}
FormattedMessage? giveCombinedErrorForNonStrongLibraries(
Set<LibraryBuilder> libraries,
{required bool emitNonPackageErrors}) {
Map<String?, List<LibraryBuilder>> libraryByPackage = {};
Map<Package, Version> enableNonNullableVersionByPackage = {};
for (LibraryBuilder libraryBuilder in libraries) {
final Package? package =
target.uriTranslator.getPackage(libraryBuilder.importUri);
if (package != null &&
package.languageVersion != null &&
package.languageVersion is! InvalidLanguageVersion) {
Version enableNonNullableVersion =
enableNonNullableVersionByPackage[package] ??=
target.getExperimentEnabledVersionInLibrary(
ExperimentalFlag.nonNullable,
new Uri(scheme: 'package', path: package.name));
Version version = new Version(
package.languageVersion!.major, package.languageVersion!.minor);
if (version < enableNonNullableVersion) {
(libraryByPackage[package.name] ??= []).add(libraryBuilder);
continue;
}
}
if (libraryBuilder.importUri.scheme == 'package') {
(libraryByPackage[null] ??= []).add(libraryBuilder);
} else {
if (emitNonPackageErrors) {
// Emit a message that doesn't mention running 'pub'.
addProblem(messageStrongModeNNBDButOptOut, -1, noLength,
libraryBuilder.fileUri);
}
}
}
if (libraryByPackage.isNotEmpty) {
List<Uri> involvedFiles = [];
List<String> dependencies = [];
libraryByPackage.forEach((String? name, List<LibraryBuilder> libraries) {
if (name != null) {
dependencies.add('package:$name');
for (LibraryBuilder libraryBuilder in libraries) {
involvedFiles.add(libraryBuilder.fileUri);
}
} else {
for (LibraryBuilder libraryBuilder in libraries) {
dependencies.add(libraryBuilder.importUri.toString());
involvedFiles.add(libraryBuilder.fileUri);
}
}
});
// Emit a message that suggests to run 'pub' to check for opted in
// versions of the packages.
return addProblem(
templateStrongModeNNBDPackageOptOut.withArguments(dependencies),
-1,
-1,
null,
involvedFiles: involvedFiles);
}
return null;
}
List<int> getSource(List<int> bytes) {
// bytes is 0-terminated. We don't want that included.
if (bytes is Uint8List) {
return new Uint8List.view(
bytes.buffer, bytes.offsetInBytes, bytes.length - 1);
}
return bytes.sublist(0, bytes.length - 1);
}
@override
Future<Null> buildOutline(SourceLibraryBuilder library) async {
Token tokens = await tokenize(library);
// ignore: unnecessary_null_comparison
if (tokens == null) return;
OutlineBuilder listener = new OutlineBuilder(library);
new ClassMemberParser(listener).parseUnit(tokens);
}
@override
Future<Null> buildBody(LibraryBuilder library) async {
if (library is SourceLibraryBuilder) {
// We tokenize source files twice to keep memory usage low. This is the
// second time, and the first time was in [buildOutline] above. So this
// time we suppress lexical errors.
Token tokens = await tokenize(library, suppressLexicalErrors: true);
// ignore: unnecessary_null_comparison
if (tokens == null) return;
DietListener listener = createDietListener(library);
DietParser parser = new DietParser(listener);
parser.parseUnit(tokens);
for (LibraryBuilder part in library.parts) {
if (part.partOfLibrary != library) {
// Part was included in multiple libraries. Skip it here.
continue;
}
Token tokens = await tokenize(part as SourceLibraryBuilder,
suppressLexicalErrors: true);
// ignore: unnecessary_null_comparison
if (tokens != null) {
listener.uri = part.fileUri;
parser.parseUnit(tokens);
}
}
}
}
// TODO(johnniwinther,jensj): Handle expression in extensions?
Future<Expression> buildExpression(
SourceLibraryBuilder libraryBuilder,
String? enclosingClass,
bool isClassInstanceMember,
FunctionNode parameters) async {
Token token = await tokenize(libraryBuilder, suppressLexicalErrors: false);
DietListener dietListener = createDietListener(libraryBuilder);
Builder parent = libraryBuilder;
if (enclosingClass != null) {
Builder? cls = dietListener.memberScope
.lookup(enclosingClass, -1, libraryBuilder.fileUri);
if (cls is ClassBuilder) {
parent = cls;
dietListener
..currentDeclaration = cls
..memberScope = cls.scope.copyWithParent(
dietListener.memberScope.withTypeVariables(cls.typeVariables),
"debugExpression in $enclosingClass");
}
}
ProcedureBuilder builder = new SourceProcedureBuilder(
null,
0,
null,
"debugExpr",
null,
null,
ProcedureKind.Method,
libraryBuilder,
0,
0,
-1,
-1,
null,
null,
AsyncMarker.Sync,
new ProcedureNameScheme(
isExtensionMember: false,
isStatic: true,
libraryReference: libraryBuilder.library.reference),
isInstanceMember: false,
isExtensionMember: false)
..parent = parent;
BodyBuilder listener = dietListener.createListener(
builder, dietListener.memberScope,
isDeclarationInstanceMember: isClassInstanceMember);
return listener.parseSingleExpression(
new Parser(listener,
useImplicitCreationExpression: useImplicitCreationExpressionInCfe),
token,
parameters);
}
@override
KernelTarget get target => super.target as KernelTarget;
DietListener createDietListener(SourceLibraryBuilder library) {
return new DietListener(library, hierarchy, coreTypes, typeInferenceEngine);
}
void resolveParts() {
List<Uri> parts = <Uri>[];
List<SourceLibraryBuilder> libraries = <SourceLibraryBuilder>[];
builders.forEach((Uri uri, LibraryBuilder library) {
if (library.loader == this) {
if (library.isPart) {
parts.add(uri);
} else {
libraries.add(library as SourceLibraryBuilder);
}
}
});
Set<Uri> usedParts = new Set<Uri>();
for (SourceLibraryBuilder library in libraries) {
library.includeParts(usedParts);
}
for (Uri uri in parts) {
if (usedParts.contains(uri)) {
builders.remove(uri);
} else {
SourceLibraryBuilder part = builders[uri] as SourceLibraryBuilder;
part.addProblem(messagePartOrphan, 0, 1, part.fileUri);
part.validatePart(null, null);
}
}
ticker.logMs("Resolved parts");
for (LibraryBuilder library in builders.values) {
if (library.loader == this) {
library.applyPatches();
}
}
ticker.logMs("Applied patches");
}
void computeLibraryScopes() {
Set<LibraryBuilder> exporters = new Set<LibraryBuilder>();
Set<LibraryBuilder> exportees = new Set<LibraryBuilder>();
for (LibraryBuilder library in builders.values) {
if (library.loader == this) {
SourceLibraryBuilder sourceLibrary = library as SourceLibraryBuilder;
sourceLibrary.buildInitialScopes();
}
if (library.exporters.isNotEmpty) {
exportees.add(library);
for (Export exporter in library.exporters) {
exporters.add(exporter.exporter);
}
}
}
Set<SourceLibraryBuilder> both = new Set<SourceLibraryBuilder>();
for (LibraryBuilder exported in exportees) {
if (exporters.contains(exported)) {
both.add(exported as SourceLibraryBuilder);
}
for (Export export in exported.exporters) {
exported.exportScope.forEach(export.addToExportScope);
}
}
bool wasChanged = false;
do {
wasChanged = false;
for (SourceLibraryBuilder exported in both) {
for (Export export in exported.exporters) {
exported.exportScope.forEach((String name, Builder member) {
if (export.addToExportScope(name, member)) {
wasChanged = true;
}
});
}
}
} while (wasChanged);
for (LibraryBuilder library in builders.values) {
if (library.loader == this) {
SourceLibraryBuilder sourceLibrary = library as SourceLibraryBuilder;
sourceLibrary.addImportsToScope();
}
}
for (LibraryBuilder exportee in exportees) {
// TODO(ahe): Change how we track exporters. Currently, when a library
// (exporter) exports another library (exportee) we add a reference to
// exporter to exportee. This creates a reference in the wrong direction
// and can lead to memory leaks.
exportee.exporters.clear();
}
ticker.logMs("Computed library scopes");
// debugPrintExports();
}
void debugPrintExports() {
// TODO(sigmund): should be `covariant SourceLibraryBuilder`.
builders.forEach((Uri uri, dynamic l) {
SourceLibraryBuilder library = l;
Set<Builder> members = new Set<Builder>();
Iterator<Builder> iterator = library.iterator;
while (iterator.moveNext()) {
members.add(iterator.current);
}
List<String> exports = <String>[];
library.exportScope.forEach((String name, Builder? member) {
while (member != null) {
if (!members.contains(member)) {
exports.add(name);
}
member = member.next;
}
});
if (exports.isNotEmpty) {
print("$uri exports $exports");
}
});
}
void resolveTypes() {
int typeCount = 0;
for (LibraryBuilder library in builders.values) {
if (library.loader == this) {
SourceLibraryBuilder sourceLibrary = library as SourceLibraryBuilder;
typeCount += sourceLibrary.resolveTypes();
}
}
ticker.logMs("Resolved $typeCount types");
}
void finishDeferredLoadTearoffs() {
int count = 0;
for (LibraryBuilder library in builders.values) {
if (library.loader == this) {
count += library.finishDeferredLoadTearoffs();
}
}
ticker.logMs("Finished deferred load tearoffs $count");
}
void finishNoSuchMethodForwarders() {
int count = 0;
for (LibraryBuilder library in builders.values) {
if (library.loader == this) {
count += library.finishForwarders();
}
}
ticker.logMs("Finished forwarders for $count procedures");
}
void resolveConstructors() {
int count = 0;
for (LibraryBuilder library in builders.values) {
if (library.loader == this) {
count += library.resolveConstructors(null);
}
}
ticker.logMs("Resolved $count constructors");
}
void installTypedefTearOffs() {
if (target.backendTarget.isTypedefTearOffLoweringEnabled) {
for (LibraryBuilder library in builders.values) {
if (library.loader == this && library is SourceLibraryBuilder) {
library.installTypedefTearOffs();
}
}
}
}
void finishTypeVariables(ClassBuilder object, TypeBuilder dynamicType) {
int count = 0;
for (LibraryBuilder library in builders.values) {
if (library.loader == this) {
count += library.finishTypeVariables(object, dynamicType);
}
}
ticker.logMs("Resolved $count type-variable bounds");
}
void computeVariances() {
int count = 0;
for (LibraryBuilder library in builders.values) {
if (library.loader == this) {
count += library.computeVariances();
}
}
ticker.logMs("Computed variances of $count type variables");
}
void computeDefaultTypes(TypeBuilder dynamicType, TypeBuilder nullType,
TypeBuilder bottomType, ClassBuilder objectClass) {
int count = 0;
for (LibraryBuilder library in builders.values) {
if (library.loader == this) {
count += library.computeDefaultTypes(
dynamicType, nullType, bottomType, objectClass);
}
}
ticker.logMs("Computed default types for $count type variables");
}
void finishNativeMethods() {
int count = 0;
for (LibraryBuilder library in builders.values) {
if (library.loader == this) {
count += library.finishNativeMethods();
}
}
ticker.logMs("Finished $count native methods");
}
void finishPatchMethods() {
int count = 0;
for (LibraryBuilder library in builders.values) {
if (library.loader == this) {
count += library.finishPatchMethods();
}
}
ticker.logMs("Finished $count patch methods");
}
/// Check that [objectClass] has no supertypes. Recover by removing any
/// found.
void checkObjectClassHierarchy(ClassBuilder objectClass) {
if (objectClass is SourceClassBuilder &&
objectClass.library.loader == this) {
if (objectClass.supertypeBuilder != null) {
objectClass.supertypeBuilder = null;
objectClass.addProblem(
messageObjectExtends, objectClass.charOffset, noLength);
}
if (objectClass.interfaceBuilders != null) {
objectClass.addProblem(
messageObjectImplements, objectClass.charOffset, noLength);
objectClass.interfaceBuilders = null;
}
if (objectClass.mixedInTypeBuilder != null) {
objectClass.addProblem(
messageObjectMixesIn, objectClass.charOffset, noLength);
objectClass.mixedInTypeBuilder = null;
}
}
}
/// Returns a list of all class builders declared in this loader. As the
/// classes are sorted, any cycles in the hierarchy are reported as
/// errors. Recover by breaking the cycles. This means that the rest of the
/// pipeline (including backends) can assume that there are no hierarchy
/// cycles.
List<SourceClassBuilder> handleHierarchyCycles(ClassBuilder objectClass) {
// Compute the initial work list of all classes declared in this loader.
List<SourceClassBuilder> workList = <SourceClassBuilder>[];
for (LibraryBuilder library in builders.values) {
if (library.loader == this) {
Iterator<Builder> members = library.iterator;
while (members.moveNext()) {
Builder member = members.current;
if (member is SourceClassBuilder) {
workList.add(member);
}
}
}
}
Set<ClassBuilder> denyListedClasses = new Set<ClassBuilder>();
for (int i = 0; i < denylistedCoreClasses.length; i++) {
denyListedClasses.add(coreLibrary.lookupLocalMember(
denylistedCoreClasses[i],
required: true) as ClassBuilder);
}
if (typedDataLibrary != null) {
for (int i = 0; i < denylistedTypedDataClasses.length; i++) {
// Allow the member to not exist. If it doesn't, nobody can extend it.
Builder? member = typedDataLibrary!
.lookupLocalMember(denylistedTypedDataClasses[i], required: false);
if (member != null) denyListedClasses.add(member as ClassBuilder);
}
}
// Sort the classes topologically.
Set<SourceClassBuilder> topologicallySortedClasses =
new Set<SourceClassBuilder>();
List<SourceClassBuilder> previousWorkList;
do {
previousWorkList = workList;
workList = <SourceClassBuilder>[];
for (int i = 0; i < previousWorkList.length; i++) {
SourceClassBuilder cls = previousWorkList[i];
Map<TypeDeclarationBuilder?, TypeAliasBuilder?> directSupertypeMap =
cls.computeDirectSupertypes(objectClass);
List<TypeDeclarationBuilder?> directSupertypes =
directSupertypeMap.keys.toList();
bool allSupertypesProcessed = true;
for (int i = 0; i < directSupertypes.length; i++) {
Builder? supertype = directSupertypes[i];
if (supertype is SourceClassBuilder &&
supertype.library.loader == this &&
!topologicallySortedClasses.contains(supertype)) {
allSupertypesProcessed = false;
break;
}
}
if (allSupertypesProcessed && cls.isPatch) {
allSupertypesProcessed =
topologicallySortedClasses.contains(cls.origin);
}
if (allSupertypesProcessed) {
topologicallySortedClasses.add(cls);
checkClassSupertypes(cls, directSupertypeMap, denyListedClasses);
} else {
workList.add(cls);
}
}
} while (previousWorkList.length != workList.length);
List<SourceClassBuilder> classes = topologicallySortedClasses.toList();
List<SourceClassBuilder> classesWithCycles = previousWorkList;
// Once the work list doesn't change in size, it's either empty, or
// contains all classes with cycles.
// Sort the classes to ensure consistent output.
classesWithCycles.sort();
for (int i = 0; i < classesWithCycles.length; i++) {
SourceClassBuilder cls = classesWithCycles[i];
target.breakCycle(cls);
classes.add(cls);
cls.addProblem(
templateCyclicClassHierarchy.withArguments(cls.fullNameForErrors),
cls.charOffset,
noLength);
}
ticker.logMs("Checked class hierarchy");
return classes;
}
void _checkConstructorsForMixin(
SourceClassBuilder cls, ClassBuilder builder) {
for (Builder constructor in builder.constructors.local.values) {
if (constructor.isConstructor && !constructor.isSynthetic) {
cls.addProblem(
templateIllegalMixinDueToConstructors
.withArguments(builder.fullNameForErrors),
cls.charOffset,
noLength,
context: [
templateIllegalMixinDueToConstructorsCause
.withArguments(builder.fullNameForErrors)
.withLocation(
constructor.fileUri!, constructor.charOffset, noLength)
]);
}
}
}
void checkClassSupertypes(
SourceClassBuilder cls,
Map<TypeDeclarationBuilder?, TypeAliasBuilder?> directSupertypeMap,
Set<ClassBuilder> denyListedClasses) {
// Check that the direct supertypes aren't deny-listed or enums.
List<TypeDeclarationBuilder?> directSupertypes =
directSupertypeMap.keys.toList();
for (int i = 0; i < directSupertypes.length; i++) {
TypeDeclarationBuilder? supertype = directSupertypes[i];
if (supertype is EnumBuilder) {
cls.addProblem(templateExtendingEnum.withArguments(supertype.name),
cls.charOffset, noLength);
} else if (!cls.library.mayImplementRestrictedTypes &&
denyListedClasses.contains(supertype)) {
TypeAliasBuilder? aliasBuilder = directSupertypeMap[supertype];
if (aliasBuilder != null) {
cls.addProblem(
templateExtendingRestricted
.withArguments(supertype!.fullNameForErrors),
cls.charOffset,
noLength,
context: [
messageTypedefCause.withLocation(
aliasBuilder.fileUri, aliasBuilder.charOffset, noLength),
]);
} else {
cls.addProblem(
templateExtendingRestricted
.withArguments(supertype!.fullNameForErrors),
cls.charOffset,
noLength);
}
}
}
// Check that the mixed-in type can be used as a mixin.
final TypeBuilder? mixedInTypeBuilder = cls.mixedInTypeBuilder;
if (mixedInTypeBuilder != null) {
bool isClassBuilder = false;
if (mixedInTypeBuilder is NamedTypeBuilder) {
TypeDeclarationBuilder? builder = mixedInTypeBuilder.declaration;
if (builder is TypeAliasBuilder) {
TypeAliasBuilder aliasBuilder = builder;
NamedTypeBuilder namedBuilder = mixedInTypeBuilder;
builder = aliasBuilder.unaliasDeclaration(namedBuilder.arguments,
isUsedAsClass: true,
usedAsClassCharOffset: namedBuilder.charOffset,
usedAsClassFileUri: namedBuilder.fileUri);
if (builder is! ClassBuilder) {
cls.addProblem(
templateIllegalMixin.withArguments(builder!.fullNameForErrors),
cls.charOffset,
noLength,
context: [
messageTypedefCause.withLocation(
aliasBuilder.fileUri, aliasBuilder.charOffset, noLength),
]);
return;
} else if (!cls.library.mayImplementRestrictedTypes &&
denyListedClasses.contains(builder)) {
cls.addProblem(
templateExtendingRestricted
.withArguments(mixedInTypeBuilder.fullNameForErrors),
cls.charOffset,
noLength,
context: [
messageTypedefUnaliasedTypeCause.withLocation(
builder.fileUri, builder.charOffset, noLength),
]);
return;
}
}
if (builder is ClassBuilder) {
isClassBuilder = true;
_checkConstructorsForMixin(cls, builder);
}
}
if (!isClassBuilder) {
// TODO(ahe): Either we need to check this for superclass and
// interfaces, or this shouldn't be necessary (or handled elsewhere).
cls.addProblem(
templateIllegalMixin
.withArguments(mixedInTypeBuilder.fullNameForErrors),
cls.charOffset,
noLength);
}
}
}
List<SourceClassBuilder> checkSemantics(ClassBuilder objectClass) {
checkObjectClassHierarchy(objectClass);
return handleHierarchyCycles(objectClass);
}
/// Builds the core AST structure needed for the outline of the component.
void buildComponent() {
for (LibraryBuilder library in builders.values) {
if (library.loader == this) {
SourceLibraryBuilder sourceLibrary = library as SourceLibraryBuilder;
Library target = sourceLibrary.build(coreLibrary);
if (!library.isPatch) {
if (sourceLibrary.referencesFrom != null) {
referenceFromIndex ??= new ReferenceFromIndex();
referenceFromIndex!.addIndexedLibrary(
target, sourceLibrary.referencesFromIndexed!);
}
libraries.add(target);
}
}
}
ticker.logMs("Built component");
}
Component computeFullComponent() {
Set<Library> libraries = new Set<Library>();
List<Library> workList = <Library>[];
for (LibraryBuilder libraryBuilder in builders.values) {
if (!libraryBuilder.isPatch &&
(libraryBuilder.loader == this ||
libraryBuilder.importUri.scheme == "dart" ||
libraryBuilder == this.first)) {
if (libraries.add(libraryBuilder.library)) {
workList.add(libraryBuilder.library);
}
}
}
while (workList.isNotEmpty) {
Library library = workList.removeLast();
for (LibraryDependency dependency in library.dependencies) {
if (libraries.add(dependency.targetLibrary)) {
workList.add(dependency.targetLibrary);
}
}
}
return new Component()..libraries.addAll(libraries);
}
void computeHierarchy() {
List<AmbiguousTypesRecord>? ambiguousTypesRecords = [];
HandleAmbiguousSupertypes onAmbiguousSupertypes =
(Class cls, Supertype a, Supertype b) {
if (ambiguousTypesRecords != null) {
ambiguousTypesRecords.add(new AmbiguousTypesRecord(cls, a, b));
}
};
if (_hierarchy == null) {
hierarchy = new ClassHierarchy(computeFullComponent(), coreTypes,
onAmbiguousSupertypes: onAmbiguousSupertypes);
} else {
hierarchy.onAmbiguousSupertypes = onAmbiguousSupertypes;
Component component = computeFullComponent();
hierarchy.coreTypes = coreTypes;
hierarchy.applyTreeChanges(const [], component.libraries, const [],
reissueAmbiguousSupertypesFor: component);
}
for (AmbiguousTypesRecord record in ambiguousTypesRecords) {
handleAmbiguousSupertypes(record.cls, record.a, record.b);
}
ambiguousTypesRecords = null;
ticker.logMs("Computed class hierarchy");
}
void handleAmbiguousSupertypes(Class cls, Supertype a, Supertype b) {
addProblem(
templateAmbiguousSupertypes.withArguments(cls.name, a.asInterfaceType,
b.asInterfaceType, cls.enclosingLibrary.isNonNullableByDefault),
cls.fileOffset,
noLength,
cls.fileUri);
}
void ignoreAmbiguousSupertypes(Class cls, Supertype a, Supertype b) {}
void computeCoreTypes(Component component) {
assert(_coreTypes == null, "CoreTypes has already been computed");
_coreTypes = new CoreTypes(component);
// These types are used on the left-hand side of the is-subtype-of relation
// to check if the return types of functions with async, sync*, and async*
// bodies are correct. It's valid to use the non-nullable types on the
// left-hand side in both opt-in and opt-out code.
_futureOfBottom = new InterfaceType(coreTypes.futureClass,
Nullability.nonNullable, <DartType>[const NeverType.nonNullable()]);
_iterableOfBottom = new InterfaceType(coreTypes.iterableClass,
Nullability.nonNullable, <DartType>[const NeverType.nonNullable()]);
_streamOfBottom = new InterfaceType(coreTypes.streamClass,
Nullability.nonNullable, <DartType>[const NeverType.nonNullable()]);
ticker.logMs("Computed core types");
}
void checkSupertypes(List<SourceClassBuilder> sourceClasses) {
for (SourceClassBuilder builder in sourceClasses) {
if (builder.library.loader == this && !builder.isPatch) {
builder.checkSupertypes(coreTypes);
}
}
ticker.logMs("Checked supertypes");
}
void checkTypes() {
for (LibraryBuilder library in builders.values) {
if (library is SourceLibraryBuilder) {
if (library.loader == this) {
library
.checkTypesInOutline(typeInferenceEngine.typeSchemaEnvironment);
}
}
}
ticker.logMs("Checked type arguments of supers against the bounds");
}
void checkOverrides(List<SourceClassBuilder> sourceClasses) {
List<DelayedCheck> overrideChecks = builderHierarchy.takeDelayedChecks();
for (int i = 0; i < overrideChecks.length; i++) {
overrideChecks[i].check(builderHierarchy);
}
ticker.logMs("Checked ${overrideChecks.length} overrides");
typeInferenceEngine.finishTopLevelInitializingFormals();
ticker.logMs("Finished initializing formals");
}
void checkAbstractMembers(List<SourceClassBuilder> sourceClasses) {
List<ClassMember> delayedMemberChecks =
builderHierarchy.takeDelayedMemberComputations();
Set<Class> changedClasses = new Set<Class>();
for (int i = 0; i < delayedMemberChecks.length; i++) {
delayedMemberChecks[i].getMember(builderHierarchy);
changedClasses.add(delayedMemberChecks[i].classBuilder.cls);
}
ticker.logMs(
"Computed ${delayedMemberChecks.length} combined member signatures");
hierarchy.applyMemberChanges(changedClasses, findDescendants: false);
ticker
.logMs("Updated ${changedClasses.length} classes in kernel hierarchy");
}
void checkRedirectingFactories(List<SourceClassBuilder> sourceClasses) {
// TODO(ahe): Move this to [ClassHierarchyBuilder].
for (SourceClassBuilder builder in sourceClasses) {
if (builder.library.loader == this && !builder.isPatch) {
builder.checkRedirectingFactories(
typeInferenceEngine.typeSchemaEnvironment);
}
}
ticker.logMs("Checked redirecting factories");
}
void addNoSuchMethodForwarders(List<SourceClassBuilder> sourceClasses) {
// TODO(ahe): Move this to [ClassHierarchyBuilder].
if (!target.backendTarget.enableNoSuchMethodForwarders) return;
List<Class> changedClasses = <Class>[];
for (SourceClassBuilder builder in sourceClasses) {
if (builder.library.loader == this && !builder.isPatch) {
if (builder.addNoSuchMethodForwarders(target, hierarchy)) {
changedClasses.add(builder.cls);
}
}
}
hierarchy.applyMemberChanges(changedClasses, findDescendants: true);
ticker.logMs("Added noSuchMethod forwarders");
}
void checkMixins(List<SourceClassBuilder> sourceClasses) {
for (SourceClassBuilder builder in sourceClasses) {
if (builder.library.loader == this && !builder.isPatch) {
Class? mixedInClass = builder.cls.mixedInClass;
if (mixedInClass != null && mixedInClass.isMixinDeclaration) {
builder.checkMixinApplication(hierarchy, coreTypes);
}
}
}
ticker.logMs("Checked mixin declaration applications");
}
void buildOutlineExpressions(CoreTypes coreTypes,
List<SynthesizedFunctionNode> synthesizedFunctionNodes) {
List<DelayedActionPerformer> delayedActionPerformers =
<DelayedActionPerformer>[];
for (LibraryBuilder library in builders.values) {
if (library.loader == this) {
(library as SourceLibraryBuilder).buildOutlineExpressions();
Iterator<Builder> iterator = library.iterator;
while (iterator.moveNext()) {
Builder declaration = iterator.current;
if (declaration is ClassBuilder) {
declaration.buildOutlineExpressions(library, coreTypes,
delayedActionPerformers, synthesizedFunctionNodes);
} else if (declaration is ExtensionBuilder) {
declaration.buildOutlineExpressions(library, coreTypes,
delayedActionPerformers, synthesizedFunctionNodes);
} else if (declaration is MemberBuilder) {
declaration.buildOutlineExpressions(library, coreTypes,
delayedActionPerformers, synthesizedFunctionNodes);
} else if (declaration is SourceTypeAliasBuilder) {
declaration.buildOutlineExpressions(library, coreTypes,
delayedActionPerformers, synthesizedFunctionNodes);
} else {
assert(
declaration is PrefixBuilder ||
declaration is DynamicTypeDeclarationBuilder ||
declaration is NeverTypeDeclarationBuilder,
"Unexpected builder in library: ${declaration} "
"(${declaration.runtimeType}");
}
}
}
}
for (DelayedActionPerformer delayedActionPerformer
in delayedActionPerformers) {
delayedActionPerformer.performDelayedActions();
}
ticker.logMs("Build outline expressions");
}
void buildClassHierarchy(
List<SourceClassBuilder> sourceClasses, ClassBuilder objectClass) {
_builderHierarchy = ClassHierarchyBuilder.build(
objectClass, sourceClasses, this, coreTypes);
typeInferenceEngine.hierarchyBuilder = builderHierarchy;
ticker.logMs("Built class hierarchy");
}
void createTypeInferenceEngine() {
_typeInferenceEngine = new TypeInferenceEngineImpl(instrumentation);
}
void performTopLevelInference(List<SourceClassBuilder> sourceClasses) {
/// The first phase of top level initializer inference, which consists of
/// creating kernel objects for all fields and top level variables that
/// might be subject to type inference, and records dependencies between
/// them.
typeInferenceEngine.prepareTopLevel(coreTypes, hierarchy);
builderHierarchy.computeTypes();
List<FieldBuilder> allImplicitlyTypedFields = <FieldBuilder>[];
for (LibraryBuilder library in builders.values) {
if (library.loader == this) {
List<FieldBuilder>? implicitlyTypedFields =
library.takeImplicitlyTypedFields();
if (implicitlyTypedFields != null) {
allImplicitlyTypedFields.addAll(implicitlyTypedFields);
}
}
}
for (int i = 0; i < allImplicitlyTypedFields.length; i++) {
// TODO(ahe): This can cause a crash for parts that failed to get
// included, see for example,
// tests/standalone_2/io/http_cookie_date_test.dart.
allImplicitlyTypedFields[i].inferType();
}
typeInferenceEngine.isTypeInferencePrepared = true;
// Since finalization of covariance may have added forwarding stubs, we need
// to recompute the class hierarchy so that method compilation will properly
// target those forwarding stubs.
hierarchy.onAmbiguousSupertypes = ignoreAmbiguousSupertypes;
ticker.logMs("Performed top level inference");
}
void transformPostInference(TreeNode node, bool transformSetLiterals,
bool transformCollections, Library clientLibrary) {
if (transformCollections) {
collectionTransformer ??= new CollectionTransformer(this);
collectionTransformer!.enterLibrary(clientLibrary);
node.accept(collectionTransformer!);
collectionTransformer!.exitLibrary();
}
if (transformSetLiterals) {
setLiteralTransformer ??= new SetLiteralTransformer(this);
setLiteralTransformer!.enterLibrary(clientLibrary);
node.accept(setLiteralTransformer!);
setLiteralTransformer!.exitLibrary();
}
}
void transformListPostInference(
List<TreeNode> list,
bool transformSetLiterals,
bool transformCollections,
Library clientLibrary) {
if (transformCollections) {
CollectionTransformer transformer =
collectionTransformer ??= new CollectionTransformer(this);
transformer.enterLibrary(clientLibrary);
for (int i = 0; i < list.length; ++i) {
list[i] = list[i].accept(transformer);
}
transformer.exitLibrary();
}
if (transformSetLiterals) {
SetLiteralTransformer transformer =
setLiteralTransformer ??= new SetLiteralTransformer(this);
transformer.enterLibrary(clientLibrary);
for (int i = 0; i < list.length; ++i) {
list[i] = list[i].accept(transformer);
}
transformer.exitLibrary();
}
}
Expression instantiateNoSuchMethodError(
Expression receiver, String name, Arguments arguments, int offset,
{bool isMethod: false,
bool isGetter: false,
bool isSetter: false,
bool isField: false,
bool isLocalVariable: false,
bool isDynamic: false,
bool isSuper: false,
bool isStatic: false,
bool isConstructor: false,
bool isTopLevel: false}) {
return target.backendTarget.instantiateNoSuchMethodError(
coreTypes, receiver, name, arguments, offset,
isMethod: isMethod,
isGetter: isGetter,
isSetter: isSetter,
isField: isField,
isLocalVariable: isLocalVariable,
isDynamic: isDynamic,
isSuper: isSuper,
isStatic: isStatic,
isConstructor: isConstructor,
isTopLevel: isTopLevel);
}
void checkMainMethods() {
DartType? listOfString;
for (LibraryBuilder libraryBuilder in builders.values) {
if (libraryBuilder.loader == this &&
libraryBuilder.isNonNullableByDefault) {
Builder? mainBuilder =
libraryBuilder.exportScope.lookupLocalMember('main', setter: false);
mainBuilder ??=
libraryBuilder.exportScope.lookupLocalMember('main', setter: true);
if (mainBuilder is MemberBuilder) {
if (mainBuilder is InvalidTypeDeclarationBuilder) {
// This is an ambiguous export, skip the check.
return;
}
if (mainBuilder.isField ||
mainBuilder.isGetter ||
mainBuilder.isSetter) {
if (mainBuilder.parent != libraryBuilder) {
libraryBuilder.addProblem(
messageMainNotFunctionDeclarationExported,
libraryBuilder.charOffset,
noLength,
libraryBuilder.fileUri,
context: [
messageExportedMain.withLocation(mainBuilder.fileUri!,
mainBuilder.charOffset, mainBuilder.name.length)
]);
} else {
libraryBuilder.addProblem(
messageMainNotFunctionDeclaration,
mainBuilder.charOffset,
mainBuilder.name.length,
mainBuilder.fileUri);
}
} else {
Procedure procedure = mainBuilder.member as Procedure;
if (procedure.function.requiredParameterCount > 2) {
if (mainBuilder.parent != libraryBuilder) {
libraryBuilder.addProblem(
messageMainTooManyRequiredParametersExported,
libraryBuilder.charOffset,
noLength,
libraryBuilder.fileUri,
context: [
messageExportedMain.withLocation(mainBuilder.fileUri!,
mainBuilder.charOffset, mainBuilder.name.length)
]);
} else {
libraryBuilder.addProblem(
messageMainTooManyRequiredParameters,
mainBuilder.charOffset,
mainBuilder.name.length,
mainBuilder.fileUri);
}
} else if (procedure.function.namedParameters
.any((parameter) => parameter.isRequired)) {
if (mainBuilder.parent != libraryBuilder) {
libraryBuilder.addProblem(
messageMainRequiredNamedParametersExported,
libraryBuilder.charOffset,
noLength,
libraryBuilder.fileUri,
context: [
messageExportedMain.withLocation(mainBuilder.fileUri!,
mainBuilder.charOffset, mainBuilder.name.length)
]);
} else {
libraryBuilder.addProblem(
messageMainRequiredNamedParameters,
mainBuilder.charOffset,
mainBuilder.name.length,
mainBuilder.fileUri);
}
} else if (procedure.function.positionalParameters.length > 0) {
DartType parameterType =
procedure.function.positionalParameters.first.type;
listOfString ??= new InterfaceType(
coreTypes.listClass,
Nullability.nonNullable,
[coreTypes.stringNonNullableRawType]);
if (!typeEnvironment.isSubtypeOf(listOfString, parameterType,
SubtypeCheckMode.withNullabilities)) {
if (mainBuilder.parent != libraryBuilder) {
libraryBuilder.addProblem(
templateMainWrongParameterTypeExported.withArguments(
parameterType,
listOfString,
libraryBuilder.isNonNullableByDefault),
libraryBuilder.charOffset,
noLength,
libraryBuilder.fileUri,
context: [
messageExportedMain.withLocation(mainBuilder.fileUri!,
mainBuilder.charOffset, mainBuilder.name.length)
]);
} else {
libraryBuilder.addProblem(
templateMainWrongParameterType.withArguments(
parameterType,
listOfString,
libraryBuilder.isNonNullableByDefault),
mainBuilder.charOffset,
mainBuilder.name.length,
mainBuilder.fileUri);
}
}
}
}
} else if (mainBuilder != null) {
if (mainBuilder.parent != libraryBuilder) {
libraryBuilder.addProblem(messageMainNotFunctionDeclarationExported,
libraryBuilder.charOffset, noLength, libraryBuilder.fileUri,
context: [
messageExportedMain.withLocation(
mainBuilder.fileUri!, mainBuilder.charOffset, noLength)
]);
} else {
libraryBuilder.addProblem(messageMainNotFunctionDeclaration,
mainBuilder.charOffset, noLength, mainBuilder.fileUri);
}
}
}
}
}
void releaseAncillaryResources() {
hierarchy = null;
_builderHierarchy = null;
_typeInferenceEngine = null;
builders.clear();
libraries.clear();
first = null;
sourceBytes.clear();
target.releaseAncillaryResources();
_coreTypes = null;
instrumentation = null;
collectionTransformer = null;
setLiteralTransformer = null;
}
@override
ClassBuilder computeClassBuilderFromTargetClass(Class cls) {
Library kernelLibrary = cls.enclosingLibrary;
LibraryBuilder? library = builders[kernelLibrary.importUri];
if (library == null) {
return target.dillTarget.loader.computeClassBuilderFromTargetClass(cls);
}
return library.lookupLocalMember(cls.name, required: true) as ClassBuilder;
}
@override
TypeBuilder computeTypeBuilder(DartType type) {
return type.accept(new TypeBuilderComputer(this));
}
BodyBuilder createBodyBuilderForField(
FieldBuilder field, TypeInferrer typeInferrer) {
return new BodyBuilder.forField(field, typeInferrer);
}
}
/// A minimal implementation of dart:core that is sufficient to create an
/// instance of [CoreTypes] and compile a program.
const String defaultDartCoreSource = """
import 'dart:_internal';
import 'dart:async';
export 'dart:async' show Future, Stream;
print(object) {}
bool identical(a, b) => false;
class Iterator<E> {
bool moveNext() => null;
E get current => null;
}
class Iterable<E> {
Iterator<E> get iterator => null;
}
class List<E> extends Iterable<E> {
factory List() => null;
factory List.unmodifiable(elements) => null;
factory List.empty({bool growable = false}) => null;
factory List.filled(int length, E fill, {bool growable = false}) => null;
factory List.generate(int length, E generator(int index),
{bool growable = true}) => null;
factory List.of() => null;
void add(E element) {}
void addAll(Iterable<E> iterable) {}
E operator [](int index) => null;
}
class _GrowableList<E> {
factory _GrowableList(int length) => null;
factory _GrowableList.empty() => null;
factory _GrowableList.filled() => null;
factory _GrowableList.generate(int length, E generator(int index)) => null;
factory _GrowableList._literal1(E e0) => null;
factory _GrowableList._literal2(E e0, E e1) => null;
factory _GrowableList._literal3(E e0, E e1, E e2) => null;
factory _GrowableList._literal4(E e0, E e1, E e2, E e3) => null;
factory _GrowableList._literal5(E e0, E e1, E e2, E e3, E e4) => null;
factory _GrowableList._literal6(E e0, E e1, E e2, E e3, E e4, E e5) => null;
factory _GrowableList._literal7(E e0, E e1, E e2, E e3, E e4, E e5, E e6) => null;
factory _GrowableList._literal8(E e0, E e1, E e2, E e3, E e4, E e5, E e6, E e7) => null;
}
class _List<E> {
factory _List() => null;
factory _List.empty() => null;
factory _List.filled() => null;
factory _List.generate(int length, E generator(int index)) => null;
}
class MapEntry<K, V> {
K key;
V value;
}
abstract class Map<K, V> extends Iterable {
factory Map.unmodifiable(other) => null;
Iterable<MapEntry<K, V>> get entries;
void operator []=(K key, V value) {}
}
abstract class _ImmutableMap<K, V> implements Map<K, V> {
dynamic _kvPairs;
}
abstract class pragma {
String name;
Object options;
}
class AbstractClassInstantiationError {}
class NoSuchMethodError {
NoSuchMethodError.withInvocation(receiver, invocation);
}
class StackTrace {}
class Null {}
class Object {
const Object();
noSuchMethod(invocation) => null;
bool operator==(dynamic) {}
}
abstract class Enum {
}
class String {}
class Symbol {}
class Set<E> {
factory Set() = Set<E>._;
external factory Set._();
factory Set.of(o) = Set<E>._of;
external factory Set._of(o);
bool add(E element) {}
void addAll(Iterable<E> iterable) {}
}
class Type {}
class _InvocationMirror {
_InvocationMirror._withType(_memberName, _type, _typeArguments,
_positionalArguments, _namedArguments);
}
class bool {}
class double extends num {}
class int extends num {}
class num {}
class _SyncIterable {}
class _SyncIterator {
var _current;
var _yieldEachIterable;
}
class Function {}
""";
/// A minimal implementation of dart:async that is sufficient to create an
/// instance of [CoreTypes] and compile program.
const String defaultDartAsyncSource = """
_asyncErrorWrapperHelper(continuation) {}
void _asyncStarMoveNextHelper(var stream) {}
_asyncThenWrapperHelper(continuation) {}
_awaitHelper(object, thenCallback, errorCallback, awaiter) {}
_completeOnAsyncReturn(_future, value, async_jump_var) {}
_completeOnAsyncError(_future, e, st, async_jump_var) {}
class _AsyncStarStreamController {
add(event) {}
addError(error, stackTrace) {}
addStream(stream) {}
close() {}
get stream => null;
}
abstract class Completer {
factory Completer.sync() => null;
get future;
complete([value]);
completeError(error, [stackTrace]);
}
class Future<T> {
factory Future.microtask(computation) => null;
}
class FutureOr {
}
class _Future {
void _completeError(Object error, StackTrace stackTrace) {}
void _asyncCompleteError(Object error, StackTrace stackTrace) {}
}
class Stream {}
class _StreamIterator {
get current => null;
moveNext() {}
cancel() {}
}
""";
/// A minimal implementation of dart:collection that is sufficient to create an
/// instance of [CoreTypes] and compile program.
const String defaultDartCollectionSource = """
abstract class LinkedHashMap<K, V> implements Map<K, V> {
factory LinkedHashMap(
{bool Function(K, K)? equals,
int Function(K)? hashCode,
bool Function(dynamic)? isValidKey}) => null;
}
class _InternalLinkedHashMap<K, V> {
_InternalLinkedHashMap();
}
abstract class LinkedHashSet<E> implements Set<E> {
factory LinkedHashSet(
{bool Function(E, E)? equals,
int Function(E)? hashCode,
bool Function(dynamic)? isValidKey}) => null;
}
class _CompactLinkedHashSet<E> {
_CompactLinkedHashSet();
}
class _UnmodifiableSet {
final Map _map;
const _UnmodifiableSet(this._map);
}
""";
/// A minimal implementation of dart:_internal that is sufficient to create an
/// instance of [CoreTypes] and compile program.
const String defaultDartInternalSource = """
class Symbol {
const Symbol(String name);
}
T unsafeCast<T>(Object v) {}
class ReachabilityError {
ReachabilityError([message]);
}
""";
/// A minimal implementation of dart:typed_data that is sufficient to create an
/// instance of [CoreTypes] and compile program.
const String defaultDartTypedDataSource = """
class Endian {
static const Endian little = null;
static const Endian big = null;
static final Endian host = null;
}
""";
class AmbiguousTypesRecord {
final Class cls;
final Supertype a;
final Supertype b;
const AmbiguousTypesRecord(this.cls, this.a, this.b);
}
class SourceLoaderDataForTesting {
final Map<TreeNode, TreeNode> _aliasMap = {};
/// Registers that [original] has been replaced by [alias] in the generated
/// AST.
void registerAlias(TreeNode original, TreeNode alias) {
_aliasMap[alias] = original;
}
/// Returns the original node for [alias] or [alias] if it was not registered
/// as an alias.
TreeNode toOriginal(TreeNode alias) {
return _aliasMap[alias] ?? alias;
}
}