From df1bacb60b3bfc271e329114dfd3befaedf6f13f Mon Sep 17 00:00:00 2001 From: Stephen Adams Date: Thu, 19 Jan 2017 19:49:30 -0800 Subject: [PATCH] dart2js-kernel: Call generative constructor body functions The generative constructor factory now calls the generative constructor body functions for the superclass chain. The generative constructor body elements are created on demand and compiled via the enqueuer. The call site is currently broken since it does not pass arguments. That will done in a separate CL. R=efortuna@google.com Review-Url: https://codereview.chromium.org/2637533003 . --- pkg/compiler/lib/src/elements/modelx.dart | 45 +++++++++ pkg/compiler/lib/src/ssa/builder.dart | 50 +--------- pkg/compiler/lib/src/ssa/builder_kernel.dart | 94 +++++++++++++------ .../lib/src/ssa/kernel_ast_adapter.dart | 12 ++- 4 files changed, 125 insertions(+), 76 deletions(-) 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.