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