+ self.provide_declaration("type_{c_name}", "extern const struct type type_{c_name};")
+
+ # const struct type_X
+ v.add_decl("const struct type type_{c_name} = \{")
+
+ # type id (for cast target)
+ if is_cast_live then
+ v.add_decl("{layout.ids[mtype]},")
+ else
+ v.add_decl("-1, /*CAST DEAD*/")
+ end
+
+ # type name
+ v.add_decl("\"{mtype}\", /* class_name_string */")
+
+ # type color (for cast target)
+ if is_cast_live then
+ if layout isa PHLayout[MType, MType] then
+ v.add_decl("{layout.masks[mtype]},")
+ else
+ v.add_decl("{layout.pos[mtype]},")
+ end
+ else
+ v.add_decl("-1, /*CAST DEAD*/")
+ end
+
+ # is_nullable bit
+ if mtype isa MNullableType then
+ v.add_decl("1,")
+ else
+ v.add_decl("0,")
+ end
+
+ # resolution table (for receiver)
+ if is_live then
+ var mclass_type = mtype
+ if mclass_type isa MNullableType then mclass_type = mclass_type.mtype
+ assert mclass_type isa MClassType
+ if resolution_tables[mclass_type].is_empty then
+ v.add_decl("NULL, /*NO RESOLUTIONS*/")
+ else
+ compile_type_resolution_table(mtype)
+ v.require_declaration("resolution_table_{c_name}")
+ v.add_decl("&resolution_table_{c_name},")
+ end
+ else
+ v.add_decl("NULL, /*DEAD*/")
+ end
+
+ # cast table (for receiver)
+ if is_live then
+ v.add_decl("{self.type_tables[mtype].length},")
+ v.add_decl("\{")
+ for stype in self.type_tables[mtype] do
+ if stype == null then
+ v.add_decl("-1, /* empty */")
+ else
+ v.add_decl("{layout.ids[stype]}, /* {stype} */")
+ end
+ end
+ v.add_decl("\},")
+ else
+ v.add_decl("0, \{\}, /*DEAD TYPE*/")
+ end
+ v.add_decl("\};")
+ end
+
+ fun compile_type_resolution_table(mtype: MType) do
+
+ var mclass_type: MClassType
+ if mtype isa MNullableType then
+ mclass_type = mtype.mtype.as(MClassType)
+ else
+ mclass_type = mtype.as(MClassType)
+ end
+
+ var layout = self.resolution_layout
+
+ # extern const struct resolution_table_X resolution_table_X
+ self.provide_declaration("resolution_table_{mtype.c_name}", "extern const struct types resolution_table_{mtype.c_name};")
+
+ # const struct fts_table_X fts_table_X
+ var v = new_visitor
+ v.add_decl("const struct types resolution_table_{mtype.c_name} = \{")
+ if layout isa PHLayout[MClassType, MType] then
+ v.add_decl("{layout.masks[mclass_type]},")
+ else
+ v.add_decl("0, /* dummy */")
+ end
+ v.add_decl("\{")
+ for t in self.resolution_tables[mclass_type] do
+ if t == null then
+ v.add_decl("NULL, /* empty */")
+ else
+ # The table stores the result of the type resolution
+ # Therefore, for a receiver `mclass_type`, and a unresolved type `t`
+ # the value stored is tv.
+ var tv = t.resolve_for(mclass_type, mclass_type, self.mainmodule, true)
+ # FIXME: What typeids means here? How can a tv not be live?
+ if self.type_layout.ids.has_key(tv) then
+ v.require_declaration("type_{tv.c_name}")
+ v.add_decl("&type_{tv.c_name}, /* {t}: {tv} */")
+ else
+ v.add_decl("NULL, /* empty ({t}: {tv} not a live type) */")
+ end
+ end
+ end
+ v.add_decl("\}")
+ v.add_decl("\};")
+ end
+
+ # Globally compile the table of the class mclass
+ # In a link-time optimisation compiler, tables are globally computed
+ # In a true separate compiler (a with dynamic loading) you cannot do this unfortnally
+ fun compile_class_to_c(mclass: MClass)
+ do
+ var mtype = mclass.intro.bound_mtype
+ var c_name = mclass.c_name
+ var c_instance_name = mclass.c_instance_name
+
+ var vft = self.method_tables[mclass]
+ var attrs = self.attr_tables[mclass]
+ var v = new_visitor
+
+ var is_dead = runtime_type_analysis != null and not runtime_type_analysis.live_classes.has(mclass) and mtype.ctype == "val*" and mclass.name != "NativeArray"
+
+ v.add_decl("/* runtime class {c_name} */")
+
+ # Build class vft
+ if not is_dead then
+ self.provide_declaration("class_{c_name}", "extern const struct class class_{c_name};")
+ v.add_decl("const struct class class_{c_name} = \{")
+ v.add_decl("{self.box_kind_of(mclass)}, /* box_kind */")
+ v.add_decl("\{")
+ for i in [0 .. vft.length[ do
+ var mpropdef = vft[i]
+ if mpropdef == null then
+ v.add_decl("NULL, /* empty */")
+ else
+ assert mpropdef isa MMethodDef
+ var rf = mpropdef.virtual_runtime_function
+ v.require_declaration(rf.c_name)
+ v.add_decl("(nitmethod_t){rf.c_name}, /* pointer to {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
+ end
+ end
+ v.add_decl("\}")
+ v.add_decl("\};")
+ end
+
+ if mtype.ctype != "val*" then
+ if mtype.mclass.name == "Pointer" or mtype.mclass.kind != extern_kind then
+ #Build instance struct
+ self.header.add_decl("struct instance_{c_instance_name} \{")
+ self.header.add_decl("const struct type *type;")
+ self.header.add_decl("const struct class *class;")
+ self.header.add_decl("{mtype.ctype} value;")
+ self.header.add_decl("\};")
+ end
+
+ if not self.runtime_type_analysis.live_types.has(mtype) then return
+
+ #Build BOX
+ self.provide_declaration("BOX_{c_name}", "val* BOX_{c_name}({mtype.ctype});")
+ v.add_decl("/* allocate {mtype} */")
+ v.add_decl("val* BOX_{mtype.c_name}({mtype.ctype} value) \{")
+ v.add("struct instance_{c_instance_name}*res = nit_alloc(sizeof(struct instance_{c_instance_name}));")
+ v.require_declaration("type_{c_name}")
+ v.add("res->type = &type_{c_name};")
+ v.require_declaration("class_{c_name}")
+ v.add("res->class = &class_{c_name};")
+ v.add("res->value = value;")
+ v.add("return (val*)res;")
+ v.add("\}")
+ return
+ else if mclass.name == "NativeArray" then
+ #Build instance struct
+ self.header.add_decl("struct instance_{c_instance_name} \{")
+ self.header.add_decl("const struct type *type;")
+ self.header.add_decl("const struct class *class;")
+ # NativeArrays are just a instance header followed by an array of values
+ self.header.add_decl("val* values[0];")
+ self.header.add_decl("\};")
+
+ #Build NEW
+ self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(int length, const struct type* type);")
+ v.add_decl("/* allocate {mtype} */")
+ v.add_decl("{mtype.ctype} NEW_{c_name}(int length, const struct type* type) \{")
+ var res = v.new_named_var(mtype, "self")
+ res.is_exact = true
+ var mtype_elt = mtype.arguments.first
+ v.add("{res} = nit_alloc(sizeof(struct instance_{c_instance_name}) + length*sizeof({mtype_elt.ctype}));")
+ v.add("{res}->type = type;")
+ hardening_live_type(v, "type")
+ v.require_declaration("class_{c_name}")
+ v.add("{res}->class = &class_{c_name};")
+ v.add("return {res};")
+ v.add("\}")
+ return
+ end
+
+ #Build NEW
+ self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(const struct type* type);")
+ v.add_decl("/* allocate {mtype} */")
+ v.add_decl("{mtype.ctype} NEW_{c_name}(const struct type* type) \{")
+ if is_dead then
+ v.add_abort("{mclass} is DEAD")
+ else
+ var res = v.new_named_var(mtype, "self")
+ res.is_exact = true
+ v.add("{res} = nit_alloc(sizeof(struct instance) + {attrs.length}*sizeof(nitattribute_t));")
+ v.add("{res}->type = type;")
+ hardening_live_type(v, "type")
+ v.require_declaration("class_{c_name}")
+ v.add("{res}->class = &class_{c_name};")
+ self.generate_init_attr(v, res, mtype)
+ v.add("return {res};")
+ end
+ v.add("\}")
+ end
+
+ # Add a dynamic test to ensure that the type referenced by `t` is a live type
+ fun hardening_live_type(v: VISITOR, t: String)
+ do
+ if not v.compiler.modelbuilder.toolcontext.opt_hardening.value then return
+ v.add("if({t} == NULL) \{")
+ v.add_abort("type null")
+ v.add("\}")
+ v.add("if({t}->table_size == 0) \{")
+ v.add("fprintf(stderr, \"Insantiation of a dead type: %s\\n\", {t}->name);")
+ v.add_abort("type dead")
+ v.add("\}")
+ end
+
+ redef fun new_visitor do return new SeparateCompilerVisitor(self)
+
+ # Stats
+
+ private var type_tables: Map[MType, Array[nullable MType]] = new HashMap[MType, Array[nullable MType]]
+ private var resolution_tables: Map[MClassType, Array[nullable MType]] = new HashMap[MClassType, Array[nullable MType]]
+ protected var method_tables: Map[MClass, Array[nullable MPropDef]] = new HashMap[MClass, Array[nullable MPropDef]]
+ protected var attr_tables: Map[MClass, Array[nullable MPropDef]] = new HashMap[MClass, Array[nullable MPropDef]]
+
+ redef fun display_stats
+ do
+ super
+ if self.modelbuilder.toolcontext.opt_tables_metrics.value then
+ display_sizes
+ end
+
+ var tc = self.modelbuilder.toolcontext
+ tc.info("# implementation of method invocation",2)
+ var nb_invok_total = modelbuilder.nb_invok_by_tables + modelbuilder.nb_invok_by_direct + modelbuilder.nb_invok_by_inline
+ tc.info("total number of invocations: {nb_invok_total}",2)
+ tc.info("invocations by VFT send: {modelbuilder.nb_invok_by_tables} ({div(modelbuilder.nb_invok_by_tables,nb_invok_total)}%)",2)
+ tc.info("invocations by direct call: {modelbuilder.nb_invok_by_direct} ({div(modelbuilder.nb_invok_by_direct,nb_invok_total)}%)",2)
+ tc.info("invocations by inlining: {modelbuilder.nb_invok_by_inline} ({div(modelbuilder.nb_invok_by_inline,nb_invok_total)}%)",2)
+ end
+
+ fun display_sizes
+ do
+ print "# size of subtyping tables"
+ print "\ttotal \tholes"
+ var total = 0
+ var holes = 0
+ for t, table in type_tables do
+ total += table.length
+ for e in table do if e == null then holes += 1
+ end
+ print "\t{total}\t{holes}"
+
+ print "# size of resolution tables"
+ print "\ttotal \tholes"
+ total = 0
+ holes = 0
+ for t, table in resolution_tables do
+ total += table.length
+ for e in table do if e == null then holes += 1
+ end
+ print "\t{total}\t{holes}"
+
+ print "# size of methods tables"
+ print "\ttotal \tholes"
+ total = 0
+ holes = 0
+ for t, table in method_tables do
+ total += table.length
+ for e in table do if e == null then holes += 1
+ end
+ print "\t{total}\t{holes}"
+
+ print "# size of attributes tables"
+ print "\ttotal \tholes"
+ total = 0
+ holes = 0
+ for t, table in attr_tables do
+ total += table.length
+ for e in table do if e == null then holes += 1
+ end
+ print "\t{total}\t{holes}"
+ end
+
+ redef fun compile_nitni_structs
+ do
+ self.header.add_decl("struct nitni_instance \{struct instance *value;\};")
+ end
+
+ redef fun finalize_ffi_for_module(nmodule)
+ do
+ var old_module = self.mainmodule
+ self.mainmodule = nmodule.mmodule.as(not null)
+ super
+ self.mainmodule = old_module
+ end
+end
+
+# A visitor on the AST of property definition that generate the C code of a separate compilation process.
+class SeparateCompilerVisitor
+ super AbstractCompilerVisitor
+
+ redef type COMPILER: SeparateCompiler
+
+ redef fun adapt_signature(m, args)
+ do
+ var msignature = m.msignature.resolve_for(m.mclassdef.bound_mtype, m.mclassdef.bound_mtype, m.mclassdef.mmodule, true)
+ var recv = args.first
+ if recv.mtype.ctype != m.mclassdef.mclass.mclass_type.ctype then
+ args.first = self.autobox(args.first, m.mclassdef.mclass.mclass_type)
+ end
+ for i in [0..msignature.arity[ do
+ var t = msignature.mparameters[i].mtype
+ if i == msignature.vararg_rank then
+ t = args[i+1].mtype
+ end
+ args[i+1] = self.autobox(args[i+1], t)
+ end
+ end
+
+ redef fun autobox(value, mtype)
+ do
+ if value.mtype == mtype then
+ return value
+ else if value.mtype.ctype == "val*" and mtype.ctype == "val*" then
+ return value
+ else if value.mtype.ctype == "val*" then
+ return self.new_expr("((struct instance_{mtype.c_instance_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
+ else if mtype.ctype == "val*" then
+ var valtype = value.mtype.as(MClassType)
+ var res = self.new_var(mtype)
+ if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(valtype) then
+ self.add("/*no autobox from {value.mtype} to {mtype}: {value.mtype} is not live! */")
+ self.add("printf(\"Dead code executed!\\n\"); show_backtrace(1);")
+ return res
+ end
+ self.require_declaration("BOX_{valtype.c_name}")
+ self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */")
+ return res
+ else if value.mtype.cname_blind == "void*" and mtype.cname_blind == "void*" then
+ return value
+ else
+ # Bad things will appen!
+ var res = self.new_var(mtype)
+ self.add("/* {res} left unintialized (cannot convert {value.mtype} to {mtype}) */")
+ self.add("printf(\"Cast error: Cannot cast %s to %s.\\n\", \"{value.mtype}\", \"{mtype}\"); show_backtrace(1);")
+ return res
+ end
+ end
+
+ # Return a C expression returning the runtime type structure of the value
+ # The point of the method is to works also with primitives types.
+ fun type_info(value: RuntimeVariable): String
+ do
+ if value.mtype.ctype == "val*" then
+ return "{value}->type"
+ else
+ compiler.undead_types.add(value.mtype)
+ self.require_declaration("type_{value.mtype.c_name}")
+ return "(&type_{value.mtype.c_name})"
+ end
+ end
+
+ redef fun compile_callsite(callsite, args)
+ do
+ var rta = compiler.runtime_type_analysis
+ var recv = args.first.mtype
+ if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null then
+ var tgs = rta.live_targets(callsite)
+ if tgs.length == 1 then
+ # DIRECT CALL
+ var mmethod = callsite.mproperty
+ self.varargize(mmethod.intro, mmethod.intro.msignature.as(not null), args)
+ var res0 = before_send(mmethod, args)
+ var res = call(tgs.first, tgs.first.mclassdef.bound_mtype, args)
+ if res0 != null then
+ assert res != null
+ self.assign(res0, res)
+ res = res0
+ end
+ add("\}") # close the before_send
+ return res
+ end
+ end
+ return super
+ end
+ redef fun send(mmethod, arguments)
+ do
+ self.varargize(mmethod.intro, mmethod.intro.msignature.as(not null), arguments)
+
+ if arguments.first.mcasttype.ctype != "val*" then
+ # In order to shortcut the primitive, we need to find the most specific method
+ # Howverr, because of performance (no flattening), we always work on the realmainmodule
+ var m = self.compiler.mainmodule
+ self.compiler.mainmodule = self.compiler.realmainmodule
+ var res = self.monomorphic_send(mmethod, arguments.first.mcasttype, arguments)
+ self.compiler.mainmodule = m
+ return res
+ end
+
+ return table_send(mmethod, arguments, mmethod.const_color)
+ end
+
+ # Handel common special cases before doing the effective method invocation
+ # This methods handle the `==` and `!=` methods and the case of the null receiver.
+ # Note: a { is open in the generated C, that enclose and protect the effective method invocation.
+ # Client must not forget to close the } after them.
+ #
+ # The value returned is the result of the common special cases.
+ # If not null, client must compine it with the result of their own effective method invocation.
+ #
+ # If `before_send` can shortcut the whole message sending, a dummy `if(0){`
+ # is generated to cancel the effective method invocation that will follow
+ # TODO: find a better approach
+ private fun before_send(mmethod: MMethod, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
+ do
+ var res: nullable RuntimeVariable = null
+ var recv = arguments.first
+ var consider_null = not self.compiler.modelbuilder.toolcontext.opt_no_check_other.value or mmethod.name == "==" or mmethod.name == "!="
+ var maybenull = recv.mcasttype isa MNullableType and consider_null
+ if maybenull then
+ self.add("if ({recv} == NULL) \{")
+ if mmethod.name == "==" then
+ res = self.new_var(bool_type)
+ var arg = arguments[1]
+ if arg.mcasttype isa MNullableType then
+ self.add("{res} = ({arg} == NULL);")
+ else if arg.mcasttype isa MNullType then
+ self.add("{res} = 1; /* is null */")
+ else
+ self.add("{res} = 0; /* {arg.inspect} cannot be null */")
+ end
+ else if mmethod.name == "!=" then
+ res = self.new_var(bool_type)
+ var arg = arguments[1]
+ if arg.mcasttype isa MNullableType then
+ self.add("{res} = ({arg} != NULL);")
+ else if arg.mcasttype isa MNullType then
+ self.add("{res} = 0; /* is null */")
+ else
+ self.add("{res} = 1; /* {arg.inspect} cannot be null */")
+ end
+ else
+ self.add_abort("Receiver is null")
+ end
+ self.add("\} else \{")
+ else
+ self.add("\{")
+ end
+ if not self.compiler.modelbuilder.toolcontext.opt_no_shortcut_equate.value and (mmethod.name == "==" or mmethod.name == "!=") then
+ if res == null then res = self.new_var(bool_type)
+ # Recv is not null, thus is arg is, it is easy to conclude (and respect the invariants)
+ var arg = arguments[1]
+ if arg.mcasttype isa MNullType then
+ if mmethod.name == "==" then
+ self.add("{res} = 0; /* arg is null but recv is not */")
+ else
+ self.add("{res} = 1; /* arg is null and recv is not */")
+ end
+ self.add("\}") # closes the null case
+ self.add("if (0) \{") # what follow is useless, CC will drop it
+ end
+ end
+ return res
+ end
+
+ private fun table_send(mmethod: MMethod, arguments: Array[RuntimeVariable], const_color: String): nullable RuntimeVariable
+ do
+ compiler.modelbuilder.nb_invok_by_tables += 1
+ if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_tables++;")
+
+ assert arguments.length == mmethod.intro.msignature.arity + 1 else debug("Invalid arity for {mmethod}. {arguments.length} arguments given.")
+ var recv = arguments.first
+
+ var res0 = before_send(mmethod, arguments)
+
+ var res: nullable RuntimeVariable
+ var msignature = mmethod.intro.msignature.resolve_for(mmethod.intro.mclassdef.bound_mtype, mmethod.intro.mclassdef.bound_mtype, mmethod.intro.mclassdef.mmodule, true)
+ var ret = msignature.return_mtype
+ if mmethod.is_new then
+ ret = arguments.first.mtype
+ res = self.new_var(ret)
+ else if ret == null then
+ res = null
+ else
+ res = self.new_var(ret)
+ end
+
+ var s = new FlatBuffer
+ var ss = new FlatBuffer
+
+ s.append("val*")
+ ss.append("{recv}")
+ for i in [0..msignature.arity[ do
+ var a = arguments[i+1]
+ var t = msignature.mparameters[i].mtype
+ if i == msignature.vararg_rank then
+ t = arguments[i+1].mcasttype
+ end
+ s.append(", {t.ctype}")
+ a = self.autobox(a, t)
+ ss.append(", {a}")
+ end
+
+
+ var r
+ if ret == null then r = "void" else r = ret.ctype
+ self.require_declaration(const_color)
+ var call = "(({r} (*)({s}))({arguments.first}->class->vft[{const_color}]))({ss}) /* {mmethod} on {arguments.first.inspect}*/"
+
+ if res != null then
+ self.add("{res} = {call};")
+ else
+ self.add("{call};")
+ end
+
+ if res0 != null then
+ assert res != null
+ assign(res0,res)
+ res = res0
+ end
+
+ self.add("\}") # closes the null case
+
+ return res
+ end
+
+ redef fun call(mmethoddef, recvtype, arguments)
+ do
+ assert arguments.length == mmethoddef.msignature.arity + 1 else debug("Invalid arity for {mmethoddef}. {arguments.length} arguments given.")
+
+ var res: nullable RuntimeVariable
+ var ret = mmethoddef.msignature.return_mtype
+ if mmethoddef.mproperty.is_new then
+ ret = arguments.first.mtype
+ res = self.new_var(ret)
+ else if ret == null then
+ res = null
+ else
+ ret = ret.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
+ res = self.new_var(ret)
+ end
+
+ if (mmethoddef.is_intern and not compiler.modelbuilder.toolcontext.opt_no_inline_intern.value) or
+ (compiler.modelbuilder.toolcontext.opt_inline_some_methods.value and mmethoddef.can_inline(self)) then
+ compiler.modelbuilder.nb_invok_by_inline += 1
+ if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_inline++;")
+ var frame = new Frame(self, mmethoddef, recvtype, arguments)
+ frame.returnlabel = self.get_name("RET_LABEL")
+ frame.returnvar = res
+ var old_frame = self.frame
+ self.frame = frame
+ self.add("\{ /* Inline {mmethoddef} ({arguments.join(",")}) on {arguments.first.inspect} */")
+ mmethoddef.compile_inside_to_c(self, arguments)
+ self.add("{frame.returnlabel.as(not null)}:(void)0;")
+ self.add("\}")
+ self.frame = old_frame
+ return res
+ end
+ compiler.modelbuilder.nb_invok_by_direct += 1
+ if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_direct++;")
+
+ # Autobox arguments
+ self.adapt_signature(mmethoddef, arguments)
+
+ self.require_declaration(mmethoddef.c_name)
+ if res == null then
+ self.add("{mmethoddef.c_name}({arguments.join(", ")}); /* Direct call {mmethoddef} on {arguments.first.inspect}*/")
+ return null
+ else
+ self.add("{res} = {mmethoddef.c_name}({arguments.join(", ")});")
+ end
+
+ return res
+ end
+
+ redef fun supercall(m: MMethodDef, recvtype: MClassType, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
+ do
+ if arguments.first.mcasttype.ctype != "val*" then
+ # In order to shortcut the primitive, we need to find the most specific method
+ # However, because of performance (no flattening), we always work on the realmainmodule
+ var main = self.compiler.mainmodule
+ self.compiler.mainmodule = self.compiler.realmainmodule
+ var res = self.monomorphic_super_send(m, recvtype, arguments)
+ self.compiler.mainmodule = main
+ return res
+ end
+ return table_send(m.mproperty, arguments, m.const_color)
+ end
+
+ redef fun vararg_instance(mpropdef, recv, varargs, elttype)
+ do
+ # A vararg must be stored into an new array
+ # The trick is that the dymaic type of the array may depends on the receiver
+ # of the method (ie recv) if the static type is unresolved
+ # This is more complex than usual because the unresolved type must not be resolved
+ # with the current receiver (ie self).
+ # Therefore to isolate the resolution from self, a local Frame is created.
+ # One can see this implementation as an inlined method of the receiver whose only
+ # job is to allocate the array
+ var old_frame = self.frame
+ var frame = new Frame(self, mpropdef, mpropdef.mclassdef.bound_mtype, [recv])
+ self.frame = frame
+ #print "required Array[{elttype}] for recv {recv.inspect}. bound=Array[{self.resolve_for(elttype, recv)}]. selfvar={frame.arguments.first.inspect}"
+ var res = self.array_instance(varargs, elttype)
+ self.frame = old_frame
+ return res
+ end
+
+ redef fun isset_attribute(a, recv)
+ do
+ self.check_recv_notnull(recv)
+ var res = self.new_var(bool_type)
+
+ # What is the declared type of the attribute?
+ var mtype = a.intro.static_mtype.as(not null)
+ var intromclassdef = a.intro.mclassdef
+ mtype = mtype.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
+
+ if mtype isa MNullableType then
+ self.add("{res} = 1; /* easy isset: {a} on {recv.inspect} */")
+ return res
+ end
+
+ self.require_declaration(a.const_color)
+ if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
+ self.add("{res} = {recv}->attrs[{a.const_color}] != NULL; /* {a} on {recv.inspect}*/")
+ else
+
+ if mtype.ctype == "val*" then
+ self.add("{res} = {recv}->attrs[{a.const_color}].val != NULL; /* {a} on {recv.inspect} */")
+ else
+ self.add("{res} = 1; /* NOT YET IMPLEMENTED: isset of primitives: {a} on {recv.inspect} */")
+ end
+ end
+ return res
+ end
+
+ redef fun read_attribute(a, recv)
+ do
+ self.check_recv_notnull(recv)
+
+ # What is the declared type of the attribute?
+ var ret = a.intro.static_mtype.as(not null)
+ var intromclassdef = a.intro.mclassdef
+ ret = ret.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
+
+ self.require_declaration(a.const_color)
+ if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
+ # Get the attribute or a box (ie. always a val*)
+ var cret = self.object_type.as_nullable
+ var res = self.new_var(cret)
+ res.mcasttype = ret
+
+ self.add("{res} = {recv}->attrs[{a.const_color}]; /* {a} on {recv.inspect} */")
+
+ # Check for Uninitialized attribute
+ if not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_initialization.value then
+ self.add("if (unlikely({res} == NULL)) \{")
+ self.add_abort("Uninitialized attribute {a.name}")
+ self.add("\}")
+ end
+
+ # Return the attribute or its unboxed version
+ # Note: it is mandatory since we reuse the box on write, we do not whant that the box escapes
+ return self.autobox(res, ret)
+ else
+ var res = self.new_var(ret)
+ self.add("{res} = {recv}->attrs[{a.const_color}].{ret.ctypename}; /* {a} on {recv.inspect} */")
+
+ # Check for Uninitialized attribute
+ if ret.ctype == "val*" and not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_initialization.value then
+ self.add("if (unlikely({res} == NULL)) \{")
+ self.add_abort("Uninitialized attribute {a.name}")
+ self.add("\}")
+ end
+
+ return res
+ end
+ end
+
+ redef fun write_attribute(a, recv, value)
+ do
+ self.check_recv_notnull(recv)
+
+ # What is the declared type of the attribute?
+ var mtype = a.intro.static_mtype.as(not null)
+ var intromclassdef = a.intro.mclassdef
+ mtype = mtype.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
+
+ # Adapt the value to the declared type
+ value = self.autobox(value, mtype)
+
+ self.require_declaration(a.const_color)
+ if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
+ var attr = "{recv}->attrs[{a.const_color}]"
+ if mtype.ctype != "val*" then
+ assert mtype isa MClassType
+ # The attribute is primitive, thus we store it in a box
+ # The trick is to create the box the first time then resuse the box
+ self.add("if ({attr} != NULL) \{")
+ self.add("((struct instance_{mtype.c_instance_name}*){attr})->value = {value}; /* {a} on {recv.inspect} */")
+ self.add("\} else \{")
+ value = self.autobox(value, self.object_type.as_nullable)
+ self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
+ self.add("\}")
+ else
+ # The attribute is not primitive, thus store it direclty
+ self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
+ end
+ else
+ self.add("{recv}->attrs[{a.const_color}].{mtype.ctypename} = {value}; /* {a} on {recv.inspect} */")
+ end
+ end
+
+ # Check that mtype is a live open type
+ fun hardening_live_open_type(mtype: MType)
+ do
+ if not compiler.modelbuilder.toolcontext.opt_hardening.value then return
+ self.require_declaration(mtype.const_color)
+ var col = mtype.const_color
+ self.add("if({col} == -1) \{")
+ self.add("fprintf(stderr, \"Resolution of a dead open type: %s\\n\", \"{mtype.to_s.escape_to_c}\");")
+ self.add_abort("open type dead")
+ self.add("\}")
+ end
+
+ # Check that mtype it a pointer to a live cast type
+ fun hardening_cast_type(t: String)
+ do
+ if not compiler.modelbuilder.toolcontext.opt_hardening.value then return
+ add("if({t} == NULL) \{")
+ add_abort("cast type null")
+ add("\}")
+ add("if({t}->id == -1 || {t}->color == -1) \{")
+ add("fprintf(stderr, \"Try to cast on a dead cast type: %s\\n\", {t}->name);")
+ add_abort("cast type dead")
+ add("\}")
+ end
+
+ redef fun init_instance(mtype)
+ do
+ self.require_declaration("NEW_{mtype.mclass.c_name}")
+ var compiler = self.compiler
+ if mtype isa MGenericType and mtype.need_anchor then
+ hardening_live_open_type(mtype)
+ link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
+ var recv = self.frame.arguments.first
+ var recv_type_info = self.type_info(recv)
+ self.require_declaration(mtype.const_color)
+ if compiler.modelbuilder.toolcontext.opt_phmod_typing.value or compiler.modelbuilder.toolcontext.opt_phand_typing.value then
+ return self.new_expr("NEW_{mtype.mclass.c_name}({recv_type_info}->resolution_table->types[HASH({recv_type_info}->resolution_table->mask, {mtype.const_color})])", mtype)
+ else
+ return self.new_expr("NEW_{mtype.mclass.c_name}({recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
+ end
+ end
+ compiler.undead_types.add(mtype)
+ self.require_declaration("type_{mtype.c_name}")
+ return self.new_expr("NEW_{mtype.mclass.c_name}(&type_{mtype.c_name})", mtype)
+ end
+
+ redef fun type_test(value, mtype, tag)
+ do
+ self.add("/* {value.inspect} isa {mtype} */")
+ var compiler = self.compiler
+
+ var recv = self.frame.arguments.first
+ var recv_type_info = self.type_info(recv)
+
+ var res = self.new_var(bool_type)
+
+ var cltype = self.get_name("cltype")
+ self.add_decl("int {cltype};")
+ var idtype = self.get_name("idtype")
+ self.add_decl("int {idtype};")
+
+ var maybe_null = self.maybe_null(value)
+ var accept_null = "0"
+ var ntype = mtype
+ if ntype isa MNullableType then
+ ntype = ntype.mtype
+ accept_null = "1"
+ end
+
+ if value.mcasttype.is_subtype(self.frame.mpropdef.mclassdef.mmodule, self.frame.mpropdef.mclassdef.bound_mtype, mtype) then
+ self.add("{res} = 1; /* easy {value.inspect} isa {mtype}*/")
+ if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
+ self.compiler.count_type_test_skipped[tag] += 1
+ self.add("count_type_test_skipped_{tag}++;")
+ end
+ return res
+ end
+
+ if ntype.need_anchor then
+ var type_struct = self.get_name("type_struct")
+ self.add_decl("const struct type* {type_struct};")
+
+ # Either with resolution_table with a direct resolution
+ hardening_live_open_type(mtype)
+ link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
+ self.require_declaration(mtype.const_color)
+ if compiler.modelbuilder.toolcontext.opt_phmod_typing.value or compiler.modelbuilder.toolcontext.opt_phand_typing.value then
+ self.add("{type_struct} = {recv_type_info}->resolution_table->types[HASH({recv_type_info}->resolution_table->mask, {mtype.const_color})];")
+ else
+ self.add("{type_struct} = {recv_type_info}->resolution_table->types[{mtype.const_color}];")
+ end
+ if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
+ self.compiler.count_type_test_unresolved[tag] += 1
+ self.add("count_type_test_unresolved_{tag}++;")
+ end
+ hardening_cast_type(type_struct)
+ self.add("{cltype} = {type_struct}->color;")
+ self.add("{idtype} = {type_struct}->id;")
+ if maybe_null and accept_null == "0" then
+ var is_nullable = self.get_name("is_nullable")
+ self.add_decl("short int {is_nullable};")
+ self.add("{is_nullable} = {type_struct}->is_nullable;")
+ accept_null = is_nullable.to_s
+ end
+ else if ntype isa MClassType then
+ compiler.undead_types.add(mtype)
+ self.require_declaration("type_{mtype.c_name}")
+ hardening_cast_type("(&type_{mtype.c_name})")
+ self.add("{cltype} = type_{mtype.c_name}.color;")
+ self.add("{idtype} = type_{mtype.c_name}.id;")
+ if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
+ self.compiler.count_type_test_resolved[tag] += 1
+ self.add("count_type_test_resolved_{tag}++;")
+ end
+ else
+ self.add("printf(\"NOT YET IMPLEMENTED: type_test(%s, {mtype}).\\n\", \"{value.inspect}\"); show_backtrace(1);")
+ end
+
+ # check color is in table
+ if maybe_null then
+ self.add("if({value} == NULL) \{")
+ self.add("{res} = {accept_null};")
+ self.add("\} else \{")
+ end
+ var value_type_info = self.type_info(value)
+ if compiler.modelbuilder.toolcontext.opt_phmod_typing.value or compiler.modelbuilder.toolcontext.opt_phand_typing.value then
+ self.add("{cltype} = HASH({value_type_info}->color, {idtype});")
+ end
+ self.add("if({cltype} >= {value_type_info}->table_size) \{")
+ self.add("{res} = 0;")
+ self.add("\} else \{")
+ self.add("{res} = {value_type_info}->type_table[{cltype}] == {idtype};")
+ self.add("\}")
+ if maybe_null then
+ self.add("\}")
+ end
+
+ return res
+ end
+
+ redef fun is_same_type_test(value1, value2)
+ do
+ var res = self.new_var(bool_type)
+ # Swap values to be symetric
+ if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then
+ var tmp = value1
+ value1 = value2
+ value2 = tmp
+ end
+ if value1.mtype.ctype != "val*" then
+ if value2.mtype == value1.mtype then
+ self.add("{res} = 1; /* is_same_type_test: compatible types {value1.mtype} vs. {value2.mtype} */")
+ else if value2.mtype.ctype != "val*" then
+ self.add("{res} = 0; /* is_same_type_test: incompatible types {value1.mtype} vs. {value2.mtype}*/")
+ else
+ var mtype1 = value1.mtype.as(MClassType)
+ self.require_declaration("class_{mtype1.c_name}")
+ self.add("{res} = ({value2} != NULL) && ({value2}->class == &class_{mtype1.c_name}); /* is_same_type_test */")
+ end
+ else
+ self.add("{res} = ({value1} == {value2}) || ({value1} != NULL && {value2} != NULL && {value1}->class == {value2}->class); /* is_same_type_test */")
+ end
+ return res
+ end
+
+ redef fun class_name_string(value)
+ do
+ var res = self.get_name("var_class_name")
+ self.add_decl("const char* {res};")
+ if value.mtype.ctype == "val*" then
+ self.add "{res} = {value} == NULL ? \"null\" : {value}->type->name;"
+ else if value.mtype isa MClassType and value.mtype.as(MClassType).mclass.kind == extern_kind then
+ self.add "{res} = \"{value.mtype.as(MClassType).mclass}\";"
+ else
+ self.require_declaration("type_{value.mtype.c_name}")
+ self.add "{res} = type_{value.mtype.c_name}.name;"
+ end
+ return res
+ end
+
+ redef fun equal_test(value1, value2)
+ do
+ var res = self.new_var(bool_type)
+ if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then
+ var tmp = value1
+ value1 = value2
+ value2 = tmp
+ end
+ if value1.mtype.ctype != "val*" then
+ if value2.mtype == value1.mtype then
+ self.add("{res} = {value1} == {value2};")
+ else if value2.mtype.ctype != "val*" then
+ self.add("{res} = 0; /* incompatible types {value1.mtype} vs. {value2.mtype}*/")
+ else
+ var mtype1 = value1.mtype.as(MClassType)
+ self.require_declaration("class_{mtype1.c_name}")
+ self.add("{res} = ({value2} != NULL) && ({value2}->class == &class_{mtype1.c_name});")
+ self.add("if ({res}) \{")
+ self.add("{res} = ({self.autobox(value2, value1.mtype)} == {value1});")
+ self.add("\}")
+ end
+ return res
+ end
+ var maybe_null = true
+ var test = new Array[String]
+ var t1 = value1.mcasttype
+ if t1 isa MNullableType then
+ test.add("{value1} != NULL")
+ t1 = t1.mtype
+ else
+ maybe_null = false
+ end
+ var t2 = value2.mcasttype
+ if t2 isa MNullableType then
+ test.add("{value2} != NULL")
+ t2 = t2.mtype
+ else
+ maybe_null = false
+ end