nitvm: fix a crash caused by a bad allocation of virtual tables
[nit.git] / src / separate_compiler.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Separate compilation of a Nit program
16 module separate_compiler
17
18 import abstract_compiler
19 import coloring
20 import rapid_type_analysis
21
22 # Add separate compiler specific options
23 redef class ToolContext
24 # --separate
25 var opt_separate: OptionBool = new OptionBool("Use separate compilation", "--separate")
26 # --no-inline-intern
27 var opt_no_inline_intern: OptionBool = new OptionBool("Do not inline call to intern methods", "--no-inline-intern")
28 # --no-union-attribute
29 var opt_no_union_attribute: OptionBool = new OptionBool("Put primitive attibutes in a box instead of an union", "--no-union-attribute")
30 # --no-shortcut-equate
31 var opt_no_shortcut_equate: OptionBool = new OptionBool("Always call == in a polymorphic way", "--no-shortcut-equal")
32 # --inline-coloring-numbers
33 var opt_inline_coloring_numbers: OptionBool = new OptionBool("Inline colors and ids (semi-global)", "--inline-coloring-numbers")
34 # --inline-some-methods
35 var opt_inline_some_methods: OptionBool = new OptionBool("Allow the separate compiler to inline some methods (semi-global)", "--inline-some-methods")
36 # --direct-call-monomorph
37 var opt_direct_call_monomorph: OptionBool = new OptionBool("Allow the separate compiler to direct call monomorph sites (semi-global)", "--direct-call-monomorph")
38 # --skip-dead-methods
39 var opt_skip_dead_methods = new OptionBool("Do not compile dead methods (semi-global)", "--skip-dead-methods")
40 # --semi-global
41 var opt_semi_global = new OptionBool("Enable all semi-global optimizations", "--semi-global")
42 # --no-colo-dead-methods
43 var opt_colo_dead_methods = new OptionBool("Force colorization of dead methods", "--colo-dead-methods")
44 # --tables-metrics
45 var opt_tables_metrics: OptionBool = new OptionBool("Enable static size measuring of tables used for vft, typing and resolution", "--tables-metrics")
46
47 redef init
48 do
49 super
50 self.option_context.add_option(self.opt_separate)
51 self.option_context.add_option(self.opt_no_inline_intern)
52 self.option_context.add_option(self.opt_no_union_attribute)
53 self.option_context.add_option(self.opt_no_shortcut_equate)
54 self.option_context.add_option(self.opt_inline_coloring_numbers, opt_inline_some_methods, opt_direct_call_monomorph, opt_skip_dead_methods, opt_semi_global)
55 self.option_context.add_option(self.opt_colo_dead_methods)
56 self.option_context.add_option(self.opt_tables_metrics)
57 end
58
59 redef fun process_options(args)
60 do
61 super
62
63 var tc = self
64 if tc.opt_semi_global.value then
65 tc.opt_inline_coloring_numbers.value = true
66 tc.opt_inline_some_methods.value = true
67 tc.opt_direct_call_monomorph.value = true
68 tc.opt_skip_dead_methods.value = true
69 end
70 end
71
72 var separate_compiler_phase = new SeparateCompilerPhase(self, null)
73 end
74
75 class SeparateCompilerPhase
76 super Phase
77 redef fun process_mainmodule(mainmodule, given_mmodules) do
78 if not toolcontext.opt_separate.value then return
79
80 var modelbuilder = toolcontext.modelbuilder
81 var analysis = modelbuilder.do_rapid_type_analysis(mainmodule)
82 modelbuilder.run_separate_compiler(mainmodule, analysis)
83 end
84 end
85
86 redef class ModelBuilder
87 fun run_separate_compiler(mainmodule: MModule, runtime_type_analysis: nullable RapidTypeAnalysis)
88 do
89 var time0 = get_time
90 self.toolcontext.info("*** GENERATING C ***", 1)
91
92 var compiler = new SeparateCompiler(mainmodule, self, runtime_type_analysis)
93 compiler.compile_header
94
95 # compile class structures
96 self.toolcontext.info("Property coloring", 2)
97 compiler.new_file("{mainmodule.name}.classes")
98 compiler.do_property_coloring
99 for m in mainmodule.in_importation.greaters do
100 for mclass in m.intro_mclasses do
101 if mclass.kind == abstract_kind or mclass.kind == interface_kind then continue
102 compiler.compile_class_to_c(mclass)
103 end
104 end
105
106 # The main function of the C
107 compiler.new_file("{mainmodule.name}.main")
108 compiler.compile_main_function
109
110 # compile methods
111 for m in mainmodule.in_importation.greaters do
112 self.toolcontext.info("Generate C for module {m}", 2)
113 compiler.new_file("{m.name}.sep")
114 compiler.compile_module_to_c(m)
115 end
116
117 # compile live & cast type structures
118 self.toolcontext.info("Type coloring", 2)
119 compiler.new_file("{mainmodule.name}.types")
120 var mtypes = compiler.do_type_coloring
121 for t in mtypes do
122 compiler.compile_type_to_c(t)
123 end
124 # compile remaining types structures (useless but needed for the symbol resolution at link-time)
125 for t in compiler.undead_types do
126 if mtypes.has(t) then continue
127 compiler.compile_type_to_c(t)
128 end
129
130 compiler.display_stats
131
132 var time1 = get_time
133 self.toolcontext.info("*** END GENERATING C: {time1-time0} ***", 2)
134 write_and_make(compiler)
135 end
136
137 # Count number of invocations by VFT
138 private var nb_invok_by_tables = 0
139 # Count number of invocations by direct call
140 private var nb_invok_by_direct = 0
141 # Count number of invocations by inlining
142 private var nb_invok_by_inline = 0
143 end
144
145 # Singleton that store the knowledge about the separate compilation process
146 class SeparateCompiler
147 super AbstractCompiler
148
149 redef type VISITOR: SeparateCompilerVisitor
150
151 # The result of the RTA (used to know live types and methods)
152 var runtime_type_analysis: nullable RapidTypeAnalysis
153
154 private var undead_types: Set[MType] = new HashSet[MType]
155 private var live_unresolved_types: Map[MClassDef, Set[MType]] = new HashMap[MClassDef, HashSet[MType]]
156
157 private var type_ids: Map[MType, Int]
158 private var type_colors: Map[MType, Int]
159 private var opentype_colors: Map[MType, Int]
160 protected var method_colors: Map[PropertyLayoutElement, Int]
161 protected var attr_colors: Map[MAttribute, Int]
162
163 init(mainmodule: MModule, mmbuilder: ModelBuilder, runtime_type_analysis: nullable RapidTypeAnalysis) do
164 super(mainmodule, mmbuilder)
165 var file = new_file("nit.common")
166 self.header = new CodeWriter(file)
167 self.runtime_type_analysis = runtime_type_analysis
168 self.compile_box_kinds
169 end
170
171 redef fun compile_header_structs do
172 self.header.add_decl("typedef void(*nitmethod_t)(void); /* general C type representing a Nit method. */")
173 self.compile_header_attribute_structs
174 self.header.add_decl("struct class \{ int box_kind; nitmethod_t vft[]; \}; /* general C type representing a Nit class. */")
175
176 # With resolution_table_table, all live type resolution are stored in a big table: resolution_table
177 self.header.add_decl("struct type \{ int id; const char *name; int color; short int is_nullable; const struct types *resolution_table; int table_size; int type_table[]; \}; /* general C type representing a Nit type. */")
178 self.header.add_decl("struct instance \{ const struct type *type; const struct class *class; nitattribute_t attrs[]; \}; /* general C type representing a Nit instance. */")
179 self.header.add_decl("struct types \{ int dummy; const struct type *types[]; \}; /* a list types (used for vts, fts and unresolved lists). */")
180 self.header.add_decl("typedef struct instance val; /* general C type representing a Nit instance. */")
181 end
182
183 fun compile_header_attribute_structs
184 do
185 if modelbuilder.toolcontext.opt_no_union_attribute.value then
186 self.header.add_decl("typedef void* nitattribute_t; /* general C type representing a Nit attribute. */")
187 else
188 self.header.add_decl("typedef union \{")
189 self.header.add_decl("void* val;")
190 for c, v in self.box_kinds do
191 var t = c.mclass_type
192 self.header.add_decl("{t.ctype} {t.ctypename};")
193 end
194 self.header.add_decl("\} nitattribute_t; /* general C type representing a Nit attribute. */")
195 end
196 end
197
198 fun compile_box_kinds
199 do
200 # Collect all bas box class
201 # FIXME: this is not completely fine with a separate compilation scheme
202 for classname in ["Int", "Bool", "Char", "Float", "NativeString", "Pointer"] do
203 var classes = self.mainmodule.model.get_mclasses_by_name(classname)
204 if classes == null then continue
205 assert classes.length == 1 else print classes.join(", ")
206 self.box_kinds[classes.first] = self.box_kinds.length + 1
207 end
208 end
209
210 var box_kinds = new HashMap[MClass, Int]
211
212 fun box_kind_of(mclass: MClass): Int
213 do
214 if mclass.mclass_type.ctype == "val*" then
215 return 0
216 else if mclass.kind == extern_kind then
217 return self.box_kinds[self.mainmodule.get_primitive_class("Pointer")]
218 else
219 return self.box_kinds[mclass]
220 end
221
222 end
223
224 fun compile_color_consts(colors: Map[Object, Int]) do
225 var v = new_visitor
226 for m, c in colors do
227 compile_color_const(v, m, c)
228 end
229 end
230
231 fun compile_color_const(v: SeparateCompilerVisitor, m: Object, color: Int) do
232 if color_consts_done.has(m) then return
233 if m isa MProperty then
234 if modelbuilder.toolcontext.opt_inline_coloring_numbers.value then
235 self.provide_declaration(m.const_color, "#define {m.const_color} {color}")
236 else
237 self.provide_declaration(m.const_color, "extern const int {m.const_color};")
238 v.add("const int {m.const_color} = {color};")
239 end
240 else if m isa MPropDef then
241 if modelbuilder.toolcontext.opt_inline_coloring_numbers.value then
242 self.provide_declaration(m.const_color, "#define {m.const_color} {color}")
243 else
244 self.provide_declaration(m.const_color, "extern const int {m.const_color};")
245 v.add("const int {m.const_color} = {color};")
246 end
247 else if m isa MType then
248 if modelbuilder.toolcontext.opt_inline_coloring_numbers.value then
249 self.provide_declaration(m.const_color, "#define {m.const_color} {color}")
250 else
251 self.provide_declaration(m.const_color, "extern const int {m.const_color};")
252 v.add("const int {m.const_color} = {color};")
253 end
254 end
255 color_consts_done.add(m)
256 end
257
258 private var color_consts_done = new HashSet[Object]
259
260 # colorize classe properties
261 fun do_property_coloring do
262
263 var rta = runtime_type_analysis
264
265 # Layouts
266 var mclasses = new HashSet[MClass].from(modelbuilder.model.mclasses)
267 var poset = mainmodule.flatten_mclass_hierarchy
268 var colorer = new POSetColorer[MClass]
269 colorer.colorize(poset)
270
271 # The dead methods, still need to provide a dead color symbol
272 var dead_methods = new Array[MMethod]
273
274 # lookup properties to build layout with
275 var mmethods = new HashMap[MClass, Set[PropertyLayoutElement]]
276 var mattributes = new HashMap[MClass, Set[MAttribute]]
277 for mclass in mclasses do
278 mmethods[mclass] = new HashSet[PropertyLayoutElement]
279 mattributes[mclass] = new HashSet[MAttribute]
280 for mprop in self.mainmodule.properties(mclass) do
281 if mprop isa MMethod then
282 if not modelbuilder.toolcontext.opt_colo_dead_methods.value and rta != null and not rta.live_methods.has(mprop) then
283 dead_methods.add(mprop)
284 continue
285 end
286 mmethods[mclass].add(mprop)
287 else if mprop isa MAttribute then
288 mattributes[mclass].add(mprop)
289 end
290 end
291 end
292
293 # Collect all super calls (dead or not)
294 var all_super_calls = new HashSet[MMethodDef]
295 for mmodule in self.mainmodule.in_importation.greaters do
296 for mclassdef in mmodule.mclassdefs do
297 for mpropdef in mclassdef.mpropdefs do
298 if not mpropdef isa MMethodDef then continue
299 if mpropdef.has_supercall then
300 all_super_calls.add(mpropdef)
301 end
302 end
303 end
304 end
305
306 # lookup super calls and add it to the list of mmethods to build layout with
307 var super_calls
308 if rta != null then
309 super_calls = rta.live_super_sends
310 else
311 super_calls = all_super_calls
312 end
313
314 for mmethoddef in super_calls do
315 var mclass = mmethoddef.mclassdef.mclass
316 mmethods[mclass].add(mmethoddef)
317 for descendant in mclass.in_hierarchy(self.mainmodule).smallers do
318 mmethods[descendant].add(mmethoddef)
319 end
320 end
321
322 # methods coloration
323 var meth_colorer = new POSetBucketsColorer[MClass, PropertyLayoutElement](poset, colorer.conflicts)
324 method_colors = meth_colorer.colorize(mmethods)
325 method_tables = build_method_tables(mclasses, super_calls)
326 compile_color_consts(method_colors)
327
328 # attribute null color to dead methods and supercalls
329 for mproperty in dead_methods do
330 compile_color_const(new_visitor, mproperty, -1)
331 end
332 for mpropdef in all_super_calls do
333 if super_calls.has(mpropdef) then continue
334 compile_color_const(new_visitor, mpropdef, -1)
335 end
336
337 # attributes coloration
338 var attr_colorer = new POSetBucketsColorer[MClass, MAttribute](poset, colorer.conflicts)
339 attr_colors = attr_colorer.colorize(mattributes)
340 attr_tables = build_attr_tables(mclasses)
341 compile_color_consts(attr_colors)
342 end
343
344 fun build_method_tables(mclasses: Set[MClass], super_calls: Set[MMethodDef]): Map[MClass, Array[nullable MPropDef]] do
345 var tables = new HashMap[MClass, Array[nullable MPropDef]]
346 for mclass in mclasses do
347 var table = new Array[nullable MPropDef]
348 var supercalls = new List[MMethodDef]
349
350 # first, fill table from parents by reverse linearization order
351 var parents = new Array[MClass]
352 if mainmodule.flatten_mclass_hierarchy.has(mclass) then
353 parents = mclass.in_hierarchy(mainmodule).greaters.to_a
354 self.mainmodule.linearize_mclasses(parents)
355 end
356
357 for parent in parents do
358 if parent == mclass then continue
359 for mproperty in self.mainmodule.properties(parent) do
360 if not mproperty isa MMethod then continue
361 if not method_colors.has_key(mproperty) then continue
362 var color = method_colors[mproperty]
363 if table.length <= color then
364 for i in [table.length .. color[ do
365 table[i] = null
366 end
367 end
368 for mpropdef in mproperty.mpropdefs do
369 if mpropdef.mclassdef.mclass == parent then
370 table[color] = mpropdef
371 end
372 end
373 end
374
375 # lookup for super calls in super classes
376 for mmethoddef in super_calls do
377 for mclassdef in parent.mclassdefs do
378 if mclassdef.mpropdefs.has(mmethoddef) then
379 supercalls.add(mmethoddef)
380 end
381 end
382 end
383 end
384
385 # then override with local properties
386 for mproperty in self.mainmodule.properties(mclass) do
387 if not mproperty isa MMethod then continue
388 if not method_colors.has_key(mproperty) then continue
389 var color = method_colors[mproperty]
390 if table.length <= color then
391 for i in [table.length .. color[ do
392 table[i] = null
393 end
394 end
395 for mpropdef in mproperty.mpropdefs do
396 if mpropdef.mclassdef.mclass == mclass then
397 table[color] = mpropdef
398 end
399 end
400 end
401
402 # lookup for super calls in local class
403 for mmethoddef in super_calls do
404 for mclassdef in mclass.mclassdefs do
405 if mclassdef.mpropdefs.has(mmethoddef) then
406 supercalls.add(mmethoddef)
407 end
408 end
409 end
410 # insert super calls in table according to receiver
411 for supercall in supercalls do
412 var color = method_colors[supercall]
413 if table.length <= color then
414 for i in [table.length .. color[ do
415 table[i] = null
416 end
417 end
418 var mmethoddef = supercall.lookup_next_definition(self.mainmodule, mclass.intro.bound_mtype)
419 table[color] = mmethoddef
420 end
421 tables[mclass] = table
422 end
423 return tables
424 end
425
426 fun build_attr_tables(mclasses: Set[MClass]): Map[MClass, Array[nullable MPropDef]] do
427 var tables = new HashMap[MClass, Array[nullable MPropDef]]
428 for mclass in mclasses do
429 var table = new Array[nullable MPropDef]
430 # first, fill table from parents by reverse linearization order
431 var parents = new Array[MClass]
432 if mainmodule.flatten_mclass_hierarchy.has(mclass) then
433 parents = mclass.in_hierarchy(mainmodule).greaters.to_a
434 self.mainmodule.linearize_mclasses(parents)
435 end
436 for parent in parents do
437 if parent == mclass then continue
438 for mproperty in self.mainmodule.properties(parent) do
439 if not mproperty isa MAttribute then continue
440 var color = attr_colors[mproperty]
441 if table.length <= color then
442 for i in [table.length .. color[ do
443 table[i] = null
444 end
445 end
446 for mpropdef in mproperty.mpropdefs do
447 if mpropdef.mclassdef.mclass == parent then
448 table[color] = mpropdef
449 end
450 end
451 end
452 end
453
454 # then override with local properties
455 for mproperty in self.mainmodule.properties(mclass) do
456 if not mproperty isa MAttribute then continue
457 var color = attr_colors[mproperty]
458 if table.length <= color then
459 for i in [table.length .. color[ do
460 table[i] = null
461 end
462 end
463 for mpropdef in mproperty.mpropdefs do
464 if mpropdef.mclassdef.mclass == mclass then
465 table[color] = mpropdef
466 end
467 end
468 end
469 tables[mclass] = table
470 end
471 return tables
472 end
473
474 # colorize live types of the program
475 private fun do_type_coloring: POSet[MType] do
476 # Collect types to colorize
477 var live_types = runtime_type_analysis.live_types
478 var live_cast_types = runtime_type_analysis.live_cast_types
479 var mtypes = new HashSet[MType]
480 mtypes.add_all(live_types)
481 mtypes.add_all(live_cast_types)
482 for c in self.box_kinds.keys do
483 mtypes.add(c.mclass_type)
484 end
485
486 # Compute colors
487 var poset = poset_from_mtypes(mtypes)
488 var colorer = new POSetColorer[MType]
489 colorer.colorize(poset)
490 type_ids = colorer.ids
491 type_colors = colorer.colors
492 type_tables = build_type_tables(poset)
493
494 # VT and FT are stored with other unresolved types in the big resolution_tables
495 self.compile_resolution_tables(mtypes)
496
497 return poset
498 end
499
500 private fun poset_from_mtypes(mtypes: Set[MType]): POSet[MType] do
501 var poset = new POSet[MType]
502 for e in mtypes do
503 poset.add_node(e)
504 for o in mtypes do
505 if e == o then continue
506 if e.is_subtype(mainmodule, null, o) then
507 poset.add_edge(e, o)
508 end
509 end
510 end
511 return poset
512 end
513
514 # Build type tables
515 fun build_type_tables(mtypes: POSet[MType]): Map[MType, Array[nullable MType]] do
516 var tables = new HashMap[MType, Array[nullable MType]]
517 for mtype in mtypes do
518 var table = new Array[nullable MType]
519 for sup in mtypes[mtype].greaters do
520 var color = type_colors[sup]
521 if table.length <= color then
522 for i in [table.length .. color[ do
523 table[i] = null
524 end
525 end
526 table[color] = sup
527 end
528 tables[mtype] = table
529 end
530 return tables
531 end
532
533 protected fun compile_resolution_tables(mtypes: Set[MType]) do
534 # resolution_tables is used to perform a type resolution at runtime in O(1)
535
536 # During the visit of the body of classes, live_unresolved_types are collected
537 # and associated to
538 # Collect all live_unresolved_types (visited in the body of classes)
539
540 # Determinate fo each livetype what are its possible requested anchored types
541 var mtype2unresolved = new HashMap[MClassType, Set[MType]]
542 for mtype in self.runtime_type_analysis.live_types do
543 var set = new HashSet[MType]
544 for cd in mtype.collect_mclassdefs(self.mainmodule) do
545 if self.live_unresolved_types.has_key(cd) then
546 set.add_all(self.live_unresolved_types[cd])
547 end
548 end
549 mtype2unresolved[mtype] = set
550 end
551
552 # Compute the table layout with the prefered method
553 var colorer = new BucketsColorer[MType, MType]
554 opentype_colors = colorer.colorize(mtype2unresolved)
555 resolution_tables = self.build_resolution_tables(mtype2unresolved)
556
557 # Compile a C constant for each collected unresolved type.
558 # Either to a color, or to -1 if the unresolved type is dead (no live receiver can require it)
559 var all_unresolved = new HashSet[MType]
560 for t in self.live_unresolved_types.values do
561 all_unresolved.add_all(t)
562 end
563 var all_unresolved_types_colors = new HashMap[MType, Int]
564 for t in all_unresolved do
565 if opentype_colors.has_key(t) then
566 all_unresolved_types_colors[t] = opentype_colors[t]
567 else
568 all_unresolved_types_colors[t] = -1
569 end
570 end
571 self.compile_color_consts(all_unresolved_types_colors)
572
573 #print "tables"
574 #for k, v in unresolved_types_tables.as(not null) do
575 # print "{k}: {v.join(", ")}"
576 #end
577 #print ""
578 end
579
580 fun build_resolution_tables(elements: Map[MClassType, Set[MType]]): Map[MClassType, Array[nullable MType]] do
581 var tables = new HashMap[MClassType, Array[nullable MType]]
582 for mclasstype, mtypes in elements do
583 var table = new Array[nullable MType]
584 for mtype in mtypes do
585 var color = opentype_colors[mtype]
586 if table.length <= color then
587 for i in [table.length .. color[ do
588 table[i] = null
589 end
590 end
591 table[color] = mtype
592 end
593 tables[mclasstype] = table
594 end
595 return tables
596 end
597
598 # Separately compile all the method definitions of the module
599 fun compile_module_to_c(mmodule: MModule)
600 do
601 var old_module = self.mainmodule
602 self.mainmodule = mmodule
603 for cd in mmodule.mclassdefs do
604 for pd in cd.mpropdefs do
605 if not pd isa MMethodDef then continue
606 var rta = runtime_type_analysis
607 if modelbuilder.toolcontext.opt_skip_dead_methods.value and rta != null and not rta.live_methoddefs.has(pd) then continue
608 #print "compile {pd} @ {cd} @ {mmodule}"
609 var r = pd.separate_runtime_function
610 r.compile_to_c(self)
611 var r2 = pd.virtual_runtime_function
612 r2.compile_to_c(self)
613 end
614 end
615 self.mainmodule = old_module
616 end
617
618 # Globaly compile the type structure of a live type
619 fun compile_type_to_c(mtype: MType)
620 do
621 assert not mtype.need_anchor
622 var is_live = mtype isa MClassType and runtime_type_analysis.live_types.has(mtype)
623 var is_cast_live = runtime_type_analysis.live_cast_types.has(mtype)
624 var c_name = mtype.c_name
625 var v = new SeparateCompilerVisitor(self)
626 v.add_decl("/* runtime type {mtype} */")
627
628 # extern const struct type_X
629 self.provide_declaration("type_{c_name}", "extern const struct type type_{c_name};")
630
631 # const struct type_X
632 v.add_decl("const struct type type_{c_name} = \{")
633
634 # type id (for cast target)
635 if is_cast_live then
636 v.add_decl("{type_ids[mtype]},")
637 else
638 v.add_decl("-1, /*CAST DEAD*/")
639 end
640
641 # type name
642 v.add_decl("\"{mtype}\", /* class_name_string */")
643
644 # type color (for cast target)
645 if is_cast_live then
646 v.add_decl("{type_colors[mtype]},")
647 else
648 v.add_decl("-1, /*CAST DEAD*/")
649 end
650
651 # is_nullable bit
652 if mtype isa MNullableType then
653 v.add_decl("1,")
654 else
655 v.add_decl("0,")
656 end
657
658 # resolution table (for receiver)
659 if is_live then
660 var mclass_type = mtype
661 if mclass_type isa MNullableType then mclass_type = mclass_type.mtype
662 assert mclass_type isa MClassType
663 if resolution_tables[mclass_type].is_empty then
664 v.add_decl("NULL, /*NO RESOLUTIONS*/")
665 else
666 compile_type_resolution_table(mtype)
667 v.require_declaration("resolution_table_{c_name}")
668 v.add_decl("&resolution_table_{c_name},")
669 end
670 else
671 v.add_decl("NULL, /*DEAD*/")
672 end
673
674 # cast table (for receiver)
675 if is_live then
676 v.add_decl("{self.type_tables[mtype].length},")
677 v.add_decl("\{")
678 for stype in self.type_tables[mtype] do
679 if stype == null then
680 v.add_decl("-1, /* empty */")
681 else
682 v.add_decl("{type_ids[stype]}, /* {stype} */")
683 end
684 end
685 v.add_decl("\},")
686 else
687 v.add_decl("0, \{\}, /*DEAD TYPE*/")
688 end
689 v.add_decl("\};")
690 end
691
692 fun compile_type_resolution_table(mtype: MType) do
693
694 var mclass_type: MClassType
695 if mtype isa MNullableType then
696 mclass_type = mtype.mtype.as(MClassType)
697 else
698 mclass_type = mtype.as(MClassType)
699 end
700
701 # extern const struct resolution_table_X resolution_table_X
702 self.provide_declaration("resolution_table_{mtype.c_name}", "extern const struct types resolution_table_{mtype.c_name};")
703
704 # const struct fts_table_X fts_table_X
705 var v = new_visitor
706 v.add_decl("const struct types resolution_table_{mtype.c_name} = \{")
707 v.add_decl("0, /* dummy */")
708 v.add_decl("\{")
709 for t in self.resolution_tables[mclass_type] do
710 if t == null then
711 v.add_decl("NULL, /* empty */")
712 else
713 # The table stores the result of the type resolution
714 # Therefore, for a receiver `mclass_type`, and a unresolved type `t`
715 # the value stored is tv.
716 var tv = t.resolve_for(mclass_type, mclass_type, self.mainmodule, true)
717 # FIXME: What typeids means here? How can a tv not be live?
718 if type_ids.has_key(tv) then
719 v.require_declaration("type_{tv.c_name}")
720 v.add_decl("&type_{tv.c_name}, /* {t}: {tv} */")
721 else
722 v.add_decl("NULL, /* empty ({t}: {tv} not a live type) */")
723 end
724 end
725 end
726 v.add_decl("\}")
727 v.add_decl("\};")
728 end
729
730 # Globally compile the table of the class mclass
731 # In a link-time optimisation compiler, tables are globally computed
732 # In a true separate compiler (a with dynamic loading) you cannot do this unfortnally
733 fun compile_class_to_c(mclass: MClass)
734 do
735 var mtype = mclass.intro.bound_mtype
736 var c_name = mclass.c_name
737 var c_instance_name = mclass.c_instance_name
738
739 var vft = self.method_tables[mclass]
740 var attrs = self.attr_tables[mclass]
741 var v = new_visitor
742
743 var rta = runtime_type_analysis
744 var is_dead = rta != null and not rta.live_classes.has(mclass) and mtype.ctype == "val*" and mclass.name != "NativeArray"
745
746 v.add_decl("/* runtime class {c_name} */")
747
748 # Build class vft
749 if not is_dead then
750 self.provide_declaration("class_{c_name}", "extern const struct class class_{c_name};")
751 v.add_decl("const struct class class_{c_name} = \{")
752 v.add_decl("{self.box_kind_of(mclass)}, /* box_kind */")
753 v.add_decl("\{")
754 for i in [0 .. vft.length[ do
755 var mpropdef = vft[i]
756 if mpropdef == null then
757 v.add_decl("NULL, /* empty */")
758 else
759 assert mpropdef isa MMethodDef
760 if rta != null and not rta.live_methoddefs.has(mpropdef) then
761 v.add_decl("NULL, /* DEAD {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
762 continue
763 end
764 var rf = mpropdef.virtual_runtime_function
765 v.require_declaration(rf.c_name)
766 v.add_decl("(nitmethod_t){rf.c_name}, /* pointer to {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
767 end
768 end
769 v.add_decl("\}")
770 v.add_decl("\};")
771 end
772
773 if mtype.ctype != "val*" then
774 if mtype.mclass.name == "Pointer" or mtype.mclass.kind != extern_kind then
775 #Build instance struct
776 self.header.add_decl("struct instance_{c_instance_name} \{")
777 self.header.add_decl("const struct type *type;")
778 self.header.add_decl("const struct class *class;")
779 self.header.add_decl("{mtype.ctype} value;")
780 self.header.add_decl("\};")
781 end
782
783 if not rta.live_types.has(mtype) then return
784
785 #Build BOX
786 self.provide_declaration("BOX_{c_name}", "val* BOX_{c_name}({mtype.ctype});")
787 v.add_decl("/* allocate {mtype} */")
788 v.add_decl("val* BOX_{mtype.c_name}({mtype.ctype} value) \{")
789 v.add("struct instance_{c_instance_name}*res = nit_alloc(sizeof(struct instance_{c_instance_name}));")
790 v.require_declaration("type_{c_name}")
791 v.add("res->type = &type_{c_name};")
792 v.require_declaration("class_{c_name}")
793 v.add("res->class = &class_{c_name};")
794 v.add("res->value = value;")
795 v.add("return (val*)res;")
796 v.add("\}")
797 return
798 else if mclass.name == "NativeArray" then
799 #Build instance struct
800 self.header.add_decl("struct instance_{c_instance_name} \{")
801 self.header.add_decl("const struct type *type;")
802 self.header.add_decl("const struct class *class;")
803 # NativeArrays are just a instance header followed by a length and an array of values
804 self.header.add_decl("int length;")
805 self.header.add_decl("val* values[0];")
806 self.header.add_decl("\};")
807
808 #Build NEW
809 self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(int length, const struct type* type);")
810 v.add_decl("/* allocate {mtype} */")
811 v.add_decl("{mtype.ctype} NEW_{c_name}(int length, const struct type* type) \{")
812 var res = v.get_name("self")
813 v.add_decl("struct instance_{c_instance_name} *{res};")
814 var mtype_elt = mtype.arguments.first
815 v.add("{res} = nit_alloc(sizeof(struct instance_{c_instance_name}) + length*sizeof({mtype_elt.ctype}));")
816 v.add("{res}->type = type;")
817 hardening_live_type(v, "type")
818 v.require_declaration("class_{c_name}")
819 v.add("{res}->class = &class_{c_name};")
820 v.add("{res}->length = length;")
821 v.add("return (val*){res};")
822 v.add("\}")
823 return
824 end
825
826 #Build NEW
827 self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(const struct type* type);")
828 v.add_decl("/* allocate {mtype} */")
829 v.add_decl("{mtype.ctype} NEW_{c_name}(const struct type* type) \{")
830 if is_dead then
831 v.add_abort("{mclass} is DEAD")
832 else
833 var res = v.new_named_var(mtype, "self")
834 res.is_exact = true
835 v.add("{res} = nit_alloc(sizeof(struct instance) + {attrs.length}*sizeof(nitattribute_t));")
836 v.add("{res}->type = type;")
837 hardening_live_type(v, "type")
838 v.require_declaration("class_{c_name}")
839 v.add("{res}->class = &class_{c_name};")
840 self.generate_init_attr(v, res, mtype)
841 v.add("return {res};")
842 end
843 v.add("\}")
844 end
845
846 # Add a dynamic test to ensure that the type referenced by `t` is a live type
847 fun hardening_live_type(v: VISITOR, t: String)
848 do
849 if not v.compiler.modelbuilder.toolcontext.opt_hardening.value then return
850 v.add("if({t} == NULL) \{")
851 v.add_abort("type null")
852 v.add("\}")
853 v.add("if({t}->table_size == 0) \{")
854 v.add("PRINT_ERROR(\"Insantiation of a dead type: %s\\n\", {t}->name);")
855 v.add_abort("type dead")
856 v.add("\}")
857 end
858
859 redef fun new_visitor do return new SeparateCompilerVisitor(self)
860
861 # Stats
862
863 private var type_tables: Map[MType, Array[nullable MType]] = new HashMap[MType, Array[nullable MType]]
864 private var resolution_tables: Map[MClassType, Array[nullable MType]] = new HashMap[MClassType, Array[nullable MType]]
865 protected var method_tables: Map[MClass, Array[nullable MPropDef]] = new HashMap[MClass, Array[nullable MPropDef]]
866 protected var attr_tables: Map[MClass, Array[nullable MPropDef]] = new HashMap[MClass, Array[nullable MPropDef]]
867
868 redef fun display_stats
869 do
870 super
871 if self.modelbuilder.toolcontext.opt_tables_metrics.value then
872 display_sizes
873 end
874 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
875 display_isset_checks
876 end
877 var tc = self.modelbuilder.toolcontext
878 tc.info("# implementation of method invocation",2)
879 var nb_invok_total = modelbuilder.nb_invok_by_tables + modelbuilder.nb_invok_by_direct + modelbuilder.nb_invok_by_inline
880 tc.info("total number of invocations: {nb_invok_total}",2)
881 tc.info("invocations by VFT send: {modelbuilder.nb_invok_by_tables} ({div(modelbuilder.nb_invok_by_tables,nb_invok_total)}%)",2)
882 tc.info("invocations by direct call: {modelbuilder.nb_invok_by_direct} ({div(modelbuilder.nb_invok_by_direct,nb_invok_total)}%)",2)
883 tc.info("invocations by inlining: {modelbuilder.nb_invok_by_inline} ({div(modelbuilder.nb_invok_by_inline,nb_invok_total)}%)",2)
884 end
885
886 fun display_sizes
887 do
888 print "# size of subtyping tables"
889 print "\ttotal \tholes"
890 var total = 0
891 var holes = 0
892 for t, table in type_tables do
893 total += table.length
894 for e in table do if e == null then holes += 1
895 end
896 print "\t{total}\t{holes}"
897
898 print "# size of resolution tables"
899 print "\ttotal \tholes"
900 total = 0
901 holes = 0
902 for t, table in resolution_tables do
903 total += table.length
904 for e in table do if e == null then holes += 1
905 end
906 print "\t{total}\t{holes}"
907
908 print "# size of methods tables"
909 print "\ttotal \tholes"
910 total = 0
911 holes = 0
912 for t, table in method_tables do
913 total += table.length
914 for e in table do if e == null then holes += 1
915 end
916 print "\t{total}\t{holes}"
917
918 print "# size of attributes tables"
919 print "\ttotal \tholes"
920 total = 0
921 holes = 0
922 for t, table in attr_tables do
923 total += table.length
924 for e in table do if e == null then holes += 1
925 end
926 print "\t{total}\t{holes}"
927 end
928
929 protected var isset_checks_count = 0
930 protected var attr_read_count = 0
931
932 fun display_isset_checks do
933 print "# total number of compiled attribute reads"
934 print "\t{attr_read_count}"
935 print "# total number of compiled isset-checks"
936 print "\t{isset_checks_count}"
937 end
938
939 redef fun compile_nitni_structs
940 do
941 self.header.add_decl("struct nitni_instance \{struct instance *value;\};")
942 end
943
944 redef fun finalize_ffi_for_module(mmodule)
945 do
946 var old_module = self.mainmodule
947 self.mainmodule = mmodule
948 super
949 self.mainmodule = old_module
950 end
951 end
952
953 # A visitor on the AST of property definition that generate the C code of a separate compilation process.
954 class SeparateCompilerVisitor
955 super AbstractCompilerVisitor
956
957 redef type COMPILER: SeparateCompiler
958
959 redef fun adapt_signature(m, args)
960 do
961 var msignature = m.msignature.resolve_for(m.mclassdef.bound_mtype, m.mclassdef.bound_mtype, m.mclassdef.mmodule, true)
962 var recv = args.first
963 if recv.mtype.ctype != m.mclassdef.mclass.mclass_type.ctype then
964 args.first = self.autobox(args.first, m.mclassdef.mclass.mclass_type)
965 end
966 for i in [0..msignature.arity[ do
967 var t = msignature.mparameters[i].mtype
968 if i == msignature.vararg_rank then
969 t = args[i+1].mtype
970 end
971 args[i+1] = self.autobox(args[i+1], t)
972 end
973 end
974
975 redef fun autobox(value, mtype)
976 do
977 if value.mtype == mtype then
978 return value
979 else if value.mtype.ctype == "val*" and mtype.ctype == "val*" then
980 return value
981 else if value.mtype.ctype == "val*" then
982 return self.new_expr("((struct instance_{mtype.c_instance_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
983 else if mtype.ctype == "val*" then
984 var valtype = value.mtype.as(MClassType)
985 var res = self.new_var(mtype)
986 if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(valtype) then
987 self.add("/*no autobox from {value.mtype} to {mtype}: {value.mtype} is not live! */")
988 self.add("PRINT_ERROR(\"Dead code executed!\\n\"); show_backtrace(1);")
989 return res
990 end
991 self.require_declaration("BOX_{valtype.c_name}")
992 self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */")
993 return res
994 else if (value.mtype.ctype == "void*" and mtype.ctype == "void*") or
995 (value.mtype.ctype == "char*" and mtype.ctype == "void*") or
996 (value.mtype.ctype == "void*" and mtype.ctype == "char*") then
997 return value
998 else
999 # Bad things will appen!
1000 var res = self.new_var(mtype)
1001 self.add("/* {res} left unintialized (cannot convert {value.mtype} to {mtype}) */")
1002 self.add("PRINT_ERROR(\"Cast error: Cannot cast %s to %s.\\n\", \"{value.mtype}\", \"{mtype}\"); show_backtrace(1);")
1003 return res
1004 end
1005 end
1006
1007 # Return a C expression returning the runtime type structure of the value
1008 # The point of the method is to works also with primitives types.
1009 fun type_info(value: RuntimeVariable): String
1010 do
1011 if value.mtype.ctype == "val*" then
1012 return "{value}->type"
1013 else
1014 compiler.undead_types.add(value.mtype)
1015 self.require_declaration("type_{value.mtype.c_name}")
1016 return "(&type_{value.mtype.c_name})"
1017 end
1018 end
1019
1020 redef fun compile_callsite(callsite, args)
1021 do
1022 var rta = compiler.runtime_type_analysis
1023 var recv = args.first.mtype
1024 if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null then
1025 var tgs = rta.live_targets(callsite)
1026 if tgs.length == 1 then
1027 # DIRECT CALL
1028 var mmethod = callsite.mproperty
1029 self.varargize(mmethod.intro, mmethod.intro.msignature.as(not null), args)
1030 var res0 = before_send(mmethod, args)
1031 var res = call(tgs.first, tgs.first.mclassdef.bound_mtype, args)
1032 if res0 != null then
1033 assert res != null
1034 self.assign(res0, res)
1035 res = res0
1036 end
1037 add("\}") # close the before_send
1038 return res
1039 end
1040 end
1041 return super
1042 end
1043 redef fun send(mmethod, arguments)
1044 do
1045 self.varargize(mmethod.intro, mmethod.intro.msignature.as(not null), arguments)
1046
1047 if arguments.first.mcasttype.ctype != "val*" then
1048 # In order to shortcut the primitive, we need to find the most specific method
1049 # Howverr, because of performance (no flattening), we always work on the realmainmodule
1050 var m = self.compiler.mainmodule
1051 self.compiler.mainmodule = self.compiler.realmainmodule
1052 var res = self.monomorphic_send(mmethod, arguments.first.mcasttype, arguments)
1053 self.compiler.mainmodule = m
1054 return res
1055 end
1056
1057 return table_send(mmethod, arguments, mmethod.const_color)
1058 end
1059
1060 # Handel common special cases before doing the effective method invocation
1061 # This methods handle the `==` and `!=` methods and the case of the null receiver.
1062 # Note: a { is open in the generated C, that enclose and protect the effective method invocation.
1063 # Client must not forget to close the } after them.
1064 #
1065 # The value returned is the result of the common special cases.
1066 # If not null, client must compine it with the result of their own effective method invocation.
1067 #
1068 # If `before_send` can shortcut the whole message sending, a dummy `if(0){`
1069 # is generated to cancel the effective method invocation that will follow
1070 # TODO: find a better approach
1071 private fun before_send(mmethod: MMethod, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1072 do
1073 var res: nullable RuntimeVariable = null
1074 var recv = arguments.first
1075 var consider_null = not self.compiler.modelbuilder.toolcontext.opt_no_check_other.value or mmethod.name == "==" or mmethod.name == "!="
1076 var maybenull = recv.mcasttype isa MNullableType and consider_null
1077 if maybenull then
1078 self.add("if ({recv} == NULL) \{")
1079 if mmethod.name == "==" then
1080 res = self.new_var(bool_type)
1081 var arg = arguments[1]
1082 if arg.mcasttype isa MNullableType then
1083 self.add("{res} = ({arg} == NULL);")
1084 else if arg.mcasttype isa MNullType then
1085 self.add("{res} = 1; /* is null */")
1086 else
1087 self.add("{res} = 0; /* {arg.inspect} cannot be null */")
1088 end
1089 else if mmethod.name == "!=" then
1090 res = self.new_var(bool_type)
1091 var arg = arguments[1]
1092 if arg.mcasttype isa MNullableType then
1093 self.add("{res} = ({arg} != NULL);")
1094 else if arg.mcasttype isa MNullType then
1095 self.add("{res} = 0; /* is null */")
1096 else
1097 self.add("{res} = 1; /* {arg.inspect} cannot be null */")
1098 end
1099 else
1100 self.add_abort("Receiver is null")
1101 end
1102 self.add("\} else \{")
1103 else
1104 self.add("\{")
1105 end
1106 if not self.compiler.modelbuilder.toolcontext.opt_no_shortcut_equate.value and (mmethod.name == "==" or mmethod.name == "!=") then
1107 if res == null then res = self.new_var(bool_type)
1108 # Recv is not null, thus is arg is, it is easy to conclude (and respect the invariants)
1109 var arg = arguments[1]
1110 if arg.mcasttype isa MNullType then
1111 if mmethod.name == "==" then
1112 self.add("{res} = 0; /* arg is null but recv is not */")
1113 else
1114 self.add("{res} = 1; /* arg is null and recv is not */")
1115 end
1116 self.add("\}") # closes the null case
1117 self.add("if (0) \{") # what follow is useless, CC will drop it
1118 end
1119 end
1120 return res
1121 end
1122
1123 private fun table_send(mmethod: MMethod, arguments: Array[RuntimeVariable], const_color: String): nullable RuntimeVariable
1124 do
1125 compiler.modelbuilder.nb_invok_by_tables += 1
1126 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_tables++;")
1127
1128 assert arguments.length == mmethod.intro.msignature.arity + 1 else debug("Invalid arity for {mmethod}. {arguments.length} arguments given.")
1129 var recv = arguments.first
1130
1131 var res0 = before_send(mmethod, arguments)
1132
1133 var res: nullable RuntimeVariable
1134 var msignature = mmethod.intro.msignature.resolve_for(mmethod.intro.mclassdef.bound_mtype, mmethod.intro.mclassdef.bound_mtype, mmethod.intro.mclassdef.mmodule, true)
1135 var ret = msignature.return_mtype
1136 if mmethod.is_new then
1137 ret = arguments.first.mtype
1138 res = self.new_var(ret)
1139 else if ret == null then
1140 res = null
1141 else
1142 res = self.new_var(ret)
1143 end
1144
1145 var s = new FlatBuffer
1146 var ss = new FlatBuffer
1147
1148 s.append("val*")
1149 ss.append("{recv}")
1150 for i in [0..msignature.arity[ do
1151 var a = arguments[i+1]
1152 var t = msignature.mparameters[i].mtype
1153 if i == msignature.vararg_rank then
1154 t = arguments[i+1].mcasttype
1155 end
1156 s.append(", {t.ctype}")
1157 a = self.autobox(a, t)
1158 ss.append(", {a}")
1159 end
1160
1161
1162 var r
1163 if ret == null then r = "void" else r = ret.ctype
1164 self.require_declaration(const_color)
1165 var call = "(({r} (*)({s}))({arguments.first}->class->vft[{const_color}]))({ss}) /* {mmethod} on {arguments.first.inspect}*/"
1166
1167 if res != null then
1168 self.add("{res} = {call};")
1169 else
1170 self.add("{call};")
1171 end
1172
1173 if res0 != null then
1174 assert res != null
1175 assign(res0,res)
1176 res = res0
1177 end
1178
1179 self.add("\}") # closes the null case
1180
1181 return res
1182 end
1183
1184 redef fun call(mmethoddef, recvtype, arguments)
1185 do
1186 assert arguments.length == mmethoddef.msignature.arity + 1 else debug("Invalid arity for {mmethoddef}. {arguments.length} arguments given.")
1187
1188 var res: nullable RuntimeVariable
1189 var ret = mmethoddef.msignature.return_mtype
1190 if mmethoddef.mproperty.is_new then
1191 ret = arguments.first.mtype
1192 res = self.new_var(ret)
1193 else if ret == null then
1194 res = null
1195 else
1196 ret = ret.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
1197 res = self.new_var(ret)
1198 end
1199
1200 if (mmethoddef.is_intern and not compiler.modelbuilder.toolcontext.opt_no_inline_intern.value) or
1201 (compiler.modelbuilder.toolcontext.opt_inline_some_methods.value and mmethoddef.can_inline(self)) then
1202 compiler.modelbuilder.nb_invok_by_inline += 1
1203 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_inline++;")
1204 var frame = new Frame(self, mmethoddef, recvtype, arguments)
1205 frame.returnlabel = self.get_name("RET_LABEL")
1206 frame.returnvar = res
1207 var old_frame = self.frame
1208 self.frame = frame
1209 self.add("\{ /* Inline {mmethoddef} ({arguments.join(",")}) on {arguments.first.inspect} */")
1210 mmethoddef.compile_inside_to_c(self, arguments)
1211 self.add("{frame.returnlabel.as(not null)}:(void)0;")
1212 self.add("\}")
1213 self.frame = old_frame
1214 return res
1215 end
1216 compiler.modelbuilder.nb_invok_by_direct += 1
1217 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_direct++;")
1218
1219 # Autobox arguments
1220 self.adapt_signature(mmethoddef, arguments)
1221
1222 self.require_declaration(mmethoddef.c_name)
1223 if res == null then
1224 self.add("{mmethoddef.c_name}({arguments.join(", ")}); /* Direct call {mmethoddef} on {arguments.first.inspect}*/")
1225 return null
1226 else
1227 self.add("{res} = {mmethoddef.c_name}({arguments.join(", ")});")
1228 end
1229
1230 return res
1231 end
1232
1233 redef fun supercall(m: MMethodDef, recvtype: MClassType, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1234 do
1235 if arguments.first.mcasttype.ctype != "val*" then
1236 # In order to shortcut the primitive, we need to find the most specific method
1237 # However, because of performance (no flattening), we always work on the realmainmodule
1238 var main = self.compiler.mainmodule
1239 self.compiler.mainmodule = self.compiler.realmainmodule
1240 var res = self.monomorphic_super_send(m, recvtype, arguments)
1241 self.compiler.mainmodule = main
1242 return res
1243 end
1244 return table_send(m.mproperty, arguments, m.const_color)
1245 end
1246
1247 redef fun vararg_instance(mpropdef, recv, varargs, elttype)
1248 do
1249 # A vararg must be stored into an new array
1250 # The trick is that the dymaic type of the array may depends on the receiver
1251 # of the method (ie recv) if the static type is unresolved
1252 # This is more complex than usual because the unresolved type must not be resolved
1253 # with the current receiver (ie self).
1254 # Therefore to isolate the resolution from self, a local Frame is created.
1255 # One can see this implementation as an inlined method of the receiver whose only
1256 # job is to allocate the array
1257 var old_frame = self.frame
1258 var frame = new Frame(self, mpropdef, mpropdef.mclassdef.bound_mtype, [recv])
1259 self.frame = frame
1260 #print "required Array[{elttype}] for recv {recv.inspect}. bound=Array[{self.resolve_for(elttype, recv)}]. selfvar={frame.arguments.first.inspect}"
1261 var res = self.array_instance(varargs, elttype)
1262 self.frame = old_frame
1263 return res
1264 end
1265
1266 redef fun isset_attribute(a, recv)
1267 do
1268 self.check_recv_notnull(recv)
1269 var res = self.new_var(bool_type)
1270
1271 # What is the declared type of the attribute?
1272 var mtype = a.intro.static_mtype.as(not null)
1273 var intromclassdef = a.intro.mclassdef
1274 mtype = mtype.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1275
1276 if mtype isa MNullableType then
1277 self.add("{res} = 1; /* easy isset: {a} on {recv.inspect} */")
1278 return res
1279 end
1280
1281 self.require_declaration(a.const_color)
1282 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1283 self.add("{res} = {recv}->attrs[{a.const_color}] != NULL; /* {a} on {recv.inspect}*/")
1284 else
1285
1286 if mtype.ctype == "val*" then
1287 self.add("{res} = {recv}->attrs[{a.const_color}].val != NULL; /* {a} on {recv.inspect} */")
1288 else
1289 self.add("{res} = 1; /* NOT YET IMPLEMENTED: isset of primitives: {a} on {recv.inspect} */")
1290 end
1291 end
1292 return res
1293 end
1294
1295 redef fun read_attribute(a, recv)
1296 do
1297 self.check_recv_notnull(recv)
1298
1299 # What is the declared type of the attribute?
1300 var ret = a.intro.static_mtype.as(not null)
1301 var intromclassdef = a.intro.mclassdef
1302 ret = ret.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1303
1304 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1305 self.compiler.attr_read_count += 1
1306 self.add("count_attr_reads++;")
1307 end
1308
1309 self.require_declaration(a.const_color)
1310 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1311 # Get the attribute or a box (ie. always a val*)
1312 var cret = self.object_type.as_nullable
1313 var res = self.new_var(cret)
1314 res.mcasttype = ret
1315
1316 self.add("{res} = {recv}->attrs[{a.const_color}]; /* {a} on {recv.inspect} */")
1317
1318 # Check for Uninitialized attribute
1319 if not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
1320 self.add("if (unlikely({res} == NULL)) \{")
1321 self.add_abort("Uninitialized attribute {a.name}")
1322 self.add("\}")
1323
1324 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1325 self.compiler.isset_checks_count += 1
1326 self.add("count_isset_checks++;")
1327 end
1328 end
1329
1330 # Return the attribute or its unboxed version
1331 # Note: it is mandatory since we reuse the box on write, we do not whant that the box escapes
1332 return self.autobox(res, ret)
1333 else
1334 var res = self.new_var(ret)
1335 self.add("{res} = {recv}->attrs[{a.const_color}].{ret.ctypename}; /* {a} on {recv.inspect} */")
1336
1337 # Check for Uninitialized attribute
1338 if ret.ctype == "val*" and not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
1339 self.add("if (unlikely({res} == NULL)) \{")
1340 self.add_abort("Uninitialized attribute {a.name}")
1341 self.add("\}")
1342 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1343 self.compiler.isset_checks_count += 1
1344 self.add("count_isset_checks++;")
1345 end
1346 end
1347
1348 return res
1349 end
1350 end
1351
1352 redef fun write_attribute(a, recv, value)
1353 do
1354 self.check_recv_notnull(recv)
1355
1356 # What is the declared type of the attribute?
1357 var mtype = a.intro.static_mtype.as(not null)
1358 var intromclassdef = a.intro.mclassdef
1359 mtype = mtype.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1360
1361 # Adapt the value to the declared type
1362 value = self.autobox(value, mtype)
1363
1364 self.require_declaration(a.const_color)
1365 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1366 var attr = "{recv}->attrs[{a.const_color}]"
1367 if mtype.ctype != "val*" then
1368 assert mtype isa MClassType
1369 # The attribute is primitive, thus we store it in a box
1370 # The trick is to create the box the first time then resuse the box
1371 self.add("if ({attr} != NULL) \{")
1372 self.add("((struct instance_{mtype.c_instance_name}*){attr})->value = {value}; /* {a} on {recv.inspect} */")
1373 self.add("\} else \{")
1374 value = self.autobox(value, self.object_type.as_nullable)
1375 self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
1376 self.add("\}")
1377 else
1378 # The attribute is not primitive, thus store it direclty
1379 self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
1380 end
1381 else
1382 self.add("{recv}->attrs[{a.const_color}].{mtype.ctypename} = {value}; /* {a} on {recv.inspect} */")
1383 end
1384 end
1385
1386 # Check that mtype is a live open type
1387 fun hardening_live_open_type(mtype: MType)
1388 do
1389 if not compiler.modelbuilder.toolcontext.opt_hardening.value then return
1390 self.require_declaration(mtype.const_color)
1391 var col = mtype.const_color
1392 self.add("if({col} == -1) \{")
1393 self.add("PRINT_ERROR(\"Resolution of a dead open type: %s\\n\", \"{mtype.to_s.escape_to_c}\");")
1394 self.add_abort("open type dead")
1395 self.add("\}")
1396 end
1397
1398 # Check that mtype it a pointer to a live cast type
1399 fun hardening_cast_type(t: String)
1400 do
1401 if not compiler.modelbuilder.toolcontext.opt_hardening.value then return
1402 add("if({t} == NULL) \{")
1403 add_abort("cast type null")
1404 add("\}")
1405 add("if({t}->id == -1 || {t}->color == -1) \{")
1406 add("PRINT_ERROR(\"Try to cast on a dead cast type: %s\\n\", {t}->name);")
1407 add_abort("cast type dead")
1408 add("\}")
1409 end
1410
1411 redef fun init_instance(mtype)
1412 do
1413 self.require_declaration("NEW_{mtype.mclass.c_name}")
1414 var compiler = self.compiler
1415 if mtype isa MGenericType and mtype.need_anchor then
1416 hardening_live_open_type(mtype)
1417 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
1418 var recv = self.frame.arguments.first
1419 var recv_type_info = self.type_info(recv)
1420 self.require_declaration(mtype.const_color)
1421 return self.new_expr("NEW_{mtype.mclass.c_name}({recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
1422 end
1423 compiler.undead_types.add(mtype)
1424 self.require_declaration("type_{mtype.c_name}")
1425 return self.new_expr("NEW_{mtype.mclass.c_name}(&type_{mtype.c_name})", mtype)
1426 end
1427
1428 redef fun type_test(value, mtype, tag)
1429 do
1430 self.add("/* {value.inspect} isa {mtype} */")
1431 var compiler = self.compiler
1432
1433 var recv = self.frame.arguments.first
1434 var recv_type_info = self.type_info(recv)
1435
1436 var res = self.new_var(bool_type)
1437
1438 var cltype = self.get_name("cltype")
1439 self.add_decl("int {cltype};")
1440 var idtype = self.get_name("idtype")
1441 self.add_decl("int {idtype};")
1442
1443 var maybe_null = self.maybe_null(value)
1444 var accept_null = "0"
1445 var ntype = mtype
1446 if ntype isa MNullableType then
1447 ntype = ntype.mtype
1448 accept_null = "1"
1449 end
1450
1451 if value.mcasttype.is_subtype(self.frame.mpropdef.mclassdef.mmodule, self.frame.mpropdef.mclassdef.bound_mtype, mtype) then
1452 self.add("{res} = 1; /* easy {value.inspect} isa {mtype}*/")
1453 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1454 self.compiler.count_type_test_skipped[tag] += 1
1455 self.add("count_type_test_skipped_{tag}++;")
1456 end
1457 return res
1458 end
1459
1460 if ntype.need_anchor then
1461 var type_struct = self.get_name("type_struct")
1462 self.add_decl("const struct type* {type_struct};")
1463
1464 # Either with resolution_table with a direct resolution
1465 hardening_live_open_type(mtype)
1466 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
1467 self.require_declaration(mtype.const_color)
1468 self.add("{type_struct} = {recv_type_info}->resolution_table->types[{mtype.const_color}];")
1469 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1470 self.compiler.count_type_test_unresolved[tag] += 1
1471 self.add("count_type_test_unresolved_{tag}++;")
1472 end
1473 hardening_cast_type(type_struct)
1474 self.add("{cltype} = {type_struct}->color;")
1475 self.add("{idtype} = {type_struct}->id;")
1476 if maybe_null and accept_null == "0" then
1477 var is_nullable = self.get_name("is_nullable")
1478 self.add_decl("short int {is_nullable};")
1479 self.add("{is_nullable} = {type_struct}->is_nullable;")
1480 accept_null = is_nullable.to_s
1481 end
1482 else if ntype isa MClassType then
1483 compiler.undead_types.add(mtype)
1484 self.require_declaration("type_{mtype.c_name}")
1485 hardening_cast_type("(&type_{mtype.c_name})")
1486 self.add("{cltype} = type_{mtype.c_name}.color;")
1487 self.add("{idtype} = type_{mtype.c_name}.id;")
1488 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1489 self.compiler.count_type_test_resolved[tag] += 1
1490 self.add("count_type_test_resolved_{tag}++;")
1491 end
1492 else
1493 self.add("PRINT_ERROR(\"NOT YET IMPLEMENTED: type_test(%s, {mtype}).\\n\", \"{value.inspect}\"); show_backtrace(1);")
1494 end
1495
1496 # check color is in table
1497 if maybe_null then
1498 self.add("if({value} == NULL) \{")
1499 self.add("{res} = {accept_null};")
1500 self.add("\} else \{")
1501 end
1502 var value_type_info = self.type_info(value)
1503 self.add("if({cltype} >= {value_type_info}->table_size) \{")
1504 self.add("{res} = 0;")
1505 self.add("\} else \{")
1506 self.add("{res} = {value_type_info}->type_table[{cltype}] == {idtype};")
1507 self.add("\}")
1508 if maybe_null then
1509 self.add("\}")
1510 end
1511
1512 return res
1513 end
1514
1515 redef fun is_same_type_test(value1, value2)
1516 do
1517 var res = self.new_var(bool_type)
1518 # Swap values to be symetric
1519 if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then
1520 var tmp = value1
1521 value1 = value2
1522 value2 = tmp
1523 end
1524 if value1.mtype.ctype != "val*" then
1525 if value2.mtype == value1.mtype then
1526 self.add("{res} = 1; /* is_same_type_test: compatible types {value1.mtype} vs. {value2.mtype} */")
1527 else if value2.mtype.ctype != "val*" then
1528 self.add("{res} = 0; /* is_same_type_test: incompatible types {value1.mtype} vs. {value2.mtype}*/")
1529 else
1530 var mtype1 = value1.mtype.as(MClassType)
1531 self.require_declaration("class_{mtype1.c_name}")
1532 self.add("{res} = ({value2} != NULL) && ({value2}->class == &class_{mtype1.c_name}); /* is_same_type_test */")
1533 end
1534 else
1535 self.add("{res} = ({value1} == {value2}) || ({value1} != NULL && {value2} != NULL && {value1}->class == {value2}->class); /* is_same_type_test */")
1536 end
1537 return res
1538 end
1539
1540 redef fun class_name_string(value)
1541 do
1542 var res = self.get_name("var_class_name")
1543 self.add_decl("const char* {res};")
1544 if value.mtype.ctype == "val*" then
1545 self.add "{res} = {value} == NULL ? \"null\" : {value}->type->name;"
1546 else if value.mtype isa MClassType and value.mtype.as(MClassType).mclass.kind == extern_kind then
1547 self.add "{res} = \"{value.mtype.as(MClassType).mclass}\";"
1548 else
1549 self.require_declaration("type_{value.mtype.c_name}")
1550 self.add "{res} = type_{value.mtype.c_name}.name;"
1551 end
1552 return res
1553 end
1554
1555 redef fun equal_test(value1, value2)
1556 do
1557 var res = self.new_var(bool_type)
1558 if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then
1559 var tmp = value1
1560 value1 = value2
1561 value2 = tmp
1562 end
1563 if value1.mtype.ctype != "val*" then
1564 if value2.mtype == value1.mtype then
1565 self.add("{res} = {value1} == {value2};")
1566 else if value2.mtype.ctype != "val*" then
1567 self.add("{res} = 0; /* incompatible types {value1.mtype} vs. {value2.mtype}*/")
1568 else
1569 var mtype1 = value1.mtype.as(MClassType)
1570 self.require_declaration("class_{mtype1.c_name}")
1571 self.add("{res} = ({value2} != NULL) && ({value2}->class == &class_{mtype1.c_name});")
1572 self.add("if ({res}) \{")
1573 self.add("{res} = ({self.autobox(value2, value1.mtype)} == {value1});")
1574 self.add("\}")
1575 end
1576 return res
1577 end
1578 var maybe_null = true
1579 var test = new Array[String]
1580 var t1 = value1.mcasttype
1581 if t1 isa MNullableType then
1582 test.add("{value1} != NULL")
1583 t1 = t1.mtype
1584 else
1585 maybe_null = false
1586 end
1587 var t2 = value2.mcasttype
1588 if t2 isa MNullableType then
1589 test.add("{value2} != NULL")
1590 t2 = t2.mtype
1591 else
1592 maybe_null = false
1593 end
1594
1595 var incompatible = false
1596 var primitive
1597 if t1.ctype != "val*" then
1598 primitive = t1
1599 if t1 == t2 then
1600 # No need to compare class
1601 else if t2.ctype != "val*" then
1602 incompatible = true
1603 else if can_be_primitive(value2) then
1604 test.add("{value1}->class == {value2}->class")
1605 else
1606 incompatible = true
1607 end
1608 else if t2.ctype != "val*" then
1609 primitive = t2
1610 if can_be_primitive(value1) then
1611 test.add("{value1}->class == {value2}->class")
1612 else
1613 incompatible = true
1614 end
1615 else
1616 primitive = null
1617 end
1618
1619 if incompatible then
1620 if maybe_null then
1621 self.add("{res} = {value1} == {value2}; /* incompatible types {t1} vs. {t2}; but may be NULL*/")
1622 return res
1623 else
1624 self.add("{res} = 0; /* incompatible types {t1} vs. {t2}; cannot be NULL */")
1625 return res
1626 end
1627 end
1628 if primitive != null then
1629 test.add("((struct instance_{primitive.c_instance_name}*){value1})->value == ((struct instance_{primitive.c_instance_name}*){value2})->value")
1630 else if can_be_primitive(value1) and can_be_primitive(value2) then
1631 test.add("{value1}->class == {value2}->class")
1632 var s = new Array[String]
1633 for t, v in self.compiler.box_kinds do
1634 s.add "({value1}->class->box_kind == {v} && ((struct instance_{t.c_instance_name}*){value1})->value == ((struct instance_{t.c_instance_name}*){value2})->value)"
1635 end
1636 test.add("({s.join(" || ")})")
1637 else
1638 self.add("{res} = {value1} == {value2};")
1639 return res
1640 end
1641 self.add("{res} = {value1} == {value2} || ({test.join(" && ")});")
1642 return res
1643 end
1644
1645 fun can_be_primitive(value: RuntimeVariable): Bool
1646 do
1647 var t = value.mcasttype
1648 if t isa MNullableType then t = t.mtype
1649 if not t isa MClassType then return false
1650 var k = t.mclass.kind
1651 return k == interface_kind or t.ctype != "val*"
1652 end
1653
1654 fun maybe_null(value: RuntimeVariable): Bool
1655 do
1656 var t = value.mcasttype
1657 return t isa MNullableType or t isa MNullType
1658 end
1659
1660 redef fun array_instance(array, elttype)
1661 do
1662 var nclass = self.get_class("NativeArray")
1663 var arrayclass = self.get_class("Array")
1664 var arraytype = arrayclass.get_mtype([elttype])
1665 var res = self.init_instance(arraytype)
1666 self.add("\{ /* {res} = array_instance Array[{elttype}] */")
1667 var length = self.int_instance(array.length)
1668 var nat = native_array_instance(elttype, length)
1669 for i in [0..array.length[ do
1670 var r = self.autobox(array[i], self.object_type)
1671 self.add("((struct instance_{nclass.c_name}*){nat})->values[{i}] = (val*) {r};")
1672 end
1673 self.send(self.get_property("with_native", arrayclass.intro.bound_mtype), [res, nat, length])
1674 self.add("\}")
1675 return res
1676 end
1677
1678 redef fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable
1679 do
1680 var mtype = self.get_class("NativeArray").get_mtype([elttype])
1681 self.require_declaration("NEW_{mtype.mclass.c_name}")
1682 assert mtype isa MGenericType
1683 var compiler = self.compiler
1684 if mtype.need_anchor then
1685 hardening_live_open_type(mtype)
1686 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
1687 var recv = self.frame.arguments.first
1688 var recv_type_info = self.type_info(recv)
1689 self.require_declaration(mtype.const_color)
1690 return self.new_expr("NEW_{mtype.mclass.c_name}({length}, {recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
1691 end
1692 compiler.undead_types.add(mtype)
1693 self.require_declaration("type_{mtype.c_name}")
1694 return self.new_expr("NEW_{mtype.mclass.c_name}({length}, &type_{mtype.c_name})", mtype)
1695 end
1696
1697 redef fun native_array_def(pname, ret_type, arguments)
1698 do
1699 var elttype = arguments.first.mtype
1700 var nclass = self.get_class("NativeArray")
1701 var recv = "((struct instance_{nclass.c_instance_name}*){arguments[0]})->values"
1702 if pname == "[]" then
1703 self.ret(self.new_expr("{recv}[{arguments[1]}]", ret_type.as(not null)))
1704 return
1705 else if pname == "[]=" then
1706 self.add("{recv}[{arguments[1]}]={arguments[2]};")
1707 return
1708 else if pname == "length" then
1709 self.ret(self.new_expr("((struct instance_{nclass.c_instance_name}*){arguments[0]})->length", ret_type.as(not null)))
1710 return
1711 else if pname == "copy_to" then
1712 var recv1 = "((struct instance_{nclass.c_instance_name}*){arguments[1]})->values"
1713 self.add("memcpy({recv1}, {recv}, {arguments[2]}*sizeof({elttype.ctype}));")
1714 return
1715 end
1716 end
1717
1718 redef fun calloc_array(ret_type, arguments)
1719 do
1720 var mclass = self.get_class("ArrayCapable")
1721 var ft = mclass.mclass_type.arguments.first.as(MParameterType)
1722 var res = self.native_array_instance(ft, arguments[1])
1723 self.ret(res)
1724 end
1725
1726 fun link_unresolved_type(mclassdef: MClassDef, mtype: MType) do
1727 assert mtype.need_anchor
1728 var compiler = self.compiler
1729 if not compiler.live_unresolved_types.has_key(self.frame.mpropdef.mclassdef) then
1730 compiler.live_unresolved_types[self.frame.mpropdef.mclassdef] = new HashSet[MType]
1731 end
1732 compiler.live_unresolved_types[self.frame.mpropdef.mclassdef].add(mtype)
1733 end
1734 end
1735
1736 redef class MMethodDef
1737 fun separate_runtime_function: AbstractRuntimeFunction
1738 do
1739 var res = self.separate_runtime_function_cache
1740 if res == null then
1741 res = new SeparateRuntimeFunction(self)
1742 self.separate_runtime_function_cache = res
1743 end
1744 return res
1745 end
1746 private var separate_runtime_function_cache: nullable SeparateRuntimeFunction
1747
1748 fun virtual_runtime_function: AbstractRuntimeFunction
1749 do
1750 var res = self.virtual_runtime_function_cache
1751 if res == null then
1752 res = new VirtualRuntimeFunction(self)
1753 self.virtual_runtime_function_cache = res
1754 end
1755 return res
1756 end
1757 private var virtual_runtime_function_cache: nullable VirtualRuntimeFunction
1758 end
1759
1760 # The C function associated to a methoddef separately compiled
1761 class SeparateRuntimeFunction
1762 super AbstractRuntimeFunction
1763
1764 redef fun build_c_name: String do return "{mmethoddef.c_name}"
1765
1766 redef fun to_s do return self.mmethoddef.to_s
1767
1768 redef fun compile_to_c(compiler)
1769 do
1770 var mmethoddef = self.mmethoddef
1771
1772 var recv = self.mmethoddef.mclassdef.bound_mtype
1773 var v = compiler.new_visitor
1774 var selfvar = new RuntimeVariable("self", recv, recv)
1775 var arguments = new Array[RuntimeVariable]
1776 var frame = new Frame(v, mmethoddef, recv, arguments)
1777 v.frame = frame
1778
1779 var msignature = mmethoddef.msignature.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
1780
1781 var sig = new FlatBuffer
1782 var comment = new FlatBuffer
1783 var ret = msignature.return_mtype
1784 if ret != null then
1785 sig.append("{ret.ctype} ")
1786 else if mmethoddef.mproperty.is_new then
1787 ret = recv
1788 sig.append("{ret.ctype} ")
1789 else
1790 sig.append("void ")
1791 end
1792 sig.append(self.c_name)
1793 sig.append("({selfvar.mtype.ctype} {selfvar}")
1794 comment.append("({selfvar}: {selfvar.mtype}")
1795 arguments.add(selfvar)
1796 for i in [0..msignature.arity[ do
1797 var mtype = msignature.mparameters[i].mtype
1798 if i == msignature.vararg_rank then
1799 mtype = v.get_class("Array").get_mtype([mtype])
1800 end
1801 comment.append(", {mtype}")
1802 sig.append(", {mtype.ctype} p{i}")
1803 var argvar = new RuntimeVariable("p{i}", mtype, mtype)
1804 arguments.add(argvar)
1805 end
1806 sig.append(")")
1807 comment.append(")")
1808 if ret != null then
1809 comment.append(": {ret}")
1810 end
1811 compiler.provide_declaration(self.c_name, "{sig};")
1812
1813 v.add_decl("/* method {self} for {comment} */")
1814 v.add_decl("{sig} \{")
1815 if ret != null then
1816 frame.returnvar = v.new_var(ret)
1817 end
1818 frame.returnlabel = v.get_name("RET_LABEL")
1819
1820 if recv != arguments.first.mtype then
1821 #print "{self} {recv} {arguments.first}"
1822 end
1823 mmethoddef.compile_inside_to_c(v, arguments)
1824
1825 v.add("{frame.returnlabel.as(not null)}:;")
1826 if ret != null then
1827 v.add("return {frame.returnvar.as(not null)};")
1828 end
1829 v.add("\}")
1830 if not self.c_name.has_substring("VIRTUAL", 0) then compiler.names[self.c_name] = "{mmethoddef.mclassdef.mmodule.name}::{mmethoddef.mclassdef.mclass.name}::{mmethoddef.mproperty.name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
1831 end
1832 end
1833
1834 # The C function associated to a methoddef on a primitive type, stored into a VFT of a class
1835 # The first parameter (the reciever) is always typed by val* in order to accept an object value
1836 class VirtualRuntimeFunction
1837 super AbstractRuntimeFunction
1838
1839 redef fun build_c_name: String do return "VIRTUAL_{mmethoddef.c_name}"
1840
1841 redef fun to_s do return self.mmethoddef.to_s
1842
1843 redef fun compile_to_c(compiler)
1844 do
1845 var mmethoddef = self.mmethoddef
1846
1847 var recv = self.mmethoddef.mclassdef.bound_mtype
1848 var v = compiler.new_visitor
1849 var selfvar = new RuntimeVariable("self", v.object_type, recv)
1850 var arguments = new Array[RuntimeVariable]
1851 var frame = new Frame(v, mmethoddef, recv, arguments)
1852 v.frame = frame
1853
1854 var sig = new FlatBuffer
1855 var comment = new FlatBuffer
1856
1857 # Because the function is virtual, the signature must match the one of the original class
1858 var intromclassdef = self.mmethoddef.mproperty.intro.mclassdef
1859 var msignature = mmethoddef.mproperty.intro.msignature.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1860 var ret = msignature.return_mtype
1861 if ret != null then
1862 sig.append("{ret.ctype} ")
1863 else if mmethoddef.mproperty.is_new then
1864 ret = recv
1865 sig.append("{ret.ctype} ")
1866 else
1867 sig.append("void ")
1868 end
1869 sig.append(self.c_name)
1870 sig.append("({selfvar.mtype.ctype} {selfvar}")
1871 comment.append("({selfvar}: {selfvar.mtype}")
1872 arguments.add(selfvar)
1873 for i in [0..msignature.arity[ do
1874 var mtype = msignature.mparameters[i].mtype
1875 if i == msignature.vararg_rank then
1876 mtype = v.get_class("Array").get_mtype([mtype])
1877 end
1878 comment.append(", {mtype}")
1879 sig.append(", {mtype.ctype} p{i}")
1880 var argvar = new RuntimeVariable("p{i}", mtype, mtype)
1881 arguments.add(argvar)
1882 end
1883 sig.append(")")
1884 comment.append(")")
1885 if ret != null then
1886 comment.append(": {ret}")
1887 end
1888 compiler.provide_declaration(self.c_name, "{sig};")
1889
1890 v.add_decl("/* method {self} for {comment} */")
1891 v.add_decl("{sig} \{")
1892 if ret != null then
1893 frame.returnvar = v.new_var(ret)
1894 end
1895 frame.returnlabel = v.get_name("RET_LABEL")
1896
1897 var subret = v.call(mmethoddef, recv, arguments)
1898 if ret != null then
1899 assert subret != null
1900 v.assign(frame.returnvar.as(not null), subret)
1901 end
1902
1903 v.add("{frame.returnlabel.as(not null)}:;")
1904 if ret != null then
1905 v.add("return {frame.returnvar.as(not null)};")
1906 end
1907 v.add("\}")
1908 if not self.c_name.has_substring("VIRTUAL", 0) then compiler.names[self.c_name] = "{mmethoddef.mclassdef.mmodule.name}::{mmethoddef.mclassdef.mclass.name}::{mmethoddef.mproperty.name} ({mmethoddef.location.file.filename}--{mmethoddef.location.line_start})"
1909 end
1910
1911 # TODO ?
1912 redef fun call(v, arguments) do abort
1913 end
1914
1915 redef class MType
1916 fun const_color: String do return "COLOR_{c_name}"
1917
1918 # C name of the instance type to use
1919 fun c_instance_name: String do return c_name
1920 end
1921
1922 redef class MClassType
1923 redef fun c_instance_name do return mclass.c_instance_name
1924 end
1925
1926 redef class MClass
1927 # Extern classes use the C instance of kernel::Pointer
1928 fun c_instance_name: String
1929 do
1930 if kind == extern_kind then
1931 return "kernel__Pointer"
1932 else return c_name
1933 end
1934 end
1935
1936 interface PropertyLayoutElement end
1937
1938 redef class MProperty
1939 super PropertyLayoutElement
1940 fun const_color: String do return "COLOR_{c_name}"
1941 end
1942
1943 redef class MPropDef
1944 super PropertyLayoutElement
1945 fun const_color: String do return "COLOR_{c_name}"
1946 end