X-Git-Url: http://nitlanguage.org diff --git a/src/interpreter/naive_interpreter.nit b/src/interpreter/naive_interpreter.nit index 36d82f8..12a54de 100644 --- a/src/interpreter/naive_interpreter.nit +++ b/src/interpreter/naive_interpreter.nit @@ -20,10 +20,11 @@ module naive_interpreter 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 @@ -53,11 +54,11 @@ redef class ModelBuilder 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 @@ -65,6 +66,7 @@ private class NaiveInterpreter # 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]) @@ -112,43 +114,26 @@ private class NaiveInterpreter # Set this mark to skip the evaluation until the end of the specified method frame var returnmark: nullable Frame = null - # Is a break executed? - # Set this mark to skip the evaluation until a labeled statement catch it with `is_break` - var breakmark: nullable EscapeMark = null - - # Is a continue executed? - # Set this mark to skip the evaluation until a labeled statement catch it with `is_continue` - var continuemark: nullable EscapeMark = null + # Is a break or a continue executed? + # Set this mark to skip the evaluation until a labeled statement catch it with `is_escape` + var escapemark: nullable EscapeMark = null # Is a return or a break or a continue executed? # Use this function to know if you must skip the evaluation of statements - fun is_escaping: Bool do return returnmark != null or breakmark != null or continuemark != null + fun is_escaping: Bool do return returnmark != null or escapemark != null # The value associated with the current return/break/continue, if any. # Set the value when you set a escapemark. # Read the value when you catch a mark or reach the end of a method var escapevalue: nullable Instance = null - # If there is a break and is associated with `escapemark`, then return true an clear the mark. - # If there is no break or if `escapemark` is null then return false. - # Use this function to catch a potential break. - fun is_break(escapemark: nullable EscapeMark): Bool + # If there is a break/continue and is associated with `escapemark`, then return true and clear the mark. + # If there is no break/continue or if `escapemark` is null then return false. + # Use this function to catch a potential break/continue. + fun is_escape(escapemark: nullable EscapeMark): Bool do - if escapemark != null and self.breakmark == escapemark then - self.breakmark = null - return true - else - return false - end - end - - # If there is a continue and is associated with `escapemark`, then return true an clear the mark. - # If there is no continue or if `escapemark` is null then return false. - # Use this function to catch a potential continue. - fun is_continue(escapemark: nullable EscapeMark): Bool - do - if escapemark != null and self.continuemark == escapemark then - self.continuemark = null + if escapemark != null and self.escapemark == escapemark then + self.escapemark = null return true else return false @@ -225,13 +210,13 @@ private class NaiveInterpreter 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`. @@ -247,6 +232,19 @@ private class NaiveInterpreter 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 @@ -256,13 +254,22 @@ private class NaiveInterpreter 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 @@ -309,55 +316,58 @@ private class NaiveInterpreter 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] + var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null) + var res = new Array[Instance] + res.add(recv) - args.add(rawargs.first) # recv + if args.is_empty then return res - for i in [0..vararg_rank[ do - args.add(rawargs[i+1]) - end + var vararg_rank = msignature.vararg_rank + var vararg_len = args.length - msignature.arity + if vararg_len < 0 then vararg_len = 0 - 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)) - - 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 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) 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}") @@ -366,14 +376,17 @@ private class NaiveInterpreter # 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) return npropdef.call(self, mpropdef, args) - else if mproperty.name == "init" then + else if mproperty.is_root_init then 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 @@ -418,7 +431,7 @@ private class NaiveInterpreter 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 @@ -447,8 +460,8 @@ private class NaiveInterpreter 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 @@ -495,6 +508,7 @@ private class NaiveInterpreter 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 @@ -507,7 +521,7 @@ private class NaiveInterpreter 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. @@ -608,7 +622,7 @@ class PrimitiveInstance[E: Object] 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 @@ -618,13 +632,13 @@ private class Frame # 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") @@ -694,7 +708,7 @@ redef class AMethPropdef 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 @@ -743,14 +757,14 @@ redef class AMethPropdef 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 @@ -797,6 +811,8 @@ redef class AMethPropdef 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 @@ -875,7 +891,7 @@ redef class AMethPropdef return v.bool_instance(args[0].to_f.is_inf != 0) end else if cname == "NativeString" then - if pname == "init" then + if pname == "new" then return v.native_string_instance("!" * args[1].to_i) end var recvval = args.first.val.as(Buffer) @@ -936,7 +952,7 @@ redef class AMethPropdef else if pname == "calloc_string" then return v.native_string_instance("!" * args[1].to_i) else if cname == "NativeArray" then - if pname == "init" then + if pname == "new" then var val = new Array[Instance].filled_with(v.null_instance, args[1].to_i) return new PrimitiveInstance[Array[Instance]](args[0].mtype, val) end @@ -1093,35 +1109,16 @@ redef class AClassdef 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 @@ -1216,20 +1213,7 @@ redef class ASelfExpr end end -redef class AContinueExpr - redef fun stmt(v) - do - var ne = self.n_expr - if ne != null then - var i = v.expr(ne) - if i == null then return - v.escapevalue = i - end - v.continuemark = self.escapemark - end -end - -redef class ABreakExpr +redef class AEscapeExpr redef fun stmt(v) do var ne = self.n_expr @@ -1238,7 +1222,7 @@ redef class ABreakExpr if i == null then return v.escapevalue = i end - v.breakmark = self.escapemark + v.escapemark = self.escapemark end end @@ -1304,7 +1288,7 @@ redef class ADoExpr redef fun stmt(v) do v.stmt(self.n_block) - v.is_break(self.escapemark) # Clear the break (if any) + v.is_escape(self.break_mark) # Clear the break (if any) end end @@ -1316,8 +1300,8 @@ redef class AWhileExpr if cond == null then return if not cond.is_true then return v.stmt(self.n_block) - if v.is_break(self.escapemark) then return - v.is_continue(self.escapemark) # Clear the break + if v.is_escape(self.break_mark) then return + v.is_escape(self.continue_mark) # Clear the break if v.is_escaping then return end end @@ -1328,8 +1312,8 @@ redef class ALoopExpr do loop v.stmt(self.n_block) - if v.is_break(self.escapemark) then return - v.is_continue(self.escapemark) # Clear the break + if v.is_escape(self.break_mark) then return + v.is_escape(self.continue_mark) # Clear the break if v.is_escaping then return end end @@ -1347,7 +1331,7 @@ redef class AForExpr #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}") @@ -1361,11 +1345,15 @@ redef class AForExpr abort end v.stmt(self.n_block) - if v.is_break(self.escapemark) then return - v.is_continue(self.escapemark) # Clear the break - if v.is_escaping then return + if v.is_escape(self.break_mark) then break + v.is_escape(self.continue_mark) # Clear the break + 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 @@ -1477,9 +1465,7 @@ redef class AStringFormExpr 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 @@ -1579,7 +1565,6 @@ redef class AAsNotnullExpr 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 @@ -1613,12 +1598,8 @@ redef class ASendExpr 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 @@ -1630,12 +1611,8 @@ redef class ASendReassignFormExpr 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 @@ -1655,16 +1632,12 @@ redef class ASuperExpr 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]) @@ -1675,14 +1648,17 @@ redef class ASuperExpr 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 @@ -1690,15 +1666,11 @@ end redef class ANewExpr redef fun expr(v) do - var mtype = v.unanchor_type(self.mtype.as(not null)) + var mtype = v.unanchor_type(self.recvtype.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}")