# 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 the integer instance associated with `val`.
fun int_instance(val: Int): Instance
do
- var ic = get_primitive_class("Int")
- var instance = new PrimitiveInstance[Int](ic.mclass_type, val)
+ var t = mainmodule.int_type
+ var instance = new PrimitiveInstance[Int](t, val)
+ init_instance_primitive(instance)
+ return instance
+ end
+
+ # Return the byte instance associated with `val`.
+ fun byte_instance(val: Byte): Instance
+ do
+ var t = mainmodule.byte_type
+ var instance = new PrimitiveInstance[Byte](t, val)
init_instance_primitive(instance)
return instance
end
# Return the char instance associated with `val`.
fun char_instance(val: Char): Instance
do
- var ic = get_primitive_class("Char")
- var instance = new PrimitiveInstance[Char](ic.mclass_type, val)
+ var t = mainmodule.char_type
+ var instance = new PrimitiveInstance[Char](t, val)
init_instance_primitive(instance)
return instance
end
# Return the float instance associated with `val`.
fun float_instance(val: Float): Instance
do
- var ic = get_primitive_class("Float")
- var instance = new PrimitiveInstance[Float](ic.mclass_type, val)
+ var t = mainmodule.float_type
+ var instance = new PrimitiveInstance[Float](t, val)
init_instance_primitive(instance)
return instance
end
fun array_instance(values: Array[Instance], elttype: MType): Instance
do
assert not elttype.need_anchor
- var nat = new PrimitiveInstance[Array[Instance]](get_primitive_class("NativeArray").get_mtype([elttype]), values)
+ var nat = new PrimitiveInstance[Array[Instance]](mainmodule.native_array_type(elttype), values)
init_instance_primitive(nat)
- var mtype = get_primitive_class("Array").get_mtype([elttype])
+ var mtype = mainmodule.array_type(elttype)
var res = new MutableInstance(mtype)
self.init_instance(res)
self.send(self.force_get_primitive_method("with_native", mtype), [res, nat, self.int_instance(values.length)])
do
var val = new FlatBuffer.from(txt)
val.add('\0')
- var ic = get_primitive_class("NativeString")
- var instance = new PrimitiveInstance[Buffer](ic.mclass_type, val)
+ var t = mainmodule.native_string_type
+ var instance = new PrimitiveInstance[Buffer](t, val)
init_instance_primitive(instance)
return instance
end
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
# This method is used to manage varargs in signatures and returns the real array
# of instances to use in the call.
# Return `null` if one of the evaluation of the arguments return null.
- fun varargize(mpropdef: MMethodDef, recv: Instance, args: SequenceRead[AExpr]): nullable Array[Instance]
+ fun varargize(mpropdef: MMethodDef, map: nullable SignatureMap, recv: Instance, args: SequenceRead[AExpr]): nullable Array[Instance]
do
var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
var res = new Array[Instance]
res.add(recv)
- if args.is_empty then return res
+ if msignature.arity == 0 then return res
+
+ if map == null then
+ assert args.length == msignature.arity else debug("Expected {msignature.arity} args, got {args.length}")
+ for ne in args do
+ var e = self.expr(ne)
+ if e == null then return null
+ res.add e
+ end
+ return res
+ end
+
+ # Eval in order of arguments, not parameters
+ var exprs = new Array[Instance].with_capacity(args.length)
+ for ne in args do
+ var e = self.expr(ne)
+ if e == null then return null
+ exprs.add e
+ end
- var vararg_rank = msignature.vararg_rank
- var vararg_len = args.length - msignature.arity
- if vararg_len < 0 then vararg_len = 0
+ # Fill `res` with the result of the evaluation according to the mapping
for i in [0..msignature.arity[ do
- if i == vararg_rank then
- var ne = args[i]
- if ne isa AVarargExpr then
- var e = self.expr(ne.n_expr)
- if e == null then return null
- res.add(e)
- continue
- end
- var vararg = new Array[Instance]
- for j in [vararg_rank..vararg_rank+vararg_len] do
- var e = self.expr(args[j])
- if e == null then return null
- vararg.add(e)
- end
- var elttype = msignature.mparameters[vararg_rank].mtype.anchor_to(self.mainmodule, recv.mtype.as(MClassType))
- res.add(self.array_instance(vararg, elttype))
- else
- var j = i
- if i > vararg_rank then j += vararg_len
- var e = self.expr(args[j])
- if e == null then return null
- res.add(e)
+ var param = msignature.mparameters[i]
+ var j = map.map.get_or_null(i)
+ if j == null then
+ # default value
+ res.add(null_instance)
+ continue
end
+ if param.is_vararg and map.vararg_decl > 0 then
+ var vararg = exprs.sub(j, map.vararg_decl)
+ var elttype = param.mtype.anchor_to(self.mainmodule, recv.mtype.as(MClassType))
+ var arg = self.array_instance(vararg, elttype)
+ res.add(arg)
+ continue
+ end
+ res.add exprs[j]
end
return res
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
# A hook to initialize a `PrimitiveInstance`
fun init_instance_primitive(recv: Instance) do end
- # Return the primitive `MClass` corresponding to the `name` given in parameter
- # `name` : name of the primitive class
- fun get_primitive_class(name: String): MClass
- do
- return mainmodule.get_primitive_class(name)
- end
-
# This function determines the correct type according to the receiver of the current propdef (self).
fun unanchor_type(mtype: MType): MType
do
# else aborts
fun to_f: Float do abort
+ # Return the integer value if the instance is a byte.
+ # else aborts
+ fun to_b: Byte do abort
+
# The real value encapsulated if the instance is primitive.
# Else aborts.
fun val: nullable Object do abort
redef fun to_i do return val.as(Int)
redef fun to_f do return val.as(Float)
+
+ redef fun to_b do return val.as(Byte)
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
var recvval = args[0].to_i
if pname == "unary -" then
return v.int_instance(-args[0].to_i)
+ else if pname == "unary +" then
+ return args[0]
else if pname == "+" then
return v.int_instance(args[0].to_i + args[1].to_i)
else if pname == "-" then
return v.char_instance(args[0].to_i.ascii)
else if pname == "to_f" then
return v.float_instance(args[0].to_i.to_f)
+ else if pname == "to_b" then
+ return v.byte_instance(args[0].to_i.to_b)
else if pname == "lshift" then
return v.int_instance(args[0].to_i.lshift(args[1].to_i))
else if pname == "rshift" then
else if pname == "strerror_ext" then
return v.native_string_instance(recvval.strerror)
end
+ else if cname == "Byte" then
+ var recvval = args[0].to_b
+ if pname == "unary -" then
+ return v.byte_instance(-args[0].to_b)
+ else if pname == "unary +" then
+ return args[0]
+ else if pname == "+" then
+ return v.byte_instance(args[0].to_b + args[1].to_b)
+ else if pname == "-" then
+ return v.byte_instance(args[0].to_b - args[1].to_b)
+ else if pname == "*" then
+ return v.byte_instance(args[0].to_b * args[1].to_b)
+ else if pname == "%" then
+ return v.byte_instance(args[0].to_b % args[1].to_b)
+ else if pname == "/" then
+ return v.byte_instance(args[0].to_b / args[1].to_b)
+ else if pname == "<" then
+ return v.bool_instance(args[0].to_b < args[1].to_b)
+ else if pname == ">" then
+ return v.bool_instance(args[0].to_b > args[1].to_b)
+ else if pname == "<=" then
+ return v.bool_instance(args[0].to_b <= args[1].to_b)
+ else if pname == ">=" then
+ return v.bool_instance(args[0].to_b >= args[1].to_b)
+ else if pname == "<=>" then
+ return v.int_instance(args[0].to_b <=> args[1].to_b)
+ else if pname == "to_f" then
+ return v.float_instance(args[0].to_b.to_f)
+ else if pname == "to_i" then
+ return v.int_instance(args[0].to_b.to_i)
+ else if pname == "lshift" then
+ return v.byte_instance(args[0].to_b.lshift(args[1].to_i))
+ else if pname == "rshift" then
+ return v.byte_instance(args[0].to_b.rshift(args[1].to_i))
+ else if pname == "byte_to_s_len" then
+ return v.int_instance(recvval.to_s.length)
+ else if pname == "native_byte_to_s" then
+ var s = recvval.to_s
+ var srecv = args[1].val.as(Buffer)
+ srecv.clear
+ srecv.append(s)
+ srecv.add('\0')
+ return null
+ end
else if cname == "Char" then
var recv = args[0].val.as(Char)
if pname == "ascii" then
var recv = args[0].to_f
if pname == "unary -" then
return v.float_instance(-recv)
+ else if pname == "unary +" then
+ return args[0]
else if pname == "+" then
return v.float_instance(recv + args[1].to_f)
else if pname == "-" then
return v.bool_instance(recv >= args[1].to_f)
else if pname == "to_i" then
return v.int_instance(recv.to_i)
+ else if pname == "to_b" then
+ return v.byte_instance(recv.to_b)
else if pname == "cos" then
return v.float_instance(args[0].to_f.cos)
else if pname == "sin" then
else if pname == "file_exists" then
return v.bool_instance(recvval.to_s.file_exists)
else if pname == "file_mkdir" then
- recvval.to_s.mkdir
- return null
+ var res = recvval.to_s.mkdir
+ return v.bool_instance(res == null)
else if pname == "file_chdir" then
- recvval.to_s.chdir
- return null
+ var res = recvval.to_s.chdir
+ return v.bool_instance(res == null)
else if pname == "file_realpath" then
return v.native_string_instance(recvval.to_s.realpath)
else if pname == "get_environ" then
return v.int_instance(res)
else if pname == "atof" then
return v.float_instance(recvval.to_f)
+ else if pname == "fast_cstring" then
+ var ns = recvval.to_cstring.to_s.substring_from(args[1].to_i)
+ return v.native_string_instance(ns)
end
else if cname == "String" then
var cs = v.send(v.force_get_primitive_method("to_cstring", args.first.mtype), [args.first])
if pname == "files" then
var res = new Array[Instance]
for f in str.files do res.add v.string_instance(f)
- return v.array_instance(res, v.get_primitive_class("String").mclass_type)
+ return v.array_instance(res, v.mainmodule.string_type)
end
else if pname == "calloc_string" then
return v.native_string_instance("!" * args[1].to_i)
else if pname == "set_buffering_type" then
return v.int_instance(recvval.as(PrimitiveNativeFile).set_buffering_type(args[1].to_i, args[2].to_i))
end
- else if pname == "calloc_array" then
- var recvtype = args.first.mtype.as(MClassType)
- var mtype: MType
- mtype = recvtype.supertype_to(v.mainmodule, recvtype, v.get_primitive_class("ArrayCapable"))
- mtype = mtype.arguments.first
- var val = new Array[Instance].filled_with(v.null_instance, args[1].to_i)
- var instance = new PrimitiveInstance[Array[Instance]](v.get_primitive_class("NativeArray").get_mtype([mtype]), val)
- v.init_instance_primitive(instance)
- return instance
else if pname == "native_argc" then
return v.int_instance(v.arguments.length)
else if pname == "native_argv" 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 mtype = self.mpropdef.static_mtype.as(not null)
+ var mpropdef = self.mpropdef
+ if mpropdef == null then return
+ var mtype = mpropdef.static_mtype.as(not null)
mtype = mtype.anchor_to(v.mainmodule, recv.mtype.as(MClassType))
if mtype isa MNullableType then
v.write_attribute(self.mpropdef.mproperty, recv, v.null_instance)
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
end
redef class AVardeclExpr
- redef fun stmt(v)
+ redef fun expr(v)
do
var ne = self.n_expr
if ne != null then
var i = v.expr(ne)
- if i == null then return
+ if i == null then return null
v.write_variable(self.variable.as(not null), i)
+ return i
end
+ return null
end
end
end
end
+redef class AImplicitSelfExpr
+ redef fun expr(v)
+ do
+ if not is_sys then return super
+ return v.mainobj
+ end
+end
+
redef class AEscapeExpr
redef fun stmt(v)
do
end
end
+redef class AWithExpr
+ redef fun stmt(v)
+ do
+ var expr = v.expr(self.n_expr)
+ if expr == null then return
+
+ v.callsite(method_start, [expr])
+ v.stmt(self.n_block)
+ v.is_escape(self.break_mark) # Clear the break
+ v.callsite(method_finish, [expr])
+ end
+end
+
redef class AAssertExpr
redef fun stmt(v)
do
end
end
+redef class AByteExpr
+ redef fun expr(v)
+ do
+ return v.byte_instance(self.value.as(not null))
+ end
+end
+
redef class AFloatExpr
redef fun expr(v)
do
if i == null then return null
array.add(i)
end
- var i = v.array_instance(array, v.get_primitive_class("Object").mclass_type)
- var res = v.send(v.force_get_primitive_method("to_s", i.mtype), [i])
+ var i = v.array_instance(array, v.mainmodule.object_type)
+ var res = v.send(v.force_get_primitive_method("plain_to_s", i.mtype), [i])
assert res != null
return res
end
do
var recv = v.expr(self.n_expr)
if recv == null then return null
- var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+ var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
if args == null then return null
var res = v.callsite(callsite, args)
do
var recv = v.expr(self.n_expr)
if recv == null then return
- var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+ var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
if args == null then return
var value = v.expr(self.n_value)
if value == null then return
var callsite = self.callsite
if callsite != null then
- var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
- if args == null then return null
- # Add additional arguments for the super init call
- if args.length == 1 then
+ var args
+ if self.n_args.n_exprs.is_empty then
+ # Add automatic arguments for the super init call
+ args = [recv]
for i in [0..callsite.msignature.arity[ do
args.add(v.frame.arguments[i+1])
end
+ else
+ args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
+ if args == null then return null
end
+
# Super init call
var res = v.callsite(callsite, args)
return res
end
- # standard call-next-method
+ # Standard call-next-method
var mpropdef = self.mpropdef
mpropdef = mpropdef.lookup_next_definition(v.mainmodule, recv.mtype)
- var args = v.varargize(mpropdef, recv, self.n_args.n_exprs)
- if args == null then return null
-
- if args.length == 1 then
+ var args
+ if self.n_args.n_exprs.is_empty then
args = v.frame.arguments
+ else
+ args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
+ if args == null then return null
end
+
var res = v.call(mpropdef, args)
return res
end
var mtype = v.unanchor_type(self.recvtype.as(not null))
var recv: Instance = new MutableInstance(mtype)
v.init_instance(recv)
- var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
+ var callsite = self.callsite
+ if callsite == null then return recv
+
+ var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
if args == null then return null
var res2 = v.callsite(callsite, args)
if res2 != null then
end
end
+redef class AVarargExpr
+ redef fun expr(v)
+ do
+ return v.expr(self.n_expr)
+ end
+end
+
+redef class ANamedargExpr
+ redef fun expr(v)
+ do
+ return v.expr(self.n_expr)
+ end
+end
+
redef class ADebugTypeExpr
redef fun stmt(v)
do