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
var modelbuilder: ModelBuilder
# The main module of the program (used to lookup method)
- var mainmodule: MModule
+ var mainmodule: MModule is writable
# The command line arguments of the interpreted program
# arguments.first is the program name
var escapemark: nullable EscapeMark = null
# The count of `catch` blocs that have been encountered and can catch an abort
- var catch_count = 0
+ var catch_count = 0 is writable
+
+ # The last error thrown on abort/runtime error where catch_count > 0
+ var last_error: nullable FatalError = null
# Is a return or a break or a continue executed?
# Use this function to know if you must skip the evaluation of statements
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
fun string_instance(txt: String): Instance
do
var nat = c_string_instance(txt)
- var res = self.send(self.force_get_primitive_method("to_s_full", nat.mtype), [nat, self.int_instance(txt.byte_length), self.int_instance(txt.length)])
+ var res = self.send(self.force_get_primitive_method("to_s_unsafe", nat.mtype), [nat, self.int_instance(txt.byte_length), self.int_instance(txt.length), self.false_instance, self.false_instance])
assert res != null
return res
end
var error_instance = new MutableInstance(modelbuilder.model.null_type) is lazy
end
+# A runtime error
+class FatalError
+ # The error message
+ var message: String
+
+ # The problematic node, if any
+ var node: nullable ANode
+end
+
# An instance represents a value of the executed program.
abstract class Instance
# The dynamic type of the instance
# ASSERT: not self.mtype.is_anchored
var mtype: MType
- # return true if the instance is the true value.
- # return false if the instance is the true value.
- # else aborts
+ # Return `true` if the instance is the `true` value.
+ #
+ # Return `false` if the instance is the `false` value.
+ # 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)
end
# Special instance to handle primitives values (int, bool, etc.)
-# The trick it just to encapsulate the <<real>> value
+# The trick is just to encapsulate the “real” value.
class PrimitiveInstance[E]
super Instance
super Frame
# Mapping between a variable and the current value
- private var map: Map[Variable, Instance] = new HashMap[Variable, Instance]
+ var map: Map[Variable, Instance] = new HashMap[Variable, Instance]
end
redef class ANode
# `v` is used to know if a colored message is displayed or not
fun fatal(v: NaiveInterpreter, message: String)
do
- if v.modelbuilder.toolcontext.opt_no_color.value == true then
+ # Abort if there is a `catch` block
+ if v.catch_count > 0 then
+ v.last_error = new FatalError(message, self)
+ abort
+ end
+
+ if v.modelbuilder.toolcontext.opt_no_color.value then
sys.stderr.write("Runtime error: {message} ({location.file.filename}:{location.line_start})\n")
else
sys.stderr.write("{location}: Runtime error: {message}\n{location.colored_line("0;31")}\n")
return res
end
- private fun call_commons(v: NaiveInterpreter, mpropdef: MMethodDef, arguments: Array[Instance], f: Frame): nullable Instance
+ # Execution of the body of the method
+ #
+ # It handle the common special cases: super, intern, extern
+ fun call_commons(v: NaiveInterpreter, mpropdef: MMethodDef, arguments: Array[Instance], f: Frame): nullable Instance
do
v.frames.unshift(f)
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.int_instance(args[0].val.as(CString).fetch_4_chars(args[1].to_i))
+ return v.uint32_instance(args[0].val.as(CString).fetch_4_chars(args[1].to_i))
else if pname == "fetch_4_hchars" then
- return v.int_instance(args[0].val.as(CString).fetch_4_hchars(args[1].to_i))
+ return v.uint32_instance(args[0].val.as(CString).fetch_4_hchars(args[1].to_i))
else if pname == "utf8_length" then
return v.int_instance(args[0].val.as(CString).utf8_length(args[1].to_i, args[2].to_i))
end
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
redef class AAbortExpr
redef fun stmt(v)
do
- # Abort as asked if there is no `catch` bloc
- if v.catch_count <= 0 then
- fatal(v, "Aborted")
- exit(1)
- else
- abort
- end
+ fatal(v, "Aborted")
+ exit(1)
end
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
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