1 # This file is part of NIT (http://www.nitlanguage.org).
3 # Copyright 2014 Frédéric Vachon <fredvac@gmail.com>
4 # Copyright 2015 Alexis Laferrière <alexis.laf@xymus.net>
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
10 # http://www.apache.org/licenses/LICENSE-2.0
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
18 # jwrapper wraps Java classes in extern Nit classes
20 # This tool takes class file, Jar archives and javap files as input,
21 # and it outputs a Nit source file.
22 # For further details on installation and usage, refer to the README file.
24 # Here's an overview of the project design :
25 # * Grammar and lexer : `grammar/javap.sablecc`
26 # * The `javap_visitor` implements the visitor that extracts data from the AST
27 # * The `code_generator` takes these data and converts it to Nit code via the `jtype_converter` module and generate the output Nit file.
28 # * The `model` contains data structures used to represent the data
29 # * The `jwrapper` module implements the user interface
33 import performance_analysis
35 import javap_test_parser
39 var opts
= new OptionContext
41 var opt_unknown
= new OptionEnum(["comment", "stub", "ignore"], "How to deal with unknown types", 0, "-u")
42 var opt_verbose
= new OptionCount("Verbosity", "-v")
43 var opt_output
= new OptionString("Output file", "-o")
44 var opt_regex
= new OptionString("Regex pattern to filter classes in Jar archives", "-r")
45 var opt_help
= new OptionBool("Show this help message", "-h", "--help")
47 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
)
50 if opts
.errors
.not_empty
or opts
.rest
.is_empty
or opt_help
.value
then
52 Usage: jwrapper [options] file [other_file [...]]
53 Input files: bytecode Java class (.class), Jar archive (.jar) or javap output (.javap)
57 if opt_help
.value
then exit
0
61 var out_file
= opt_output
.value
62 if out_file
== null then out_file
= "out.nit"
64 if not "javap".program_is_in_path
then
65 print
"Error: 'javap' must be in PATH"
70 var regex_code
= opt_regex
.value
71 if regex_code
!= null then
72 regex
= regex_code
.to_re
73 var error
= regex
.compile
75 print_error
"Regex Error: {error}"
80 # List of bytecode Java classes and javap output files
81 var class_files
= new Array[String]
82 var javap_files
= new Array[String]
85 # Sort through input files passed as arguments
86 for input
in opts
.rest
do
87 var ext
= input
.file_extension
88 if ext
== "class" then
90 else if ext
== "javap" then
92 else if ext
== "jar" then
96 if not out_dir
.file_exists
then out_dir
.mkdir
98 if opt_verbose
.value
> 0 then print
"# Extracting {input}"
101 var cmd
= "cd {out_dir}; jar -xf {input}"
102 var status
= system
(cmd
)
104 print_error
"Warning: Failed to extract Jar archive '{input}'"
109 var javap
= new ProcessReader("jar", "-tf", input
)
110 var output
= javap
.read_all
112 for path
in output
.split
("\n") do
113 if path
.file_extension
== "class" then
115 # Filter out the classes that do not answer to the Regex
116 if regex
!= null and not path
.has
(regex
) then continue
118 class_files
.add out_dir
/ path
123 sys
.perfs
["jar extract"].add clock
.lapse
125 print_error
"Warning: Unsupported file extension for input file '{input}'"
129 if class_files
.is_empty
and javap_files
.is_empty
then
130 print_error
"Error: No valid input files, quitting"
134 var model
= new JavaModel
135 var converter
= new JavaTypeConverter
137 # Concatenated javap output for all the target files
138 var javap_output
= ""
140 # Parse and analyze all the classes at once
141 if class_files
.not_empty
then
143 if opt_verbose
.value
> 0 then print
"# Running javap on {class_files.length} Java classes"
146 # Run javap of the class file
147 class_files
.unshift
"-public"
148 var javap
= new ProcessReader("javap", class_files
...)
149 javap_output
+= javap
.read_all
152 var status
= javap
.status
155 print_error
"Warning: javap failed to parse all class files, is it a valid class file?"
159 sys
.perfs
["javap"].add clock
.lapse
162 # Concatenate the preprocessed javap outputs
163 for class_file
in javap_files
do
164 if opt_verbose
.value
> 0 then print
"# Using the preprocessed file {class_file}"
166 var ext
= class_file
.file_extension
167 assert ext
== "javap"
169 javap_output
+= class_file
.to_path
.read_all
172 if opt_verbose
.value
> 0 then print
"# Parsing javap output"
173 if opt_verbose
.value
> 1 then javap_output
.write_to_file
"tests/javap.javap"
176 var lexer
= new Lexer_javap(javap_output
)
177 var parser
= new Parser_javap
178 var tokens
= lexer
.lex
179 parser
.tokens
.add_all tokens
180 sys
.perfs
["core lexer"].add clock
.lapse
183 var root_node
= parser
.parse
184 if root_node
isa NError then
185 print_error
"Warning: Parsing failed with {root_node.message}:{root_node.position or else ""}"
188 sys
.perfs
["core parser"].add clock
.lapse
191 if opt_verbose
.value
> 0 then print
"# Building model"
192 assert root_node
isa NStart
193 var visitor
= new JavaVisitor(model
)
194 visitor
.enter_visit root_node
195 sys
.perfs
["core model"].add clock
.lapse
199 sys
.perfs
["core resolve"].add clock
.lapse
201 # Build class hierarchy
202 model
.build_class_hierarchy
203 sys
.perfs
["core hierarchy"].add clock
.lapse
205 if opt_verbose
.value
> 0 then print
"# Generating Nit code"
207 # Generate the Nit module
208 var use_comment
= opt_unknown
.value
== 0
209 var use_stub
= opt_unknown
.value
== 1
210 var generator
= new CodeGenerator(out_file
, model
, use_comment
, use_stub
)
212 sys
.perfs
["code generator"].add clock
.lapse
214 # Write the model to a file, for use by subsequent passes
215 generator
.write_model_to_file
216 sys
.perfs
["writing model"].add clock
.lapse
218 if opt_verbose
.value
> 1 then
219 print
"# Performance Analysis:"
222 print
"# {model.unknown_types.length} unknown types:"
224 for id
, ntype
in model
.unknown_types
do