blob: a64fe581f12f7be928f4f34308138c6a63545c48 [file] [log] [blame]
// 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;
class NotImplemented {
String message;
NotImplemented(this.message);
String toString() => message;
}
class Interpreter {
Program program;
StatementExecuter visitor = new StatementExecuter();
Environment env = new Environment.empty();
Interpreter(this.program);
void run() {
assert(program.libraries.isEmpty);
Procedure mainMethod = program.mainMethod;
Statement statementBlock = mainMethod.function.body;
visitor.exec(statementBlock, env);
}
}
class InvalidExpressionError {
InvalidExpression expression;
InvalidExpressionError(this.expression);
String toString() =>
'Invalid expression at ${expression.location.toString()}';
}
class Binding {
final VariableDeclaration variable;
Value value;
Binding(this.variable, this.value);
}
class Environment {
final List<Binding> bindings = <Binding>[];
final Environment parent;
Environment.empty() : parent = null;
Environment(this.parent);
bool contains(VariableDeclaration variable) {
for (Binding b in bindings.reversed) {
if (identical(b.variable, variable)) return true;
}
return parent?.contains(variable) ?? false;
}
Binding lookupBinding(VariableDeclaration variable) {
assert(contains(variable));
for (Binding b in bindings) {
if (identical(b.variable, variable)) return b;
}
return parent.lookupBinding(variable);
}
Value lookup(VariableDeclaration variable) {
return lookupBinding(variable).value;
}
void assign(VariableDeclaration variable, Value value) {
assert(contains(variable));
lookupBinding(variable).value = value;
}
void expand(VariableDeclaration variable, Value value) {
assert(!contains(variable));
bindings.add(new Binding(variable, value));
}
}
class Evaluator extends ExpressionVisitor1<Value> {
Value eval(Expression expr, Environment env) => expr.accept1(this, env);
Value defaultExpression(Expression node, env) {
throw new NotImplemented('Evaluation for expressions of type '
'${node.runtimeType} is not implemented.');
}
Value visitInvalidExpression1(InvalidExpression node, env) {
throw new InvalidExpressionError(node);
}
Value visitVariableGet(VariableGet node, env) {
return env.lookup(node.variable);
}
Value visitVariableSet(VariableSet node, env) {
return env.assign(node.variable, eval(node.value, env));
}
Value visitPropertyGet(PropertyGet node, env) {
Value receiver = eval(node.receiver, env);
return receiver.class_.lookupGetter(node.name)(receiver);
}
Value visitPropertySet(PropertySet node, env) {
Value receiver = eval(node.receiver, env);
Value value = eval(node.value, env);
receiver.class_.lookupSetter(node.name)(receiver, value);
return value;
}
Value visitDirectPropertyGet(DirectPropertyGet node, env) {
Value receiver = eval(node.receiver, env);
return receiver.class_.getProperty(receiver, node.target);
}
Value visitDirectPropertySet(DirectPropertySet node, env) {
Value receiver = eval(node.receiver, env);
Value value = eval(node.value, env);
receiver.class_.setProperty(receiver, node.target, value);
return value;
}
Value visitStaticGet(StaticGet node, env) => defaultExpression(node, env);
Value visitStaticSet(StaticSet node, env) => defaultExpression(node, env);
Value visitStaticInvocation(StaticInvocation node, env) {
if ('print' == node.name.toString()) {
// Special evaluation of print.
var res = eval(node.arguments.positional[0], env);
print(res.value);
return Value.nullInstance;
} else {
throw new NotImplemented('Support for statement type '
'${node.runtimeType} is not implemented');
}
}
Value visitMethodInvocation(MethodInvocation node, env) {
// Currently supports only method invocation with <2 arguments and is used
// to evaluate implemented operators for int, double and String values.
var receiver = eval(node.receiver, env);
if (node.arguments.positional.isNotEmpty) {
var argValue = eval(node.arguments.positional.first, env);
return receiver.invokeMethod(node.name, argValue);
} else {
return receiver.invokeMethod(node.name);
}
}
Value visitConstructorInvocation(ConstructorInvocation node, env) {
Class class_ = new Class(node.target.enclosingClass.reference);
Environment emptyEnv = new Environment.empty();
// Currently we don't support initializers.
// TODO: Modify to respect dart semantics for initialization.
// 1. Init fields and eval initializers, repeat the same with super.
// 2. Eval the Function body of the constructor.
List<Value> fields = class_.instanceFields
.map((Field f) => eval(f.initializer ?? new NullLiteral(), emptyEnv))
.toList(growable: false);
return new ObjectValue(class_, fields);
}
Value visitNot(Not node, env) {
Value operand = eval(node.operand, env).toBoolean();
return identical(operand, Value.trueInstance)
? Value.falseInstance
: Value.trueInstance;
}
Value visitLogicalExpression(LogicalExpression node, env) {
if ('||' == node.operator) {
BoolValue left = eval(node.left, env).toBoolean();
return identical(left, Value.trueInstance)
? Value.trueInstance
: eval(node.right, env).toBoolean();
} else {
assert('&&' == node.operator);
BoolValue left = eval(node.left, env).toBoolean();
return identical(left, Value.falseInstance)
? Value.falseInstance
: eval(node.right, env).toBoolean();
}
}
Value visitConditionalExpression(ConditionalExpression node, env) {
var condition = eval(node.condition, env).toBoolean();
return identical(condition, Value.trueInstance)
? eval(node.then, env)
: eval(node.otherwise, env);
}
Value visitStringConcatenation(StringConcatenation node, env) {
StringBuffer res = new StringBuffer();
for (Expression e in node.expressions) {
res.write(eval(e, env).value);
}
return new StringValue(res.toString());
}
// Evaluation of BasicLiterals.
Value visitStringLiteral(StringLiteral node, env) =>
new StringValue(node.value);
Value visitIntLiteral(IntLiteral node, env) => new IntValue(node.value);
Value visitDoubleLiteral(DoubleLiteral node, env) =>
new DoubleValue(node.value);
Value visitBoolLiteral(BoolLiteral node, env) =>
node.value ? Value.trueInstance : Value.falseInstance;
Value visitNullLiteral(NullLiteral node, env) => Value.nullInstance;
Value visitLet(Let node, env) {
var value = eval(node.variable.initializer, env);
var letEnv = new Environment(env);
letEnv.expand(node.variable, value);
return eval(node.body, letEnv);
}
}
/// Executes statements.
class StatementExecuter extends StatementVisitor1 {
Evaluator evaluator = new Evaluator();
exec(Statement statement, env) => statement.accept1(this, env);
eval(Expression expression, env) => evaluator.eval(expression, env);
defaultStatement(Statement node, env) {
throw notImplemented(
m: "Execution is not implemented for statement:\n$node ");
}
visitInvalidStatement(InvalidStatement node, env) {
throw "Invalid statement at ${node.location}";
}
visitExpressionStatement(ExpressionStatement node, env) {
return eval(node.expression, env);
}
visitBlock(Block node, env) {
Environment blockEnv = new Environment(env);
for (Statement s in node.statements) {
exec(s, blockEnv);
}
}
visitEmptyStatement(EmptyStatement node, env) {}
visitIfStatement(IfStatement node, env) {
Value condition = eval(node.condition, env).toBoolean();
if (identical(Value.trueInstance, condition)) {
exec(node.then, env);
} else {
exec(node.otherwise, env);
}
}
visitVariableDeclaration(VariableDeclaration node, env) {
Value value = node.initializer != null
? eval(node.initializer, env)
: Value.nullInstance;
env.expand(node, value);
}
}
typedef Value Getter(Value receiver);
typedef void Setter(Value receiver, Value value);
// TODO(zhivkag): Change misleading name.
// This is representation of a class in the interpreter, not a declaration.
class Class {
static final Map<Reference, Class> _classes = <Reference, Class>{};
Class superclass;
List<Field> instanceFields = <Field>[];
List<Field> staticFields = <Field>[];
// Implicit getters and setters for instance Fields.
Map<Name, Getter> getters = <Name, Getter>{};
Map<Name, Setter> setters = <Name, Setter>{};
// The initializers of static fields are evaluated the first time the field
// is accessed.
List<Value> staticFieldValues = <Value>[];
List<Procedure> methods = <Procedure>[];
int get instanceSize => instanceFields.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);
}
_populateInstanceFields(currentClass);
// TODO: Populate methods.
}
Getter lookupGetter(Name name) {
Getter getter = getters[name];
if (getter != null) return getter;
if (superclass != null) return superclass.lookupGetter(name);
return (Value receiver) => notImplemented(obj: name);
}
Setter lookupSetter(Name name) {
Setter setter = setters[name];
if (setter != null) return setter;
if (superclass != null) return lookupSetter(name);
return (Value receiver, Value value) => notImplemented(obj: name);
}
Value getProperty(ObjectValue object, Member member) {
if (member is Field) {
int index = instanceFields.indexOf(member);
// TODO: throw NoSuchMethodError instead.
if (index < 0) return notImplemented(m: 'NoSuchMethod: ${member}');
return object.fields[index];
}
return notImplemented(obj: member);
}
Value setProperty(ObjectValue object, Member member, Value value) {
if (member is Field) {
int index = instanceFields.indexOf(member);
// TODO: throw NoSuchMethodError instead.
if (index < 0) return notImplemented(m: 'NoSuchMethod: ${member}');
object.fields[index] = value;
return Value.nullInstance;
}
return notImplemented(obj: member);
}
/// Populates instance variables and the corresponding implicit getters and
/// setters for the current class and its superclass recursively.
_populateInstanceFields(ast.Class class_) {
if (class_.superclass != null) {
_populateInstanceFields(class_.superclass);
}
for (Field f in class_.fields) {
if (f.isStatic) continue;
instanceFields.add(f);
assert(f.hasImplicitGetter);
int currentFieldIndex = instanceFields.length - 1;
// Shadowing an inherited getter with the same name.
getters[f.name] = (Value receiver) => receiver.fields[currentFieldIndex];
if (f.hasImplicitSetter) {
// Shadowing an inherited setter with the same name.
setters[f.name] = (Value receiver, Value value) =>
receiver.fields[currentFieldIndex] = value;
}
}
}
}
abstract class Value {
Class get class_;
List<Value> 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]) {
throw notImplemented(obj: name);
}
}
class ObjectValue extends Value {
Class class_;
List<Value> fields;
Object get value => this;
ObjectValue(this.class_, this.fields);
}
abstract class LiteralValue extends Value {
Class get class_ =>
notImplemented(m: "Loading class for literal is not implemented.");
List<Value> 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');
}