# Is a return executed?
# Set this mark to skip the evaluation until the end of the specified method frame
- var returnmark: nullable Frame = null
+ var returnmark: nullable FRAME = null
# Is a break or a continue executed?
# Set this mark to skip the evaluation until a labeled statement catch it with `is_escape`
return res
end
+ # The virtual type of the frames used in the execution engine
+ type FRAME: Frame
+
# The current frame used to store local variables of the current method executed
- fun frame: Frame do return frames.first
+ fun frame: FRAME do return frames.first
# The stack of all frames. The first one is the current one.
- var frames = new List[Frame]
+ var frames = new List[FRAME]
# Return a stack trace. One line per function
fun stack_trace: String
return frames.first.arguments.first.mtype.as(MClassType)
end
+ # Initialize the environment for a call and return a new Frame
+ # *`node` The AST node
+ # *`mpropdef` The corresponding mpropdef
+ # *`args` Arguments of the call
+ fun new_frame(node: ANode, mpropdef: MPropDef, args: Array[Instance]): FRAME
+ do
+ return new InterpreterFrame(node, mpropdef, args)
+ end
+
# Exit the program with a message
fun fatal(message: String)
do
# Retrieve the value of the variable in the current frame
fun read_variable(v: Variable): Instance
do
- var f = frames.first
+ var f = frames.first.as(InterpreterFrame)
return f.map[v]
end
# Assign the value of the variable in the current frame
fun write_variable(v: Variable, value: Instance)
do
- var f = frames.first
+ var f = frames.first.as(InterpreterFrame)
f.map[v] = value
end
var node = modelbuilder.mpropdef2node(mpropdef)
if mpropdef.is_abstract then
if node != null then
- self.frames.unshift new Frame(node, mpropdef, args)
+ self.frames.unshift new_frame(node, mpropdef, args)
end
fatal("Abstract method `{mpropdef.mproperty.name}` called on `{args.first.mtype}`")
abort
end
# Information about local variables in a running method
-class Frame
+abstract class Frame
# The current visited node
# The node is stored by frame to keep a stack trace
var current_node: ANode
var mpropdef: MPropDef
# Arguments of the method (the first is the receiver)
var arguments: Array[Instance]
+ # Indicate if the expression has an array comprehension form
+ var comprehension: nullable Array[Instance] = null
+end
+
+# Implementation of a Frame with a Hashmap to store local variables
+class InterpreterFrame
+ super Frame
+
# Mapping between a variable and the current value
private var map: Map[Variable, Instance] = new HashMap[Variable, Instance]
- var comprehension: nullable Array[Instance] = null
end
redef class ANode
redef fun call(v, mpropdef, args)
do
- var f = new Frame(self, self.mpropdef.as(not null), args)
+ var f = v.new_frame(self, mpropdef, args)
var res = call_commons(v, mpropdef, args, f)
v.frames.shift
if v.returnmark == f then
if mpropdef == mreadpropdef then
assert args.length == 1
if not is_lazy or v.isset_attribute(attr, recv) then return v.read_attribute(attr, recv)
- return evaluate_expr(v, recv)
+ var f = v.new_frame(self, mpropdef, args)
+ return evaluate_expr(v, recv, f)
else if mpropdef == mwritepropdef then
assert args.length == 2
v.write_attribute(attr, recv, args[1])
do
if is_lazy then return
if has_value then
- evaluate_expr(v, recv)
+ var f = v.new_frame(self, mpropdef.as(not null), [recv])
+ evaluate_expr(v, recv, f)
return
end
var mpropdef = self.mpropdef
end
end
- private fun evaluate_expr(v: NaiveInterpreter, recv: Instance): Instance
+ private fun evaluate_expr(v: NaiveInterpreter, recv: Instance, f: Frame): Instance
do
assert recv isa MutableInstance
- var f = new Frame(self, self.mpropdef.as(not null), [recv])
v.frames.unshift(f)
var val