50e1444644
R=ahe@google.com, kmillikin@google.com, paulberry@google.com, sigmund@google.com Change-Id: Iea7360e5c80342e303ff1bd261754676ac3a057a Reviewed-on: https://dart-review.googlesource.com/3081 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Reviewed-by: Dan Rubel <danrubel@google.com> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
2159 lines
73 KiB
Dart
2159 lines
73 KiB
Dart
// Copyright (c) 2017, 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.interpreter;
|
|
|
|
import '../ast.dart';
|
|
import '../ast.dart' as ast show Class;
|
|
|
|
import '../log.dart';
|
|
export '../log.dart';
|
|
|
|
class NotImplemented {
|
|
String message;
|
|
|
|
NotImplemented(this.message);
|
|
|
|
String toString() => message;
|
|
}
|
|
|
|
class Interpreter {
|
|
// The execution of the program starts with empty main environment.
|
|
static MainEnvironment mainEnvironment =
|
|
new MainEnvironment(<Member, Location>{});
|
|
|
|
final Program program;
|
|
final StatementExecuter visitor = new StatementExecuter();
|
|
|
|
Interpreter(this.program);
|
|
|
|
void run() {
|
|
assert(program.libraries.isEmpty);
|
|
Procedure mainMethod = program.mainMethod;
|
|
|
|
if (mainMethod == null) return;
|
|
|
|
Statement statementBlock = mainMethod.function.body;
|
|
ExecConfiguration configuration = new ExecConfiguration(
|
|
statementBlock, new Environment.empty(), new State.initial());
|
|
visitor.trampolinedExecution(configuration);
|
|
}
|
|
}
|
|
|
|
class Location {
|
|
Value value;
|
|
|
|
Location.empty();
|
|
Location(this.value);
|
|
}
|
|
|
|
class Binding {
|
|
final VariableDeclaration variable;
|
|
final Location location;
|
|
|
|
Binding(this.variable, this.location);
|
|
}
|
|
|
|
/// Represents the top level environment that binds previously accessed or set
|
|
/// static fields to the location that stores their value.
|
|
class MainEnvironment {
|
|
final Map<Member, Location> _staticFields;
|
|
|
|
MainEnvironment(this._staticFields);
|
|
|
|
bool contains(Member member) => _staticFields[member] != null;
|
|
|
|
Value lookup(Member member) {
|
|
assert(contains(member));
|
|
return _staticFields[member].value;
|
|
}
|
|
|
|
void updateStore(Member member, Value value) {
|
|
assert(contains(member));
|
|
_staticFields[member].value = value;
|
|
}
|
|
|
|
MainEnvironment extend(Member member, Value value) {
|
|
var newMap = new Map<Member, Location>.from(_staticFields);
|
|
newMap[member] = new Location(value);
|
|
return new MainEnvironment(newMap);
|
|
}
|
|
}
|
|
|
|
class Environment {
|
|
final List<Binding> bindings = <Binding>[];
|
|
final Environment parent;
|
|
|
|
Value get thisInstance {
|
|
return containsThis()
|
|
? lookupThis().value
|
|
: throw "Invalid reference to 'this' expression";
|
|
}
|
|
|
|
Environment.empty() : parent = null;
|
|
Environment(this.parent);
|
|
|
|
bool contains(VariableDeclaration variable) {
|
|
for (Binding b in bindings) {
|
|
if (identical(b.variable, variable)) return true;
|
|
}
|
|
return parent?.contains(variable) ?? false;
|
|
}
|
|
|
|
bool containsThis() {
|
|
for (Binding b in bindings) {
|
|
if (identical(b.variable.name, 'this')) return true;
|
|
}
|
|
return parent?.containsThis() ?? false;
|
|
}
|
|
|
|
Binding lookupBinding(VariableDeclaration variable) {
|
|
assert(contains(variable));
|
|
for (Binding b in bindings) {
|
|
if (identical(b.variable, variable)) return b;
|
|
}
|
|
return parent.lookupBinding(variable);
|
|
}
|
|
|
|
Location lookupThis() {
|
|
assert(containsThis());
|
|
for (Binding b in bindings) {
|
|
if (identical(b.variable.name, 'this')) return b.location;
|
|
}
|
|
return parent.lookupThis();
|
|
}
|
|
|
|
Value lookup(VariableDeclaration variable) {
|
|
return lookupBinding(variable).location.value;
|
|
}
|
|
|
|
void updateStore(VariableDeclaration variable, Value value) {
|
|
assert(contains(variable));
|
|
lookupBinding(variable).location.value = value;
|
|
}
|
|
|
|
Environment extend(VariableDeclaration variable, Value value) {
|
|
assert(!contains(variable));
|
|
return new Environment(this)
|
|
..bindings.add(new Binding(variable, new Location(value)));
|
|
}
|
|
|
|
Environment extendWithThis(ObjectValue v) {
|
|
assert(!containsThis());
|
|
return extend(new VariableDeclaration('this'), v);
|
|
}
|
|
}
|
|
|
|
/// Evaluate expressions.
|
|
class Evaluator extends ExpressionVisitor1<Configuration, EvalConfiguration> {
|
|
Configuration eval(Expression expr, EvalConfiguration config) =>
|
|
expr.accept1(this, config);
|
|
|
|
Configuration evalList(List<InterpreterExpression> list, Environment env,
|
|
ExceptionComponents exceptionComponents, ApplicationContinuation cont) {
|
|
if (list.isNotEmpty) {
|
|
return new EvalConfiguration(
|
|
list.first.expression,
|
|
env,
|
|
exceptionComponents,
|
|
new ExpressionListEK(
|
|
list.first, list.skip(1), env, exceptionComponents, cont));
|
|
}
|
|
return new ApplicationConfiguration(cont, <InterpreterValue>[]);
|
|
}
|
|
|
|
Configuration defaultExpression(Expression node, EvalConfiguration config) {
|
|
throw new NotImplemented('Evaluation for expressions of type '
|
|
'${node.runtimeType} is not implemented.');
|
|
}
|
|
|
|
Configuration visitInvalidExpression1(
|
|
InvalidExpression node, EvalConfiguration config) {
|
|
throw 'Invalid expression at ${node.location.toString()}';
|
|
}
|
|
|
|
Configuration visitVariableGet(VariableGet node, EvalConfiguration config) {
|
|
Value value = config.environment.lookup(node.variable);
|
|
return new ValuePassingConfiguration(config.continuation, value);
|
|
}
|
|
|
|
Configuration visitVariableSet(VariableSet node, EvalConfiguration config) {
|
|
var cont = new VariableSetEK(
|
|
node.variable, config.environment, config.continuation);
|
|
return new EvalConfiguration(
|
|
node.value, config.environment, config.exceptionComponents, cont);
|
|
}
|
|
|
|
Configuration visitPropertyGet(PropertyGet node, EvalConfiguration config) {
|
|
var cont = new PropertyGetEK(
|
|
node.name, config.exceptionComponents, config.continuation);
|
|
return new EvalConfiguration(
|
|
node.receiver, config.environment, config.exceptionComponents, cont);
|
|
}
|
|
|
|
Configuration visitPropertySet(PropertySet node, EvalConfiguration config) {
|
|
var cont = new PropertySetEK(node.value, node.name, config.environment,
|
|
config.exceptionComponents, config.continuation);
|
|
return new EvalConfiguration(
|
|
node.receiver, config.environment, config.exceptionComponents, cont);
|
|
}
|
|
|
|
Configuration visitStaticGet(StaticGet node, EvalConfiguration config) {
|
|
Member member = node.target;
|
|
|
|
if (member is Procedure && !member.isAccessor) {
|
|
// Create a closure for the method tear off.
|
|
var v = new FunctionValue(member.function, new Environment.empty());
|
|
return new ValuePassingConfiguration(config.continuation, v);
|
|
}
|
|
|
|
if (member is Procedure && member.isGetter) {
|
|
// Execute the body of the getter.
|
|
var state = new State.initial()
|
|
.withReturnContinuation(config.continuation)
|
|
.withContinuation(new ExitSK(config.continuation, Value.nullInstance))
|
|
.withException(config.exceptionComponents);
|
|
return new ExecConfiguration(
|
|
member.function.body, new Environment.empty(), state);
|
|
}
|
|
|
|
assert(member is Field);
|
|
if (Interpreter.mainEnvironment.contains(member)) {
|
|
// Read the value for the member in the main environment.
|
|
return new ValuePassingConfiguration(
|
|
config.continuation, Interpreter.mainEnvironment.lookup(member));
|
|
}
|
|
|
|
// Otherwise, the static field is accessed for the first time.
|
|
// We extend the main environment with a new binding.
|
|
Interpreter.mainEnvironment =
|
|
Interpreter.mainEnvironment.extend(member, Value.nullInstance);
|
|
|
|
if ((member as Field).initializer == null) {
|
|
return new ValuePassingConfiguration(
|
|
config.continuation, Value.nullInstance);
|
|
}
|
|
|
|
// The initializer expression is evaluated otherwise.
|
|
var cont = new StaticSetEK(member, config.continuation);
|
|
return new EvalConfiguration((member as Field).initializer,
|
|
new Environment.empty(), config.exceptionComponents, cont);
|
|
}
|
|
|
|
Configuration visitStaticSet(StaticSet node, EvalConfiguration config) {
|
|
Member member = node.target;
|
|
ExpressionContinuation cont;
|
|
|
|
if (member is Procedure) {
|
|
assert(member.isSetter);
|
|
cont = new StaticSetterEK(
|
|
member.function, config.exceptionComponents, config.continuation);
|
|
} else {
|
|
assert(member is Field);
|
|
cont = new StaticSetEK(member, config.continuation);
|
|
}
|
|
|
|
return new EvalConfiguration(
|
|
node.value, config.environment, config.exceptionComponents, cont);
|
|
}
|
|
|
|
Configuration visitStaticInvocation(
|
|
StaticInvocation node, EvalConfiguration config) {
|
|
if ('print' == node.name.toString()) {
|
|
var cont = new PrintEK(config.continuation);
|
|
return new EvalConfiguration(node.arguments.positional.first,
|
|
config.environment, config.exceptionComponents, cont);
|
|
} else {
|
|
log.info('static-invocation-${node.target.name.toString()}\n');
|
|
|
|
List<InterpreterExpression> args =
|
|
_getArgumentExpressions(node.arguments, node.target.function);
|
|
ApplicationContinuation cont = new StaticInvocationA(node.target.function,
|
|
config.exceptionComponents, config.continuation);
|
|
return new EvalListConfiguration(
|
|
args, config.environment, config.exceptionComponents, cont);
|
|
}
|
|
}
|
|
|
|
Configuration visitMethodInvocation(
|
|
MethodInvocation node, EvalConfiguration config) {
|
|
// Currently supports only method invocation with <2 arguments and is used
|
|
// to evaluate implemented operators for int, double and String values.
|
|
var cont = new MethodInvocationEK(node.arguments, node.name,
|
|
config.environment, config.exceptionComponents, config.continuation);
|
|
|
|
return new EvalConfiguration(
|
|
node.receiver, config.environment, config.exceptionComponents, cont);
|
|
}
|
|
|
|
Configuration visitConstructorInvocation(
|
|
ConstructorInvocation node, EvalConfiguration config) {
|
|
ApplicationContinuation cont = new ConstructorInvocationA(
|
|
node.target, config.exceptionComponents, config.continuation);
|
|
var args = _getArgumentExpressions(node.arguments, node.target.function);
|
|
|
|
return new EvalListConfiguration(
|
|
args, config.environment, config.exceptionComponents, cont);
|
|
}
|
|
|
|
Configuration visitNot(Not node, EvalConfiguration config) {
|
|
return new EvalConfiguration(node.operand, config.environment,
|
|
config.exceptionComponents, new NotEK(config.continuation));
|
|
}
|
|
|
|
Configuration visitLogicalExpression(
|
|
LogicalExpression node, EvalConfiguration config) {
|
|
if ('||' == node.operator) {
|
|
var cont = new OrEK(node.right, config.environment,
|
|
config.exceptionComponents, config.continuation);
|
|
return new EvalConfiguration(
|
|
node.left, config.environment, config.exceptionComponents, cont);
|
|
} else {
|
|
assert('&&' == node.operator);
|
|
var cont = new AndEK(node.right, config.environment,
|
|
config.exceptionComponents, config.continuation);
|
|
return new EvalConfiguration(
|
|
node.left, config.environment, config.exceptionComponents, cont);
|
|
}
|
|
}
|
|
|
|
Configuration visitConditionalExpression(
|
|
ConditionalExpression node, EvalConfiguration config) {
|
|
var cont = new ConditionalEK(node.then, node.otherwise, config.environment,
|
|
config.exceptionComponents, config.continuation);
|
|
return new EvalConfiguration(
|
|
node.condition, config.environment, config.exceptionComponents, cont);
|
|
}
|
|
|
|
Configuration visitStringConcatenation(
|
|
StringConcatenation node, EvalConfiguration config) {
|
|
var cont = new StringConcatenationA(config.continuation);
|
|
var expressions = node.expressions
|
|
.map((Expression e) => new PositionalExpression(e))
|
|
.toList();
|
|
return new EvalListConfiguration(
|
|
expressions, config.environment, config.exceptionComponents, cont);
|
|
}
|
|
|
|
Configuration visitThisExpression(
|
|
ThisExpression node, EvalConfiguration config) {
|
|
return new ValuePassingConfiguration(
|
|
config.continuation, config.environment.thisInstance);
|
|
}
|
|
|
|
Configuration visitThrow(Throw node, EvalConfiguration config) {
|
|
var cont = new ThrowEK(config.exceptionComponents);
|
|
|
|
return new EvalConfiguration(
|
|
node.expression, config.environment, config.exceptionComponents, cont);
|
|
}
|
|
|
|
Configuration visitFunctionExpression(
|
|
FunctionExpression node, EvalConfiguration config) {
|
|
var val = new FunctionValue(node.function, config.environment);
|
|
return new ValuePassingConfiguration(config.continuation, val);
|
|
}
|
|
|
|
// Evaluation of BasicLiterals.
|
|
Configuration visitStringLiteral(
|
|
StringLiteral node, EvalConfiguration config) {
|
|
return new ValuePassingConfiguration(
|
|
config.continuation, new StringValue(node.value));
|
|
}
|
|
|
|
Configuration visitIntLiteral(IntLiteral node, EvalConfiguration config) {
|
|
return new ValuePassingConfiguration(
|
|
config.continuation, new IntValue(node.value));
|
|
}
|
|
|
|
Configuration visitDoubleLiteral(
|
|
DoubleLiteral node, EvalConfiguration config) {
|
|
return new ValuePassingConfiguration(
|
|
config.continuation, new DoubleValue(node.value));
|
|
}
|
|
|
|
Configuration visitBoolLiteral(BoolLiteral node, EvalConfiguration config) {
|
|
Value value = node.value ? Value.trueInstance : Value.falseInstance;
|
|
return new ValuePassingConfiguration(config.continuation, value);
|
|
}
|
|
|
|
Configuration visitNullLiteral(NullLiteral node, EvalConfiguration config) {
|
|
return new ValuePassingConfiguration(
|
|
config.continuation, Value.nullInstance);
|
|
}
|
|
|
|
Configuration visitLet(Let node, EvalConfiguration config) {
|
|
var letCont = new LetEK(node.variable, node.body, config.environment,
|
|
config.exceptionComponents, config.continuation);
|
|
return new EvalConfiguration(node.variable.initializer, config.environment,
|
|
config.exceptionComponents, letCont);
|
|
}
|
|
}
|
|
|
|
/// Represents a state for statement execution.
|
|
class State {
|
|
final Label labels;
|
|
// TODO: Add switch labels.
|
|
final ExceptionComponents exceptionComponents;
|
|
final ExpressionContinuation returnContinuation;
|
|
final StatementContinuation continuation;
|
|
|
|
State(this.labels, this.exceptionComponents, this.returnContinuation,
|
|
this.continuation);
|
|
|
|
State.initial()
|
|
: labels = null,
|
|
exceptionComponents = new ExceptionComponents.initial(),
|
|
returnContinuation = null,
|
|
continuation = null;
|
|
|
|
State withBreak(Statement stmt, Environment env) {
|
|
var cont = new BreakBK(continuation, env);
|
|
Label breakLabels = new Label(stmt, cont, labels);
|
|
return new State(
|
|
breakLabels, exceptionComponents, returnContinuation, continuation);
|
|
}
|
|
|
|
State withReturnContinuation(ExpressionContinuation returnCont) {
|
|
return new State(labels, exceptionComponents, returnCont, continuation);
|
|
}
|
|
|
|
State withContinuation(StatementContinuation cont) {
|
|
return new State(labels, exceptionComponents, returnContinuation, cont);
|
|
}
|
|
|
|
State withException(ExceptionComponents ecs) {
|
|
return new State(labels, ecs, returnContinuation, continuation);
|
|
}
|
|
|
|
Label lookupLabel(LabeledStatement s) {
|
|
assert(labels != null);
|
|
return labels.lookupLabel(s);
|
|
}
|
|
}
|
|
|
|
/// Represents a labeled statement, the corresponding continuation and the
|
|
/// enclosing label.
|
|
class Label {
|
|
final LabeledStatement statement;
|
|
final BreakContinuation continuation;
|
|
final Label enclosingLabel;
|
|
|
|
Label(this.statement, this.continuation, this.enclosingLabel);
|
|
|
|
Label lookupLabel(LabeledStatement s) {
|
|
if (identical(s, statement)) return this;
|
|
assert(enclosingLabel != null);
|
|
return enclosingLabel.lookupLabel(s);
|
|
}
|
|
|
|
// Recursively install finally break to all labels.
|
|
Label withFinalizer(Statement finalizer, Environment env, State state) {
|
|
var label = enclosingLabel?.withFinalizer(finalizer, env, state);
|
|
var finallyCont = new FinallyBK(finalizer, env, state, continuation);
|
|
return new Label(statement, finallyCont, label);
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
// Configurations
|
|
// ------------------------------------------------------------------------
|
|
|
|
abstract class Configuration {
|
|
/// Executes the current and returns the next configuration.
|
|
Configuration step(StatementExecuter executer);
|
|
}
|
|
|
|
/// Configuration for evaluating an [Expression].
|
|
class EvalConfiguration extends Configuration {
|
|
final Expression expression;
|
|
|
|
/// Environment in which the expression is evaluated.
|
|
final Environment environment;
|
|
|
|
/// Exception components.
|
|
final ExceptionComponents exceptionComponents;
|
|
|
|
/// Next continuation to be applied.
|
|
final Continuation continuation;
|
|
|
|
EvalConfiguration(this.expression, this.environment, this.exceptionComponents,
|
|
this.continuation);
|
|
|
|
Configuration step(StatementExecuter executer) =>
|
|
executer.eval(expression, this);
|
|
}
|
|
|
|
/// Configuration for evaluating a `List<InterpreterExpression>`.
|
|
class EvalListConfiguration extends Configuration {
|
|
final List<InterpreterExpression> expressions;
|
|
final Environment environment;
|
|
final ExceptionComponents exceptionComponents;
|
|
final ApplicationContinuation continuation;
|
|
|
|
EvalListConfiguration(this.expressions, this.environment,
|
|
this.exceptionComponents, this.continuation);
|
|
|
|
Configuration step(StatementExecuter executer) => executer.evalList(
|
|
expressions, environment, exceptionComponents, continuation);
|
|
}
|
|
|
|
/// Configuration for execution of a [Statement].
|
|
class ExecConfiguration extends Configuration {
|
|
final Statement currentStatement;
|
|
final Environment environment;
|
|
final State state;
|
|
|
|
ExecConfiguration(this.currentStatement, this.environment, this.state);
|
|
|
|
Configuration step(StatementExecuter executer) {
|
|
return executer.exec(currentStatement, this);
|
|
}
|
|
}
|
|
|
|
/// Configuration for applying a [StatementContinuation] to an [Environment].
|
|
class ForwardConfiguration extends Configuration {
|
|
final StatementContinuation continuation;
|
|
final Environment environment;
|
|
|
|
ForwardConfiguration(this.continuation, this.environment);
|
|
|
|
Configuration step(StatementExecuter _) => continuation?.call(environment);
|
|
}
|
|
|
|
/// Configuration for applying [ExpressionContinuation] to a [Value].
|
|
class ValuePassingConfiguration extends Configuration {
|
|
final ExpressionContinuation continuation;
|
|
final Value value;
|
|
|
|
ValuePassingConfiguration(this.continuation, this.value);
|
|
|
|
Configuration step(StatementExecuter _) => continuation(value);
|
|
}
|
|
|
|
/// Configuration for applying an [ApplicationContinuation] to a
|
|
/// `List<InterpreterValue>`.
|
|
class ApplicationConfiguration extends Configuration {
|
|
final ApplicationContinuation continuation;
|
|
final List<InterpreterValue> values;
|
|
|
|
ApplicationConfiguration(this.continuation, this.values);
|
|
|
|
Configuration step(StatementExecuter _) => continuation(values);
|
|
}
|
|
|
|
/// Configuration for applying an [ExceptionHandler] to an exception and a
|
|
/// stack trace.
|
|
class ThrowConfiguration extends Configuration {
|
|
final ExceptionHandler handler;
|
|
final Value exception;
|
|
final StackTrace stacktrace;
|
|
|
|
ThrowConfiguration(this.handler, this.exception, this.stacktrace);
|
|
|
|
Configuration step(StatementExecuter _) => handler(exception, stacktrace);
|
|
}
|
|
|
|
class BreakConfiguration extends Configuration {
|
|
final BreakContinuation continuation;
|
|
|
|
BreakConfiguration(this.continuation);
|
|
|
|
Configuration step(StatementExecuter _) => continuation();
|
|
}
|
|
|
|
abstract class BreakContinuation extends Continuation {
|
|
Configuration call();
|
|
}
|
|
|
|
class BreakBK extends BreakContinuation {
|
|
final StatementContinuation continuation;
|
|
final Environment environment;
|
|
|
|
BreakBK(this.continuation, this.environment);
|
|
|
|
Configuration call() => new ForwardConfiguration(continuation, environment);
|
|
}
|
|
|
|
class FinallyBK extends BreakContinuation {
|
|
final Statement finalizer;
|
|
final Environment environment;
|
|
final State state;
|
|
final BreakContinuation continuation;
|
|
|
|
FinallyBK(this.finalizer, this.environment, this.state, this.continuation);
|
|
|
|
Configuration call() {
|
|
var cont = new BreakSK(continuation);
|
|
return new ExecConfiguration(
|
|
finalizer, environment, state.withContinuation(cont));
|
|
}
|
|
}
|
|
|
|
class BreakSK extends StatementContinuation {
|
|
final BreakContinuation continuation;
|
|
|
|
BreakSK(this.continuation);
|
|
|
|
Configuration call(Environment _) => new BreakConfiguration(continuation);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
// Interpreter Expressions and Values
|
|
// ------------------------------------------------------------------------
|
|
|
|
abstract class InterpreterExpression {
|
|
Expression get expression;
|
|
|
|
InterpreterValue assignValue(Value v);
|
|
}
|
|
|
|
class PositionalExpression extends InterpreterExpression {
|
|
final Expression expression;
|
|
|
|
PositionalExpression(this.expression);
|
|
|
|
InterpreterValue assignValue(Value v) => new PositionalValue(v);
|
|
}
|
|
|
|
class NamedExpression extends InterpreterExpression {
|
|
final String name;
|
|
final Expression expression;
|
|
|
|
NamedExpression(this.name, this.expression);
|
|
InterpreterValue assignValue(Value v) => new NamedValue(name, v);
|
|
}
|
|
|
|
class FieldInitializerExpression extends InterpreterExpression {
|
|
final Field field;
|
|
final Expression expression;
|
|
|
|
FieldInitializerExpression(this.field, this.expression);
|
|
|
|
InterpreterValue assignValue(Value v) => new FieldInitializerValue(field, v);
|
|
}
|
|
|
|
abstract class InterpreterValue {
|
|
Value get value;
|
|
}
|
|
|
|
class PositionalValue extends InterpreterValue {
|
|
final Value value;
|
|
|
|
PositionalValue(this.value);
|
|
}
|
|
|
|
class NamedValue extends InterpreterValue {
|
|
final String name;
|
|
final Value value;
|
|
|
|
NamedValue(this.name, this.value);
|
|
}
|
|
|
|
class FieldInitializerValue extends InterpreterValue {
|
|
final Field field;
|
|
final Value value;
|
|
|
|
FieldInitializerValue(this.field, this.value);
|
|
}
|
|
|
|
abstract class Continuation {}
|
|
|
|
// ------------------------------------------------------------------------
|
|
// Statement Continuations
|
|
// ------------------------------------------------------------------------
|
|
|
|
/// Represents a the continuation for the execution of the next statement of
|
|
/// the program.
|
|
///
|
|
/// There are various kinds of [StatementContinuation]s and their names are
|
|
/// suffixed with "SK".
|
|
abstract class StatementContinuation extends Continuation {
|
|
Configuration call(Environment env);
|
|
}
|
|
|
|
/// Applies the expression continuation to the provided value.
|
|
class ExitSK extends StatementContinuation {
|
|
final ExpressionContinuation continuation;
|
|
final Value value;
|
|
|
|
ExitSK(this.continuation, this.value);
|
|
|
|
Configuration call(Environment _) =>
|
|
new ValuePassingConfiguration(continuation, value);
|
|
}
|
|
|
|
/// Executes the next statement from a block with the corresponding environment
|
|
/// or proceeds with next statement continuation.
|
|
class BlockSK extends StatementContinuation {
|
|
final List<Statement> statements;
|
|
final Environment enclosingEnv;
|
|
final State state;
|
|
|
|
BlockSK(this.statements, this.enclosingEnv, this.state);
|
|
|
|
BlockSK.fromConfig(this.statements, ExecConfiguration conf)
|
|
: enclosingEnv = conf.environment,
|
|
state = conf.state;
|
|
|
|
Configuration call(Environment env) {
|
|
if (statements.isEmpty) {
|
|
return new ForwardConfiguration(state.continuation, enclosingEnv);
|
|
}
|
|
// Proceed with the execution statement when there are some remaining to
|
|
// be executed.
|
|
var cont = new BlockSK(statements.skip(1).toList(), enclosingEnv, state);
|
|
return new ExecConfiguration(
|
|
statements.first, env, state.withContinuation(cont));
|
|
}
|
|
}
|
|
|
|
class WhileConditionSK extends StatementContinuation {
|
|
final Expression condition;
|
|
final Statement body;
|
|
final Environment enclosingEnv;
|
|
final State state;
|
|
|
|
WhileConditionSK(this.condition, this.body, this.enclosingEnv, this.state);
|
|
|
|
Configuration call(Environment _) {
|
|
// Evaluate the condition for the while loop execution.
|
|
var cont = new WhileConditionEK(condition, body, enclosingEnv, state);
|
|
return new EvalConfiguration(
|
|
condition, enclosingEnv, state.exceptionComponents, cont);
|
|
}
|
|
}
|
|
|
|
/// Applies the expression continuation to the provided value.
|
|
class NewSK extends StatementContinuation {
|
|
final ExpressionContinuation continuation;
|
|
final Location location;
|
|
|
|
NewSK(this.continuation, this.location);
|
|
|
|
Configuration call(Environment _) =>
|
|
new ValuePassingConfiguration(continuation, location.value);
|
|
}
|
|
|
|
class ConstructorBodySK extends StatementContinuation {
|
|
final Statement body;
|
|
final Environment environment;
|
|
final ExceptionComponents exceptionComponents;
|
|
final StatementContinuation continuation;
|
|
|
|
ConstructorBodySK(
|
|
this.body, this.environment, this.exceptionComponents, this.continuation);
|
|
|
|
Configuration call(Environment _) {
|
|
return new ExecConfiguration(body, environment,
|
|
new State(null, exceptionComponents, null, continuation));
|
|
}
|
|
}
|
|
|
|
/// Represents the statement continuation for execution of the finalizer
|
|
/// statement.
|
|
class FinallySK extends StatementContinuation {
|
|
final Statement finallyStatement;
|
|
final Environment environment;
|
|
final State state;
|
|
|
|
FinallySK(this.finallyStatement, this.environment, this.state);
|
|
Configuration call(Environment _) {
|
|
return new ExecConfiguration(finallyStatement, environment, state);
|
|
}
|
|
}
|
|
|
|
/// Represents the statement continuation that applies the captured handler to
|
|
/// the current exception.
|
|
///
|
|
/// It is used as next statement continuation for the execution of the finalizer
|
|
/// statement in [TryFinally] to ensure the finalizer is executed before
|
|
/// applying the next handler to the current exception.
|
|
class RethrowSK extends StatementContinuation {
|
|
final ExceptionHandler handler;
|
|
final Value exception;
|
|
final StackTrace stackTrace;
|
|
|
|
RethrowSK(this.handler, this.exception, this.stackTrace);
|
|
|
|
Configuration call(Environment _) {
|
|
return new ThrowConfiguration(handler, exception, stackTrace);
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
// Application Continuations
|
|
// ------------------------------------------------------------------------
|
|
/// Represents the continuation called after the evaluation of argument
|
|
/// expressions.
|
|
///
|
|
/// There are various kinds of [ApplicationContinuation] and their names are
|
|
/// suffixed with "A".
|
|
abstract class ApplicationContinuation extends Continuation {
|
|
Configuration call(List<InterpreterValue> values);
|
|
|
|
/// Binds actual argument values to formal parameters of the function in a
|
|
/// new environment or in the provided initial environment.
|
|
/// TODO: Add checks for validation of arguments according to spec.
|
|
static Environment createEnvironment(
|
|
FunctionNode function, List<InterpreterValue> args,
|
|
[Environment parentEnv]) {
|
|
Environment newEnv = new Environment(parentEnv ?? new Environment.empty());
|
|
|
|
List<PositionalValue> positional = args.reversed
|
|
.where((InterpreterValue av) => av is PositionalValue)
|
|
.toList();
|
|
|
|
// Add positional parameters.
|
|
for (int i = 0; i < positional.length; ++i) {
|
|
newEnv =
|
|
newEnv.extend(function.positionalParameters[i], positional[i].value);
|
|
}
|
|
|
|
Map<String, Value> named = <String, Value>{};
|
|
for (InterpreterValue argValue in args) {
|
|
if (argValue is NamedValue) {
|
|
named[argValue.name] = argValue.value;
|
|
}
|
|
}
|
|
|
|
// Add named parameters.
|
|
for (VariableDeclaration v in function.namedParameters) {
|
|
newEnv = newEnv.extend(v, named[v.name.toString()]);
|
|
}
|
|
|
|
return newEnv;
|
|
}
|
|
}
|
|
|
|
/// Represents the application continuation called after the evaluation of all
|
|
/// argument expressions for an invocation.
|
|
class ValueA extends ApplicationContinuation {
|
|
final InterpreterValue value;
|
|
final ApplicationContinuation applicationContinuation;
|
|
|
|
ValueA(this.value, this.applicationContinuation);
|
|
|
|
Configuration call(List<InterpreterValue> args) {
|
|
args.add(value);
|
|
return new ApplicationConfiguration(applicationContinuation, args);
|
|
}
|
|
}
|
|
|
|
class StringConcatenationA extends ApplicationContinuation {
|
|
final ExpressionContinuation continuation;
|
|
|
|
StringConcatenationA(this.continuation);
|
|
|
|
Configuration call(List<InterpreterValue> values) {
|
|
StringBuffer result = new StringBuffer();
|
|
for (InterpreterValue v in values.reversed) {
|
|
result.write(v.value.value);
|
|
}
|
|
return new ValuePassingConfiguration(
|
|
continuation, new StringValue(result.toString()));
|
|
}
|
|
}
|
|
|
|
/// Represents the application continuation for static invocation.
|
|
class StaticInvocationA extends ApplicationContinuation {
|
|
final FunctionNode function;
|
|
final ExceptionComponents exceptionComponents;
|
|
final ExpressionContinuation continuation;
|
|
|
|
StaticInvocationA(this.function, this.exceptionComponents, this.continuation);
|
|
|
|
Configuration call(List<InterpreterValue> argValues) {
|
|
Environment functionEnv =
|
|
ApplicationContinuation.createEnvironment(function, argValues);
|
|
State bodyState = new State(null, exceptionComponents, continuation,
|
|
new ExitSK(continuation, Value.nullInstance));
|
|
|
|
return new ExecConfiguration(function.body, functionEnv, bodyState);
|
|
}
|
|
}
|
|
|
|
/// Represents the application continuation for constructor invocation applied
|
|
/// on the list of evaluated arguments when a constructor is invoked with new.
|
|
///
|
|
/// It creates the newly allocated object instance.
|
|
class ConstructorInvocationA extends ApplicationContinuation {
|
|
final Constructor constructor;
|
|
final ExceptionComponents exceptionComponents;
|
|
final ExpressionContinuation continuation;
|
|
|
|
ConstructorInvocationA(
|
|
this.constructor, this.exceptionComponents, this.continuation);
|
|
|
|
Configuration call(List<InterpreterValue> argValues) {
|
|
Environment ctrEnv = ApplicationContinuation.createEnvironment(
|
|
constructor.function, argValues);
|
|
var class_ = new Class(constructor.enclosingClass.reference);
|
|
var newObject = new ObjectValue(class_);
|
|
var cont = new InitializationEK(constructor, ctrEnv, exceptionComponents,
|
|
new NewSK(continuation, new Location(newObject)));
|
|
|
|
return new ValuePassingConfiguration(cont, newObject);
|
|
}
|
|
}
|
|
|
|
/// Represents the application continuation for constructor invocation applied
|
|
/// on the list of evaluated arguments when a constructor is invoked in a
|
|
/// constructor initializer list with a [RedirectingInitializer] or
|
|
/// [SuperInitializer].
|
|
class ConstructorInitializerA extends ApplicationContinuation {
|
|
final Constructor constructor;
|
|
final Location location;
|
|
final ExceptionComponents exceptionComponents;
|
|
final ConstructorBodySK continuation;
|
|
|
|
ConstructorInitializerA(this.constructor, this.location,
|
|
this.exceptionComponents, this.continuation);
|
|
|
|
Configuration call(List<InterpreterValue> vs) {
|
|
Environment ctrEnv =
|
|
ApplicationContinuation.createEnvironment(constructor.function, vs);
|
|
var cont = new InitializationEK(
|
|
constructor, ctrEnv, exceptionComponents, continuation);
|
|
|
|
return new ValuePassingConfiguration(cont, location.value);
|
|
}
|
|
}
|
|
|
|
/// Represents the application continuation applied on the list of evaluated
|
|
/// field initializer expressions.
|
|
class InstanceFieldsA extends ApplicationContinuation {
|
|
final Constructor constructor;
|
|
final Location location;
|
|
final Environment environment;
|
|
final ExceptionComponents exceptionComponents;
|
|
final ConstructorBodySK continuation;
|
|
|
|
final Class _currentClass;
|
|
|
|
InstanceFieldsA(this.constructor, this.location, this.environment,
|
|
this.exceptionComponents, this.continuation)
|
|
: _currentClass = new Class(constructor.enclosingClass.reference);
|
|
|
|
Configuration call(List<InterpreterValue> fieldValues) {
|
|
for (FieldInitializerValue f in fieldValues) {
|
|
// Directly set the field with the corresponding implicit setter.
|
|
_currentClass.implicitSetters[f.field.name](location.value, f.value);
|
|
}
|
|
|
|
if (constructor.initializers.length == 0) {
|
|
// This can happen when initializing fields of a constructor with an empty
|
|
// initializer list.
|
|
return new ForwardConfiguration(continuation, environment);
|
|
}
|
|
|
|
if (constructor.initializers.first is SuperInitializer) {
|
|
// Target constructor is from the superclass `object`.
|
|
if (_currentClass.superclass.superclass == null) {
|
|
// TODO(zhivkag): Execute the constructor when support for
|
|
// native/external functions is added.
|
|
_initializeNullFields(_currentClass, location.value);
|
|
return new ForwardConfiguration(continuation, environment);
|
|
}
|
|
|
|
return _createEvalListConfig(constructor.initializers.first);
|
|
}
|
|
|
|
// Otherwise, the next expression from Field or Local initializers will be
|
|
// evaluated.
|
|
return _createEvalConfig(constructor.initializers.first);
|
|
}
|
|
|
|
Configuration _createEvalListConfig(SuperInitializer initializer) {
|
|
List<InterpreterExpression> args = _getArgumentExpressions(
|
|
initializer.arguments, initializer.target.function);
|
|
var cont = new ConstructorInitializerA(
|
|
initializer.target, location, exceptionComponents, continuation);
|
|
|
|
return new EvalListConfiguration(
|
|
args, environment, exceptionComponents, cont);
|
|
}
|
|
|
|
EvalConfiguration _createEvalConfig(Initializer initializer) {
|
|
Expression expr = (initializer is FieldInitializer)
|
|
? initializer.value
|
|
: (initializer as LocalInitializer).variable.initializer;
|
|
|
|
// We start with index = 0 since we are evaluating the expression for the
|
|
// first initializer in the initializer list.
|
|
var cont = new InitializerListEK(constructor, 0, location, environment,
|
|
exceptionComponents, continuation);
|
|
return new EvalConfiguration(expr, environment, exceptionComponents, cont);
|
|
}
|
|
}
|
|
|
|
class FunctionValueA extends ApplicationContinuation {
|
|
final FunctionValue receiver;
|
|
final ExceptionComponents exceptionComponents;
|
|
final ExpressionContinuation returnContinuation;
|
|
|
|
FunctionValueA(
|
|
this.receiver, this.exceptionComponents, this.returnContinuation);
|
|
|
|
Configuration call(List<InterpreterValue> vs) {
|
|
Environment env = ApplicationContinuation.createEnvironment(
|
|
receiver.function, vs, receiver.environment);
|
|
var scont = new ExitSK(returnContinuation, Value.nullInstance);
|
|
var state = new State(null, exceptionComponents, returnContinuation, scont);
|
|
return new ExecConfiguration(receiver.function.body, env, state);
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
// Expression Continuations
|
|
// ------------------------------------------------------------------------
|
|
|
|
/// Represents an expression continuation.
|
|
///
|
|
/// There are various kinds of [ExpressionContinuation]s and their names are
|
|
/// suffixed with "EK".
|
|
abstract class ExpressionContinuation extends Continuation {
|
|
Configuration call(Value v);
|
|
}
|
|
|
|
/// Represents a continuation that returns the next [ExecConfiguration]
|
|
/// to be executed.
|
|
class ExpressionEK extends ExpressionContinuation {
|
|
final StatementContinuation continuation;
|
|
final Environment environment;
|
|
|
|
ExpressionEK(this.continuation, this.environment);
|
|
|
|
Configuration call(Value _) {
|
|
return new ForwardConfiguration(continuation, environment);
|
|
}
|
|
}
|
|
|
|
class PrintEK extends ExpressionContinuation {
|
|
final ExpressionContinuation continuation;
|
|
|
|
PrintEK(this.continuation);
|
|
|
|
Configuration call(Value v) {
|
|
log.info('print(${v.value.runtimeType}: ${v.value})\n');
|
|
print(v.value);
|
|
return new ValuePassingConfiguration(continuation, Value.nullInstance);
|
|
}
|
|
}
|
|
|
|
class PropertyGetEK extends ExpressionContinuation {
|
|
final Name name;
|
|
final ExceptionComponents exceptionComponents;
|
|
final ExpressionContinuation continuation;
|
|
|
|
PropertyGetEK(this.name, this.exceptionComponents, this.continuation);
|
|
|
|
Configuration call(Value receiver) {
|
|
if (receiver.class_.isMethod(name)) {
|
|
var method = receiver.class_.lookup(name);
|
|
// Return the function value with this bound to receiver.
|
|
var env = new Environment.empty().extendWithThis(receiver);
|
|
return new ValuePassingConfiguration(
|
|
continuation, new FunctionValue(method, env));
|
|
}
|
|
|
|
if (receiver.class_.isGetter(name)) {
|
|
var getter = receiver.class_.lookup(name);
|
|
var state = new State.initial()
|
|
.withReturnContinuation(continuation)
|
|
.withContinuation(new ExitSK(continuation, Value.nullInstance))
|
|
.withException(exceptionComponents);
|
|
return new ExecConfiguration(
|
|
getter.body, new Environment.empty().extendWithThis(receiver), state);
|
|
}
|
|
|
|
Value propertyValue = receiver.class_.lookupImplicitGetter(name)(receiver);
|
|
return new ValuePassingConfiguration(continuation, propertyValue);
|
|
}
|
|
}
|
|
|
|
class PropertySetEK extends ExpressionContinuation {
|
|
final Expression value;
|
|
final Name setterName;
|
|
final Environment environment;
|
|
final ExceptionComponents exceptionComponents;
|
|
final ExpressionContinuation continuation;
|
|
|
|
PropertySetEK(this.value, this.setterName, this.environment,
|
|
this.exceptionComponents, this.continuation);
|
|
|
|
Configuration call(Value receiver) {
|
|
var cont =
|
|
new SetterEK(receiver, setterName, exceptionComponents, continuation);
|
|
return new EvalConfiguration(value, environment, exceptionComponents, cont);
|
|
}
|
|
}
|
|
|
|
class SetterEK extends ExpressionContinuation {
|
|
final Value receiver;
|
|
final Name name;
|
|
final ExceptionComponents exceptionComponents;
|
|
final ExpressionContinuation continuation;
|
|
|
|
SetterEK(
|
|
this.receiver, this.name, this.exceptionComponents, this.continuation);
|
|
|
|
Configuration call(Value v) {
|
|
if (receiver.class_.isSetter(name)) {
|
|
var setter = receiver.class_.lookup(name);
|
|
var env = new Environment.empty()
|
|
.extendWithThis(receiver)
|
|
.extend(setter.positionalParameters.first, v);
|
|
var state = new State.initial()
|
|
.withReturnContinuation(continuation)
|
|
.withContinuation(new ExitSK(continuation, Value.nullInstance))
|
|
.withException(exceptionComponents);
|
|
return new ExecConfiguration(setter.body, env, state);
|
|
}
|
|
|
|
Setter setter = receiver.class_.lookupImplicitSetter(name);
|
|
setter(receiver, v);
|
|
return new ValuePassingConfiguration(continuation, v);
|
|
}
|
|
}
|
|
|
|
class StaticSetEK extends ExpressionContinuation {
|
|
final Member member;
|
|
final ExpressionContinuation continuation;
|
|
|
|
StaticSetEK(this.member, this.continuation);
|
|
|
|
Configuration call(Value v) {
|
|
if (Interpreter.mainEnvironment.contains(member)) {
|
|
Interpreter.mainEnvironment.updateStore(member, v);
|
|
} else {
|
|
Interpreter.mainEnvironment =
|
|
Interpreter.mainEnvironment.extend(member, v);
|
|
}
|
|
return new ValuePassingConfiguration(continuation, v);
|
|
}
|
|
}
|
|
|
|
class StaticSetterEK extends ExpressionContinuation {
|
|
final FunctionNode setter;
|
|
final ExceptionComponents exceptionComponents;
|
|
final ExpressionContinuation expressionContinuation;
|
|
|
|
StaticSetterEK(
|
|
this.setter, this.exceptionComponents, this.expressionContinuation);
|
|
|
|
Configuration call(Value v) {
|
|
VariableDeclaration arg = setter.positionalParameters.first;
|
|
var env = new Environment.empty().extend(arg, v);
|
|
var state = new State.initial()
|
|
.withException(exceptionComponents)
|
|
.withReturnContinuation(expressionContinuation)
|
|
.withContinuation(
|
|
new ExitSK(expressionContinuation, Value.nullInstance));
|
|
|
|
return new ExecConfiguration(setter.body, env, state);
|
|
}
|
|
}
|
|
|
|
/// Represents a continuation to be called after the evaluation of an actual
|
|
/// argument for function invocation.
|
|
class ExpressionListEK extends ExpressionContinuation {
|
|
final InterpreterExpression currentExpression;
|
|
final List<InterpreterExpression> expressions;
|
|
final Environment environment;
|
|
final ExceptionComponents exceptionComponents;
|
|
final ApplicationContinuation applicationContinuation;
|
|
|
|
ExpressionListEK(this.currentExpression, this.expressions, this.environment,
|
|
this.exceptionComponents, this.applicationContinuation);
|
|
|
|
Configuration call(Value v) {
|
|
ValueA app =
|
|
new ValueA(currentExpression.assignValue(v), applicationContinuation);
|
|
return new EvalListConfiguration(
|
|
expressions, environment, exceptionComponents, app);
|
|
}
|
|
}
|
|
|
|
class MethodInvocationEK extends ExpressionContinuation {
|
|
final Arguments arguments;
|
|
final Name methodName;
|
|
final Environment environment;
|
|
final ExceptionComponents exceptionComponents;
|
|
final ExpressionContinuation continuation;
|
|
|
|
MethodInvocationEK(this.arguments, this.methodName, this.environment,
|
|
this.exceptionComponents, this.continuation);
|
|
|
|
Configuration call(Value receiver) {
|
|
if (receiver is LiteralValue) {
|
|
// TODO(zhivkag): CPS method invocation for literals
|
|
if (arguments.positional.isEmpty) {
|
|
Value returnValue = receiver.invokeMethod(methodName);
|
|
return new ValuePassingConfiguration(continuation, returnValue);
|
|
}
|
|
var cont = new ArgumentsEK(
|
|
receiver, methodName, arguments, environment, continuation);
|
|
|
|
return new EvalConfiguration(
|
|
arguments.positional.first, environment, exceptionComponents, cont);
|
|
}
|
|
|
|
FunctionValue fun;
|
|
if (receiver is FunctionValue) {
|
|
// TODO(zhivkag): Throw an exception when method is not call.
|
|
assert(methodName.toString() == "call");
|
|
fun = receiver;
|
|
} else {
|
|
assert(receiver.class_.isMethod(methodName));
|
|
fun = new FunctionValue(receiver.class_.lookup(methodName),
|
|
new Environment.empty().extendWithThis(receiver));
|
|
}
|
|
|
|
var args = _getArgumentExpressions(arguments, fun.function);
|
|
var acont = new FunctionValueA(receiver, exceptionComponents, continuation);
|
|
return new EvalListConfiguration(
|
|
args, environment, exceptionComponents, acont);
|
|
}
|
|
}
|
|
|
|
class ArgumentsEK extends ExpressionContinuation {
|
|
final Value receiver;
|
|
final Name methodName;
|
|
final Arguments arguments;
|
|
final Environment environment;
|
|
final ExpressionContinuation continuation;
|
|
|
|
ArgumentsEK(this.receiver, this.methodName, this.arguments, this.environment,
|
|
this.continuation);
|
|
|
|
Configuration call(Value value) {
|
|
// Currently evaluates only one argument, for simple method invocations
|
|
// with 1 argument.
|
|
Value returnValue = receiver.invokeMethod(methodName, value);
|
|
return new ValuePassingConfiguration(continuation, returnValue);
|
|
}
|
|
}
|
|
|
|
class VariableSetEK extends ExpressionContinuation {
|
|
final VariableDeclaration variable;
|
|
final Environment environment;
|
|
final ExpressionContinuation continuation;
|
|
|
|
VariableSetEK(this.variable, this.environment, this.continuation);
|
|
|
|
Configuration call(Value value) {
|
|
environment.updateStore(variable, value);
|
|
return new ValuePassingConfiguration(continuation, value);
|
|
}
|
|
}
|
|
|
|
class NotEK extends ExpressionContinuation {
|
|
final ExpressionContinuation continuation;
|
|
|
|
NotEK(this.continuation);
|
|
|
|
Configuration call(Value value) {
|
|
Value notValue = identical(Value.trueInstance, value)
|
|
? Value.falseInstance
|
|
: Value.trueInstance;
|
|
return new ValuePassingConfiguration(continuation, notValue);
|
|
}
|
|
}
|
|
|
|
class OrEK extends ExpressionContinuation {
|
|
final Expression right;
|
|
final Environment environment;
|
|
final ExceptionComponents exceptionComponents;
|
|
final ExpressionContinuation continuation;
|
|
|
|
OrEK(this.right, this.environment, this.exceptionComponents,
|
|
this.continuation);
|
|
|
|
Configuration call(Value left) {
|
|
return identical(Value.trueInstance, left)
|
|
? new ValuePassingConfiguration(continuation, Value.trueInstance)
|
|
: new EvalConfiguration(
|
|
right, environment, exceptionComponents, continuation);
|
|
}
|
|
}
|
|
|
|
class AndEK extends ExpressionContinuation {
|
|
final Expression right;
|
|
final Environment environment;
|
|
final ExceptionComponents exceptionComponents;
|
|
final ExpressionContinuation continuation;
|
|
|
|
AndEK(this.right, this.environment, this.exceptionComponents,
|
|
this.continuation);
|
|
|
|
Configuration call(Value left) {
|
|
return identical(Value.falseInstance, left)
|
|
? new ValuePassingConfiguration(continuation, Value.falseInstance)
|
|
: new EvalConfiguration(
|
|
right, environment, exceptionComponents, continuation);
|
|
}
|
|
}
|
|
|
|
class ConditionalEK extends ExpressionContinuation {
|
|
final Expression then;
|
|
final Expression otherwise;
|
|
final Environment environment;
|
|
final ExceptionComponents exceptionComponents;
|
|
final ExpressionContinuation continuation;
|
|
|
|
ConditionalEK(this.then, this.otherwise, this.environment,
|
|
this.exceptionComponents, this.continuation);
|
|
|
|
Configuration call(Value value) {
|
|
return identical(Value.trueInstance, value)
|
|
? new EvalConfiguration(
|
|
then, environment, exceptionComponents, continuation)
|
|
: new EvalConfiguration(
|
|
otherwise, environment, exceptionComponents, continuation);
|
|
}
|
|
}
|
|
|
|
class LetEK extends ExpressionContinuation {
|
|
final VariableDeclaration variable;
|
|
final Expression letBody;
|
|
final Environment environment;
|
|
final ExceptionComponents exceptionComponents;
|
|
final ExpressionContinuation continuation;
|
|
|
|
LetEK(this.variable, this.letBody, this.environment, this.exceptionComponents,
|
|
this.continuation);
|
|
|
|
Configuration call(Value value) {
|
|
var letEnv = environment.extend(variable, value);
|
|
return new EvalConfiguration(
|
|
letBody, letEnv, exceptionComponents, continuation);
|
|
}
|
|
}
|
|
|
|
/// Represents the continuation for the condition expression in [WhileStatement].
|
|
class WhileConditionEK extends ExpressionContinuation {
|
|
final Expression condition;
|
|
final Statement body;
|
|
final Environment enclosingEnv;
|
|
final State state;
|
|
|
|
WhileConditionEK(this.condition, this.body, this.enclosingEnv, this.state);
|
|
|
|
Configuration call(Value v) {
|
|
if (identical(v, Value.falseInstance)) {
|
|
return new ForwardConfiguration(state.continuation, enclosingEnv);
|
|
}
|
|
var cont = new WhileConditionSK(condition, body, enclosingEnv, state);
|
|
return new ExecConfiguration(
|
|
body, enclosingEnv, state.withContinuation(cont));
|
|
}
|
|
}
|
|
|
|
/// Represents the continuation for the condition expression in [IfStatement].
|
|
class IfConditionEK extends ExpressionContinuation {
|
|
final Statement then;
|
|
final Statement otherwise;
|
|
final Environment environment;
|
|
final State state;
|
|
|
|
IfConditionEK(this.then, this.otherwise, this.environment, this.state);
|
|
|
|
Configuration call(Value v) {
|
|
if (identical(v, Value.trueInstance)) {
|
|
log.info("if-then\n");
|
|
return new ExecConfiguration(then, environment, state);
|
|
} else if (otherwise != null) {
|
|
log.info("if-otherwise\n");
|
|
return new ExecConfiguration(otherwise, environment, state);
|
|
}
|
|
return new ForwardConfiguration(state.continuation, environment);
|
|
}
|
|
}
|
|
|
|
/// Represents the continuation for the initializer expression in
|
|
/// [VariableDeclaration].
|
|
class VariableInitializerEK extends ExpressionContinuation {
|
|
final VariableDeclaration variable;
|
|
final Environment environment;
|
|
final StatementContinuation continuation;
|
|
VariableInitializerEK(this.variable, this.environment, this.continuation);
|
|
|
|
Configuration call(Value v) {
|
|
return new ForwardConfiguration(
|
|
continuation, environment.extend(variable, v));
|
|
}
|
|
}
|
|
|
|
/// Expression continuation that further initializes the newly allocated object
|
|
/// instance with running the constructor.
|
|
class InitializationEK extends ExpressionContinuation {
|
|
final Constructor constructor;
|
|
final Environment environment;
|
|
final ExceptionComponents exceptionComponents;
|
|
final StatementContinuation continuation;
|
|
|
|
InitializationEK(this.constructor, this.environment, this.exceptionComponents,
|
|
this.continuation);
|
|
|
|
Configuration call(Value value) {
|
|
Location location = new Location(value);
|
|
|
|
if (constructor.initializers.isNotEmpty &&
|
|
constructor.initializers.last is RedirectingInitializer) {
|
|
return _createRedirectingInitializerConfig(
|
|
constructor.initializers.first, location);
|
|
}
|
|
// The statement body is captured by the next statement continuation and
|
|
// expressions for field initialization are evaluated.
|
|
var ctrEnv = environment.extendWithThis(value);
|
|
var bodyCont = new ConstructorBodySK(
|
|
constructor.function.body, ctrEnv, exceptionComponents, continuation);
|
|
var initializers = _getFieldInitializers(constructor.enclosingClass);
|
|
var fieldsCont = new InstanceFieldsA(
|
|
constructor, location, ctrEnv, exceptionComponents, bodyCont);
|
|
return new EvalListConfiguration(
|
|
initializers, new Environment.empty(), exceptionComponents, fieldsCont);
|
|
}
|
|
|
|
/// Creates the next configuration to further initializer the value for
|
|
/// redirecting constructors.
|
|
Configuration _createRedirectingInitializerConfig(
|
|
Initializer initializer, Location location) {
|
|
Initializer current = constructor.initializers.first;
|
|
if (current is RedirectingInitializer) {
|
|
// Evaluate the list of arguments for invoking the target constructor in
|
|
// the current environment.
|
|
List<InterpreterExpression> exprs =
|
|
_getArgumentExpressions(current.arguments, current.target.function);
|
|
var cont = new ConstructorInitializerA(
|
|
current.target, location, exceptionComponents, continuation);
|
|
return new EvalListConfiguration(
|
|
exprs, environment, exceptionComponents, cont);
|
|
}
|
|
Expression expr = (current is FieldInitializer)
|
|
? current.value
|
|
: (current as LocalInitializer).variable.initializer;
|
|
|
|
// The index is set to 0 since we are evaluating the expression for the
|
|
// first initializer in the initializer list.
|
|
var cont = new InitializerListEK(constructor, 0, location, environment,
|
|
exceptionComponents, continuation);
|
|
return new EvalConfiguration(expr, environment, exceptionComponents, cont);
|
|
}
|
|
}
|
|
|
|
class InitializerListEK extends ExpressionContinuation {
|
|
final Constructor constructor;
|
|
final int initializerIndex;
|
|
final Location location;
|
|
final Environment environment;
|
|
final ExceptionComponents exceptionComponents;
|
|
final ConstructorBodySK continuation;
|
|
|
|
final Class _currentClass;
|
|
|
|
InitializerListEK(this.constructor, this.initializerIndex, this.location,
|
|
this.environment, this.exceptionComponents, this.continuation)
|
|
: _currentClass = new Class(constructor.enclosingClass.reference);
|
|
|
|
/// Creates a continuation for the evaluation of the initializer at position
|
|
/// [index].
|
|
InitializerListEK withInitializerIndex(int index) {
|
|
return new InitializerListEK(constructor, index, location, environment,
|
|
exceptionComponents, continuation);
|
|
}
|
|
|
|
Configuration call(Value value) {
|
|
Initializer current = constructor.initializers[initializerIndex];
|
|
if (current is FieldInitializer) {
|
|
_currentClass.lookupImplicitSetter(current.field.name)(
|
|
location.value, value);
|
|
return _createNextConfiguration(environment);
|
|
}
|
|
if (current is LocalInitializer) {
|
|
Environment newEnv = environment.extend(current.variable, value);
|
|
return _createNextConfiguration(newEnv);
|
|
}
|
|
throw "Value can't be applied to initalizer of type ${current.runtimeType}";
|
|
}
|
|
|
|
Configuration _createNextConfiguration(Environment env) {
|
|
assert(initializerIndex + 1 < constructor.initializers.length);
|
|
Initializer next = constructor.initializers[initializerIndex + 1];
|
|
if (next is SuperInitializer) {
|
|
// TODO(zhivkag): Execute constructor of "object" class when support for
|
|
// native/external functions is added.
|
|
if (_currentClass.superclass.superclass == null) {
|
|
_initializeNullFields(_currentClass, location.value);
|
|
return new ForwardConfiguration(continuation, environment);
|
|
}
|
|
return _createEvalListConfig(next.arguments, next.target, env);
|
|
}
|
|
|
|
if (next is RedirectingInitializer) {
|
|
return _createEvalListConfig(next.arguments, next.target, env);
|
|
}
|
|
|
|
Expression nextExpr = (next is FieldInitializer)
|
|
? next.value
|
|
: (next as LocalInitializer).variable.initializer;
|
|
|
|
var cont = withInitializerIndex(initializerIndex + 1);
|
|
return new EvalConfiguration(nextExpr, env, exceptionComponents, cont);
|
|
}
|
|
|
|
Configuration _createEvalListConfig(
|
|
Arguments args, Constructor ctr, Environment env) {
|
|
List<InterpreterExpression> exprs =
|
|
_getArgumentExpressions(args, ctr.function);
|
|
var cont = new ConstructorInitializerA(
|
|
ctr, location, exceptionComponents, continuation);
|
|
return new EvalListConfiguration(exprs, env, exceptionComponents, cont);
|
|
}
|
|
}
|
|
|
|
class ThrowEK extends ExpressionContinuation {
|
|
final ExceptionComponents exceptionComponents;
|
|
|
|
ThrowEK(this.exceptionComponents);
|
|
|
|
Configuration call(Value value) {
|
|
return new ThrowConfiguration(
|
|
exceptionComponents.handler, value, exceptionComponents.stackTrace);
|
|
}
|
|
}
|
|
|
|
/// Represents the expression continuation that ensures the finalizer of a
|
|
/// [TryFinally] node is executed before applying the return continuation to
|
|
/// the given value.
|
|
///
|
|
/// It executes the captured finalizer statement and adds a statement
|
|
/// continuation that will apply the return continuation to the given value
|
|
/// when/if reached.
|
|
class FinallyReturnEK extends ExpressionContinuation {
|
|
final Statement statement;
|
|
final Environment environment;
|
|
final State state;
|
|
|
|
FinallyReturnEK(this.statement, this.environment, this.state);
|
|
|
|
Configuration call(Value value) {
|
|
return new ExecConfiguration(statement, environment,
|
|
state.withContinuation(new ExitSK(state.returnContinuation, value)));
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
// Exceptions Handlers
|
|
// ------------------------------------------------------------------------
|
|
|
|
abstract class ExceptionHandler extends Continuation {
|
|
Configuration call(Value exception, StackTrace stacktrace);
|
|
|
|
static String errorMessage(Value exception, StackTrace stacktrace) {
|
|
return 'Uncaught exception '
|
|
'"${exception.value.runtimeType} : ${exception.value}"\n'
|
|
'${stacktrace.toString()}';
|
|
}
|
|
}
|
|
|
|
/// Handler for showing an exception to the user and returning a halting the
|
|
/// execution of the program when an exception is not handled.
|
|
class MainHandler extends ExceptionHandler {
|
|
Configuration call(Value exception, StackTrace stacktrace) {
|
|
var message = ExceptionHandler.errorMessage(exception, stacktrace);
|
|
log.info(message);
|
|
print(message);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// Represents the handler that either executes a matching catch clause or
|
|
/// applies the next handler to the given exception.
|
|
class CatchHandler extends ExceptionHandler {
|
|
final List<Catch> catches;
|
|
final Environment environment;
|
|
final State state;
|
|
|
|
CatchHandler(this.catches, this.environment, this.state);
|
|
|
|
Configuration call(Value exception, StackTrace stackTrace) {
|
|
if (catches.isEmpty) {
|
|
return new ThrowConfiguration(
|
|
state.exceptionComponents.handler, exception, stackTrace);
|
|
}
|
|
|
|
// TODO(zhivkag): Check if there is a matching catch clause instead.
|
|
var currentCatch = catches.first;
|
|
if (currentCatch.guard is DynamicType) {
|
|
// Exception is caught, execute the body.
|
|
|
|
var env = environment;
|
|
if (currentCatch.exception != null) {
|
|
env = env.extend(currentCatch.exception, exception);
|
|
}
|
|
if (currentCatch.stackTrace != null) {
|
|
env = env.extend(
|
|
currentCatch.stackTrace, new StringValue(stackTrace.toString()));
|
|
}
|
|
var ecs = new ExceptionComponents(state.exceptionComponents.handler,
|
|
state.exceptionComponents.stackTrace, stackTrace, exception);
|
|
var newState = new State.initial()
|
|
.withContinuation(state.continuation)
|
|
.withReturnContinuation(state.returnContinuation)
|
|
.withException(ecs);
|
|
return new ExecConfiguration(currentCatch.body, env, newState);
|
|
}
|
|
|
|
var catchHandler =
|
|
new CatchHandler(catches.skip(1).toList(), environment, state);
|
|
return new ThrowConfiguration(catchHandler, exception, stackTrace);
|
|
}
|
|
}
|
|
|
|
/// Represents the handler that executes the corresponding finalizer before
|
|
/// applying the next handler to the given exception.
|
|
///
|
|
/// Applying the next handler to the given exception is supported with adding
|
|
/// [RethrowSK] as next statement continuation.
|
|
class FinallyHandler extends ExceptionHandler {
|
|
final Statement finallyStatement;
|
|
final Environment environment;
|
|
final State state;
|
|
|
|
FinallyHandler(this.finallyStatement, this.environment, this.state);
|
|
Configuration call(Value exception, StackTrace stackTrace) {
|
|
// A finally handler can't handle an exception, only execute the
|
|
// corresponding finally statement and rethrow.
|
|
var cont =
|
|
new RethrowSK(state.exceptionComponents.handler, exception, stackTrace);
|
|
var newState = state.withContinuation(cont);
|
|
return new ExecConfiguration(finallyStatement, environment, newState);
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
// Exceptions
|
|
// ------------------------------------------------------------------------
|
|
/// Represents the components for Exception handling.
|
|
///
|
|
/// It contains the current of exception handler, a stack trace and optional
|
|
/// components, current stacktrace and exception.
|
|
class ExceptionComponents {
|
|
final ExceptionHandler handler;
|
|
final StackTrace stackTrace;
|
|
|
|
/// Current exception and stack trace.
|
|
///
|
|
/// Components enabling support for `rethrow` expressions and set only in
|
|
/// catch clauses.
|
|
final StackTrace currentStackTrace;
|
|
final Value currentException;
|
|
|
|
ExceptionComponents(this.handler, this.stackTrace, this.currentStackTrace,
|
|
this.currentException);
|
|
|
|
ExceptionComponents.initial()
|
|
: handler = new MainHandler(),
|
|
stackTrace = new StackTrace(null, null),
|
|
currentStackTrace = null,
|
|
currentException = null;
|
|
}
|
|
|
|
class StackTrace {
|
|
final Expression expression;
|
|
final StackTrace stackTrace;
|
|
|
|
StackTrace(this.expression, this.stackTrace);
|
|
|
|
String toString() {
|
|
var buffer = new StringBuffer('in main()');
|
|
var current = this;
|
|
while (current.expression != null) {
|
|
buffer.write('at ${current.expression.toString()}\n');
|
|
current = current.stackTrace;
|
|
}
|
|
return buffer.toString();
|
|
}
|
|
}
|
|
|
|
/// Executes statements.
|
|
///
|
|
/// Execution of a statement completes in one of the following ways:
|
|
/// - It completes normally, in which case the execution proceeds to applying
|
|
/// the next continuation.
|
|
/// - It breaks with a label, in which case the corresponding continuation is
|
|
/// returned and applied.
|
|
/// - It returns with or without value, in which case the return continuation is
|
|
/// returned and applied accordingly.
|
|
/// - It throws, in which case the handler is returned and applied accordingly.
|
|
class StatementExecuter
|
|
extends StatementVisitor1<Configuration, ExecConfiguration> {
|
|
final Evaluator evaluator = new Evaluator();
|
|
|
|
void trampolinedExecution(Configuration configuration) {
|
|
while (configuration != null) {
|
|
configuration = configuration.step(this);
|
|
}
|
|
}
|
|
|
|
Configuration exec(Statement statement, ExecConfiguration conf) =>
|
|
statement.accept1(this, conf);
|
|
Configuration eval(Expression expression, EvalConfiguration config) =>
|
|
evaluator.eval(expression, config);
|
|
Configuration evalList(List<InterpreterExpression> es, Environment env,
|
|
ExceptionComponents ecs, Continuation cont) =>
|
|
evaluator.evalList(es, env, ecs, cont);
|
|
|
|
Configuration defaultStatement(Statement node, ExecConfiguration conf) {
|
|
throw notImplemented(
|
|
m: "Execution is not implemented for statement:\n$node ");
|
|
}
|
|
|
|
Configuration visitInvalidStatement(
|
|
InvalidStatement node, ExecConfiguration conf) {
|
|
throw "Invalid statement at ${node.location}";
|
|
}
|
|
|
|
Configuration visitExpressionStatement(
|
|
ExpressionStatement node, ExecConfiguration conf) {
|
|
var cont = new ExpressionEK(conf.state.continuation, conf.environment);
|
|
return new EvalConfiguration(node.expression, conf.environment,
|
|
conf.state.exceptionComponents, cont);
|
|
}
|
|
|
|
Configuration visitBlock(Block node, ExecConfiguration conf) {
|
|
if (node.statements.isEmpty) {
|
|
return new ForwardConfiguration(
|
|
conf.state.continuation, conf.environment);
|
|
}
|
|
|
|
var cont = new BlockSK.fromConfig(node.statements.skip(1).toList(), conf);
|
|
return new ExecConfiguration(node.statements.first, conf.environment,
|
|
conf.state.withContinuation(cont));
|
|
}
|
|
|
|
Configuration visitEmptyStatement(
|
|
EmptyStatement node, ExecConfiguration conf) {
|
|
return new ForwardConfiguration(conf.state.continuation, conf.environment);
|
|
}
|
|
|
|
Configuration visitIfStatement(IfStatement node, ExecConfiguration conf) {
|
|
var cont = new IfConditionEK(
|
|
node.then, node.otherwise, conf.environment, conf.state);
|
|
|
|
return new EvalConfiguration(
|
|
node.condition, conf.environment, conf.state.exceptionComponents, cont);
|
|
}
|
|
|
|
Configuration visitLabeledStatement(
|
|
LabeledStatement node, ExecConfiguration conf) {
|
|
return new ExecConfiguration(node.body, conf.environment,
|
|
conf.state.withBreak(node, conf.environment));
|
|
}
|
|
|
|
Configuration visitBreakStatement(
|
|
BreakStatement node, ExecConfiguration conf) {
|
|
Label l = conf.state.lookupLabel(node.target);
|
|
return new BreakConfiguration(l.continuation);
|
|
}
|
|
|
|
Configuration visitWhileStatement(
|
|
WhileStatement node, ExecConfiguration conf) {
|
|
var cont = new WhileConditionEK(
|
|
node.condition, node.body, conf.environment, conf.state);
|
|
|
|
return new EvalConfiguration(
|
|
node.condition, conf.environment, conf.state.exceptionComponents, cont);
|
|
}
|
|
|
|
Configuration visitDoStatement(DoStatement node, ExecConfiguration conf) {
|
|
var cont = new WhileConditionSK(
|
|
node.condition, node.body, conf.environment, conf.state);
|
|
|
|
return new ExecConfiguration(
|
|
node.body, conf.environment, conf.state.withContinuation(cont));
|
|
}
|
|
|
|
Configuration visitReturnStatement(
|
|
ReturnStatement node, ExecConfiguration conf) {
|
|
assert(conf.state.returnContinuation != null);
|
|
log.info('return\n');
|
|
if (node.expression == null) {
|
|
return new ValuePassingConfiguration(
|
|
conf.state.returnContinuation, Value.nullInstance);
|
|
}
|
|
return new EvalConfiguration(node.expression, conf.environment,
|
|
conf.state.exceptionComponents, conf.state.returnContinuation);
|
|
}
|
|
|
|
Configuration visitTryCatch(TryCatch node, ExecConfiguration conf) {
|
|
var handler = new CatchHandler(node.catches, conf.environment, conf.state);
|
|
var exceptionComponents = new ExceptionComponents(
|
|
handler,
|
|
conf.state.exceptionComponents.stackTrace,
|
|
conf.state.exceptionComponents.currentStackTrace,
|
|
conf.state.exceptionComponents.currentException);
|
|
var state = conf.state.withException(exceptionComponents);
|
|
return new ExecConfiguration(node.body, conf.environment, state);
|
|
}
|
|
|
|
Configuration visitTryFinally(TryFinally node, ExecConfiguration conf) {
|
|
var cont = new FinallySK(node.finalizer, conf.environment, conf.state);
|
|
var returnCont =
|
|
new FinallyReturnEK(node.finalizer, conf.environment, conf.state);
|
|
var labels = conf.state.labels
|
|
?.withFinalizer(node.finalizer, conf.environment, conf.state);
|
|
var handler =
|
|
new FinallyHandler(node.finalizer, conf.environment, conf.state);
|
|
var exceptionComponents = new ExceptionComponents(
|
|
handler,
|
|
conf.state.exceptionComponents.stackTrace,
|
|
conf.state.exceptionComponents.currentStackTrace,
|
|
conf.state.exceptionComponents.currentException);
|
|
var state = new State(labels, exceptionComponents, returnCont, cont);
|
|
return new ExecConfiguration(node.body, conf.environment, state);
|
|
}
|
|
|
|
Configuration visitVariableDeclaration(
|
|
VariableDeclaration node, ExecConfiguration conf) {
|
|
if (node.initializer != null) {
|
|
var cont = new VariableInitializerEK(
|
|
node, conf.environment, conf.state.continuation);
|
|
return new EvalConfiguration(node.initializer, conf.environment,
|
|
conf.state.exceptionComponents, cont);
|
|
}
|
|
return new ForwardConfiguration(conf.state.continuation,
|
|
conf.environment.extend(node, Value.nullInstance));
|
|
}
|
|
|
|
Configuration visitFunctionDeclaration(
|
|
FunctionDeclaration node, ExecConfiguration conf) {
|
|
var newEnv = conf.environment.extend(node.variable, Value.nullInstance);
|
|
var fun = new FunctionValue(node.function, newEnv);
|
|
newEnv.updateStore(node.variable, fun);
|
|
return new ForwardConfiguration(conf.state.continuation, newEnv);
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
// VALUES
|
|
// ------------------------------------------------------------------------
|
|
|
|
typedef Value Getter(Value receiver);
|
|
typedef void Setter(Value receiver, Value value);
|
|
|
|
class Class {
|
|
static final Map<Reference, Class> _classes = <Reference, Class>{};
|
|
|
|
/// The immediate superclass, or `null` if this is the root class object.
|
|
Class superclass;
|
|
|
|
/// The class definitions from the `implements` clause.
|
|
final List<Supertype> interfaces = <Supertype>[];
|
|
|
|
/// Implicit getters for instance fields.
|
|
Map<Name, Getter> implicitGetters = <Name, Getter>{};
|
|
|
|
/// Implicit setters for non final instance fields.
|
|
Map<Name, Setter> implicitSetters = <Name, Setter>{};
|
|
|
|
/// Instance methods, explicit getters and setters.
|
|
Map<Name, Procedure> methods = <Name, Procedure>{};
|
|
|
|
int get instanceSize => implicitGetters.length;
|
|
|
|
factory Class(Reference classRef) {
|
|
return _classes.putIfAbsent(
|
|
classRef, () => new Class._internal(classRef.asClass));
|
|
}
|
|
|
|
Class._internal(ast.Class currentClass) {
|
|
if (currentClass.superclass != null) {
|
|
superclass = new Class(currentClass.superclass.reference);
|
|
}
|
|
|
|
_populateImplicitGettersAndSetters(currentClass);
|
|
_populateInstanceMethods(currentClass);
|
|
}
|
|
|
|
Getter lookupImplicitGetter(Name name) {
|
|
Getter getter = implicitGetters[name];
|
|
if (getter != null) return getter;
|
|
if (superclass != null) return superclass.lookupImplicitGetter(name);
|
|
return (Value receiver) => notImplemented(obj: name);
|
|
}
|
|
|
|
Setter lookupImplicitSetter(Name name) {
|
|
Setter setter = implicitSetters[name];
|
|
if (setter != null) return setter;
|
|
if (superclass != null) return superclass.lookupImplicitSetter(name);
|
|
// TODO(zhivkag): Throw NoSuchInstance error instead.
|
|
return (Value receiver, Value value) => notImplemented(obj: name);
|
|
}
|
|
|
|
FunctionNode lookup(Name name) => methods[name]?.function;
|
|
|
|
bool isGetter(Name name) => methods[name]?.isGetter ?? false;
|
|
bool isSetter(Name name) => methods[name]?.isSetter ?? false;
|
|
bool isMethod(Name name) => !(methods[name]?.isAccessor ?? true);
|
|
|
|
/// Populates implicit getters and setters for the current class and its
|
|
/// superclass recursively.
|
|
_populateImplicitGettersAndSetters(ast.Class class_) {
|
|
if (class_.superclass != null) {
|
|
_populateImplicitGettersAndSetters(class_.superclass);
|
|
}
|
|
|
|
for (Field f in class_.fields) {
|
|
if (f.isStatic) continue;
|
|
assert(f.hasImplicitGetter);
|
|
|
|
int currentFieldIndex = implicitGetters.length;
|
|
// Shadowing an inherited getter with the same name.
|
|
implicitGetters[f.name] =
|
|
(Value receiver) => receiver.fields[currentFieldIndex].value;
|
|
if (f.hasImplicitSetter) {
|
|
// Shadowing an inherited setter with the same name.
|
|
implicitSetters[f.name] = (Value receiver, Value value) =>
|
|
receiver.fields[currentFieldIndex].value = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Populates instance methods, getters and setters for the current class and
|
|
/// its super class recursively.
|
|
_populateInstanceMethods(ast.Class class_) {
|
|
if (class_.superclass != null) {
|
|
_populateInstanceMethods(class_.superclass);
|
|
}
|
|
|
|
for (Member m in class_.members) {
|
|
if (m is Procedure) {
|
|
// Shadowing an inherited method, getter or setter with the same name.
|
|
methods[m.name] = m;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
abstract class Value {
|
|
Class get class_;
|
|
List<Location> get fields;
|
|
Object get value;
|
|
|
|
static final NullValue nullInstance = const NullValue();
|
|
static final BoolValue trueInstance = const BoolValue(true);
|
|
static final BoolValue falseInstance = const BoolValue(false);
|
|
|
|
const Value();
|
|
|
|
BoolValue toBoolean() {
|
|
return identical(this, Value.trueInstance)
|
|
? Value.trueInstance
|
|
: Value.falseInstance;
|
|
}
|
|
|
|
BoolValue equals(Value other) =>
|
|
value == other?.value ? Value.trueInstance : Value.falseInstance;
|
|
|
|
Value invokeMethod(Name name, [Value arg]) {
|
|
if (name.toString() == "==") return equals(arg);
|
|
throw notImplemented(obj: name);
|
|
}
|
|
}
|
|
|
|
class ObjectValue extends Value {
|
|
final Class class_;
|
|
final List<Location> fields;
|
|
Object get value => this;
|
|
|
|
ObjectValue(this.class_) : fields = new List<Location>(class_.instanceSize) {
|
|
for (int i = 0; i < fields.length; i++) {
|
|
// Create fresh locations for each field.
|
|
fields[i] = new Location.empty();
|
|
}
|
|
}
|
|
}
|
|
|
|
class FunctionValue extends Value {
|
|
Class get class_ => throw 'Class for FunctionValue is not defined';
|
|
List<Location> get fields => throw 'FunctionValue has no fields.';
|
|
|
|
FunctionValue get value => this;
|
|
|
|
final FunctionNode function;
|
|
final Environment environment;
|
|
|
|
FunctionValue(this.function, this.environment);
|
|
}
|
|
|
|
abstract class LiteralValue extends Value {
|
|
Class get class_ =>
|
|
notImplemented(m: "Loading class for literal is not implemented.");
|
|
List<Location> get fields =>
|
|
notImplemented(m: "Literal value does not have fields");
|
|
|
|
const LiteralValue();
|
|
}
|
|
|
|
class StringValue extends LiteralValue {
|
|
final String value;
|
|
|
|
static final operators = <String, Function>{
|
|
'[]': (StringValue v1, Value v2) => v1[v2],
|
|
'==': (StringValue v1, Value v2) => v1.equals(v2)
|
|
};
|
|
|
|
StringValue(this.value);
|
|
|
|
Value invokeMethod(Name name, [Value arg]) {
|
|
if (!operators.containsKey(name.name)) {
|
|
return notImplemented(obj: name);
|
|
}
|
|
return operators[name.name](this, arg);
|
|
}
|
|
|
|
// Operators
|
|
Value operator [](Value index) => new StringValue(value[index.value]);
|
|
}
|
|
|
|
abstract class NumValue extends LiteralValue {
|
|
num get value;
|
|
|
|
NumValue();
|
|
|
|
factory NumValue.fromValue(num value) {
|
|
if (value is int) {
|
|
return new IntValue(value);
|
|
} else {
|
|
assert(value is double);
|
|
return new DoubleValue(value);
|
|
}
|
|
}
|
|
|
|
static final operators = <String, Function>{
|
|
'+': (NumValue v1, Value v2) => v1 + v2,
|
|
'-': (NumValue v1, Value v2) => v1 - v2,
|
|
'>': (NumValue v1, Value v2) => v1 > v2,
|
|
'<': (NumValue v1, Value v2) => v1 < v2,
|
|
'==': (NumValue v1, Value v2) => v1.equals(v2),
|
|
'unary-': (NumValue v1) => -v1,
|
|
};
|
|
|
|
Value invokeMethod(Name name, [Value arg]) {
|
|
if (!operators.containsKey(name.name)) return notImplemented(obj: name);
|
|
if (arg == null) return operators[name.name](this);
|
|
return operators[name.name](this, arg);
|
|
}
|
|
|
|
// Operators
|
|
NumValue operator +(Value other) =>
|
|
new NumValue.fromValue(value + other.value);
|
|
NumValue operator -(Value other) =>
|
|
new NumValue.fromValue(value - other.value);
|
|
NumValue operator -() => new NumValue.fromValue(-value);
|
|
|
|
BoolValue operator >(Value other) =>
|
|
value > other.value ? Value.trueInstance : Value.falseInstance;
|
|
BoolValue operator <(Value other) =>
|
|
value < other.value ? Value.trueInstance : Value.falseInstance;
|
|
}
|
|
|
|
class IntValue extends NumValue {
|
|
final int value;
|
|
|
|
IntValue(this.value);
|
|
}
|
|
|
|
class DoubleValue extends NumValue {
|
|
final double value;
|
|
|
|
DoubleValue(this.value);
|
|
}
|
|
|
|
class BoolValue extends LiteralValue {
|
|
final bool value;
|
|
|
|
const BoolValue(this.value);
|
|
}
|
|
|
|
class NullValue extends LiteralValue {
|
|
Object get value => null;
|
|
|
|
const NullValue();
|
|
}
|
|
|
|
notImplemented({String m, Object obj}) {
|
|
throw new NotImplemented(m ?? 'Evaluation for $obj is not implemented');
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
// INTERNAL FUNCTIONS
|
|
// ------------------------------------------------------------------------
|
|
|
|
/// Creates a list of all argument expressions to be evaluated for the
|
|
/// invocation of the provided [FunctionNode] containing the actual arguments
|
|
/// and the optional argument initializers.
|
|
List<InterpreterExpression> _getArgumentExpressions(
|
|
Arguments providedArgs, FunctionNode fun) {
|
|
List<InterpreterExpression> args = <InterpreterExpression>[];
|
|
// Add positional arguments expressions.
|
|
args.addAll(providedArgs.positional
|
|
.map((Expression e) => new PositionalExpression(e)));
|
|
|
|
// Add optional positional argument initializers.
|
|
for (int i = providedArgs.positional.length;
|
|
i < fun.positionalParameters.length;
|
|
i++) {
|
|
args.add(new PositionalExpression(fun.positionalParameters[i].initializer));
|
|
}
|
|
|
|
Map<String, NamedExpression> namedFormals = <String, NamedExpression>{};
|
|
for (VariableDeclaration vd in fun.namedParameters) {
|
|
if (vd is NamedValue) {
|
|
namedFormals[vd.name] = new NamedExpression(vd.name, vd.initializer);
|
|
}
|
|
}
|
|
|
|
// Add named expressions.
|
|
for (int i = 0; i < providedArgs.named.length; i++) {
|
|
var current = providedArgs.named[i];
|
|
args.add(new NamedExpression(current.name, current.value));
|
|
namedFormals.remove(current.name);
|
|
}
|
|
|
|
// Add missing optional named initializers.
|
|
args.addAll(namedFormals.values);
|
|
|
|
return args;
|
|
}
|
|
|
|
/// Creates a list of all field expressions to be evaluated.
|
|
///
|
|
/// A field expression is an initializer expression for a given field defined
|
|
/// when the field was created.
|
|
List<InterpreterExpression> _getFieldInitializers(ast.Class class_) {
|
|
var fieldInitializers = new List<InterpreterExpression>();
|
|
|
|
for (Field f in class_.fields) {
|
|
if (!f.isStatic && f.initializer != null) {
|
|
fieldInitializers.add(new FieldInitializerExpression(f, f.initializer));
|
|
}
|
|
}
|
|
|
|
return fieldInitializers;
|
|
}
|
|
|
|
/// Initializes all non initialized fields from the provided class to
|
|
/// `Value.nullInstance` in the provided value.
|
|
void _initializeNullFields(Class class_, Value value) {
|
|
int startIndex = class_.superclass?.instanceSize ?? 0;
|
|
for (int i = startIndex; i < class_.instanceSize; i++) {
|
|
if (value.fields[i].value == null) {
|
|
value.fields[i].value = Value.nullInstance;
|
|
}
|
|
}
|
|
}
|