Index: sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder_visitor.dart |
diff --git a/sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder.dart b/sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder_visitor.dart |
similarity index 77% |
copy from sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder.dart |
copy to sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder_visitor.dart |
index 9c7b87d16dc78eea7c15ed8a529c08013d44ac3b..ae3babf3c0b01bd974e736f5e37ceb768a7e1b14 100644 |
--- a/sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder.dart |
+++ b/sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder_visitor.dart |
@@ -2,19 +2,7 @@ |
// for details. All rights reserved. Use of this source code is governed by a |
// BSD-style license that can be found in the LICENSE file. |
-library dart2js.ir_builder; |
- |
-import '../constants/expressions.dart'; |
-import '../constants/values.dart' show PrimitiveConstantValue; |
-import '../dart_backend/dart_backend.dart' show DartBackend; |
-import '../dart_types.dart'; |
-import '../dart2jslib.dart'; |
-import '../elements/elements.dart'; |
-import '../source_file.dart'; |
-import '../tree/tree.dart' as ast; |
-import '../scanner/scannerlib.dart' show Token, isUserDefinableOperator; |
-import '../universe/universe.dart' show SelectorKind; |
-import 'cps_ir_nodes.dart' as ir; |
+part of dart2js.ir_builder; |
/** |
* This task iterates through all resolved elements and builds [ir.Node]s. The |
@@ -117,485 +105,6 @@ class _GetterElements { |
_GetterElements({this.result, this.index, this.receiver}) ; |
} |
-/// A mapping from variable elements to their compile-time values. |
-/// |
-/// Map elements denoted by parameters and local variables to the |
-/// [ir.Primitive] that is their value. Parameters and locals are |
-/// 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; |
- |
- /// A reverse map from environment indexes to the variable. |
- final List<Element> index2variable; |
- |
- /// A map from environment indexes to their value. |
- final List<ir.Primitive> index2value; |
- |
- Environment.empty() |
- : variable2index = <Element, int>{}, |
- index2variable = <Element>[], |
- index2value = <ir.Primitive>[]; |
- |
- /// Construct an environment that is a copy of another one. |
- /// |
- /// The mapping from elements to indexes is shared, not copied. |
- Environment.from(Environment other) |
- : variable2index = other.variable2index, |
- index2variable = new List<Element>.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) { |
- // 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 |
- // are not looked up by name. |
- // |
- // TODO(kmillikin): This is still kind of fishy. Refactor to not share |
- // name maps or else garbage collect unneeded names. |
- assert(element == null || !variable2index.containsKey(element)); |
- variable2index[element] = index2variable.length; |
- index2variable.add(element); |
- index2value.add(value); |
- } |
- |
- ir.Primitive lookup(Element element) { |
- assert(!element.isConst); |
- assert(invariant(element, variable2index.containsKey(element), |
- message: "Unknown variable: $element.")); |
- return index2value[variable2index[element]]; |
- } |
- |
- void update(Element element, ir.Primitive value) { |
- index2value[variable2index[element]] = value; |
- } |
- |
- /// Verify that the variable2index and index2variable maps agree up to the |
- /// index [length] exclusive. |
- bool sameDomain(int length, Environment other) { |
- assert(this.length >= length); |
- 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]; |
- if (variable != other.index2variable[i]) return false; |
- |
- // The variable maps to the same index in both environments. |
- int index = variable2index[variable]; |
- if (index == null || index != other.variable2index[variable]) { |
- return false; |
- } |
- } |
- return true; |
- } |
-} |
- |
-/// A class to collect breaks or continues. |
-/// |
-/// When visiting a potential target of breaks or continues, any breaks or |
-/// continues are collected by a JumpCollector and processed later, on demand. |
-/// The site of the break or continue is represented by a continuation |
-/// invocation that will have its target and arguments filled in later. |
-/// |
-/// The environment of the builder at that point is captured and should not |
-/// be subsequently mutated until the jump is resolved. |
-class JumpCollector { |
- final JumpTarget target; |
- final List<ir.InvokeContinuation> _invocations = <ir.InvokeContinuation>[]; |
- final List<Environment> _environments = <Environment>[]; |
- |
- JumpCollector(this.target); |
- |
- bool get isEmpty => _invocations.isEmpty; |
- int get length => _invocations.length; |
- List<ir.InvokeContinuation> get invocations => _invocations; |
- List<Environment> get environments => _environments; |
- |
- void addJump(IrBuilder builder) { |
- ir.InvokeContinuation invoke = new ir.InvokeContinuation.uninitialized(); |
- builder.add(invoke); |
- _invocations.add(invoke); |
- _environments.add(builder.environment); |
- builder._current = null; |
- // TODO(kmillikin): Can we set builder.environment to null to make it |
- // less likely to mutate it? |
- } |
-} |
- |
-/// Mixin that provided encapsulated access to nested builders. |
-class IrBuilderMixin { |
- IrBuilder _irBuilder; |
- |
- /// Execute [f] with [builder] as the current builder. |
- withBuilder(IrBuilder builder, f()) { |
- assert(builder != null); |
- IrBuilder prev = _irBuilder; |
- _irBuilder = builder; |
- var result = f(); |
- _irBuilder = prev; |
- return result; |
- } |
- |
- /// The current builder. |
- IrBuilder get irBuilder { |
- assert(_irBuilder != null); |
- return _irBuilder; |
- } |
-} |
- |
- |
-/// Shared state between nested builders. |
-class IrBuilderSharedState { |
- final ConstantSystem constantSystem; |
- |
- /// A stack of collectors for breaks. |
- final List<JumpCollector> breakCollectors = <JumpCollector>[]; |
- |
- /// A stack of collectors for continues. |
- final List<JumpCollector> continueCollectors = <JumpCollector>[]; |
- |
- final List<ConstDeclaration> localConstants = <ConstDeclaration>[]; |
- |
- final Iterable<Entity> closureLocals; |
- |
- final FunctionElement currentFunction; |
- |
- final ir.Continuation returnContinuation = new ir.Continuation.retrn(); |
- |
- IrBuilderSharedState(this.constantSystem, |
- this.currentFunction, |
- this.closureLocals); |
-} |
- |
-/// A factory for building the cps IR. |
-class IrBuilder { |
- // 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; |
- |
- /// A map from variable indexes to their values. |
- Environment environment; |
- |
- // The IR builder maintains a context, which is an expression with a hole in |
- // it. The hole represents the focus where new expressions can be added. |
- // The context is implemented by 'root' which is the root of the expression |
- // and 'current' which is the expression that immediately contains the hole. |
- // Not all expressions have a hole (e.g., invocations, which always occur in |
- // tail position, do not have a hole). Expressions with a hole have a plug |
- // method. |
- // |
- // Conceptually, visiting a statement takes a context as input and returns |
- // either a new context or else an expression without a hole if all |
- // control-flow paths through the statement have exited. An expression |
- // without a hole is represented by a (root, current) pair where root is the |
- // expression and current is null. |
- // |
- // Conceptually again, visiting an expression takes a context as input and |
- // returns either a pair of a new context and a definition denoting |
- // the expression's value, or else an expression without a hole if all |
- // control-flow paths through the expression have exited. |
- // |
- // We do not pass contexts as arguments or return them. Rather we use the |
- // current context (root, current) as the visitor state and mutate current. |
- // Visiting a statement returns null; visiting an expression returns the |
- // primitive denoting its value. |
- |
- ir.Expression _root = null; |
- ir.Expression _current = null; |
- |
- IrBuilder(ConstantSystem constantSystem, |
- FunctionElement currentFunction, |
- Iterable<Entity> closureLocals) |
- : this.state = new IrBuilderSharedState( |
- constantSystem, currentFunction, closureLocals), |
- this.environment = new Environment.empty(); |
- |
- /// Construct a delimited visitor for visiting a subtree. |
- /// |
- /// The delimited visitor has its own compile-time environment mapping |
- /// 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.environment = new Environment.from(parent.environment); |
- |
- /// Construct a visitor for a recursive continuation. |
- /// |
- /// The recursive continuation builder has fresh parameters (i.e. SSA phis) |
- /// for all the local variables in the parent, because the invocation sites |
- /// of the continuation are not all known when the builder is created. The |
- /// 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.environment = new Environment.empty() { |
- parent.environment.index2variable.forEach(createParameter); |
- } |
- |
- |
- bool get isOpen => _root == null || _current != null; |
- |
- /// Create a parameter for [parameterElement] and add it to the current |
- /// environment. |
- /// |
- /// [isClosureVariable] marks whether [parameterElement] is accessed from an |
- /// inner function. |
- void createParameter(LocalElement parameterElement, |
- {bool isClosureVariable: false}) { |
- ir.Parameter parameter = new ir.Parameter(parameterElement); |
- _parameters.add(parameter); |
- if (isClosureVariable) { |
- add(new ir.SetClosureVariable(parameterElement, parameter)); |
- } else { |
- environment.extend(parameterElement, parameter); |
- } |
- } |
- |
- /// Add the constant [variableElement] to the environment with [value] as its |
- /// constant value. |
- void declareLocalConstant(LocalVariableElement variableElement, |
- ConstantExpression value) { |
- state.localConstants.add(new ConstDeclaration(variableElement, value)); |
- } |
- |
- /// Add [variableElement] to the environment with [initialValue] as its |
- /// initial value. |
- /// |
- /// [isClosureVariable] marks whether [variableElement] is accessed from an |
- /// inner function. |
- void declareLocalVariable(LocalVariableElement variableElement, |
- {ir.Primitive initialValue, |
- bool isClosureVariable: false}) { |
- assert(isOpen); |
- if (initialValue == null) { |
- // TODO(kmillikin): Consider pooling constants. |
- // The initial value is null. |
- initialValue = makePrimConst(state.constantSystem.createNull()); |
- add(new ir.LetPrim(initialValue)); |
- } |
- if (isClosureVariable) { |
- add(new ir.SetClosureVariable(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); |
- } |
- } |
- |
- // 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, |
- // the new hole must be in the newly added expression---which becomes the |
- // new value of current. |
- void add(ir.Expression expr) { |
- assert(isOpen); |
- if (_root == null) { |
- _root = _current = expr; |
- } else { |
- _current = _current.plug(expr); |
- } |
- } |
- |
- ir.Primitive continueWithExpression(ir.Expression build(ir.Continuation k)) { |
- ir.Parameter v = new ir.Parameter(null); |
- ir.Continuation k = new ir.Continuation([v]); |
- ir.Expression expression = build(k); |
- add(new ir.LetCont(k, expression)); |
- return v; |
- } |
- |
- ir.Constant makeConst(ConstantExpression exp) { |
- return new ir.Constant(exp); |
- } |
- |
- ir.Constant makePrimConst(PrimitiveConstantValue value) { |
- return makeConst(new PrimitiveConstantExpression(value)); |
- } |
- |
- // TODO(johnniwinther): Build constants directly through [ConstExp] when these |
- // are created from analyzer2dart. |
- ir.Node buildPrimConst(PrimitiveConstantValue constant) { |
- assert(isOpen); |
- ir.Node prim = makePrimConst(constant); |
- add(new ir.LetPrim(prim)); |
- return prim; |
- } |
- |
- /// Create an integer literal. |
- ir.Constant buildIntegerLiteral(int value) { |
- return buildPrimConst(state.constantSystem.createInt(value)); |
- } |
- |
- /// Create an double literal. |
- ir.Constant buildDoubleLiteral(double value) { |
- return buildPrimConst(state.constantSystem.createDouble(value)); |
- } |
- |
- /// Create an bool literal. |
- ir.Constant buildBooleanLiteral(bool value) { |
- return buildPrimConst(state.constantSystem.createBool(value)); |
- } |
- |
- /// Create an null literal. |
- ir.Constant buildNullLiteral() { |
- return buildPrimConst(state.constantSystem.createNull()); |
- } |
- |
- /// Create a string literal. |
- ir.Constant buildStringLiteral(String value) { |
- return buildPrimConst( |
- state.constantSystem.createString(new ast.DartString.literal(value))); |
- } |
- |
- /// Create a get access of [local]. |
- ir.Primitive buildLocalGet(Element local) { |
- assert(isOpen); |
- return environment.lookup(local); |
- } |
- |
- /// Create a get access of the static [element]. |
- ir.Primitive buildStaticGet(Element element, Selector selector) { |
- assert(isOpen); |
- assert(selector.isGetter); |
- return continueWithExpression( |
- (k) => new ir.InvokeStatic( |
- element, selector, k, const <ir.Definition>[])); |
- } |
- |
- /// Create a dynamic get access on [receiver] where the property is defined |
- /// by the getter [selector]. |
- ir.Primitive buildDynamicGet(ir.Primitive receiver, Selector selector) { |
- assert(isOpen); |
- assert(selector.isGetter); |
- return continueWithExpression( |
- (k) => new ir.InvokeMethod( |
- receiver, selector, k, const <ir.Definition>[])); |
- } |
- |
- /** |
- * Add an explicit `return null` for functions that don't have a return |
- * statement on each branch. This includes functions with an empty body, |
- * such as `foo(){ }`. |
- */ |
- void ensureReturn() { |
- if (!isOpen) return; |
- ir.Constant constant = makePrimConst(state.constantSystem.createNull()); |
- add(new ir.LetPrim(constant)); |
- add(new ir.InvokeContinuation(state.returnContinuation, [constant])); |
- _current = null; |
- } |
- |
- /// Create a [ir.FunctionDefinition] for [element] using [_root] as the body. |
- /// |
- /// Parameters must be created before the construction of the body using |
- /// [createParameter]. |
- ir.FunctionDefinition buildFunctionDefinition( |
- FunctionElement element, |
- List<ConstantExpression> defaults) { |
- if (!element.isAbstract) { |
- ensureReturn(); |
- return new ir.FunctionDefinition( |
- element, state.returnContinuation, _parameters, _root, |
- state.localConstants, defaults); |
- } else { |
- assert(invariant(element, _root == null, |
- message: "Non-empty body for abstract method $element: $_root")); |
- assert(invariant(element, state.localConstants.isEmpty, |
- message: "Local constants for abstract method $element: " |
- "${state.localConstants}")); |
- return new ir.FunctionDefinition.abstract( |
- element, _parameters, defaults); |
- } |
- } |
- |
- |
- /// Create a super invocation with method name and arguments structure defined |
- /// by [selector] and argument values defined by [arguments]. |
- ir.Primitive buildSuperInvocation(Selector selector, |
- List<ir.Definition> arguments) { |
- assert(isOpen); |
- return continueWithExpression( |
- (k) => new ir.InvokeSuperMethod(selector, k, arguments)); |
- |
- } |
- |
- /// Create a dynamic invocation on [receiver] with method name and arguments |
- /// structure defined by [selector] and argument values defined by |
- /// [arguments]. |
- ir.Primitive buildDynamicInvocation(ir.Definition receiver, |
- Selector selector, |
- List<ir.Definition> arguments) { |
- assert(isOpen); |
- return continueWithExpression( |
- (k) => new ir.InvokeMethod(receiver, selector, k, arguments)); |
- } |
- |
- /// Create a static invocation of [element] with arguments structure defined |
- /// by [selector] and argument values defined by [arguments]. |
- ir.Primitive buildStaticInvocation(Element element, |
- Selector selector, |
- List<ir.Definition> arguments) { |
- return continueWithExpression( |
- (k) => new ir.InvokeStatic(element, selector, k, arguments)); |
- } |
- |
- /// Create a return statement `return value;` or `return;` if [value] is |
- /// null. |
- void buildReturn([ir.Primitive value]) { |
- // Build(Return(e), C) = C'[InvokeContinuation(return, x)] |
- // where (C', x) = Build(e, C) |
- // |
- // Return without a subexpression is translated as if it were return null. |
- assert(isOpen); |
- if (value == null) { |
- value = makePrimConst(state.constantSystem.createNull()); |
- add(new ir.LetPrim(value)); |
- } |
- add(new ir.InvokeContinuation(state.returnContinuation, [value])); |
- _current = null; |
- } |
- |
- // Build(BreakStatement L, C) = C[InvokeContinuation(...)] |
- // |
- // The continuation and arguments are filled in later after translating |
- // the body containing the break. |
- bool buildBreak(JumpTarget target) { |
- return buildJumpInternal(target, state.breakCollectors); |
- } |
- |
- // Build(ContinueStatement L, C) = C[InvokeContinuation(...)] |
- // |
- // The continuation and arguments are filled in later after translating |
- // the body containing the continue. |
- bool buildContinue(JumpTarget target) { |
- return buildJumpInternal(target, state.continueCollectors); |
- } |
- |
- bool buildJumpInternal(JumpTarget target, |
- Iterable<JumpCollector> collectors) { |
- assert(isOpen); |
- for (JumpCollector collector in collectors) { |
- if (target == collector.target) { |
- collector.addJump(this); |
- return true; |
- } |
- } |
- return false; |
- } |
-} |
- |
/** |
* A tree visitor that builds [IrNodes]. The visit methods add statements using |
* to the [builder] and return the last added statement for trees that represent |