blob: e2a0bc85be035bdbbdc2e916030693b661ab87e0 [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:async' show Future;
import 'dart:convert' show utf8;
import 'dart:typed_data' show Uint8List;
import 'package:kernel/ast.dart'
show
Arguments,
BottomType,
Class,
Component,
DartType,
Expression,
FunctionNode,
InterfaceType,
Library,
LibraryDependency,
ProcedureKind,
Supertype;
import 'package:kernel/class_hierarchy.dart'
show ClassHierarchy, HandleAmbiguousSupertypes;
import 'package:kernel/core_types.dart' show CoreTypes;
import '../../api_prototype/file_system.dart';
import '../../base/instrumentation.dart'
show Instrumentation, InstrumentationValueLiteral;
import '../blacklisted_classes.dart' show blacklistedCoreClasses;
import '../export.dart' show Export;
import '../import.dart' show Import;
import '../fasta_codes.dart'
show
LocatedMessage,
Message,
SummaryTemplate,
Template,
messageObjectExtends,
messageObjectImplements,
messageObjectMixesIn,
messagePartOrphan,
noLength,
templateAmbiguousSupertypes,
templateCantReadFile,
templateCyclicClassHierarchy,
templateDuplicatedLibraryExport,
templateDuplicatedLibraryExportContext,
templateDuplicatedLibraryImport,
templateDuplicatedLibraryImportContext,
templateExtendingEnum,
templateExtendingRestricted,
templateIllegalMixin,
templateIllegalMixinDueToConstructors,
templateIllegalMixinDueToConstructorsCause,
templateInternalProblemUriMissingScheme,
templateSourceOutlineSummary,
templateUntranslatableUri;
import '../fasta_codes.dart' as fasta_codes;
import '../kernel/kernel_shadow_ast.dart'
show ShadowClass, ShadowTypeInferenceEngine;
import '../kernel/kernel_builder.dart'
show
ClassBuilder,
ClassHierarchyBuilder,
Declaration,
EnumBuilder,
KernelFieldBuilder,
KernelProcedureBuilder,
LibraryBuilder,
NamedTypeBuilder,
TypeBuilder;
import '../kernel/kernel_target.dart' show KernelTarget;
import '../kernel/body_builder.dart' show BodyBuilder;
import '../loader.dart' show Loader, untranslatableUriScheme;
import '../parser/class_member_parser.dart' show ClassMemberParser;
import '../parser.dart' show Parser, lengthForToken, offsetForToken;
import '../problems.dart' show internalProblem, unhandled;
import '../scanner.dart' show ErrorToken, ScannerResult, Token, scan;
import '../severity.dart' show Severity;
import '../type_inference/interface_resolver.dart' show InterfaceResolver;
import '../type_inference/type_inferrer.dart'
show LegacyModeMixinInferrer, StrongModeMixinInferrer;
import 'diet_listener.dart' show DietListener;
import 'diet_parser.dart' show DietParser;
import 'outline_builder.dart' show OutlineBuilder;
import 'source_class_builder.dart' show SourceClassBuilder;
import 'source_library_builder.dart' show SourceLibraryBuilder;
class SourceLoader<L> extends Loader<L> {
/// 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>>{};
// Used when building directly to kernel.
ClassHierarchy hierarchy;
CoreTypes coreTypes;
// Used when checking whether a return type of an async function is valid.
DartType futureOfBottom;
DartType iterableOfBottom;
DartType streamOfBottom;
ShadowTypeInferenceEngine typeInferenceEngine;
InterfaceResolver interfaceResolver;
Instrumentation instrumentation;
SourceLoader(this.fileSystem, this.includeComments, KernelTarget target)
: super(target);
Template<SummaryTemplate> get outlineSummaryTemplate =>
templateSourceOutlineSummary;
bool get isSourceLoader => true;
Future<Token> tokenize(SourceLibraryBuilder library,
{bool suppressLexicalErrors: false}) async {
Uri uri = library.fileUri;
// Lookup the file URI in the cache.
List<int> bytes = sourceBytes[uri];
if (bytes == null) {
// Error recovery.
if (uri.scheme == untranslatableUriScheme) {
Message message = templateUntranslatableUri.withArguments(library.uri);
library.addProblemAtAccessors(message);
bytes = synthesizeSourceForMissingFile(library.uri, null);
} else if (!uri.hasScheme) {
return internalProblem(
templateInternalProblemUriMissingScheme.withArguments(uri),
-1,
library.uri);
} else if (uri.scheme == SourceLibraryBuilder.MALFORMED_URI_SCHEME) {
bytes = synthesizeSourceForMissingFile(library.uri, null);
}
if (bytes != null) {
Uint8List zeroTerminatedBytes = new Uint8List(bytes.length + 1);
zeroTerminatedBytes.setRange(0, bytes.length, bytes);
bytes = zeroTerminatedBytes;
sourceBytes[uri] = 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(uri).readAsBytes();
} on FileSystemException catch (e) {
Message message = templateCantReadFile.withArguments(uri, e.message);
library.addProblemAtAccessors(message);
rawBytes = synthesizeSourceForMissingFile(library.uri, message);
}
Uint8List zeroTerminatedBytes = new Uint8List(rawBytes.length + 1);
zeroTerminatedBytes.setRange(0, rawBytes.length, rawBytes);
bytes = zeroTerminatedBytes;
sourceBytes[uri] = bytes;
byteCount += rawBytes.length;
}
ScannerResult result = scan(bytes, includeComments: includeComments);
Token token = result.tokens;
if (!suppressLexicalErrors) {
List<int> source = getSource(bytes);
target.addSourceInformation(library.fileUri, result.lineStarts, source);
}
while (token is ErrorToken) {
if (!suppressLexicalErrors) {
ErrorToken error = token;
library.addProblem(error.assertionMessage, offsetForToken(token),
lengthForToken(token), uri);
}
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);
default:
return utf8.encode(message == null ? "" : "/* ${message.message} */");
}
}
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);
}
Future<Null> buildOutline(SourceLibraryBuilder library) async {
Token tokens = await tokenize(library);
if (tokens == null) return;
OutlineBuilder listener = new OutlineBuilder(library);
new ClassMemberParser(listener).parseUnit(tokens);
}
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);
if (tokens == null) return;
DietListener listener = createDietListener(library);
DietParser parser = new DietParser(listener);
parser.parseUnit(tokens);
for (SourceLibraryBuilder part in library.parts) {
if (part.partOfLibrary != library) {
// Part was included in multiple libraries. Skip it here.
continue;
}
Token tokens = await tokenize(part);
if (tokens != null) {
listener.uri = part.fileUri;
listener.partDirectiveIndex = 0;
parser.parseUnit(tokens);
}
}
}
}
Future<Expression> buildExpression(
SourceLibraryBuilder library,
String enclosingClass,
bool isInstanceMember,
FunctionNode parameters) async {
Token token = await tokenize(library, suppressLexicalErrors: false);
if (token == null) return null;
DietListener dietListener = createDietListener(library);
Declaration parent = library;
if (enclosingClass != null) {
Declaration cls =
dietListener.memberScope.lookup(enclosingClass, -1, null);
if (cls is ClassBuilder) {
parent = cls;
dietListener
..currentClass = cls
..memberScope = cls.scope.copyWithParent(
dietListener.memberScope.withTypeVariables(cls.typeVariables),
"debugExpression in $enclosingClass");
}
}
KernelProcedureBuilder builder = new KernelProcedureBuilder(null, 0, null,
"debugExpr", null, null, ProcedureKind.Method, library, 0, 0, -1, -1)
..parent = parent;
BodyBuilder listener = dietListener.createListener(
builder, dietListener.memberScope, isInstanceMember);
return listener.parseSingleExpression(
new Parser(listener), token, parameters);
}
KernelTarget get target => super.target;
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);
}
}
});
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];
part.addProblem(messagePartOrphan, 0, 1, part.fileUri);
part.validatePart(null, null);
}
}
ticker.logMs("Resolved parts");
builders.forEach((Uri uri, LibraryBuilder library) {
if (library.loader == this) {
library.applyPatches();
}
});
ticker.logMs("Applied patches");
}
void computeLibraryScopes() {
Set<LibraryBuilder> exporters = new Set<LibraryBuilder>();
Set<LibraryBuilder> exportees = new Set<LibraryBuilder>();
builders.forEach((Uri uri, LibraryBuilder library) {
if (library.loader == this) {
SourceLibraryBuilder sourceLibrary = library;
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);
}
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, Declaration member) {
if (export.addToExportScope(name, member)) {
wasChanged = true;
}
});
}
}
} while (wasChanged);
builders.forEach((Uri uri, LibraryBuilder library) {
if (library.loader == this) {
SourceLibraryBuilder sourceLibrary = library;
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 `covarint SourceLibraryBuilder`.
builders.forEach((Uri uri, dynamic l) {
SourceLibraryBuilder library = l;
Set<Declaration> members = new Set<Declaration>();
Iterator<Declaration> iterator = library.iterator;
while (iterator.moveNext()) {
members.add(iterator.current);
}
List<String> exports = <String>[];
library.exportScope.forEach((String name, Declaration 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;
builders.forEach((Uri uri, LibraryBuilder library) {
if (library.loader == this) {
SourceLibraryBuilder sourceLibrary = library;
typeCount += sourceLibrary.resolveTypes();
}
});
ticker.logMs("Resolved $typeCount types");
}
void finalizeInitializingFormals() {
int formalCount = 0;
builders.forEach((Uri uri, LibraryBuilder library) {
if (library.loader == this) {
SourceLibraryBuilder sourceLibrary = library;
formalCount += sourceLibrary.finalizeInitializingFormals();
}
});
ticker.logMs("Finalized $formalCount initializing formals");
}
void finishDeferredLoadTearoffs() {
int count = 0;
builders.forEach((Uri uri, LibraryBuilder library) {
if (library.loader == this) {
count += library.finishDeferredLoadTearoffs();
}
});
ticker.logMs("Finished deferred load tearoffs $count");
}
void finishNoSuchMethodForwarders() {
int count = 0;
builders.forEach((Uri uri, LibraryBuilder library) {
if (library.loader == this) {
count += library.finishForwarders();
}
});
ticker.logMs("Finished forwarders for $count procedures");
}
void resolveConstructors() {
int count = 0;
builders.forEach((Uri uri, LibraryBuilder library) {
if (library.loader == this) {
count += library.resolveConstructors(null);
}
});
ticker.logMs("Resolved $count constructors");
}
void finishTypeVariables(ClassBuilder object, TypeBuilder dynamicType) {
int count = 0;
builders.forEach((Uri uri, LibraryBuilder library) {
if (library.loader == this) {
count += library.finishTypeVariables(object, dynamicType);
}
});
ticker.logMs("Resolved $count type-variable bounds");
}
void computeDefaultTypes(TypeBuilder dynamicType, TypeBuilder bottomType,
ClassBuilder objectClass) {
int count = 0;
builders.forEach((Uri uri, LibraryBuilder library) {
if (library.loader == this) {
count +=
library.computeDefaultTypes(dynamicType, bottomType, objectClass);
}
});
ticker.logMs("Computed default types for $count type variables");
}
void finishNativeMethods() {
int count = 0;
builders.forEach((Uri uri, LibraryBuilder library) {
if (library.loader == this) {
count += library.finishNativeMethods();
}
});
ticker.logMs("Finished $count native methods");
}
void finishPatchMethods() {
int count = 0;
builders.forEach((Uri uri, LibraryBuilder library) {
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.supertype != null) {
objectClass.supertype = null;
objectClass.addProblem(
messageObjectExtends, objectClass.charOffset, noLength);
}
if (objectClass.interfaces != null) {
objectClass.addProblem(
messageObjectImplements, objectClass.charOffset, noLength);
objectClass.interfaces = null;
}
if (objectClass.mixedInType != null) {
objectClass.addProblem(
messageObjectMixesIn, objectClass.charOffset, noLength);
objectClass.mixedInType = null;
}
}
}
/// Returns a list of all class builders declared in this loader. As the
/// classes are sorted, any cycles in the hiearchy 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<Declaration> members = library.iterator;
while (members.moveNext()) {
Declaration member = members.current;
if (member is SourceClassBuilder) {
workList.add(member);
}
}
}
}
Set<ClassBuilder> blackListedClasses = new Set<ClassBuilder>();
for (int i = 0; i < blacklistedCoreClasses.length; i++) {
blackListedClasses.add(coreLibrary[blacklistedCoreClasses[i]]);
}
// 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];
List<Declaration> directSupertypes =
cls.computeDirectSupertypes(objectClass);
bool allSupertypesProcessed = true;
for (int i = 0; i < directSupertypes.length; i++) {
Declaration supertype = directSupertypes[i];
if (supertype is SourceClassBuilder &&
supertype.library.loader == this &&
!topologicallySortedClasses.contains(supertype)) {
allSupertypesProcessed = false;
break;
}
}
if (allSupertypesProcessed) {
topologicallySortedClasses.add(cls);
checkClassSupertypes(cls, directSupertypes, blackListedClasses);
} 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 checkClassSupertypes(
SourceClassBuilder cls,
List<Declaration> directSupertypes,
Set<ClassBuilder> blackListedClasses) {
// Check that the direct supertypes aren't black-listed or enums.
for (int i = 0; i < directSupertypes.length; i++) {
Declaration supertype = directSupertypes[i];
if (supertype is EnumBuilder) {
cls.addProblem(templateExtendingEnum.withArguments(supertype.name),
cls.charOffset, noLength);
} else if (!cls.library.mayImplementRestrictedTypes &&
blackListedClasses.contains(supertype)) {
cls.addProblem(
templateExtendingRestricted
.withArguments(supertype.fullNameForErrors),
cls.charOffset,
noLength);
}
}
// Check that the mixed-in type can be used as a mixin.
final TypeBuilder mixedInType = cls.mixedInType;
if (mixedInType != null) {
bool isClassBuilder = false;
if (mixedInType is NamedTypeBuilder) {
var builder = mixedInType.declaration;
if (builder is ClassBuilder) {
isClassBuilder = true;
for (Declaration constructory in builder.constructors.local.values) {
if (constructory.isConstructor && !constructory.isSynthetic) {
cls.addProblem(
templateIllegalMixinDueToConstructors
.withArguments(builder.fullNameForErrors),
cls.charOffset,
noLength,
context: [
templateIllegalMixinDueToConstructorsCause
.withArguments(builder.fullNameForErrors)
.withLocation(constructory.fileUri,
constructory.charOffset, noLength)
]);
}
}
}
}
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(mixedInType.fullNameForErrors),
cls.charOffset,
noLength);
}
}
}
List<SourceClassBuilder> checkSemantics(ClassBuilder objectClass) {
checkObjectClassHierarchy(objectClass);
List<SourceClassBuilder> classes = handleHierarchyCycles(objectClass);
// Check imports and exports for duplicate names.
// This is rather silly, e.g. it makes importing 'foo' and exporting another
// 'foo' ok.
builders.forEach((Uri uri, LibraryBuilder library) {
if (library is SourceLibraryBuilder && library.loader == this) {
// Check exports.
if (library.exports.isNotEmpty) {
Map<String, List<Export>> nameToExports;
bool errorExports = false;
for (Export export in library.exports) {
String name = export.exported?.name ?? '';
if (name != '') {
nameToExports ??= new Map<String, List<Export>>();
List<Export> exports = nameToExports[name] ??= <Export>[];
exports.add(export);
if (exports[0].exported != export.exported) errorExports = true;
}
}
if (errorExports) {
for (String name in nameToExports.keys) {
List<Export> exports = nameToExports[name];
if (exports.length < 2) continue;
List<LocatedMessage> context = <LocatedMessage>[];
for (Export export in exports.skip(1)) {
context.add(templateDuplicatedLibraryExportContext
.withArguments(name)
.withLocation(uri, export.charOffset, noLength));
}
library.addProblem(
templateDuplicatedLibraryExport.withArguments(name),
exports[0].charOffset,
noLength,
uri,
context: context);
}
}
}
// Check imports.
if (library.imports.isNotEmpty) {
Map<String, List<Import>> nameToImports;
bool errorImports;
for (Import import in library.imports) {
String name = import.imported?.name ?? '';
if (name != '') {
nameToImports ??= new Map<String, List<Import>>();
List<Import> imports = nameToImports[name] ??= <Import>[];
imports.add(import);
if (imports[0].imported != import.imported) errorImports = true;
}
}
if (errorImports != null) {
for (String name in nameToImports.keys) {
List<Import> imports = nameToImports[name];
if (imports.length < 2) continue;
List<LocatedMessage> context = <LocatedMessage>[];
for (Import import in imports.skip(1)) {
context.add(templateDuplicatedLibraryImportContext
.withArguments(name)
.withLocation(uri, import.charOffset, noLength));
}
library.addProblem(
templateDuplicatedLibraryImport.withArguments(name),
imports[0].charOffset,
noLength,
uri,
context: context);
}
}
}
}
});
ticker.logMs("Checked imports and exports for duplicate names");
return classes;
}
void buildComponent() {
builders.forEach((Uri uri, LibraryBuilder library) {
if (library.loader == this) {
SourceLibraryBuilder sourceLibrary = library;
L target = sourceLibrary.build(coreLibrary);
if (!library.isPatch && !library.isSynthetic) {
libraries.add(target);
}
}
});
ticker.logMs("Built component");
}
Component computeFullComponent() {
Set<Library> libraries = new Set<Library>();
List<Library> workList = <Library>[];
builders.forEach((Uri uri, LibraryBuilder library) {
if (!library.isPatch &&
(library.loader == this || library.fileUri.scheme == "dart")) {
if (libraries.add(library.target)) {
workList.add(library.target);
}
}
});
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<List> ambiguousTypesRecords = [];
HandleAmbiguousSupertypes onAmbiguousSupertypes =
(Class cls, Supertype a, Supertype b) {
if (ambiguousTypesRecords != null) {
ambiguousTypesRecords.add([cls, a, b]);
}
};
if (hierarchy == null) {
hierarchy = new ClassHierarchy(computeFullComponent(),
onAmbiguousSupertypes: onAmbiguousSupertypes,
mixinInferrer: target.legacyMode
? new LegacyModeMixinInferrer()
: new StrongModeMixinInferrer(this));
} else {
hierarchy.onAmbiguousSupertypes = onAmbiguousSupertypes;
Component component = computeFullComponent();
hierarchy.applyTreeChanges(const [], component.libraries,
reissueAmbiguousSupertypesFor: component);
}
for (List record in ambiguousTypesRecords) {
handleAmbiguousSupertypes(record[0], record[1], record[2]);
}
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.fileOffset,
noLength,
cls.fileUri);
}
void ignoreAmbiguousSupertypes(Class cls, Supertype a, Supertype b) {}
void computeCoreTypes(Component component) {
coreTypes = new CoreTypes(component);
futureOfBottom = new InterfaceType(
coreTypes.futureClass, <DartType>[const BottomType()]);
iterableOfBottom = new InterfaceType(
coreTypes.iterableClass, <DartType>[const BottomType()]);
streamOfBottom = new InterfaceType(
coreTypes.streamClass, <DartType>[const BottomType()]);
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 checkBounds() {
if (target.legacyMode) return;
builders.forEach((Uri uri, LibraryBuilder library) {
if (library is SourceLibraryBuilder) {
if (library.loader == this) {
library
.checkBoundsInOutline(typeInferenceEngine.typeSchemaEnvironment);
}
}
});
ticker.logMs("Checked type arguments of supers against the bounds");
}
void checkOverrides(List<SourceClassBuilder> sourceClasses) {
assert(hierarchy != null);
for (SourceClassBuilder builder in sourceClasses) {
if (builder.library.loader == this && !builder.isPatch) {
builder.checkOverrides(
hierarchy, typeInferenceEngine?.typeSchemaEnvironment);
}
}
ticker.logMs("Checked overrides");
}
void checkAbstractMembers(List<SourceClassBuilder> sourceClasses) {
// TODO(ahe): Move this to [ClassHierarchyBuilder].
if (target.legacyMode) return;
assert(hierarchy != null);
for (SourceClassBuilder builder in sourceClasses) {
if (builder.library.loader == this && !builder.isPatch) {
builder.checkAbstractMembers(
coreTypes, hierarchy, typeInferenceEngine.typeSchemaEnvironment);
}
}
ticker.logMs("Checked abstract members");
}
void checkRedirectingFactories(List<SourceClassBuilder> sourceClasses) {
// TODO(ahe): Move this to [ClassHierarchyBuilder].
if (target.legacyMode) return;
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 = new List<Class>();
for (SourceClassBuilder builder in sourceClasses) {
if (builder.library.loader == this && !builder.isPatch) {
if (builder.addNoSuchMethodForwarders(target, hierarchy)) {
changedClasses.add(builder.target);
}
}
}
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) {
if (builder.isMixinDeclaration) {
builder.checkMixinDeclaration();
}
Class mixedInClass = builder.cls.mixedInClass;
if (mixedInClass != null && mixedInClass.isMixinDeclaration) {
builder.checkMixinApplication(hierarchy);
}
}
}
ticker.logMs("Checked mixin declaration applications");
}
void buildClassHierarchy(
List<SourceClassBuilder> sourceClasses, ClassBuilder objectClass) {
if (!target.legacyMode) return;
ticker.logMs("Building class hierarchy");
ClassHierarchyBuilder classHierarchyBuilder =
new ClassHierarchyBuilder(objectClass);
for (int i = 0; i < sourceClasses.length; i++) {
classHierarchyBuilder.add(sourceClasses[i]);
}
ticker.logMs("Built class hierarchy");
}
void createTypeInferenceEngine() {
if (target.legacyMode) return;
typeInferenceEngine = new ShadowTypeInferenceEngine(instrumentation);
}
void performTopLevelInference(List<SourceClassBuilder> sourceClasses) {
if (target.legacyMode) return;
/// 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);
interfaceResolver = new InterfaceResolver(
typeInferenceEngine,
typeInferenceEngine.typeSchemaEnvironment,
instrumentation,
target.legacyMode);
for (LibraryBuilder library in builders.values) {
if (library.loader == this) {
Iterator<Declaration> iterator = library.iterator;
while (iterator.moveNext()) {
Declaration member = iterator.current;
if (member is KernelFieldBuilder) {
member.prepareTopLevelInference();
}
}
}
}
for (int i = 0; i < sourceClasses.length; i++) {
sourceClasses[i].prepareTopLevelInference();
}
typeInferenceEngine.isTypeInferencePrepared = true;
ticker.logMs("Prepared top level inference");
/// The second phase of top level initializer inference, which is to visit
/// fields and top level variables in topologically-sorted order and assign
/// their types.
typeInferenceEngine.finishTopLevelFields();
List<Class> changedClasses = new List<Class>();
for (var builder in sourceClasses) {
if (builder.isPatch) continue;
ShadowClass class_ = builder.target;
int memberCount = class_.fields.length +
class_.constructors.length +
class_.procedures.length +
class_.redirectingFactoryConstructors.length;
class_.finalizeCovariance(interfaceResolver);
ShadowClass.clearClassInferenceInfo(class_);
int newMemberCount = class_.fields.length +
class_.constructors.length +
class_.procedures.length +
class_.redirectingFactoryConstructors.length;
if (newMemberCount != memberCount) {
// The inference potentially adds new members (but doesn't otherwise
// change the classes), so if the member count has changed we need to
// update the class in the class hierarchy.
changedClasses.add(class_);
}
}
typeInferenceEngine.finishTopLevelInitializingFormals();
if (instrumentation != null) {
builders.forEach((Uri uri, LibraryBuilder library) {
if (library.loader == this) {
library.instrumentTopLevelInference(instrumentation);
}
});
}
interfaceResolver = null;
// 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;
hierarchy.applyMemberChanges(changedClasses, findDescendants: true);
ticker.logMs("Performed top level inference");
}
Expression instantiateInvocation(Expression receiver, String name,
Arguments arguments, int offset, bool isSuper) {
return target.backendTarget.instantiateInvocation(
coreTypes, receiver, name, arguments, offset, isSuper);
}
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 recordMessage(Severity severity, Message message, int charOffset,
int length, Uri fileUri,
{List<LocatedMessage> context}) {
if (instrumentation == null) return;
if (charOffset == -1 &&
(message.code == fasta_codes.codeConstConstructorWithBody ||
message.code == fasta_codes.codeConstructorNotFound ||
message.code == fasta_codes.codeSuperclassHasNoDefaultConstructor ||
message.code == fasta_codes.codeTypeArgumentsOnTypeVariable ||
message.code == fasta_codes.codeUnspecified)) {
// TODO(ahe): All warnings should have a charOffset, but currently, some
// warnings lack them.
return;
}
String severityString;
switch (severity) {
case Severity.error:
severityString = "error";
break;
case Severity.internalProblem:
severityString = "internal problem";
break;
case Severity.warning:
severityString = "warning";
break;
case Severity.errorLegacyWarning:
// Should have been resolved to either error or warning at this point.
// Use a property name expressing that, in case it slips through.
severityString = "unresolved severity";
break;
case Severity.context:
severityString = "context";
break;
case Severity.ignored:
unhandled("IGNORED", "recordMessage", charOffset, fileUri);
return;
}
instrumentation.record(
fileUri,
charOffset,
severityString,
// TODO(ahe): Should I add an InstrumentationValue for Message?
new InstrumentationValueLiteral(message.code.name));
if (context != null) {
for (LocatedMessage contextMessage in context) {
instrumentation.record(
contextMessage.uri,
contextMessage.charOffset,
"context",
new InstrumentationValueLiteral(contextMessage.code.name));
}
}
}
void releaseAncillaryResources() {
hierarchy = null;
typeInferenceEngine = null;
}
}
/// 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';
print(object) {}
class Iterator {}
class Iterable {}
class List extends Iterable {
factory List.unmodifiable(elements) => null;
}
class Map extends Iterable {
factory Map.unmodifiable(other) => null;
}
class NoSuchMethodError {
NoSuchMethodError.withInvocation(receiver, invocation);
}
class Null {}
class Object {
noSuchMethod(invocation) => null;
}
class String {}
class Symbol {}
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;
}
""";
/// A minimal implementation of dart:async that is sufficient to create an
/// instance of [CoreTypes] and compile program.
const String defaultDartAsyncSource = """
_asyncErrorWrapperHelper(continuation) {}
_asyncStackTraceHelper(async_op) {}
_asyncThenWrapperHelper(continuation) {}
_awaitHelper(object, thenCallback, errorCallback, awaiter) {}
_completeOnAsyncReturn(completer, value) {}
class _AsyncStarStreamController {
add(event) {}
addError(error, stackTrace) {}
addStream(stream) {}
close() {}
get stream => null;
}
class Completer {
factory Completer.sync() => null;
get future;
complete([value]);
completeError(error, [stackTrace]);
}
class Future {
factory Future.microtask(computation) => null;
}
class FutureOr {
}
class _AsyncAwaitCompleter implements Completer {
get future => null;
complete([value]) {}
completeError(error, [stackTrace]) {}
}
class Stream {}
class _StreamIterator {
get current => null;
moveNext() {}
cancel() {}
}
""";