Index: pkg/kernel/lib/interpreter/interpreter.dart |
diff --git a/pkg/kernel/lib/interpreter/interpreter.dart b/pkg/kernel/lib/interpreter/interpreter.dart |
index 6741928fa9ff7753c95d631e6446171560f27145..87f74f643a8a47988c553348999c3699a6a86a32 100644 |
--- a/pkg/kernel/lib/interpreter/interpreter.dart |
+++ b/pkg/kernel/lib/interpreter/interpreter.dart |
@@ -21,6 +21,10 @@ class Interpreter { |
Program program; |
StatementExecuter visitor = new StatementExecuter(); |
+ // The execution of the program starts with empty main environment. |
+ static MainEnvironment mainEnvironment = |
+ new MainEnvironment(<Member, Location>{}); |
+ |
Interpreter(this.program); |
void run() { |
@@ -57,6 +61,32 @@ class Binding { |
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 { |
Dmitry Stefantsov
2017/08/16 12:06:46
Now that [MainEnvironment]s are immutable and we t
zhivkag
2017/08/16 12:27:44
One difference is that [MainEnvironment] maps [Mem
Dmitry Stefantsov
2017/08/16 12:38:50
Got it. Thanks for the explanation! No, I think
|
+ 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 update(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; |
@@ -104,7 +134,7 @@ class Environment { |
return lookupBinding(variable).location.value; |
} |
- void assign(VariableDeclaration variable, Value value) { |
+ void update(VariableDeclaration variable, Value value) { |
Dmitry Stefantsov
2017/08/16 12:06:47
How about renaming [update] to [updateStore] to be
zhivkag
2017/08/16 12:27:44
Done. I also renamed assign -> update -> updateSto
Dmitry Stefantsov
2017/08/16 12:38:50
I think [updateStore] is ok. As for "updateLocati
|
assert(contains(variable)); |
lookupBinding(variable).location.value = value; |
} |
@@ -174,10 +204,65 @@ class Evaluator extends ExpressionVisitor1<Configuration, EvalConfiguration> { |
node.receiver, config.environment, config.exceptionComponents, cont); |
} |
- Configuration visitStaticGet(StaticGet node, EvalConfiguration config) => |
- defaultExpression(node, config); |
- Configuration visitStaticSet(StaticSet node, EvalConfiguration config) => |
- defaultExpression(node, config); |
+ 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 EnvironmentLocation(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) { |
@@ -429,8 +514,9 @@ class ExecConfiguration extends Configuration { |
ExecConfiguration(this.currentStatement, this.environment, this.state); |
- Configuration step(StatementExecuter executer) => |
- executer.exec(currentStatement, this); |
+ Configuration step(StatementExecuter executer) { |
+ return executer.exec(currentStatement, this); |
+ } |
} |
/// Configuration for applying a [StatementContinuation] to an [Environment]. |
@@ -1003,6 +1089,39 @@ class SetterEK extends ExpressionContinuation { |
} |
} |
+class StaticSetEK extends ExpressionContinuation { |
+ final Member member; |
+ final ExpressionContinuation continuation; |
+ |
+ StaticSetEK(this.member, this.continuation); |
+ |
+ Configuration call(Value v) { |
+ Interpreter.mainEnvironment.update(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 { |
@@ -1082,7 +1201,7 @@ class VariableSetEK extends ExpressionContinuation { |
VariableSetEK(this.variable, this.environment, this.continuation); |
Configuration call(Value value) { |
- environment.assign(variable, value); |
+ environment.update(variable, value); |
return new ValuePassingConfiguration(continuation, value); |
} |
} |
@@ -1541,10 +1660,9 @@ class StatementExecuter |
conf.state.continuation, conf.environment); |
} |
- var env = new Environment(conf.environment); |
var cont = new BlockSK.fromConfig(node.statements.skip(1).toList(), conf); |
- return new ExecConfiguration( |
- node.statements.first, env, conf.state.withContinuation(cont)); |
+ return new ExecConfiguration(node.statements.first, conf.environment, |
+ conf.state.withContinuation(cont)); |
} |
Configuration visitEmptyStatement( |