ni: fix isa checks with new module names and updates c_src accordingly
[nit.git] / src / native_interface / frontier.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2011 Alexis Laferrière <alexis.laf@xymus.net>
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 # Information and tools relevant to the frontier files
18 module frontier
19
20 import metamodel
21
22 import ni_tools
23 import ni_metamodel
24
25 redef class MMSrcModule
26 fun compile_frontier( v : FrontierVisitor )
27 do
28 # assumes is extern hybrid (verified by caller)
29
30 v.body.add( "#include \"{name}._nitni.h\"\n" )
31
32 # guard
33 v.header_top.add( "#include <nit_common.h>\n" )
34 v.header_top.add( "#include \"{v.cprogram.module_header_name(self)}\"\n" )
35
36 var guard_name = "{name.to_s.to_upper}_NITNI_H"
37 v.header_top.add( "#ifndef {guard_name}\n" )
38 v.header_top.add( "#define {guard_name}\n\n" )
39
40 # import custom _nit.h file from frontier
41 var native_header = "{directory.path}/{name}.nit.h"
42 if not native_header.file_exists then # try old style
43 native_header = "{directory.path}/{name}_nit.h"
44 end
45 if native_header.file_exists then
46 v.body.add( "#include \"{native_header.path_from_parent}\"\n" )
47 v.header.add( "#include \"{native_header.path_from_parent}\"\n" )
48 end
49
50 for local_class in local_classes do
51 ### extern methods
52 for prop in local_class.local_local_properties do
53 # if defined of redefined in this module
54 # and is extern
55 if prop.mmmodule == self and
56 prop isa MMSrcMethod and prop.is_extern then
57 prop.compile_extern_to_frontier( v )
58 end
59 end
60
61 ### extern classes
62 # if class is extern and defined here first
63 if local_class.global.intro == local_class and
64 local_class.global.is_extern then
65 local_class.compile_defaut_extern_type( v )
66 end
67 end
68
69 v.header.add( "#endif\n" )
70 end
71 end
72
73 redef class MMSrcMethod
74 fun compile_extern_to_frontier( v : FrontierVisitor )
75 do
76 # defines types used in signature
77 if signature != null then
78 signature.compile_frontier( v )
79 end
80
81 for imported in explicit_imports do
82 # adds friendly access to property
83 v.friendlys.add( imported )
84
85 # defines relevant types
86 imported.signature.compile_frontier( v )
87 end
88
89 # adds casts and as verifications
90 for cast in explicit_casts do
91 v.casts.add( cast )
92
93 v.types.add( cast.from )
94 v.types.add( cast.to )
95 end
96
97 # adds super
98 if need_super then
99 compile_super_to_frontier( v )
100 end
101
102 # adds function in frontier to be called by pure nit
103 compile_out_to_frontier( v )
104 end
105
106 # Compiles body and header for the friendly super method.
107 # The friendly super method is called by custom native code and
108 # calls the generated C code to execute the real implementation.
109 # It handles types conversion and verifications.
110 fun compile_super_to_frontier( v : FrontierVisitor )
111 do
112 # header
113 v.header.add( "\n/* friendly for super of {full_name} */\n" )
114 v.header.add( "{frontier_super_csignature_from( mmmodule )};\n" )
115
116 # Defines a local name for simplier use, as with friendlys.
117 v.header.add( "#ifndef {friendly_super_cname}\n" )
118 v.header.add( "#define {friendly_super_cname} {local_friendly_super_name_from( mmmodule )}\n" )
119 v.header.add( "#endif\n" )
120
121 # body
122 v.body.add( "\n/* friendly for super of {full_name} */\n" )
123 var fc = new FunctionCompiler( friendly_super_csignature )
124
125 # params
126 var params = new Array[String]
127
128 # receiver
129 var name_for_sub = "recv___nit"
130 fc.decls.add( "val_t {name_for_sub};\n" )
131 fc.exprs.add( "{signature.recv.assign_from_friendly( name_for_sub, "recv" )};\n" )
132 params.add( name_for_sub )
133
134 # other params
135 for p in signature.params do
136 name_for_sub = "{p.name}___nit"
137 fc.decls.add( "val_t {name_for_sub};\n" )
138 fc.exprs.add( "{p.mmtype.assign_from_friendly( name_for_sub, p.name.to_s )};\n" )
139 params.add( name_for_sub )
140 end
141
142 # hook to generated C
143 var return_type : nullable MMType = null
144
145 if signature.return_type != null then
146 return_type = signature.return_type
147 end
148
149 var s = new Buffer
150 if return_type != null then
151 fc.decls.add( "{return_type.friendly_extern_name} return___nitni;\n" )
152 fc.decls.add( "val_t return___nit;\n" )
153 s.append( "return___nit = " )
154 end
155
156 s.append( "{super_meth_call}( recv___nit )" )
157
158 s.append( "( {params.join( ", " )} );\n" )
159
160 fc.exprs.add( s.to_s )
161
162 # verify and return
163 if return_type != null
164 then
165 fc.exprs.add( "{return_type.assign_to_friendly( "return___nitni", "return___nit" )};\n" )
166 fc.exprs.add( "return return___nitni;\n" )
167 end
168
169 v.body.append( fc.to_writer )
170 end
171
172 # Compiles body and header for the out method.
173 # The out method is called by generated C code
174 # It handles variables conversions and verification
175 fun compile_out_to_frontier( v : FrontierVisitor )
176 do
177 # header
178 v.header.add( "\n/* out/indirect function for {full_name} */\n" )
179 v.header.add( "{out_csignature};\n" ) # incoming types boxed
180
181 # body
182 v.body.add( "/* out/indirect function for {full_name} */\n" )
183 var fc = new FunctionCompiler( out_csignature )
184
185 # params
186 var params = new List[String]
187
188 if not is_init then
189 var name_for_impl = "recv___nitni"
190 fc.decls.add( "{signature.recv.friendly_extern_name} {name_for_impl};\n" )
191 fc.exprs.add( "{signature.recv.assign_to_friendly( name_for_impl, "recv" )};\n" )
192 params.add( name_for_impl )
193 end
194
195 for p in signature.params do
196 var name_for_impl = "{p.name}___nitni"
197 fc.decls.add( "{p.mmtype.friendly_extern_name} {name_for_impl};\n" )
198 fc.exprs.add( "{p.mmtype.assign_to_friendly( name_for_impl, p.name.to_s )};\n" )
199 params.add( name_for_impl )
200 end
201
202 # call to impl
203 var return_type : nullable MMType = null
204
205 if signature.return_type != null then
206 return_type = signature.return_type
207 else if is_init then
208 return_type = local_class.get_type
209 end
210
211 var s = new Buffer
212 if return_type != null then
213 fc.decls.add( "{return_type.friendly_extern_name} return___nitni;\n" )
214 fc.decls.add( "val_t return___nit;\n" )
215 s.append( "return___nitni = " )
216 end
217
218 s.append( "{extern_name.as(not null)}( {params.join( ", " )} );\n" )
219
220 fc.exprs.add( s.to_s )
221
222 # return
223 if return_type != null then
224 fc.exprs.add( "{return_type.assign_from_friendly( "return___nit", "return___nitni" )};\n" )
225 fc.exprs.add( "return return___nit;\n" )
226 end
227
228 v.body.append( fc.to_writer )
229 end
230
231 end
232
233 redef class MMLocalClass
234 # Defines a defaut type for special of pointers in frontier.
235 # Can be overriden in the custime .nit.h file, as seen with nits.
236 fun compile_defaut_extern_type( v : FrontierVisitor )
237 do
238 v.header.add( "#ifndef {get_type.friendly_extern_name}\n" )
239 v.header.add( "\ttypedef void* {get_type.friendly_extern_name};\n" )
240 v.header.add( "#endif\n\n" )
241 end
242 end
243
244 redef class MMSignature
245 fun compile_frontier( v : FrontierVisitor )
246 do
247 # receiver
248 v.types.add( recv )
249
250 # params
251 for p in params do v.types.add( p.mmtype )
252
253 # return
254 var rt = return_type
255 if rt != null then
256 v.types.add( rt )
257 end
258 end
259 end
260
261 class FrontierVisitor
262 # frontier file header
263
264 # header comments, imports, guard and types
265 var header_top : Writer = new Writer
266
267 # rest of header
268 var header : Writer = new Writer
269
270 # frontier file body
271 var body : Writer = new Writer
272
273 # set of imported functions, cached to avoid repetitions
274 var friendlys : Set[ MMExplicitImport ] = new HashSet[ MMExplicitImport ]
275
276 # set of relevant types, cached to avoid repetitions
277 var types : Set[ MMType ] = new HashSet[ MMType ]
278
279 # set of imported casts and as, cached to avoid repetitions
280 var casts : Set[ MMImportedCast ] = new HashSet[ MMImportedCast ]
281
282 var mmmodule : MMModule
283
284 var cprogram : CProgram
285
286 fun compile_cached
287 do
288 # types
289 for t in types do t.compile_to_frontier( self )
290
291 # friendlys
292 for friendly in friendlys do friendly.compile_friendly_to_frontier( self )
293
294 # casts
295 for cast in casts do cast.compile_to_frontier( self )
296 end
297
298 fun write_to_files( base_path : String )
299 do
300 var path = "{base_path}._nitni.h"
301 var stream = new OFStream.open( path )
302 header_top.write_to_stream( stream )
303 header.write_to_stream( stream )
304 stream.close
305
306 path = "{base_path}._nitni.c"
307 stream = new OFStream.open( path )
308 body.write_to_stream( stream )
309 stream.close
310 end
311 end
312
313 redef class String
314 # return path from one level deeper
315 # could be moved to stdlib.file
316 fun path_from_parent : String
317 do
318 if self[0] == '/' # is_absolute
319 then
320 return self
321 else
322 return "../{self}"
323 end
324
325 end
326 end
327
328 redef class MMImportedCast
329 # Defines functions to cast types and verify the type of an object.
330 fun compile_to_frontier( v : FrontierVisitor )
331 do
332 # compile isa check
333 if not ( is_about_nullable_only and is_not_null_to_nullable ) then
334 v.header.add( "\n/* Type check for {from} with {to} */\n" )
335 v.header.add( "{is_a_local_csignature( v.mmmodule )};\n" )
336
337 v.header.add( "#ifndef {is_a_friendly_extern_name}\n" )
338 v.header.add( "#define {is_a_friendly_extern_name} {is_a_local_cname( v.mmmodule )}\n" )
339 v.header.add( "#endif\n" )
340
341 var fc = compile_is( v.mmmodule )
342 v.body.append( fc.to_writer )
343 end
344
345 # compile cast itself
346 v.header.add( "\n/* Cast for {from} to {to} */\n" )
347 v.header.add( "{as_local_csignature( v.mmmodule )};\n" )
348
349 v.header.add( "#ifndef {as_friendly_extern_name}\n" )
350 v.header.add( "#define {as_friendly_extern_name} {as_local_cname( v.mmmodule )}\n" )
351 v.header.add( "#endif\n" )
352
353 var fc = compile_as( v.mmmodule )
354 v.body.append( fc.to_writer )
355 end
356
357 # Compiles a function to cast an object to a different type.
358 # Verify type and if it is null.
359 fun compile_as( m : MMModule ) : FunctionCompiler
360 do
361 var fc = new FunctionCompiler( as_local_csignature( m ) )
362
363 var out_name = "out"
364 var temp_name = "temp"
365
366 fc.decls.add( "val_t {temp_name};\n" )
367 fc.decls.add( "{to.friendly_extern_name} {out_name};\n" )
368
369 fc.exprs.add( "{from.assign_from_friendly(temp_name, in_name)};\n" )
370
371 # makes sur it is not null if it cannot be
372 if not to.is_nullable then
373 compile_check_is_not_null( fc, temp_name )
374 end
375
376 # makes sure it's the right type, unless it's only a cast about null
377 if not is_about_nullable_only then # inter types
378 to.compile_check_isa( fc, temp_name )
379 end
380
381 fc.exprs.add( "{to.assign_to_friendly(out_name, temp_name)};\n" )
382
383 fc.exprs.add( "return {out_name};\n" )
384
385 return fc
386 end
387
388 # Compiles a function to verify if an object is of the given type.
389 # Verify type and if it is null.
390 fun compile_is( m : MMModule ) : FunctionCompiler
391 do
392 var fc = new FunctionCompiler( is_a_local_csignature( m ) )
393
394 var temp_name = "temp"
395 fc.decls.add( "val_t {temp_name};\n" )
396
397 fc.exprs.add( "{from.assign_from_friendly(temp_name, in_name)};\n" )
398
399 if is_nullable_to_not_null then # from null
400 if is_about_nullable_only then # opposite, we want to know if null
401 fc.exprs.add( "if ( ! ISNULL({temp_name}) ) return 0;\n" )
402 else
403 fc.exprs.add( "if ( ISNULL({temp_name}) ) return 0;\n" )
404 end
405 end
406
407 if not is_about_nullable_only then # inter types
408 fc.exprs.add( "if ( ! {to.compile_condition_isa( temp_name )} ) return 0;\n" )
409 end
410
411 fc.exprs.add( "return 1;\n" )
412
413 return fc
414 end
415
416 # Compiles lines of code to check if an object is not null.
417 # Is to be nested within another function.
418 fun compile_check_is_not_null( fc : FunctionCompiler, name : String )
419 do
420 fc.exprs.add( "if ( ISNULL({name}) )\{" )
421 fc.exprs.add( "\tfprintf( stderr, \"Casting from {from} to {to} failed because value is null.\" );\n" )
422 fc.exprs.add( "\tabort();\n" )
423 fc.exprs.add( "\}" )
424 end
425
426 redef fun ==( other )
427 do
428 return other isa MMImportedCast and
429 other.from == from and other.to == to
430 end
431 redef fun hash
432 do
433 return from.hash + to.hash
434 end
435 end
436
437 redef class MMType
438 # Compiles a lines of code to ensure that an object is of the given type.
439 # Aborts when it is of the wrong type
440 # Does not check if it is null.
441 # Is to be nested within another function.
442 fun compile_check_isa( fc : FunctionCompiler, name : String )
443 do
444 fc.exprs.add( "if ( ! {compile_condition_isa( name )} )\{" )
445 fc.exprs.add( "\tfprintf( stderr, \"Casting to {self} failed because value is not a {self}.\" );\n" )
446 fc.exprs.add( "\tabort();\n" )
447 fc.exprs.add( "\}" )
448 end
449
450 # Compiles an expression to verify if an object is of the given type.
451 # To be nested within a condition.
452 fun compile_condition_isa( var_name : String ) : String
453 do
454 return "( ISOBJ( {var_name} ) ? OBJISA( {var_name}, {local_class.cname} ): VALISA( {var_name}, {local_class.cname} ) )"
455 end
456
457 # Defines a friendly type in C for a given Nit type.
458 # Standard Nit classes are kept within a struct.
459 fun compile_to_frontier( v : FrontierVisitor )
460 do
461 var pi = local_class.primitive_info
462 if pi == null or is_nullable then
463 var name = friendly_extern_name
464 var guard = "{name.to_s.to_upper}_TYPE"
465
466 # defines struct
467 v.header_top.add( "#ifndef {guard}\n" )
468 v.header_top.add( "#define {guard}\n" )
469 v.header_top.add( "typedef struct s_{name}\{\n" )
470 v.header_top.add( "\tval_t v;\n" )
471 v.header_top.add( "\} {name};\n" )
472 v.header_top.add( "#endif\n\n" )
473
474 # add null version, as a struct
475 if is_nullable then
476 var null_getter = "null_{as_notnull.friendly_extern_name}"
477 var null_getter_local = "{mmmodule.to_s}_{null_getter}"
478
479 v.header.add( "{name} {null_getter_local}();\n" )
480
481 v.header.add( "#ifndef {null_getter}\n" )
482 v.header.add( "#define {null_getter} {null_getter_local}\n" )
483 v.header.add( "#endif\n\n" )
484
485 v.body.add( "{name} {null_getter_local}()\n" )
486 v.body.add( "\{\n" )
487 v.body.add( "\t{name} n;\n" )
488 v.body.add( "\tn.v = NIT_NULL;\n" )
489 v.body.add( "\treturn n;\n" )
490 v.body.add( "\}\n\n" )
491 end
492 end
493 end
494 end
495
496 redef class MMExplicitImport
497 fun compile_friendly_to_frontier( v : FrontierVisitor )
498 do
499 # prototype in header
500 v.header.add( "/* friendly for {method.full_name} */\n" )
501 v.header.add( "{method.frontier_csignature_from( v.mmmodule, local_class )};\n" )
502
503 # Defines a simplier name to be used within this module and to prevent
504 # conflict with other modules importing the same friendly.
505 v.header.add( "#ifndef {method.friendly_extern_name( local_class )}\n" )
506 v.header.add( "#define {method.friendly_extern_name( local_class )} {method.local_friendly_name_from( v.mmmodule, local_class )}\n" )
507 v.header.add( "#endif\n" )
508
509 # implementation in body
510 v.body.add( "/* friendly for {method.full_name} */\n" )
511
512 var fc = new FunctionCompiler( method.frontier_csignature_from( v.mmmodule, local_class ) )
513
514 # params
515 var params = new Array[String]
516
517 # if not init, add receiver
518 if not method.is_init then
519 var name_for_sub = "recv___nit"
520 fc.decls.add( "val_t {name_for_sub};\n" )
521 fc.exprs.add( "{signature.recv.assign_from_friendly( name_for_sub, "recv" )};\n" )
522 params.add( name_for_sub )
523 end
524
525 for p in signature.params do
526 var name_for_sub = "{p.name}___nit"
527 fc.decls.add( "val_t {name_for_sub};\n" )
528 fc.exprs.add( "{p.mmtype.assign_from_friendly( name_for_sub, p.name.to_s )};\n" )
529 params.add( name_for_sub )
530 end
531
532 # call to nit
533 var return_type : nullable MMType = null
534
535 # handles return of method or constructor
536 if method.signature.return_type != null then
537 return_type = method.signature.return_type
538 else if method.is_init then
539 return_type = method.local_class.get_type
540 end
541
542 var s = new Buffer
543 if return_type != null then
544 fc.decls.add( "{return_type.friendly_extern_name} result___nitni;\n" )
545 fc.decls.add( "val_t result___nit;\n" )
546 s.append( "result___nit = " )
547 end
548
549 # hook to generated C code
550 if method.is_init then
551 s.append( "NEW_{local_class}_{method.global.intro.cname}" )
552 else
553 s.append( "{method.global.meth_call}( recv___nit )" )
554 end
555
556 s.append( "( {params.join( ", " )} );\n" )
557
558 fc.exprs.add( s.to_s )
559
560 # return
561 if return_type != null then
562 var result_name_nitni = "result___nitni"
563 var result_name_nit = "result___nit"
564
565 fc.exprs.add( "{return_type.assign_to_friendly( result_name_nitni, result_name_nit )};\n" )
566 fc.exprs.add( "return {result_name_nitni};\n" )
567 end
568
569 v.body.append( fc.to_writer )
570 end
571
572 redef fun hash
573 do
574 return method.global.to_s.hash + local_class.to_s.hash
575 end
576 redef fun == ( other )
577 do
578 return other isa MMExplicitImport and
579 method == other.method and local_class == other.local_class
580 end
581 end