X-Git-Url: http://nitlanguage.org diff --git a/src/interpreter/naive_interpreter.nit b/src/interpreter/naive_interpreter.nit index 5b79f0c..21118ab 100644 --- a/src/interpreter/naive_interpreter.nit +++ b/src/interpreter/naive_interpreter.nit @@ -20,10 +20,12 @@ module naive_interpreter import literal import semantize private import parser::tables +import mixin +import primitive_types 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 @@ -45,36 +47,19 @@ redef class ModelBuilder self.toolcontext.info("*** START INTERPRETING ***", 1) var interpreter = new NaiveInterpreter(self, mainmodule, arguments) - init_naive_interpreter(interpreter, mainmodule) + interpreter.start(mainmodule) var time1 = get_time self.toolcontext.info("*** END INTERPRETING: {time1-time0} ***", 2) end - - private fun init_naive_interpreter(interpreter: NaiveInterpreter, mainmodule: MModule) do - var sys_type = mainmodule.sys_type - if sys_type == null then return # no class Sys - var mainobj = new MutableInstance(sys_type) - interpreter.mainobj = mainobj - interpreter.init_instance(mainobj) - var initprop = mainmodule.try_get_primitive_method("init", sys_type.mclass) - if initprop != null then - interpreter.send(initprop, [mainobj]) - end - var mainprop = mainmodule.try_get_primitive_method("run", sys_type.mclass) or else - mainmodule.try_get_primitive_method("main", sys_type.mclass) - if mainprop != null then - interpreter.send(mainprop, [mainobj]) - end - end 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 @@ -82,71 +67,76 @@ private class NaiveInterpreter # arguments[1] is the first argument var arguments: Array[String] - var mainobj: nullable Instance + # The main Sys instance + var mainobj: nullable Instance is noinit - init(modelbuilder: ModelBuilder, mainmodule: MModule, arguments: Array[String]) + init do - self.modelbuilder = modelbuilder - self.mainmodule = mainmodule - self.arguments = arguments - self.true_instance = new PrimitiveInstance[Bool](mainmodule.bool_type, true) - self.false_instance = new PrimitiveInstance[Bool](mainmodule.bool_type, false) - self.null_instance = new MutableInstance(mainmodule.model.null_type) + if mainmodule.model.get_mclasses_by_name("Bool") != null then + self.true_instance = new PrimitiveInstance[Bool](mainmodule.bool_type, true) + init_instance_primitive(self.true_instance) + self.false_instance = new PrimitiveInstance[Bool](mainmodule.bool_type, false) + init_instance_primitive(self.false_instance) + end + self.null_instance = new PrimitiveInstance[nullable Object](mainmodule.model.null_type, null) + end + + # Starts the interpreter on the main module of a program + fun start(mainmodule: MModule) do + var interpreter = self + var sys_type = mainmodule.sys_type + if sys_type == null then return # no class Sys + var mainobj = new MutableInstance(sys_type) + interpreter.mainobj = mainobj + interpreter.init_instance(mainobj) + var initprop = mainmodule.try_get_primitive_method("init", sys_type.mclass) + if initprop != null then + interpreter.send(initprop, [mainobj]) + end + var mainprop = mainmodule.try_get_primitive_method("run", sys_type.mclass) or else + mainmodule.try_get_primitive_method("main", sys_type.mclass) + if mainprop != null then + interpreter.send(mainprop, [mainobj]) + end end # Subtype test in the context of the mainmodule fun is_subtype(sub, sup: MType): Bool do - return sub.is_subtype(self.mainmodule, self.frame.arguments.first.mtype.as(MClassType), sup) + return sub.is_subtype(self.mainmodule, current_receiver_class, sup) end + # Get a primitive method in the context of the main module fun force_get_primitive_method(name: String, recv: MType): MMethod do assert recv isa MClassType - return self.modelbuilder.force_get_primitive_method(self.frame.current_node, name, recv.mclass, self.mainmodule) + return self.modelbuilder.force_get_primitive_method(current_node, name, recv.mclass, self.mainmodule) end # 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 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 - 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 + # 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.continuemark == escapemark then - self.continuemark = null + if escapemark != null and self.escapemark == escapemark then + self.escapemark = null return true else return false @@ -183,14 +173,20 @@ private class NaiveInterpreter # If `n` cannot be evaluated, then aborts. fun stmt(n: nullable AExpr) do - if n != null then - var frame = self.frame - var old = frame.current_node - frame.current_node = n - #n.debug("Execute stmt") - n.stmt(self) - frame.current_node = old + if n == null then return + + if n.comprehension != null then + var comprehension = frame.comprehension.as(not null) + var i = expr(n) + if i != null then comprehension.add(i) + return end + + var frame = self.frame + var old = frame.current_node + frame.current_node = n + n.stmt(self) + frame.current_node = old end # Map used to store values of nodes that must be evaluated once in the system (`AOnceExpr`) @@ -205,62 +201,98 @@ private class NaiveInterpreter # Return the integer instance associated with `val`. fun int_instance(val: Int): Instance do - var ic = self.mainmodule.get_primitive_class("Int") - return 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 char instance associated with `val`. fun char_instance(val: Char): Instance do - var ic = self.mainmodule.get_primitive_class("Char") - return 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 = self.mainmodule.get_primitive_class("Float") - return 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 - # The unique intance of the `true` value. - var true_instance: Instance + # The unique instance of the `true` value. + var true_instance: Instance is noinit - # The unique intance of the `false` value. - var false_instance: Instance + # The unique instance of the `false` value. + var false_instance: Instance is noinit - # The unique intance of the `null` value. - var null_instance: Instance + # The unique instance of the `null` value. + var null_instance: Instance is noinit # Return a new array made of `values`. # The dynamic type of the result is Array[elttype]. fun array_instance(values: Array[Instance], elttype: MType): Instance do assert not elttype.need_anchor - var nat = new PrimitiveInstance[Array[Instance]](self.mainmodule.get_primitive_class("NativeArray").get_mtype([elttype]), values) - var mtype = self.mainmodule.get_primitive_class("Array").get_mtype([elttype]) + var nat = new PrimitiveInstance[Array[Instance]](mainmodule.native_array_type(elttype), values) + init_instance_primitive(nat) + 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)]) return res end + # Return a instance associated to a primitive class + # Current primitive classes are `Int`, `Bool`, and `String` + 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 var val = new FlatBuffer.from(txt) val.add('\0') - var ic = self.mainmodule.get_primitive_class("NativeString") - return 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 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 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: 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 @@ -272,13 +304,36 @@ private class NaiveInterpreter return b.to_s end + # The current node, used to print errors, debug and stack-traces + fun current_node: nullable ANode + do + if frames.is_empty then return null + return frames.first.current_node + end + + # The dynamic type of the current `self` + fun current_receiver_class: MClassType + do + 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 - if frames.is_empty then + var node = current_node + if node == null then print message else - self.frame.current_node.fatal(self, message) + node.fatal(self, message) end exit(1) end @@ -286,62 +341,80 @@ private class NaiveInterpreter # Debug on the current node fun debug(message: String) do - if frames.is_empty then + var node = current_node + if node == null then print message else - self.frame.current_node.debug(message) + node.debug(message) end end - # Store known method, used to trace methods as thez are reached - var discover_call_trace: Set[MMethodDef] = new HashSet[MMethodDef] + # Retrieve the value of the variable in the current frame + fun read_variable(v: Variable): Instance + do + var f = frames.first.as(InterpreterFrame) + return f.map[v] + end - # Common code for calls to injected methods and normal methods - fun call_commons(mpropdef: MMethodDef, args: Array[Instance]): Array[Instance] + # Assign the value of the variable in the current frame + fun write_variable(v: Variable, value: 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 f = frames.first.as(InterpreterFrame) + f.map[v] = value + end - args.add(rawargs.first) # recv + # Store known methods, used to trace methods as they are reached + var discover_call_trace: Set[MMethodDef] = new HashSet[MMethodDef] - for i in [0..vararg_rank[ do - args.add(rawargs[i+1]) - end + # 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 msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null) + var res = new Array[Instance] + res.add(recv) - 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)) + if args.is_empty then return res + + 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 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}") @@ -349,22 +422,35 @@ private class NaiveInterpreter assert args.length == mpropdef.msignature.arity + 1 else debug("Invalid arity for {mpropdef}. {args.length} arguments given.") # Look for the AST node that implements the property - var mproperty = mpropdef.mproperty - 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 - var nclassdef = self.modelbuilder.mclassdef2nclassdef[mpropdef.mclassdef] - self.parameter_check(nclassdef, mpropdef, args) - return nclassdef.call(self, mpropdef, args) + var val = mpropdef.constant_value + + var node = modelbuilder.mpropdef2node(mpropdef) + if mpropdef.is_abstract then + if node != null then + self.frames.unshift new_frame(node, mpropdef, args) + end + fatal("Abstract method `{mpropdef.mproperty.name}` called on `{args.first.mtype}`") + abort + end + + if node isa APropdef then + self.parameter_check(node, mpropdef, args) + return node.call(self, mpropdef, args) + else if node isa AClassdef then + self.parameter_check(node, mpropdef, args) + return node.call(self, mpropdef, args) + else if node != null then + fatal("Fatal Error: method {mpropdef} associated to unexpected AST node {node.location}") + abort + else if val != null then + return value_instance(val) else fatal("Fatal Error: method {mpropdef} not found in the AST") abort end end - # Generate type checks in the C code to check covariant parameters + # Execute type checks of covariant parameters fun parameter_check(node: ANode, mpropdef: MMethodDef, args: Array[Instance]) do var msignature = mpropdef.msignature @@ -376,6 +462,8 @@ private class NaiveInterpreter var origmtype = mpropdef.mproperty.intro.msignature.mparameters[i].mtype if not origmtype.need_anchor then continue + #print "{mpropdef}: {mpropdef.mproperty.intro.msignature.mparameters[i]}" + # get the parameter type var mtype = msignature.mparameters[i].mtype var anchor = args.first.mtype.as(MClassType) @@ -390,7 +478,7 @@ private class NaiveInterpreter fun send_commons(mproperty: MMethod, args: Array[Instance], mtype: MType): nullable Instance do if mtype isa MNullType then - if mproperty.name == "==" then + if mproperty.name == "==" or mproperty.name == "is_same_instance" then return self.bool_instance(args[0] == args[1]) else if mproperty.name == "!=" then return self.bool_instance(args[0] != args[1]) @@ -402,7 +490,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 @@ -419,7 +507,7 @@ private class NaiveInterpreter self.send(p, args) else if p isa MAttribute then assert recv isa MutableInstance - recv.attributes[p] = arguments[i] + write_attribute(p, recv, arguments[i]) i += 1 else abort end @@ -431,8 +519,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 @@ -479,19 +567,14 @@ private class NaiveInterpreter var cds = mtype.collect_mclassdefs(self.mainmodule).to_a self.mainmodule.linearize_mclassdefs(cds) for cd in cds do - var n = self.modelbuilder.mclassdef2nclassdef[cd] - for npropdef in n.n_propdefs do - if npropdef isa AAttrPropdef then - res.add(npropdef) - end - end + res.add_all(modelbuilder.collect_attr_propdef(cd)) end cache[mtype] = res 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. @@ -502,10 +585,13 @@ private class NaiveInterpreter end end - # This function determine the correct type according the reciever of the current definition (self). + # A hook to initialize a `PrimitiveInstance` + fun init_instance_primitive(recv: Instance) do end + + # This function determines the correct type according to the receiver of the current propdef (self). fun unanchor_type(mtype: MType): MType do - return mtype.anchor_to(self.mainmodule, self.frame.arguments.first.mtype.as(MClassType)) + return mtype.anchor_to(self.mainmodule, current_receiver_class) end # Placebo instance used to mark internal error result when `null` already have a meaning. @@ -540,7 +626,7 @@ abstract class Instance # The real value encapsulated if the instance is primitive. # Else aborts. - fun val: Object do abort + fun val: nullable Object do abort end # A instance with attribute (standards objects) @@ -553,18 +639,12 @@ end # Special instance to handle primitives values (int, bool, etc.) # The trick it just to encapsulate the <> value -class PrimitiveInstance[E: Object] +class PrimitiveInstance[E] super Instance # The real value encapsulated redef var val: E - init(mtype: MType, val: E) - do - super(mtype) - self.val = val - end - redef fun is_true do if val == true then return true @@ -574,17 +654,17 @@ class PrimitiveInstance[E: Object] redef fun ==(o) do - if not o isa PrimitiveInstance[Object] then return false + if not o isa PrimitiveInstance[nullable Object] then return false return self.val == o.val end redef fun eq_is(o) do - if not o isa PrimitiveInstance[Object] then return false + if not o isa PrimitiveInstance[nullable Object] then return false return self.val.is_same_instance(o.val) end - redef fun to_s do return "{mtype}#{val.object_id}({val})" + redef fun to_s do return "{mtype}#{val.object_id}({val or else "null"})" redef fun to_i do return val.as(Int) @@ -592,23 +672,31 @@ class PrimitiveInstance[E: Object] end # Information about local variables in a running method -private class Frame +abstract class Frame # The current visited node # The node is stored by frame to keep a stack trace var current_node: ANode # The executed property. # A Method in case of a call, an attribute in case of a default initialization. var mpropdef: MPropDef - # Arguments of the method (the first is te receiver + # Arguments of the method (the first is the receiver) var arguments: Array[Instance] - # Mapping betwen a variable an the current value - var map: Map[Variable, Instance] = new HashMap[Variable, 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] 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") @@ -635,7 +723,7 @@ redef class AMethPropdef 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 @@ -649,17 +737,12 @@ redef class AMethPropdef private fun call_commons(v: NaiveInterpreter, mpropdef: MMethodDef, arguments: Array[Instance], f: Frame): nullable Instance do + v.frames.unshift(f) + for i in [0..mpropdef.msignature.arity[ do var variable = self.n_signature.n_params[i].variable assert variable != null - f.map[variable] = arguments[i+1] - end - - v.frames.unshift(f) - - if mpropdef.is_abstract then - v.fatal("Abstract method `{mpropdef.mproperty.name}` called on `{arguments.first.mtype}`") - abort + v.write_variable(variable, arguments[i+1]) end # Call the implicit super-init @@ -678,7 +761,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 @@ -727,17 +810,23 @@ 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 + else if pname == "buffer_mode_full" then + return v.int_instance(sys.buffer_mode_full) + else if pname == "buffer_mode_line" then + return v.int_instance(sys.buffer_mode_line) + else if pname == "buffer_mode_none" then + return v.int_instance(sys.buffer_mode_none) else if pname == "sys" then return v.mainobj else if cname == "Int" then @@ -781,8 +870,17 @@ 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 == "int_to_s_len" then + return v.int_instance(recvval.to_s.length) else if pname == "native_int_to_s" then - return v.native_string_instance(recvval.to_s) + var s = recvval.to_s + var srecv = args[1].val.as(Buffer) + srecv.clear + srecv.append(s) + srecv.add('\0') + return null else if pname == "strerror_ext" then return v.native_string_instance(recvval.strerror) end @@ -857,9 +955,11 @@ redef class AMethPropdef return v.bool_instance(args[0].to_f.is_nan) else if pname == "is_inf_extern" then return v.bool_instance(args[0].to_f.is_inf != 0) + else if pname == "round" then + return v.float_instance(args[0].to_f.round) 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) @@ -885,13 +985,13 @@ redef class AMethPropdef if fromval < 0 then debug("Illegal access on {recvval} for element {fromval}/{recvval.length}") end - if fromval + lenval >= recvval.length then + if fromval + lenval > recvval.length then debug("Illegal access on {recvval} for element {fromval}+{lenval}/{recvval.length}") end if toval < 0 then debug("Illegal access on {destval} for element {toval}/{destval.length}") end - if toval + lenval >= destval.length then + if toval + lenval > destval.length then debug("Illegal access on {destval} for element {toval}+{lenval}/{destval.length}") end recvval.as(FlatBuffer).copy(fromval, lenval, destval, toval) @@ -917,12 +1017,22 @@ redef class AMethPropdef else if pname == "atof" then return v.float_instance(recvval.to_f) end + else if cname == "String" then + var cs = v.send(v.force_get_primitive_method("to_cstring", args.first.mtype), [args.first]) + var str = cs.val.to_s + 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.mainmodule.string_type) + end 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) + var instance = new PrimitiveInstance[Array[Instance]](args[0].mtype, val) + v.init_instance_primitive(instance) + return instance end var recvval = args.first.val.as(Array[Instance]) if pname == "[]" then @@ -936,46 +1046,57 @@ redef class AMethPropdef else if pname == "length" then return v.int_instance(recvval.length) else if pname == "copy_to" then - recvval.copy(0, args[2].to_i, args[1].val.as(Array[Instance]), 0) + recvval.copy_to(0, args[2].to_i, args[1].val.as(Array[Instance]), 0) return null end else if cname == "NativeFile" then if pname == "native_stdout" then - return new PrimitiveInstance[OStream](mpropdef.mclassdef.mclass.mclass_type, sys.stdout) + var inst = new PrimitiveNativeFile.native_stdout + var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst) + v.init_instance_primitive(instance) + return instance else if pname == "native_stdin" then - return new PrimitiveInstance[IStream](mpropdef.mclassdef.mclass.mclass_type, sys.stdin) + var inst = new PrimitiveNativeFile.native_stdin + var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst) + v.init_instance_primitive(instance) + return instance else if pname == "native_stderr" then - return new PrimitiveInstance[OStream](mpropdef.mclassdef.mclass.mclass_type, sys.stderr) + var inst = new PrimitiveNativeFile.native_stderr + var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst) + v.init_instance_primitive(instance) + return instance else if pname == "io_open_read" then var a1 = args[1].val.as(Buffer) - return new PrimitiveInstance[IStream](mpropdef.mclassdef.mclass.mclass_type, new IFStream.open(a1.to_s)) + var inst = new PrimitiveNativeFile.io_open_read(a1.to_s) + var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst) + v.init_instance_primitive(instance) + return instance else if pname == "io_open_write" then var a1 = args[1].val.as(Buffer) - return new PrimitiveInstance[OStream](mpropdef.mclassdef.mclass.mclass_type, new OFStream.open(a1.to_s)) + var inst = new PrimitiveNativeFile.io_open_write(a1.to_s) + var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst) + v.init_instance_primitive(instance) + return instance end var recvval = args.first.val if pname == "io_write" then var a1 = args[1].val.as(Buffer) - recvval.as(OStream).write(a1.substring(0, args[2].to_i).to_s) - return args[2] + return v.int_instance(recvval.as(PrimitiveNativeFile).io_write(a1.to_cstring, args[2].to_i)) else if pname == "io_read" then - var str = recvval.as(IStream).read(args[2].to_i) var a1 = args[1].val.as(Buffer) - new FlatBuffer.from(str).copy(0, str.length, a1.as(FlatBuffer), 0) - return v.int_instance(str.length) + var ns = new NativeString(a1.length) + var len = recvval.as(PrimitiveNativeFile).io_read(ns, args[2].to_i) + a1.clear + a1.append(ns.to_s_with_length(len)) + return v.int_instance(len) + else if pname == "flush" then + recvval.as(PrimitiveNativeFile).flush + return null else if pname == "io_close" then - recvval.as(IOS).close - return v.int_instance(0) - else if pname == "address_is_null" then - return v.false_instance + return v.int_instance(recvval.as(PrimitiveNativeFile).io_close) + 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.mainmodule.get_primitive_class("ArrayCapable")) - mtype = mtype.arguments.first - var val = new Array[Instance].filled_with(v.null_instance, args[1].to_i) - return new PrimitiveInstance[Array[Instance]](v.mainmodule.get_primitive_class("NativeArray").get_mtype([mtype]), val) else if pname == "native_argc" then return v.int_instance(v.arguments.length) else if pname == "native_argv" then @@ -988,6 +1109,9 @@ redef class AMethPropdef return v.native_string_instance(txt) else if pname == "get_time" then return v.int_instance(get_time) + else if pname == "srand" then + srand + return null else if pname == "srand_from" then srand_from(args[1].to_i) return null @@ -1008,19 +1132,16 @@ redef class AMethPropdef else if pname == "errno" then return v.int_instance(sys.errno) else if pname == "address_is_null" then + var recv = args[0] + if recv isa PrimitiveInstance[PrimitiveNativeFile] then + return v.bool_instance(recv.val.address_is_null) + end return v.false_instance end return v.error_instance end end -redef class AbstractArray[E] - fun copy(start: Int, len: Int, dest: AbstractArray[E], new_start: Int) - do - self.copy_to(start, len, dest, new_start) - end -end - redef class AAttrPropdef redef fun call(v, mpropdef, args) do @@ -1030,7 +1151,8 @@ redef class AAttrPropdef 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]) @@ -1044,27 +1166,42 @@ redef class AAttrPropdef private fun init_expr(v: NaiveInterpreter, recv: Instance) do if is_lazy then return - var nexpr = self.n_expr - if nexpr != null then - evaluate_expr(v, recv) + if has_value then + 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 nexpr = self.n_expr - assert nexpr != null - var f = new Frame(self, self.mpropdef.as(not null), [recv]) v.frames.unshift(f) - var val = v.expr(nexpr) + + var val + + var nexpr = self.n_expr + var nblock = self.n_block + if nexpr != null then + val = v.expr(nexpr) + else if nblock != null then + v.stmt(nblock) + assert v.returnmark == f + val = v.escapevalue + v.returnmark = null + v.escapevalue = null + else + abort + end assert val != null + v.frames.shift assert not v.is_escaping v.write_attribute(self.mpropdef.mproperty, recv, val) @@ -1077,35 +1214,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 @@ -1114,7 +1232,7 @@ redef class AExpr # Return a possible value # NOTE: Do not call this method directly, but use `v.expr` # This method is here to be implemented by subclasses. - private fun expr(v: NaiveInterpreter): nullable Instance + protected fun expr(v: NaiveInterpreter): nullable Instance do fatal(v, "NOT YET IMPLEMENTED expr {class_name}") abort @@ -1123,7 +1241,7 @@ redef class AExpr # Evaluate the node as a statement. # NOTE: Do not call this method directly, but use `v.stmt` # This method is here to be implemented by subclasses (no need to return something). - private fun stmt(v: NaiveInterpreter) + protected fun stmt(v: NaiveInterpreter) do expr(v) end @@ -1152,21 +1270,23 @@ redef class ABlockExpr 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 - v.frame.map[self.variable.as(not null)] = i + if i == null then return null + v.write_variable(self.variable.as(not null), i) + return i end + return null end end redef class AVarExpr redef fun expr(v) do - return v.frame.map[self.variable.as(not null)] + return v.read_variable(self.variable.as(not null)) end end @@ -1175,7 +1295,7 @@ redef class AVarAssignExpr do var i = v.expr(self.n_value) if i == null then return null - v.frame.map[self.variable.as(not null)] = i + v.write_variable(self.variable.as(not null), i) return i end end @@ -1183,12 +1303,13 @@ end redef class AVarReassignExpr redef fun stmt(v) do - var vari = v.frame.map[self.variable.as(not null)] + var variable = self.variable.as(not null) + var vari = v.read_variable(variable) var value = v.expr(self.n_value) if value == null then return var res = v.callsite(reassign_callsite, [vari, value]) assert res != null - v.frame.map[self.variable.as(not null)] = res + v.write_variable(variable, res) end end @@ -1199,20 +1320,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 @@ -1221,7 +1329,7 @@ redef class ABreakExpr if i == null then return v.escapevalue = i end - v.breakmark = self.escapemark + v.escapemark = self.escapemark end end @@ -1287,7 +1395,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 @@ -1299,8 +1407,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 @@ -1311,8 +1419,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 @@ -1330,25 +1438,42 @@ 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}") - v.frame.map[self.variables.first] = item + v.write_variable(self.variables.first, item) else if self.variables.length == 2 then var key = v.callsite(method_key, [iter]).as(not null) - v.frame.map[self.variables[0]] = key + v.write_variable(self.variables[0], key) var item = v.callsite(method_item, [iter]).as(not null) - v.frame.map[self.variables[1]] = item + v.write_variable(self.variables[1], item) else 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 + +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 @@ -1445,11 +1570,18 @@ redef class AArrayExpr redef fun expr(v) do var val = new Array[Instance] - for nexpr in self.n_exprs.n_exprs do - var i = v.expr(nexpr) - if i == null then return null - val.add(i) + var old_comprehension = v.frame.comprehension + v.frame.comprehension = val + for nexpr in self.n_exprs do + if nexpr isa AForExpr then + v.stmt(nexpr) + else + var i = v.expr(nexpr) + if i == null then return null + val.add(i) + end end + v.frame.comprehension = old_comprehension var mtype = v.unanchor_type(self.mtype.as(not null)).as(MClassType) var elttype = mtype.arguments.first return v.array_instance(val, elttype) @@ -1460,9 +1592,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 @@ -1475,7 +1605,7 @@ redef class ASuperstringExpr if i == null then return null array.add(i) end - var i = v.array_instance(array, v.mainmodule.get_primitive_class("Object").mclass_type) + var i = v.array_instance(array, v.mainmodule.object_type) var res = v.send(v.force_get_primitive_method("to_s", i.mtype), [i]) assert res != null return res @@ -1562,7 +1692,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 @@ -1596,12 +1725,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 @@ -1613,12 +1738,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 @@ -1638,16 +1759,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]) @@ -1658,14 +1775,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 @@ -1673,15 +1793,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}")