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