243 lines
8.2 KiB
Dart
243 lines
8.2 KiB
Dart
// 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 1, because the parent occupies item 0, and all other
|
|
// variables are therefore shifted by 1.
|
|
VectorSet initializer =
|
|
new VectorSet(expression, variables.length + 1, 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 1 in case of success, because the parent occupies
|
|
// item 0, and all other variables are therefore shifted by 1.
|
|
return index == -1
|
|
? parent.lookup(variable)
|
|
: new VectorGet(expression, index + 1);
|
|
}
|
|
|
|
Expression assign(VariableDeclaration variable, Expression value,
|
|
{bool voidContext: false}) {
|
|
var index = variables.indexOf(variable);
|
|
// Increase index by 1 in case of success, because the parent occupies
|
|
// item 0, and all other variables are therefore shifted by 1.
|
|
return index == -1
|
|
? parent.assign(variable, value, voidContext: voidContext)
|
|
: new VectorSet(expression, index + 1, 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 1, because the parent occupies item 0, and all
|
|
// other variables are therefore shifted by 1.
|
|
return new VectorGet(context, index + 1);
|
|
}
|
|
// Item 0 of a context always points to its parent.
|
|
context = new VectorGet(context, 0);
|
|
}
|
|
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 1, because the parent occupies item 0, and all
|
|
// other variables are therefore shifted by 1.
|
|
return new VectorSet(context, index + 1, value);
|
|
}
|
|
// Item 0 of a context always points to its parent.
|
|
context = new VectorGet(context, 0);
|
|
}
|
|
throw 'Unbound NestedContext.lookup($variable)';
|
|
}
|
|
|
|
Context toNestedContext([Accessor accessor]) {
|
|
return new NestedContext(converter, accessor ?? this.accessor, variabless);
|
|
}
|
|
}
|