blob: 3a3495e1aea04876b9755b211af885b076eb25c7 [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
Arguments,
Class,
ConstructorInvocation,
Expression,
ExpressionStatement,
IntLiteral,
InterfaceType,
MethodInvocation,
Name,
NullLiteral,
PropertyGet,
PropertySet,
StringLiteral,
Throw,
VariableDeclaration,
VariableGet,
VariableSet;
import '../../frontend/accessors.dart'
show Accessor, IndexAccessor, 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 IntLiteral size;
final List<VariableDeclaration> variables = <VariableDeclaration>[];
final Map<VariableDeclaration, Arguments> initializers =
<VariableDeclaration, Arguments>{};
LocalContext._internal(this.converter, this.parent, this.self, this.size);
factory LocalContext(ClosureConverter converter, Context parent) {
Class contextClass = converter.contextClass;
assert(contextClass.constructors.length == 1);
IntLiteral zero = new IntLiteral(0);
VariableDeclaration declaration = new VariableDeclaration.forValue(
new ConstructorInvocation(
contextClass.constructors.first, new Arguments(<Expression>[zero])),
type: new InterfaceType(contextClass));
declaration.name = "#context";
converter.insert(declaration);
converter.insert(new ExpressionStatement(new PropertySet(
new VariableGet(declaration), new Name('parent'), parent.expression)));
return new LocalContext._internal(converter, parent, declaration, zero);
}
Expression get expression => accessor.buildSimpleRead();
Accessor get accessor => new VariableAccessor(self);
void extend(VariableDeclaration variable, Expression value) {
Arguments arguments =
new Arguments(<Expression>[new IntLiteral(variables.length), value]);
converter.insert(new ExpressionStatement(
new MethodInvocation(expression, new Name('[]='), arguments)));
++size.value;
variables.add(variable);
initializers[variable] = arguments;
}
void update(VariableDeclaration variable, Expression value) {
Arguments arguments = initializers[variable];
arguments.positional[1] = value;
value.parent = arguments;
}
Expression lookup(VariableDeclaration variable) {
var index = variables.indexOf(variable);
return index == -1
? parent.lookup(variable)
: new MethodInvocation(expression, new Name('[]'),
new Arguments(<Expression>[new IntLiteral(index)]));
}
Expression assign(VariableDeclaration variable, Expression value,
{bool voidContext: false}) {
var index = variables.indexOf(variable);
return index == -1
? parent.assign(variable, value, voidContext: voidContext)
: IndexAccessor
.make(expression, new IntLiteral(index), null, null)
.buildAssignment(value, voidContext: voidContext);
}
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 MethodInvocation(
new VariableGet(self), new Name("copy"), new Arguments.empty()));
}
}
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) {
var context = expression;
for (var variables in variabless) {
var index = variables.indexOf(variable);
if (index != -1) {
return new MethodInvocation(context, new Name('[]'),
new Arguments(<Expression>[new IntLiteral(index)]));
}
context = new PropertyGet(context, new Name('parent'));
}
throw 'Unbound NestedContext.lookup($variable)';
}
Expression assign(VariableDeclaration variable, Expression value,
{bool voidContext: false}) {
var context = expression;
for (var variables in variabless) {
var index = variables.indexOf(variable);
if (index != -1) {
return IndexAccessor
.make(context, new IntLiteral(index), null, null)
.buildAssignment(value, voidContext: voidContext);
}
context = new PropertyGet(context, new Name('parent'));
}
throw 'Unbound NestedContext.lookup($variable)';
}
Context toNestedContext([Accessor accessor]) {
return new NestedContext(converter, accessor ?? this.accessor, variabless);
}
}