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
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
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
do
# 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 == true then
+ 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 i = v.expr(ne)
if i == null then return
v.escapevalue = i
+ else
+ v.escapevalue = null
end
v.escapemark = self.escapemark
end
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")