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 redef class MMSrcModule
28 fun compile_frontier
( v
: FrontierVisitor )
30 # assumes is extern hybrid (verified by caller)
32 v
.body
.add
( "#include \"{name}._nitni
.h\
"\n" )
35 v
.header_top
.add
( "#include <nit_common.h>\n" )
36 v
.header_top
.add
( "#include \"{v.cprogram.module_header_name(self)}\
"\n" )
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" )
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"
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" )
53 for local_class
in local_classes
do
55 for prop
in local_class
.local_local_properties
do
56 # if defined of redefined in this module
58 if prop
.mmmodule
== self and
59 prop
isa MMSrcMethod and prop
.is_extern
then
60 prop
.compile_extern_to_frontier
( v
)
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
)
72 v
.header
.add
( "#endif\n" )
76 redef class MMSrcMethod
77 fun compile_extern_to_frontier
( v
: FrontierVisitor )
79 # defines types used in signature
80 if signature
!= null then
81 signature
.compile_frontier
( v
)
84 for imported
in explicit_imports
do
85 # adds friendly access to property
86 v
.friendlys
.add
( imported
)
88 # defines relevant types
89 imported
.signature
.compile_frontier
( v
)
92 # adds casts and as verifications
93 for cast
in explicit_casts
do
96 v
.types
.add
( cast
.from
.direct_type
)
97 v
.types
.add
( cast
.to
.direct_type
)
102 compile_super_to_frontier
( v
)
105 # adds function in frontier to be called by pure nit
106 compile_out_to_frontier
( v
)
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 )
116 v
.header
.add
( "\n/* friendly for super of {full_name} */\n" )
117 v
.header
.add
( "{frontier_super_csignature_from( mmmodule )};\n" )
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" )
125 v
.body
.add
( "\n/* friendly for super of {full_name} */\n" )
126 var fc
= new FunctionCompiler( friendly_super_csignature
)
129 var params
= new Array[String]
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
)
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
)
145 # hook to generated C
146 var return_type
: nullable MMType = null
148 if signature
.return_type
!= null then
149 return_type
= signature
.return_type
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 = " )
159 s
.append
( "{super_meth_call}( recv___nit )" )
161 s
.append
( "( {params.join( ", " )} );\n" )
163 fc
.exprs
.add
( s
.to_s
)
166 if return_type
!= null
168 fc
.exprs
.add
( "{return_type.assign_to_friendly( "return___nitni", "return___nit" )};\n" )
169 fc
.exprs
.add
( "return return___nitni;\n" )
172 v
.body
.append
( fc
.to_writer
)
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 )
181 v
.header
.add
( "\n/* out/indirect function for {full_name} */\n" )
182 v
.header
.add
( "{out_csignature};\n" ) # incoming types boxed
185 v
.body
.add
( "/* out/indirect function for {full_name} */\n" )
186 var fc
= new FunctionCompiler( out_csignature
)
189 var params
= new List[String]
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
)
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
)
206 var return_type
: nullable MMType = null
208 if signature
.return_type
!= null then
209 return_type
= signature
.return_type
211 return_type
= local_class
.get_type
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 = " )
222 s
.append
( "{extern_name.as(not null)}( {params.join( ", " )} );\n" )
224 fc
.exprs
.add
( s
.to_s
)
226 if return_type
!= null then
227 fc
.exprs
.add
( "{return_type.assign_from_friendly( "return___nit", "return___nitni" )};\n" )
230 fc
.exprs
.add
( "nitni_local_ref_clean( );\n" )
233 if return_type
!= null then
234 fc
.exprs
.add
( "return return___nit;\n" )
237 v
.body
.append
( fc
.to_writer
)
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 )
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" )
253 redef class MMSignature
254 fun compile_frontier
( v
: FrontierVisitor )
257 v
.types
.add
( recv
.direct_type
)
260 for p
in params
do v
.types
.add
( p
.mmtype
.direct_type
)
265 v
.types
.add
( rt
.direct_type
)
270 class FrontierVisitor
271 # frontier file header
273 # header comments, imports, guard and types
274 var header_top
: Writer = new Writer
277 var header
: Writer = new Writer
280 var body
: Writer = new Writer
282 # set of imported functions, cached to avoid repetitions
283 var friendlys
: Set[ MMExplicitImport ] = new HashSet[ MMExplicitImport ]
285 # set of relevant types, cached to avoid repetitions
286 var types
: Set[ MMType ] = new HashSet[ MMType ]
288 # set of imported casts and as, cached to avoid repetitions
289 var casts
: Set[ MMImportedCast ] = new HashSet[ MMImportedCast ]
291 var mmmodule
: MMModule
293 var cprogram
: CProgram
298 for t
in types
do t
.compile_to_frontier
( self )
301 for friendly
in friendlys
do friendly
.compile_friendly_to_frontier
( self )
304 for cast
in casts
do cast
.compile_to_frontier
( self )
307 fun write_to_files
( base_path
: String )
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
)
315 path
= "{base_path}._nitni.c"
316 stream
= new OFStream.open
( path
)
317 body
.write_to_stream
( stream
)
322 redef class MMImportedCast
323 # Defines functions to cast types and verify the type of an object.
324 fun compile_to_frontier
( v
: FrontierVisitor )
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" )
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" )
335 var fc
= compile_is
( v
.mmmodule
)
336 v
.body
.append
( fc
.to_writer
)
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" )
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" )
347 var fc
= compile_as
( v
.mmmodule
)
348 v
.body
.append
( fc
.to_writer
)
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
355 var fc
= new FunctionCompiler( as_local_csignature
( m
) )
358 var temp_name
= "temp"
360 fc
.decls
.add
( "val_t {temp_name};\n" )
361 to
.compile_new_local_ref
( out_name
, fc
, true )
363 fc
.exprs
.add
( "{from.assign_from_friendly(temp_name, in_name)};\n" )
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
)
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
)
375 fc
.exprs
.add
( "{to.assign_to_friendly(out_name, temp_name)};\n" )
377 fc
.exprs
.add
( "return {out_name};\n" )
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
386 var fc
= new FunctionCompiler( is_a_local_csignature
( m
) )
388 var temp_name
= "temp"
389 fc
.decls
.add
( "val_t {temp_name};\n" )
391 fc
.exprs
.add
( "{from.assign_from_friendly(temp_name, in_name)};\n" )
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" )
397 fc
.exprs
.add
( "if ( ISNULL({temp_name}) ) return 0;\n" )
401 if not is_about_nullable_only
then # inter types
402 fc
.exprs
.add
( "if ( ! {to.compile_condition_isa( temp_name )} ) return 0;\n" )
405 fc
.exprs
.add
( "return 1;\n" )
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 )
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" )
420 redef fun ==( other
)
422 return other
isa MMImportedCast and
423 other
.from
== from
and other
.to
== to
427 return from
.hash
+ to
.hash
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 )
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" )
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
448 return "( ISOBJ( {var_name} ) ? OBJISA( {var_name}, {local_class.cname} ): VALISA( {var_name}, {local_class.cname} ) )"
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 )
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"
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" )
468 # add null version, as a struct
470 var local_null_getter
= local_friendly_null_getter_from
( mmmodule
)
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" )
476 v
.header_top
.add
( "{name} {local_null_getter}();\n" )
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
)
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" )
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" )
492 v
.header_top
.add
( "#endif\n" )
496 fun compile_new_local_ref
( var_name
: String, fc
: FunctionCompiler, stack_it
: Bool )
498 var type_name
= friendly_extern_name
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" )
506 fc
.exprs
.add
( "nitni_local_ref_add( (struct nitni_ref *){var_name} );\n" )
512 redef class MMExplicitImport
513 fun compile_friendly_to_frontier
( v
: FrontierVisitor )
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" )
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" )
525 # implementation in body
526 v
.body
.add
( "/* friendly for {method.full_name} */\n" )
528 var fc
= new FunctionCompiler( method
.frontier_csignature_from
( v
.mmmodule
, local_class
) )
531 var params
= new Array[String]
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
)
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
)
549 var return_type
: nullable MMType = null
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
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 = " )
565 # hook to generated C code
566 if method
.is_init
then
567 s
.append
( "NEW_{local_class}_{method.global.intro.cname}" )
569 s
.append
( "{method.global.meth_call}( recv___nit )" )
572 s
.append
( "( {params.join( ", " )} );\n" )
574 fc
.exprs
.add
( s
.to_s
)
577 if return_type
!= null then
578 var result_name_nitni
= "result___nitni"
579 var result_name_nit
= "result___nit"
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" )
585 v
.body
.append
( fc
.to_writer
)
590 return method
.global
.to_s
.hash
+ local_class
.to_s
.hash
592 redef fun == ( other
)
594 return other
isa MMExplicitImport and
595 method
== other
.method
and local_class
== other
.local_class