X-Git-Url: http://nitlanguage.org diff --git a/src/compiler/java_compiler.nit b/src/compiler/java_compiler.nit index 50c9bc5..60aea2b 100644 --- a/src/compiler/java_compiler.nit +++ b/src/compiler/java_compiler.nit @@ -36,9 +36,12 @@ redef class ToolContext # Where to output tmp files var opt_compile_dir = new OptionString("Directory used to generate temporary files", "--compile-dir") + # Compile using ant instead of make (faster, but no error display) + var opt_ant = new OptionBool("Batch with ant (faster, but no error display)", "--ant") + redef init do super - option_context.add_option(opt_output, opt_compile_dir) + option_context.add_option(opt_output, opt_compile_dir, opt_ant) end end @@ -72,7 +75,11 @@ redef class ModelBuilder time0 = time1 toolcontext.info("*** COMPILING JAVA ***", 1) - build_with_make(compiler, jfiles) + if toolcontext.opt_ant.value then + build_with_ant(compiler, jfiles) + else + build_with_make(compiler, jfiles) + end write_shell_script(compiler) time1 = get_time @@ -83,14 +90,32 @@ redef class ModelBuilder fun write_java_files(compiler: JavaCompiler): Array[String] do var jfiles = new Array[String] for f in compiler.files do - var file = new FileWriter.open("{compiler.compile_dir}/{f.filename}") + var filepath = "{compiler.compile_dir}/{f.filename}" + var file = cache_file(filepath) for line in f.lines do file.write(line) - file.close + close_cache(filepath, file) jfiles.add(f.filename) end return jfiles end + # Cache a file as `{filepath}.tmp` and replace the original if different + private fun cache_file(filepath: String): FileWriter do + if toolcontext.opt_ant.value and filepath.file_exists then + return new FileWriter.open("{filepath}.tmp") + else + return new FileWriter.open(filepath) + end + end + + # Close the writer and move tmp file to original if modified + private fun close_cache(filepath: String, file: FileWriter) do + file.close + if "{filepath}.tmp".file_exists then + sys.system("if ! diff {filepath}.tmp {filepath} > /dev/null; then mv {filepath}.tmp {filepath}; else rm {filepath}.tmp; fi") + end + end + # Compile Java generated files using `make` fun build_with_make(compiler: JavaCompiler, jfiles: Array[String]) do write_manifest(compiler) @@ -107,6 +132,23 @@ redef class ModelBuilder if res != 0 then toolcontext.error(null, "make failed! Error code: {res}.") end + # Compile Java sources using `ant` + fun build_with_ant(compiler: JavaCompiler, jfiles: Array[String]) do + compile_antfile(compiler, jfiles) + var outname = compiler.outname.to_path.filename + var antpath = "{compiler.compile_dir}/{outname}.xml" + self.toolcontext.info("ant jar -f {antpath}", 2) + var res + if self.toolcontext.verbose_level >= 3 then + res = sys.system("ant jar -f {antpath} 2>&1") + else + res = sys.system("ant jar -f {antpath} 2>&1 > /dev/null") + end + if res != 0 then + toolcontext.error(null, "ant compile failed! Error code: {res}.") + end + end + # Write the Makefile used to compile Java generated files into an executable jar fun write_makefile(compiler: JavaCompiler, jfiles: Array[String]) do # list class files from jfiles @@ -137,6 +179,31 @@ redef class ModelBuilder toolcontext.info("Generated makefile: {makename}", 2) end + # The Ant `build.xml` script used to compile build the final jar + fun compile_antfile(compiler: JavaCompiler, jfiles: Array[String]) do + var compile_dir = compiler.compile_dir + var outname = compiler.outname.to_path.filename + var outpath = (sys.getcwd / compiler.outname).simplify_path + var antname = "{compile_dir}/{outname}.xml" + var antfile = new FileWriter.open(antname) + var jname = compiler.mainmodule.jname + antfile.write("") + antfile.write(" ") + antfile.write(" ") + antfile.write(" ") + antfile.write(" ") + antfile.write(" ") + antfile.write(" ") + antfile.write(" ") + antfile.write(" ") + antfile.write(" ") + antfile.write(" ") + antfile.write(" ") + antfile.write("") + antfile.close + toolcontext.info("Generated antfile: {antname}", 2) + end + # Write the Java manifest file private fun write_manifest(compiler: JavaCompiler) do var compile_dir = compiler.compile_dir @@ -459,8 +526,8 @@ class JavaCompilerVisitor res.add(null_instance) continue end - if param.is_vararg and map.vararg_decl > 0 then - var vararg = exprs.sub(j, map.vararg_decl) + if param.is_vararg and args[i].vararg_decl > 0 then + var vararg = exprs.sub(j, args[i].vararg_decl) var elttype = param.mtype var arg = self.vararg_instance(mpropdef, recv, vararg, elttype) res.add(arg) @@ -634,6 +701,14 @@ class JavaCompilerVisitor # Compile a statement (if any) fun stmt(nexpr: nullable AExpr) do if nexpr == null then return + if nexpr.mtype == null and not nexpr.is_typed then + # Untyped expression. + # Might mean dead code or invalid code + # so aborts + add_abort("FATAL: bad statement executed.") + return + end + var old = self.current_node current_node = nexpr nexpr.stmt(self) @@ -651,6 +726,19 @@ class JavaCompilerVisitor res = nexpr.expr(self) end + if res == null then + # Untyped expression. + # Might mean dead code or invalid code. + # so aborts + add_abort("FATAL: bad expression executed.") + # and return a placebo result to please the C compiler + if mtype == null then mtype = compiler.mainmodule.object_type + res = null_instance + + self.current_node = old + return res + end + if mtype != null then mtype = anchor(mtype) res = autobox(res, mtype) @@ -696,6 +784,13 @@ class JavaCompilerVisitor # Used by aborts, asserts, casts, etc. fun add_abort(message: String) do add("System.err.print(\"Runtime error: {message}\");") + add_raw_abort + end + + # Abort without displaying the cause. + # + # Used to customizable errors. + private fun add_raw_abort do var node = current_node if node != null then add("System.err.print(\" ({node.location.short_location})\");") @@ -704,6 +799,15 @@ class JavaCompilerVisitor add("System.exit(1);") end + # Add a dynamic cast + fun add_cast(value: RuntimeVariable, mtype: MType) do + var res = type_test(value, mtype) + add("if (!{res}) \{") + add("System.err.print(\"Runtime error: Cast failed. Expected `{mtype.to_s.escape_to_c}`, got `\" + {value}.rtclass.class_name + \"`\");") + add_raw_abort + add("\}") + end + # Types handling # Anchor a type to the main module and the current receiver @@ -1197,7 +1301,7 @@ redef class MType # Is the associated Java type a primitive one? # - # ENSURE `result == (java_type != "Object")` + # ENSURE `result == (java_type != "RTVal")` var is_java_primitive: Bool is lazy do return java_type != "RTVal" end @@ -1214,7 +1318,7 @@ redef class MClassType return "double" else if mclass.name == "Byte" then return "byte" - else if mclass.name == "NativeString" then + else if mclass.name == "CString" then return "String" else if mclass.name == "NativeArray" then return "Array" @@ -1226,7 +1330,7 @@ end redef class MClass # Runtime name - private fun rt_name: String do return "RTClass_{intro.mmodule.jname}_{jname}" + private fun rt_name: String do return "RTClass_{intro_mmodule.jname}_{jname}" # Generate a Java RTClass for a Nit MClass fun compile_to_java(v: JavaCompilerVisitor) do @@ -1378,8 +1482,7 @@ end redef class AClassdef private fun compile_to_java(v: JavaCompilerVisitor, 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 if not mpropdef.is_intro then v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments) end @@ -1484,10 +1587,10 @@ redef class AMethPropdef else if pname == "%" then v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null))) return true - else if pname == "lshift" then + else if pname == "<<" then v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null))) return true - else if pname == "rshift" then + else if pname == ">>" then v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null))) return true else if pname == "==" then @@ -1588,10 +1691,10 @@ redef class AMethPropdef else if pname == "%" then v.ret(v.new_expr("(byte)({arguments[0]} % {arguments[1]})", ret.as(not null))) return true - else if pname == "lshift" then + else if pname == "<<" then v.ret(v.new_expr("(byte)({arguments[0]} << {arguments[1]})", ret.as(not null))) return true - else if pname == "rshift" then + else if pname == ">>" then v.ret(v.new_expr("(byte)({arguments[0]} >> {arguments[1]})", ret.as(not null))) return true else if pname == "==" then @@ -2126,12 +2229,17 @@ redef class ANotExpr end end -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)) +redef class AIntegerExpr + redef fun expr(v) do + if value isa Int then + return v.int_instance(self.value.as(Int)) + else if value isa Byte then + return v.byte_instance(self.value.as(Byte)) + else + # Should not happen + abort + end + end end redef class AFloatExpr @@ -2154,6 +2262,27 @@ redef class ANullExpr redef fun expr(v) do return v.null_instance end +redef class AAsCastExpr + redef fun expr(v) + do + var i = v.expr(n_expr, null) + v.add_cast(i, mtype.as(not null)) + return i + end +end + +redef class AAsNotnullExpr + redef fun expr(v) do + var i = v.expr(n_expr, null) + if i.mtype.is_java_primitive then return i + + v.add("if ({i} == null || {i}.is_null()) \{") + v.add_abort("Cast failed") + v.add("\}") + return i + end +end + redef class AIsaExpr redef fun expr(v) do