| // 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 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; |
| } |
| } |
| } |