Chromium Code Reviews| Index: pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart |
| diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart |
| index 3a1ea5d47e1adabff139e5fcf01f7eb8da9e672b..b3ad059f0b9dbab72dbbcceb413be8757ed35475 100644 |
| --- a/pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart |
| +++ b/pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart |
| @@ -15,6 +15,9 @@ import '../scanner/scannerlib.dart' show Token, isUserDefinableOperator; |
| import '../universe/universe.dart' show SelectorKind; |
| import 'cps_ir_nodes.dart' as ir; |
| import '../elements/modelx.dart' show SynthesizedConstructorElementX; |
| +import '../closure.dart'; |
| +import '../closure.dart' as closurelib; |
| +import '../js_backend/js_backend.dart' show JavaScriptBackend; |
| part 'cps_ir_builder_visitor.dart'; |
| @@ -25,17 +28,17 @@ part 'cps_ir_builder_visitor.dart'; |
| /// assigned indexes which can be used to refer to them. |
| class Environment { |
| /// A map from elements to their environment index. |
| - final Map<Element, int> variable2index; |
| + final Map<Local, int> variable2index; |
| /// A reverse map from environment indexes to the variable. |
| - final List<Element> index2variable; |
| + final List<Local> index2variable; |
| /// A map from environment indexes to their value. |
| final List<ir.Primitive> index2value; |
| Environment.empty() |
| - : variable2index = <Element, int>{}, |
| - index2variable = <Element>[], |
| + : variable2index = <Local, int>{}, |
| + index2variable = <Local>[], |
| index2value = <ir.Primitive>[]; |
| /// Construct an environment that is a copy of another one. |
| @@ -43,14 +46,14 @@ class Environment { |
| /// The mapping from elements to indexes is shared, not copied. |
| Environment.from(Environment other) |
| : variable2index = other.variable2index, |
| - index2variable = new List<Element>.from(other.index2variable), |
| + index2variable = new List<Local>.from(other.index2variable), |
| index2value = new List<ir.Primitive>.from(other.index2value); |
| get length => index2variable.length; |
| ir.Primitive operator [](int index) => index2value[index]; |
| - void extend(Element element, ir.Primitive value) { |
| + void extend(Local element, ir.Primitive value) { |
| // Assert that the name is not already in the environment. `null` is used |
| // as the name of anonymous variables. Because the variable2index map is |
| // shared, `null` can already occur. This is safe because such variables |
| @@ -64,14 +67,13 @@ class Environment { |
| index2value.add(value); |
| } |
| - ir.Primitive lookup(Element element) { |
| - assert(!element.isConst); |
| + ir.Primitive lookup(Local element) { |
| assert(invariant(element, variable2index.containsKey(element), |
| message: "Unknown variable: $element.")); |
| return index2value[variable2index[element]]; |
| } |
| - void update(Element element, ir.Primitive value) { |
| + void update(Local element, ir.Primitive value) { |
| index2value[variable2index[element]] = value; |
| } |
| @@ -82,7 +84,7 @@ class Environment { |
| assert(other.length >= length); |
| for (int i = 0; i < length; ++i) { |
| // An index maps to the same variable in both environments. |
| - Element variable = index2variable[i]; |
| + Local variable = index2variable[i]; |
| if (variable != other.index2variable[i]) return false; |
| // The variable maps to the same index in both environments. |
| @@ -179,8 +181,6 @@ abstract class IrBuilderMixin<N> { |
| /// Shared state between IrBuilders of nested functions. |
| class IrBuilderClosureState { |
|
floitsch
2015/01/08 18:29:35
Probably different CL, but I'm not sure the names
asgerf
2015/01/12 13:15:42
I completely agree, but I can't seem to think of b
|
| - final Iterable<Entity> closureLocals; |
| - |
| /// Maps local variables to their corresponding [ClosureVariable] object. |
| final Map<Local, ir.ClosureVariable> local2closure = |
| <Local, ir.ClosureVariable>{}; |
| @@ -194,13 +194,12 @@ class IrBuilderClosureState { |
| return function2closures.putIfAbsent(element, () => <ir.ClosureVariable>[]); |
| } |
| - IrBuilderClosureState(this.closureLocals) { |
| - for (Local local in closureLocals) { |
| - ExecutableElement context = local.executableContext; |
| - ir.ClosureVariable variable = new ir.ClosureVariable(context, local); |
| - local2closure[local] = variable; |
| - getClosureList(context).add(variable); |
| - } |
| + /// Creates a closure variable for the given local. |
| + void makeClosureVariable(Local local) { |
| + ir.ClosureVariable variable = |
| + new ir.ClosureVariable(local.executableContext, local); |
| + local2closure[local] = variable; |
| + getClosureList(local.executableContext).add(variable); |
| } |
| } |
| @@ -222,21 +221,46 @@ class IrBuilderSharedState { |
| final List<ir.Definition> functionParameters = <ir.Definition>[]; |
| + /// Maps boxed locals to their location. These locals are not part of |
| + /// the environment. |
| + final Map<Local, ClosureLocation> boxedVariables = {}; |
| + |
| + /// If non-null, this refers to the receiver in the enclosing method. |
|
floitsch
2015/01/08 18:29:35
to the receiver (`this`) in ...
asgerf
2015/01/12 13:15:42
Done.
|
| + ir.Primitive receiver; |
| + |
| IrBuilderSharedState(this.constantSystem, this.currentElement); |
| } |
| /// A factory for building the cps IR. |
| -class IrBuilder { |
| +/// |
| +/// [DartIrBuilder] and [JsIrBuilder] implement nested functions and captured |
| +/// variables in different ways. |
| +abstract class IrBuilder { |
| + IrBuilder _makeInstance(); |
| + |
| + void declareLocalVariable(LocalVariableElement element, {ir.Primitive initialValue}); |
|
floitsch
2015/01/08 18:29:36
long line.
|
| + void declareLocalFunction(LocalFunctionElement element, Object function); |
| + ir.Primitive buildFunctionExpression(Object function); |
| + ir.Primitive buildLocalGet(LocalElement element); |
| + ir.Primitive buildLocalSet(LocalElement element, ir.Primitive value); |
| + |
| + void _enterClosureEnvironment(ClosureEnvironment env); |
|
floitsch
2015/01/08 18:29:36
comments!
|
| + void _enterScope(ClosureScope scope); |
| + void _createFunctionParameter(ParameterElement parameterElement); |
| + void _migrateLoopVariables(ClosureScope scope); |
| + |
| // TODO(johnniwinther): Make these field final and remove the default values |
| // when [IrBuilder] is a property of [IrBuilderVisitor] instead of a mixin. |
| final List<ir.Parameter> _parameters = <ir.Parameter>[]; |
| - final IrBuilderSharedState state; |
| + IrBuilderSharedState state; |
| - final IrBuilderClosureState closure; |
| + IrBuilderClosureState closure; |
|
floitsch
2015/01/08 18:29:36
Again, probably different CL:
closureState, or eve
|
| /// A map from variable indexes to their values. |
| + /// [BoxLocal]s map to their box. [LocalElement]s that are boxed are not |
|
floitsch
2015/01/08 18:29:35
New line before. (Otherwise markdown merges them).
|
| + /// in the map; look up their [BoxLocal] instead. |
| Environment environment; |
| // The IR builder maintains a context, which is an expression with a hole in |
| @@ -266,12 +290,11 @@ class IrBuilder { |
| ir.Expression _root = null; |
| ir.Expression _current = null; |
| - IrBuilder(ConstantSystem constantSystem, |
| - ExecutableElement currentElement, |
| - Iterable<Entity> closureLocals) |
| - : this.state = new IrBuilderSharedState(constantSystem, currentElement), |
| - this.closure = new IrBuilderClosureState(closureLocals), |
| - this.environment = new Environment.empty(); |
| + void _init(ConstantSystem constantSystem, ExecutableElement currentElement) { |
|
floitsch
2015/01/08 18:29:36
add comment.
Could you keep this a constructor?
B
asgerf
2015/01/12 13:15:42
Added comment.
The subclasses have two constructo
|
| + state = new IrBuilderSharedState(constantSystem, currentElement); |
| + closure = new IrBuilderClosureState(); |
| + environment = new Environment.empty(); |
| + } |
| /// Construct a delimited visitor for visiting a subtree. |
| /// |
| @@ -279,10 +302,12 @@ class IrBuilder { |
| /// local variables to their values, which is initially a copy of the parent |
| /// environment. It has its own context for building an IR expression, so |
| /// the built expression is not plugged into the parent's context. |
| - IrBuilder.delimited(IrBuilder parent) |
| - : this.state = parent.state, |
| - this.closure = parent.closure, |
| - this.environment = new Environment.from(parent.environment); |
| + IrBuilder makeDelimitedBuilder() { |
| + return _makeInstance() |
| + ..state = state |
|
floitsch
2015/01/08 18:29:36
nit: indentation. (by 4).
|
| + ..closure = closure |
| + ..environment = new Environment.from(environment); |
| + } |
| /// Construct a visitor for a recursive continuation. |
| /// |
| @@ -292,54 +317,46 @@ class IrBuilder { |
| /// recursive invocations will be passed values for all the local variables, |
| /// which may be eliminated later if they are redundant---if they take on |
| /// the same value at all invocation sites. |
| - IrBuilder.recursive(IrBuilder parent) |
| - : this.state = parent.state, |
| - this.closure = parent.closure, |
| - this.environment = new Environment.empty() { |
| - parent.environment.index2variable.forEach(createLocalParameter); |
| + IrBuilder makeRecursiveBuilder() { |
| + IrBuilder inner = _makeInstance() |
| + ..state = state |
| + ..closure = closure |
| + ..environment = new Environment.empty(); |
| + environment.index2variable.forEach(inner.createLocalParameter); |
| + return inner; |
| } |
| /// Construct a builder for an inner function. |
| - IrBuilder.innerFunction(IrBuilder parent, |
| - ExecutableElement currentElement) |
| - : this.state = new IrBuilderSharedState(parent.state.constantSystem, |
| - currentElement), |
| - this.closure = parent.closure, |
| - this.environment = new Environment.empty(); |
| - |
| + IrBuilder makeInnerFunctionBuilder(ExecutableElement currentElement) { |
| + return _makeInstance() |
| + ..state = new IrBuilderSharedState(state.constantSystem, currentElement) |
| + ..closure = closure |
| + ..environment = new Environment.empty(); |
| + } |
| bool get isOpen => _root == null || _current != null; |
| - /// True if [element] is a local variable, local function, or parameter that |
| - /// is accessed from an inner function. Recursive self-references in a local |
| - /// function count as closure accesses. |
| - /// |
| - /// If `true`, [element] is a [LocalElement]. |
| - bool isClosureVariable(Element element) { |
| - return closure.closureLocals.contains(element); |
| + void beginField({ ClosureScope closureScope }) { |
|
floitsch
2015/01/08 18:29:36
nit: apparently we don't put spaces after "{" and
floitsch
2015/01/08 18:29:36
comment.
Is this the rhs of a field-init?
asgerf
2015/01/12 13:15:43
Done.
|
| + beginFunction([], closureScope: closureScope); |
| } |
| - /// Returns the [ClosureVariable] corresponding to the given variable. |
| - /// Returns `null` for non-closure variables. |
| - ir.ClosureVariable getClosureVariable(LocalElement element) { |
| - return closure.local2closure[element]; |
| - } |
| - |
| - /// Adds the given parameter to the function currently being built. |
| - void createFunctionParameter(ParameterElement parameterElement) { |
| - if (isClosureVariable(parameterElement)) { |
| - state.functionParameters.add(getClosureVariable(parameterElement)); |
| - } else { |
| - state.functionParameters.add(createLocalParameter(parameterElement)); |
| + void beginFunction(Iterable<ParameterElement> parameters, |
|
floitsch
2015/01/08 18:29:36
Apparently this method is not just used for functi
asgerf
2015/01/12 13:15:43
Fixed.
The only shared part was _enterScope, so I
|
| + { ClosureScope closureScope, |
| + ClosureEnvironment closureEnvironment } ) { |
| + if (closureEnvironment != null) { |
| + _enterClosureEnvironment(closureEnvironment); |
|
floitsch
2015/01/08 18:29:35
unconditionally _enterClosureEnvironment(...) and
asgerf
2015/01/12 13:15:43
Done.
|
| + } |
| + if (closureScope != null) { |
| + _enterScope(closureScope); |
|
floitsch
2015/01/08 18:29:35
ditto.
asgerf
2015/01/12 13:15:42
Done.
|
| } |
| + parameters.forEach(_createFunctionParameter); |
| } |
| - /// Create a parameter for [parameterElement] and add it to the current |
| - /// environment. |
| - ir.Parameter createLocalParameter(LocalElement parameterElement) { |
| - ir.Parameter parameter = new ir.Parameter(parameterElement); |
| + /// Create a parameter for [local] and add it to the current environment. |
|
floitsch
2015/01/08 18:29:36
Creates ... adds ...
asgerf
2015/01/12 13:15:43
Done.
|
| + ir.Parameter createLocalParameter(Local local) { |
| + ir.Parameter parameter = new ir.Parameter(local); |
| _parameters.add(parameter); |
| - environment.extend(parameterElement, parameter); |
| + environment.extend(local, parameter); |
| return parameter; |
| } |
| @@ -350,43 +367,6 @@ class IrBuilder { |
| state.localConstants.add(new ConstDeclaration(variableElement, value)); |
| } |
| - /// Add [variableElement] to the environment with [initialValue] as its |
| - /// initial value. |
| - void declareLocalVariable(LocalVariableElement variableElement, |
| - {ir.Primitive initialValue}) { |
| - assert(isOpen); |
| - if (initialValue == null) { |
| - // TODO(kmillikin): Consider pooling constants. |
| - // The initial value is null. |
| - initialValue = buildNullLiteral(); |
| - } |
| - if (isClosureVariable(variableElement)) { |
| - add(new ir.SetClosureVariable(getClosureVariable(variableElement), |
| - initialValue, |
| - isDeclaration: true)); |
| - } else { |
| - // In case a primitive was introduced for the initializer expression, |
| - // use this variable element to help derive a good name for it. |
| - initialValue.useElementAsHint(variableElement); |
| - environment.extend(variableElement, initialValue); |
| - } |
| - } |
| - |
| - /// Add [functionElement] to the environment with provided [definition]. |
| - void declareLocalFunction(LocalFunctionElement functionElement, |
| - ir.FunctionDefinition definition) { |
| - assert(isOpen); |
| - if (isClosureVariable(functionElement)) { |
| - ir.ClosureVariable variable = getClosureVariable(functionElement); |
| - add(new ir.DeclareFunction(variable, definition)); |
| - } else { |
| - ir.CreateFunction prim = new ir.CreateFunction(definition); |
| - add(new ir.LetPrim(prim)); |
| - environment.extend(functionElement, prim); |
| - prim.useElementAsHint(functionElement); |
| - } |
| - } |
| - |
| // Plug an expression into the 'hole' in the context being accumulated. The |
| // empty context (just a hole) is represented by root (and current) being |
| // null. Since the hole in the current context is filled by this function, |
| @@ -522,8 +502,8 @@ class IrBuilder { |
| assert(isOpen); |
| // The then and else expressions are delimited. |
| - IrBuilder thenBuilder = new IrBuilder.delimited(this); |
| - IrBuilder elseBuilder = new IrBuilder.delimited(this); |
| + IrBuilder thenBuilder = makeDelimitedBuilder(); |
| + IrBuilder elseBuilder = makeDelimitedBuilder(); |
| ir.Primitive thenValue = buildThenExpression(thenBuilder); |
| ir.Primitive elseValue = buildElseExpression(elseBuilder); |
| @@ -561,13 +541,6 @@ class IrBuilder { |
| } |
| - /// Create a function expression from [definition]. |
| - ir.Primitive buildFunctionExpression(ir.FunctionDefinition definition) { |
| - ir.CreateFunction prim = new ir.CreateFunction(definition); |
| - add(new ir.LetPrim(prim)); |
| - return prim; |
| - } |
| - |
| /** |
| * Add an explicit `return null` for functions that don't have a return |
| * statement on each branch. This includes functions with an empty body, |
| @@ -766,44 +739,12 @@ class IrBuilder { |
| (k) => new ir.ConcatenateStrings(k, arguments)); |
| } |
| - /// Create a read access of [local]. |
| - ir.Primitive buildLocalGet(LocalElement local) { |
| - assert(isOpen); |
| - if (isClosureVariable(local)) { |
| - ir.Primitive result = |
| - new ir.GetClosureVariable(getClosureVariable(local)); |
| - add(new ir.LetPrim(result)); |
| - return result; |
| - } else { |
| - return environment.lookup(local); |
| - } |
| - } |
| - |
| - /// Create a write access to [local] with the provided [value]. |
| - ir.Primitive buildLocalSet(LocalElement local, ir.Primitive value) { |
| - assert(isOpen); |
| - if (isClosureVariable(local)) { |
| - add(new ir.SetClosureVariable(getClosureVariable(local), value)); |
| - } else { |
| - value.useElementAsHint(local); |
| - environment.update(local, value); |
| - } |
| - return value; |
| - } |
| - |
| /// Create an invocation of [local] where the argument structure is defined |
| /// by [selector] and the argument values are defined by [arguments]. |
| ir.Primitive buildLocalInvocation(LocalElement local, |
| Selector selector, |
| List<ir.Definition> arguments) { |
| - ir.Primitive receiver; |
| - if (isClosureVariable(local)) { |
| - receiver = new ir.GetClosureVariable(getClosureVariable(local)); |
| - add(new ir.LetPrim(receiver)); |
| - } else { |
| - receiver = environment.lookup(local); |
| - } |
| - return _buildInvokeCall(receiver, selector, arguments); |
| + return _buildInvokeCall(buildLocalGet(local), selector, arguments); |
| } |
| /// Create an invocation of the [functionExpression] where the argument |
| @@ -829,8 +770,8 @@ class IrBuilder { |
| assert(isOpen); |
| // The then and else parts are delimited. |
| - IrBuilder thenBuilder = new IrBuilder.delimited(this); |
| - IrBuilder elseBuilder = new IrBuilder.delimited(this); |
| + IrBuilder thenBuilder = makeDelimitedBuilder(); |
| + IrBuilder elseBuilder = makeDelimitedBuilder(); |
| buildThenPart(thenBuilder); |
| buildElsePart(elseBuilder); |
| @@ -917,7 +858,8 @@ class IrBuilder { |
| SubbuildFunction buildCondition, |
| SubbuildFunction buildBody, |
| SubbuildFunction buildUpdate, |
| - JumpTarget target}) { |
| + JumpTarget target, |
| + ClosureScope closureScope}) { |
|
floitsch
2015/01/08 18:29:36
add comment about closureScope variable.
|
| assert(isOpen); |
| // For loops use four named continuations: the entry to the condition, |
| @@ -942,9 +884,13 @@ class IrBuilder { |
| // invocation of the continue continuation (i.e., no continues in the |
| // body), the continue continuation is inlined in the body. |
| + if (closureScope != null) { |
| + _enterScope(closureScope); |
|
floitsch
2015/01/08 18:29:36
as above (and for the remaining ones): uncondition
|
| + } |
| + |
| buildInitializer(this); |
| - IrBuilder condBuilder = new IrBuilder.recursive(this); |
| + IrBuilder condBuilder = makeRecursiveBuilder(); |
| ir.Primitive condition = buildCondition(condBuilder); |
| if (condition == null) { |
| // If the condition is empty then the body is entered unconditionally. |
| @@ -956,7 +902,7 @@ class IrBuilder { |
| state.breakCollectors.add(breakCollector); |
| state.continueCollectors.add(continueCollector); |
| - IrBuilder bodyBuilder = new IrBuilder.delimited(condBuilder); |
| + IrBuilder bodyBuilder = condBuilder.makeDelimitedBuilder(); |
| buildBody(bodyBuilder); |
| assert(state.breakCollectors.last == breakCollector); |
| assert(state.continueCollectors.last == continueCollector); |
| @@ -969,8 +915,11 @@ class IrBuilder { |
| // is instead placed just outside the body of the body continuation. |
| bool hasContinues = !continueCollector.isEmpty; |
| IrBuilder updateBuilder = hasContinues |
| - ? new IrBuilder.recursive(condBuilder) |
| + ? condBuilder.makeRecursiveBuilder() |
| : bodyBuilder; |
| + if (closureScope != null) { |
| + updateBuilder._migrateLoopVariables(closureScope); |
|
floitsch
2015/01/08 18:29:35
ditto
|
| + } |
| buildUpdate(updateBuilder); |
| // Create body entry and loop exit continuations and a branch to them. |
| @@ -1056,21 +1005,22 @@ class IrBuilder { |
| Element variableElement, |
| Selector variableSelector, |
| SubbuildFunction buildBody, |
| - JumpTarget target}) { |
| + JumpTarget target, |
| + ClosureScope closureScope}) { |
| // The for-in loop |
| // |
| // for (a in e) s; |
| // |
| // Is compiled analogously to: |
| // |
| - // a = e.iterator; |
| - // while (a.moveNext()) { |
| - // var n0 = a.current; |
| + // it = e.iterator; |
| + // while (it.moveNext()) { |
| + // var a = it.current; |
| // s; |
| // } |
| // The condition and body are delimited. |
| - IrBuilder condBuilder = new IrBuilder.recursive(this); |
| + IrBuilder condBuilder = makeRecursiveBuilder(); |
| ir.Primitive expressionReceiver = buildExpression(this); |
| List<ir.Primitive> emptyArguments = new List<ir.Primitive>(); |
| @@ -1094,7 +1044,10 @@ class IrBuilder { |
| state.breakCollectors.add(breakCollector); |
| state.continueCollectors.add(continueCollector); |
| - IrBuilder bodyBuilder = new IrBuilder.delimited(condBuilder); |
| + IrBuilder bodyBuilder = condBuilder.makeDelimitedBuilder(); |
| + if (closureScope != null) { |
| + bodyBuilder._enterScope(closureScope); |
| + } |
| if (buildVariableDeclaration != null) { |
| buildVariableDeclaration(bodyBuilder); |
| } |
| @@ -1170,7 +1123,8 @@ class IrBuilder { |
| /// statements that have this `while` statement as their target. |
| void buildWhile({SubbuildFunction buildCondition, |
| SubbuildFunction buildBody, |
| - JumpTarget target}) { |
| + JumpTarget target, |
| + ClosureScope closureScope}) { |
| assert(isOpen); |
| // While loops use four named continuations: the entry to the body, the |
| // loop exit, the loop back edge (continue), and the loop exit (break). |
| @@ -1189,7 +1143,7 @@ class IrBuilder { |
| // statement occurs in the exit continuation). |
| // The condition and body are delimited. |
| - IrBuilder condBuilder = new IrBuilder.recursive(this); |
| + IrBuilder condBuilder = makeRecursiveBuilder(); |
| ir.Primitive condition = buildCondition(condBuilder); |
| JumpCollector breakCollector = new JumpCollector(target); |
| @@ -1197,7 +1151,10 @@ class IrBuilder { |
| state.breakCollectors.add(breakCollector); |
| state.continueCollectors.add(continueCollector); |
| - IrBuilder bodyBuilder = new IrBuilder.delimited(condBuilder); |
| + IrBuilder bodyBuilder = condBuilder.makeDelimitedBuilder(); |
| + if (closureScope != null) { |
| + bodyBuilder._enterScope(closureScope); |
| + } |
| buildBody(bodyBuilder); |
| assert(state.breakCollectors.last == breakCollector); |
| assert(state.continueCollectors.last == continueCollector); |
| @@ -1375,18 +1332,18 @@ class IrBuilder { |
| // The translation must convert both e0 and e1 to booleans and handle |
| // local variable assignments in e1. |
| - IrBuilder rightBuilder = new IrBuilder.delimited(this); |
| + IrBuilder rightBuilder = makeDelimitedBuilder(); |
| ir.Primitive rightValue = buildRightValue(rightBuilder); |
| // A dummy empty target for the branch on the left subexpression branch. |
| // This enables using the same infrastructure for join-point continuations |
| // as in visitIf and visitConditional. It will hold a definition of the |
| // appropriate constant and an invocation of the join-point continuation. |
| - IrBuilder emptyBuilder = new IrBuilder.delimited(this); |
| + IrBuilder emptyBuilder = makeDelimitedBuilder(); |
| // Dummy empty targets for right true and right false. They hold |
| // definitions of the appropriate constant and an invocation of the |
| // join-point continuation. |
| - IrBuilder rightTrueBuilder = new IrBuilder.delimited(rightBuilder); |
| - IrBuilder rightFalseBuilder = new IrBuilder.delimited(rightBuilder); |
| + IrBuilder rightTrueBuilder = rightBuilder.makeDelimitedBuilder(); |
| + IrBuilder rightFalseBuilder = rightBuilder.makeDelimitedBuilder(); |
| // If we don't evaluate the right subexpression, the value of the whole |
| // expression is this constant. |
| @@ -1448,12 +1405,15 @@ class IrBuilder { |
| return joinContinuation.parameters.last; |
| } |
| - /// Creates an access to `this`. |
| + /// Creates an access to the receiver from the current (or enclosing) method. |
| + /// |
| + /// If inside a closure class, [buildThis] will redirect acess through closure |
| + /// fields in order to access the receiver from the enclosing method. |
| ir.Primitive buildThis() { |
| - assert(isOpen); |
| - ir.Primitive result = new ir.This(); |
| - add(new ir.LetPrim(result)); |
| - return result; |
| + if (state.receiver != null) return state.receiver; |
| + ir.Primitive thisPrim = new ir.This(); |
| + add(new ir.LetPrim(thisPrim)); |
| + return thisPrim; |
| } |
| /// Create a non-recursive join-point continuation. |
| @@ -1561,3 +1521,321 @@ class IrBuilder { |
| return join; |
| } |
| } |
| + |
| +/// Dart-specific subclass of [IrBuilder]. |
| +/// |
| +/// Inner functions are represented by an [ir.FunctionDefinition] with the |
| +/// IR for the inner function nested inside. |
| +/// |
| +/// Captured variables are translated to ref cells using [GetClosureVariable] |
| +/// and [SetClosureVariable]. |
| +class DartIrBuilder extends IrBuilder { |
| + ClosureVariableInfo closureVariables; |
| + |
| + IrBuilder _makeInstance() => new DartIrBuilder._blank(closureVariables); |
| + DartIrBuilder._blank(this.closureVariables); |
| + |
| + DartIrBuilder(ConstantSystem constantSystem, |
| + ExecutableElement currentElement, |
| + this.closureVariables) { |
| + _init(constantSystem, currentElement); |
| + closureVariables.capturedVariables.forEach(closure.makeClosureVariable); |
| + } |
| + |
| + /// True if [local] is stored in a ref cell. |
|
floitsch
2015/01/08 18:29:35
What's a "ref cell" ?
Explain more.
asgerf
2015/01/12 13:15:42
Changed to isInClosureVariable. I was thinking abo
|
| + bool isInRefCell(Local local) { |
| + return closure.local2closure.containsKey(local); |
| + } |
| + |
| + ir.ClosureVariable getRefCell(Local local) => closure.local2closure[local]; |
| + |
| + void _enterScope(ClosureScope scope) { |
| + throw 'Not supported'; |
| + } |
| + |
| + void _enterClosureEnvironment(ClosureEnvironment env) { |
| + throw 'Not supported'; |
| + } |
| + |
| + void _migrateLoopVariables(ClosureScope scope) { |
| + throw 'Not supported'; |
| + } |
| + |
| + void _createFunctionParameter(ParameterElement parameterElement) { |
| + ir.Parameter parameter = new ir.Parameter(parameterElement); |
| + _parameters.add(parameter); |
| + if (isInRefCell(parameterElement)) { |
| + state.functionParameters.add(getRefCell(parameterElement)); |
| + } else { |
| + state.functionParameters.add(parameter); |
| + environment.extend(parameterElement, parameter); |
| + } |
| + } |
| + |
| + void declareLocalVariable(LocalVariableElement variableElement, |
| + {ir.Primitive initialValue}) { |
| + assert(isOpen); |
| + if (initialValue == null) { |
| + initialValue = buildNullLiteral(); |
| + } |
| + if (isInRefCell(variableElement)) { |
| + add(new ir.SetClosureVariable(getRefCell(variableElement), |
| + initialValue, |
| + isDeclaration: true)); |
| + } else { |
| + initialValue.useElementAsHint(variableElement); |
| + environment.extend(variableElement, initialValue); |
| + } |
| + } |
| + |
| + /// Add [functionElement] to the environment with provided [definition]. |
| + void declareLocalFunction(LocalFunctionElement functionElement, |
| + ir.FunctionDefinition definition) { |
| + assert(isOpen); |
| + if (isInRefCell(functionElement)) { |
| + ir.ClosureVariable variable = getRefCell(functionElement); |
| + add(new ir.DeclareFunction(variable, definition)); |
| + } else { |
| + ir.CreateFunction prim = new ir.CreateFunction(definition); |
| + add(new ir.LetPrim(prim)); |
| + environment.extend(functionElement, prim); |
| + prim.useElementAsHint(functionElement); |
| + } |
| + } |
| + |
| + /// Create a function expression from [definition]. |
| + ir.Primitive buildFunctionExpression(ir.FunctionDefinition definition) { |
| + ir.CreateFunction prim = new ir.CreateFunction(definition); |
| + add(new ir.LetPrim(prim)); |
| + return prim; |
| + } |
| + |
| + /// Create a read access of [local]. |
| + ir.Primitive buildLocalGet(LocalElement local) { |
| + assert(isOpen); |
| + if (isInRefCell(local)) { |
| + ir.Primitive result = new ir.GetClosureVariable(getRefCell(local)); |
| + result.useElementAsHint(local); |
| + add(new ir.LetPrim(result)); |
| + return result; |
| + } else { |
| + return environment.lookup(local); |
| + } |
| + } |
| + |
| + /// Create a write access to [local] with the provided [value]. |
| + ir.Primitive buildLocalSet(LocalElement local, ir.Primitive value) { |
| + assert(isOpen); |
| + if (isInRefCell(local)) { |
| + add(new ir.SetClosureVariable(getRefCell(local), value)); |
| + } else { |
| + value.useElementAsHint(local); |
| + environment.update(local, value); |
| + } |
| + return value; |
| + } |
| + |
| + |
| +} |
| + |
| +/// JS-specific subclass of [IrBuilder]. |
| +/// |
| +/// Inner functions are repsented by a [ClosureClassElement], and captured |
|
floitsch
2015/01/08 18:29:36
represented
asgerf
2015/01/12 13:15:42
Done.
|
| +/// variables are boxed as necessary using [CreateBox], [GetField], [SetField]. |
| +class JsIrBuilder extends IrBuilder { |
| + IrBuilder _makeInstance() => new JsIrBuilder._blank(); |
| + JsIrBuilder._blank(); |
| + |
| + JsIrBuilder(ConstantSystem constantSystem, ExecutableElement currentElement) { |
| + _init(constantSystem, currentElement); |
| + } |
| + |
| + void _enterClosureEnvironment(ClosureEnvironment env) { |
| + ir.Primitive thisPrim = new ir.This(); |
|
floitsch
2015/01/08 18:29:36
more comments.
asgerf
2015/01/12 13:15:42
Done.
|
| + add(new ir.LetPrim(thisPrim)); |
| + env.freeVariables.forEach((Local local, ClosureLocation location) { |
| + if (location.isBox) { |
| + state.boxedVariables[local] = location; |
| + } else { |
| + ir.Primitive load = new ir.GetField(thisPrim, location.field); |
|
floitsch
2015/01/08 18:29:35
Add comment, that dead-code elimination will remov
asgerf
2015/01/12 13:15:43
Shrinking reductions will remove a dead GetField,
|
| + add(new ir.LetPrim(load)); |
| + environment.extend(local, load); |
| + } |
| + }); |
| + if (env.thisLocal != null && env.freeVariables.containsKey(env.thisLocal)) { |
| + state.receiver = environment.lookup(env.thisLocal); |
| + } |
| + if (env.selfReference != null) { |
| + environment.extend(env.selfReference, thisPrim); |
| + } |
| + } |
| + |
| + void _enterScope(ClosureScope scope) { |
| + if (scope.box != null) { |
| + ir.CreateBox boxPrim = new ir.CreateBox(); |
| + add(new ir.LetPrim(boxPrim)); |
| + environment.extend(scope.box, boxPrim); |
| + boxPrim.useElementAsHint(scope.box); |
| + } |
| + scope.capturedVariables.forEach((Local local, ClosureLocation location) { |
| + assert(!state.boxedVariables.containsKey(local)); |
| + if (location.isBox) { |
| + state.boxedVariables[local] = location; |
| + } |
| + }); |
| + } |
| + |
| + void _createFunctionParameter(ParameterElement parameterElement) { |
| + ir.Parameter parameter = new ir.Parameter(parameterElement); |
| + _parameters.add(parameter); |
| + state.functionParameters.add(parameter); |
| + ClosureLocation location = state.boxedVariables[parameterElement]; |
| + if (location != null) { |
| + add(new ir.SetField(environment.lookup(location.box), |
| + location.field, |
| + parameter)); |
| + } else { |
| + environment.extend(parameterElement, parameter); |
| + } |
| + } |
| + |
| + void declareLocalVariable(LocalElement variableElement, |
| + {ir.Primitive initialValue}) { |
| + assert(isOpen); |
| + if (initialValue == null) { |
| + initialValue = buildNullLiteral(); |
| + } |
| + ClosureLocation location = state.boxedVariables[variableElement]; |
| + if (location != null) { |
| + add(new ir.SetField(environment.lookup(location.box), |
| + location.field, |
| + initialValue)); |
| + } else { |
| + initialValue.useElementAsHint(variableElement); |
| + environment.extend(variableElement, initialValue); |
| + } |
| + } |
| + |
| + /// Add [functionElement] to the environment with provided [definition]. |
| + void declareLocalFunction(LocalFunctionElement functionElement, |
| + ClosureClassElement classElement) { |
| + ir.Primitive closure = buildFunctionExpression(classElement); |
| + declareLocalVariable(functionElement, initialValue: closure); |
| + } |
| + |
| + ir.Primitive buildFunctionExpression(ClosureClassElement classElement) { |
| + List<ir.Primitive> arguments = <ir.Primitive>[]; |
| + for (ClosureFieldElement field in classElement.closureFields) { |
| + arguments.add(environment.lookup(field.local)); |
| + } |
| + ir.Primitive closure = new ir.CreateClosureClass(classElement, arguments); |
| + add(new ir.LetPrim(closure)); |
| + return closure; |
| + } |
| + |
| + /// Create a read access of [local]. |
| + ir.Primitive buildLocalGet(LocalElement local) { |
| + assert(isOpen); |
| + ClosureLocation location = state.boxedVariables[local]; |
| + if (location != null) { |
| + ir.Primitive result = new ir.GetField(environment.lookup(location.box), |
| + location.field); |
| + result.useElementAsHint(local); |
| + add(new ir.LetPrim(result)); |
| + return result; |
| + } else { |
| + return environment.lookup(local); |
| + } |
| + } |
| + |
| + /// Create a write access to [local] with the provided [value]. |
| + ir.Primitive buildLocalSet(LocalElement local, ir.Primitive value) { |
| + assert(isOpen); |
| + ClosureLocation location = state.boxedVariables[local]; |
| + if (location != null) { |
| + add(new ir.SetField(environment.lookup(location.box), |
| + location.field, |
| + value)); |
| + } else { |
| + value.useElementAsHint(local); |
| + environment.update(local, value); |
| + } |
| + return value; |
| + } |
| + |
| + void _migrateLoopVariables(ClosureScope scope) { |
| + if (scope.boxedLoopVariables.isEmpty) return; |
| + ir.Primitive box = environment.lookup(scope.box); |
| + ir.Primitive newBox = new ir.CreateBox(); |
| + newBox.useElementAsHint(scope.box); |
| + add(new ir.LetPrim(newBox)); |
| + for (VariableElement loopVar in scope.boxedLoopVariables) { |
| + ClosureLocation location = scope.capturedVariables[loopVar]; |
| + ir.Primitive get = new ir.GetField(box, location.field); |
| + add(new ir.LetPrim(get)); |
| + add(new ir.SetField(newBox, location.field, get)); |
| + } |
| + environment.update(scope.box, newBox); |
| + } |
| + |
| +} |
| + |
| + |
| +/// Location of a variable relative to a given closure. |
| +class ClosureLocation { |
| + /// If not `null`, this location is [box].[field]. |
| + /// The location of [box] can be obtained separately from an |
| + /// enclosing [ClosureEnvironment] or [ClosureScope]. |
| + /// If `null`, then the location is [field] on the enclosing function object. |
| + final BoxLocal box; |
| + |
| + /// The field in which the variable is stored. |
| + final Entity field; |
| + |
| + bool get isBox => box != null; |
| + |
| + ClosureLocation(this.box, this.field); |
| +} |
| + |
| +/// Introduces a new box and binds local variables to this box. |
| +/// |
| +/// A [ClosureScope] may exist for each function and for each loop. |
| +/// Generally, one may pass `null` to the [IrBuilder] instead of a |
| +/// [ClosureScope] when a given scope has no boxed variables. |
| +class ClosureScope { |
| + /// This box is now in scope and [capturedVariables] may use it. |
| + final BoxLocal box; |
| + |
| + /// Maps [LocalElement]s to their location. |
| + final Map<Local, ClosureLocation> capturedVariables; |
| + |
| + /// If this is the scope of a for-loop, [boxedLoopVariables] is the list |
| + /// of boxed variables that are declared in the initializer. |
| + final List<VariableElement> boxedLoopVariables; |
| + |
| + ClosureScope(this.box, this.capturedVariables, this.boxedLoopVariables); |
| +} |
| + |
| +/// Environment passed when building a nested function, describing how |
| +/// to access variables from the enclosing scope. |
| +class ClosureEnvironment { |
| + /// References to this local should be treated as recursive self-reference. |
| + /// (This is *not* in [freeVariables]). |
| + final LocalFunctionElement selfReference; |
| + |
| + /// If non-null, [thisLocal] has an entry in [freeVariables] describing where |
| + /// to find the captured value of `this`. |
| + final ThisLocal thisLocal; |
| + |
| + /// Maps [LocalElement]s, [BoxLocal]s and [ThisLocal] to their location. |
| + final Map<Local, ClosureLocation> freeVariables; |
| + |
| + ClosureEnvironment(this.selfReference, this.thisLocal, this.freeVariables); |
| +} |
| + |
| +/// Information about which variables are captured in a closure. |
| +/// This is used by the [DartIrBuilder] instead of [ClosureScope] and |
| +/// [ClosureEnvironment]. |
| +abstract class ClosureVariableInfo { |
| + Iterable<Local> get capturedVariables; |
| +} |