X-Git-Url: http://nitlanguage.org diff --git a/contrib/jwrapper/src/jwrapper.nit b/contrib/jwrapper/src/jwrapper.nit index 09d29cf..bf25f1e 100644 --- a/contrib/jwrapper/src/jwrapper.nit +++ b/contrib/jwrapper/src/jwrapper.nit @@ -1,6 +1,7 @@ # This file is part of NIT (http://www.nitlanguage.org). # # Copyright 2014 Frédéric Vachon +# Copyright 2015 Alexis Laferrière # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -29,6 +30,8 @@ module jwrapper import opts +import performance_analysis + import javap_test_parser import code_generator import javap_visitor @@ -38,9 +41,10 @@ var opts = new OptionContext var opt_unknown = new OptionEnum(["comment", "stub", "ignore"], "How to deal with unknown types", 0, "-u") var opt_verbose = new OptionCount("Verbosity", "-v") var opt_output = new OptionString("Output file", "-o") +var opt_regex = new OptionString("Regex pattern to filter classes in Jar archives", "-r") var opt_help = new OptionBool("Show this help message", "-h", "--help") -opts.add_option(opt_output, opt_unknown, opt_verbose, opt_help) +opts.add_option(opt_output, opt_unknown, opt_extern_class_prefix, opt_libs, opt_regex, opt_cast_objects, opt_arrays, opt_save_model, opt_load_models, opt_no_properties, opt_verbose, opt_help) opts.parse args if opts.errors.not_empty or opts.rest.is_empty or opt_help.value then @@ -62,15 +66,167 @@ if not "javap".program_is_in_path then exit 1 end -var javap = new ProcessReader("javap", "-public", dot_class) +var regex = null +var regex_code = opt_regex.value +if regex_code != null then + regex = regex_code.to_re + var error = regex.compile + if error != null then + print_error "Regex Error: {error}" + exit 1 + end +end + +# List of bytecode Java classes and javap output files +var class_files = new Array[String] +var javap_files = new Array[String] +var clock = new Clock + +# Sort through input files passed as arguments +for input in opts.rest do + var ext = input.file_extension + if ext == "class" then + class_files.add input + else if ext == "javap" then + javap_files.add input + else if ext == "jar" then + clock.lapse + + var out_dir = "tmp" + if not out_dir.file_exists then out_dir.mkdir -var p = new TestParser_javap -var tree = p.work(javap.read_all) + if opt_verbose.value > 0 then print "# Extracting {input}" + # Extract all files + var cmd = "cd {out_dir}; jar -xf {input}" + var status = system(cmd) + if status != 0 then + print_error "Warning: Failed to extract Jar archive '{input}'" + continue + end + + # List .class files + var javap = new ProcessReader("jar", "-tf", input) + var output = javap.read_all + javap.wait + for path in output.split("\n") do + if path.file_extension == "class" then + + # Filter out the classes that do not answer to the Regex + if regex != null and not path.has(regex) then continue + + class_files.add out_dir / path + end + end + javap.close + + sys.perfs["jar extract"].add clock.lapse + else + print_error "Warning: Unsupported file extension for input file '{input}'" + end +end + +if class_files.is_empty and javap_files.is_empty then + print_error "Error: No valid input files, quitting" + exit 1 +end + +var model = new JavaModel var converter = new JavaTypeConverter -var visitor = new JavaVisitor(converter) -visitor.enter_visit(tree) +# Concatenated javap output for all the target files +var javap_output = "" + +# Parse and analyze all the classes at once +if class_files.not_empty then -var generator = new CodeGenerator(out_file, visitor.java_class, opt_attr.value, opt_comment.value) + if opt_verbose.value > 0 then print "# Running javap on {class_files.length} Java classes" + clock.lapse + + # Run javap of the class file + class_files.unshift "-public" + var javap = new ProcessReader("javap", class_files...) + javap_output += javap.read_all + javap.wait + javap.close + var status = javap.status + + if status != 0 then + print_error "Warning: javap failed to parse all class files, is it a valid class file?" + exit 1 + end + + sys.perfs["javap"].add clock.lapse +end + +# Concatenate the preprocessed javap outputs +for class_file in javap_files do + if opt_verbose.value > 0 then print "# Using the preprocessed file {class_file}" + + var ext = class_file.file_extension + assert ext == "javap" + + javap_output += class_file.to_path.read_all +end + +if opt_verbose.value > 0 then print "# Parsing javap output" +if opt_verbose.value > 1 then javap_output.write_to_file "tests/javap.javap" + +# Lexer +var lexer = new Lexer_javap(javap_output) +var parser = new Parser_javap +var tokens = lexer.lex +parser.tokens.add_all tokens +sys.perfs["core lexer"].add clock.lapse + +# Parser +var root_node = parser.parse +if root_node isa NError then + print_error "Warning: Parsing failed with {root_node.message}:{root_node.position or else ""}" + exit 1 +end +sys.perfs["core parser"].add clock.lapse + +# Build model +if opt_verbose.value > 0 then print "# Building model" +assert root_node isa NStart +var visitor = new JavaVisitor(model) +visitor.enter_visit root_node +sys.perfs["core model"].add clock.lapse + +# Resolve types +model.resolve_types +sys.perfs["core resolve"].add clock.lapse + +# Build class hierarchy +model.build_class_hierarchy +sys.perfs["core hierarchy"].add clock.lapse + +if opt_verbose.value > 0 then print "# Generating Nit code" + +# Generate the Nit module +var use_comment = opt_unknown.value == 0 +var use_stub = opt_unknown.value == 1 +var generator = new CodeGenerator(out_file, model, use_comment, use_stub) generator.generate +sys.perfs["code generator"].add clock.lapse + +# Write the model to a file, for use by subsequent passes +generator.write_model_to_file +sys.perfs["writing model"].add clock.lapse + +if opt_verbose.value > 1 then + print "# Performance Analysis:" + print sys.perfs + + print "# {model.unknown_types.length} unknown types:" + var c = 0 + for id, ntype in model.unknown_types do + print "* {id}" + c += 1 + if c > 100 then + print "* ..." + break + end + end +end