1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2011 Alexis Laferrière <alexis.laf@xymus.net>
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 # Information and tools relevant to the frontier files
25 import syntax
# FIXME: to remove since it breaks modularity
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
34 # needs to be boxed or unboxed
35 # anything using the GC
36 fun needs_preparation
: Bool
38 return ni_type
.local_class
.primitive_info
== null or
39 not ni_type
.local_class
.primitive_info
.tagged
or
43 # prepare variable to callback to Nit
44 fun prepare_for_nit
( fc
: FunctionCompiler )
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" )
52 fun prepare_for_c
( fc
: FunctionCompiler )
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" )
60 # format of the variable to callback to Nit
61 fun as_arg_to_nit
: String
63 if needs_preparation
then
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
)
69 return "{ni_from_name}->ref.val"
73 # format of the variable to call C implementation functions
74 fun as_arg_to_c
: String
76 if needs_preparation
then
79 return ni_type
.unboxtype
( ni_from_name
)
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
92 class ReceiverVariable
95 redef fun ni_from_name
do return "recv"
96 redef fun ni_to_name
do return "trans_recv"
98 redef var ni_type
: MMType
99 init ( t
: MMType ) do ni_type
= t
105 redef fun ni_from_name
do return "orig_return"
106 redef fun ni_to_name
do return "trans_return"
108 redef var ni_type
: MMType
109 init ( t
: MMType ) do ni_type
= t
111 # used only by friendly callbacks to Nit
112 redef fun prepare_for_c
( fc
)
114 fc
.decls
.add
( "val_t {ni_from_name};\n" )
115 ni_type
.compile_new_local_ref
( ni_to_name
, fc
, true )
117 redef fun prepare_for_nit
( fc
)
119 ni_type
.compile_new_local_ref
( ni_from_name
, fc
, false )
120 fc
.decls
.add
( "val_t {ni_to_name};\n" )
124 redef class MMSrcModule
125 fun compile_frontier
( v
: FrontierVisitor )
127 # assumes is extern hybrid (verified by caller)
129 v
.body
.add
( "#include \"{name}._nitni
.h\
"\n" )
132 v
.header_top
.add
( "#include <nit_common.h>\n" )
133 v
.header_top
.add
( "#include \"{v.cprogram.module_header_name(self)}\
"\n" )
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" )
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"
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" )
150 v
.header
.add
( "#include <{cname}._ffi.h>\n" )
153 for local_class
in local_classes
do
155 for prop
in local_class
.local_local_properties
do
156 # if defined of redefined in this module
158 if prop
.mmmodule
== self and
159 prop
isa MMSrcMethod and prop
.is_extern
then
160 prop
.compile_extern_to_frontier
( v
)
167 v
.header
.add
( "#endif\n" )
171 redef class MMSrcMethod
172 fun compile_extern_to_frontier
( v
: FrontierVisitor )
174 # defines types used in signature
175 if signature
!= null then
176 signature
.compile_frontier
( v
)
179 for imported
in explicit_imports
do
180 # adds friendly access to property
181 v
.friendlys
.add
( imported
)
183 # defines relevant types
184 imported
.signature
.compile_frontier
( v
)
187 # adds casts and as verifications
188 for cast
in explicit_casts
do
191 v
.types
.add
( cast
.from
.direct_type
)
192 v
.types
.add
( cast
.to
.direct_type
)
197 compile_super_to_frontier
( v
)
200 # adds function in frontier to be called by pure nit
201 compile_out_to_frontier
( v
)
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 )
211 v
.header
.add
( "\n/* friendly for super of {full_name} */\n" )
212 v
.header
.add
( "{frontier_super_csignature_from( mmmodule )};\n" )
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" )
220 v
.body
.add
( "\n/* friendly for super of {full_name} */\n" )
221 var fc
= new FunctionCompiler( friendly_super_csignature
)
224 var params
= new Array[NiVariable]
225 params
.add
( signature
.recv_ni_variable
)
226 params
.add_all
( signature
.params
)
229 for p
in params
do p
.prepare_for_nit
( fc
)
232 var args
= new Array[String]
233 for p
in params
do args
.add
( p
.as_arg_to_nit
)
235 # hook to generated C
236 var rnv
= signature
.return_ni_variable
239 rnv
.prepare_for_c
( fc
)
240 s
.append
( "{rnv.ni_from_name} = " )
243 s
.append
( "{super_meth_call}( {signature.recv_ni_variable.as_arg_to_nit} )" )
244 s
.append
( "( {args.join( ", " )} );\n" )
246 fc
.exprs
.add
( s
.to_s
)
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" )
254 v
.body
.append
( fc
.to_writer
)
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 )
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 )
271 v
.header
.add
( "\n/* out/indirect function for {full_name} */\n" )
272 v
.header
.add
( "{out_csignature};\n" ) # incoming types boxed
275 v
.body
.add
( "/* out/indirect function for {full_name} */\n" )
276 var fc
= new FunctionCompiler( out_csignature
)
279 var params
= new List[NiVariable]
280 if not is_init
then params
.add
( signature
.recv_ni_variable
)
281 params
.add_all
( signature
.params
)
283 var args
= new List[String]
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
)
295 var rnv
= signature
.return_ni_variable
296 if rnv
== null and is_init
then
297 rnv
= new ReturnVariable( signature
.recv
)
302 rnv
.prepare_for_nit
( fc
)
303 s
.append
( "{rnv.ni_from_name} = " )
306 s
.append
( "{extern_name.as(not null)}( {args.join( ", " )} );\n" )
308 fc
.exprs
.add
( s
.to_s
)
311 fc
.exprs
.add
( "{rnv.ni_type.assign_from_friendly( rnv.ni_to_name, rnv.ni_from_name )};\n" )
314 fc
.exprs
.add
( "nitni_local_ref_clean( );\n" )
318 fc
.exprs
.add
( "return {rnv.ni_to_name};\n" )
321 v
.body
.append
( fc
.to_writer
)
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
)
333 if return_type
!= null then
334 return_ni_variable
= new ReturnVariable( return_type
)
336 return_ni_variable
= null
338 recv_ni_variable
= new ReceiverVariable( recv_type
)
341 fun compile_frontier
( v
: FrontierVisitor )
344 v
.types
.add
( recv
.direct_type
)
347 for p
in params
do v
.types
.add
( p
.mmtype
.direct_type
)
352 v
.types
.add
( rt
.direct_type
)
357 class FrontierVisitor
358 # frontier file header
360 # header comments, imports, guard and types
361 var header_top
: Writer = new Writer
364 var header
: Writer = new Writer
367 var body
: Writer = new Writer
369 # set of imported functions, cached to avoid repetitions
370 var friendlys
: Set[ MMExplicitImport ] = new HashSet[ MMExplicitImport ]
372 # set of relevant types, cached to avoid repetitions
373 var types
: Set[ MMType ] = new HashSet[ MMType ]
375 # set of imported casts and as, cached to avoid repetitions
376 var casts
: Set[ MMImportedCast ] = new HashSet[ MMImportedCast ]
378 var mmmodule
: MMModule
380 var cprogram
: CProgram
382 # compiles cached types and callbacks
386 for t
in types
do t
.compile_to_frontier
( self )
389 for friendly
in friendlys
do friendly
.compile_friendly_to_frontier
( self )
392 for cast
in casts
do cast
.compile_to_frontier
( self )
395 fun write_to_files
( base_path
: String )
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
)
403 path
= "{base_path}._nitni.c"
404 stream
= new OFStream.open
( path
)
405 body
.write_to_stream
( stream
)
410 redef class MMImportedCast
411 # Defines functions to cast types and verify the type of an object.
412 fun compile_to_frontier
( v
: FrontierVisitor )
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" )
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" )
423 var fc
= compile_is
( v
.mmmodule
)
424 v
.body
.append
( fc
.to_writer
)
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" )
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" )
435 var fc
= compile_as
( v
.mmmodule
)
436 v
.body
.append
( fc
.to_writer
)
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
443 var fc
= new FunctionCompiler( as_local_csignature
( m
) )
446 var temp_name
= "temp"
448 fc
.decls
.add
( "val_t {temp_name};\n" )
449 to
.compile_new_local_ref
( out_name
, fc
, true )
451 fc
.exprs
.add
( "{from.assign_from_friendly(temp_name, in_name)};\n" )
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
)
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
)
463 fc
.exprs
.add
( "{to.assign_to_friendly(out_name, temp_name)};\n" )
465 fc
.exprs
.add
( "return {out_name};\n" )
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
474 var fc
= new FunctionCompiler( is_a_local_csignature
( m
) )
476 var temp_name
= "temp"
477 fc
.decls
.add
( "val_t {temp_name};\n" )
479 fc
.exprs
.add
( "{from.assign_from_friendly(temp_name, in_name)};\n" )
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" )
485 fc
.exprs
.add
( "if ( ISNULL({temp_name}) ) return 0;\n" )
489 if not is_about_nullable_only
then # inter types
490 fc
.exprs
.add
( "if ( ! {to.compile_condition_isa( temp_name )} ) return 0;\n" )
493 fc
.exprs
.add
( "return 1;\n" )
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 )
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" )
508 redef fun ==( other
)
510 return other
isa MMImportedCast and
511 other
.from
== from
and other
.to
== to
515 return from
.hash
+ to
.hash
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 )
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" )
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
536 return "( ISOBJ( {var_name} ) ? ISNULL( {var_name} ) || OBJISA( {var_name}, {local_class.cname} ): VALISA( {var_name}, {local_class.cname} ) )"
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 )
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"
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" )
556 # add null version, as a struct
558 var local_null_getter
= local_friendly_null_getter_from
( mmmodule
)
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" )
564 v
.header_top
.add
( "{name} {local_null_getter}();\n" )
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
)
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" )
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" )
580 v
.header_top
.add
( "#endif\n" )
584 fun compile_new_local_ref
( var_name
: String, fc
: FunctionCompiler, stack_it
: Bool )
586 var type_name
= friendly_extern_name
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" )
594 fc
.exprs
.add
( "nitni_local_ref_add( (struct nitni_ref *){var_name} );\n" )
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 )
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" )
611 redef class MMExplicitImport
612 fun compile_friendly_to_frontier
( v
: FrontierVisitor )
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" )
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" )
624 # implementation in body
625 v
.body
.add
( "/* friendly for {method.full_name} */\n" )
627 var fc
= new FunctionCompiler( method
.frontier_csignature_from
( v
.mmmodule
, local_class
) )
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
)
634 for nv
in params
do nv
.prepare_for_nit
( fc
)
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
)
643 rnv
.prepare_for_c
( fc
)
644 s
.append
( "{rnv.ni_from_name} = " )
647 # hook to generated C code
648 if method
.is_init
then
649 s
.append
( "NEW_{local_class}_{method.global.intro.cname}" )
651 s
.append
( "{method.global.meth_call}( {signature.recv_ni_variable.as_arg_to_nit} )" )
654 var args
= new Array[String]
655 for p
in params
do args
.add
( p
.as_arg_to_nit
)
657 s
.append
( "( {args.join( ", " )} );\n" )
659 fc
.exprs
.add
( s
.to_s
)
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" )
667 v
.body
.append
( fc
.to_writer
)
672 return method
.global
.to_s
.hash
+ local_class
.to_s
.hash
674 redef fun == ( other
)
676 return other
isa MMExplicitImport and
677 method
== other
.method
and local_class
== other
.local_class