diff --git a/pkg/compiler/lib/src/elements/modelx.dart b/pkg/compiler/lib/src/elements/modelx.dart index 6ff57cc0812..7f887ac2000 100644 --- a/pkg/compiler/lib/src/elements/modelx.dart +++ b/pkg/compiler/lib/src/elements/modelx.dart @@ -2414,6 +2414,51 @@ class ConstructorBodyElementX extends BaseFunctionElementX functionSignature = constructor.functionSignature; } + /// Returns the constructor body associated with the given constructor or + /// creates a new constructor body, if none can be found. + /// + /// Returns `null` if the constructor does not have a body. + static ConstructorBodyElementX createFromResolvedAst( + ResolvedAst constructorResolvedAst) { + ConstructorElement constructor = + constructorResolvedAst.element.implementation; + assert(constructor.isGenerativeConstructor); + if (constructorResolvedAst.kind != ResolvedAstKind.PARSED) return null; + + FunctionExpression node = constructorResolvedAst.node; + // If we know the body doesn't have any code, we don't generate it. + if (!node.hasBody) return null; + if (node.hasEmptyBody) return null; + ClassElement classElement = constructor.enclosingClass; + ConstructorBodyElement bodyElement; + classElement.forEachBackendMember((Element backendMember) { + if (backendMember.isGenerativeConstructorBody) { + ConstructorBodyElement body = backendMember; + if (body.constructor == constructor) { + // TODO(kasperl): Find a way of stopping the iteration + // through the backend members. + bodyElement = backendMember; + } + } + }); + if (bodyElement == null) { + bodyElement = + new ConstructorBodyElementX(constructorResolvedAst, constructor); + classElement.addBackendMember(bodyElement); + + if (constructor.isPatch) { + // Create origin body element for patched constructors. + ConstructorBodyElementX patch = bodyElement; + ConstructorBodyElementX origin = new ConstructorBodyElementX( + constructorResolvedAst, constructor.origin); + origin.applyPatch(patch); + classElement.origin.addBackendMember(bodyElement.origin); + } + } + assert(bodyElement.isGenerativeConstructorBody); + return bodyElement; + } + bool get hasNode => _resolvedAst.kind == ResolvedAstKind.PARSED; FunctionExpression get node => _resolvedAst.node; diff --git a/pkg/compiler/lib/src/ssa/builder.dart b/pkg/compiler/lib/src/ssa/builder.dart index 824d7745778..afa7f375c29 100644 --- a/pkg/compiler/lib/src/ssa/builder.dart +++ b/pkg/compiler/lib/src/ssa/builder.dart @@ -797,53 +797,6 @@ class SsaBuilder extends ast.Visitor return closeFunction(); } - /** - * Returns the constructor body associated with the given constructor or - * creates a new constructor body, if none can be found. - * - * Returns [:null:] if the constructor does not have a body. - */ - ConstructorBodyElement getConstructorBody( - ResolvedAst constructorResolvedAst) { - ConstructorElement constructor = - constructorResolvedAst.element.implementation; - assert(constructor.isGenerativeConstructor); - if (constructorResolvedAst.kind != ResolvedAstKind.PARSED) return null; - - ast.FunctionExpression node = constructorResolvedAst.node; - // If we know the body doesn't have any code, we don't generate it. - if (!node.hasBody) return null; - if (node.hasEmptyBody) return null; - ClassElement classElement = constructor.enclosingClass; - ConstructorBodyElement bodyElement; - classElement.forEachBackendMember((Element backendMember) { - if (backendMember.isGenerativeConstructorBody) { - ConstructorBodyElement body = backendMember; - if (body.constructor == constructor) { - // TODO(kasperl): Find a way of stopping the iteration - // through the backend members. - bodyElement = backendMember; - } - } - }); - if (bodyElement == null) { - bodyElement = - new ConstructorBodyElementX(constructorResolvedAst, constructor); - classElement.addBackendMember(bodyElement); - - if (constructor.isPatch) { - // Create origin body element for patched constructors. - ConstructorBodyElementX patch = bodyElement; - ConstructorBodyElementX origin = new ConstructorBodyElementX( - constructorResolvedAst, constructor.origin); - origin.applyPatch(patch); - classElement.origin.addBackendMember(bodyElement.origin); - } - } - assert(bodyElement.isGenerativeConstructorBody); - return bodyElement; - } - /** * This method sets up the local state of the builder for inlining [function]. * The arguments of the function are inserted into the [localsHandler]. @@ -1351,7 +1304,8 @@ class SsaBuilder extends ast.Visitor HInstruction interceptor = null; for (int index = constructorResolvedAsts.length - 1; index >= 0; index--) { ResolvedAst constructorResolvedAst = constructorResolvedAsts[index]; - ConstructorBodyElement body = getConstructorBody(constructorResolvedAst); + ConstructorBodyElement body = + ConstructorBodyElementX.createFromResolvedAst(constructorResolvedAst); if (body == null) continue; List bodyCallInputs = []; diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart index 8dd05aed6f7..399e9c46387 100644 --- a/pkg/compiler/lib/src/ssa/builder_kernel.dart +++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart @@ -18,6 +18,7 @@ import '../constants/values.dart' TypeConstantValue; import '../elements/resolution_types.dart'; import '../elements/elements.dart'; +import '../elements/entities.dart' show MemberEntity; import '../io/source_information.dart'; import '../js/js.dart' as js; import '../js_backend/backend.dart' show JavaScriptBackend; @@ -91,6 +92,7 @@ class SsaKernelBuilderTask extends CompilerTask { class KernelSsaBuilder extends ir.Visitor with GraphBuilder { ir.Node target; + bool _targetIsConstructorBody = false; final AstElement targetElement; final ResolvedAst resolvedAst; final ClosedWorld closedWorld; @@ -156,11 +158,17 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { originTarget = originTarget.origin; } if (originTarget is FunctionElement) { + if (originTarget is ConstructorBodyElement) { + ConstructorBodyElement body = originTarget; + _targetIsConstructorBody = true; + originTarget = body.constructor; + } target = kernel.functions[originTarget]; // Closures require a lookup one level deeper in the closure class mapper. if (target == null) { + FunctionElement originTargetFunction = originTarget; ClosureClassMap classMap = compiler.closureToClassMapper - .getClosureToClassMapping(originTarget.resolvedAst); + .getClosureToClassMapping(originTargetFunction.resolvedAst); if (classMap.closureElement != null) { target = kernel.localFunctions[classMap.closureElement]; } @@ -179,7 +187,11 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { } else if (target is ir.Field) { buildField(target); } else if (target is ir.Constructor) { - buildConstructor(target); + if (_targetIsConstructorBody) { + buildConstructorBody(target); + } else { + buildConstructor(target); + } } else if (target is ir.FunctionExpression) { _targetFunction = (target as ir.FunctionExpression).function; buildFunctionNode(_targetFunction); @@ -187,7 +199,7 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { _targetFunction = (target as ir.FunctionDeclaration).function; buildFunctionNode(_targetFunction); } else { - throw 'No case implemented to handle $target'; + throw 'No case implemented to handle target: $target'; } assert(graph.isValid()); return graph; @@ -261,8 +273,9 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { // initializer list? If so then this is unnecessary... Map fieldValues = _collectFieldValues(constructor.enclosingClass); + List constructorChain = []; - _buildInitializers(constructor, fieldValues); + _buildInitializers(constructor, constructorChain, fieldValues); final constructorArguments = []; astAdapter.getClass(constructor.enclosingClass).forEachInstanceField( @@ -273,7 +286,7 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { // TODO(het): If the class needs runtime type information, add it as a // constructor argument. - HInstruction create = new HCreate( + HInstruction newObject = new HCreate( astAdapter.getClass(constructor.enclosingClass), constructorArguments, new TypeMask.nonNullExact( @@ -283,14 +296,40 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { ], hasRtiInput: false); - add(create); + add(newObject); // Generate calls to the constructor bodies. - closeAndGotoExit(new HReturn(create, null)); + for (ir.Constructor body in constructorChain.reversed) { + if (_isEmptyStatement(body.function.body)) continue; + + List bodyCallInputs = []; + bodyCallInputs.add(newObject); + + // TODO(sra): Pass arguments, boxes and type parameters. + _invokeConstructorBody(body, bodyCallInputs); + } + + closeAndGotoExit(new HReturn(newObject, null)); closeFunction(); } + static bool _isEmptyStatement(ir.Statement body) { + if (body is ir.EmptyStatement) return true; + if (body is ir.Block) return body.statements.every(_isEmptyStatement); + return false; + } + + void _invokeConstructorBody( + ir.Constructor constructor, List inputs) { + // TODO(sra): Inline the constructor body. + MemberEntity constructorBody = + astAdapter.getConstructorBodyEntity(constructor); + HInvokeConstructorBody invoke = new HInvokeConstructorBody( + constructorBody, inputs, commonMasks.nonNullType); + add(invoke); + } + /// Maps the instance fields of a class to their SSA values. Map _collectFieldValues(ir.Class clazz) { final fieldValues = {}; @@ -315,7 +354,10 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { /// Collects field initializers all the way up the inheritance chain. void _buildInitializers( - ir.Constructor constructor, Map fieldValues) { + ir.Constructor constructor, + List constructorChain, + Map fieldValues) { + constructorChain.add(constructor); var foundSuperOrRedirectCall = false; for (var initializer in constructor.initializers) { if (initializer is ir.SuperInitializer || @@ -324,29 +366,17 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { var superOrRedirectConstructor = initializer.target; var arguments = _normalizeAndBuildArguments( superOrRedirectConstructor.function, initializer.arguments); - _buildInlinedInitializers( - superOrRedirectConstructor, arguments, fieldValues); + _buildInlinedInitializers(superOrRedirectConstructor, arguments, + constructorChain, fieldValues); } else if (initializer is ir.FieldInitializer) { initializer.value.accept(this); fieldValues[initializer.field] = pop(); } } - // Kernel always set the super initializer at the end, so if there was no - // super-call initializer, then the default constructor is called in the - // superclass. if (!foundSuperOrRedirectCall) { - if (constructor.enclosingClass != astAdapter.objectClass) { - var superclass = constructor.enclosingClass.superclass; - var defaultConstructor = superclass.constructors - .firstWhere((c) => c.name.name == '', orElse: () => null); - if (defaultConstructor == null) { - compiler.reporter.internalError( - NO_LOCATION_SPANNABLE, 'Could not find default constructor.'); - } - _buildInlinedInitializers( - defaultConstructor, [], fieldValues); - } + assert(constructor.enclosingClass == astAdapter.objectClass, + 'All constructors have super-constructor initializers, except Object()'); } } @@ -396,8 +426,11 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { /// Inlines the given super [constructor]'s initializers by collecting its /// field values and building its constructor initializers. We visit super /// constructors all the way up to the [Object] constructor. - void _buildInlinedInitializers(ir.Constructor constructor, - List arguments, Map fieldValues) { + void _buildInlinedInitializers( + ir.Constructor constructor, + List arguments, + List constructorChain, + Map fieldValues) { // TODO(het): Handle RTI if class needs it fieldValues.addAll(_collectFieldValues(constructor.enclosingClass)); @@ -414,7 +447,7 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { // TODO(het): set the locals handler state as if we were inlining the // constructor. - _buildInitializers(constructor, fieldValues); + _buildInitializers(constructor, constructorChain, fieldValues); } HTypeConversion buildFunctionTypeConversion( @@ -424,6 +457,13 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { type, kind, original.instructionType, reifiedType, original); } + /// Builds generative constructor body. + void buildConstructorBody(ir.Constructor constructor) { + openFunction(); + constructor.function.body.accept(this); + closeFunction(); + } + /// Builds a SSA graph for FunctionNodes, found in FunctionExpressions and /// Procedures. void buildFunctionNode(ir.FunctionNode functionNode) { diff --git a/pkg/compiler/lib/src/ssa/kernel_ast_adapter.dart b/pkg/compiler/lib/src/ssa/kernel_ast_adapter.dart index 1337bd5ca82..6c6425a0045 100644 --- a/pkg/compiler/lib/src/ssa/kernel_ast_adapter.dart +++ b/pkg/compiler/lib/src/ssa/kernel_ast_adapter.dart @@ -12,6 +12,7 @@ import '../constants/expressions.dart'; import '../constants/values.dart'; import '../elements/resolution_types.dart'; import '../elements/elements.dart'; +import '../elements/entities.dart' show MemberEntity; import '../elements/modelx.dart'; import '../js/js.dart' as js; import '../js_backend/backend_helpers.dart'; @@ -103,7 +104,8 @@ class KernelAstAdapter { _compiler.globalInference.results; GlobalTypeInferenceElementResult _resultOf(Element e) => - _globalInferenceResults.resultOf(e); + _globalInferenceResults + .resultOf(e is ConstructorBodyElementX ? e.constructor : e); ConstantValue getConstantForSymbol(ir.SymbolLiteral node) { if (kernel.syntheticNodes.contains(node)) { @@ -813,6 +815,14 @@ class KernelAstAdapter { metadata, _typeLookup(resolveAsRaw: false), _compiler, isJsInterop: false); } + + MemberEntity getConstructorBodyEntity(ir.Constructor constructor) { + AstElement element = getElement(constructor); + MemberEntity constructorBody = + ConstructorBodyElementX.createFromResolvedAst(element.resolvedAst); + assert(constructorBody != null); + return constructorBody; + } } /// Kinds of foreign functions.