// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file // 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 kernel.transformations.closure.context; import '../../ast.dart' show Expression, FunctionNode, NullLiteral, StringLiteral, Throw, TreeNode, VariableDeclaration, VariableGet, VariableSet, VectorCreation, VectorGet, VectorSet, VectorCopy; import '../../frontend/accessors.dart' show Accessor, VariableAccessor; import 'converter.dart' show ClosureConverter; abstract class Context { /// Returns a new expression for accessing this context. Expression get expression; /// Returns an accessor (or null) for accessing this context. Accessor get accessor; /// Extend the context to include [variable] initialized to [value]. For /// example, this replaces the [VariableDeclaration] node of a captured local /// variable. /// /// This may create a new context and update the `context` field of the /// current [ClosureConverter]. // TODO(ahe): Return context instead? void extend(VariableDeclaration variable, Expression value); /// Update the initializer [value] of [variable] which was previously added /// with [extend]. This is used when [value] isn't available when the context /// was extended. void update(VariableDeclaration variable, Expression value) { throw "not supported $runtimeType"; } /// Returns a new expression for reading the value of [variable] from this /// context. For example, for replacing a [VariableGet] of a captured local /// variable. Expression lookup(VariableDeclaration variable); /// Returns a new expression which stores [value] in [variable] in this /// context. For example, for replacing a [VariableSet] of a captured local /// variable. Expression assign(VariableDeclaration variable, Expression value, {bool voidContext: false}); /// Returns a new context whose parent is this context. The optional argument /// [accessor] controls how the nested context access this context. This is /// used, for example, when hoisting a local function. In this case, access /// to this context can't be accessed directly via [expression]. In other /// cases, for example, a for-loop, this context is still in scope and can be /// accessed directly (with [accessor]). Context toNestedContext([Accessor accessor]); /// Returns a new expression which will copy this context and store the copy /// in the local variable currently holding this context. Expression clone() { return new Throw( new StringLiteral("Context clone not implemented for ${runtimeType}")); } } class NoContext extends Context { final ClosureConverter converter; NoContext(this.converter); Expression get expression => new NullLiteral(); Accessor get accessor => null; void extend(VariableDeclaration variable, Expression value) { converter.context = new LocalContext(converter, this) ..extend(variable, value); } Expression lookup(VariableDeclaration variable) { throw 'Unbound NoContext.lookup($variable)'; } Expression assign(VariableDeclaration variable, Expression value, {bool voidContext: false}) { throw 'Unbound NoContext.assign($variable, ...)'; } Context toNestedContext([Accessor accessor]) { return new NestedContext( converter, accessor, >[]); } } class LocalContext extends Context { final ClosureConverter converter; final Context parent; final VariableDeclaration self; final VectorCreation vectorCreation; final List variables = []; final Map initializers = {}; LocalContext._internal( this.converter, this.parent, this.self, this.vectorCreation); factory LocalContext(ClosureConverter converter, Context parent) { converter.rewriter.insertContextDeclaration(parent.expression); return new LocalContext._internal( converter, parent, converter.rewriter.contextDeclaration, converter.rewriter.vectorCreation); } Expression get expression => accessor.buildSimpleRead(); Accessor get accessor => new VariableAccessor(self, null, TreeNode.noOffset); void extend(VariableDeclaration variable, Expression value) { // Increase index by 2, because the type arguments vector occupies position // 0, the parent occupies position 1, and all other variables are therefore // shifted by 2. VectorSet initializer = new VectorSet(expression, variables.length + 2, value); value.parent = initializer; converter.rewriter.insertExtendContext(initializer); if (variable.parent is FunctionNode) { converter.rewriter.insertZeroOutParameter(variable); } ++vectorCreation.length; variables.add(variable); initializers[variable] = initializer; } void update(VariableDeclaration variable, Expression value) { VectorSet initializer = initializers[variable]; initializer.value = value; value.parent = initializer; } Expression lookup(VariableDeclaration variable) { var index = variables.indexOf(variable); // Increase index by 2 in case of success, because the type arguments vector // occupies position 0, the parent occupies position 1, and all other // variables are therefore shifted by 2. return index == -1 ? parent.lookup(variable) : new VectorGet(expression, index + 2); } Expression assign(VariableDeclaration variable, Expression value, {bool voidContext: false}) { var index = variables.indexOf(variable); // Increase index by 2 in case of success, because the type arguments vector // occupies position 0, the parent occupies position 1, and all other // variables are therefore shifted by 2. return index == -1 ? parent.assign(variable, value, voidContext: voidContext) : new VectorSet(expression, index + 2, value); } Context toNestedContext([Accessor accessor]) { accessor ??= this.accessor; List> variabless = >[]; var current = this; while (current != null && current is! NoContext) { if (current is LocalContext) { variabless.add(current.variables); current = current.parent; } else if (current is NestedContext) { variabless.addAll((current as NestedContext).variabless); current = null; } } return new NestedContext(converter, accessor, variabless); } Expression clone() { self.isFinal = false; return new VariableSet(self, new VectorCopy(new VariableGet(self))); } } class NestedContext extends Context { final ClosureConverter converter; final Accessor accessor; final List> variabless; NestedContext(this.converter, this.accessor, this.variabless); Expression get expression { return accessor?.buildSimpleRead() ?? new NullLiteral(); } void extend(VariableDeclaration variable, Expression value) { converter.context = new LocalContext(converter, this) ..extend(variable, value); } Expression lookup(VariableDeclaration variable) { Expression context = expression; for (var variables in variabless) { var index = variables.indexOf(variable); if (index != -1) { // Increase index by 2 in case of success, because the type arguments // vector occupies position 0, the parent occupies item 1, and all other // variables are therefore shifted by 2. return new VectorGet(context, index + 2); } // Item 1 of a context always points to its parent. context = new VectorGet(context, 1); } throw 'Unbound NestedContext.lookup($variable)'; } Expression assign(VariableDeclaration variable, Expression value, {bool voidContext: false}) { Expression context = expression; for (List variables in variabless) { var index = variables.indexOf(variable); if (index != -1) { // Increase index by 2 in case of success, because the type arguments // vector occupies position 0, the parent occupies item 1, and all other // variables are therefore shifted by 2. return new VectorSet(context, index + 2, value); } // Item 1 of a context always points to its parent. context = new VectorGet(context, 1); } throw 'Unbound NestedContext.lookup($variable)'; } Context toNestedContext([Accessor accessor]) { return new NestedContext(converter, accessor ?? this.accessor, variabless); } }