Chromium Code Reviews| Index: pkg/compiler/lib/src/ssa/builder_kernel.dart |
| diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart |
| index 9d1d2bf0699bb4bac62fbfe4174bef5dfd141cbc..8a0e17737cafae284b0c4cb9e45a40fafd42267e 100644 |
| --- a/pkg/compiler/lib/src/ssa/builder_kernel.dart |
| +++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart |
| @@ -158,6 +158,7 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { |
| if (targetElement is ConstructorBodyElement) { |
| _targetIsConstructorBody = true; |
| } |
| + _targetStack.add(target); |
| } |
| HGraph build() { |
| @@ -219,67 +220,123 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { |
| return result; |
| } |
| + /// Extend current method parameters with parameters for the class type |
| + /// parameters. If the class has type parameters but does not need them, bind |
| + /// to `dynamic` (represented as `null`) so the bindings are available for |
| + /// building types up the inheritance chain of generative constructors. |
| void _addClassTypeVariablesIfNeeded(ir.Member constructor) { |
| var enclosing = constructor.enclosingClass; |
| - if (rtiNeed.classNeedsRti(astAdapter.getElement(enclosing))) { |
| - enclosing.typeParameters.forEach((ir.TypeParameter typeParameter) { |
| - var typeParamElement = astAdapter.getElement(typeParameter); |
| - HParameterValue param = |
| - addParameter(typeParamElement, commonMasks.nonNullType); |
| - // This is a little bit wacky (and n^2) until we make the localsHandler |
| - // take Kernel DartTypes instead of just the AST DartTypes. |
| - var typeVariableType = astAdapter |
| - .getClass(enclosing) |
| - .typeVariables |
| - .firstWhere( |
| - (ResolutionTypeVariableType i) => i.name == typeParameter.name); |
| - localsHandler.directLocals[ |
| - localsHandler.getTypeVariableAsLocal(typeVariableType)] = param; |
| - }); |
| - } |
| + bool needParameters; |
| + enclosing.typeParameters.forEach((ir.TypeParameter typeParameter) { |
| + var typeParamElement = astAdapter.getElement(typeParameter); |
| + HInstruction param; |
| + needParameters ??= |
| + rtiNeed.classNeedsRti(astAdapter.getElement(enclosing)); |
| + if (needParameters) { |
| + param = addParameter(typeParamElement, commonMasks.nonNullType); |
| + } else { |
| + // Unused, so bind to `dynamic`. |
| + param = graph.addConstantNull(closedWorld); |
| + } |
| + // This is a little bit wacky (and n^2) until we make the localsHandler |
| + // take Kernel DartTypes instead of just the AST DartTypes. |
| + var typeVariableType = astAdapter |
| + .getClass(enclosing) |
| + .typeVariables |
| + .firstWhere( |
| + (ResolutionTypeVariableType i) => i.name == typeParameter.name); |
| + localsHandler.directLocals[ |
| + localsHandler.getTypeVariableAsLocal(typeVariableType)] = param; |
| + }); |
| } |
| - /// Builds generative constructors. |
| + /// Builds a generative constructor. |
| + /// |
| + /// Generative constructors are built in stages, in effect inlining the |
| + /// initializers and constructor bodies up the inheritance chain. |
| + /// |
| + /// 1. Extend method parameters with parameters the class's type parameters. |
|
Siggi Cherem (dart-lang)
2017/04/27 00:32:27
with parameters the => with the
|
| + /// |
| + /// 2. Add type checks for value parameters (might need result of (1)). |
| + /// |
| + /// 3. Walk inheritance chain to build bindings for type parameters of |
| + /// superclasses and mixed-in classes. |
| + /// |
| + /// 4. Collect initializer values. Walk up inheritance chain to collect field |
| + /// initializers from field declarations, initializing parameters and |
| + /// initializer. |
|
Siggi Cherem (dart-lang)
2017/04/27 00:32:27
"intializer" => "initializer assignments"? or "ini
|
| + /// |
| + /// 5. Create reified type information for instance. |
|
Siggi Cherem (dart-lang)
2017/04/27 00:32:27
nit: for instance => for the object instance? (I r
|
| + /// |
| + /// 6. Allocate instance and assign initializers and reified type information |
|
Siggi Cherem (dart-lang)
2017/04/27 00:32:27
instance => object instance
|
| + /// to fields by calling JavaScript constructor. |
| /// |
| - /// Generative constructors are built in two stages. |
| + /// 7. Walk inheritance chain to call or inline constructor bodies. |
| /// |
| - /// First, the field values for every instance field for every class in the |
| - /// class hierarchy are collected. Then, create a function body that sets |
| - /// all of the instance fields to the collected values and call the |
| - /// constructor bodies for all constructors in the hierarchy. |
| + /// All the bindings are put in the constructor's locals handler. The |
| + /// implication is that a class cannot be extended or mixed-in twice. If we in |
| + /// future support repeated uses of a mixin class, we should do so by cloning |
|
Siggi Cherem (dart-lang)
2017/04/27 00:32:27
in future => in the future
|
| + /// the mixin class in the Kernel input. |
| void buildConstructor(ir.Constructor constructor) { |
| + ir.Class constructedClass = constructor.enclosingClass; |
| + |
| openFunction(); |
| _addClassTypeVariablesIfNeeded(constructor); |
| + // TODO(sra): Type parameter constraint checks. |
| + |
| + // TODO(sra): Checked mode parameter checks. |
| + |
| // Collect field values for the current class. |
| - // TODO(het): Does kernel always put field initializers in the constructor |
| - // initializer list? If so then this is unnecessary... |
| Map<ir.Field, HInstruction> fieldValues = |
| - _collectFieldValues(constructor.enclosingClass); |
| + _collectFieldValues(constructedClass); |
| List<ir.Constructor> constructorChain = <ir.Constructor>[]; |
| - |
| _buildInitializers(constructor, constructorChain, fieldValues); |
| final constructorArguments = <HInstruction>[]; |
| // Doing this instead of fieldValues.forEach because we haven't defined the |
| // order of the arguments here. We can define that with JElements. |
| - astAdapter.getClass(constructor.enclosingClass).forEachInstanceField( |
| + astAdapter.getClass(constructedClass).forEachInstanceField( |
| (ClassElement enclosingClass, FieldElement member) { |
| var value = fieldValues[astAdapter.getFieldFromElement(member)]; |
| + assert(value != null, |
| + 'No value for field ${member} aka ${astAdapter.getFieldFromElement(member)}'); |
| constructorArguments.add(value); |
| }, includeSuperAndInjectedMembers: true); |
| - // TODO(het): If the class needs runtime type information, add it as a |
| - // constructor argument. |
| + // Create the runtime type information, if needed. |
| + bool hasRtiInput = backend.rtiNeed |
| + .classNeedsRtiField(astAdapter.getClass(constructedClass)); |
| + if (hasRtiInput) { |
| + // Read the values of the type arguments and create a HTypeInfoExpression |
| + // to set on the newly create object. |
| + List<HInstruction> typeArguments = <HInstruction>[]; |
| + for (ir.DartType typeParameter |
| + in constructedClass.thisType.typeArguments) { |
| + HInstruction argument = localsHandler.readLocal(localsHandler |
| + .getTypeVariableAsLocal(astAdapter.getDartType(typeParameter) |
| + as ResolutionTypeVariableType)); |
| + typeArguments.add(argument); |
| + } |
| + |
| + HInstruction typeInfo = new HTypeInfoExpression( |
| + TypeInfoExpressionKind.INSTANCE, |
| + astAdapter.getClass(constructedClass).thisType, |
| + typeArguments, |
| + commonMasks.dynamicType); |
| + add(typeInfo); |
| + constructorArguments.add(typeInfo); |
| + } |
| + |
| HInstruction newObject = new HCreate( |
| - astAdapter.getClass(constructor.enclosingClass), |
| + astAdapter.getClass(constructedClass), |
| constructorArguments, |
| new TypeMask.nonNullExact( |
| - astAdapter.getClass(constructor.enclosingClass), closedWorld), |
| + astAdapter.getClass(constructedClass), closedWorld), |
| instantiatedTypes: <ResolutionInterfaceType>[ |
| - astAdapter.getClass(constructor.enclosingClass).thisType |
| + astAdapter.getClass(constructedClass).thisType |
| ], |
| - hasRtiInput: false); |
| + hasRtiInput: hasRtiInput); |
| add(newObject); |
| @@ -316,7 +373,16 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { |
| bodyCallInputs.add(localsHandler.readLocal(scopeData.boxElement)); |
| } |
| - // TODO(sra): Pass type arguments. |
| + // Pass type arguments. |
| + ir.Class currentClass = body.enclosingClass; |
| + if (backend.rtiNeed.classNeedsRti(astAdapter.getClass(currentClass))) { |
| + for (ir.DartType typeParameter in currentClass.thisType.typeArguments) { |
| + HInstruction argument = localsHandler.readLocal(localsHandler |
| + .getTypeVariableAsLocal(astAdapter.getDartType(typeParameter) |
| + as ResolutionTypeVariableType)); |
| + bodyCallInputs.add(argument); |
| + } |
| + } |
| _invokeConstructorBody(body, bodyCallInputs); |
| } |
| @@ -341,6 +407,27 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { |
| add(invoke); |
| } |
| + withCurrentIrNode(ir.Node node, f()) { |
| + compiler.reporter.withCurrentElement(astAdapter.getElement(node), f); |
| + } |
| + |
| + /// Sets context for generating code that is the result of inlining |
| + /// [inlinedTarget]. |
| + inlinedFrom(ir.TreeNode inlinedTarget, f()) { |
| + withCurrentIrNode(inlinedTarget, () { |
| + SourceInformationBuilder oldSourceInformationBuilder = |
| + sourceInformationBuilder; |
| + // TODO(sra): Update sourceInformationBuilder to Kernel. |
| + // sourceInformationBuilder = |
| + // sourceInformationBuilder.forContext(resolvedAst); |
| + _targetStack.add(inlinedTarget); |
| + var result = f(); |
| + sourceInformationBuilder = oldSourceInformationBuilder; |
| + _targetStack.removeLast(); |
| + return result; |
| + }); |
| + } |
| + |
| /// Maps the instance fields of a class to their SSA values. |
| Map<ir.Field, HInstruction> _collectFieldValues(ir.Class clazz) { |
| final fieldValues = <ir.Field, HInstruction>{}; |
| @@ -353,8 +440,10 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { |
| // Gotta update the resolvedAst when we're looking at field values |
| // outside the constructor. |
| astAdapter.pushResolvedAst(field); |
| - field.initializer.accept(this); |
| - fieldValues[field] = pop(); |
| + inlinedFrom(field, () { |
| + field.initializer.accept(this); |
| + fieldValues[field] = pop(); |
| + }); |
| astAdapter.popResolvedAstStack(); |
| } |
| } |
| @@ -368,26 +457,36 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { |
| ir.Constructor constructor, |
| List<ir.Constructor> constructorChain, |
| Map<ir.Field, HInstruction> fieldValues) { |
| + astAdapter.assertAtResolvedAstFor(constructor); |
| constructorChain.add(constructor); |
| + |
| var foundSuperOrRedirectCall = false; |
| for (var initializer in constructor.initializers) { |
| - if (initializer is ir.SuperInitializer || |
| - initializer is ir.RedirectingInitializer) { |
| - foundSuperOrRedirectCall = true; |
| - var superOrRedirectConstructor = initializer.target; |
| - var arguments = _normalizeAndBuildArguments( |
| - superOrRedirectConstructor.function, initializer.arguments); |
| - _buildInlinedInitializers(superOrRedirectConstructor, arguments, |
| - constructorChain, fieldValues); |
| - } else if (initializer is ir.FieldInitializer) { |
| + if (initializer is ir.FieldInitializer) { |
| initializer.value.accept(this); |
| fieldValues[initializer.field] = pop(); |
| + } else if (initializer is ir.SuperInitializer) { |
| + assert(!foundSuperOrRedirectCall); |
| + foundSuperOrRedirectCall = true; |
| + _inlineSuperInitializer( |
| + initializer, constructorChain, fieldValues, constructor); |
| + } else if (initializer is ir.RedirectingInitializer) { |
| + assert(!foundSuperOrRedirectCall); |
| + foundSuperOrRedirectCall = true; |
| + _inlineRedirectingInitializer( |
| + initializer, constructorChain, fieldValues, constructor); |
| + } else if (initializer is ir.LocalInitializer) { |
| + assert(false, 'ir.LocalInitializer not handled'); |
| + } else if (initializer is ir.InvalidInitializer) { |
| + assert(false, 'ir.InvalidInitializer not handled'); |
| } |
| } |
| if (!foundSuperOrRedirectCall) { |
| - assert(constructor.enclosingClass == astAdapter.objectClass, |
| - 'All constructors have super-constructor initializers, except Object()'); |
| + assert( |
| + constructor.enclosingClass == astAdapter.objectClass, |
| + 'All constructors should have super- or redirecting- initializers,' |
| + ' except Object()'); |
| } |
| } |
| @@ -432,17 +531,82 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { |
| return builtArguments; |
| } |
| + /// Creates localsHandler bindings for type parameters of a Supertype. |
| + void _bindSupertypeTypeParameters(ir.Supertype supertype) { |
| + ir.Class cls = supertype.classNode; |
| + var parameters = cls.typeParameters; |
| + var arguments = supertype.typeArguments; |
| + assert(arguments.length == parameters.length); |
| + |
| + for (int i = 0; i < parameters.length; i++) { |
| + ir.DartType argument = arguments[i]; |
| + ir.TypeParameter parameter = parameters[i]; |
| + |
| + localsHandler.updateLocal( |
| + localsHandler.getTypeVariableAsLocal( |
| + astAdapter.getDartType(new ir.TypeParameterType(parameter))), |
| + typeBuilder.analyzeTypeArgument( |
| + astAdapter.getDartType(argument), sourceElement)); |
| + } |
| + } |
| + |
| + /// Inlines the given redirecting [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 _inlineRedirectingInitializer( |
| + ir.RedirectingInitializer initializer, |
| + List<ir.Constructor> constructorChain, |
| + Map<ir.Field, HInstruction> fieldValues, |
| + ir.Constructor caller) { |
| + var superOrRedirectConstructor = initializer.target; |
| + var arguments = _normalizeAndBuildArguments( |
| + superOrRedirectConstructor.function, initializer.arguments); |
| + |
| + // Redirecting initializer already has [localsHandler] bindings for type |
| + // parameters from the redirecting constructor. |
| + |
| + // For redirecting constructors, the fields will be initialized later by the |
| + // effective target, so we don't do it here. |
| + |
| + _inlineSuperOrRedirectCommon(initializer, superOrRedirectConstructor, |
| + arguments, constructorChain, fieldValues, caller); |
| + } |
| + |
| /// 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( |
| + void _inlineSuperInitializer( |
| + ir.SuperInitializer initializer, |
| + List<ir.Constructor> constructorChain, |
| + Map<ir.Field, HInstruction> fieldValues, |
| + ir.Constructor caller) { |
| + var target = initializer.target; |
| + var arguments = |
| + _normalizeAndBuildArguments(target.function, initializer.arguments); |
| + |
| + ir.Class callerClass = caller.enclosingClass; |
| + _bindSupertypeTypeParameters(callerClass.supertype); |
| + if (callerClass.mixedInType != null) { |
| + _bindSupertypeTypeParameters(callerClass.mixedInType); |
| + } |
| + |
| + ir.Class cls = target.enclosingClass; |
| + |
| + inlinedFrom(target, () { |
| + fieldValues.addAll(_collectFieldValues(cls)); |
| + }); |
| + |
| + _inlineSuperOrRedirectCommon( |
| + initializer, target, arguments, constructorChain, fieldValues, caller); |
| + } |
| + |
| + void _inlineSuperOrRedirectCommon( |
| + ir.Initializer initializer, |
| ir.Constructor constructor, |
| List<HInstruction> arguments, |
| List<ir.Constructor> constructorChain, |
| - Map<ir.Field, HInstruction> fieldValues) { |
| - // TODO(het): Handle RTI if class needs it |
| - fieldValues.addAll(_collectFieldValues(constructor.enclosingClass)); |
| - |
| + Map<ir.Field, HInstruction> fieldValues, |
| + ir.Constructor caller) { |
| var signature = astAdapter.getFunctionSignature(constructor.function); |
| var index = 0; |
| signature.orderedForEachParameter((ParameterElement parameter) { |
| @@ -454,14 +618,28 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { |
| localsHandler.updateLocal(parameter, argument); |
| }); |
| - // TODO(het): set the locals handler state as if we were inlining the |
| - // constructor. |
| - _buildInitializers(constructor, constructorChain, fieldValues); |
| + // Set the locals handler state as if we were inlining the constructor. |
| + astAdapter.pushResolvedAst(constructor); |
| + ResolvedAst resolvedAst = astAdapter.getElement(constructor).resolvedAst; |
| + ClosureClassMap oldClosureData = localsHandler.closureData; |
| + ClosureClassMap newClosureData = |
| + compiler.closureToClassMapper.getClosureToClassMapping(resolvedAst); |
| + localsHandler.closureData = newClosureData; |
| + if (resolvedAst.kind == ResolvedAstKind.PARSED) { |
| + localsHandler.enterScope( |
| + resolvedAst.node, astAdapter.getElement(constructor)); |
| + } |
| + inlinedFrom(constructor, () { |
| + _buildInitializers(constructor, constructorChain, fieldValues); |
| + }); |
| + localsHandler.closureData = oldClosureData; |
| + astAdapter.popResolvedAstStack(); |
| } |
| /// Builds generative constructor body. |
| void buildConstructorBody(ir.Constructor constructor) { |
| openFunction(); |
| + _addClassTypeVariablesIfNeeded(constructor); |
| constructor.function.body.accept(this); |
| closeFunction(); |
| } |
| @@ -547,22 +725,25 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { |
| // TODO(sra): Re-implement type builder using Kernel types and the |
| // `target` for context. |
| @override |
| - Element get sourceElement => _sourceElementForTarget(target); |
| + Element get sourceElement => _sourceElementForTarget(_targetStack.last); |
| + |
| + List<ir.Node> _targetStack = <ir.Node>[]; |
| Element _sourceElementForTarget(ir.Node target) { |
| // For closure-converted (i.e. local functions) the source element is the |
| // 'call' method of the class that represents the closure. |
| - if (target is ir.FunctionExpression) { |
| + Element callMethodOfClosureClass() { |
| LocalFunctionElement element = astAdapter.getElement(target); |
| ClosureClassMap classMap = |
| closureToClassMapper.getClosureToClassMapping(element.resolvedAst); |
| return classMap.callElement; |
| } |
| + |
| + if (target is ir.FunctionExpression) { |
| + return callMethodOfClosureClass(); |
| + } |
| if (target is ir.FunctionDeclaration) { |
| - LocalFunctionElement element = astAdapter.getElement(target); |
| - ClosureClassMap classMap = |
| - closureToClassMapper.getClosureToClassMapping(element.resolvedAst); |
| - return classMap.callElement; |
| + return callMethodOfClosureClass(); |
| } |
| Element element = astAdapter.getElement(target); |
| return element; |
| @@ -2020,36 +2201,44 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { |
| values.add(_defaultValueForParameter(parameter)); |
| } |
| - if (arguments.named.isEmpty) return values; |
| - |
| - var namedValues = <String, HInstruction>{}; |
| - for (ir.NamedExpression argument in arguments.named) { |
| - argument.value.accept(this); |
| - namedValues[argument.name] = pop(); |
| - } |
| + if (arguments.named.isNotEmpty) { |
| + var namedValues = <String, HInstruction>{}; |
| + for (ir.NamedExpression argument in arguments.named) { |
| + argument.value.accept(this); |
| + namedValues[argument.name] = pop(); |
| + } |
| - // Visit named arguments in parameter-position order, selecting provided or |
| - // default value. |
| - // TODO(sra): Ensure the stored order is canonical so we don't have to |
| - // sort. The old builder uses CallStructure.makeArgumentList which depends |
| - // on the old element model. |
| - var namedParameters = target.namedParameters.toList() |
| - ..sort((ir.VariableDeclaration a, ir.VariableDeclaration b) => |
| - a.name.compareTo(b.name)); |
| - for (ir.VariableDeclaration parameter in namedParameters) { |
| - HInstruction value = namedValues[parameter.name]; |
| - if (value == null) { |
| - values.add(_defaultValueForParameter(parameter)); |
| - } else { |
| - values.add(value); |
| - namedValues.remove(parameter.name); |
| + // Visit named arguments in parameter-position order, selecting provided |
| + // or default value. |
| + // TODO(sra): Ensure the stored order is canonical so we don't have to |
| + // sort. The old builder uses CallStructure.makeArgumentList which depends |
| + // on the old element model. |
| + var namedParameters = target.namedParameters.toList() |
| + ..sort((ir.VariableDeclaration a, ir.VariableDeclaration b) => |
| + a.name.compareTo(b.name)); |
| + for (ir.VariableDeclaration parameter in namedParameters) { |
| + HInstruction value = namedValues[parameter.name]; |
| + if (value == null) { |
| + values.add(_defaultValueForParameter(parameter)); |
| + } else { |
| + values.add(value); |
| + namedValues.remove(parameter.name); |
| + } |
| } |
| + assert(namedValues.isEmpty); |
| } |
| - assert(namedValues.isEmpty); |
| return values; |
| } |
| + void _addTypeArguments(List<HInstruction> values, ir.Arguments arguments) { |
| + // need to translate type to |
| + for (ir.DartType type in arguments.types) { |
| + values.add(typeBuilder.analyzeTypeArgument( |
| + astAdapter.getDartType(type), sourceElement)); |
| + } |
| + } |
| + |
| HInstruction _defaultValueForParameter(ir.VariableDeclaration parameter) { |
| ir.Expression initializer = parameter.initializer; |
| if (initializer == null) return graph.addConstantNull(closedWorld); |
| @@ -2074,6 +2263,15 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { |
| List<HInstruction> arguments = |
| _visitArgumentsForStaticTarget(target.function, invocation.arguments); |
| + // Factory constructors take type parameters; other static methods ignore |
| + // them. |
| + if (target.kind == ir.ProcedureKind.Factory) { |
| + if (backend.rtiNeed |
| + .classNeedsRti(astAdapter.getClass(target.enclosingClass))) { |
| + _addTypeArguments(arguments, invocation.arguments); |
| + } |
| + } |
| + |
| _pushStaticInvocation(target, arguments, typeMask); |
| } |
| @@ -2786,6 +2984,10 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { |
| // TODO(sra): For JS-interop targets, process arguments differently. |
| List<HInstruction> arguments = |
| _visitArgumentsForStaticTarget(target.function, invocation.arguments); |
| + if (backend.rtiNeed |
| + .classNeedsRti(astAdapter.getClass(target.enclosingClass))) { |
| + _addTypeArguments(arguments, invocation.arguments); |
| + } |
| TypeMask typeMask = new TypeMask.nonNullExact( |
| astAdapter.getClass(target.enclosingClass), closedWorld); |
| _pushStaticInvocation(target, arguments, typeMask); |