pep8analysis: add doc to web interface
[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*" then
995 return value
996 else
997 # Bad things will appen!
998 var res = self.new_var(mtype)
999 self.add("/* {res} left unintialized (cannot convert {value.mtype} to {mtype}) */")
1000 self.add("PRINT_ERROR(\"Cast error: Cannot cast %s to %s.\\n\", \"{value.mtype}\", \"{mtype}\"); show_backtrace(1);")
1001 return res
1002 end
1003 end
1004
1005 # Return a C expression returning the runtime type structure of the value
1006 # The point of the method is to works also with primitives types.
1007 fun type_info(value: RuntimeVariable): String
1008 do
1009 if value.mtype.ctype == "val*" then
1010 return "{value}->type"
1011 else
1012 compiler.undead_types.add(value.mtype)
1013 self.require_declaration("type_{value.mtype.c_name}")
1014 return "(&type_{value.mtype.c_name})"
1015 end
1016 end
1017
1018 redef fun compile_callsite(callsite, args)
1019 do
1020 var rta = compiler.runtime_type_analysis
1021 var recv = args.first.mtype
1022 if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null then
1023 var tgs = rta.live_targets(callsite)
1024 if tgs.length == 1 then
1025 # DIRECT CALL
1026 var mmethod = callsite.mproperty
1027 self.varargize(mmethod.intro, mmethod.intro.msignature.as(not null), args)
1028 var res0 = before_send(mmethod, args)
1029 var res = call(tgs.first, tgs.first.mclassdef.bound_mtype, args)
1030 if res0 != null then
1031 assert res != null
1032 self.assign(res0, res)
1033 res = res0
1034 end
1035 add("\}") # close the before_send
1036 return res
1037 end
1038 end
1039 return super
1040 end
1041 redef fun send(mmethod, arguments)
1042 do
1043 self.varargize(mmethod.intro, mmethod.intro.msignature.as(not null), arguments)
1044
1045 if arguments.first.mcasttype.ctype != "val*" then
1046 # In order to shortcut the primitive, we need to find the most specific method
1047 # Howverr, because of performance (no flattening), we always work on the realmainmodule
1048 var m = self.compiler.mainmodule
1049 self.compiler.mainmodule = self.compiler.realmainmodule
1050 var res = self.monomorphic_send(mmethod, arguments.first.mcasttype, arguments)
1051 self.compiler.mainmodule = m
1052 return res
1053 end
1054
1055 return table_send(mmethod, arguments, mmethod.const_color)
1056 end
1057
1058 # Handel common special cases before doing the effective method invocation
1059 # This methods handle the `==` and `!=` methods and the case of the null receiver.
1060 # Note: a { is open in the generated C, that enclose and protect the effective method invocation.
1061 # Client must not forget to close the } after them.
1062 #
1063 # The value returned is the result of the common special cases.
1064 # If not null, client must compine it with the result of their own effective method invocation.
1065 #
1066 # If `before_send` can shortcut the whole message sending, a dummy `if(0){`
1067 # is generated to cancel the effective method invocation that will follow
1068 # TODO: find a better approach
1069 private fun before_send(mmethod: MMethod, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1070 do
1071 var res: nullable RuntimeVariable = null
1072 var recv = arguments.first
1073 var consider_null = not self.compiler.modelbuilder.toolcontext.opt_no_check_other.value or mmethod.name == "==" or mmethod.name == "!="
1074 var maybenull = recv.mcasttype isa MNullableType and consider_null
1075 if maybenull then
1076 self.add("if ({recv} == NULL) \{")
1077 if mmethod.name == "==" then
1078 res = self.new_var(bool_type)
1079 var arg = arguments[1]
1080 if arg.mcasttype isa MNullableType then
1081 self.add("{res} = ({arg} == NULL);")
1082 else if arg.mcasttype isa MNullType then
1083 self.add("{res} = 1; /* is null */")
1084 else
1085 self.add("{res} = 0; /* {arg.inspect} cannot be null */")
1086 end
1087 else if mmethod.name == "!=" then
1088 res = self.new_var(bool_type)
1089 var arg = arguments[1]
1090 if arg.mcasttype isa MNullableType then
1091 self.add("{res} = ({arg} != NULL);")
1092 else if arg.mcasttype isa MNullType then
1093 self.add("{res} = 0; /* is null */")
1094 else
1095 self.add("{res} = 1; /* {arg.inspect} cannot be null */")
1096 end
1097 else
1098 self.add_abort("Receiver is null")
1099 end
1100 self.add("\} else \{")
1101 else
1102 self.add("\{")
1103 end
1104 if not self.compiler.modelbuilder.toolcontext.opt_no_shortcut_equate.value and (mmethod.name == "==" or mmethod.name == "!=") then
1105 if res == null then res = self.new_var(bool_type)
1106 # Recv is not null, thus is arg is, it is easy to conclude (and respect the invariants)
1107 var arg = arguments[1]
1108 if arg.mcasttype isa MNullType then
1109 if mmethod.name == "==" then
1110 self.add("{res} = 0; /* arg is null but recv is not */")
1111 else
1112 self.add("{res} = 1; /* arg is null and recv is not */")
1113 end
1114 self.add("\}") # closes the null case
1115 self.add("if (0) \{") # what follow is useless, CC will drop it
1116 end
1117 end
1118 return res
1119 end
1120
1121 private fun table_send(mmethod: MMethod, arguments: Array[RuntimeVariable], const_color: String): nullable RuntimeVariable
1122 do
1123 compiler.modelbuilder.nb_invok_by_tables += 1
1124 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_tables++;")
1125
1126 assert arguments.length == mmethod.intro.msignature.arity + 1 else debug("Invalid arity for {mmethod}. {arguments.length} arguments given.")
1127 var recv = arguments.first
1128
1129 var res0 = before_send(mmethod, arguments)
1130
1131 var res: nullable RuntimeVariable
1132 var msignature = mmethod.intro.msignature.resolve_for(mmethod.intro.mclassdef.bound_mtype, mmethod.intro.mclassdef.bound_mtype, mmethod.intro.mclassdef.mmodule, true)
1133 var ret = msignature.return_mtype
1134 if mmethod.is_new then
1135 ret = arguments.first.mtype
1136 res = self.new_var(ret)
1137 else if ret == null then
1138 res = null
1139 else
1140 res = self.new_var(ret)
1141 end
1142
1143 var s = new FlatBuffer
1144 var ss = new FlatBuffer
1145
1146 s.append("val*")
1147 ss.append("{recv}")
1148 for i in [0..msignature.arity[ do
1149 var a = arguments[i+1]
1150 var t = msignature.mparameters[i].mtype
1151 if i == msignature.vararg_rank then
1152 t = arguments[i+1].mcasttype
1153 end
1154 s.append(", {t.ctype}")
1155 a = self.autobox(a, t)
1156 ss.append(", {a}")
1157 end
1158
1159
1160 var r
1161 if ret == null then r = "void" else r = ret.ctype
1162 self.require_declaration(const_color)
1163 var call = "(({r} (*)({s}))({arguments.first}->class->vft[{const_color}]))({ss}) /* {mmethod} on {arguments.first.inspect}*/"
1164
1165 if res != null then
1166 self.add("{res} = {call};")
1167 else
1168 self.add("{call};")
1169 end
1170
1171 if res0 != null then
1172 assert res != null
1173 assign(res0,res)
1174 res = res0
1175 end
1176
1177 self.add("\}") # closes the null case
1178
1179 return res
1180 end
1181
1182 redef fun call(mmethoddef, recvtype, arguments)
1183 do
1184 assert arguments.length == mmethoddef.msignature.arity + 1 else debug("Invalid arity for {mmethoddef}. {arguments.length} arguments given.")
1185
1186 var res: nullable RuntimeVariable
1187 var ret = mmethoddef.msignature.return_mtype
1188 if mmethoddef.mproperty.is_new then
1189 ret = arguments.first.mtype
1190 res = self.new_var(ret)
1191 else if ret == null then
1192 res = null
1193 else
1194 ret = ret.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
1195 res = self.new_var(ret)
1196 end
1197
1198 if (mmethoddef.is_intern and not compiler.modelbuilder.toolcontext.opt_no_inline_intern.value) or
1199 (compiler.modelbuilder.toolcontext.opt_inline_some_methods.value and mmethoddef.can_inline(self)) then
1200 compiler.modelbuilder.nb_invok_by_inline += 1
1201 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_inline++;")
1202 var frame = new Frame(self, mmethoddef, recvtype, arguments)
1203 frame.returnlabel = self.get_name("RET_LABEL")
1204 frame.returnvar = res
1205 var old_frame = self.frame
1206 self.frame = frame
1207 self.add("\{ /* Inline {mmethoddef} ({arguments.join(",")}) on {arguments.first.inspect} */")
1208 mmethoddef.compile_inside_to_c(self, arguments)
1209 self.add("{frame.returnlabel.as(not null)}:(void)0;")
1210 self.add("\}")
1211 self.frame = old_frame
1212 return res
1213 end
1214 compiler.modelbuilder.nb_invok_by_direct += 1
1215 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_direct++;")
1216
1217 # Autobox arguments
1218 self.adapt_signature(mmethoddef, arguments)
1219
1220 self.require_declaration(mmethoddef.c_name)
1221 if res == null then
1222 self.add("{mmethoddef.c_name}({arguments.join(", ")}); /* Direct call {mmethoddef} on {arguments.first.inspect}*/")
1223 return null
1224 else
1225 self.add("{res} = {mmethoddef.c_name}({arguments.join(", ")});")
1226 end
1227
1228 return res
1229 end
1230
1231 redef fun supercall(m: MMethodDef, recvtype: MClassType, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1232 do
1233 if arguments.first.mcasttype.ctype != "val*" then
1234 # In order to shortcut the primitive, we need to find the most specific method
1235 # However, because of performance (no flattening), we always work on the realmainmodule
1236 var main = self.compiler.mainmodule
1237 self.compiler.mainmodule = self.compiler.realmainmodule
1238 var res = self.monomorphic_super_send(m, recvtype, arguments)
1239 self.compiler.mainmodule = main
1240 return res
1241 end
1242 return table_send(m.mproperty, arguments, m.const_color)
1243 end
1244
1245 redef fun vararg_instance(mpropdef, recv, varargs, elttype)
1246 do
1247 # A vararg must be stored into an new array
1248 # The trick is that the dymaic type of the array may depends on the receiver
1249 # of the method (ie recv) if the static type is unresolved
1250 # This is more complex than usual because the unresolved type must not be resolved
1251 # with the current receiver (ie self).
1252 # Therefore to isolate the resolution from self, a local Frame is created.
1253 # One can see this implementation as an inlined method of the receiver whose only
1254 # job is to allocate the array
1255 var old_frame = self.frame
1256 var frame = new Frame(self, mpropdef, mpropdef.mclassdef.bound_mtype, [recv])
1257 self.frame = frame
1258 #print "required Array[{elttype}] for recv {recv.inspect}. bound=Array[{self.resolve_for(elttype, recv)}]. selfvar={frame.arguments.first.inspect}"
1259 var res = self.array_instance(varargs, elttype)
1260 self.frame = old_frame
1261 return res
1262 end
1263
1264 redef fun isset_attribute(a, recv)
1265 do
1266 self.check_recv_notnull(recv)
1267 var res = self.new_var(bool_type)
1268
1269 # What is the declared type of the attribute?
1270 var mtype = a.intro.static_mtype.as(not null)
1271 var intromclassdef = a.intro.mclassdef
1272 mtype = mtype.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1273
1274 if mtype isa MNullableType then
1275 self.add("{res} = 1; /* easy isset: {a} on {recv.inspect} */")
1276 return res
1277 end
1278
1279 self.require_declaration(a.const_color)
1280 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1281 self.add("{res} = {recv}->attrs[{a.const_color}] != NULL; /* {a} on {recv.inspect}*/")
1282 else
1283
1284 if mtype.ctype == "val*" then
1285 self.add("{res} = {recv}->attrs[{a.const_color}].val != NULL; /* {a} on {recv.inspect} */")
1286 else
1287 self.add("{res} = 1; /* NOT YET IMPLEMENTED: isset of primitives: {a} on {recv.inspect} */")
1288 end
1289 end
1290 return res
1291 end
1292
1293 redef fun read_attribute(a, recv)
1294 do
1295 self.check_recv_notnull(recv)
1296
1297 # What is the declared type of the attribute?
1298 var ret = a.intro.static_mtype.as(not null)
1299 var intromclassdef = a.intro.mclassdef
1300 ret = ret.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1301
1302 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1303 self.compiler.attr_read_count += 1
1304 self.add("count_attr_reads++;")
1305 end
1306
1307 self.require_declaration(a.const_color)
1308 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1309 # Get the attribute or a box (ie. always a val*)
1310 var cret = self.object_type.as_nullable
1311 var res = self.new_var(cret)
1312 res.mcasttype = ret
1313
1314 self.add("{res} = {recv}->attrs[{a.const_color}]; /* {a} on {recv.inspect} */")
1315
1316 # Check for Uninitialized attribute
1317 if not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
1318 self.add("if (unlikely({res} == NULL)) \{")
1319 self.add_abort("Uninitialized attribute {a.name}")
1320 self.add("\}")
1321
1322 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1323 self.compiler.isset_checks_count += 1
1324 self.add("count_isset_checks++;")
1325 end
1326 end
1327
1328 # Return the attribute or its unboxed version
1329 # Note: it is mandatory since we reuse the box on write, we do not whant that the box escapes
1330 return self.autobox(res, ret)
1331 else
1332 var res = self.new_var(ret)
1333 self.add("{res} = {recv}->attrs[{a.const_color}].{ret.ctypename}; /* {a} on {recv.inspect} */")
1334
1335 # Check for Uninitialized attribute
1336 if ret.ctype == "val*" and not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
1337 self.add("if (unlikely({res} == NULL)) \{")
1338 self.add_abort("Uninitialized attribute {a.name}")
1339 self.add("\}")
1340 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1341 self.compiler.isset_checks_count += 1
1342 self.add("count_isset_checks++;")
1343 end
1344 end
1345
1346 return res
1347 end
1348 end
1349
1350 redef fun write_attribute(a, recv, value)
1351 do
1352 self.check_recv_notnull(recv)
1353
1354 # What is the declared type of the attribute?
1355 var mtype = a.intro.static_mtype.as(not null)
1356 var intromclassdef = a.intro.mclassdef
1357 mtype = mtype.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1358
1359 # Adapt the value to the declared type
1360 value = self.autobox(value, mtype)
1361
1362 self.require_declaration(a.const_color)
1363 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1364 var attr = "{recv}->attrs[{a.const_color}]"
1365 if mtype.ctype != "val*" then
1366 assert mtype isa MClassType
1367 # The attribute is primitive, thus we store it in a box
1368 # The trick is to create the box the first time then resuse the box
1369 self.add("if ({attr} != NULL) \{")
1370 self.add("((struct instance_{mtype.c_instance_name}*){attr})->value = {value}; /* {a} on {recv.inspect} */")
1371 self.add("\} else \{")
1372 value = self.autobox(value, self.object_type.as_nullable)
1373 self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
1374 self.add("\}")
1375 else
1376 # The attribute is not primitive, thus store it direclty
1377 self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
1378 end
1379 else
1380 self.add("{recv}->attrs[{a.const_color}].{mtype.ctypename} = {value}; /* {a} on {recv.inspect} */")
1381 end
1382 end
1383
1384 # Check that mtype is a live open type
1385 fun hardening_live_open_type(mtype: MType)
1386 do
1387 if not compiler.modelbuilder.toolcontext.opt_hardening.value then return
1388 self.require_declaration(mtype.const_color)
1389 var col = mtype.const_color
1390 self.add("if({col} == -1) \{")
1391 self.add("PRINT_ERROR(\"Resolution of a dead open type: %s\\n\", \"{mtype.to_s.escape_to_c}\");")
1392 self.add_abort("open type dead")
1393 self.add("\}")
1394 end
1395
1396 # Check that mtype it a pointer to a live cast type
1397 fun hardening_cast_type(t: String)
1398 do
1399 if not compiler.modelbuilder.toolcontext.opt_hardening.value then return
1400 add("if({t} == NULL) \{")
1401 add_abort("cast type null")
1402 add("\}")
1403 add("if({t}->id == -1 || {t}->color == -1) \{")
1404 add("PRINT_ERROR(\"Try to cast on a dead cast type: %s\\n\", {t}->name);")
1405 add_abort("cast type dead")
1406 add("\}")
1407 end
1408
1409 redef fun init_instance(mtype)
1410 do
1411 self.require_declaration("NEW_{mtype.mclass.c_name}")
1412 var compiler = self.compiler
1413 if mtype isa MGenericType and mtype.need_anchor then
1414 hardening_live_open_type(mtype)
1415 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
1416 var recv = self.frame.arguments.first
1417 var recv_type_info = self.type_info(recv)
1418 self.require_declaration(mtype.const_color)
1419 return self.new_expr("NEW_{mtype.mclass.c_name}({recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
1420 end
1421 compiler.undead_types.add(mtype)
1422 self.require_declaration("type_{mtype.c_name}")
1423 return self.new_expr("NEW_{mtype.mclass.c_name}(&type_{mtype.c_name})", mtype)
1424 end
1425
1426 redef fun type_test(value, mtype, tag)
1427 do
1428 self.add("/* {value.inspect} isa {mtype} */")
1429 var compiler = self.compiler
1430
1431 var recv = self.frame.arguments.first
1432 var recv_type_info = self.type_info(recv)
1433
1434 var res = self.new_var(bool_type)
1435
1436 var cltype = self.get_name("cltype")
1437 self.add_decl("int {cltype};")
1438 var idtype = self.get_name("idtype")
1439 self.add_decl("int {idtype};")
1440
1441 var maybe_null = self.maybe_null(value)
1442 var accept_null = "0"
1443 var ntype = mtype
1444 if ntype isa MNullableType then
1445 ntype = ntype.mtype
1446 accept_null = "1"
1447 end
1448
1449 if value.mcasttype.is_subtype(self.frame.mpropdef.mclassdef.mmodule, self.frame.mpropdef.mclassdef.bound_mtype, mtype) then
1450 self.add("{res} = 1; /* easy {value.inspect} isa {mtype}*/")
1451 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1452 self.compiler.count_type_test_skipped[tag] += 1
1453 self.add("count_type_test_skipped_{tag}++;")
1454 end
1455 return res
1456 end
1457
1458 if ntype.need_anchor then
1459 var type_struct = self.get_name("type_struct")
1460 self.add_decl("const struct type* {type_struct};")
1461
1462 # Either with resolution_table with a direct resolution
1463 hardening_live_open_type(mtype)
1464 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
1465 self.require_declaration(mtype.const_color)
1466 self.add("{type_struct} = {recv_type_info}->resolution_table->types[{mtype.const_color}];")
1467 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1468 self.compiler.count_type_test_unresolved[tag] += 1
1469 self.add("count_type_test_unresolved_{tag}++;")
1470 end
1471 hardening_cast_type(type_struct)
1472 self.add("{cltype} = {type_struct}->color;")
1473 self.add("{idtype} = {type_struct}->id;")
1474 if maybe_null and accept_null == "0" then
1475 var is_nullable = self.get_name("is_nullable")
1476 self.add_decl("short int {is_nullable};")
1477 self.add("{is_nullable} = {type_struct}->is_nullable;")
1478 accept_null = is_nullable.to_s
1479 end
1480 else if ntype isa MClassType then
1481 compiler.undead_types.add(mtype)
1482 self.require_declaration("type_{mtype.c_name}")
1483 hardening_cast_type("(&type_{mtype.c_name})")
1484 self.add("{cltype} = type_{mtype.c_name}.color;")
1485 self.add("{idtype} = type_{mtype.c_name}.id;")
1486 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1487 self.compiler.count_type_test_resolved[tag] += 1
1488 self.add("count_type_test_resolved_{tag}++;")
1489 end
1490 else
1491 self.add("PRINT_ERROR(\"NOT YET IMPLEMENTED: type_test(%s, {mtype}).\\n\", \"{value.inspect}\"); show_backtrace(1);")
1492 end
1493
1494 # check color is in table
1495 if maybe_null then
1496 self.add("if({value} == NULL) \{")
1497 self.add("{res} = {accept_null};")
1498 self.add("\} else \{")
1499 end
1500 var value_type_info = self.type_info(value)
1501 self.add("if({cltype} >= {value_type_info}->table_size) \{")
1502 self.add("{res} = 0;")
1503 self.add("\} else \{")
1504 self.add("{res} = {value_type_info}->type_table[{cltype}] == {idtype};")
1505 self.add("\}")
1506 if maybe_null then
1507 self.add("\}")
1508 end
1509
1510 return res
1511 end
1512
1513 redef fun is_same_type_test(value1, value2)
1514 do
1515 var res = self.new_var(bool_type)
1516 # Swap values to be symetric
1517 if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then
1518 var tmp = value1
1519 value1 = value2
1520 value2 = tmp
1521 end
1522 if value1.mtype.ctype != "val*" then
1523 if value2.mtype == value1.mtype then
1524 self.add("{res} = 1; /* is_same_type_test: compatible types {value1.mtype} vs. {value2.mtype} */")
1525 else if value2.mtype.ctype != "val*" then
1526 self.add("{res} = 0; /* is_same_type_test: incompatible types {value1.mtype} vs. {value2.mtype}*/")
1527 else
1528 var mtype1 = value1.mtype.as(MClassType)
1529 self.require_declaration("class_{mtype1.c_name}")
1530 self.add("{res} = ({value2} != NULL) && ({value2}->class == &class_{mtype1.c_name}); /* is_same_type_test */")
1531 end
1532 else
1533 self.add("{res} = ({value1} == {value2}) || ({value1} != NULL && {value2} != NULL && {value1}->class == {value2}->class); /* is_same_type_test */")
1534 end
1535 return res
1536 end
1537
1538 redef fun class_name_string(value)
1539 do
1540 var res = self.get_name("var_class_name")
1541 self.add_decl("const char* {res};")
1542 if value.mtype.ctype == "val*" then
1543 self.add "{res} = {value} == NULL ? \"null\" : {value}->type->name;"
1544 else if value.mtype isa MClassType and value.mtype.as(MClassType).mclass.kind == extern_kind then
1545 self.add "{res} = \"{value.mtype.as(MClassType).mclass}\";"
1546 else
1547 self.require_declaration("type_{value.mtype.c_name}")
1548 self.add "{res} = type_{value.mtype.c_name}.name;"
1549 end
1550 return res
1551 end
1552
1553 redef fun equal_test(value1, value2)
1554 do
1555 var res = self.new_var(bool_type)
1556 if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then
1557 var tmp = value1
1558 value1 = value2
1559 value2 = tmp
1560 end
1561 if value1.mtype.ctype != "val*" then
1562 if value2.mtype == value1.mtype then
1563 self.add("{res} = {value1} == {value2};")
1564 else if value2.mtype.ctype != "val*" then
1565 self.add("{res} = 0; /* incompatible types {value1.mtype} vs. {value2.mtype}*/")
1566 else
1567 var mtype1 = value1.mtype.as(MClassType)
1568 self.require_declaration("class_{mtype1.c_name}")
1569 self.add("{res} = ({value2} != NULL) && ({value2}->class == &class_{mtype1.c_name});")
1570 self.add("if ({res}) \{")
1571 self.add("{res} = ({self.autobox(value2, value1.mtype)} == {value1});")
1572 self.add("\}")
1573 end
1574 return res
1575 end
1576 var maybe_null = true
1577 var test = new Array[String]
1578 var t1 = value1.mcasttype
1579 if t1 isa MNullableType then
1580 test.add("{value1} != NULL")
1581 t1 = t1.mtype
1582 else
1583 maybe_null = false
1584 end
1585 var t2 = value2.mcasttype
1586 if t2 isa MNullableType then
1587 test.add("{value2} != NULL")
1588 t2 = t2.mtype
1589 else
1590 maybe_null = false
1591 end
1592
1593 var incompatible = false
1594 var primitive
1595 if t1.ctype != "val*" then
1596 primitive = t1
1597 if t1 == t2 then
1598 # No need to compare class
1599 else if t2.ctype != "val*" then
1600 incompatible = true
1601 else if can_be_primitive(value2) then
1602 test.add("{value1}->class == {value2}->class")
1603 else
1604 incompatible = true
1605 end
1606 else if t2.ctype != "val*" then
1607 primitive = t2
1608 if can_be_primitive(value1) then
1609 test.add("{value1}->class == {value2}->class")
1610 else
1611 incompatible = true
1612 end
1613 else
1614 primitive = null
1615 end
1616
1617 if incompatible then
1618 if maybe_null then
1619 self.add("{res} = {value1} == {value2}; /* incompatible types {t1} vs. {t2}; but may be NULL*/")
1620 return res
1621 else
1622 self.add("{res} = 0; /* incompatible types {t1} vs. {t2}; cannot be NULL */")
1623 return res
1624 end
1625 end
1626 if primitive != null then
1627 test.add("((struct instance_{primitive.c_instance_name}*){value1})->value == ((struct instance_{primitive.c_instance_name}*){value2})->value")
1628 else if can_be_primitive(value1) and can_be_primitive(value2) then
1629 test.add("{value1}->class == {value2}->class")
1630 var s = new Array[String]
1631 for t, v in self.compiler.box_kinds do
1632 s.add "({value1}->class->box_kind == {v} && ((struct instance_{t.c_instance_name}*){value1})->value == ((struct instance_{t.c_instance_name}*){value2})->value)"
1633 end
1634 test.add("({s.join(" || ")})")
1635 else
1636 self.add("{res} = {value1} == {value2};")
1637 return res
1638 end
1639 self.add("{res} = {value1} == {value2} || ({test.join(" && ")});")
1640 return res
1641 end
1642
1643 fun can_be_primitive(value: RuntimeVariable): Bool
1644 do
1645 var t = value.mcasttype
1646 if t isa MNullableType then t = t.mtype
1647 if not t isa MClassType then return false
1648 var k = t.mclass.kind
1649 return k == interface_kind or t.ctype != "val*"
1650 end
1651
1652 fun maybe_null(value: RuntimeVariable): Bool
1653 do
1654 var t = value.mcasttype
1655 return t isa MNullableType or t isa MNullType
1656 end
1657
1658 redef fun array_instance(array, elttype)
1659 do
1660 var nclass = self.get_class("NativeArray")
1661 var arrayclass = self.get_class("Array")
1662 var arraytype = arrayclass.get_mtype([elttype])
1663 var res = self.init_instance(arraytype)
1664 self.add("\{ /* {res} = array_instance Array[{elttype}] */")
1665 var length = self.int_instance(array.length)
1666 var nat = native_array_instance(elttype, length)
1667 for i in [0..array.length[ do
1668 var r = self.autobox(array[i], self.object_type)
1669 self.add("((struct instance_{nclass.c_name}*){nat})->values[{i}] = (val*) {r};")
1670 end
1671 self.send(self.get_property("with_native", arrayclass.intro.bound_mtype), [res, nat, length])
1672 self.add("\}")
1673 return res
1674 end
1675
1676 fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable
1677 do
1678 var mtype = self.get_class("NativeArray").get_mtype([elttype])
1679 self.require_declaration("NEW_{mtype.mclass.c_name}")
1680 assert mtype isa MGenericType
1681 var compiler = self.compiler
1682 if mtype.need_anchor then
1683 hardening_live_open_type(mtype)
1684 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
1685 var recv = self.frame.arguments.first
1686 var recv_type_info = self.type_info(recv)
1687 self.require_declaration(mtype.const_color)
1688 return self.new_expr("NEW_{mtype.mclass.c_name}({length}, {recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
1689 end
1690 compiler.undead_types.add(mtype)
1691 self.require_declaration("type_{mtype.c_name}")
1692 return self.new_expr("NEW_{mtype.mclass.c_name}({length}, &type_{mtype.c_name})", mtype)
1693 end
1694
1695 redef fun native_array_def(pname, ret_type, arguments)
1696 do
1697 var elttype = arguments.first.mtype
1698 var nclass = self.get_class("NativeArray")
1699 var recv = "((struct instance_{nclass.c_instance_name}*){arguments[0]})->values"
1700 if pname == "[]" then
1701 self.ret(self.new_expr("{recv}[{arguments[1]}]", ret_type.as(not null)))
1702 return
1703 else if pname == "[]=" then
1704 self.add("{recv}[{arguments[1]}]={arguments[2]};")
1705 return
1706 else if pname == "length" then
1707 self.ret(self.new_expr("((struct instance_{nclass.c_instance_name}*){arguments[0]})->length", ret_type.as(not null)))
1708 return
1709 else if pname == "copy_to" then
1710 var recv1 = "((struct instance_{nclass.c_instance_name}*){arguments[1]})->values"
1711 self.add("memcpy({recv1}, {recv}, {arguments[2]}*sizeof({elttype.ctype}));")
1712 return
1713 end
1714 end
1715
1716 redef fun calloc_array(ret_type, arguments)
1717 do
1718 var mclass = self.get_class("ArrayCapable")
1719 var ft = mclass.mclass_type.arguments.first.as(MParameterType)
1720 var res = self.native_array_instance(ft, arguments[1])
1721 self.ret(res)
1722 end
1723
1724 fun link_unresolved_type(mclassdef: MClassDef, mtype: MType) do
1725 assert mtype.need_anchor
1726 var compiler = self.compiler
1727 if not compiler.live_unresolved_types.has_key(self.frame.mpropdef.mclassdef) then
1728 compiler.live_unresolved_types[self.frame.mpropdef.mclassdef] = new HashSet[MType]
1729 end
1730 compiler.live_unresolved_types[self.frame.mpropdef.mclassdef].add(mtype)
1731 end
1732 end
1733
1734 redef class MMethodDef
1735 fun separate_runtime_function: AbstractRuntimeFunction
1736 do
1737 var res = self.separate_runtime_function_cache
1738 if res == null then
1739 res = new SeparateRuntimeFunction(self)
1740 self.separate_runtime_function_cache = res
1741 end
1742 return res
1743 end
1744 private var separate_runtime_function_cache: nullable SeparateRuntimeFunction
1745
1746 fun virtual_runtime_function: AbstractRuntimeFunction
1747 do
1748 var res = self.virtual_runtime_function_cache
1749 if res == null then
1750 res = new VirtualRuntimeFunction(self)
1751 self.virtual_runtime_function_cache = res
1752 end
1753 return res
1754 end
1755 private var virtual_runtime_function_cache: nullable VirtualRuntimeFunction
1756 end
1757
1758 # The C function associated to a methoddef separately compiled
1759 class SeparateRuntimeFunction
1760 super AbstractRuntimeFunction
1761
1762 redef fun build_c_name: String do return "{mmethoddef.c_name}"
1763
1764 redef fun to_s do return self.mmethoddef.to_s
1765
1766 redef fun compile_to_c(compiler)
1767 do
1768 var mmethoddef = self.mmethoddef
1769
1770 var recv = self.mmethoddef.mclassdef.bound_mtype
1771 var v = compiler.new_visitor
1772 var selfvar = new RuntimeVariable("self", recv, recv)
1773 var arguments = new Array[RuntimeVariable]
1774 var frame = new Frame(v, mmethoddef, recv, arguments)
1775 v.frame = frame
1776
1777 var msignature = mmethoddef.msignature.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
1778
1779 var sig = new FlatBuffer
1780 var comment = new FlatBuffer
1781 var ret = msignature.return_mtype
1782 if ret != null then
1783 sig.append("{ret.ctype} ")
1784 else if mmethoddef.mproperty.is_new then
1785 ret = recv
1786 sig.append("{ret.ctype} ")
1787 else
1788 sig.append("void ")
1789 end
1790 sig.append(self.c_name)
1791 sig.append("({selfvar.mtype.ctype} {selfvar}")
1792 comment.append("({selfvar}: {selfvar.mtype}")
1793 arguments.add(selfvar)
1794 for i in [0..msignature.arity[ do
1795 var mtype = msignature.mparameters[i].mtype
1796 if i == msignature.vararg_rank then
1797 mtype = v.get_class("Array").get_mtype([mtype])
1798 end
1799 comment.append(", {mtype}")
1800 sig.append(", {mtype.ctype} p{i}")
1801 var argvar = new RuntimeVariable("p{i}", mtype, mtype)
1802 arguments.add(argvar)
1803 end
1804 sig.append(")")
1805 comment.append(")")
1806 if ret != null then
1807 comment.append(": {ret}")
1808 end
1809 compiler.provide_declaration(self.c_name, "{sig};")
1810
1811 v.add_decl("/* method {self} for {comment} */")
1812 v.add_decl("{sig} \{")
1813 if ret != null then
1814 frame.returnvar = v.new_var(ret)
1815 end
1816 frame.returnlabel = v.get_name("RET_LABEL")
1817
1818 if recv != arguments.first.mtype then
1819 #print "{self} {recv} {arguments.first}"
1820 end
1821 mmethoddef.compile_inside_to_c(v, arguments)
1822
1823 v.add("{frame.returnlabel.as(not null)}:;")
1824 if ret != null then
1825 v.add("return {frame.returnvar.as(not null)};")
1826 end
1827 v.add("\}")
1828 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})"
1829 end
1830 end
1831
1832 # The C function associated to a methoddef on a primitive type, stored into a VFT of a class
1833 # The first parameter (the reciever) is always typed by val* in order to accept an object value
1834 class VirtualRuntimeFunction
1835 super AbstractRuntimeFunction
1836
1837 redef fun build_c_name: String do return "VIRTUAL_{mmethoddef.c_name}"
1838
1839 redef fun to_s do return self.mmethoddef.to_s
1840
1841 redef fun compile_to_c(compiler)
1842 do
1843 var mmethoddef = self.mmethoddef
1844
1845 var recv = self.mmethoddef.mclassdef.bound_mtype
1846 var v = compiler.new_visitor
1847 var selfvar = new RuntimeVariable("self", v.object_type, recv)
1848 var arguments = new Array[RuntimeVariable]
1849 var frame = new Frame(v, mmethoddef, recv, arguments)
1850 v.frame = frame
1851
1852 var sig = new FlatBuffer
1853 var comment = new FlatBuffer
1854
1855 # Because the function is virtual, the signature must match the one of the original class
1856 var intromclassdef = self.mmethoddef.mproperty.intro.mclassdef
1857 var msignature = mmethoddef.mproperty.intro.msignature.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1858 var ret = msignature.return_mtype
1859 if ret != null then
1860 sig.append("{ret.ctype} ")
1861 else if mmethoddef.mproperty.is_new then
1862 ret = recv
1863 sig.append("{ret.ctype} ")
1864 else
1865 sig.append("void ")
1866 end
1867 sig.append(self.c_name)
1868 sig.append("({selfvar.mtype.ctype} {selfvar}")
1869 comment.append("({selfvar}: {selfvar.mtype}")
1870 arguments.add(selfvar)
1871 for i in [0..msignature.arity[ do
1872 var mtype = msignature.mparameters[i].mtype
1873 if i == msignature.vararg_rank then
1874 mtype = v.get_class("Array").get_mtype([mtype])
1875 end
1876 comment.append(", {mtype}")
1877 sig.append(", {mtype.ctype} p{i}")
1878 var argvar = new RuntimeVariable("p{i}", mtype, mtype)
1879 arguments.add(argvar)
1880 end
1881 sig.append(")")
1882 comment.append(")")
1883 if ret != null then
1884 comment.append(": {ret}")
1885 end
1886 compiler.provide_declaration(self.c_name, "{sig};")
1887
1888 v.add_decl("/* method {self} for {comment} */")
1889 v.add_decl("{sig} \{")
1890 if ret != null then
1891 frame.returnvar = v.new_var(ret)
1892 end
1893 frame.returnlabel = v.get_name("RET_LABEL")
1894
1895 var subret = v.call(mmethoddef, recv, arguments)
1896 if ret != null then
1897 assert subret != null
1898 v.assign(frame.returnvar.as(not null), subret)
1899 end
1900
1901 v.add("{frame.returnlabel.as(not null)}:;")
1902 if ret != null then
1903 v.add("return {frame.returnvar.as(not null)};")
1904 end
1905 v.add("\}")
1906 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})"
1907 end
1908
1909 # TODO ?
1910 redef fun call(v, arguments) do abort
1911 end
1912
1913 redef class MType
1914 fun const_color: String do return "COLOR_{c_name}"
1915
1916 # C name of the instance type to use
1917 fun c_instance_name: String do return c_name
1918 end
1919
1920 redef class MClassType
1921 redef fun c_instance_name do return mclass.c_instance_name
1922 end
1923
1924 redef class MClass
1925 # Extern classes use the C instance of kernel::Pointer
1926 fun c_instance_name: String
1927 do
1928 if kind == extern_kind then
1929 return "kernel__Pointer"
1930 else return c_name
1931 end
1932 end
1933
1934 interface PropertyLayoutElement end
1935
1936 redef class MProperty
1937 super PropertyLayoutElement
1938 fun const_color: String do return "COLOR_{c_name}"
1939 end
1940
1941 redef class MPropDef
1942 super PropertyLayoutElement
1943 fun const_color: String do return "COLOR_{c_name}"
1944 end