tools: protect ccache detection to avoid pollution on stderr
[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 )
97 v.types.add( cast.to )
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 fc.decls.add( "{return_type.friendly_extern_name} return___nitni;\n" )
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 fc.decls.add( "{signature.recv.friendly_extern_name} {name_for_impl};\n" )
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 fc.decls.add( "{p.mmtype.friendly_extern_name} {name_for_impl};\n" )
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 fc.decls.add( "{return_type.friendly_extern_name} return___nitni;\n" )
217 fc.decls.add( "val_t return___nit;\n" )
218 s.append( "return___nitni = " )
219 end
220
221 s.append( "{extern_name.as(not null)}( {params.join( ", " )} );\n" )
222
223 fc.exprs.add( s.to_s )
224
225 # return
226 if return_type != null then
227 fc.exprs.add( "{return_type.assign_from_friendly( "return___nit", "return___nitni" )};\n" )
228 fc.exprs.add( "return return___nit;\n" )
229 end
230
231 v.body.append( fc.to_writer )
232 end
233
234 end
235
236 redef class MMLocalClass
237 # Defines a defaut type for special of pointers in frontier.
238 # Can be overriden in the custime .nit.h file, as seen with nits.
239 fun compile_defaut_extern_type( v : FrontierVisitor )
240 do
241 v.header.add( "#ifndef {get_type.friendly_extern_name}\n" )
242 v.header.add( "\ttypedef void* {get_type.friendly_extern_name};\n" )
243 v.header.add( "#endif\n\n" )
244 end
245 end
246
247 redef class MMSignature
248 fun compile_frontier( v : FrontierVisitor )
249 do
250 # receiver
251 v.types.add( recv )
252
253 # params
254 for p in params do v.types.add( p.mmtype )
255
256 # return
257 var rt = return_type
258 if rt != null then
259 v.types.add( rt )
260 end
261 end
262 end
263
264 class FrontierVisitor
265 # frontier file header
266
267 # header comments, imports, guard and types
268 var header_top : Writer = new Writer
269
270 # rest of header
271 var header : Writer = new Writer
272
273 # frontier file body
274 var body : Writer = new Writer
275
276 # set of imported functions, cached to avoid repetitions
277 var friendlys : Set[ MMExplicitImport ] = new HashSet[ MMExplicitImport ]
278
279 # set of relevant types, cached to avoid repetitions
280 var types : Set[ MMType ] = new HashSet[ MMType ]
281
282 # set of imported casts and as, cached to avoid repetitions
283 var casts : Set[ MMImportedCast ] = new HashSet[ MMImportedCast ]
284
285 var mmmodule : MMModule
286
287 var cprogram : CProgram
288
289 fun compile_cached
290 do
291 # types
292 for t in types do t.compile_to_frontier( self )
293
294 # friendlys
295 for friendly in friendlys do friendly.compile_friendly_to_frontier( self )
296
297 # casts
298 for cast in casts do cast.compile_to_frontier( self )
299 end
300
301 fun write_to_files( base_path : String )
302 do
303 var path = "{base_path}._nitni.h"
304 var stream = new OFStream.open( path )
305 header_top.write_to_stream( stream )
306 header.write_to_stream( stream )
307 stream.close
308
309 path = "{base_path}._nitni.c"
310 stream = new OFStream.open( path )
311 body.write_to_stream( stream )
312 stream.close
313 end
314 end
315
316 redef class MMImportedCast
317 # Defines functions to cast types and verify the type of an object.
318 fun compile_to_frontier( v : FrontierVisitor )
319 do
320 # compile isa check
321 if not ( is_about_nullable_only and is_not_null_to_nullable ) then
322 v.header.add( "\n/* Type check for {from} with {to} */\n" )
323 v.header.add( "{is_a_local_csignature( v.mmmodule )};\n" )
324
325 v.header.add( "#ifndef {is_a_friendly_extern_name}\n" )
326 v.header.add( "#define {is_a_friendly_extern_name} {is_a_local_cname( v.mmmodule )}\n" )
327 v.header.add( "#endif\n" )
328
329 var fc = compile_is( v.mmmodule )
330 v.body.append( fc.to_writer )
331 end
332
333 # compile cast itself
334 v.header.add( "\n/* Cast for {from} to {to} */\n" )
335 v.header.add( "{as_local_csignature( v.mmmodule )};\n" )
336
337 v.header.add( "#ifndef {as_friendly_extern_name}\n" )
338 v.header.add( "#define {as_friendly_extern_name} {as_local_cname( v.mmmodule )}\n" )
339 v.header.add( "#endif\n" )
340
341 var fc = compile_as( v.mmmodule )
342 v.body.append( fc.to_writer )
343 end
344
345 # Compiles a function to cast an object to a different type.
346 # Verify type and if it is null.
347 fun compile_as( m : MMModule ) : FunctionCompiler
348 do
349 var fc = new FunctionCompiler( as_local_csignature( m ) )
350
351 var out_name = "out"
352 var temp_name = "temp"
353
354 fc.decls.add( "val_t {temp_name};\n" )
355 fc.decls.add( "{to.friendly_extern_name} {out_name};\n" )
356
357 fc.exprs.add( "{from.assign_from_friendly(temp_name, in_name)};\n" )
358
359 # makes sur it is not null if it cannot be
360 if not to.is_nullable then
361 compile_check_is_not_null( fc, temp_name )
362 end
363
364 # makes sure it's the right type, unless it's only a cast about null
365 if not is_about_nullable_only then # inter types
366 to.compile_check_isa( fc, temp_name )
367 end
368
369 fc.exprs.add( "{to.assign_to_friendly(out_name, temp_name)};\n" )
370
371 fc.exprs.add( "return {out_name};\n" )
372
373 return fc
374 end
375
376 # Compiles a function to verify if an object is of the given type.
377 # Verify type and if it is null.
378 fun compile_is( m : MMModule ) : FunctionCompiler
379 do
380 var fc = new FunctionCompiler( is_a_local_csignature( m ) )
381
382 var temp_name = "temp"
383 fc.decls.add( "val_t {temp_name};\n" )
384
385 fc.exprs.add( "{from.assign_from_friendly(temp_name, in_name)};\n" )
386
387 if is_nullable_to_not_null then # from null
388 if is_about_nullable_only then # opposite, we want to know if null
389 fc.exprs.add( "if ( ! ISNULL({temp_name}) ) return 0;\n" )
390 else
391 fc.exprs.add( "if ( ISNULL({temp_name}) ) return 0;\n" )
392 end
393 end
394
395 if not is_about_nullable_only then # inter types
396 fc.exprs.add( "if ( ! {to.compile_condition_isa( temp_name )} ) return 0;\n" )
397 end
398
399 fc.exprs.add( "return 1;\n" )
400
401 return fc
402 end
403
404 # Compiles lines of code to check if an object is not null.
405 # Is to be nested within another function.
406 fun compile_check_is_not_null( fc : FunctionCompiler, name : String )
407 do
408 fc.exprs.add( "if ( ISNULL({name}) )\{" )
409 fc.exprs.add( "\tfprintf( stderr, \"Casting from {from} to {to} failed because value is null.\" );\n" )
410 fc.exprs.add( "\tabort();\n" )
411 fc.exprs.add( "\}" )
412 end
413
414 redef fun ==( other )
415 do
416 return other isa MMImportedCast and
417 other.from == from and other.to == to
418 end
419 redef fun hash
420 do
421 return from.hash + to.hash
422 end
423 end
424
425 redef class MMType
426 # Compiles a lines of code to ensure that an object is of the given type.
427 # Aborts when it is of the wrong type
428 # Does not check if it is null.
429 # Is to be nested within another function.
430 fun compile_check_isa( fc : FunctionCompiler, name : String )
431 do
432 fc.exprs.add( "if ( ! {compile_condition_isa( name )} )\{" )
433 fc.exprs.add( "\tfprintf( stderr, \"Casting to {self} failed because value is not a {self}.\" );\n" )
434 fc.exprs.add( "\tabort();\n" )
435 fc.exprs.add( "\}" )
436 end
437
438 # Compiles an expression to verify if an object is of the given type.
439 # To be nested within a condition.
440 fun compile_condition_isa( var_name : String ) : String
441 do
442 return "( ISOBJ( {var_name} ) ? OBJISA( {var_name}, {local_class.cname} ): VALISA( {var_name}, {local_class.cname} ) )"
443 end
444
445 # Defines a friendly type in C for a given Nit type.
446 # Standard Nit classes are kept within a struct.
447 fun compile_to_frontier( v : FrontierVisitor )
448 do
449 var pi = local_class.primitive_info
450 if pi == null or is_nullable then
451 var name = friendly_extern_name
452 var guard = "{name.to_s.to_upper}_TYPE"
453
454 # defines struct
455 v.header_top.add( "#ifndef {guard}\n" )
456 v.header_top.add( "#define {guard}\n" )
457 v.header_top.add( "typedef struct s_{name}\{\n" )
458 v.header_top.add( "\tval_t v;\n" )
459 v.header_top.add( "\} {name};\n" )
460 v.header_top.add( "#endif\n\n" )
461
462 # add null version, as a struct
463 if is_nullable then
464 var null_getter = "null_{as_notnull.friendly_extern_name}"
465 var null_getter_local = "{mmmodule.to_s}_{null_getter}"
466
467 v.header.add( "{name} {null_getter_local}();\n" )
468
469 v.header.add( "#ifndef {null_getter}\n" )
470 v.header.add( "#define {null_getter} {null_getter_local}\n" )
471 v.header.add( "#endif\n\n" )
472
473 v.body.add( "{name} {null_getter_local}()\n" )
474 v.body.add( "\{\n" )
475 v.body.add( "\t{name} n;\n" )
476 v.body.add( "\tn.v = NIT_NULL;\n" )
477 v.body.add( "\treturn n;\n" )
478 v.body.add( "\}\n\n" )
479 end
480 end
481 end
482 end
483
484 redef class MMExplicitImport
485 fun compile_friendly_to_frontier( v : FrontierVisitor )
486 do
487 # prototype in header
488 v.header.add( "/* friendly for {method.full_name} */\n" )
489 v.header.add( "{method.frontier_csignature_from( v.mmmodule, local_class )};\n" )
490
491 # Defines a simplier name to be used within this module and to prevent
492 # conflict with other modules importing the same friendly.
493 v.header.add( "#ifndef {method.friendly_extern_name( local_class )}\n" )
494 v.header.add( "#define {method.friendly_extern_name( local_class )} {method.local_friendly_name_from( v.mmmodule, local_class )}\n" )
495 v.header.add( "#endif\n" )
496
497 # implementation in body
498 v.body.add( "/* friendly for {method.full_name} */\n" )
499
500 var fc = new FunctionCompiler( method.frontier_csignature_from( v.mmmodule, local_class ) )
501
502 # params
503 var params = new Array[String]
504
505 # if not init, add receiver
506 if not method.is_init then
507 var name_for_sub = "recv___nit"
508 fc.decls.add( "val_t {name_for_sub};\n" )
509 fc.exprs.add( "{signature.recv.assign_from_friendly( name_for_sub, "recv" )};\n" )
510 params.add( name_for_sub )
511 end
512
513 for p in signature.params do
514 var name_for_sub = "{p.name}___nit"
515 fc.decls.add( "val_t {name_for_sub};\n" )
516 fc.exprs.add( "{p.mmtype.assign_from_friendly( name_for_sub, p.name.to_s )};\n" )
517 params.add( name_for_sub )
518 end
519
520 # call to nit
521 var return_type : nullable MMType = null
522
523 # handles return of method or constructor
524 if method.signature.return_type != null then
525 return_type = method.signature.return_type
526 else if method.is_init then
527 return_type = method.local_class.get_type
528 end
529
530 var s = new Buffer
531 if return_type != null then
532 fc.decls.add( "{return_type.friendly_extern_name} result___nitni;\n" )
533 fc.decls.add( "val_t result___nit;\n" )
534 s.append( "result___nit = " )
535 end
536
537 # hook to generated C code
538 if method.is_init then
539 s.append( "NEW_{local_class}_{method.global.intro.cname}" )
540 else
541 s.append( "{method.global.meth_call}( recv___nit )" )
542 end
543
544 s.append( "( {params.join( ", " )} );\n" )
545
546 fc.exprs.add( s.to_s )
547
548 # return
549 if return_type != null then
550 var result_name_nitni = "result___nitni"
551 var result_name_nit = "result___nit"
552
553 fc.exprs.add( "{return_type.assign_to_friendly( result_name_nitni, result_name_nit )};\n" )
554 fc.exprs.add( "return {result_name_nitni};\n" )
555 end
556
557 v.body.append( fc.to_writer )
558 end
559
560 redef fun hash
561 do
562 return method.global.to_s.hash + local_class.to_s.hash
563 end
564 redef fun == ( other )
565 do
566 return other isa MMExplicitImport and
567 method == other.method and local_class == other.local_class
568 end
569 end