import semantize
private import parser::tables
import mixin
-import primitive_types
private import model::serialize_model
+private import frontend::explain_assert_api
redef class ToolContext
# --discover-call-trace
# The main Sys instance
var mainobj: nullable Instance is noinit
+ # Name of all supported functional names
+ var routine_types: Set[String] = new HashSet[String]
+
init
do
if mainmodule.model.get_mclasses_by_name("Bool") != null then
init_instance_primitive(self.false_instance)
end
self.null_instance = new PrimitiveInstance[nullable Object](mainmodule.model.null_type, null)
+
+ routine_types.add("RoutineRef")
+ for name in ["Proc", "Fun", "ProcRef", "FunRef"] do
+ # 20 is a magic number = upper limit of the arity of each functional class.
+ # i.e. Proc0, Proc1, ... Proc19
+ for i in [0..20[ do
+ routine_types.add("{name}{i}")
+ end
+ end
end
# Starts the interpreter on the main module of a program
do
var instance = c_string_instance_len(txt.byte_length+1)
var val = instance.val
- val[txt.byte_length] = 0u8
+ val[txt.byte_length] = 0
txt.to_cstring.copy_to(val, txt.byte_length, 0, 0)
return instance
return instance
end
+ # Return a new C string instance sharing the same data space as `txt`
+ fun c_string_instance_fast_cstr(txt: CString, from: Int): Instance
+ do
+ var ncstr = txt.fast_cstring(from)
+ var t = mainmodule.c_string_type
+
+ var instance = new PrimitiveInstance[CString](t, ncstr)
+ init_instance_primitive(instance)
+
+ return instance
+ end
+
# Return a new C string initialized of `length`
fun c_string_instance_len(length: Int): PrimitiveInstance[CString]
do
# Store known methods, used to trace methods as they are reached
var discover_call_trace: Set[MMethodDef] = new HashSet[MMethodDef]
+ # Consumes an iterator of expressions and tries to map each element to
+ # its corresponding Instance.
+ #
+ # If any AExprs doesn't resolve to an Instance, then it returns null.
+ # Otherwise return an array of instances
+ fun aexprs_to_instances(aexprs: Iterator[AExpr]): nullable Array[Instance]
+ do
+ var accumulator = new Array[Instance]
+ for aexpr in aexprs do
+ var instance = expr(aexpr)
+ if instance == null then return null
+ accumulator.push(instance)
+ end
+ return accumulator
+ 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.
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
+ var rest_args = aexprs_to_instances(args.iterator)
+ if rest_args == null then return null
+ res.append(rest_args)
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 exprs = aexprs_to_instances(args.iterator)
+ if exprs == null then return null
# Fill `res` with the result of the evaluation according to the mapping
for i in [0..msignature.arity[ do
# Abort if the instance is not a boolean value.
fun is_true: Bool do abort
+ # Return `true` if the instance is null.
+ # Return `false` otherwise.
+ fun is_null: Bool do return mtype isa MNullType
+
# Return true if `self` IS `o` (using the Nit semantic of is)
fun eq_is(o: Instance): Bool do return self.is_same_instance(o)
var attributes: Map[MAttribute, Instance] = new HashMap[MAttribute, Instance]
end
+# An instance with the original receiver and callsite (for function reference)
+class CallrefInstance
+ super Instance
+
+ # The original receiver
+ #
+ # ~~~nitish
+ # var a = new A
+ # var f = &a.toto # `a` is the original receiver
+ # ~~~
+ var recv: Instance
+
+ # The original callsite
+ #
+ # ~~~nitish
+ # var a = new A
+ # var f = &a.toto # `toto` is the original callsite
+ # ~~~
+ var callsite: CallSite
+end
+
# Special instance to handle primitives values (int, bool, etc.)
# The trick is just to encapsulate the “real” value.
class PrimitiveInstance[E]
do
var pname = mpropdef.mproperty.name
var cname = mpropdef.mclassdef.mclass.name
+
+ if pname == "call" and v.routine_types.has(cname) then
+ var routine = args.shift
+ assert routine isa CallrefInstance
+ # Swap the receiver position with the original recv of the call form.
+ args.unshift routine.recv
+ var res = v.callsite(routine.callsite, args)
+ # recover the old args state
+ args.shift
+ args.unshift routine
+ return res
+ end
+
if pname == "output" then
var recv = args.first
recv.val.output
var recvval = args.first.val.as(CString)
if pname == "[]" then
var arg1 = args[1].to_i
- return v.byte_instance(recvval[arg1])
+ return v.int_instance(recvval[arg1])
else if pname == "[]=" then
var arg1 = args[1].to_i
- recvval[arg1] = args[2].val.as(Byte)
+ recvval[arg1] = args[2].val.as(Int)
return null
else if pname == "copy_to" then
# sig= copy_to(dest: CString, length: Int, from: Int, to: Int)
else if pname == "atoi" then
return v.int_instance(recvval.atoi)
else if pname == "fast_cstring" then
- var ns = recvval.fast_cstring(args[1].to_i)
- return v.c_string_instance(ns.to_s)
+ return v.c_string_instance_fast_cstr(args[0].val.as(CString), args[1].to_i)
else if pname == "fetch_4_chars" then
return v.uint32_instance(args[0].val.as(CString).fetch_4_chars(args[1].to_i))
else if pname == "fetch_4_hchars" then
else if mpropdef == mwritepropdef then
assert args.length == 2
var arg = args[1]
- if is_optional and arg.mtype isa MNullType then
+ if is_optional and arg.is_null then
var f = v.new_frame(self, mpropdef, args)
arg = evaluate_expr(v, recv, f)
end
var i = v.expr(ne)
if i == null then return
v.escapevalue = i
+ else
+ v.escapevalue = null
end
v.escapemark = self.escapemark
end
for g in n_groups do
var col = v.expr(g.n_expr)
if col == null then return
- if col.mtype isa MNullType then fatal(v, "Receiver is null")
+ if col.is_null then fatal(v, "Receiver is null")
var iter = v.callsite(g.method_iterator, [col]).as(not null)
iters.add iter
if not cond.is_true then
v.stmt(self.n_else)
if v.is_escaping then return
+
+ # Explain assert if it fails
+ var explain_assert_str = explain_assert_str
+ if explain_assert_str != null then
+ var i = v.expr(explain_assert_str)
+ if i isa MutableInstance then
+ var res = v.send(v.force_get_primitive_method("to_cstring", i.mtype), [i])
+ if res != null then
+ var val = res.val
+ if val != null then
+ print_error "Runtime assert: {val.to_s}"
+ end
+ end
+ end
+ end
+
var nid = self.n_id
if nid != null then
fatal(v, "Assert '{nid.text}' failed")
redef class ACharExpr
redef fun expr(v)
do
- if is_ascii then return v.byte_instance(self.value.as(not null).ascii)
- if is_code_point then return v.int_instance(self.value.as(not null).code_point)
+ if is_code_point then
+ return v.int_instance(self.value.as(not null).code_point)
+ end
return v.char_instance(self.value.as(not null))
end
end
do
var i = v.expr(self.n_expr)
if i == null then return null
- if i.mtype isa MNullType then
+ if i.is_null then
fatal(v, "Cast failed")
end
return i
do
var recv = v.expr(self.n_expr)
if recv == null then return null
+
+ # Safe call shortcut if recv is null
+ if is_safe and recv.is_null then
+ return recv
+ end
+
var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
if args == null then return null
-
var res = v.callsite(callsite, args)
return res
end
end
+redef class ACallrefExpr
+ redef fun expr(v)
+ do
+ var recv = v.expr(self.n_expr)
+ if recv == null then return null
+ assert mtype != null
+ var inst = new CallrefInstance(mtype.as(not null), recv, callsite.as(not null))
+ return inst
+ end
+end
+
redef class ASendReassignFormExpr
redef fun stmt(v)
do
do
var recv = v.expr(self.n_expr)
if recv == null then return null
- if recv.mtype isa MNullType then fatal(v, "Receiver is null")
+ if recv.is_null then fatal(v, "Receiver is null")
var mproperty = self.mproperty.as(not null)
return v.read_attribute(mproperty, recv)
end
do
var recv = v.expr(self.n_expr)
if recv == null then return
- if recv.mtype isa MNullType then fatal(v, "Receiver is null")
+ if recv.is_null then fatal(v, "Receiver is null")
var i = v.expr(self.n_value)
if i == null then return
var mproperty = self.mproperty.as(not null)
do
var recv = v.expr(self.n_expr)
if recv == null then return
- if recv.mtype isa MNullType then fatal(v, "Receiver is null")
+ if recv.is_null then fatal(v, "Receiver is null")
var value = v.expr(self.n_value)
if value == null then return
var mproperty = self.mproperty.as(not null)
do
var recv = v.expr(self.n_expr)
if recv == null then return null
- if recv.mtype isa MNullType then fatal(v, "Receiver is null")
+ if recv.is_null then fatal(v, "Receiver is null")
var mproperty = self.mproperty.as(not null)
return v.bool_instance(v.isset_attribute(mproperty, recv))
end
end
end
+redef class ASafeExpr
+ redef fun expr(v)
+ do
+ return v.expr(self.n_expr)
+ end
+end
+
redef class ANamedargExpr
redef fun expr(v)
do