9c5cbc2ff97da6aab2799f7bb2faf201aa57d341
[nit.git] / contrib / jwrapper / src / code_generator.nit
1 # This file is part of NIT (http://www.nitlanguage.org).
2 #
3 # Copyright 2014 Frédéric Vachon <fredvac@gmail.com>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Services to generate extern class `in "Java"`
18 module code_generator
19
20 intrude import types
21
22 class CodeGenerator
23
24 var file_out: OFStream
25 fun code_warehouse: CodeWarehouse do return once new CodeWarehouse
26
27 init (file_name: String)
28 do
29 file_out = new OFStream.open(file_name)
30 end
31
32 fun gen_class_header(full_class_name: Array[String])
33 do
34 file_out.write("extern class Native{full_class_name.last} in \"Java\" `\{ {full_class_name.join(".")} `\}")
35 file_out.write("\tsuper JavaObject\n\tredef type SELF: Native{full_class_name.last}\n\n")
36 end
37
38 fun gen_variable(jid: String, jtype: JavaType)
39 do
40 file_out.write("\tvar {jid.to_snake_case}: {jtype.to_nit_type}\n")
41 end
42
43 fun gen_method(jparam_list: Array[JavaType], jreturn_type: JavaType, jmethod_id: String)
44 do
45 var java_params = ""
46 var nit_params = ""
47 var nit_id = "arg"
48 var nit_id_no = 0
49 var nit_types = new Array[NitType]
50 var comment = ""
51
52 # Parameters
53 for i in [0..jparam_list.length[ do
54 var jparam = jparam_list[i]
55 var nit_type = jparam.to_nit_type
56 var cast = ""
57
58 if not jparam.is_collection then cast = jparam.param_cast
59
60 nit_types.add(nit_type)
61 nit_type.arg_id = "{nit_id}{nit_id_no}"
62
63 if i == jparam_list.length - 1 then
64 java_params += "{cast}{nit_id}{nit_id_no}"
65 nit_params += "{nit_id}{nit_id_no}: {nit_type}"
66 else
67 java_params += "{cast}{nit_id}{nit_id_no}" + ", "
68 nit_params += "{nit_id}{nit_id_no}: {nit_type}, "
69 end
70
71 nit_id_no += 1
72 # Comment if one type is unknown
73 if not nit_type.is_complete then comment = "#"
74 end
75
76 # Method identifier
77 var method_id = jmethod_id.to_snake_case
78 var nit_signature = new Array[String]
79
80 nit_signature.add "\tfun {method_id}"
81
82 if not jparam_list.is_empty then
83 nit_signature.add "({nit_params})"
84 end
85
86 var return_type = null
87
88 if not jreturn_type.is_void then
89 return_type = jreturn_type.to_nit_type
90 if not return_type.is_complete then comment = "#"
91 nit_signature.add ": {return_type} "
92 end
93
94 file_out.write(comment + nit_signature.join(""))
95
96 var param_to_copy = param_to_copy(jparam_list, nit_types)
97
98 # Copy one parameter, the return value, one parameter and the return value or nothing
99 if return_type != null then
100 if return_type.is_complete and jreturn_type.is_collection then
101 if param_to_copy != null then
102 var rtype_couple = new Couple[JavaType, NitType](jreturn_type, return_type)
103 file_out.write(code_warehouse.param_return_copy(rtype_couple, param_to_copy, jmethod_id, java_params))
104 else
105 file_out.write(code_warehouse.return_type_copy(jreturn_type, return_type, jmethod_id, java_params))
106 end
107 else if param_to_copy != null then
108 file_out.write(code_warehouse.param_type_copy(param_to_copy.first, param_to_copy.second, jmethod_id, java_params, true))
109 else
110 file_out.write(" in \"Java\" `\{\n\t\t{comment}return {jreturn_type.return_cast} recv.{jmethod_id}({java_params}); \n\t{comment}`\}\n")
111 end
112 else if jreturn_type.is_void then
113 if param_to_copy != null then
114 file_out.write(code_warehouse.param_type_copy(param_to_copy.first, param_to_copy.second, jmethod_id, java_params, false))
115 else
116 file_out.write(" in \"Java\" `\{\n\t\t{comment}recv.{jmethod_id}({java_params}); \n\t{comment}`\}\n")
117 end
118 else
119 file_out.write(" in \"Java\" `\{\n\t\t{comment}recv.{jmethod_id}({java_params}); \n\t{comment}`\}\n")
120 end
121 end
122
123 # Only one collection type parameter can be copied
124 # If there's none or more than one then `null` is returned
125 fun param_to_copy(jtypes: Array[JavaType], ntypes: Array[NitType]): nullable Couple[JavaType, NitType]
126 do
127 var counter = 0
128 var couple = null
129 for i in [0..jtypes.length[ do
130 if jtypes[i].is_collection and ntypes[i].is_complete then
131 counter += 1
132 if counter > 1 then return null
133 couple = new Couple[JavaType, NitType](jtypes[i], ntypes[i])
134 end
135 end
136
137 return couple
138 end
139 end
140
141 # Contains raw code mostly used to copy collections
142 class CodeWarehouse
143
144 # Collection as return value
145 fun return_type_copy(java_type: JavaType, nit_type: NitType, jmethod_id, params_id: String): String
146 do
147 var narray_id = "nit_array"
148 var loop_ = create_loop(java_type, nit_type, false, "java_array", narray_id)
149 var imports = create_imports(nit_type, false)
150
151 return """{{{imports}}} in "Java" `{
152 {{{java_type.to_s}}} java_array = recv.{{{jmethod_id}}}({{{params_id}}});
153 int {{{narray_id}}} = new_{{{nit_type.id}}}_of_{{{nit_type.generic_params.join("_")}}}();
154
155 {{{loop_}}}
156
157 return {{{narray_id}}};
158 `}
159 """
160 end
161
162 # Collection as parameter
163 fun param_type_copy(java_type: JavaType, nit_type: NitType, jmethod_id, params_id: String, has_return: Bool): String
164 do
165 var narray_id = "nit_array"
166 var jarray_id = "java_array"
167 var loop_ = create_loop(java_type, nit_type, true, jarray_id, narray_id)
168 var imports = create_imports(nit_type, true)
169 var jtype = java_type.to_s
170 var jinstanciation = create_array_instance(java_type, nit_type, jarray_id)
171 var return_str = ""
172
173 if has_return then
174 return_str = "return "
175 end
176
177 params_id = params_id.replace(nit_type.arg_id, jarray_id)
178
179 return """{{{imports}}} in "Java" `{
180 {{{jinstanciation}}}
181 int {{{narray_id}}} = new_{{{nit_type.id}}}_of_{{{nit_type.generic_params.join("_")}}}();
182
183 {{{loop_}}}
184
185 {{{return_str}}}recv.{{{jmethod_id}}}({{{params_id}}});
186 `}
187 """
188 end
189
190 # One collection parameter and the return type will be copied
191 fun param_return_copy(return_types, param_types: Couple[JavaType, NitType], jmethod_id, params_id: String): String
192 do
193 var narray_id = "nit_array"
194 var narray_id2 = "nit_array2"
195
196 var r_jtype = return_types.first
197 var r_ntype = return_types.second
198
199 var p_jtype = param_types.first
200 var p_ntype = param_types.second
201
202 var r_loop = create_loop(r_jtype, r_ntype, false, "java_array", narray_id)
203 var p_loop = create_loop(p_jtype, p_ntype, true, "java_array2", narray_id2)
204
205 var imports = new Array[String]
206
207 # Avoid import duplication
208 if p_ntype.to_s != r_ntype.to_s then
209 imports.add create_imports(p_ntype, true)
210 end
211
212 imports.add create_imports(r_ntype, false)
213
214 params_id = params_id.replace(p_ntype.arg_id, narray_id)
215
216 var jinstanciation = create_array_instance(p_jtype, p_ntype, "java_array")
217
218 return """{{{imports.join(", ")}}} in "Java" `{
219 {{{jinstanciation}}}
220
221 {{{p_loop}}}
222
223 {{{r_jtype.to_s}}} java_array2 = recv.{{{jmethod_id}}}({{{params_id}}});
224 int {{{narray_id2}}} = new_{{{r_ntype.id}}}_of_{{{r_ntype.generic_params.join("_")}}}();
225
226 {{{r_loop}}}
227
228 return {{{narray_id2}}};
229 `}
230 """
231 end
232
233 private fun create_array_instance(java_type: JavaType, nit_type: NitType, jarray_id: String): String
234 do
235 var jtype = java_type.to_s
236 var instanciation = ""
237
238 if java_type.is_primitive_array then
239 instanciation = "{jtype} {jarray_id} = new {java_type.full_id}[Array_of_{nit_type.generic_params[0]}_length({nit_type.arg_id})];"
240 else
241 instanciation = "{jtype} {jarray_id} = new {jtype}();"
242 end
243
244 return instanciation
245 end
246
247 private fun create_imports(nit_type: NitType, is_param: Bool): String
248 do
249 var imports = ""
250 var ntype = nit_type.to_s
251 var gen_type = nit_type.generic_params.join(", ")
252
253 if not is_param then
254 if nit_type.is_map then
255 imports = """import {{{ntype}}}, {{{ntype}}}.[]="""
256 else
257 imports = """import {{{ntype}}}, {{{ntype}}}.add"""
258 end
259 else if nit_type.id == "Array" then
260 imports = """import {{{ntype}}}.length, {{{ntype}}}.[]"""
261 else if nit_type.is_map then
262 imports = """import {{{ntype}}}.iterator, Iterator[{{{gen_type}}}].is_ok, Iterator[{{{gen_type}}}].next, Iterator[{{{gen_type}}}].item, Iterator[{{{gen_type}}}].key"""
263 else
264 imports = """import {{{ntype}}}.iterator, Iterator[{{{gen_type}}}].is_ok, Iterator[{{{gen_type}}}].next, Iterator[{{{gen_type}}}].item"""
265 end
266
267 return imports
268 end
269
270 private fun create_loop(java_type: JavaType, nit_type: NitType, is_param: Bool, jarray_id, narray_id: String): String
271 do
272 var loop_header = ""
273 var loop_body = ""
274 var gen_type = nit_type.generic_params.join("_")
275
276 if is_param then
277 if java_type.is_primitive_array then
278 loop_header = "for(int i=0; i < {jarray_id}.length; ++i)"
279 loop_body = """\t\t\t{{{jarray_id}}}[i] = {{{java_type.param_cast}}}Array_of_{{{gen_type}}}__index({{{nit_type.arg_id}}}, i);"""
280 else if nit_type.id == "Array" then
281 loop_header = """int length = Array_of_{{{gen_type}}}_length({{{nit_type.arg_id}}});\n\t\tfor(int i=0; i < length; ++i)"""
282 loop_body = """\t\t\t{{{jarray_id}}}.add({{{java_type.param_cast}}}Array_of_{{{gen_type}}}__index({{{narray_id}}}, i));"""
283 else
284 loop_header = """int itr = {{{nit_type.id}}}_of_{{{gen_type}}}_iterator({{{nit_type.arg_id}}});\n\t\twhile(Iterator_of_{{{gen_type}}}_is_ok(itr)) {"""
285 if nit_type.is_map then
286 var key_cast = java_type.to_cast(java_type.generic_params[0].id, true)
287 var value_cast = java_type.to_cast(java_type.generic_params[1].id, true)
288 loop_body = """\t\t\t{{{jarray_id}}}[{{{key_cast}}}iterator_of_{{{nit_type.id}}}_key(itr)] = {{{value_cast}}}iterator_of_{{{nit_type.id}}}_item(itr);\n\t\t\titerator_of_{{{gen_type}}}_next(itr);\n\t\t}"""
289 else
290 loop_body = """\t\t\t{{{jarray_id}}}.add({{{java_type.param_cast}}}iterator_of_{{{nit_type.id}}}_item(itr));\n\t\t\titerator_of_{{{gen_type}}}_next(itr);\n\t\t}"""
291 end
292 end
293 else
294 if nit_type.is_map then
295 var key_cast = java_type.to_cast(java_type.generic_params[0].id, false)
296 var value_cast = java_type.to_cast(java_type.generic_params[1].id, false)
297 loop_header = """for (java.util.Map.Entry<{{{java_type.generic_params[0]}}}, {{{java_type.generic_params[1]}}}> e: {{{jarray_id}}})"""
298 loop_body = """\t\t\t{{{nit_type.id}}}_of_{{{gen_type}}}_{{{nit_type.generic_params[1]}}}__index_assign({{{narray_id}}}, {{{key_cast}}}e.getKey(), {{{value_cast}}}e.getValue()); """
299 else if java_type.is_iterable then
300 loop_header = """for ({{{java_type.generic_params[0]}}} e: {{{jarray_id}}})"""
301 loop_body = """\t\t\t{{{nit_type.id}}}_of_{{{gen_type}}}_add({{{narray_id}}}, {{{java_type.return_cast}}}e);"""
302 else
303 loop_header = "for(int i=0; i < {jarray_id}.length; ++i)"
304 loop_body = """\t\t\t{{{nit_type.id}}}_of_{{{gen_type}}}_add({{{narray_id}}}, {{{java_type.return_cast}}}{{{jarray_id}}}[i]);"""
305 end
306 end
307
308 return loop_header + "\n" + loop_body
309 end
310 end