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