Index: pkg/compiler/lib/src/kernel/kernel.dart |
diff --git a/pkg/compiler/lib/src/kernel/kernel.dart b/pkg/compiler/lib/src/kernel/kernel.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..34ac4f82d8607ca3d03d00669f57ef04c10a235b |
--- /dev/null |
+++ b/pkg/compiler/lib/src/kernel/kernel.dart |
@@ -0,0 +1,584 @@ |
+// 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.md file. |
+ |
+import 'dart:collection' show Queue; |
+ |
+import 'package:kernel/ast.dart' as ir; |
+import 'package:kernel/checks.dart' show CheckParentPointers; |
+import 'package:kernel/frontend/super_calls.dart' show moveSuperCallLast; |
+ |
+import '../compiler.dart' show Compiler; |
+import '../constants/expressions.dart' show TypeConstantExpression; |
+import '../dart_types.dart' |
+ show DartType, FunctionType, InterfaceType, TypeKind, TypeVariableType; |
+import '../diagnostics/messages.dart' show MessageKind; |
+import '../diagnostics/spannable.dart' show Spannable; |
+import '../elements/elements.dart' |
+ show |
+ ClassElement, |
+ ConstructorElement, |
+ Element, |
+ FieldElement, |
+ FunctionElement, |
+ LibraryElement, |
+ MixinApplicationElement, |
+ TypeVariableElement; |
+import '../elements/modelx.dart' show ErroneousFieldElementX; |
+import '../tree/tree.dart' show FunctionExpression, Node; |
+import 'kernel_visitor.dart' show IrFunction, KernelVisitor; |
+ |
+typedef void WorkAction(); |
+ |
+class WorkItem { |
+ final Element element; |
+ final WorkAction action; |
+ |
+ WorkItem(this.element, this.action); |
+} |
+ |
+class Kernel { |
+ final Compiler compiler; |
+ |
+ final Map<LibraryElement, ir.Library> libraries = |
+ <LibraryElement, ir.Library>{}; |
+ |
+ final Map<ClassElement, ir.Class> classes = <ClassElement, ir.Class>{}; |
+ |
+ final Map<FunctionElement, ir.Member> functions = |
+ <FunctionElement, ir.Member>{}; |
+ |
+ final Map<FieldElement, ir.Field> fields = <FieldElement, ir.Field>{}; |
+ |
+ final Map<TypeVariableElement, ir.TypeParameter> typeParameters = |
+ <TypeVariableElement, ir.TypeParameter>{}; |
+ |
+ final Map<TypeVariableElement, ir.TypeParameter> factoryTypeParameters = |
+ <TypeVariableElement, ir.TypeParameter>{}; |
+ |
+ final Set<ir.TreeNode> checkedNodes = new Set<ir.TreeNode>(); |
+ |
+ final Map<LibraryElement, Map<String, int>> mixinApplicationNamesByLibrary = |
+ <LibraryElement, Map<String, int>>{}; |
+ |
+ /// FIFO queue of work that needs to be completed before the returned AST |
+ /// nodes are correct. |
+ final Queue<WorkItem> workQueue = new Queue<WorkItem>(); |
+ |
+ Kernel(this.compiler); |
+ |
+ void addWork(Element element, WorkAction action) { |
+ workQueue.addLast(new WorkItem(element, action)); |
+ } |
+ |
+ void checkMember(Element key, ir.TreeNode value) { |
+ if (!checkedNodes.add(value)) return; |
+ if (value.parent == null) { |
+ internalError(key, "Missing parent on IR node."); |
+ } |
+ try { |
+ CheckParentPointers.check(value); |
+ } catch (e, s) { |
+ internalError(key, "$e\n$s"); |
+ } |
+ } |
+ |
+ void checkLibrary(Element key, ir.Library library) { |
+ if (!checkedNodes.add(library)) return; |
+ CheckParentPointers.check(library); |
+ } |
+ |
+ void processWorkQueue() { |
+ while (workQueue.isNotEmpty) { |
+ WorkItem work = workQueue.removeFirst(); |
+ work.action(); |
+ } |
+ assert(() { |
+ libraries.forEach(checkLibrary); |
+ classes.forEach(checkMember); |
+ functions.forEach(checkMember); |
+ fields.forEach(checkMember); |
+ return true; |
+ }); |
+ } |
+ |
+ ir.Name irName(String name, Element element) { |
+ ir.Library irLibrary = null; |
+ if (name.startsWith("_")) { |
+ ClassElement cls = element.enclosingClass; |
+ if (cls != null && cls.isMixinApplication) { |
+ MixinApplicationElement mixinApplication = cls; |
+ element = mixinApplication.mixin; |
+ } |
+ irLibrary = libraryToIr(element.library); |
+ } |
+ return new ir.Name(name, irLibrary); |
+ } |
+ |
+ ir.Library libraryToIr(LibraryElement library) { |
+ library = library.declaration; |
+ return libraries.putIfAbsent(library, () { |
+ String name = library.hasLibraryName ? library.libraryName : null; |
+ ir.Library libraryNode = new ir.Library(library.canonicalUri, |
+ name: name, classes: null, procedures: null, fields: null); |
+ addWork(library, () { |
+ Queue<ir.Class> classes = new Queue<ir.Class>(); |
+ Queue<ir.Member> members = new Queue<ir.Member>(); |
+ library.implementation.forEachLocalMember((Element e) { |
+ if (e.isClass) { |
+ classes.addFirst(classToIr(e)); |
+ } else if (e.isFunction || e.isAccessor) { |
+ members.addFirst(functionToIr(e)); |
+ } else if (e.isField) { |
+ members.addFirst(fieldToIr(e)); |
+ } else if (e.isTypedef) { |
+ // Ignored, typedefs are unaliased on use. |
+ } else { |
+ internalError(e, "Unhandled library member: $e"); |
+ } |
+ }); |
+ // The elements were inserted in reverse order as forEachLocalMember |
+ // above gives them in reversed order. |
+ classes.forEach(libraryNode.addClass); |
+ members.forEach(libraryNode.addMember); |
+ }); |
+ return libraryNode; |
+ }); |
+ } |
+ |
+ /// Compute a name for [cls]. We want to have unique names in a library, but |
+ /// mixin applications can lead to multiple classes with the same name. So |
+ /// for those we append `#` and a number. |
+ String computeName(ClassElement cls) { |
+ String name = cls.name; |
+ if (!cls.isUnnamedMixinApplication) return name; |
+ Map<String, int> mixinApplicationNames = mixinApplicationNamesByLibrary |
+ .putIfAbsent(cls.library.implementation, () => <String, int>{}); |
+ int count = mixinApplicationNames.putIfAbsent(name, () => 0); |
+ mixinApplicationNames[name] = count + 1; |
+ return "$name#$count"; |
+ } |
+ |
+ ir.Class classToIr(ClassElement cls) { |
+ cls = cls.declaration; |
+ return classes.putIfAbsent(cls, () { |
+ String name = computeName(cls); |
+ ir.Class classNode = new ir.Class( |
+ name: name, |
+ isAbstract: cls.isAbstract, |
+ typeParameters: null, |
+ implementedTypes: null, |
+ constructors: null, |
+ procedures: null, |
+ fields: null); |
+ addWork(cls, () { |
+ if (cls.supertype != null) { |
+ classNode.supertype = interfaceTypeToIr(cls.supertype); |
+ } |
+ classNode.parent = libraryToIr(cls.library); |
+ if (cls.isUnnamedMixinApplication) { |
+ classNode.enclosingLibrary.addClass(classNode); |
+ } |
+ cls.implementation |
+ .forEachMember((ClassElement enclosingClass, Element member) { |
+ if (member.enclosingClass.declaration != cls) { |
+ internalError(cls, "`$member` isn't mine."); |
+ } else if (member.isFunction || |
+ member.isAccessor || |
+ member.isConstructor) { |
+ classNode.addMember(functionToIr(member)); |
+ } else if (member.isField) { |
+ classNode.addMember(fieldToIr(member)); |
+ } else { |
+ internalError(member, "Unhandled class member: $member"); |
+ } |
+ }); |
+ classNode.typeParameters.addAll(typeVariablesToIr(cls.typeVariables)); |
+ for (ir.InterfaceType interface in typesToIr(cls.interfaces.toList())) { |
+ classNode.implementedTypes.add(interface); |
+ } |
+ }); |
+ return classNode; |
+ }); |
+ } |
+ |
+ bool hasHierarchyProblem(ClassElement cls) => cls.hasIncompleteHierarchy; |
+ |
+ ir.InterfaceType interfaceTypeToIr(InterfaceType type) { |
+ ir.Class cls = classToIr(type.element); |
+ if (type.typeArguments.isEmpty) { |
+ return cls.rawType; |
+ } else { |
+ return new ir.InterfaceType(cls, typesToIr(type.typeArguments)); |
+ } |
+ } |
+ |
+ // TODO(ahe): Remove this method when dart2js support generic type arguments. |
+ List<ir.TypeParameter> typeParametersNotImplemented() { |
+ return const <ir.TypeParameter>[]; |
+ } |
+ |
+ ir.FunctionType functionTypeToIr(FunctionType type) { |
+ List<ir.TypeParameter> typeParameters = typeParametersNotImplemented(); |
+ int requiredParameterCount = type.parameterTypes.length; |
+ List<ir.DartType> positionalParameters = |
+ new List<ir.DartType>.from(typesToIr(type.parameterTypes)) |
+ ..addAll(typesToIr(type.optionalParameterTypes)); |
+ Map<String, ir.DartType> namedParameters = <String, ir.DartType>{}; |
+ for (int i = 0; i < type.namedParameters.length; i++) { |
+ namedParameters[type.namedParameters[i]] = |
+ typeToIr(type.namedParameterTypes[i]); |
+ } |
+ ir.DartType returnType = typeToIr(type.returnType); |
+ |
+ return new ir.FunctionType(positionalParameters, returnType, |
+ namedParameters: namedParameters, |
+ typeParameters: typeParameters, |
+ requiredParameterCount: requiredParameterCount); |
+ } |
+ |
+ ir.TypeParameterType typeVariableTypeToIr(TypeVariableType type) { |
+ return new ir.TypeParameterType(typeVariableToIr(type.element)); |
+ } |
+ |
+ List<ir.DartType> typesToIr(List<DartType> types) { |
+ List<ir.DartType> result = new List<ir.DartType>(types.length); |
+ for (int i = 0; i < types.length; i++) { |
+ result[i] = typeToIr(types[i]); |
+ } |
+ return result; |
+ } |
+ |
+ ir.DartType typeToIr(DartType type) { |
+ switch (type.kind) { |
+ case TypeKind.FUNCTION: |
+ return functionTypeToIr(type); |
+ |
+ case TypeKind.INTERFACE: |
+ return interfaceTypeToIr(type); |
+ |
+ case TypeKind.STATEMENT: |
+ throw "Internal error: statement type: $type."; |
+ |
+ case TypeKind.TYPEDEF: |
+ type.computeUnaliased(compiler.resolution); |
+ return typeToIr(type.unaliased); |
+ |
+ case TypeKind.TYPE_VARIABLE: |
+ return typeVariableTypeToIr(type); |
+ |
+ case TypeKind.MALFORMED_TYPE: |
+ return const ir.InvalidType(); |
+ |
+ case TypeKind.DYNAMIC: |
+ return const ir.DynamicType(); |
+ |
+ case TypeKind.VOID: |
+ return const ir.VoidType(); |
+ } |
+ } |
+ |
+ ir.DartType typeLiteralToIr(TypeConstantExpression constant) { |
+ return typeToIr(constant.type); |
+ } |
+ |
+ void setParent(ir.Member member, Element element) { |
+ if (element.isLocal) { |
+ member.parent = elementToIr(element.enclosingElement); |
+ } else if (element.isTopLevel) { |
+ member.parent = elementToIr(element.library); |
+ } else if (element.isClassMember) { |
+ member.parent = elementToIr(element.enclosingClass); |
+ } else { |
+ member.parent = elementToIr(element.enclosingElement); |
+ } |
+ } |
+ |
+ bool isNativeMethod(FunctionElement element) { |
+ // This method is a (modified) copy of the same method in |
+ // `pkg/compiler/lib/src/native/enqueue.dart`. |
+ if (!compiler.backend.canLibraryUseNative(element.library)) return false; |
+ return compiler.reporter.withCurrentElement(element, () { |
+ FunctionExpression functionExpression = |
+ element.node?.asFunctionExpression(); |
+ if (functionExpression == null) return false; |
+ Node body = functionExpression.body; |
+ if (body == null) return false; |
+ if (identical(body.getBeginToken().stringValue, 'native')) return true; |
+ return false; |
+ }); |
+ } |
+ |
+ ir.Member functionToIr(FunctionElement function) { |
+ if (function.isDeferredLoaderGetter) { |
+ internalError(function, "Deferred loader."); |
+ } |
+ if (function.isLocal) { |
+ internalError(function, "Local function."); |
+ } |
+ if (isSyntheticError(function)) { |
+ internalError(function, "Synthetic error function: $function."); |
+ } |
+ function = function.declaration; |
+ return functions.putIfAbsent(function, () { |
+ function = function.implementation; |
+ ir.Member member; |
+ ir.Constructor constructor; |
+ ir.Procedure procedure; |
+ ir.Name name = irName(function.name, function); |
+ bool isNative = isNativeMethod(function); |
+ if (function.isGenerativeConstructor) { |
+ member = constructor = new ir.Constructor(null, |
+ name: name, |
+ isConst: function.isConst, |
+ isExternal: isNative || function.isExternal, |
+ initializers: null); |
+ } else { |
+ member = procedure = new ir.Procedure(name, null, null, |
+ isAbstract: function.isAbstract, |
+ isStatic: function.isStatic || |
+ function.isTopLevel || |
+ function.isFactoryConstructor, |
+ isExternal: isNative || function.isExternal, |
+ isConst: false); // TODO(ahe): When is this true? |
+ } |
+ addWork(function, () { |
+ setParent(member, function); |
+ KernelVisitor visitor = |
+ new KernelVisitor(function, function.treeElements, this); |
+ beginFactoryScope(function); |
+ IrFunction irFunction = visitor.buildFunction(); |
+ // TODO(ahe): Add addFunction/set function to [ir.Procedure]. |
+ irFunction.node.parent = member; |
+ if (irFunction.isConstructor) { |
+ assert(irFunction.kind == null); |
+ constructor.function = irFunction.node; |
+ constructor.initializers = irFunction.initializers; |
+ // TODO(ahe): Add setInitializers to [ir.Constructor]. |
+ for (ir.Initializer initializer in irFunction.initializers) { |
+ initializer.parent = constructor; |
+ } |
+ moveSuperCallLast(constructor); |
+ } else { |
+ assert(irFunction.kind != null); |
+ procedure.function = irFunction.node; |
+ procedure.kind = irFunction.kind; |
+ } |
+ endFactoryScope(function); |
+ assert(() { |
+ visitor.locals.forEach(checkMember); |
+ return true; |
+ }); |
+ }); |
+ return member; |
+ }); |
+ } |
+ |
+ /// Adds the type parameters of the enclosing class of [function] to |
+ /// [factoryTypeParameters]. This serves as a local scope for type variables |
+ /// resolved inside the factory. |
+ /// |
+ /// This method solves the problem that a factory method really is a generic |
+ /// method that has its own type parameters, one for each type parameter in |
+ /// the enclosing class. |
+ void beginFactoryScope(FunctionElement function) { |
+ assert(factoryTypeParameters.isEmpty); |
+ if (!function.isFactoryConstructor) return; |
+ ClassElement cls = function.enclosingClass; |
+ for (DartType type in cls.typeVariables) { |
+ if (type.isTypeVariable) { |
+ TypeVariableElement variable = type.element; |
+ factoryTypeParameters[variable] = |
+ new ir.TypeParameter(variable.name, null); |
+ } |
+ } |
+ for (DartType type in cls.typeVariables) { |
+ if (type.isTypeVariable) { |
+ TypeVariableElement variable = type.element; |
+ factoryTypeParameters[variable].bound = typeToIr(variable.bound); |
+ } |
+ } |
+ } |
+ |
+ /// Ends the local scope started by [beginFactoryScope]. |
+ void endFactoryScope(FunctionElement function) { |
+ factoryTypeParameters.clear(); |
+ } |
+ |
+ ir.Field fieldToIr(FieldElement field) { |
+ if (isSyntheticError(field)) { |
+ internalError(field, "Synthetic error field: $field."); |
+ } |
+ field = field.declaration; |
+ return fields.putIfAbsent(field, () { |
+ field = field.implementation; |
+ ir.DartType type = |
+ field.isMalformed ? const ir.InvalidType() : typeToIr(field.type); |
+ ir.Field fieldNode = new ir.Field(irName(field.memberName.text, field), |
+ type: type, |
+ initializer: null, |
+ isFinal: field.isFinal, |
+ isStatic: field.isStatic || field.isTopLevel, |
+ isConst: field.isConst); |
+ addWork(field, () { |
+ setParent(fieldNode, field); |
+ if (!field.isMalformed && |
+ !field.isInstanceMember && |
+ field.initializer != null) { |
+ KernelVisitor visitor = |
+ new KernelVisitor(field, field.treeElements, this); |
+ fieldNode.initializer = visitor.buildInitializer() |
+ ..parent = fieldNode; |
+ } |
+ }); |
+ return fieldNode; |
+ }); |
+ } |
+ |
+ ir.TypeParameter typeVariableToIr(TypeVariableElement variable) { |
+ variable = variable.declaration; |
+ ir.TypeParameter parameter = factoryTypeParameters[variable]; |
+ if (parameter != null) return parameter; |
+ return typeParameters.putIfAbsent(variable, () { |
+ ir.TypeParameter parameter = new ir.TypeParameter(variable.name, null); |
+ addWork(variable, () { |
+ // TODO(ahe): This assignment will probably not be correct when dart2js |
+ // supports generic methods. |
+ ClassElement cls = variable.typeDeclaration; |
+ parameter.parent = classToIr(cls); |
+ parameter.bound = typeToIr(variable.bound); |
+ }); |
+ return parameter; |
+ }); |
+ } |
+ |
+ List<ir.TypeParameter> typeVariablesToIr(List<DartType> variables) { |
+ List<ir.TypeParameter> result = |
+ new List<ir.TypeParameter>(variables.length); |
+ for (int i = 0; i < variables.length; i++) { |
+ TypeVariableType type = variables[i]; |
+ result[i] = typeVariableToIr(type.element); |
+ } |
+ return result; |
+ } |
+ |
+ ir.TreeNode elementToIr(Element element) { |
+ if (element.isLibrary) return libraryToIr(element); |
+ if (element.isClass) return classToIr(element); |
+ if (element.isFunction || element.isAccessor) return functionToIr(element); |
+ if (element.isField) return fieldToIr(element); |
+ throw "unhandled element: $element"; |
+ } |
+ |
+ void debugMessage(Spannable spannable, String message) { |
+ compiler.reporter |
+ .reportHintMessage(spannable, MessageKind.GENERIC, {'text': message}); |
+ } |
+ |
+ void internalError(Spannable spannable, String message) { |
+ compiler.reporter.internalError(spannable, message); |
+ throw message; |
+ } |
+ |
+ ConstructorTarget computeEffectiveTarget( |
+ ConstructorElement constructor, DartType type) { |
+ constructor = constructor.implementation; |
+ Set<ConstructorElement> seen = new Set<ConstructorElement>(); |
+ functionToIr(constructor); |
+ while (constructor != constructor.effectiveTarget) { |
+ type = constructor.computeEffectiveTargetType(type); |
+ if (constructor.isGenerativeConstructor) break; |
+ if (!seen.add(constructor)) break; |
+ constructor = constructor.effectiveTarget.implementation; |
+ if (isSyntheticError(constructor)) break; |
+ functionToIr(constructor); |
+ } |
+ return new ConstructorTarget(constructor, type); |
+ } |
+ |
+ /// Returns true if [element] is synthesized to recover or represent a |
+ /// semantic error, for example, missing, duplicated, or ambiguous elements. |
+ /// However, returns false for elements that have an unrecoverable syntax |
+ /// error. Both kinds of element will return true from [Element.isMalformed], |
+ /// but they must be handled differently. For example, a static call to |
+ /// synthetic error element should be compiled to [ir.InvalidExpression], |
+ /// whereas a static call to a method which has a syntax error should be |
+ /// compiled to a static call to the method. The method itself will have a |
+ /// method body that is [ir.InvalidStatement]. |
+ bool isSyntheticError(Element element) { |
+ if (element.isAmbiguous) return true; |
+ if (element.isError) return true; |
+ if (element.isField && element is ErroneousFieldElementX) { |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ ir.Procedure getDartCoreMethod(String name) { |
+ LibraryElement library = |
+ compiler.libraryLoader.lookupLibrary(Uri.parse("dart:core")); |
+ Element function = library.implementation.localLookup(name); |
+ return functionToIr(function); |
+ } |
+ |
+ ir.Procedure getMalformedTypeErrorBuilder() { |
+ return getDartCoreMethod('_malformedTypeError'); |
+ } |
+ |
+ ir.Procedure getUnresolvedConstructorBuilder() { |
+ return getDartCoreMethod('_unresolvedConstructorError'); |
+ } |
+ |
+ ir.Procedure getUnresolvedStaticGetterBuilder() { |
+ return getDartCoreMethod('_unresolvedStaticGetterError'); |
+ } |
+ |
+ ir.Procedure getUnresolvedStaticSetterBuilder() { |
+ return getDartCoreMethod('_unresolvedStaticSetterError'); |
+ } |
+ |
+ ir.Procedure getUnresolvedStaticMethodBuilder() { |
+ return getDartCoreMethod('_unresolvedStaticMethodError'); |
+ } |
+ |
+ ir.Procedure getUnresolvedTopLevelGetterBuilder() { |
+ return getDartCoreMethod('_unresolvedTopLevelGetterError'); |
+ } |
+ |
+ ir.Procedure getUnresolvedTopLevelSetterBuilder() { |
+ return getDartCoreMethod('_unresolvedTopLevelSetterError'); |
+ } |
+ |
+ ir.Procedure getUnresolvedTopLevelMethodBuilder() { |
+ return getDartCoreMethod('_unresolvedTopLevelMethodError'); |
+ } |
+ |
+ ir.Procedure getUnresolvedSuperGetterBuilder() { |
+ return getDartCoreMethod('_unresolvedSuperGetterError'); |
+ } |
+ |
+ ir.Procedure getUnresolvedSuperSetterBuilder() { |
+ return getDartCoreMethod('_unresolvedSuperSetterError'); |
+ } |
+ |
+ ir.Procedure getUnresolvedSuperMethodBuilder() { |
+ return getDartCoreMethod('_unresolvedSuperMethodError'); |
+ } |
+ |
+ ir.Procedure getGenericNoSuchMethodBuilder() { |
+ return getDartCoreMethod('_genericNoSuchMethod'); |
+ } |
+ |
+ ir.Procedure getFallThroughErrorBuilder() { |
+ return getDartCoreMethod('_fallThroughError'); |
+ } |
+} |
+ |
+class ConstructorTarget { |
+ final ConstructorElement element; |
+ final DartType type; |
+ |
+ ConstructorTarget(this.element, this.type); |
+ |
+ String toString() => "ConstructorTarget($element, $type)"; |
+} |