import literal
import semantize
private import parser::tables
+import mixin
redef class ToolContext
# --discover-call-trace
- var opt_discover_call_trace: OptionBool = new OptionBool("Trace calls of the first invocation of a method", "--discover-call-trace")
+ var opt_discover_call_trace = new OptionBool("Trace calls of the first invocation of a method", "--discover-call-trace")
redef init
do
end
# The visitor that interprets the Nit Program by walking on the AST
-private class NaiveInterpreter
+class NaiveInterpreter
# The modelbuilder that know the AST and its associations with the model
var modelbuilder: ModelBuilder
- # The main moduleof the program (used to lookup methoda
+ # The main module of the program (used to lookup method)
var mainmodule: MModule
# The command line arguments of the interpreted program
# arguments[1] is the first argument
var arguments: Array[String]
+ # The main Sys instance
var mainobj: nullable Instance
init(modelbuilder: ModelBuilder, mainmodule: MModule, arguments: Array[String])
return new PrimitiveInstance[Float](ic.mclass_type, val)
end
- # The unique intance of the `true` value.
+ # The unique instance of the `true` value.
var true_instance: Instance
- # The unique intance of the `false` value.
+ # The unique instance of the `false` value.
var false_instance: Instance
- # The unique intance of the `null` value.
+ # The unique instance of the `null` value.
var null_instance: Instance
# Return a new array made of `values`.
return res
end
+ fun value_instance(object: Object): Instance
+ do
+ if object isa Int then
+ return int_instance(object)
+ else if object isa Bool then
+ return bool_instance(object)
+ else if object isa String then
+ return string_instance(object)
+ else
+ abort
+ end
+ end
+
# Return a new native string initialized with `txt`
fun native_string_instance(txt: String): Instance
do
return new PrimitiveInstance[Buffer](ic.mclass_type, val)
end
+ # Return a new String instance for `txt`
+ fun string_instance(txt: String): Instance
+ do
+ var nat = native_string_instance(txt)
+ var res = self.send(self.force_get_primitive_method("to_s_with_length", nat.mtype), [nat, self.int_instance(txt.length)])
+ assert res != null
+ return res
+ end
+
# The current frame used to store local variables of the current method executed
fun frame: Frame do return frames.first
# The stack of all frames. The first one is the current one.
- var frames: List[Frame] = new List[Frame]
+ var frames = new List[Frame]
- # Return a stack stace. One line per function
+ # Return a stack trace. One line per function
fun stack_trace: String
do
var b = new FlatBuffer
f.map[v] = value
end
- # Store known method, used to trace methods as thez are reached
+ # Store known methods, used to trace methods as they are reached
var discover_call_trace: Set[MMethodDef] = new HashSet[MMethodDef]
- # Common code for calls to injected methods and normal methods
- fun call_commons(mpropdef: MMethodDef, args: Array[Instance]): Array[Instance]
+ # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
+ # 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]
do
- var vararg_rank = mpropdef.msignature.vararg_rank
- if vararg_rank >= 0 then
- assert args.length >= mpropdef.msignature.arity + 1 # because of self
- var rawargs = args
- args = new Array[Instance]
-
- args.add(rawargs.first) # recv
+ var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
+ var res = new Array[Instance]
+ res.add(recv)
- for i in [0..vararg_rank[ do
- args.add(rawargs[i+1])
- end
+ if args.is_empty then return res
- var vararg_lastrank = vararg_rank + rawargs.length-1-mpropdef.msignature.arity
- var vararg = new Array[Instance]
- for i in [vararg_rank..vararg_lastrank] do
- vararg.add(rawargs[i+1])
- end
- # FIXME: its it to late to determine the vararg type, this should have been done during a previous analysis
- var elttype = mpropdef.msignature.mparameters[vararg_rank].mtype.anchor_to(self.mainmodule, args.first.mtype.as(MClassType))
- args.add(self.array_instance(vararg, elttype))
+ var vararg_rank = msignature.vararg_rank
+ var vararg_len = args.length - msignature.arity
+ if vararg_len < 0 then vararg_len = 0
- for i in [vararg_lastrank+1..rawargs.length-1[ do
- args.add(rawargs[i+1])
+ for i in [0..msignature.arity[ do
+ if i == vararg_rank then
+ 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)
end
end
- return args
+ return res
end
# Execute `mpropdef` for a `args` (where `args[0]` is the receiver).
- # Return a falue if `mpropdef` is a function, or null if it is a procedure.
- # The call is direct/static. There is no message-seding/late-binding.
+ # Return a value if `mpropdef` is a function, or null if it is a procedure.
+ # The call is direct/static. There is no message-sending/late-binding.
fun call(mpropdef: MMethodDef, args: Array[Instance]): nullable Instance
do
- args = call_commons(mpropdef, args)
- return call_without_varargs(mpropdef, args)
- end
-
- # Common code to call and this function
- #
- # Call only executes the variadic part, this avoids
- # double encapsulation of variadic parameters into an Array
- fun call_without_varargs(mpropdef: MMethodDef, args: Array[Instance]): nullable Instance
- do
if self.modelbuilder.toolcontext.opt_discover_call_trace.value and not self.discover_call_trace.has(mpropdef) then
self.discover_call_trace.add mpropdef
self.debug("Discovered {mpropdef}")
# Look for the AST node that implements the property
var mproperty = mpropdef.mproperty
+ var val = mpropdef.constant_value
if self.modelbuilder.mpropdef2npropdef.has_key(mpropdef) then
var npropdef = self.modelbuilder.mpropdef2npropdef[mpropdef]
self.parameter_check(npropdef, mpropdef, args)
var nclassdef = self.modelbuilder.mclassdef2nclassdef[mpropdef.mclassdef]
self.parameter_check(nclassdef, mpropdef, args)
return nclassdef.call(self, mpropdef, args)
+ else if val != null then
+ return value_instance(val)
else
fatal("Fatal Error: method {mpropdef} not found in the AST")
abort
end
# Execute a full `callsite` for given `args`
- # Use this method, instead of `send` to execute and control the aditionnal behavior of the call-sites
+ # Use this method, instead of `send` to execute and control the additional behavior of the call-sites
fun callsite(callsite: nullable CallSite, arguments: Array[Instance]): nullable Instance
do
var initializers = callsite.mpropdef.initializers
end
# Execute `mproperty` for a `args` (where `args[0]` is the receiver).
- # Return a falue if `mproperty` is a function, or null if it is a procedure.
- # The call is polimotphic. There is a message-seding/late-bindng according to te receiver (args[0]).
+ # Return a value if `mproperty` is a function, or null if it is a procedure.
+ # The call is polymorphic. There is a message-sending/late-binding according to the receiver (args[0]).
fun send(mproperty: MMethod, args: Array[Instance]): nullable Instance
do
var recv = args.first
var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
self.mainmodule.linearize_mclassdefs(cds)
for cd in cds do
+ if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue
var n = self.modelbuilder.mclassdef2nclassdef[cd]
for npropdef in n.n_propdefs do
if npropdef isa AAttrPropdef then
return res
end
- var collect_attr_propdef_cache = new HashMap[MType, Array[AAttrPropdef]]
+ private var collect_attr_propdef_cache = new HashMap[MType, Array[AAttrPropdef]]
# Fill the initial values of the newly created instance `recv`.
# `recv.mtype` is used to know what must be filled.
end
# Information about local variables in a running method
-private class Frame
+class Frame
# The current visited node
# The node is stored by frame to keep a stack trace
var current_node: ANode
# Arguments of the method (the first is the receiver)
var arguments: Array[Instance]
# Mapping between a variable and the current value
- var map: Map[Variable, Instance] = new HashMap[Variable, Instance]
+ private var map: Map[Variable, Instance] = new HashMap[Variable, Instance]
end
redef class ANode
# Aborts the program with a message
# `v` is used to know if a colored message is displayed or not
- private fun fatal(v: NaiveInterpreter, message: String)
+ fun fatal(v: NaiveInterpreter, message: String)
do
if v.modelbuilder.toolcontext.opt_no_color.value == true then
sys.stderr.write("Runtime error: {message} ({location.file.filename}:{location.line_start})\n")
if auto_super_call then
# standard call-next-method
var superpd = mpropdef.lookup_next_definition(v.mainmodule, arguments.first.mtype)
- v.call_without_varargs(superpd, arguments)
+ v.call(superpd, arguments)
end
if mpropdef.is_intern or mpropdef.is_extern then
var txt = recv.mtype.to_s
return v.native_string_instance(txt)
else if pname == "==" then
- # == is correclt redefined for instances
+ # == is correctly redefined for instances
return v.bool_instance(args[0] == args[1])
else if pname == "!=" then
return v.bool_instance(args[0] != args[1])
else if pname == "is_same_type" then
return v.bool_instance(args[0].mtype == args[1].mtype)
else if pname == "is_same_instance" then
- return v.bool_instance(args[1] != null and args[0].eq_is(args[1]))
+ return v.bool_instance(args[0].eq_is(args[1]))
else if pname == "exit" then
exit(args[1].to_i)
abort
return v.int_instance(args[0].to_i.bin_or(args[1].to_i))
else if pname == "bin_xor" then
return v.int_instance(args[0].to_i.bin_xor(args[1].to_i))
+ else if pname == "bin_not" then
+ return v.int_instance(args[0].to_i.bin_not)
else if pname == "native_int_to_s" then
return v.native_string_instance(recvval.to_s)
else if pname == "strerror_ext" then
private fun call(v: NaiveInterpreter, mpropdef: MMethodDef, args: Array[Instance]): nullable Instance
do
if mpropdef.mproperty.is_root_init then
- assert self.super_inits == null
assert args.length == 1
if not mpropdef.is_intro then
# standard call-next-method
var superpd = mpropdef.lookup_next_definition(v.mainmodule, args.first.mtype)
- v.call_without_varargs(superpd, args)
+ v.call(superpd, args)
end
return null
+ else
+ abort
end
-
- var super_inits = self.super_inits
- if super_inits != null then
- var args_of_super = args
- if args.length > 1 then args_of_super = [args.first]
- for su in super_inits do
- v.send(su, args_of_super)
- end
- end
- var recv = args.first
- assert recv isa MutableInstance
- var i = 1
- # Collect undefined attributes
- for npropdef in self.n_propdefs do
- if npropdef isa AAttrPropdef and not npropdef.noinit and npropdef.n_expr == null then
- v.write_attribute(npropdef.mpropdef.mproperty, recv, args[i])
- i += 1
- end
- end
- return null
end
end
#self.debug("iter {iter}")
loop
var isok = v.callsite(method_is_ok, [iter]).as(not null)
- if not isok.is_true then return
+ if not isok.is_true then break
if self.variables.length == 1 then
var item = v.callsite(method_item, [iter]).as(not null)
#self.debug("item {item}")
abort
end
v.stmt(self.n_block)
- if v.is_break(self.escapemark) then return
+ if v.is_break(self.escapemark) then break
v.is_continue(self.escapemark) # Clear the break
- if v.is_escaping then return
+ if v.is_escaping then break
v.callsite(method_next, [iter])
end
+ var method_finish = self.method_finish
+ if method_finish != null then
+ v.callsite(method_finish, [iter])
+ end
end
end
redef fun expr(v)
do
var txt = self.value.as(not null)
- var nat = v.native_string_instance(txt)
- var res = v.send(v.force_get_primitive_method("to_s", nat.mtype), [nat]).as(not null)
- return res
+ return v.string_instance(txt)
end
end
do
var i = v.expr(self.n_expr)
if i == null then return null
- var mtype = v.unanchor_type(self.mtype.as(not null))
if i.mtype isa MNullType then
fatal(v, "Cast failed")
end
do
var recv = v.expr(self.n_expr)
if recv == null then return null
- var args = [recv]
- for a in self.raw_arguments do
- var i = v.expr(a)
- if i == null then return null
- args.add(i)
- end
+ var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+ if args == null then return null
var res = v.callsite(callsite, args)
return res
do
var recv = v.expr(self.n_expr)
if recv == null then return
- var args = [recv]
- for a in self.raw_arguments do
- var i = v.expr(a)
- if i == null then return
- args.add(i)
- end
+ var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+ if args == null then return
var value = v.expr(self.n_value)
if value == null then return
redef fun expr(v)
do
var recv = v.frame.arguments.first
- var args = [recv]
- for a in self.n_args.n_exprs do
- var i = v.expr(a)
- if i == null then return null
- args.add(i)
- end
var callsite = self.callsite
if callsite != null then
- # Add additionnals arguments for the super init call
+ 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
for i in [0..callsite.msignature.arity[ do
args.add(v.frame.arguments[i+1])
return res
end
+ # 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
args = v.frame.arguments
end
-
- # stantard call-next-method
- var mpropdef = self.mpropdef
- mpropdef = mpropdef.lookup_next_definition(v.mainmodule, recv.mtype)
- var res = v.call_without_varargs(mpropdef, args)
+ var res = v.call(mpropdef, args)
return res
end
end
var mtype = v.unanchor_type(self.mtype.as(not null))
var recv: Instance = new MutableInstance(mtype)
v.init_instance(recv)
- var args = [recv]
- for a in self.n_args.n_exprs do
- var i = v.expr(a)
- if i == null then return null
- args.add(i)
- end
+ var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
+ if args == null then return null
var res2 = v.callsite(callsite, args)
if res2 != null then
#self.debug("got {res2} from {mproperty}. drop {recv}")