blob: 3e4ed1790904785fa56979252c480ee6682998b1 [file] [log] [blame]
// 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,
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, <List<VariableDeclaration>>[]);
}
}
class LocalContext extends Context {
final ClosureConverter converter;
final Context parent;
final VariableDeclaration self;
final VectorCreation vectorCreation;
final List<VariableDeclaration> variables = <VariableDeclaration>[];
final Map<VariableDeclaration, VectorSet> initializers =
<VariableDeclaration, VectorSet>{};
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);
++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<List<VariableDeclaration>> variabless = <List<VariableDeclaration>>[];
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<List<VariableDeclaration>> 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<VariableDeclaration> 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);
}
}