private import annotation
import mixin
import counter
+import pkgconfig
+private import explain_assert_api
# Add compiling options
redef class ToolContext
var opt_output = new OptionString("Filename of the generated executable", "-o", "--output")
# --dir
var opt_dir = new OptionString("Output directory", "--dir")
+ # --run
+ var opt_run = new OptionBool("Execute the binary after the compilation", "--run")
# --no-cc
var opt_no_cc = new OptionBool("Do not invoke the C compiler", "--no-cc")
# --no-main
var opt_no_main = new OptionBool("Do not generate main entry point", "--no-main")
+ # --shared-lib
+ var opt_shared_lib = new OptionBool("Compile to a native shared library", "--shared-lib")
# --make-flags
var opt_make_flags = new OptionString("Additional options to the `make` command", "--make-flags")
# --max-c-lines
var opt_release = new OptionBool("Compile in release mode and finalize application", "--release")
# -g
var opt_debug = new OptionBool("Compile in debug mode (no C-side optimization)", "-g", "--debug")
+ # --trace
+ var opt_trace = new OptionBool("Compile with lttng's instrumentation", "--trace")
redef init
do
super
- self.option_context.add_option(self.opt_output, self.opt_dir, self.opt_no_cc, self.opt_no_main, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening)
+ self.option_context.add_option(self.opt_output, self.opt_dir, self.opt_run, self.opt_no_cc, self.opt_no_main, self.opt_shared_lib, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening)
self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_attr_isset, self.opt_no_check_assert, self.opt_no_check_autocast, self.opt_no_check_null, self.opt_no_check_all)
self.option_context.add_option(self.opt_typing_test_metrics, self.opt_invocation_metrics, self.opt_isset_checks_metrics)
self.option_context.add_option(self.opt_no_stacktrace)
self.option_context.add_option(self.opt_release)
self.option_context.add_option(self.opt_max_c_lines, self.opt_group_c_files)
self.option_context.add_option(self.opt_debug)
+ self.option_context.add_option(self.opt_trace)
opt_no_main.hidden = true
+ opt_shared_lib.hidden = true
end
redef fun process_options(args)
var time1 = get_time
self.toolcontext.info("*** END WRITING C: {time1-time0} ***", 2)
+ if not toolcontext.check_errors then return
+
# Execute the Makefile
if self.toolcontext.opt_no_cc.value then return
sys.system("rm -r -- '{root_compile_dir.escape_to_sh}/'")
end
+ if toolcontext.opt_run.value then
+ var mainmodule = compiler.mainmodule
+ var out = outfile(mainmodule)
+ var cmd = ["."/out]
+ cmd.append toolcontext.option_context.rest
+ toolcontext.exec_and_check(cmd, "--run")
+ end
+
time1 = get_time
self.toolcontext.info("*** END COMPILING C: {time1-time0} ***", 2)
end
compiler.files_to_copy.add "{clib}/gc_chooser.c"
compiler.files_to_copy.add "{clib}/gc_chooser.h"
+ # Add lttng traces provider to external bodies
+ if toolcontext.opt_trace.value then
+ #-I. is there in order to make the TRACEPOINT_INCLUDE directive in clib/traces.h refer to the directory in which gcc was invoked.
+ var traces = new ExternCFile("traces.c", "-I.")
+ traces.pkgconfigs.add "lttng-ust"
+ compiler.extern_bodies.add(traces)
+ compiler.files_to_copy.add "{clib}/traces.c"
+ compiler.files_to_copy.add "{clib}/traces.h"
+ end
+
# FFI
for m in compiler.mainmodule.in_importation.greaters do
compiler.finalize_ffi_for_module(m)
end
var debug = toolcontext.opt_debug.value
- makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g{ if not debug then " -O2 " else " "}-Wno-unused-value -Wno-switch -Wno-attributes\nCINCL =\nLDFLAGS ?= \nLDLIBS ?= -lm {linker_options.join(" ")}\n\n")
+ makefile.write """
+ifeq ($(origin CC), default)
+ CC = ccache cc
+endif
+ifeq ($(origin CXX), default)
+ CXX = ccache c++
+endif
+CFLAGS ?= -g {{{if not debug then "-O2" else ""}}} -Wno-unused-value -Wno-switch -Wno-attributes -Wno-trigraphs
+CINCL =
+LDFLAGS ?=
+LDLIBS ?= -lm {{{linker_options.join(" ")}}}
+\n"""
+
+ if self.toolcontext.opt_trace.value then makefile.write "LDLIBS += -llttng-ust -ldl\n"
+
+ if toolcontext.opt_shared_lib.value then
+ makefile.write """
+CFLAGS += -fPIC
+LDFLAGS += -shared -Wl,-soname,{{{outname}}}
+"""
+ end
makefile.write "\n# SPECIAL CONFIGURATION FLAGS\n"
if platform.supports_libunwind then
if toolcontext.opt_no_stacktrace.value then
- makefile.write "NO_STACKTRACE=True"
+ makefile.write "NO_STACKTRACE ?= True"
else
- makefile.write "NO_STACKTRACE= # Set to `True` to enable"
+ makefile.write "NO_STACKTRACE ?= # Set to `True` to enable"
end
end
LDLIBS := $(filter-out -lrt,$(LDLIBS))
endif
+# Special configuration for Windows under mingw64
+ifneq ($(findstring MINGW64,$(uname_S)),)
+ # Use the pcreposix regex library
+ LDLIBS += -lpcreposix
+
+ # Remove POSIX flag -lrt
+ LDLIBS := $(filter-out -lrt,$(LDLIBS))
+
+ # Silence warnings when storing Int, Char and Bool as pointer address
+ CFLAGS += -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast
+endif
+
+# Add the compilation dir to the Java CLASSPATH
+ifeq ($(CLASSPATH),)
+ CLASSPATH := .
+else
+ CLASSPATH := $(CLASSPATH):.
+endif
+
"""
makefile.write("all: {outpath}\n")
f.close
end
- var java_files = new Array[ExternFile]
-
+ # pkg-config annotation support
var pkgconfigs = new Array[String]
for f in compiler.extern_bodies do
pkgconfigs.add_all f.pkgconfigs
end
- # Protect pkg-config
+
+ # Only test if pkg-config is used
if not pkgconfigs.is_empty then
+
+ # Check availability of pkg-config, silence the proc output
+ toolcontext.check_pkgconfig_packages pkgconfigs
+
+ # Double the check in the Makefile in case it's distributed
makefile.write """
# does pkg-config exists?
ifneq ($(shell which pkg-config >/dev/null; echo $$?), 0)
end
# Compile each required extern body into a specific .o
+ var java_files = new Array[ExternFile]
for f in compiler.extern_bodies do
var o = f.makefile_rule_name
- var ff = f.filename.basename
- makefile.write("{o}: {ff}\n")
+ makefile.write("{o}: {f.filename}\n")
makefile.write("\t{f.makefile_rule_content}\n\n")
dep_rules.add(f.makefile_rule_name)
end
makefile.write("{outpath}: {dep_rules.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath.escape_to_sh} {ofiles.join(" ")} $(LDLIBS) {pkg}\n\n")
# Clean
- makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n")
+ makefile.write("clean:\n\trm -f {ofiles.join(" ")} 2>/dev/null\n")
if outpath != real_outpath then
- makefile.write("\trm -- {outpath.escape_to_sh} 2>/dev/null\n")
+ makefile.write("\trm -f -- {outpath.escape_to_sh} 2>/dev/null\n")
end
makefile.close
self.toolcontext.info("Generated makefile: {makepath}", 2)
var res
if self.toolcontext.verbose_level >= 3 then
res = sys.system("{command} 2>&1")
+ else if is_windows then
+ res = sys.system("{command} 2>&1 >nul")
else
res = sys.system("{command} 2>&1 >/dev/null")
end
# The list of all associated files
# Used to generate .c files
- var files = new List[CodeFile]
+ var files = new Array[CodeFile]
# Initialize a visitor specific for a compiler engine
fun new_visitor: VISITOR is abstract
stream.write("const char* get_nit_name(register const char* procname, register unsigned int len);\n")
stream.close
- extern_bodies.add(new ExternCFile("{compile_dir}/c_functions_hash.c", ""))
+ extern_bodies.add(new ExternCFile("c_functions_hash.c", ""))
end
# Compile C headers
self.header.add_decl("#include <stdlib.h>")
self.header.add_decl("#include <stdio.h>")
self.header.add_decl("#include <string.h>")
+ # longjmp !
+ self.header.add_decl("#include <setjmp.h>\n")
self.header.add_decl("#include <sys/types.h>\n")
self.header.add_decl("#include <unistd.h>\n")
self.header.add_decl("#include <stdint.h>\n")
+ self.header.add_decl("#ifdef __linux__")
+ self.header.add_decl(" #include <endian.h>")
+ self.header.add_decl("#endif")
self.header.add_decl("#include <inttypes.h>\n")
self.header.add_decl("#include \"gc_chooser.h\"")
+ if modelbuilder.toolcontext.opt_trace.value then self.header.add_decl("#include \"traces.h\"")
+ self.header.add_decl("#ifdef __APPLE__")
+ self.header.add_decl(" #include <TargetConditionals.h>")
+ self.header.add_decl(" #include <syslog.h>")
+ self.header.add_decl(" #include <libkern/OSByteOrder.h>")
+ self.header.add_decl(" #define be32toh(x) OSSwapBigToHostInt32(x)")
+ self.header.add_decl("#endif")
+ self.header.add_decl("#ifdef _WIN32")
+ self.header.add_decl(" #define be32toh(val) _byteswap_ulong(val)")
+ self.header.add_decl("#endif")
self.header.add_decl("#ifdef ANDROID")
+ self.header.add_decl(" #ifndef be32toh")
+ self.header.add_decl(" #define be32toh(val) betoh32(val)")
+ self.header.add_decl(" #endif")
self.header.add_decl(" #include <android/log.h>")
self.header.add_decl(" #define PRINT_ERROR(...) (void)__android_log_print(ANDROID_LOG_WARN, \"Nit\", __VA_ARGS__)")
+ self.header.add_decl("#elif TARGET_OS_IPHONE")
+ self.header.add_decl(" #define PRINT_ERROR(...) syslog(LOG_ERR, __VA_ARGS__)")
self.header.add_decl("#else")
self.header.add_decl(" #define PRINT_ERROR(...) fprintf(stderr, __VA_ARGS__)")
self.header.add_decl("#endif")
compile_header_structs
compile_nitni_structs
+ compile_catch_stack
var gccd_disable = modelbuilder.toolcontext.opt_no_gcc_directive.value
if gccd_disable.has("noreturn") or gccd_disable.has("all") then
self.header.add_decl("extern val *glob_sys;")
end
+ # Stack stocking environment for longjumps
+ protected fun compile_catch_stack do
+ self.header.add_decl """
+struct catch_stack_t {
+ int cursor;
+ int currentSize;
+ jmp_buf *envs;
+};
+extern struct catch_stack_t *getCatchStack();
+"""
+ end
+
# Declaration of structures for live Nit types
protected fun compile_header_structs is abstract
v.add "\}"
end
+ # Hook to add specif piece of code before the the main C function.
+ #
+ # Is called by `compile_main_function`
+ fun compile_before_main(v: VISITOR)
+ do
+ end
+
+ # Hook to add specif piece of code at the begin on the main C function.
+ #
+ # Is called by `compile_main_function`
+ fun compile_begin_main(v: VISITOR)
+ do
+ end
+
# Generate the main C function.
#
# This function:
v.add_decl("char **glob_argv;")
v.add_decl("val *glob_sys;")
+ # Store catch stack in thread local storage
+ v.add_decl """
+#if defined(TARGET_OS_IPHONE)
+ // Use pthread_key_create and others for iOS
+ #include <pthread.h>
+
+ static pthread_key_t catch_stack_key;
+ static pthread_once_t catch_stack_key_created = PTHREAD_ONCE_INIT;
+
+ static void create_catch_stack()
+ {
+ pthread_key_create(&catch_stack_key, NULL);
+ }
+
+ struct catch_stack_t *getCatchStack()
+ {
+ pthread_once(&catch_stack_key_created, &create_catch_stack);
+ struct catch_stack_t *data = pthread_getspecific(catch_stack_key);
+ if (data == NULL) {
+ data = malloc(sizeof(struct catch_stack_t));
+ data->cursor = -1;
+ data->currentSize = 0;
+ data->envs = NULL;
+ pthread_setspecific(catch_stack_key, data);
+ }
+ return data;
+ }
+#else
+ // Use __thread when available
+ __thread struct catch_stack_t catchStack = {-1, 0, NULL};
+
+ struct catch_stack_t *getCatchStack()
+ {
+ return &catchStack;
+ }
+#endif
+"""
+
if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
for tag in count_type_test_tags do
v.add_decl("long count_type_test_resolved_{tag};")
v.add_decl("\}")
v.add_decl("void sig_handler(int signo)\{")
+ v.add_decl "#ifdef _WIN32"
+ v.add_decl "PRINT_ERROR(\"Caught signal : %s\\n\", signo);"
+ v.add_decl "#else"
v.add_decl("PRINT_ERROR(\"Caught signal : %s\\n\", strsignal(signo));")
+ v.add_decl "#endif"
v.add_decl("show_backtrace();")
# rethrows
v.add_decl("signal(signo, SIG_DFL);")
+ v.add_decl "#ifndef _WIN32"
v.add_decl("kill(getpid(), signo);")
+ v.add_decl "#endif"
v.add_decl("\}")
v.add_decl("void fatal_exit(int status) \{")
v.add_decl("exit(status);")
v.add_decl("\}")
+ compile_before_main(v)
+
if no_main then
v.add_decl("int nit_main(int argc, char** argv) \{")
else
v.add_decl("int main(int argc, char** argv) \{")
end
- v.add "#ifndef ANDROID"
+ compile_begin_main(v)
+
+ v.add "#if !defined(__ANDROID__) && !defined(TARGET_OS_IPHONE)"
v.add("signal(SIGABRT, sig_handler);")
v.add("signal(SIGFPE, sig_handler);")
v.add("signal(SIGILL, sig_handler);")
v.add("signal(SIGTERM, sig_handler);")
v.add("signal(SIGSEGV, sig_handler);")
v.add "#endif"
+ v.add "#ifndef _WIN32"
v.add("signal(SIGPIPE, SIG_IGN);")
+ v.add "#endif"
v.add("glob_argc = argc; glob_argv = argv;")
v.add("initialize_gc_option();")
fun finalize_ffi_for_module(mmodule: MModule) do mmodule.finalize_ffi(self)
end
-# A file unit (may be more than one file if
-# A file unit aim to be autonomous and is made or one or more `CodeWriter`s
+# C code file generated from the `writers` and the `required_declarations`
+#
+# A file unit aims to be autonomous and is made or one or more `CodeWriter`s.
class CodeFile
+ # Basename of the file, will be appended by `.h` and `.c`
var name: String
+
+ # `CodeWriter` used in sequence to fill the top of the body, then the bottom
var writers = new Array[CodeWriter]
+
+ # Required declarations keys
+ #
+ # See: `provide_declaration`
var required_declarations = new HashSet[String]
end
-# Where to store generated lines
+# Store generated lines
+#
+# Instances are added to `file.writers` at construction.
class CodeWriter
+ # Parent `CodeFile`
var file: CodeFile
- var lines: List[String] = new List[String]
- var decl_lines: List[String] = new List[String]
- # Add a line in the main part of the generated C
+ # Main lines of code (written at the bottom of the body)
+ var lines = new Array[String]
+
+ # Lines of code for declarations (written at the top of the body)
+ var decl_lines = new Array[String]
+
+ # Add a line in the main lines of code (to `lines`)
fun add(s: String) do self.lines.add(s)
- # Add a line in the
- # (used for local or global declaration)
+ # Add a declaration line (to `decl_lines`)
+ #
+ # Used for local and global declaration.
fun add_decl(s: String) do self.decl_lines.add(s)
init
fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable is abstract
- fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) is abstract
-
- fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
+ fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]): Bool do return false
# Return an element of a native array.
# The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
# The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
fun native_array_set(native_array: RuntimeVariable, index: Int, value: RuntimeVariable) is abstract
+ # Allocate `size` bytes with the low_level `nit_alloc` C function
+ #
+ # This method can be redefined to inject statistic or tracing code.
+ #
+ # `tag` if any, is used to mark the class of the allocated object.
+ fun nit_alloc(size: String, tag: nullable String): String
+ do
+ return "nit_alloc({size})"
+ 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 runtime variables to use in the call.
do
mtype = self.anchor(mtype)
var valmtype = value.mcasttype
+
+ # CPrimitive is the best you can do
+ if valmtype.is_c_primitive then
+ return value
+ end
+
+ # Do nothing if useless autocast
if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
return value
end
+ # Just as_not_null if the target is not nullable.
+ #
+ # eg `nullable PreciseType` adapted to `Object` gives precisetype.
if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
- var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
- return res
- else
- var res = new RuntimeVariable(value.name, valmtype, mtype)
- return res
+ mtype = valmtype.mtype
end
+
+ var res = new RuntimeVariable(value.name, value.mtype, mtype)
+ return res
end
# Generate a super call from a method definition
# Checks
+ # Can value be null? (according to current knowledge)
+ fun maybe_null(value: RuntimeVariable): Bool
+ do
+ return value.mcasttype isa MNullableType or value.mcasttype isa MNullType
+ end
+
# Add a check and an abort for a null receiver if needed
fun check_recv_notnull(recv: RuntimeVariable)
do
if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
- var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
- if maybenull then
+ if maybe_null(recv) then
self.add("if (unlikely({recv} == NULL)) \{")
self.add_abort("Receiver is null")
self.add("\}")
end
# Return a "const char*" variable associated to the classname of the dynamic type of an object
- # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
+ # NOTE: we do not return a `RuntimeVariable` "CString" as the class may not exist in the module/program
fun class_name_string(value: RuntimeVariable): String is abstract
# Variables handling
fun int8_instance(value: Int8): RuntimeVariable
do
var t = mmodule.int8_type
- var res = new RuntimeVariable("((int8_t){value.to_s})", t, t)
+ var res = new RuntimeVariable("INT8_C({value.to_s})", t, t)
return res
end
fun int16_instance(value: Int16): RuntimeVariable
do
var t = mmodule.int16_type
- var res = new RuntimeVariable("((int16_t){value.to_s})", t, t)
+ var res = new RuntimeVariable("INT16_C({value.to_s})", t, t)
return res
end
fun uint16_instance(value: UInt16): RuntimeVariable
do
var t = mmodule.uint16_type
- var res = new RuntimeVariable("((uint16_t){value.to_s})", t, t)
+ var res = new RuntimeVariable("UINT16_C({value.to_s})", t, t)
return res
end
fun int32_instance(value: Int32): RuntimeVariable
do
var t = mmodule.int32_type
- var res = new RuntimeVariable("((int32_t){value.to_s})", t, t)
+ var res = new RuntimeVariable("INT32_C({value.to_s})", t, t)
return res
end
fun uint32_instance(value: UInt32): RuntimeVariable
do
var t = mmodule.uint32_type
- var res = new RuntimeVariable("((uint32_t){value.to_s})", t, t)
+ var res = new RuntimeVariable("UINT32_C({value.to_s})", t, t)
return res
end
return res
end
+ # Generates a CString instance fully escaped in C-style \xHH fashion
+ fun c_string_instance(ns: CString, len: Int): RuntimeVariable do
+ var mtype = mmodule.c_string_type
+ var nat = new_var(mtype)
+ var byte_esc = new Buffer.with_cap(len * 4)
+ for i in [0 .. len[ do
+ byte_esc.append("\\x{ns[i].to_hex}")
+ end
+ self.add("{nat} = \"{byte_esc}\";")
+ return nat
+ end
+
# Generate a string value
fun string_instance(string: String): RuntimeVariable
do
self.add("if (likely({name}!=NULL)) \{")
self.add("{res} = {name};")
self.add("\} else \{")
- var native_mtype = mmodule.native_string_type
+ var native_mtype = mmodule.c_string_type
var nat = self.new_var(native_mtype)
self.add("{nat} = \"{string.escape_to_c}\";")
- var bytelen = self.int_instance(string.bytelen)
+ var byte_length = self.int_instance(string.byte_length)
var unilen = self.int_instance(string.length)
- self.add("{res} = {self.send(self.get_property("to_s_full", native_mtype), [nat, bytelen, unilen]).as(not null)};")
+ self.add("{res} = {self.send(self.get_property("to_s_unsafe", native_mtype), [nat, byte_length, unilen, value_instance(false), value_instance(false)]).as(not null)};")
self.add("{name} = {res};")
self.add("\}")
return res
# This is used for the legacy FFI
fun add_extern(mmodule: MModule)
do
- var file = mmodule.location.file.filename
+ var file = mmodule.filepath
file = file.strip_extension(".nit")
var tryfile = file + ".nit.h"
if tryfile.file_exists then
# used by aborts, asserts, casts, etc.
fun add_abort(message: String)
do
+ add_raw_throw
self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
add_raw_abort
end
+ # Generate a long jump if there is a catch block.
+ #
+ # This method should be called before the error messages and before a `add_raw_abort`.
+ fun add_raw_throw
+ do
+ self.add("\{")
+ self.add("struct catch_stack_t *catchStack = getCatchStack();")
+ self.add("if(catchStack->cursor >= 0)\{")
+ self.add(" longjmp(catchStack->envs[catchStack->cursor], 1);")
+ self.add("\}")
+ self.add("\}")
+ end
+
+ # Generate abort without a message.
+ #
+ # Used when one need a more complex message.
+ # Do not forget to call `add_raw_abort` before the display of a custom user message.
fun add_raw_abort
do
- if self.current_node != null and self.current_node.location.file != null and
- self.current_node.location.file.mmodule != null then
+ var current_node = self.current_node
+ if current_node != null and current_node.location.file != null and
+ current_node.location.file.mmodule != null then
var f = "FILE_{self.current_node.location.file.mmodule.c_name}"
self.require_declaration(f)
self.add("PRINT_ERROR(\" (%s:%d)\\n\", {f}, {current_node.location.line_start});")
do
var res = self.type_test(value, mtype, tag)
self.add("if (unlikely(!{res})) \{")
+ self.add_raw_throw
var cn = self.class_name_string(value)
self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
self.add_raw_abort
return "int32_t"
else if mclass.name == "UInt32" then
return "uint32_t"
- else if mclass.name == "NativeString" then
+ else if mclass.name == "CString" then
return "char*"
else if mclass.name == "NativeArray" then
return "val*"
return "i32"
else if mclass.name == "UInt32" then
return "u32"
- else if mclass.name == "NativeString" then
+ else if mclass.name == "CString" then
return "str"
else if mclass.name == "NativeArray" then
#return "{self.arguments.first.ctype}*"
else if node isa AClassdef then
# Automatic free init is always inlined since it is empty or contains only attribtes assigments
return true
+ else if node == null then
+ return true
else
abort
end
var node = modelbuilder.mpropdef2node(self)
if is_abstract then
+ v.add_raw_throw
var cn = v.class_name_string(arguments.first)
v.current_node = node
v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
var msignature = self.msignature.as(not null)
for i in [0..msignature.arity[ do
+ var mp = msignature.mparameters[i]
# skip test for vararg since the array is instantiated with the correct polymorphic type
- if msignature.vararg_rank == i then continue
+ if mp.is_vararg then continue
# skip if the cast is not required
var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
if not origmtype.need_anchor then continue
# get the parameter type
- var mtype = msignature.mparameters[i].mtype
+ var mtype = mp.mtype
# generate the cast
# note that v decides if and how to implements the cast
- v.add("/* Covariant cast for argument {i} ({msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
+ v.add("/* Covariant cast for argument {i} ({mp.name}) {arguments[i+1].inspect} isa {mtype} */")
v.add_cast(arguments[i+1], mtype, "covariance")
end
end
# Try special compilation
if mpropdef.is_intern then
if compile_intern_to_c(v, mpropdef, arguments) then return
- else if mpropdef.is_extern then
+ end
+ if mpropdef.is_extern then
if mpropdef.mproperty.is_init then
if compile_externinit_to_c(v, mpropdef, arguments) then return
else
end
# We have a problem
+ v.add_raw_throw
var cn = v.class_name_string(arguments.first)
v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
v.add_raw_abort
v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
return true
end
- else if cname == "NativeString" then
+ else if cname == "CString" then
if pname == "[]" then
v.ret(v.new_expr("(unsigned char)((int){arguments[0]}[{arguments[1]}])", ret.as(not null)))
return true
else if pname == "fast_cstring" then
v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
return true
+ else if pname == "==" then
+ v.ret(v.equal_test(arguments[0], arguments[1]))
+ return true
+ else if pname == "!=" then
+ var res = v.equal_test(arguments[0], arguments[1])
+ v.ret(v.new_expr("!{res}", ret.as(not null)))
+ return true
else if pname == "new" then
- v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
+ var alloc = v.nit_alloc(arguments[1].to_s, "CString")
+ v.ret(v.new_expr("(char*){alloc}", ret.as(not null)))
+ return true
+ else if pname == "fetch_4_chars" then
+ v.ret(v.new_expr("*((uint32_t*)({arguments[0]} + {arguments[1]}))", ret.as(not null)))
+ return true
+ else if pname == "fetch_4_hchars" then
+ v.ret(v.new_expr("(uint32_t)be32toh(*((uint32_t*)({arguments[0]} + {arguments[1]})))", ret.as(not null)))
return true
end
else if cname == "NativeArray" then
- v.native_array_def(pname, ret, arguments)
- return true
+ return v.native_array_def(pname, ret, arguments)
else if cname == "Int8" then
if pname == "output" then
v.add("printf(\"%\"PRIi8 \"\\n\", {arguments.first});")
end
end
if pname == "exit" then
- v.add("exit({arguments[1]});")
+ v.add("exit((int){arguments[1]});")
return true
else if pname == "sys" then
v.ret(v.new_expr("glob_sys", ret.as(not null)))
return true
- else if pname == "calloc_string" then
- v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
- return true
- else if pname == "calloc_array" then
- v.calloc_array(ret.as(not null), arguments)
- return true
else if pname == "object_id" then
v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
return true
v.assign(v.frame.returnvar.as(not null), res)
else if mpropdef == mwritepropdef then
assert arguments.length == 2
- v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
+ var recv = arguments.first
+ var arg = arguments[1]
+ if is_optional and v.maybe_null(arg) then
+ var value = v.new_var(self.mpropdef.static_mtype.as(not null))
+ v.add("if ({arg} == NULL) \{")
+ v.assign(value, evaluate_expr(v, recv))
+ v.add("\} else \{")
+ v.assign(value, arg)
+ v.add("\}")
+ arg = value
+ end
+ v.write_attribute(self.mpropdef.mproperty, arguments.first, arg)
if is_lazy then
var ret = self.mtype
var useiset = not ret.is_c_primitive and not ret isa MNullableType
fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
do
- if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
+ if has_value and not is_lazy and not is_optional and not n_expr isa ANullExpr then evaluate_expr(v, recv)
end
# Evaluate, store and return the default value of the attribute
redef class AClassdef
private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
do
- if mpropdef == self.mfree_init then
- assert mpropdef.mproperty.is_root_init
+ if mpropdef.mproperty.is_root_init then
assert arguments.length == 1
if not mpropdef.is_intro then
v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
redef class ADoExpr
redef fun stmt(v)
do
- v.stmt(self.n_block)
- v.add_escape_label(break_mark)
+ if self.n_catch != null then
+ v.add("\{")
+ v.add("struct catch_stack_t *catchStack = getCatchStack();")
+ v.add("if(catchStack->currentSize == 0) \{")
+ v.add(" catchStack->cursor = -1;")
+ v.add(" catchStack->currentSize = 100;")
+ v.add(" catchStack->envs = malloc(sizeof(jmp_buf)*100);")
+ v.add("\} else if(catchStack->cursor == catchStack->currentSize - 1) \{")
+ v.add(" catchStack->currentSize *= 2;")
+ v.add(" catchStack->envs = realloc(catchStack->envs, sizeof(jmp_buf)*catchStack->currentSize);")
+ v.add("\}")
+ v.add("catchStack->cursor += 1;")
+ v.add("if(!setjmp(catchStack->envs[catchStack->cursor]))\{")
+ v.stmt(self.n_block)
+ v.add("catchStack->cursor -= 1;")
+ v.add("\}else \{")
+ v.add("catchStack->cursor -= 1;")
+ v.stmt(self.n_catch)
+ v.add("\}")
+ v.add("\}")
+ else
+ v.stmt(self.n_block)
+ end
+ v.add_escape_label(break_mark)
end
end
var cond = v.expr_bool(self.n_expr)
v.add("if (unlikely(!{cond})) \{")
v.stmt(self.n_else)
+
+ explain_assert v
+
var nid = self.n_id
if nid != null then
v.add_abort("Assert '{nid.text}' failed")
end
v.add("\}")
end
+
+ # Explain assert if it fails
+ private fun explain_assert(v: AbstractCompilerVisitor)
+ do
+ var explain_assert_str = explain_assert_str
+ if explain_assert_str == null then return
+
+ var nas = v.compiler.modelbuilder.model.get_mclasses_by_name("NativeArray")
+ if nas == null then return
+
+ nas = v.compiler.modelbuilder.model.get_mclasses_by_name("Array")
+ if nas == null or nas.is_empty then return
+
+ var expr = explain_assert_str.expr(v)
+ if expr == null then return
+
+ var cstr = v.send(v.get_property("to_cstring", expr.mtype), [expr])
+ if cstr == null then return
+
+ v.add "PRINT_ERROR(\"Runtime assert: %s\\n\", {cstr});"
+ end
end
redef class AOrExpr
do
var res = v.new_var(self.mtype.as(not null))
var i1 = v.expr(self.n_expr, null)
+
+ if not v.maybe_null(i1) then return i1
+
v.add("if ({i1}!=NULL) \{")
v.assign(res, i1)
v.add("\} else \{")
end
redef class ACharExpr
- redef fun expr(v) do return v.char_instance(self.value.as(not null))
+ redef fun expr(v) do
+ if is_code_point then
+ return v.int_instance(value.as(not null).code_point)
+ end
+ return v.char_instance(self.value.as(not null))
+ end
end
redef class AArrayExpr
end
end
+redef class AugmentedStringFormExpr
+ # Factorize the making of a `Regex` object from a literal prefixed string
+ protected fun make_re(v: AbstractCompilerVisitor, rs: RuntimeVariable): nullable RuntimeVariable do
+ var re = to_re
+ assert re != null
+ var res = v.compile_callsite(re, [rs])
+ if res == null then
+ print "Cannot call property `to_re` on {self}"
+ abort
+ end
+ for i in suffix.chars do
+ if i == 'i' then
+ var ign = ignore_case
+ assert ign != null
+ v.compile_callsite(ign, [res, v.bool_instance(true)])
+ continue
+ end
+ if i == 'm' then
+ var nl = newline
+ assert nl != null
+ v.compile_callsite(nl, [res, v.bool_instance(true)])
+ continue
+ end
+ if i == 'b' then
+ var ext = extended
+ assert ext != null
+ v.compile_callsite(ext, [res, v.bool_instance(false)])
+ continue
+ end
+ # Should not happen, this needs to be updated
+ # along with the addition of new suffixes
+ abort
+ end
+ return res
+ end
+end
+
redef class AStringFormExpr
- redef fun expr(v) do return v.string_instance(self.value.as(not null))
+ redef fun expr(v) do return v.string_instance(value)
+end
+
+redef class AStringExpr
+ redef fun expr(v) do
+ var s = v.string_instance(value)
+ if is_string then return s
+ if is_bytestring then
+ var ns = v.c_string_instance(bytes.items, bytes.length)
+ var ln = v.int_instance(bytes.length)
+ var cs = to_bytes_with_copy
+ assert cs != null
+ var res = v.compile_callsite(cs, [ns, ln])
+ assert res != null
+ s = res
+ else if is_re then
+ var res = make_re(v, s)
+ assert res != null
+ s = res
+ else
+ print "Unimplemented prefix or suffix for {self}"
+ abort
+ end
+ return s
+ end
end
redef class ASuperstringExpr
redef fun expr(v)
do
- var type_string = mtype.as(not null)
+ var type_string = v.mmodule.string_type
# Collect elements of the superstring
var array = new Array[AExpr]
v.native_array_set(a, i, e)
end
- # Fast join the native string to get the result
+ # Fast join the C string to get the result
var res = v.send(v.get_property("native_to_s", a.mtype), [a])
+ assert res != null
+
+ if is_re then res = make_re(v, res)
# We finish to work with the native array,
# so store it so that it can be reused
v.add("{varonce} = {a};")
+
return res
end
end
var i = v.expr(self.n_expr, null)
if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
- if i.mtype.is_c_primitive then return i
+ if not v.maybe_null(i) then return i
v.add("if (unlikely({i} == NULL)) \{")
v.add_abort("Cast failed")
return v.native_array_instance(elttype, l)
end
- var recv = v.init_instance_or_extern(mtype)
-
var callsite = self.callsite
- if callsite == null then return recv
+ if callsite == null then return v.init_instance_or_extern(mtype)
if callsite.is_broken then return null
+ var recv
+ # new factories are badly implemented.
+ # They assume a stub temporary receiver exists.
+ # This temporary receiver is required because it
+ # currently holds the method and the formal types.
+ #
+ # However, this object could be reused if the formal types are the same.
+ # Therefore, the following code will `once` it in these case
+ if callsite.mproperty.is_new and not mtype.need_anchor then
+ var name = v.get_name("varoncenew")
+ var guard = v.get_name(name + "_guard")
+ v.add_decl("static {mtype.ctype} {name};")
+ v.add_decl("static int {guard};")
+ recv = v.new_var(mtype)
+ v.add("if (likely({guard})) \{")
+ v.add("{recv} = {name};")
+ v.add("\} else \{")
+ var i = v.init_instance_or_extern(mtype)
+ v.add("{recv} = {i};")
+ v.add("{name} = {recv};")
+ v.add("{guard} = 1;")
+ v.add("\}")
+ else
+ recv = v.init_instance_or_extern(mtype)
+ end
+
var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
var res2 = v.compile_callsite(callsite, args)
if res2 != null then
var modelbuilder = new ModelBuilder(model, toolcontext)
var arguments = toolcontext.option_context.rest
+if toolcontext.opt_run.value then
+ # When --run, only the first is the program, the rest is the run arguments
+ arguments = [toolcontext.option_context.rest.shift]
+end
if arguments.length > 1 and toolcontext.opt_output.value != null then
print "Option Error: --output needs a single source file. Do you prefer --dir?"
exit 1