Merge: Portable stack-traces
[nit.git] / src / compiler / abstract_compiler.nit
index 7e17187..a095201 100644 (file)
@@ -104,15 +104,12 @@ redef class ToolContext
 end
 
 redef class ModelBuilder
-       # The compilation directory
-       var compile_dir: String
-
        # Simple indirection to `Toolchain::write_and_make`
        protected fun write_and_make(compiler: AbstractCompiler)
        do
                var platform = compiler.target_platform
                var toolchain = platform.toolchain(toolcontext, compiler)
-               compile_dir = toolchain.compile_dir
+               compiler.toolchain = toolchain
                toolchain.write_and_make
        end
 end
@@ -134,14 +131,21 @@ class Toolchain
        # Compiler of the target program
        var compiler: AbstractCompiler
 
-       # Directory where to generate all C files
-       fun compile_dir: String
+       # Directory where to generate all files
+       #
+       # The option `--compile_dir` change this directory.
+       fun root_compile_dir: String
        do
                var compile_dir = toolcontext.opt_compile_dir.value
-               if compile_dir == null then compile_dir = ".nit_compile"
+               if compile_dir == null then compile_dir = "nit_compile"
                return compile_dir
        end
 
+       # Directory where to generate all C files
+       #
+       # By default it is `root_compile_dir` but some platform may require that it is a subdirectory.
+       fun compile_dir: String do return root_compile_dir
+
        # Write all C files and compile them
        fun write_and_make is abstract
 end
@@ -154,12 +158,16 @@ class MakefileToolchain
        do
                var compile_dir = compile_dir
 
+               # Remove the compilation directory unless explicitly set
+               var auto_remove = toolcontext.opt_compile_dir.value == null
+
                # Generate the .h and .c files
                # A single C file regroups many compiled rumtime functions
                # Note that we do not try to be clever an a small change in a Nit source file may change the content of all the generated .c files
                var time0 = get_time
                self.toolcontext.info("*** WRITING C ***", 1)
 
+               root_compile_dir.mkdir
                compile_dir.mkdir
 
                var cfiles = new Array[String]
@@ -181,6 +189,10 @@ class MakefileToolchain
 
                compile_c_code(compile_dir)
 
+               if auto_remove then
+                       sys.system("rm -r -- '{root_compile_dir.escape_to_sh}/'")
+               end
+
                time1 = get_time
                self.toolcontext.info("*** END COMPILING C: {time1-time0} ***", 2)
        end
@@ -316,7 +328,7 @@ class MakefileToolchain
                var outpath = real_outpath.escape_to_mk
                if outpath != real_outpath then
                        # If the name is crazy and need escaping, we will do an indirection
-                       # 1. generate the binary in the .nit_compile dir under an escaped name
+                       # 1. generate the binary in the nit_compile dir under an escaped name
                        # 2. copy the binary at the right place in the `all` goal.
                        outpath = mainmodule.c_name
                end
@@ -510,6 +522,11 @@ abstract class AbstractCompiler
        # The modelbuilder used to know the model and the AST
        var modelbuilder: ModelBuilder is protected writable
 
+       # The associated toolchain
+       #
+       # Set by `modelbuilder.write_and_make` and permit sub-routines to access the current toolchain if required.
+       var toolchain: Toolchain is noinit
+
        # Is hardening asked? (see --hardening)
        fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
 
@@ -573,7 +590,7 @@ abstract class AbstractCompiler
        # Binds the generated C function names to Nit function names
        fun build_c_to_nit_bindings
        do
-               var compile_dir = modelbuilder.compile_dir
+               var compile_dir = toolchain.compile_dir
 
                var stream = new FileWriter.open("{compile_dir}/c_functions_hash.c")
                stream.write("#include <string.h>\n")
@@ -620,6 +637,7 @@ abstract class AbstractCompiler
                self.header.add_decl("#include <string.h>")
                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("#include \"gc_chooser.h\"")
                self.header.add_decl("#ifdef ANDROID")
                self.header.add_decl("  #include <android/log.h>")
@@ -1464,6 +1482,14 @@ abstract class AbstractCompilerVisitor
                return res
        end
 
+       # Generate a byte value
+       fun byte_instance(value: Byte): RuntimeVariable
+       do
+               var t = mmodule.byte_type
+               var res = new RuntimeVariable("((unsigned char){value.to_s})", t, t)
+               return res
+       end
+
        # Generate a char value
        fun char_instance(value: Char): RuntimeVariable
        do
@@ -1844,11 +1870,13 @@ redef class MClassType
                else if mclass.name == "Bool" then
                        return "short int"
                else if mclass.name == "Char" then
-                       return "char"
+                       return "uint32_t"
                else if mclass.name == "Float" then
                        return "double"
+               else if mclass.name == "Byte" then
+                       return "unsigned char"
                else if mclass.name == "NativeString" then
-                       return "char*"
+                       return "unsigned char*"
                else if mclass.name == "NativeArray" then
                        return "val*"
                else
@@ -1877,6 +1905,8 @@ redef class MClassType
                        return "c"
                else if mclass.name == "Float" then
                        return "d"
+               else if mclass.name == "Byte" then
+                       return "b"
                else if mclass.name == "NativeString" then
                        return "str"
                else if mclass.name == "NativeArray" then
@@ -2107,13 +2137,16 @@ redef class AMethPropdef
                        else if pname == "to_f" then
                                v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
                                return true
+                       else if pname == "to_b" then
+                               v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
+                               return true
                        else if pname == "ascii" then
-                               v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
+                               v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
                                return true
                        end
                else if cname == "Char" then
                        if pname == "output" then
-                               v.add("printf(\"%c\", {arguments.first});")
+                               v.add("printf(\"%c\", ((unsigned char){arguments.first}));")
                                return true
                        else if pname == "object_id" then
                                v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
@@ -2147,7 +2180,70 @@ redef class AMethPropdef
                                v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
                                return true
                        else if pname == "ascii" then
-                               v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
+                               v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
+                               return true
+                       end
+               else if cname == "Byte" then
+                       if pname == "output" then
+                               v.add("printf(\"%x\\n\", {arguments.first});")
+                               return true
+                       else if pname == "object_id" then
+                               v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
+                               return true
+                       else if pname == "+" then
+                               v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "-" then
+                               v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary -" then
+                               v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary +" then
+                               v.ret(arguments[0])
+                               return true
+                       else if pname == "*" then
+                               v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "/" then
+                               v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "%" then
+                               v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "lshift" then
+                               v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "rshift" 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 == "<" then
+                               v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">" then
+                               v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "<=" then
+                               v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">=" then
+                               v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i" then
+                               v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_f" then
+                               v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "ascii" then
+                               v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
                                return true
                        end
                else if cname == "Bool" then
@@ -2218,13 +2314,16 @@ redef class AMethPropdef
                        else if pname == "to_i" then
                                v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
                                return true
+                       else if pname == "to_b" then
+                               v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
+                               return true
                        end
                else if cname == "NativeString" then
                        if pname == "[]" then
-                               v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
+                               v.ret(v.new_expr("(uint32_t){arguments[0]}[{arguments[1]}]", ret.as(not null)))
                                return true
                        else if pname == "[]=" then
-                               v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
+                               v.add("{arguments[0]}[{arguments[1]}]=(unsigned char){arguments[2]};")
                                return true
                        else if pname == "copy_to" then
                                v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
@@ -2236,7 +2335,7 @@ redef class AMethPropdef
                                v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", 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)))
+                               v.ret(v.new_expr("(unsigned char*)nit_alloc({arguments[1]})", ret.as(not null)))
                                return true
                        end
                else if cname == "NativeArray" then
@@ -2250,7 +2349,7 @@ redef class AMethPropdef
                        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)))
+                       v.ret(v.new_expr("(unsigned 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)
@@ -2794,6 +2893,10 @@ redef class AIntExpr
        redef fun expr(v) do return v.int_instance(self.value.as(not null))
 end
 
+redef class AByteExpr
+       redef fun expr(v) do return v.byte_instance(self.value.as(not null))
+end
+
 redef class AFloatExpr
        redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
 end