--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2009 Jean Privat <jean@pryen.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# iregisters slot allocation
+package allocate_iregister_slots
+
+import icode
+
+# The main class that performs the iregister slot allocation
+# The algorithm is quite naive but works
+# Things TODO:
+# * flow control
+# * register aliasing
+# * IMove optimization
+class IRegisterSlotAllocationVisitor
+special ICodeVisitor
+ # The visitor works in two pass:
+ # First pass is used to detect first and last iregisters occurences and slot groups
+ # Second pass is used to assign an effective slot to iregsiters
+ var _pass: Int = 0
+
+ redef fun visit_iregister_read(ic: ICode, r: IRegister)
+ do
+ var pass = _pass
+ if pass == 0 then
+ if r._is_local and r._local_iroutine != _current_ir then
+ if r._local_iroutine == null then
+ r._local_iroutine = _current_ir
+ else
+ r._is_local = false
+ end
+ end
+ _lasts[r] = ic
+ r._slot_index = null
+ else if pass == 1 and _lasts.has_key(r) and _lasts[r] == ic then
+ free(r)
+ end
+ end
+
+ redef fun visit_iregister_write(ic: ICode, r: IRegister)
+ do
+ var pass = _pass
+ if pass == 0 then
+ # Process locality
+ if r._is_local and r._local_iroutine != _current_ir then
+ if r._local_iroutine == null then
+ r._local_iroutine = _current_ir
+ else
+ r._is_local = false
+ end
+ end
+ r._slot_index = null
+ # The first write make it live
+ if not _firsts.has_key(r) then _firsts[r] = ic
+ # A read iregistre is stile live on a write
+ if _lasts.has_key(r) then _lasts[r] = ic
+ else if pass == 1 then
+ if _firsts[r] == ic then
+ register(r)
+ else if _lasts.has_key(r) and _lasts[r] == ic then
+ free(r)
+ end
+ end
+ end
+
+ # Iregister from outside a loop/closure cannot die inside the loop/closure
+ # So, the only register that can die in loops/closure are those born inside the loop/closure
+
+ # Registers born in the current loop/closure
+ # So they can die immediatly if needed
+ var _live: HashSet[IRegister] = new HashSet[IRegister]
+
+ # Registers born outsde the current loop/closure that wanted to die
+ # Thez will be effectively freed at the end of the loop/closure
+ var _deferred: HashSet[IRegister] = new HashSet[IRegister]
+
+ # Free the deferred that can be freed
+ # "Can" because of neested loops/closures
+ # new_def will be cleared and used as the new _deferred attribute
+ fun deferred_free(new_def: HashSet[IRegister])
+ do
+ var old_def = _deferred
+ if not old_def.is_empty then
+ new_def.clear
+ _deferred = new_def
+ for r in old_def do
+ free(r)
+ end
+ end
+ end
+
+ redef fun visit_icode(ic)
+ do
+ if _pass == 1 and ic isa ILoop then
+ var old_live = _live
+ var new_live = new HashSet[IRegister]
+ _live = new_live
+ super
+ _live = old_live
+ deferred_free(new_live)
+ else
+ super
+ end
+ end
+
+ redef fun visit_closure_defs(closdefs)
+ do
+ if _pass == 1 then
+ var old_live = _live
+ var new_live = new HashSet[IRegister]
+ _live = new_live
+ super
+ _live = old_live
+ deferred_free(new_live)
+ else
+ super
+ end
+ end
+
+ # The current iroutine
+ # Used to detect locality of iregisters
+ var _current_ir: IRoutine
+
+ redef fun visit_iroutine(ir)
+ do
+ var res = ir.result
+ if _pass == 0 then
+ var old_ir = _current_ir
+ _current_ir = ir
+ for r in ir.params do
+ _firsts[r] = self
+ end
+ super
+ if res != null then
+ _lasts[res] = self
+ end
+ _current_ir = old_ir
+ else
+ var old_bool_slots = _bool_slots
+ _bool_slots = new SlotGroup
+ var old_std_slots = _std_slots
+ if ir isa IClosureDef then
+ _std_slots = new SlotGroup
+ end
+ for r in ir.params do
+ register(r)
+ end
+ super
+ if res != null then free(res)
+ ir._bool_slots_nb = _bool_slots._next_index
+ _bool_slots = old_bool_slots
+ ir._std_slots_nb = _std_slots._next_index
+ _std_slots = old_std_slots
+ end
+ end
+
+ # Global slots are used for non local registers and some main iroutine registers
+ var _global_slots: SlotGroup = new SlotGroup
+
+ # Standad slots are used for local generic registers
+ # In main iroutine, _global_slots == _std_slots
+ var _std_slots: SlotGroup
+
+ # Bool slots are used to store local boolean registers
+ var _bool_slots: SlotGroup = new SlotGroup
+
+ # Assign a slot to a register according to its locality and its type
+ fun register(r: IRegister)
+ do
+ if not _lasts.has_key(r) then return
+ assert r._slot_index == null
+ _live.add(r)
+ if not r._is_local then
+ _global_slots.register(r)
+ else if (r.stype.local_class.name == once ("Bool".to_symbol)) then
+ r._in_bool_slots = true
+ _bool_slots.register(r)
+ else
+ _std_slots.register(r)
+ end
+ end
+
+ # Release a register, thus its slot can be reused
+ fun free(r: IRegister)
+ do
+ var i = r._slot_index
+ if i == null then return
+ if not _live.has(r) then
+ _deferred.add(r)
+ else if _lasts.has_key(r) then
+ if r._in_bool_slots then
+ _bool_slots.free(r)
+ else if r._is_local then
+ _std_slots.free(r)
+ else
+ _global_slots.free(r)
+ end
+ _lasts.remove_at(r) # Free a register only once
+ end
+ end
+
+ # What is the first occurences of iregisters
+ # So that a slot is not needed before
+ var _firsts: HashMap[IRegister, Object] = new HashMap[IRegister, Object]
+
+ # What is the last occurences of iregisters
+ # So that a slot may be no more needed after
+ # ("may" because of loops/closure)
+ var _lasts: HashMap[IRegister, Object] = new HashMap[IRegister, Object]
+
+ # Run the slot allocation
+ fun iroutine_slot_allocation
+ do
+ var ir = _current_ir
+ visit_iroutine(ir)
+ _pass = 1
+ visit_iroutine(ir)
+ end
+
+ init(ir: IRoutine)
+ do
+ _current_ir = ir
+ _std_slots = _global_slots
+ end
+end
+
+# Group or equivalent slots shared by registers
+class SlotGroup
+ # The free slots in the group
+ var _free: List[Int] = new List[Int]
+
+ # The next free slot
+ var _next_index: Int = 0
+
+ # Assign a slot to the register
+ fun register(r: IRegister)
+ do
+ if _free.is_empty then
+ r._slot_index = _next_index
+ _next_index += 1
+ else
+ r._slot_index = _free.pop
+ end
+ end
+
+ # Reuse the slot of the register
+ fun free(r: IRegister)
+ do
+ _free.push(r._slot_index.as(not null))
+ end
+end
+
+redef class IRoutine
+ # The number of standard slots (stored in an array)
+ readable var _std_slots_nb: Int = 0
+
+ # The number of bool slots
+ readable var _bool_slots_nb: Int = 0
+
+ fun allocate_iregister_slots
+ do
+ var v = new IRegisterSlotAllocationVisitor(self)
+ v.iroutine_slot_allocation
+ end
+end
+
+redef class IRegister
+ # The slot index of the register in its slot group
+ # Is null if the iregister is dead
+ # (or if iregister slot allocation is not performed yet)
+ readable writable var _slot_index: nullable Int
+
+ # Is the register local to a single iroutine?
+ readable writable var _is_local: Bool = true
+
+ # If is_local, then what iroutine
+ readable writable var _local_iroutine: nullable IRoutine
+
+ # Is the register stored in the bool slot groups?
+ readable writable var _in_bool_slots: Bool = false
+end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2009 Jean Privat <jean@pryen.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Intermediate code analysis and optimizations
+package analysis
+
+import icode
+import icode_dump
+import allocate_iregister_slots
+import inline_methods
+
+redef class IRoutine
+ # Perfom all optimizations
+ fun optimize
+ do
+ inline_methods
+ allocate_iregister_slots
+ end
+end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2009 Jean Privat <jean@pryen.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Output the intermediate code unded a textual representation
+package icode_dump
+
+import icode
+import allocate_iregister_slots
+
+redef class IRoutine
+ # Output the intermediate code representation of the iroutine
+ fun dump(icd: ICodeDumper)
+ do
+ if not params.is_empty then
+ var a = new Array[String]
+ for p in params do
+ a.add(icd.register(p))
+ end
+ icd.write "Parameters: {a.join(", ")}"
+ end
+ var r = result
+ if r != null then
+ icd.write "Result: {icd.register(r)}"
+ end
+ var closdecls = closure_decls
+ if closdecls != null then
+ for c in closdecls do
+ c.dump(icd)
+ end
+ end
+ body.dump(icd)
+ end
+end
+
+redef class IClosureDecl
+ # Output the intermediate code representation of the iclosuredecl
+ fun dump(icd: ICodeDumper)
+ do
+ icd.write "Closure: {icd.closdecl(self)}"
+ if default != null then
+ icd.indent
+ default.dump(icd)
+ icd.unindent
+ end
+ end
+end
+
+class ICodeDumper
+ var _ids: HashMap[Object, String] = new HashMap[Object, String]
+ var _last_value: Int = 0
+
+ # Return the name of e
+ # If e is unknown, a new name is gived
+ fun register(e: IRegister): String
+ do
+ if _ids.has_key(e) then
+ return _ids[e]
+ else
+ var i = e.slot_index
+ if i == null then
+ _last_value += 1
+ var s = "r{_last_value}"
+ _ids[e] = s
+ return s
+ else
+ _last_value += 1
+ var s = "REG{i}(r{_last_value})"
+ _ids[e] = s
+ return s
+ end
+ end
+ end
+
+ # Returns the names of es (separated with comma)
+ fun register_all(es: nullable Collection[IRegister]): String
+ do
+ if es == null then return ""
+ var a = new Array[String]
+ for e in es do
+ a.add(register(e))
+ end
+ return a.join(", ")
+ end
+
+ var _last_clos: Int = 0
+
+ # Return the name of e
+ # If e is unknown, a new name is gived
+ fun closdecl(e: IClosureDecl): String
+ do
+ if _ids.has_key(e) then
+ return _ids[e]
+ else
+ _last_clos += 1
+ var s = "clos{_last_clos}"
+ _ids[e] = s
+ return s
+ end
+ end
+
+ var _last_label: Int = 0
+ # Return the name of e
+ # If e is unknown, a new name is gived
+ fun lab(e: ISeq): String
+ do
+ if _ids.has_key(e) then
+ return _ids[e]
+ else
+ _last_label += 1
+ var s = "[l{_last_label}]"
+ _ids[e] = s
+ return s
+ end
+ end
+
+ var _last_line: Int = 0
+ # Return the line index of e
+ fun line(e: ICode): String
+ do
+ if _ids.has_key(e) then
+ return _ids[e]
+ else
+ _last_line += 1
+ var s = "{_last_line}"
+ _ids[e] = s
+ return s
+ end
+ end
+
+ # Is the label e known? (because we goto to it)
+ fun has_lab(e: ISeq): Bool
+ do
+ return _ids.has_key(e)
+ end
+
+ # Output something
+ fun write(s: String)
+ do
+ for i in [0.._indent_level[ do
+ printn " "
+ end
+ print s
+ end
+
+ var _indent_level: Int = 0
+
+ # Indent the next writes
+ fun indent do _indent_level += 1
+
+ # Outdent the next writes
+ fun unindent do _indent_level -= 1
+end
+
+redef class ICode
+ # Output the intermediate code representation
+ fun dump(icd: ICodeDumper)
+ do
+ var result = result
+ var s = ""
+ var l = location
+ if l != null then
+ s = " ... {l}"
+ end
+ if result == null then
+ icd.write "{icd.line(self)}: {dump_intern(icd)}{s}"
+ else
+ icd.write "{icd.line(self)}: {icd.register(result)} := {dump_intern(icd)}{s}"
+ end
+ end
+
+ # Output the intermediate code representation (inner method)
+ fun dump_intern(icd: ICodeDumper): String do return "???"
+end
+
+redef class ICodeN
+ redef fun dump(icd: ICodeDumper)
+ do
+ super
+ var closure_defs = closure_defs
+ if closure_defs != null then
+ for clos in closure_defs do
+ if clos == null then
+ icd.write "CLOSURE = NULL"
+ else
+ icd.write "CLOSURE"
+ icd.indent
+ clos.dump(icd)
+ icd.unindent
+ end
+ end
+ end
+ end
+end
+
+redef class ISeq
+ redef fun dump(icd)
+ do
+ for ic in icodes do
+ ic.dump(icd)
+ end
+ if icd.has_lab(self) then icd.write("{icd.lab(self)}:")
+ end
+end
+
+redef class IIf
+ redef fun dump(icd)
+ do
+ icd.write "IF({icd.register(expr)}) \{"
+ icd.indent
+ then_seq.dump(icd)
+ icd.unindent
+ icd.write "} ELSE \{"
+ icd.indent
+ else_seq.dump(icd)
+ icd.unindent
+ icd.write "}"
+ end
+end
+
+redef class ILoop
+ redef fun dump(icd)
+ do
+ icd.write "LOOP \{"
+ icd.indent
+ for ic in icodes do
+ ic.dump(icd)
+ end
+ icd.unindent
+ icd.write "}"
+ if icd.has_lab(self) then icd.write("{icd.lab(self)}:")
+ end
+end
+
+redef class IEscape
+ redef fun dump_intern(icd)
+ do
+ return "ESCAPE {icd.lab(seq)}"
+ end
+end
+
+redef class IAbort
+ redef fun dump_intern(icd)
+ do
+ var pl = property_location
+ if pl != null then
+ return "ABORT (\"{texts.join("\", \"")}\") in {pl.full_name}"
+ else
+ return "ABORT (\"{texts.join("\", \"")}\")"
+ end
+ end
+end
+
+redef class ICall
+ redef fun dump_intern(icd)
+ do
+ return "CALL {property.full_name}({icd.register_all(exprs)})"
+ end
+end
+
+redef class IClosCall
+ redef fun dump_intern(icd)
+ do
+ return "CLOS_CALL {icd.closdecl(closure_decl)}({icd.register_all(exprs)})"
+ end
+end
+
+redef class IAttrRead
+ redef fun dump_intern(icd)
+ do
+ return "ATTR_READ {property.full_name}({icd.register(expr)})"
+ end
+end
+
+redef class IAttrWrite
+ redef fun dump_intern(icd)
+ do
+ return "ATTR_WRITE {property.full_name}({icd.register(expr1)}) := {icd.register(expr2)}"
+ end
+end
+
+redef class IAttrIsset
+ redef fun dump_intern(icd)
+ do
+ return "ATTR_ISSET {property.full_name}({icd.register(expr)})"
+ end
+end
+
+redef class ITypeCheck
+ redef fun dump_intern(icd)
+ do
+ return "CHECKTYPE {icd.register(expr)} isa {stype}"
+ end
+end
+
+redef class INative
+ redef fun dump_intern(icd)
+ do
+ if exprs.is_empty then
+ return "NATIVE \"{code}\""
+ else
+ return "NATIVE \"{code}\"({icd.register_all(exprs)})"
+ end
+ end
+end
+
+redef class IMove
+ redef fun dump_intern(icd)
+ do
+ return "{icd.register(expr)}"
+ end
+end
+
+redef class IIs
+ redef fun dump_intern(icd)
+ do
+ return "{icd.register(expr1)} is {icd.register(expr2)}"
+ end
+end
+
+redef class INot
+ redef fun dump_intern(icd)
+ do
+ return "NOT {icd.register(expr)}"
+ end
+end
+
+redef class IOnce
+ redef fun dump(icd)
+ do
+ icd.write "{icd.register(result.as(not null))} := ONCE \{"
+ icd.indent
+ body.dump(icd)
+ icd.unindent
+ icd.write "}"
+ end
+end
+
+redef class IHasClos
+ redef fun dump_intern(icd)
+ do
+ return "HASCLOS {icd.closdecl(closure_decl)}"
+ end
+end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2009 Jean Privat <jean@pryen.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Detect inlinable methods and inline them
+package inline_methods
+
+import icode
+
+private class InlineMethodVisitor
+special ICodeVisitor
+ var _pass: Int = 0
+
+ redef fun visit_icode(ic)
+ do
+ if ic isa ICall then
+ var m = ic.property
+ if m.iroutine != null and ic.is_inlinable then
+ var ir = m.iroutine.as(not null)
+ var seq = new ISeq
+ current_icode.insert_before(seq)
+ var e = ir.inline_in_seq(seq, ic.exprs)
+ var r = ic.result
+ if r != null then
+ assert e != null
+ current_icode.insert_before(new IMove(r, e))
+ end
+ current_icode.delete
+ visit_icode(seq)
+ end
+ end
+ super
+ end
+end
+
+redef class ICall
+ fun is_inlinable: Bool
+ do
+ var m = property
+ var mn = m.name
+ var cn = m.local_class.name
+ return (m.is_intern and cn != once ("Object".to_symbol)) or
+ (cn == (once ("Array".to_symbol)) and (mn == (once ("length".to_symbol)) or mn == (once ("[]".to_symbol)))) or
+ (cn == (once ("AbstractArrayRead".to_symbol)) and (mn == (once ("length".to_symbol)) or mn == (once ("[]".to_symbol))))
+ end
+end
+
+redef class IRoutine
+ fun inline_methods
+ do
+ var v = new InlineMethodVisitor
+ v.visit_iroutine(self)
+ end
+end
package compiling
import compiling_base
-private import compiling_methods
private import compiling_global
-private import syntax
+private import compiling_icode
redef class MMModule
# Compile the program
# Common things for NIT compilation and C generation
package compiling_base
-import syntax
+import mmloader
private import utils
import primitive_info
# Compute and generate tables for classes and modules.
package compiling_global
-#import compiling_base
-private import compiling_methods
-private import syntax
+private import compiling_icode
# Something that store color of table elements
class ColorContext
if not c isa MMConcreteClass then continue
for pg in c.global_properties do
var p = c[pg]
- if p.local_class == c then
+ if p.local_class == c and p isa MMMethod then
p.compile_property_to_c(v)
end
if pg.is_init_for(c) then
var pi = primitive_info
if pi == null then
- v.cfc = new CFunctionContext(v)
- v.nmc = new NitMethodContext(null)
- var s = "val_t NEW_{name}(void)"
- v.add_instr(s + " \{")
- v.indent
- var ctx_old = v.ctx
- v.ctx = new CContext
-
- var self_var = new ParamVariable(once ("self".to_symbol), null)
- var self_var_cname = v.cfc.register_variable(self_var)
- v.nmc.method_params = [self_var]
-
- v.add_instr("obj_t obj;")
- v.add_instr("obj = alloc(sizeof(val_t) * {itab.length});")
- v.add_instr("obj->vft = (classtable_elt_t*)VFT_{name};")
- v.add_assignment(self_var_cname, "OBJ2VAL(obj)")
+ var iself = new IRegister(get_type)
+ var iselfa = [iself]
+ var iroutine = new IRoutine(new Array[IRegister], iself)
+ var icb = new ICodeBuilder(module, iroutine, null)
+ var obj = new INative("OBJ2VAL(obj)", null)
+ obj.result = iself
+ icb.stmt(obj)
for g in global_properties do
var p = self[g]
var t = p.signature.return_type
if p isa MMAttribute and t != null then
+ var ir = p.iroutine
+ if ir == null then continue
# FIXME: Not compatible with sep compilation
- assert p isa MMSrcAttribute
- var np = p.node
- var ne = np.n_expr
- if ne != null then
- var e = ne.compile_expr(v)
- v.add_instr("{p.global.attr_access}(obj) = {e};")
- end
+ var e = ir.inline_in_seq(icb.seq, iselfa).as(not null)
+ icb.stmt(new IAttrWrite(p, iself, e))
end
end
- v.add_instr("return OBJ2VAL(obj);")
- v.cfc.generate_var_decls
+
+ var cname = "NEW_{name}"
+ var args = iroutine.compile_signature_to_c(v, cname, "new {name}", null, null)
+ var ctx_old = v.ctx
+ v.ctx = new CContext
+ v.add_decl("obj_t obj;")
+ v.add_instr("obj = alloc(sizeof(val_t) * {itab.length});")
+ v.add_instr("obj->vft = (classtable_elt_t*)VFT_{name};")
+ var r = iroutine.compile_to_c(v, cname, args).as(not null)
+ v.add_instr("return {r};")
ctx_old.append(v.ctx)
v.ctx = ctx_old
v.unindent
v.add_instr("}")
# Compile CHECKNAME
- var s = "void CHECKNEW_{name}(val_t self, char *from)"
- v.add_instr(s + " \{")
- v.indent
- var ctx_old = v.ctx
- v.ctx = new CContext
+ var iself = new IRegister(get_type)
+ var iselfa = [iself]
+ var iroutine = new IRoutine(iselfa, null)
+ var icb = new ICodeBuilder(module, iroutine, null)
for g in global_properties do
var p = self[g]
var t = p.signature.return_type
if p isa MMAttribute and t != null and not t.is_nullable then
- v.add_instr("if ({p.global.attr_access}(self) == NIT_NULL) \{ fprintf(stderr, \"Uninitialized attribute %s at %s.\\n\", \"{p.full_name}\", from); nit_exit(1); \}")
+ icb.add_attr_check(p, iself)
end
end
+ var cname = "CHECKNEW_{name}"
+ var args = iroutine.compile_signature_to_c(v, cname, "check new {name}", null, null)
+ var ctx_old = v.ctx
+ v.ctx = new CContext
+ iroutine.compile_to_c(v, cname, args)
ctx_old.append(v.ctx)
v.ctx = ctx_old
v.unindent
var p = self[g]
# FIXME skip invisible constructors
if not p.global.is_init_for(self) then continue
- var params = new Array[String]
- var args = ["self"]
- for i in [0..p.signature.arity[ do
- params.add("val_t p{i}")
- args.add("p{i}")
- end
- args.add("init_table")
- var s = "val_t NEW_{self}_{p.global.intro.cname}({params.join(", ")}) \{"
- v.add_instr(s)
- v.indent
+ assert p isa MMMethod
+
+ var iself = new IRegister(get_type)
+ var iparams = new Array[IRegister]
+ for i in [0..p.signature.arity[ do iparams.add(new IRegister(p.signature[i]))
+ var iroutine = new IRoutine(iparams, iself)
+ iroutine.location = p.iroutine.location
+ var icb = new ICodeBuilder(module, iroutine, p)
+
+ var inew = new INative("NEW_{name}()", null)
+ inew.result = iself
+ icb.stmt(inew)
+ var iargs = [iself]
+ iargs.add_all(iparams)
+ icb.stmt(new INative("{p.cname}(@@@{", @@@"*iparams.length}, init_table)", iargs))
+ icb.stmt(new INative("CHECKNEW_{name}(@@@)", [iself]))
+
+ var cname = "NEW_{self}_{p.global.intro.cname}"
+ var new_args = iroutine.compile_signature_to_c(v, cname, "new {self} {p.full_name}", null, null)
+ var ctx_old = v.ctx
+ v.ctx = new CContext
v.add_instr(init_table_decl)
- v.add_instr("val_t self = NEW_{name}();")
- v.add_instr("{p.cname}({args.join(", ")});")
- v.add_instr("CHECKNEW_{name}(self, \"{p.full_name} for {self}\");")
- v.add_instr("return self;")
+ var e = iroutine.compile_to_c(v, cname, new_args).as(not null)
+ v.add_instr("return {e};")
+ ctx_old.append(v.ctx)
+ v.ctx = ctx_old
v.unindent
v.add_instr("}")
end
end
end
+redef class MMMethod
+ fun compile_property_to_c(v: CompilerVisitor)
+ do
+ var ir = iroutine
+ assert ir != null
+
+ var more_params: nullable String = null
+ if global.is_init then more_params = "int* init_table"
+ var args = ir.compile_signature_to_c(v, cname, full_name, null, more_params)
+ var ctx_old = v.ctx
+ v.ctx = new CContext
+
+ v.out_contexts.clear
+
+ var itpos: nullable String = null
+ if global.is_init then
+ itpos = "itpos{v.new_number}"
+ v.add_decl("int {itpos} = VAL2OBJ({args.first})->vft[{local_class.global.init_table_pos_id}].i;")
+ v.add_instr("if (init_table[{itpos}]) return;")
+ end
+
+ var s = ir.compile_to_c(v, cname, args)
+
+ if itpos != null then
+ v.add_instr("init_table[{itpos}] = 1;")
+ end
+ if s == null then
+ v.add_instr("return;")
+ else
+ v.add_instr("return ", s, ";")
+ end
+
+ ctx_old.append(v.ctx)
+ v.ctx = ctx_old
+ v.unindent
+ v.add_instr("}")
+
+ for ctx in v.out_contexts do v.ctx.merge(ctx)
+ end
+end
+
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2009 Jean Privat <jean@pryen.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Generate C code from intermediate code representation
+package compiling_icode
+
+import icode
+private import analysis
+import compiling_base
+
+# Compiler context from ICode to C
+class I2CCompilerVisitor
+ # Associate things
+ var _ids: HashMap[Object, String] = new HashMap[Object, String]
+ # Associate other things
+ var _ids2: HashMap[Object, String] = new HashMap[Object, String]
+
+ # Return the string associated with a register
+ fun register(e: IRegister): String
+ do
+ if e.stype isa MMTypeNone then return "NIT_NULL"
+ var ids = _ids
+ if closure and not e.is_local then ids = _ids2
+ if ids.has_key(e) then
+ return ids[e]
+ else
+ var i = e.slot_index
+ if i == null then
+ # The register is dead
+ var s = "NIT_NULL"
+ ids[e] = s
+ return s
+ else
+ var s: String
+ var strs: HashMap[Int, String]
+ if e.in_bool_slots then
+ strs = once new HashMap[Int, String]
+ if not strs.has_key(i) then strs[i] = "REGB{i}"
+ else if closure and not e.is_local then
+ strs = once new HashMap[Int, String]
+ if not strs.has_key(i) then strs[i] = "closctx->variable[{i}]"
+ else
+ strs = once new HashMap[Int, String]
+ if not strs.has_key(i) then strs[i] = "REG[{i}]"
+ end
+ s = strs[i]
+ ids[e] = s
+ return s
+ end
+ end
+ end
+
+ # Return the strings associated with registers
+ fun registers(a: Collection[IRegister]): Array[String]
+ do
+ var r = new Array[String].with_capacity(a.length)
+ for e in a do
+ r.add(register(e))
+ end
+ return r
+ end
+
+ var _last_number: Int = 0
+ # Give a new unique number (unique for the visitor)
+ fun new_number: Int
+ do
+ _last_number += 1
+ return _last_number
+ end
+
+ # Return the string associated with a escape label
+ fun lab(e: ISeq): String
+ do
+ if _ids.has_key(e) then
+ return _ids[e]
+ else
+ var s = "label{new_number}"
+ _ids[e] = s
+ return s
+ end
+ end
+
+ # The rank (number) of each closure
+ readable var _closures: HashMap[IClosureDecl, Int] = new HashMap[IClosureDecl, Int]
+
+ # The functionnal type of each closure
+ readable var _clostypes: HashMap[IClosureDecl, String] = new HashMap[IClosureDecl, String]
+
+ # label locally accessibles
+ readable writable var _local_labels: HashSet[ISeq] = new HashSet[ISeq]
+
+ # Not local escaped labels
+ # The integer value is an index identifying the label
+ readable writable var _escaped_labels: HashMap[ISeq, Int] = new HashMap[ISeq, Int]
+
+ # Register a escape to a non local label and return an index identifying the label
+ fun register_escape_label(e: ISeq): Int
+ do
+ if _escaped_labels.has_key(e) then
+ return _escaped_labels[e]
+ else
+ var res = _escaped_labels.length + 1
+ _escaped_labels[e] = res
+ return res
+ end
+ end
+
+ # Add a C label mark (if needed)
+ fun add_label(e: ISeq)
+ do
+ if _ids.has_key(e) then
+ add_instr("{_ids[e]}: while(0);")
+ end
+ end
+
+ # Add a goto to a label (even outside a closure)
+ fun add_goto(seq: ISeq)
+ do
+ if local_labels.has(seq) then
+ add_instr("goto {lab(seq)};")
+ else
+ assert closure
+ var ind = register_escape_label(seq)
+ add_instr("closctx->has_broke = (val_t*){ind};")
+ add_instr("goto {lab(return_label.as(not null))};")
+ end
+ end
+
+ # Are we in a closure ?
+ readable writable var _closure: Bool = false
+
+ # The current compiler visitor
+ readable var _visitor: CompilerVisitor
+
+ # The current compiled iroutine
+ readable var _iroutine: IRoutine
+
+ # The return label of the current compiling C function
+ readable writable var _return_label: nullable ISeq = null
+
+ fun add_decl(s: String)
+ do
+ visitor.add_decl(s)
+ end
+
+ fun add_instr(s: String)
+ do
+ var l = _next_location
+ if l != null then
+ visitor.add_instr("/* ", l.file, ":", l.line_start.to_s, " */")
+ _next_location = null
+ end
+ visitor.add_instr(s)
+ end
+
+ fun indent
+ do
+ visitor.indent
+ end
+
+ fun unindent
+ do
+ visitor.unindent
+ end
+
+ fun add_assignment(to, from: String)
+ do
+ visitor.add_assignment(to, from)
+ end
+
+ var _last_location: nullable Location = null
+ var _next_location: nullable Location = null
+
+ # Add location information in a comment
+ # Do nothing if the last location added is the same
+ fun add_location(l: nullable Location)
+ do
+ var last = _last_location
+ if last == l or l == null then return
+ _last_location = l
+ if last != null and last.file == l.file and last.line_start == l.line_start then
+ return
+ else
+ _next_location = l
+ end
+ end
+
+ # The C fonction name of the iroutine
+ readable var _basecname: String
+
+ init(v: CompilerVisitor, ir: IRoutine, cname: String)
+ do
+ _visitor = v
+ _iroutine = ir
+ _basecname = cname
+ end
+end
+
+redef class IRoutine
+ # Declare and start a C function that match the routine
+ # Return what must be given to compile_inside_to_c or to compile_to_c
+ # After the method, an openinig { and and indent is added.
+ # So, do not forget to add a sub_context, to unintent and to add a closing }
+ fun compile_signature_to_c(v: CompilerVisitor, cname: String, human_name: nullable String, before_params, after_params: nullable String): Array[String]
+ do
+ var cargs = new Array[String]
+ var cparams = new Array[String]
+ if before_params != null then cparams.add(before_params)
+ for i in [0..params.length[ do
+ cargs.add("p{i}")
+ cparams.add("val_t p{i}")
+ end
+ if closure_decls != null then
+ for i in [0..closure_decls.length[ do
+ var closcn = "CLOS_{cname}_{i}"
+ var cs = closure_decls[i].closure.signature
+ var subparams = new Array[String] # Parameters of the closure
+ subparams.add("struct WBT_ *")
+ for j in [0..cs.arity[ do
+ subparams.add("val_t")
+ end
+ var rr = "void"
+ if cs.return_type != null then rr = "val_t"
+ v.add_decl("typedef {rr} (*{closcn})({subparams.join(", ")});")
+ cargs.add("wd{i}")
+ cparams.add("struct WBT_ *wd{i}")
+ end
+ end
+ if after_params != null then cparams.add(after_params)
+ var r = "void"
+ if result != null then r = "val_t"
+ var p: String
+ if cparams.is_empty then
+ p = "void"
+ else
+ p = cparams.join(", ")
+ end
+ if human_name != null then v.add_decl("#define LOCATE_", cname, " \"", human_name, "\"")
+ v.add_decl(r, " ", cname, "(", p, ");")
+ v.add_decl("typedef ", r, " (*", cname, "_t)(", p, ");")
+ v.add_instr(r, " ", cname, "(", p, ")\{")
+ v.indent
+ return cargs
+ end
+
+ # Compile the body of the routine, return the result value is any
+ fun compile_inside_to_c(v: I2CCompilerVisitor, args: Array[String]): nullable String
+ do
+ # Add the trace
+ var ll = 0
+ if location != null then
+ ll = location.line_start
+ end
+ v.add_decl("struct trace_t trace = \{NULL, NULL, {ll}, LOCATE_{v.basecname}\};")
+ v.add_instr("trace.prev = tracehead; tracehead = &trace;")
+ v.add_instr("trace.file = LOCATE_{v.visitor.module.name};")
+
+ # Add local variables
+ if std_slots_nb == 0 then
+ v.add_decl("val_t *REG = NULL;")
+ else
+ v.add_decl("val_t REG[{std_slots_nb}];")
+ end
+ for i in [0..bool_slots_nb[ do
+ v.add_decl("val_t REGB{i};")
+ end
+ var iclosdecls = closure_decls
+ if iclosdecls != null then
+ v.add_decl("struct WBT_ *CREG[{iclosdecls.length}];")
+ else
+ v.add_decl("struct WBT_ **CREG = NULL;")
+ end
+ var i = 0
+ for r in params do
+ if r.slot_index != null then v.add_assignment(v.register(r), args[i])
+ i += 1
+ end
+ var iclosdecls = closure_decls
+ if iclosdecls != null then
+ for i in [0..iclosdecls.length[ do
+ var iclosdecl = iclosdecls[i]
+ v.add_instr("CREG[{i}] = {args[params.length+i]};")
+ v.closures[iclosdecl] = i
+ var cs = iclosdecl.closure.signature # Closure signature
+ var subparams = new Array[String] # Parameters of the closure
+ subparams.add("struct WBT_ *")
+ for j in [0..cs.arity[ do
+ var p = "val_t"
+ subparams.add(p)
+ end
+ var r = "void"
+ if cs.return_type != null then r = "val_t"
+ v.clostypes[iclosdecl] = "{r} (*)({subparams.join(", ")})"
+ end
+ end
+ v.add_decl("val_t tmp;")
+
+ # Prepare return
+ var old_rl = v.return_label
+ v.return_label = body
+
+ # Compile body
+ body.compile_to_c(v)
+
+ v.add_instr("tracehead = trace.prev;")
+ v.return_label = old_rl
+ var r = result
+ if r != null then
+ return v.register(r)
+ else
+ return null
+ end
+ end
+
+ # Full compilation of the routine
+ # Including optimization and other stuff.
+ # cv must be in the correct function
+ fun compile_to_c(cv: CompilerVisitor, cname: String, args: Array[String]): nullable String
+ do
+ optimize
+ var v = new I2CCompilerVisitor(cv, self, cname)
+ return compile_inside_to_c(v, args)
+ end
+end
+
+redef class ICode
+ # Full compilation of the icode
+ fun compile_to_c(v: I2CCompilerVisitor)
+ do
+ v.add_location(location)
+ store_result(v, inner_compile_to_c(v))
+ end
+
+ # Is a result really needed
+ private fun need_result: Bool
+ do
+ var r = result
+ return r != null and r.slot_index != null
+ end
+
+ # Store s in the result value of self
+ private fun store_result(v: I2CCompilerVisitor, s: nullable String)
+ do
+ var r = result
+ if r != null and r.slot_index != null then
+ assert s != null
+ v.add_assignment(v.register(r), s)
+ else if s != null and not self isa IMove then
+ v.add_instr(s + ";")
+ end
+ end
+
+ # Compilation of without the result assigment
+ # Return the right value is case of expression
+ # Return the full expression (witout ;) in case of statement
+ private fun inner_compile_to_c(v: I2CCompilerVisitor): nullable String is abstract
+end
+
+redef class ISeq
+ redef fun inner_compile_to_c(v)
+ do
+ v.local_labels.add(self)
+ for ic in icodes do
+ ic.compile_to_c(v)
+ end
+ v.add_label(self)
+ return null
+ end
+end
+
+redef class IIf
+ redef fun inner_compile_to_c(v)
+ do
+ v.add_instr("if (UNTAG_Bool({v.register(expr)})) \{")
+ if not then_seq.icodes.is_empty then
+ v.indent
+ then_seq.inner_compile_to_c(v)
+ v.unindent
+ end
+ if not else_seq.icodes.is_empty then
+ v.add_instr("} else \{")
+ v.indent
+ else_seq.inner_compile_to_c(v)
+ v.unindent
+ end
+ v.add_instr("}")
+ return null
+ end
+end
+
+redef class ILoop
+ redef fun inner_compile_to_c(v)
+ do
+ v.local_labels.add(self)
+ v.add_instr("while(1) \{")
+ v.indent
+ for ic in icodes do
+ ic.compile_to_c(v)
+ end
+ v.unindent
+ v.add_instr("}")
+ v.add_label(self)
+ return null
+ end
+end
+
+redef class IEscape
+ redef fun inner_compile_to_c(v)
+ do
+ v.add_goto(seq)
+ return null
+ end
+end
+
+redef class IAbsCall
+ redef fun compile_to_c(v)
+ do
+ v.add_location(location)
+ var args = v.registers(exprs)
+
+ # Compile closure definitions
+ var old_el = v.escaped_labels
+ var closdefs = closure_defs
+ var closcns: nullable Array[String] = null
+ if closdefs != null then
+ v.escaped_labels = new HashMap[ISeq, Int]
+ closcns = new Array[String]
+ for cd in closdefs do
+ if cd != null then
+ var cn = cd.compile_closure(v)
+ args.add(cn)
+ closcns.add(cn)
+ else
+ args.add("NULL")
+ end
+ end
+ end
+
+ var s = compile_call_to_c(v, args)
+ var r: nullable String = s
+
+ # Intercept escapes
+ if closcns != null then
+ var els = v.escaped_labels
+ v.escaped_labels = old_el
+ # Is there possible escapes?
+ if not els.is_empty then
+ # Call in a tmp variable to avoid 'break' overwrite
+ if need_result then
+ r = "tmp"
+ v.add_assignment(r, s)
+ else
+ r = null
+ v.add_instr(s + ";")
+ end
+ # What is the escape index (if any?)
+ # It's computed as the union of has_broke
+ var switcha = new Array[String]
+ for cn in closcns do
+ switcha.add("((int)({cn}->has_broke))")
+ end
+ var switch = switcha.join(" | ")
+ # What are the expected escape indexes
+ v.add_instr("switch ({switch}) \{")
+ v.indent
+ # No escape occured, continue as usual
+ v.add_instr("case 0: break;")
+ var lls = v.local_labels
+ var iels = els.iterator
+ var forward_escape = false
+ while iels.is_ok do
+ var seq = iels.key
+ if lls.has(seq) then
+ # Local escape occured
+ v.add_instr("case {iels.item}: goto {v.lab(seq)};")
+ else
+ # Forward escape occured: register the escape label
+ assert v.closure
+ v.register_escape_label(seq)
+ forward_escape = true
+ end
+ iels.next
+ end
+ # Forward escape occured, just pass to the next one
+ if forward_escape then
+ v.add_instr("default: closctx->has_broke = (val_t*)({switch}); goto {v.lab(v.return_label.as(not null))};")
+ end
+ v.unindent
+ v.add_instr("\}")
+ end
+ end
+
+ store_result(v, r)
+ end
+
+ redef fun inner_compile_to_c(v) do abort
+
+ # The single invocation witout fancy stuffs
+ private fun compile_call_to_c(v: I2CCompilerVisitor, args: Array[String]): String is abstract
+end
+
+redef class ICall
+ redef fun compile_call_to_c(v, args)
+ do
+ var prop = property
+ if prop.global.is_init then args.add("init_table")
+ if prop.name == (once ("add".to_symbol)) and prop.local_class.name == (once ("Array".to_symbol)) then
+ return "{prop.cname}({args.join(", ")})"
+ else
+ return "{prop.global.meth_call}({args[0]})({args.join(", ")})"
+ end
+ end
+end
+
+redef class ISuper
+ redef fun compile_call_to_c(v, args)
+ do
+ var prop = property
+ if prop.global.is_init then args.add("init_table")
+ return "{prop.super_meth_call}({args[0]})({args.join(", ")})"
+ end
+end
+
+redef class INew
+ redef fun compile_call_to_c(v, args)
+ do
+ return "NEW_{stype.local_class}_{property.global.intro.cname}({args.join(", ")})"
+ end
+end
+
+redef class INative
+ redef fun inner_compile_to_c(v)
+ do
+ if exprs.is_empty then
+ return code
+ else
+ var res = new Buffer
+ var i = 0
+ var c = code.split_with("@@@")
+ for s in c do
+ res.append(s)
+ if i < exprs.length and i < c.length-1 then
+ res.append(v.register(exprs[i]))
+ end
+ i += 1
+ end
+ return res.to_s
+ end
+ end
+end
+
+redef class IAbort
+ redef fun inner_compile_to_c(v)
+ do
+ var s = new Buffer.from("fprintf(stderr")
+ for t in texts do
+ s.append(", \"{t}\"")
+ end
+ s.append(");")
+ v.add_instr(s.to_s)
+
+ var ll = location
+ var pl = property_location
+ s = new Buffer.from("fprintf(stderr, \"")
+ if pl != null then s.append(" in %s")
+ s.append(" (%s")
+ if ll != null then
+ s.append(":%d")
+ end
+ s.append(")\\n\", ")
+ if pl != null then s.append("LOCATE_{pl.cname}, ")
+ s.append("LOCATE_{module_location.name}")
+ if ll != null then
+ s.append(", {ll.line_start}")
+ end
+ s.append(");")
+ v.add_instr(s.to_s)
+
+ v.add_instr("nit_exit(1);")
+ return null
+ end
+end
+
+redef class IMove
+ redef fun inner_compile_to_c(v)
+ do
+ return v.register(expr)
+ end
+end
+
+redef class IAttrRead
+ redef fun inner_compile_to_c(v)
+ do
+ return "{property.global.attr_access}({v.register(expr)})"
+ end
+end
+
+redef class IAttrIsset
+ redef fun inner_compile_to_c(v)
+ do
+ return "TAG_Bool({property.global.attr_access}({v.register(expr)})!=NIT_NULL)"
+ end
+end
+
+redef class IAttrWrite
+ redef fun inner_compile_to_c(v)
+ do
+ v.add_instr("{property.global.attr_access}({v.register(expr1)}) = {v.register(expr2)};")
+ return null
+ end
+end
+
+redef class ITypeCheck
+ redef fun inner_compile_to_c(v)
+ do
+ # FIXME handle formaltypes
+ var g = stype.local_class.global
+ var recv = v.register(expr)
+ var s = ""
+ if expr.stype.is_nullable then
+ if stype.is_nullable then
+ s = "({recv}==NIT_NULL) || "
+ else if stype.as_nullable == expr.stype then
+ return "TAG_Bool({recv}!=NIT_NULL)"
+ else
+ s = "({recv}!=NIT_NULL) && "
+ end
+ end
+ return "TAG_Bool({s}VAL_ISA({recv}, {g.color_id}, {g.id_id})) /*cast {stype}*/"
+ end
+end
+
+redef class IIs
+ redef fun inner_compile_to_c(v)
+ do
+ var t1 = expr1.stype
+ var t2 = expr2.stype
+ if t1 isa MMTypeNone then
+ if t2 isa MMTypeNone then
+ return "TAG_Bool(1)"
+ else if t2.is_nullable then
+ return "TAG_Bool({v.register(expr2)}==NIT_NULL)"
+ else
+ return "TAG_Bool(0)"
+ end
+ else if t1.is_nullable then
+ if t2 isa MMTypeNone then
+ return "TAG_Bool({v.register(expr1)}==NIT_NULL)"
+ else if t2.is_nullable then
+ return "TAG_Bool(IS_EQUAL_NN({v.register(expr1)},{v.register(expr2)}))"
+ else
+ return "TAG_Bool(IS_EQUAL_ON({v.register(expr2)},{v.register(expr1)}))"
+ end
+ else
+ if t2 isa MMTypeNone then
+ return "TAG_Bool(0)"
+ else if t2.is_nullable then
+ return "TAG_Bool(IS_EQUAL_ON({v.register(expr1)},{v.register(expr2)}))"
+ else
+ return "TAG_Bool(IS_EQUAL_OO({v.register(expr1)},{v.register(expr2)}))"
+ end
+ end
+ end
+end
+
+redef class INot
+ redef fun inner_compile_to_c(v)
+ do
+ return "TAG_Bool(!UNTAG_Bool({v.register(expr)}))"
+ end
+end
+
+redef class IOnce
+ redef fun inner_compile_to_c(v)
+ do
+ var i = v.new_number
+ var res = result.as(not null)
+ if res.stype.is_nullable then
+ v.add_decl("static val_t once_value_{i}; static int once_bool_{i}; /* Once value */")
+ v.add_instr("if (!once_bool_{i}) \{")
+ else
+ # Since the value is not nullable, we use the null value to represent the boolean
+ v.add_decl("static val_t once_value_{i}; /* Once value */")
+ v.add_instr("if (!once_value_{i}) \{")
+ end
+ v.indent
+ body.compile_to_c(v)
+ var e = v.register(result.as(not null))
+ v.add_instr("once_value_{i} = {e};")
+ if res.stype.is_nullable then v.add_instr("once_bool_{i} = true;")
+ v.unindent
+ v.add_instr("} else {e} = once_value_{i};")
+ return e
+ end
+end
+
+redef class IClosCall
+ redef fun compile_to_c(v: I2CCompilerVisitor)
+ do
+ v.add_location(location)
+ var ivar: String
+ if v.closure then
+ ivar = "closctx->closurevariable[{v.closures[closure_decl]}]"
+ else
+ ivar = "CREG[{v.closures[closure_decl]}]"
+ end
+ var args = [ivar]
+ args.append(v.registers(exprs))
+
+ var s = "(({v.clostypes[closure_decl]})({ivar}->fun))({args.join(", ")})"
+ store_result(v, s)
+
+ v.add_instr("if ({ivar}->has_broke) \{")
+ v.indent
+ var bs = break_seq
+ if bs != null then
+ bs.compile_to_c(v)
+ end
+ v.add_goto(v.iroutine.body)
+ v.unindent
+ v.add_instr("\}")
+ end
+
+ redef fun inner_compile_to_c(v) do abort
+end
+
+redef class IHasClos
+ redef fun inner_compile_to_c(v)
+ do
+ var ivar: String
+ if v.closure then
+ ivar = "closctx->closurevariable[{v.closures[closure_decl]}]"
+ else
+ ivar = "CREG[{v.closures[closure_decl]}]"
+ end
+ return "TAG_Bool({ivar} != NULL)"
+ end
+end
+
+
+redef class IClosureDef
+ fun compile_closure(v: I2CCompilerVisitor): String
+ do
+ var cfc_old = v.closure
+ v.closure = true
+ var lls_old = v.local_labels
+ v.local_labels = new HashSet[ISeq]
+
+ var cv = v.visitor
+ var ctx_old = cv.ctx
+ cv.ctx = new CContext
+ cv.out_contexts.add(cv.ctx)
+
+ var cname = "OC_{v.basecname}_{v.new_number}"
+ var args = compile_signature_to_c(v.visitor, cname, null, "struct WBT_ *closctx", null)
+ var ctx_old2 = cv.ctx
+ cv.ctx = new CContext
+
+ var s = compile_inside_to_c(v, args)
+ if s == null then
+ v.add_instr("return;")
+ else
+ v.add_instr("return {s};")
+ end
+
+ ctx_old2.append(cv.ctx)
+ cv.ctx = ctx_old2
+ v.unindent
+ v.add_instr("}")
+ cv.ctx = ctx_old
+
+ v.closure = cfc_old
+
+ # Build closure
+ var closcnv = "wbclos{v.new_number}"
+ v.add_decl("struct WBT_ {closcnv};")
+ v.add_instr("{closcnv}.fun = (fun_t){cname};")
+ v.add_instr("{closcnv}.has_broke = NULL;")
+ if cfc_old then
+ v.add_instr("{closcnv}.variable = closctx->variable;")
+ v.add_instr("{closcnv}.closurevariable = closctx->closurevariable;")
+ else
+ v.add_instr("{closcnv}.variable = REG;")
+ v.add_instr("{closcnv}.closurevariable = CREG;")
+ end
+
+ v.local_labels = lls_old
+ return "(&{closcnv})"
+ end
+end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2008 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Compile method bodies, statments and expressions to C.
-package compiling_methods
-
-import compiling_base
-private import syntax
-
-redef class CompilerVisitor
- # Compile a statment node
- fun compile_stmt(n: nullable PExpr)
- do
- if n == null then return
- #add_instr("/* Compile stmt {n.locate} */")
- n.prepare_compile_stmt(self)
- var i = cfc._variable_index
- n.compile_stmt(self)
- cfc._variable_index = i
- end
-
- # Compile is expression node
- fun compile_expr(n: PExpr): String
- do
- #add_instr("/* Compile expr {n.locate} */")
- var i = cfc._variable_index
- var s = n.compile_expr(self)
- cfc._variable_index = i
- if s[0] == ' ' or cfc.is_valid_variable(s) then
- return s
- end
- var v = cfc.get_var("Result")
- add_assignment(v, s)
- return v
- end
-
- # Ensure that a c expression is a var
- fun ensure_var(s: String, comment: String): String
- do
- if cfc.is_valid_variable(s) then
- add_instr("/* Ensure var {s}: {comment}*/")
- return s
- end
- var v = cfc.get_var(null)
- add_assignment(v, "{s} /* Ensure var: {comment}*/")
- return v
- end
-
- readable writable var _cfc: nullable CFunctionContext
-
- readable writable var _nmc: nullable NitMethodContext
-
- # Generate an fprintf to display an error location
- fun printf_locate_error(node: PNode): String
- do
- var s = new Buffer.from("fprintf(stderr, \"")
- if nmc != null then s.append(" in %s")
- s.append(" (%s:%d)\\n\", ")
- if nmc != null then s.append("LOCATE_{nmc.method.cname}, ")
- s.append("LOCATE_{module.name}, {node.location.line_start});")
- return s.to_s
- end
-
- fun invoke_super_init_calls_after(start_prop: nullable MMMethod)
- do
- var n = nmc.method.node
- assert n isa AConcreteInitPropdef
-
- if n.super_init_calls.is_empty then return
- var i = 0
- var j = 0
- #var s = ""
- if start_prop != null then
- while n.super_init_calls[i] != start_prop do
- #s.append(" {n.super_init_calls[i]}")
- i += 1
- end
- i += 1
- #s.append(" {start_prop}")
-
- while n.explicit_super_init_calls[j] != start_prop do
- j += 1
- end
- j += 1
- end
- var stop_prop: nullable MMMethod = null
- if j < n.explicit_super_init_calls.length then
- stop_prop = n.explicit_super_init_calls[j]
- end
- var l = n.super_init_calls.length
- #s.append(" [")
- while i < l do
- var p = n.super_init_calls[i]
- if p == stop_prop then break
- var cargs = new Array[String]
- if p.signature.arity == 0 then
- cargs.add(cfc.varname(nmc.method_params[0]))
- else
- for va in nmc.method_params.as(not null) do
- cargs.add(cfc.varname(va))
- end
- end
- #s.append(" {p}")
- p.compile_stmt_call(self, cargs)
- i += 1
- end
- #s.append(" ]")
- #while i < l do
- # s.append(" {n.super_init_calls[i]}")
- # i += 1
- #end
- #if stop_prop != null then s.append(" (stop at {stop_prop})")
- #n.printl("implicit calls in {n.method}: {s}")
- end
-end
-
-# A C function currently written
-class CFunctionContext
- readable var _visitor: CompilerVisitor
-
- # Next available variable number
- var _variable_index: Int = 0
-
- # Total number of variable
- var _variable_index_max: Int = 0
-
- # Association between nit variable and the corrsponding c variable index
- var _varindexes: Map[Variable, Int] = new HashMap[Variable, Int]
-
- # Are we currenlty in a closure definition?
- readable writable var _closure: nullable NitMethodContext = null
-
- # Return the cvariable of a Nit variable
- fun varname(v: Variable): String
- do
- if v isa ClosureVariable then
- return closure_variable(_varindexes[v])
- else
- return variable(_varindexes[v])
- end
- end
-
- # Return the next available variable
- fun get_var(comment: nullable String): String
- do
- var v = variable(_variable_index)
- _variable_index = _variable_index + 1
- if _variable_index > _variable_index_max then
- _variable_index_max = _variable_index
- end
- if comment != null then
- visitor.add_instr("/* Register {v}: {comment} */")
- end
- return v
- end
-
- fun register_variable(v: Variable): String
- do
- _varindexes[v] = _variable_index
- var s = get_var("Local variable")
- return s
- end
-
- # Next available closure variable number
- var _closurevariable_index: Int = 0
-
- fun register_closurevariable(v: ClosureVariable): String
- do
- var s = "closurevariable[{_closurevariable_index}]"
- _varindexes[v] = _closurevariable_index
- _closurevariable_index += 1
- if _closure != null then
- return "(closctx->{s})"
- else
- return s
- end
- end
-
- # Return the ith cvariable
- protected fun variable(i: Int): String
- do
- if closure != null then
- var vn = once new Array[String]
- if vn.length <= i then
- for j in [vn.length..i] do
- vn[j] = "(closctx->variable[{j}])"
- end
- end
- return vn[i]
- else
- var vn = once new Array[String]
- if vn.length <= i then
- for j in [vn.length..i] do
- vn[j] = "variable[{j}]"
- end
- end
- return vn[i]
- end
- end
-
- # Return the ith closurevariable
- protected fun closure_variable(i: Int): String
- do
- if closure != null then
- return "(closctx->closurevariable[{i}])"
- else
- return "closurevariable[{i}]"
- end
- end
-
- # Is s a valid variable
- protected fun is_valid_variable(s: String): Bool
- do
- for i in [0.._variable_index[ do
- if s == variable(i) then return true
- end
- return false
- end
-
- # Mark the variable available
- fun free_var(v: String)
- do
- # FIXME: So ugly..
- if v == variable(_variable_index-1) then
- _variable_index = _variable_index - 1
- end
- end
-
- # Generate the local variable declarations
- # To use at the end of the C function once all variables are known
- fun generate_var_decls
- do
- if _variable_index_max > 0 then
- visitor.add_decl("val_t variable[{_variable_index_max}];")
- else
- visitor.add_decl("val_t *variable = NULL;")
- end
- if _closurevariable_index > 0 then
- visitor.add_decl("struct WBT_ *closurevariable[{_closurevariable_index}];")
- else
- visitor.add_decl("struct WBT_ **closurevariable = NULL;")
- end
- end
-
- init(v: CompilerVisitor) do _visitor = v
-end
-
-# A Nit method currenlty compiled
-class NitMethodContext
- # Current method compiled
- readable var _method: nullable MMMethod
-
- # Association between parameters and the corresponding variables
- readable writable var _method_params: nullable Array[ParamVariable]
-
- # Where a nit return must branch
- readable writable var _return_label: nullable String
-
- # Where a nit break must branch
- readable writable var _break_label: nullable String
-
- # Where a nit continue must branch
- readable writable var _continue_label: nullable String
-
- # Variable where a functionnal nit return must store its value
- readable writable var _return_value: nullable String
-
- # Variable where a functionnal nit break must store its value
- readable writable var _break_value: nullable String
-
- # Variable where a functionnal nit continue must store its value
- readable writable var _continue_value: nullable String
-
- init(method: nullable MMMethod)
- do
- _method = method
- end
-end
-
-###############################################################################
-
-redef class ClosureVariable
- readable writable var _ctypename: nullable String
-end
-
-redef class MMAttribute
- # Compile a read acces on selffor a given reciever.
- fun compile_isset(v: CompilerVisitor, n: PNode, recv: String): String
- do
- return "TAG_Bool({global.attr_access}({recv})!=NIT_NULL) /* isset {local_class}::{name}*/"
- end
-
- # Compile a read acces on selffor a given reciever.
- fun compile_read_access(v: CompilerVisitor, n: PNode, recv: String): String
- do
- var res = "{global.attr_access}({recv}) /*{local_class}::{name}*/"
- if not signature.return_type.is_nullable then
- res = v.ensure_var(res, "{local_class}::{name}")
- v.add_instr("if ({res} == NIT_NULL) \{ fprintf(stderr, \"Uninitialized attribute %s\", \"{name}\"); {v.printf_locate_error(n)} nit_exit(1); } /* implicit isset */;")
- end
- return res
- end
-
- # Compile a write acces on selffor a given reciever.
- fun compile_write_access(v: CompilerVisitor, n: nullable PNode, recv: String, value: String)
- do
- v.add_instr("{global.attr_access}({recv}) /*{local_class}::{name}*/ = {value};")
- end
-end
-
-redef class MMLocalProperty
- # Compile the property as a C property
- fun compile_property_to_c(v: CompilerVisitor) do end
-end
-
-redef class MMMethod
- # Compile as an expression.
- # require that signature.return_type != null
- fun compile_expr_call(v: CompilerVisitor, cargs: Array[String]): String
- do
- assert signature.return_type != null
- var s = intern_compile_call(v, cargs)
- assert s != null
- return s
- end
-
- # Compile as a statement.
- # require that signature.return_type == null
- fun compile_stmt_call(v: CompilerVisitor, cargs: Array[String])
- do
- assert signature.return_type == null
- var s = intern_compile_call(v, cargs)
- assert s == null
- end
-
- # Compile a call on self for given arguments
- # Most calls are compiled with a table access,
- # primitive calles are inlined
- # == and != are guarded and possibly inlined
- private fun intern_compile_call(v: CompilerVisitor, cargs: Array[String]): nullable String
- do
- var i = self
- if i isa MMSrcMethod then
- if i isa MMMethSrcMethod and i.node isa AInternMethPropdef or
- (i.local_class.name == (once "Array".to_symbol) and name == (once "[]".to_symbol))
- then
- var e = i.do_compile_inside(v, cargs)
- return e
- end
- end
- var ee = once "==".to_symbol
- var ne = once "!=".to_symbol
- if name == ne then
- var eqp = signature.recv.local_class.select_method(ee)
- var eqcall = eqp.compile_expr_call(v, cargs)
- return "TAG_Bool(!UNTAG_Bool({eqcall}))"
- end
- if global.is_init then
- cargs = cargs.to_a
- cargs.add("init_table /*YYY*/")
- end
-
- var m = "{global.meth_call}({cargs[0]})"
- var vcall = "{m}({cargs.join(", ")}) /*{local_class}::{name}*/"
- if name == ee then
- vcall = "UNTAG_Bool({vcall})"
- var obj = once "Object".to_symbol
- if i.local_class.name == obj then
- vcall = "(({m}=={i.cname})?(IS_EQUAL_NN({cargs[0]},{cargs[1]})):({vcall}))"
- end
- vcall = "TAG_Bool(({cargs.first} == {cargs[1]}) || (({cargs.first} != NIT_NULL) && {vcall}))"
- end
- if signature.return_type != null then
- return vcall
- else
- v.add_instr(vcall + ";")
- return null
- end
- end
-
- # Compile a call on self for given arguments and given closures
- fun compile_call_and_closures(v: CompilerVisitor, cargs: Array[String], clos_defs: nullable Array[PClosureDef]): nullable String
- do
- var ve: String
- var arity = 0
- if clos_defs != null then arity = clos_defs.length
-
- # Prepare result value.
- # In case of procedure, the return value is still used to intercept breaks
- var old_bv = v.nmc.break_value
- ve = v.cfc.get_var("Closure return value and escape marker")
- v.nmc.break_value = ve
-
- # Compile closure to c function
- var realcargs = new Array[String] # Args to pass to the C function call
- var closcns = new Array[String] # Closure C structure names
- realcargs.add_all(cargs)
- for i in [0..arity[ do
- var cn = clos_defs[i].compile_closure(v, closure_cname(i))
- closcns.add(cn)
- realcargs.add(cn)
- end
- for i in [arity..signature.closures.length[ do
- realcargs.add("NULL")
- end
-
- v.nmc.break_value = old_bv
-
- # Call
- var e = intern_compile_call(v, realcargs)
- if e != null then
- v.add_assignment(ve, e)
- e = ve
- end
-
- # Intercept returns and breaks
- for i in [0..arity[ do
- # A break or a return is intercepted
- v.add_instr("if ({closcns[i]}->has_broke != NULL) \{")
- v.indent
- # A passtrought break or a return is intercepted: go the the next closure
- v.add_instr("if ({closcns[i]}->has_broke != &({ve})) \{")
- v.indent
- if v.cfc.closure == v.nmc then v.add_instr("closctx->has_broke = {closcns[i]}->has_broke; closctx->broke_value = {closcns[i]}->broke_value;")
- v.add_instr("goto {v.nmc.return_label};")
- v.unindent
- # A direct break is interpected
- if e != null then
- # overwrite the returned value in a function
- v.add_instr("\} else {ve} = {closcns[i]}->broke_value;")
- else
- # Do nothing in a procedure
- v.add_instr("\}")
- end
- v.unindent
- v.add_instr("\}")
- end
- return e
- end
-
- # Compile a call as constructor with given args
- fun compile_constructor_call(v: CompilerVisitor, recvtype: MMType, cargs: Array[String]): String
- do
- return "NEW_{recvtype.local_class}_{global.intro.cname}({cargs.join(", ")}) /*new {recvtype}*/"
- end
-
- # Compile a call as call-next-method on self with given args
- fun compile_super_call(v: CompilerVisitor, cargs: Array[String]): String
- do
- if global.is_init then cargs.add("init_table")
- var m = "{super_meth_call}({cargs[0]})"
- var vcall = "{m}({cargs.join(", ")}) /*super {local_class}::{name}*/"
- return vcall
- end
-
- # Cname of the i-th closure C struct type
- protected fun closure_cname(i: Int): String
- do
- return "FWBT_{cname}_{i}"
- end
-
- # Compile and declare the signature to C
- protected fun decl_csignature(v: CompilerVisitor, args: Array[String]): String
- do
- var params = new Array[String]
- params.add("val_t {args[0]}")
- for i in [0..signature.arity[ do
- var p = "val_t {args[i+1]}"
- params.add(p)
- end
-
- var first_closure_index = signature.arity + 1 # Wich parameter is the first closure
- for i in [0..signature.closures.length[ do
- var closcn = closure_cname(i)
- var cs = signature.closures[i].signature # Closure signature
- var subparams = new Array[String] # Parameters of the closure
- subparams.add("struct WBT_ *")
- for j in [0..cs.arity[ do
- var p = "val_t"
- subparams.add(p)
- end
- var r = "void"
- if cs.return_type != null then r = "val_t"
- params.add("struct WBT_ *{args[first_closure_index+i]}")
- v.add_decl("typedef {r} (*{closcn})({subparams.join(", ")});")
- end
-
- if global.is_init then
- params.add("int* init_table")
- end
-
- var ret: String
- if signature.return_type != null then
- ret = "val_t"
- else
- ret = "void"
- end
-
- var p = params.join(", ")
- var s = "{ret} {cname}({p})"
- v.add_decl("typedef {ret} (* {cname}_t)({p});")
- v.add_decl(s + ";")
- return s
- end
-
- redef fun compile_property_to_c(v)
- do
- v.cfc = new CFunctionContext(v)
-
- var args = new Array[String]
- args.add(" self")
- for i in [0..signature.arity[ do
- args.add(" param{i}")
- end
- for i in [0..signature.closures.length[ do
- args.add(" wd{i}")
- end
- var cs = decl_csignature(v, args)
- v.add_decl("#define LOCATE_{cname} \"{full_name}\"")
-
- v.add_instr("{cs} \{")
- v.indent
- var ctx_old = v.ctx
- v.ctx = new CContext
-
- v.out_contexts.clear
-
- var itpos: nullable String = null
- if global.is_init then
- itpos = "itpos{v.new_number}"
- v.add_decl("int {itpos} = VAL2OBJ(self)->vft[{local_class.global.init_table_pos_id}].i;")
- v.add_instr("if (init_table[{itpos}]) return;")
- end
-
- var ln = 0
- var s = self
- if s.node != null then ln = s.node.location.line_start
- v.add_decl("struct trace_t trace = \{NULL, NULL, {ln}, LOCATE_{cname}};")
- v.add_instr("trace.prev = tracehead; tracehead = &trace;")
- v.add_instr("trace.file = LOCATE_{module.name};")
-
- var s = do_compile_inside(v, args)
-
- if itpos != null then
- v.add_instr("init_table[{itpos}] = 1;")
- end
-
- v.add_instr("tracehead = trace.prev;")
- if s == null then
- v.add_instr("return;")
- else
- v.add_instr("return {s};")
- end
-
- v.cfc.generate_var_decls
-
- ctx_old.append(v.ctx)
- v.ctx = ctx_old
- v.unindent
- v.add_instr("}")
-
- for ctx in v.out_contexts do v.ctx.merge(ctx)
- end
-
- # Compile the method body inline
- fun do_compile_inside(v: CompilerVisitor, params: Array[String]): nullable String is abstract
-end
-
-redef class MMReadImplementationMethod
- redef fun do_compile_inside(v, params)
- do
- return node.prop.compile_read_access(v, node, params[0])
- end
-end
-
-redef class MMWriteImplementationMethod
- redef fun do_compile_inside(v, params)
- do
- node.prop.compile_write_access(v, node, params[0], params[1])
- return null
- end
-end
-
-redef class MMMethSrcMethod
- redef fun do_compile_inside(v, params)
- do
- return node.do_compile_inside(v, self, params)
- end
-end
-
-redef class MMImplicitInit
- redef fun do_compile_inside(v, params)
- do
- var f = params.length - unassigned_attributes.length
- var recv = params.first
- for sp in super_inits do
- assert sp isa MMMethod
- var args_recv = [recv]
- if sp == super_init then
- var args = new Array[String].with_capacity(f)
- args.add(recv)
- for i in [1..f[ do
- args.add(params[i])
- end
- sp.compile_stmt_call(v, args)
- else
- sp.compile_stmt_call(v, args_recv)
- end
- end
- for i in [f..params.length[ do
- var attribute = unassigned_attributes[i-f]
- attribute.compile_write_access(v, null, recv, params[i])
- end
- return null
- end
-end
-
-redef class MMType
- # Compile a subtype check to self
- # Return a NIT Bool
- fun compile_cast(v: CompilerVisitor, recv: String, fromtype: MMType): String
- do
- # Fixme: handle formaltypes
- var g = local_class.global
- var s = ""
- if fromtype.is_nullable then
- if self.is_nullable then
- s = "({recv}==NIT_NULL) || "
- else
- s = "({recv}!=NIT_NULL) && "
- end
- else
- # FIXME This is used to not break code without the nullable KW
- s = "({recv}==NIT_NULL) || "
- end
- return "TAG_Bool({s}VAL_ISA({recv}, {g.color_id}, {g.id_id})) /*cast {self}*/"
- end
-
- # Compile a cast assertion
- fun compile_type_check(v: CompilerVisitor, recv: String, n: PNode, fromtype: MMType)
- do
- # Fixme: handle formaltypes
- var g = local_class.global
- var s = ""
- if fromtype.is_nullable then
- if self.is_nullable then
- s = "({recv}!=NIT_NULL) && "
- else
- s = "({recv}==NIT_NULL) || "
- end
- else
- # FIXME This is used to not break code without the nullable KW
- s = "({recv}!=NIT_NULL) && "
- end
- v.add_instr("if ({s}!VAL_ISA({recv}, {g.color_id}, {g.id_id})) \{ fprintf(stderr, \"Cast failed\"); {v.printf_locate_error(n)} nit_exit(1); } /*cast {self}*/;")
- end
-
- # Compile a notnull cast assertion
- fun compile_notnull_check(v: CompilerVisitor, recv: String, n: PNode)
- do
- if is_nullable then
- v.add_instr("if (({recv}==NIT_NULL)) \{ fprintf(stderr, \"Cast failed\"); {v.printf_locate_error(n)} nit_exit(1); } /*cast {self}*/;")
- end
- end
-end
-
-###############################################################################
-
-redef class AMethPropdef
- # Compile the method body
- fun do_compile_inside(v: CompilerVisitor, method: MMMethod, params: Array[String]): nullable String is abstract
-end
-
-redef class PSignature
- fun compile_parameters(v: CompilerVisitor, orig_sig: MMSignature, params: Array[String]) is abstract
-end
-
-redef class ASignature
- redef fun compile_parameters(v: CompilerVisitor, orig_sig: MMSignature, params: Array[String])
- do
- for ap in n_params do
- var cname = v.cfc.register_variable(ap.variable)
- v.nmc.method_params.add(ap.variable)
- var orig_type = orig_sig[ap.position]
- if not orig_type < ap.variable.stype.as(not null) then
- # FIXME: do not test always
- # FIXME: handle formal types
- v.add_instr("/* check if p<{ap.variable.stype} with p:{orig_type} */")
- ap.variable.stype.compile_type_check(v, params[ap.position], ap, orig_type)
- end
- v.add_assignment(cname, params[ap.position])
- end
- for i in [0..n_closure_decls.length[ do
- var wd = n_closure_decls[i]
- var cname = v.cfc.register_closurevariable(wd.variable)
- wd.variable.ctypename = v.nmc.method.closure_cname(i)
- v.add_assignment(cname, "{params[orig_sig.arity + i]}")
- end
- end
-end
-
-redef class AConcreteMethPropdef
- redef fun do_compile_inside(v, method, params)
- do
- var old_nmc = v.nmc
- v.nmc = new NitMethodContext(method)
-
- var selfcname = v.cfc.register_variable(self_var)
- v.add_assignment(selfcname, params[0])
- params.shift
- v.nmc.method_params = [self_var]
-
- var orig_meth: MMLocalProperty = method.global.intro
- var orig_sig = orig_meth.signature_for(method.signature.recv)
- if n_signature != null then
- n_signature.compile_parameters(v, orig_sig, params)
- end
-
- v.nmc.return_label = "return_label{v.new_number}"
- v.nmc.return_value = v.cfc.get_var("Method return value and escape marker")
- if self isa AConcreteInitPropdef then
- v.invoke_super_init_calls_after(null)
- end
- v.compile_stmt(n_block)
- v.add_instr("{v.nmc.return_label}: while(false);")
-
- var ret: nullable String = null
- if method.signature.return_type != null then
- ret = v.nmc.return_value
- end
-
- v.nmc = old_nmc
- return ret
- end
-end
-
-redef class ADeferredMethPropdef
- redef fun do_compile_inside(v, method, params)
- do
- v.add_instr("fprintf(stderr, \"Deferred method called\");")
- v.add_instr(v.printf_locate_error(self))
- v.add_instr("nit_exit(1);")
- if method.signature.return_type != null then
- return("NIT_NULL")
- else
- return null
- end
- end
-end
-
-redef class AExternMethPropdef
- redef fun do_compile_inside(v, method, params)
- do
- var ename = "{method.module.name}_{method.local_class.name}_{method.local_class.name}_{method.name}_{method.signature.arity}"
- if n_extern != null then
- ename = n_extern.text
- ename = ename.substring(1, ename.length-2)
- end
- var sig = method.signature
- if params.length != sig.arity + 1 then
- printl("par:{params.length} sig:{sig.arity}")
- end
- var args = new Array[String]
- args.add(sig.recv.unboxtype(params[0]))
- for i in [0..sig.arity[ do
- args.add(sig[i].unboxtype(params[i+1]))
- end
- var s = "{ename}({args.join(", ")})"
- if sig.return_type != null then
- return sig.return_type.boxtype(s)
- else
- v.add_instr("{s};")
- return null
- end
- end
-end
-
-redef class AInternMethPropdef
- redef fun do_compile_inside(v, method, p)
- do
- var c = method.local_class.name
- var n = method.name
- var s: nullable String = null
- if c == once "Int".to_symbol then
- if n == once "object_id".to_symbol then
- s = "{p[0]}"
- else if n == once "unary -".to_symbol then
- s = "TAG_Int(-UNTAG_Int({p[0]}))"
- else if n == once "output".to_symbol then
- v.add_instr("printf(\"%ld\\n\", UNTAG_Int({p[0]}));")
- else if n == once "ascii".to_symbol then
- s = "TAG_Char(UNTAG_Int({p[0]}))"
- else if n == once "succ".to_symbol then
- s = "TAG_Int(UNTAG_Int({p[0]})+1)"
- else if n == once "prec".to_symbol then
- s = "TAG_Int(UNTAG_Int({p[0]})-1)"
- else if n == once "to_f".to_symbol then
- s = "BOX_Float((float)UNTAG_Int({p[0]}))"
- else if n == once "+".to_symbol then
- s = "TAG_Int(UNTAG_Int({p[0]})+UNTAG_Int({p[1]}))"
- else if n == once "-".to_symbol then
- s = "TAG_Int(UNTAG_Int({p[0]})-UNTAG_Int({p[1]}))"
- else if n == once "*".to_symbol then
- s = "TAG_Int(UNTAG_Int({p[0]})*UNTAG_Int({p[1]}))"
- else if n == once "/".to_symbol then
- s = "TAG_Int(UNTAG_Int({p[0]})/UNTAG_Int({p[1]}))"
- else if n == once "%".to_symbol then
- s = "TAG_Int(UNTAG_Int({p[0]})%UNTAG_Int({p[1]}))"
- else if n == once "<".to_symbol then
- s = "TAG_Bool(UNTAG_Int({p[0]})<UNTAG_Int({p[1]}))"
- else if n == once ">".to_symbol then
- s = "TAG_Bool(UNTAG_Int({p[0]})>UNTAG_Int({p[1]}))"
- else if n == once "<=".to_symbol then
- s = "TAG_Bool(UNTAG_Int({p[0]})<=UNTAG_Int({p[1]}))"
- else if n == once ">=".to_symbol then
- s = "TAG_Bool(UNTAG_Int({p[0]})>=UNTAG_Int({p[1]}))"
- else if n == once "lshift".to_symbol then
- s = "TAG_Int(UNTAG_Int({p[0]})<<UNTAG_Int({p[1]}))"
- else if n == once "rshift".to_symbol then
- s = "TAG_Int(UNTAG_Int({p[0]})>>UNTAG_Int({p[1]}))"
- else if n == once "==".to_symbol then
- s = "TAG_Bool(({p[0]})==({p[1]}))"
- else if n == once "!=".to_symbol then
- s = "TAG_Bool(({p[0]})!=({p[1]}))"
- end
- else if c == once "Float".to_symbol then
- if n == once "object_id".to_symbol then
- s = "TAG_Int((bigint)UNBOX_Float({p[0]}))"
- else if n == once "unary -".to_symbol then
- s = "BOX_Float(-UNBOX_Float({p[0]}))"
- else if n == once "output".to_symbol then
- v.add_instr("printf(\"%f\\n\", UNBOX_Float({p[0]}));")
- else if n == once "to_i".to_symbol then
- s = "TAG_Int((bigint)UNBOX_Float({p[0]}))"
- else if n == once "+".to_symbol then
- s = "BOX_Float(UNBOX_Float({p[0]})+UNBOX_Float({p[1]}))"
- else if n == once "-".to_symbol then
- s = "BOX_Float(UNBOX_Float({p[0]})-UNBOX_Float({p[1]}))"
- else if n == once "*".to_symbol then
- s = "BOX_Float(UNBOX_Float({p[0]})*UNBOX_Float({p[1]}))"
- else if n == once "/".to_symbol then
- s = "BOX_Float(UNBOX_Float({p[0]})/UNBOX_Float({p[1]}))"
- else if n == once "<".to_symbol then
- s = "TAG_Bool(UNBOX_Float({p[0]})<UNBOX_Float({p[1]}))"
- else if n == once ">".to_symbol then
- s = "TAG_Bool(UNBOX_Float({p[0]})>UNBOX_Float({p[1]}))"
- else if n == once "<=".to_symbol then
- s = "TAG_Bool(UNBOX_Float({p[0]})<=UNBOX_Float({p[1]}))"
- else if n == once ">=".to_symbol then
- s = "TAG_Bool(UNBOX_Float({p[0]})>=UNBOX_Float({p[1]}))"
- end
- else if c == once "Char".to_symbol then
- if n == once "object_id".to_symbol then
- s = "TAG_Int(UNTAG_Char({p[0]}))"
- else if n == once "unary -".to_symbol then
- s = "TAG_Char(-UNTAG_Char({p[0]}))"
- else if n == once "output".to_symbol then
- v.add_instr("printf(\"%c\", (unsigned char)UNTAG_Char({p[0]}));")
- else if n == once "ascii".to_symbol then
- s = "TAG_Int((unsigned char)UNTAG_Char({p[0]}))"
- else if n == once "succ".to_symbol then
- s = "TAG_Char(UNTAG_Char({p[0]})+1)"
- else if n == once "prec".to_symbol then
- s = "TAG_Char(UNTAG_Char({p[0]})-1)"
- else if n == once "to_i".to_symbol then
- s = "TAG_Int(UNTAG_Char({p[0]})-'0')"
- else if n == once "+".to_symbol then
- s = "TAG_Char(UNTAG_Char({p[0]})+UNTAG_Char({p[1]}))"
- else if n == once "-".to_symbol then
- s = "TAG_Char(UNTAG_Char({p[0]})-UNTAG_Char({p[1]}))"
- else if n == once "*".to_symbol then
- s = "TAG_Char(UNTAG_Char({p[0]})*UNTAG_Char({p[1]}))"
- else if n == once "/".to_symbol then
- s = "TAG_Char(UNTAG_Char({p[0]})/UNTAG_Char({p[1]}))"
- else if n == once "%".to_symbol then
- s = "TAG_Char(UNTAG_Char({p[0]})%UNTAG_Char({p[1]}))"
- else if n == once "<".to_symbol then
- s = "TAG_Bool(UNTAG_Char({p[0]})<UNTAG_Char({p[1]}))"
- else if n == once ">".to_symbol then
- s = "TAG_Bool(UNTAG_Char({p[0]})>UNTAG_Char({p[1]}))"
- else if n == once "<=".to_symbol then
- s = "TAG_Bool(UNTAG_Char({p[0]})<=UNTAG_Char({p[1]}))"
- else if n == once ">=".to_symbol then
- s = "TAG_Bool(UNTAG_Char({p[0]})>=UNTAG_Char({p[1]}))"
- else if n == once "==".to_symbol then
- s = "TAG_Bool(({p[0]})==({p[1]}))"
- else if n == once "!=".to_symbol then
- s = "TAG_Bool(({p[0]})!=({p[1]}))"
- end
- else if c == once "Bool".to_symbol then
- if n == once "object_id".to_symbol then
- s = "TAG_Int(UNTAG_Bool({p[0]}))"
- else if n == once "unary -".to_symbol then
- s = "TAG_Bool(-UNTAG_Bool({p[0]}))"
- else if n == once "output".to_symbol then
- v.add_instr("(void)printf(UNTAG_Bool({p[0]})?\"true\\n\":\"false\\n\");")
- else if n == once "ascii".to_symbol then
- s = "TAG_Bool(UNTAG_Bool({p[0]}))"
- else if n == once "to_i".to_symbol then
- s = "TAG_Int(UNTAG_Bool({p[0]}))"
- else if n == once "==".to_symbol then
- s = "TAG_Bool(({p[0]})==({p[1]}))"
- else if n == once "!=".to_symbol then
- s = "TAG_Bool(({p[0]})!=({p[1]}))"
- end
- else if c == once "NativeArray".to_symbol then
- if n == once "object_id".to_symbol then
- s = "TAG_Int(UNBOX_NativeArray({p[0]}))"
- else if n == once "[]".to_symbol then
- s = "UNBOX_NativeArray({p[0]})[UNTAG_Int({p[1]})]"
- else if n == once "[]=".to_symbol then
- v.add_instr("UNBOX_NativeArray({p[0]})[UNTAG_Int({p[1]})]={p[2]};")
- else if n == once "copy_to".to_symbol then
- v.add_instr("(void)memcpy(UNBOX_NativeArray({p[1]}), UNBOX_NativeArray({p[0]}), UNTAG_Int({p[2]})*sizeof(val_t));")
- end
- else if c == once "NativeString".to_symbol then
- if n == once "object_id".to_symbol then
- s = "TAG_Int(UNBOX_NativeString({p[0]}))"
- else if n == once "atoi".to_symbol then
- s = "TAG_Int(atoi(UNBOX_NativeString({p[0]})))"
- else if n == once "[]".to_symbol then
- s = "TAG_Char(UNBOX_NativeString({p[0]})[UNTAG_Int({p[1]})])"
- else if n == once "[]=".to_symbol then
- v.add_instr("UNBOX_NativeString({p[0]})[UNTAG_Int({p[1]})]=UNTAG_Char({p[2]});")
- else if n == once "copy_to".to_symbol then
- v.add_instr("(void)memcpy(UNBOX_NativeString({p[1]})+UNTAG_Int({p[4]}), UNBOX_NativeString({p[0]})+UNTAG_Int({p[3]}), UNTAG_Int({p[2]}));")
- end
- else if n == once "object_id".to_symbol then
- s = "TAG_Int((bigint){p[0]})"
- else if n == once "sys".to_symbol then
- s = "(G_sys)"
- else if n == once "is_same_type".to_symbol then
- s = "TAG_Bool((VAL2VFT({p[0]})==VAL2VFT({p[1]})))"
- else if n == once "exit".to_symbol then
- v.add_instr("exit(UNTAG_Int({p[1]}));")
- else if n == once "calloc_array".to_symbol then
- s = "BOX_NativeArray((val_t*)malloc((UNTAG_Int({p[1]}) * sizeof(val_t))))"
- else if n == once "calloc_string".to_symbol then
- s = "BOX_NativeString((char*)malloc((UNTAG_Int({p[1]}) * sizeof(char))))"
-
- else
- stderr.write("{location}: Fatal error: unknown intern method {method.full_name}.\n")
- exit(1)
- end
- if method.signature.return_type != null and s == null then
- s = "NIT_NULL /*stub*/"
- end
- return s
- end
-end
-
-###############################################################################
-
-redef class PExpr
- # Compile the node as an expression
- # Only the visitor should call it
- fun compile_expr(v: CompilerVisitor): String is abstract
-
- # Prepare a call of node as a statement
- # Only the visitor should call it
- # It's used for local variable managment
- fun prepare_compile_stmt(v: CompilerVisitor) do end
-
- # Compile the node as a statement
- # Only the visitor should call it
- fun compile_stmt(v: CompilerVisitor) do printl("Error!")
-end
-
-redef class ABlockExpr
- redef fun compile_stmt(v)
- do
- for n in n_expr do
- v.compile_stmt(n)
- end
- end
-end
-
-redef class AVardeclExpr
- redef fun prepare_compile_stmt(v)
- do
- v.cfc.register_variable(variable)
- end
-
- redef fun compile_stmt(v)
- do
- var cname = v.cfc.varname(variable)
- if n_expr == null then
- v.add_instr("/*{cname} is variable {variable.name}*/")
- else
- var e = v.compile_expr(n_expr.as(not null))
- v.add_assignment(cname, e)
- end
- end
-end
-
-redef class AReturnExpr
- redef fun compile_stmt(v)
- do
- if n_expr != null then
- var e = v.compile_expr(n_expr.as(not null))
- v.add_assignment(v.nmc.return_value.as(not null), e)
- end
- if v.cfc.closure == v.nmc then v.add_instr("closctx->has_broke = &({v.nmc.return_value});")
- v.add_instr("goto {v.nmc.return_label};")
- end
-end
-
-redef class ABreakExpr
- redef fun compile_stmt(v)
- do
- if n_expr != null then
- var e = v.compile_expr(n_expr.as(not null))
- v.add_assignment(v.nmc.break_value.as(not null), e)
- end
- if v.cfc.closure == v.nmc then v.add_instr("closctx->has_broke = &({v.nmc.break_value}); closctx->broke_value = *closctx->has_broke;")
- v.add_instr("goto {v.nmc.break_label};")
- end
-end
-
-redef class AContinueExpr
- redef fun compile_stmt(v)
- do
- if n_expr != null then
- var e = v.compile_expr(n_expr.as(not null))
- v.add_assignment(v.nmc.continue_value.as(not null), e)
- end
- v.add_instr("goto {v.nmc.continue_label};")
- end
-end
-
-redef class AAbortExpr
- redef fun compile_stmt(v)
- do
- v.add_instr("fprintf(stderr, \"Aborted\"); {v.printf_locate_error(self)} nit_exit(1);")
- end
-end
-
-redef class ADoExpr
- redef fun compile_stmt(v)
- do
- v.compile_stmt(n_block)
- end
-end
-
-redef class AIfExpr
- redef fun compile_stmt(v)
- do
- var e = v.compile_expr(n_expr)
- v.add_instr("if (UNTAG_Bool({e})) \{ /*if*/")
- v.cfc.free_var(e)
- if n_then != null then
- v.indent
- v.compile_stmt(n_then)
- v.unindent
- end
- if n_else != null then
- v.add_instr("} else \{ /*if*/")
- v.indent
- v.compile_stmt(n_else)
- v.unindent
- end
- v.add_instr("}")
- end
-end
-
-redef class AIfexprExpr
- redef fun compile_expr(v)
- do
- var e = v.compile_expr(n_expr)
- v.add_instr("if (UNTAG_Bool({e})) \{ /*if*/")
- v.cfc.free_var(e)
- v.indent
- var e = v.ensure_var(v.compile_expr(n_then), "Then value")
- v.unindent
- v.add_instr("} else \{ /*if*/")
- v.cfc.free_var(e)
- v.indent
- var e2 = v.ensure_var(v.compile_expr(n_else), "Else value")
- v.add_assignment(e, e2)
- v.unindent
- v.add_instr("}")
- return e
- end
-end
-
-class AControlableBlock
-special PExpr
- fun compile_inside_block(v: CompilerVisitor) is abstract
- redef fun compile_stmt(v)
- do
- var old_break_label = v.nmc.break_label
- var old_continue_label = v.nmc.continue_label
- var id = v.new_number
- v.nmc.break_label = "break_{id}"
- v.nmc.continue_label = "continue_{id}"
-
- compile_inside_block(v)
-
-
- v.nmc.break_label = old_break_label
- v.nmc.continue_label = old_continue_label
- end
-end
-
-redef class AWhileExpr
-special AControlableBlock
- redef fun compile_inside_block(v)
- do
- v.add_instr("while (true) \{ /*while*/")
- v.indent
- var e = v.compile_expr(n_expr)
- v.add_instr("if (!UNTAG_Bool({e})) break; /* while*/")
- v.cfc.free_var(e)
- v.compile_stmt(n_block)
- v.add_instr("{v.nmc.continue_label}: while(0);")
- v.unindent
- v.add_instr("}")
- v.add_instr("{v.nmc.break_label}: while(0);")
- end
-end
-
-redef class AForExpr
-special AControlableBlock
- redef fun compile_inside_block(v)
- do
- var e = v.compile_expr(n_expr)
- var ittype = meth_iterator.signature.return_type
- v.cfc.free_var(e)
- var iter = v.cfc.get_var("For iterator")
- v.add_assignment(iter, meth_iterator.compile_expr_call(v, [e]))
- v.add_instr("while (true) \{ /*for*/")
- v.indent
- var ok = v.cfc.get_var("For 'is_ok' result")
- v.add_assignment(ok, meth_is_ok.compile_expr_call(v, [iter]))
- v.add_instr("if (!UNTAG_Bool({ok})) break; /*for*/")
- v.cfc.free_var(ok)
- var e = meth_item.compile_expr_call(v, [iter])
- e = v.ensure_var(e, "For item")
- var cname = v.cfc.register_variable(variable)
- v.add_assignment(cname, e)
- v.compile_stmt(n_block)
- v.add_instr("{v.nmc.continue_label}: while(0);")
- meth_next.compile_stmt_call(v, [iter])
- v.unindent
- v.add_instr("}")
- v.add_instr("{v.nmc.break_label}: while(0);")
- end
-end
-
-redef class AAssertExpr
- redef fun compile_stmt(v)
- do
- var e = v.compile_expr(n_expr)
- var s = ""
- if n_id != null then
- s = " '{n_id.text}' "
- end
- v.add_instr("if (!UNTAG_Bool({e})) \{ fprintf(stderr, \"Assert%s failed\", \"{s}\"); {v.printf_locate_error(self)} nit_exit(1);}")
- end
-end
-
-redef class AVarExpr
- redef fun compile_expr(v)
- do
- return " {v.cfc.varname(variable)} /*{variable.name}*/"
- end
-end
-
-redef class AVarAssignExpr
- redef fun compile_stmt(v)
- do
- var e = v.compile_expr(n_value)
- v.add_assignment(v.cfc.varname(variable), "{e} /*{variable.name}=*/")
- end
-end
-
-redef class AVarReassignExpr
- redef fun compile_stmt(v)
- do
- var e1 = v.cfc.varname(variable)
- var e2 = v.compile_expr(n_value)
- var e3 = assign_method.compile_expr_call(v, [e1, e2])
- v.add_assignment(v.cfc.varname(variable), "{e3} /*{variable.name}*/")
- end
-end
-
-redef class ASelfExpr
- redef fun compile_expr(v)
- do
- return v.cfc.varname(v.nmc.method_params[0])
- end
-end
-
-redef class AOrExpr
- redef fun compile_expr(v)
- do
- var e = v.ensure_var(v.compile_expr(n_expr), "Left 'or' operand")
- v.add_instr("if (!UNTAG_Bool({e})) \{ /* or */")
- v.cfc.free_var(e)
- v.indent
- var e2 = v.compile_expr(n_expr2)
- v.add_assignment(e, e2)
- v.unindent
- v.add_instr("}")
- return e
- end
-end
-
-redef class AAndExpr
- redef fun compile_expr(v)
- do
- var e = v.ensure_var(v.compile_expr(n_expr), "Left 'and' operand")
- v.add_instr("if (UNTAG_Bool({e})) \{ /* and */")
- v.cfc.free_var(e)
- v.indent
- var e2 = v.compile_expr(n_expr2)
- v.add_assignment(e, e2)
- v.unindent
- v.add_instr("}")
- return e
- end
-end
-
-redef class ANotExpr
- redef fun compile_expr(v)
- do
- return " TAG_Bool(!UNTAG_Bool({v.compile_expr(n_expr)}))"
- end
-end
-
-redef class AEeExpr
- redef fun compile_expr(v)
- do
- var e = v.compile_expr(n_expr)
- var e2 = v.compile_expr(n_expr2)
- return "TAG_Bool(IS_EQUAL_NN({e},{e2}))"
- end
-end
-
-redef class AIsaExpr
- redef fun compile_expr(v)
- do
- var e = v.compile_expr(n_expr)
- return n_type.stype.compile_cast(v, e, n_expr.stype)
- end
-end
-
-redef class AAsCastExpr
- redef fun compile_expr(v)
- do
- var e = v.compile_expr(n_expr)
- n_type.stype.compile_type_check(v, e, self, n_expr.stype)
- return e
- end
-end
-
-redef class AAsNotnullExpr
- redef fun compile_expr(v)
- do
- var e = v.compile_expr(n_expr)
- n_expr.stype.compile_notnull_check(v, e, self)
- return e
- end
-end
-
-redef class ATrueExpr
- redef fun compile_expr(v)
- do
- return " TAG_Bool(true)"
- end
-end
-
-redef class AFalseExpr
- redef fun compile_expr(v)
- do
- return " TAG_Bool(false)"
- end
-end
-
-redef class AIntExpr
- redef fun compile_expr(v)
- do
- return " TAG_Int({n_number.text})"
- end
-end
-
-redef class AFloatExpr
- redef fun compile_expr(v)
- do
- return "BOX_Float({n_float.text})"
- end
-end
-
-redef class ACharExpr
- redef fun compile_expr(v)
- do
- return " TAG_Char({n_char.text})"
- end
-end
-
-redef class AStringFormExpr
- redef fun compile_expr(v)
- do
- compute_string_info
- var i = v.new_number
- var cvar = v.cfc.get_var("Once String constant")
- v.add_decl("static val_t once_value_{i} = NIT_NULL; /* Once value for string {cvar}*/")
- v.add_instr("if (once_value_{i} != NIT_NULL) {cvar} = once_value_{i};")
- v.add_instr("else \{")
- v.indent
- v.cfc.free_var(cvar)
- var e = meth_with_native.compile_constructor_call(v, stype, ["BOX_NativeString(\"{_cstring}\")", "TAG_Int({_cstring_length})"])
- v.add_assignment(cvar, e)
- v.add_instr("once_value_{i} = {cvar};")
- v.unindent
- v.add_instr("}")
- return cvar
- end
-
- # The raw string value
- protected fun string_text: String is abstract
-
- # The string in a C native format
- protected var _cstring: nullable String
-
- # The string length in bytes
- protected var _cstring_length: nullable Int
-
- # Compute _cstring and _cstring_length using string_text
- protected fun compute_string_info
- do
- var len = 0
- var str = string_text
- var res = new Buffer
- var i = 0
- while i < str.length do
- var c = str[i]
- if c == '\\' then
- i = i + 1
- var c2 = str[i]
- if c2 != '{' and c2 != '}' then
- res.add(c)
- end
- c = c2
- end
- len = len + 1
- res.add(c)
- i = i + 1
- end
- _cstring = res.to_s
- _cstring_length = len
- end
-end
-
-redef class AStringExpr
- redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2)
-end
-redef class AStartStringExpr
- redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2)
-end
-redef class AMidStringExpr
- redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2)
-end
-redef class AEndStringExpr
- redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2)
-end
-
-redef class ASuperstringExpr
- redef fun compile_expr(v)
- do
- var array = meth_with_capacity.compile_constructor_call(v, atype, ["TAG_Int({n_exprs.length})"])
- array = v.ensure_var(array, "Array (for super-string)")
-
- for ne in n_exprs do
- var e = v.ensure_var(v.compile_expr(ne), "super-string element")
- if ne.stype != stype then
- v.cfc.free_var(e)
- e = meth_to_s.compile_expr_call(v, [e])
- end
- v.cfc.free_var(e)
- meth_add.compile_stmt_call(v, [array, e])
- end
-
- return meth_to_s.compile_expr_call(v, [array])
- end
-end
-
-redef class ANullExpr
- redef fun compile_expr(v)
- do
- return " NIT_NULL /*null*/"
- end
-end
-
-redef class AArrayExpr
- redef fun compile_expr(v)
- do
- var recv = meth_with_capacity.compile_constructor_call(v, stype, ["TAG_Int({n_exprs.length})"])
- recv = v.ensure_var(recv, "Literal array")
-
- for ne in n_exprs do
- var e = v.compile_expr(ne)
- meth_add.compile_stmt_call(v, [recv, e])
- end
- return recv
- end
-end
-
-redef class ARangeExpr
- redef fun compile_expr(v)
- do
- var e = v.compile_expr(n_expr)
- var e2 = v.compile_expr(n_expr2)
- return meth_init.compile_constructor_call(v, stype, [e, e2])
- end
-end
-
-redef class ASuperExpr
- redef fun compile_stmt(v)
- do
- var e = intern_compile_call(v)
- if e != null then
- v.add_instr(e + ";")
- end
- end
-
- redef fun compile_expr(v)
- do
- var e = intern_compile_call(v)
- assert e != null
- return e
- end
-
- private fun intern_compile_call(v: CompilerVisitor): nullable String
- do
- var arity = v.nmc.method_params.length - 1
- if init_in_superclass != null then
- arity = init_in_superclass.signature.arity
- end
- var args = new Array[String].with_capacity(arity + 1)
- args.add(v.cfc.varname(v.nmc.method_params[0]))
- if n_args.length != arity then
- for i in [0..arity[ do
- args.add(v.cfc.varname(v.nmc.method_params[i + 1]))
- end
- else
- for na in n_args do
- args.add(v.compile_expr(na))
- end
- end
- #return "{prop.cname}({args.join(", ")}) /*super {prop.local_class}::{prop.name}*/"
- if init_in_superclass != null then
- return init_in_superclass.intern_compile_call(v, args)
- else
- return prop.compile_super_call(v, args)
- end
- end
-end
-
-redef class AAttrExpr
- redef fun compile_expr(v)
- do
- var e = v.compile_expr(n_expr)
- return prop.compile_read_access(v, n_id, e)
- end
-end
-
-redef class AAttrAssignExpr
- redef fun compile_stmt(v)
- do
- var e = v.compile_expr(n_expr)
- var e2 = v.compile_expr(n_value)
- prop.compile_write_access(v, n_id, e, e2)
- end
-end
-redef class AAttrReassignExpr
- redef fun compile_stmt(v)
- do
- var e1 = v.compile_expr(n_expr)
- var e2 = prop.compile_read_access(v, n_id, e1)
- var e3 = v.compile_expr(n_value)
- var e4 = assign_method.compile_expr_call(v, [e2, e3])
- prop.compile_write_access(v, n_id, e1, e4)
- end
-end
-
-redef class AIssetAttrExpr
- redef fun compile_expr(v)
- do
- var e = v.compile_expr(n_expr)
- return prop.compile_isset(v, n_id, e)
- end
-end
-
-redef class AAbsAbsSendExpr
- # Compile each argument and add them to the array
- fun compile_arguments_in(v: CompilerVisitor, cargs: Array[String])
- do
- for a in arguments do
- cargs.add(v.compile_expr(a))
- end
- end
-
-end
-
-redef class ASendExpr
- private fun intern_compile_call(v: CompilerVisitor): nullable String
- do
- var recv = v.compile_expr(n_expr)
- var cargs = new Array[String]
- cargs.add(recv)
- compile_arguments_in(v, cargs)
-
- var e: nullable String
- if prop_signature.closures.is_empty then
- e = prop.intern_compile_call(v, cargs)
- else
- e = prop.compile_call_and_closures(v, cargs, closure_defs)
- end
-
- if prop.global.is_init then
- v.invoke_super_init_calls_after(prop)
- end
- return e
- end
-
- redef fun compile_expr(v)
- do
- var e = intern_compile_call(v)
- assert e != null
- return e
- end
-
- redef fun compile_stmt(v)
- do
- var e = intern_compile_call(v)
- if e != null then
- v.add_instr(e + ";")
- end
- end
-end
-
-redef class ASendReassignExpr
- redef fun compile_expr(v) do abort
-
- redef fun compile_stmt(v)
- do
- var recv = v.compile_expr(n_expr)
- var cargs = new Array[String]
- cargs.add(recv)
- compile_arguments_in(v, cargs)
-
- var e2 = read_prop.compile_expr_call(v, cargs)
- var e3 = v.compile_expr(n_value)
- var e4 = assign_method.compile_expr_call(v, [e2, e3])
- cargs.add(e4)
- prop.compile_stmt_call(v, cargs)
- end
-end
-
-redef class ANewExpr
- redef fun compile_expr(v)
- do
- var cargs = new Array[String]
- compile_arguments_in(v, cargs)
- return prop.compile_constructor_call(v, stype, cargs)
- end
-
- redef fun compile_stmt(v) do abort
-end
-
-redef class PClosureDef
- # Compile the closure definition as a function in v.out_contexts
- # Return the cname of the function
- fun compile_closure(v: CompilerVisitor, closcn: String): String is abstract
-
- # Compile the closure definition inside the current C function.
- fun do_compile_inside(v: CompilerVisitor, params: nullable Array[String]): nullable String is abstract
-end
-
-redef class AClosureDef
- # The cname of the function
- readable var _cname: nullable String
-
- redef fun compile_closure(v, closcn)
- do
- var ctx_old = v.ctx
- v.ctx = new CContext
- v.out_contexts.add(v.ctx)
-
- var cfc_old = v.cfc.closure
- v.cfc.closure = v.nmc
-
- var old_rv = v.nmc.return_value
- var old_bv = v.nmc.break_value
- if cfc_old == null then
- v.nmc.return_value = "closctx->{old_rv}"
- v.nmc.break_value = "closctx->{old_bv}"
- end
-
- var cname = "OC_{v.nmc.method.cname}_{v.out_contexts.length}"
- _cname = cname
- var args = new Array[String]
- for i in [0..closure.signature.arity[ do
- args.add(" param{i}")
- end
-
- var cs = decl_csignature(v, args, closcn)
-
- v.add_instr("{cs} \{")
- v.indent
- var ctx_old2 = v.ctx
- v.ctx = new CContext
-
- v.add_decl("struct trace_t trace = \{NULL, NULL, {location.line_start}, LOCATE_{v.nmc.method.cname}};")
- v.add_instr("trace.prev = tracehead; tracehead = &trace;")
-
- v.add_instr("trace.file = LOCATE_{v.module.name};")
- var s = do_compile_inside(v, args)
-
- v.add_instr("{v.nmc.return_label}:")
- v.add_instr("tracehead = trace.prev;")
- if s == null then
- v.add_instr("return;")
- else
- v.add_instr("return {s};")
- end
-
- ctx_old2.append(v.ctx)
- v.ctx = ctx_old2
- v.unindent
- v.add_instr("}")
- v.ctx = ctx_old
-
- v.cfc.closure = cfc_old
- v.nmc.return_value = old_rv
- v.nmc.break_value = old_bv
-
- # Build closure
- var closcnv = "wbclos{v.new_number}"
- v.add_decl("struct WBT_ {closcnv};")
- v.add_instr("{closcnv}.fun = (fun_t){cname};")
- v.add_instr("{closcnv}.has_broke = NULL;")
- if cfc_old != null then
- v.add_instr("{closcnv}.variable = closctx->variable;")
- v.add_instr("{closcnv}.closurevariable = closctx->closurevariable;")
- else
- v.add_instr("{closcnv}.variable = variable;")
- v.add_instr("{closcnv}.closurevariable = closurevariable;")
- end
-
- return "(&{closcnv})"
- end
-
- protected fun decl_csignature(v: CompilerVisitor, args: Array[String], closcn: String): String
- do
- var params = new Array[String]
- params.add("struct WBT_ *closctx")
- for i in [0..closure.signature.arity[ do
- var p = "val_t {args[i]}"
- params.add(p)
- end
- var ret: String
- if closure.signature.return_type != null then
- ret = "val_t"
- else
- ret = "void"
- end
- var p = params.join(", ")
- var s = "{ret} {cname}({p})"
- v.add_decl("typedef {ret} (* {cname}_t)({p});")
- v.add_decl(s + ";")
- return s
- end
-
- redef fun do_compile_inside(v, params)
- do
- for i in [0..variables.length[ do
- var vacname = v.cfc.register_variable(variables[i])
- v.add_assignment(vacname, params[i])
- end
-
- var old_cv = v.nmc.continue_value
- var old_cl = v.nmc.continue_label
- var old_bl = v.nmc.break_label
-
- v.nmc.continue_value = v.cfc.get_var("Continue value and escape marker")
- v.nmc.continue_label = "continue_label{v.new_number}"
- v.nmc.break_label = v.nmc.return_label
-
- v.compile_stmt(n_expr)
-
- v.add_instr("{v.nmc.continue_label}: while(false);")
-
- var ret: nullable String = null
- if closure.signature.return_type != null then ret = v.nmc.continue_value
-
- v.nmc.continue_value = old_cv
- v.nmc.continue_label = old_cl
- v.nmc.break_label = old_bl
-
- return ret
- end
-end
-
-redef class PClosureDecl
- fun do_compile_inside(v: CompilerVisitor, params: Array[String]): nullable String is abstract
-end
-redef class AClosureDecl
- redef fun do_compile_inside(v, params)
- do
- n_signature.compile_parameters(v, variable.closure.signature, params)
-
- var old_cv = v.nmc.continue_value
- var old_cl = v.nmc.continue_label
- var old_bl = v.nmc.break_label
-
- v.nmc.continue_value = v.cfc.get_var("Continue value and escape marker")
- v.nmc.continue_label = "continue_label{v.new_number}"
- v.nmc.break_label = v.nmc.return_label
-
- v.compile_stmt(n_expr)
-
- v.add_instr("{v.nmc.continue_label}: while(false);")
-
- var ret: nullable String = null
- if variable.closure.signature.return_type != null then ret = v.nmc.continue_value
-
- v.nmc.continue_value = old_cv
- v.nmc.continue_label = old_cl
- v.nmc.break_label = old_bl
-
- return ret
- end
-end
-
-redef class AClosureCallExpr
- fun intern_compile_call(v: CompilerVisitor): nullable String
- do
- var cargs = new Array[String]
- compile_arguments_in(v, cargs)
- var va: nullable String = null
- if variable.closure.signature.return_type != null then va = v.cfc.get_var("Closure call result value")
-
- if variable.closure.is_optional then
- v.add_instr("if({v.cfc.varname(variable)}==NULL) \{")
- v.indent
- var n = variable.decl
- assert n isa AClosureDecl
- var s = n.do_compile_inside(v, cargs)
- if s != null then v.add_assignment(va.as(not null), s)
- v.unindent
- v.add_instr("} else \{")
- v.indent
- end
-
- var ivar = v.cfc.varname(variable)
- var cargs2 = [ivar]
- cargs2.append(cargs)
- var s = "(({variable.ctypename})({ivar}->fun))({cargs2.join(", ")}) /* Invoke closure {variable} */"
- if va != null then
- v.add_assignment(va, s)
- else
- v.add_instr("{s};")
- end
- v.add_instr("if ({ivar}->has_broke) \{")
- v.indent
- if n_closure_defs.length == 1 then do
- n_closure_defs.first.do_compile_inside(v, null)
- end
- if v.cfc.closure == v.nmc then v.add_instr("if ({ivar}->has_broke) \{ closctx->has_broke = {ivar}->has_broke; closctx->broke_value = {ivar}->broke_value;\}")
- v.add_instr("goto {v.nmc.return_label};")
- v.unindent
- v.add_instr("\}")
-
- if variable.closure.is_optional then
- v.unindent
- v.add_instr("\}")
- end
- return va
- end
-
- redef fun compile_expr(v)
- do
- var e = intern_compile_call(v)
- assert e != null
- return e
- end
-
- redef fun compile_stmt(v)
- do
- var e = intern_compile_call(v)
- if e != null then
- v.add_instr(e + ";")
- end
- end
-end
-
-redef class AProxyExpr
- redef fun compile_expr(v)
- do
- return v.compile_expr(n_expr)
- end
-end
-
-redef class AOnceExpr
- redef fun compile_expr(v)
- do
- var i = v.new_number
- var cvar = v.cfc.get_var("Once expression result")
- v.add_decl("static val_t once_value_{i}; static int once_bool_{i}; /* Once value for {cvar}*/")
- v.add_instr("if (once_bool_{i}) {cvar} = once_value_{i};")
- v.add_instr("else \{")
- v.indent
- v.cfc.free_var(cvar)
- var e = v.compile_expr(n_expr)
- v.add_assignment(cvar, e)
- v.add_instr("once_value_{i} = {cvar};")
- v.add_instr("once_bool_{i} = true;")
- v.unindent
- v.add_instr("}")
- return cvar
- end
-end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2009 Jean Privat <jean@pryen.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Nit intermediate code representation
+import icode_base
+import icode_tools
+import icode_builder
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2009 Jean Privat <jean@pryen.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Base classes for Nit intermediate code representation
+package icode_base
+
+import metamodel
+import mmloader
+
+## UTILITY CLASSES ##
+
+# Register store local variable and intermediate results
+class IRegister
+ # The static type
+ readable var _stype: MMType
+ init(s: MMType)
+ do
+ _stype = s
+ end
+end
+
+# A Closure declaration
+class IClosureDecl
+ # The associated closure definition
+ readable var _closure: MMClosure
+
+ # The default implementation
+ readable writable var _default: nullable IRoutine
+
+ init(c: MMClosure) do _closure = c
+end
+
+# A routine is a sequence of icodes with entry iregisters (params) and an exit iregister (result)
+class IRoutine
+ # The parameters of the routine
+ readable var _params: IndexedCollection[IRegister]
+
+ # The closure declared
+ readable writable var _closure_decls: nullable IndexedCollection[IClosureDecl] = null
+
+ # The result of the routine
+ readable var _result: nullable IRegister
+
+ # The sequence of icode
+ readable var _body: ISeq = new ISeq
+
+ # The location of the iroutine (if any)
+ readable writable var _location: nullable Location = null
+
+ init(p: IndexedCollection[IRegister], r: nullable IRegister)
+ do
+ _params = p.to_a
+ _result = r
+ end
+end
+
+# A closure definition in a iroutine body
+class IClosureDef
+special IRoutine
+ init(p: Array[IRegister], r: nullable IRegister)
+ do
+ super(p, r)
+ end
+end
+
+## INTERMEDIATE CODE ##
+
+# The root of the intermediate code representation
+abstract class ICode
+ # The number of registers used by the icode
+ fun arity: Int is abstract
+
+ # The result of the icode (if any)
+ readable writable var _result: nullable IRegister = null
+
+ # The location of the icode (if any)
+ readable writable var _location: nullable Location = null
+end
+
+# An icode that uses no registers (no args)
+abstract class ICode0
+special ICode
+ redef fun arity do return 0
+end
+
+# An icode that uses a single register (1 arg)
+abstract class ICode1
+special ICode
+ redef fun arity do return 1
+
+ # The single argument
+ readable var _expr: IRegister
+
+ init(e: IRegister) do _expr = e
+end
+
+# An icode that uses two single registers (2 args)
+abstract class ICode2
+special ICode
+ redef fun arity do return 2
+
+ # The first argument
+ readable var _expr1: IRegister
+
+ # The second argument
+ readable var _expr2: IRegister
+
+ init(e1, e2: IRegister)
+ do
+ _expr1 = e1
+ _expr2 = e2
+ end
+end
+
+# An icode that uses a variable number of registers (n args) and a variable number of closure definitions
+abstract class ICodeN
+special ICode
+ redef fun arity do return _exprs.length
+
+ # All arguments
+ readable var _exprs: IndexedCollection[IRegister]
+
+ # All closure definition
+ readable writable var _closure_defs: nullable IndexedCollection[nullable IClosureDef]
+
+ init(e: nullable IndexedCollection[IRegister])
+ do
+ if e == null then
+ _exprs = new Array[IRegister]
+ else
+ _exprs = e
+ end
+ end
+end
+
+#################################################
+
+# A linear sequence of ICode
+class ISeq
+special ICode0
+ # The sequence of icode
+ readable var _icodes: List[ICode] = new List[ICode]
+ init do end
+end
+
+# An infinite loop of ICode
+# Use IEscape to exit
+class ILoop
+special ISeq
+ init do end
+end
+
+# A Condidianal if-then-else statement
+# expr is the condition
+class IIf
+special ICode1
+ # The 'then' sequence of icode
+ readable var _then_seq: ISeq = new ISeq
+ # The 'else' sequence of icode
+ readable var _else_seq: ISeq = new ISeq
+ init(e: IRegister) do super
+end
+
+# Escape to to end of a parent sequence
+class IEscape
+special ICode0
+ # The seqeuence to escape
+ # The control flow continues at the next icode after the sequence
+ readable var _seq: ISeq
+ init(seq: ISeq) do _seq = seq
+end
+
+# An abort statement
+class IAbort
+special ICode0
+ # The reason the abort occured
+ # tests.first is the format
+ readable var _texts: Array[String]
+ # The local property that has the abort (if any)
+ readable var _property_location: nullable MMLocalProperty
+ # The module that has the abort
+ readable var _module_location: MMModule
+ init(t: Array[String], pl: nullable MMLocalProperty, ml: MMModule)
+ do
+ _texts = t
+ _property_location = pl
+ _module_location = ml
+ end
+end
+
+#################################################
+
+# The root of all method invocations
+abstract class IAbsCall
+special ICodeN
+ # The called method
+ readable var _property: MMMethod
+
+ init(p: MMMethod, e: IndexedCollection[IRegister])
+ do
+ super(e)
+ _property = p
+ end
+end
+
+# A simple procedure or function call
+# exprs.first is the reciever, other are arguments
+class ICall
+special IAbsCall
+ init(p, e) do super
+end
+
+# A super method call
+# exprs.first is the reciever, other are arguments
+class ISuper
+special IAbsCall
+ init(p, e) do super
+end
+
+# An instantiation
+# no reciever, all exprs are arguments
+class INew
+special ICall
+ # The type to instantiate
+ readable var _stype: MMType
+ init(t: MMType, p: MMMethod, a: IndexedCollection[IRegister])
+ do
+ super(p, a)
+ _stype = t
+ end
+end
+
+# A closure call
+# exprs are the arguments
+class IClosCall
+special ICodeN
+ # The called closure
+ readable var _closure_decl: IClosureDecl
+
+ # The !break sequence (if any)
+ readable writable var _break_seq: nullable ISeq = null
+
+ init(c: IClosureDecl, e: IndexedCollection[IRegister])
+ do
+ super(e)
+ _closure_decl = c
+ end
+end
+
+# A native C code
+# Mainly used to implements things that do not have a specific ICode yet
+# expr are the arguments
+class INative
+special ICodeN
+ # The native C code
+ # Special character sequence '@@@' will be substitued in order with the arguments
+ readable var _code: String
+
+ init(c: String, e: nullable IndexedCollection[IRegister])
+ do
+ super(e)
+ _code = c
+ end
+end
+
+# A register assigment
+# expr is the assigned value
+# result is the register assigned
+class IMove
+special ICode1
+ init(r: IRegister, e: IRegister)
+ do
+ super(e)
+ _result = r
+ end
+end
+
+# An attribute read access
+# expr is the reciever
+class IAttrRead
+special ICode1
+ # The accessed attribute
+ readable var _property: MMAttribute
+
+ init(p: MMAttribute, r: IRegister)
+ do
+ super(r)
+ _property = p
+ end
+end
+
+# An attribute assignment
+# expr1 is the receiver, expr2 is the assigned value
+class IAttrWrite
+special ICode2
+ # The accessed attribute
+ readable var _property: MMAttribute
+
+ init(p: MMAttribute, r: IRegister, v: IRegister)
+ do
+ super(r, v)
+ _property = p
+ end
+end
+
+
+# An attribute is_set check
+# expr is the reciever
+class IAttrIsset
+special ICode1
+ # The accessed attribute
+ readable var _property: MMAttribute
+
+ init(p: MMAttribute, r: IRegister)
+ do
+ super(r)
+ _property = p
+ end
+end
+
+# A type check
+# expr is the expression checked
+class ITypeCheck
+special ICode1
+ # The static type checkes to
+ readable var _stype: MMType
+
+ init(e: IRegister, t: MMType)
+ do
+ super(e)
+ _stype = t
+ end
+end
+
+# The 'is' operator
+# expr1 and expr2 are both operands
+class IIs
+special ICode2
+ init(e1, e2: IRegister)
+ do
+ super
+ end
+end
+
+# The unary 'not' operation
+# expr is the operand
+class INot
+special ICode1
+ init(e: IRegister)
+ do
+ super
+ end
+end
+
+# Evaluate body once them return the same value again and again
+# if result is not null, then it must also be assigned in the body
+class IOnce
+special ICode0
+ readable var _body: ISeq = new ISeq
+ init do end
+end
+
+# Is a closure given as a parameter?
+class IHasClos
+special ICode0
+ # The called closure
+ readable var _closure_decl: IClosureDecl
+
+ init(c: IClosureDecl)
+ do
+ _closure_decl = c
+ end
+end
+
+#################################################
+
+redef class MMAttribute
+ # The attached initialisation iroutine if any
+ # To call between the allocate-instance and the initialize-instance
+ fun iroutine: nullable IRoutine is abstract
+end
+
+redef class MMMethod
+ # The attached body iroutine if any
+ fun iroutine: nullable IRoutine is abstract
+end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2009 Jean Privat <jean@pryen.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Help generation of icode
+package icode_builder
+
+import icode_base
+
+# Helps to generate icodes in a iroutine
+class ICodeBuilder
+ # Add a new statment in the current icode sequence
+ # Can be used with expression if the result is ignored of if the result is already set
+ fun stmt(s: ICode)
+ do
+ s.location = _current_location
+ _seq.icodes.add(s)
+ end
+
+ # Add a new expression in the current icode sequence and return a new associated result register
+ fun expr(e: ICode, s: MMType): IRegister
+ do
+ stmt(e)
+ assert e.result == null
+ var reg = new_register(s)
+ e.result = reg
+ return reg
+ end
+
+ # Add an assignement (IMove) in the current icode sequence
+ fun add_assignment(reg: IRegister, v: IRegister)
+ do
+ stmt(new IMove(reg, v))
+ end
+
+ # Check that the reciever e is not null (IIs + IAbort)
+ fun add_null_reciever_check(e: IRegister)
+ do
+ var nul = new_register(module.type_none)
+ var c = expr(new IIs(e, nul), module.type_bool)
+ var iif = new IIf(c)
+ stmt(iif)
+ var old_seq = seq
+ seq = iif.then_seq
+ add_abort("Reciever is null")
+ seq = old_seq
+ end
+
+ # Add a type cast (ITypeCheck + IAbort) in the current icode sequence
+ fun add_type_cast(e: IRegister, stype: MMType)
+ do
+ var c = expr(new ITypeCheck(e, stype), module.type_bool)
+ var iif = new IIf(c)
+ stmt(iif)
+ var old_seq = seq
+ seq = iif.else_seq
+ add_abort("Cast failed")
+ seq = old_seq
+ end
+
+ # Add an attr check (IAttrIsset + IAbort) in the current icode sequence
+ fun add_attr_check(prop: MMAttribute, e: IRegister)
+ do
+ if not prop.signature.return_type.is_nullable then
+ var cond = expr(new IAttrIsset(prop, e), module.type_bool)
+ var iif = new IIf(cond)
+ stmt(iif)
+ var seq_old = seq
+ seq = iif.else_seq
+ add_abort("Uninitialized attribute %s", prop.name.to_s)
+ seq = seq_old
+ end
+ end
+
+ # Add an IAttrRead guarded by an add_attr_check in the current icode sequence
+ fun add_attr_read(prop: MMAttribute, e: IRegister): IRegister
+ do
+ add_attr_check(prop, e)
+ return expr(new IAttrRead(prop, e), prop.signature.return_type.as(not null))
+ end
+
+ # Add a localized IAbort
+ fun add_abort(s: String...)
+ do
+ stmt(new IAbort(s, method, module))
+ end
+
+ # Add an assigment to the iroutine return value
+ # Beware, no jump is generated
+ fun add_return_value(reg: IRegister)
+ do
+ add_assignment(iroutine.result.as(not null), reg)
+ end
+
+ # Add an ICall with possible simple inlining in the current icode sequence
+ fun add_call(prop: MMMethod, args: Array[IRegister], closcns: nullable Array[nullable IClosureDef]): nullable IRegister
+ do
+ var ee = once "==".to_symbol
+
+ # Inline "x!=y" as "not x==y"
+ var ne = once "!=".to_symbol
+ if prop.name == ne then
+ var eqp = prop.signature.recv.local_class.select_method(ee)
+ var eqcall = add_call(eqp, args, closcns).as(not null)
+ return expr(new INot(eqcall), module.type_bool)
+ end
+
+ # TODO: Inline x==y as "x is y or (x != null and (== is not the Object one) and x.==(y))"
+ # inline "x==y" as "x is y or x != null and x.==(y)"
+ var icall = new ICall(prop, args)
+ icall.closure_defs = closcns
+ if prop.name == ee then
+ # Prepare the result
+ var reg = new_register(module.type_bool)
+ # "x is y"
+ var cond = expr(new IIs(args[0], args[1]), module.type_bool)
+ var iif = new IIf(cond)
+ stmt(iif)
+ var seq_old = seq
+ seq = iif.then_seq
+ add_assignment(reg, cond)
+ # "or"
+ seq = iif.else_seq
+ # Do the "x != null" part iff x is nullable
+ if args[0].stype.is_nullable then
+ var nul = new_register(module.type_none)
+ cond = expr(new IIs(args[0], nul), module.type_bool)
+ iif = new IIf(cond)
+ stmt(iif)
+ seq = iif.then_seq
+ add_assignment(reg, expr(new INative("TAG_Bool(false)", null), module.type_bool))
+ seq = iif.else_seq
+ end
+ # "x.==(y)"
+ add_assignment(reg, expr(icall, module.type_bool))
+ seq = seq_old
+ return reg
+ end
+
+ var rtype = prop.signature.return_type
+ if rtype != null then
+ return expr(icall, rtype)
+ else
+ stmt(icall)
+ return null
+ end
+ end
+
+ # Get a new register
+ fun new_register(s: MMType): IRegister
+ do
+ return new IRegister(s)
+ end
+
+ # The module where classes and types are extracted
+ readable var _module: MMModule
+
+ # The current iroutine build
+ readable var _iroutine: IRoutine
+
+ # The current sequence of icodes
+ readable writable var _seq: ISeq
+
+ # The method associated to the iroutine (if any)
+ readable var _method: nullable MMMethod
+
+ init(module: MMModule, r: IRoutine, m: nullable MMMethod)
+ do
+ _module = module
+ _current_location = r.location
+ _iroutine = r
+ _seq = r.body
+ _method = m
+ end
+
+ # New ICodes are set to this location
+ readable writable var _current_location: nullable Location = null
+end
+
+redef class MMSignature
+ # Create an empty IRoutine that match the signature
+ fun generate_empty_iroutine: IRoutine
+ do
+ var args = new Array[IRegister]
+ args.add(new IRegister(recv)) # Self
+ for i in [0..arity[ do
+ args.add(new IRegister(self[i]))
+ end
+ var res: nullable IRegister = null
+ var rtype = return_type
+ if rtype != null then
+ res = new IRegister(rtype)
+ end
+ var iroutine = new IRoutine(args, res)
+ var clos: nullable Array[IClosureDecl] = null
+ if not closures.is_empty then
+ clos = new Array[IClosureDecl]
+ for c in closures do
+ clos.add(new IClosureDecl(c))
+ end
+ iroutine.closure_decls = clos
+ end
+ return iroutine
+ end
+
+ # Create an empty IClosureDef that match the signature
+ fun generate_empty_iclosuredef: IClosureDef
+ do
+ var args = new Array[IRegister]
+ for i in [0..arity[ do
+ args.add(new IRegister(self[i]))
+ end
+ var res: nullable IRegister = null
+ var rtype = return_type
+ if rtype != null then
+ res = new IRegister(rtype)
+ end
+ var iroutine = new IClosureDef(args, res)
+ var clos: nullable Array[IClosureDecl] = null
+ if not closures.is_empty then
+ clos = new Array[IClosureDecl]
+ for c in closures do
+ clos.add(new IClosureDecl(c))
+ end
+ iroutine.closure_decls = clos
+ end
+ return iroutine
+ end
+end
+
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2009 Jean Privat <jean@pryen.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Tools to manipulate intermediace nit code representation
+import icode_base
+
+# A simple visitor to visit icode structures
+class ICodeVisitor
+ # Called when a iregister is read in a icode
+ fun visit_iregister_read(ic: ICode, r: IRegister) do end
+
+ # Called when a iregister is wrote in a icode
+ fun visit_iregister_write(ic: ICode, r: IRegister) do end
+
+ # The current icode iterator.
+ # Can be used to insert_before, used to change the item or deleted
+ readable var _current_icode: nullable ListIterator[ICode] = null
+
+ # Called when a icode is visited
+ # Automatically visits iregisters and sub-icodes
+ fun visit_icode(ic: nullable ICode)
+ do
+ if ic == null then return
+ if ic isa ISeq then
+ var old_icode = _current_icode
+ var cur = ic.icodes.iterator
+ while cur.is_ok do
+ _current_icode = cur
+ var ic2 = cur.item
+ visit_icode(ic2)
+ cur.next
+ end
+ _current_icode = old_icode
+ else if ic isa IIf then
+ visit_iregister_read(ic, ic.expr)
+ visit_icode(ic.then_seq)
+ visit_icode(ic.else_seq)
+ else if ic isa IOnce then
+ visit_icode(ic.body)
+ else if ic isa ICode1 then
+ visit_iregister_read(ic, ic.expr)
+ else if ic isa ICode2 then
+ visit_iregister_read(ic, ic.expr1)
+ visit_iregister_read(ic, ic.expr2)
+ else if ic isa ICodeN then
+ for e in ic.exprs do
+ visit_iregister_read(ic, e)
+ end
+ var closdefs = ic.closure_defs
+ if ic isa IClosCall then
+ visit_icode(ic.break_seq)
+ end
+ if closdefs != null then
+ visit_closure_defs(closdefs)
+ end
+ end
+ var r = ic.result
+ if r != null then visit_iregister_write(ic, r)
+ end
+
+ # Called when closure definitions are visited
+ # Automatically visits each closure definition
+ fun visit_closure_defs(closdefs: Collection[nullable IClosureDef])
+ do
+ for e in closdefs do
+ if e != null then
+ visit_iroutine(e)
+ end
+ end
+ end
+
+ # Called when an iroutine is visited
+ # Automatically visits the body
+ # Warning: parameters of result registers are not visited
+ fun visit_iroutine(ir: IRoutine)
+ do
+ visit_icode(ir.body)
+ end
+end
+
+redef class IRoutine
+ # Inline an iroutine in an icode sequence
+ fun inline_in_seq(seq: ISeq, args: IndexedCollection[IRegister]): nullable IRegister
+ do
+ var d = new ICodeDupContext
+ if args.length != params.length then print "{args.length} != {params.length}"
+ assert args.length == params.length
+ for i in [0..args.length[ do
+ # FIXME The following assumes that params are readonly.
+ # The alternative is safe but add one move :/
+ d._registers[params[i]] = args[i]
+ #seq.icodes.add(new IMove(d.dup_ireg(params[i]), args[i]))
+ end
+ seq.icodes.add(body.dup_with(d))
+ var r = result
+ if r != null then r = d.dup_ireg(r)
+ return r
+ end
+end
+
+# This class stores reference to allow correct duplication of icodes
+private class ICodeDupContext
+ # Duplicate one register
+ # Subsequent invocation will return the same register
+ fun dup_ireg(r: IRegister): IRegister
+ do
+ var rs = _registers
+ if rs.has_key(r) then
+ return rs[r]
+ else
+ var r2 = new IRegister(r.stype)
+ rs[r] = r2
+ return r2
+ end
+ end
+
+ # Duplicate a bunch of registers
+ # Subsequent invocation will return the same registers
+ fun dup_iregs(regs: IndexedCollection[IRegister]): IndexedCollection[IRegister]
+ do
+ var a = new Array[IRegister].with_capacity(regs.length)
+ for r in regs do
+ a.add(dup_ireg(r))
+ end
+ return a
+ end
+
+ # The associoation between old_seq and new_seq
+ # Directly used by the IEscape
+ var _seqs: Map[ISeq, ISeq] = new HashMap[ISeq, ISeq]
+
+ # The assocation between old_ireg and new_ireg
+ # Used by dup_ireg
+ var _registers: Map[IRegister, IRegister] = new HashMap[IRegister, IRegister]
+end
+
+redef class ICode
+ # Duplicate the current icode (generic part)
+ private fun dup_with(d: ICodeDupContext): ICode
+ do
+ var c = inner_dup_with(d)
+ var r = result
+ if r != null then c.result = d.dup_ireg(r)
+ c.location = location
+ return c
+ end
+
+ # Duplicate the current icode (specific part)
+ private fun inner_dup_with(d: ICodeDupContext): ICode is abstract
+end
+
+redef class ISeq
+ redef fun inner_dup_with(d)
+ do
+ var c2 = new ISeq
+ dup_seq_to(d, c2)
+ return c2
+ end
+
+ # Duplicate each icode and store them in dest
+ # Note: dest must be empty and not modified afted duplication or IEscapes may be wrongly duplicated
+ private fun dup_seq_to(d: ICodeDupContext, dest: ISeq)
+ do
+ d._seqs[self] = dest
+ for c in icodes do
+ dest.icodes.add(c.dup_with(d))
+ end
+ end
+end
+
+redef class ILoop
+ redef fun inner_dup_with(d)
+ do
+ var c2 = new ILoop
+ dup_seq_to(d, c2)
+ return c2
+ end
+end
+
+redef class IIf
+ redef fun inner_dup_with(d)
+ do
+ var c2 = new IIf(d.dup_ireg(expr))
+ then_seq.dup_seq_to(d, c2.then_seq)
+ else_seq.dup_seq_to(d, c2.else_seq)
+ return c2
+ end
+end
+
+redef class IEscape
+ redef fun inner_dup_with(d)
+ do
+ if d._seqs.has_key(seq) then
+ # Jump to a duplicated sequence
+ return new IEscape(d._seqs[seq])
+ else
+ # Jump to an englobing unduplicated sequence
+ return new IEscape(seq)
+ end
+ end
+end
+
+redef class IAbort
+ redef fun inner_dup_with(d)
+ do
+ return new IAbort(texts, property_location, module_location)
+ end
+end
+
+redef class ICall
+ redef fun inner_dup_with(d)
+ do
+ return new ICall(property, d.dup_iregs(exprs))
+ end
+end
+
+redef class ISuper
+ redef fun inner_dup_with(d)
+ do
+ return new ISuper(property, d.dup_iregs(exprs))
+ end
+end
+
+redef class INew
+ redef fun inner_dup_with(d)
+ do
+ return new INew(stype, property, d.dup_iregs(exprs))
+ end
+end
+
+redef class IClosCall
+ redef fun inner_dup_with(d)
+ do
+ var c2 = new IClosCall(closure_decl, d.dup_iregs(exprs))
+ var bs = break_seq
+ if bs != null then
+ var bs2 = new ISeq
+ c2.break_seq = bs2
+ bs.dup_seq_to(d, bs2)
+ end
+ return c2
+ end
+end
+
+redef class INative
+ redef fun inner_dup_with(d)
+ do
+ return new INative(code, d.dup_iregs(exprs))
+ end
+end
+
+redef class IMove
+ redef fun inner_dup_with(d)
+ do
+ return new IMove(d.dup_ireg(result.as(not null)), d.dup_ireg(expr))
+ end
+end
+
+redef class IAttrRead
+ redef fun inner_dup_with(d)
+ do
+ return new IAttrRead(property, d.dup_ireg(expr))
+ end
+end
+
+redef class IAttrWrite
+ redef fun inner_dup_with(d)
+ do
+ return new IAttrWrite(property, d.dup_ireg(expr1), d.dup_ireg(expr2))
+ end
+end
+
+redef class IAttrIsset
+ redef fun inner_dup_with(d)
+ do
+ return new IAttrIsset(property, d.dup_ireg(expr))
+ end
+end
+
+redef class ITypeCheck
+ redef fun inner_dup_with(d)
+ do
+ return new ITypeCheck(d.dup_ireg(expr), stype)
+ end
+end
+
+redef class IIs
+ redef fun inner_dup_with(d)
+ do
+ return new IIs(d.dup_ireg(expr1), d.dup_ireg(expr2))
+ end
+end
+
+redef class INot
+ redef fun inner_dup_with(d)
+ do
+ return new INot(d.dup_ireg(expr))
+ end
+end
+
+redef class IOnce
+ redef fun inner_dup_with(d)
+ do
+ var c2 = new IOnce
+ body.dup_seq_to(d, c2.body)
+ return c2
+ end
+end
+
+redef class IHasClos
+ redef fun inner_dup_with(d)
+ do
+ return new IHasClos(closure_decl)
+ end
+end
redef class MMModule
# The type of null
readable var _type_none: MMTypeNone = new MMTypeNone(self)
+
+ # The type of bool
+ fun type_bool: MMType
+ do
+ return class_by_name(once ("Bool".to_symbol)).get_type
+ end
end
package nitc
import abstracttool
+import analysis
private import compiling
+private import syntax
# The main class of the nitcompiler program
class NitCompiler
readable var _opt_bindir: OptionString = new OptionString("NIT tools directory", "--bindir")
readable var _opt_compdir: OptionString = new OptionString("Intermediate compilation directory", "--compdir")
readable var _opt_extension_prefix: OptionString = new OptionString("Append prefix to file extension", "-p", "--extension-prefix")
+ readable var _opt_dump: OptionBool = new OptionBool("Dump intermediate code", "--dump")
init
do
super("nitc")
- option_context.add_option(opt_output, opt_boost, opt_no_cc, opt_global, opt_clibdir, opt_bindir, opt_compdir, opt_extension_prefix)
+ option_context.add_option(opt_output, opt_boost, opt_no_cc, opt_global, opt_clibdir, opt_bindir, opt_compdir, opt_extension_prefix, opt_dump)
end
redef fun process_options
end
end
+ fun dump_intermediate_code(mods: Collection[MMModule])
+ do
+ for mod in mods do
+ for c in mod.local_classes do
+ if not c isa MMConcreteClass then continue
+ for p in c.local_local_properties do
+ var routine: nullable IRoutine = null
+ if p isa MMAttribute then
+ routine = p.iroutine
+ else if p isa MMMethod then
+ routine = p.iroutine
+ end
+ if routine == null then continue
+ print "**** Property {p.full_name} ****"
+ var icd = new ICodeDumper
+ routine.dump(icd)
+ print "**** OPTIMIZE {p.full_name} ****"
+ routine.optimize
+ icd = new ICodeDumper
+ routine.dump(icd)
+ end
+ end
+ end
+ end
+
redef fun perform_work(mods)
do
+ if opt_dump.value then
+ dump_intermediate_code(mods)
+ end
for mod in mods do
mod.compile_prog_to_c(self)
end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2009 Jean Privat <jean@pryen.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Things needed by typing.nit to generate intermediate code from AST
+package icode_generation
+
+import icode
+import syntax_base
+private import typing
+private import primitive_info
+
+# An AST2ICode context stores the currently built icode informations
+class A2IContext
+special ICodeBuilder
+ redef fun stmt(s: ICode)
+ do
+ if _current_node != null then
+ current_location = _current_node.location
+ else if visitor.current_node != null then
+ current_location = visitor.current_node.location
+ end
+ super
+ end
+
+ # Prepare a new array of length item
+ fun add_new_array(stype: MMType, length: Int): IRegister
+ do
+ var prop = visitor.get_method(stype, once "with_capacity".to_symbol)
+ var ni = expr(new INative("TAG_Int({length})", null), visitor.type_int)
+ return expr(new INew(stype, prop, [ni]), stype)
+ end
+
+ # Add an array add
+ fun add_call_array_add(recv, item: IRegister)
+ do
+ var stype = recv.stype
+ var prop = visitor.get_method(stype, once "add".to_symbol)
+ stmt(new ICall(prop, [recv, item]))
+ end
+
+ # Get the iregister associated with a variable
+ # Or assign one if none exists
+ fun variable(v: Variable): IRegister
+ do
+ if _variables.has_key(v) then
+ return _variables[v]
+ else
+ var reg = new_register(v.stype.as(not null))
+ _variables[v] = reg
+ return reg
+ end
+ end
+
+ # Current registered variable
+ var _variables: HashMap[Variable, IRegister] = new HashMap[Variable, IRegister]
+
+ # Current registered closurevariables
+ readable var _closurevariables: HashMap[ClosureVariable, IClosureDecl] = new HashMap[ClosureVariable, IClosureDecl]
+
+ # The current syntax visitor
+ readable var _visitor: AbsSyntaxVisitor
+
+ # Where a nit return must branch
+ readable writable var _return_seq: nullable ISeq
+
+ # Register where a functionnal nit return must store its value
+ readable writable var _return_value: nullable IRegister
+
+ init(visitor: AbsSyntaxVisitor, r: IRoutine, m: nullable MMMethod)
+ do
+ super(visitor.module, r, m)
+ _visitor = visitor
+ _return_seq = r.body
+ _return_value = r.result
+ end
+
+ # Insert implicit super init calls
+ fun invoke_super_init_calls_after(start_prop: nullable MMMethod)
+ do
+ var p = method
+ assert p isa MMSrcMethod
+ var n = p.node
+ assert n isa AConcreteInitPropdef
+
+ if n.super_init_calls.is_empty then return
+ var i = 0
+ var j = 0
+ if start_prop != null then
+ while n.super_init_calls[i] != start_prop do
+ i += 1
+ end
+ i += 1
+
+ while n.explicit_super_init_calls[j] != start_prop do
+ j += 1
+ end
+ j += 1
+ end
+ var stop_prop: nullable MMMethod = null
+ if j < n.explicit_super_init_calls.length then
+ stop_prop = n.explicit_super_init_calls[j]
+ end
+ var l = n.super_init_calls.length
+ while i < l do
+ var p = n.super_init_calls[i]
+ if p == stop_prop then break
+ var cargs = new Array[IRegister]
+ if p.signature.arity == 0 then
+ cargs.add(iroutine.params.first)
+ else
+ for va in iroutine.params do
+ cargs.add(va)
+ end
+ end
+ stmt(new ICall(p, cargs))
+ i += 1
+ end
+ end
+
+ # The current PExpr
+ var _current_node: nullable PExpr = null
+
+ # Generate icode in the current sequence from a statement
+ fun generate_stmt(n: nullable PExpr)
+ do
+ if n == null then return
+ var old = _current_node
+ _current_node = n
+ n.generate_icode(self)
+ _current_node = old
+ end
+
+ # Generate icode in the current sequence from an expression
+ fun generate_expr(n: PExpr): IRegister
+ do
+ var old = _current_node
+ _current_node = n
+ var reg = n.generate_icode(self).as(not null)
+ _current_node = old
+ return reg
+ end
+end
+
+redef class EscapableBlock
+ # Where a nit break must branch
+ readable writable var _break_seq: nullable ISeq
+
+ # Where a nit continue must branch
+ readable writable var _continue_seq: nullable ISeq
+
+ # Register where a functionnal nit break must store its value
+ readable writable var _break_value: nullable IRegister
+
+ # Register where a functionnal nit continue must store its value
+ readable writable var _continue_value: nullable IRegister
+end
+
+redef class MMSrcModule
+ # Generate icode for method bodies
+ fun generate_icode(tc: ToolContext)
+ do
+ var v = new A2IVisitor(tc, self)
+ for c in src_local_classes do
+ for p in c.src_local_properties do
+ if p isa MMSrcMethod then
+ p.generate_iroutine(v)
+ else if p isa MMSrcAttribute then
+ p.generate_iroutine(v)
+ end
+ end
+ end
+ end
+end
+
+redef class MMSrcAttribute
+ redef readable writable var _iroutine: nullable IRoutine
+
+ # Generate the initialization iroutine
+ fun generate_iroutine(visitor: A2IVisitor)
+ do
+ if node.n_expr != null then
+ var iroutine = signature.generate_empty_iroutine
+ iroutine.location = node.location
+ var v = new A2IContext(visitor, iroutine, null)
+ visitor.icode_ctx = v
+ visitor.enter_visit(node)
+ visitor.icode_ctx = null
+ _iroutine = iroutine
+ end
+ end
+end
+
+redef class MMSrcMethod
+ redef readable writable var _iroutine: nullable IRoutine
+
+ # Generate the body iroutine
+ fun generate_iroutine(visitor: A2IVisitor)
+ do
+ var iroutine = signature.generate_empty_iroutine
+ if node != null then
+ iroutine.location = node.location
+ end
+ var v = new A2IContext(visitor, iroutine, self)
+ visitor.icode_ctx = v
+ inner_generate_iroutine(v)
+ visitor.icode_ctx = null
+ _iroutine = iroutine
+ end
+
+ # Generate the body iroutine (specific part)
+ fun inner_generate_iroutine(v: A2IContext) is abstract
+end
+
+redef class MMReadImplementationMethod
+ redef fun inner_generate_iroutine(v)
+ do
+ var e = v.add_attr_read(node.prop, v.iroutine.params.first)
+ v.add_return_value(e)
+ end
+end
+
+redef class MMWriteImplementationMethod
+ redef fun inner_generate_iroutine(v)
+ do
+ var params = v.iroutine.params
+ v.stmt(new IAttrWrite(node.prop, params[0], params[1]))
+ end
+end
+
+redef class MMMethSrcMethod
+ redef fun inner_generate_iroutine(v)
+ do
+ v.visitor.enter_visit(node)
+ end
+end
+
+redef class MMImplicitInit
+ redef fun inner_generate_iroutine(v)
+ do
+ var params = v.iroutine.params
+ var f = params.length - unassigned_attributes.length
+ var recv = params.first
+ for sp in super_inits do
+ assert sp isa MMMethod
+ var args_recv = [recv]
+ if sp == super_init then
+ var args = new Array[IRegister].with_capacity(f)
+ args.add(recv)
+ for i in [1..f[ do
+ args.add(params[i])
+ end
+ v.stmt(new ICall(sp, args))
+ else
+ v.stmt(new ICall(sp, args_recv))
+ end
+ end
+ for i in [f..params.length[ do
+ var attribute = unassigned_attributes[i-f]
+ v.stmt(new IAttrWrite(attribute, recv, params[i]))
+ end
+ end
+end
+
+class A2IVisitor
+special AbsSyntaxVisitor
+ writable var _icode_ctx: nullable A2IContext
+ fun icode_ctx: A2IContext do return _icode_ctx.as(not null)
+ redef fun visit(n) do n.accept_icode_generation(self)
+ init(tc, m) do super
+end
+
+
+###############################################################################
+
+redef class PNode
+ fun accept_icode_generation(v: A2IVisitor) do accept_abs_syntax_visitor(v) end
+end
+
+redef class AAttrPropdef
+ redef fun accept_icode_generation(vv)
+ do
+ var v = vv.icode_ctx
+ v.stmt(new IMove(v.variable(self_var), v.iroutine.params.first))
+ super
+ var ne = n_expr
+ if ne != null then
+ v.stmt(new IMove(v.iroutine.result.as(not null), v.generate_expr(ne)))
+ end
+ end
+end
+
+redef class AMethPropdef
+ redef fun accept_icode_generation(vv)
+ do
+ super
+ fill_iroutine(vv.icode_ctx, method)
+ end
+
+ # Compile the method body common preambule (before specific body stuff if any)
+ fun fill_iroutine(v: A2IContext, method: MMSrcMethod) is abstract
+end
+
+redef class PSignature
+ fun fill_iroutine_parameters(v: A2IContext, orig_sig: MMSignature, params: IndexedCollection[IRegister], closdecls: nullable IndexedCollection[IClosureDecl]) is abstract
+end
+
+redef class ASignature
+ redef fun fill_iroutine_parameters(v: A2IContext, orig_sig: MMSignature, params: IndexedCollection[IRegister], closdecls: nullable IndexedCollection[IClosureDecl])
+ do
+ for ap in n_params do
+ var reg = v.variable(ap.variable)
+ var orig_type = orig_sig[ap.position]
+ var apst = ap.variable.stype.as(not null)
+ if not orig_type < apst then
+ v.add_type_cast(params[ap.position], apst)
+ end
+ v.stmt(new IMove(reg, params[ap.position]))
+ end
+ for i in [0..n_closure_decls.length[ do
+ var wd = n_closure_decls[i]
+ v.closurevariables[wd.variable] = closdecls[i]
+ end
+ end
+end
+
+redef class AClosureDecl
+ redef fun accept_icode_generation(vv)
+ do
+ var v = vv.icode_ctx
+ var iclos = variable.closure.signature.generate_empty_iclosuredef
+ var old_seq = v.seq
+ v.seq = iclos.body
+ escapable.continue_seq = iclos.body
+ escapable.continue_value = iclos.result
+ n_signature.fill_iroutine_parameters(v, variable.closure.signature, iclos.params, null)
+
+ if n_expr != null then
+ v.generate_stmt(n_expr)
+ v.iroutine.closure_decls[position].default = iclos
+ end
+ v.seq = old_seq
+ end
+end
+
+redef class AConcreteMethPropdef
+ redef fun fill_iroutine(v, method)
+ do
+ var params = v.iroutine.params.to_a
+ var selfreg = v.variable(self_var)
+ v.stmt(new IMove(selfreg, params[0]))
+ params.shift
+
+ var orig_meth: MMLocalProperty = method.global.intro
+ var orig_sig = orig_meth.signature_for(method.signature.recv)
+ if n_signature != null then
+ n_signature.fill_iroutine_parameters(v, orig_sig, params, v.iroutine.closure_decls)
+ end
+
+ if self isa AConcreteInitPropdef then
+ v.invoke_super_init_calls_after(null)
+ end
+
+ if n_block != null then
+ v.generate_stmt(n_block)
+ end
+ end
+end
+
+redef class ADeferredMethPropdef
+ redef fun fill_iroutine(v, method)
+ do
+ v.add_abort("Deferred method called")
+ end
+end
+
+redef class AExternMethPropdef
+ redef fun fill_iroutine(v, method)
+ do
+ var params = v.iroutine.params
+ var ename = "{method.module.name}_{method.local_class.name}_{method.local_class.name}_{method.name}_{method.signature.arity}"
+ if n_extern != null then
+ ename = n_extern.text
+ ename = ename.substring(1, ename.length-2)
+ end
+ var sig = method.signature
+ assert params.length == sig.arity + 1
+ var args = new Array[String]
+ args.add(sig.recv.unboxtype("@@@"))
+ for i in [0..sig.arity[ do
+ args.add(sig[i].unboxtype("@@@"))
+ end
+ var s = "{ename}({args.join(", ")})"
+ var rtype = sig.return_type
+ if rtype != null then
+ s = rtype.boxtype(s)
+ v.add_return_value(v.expr(new INative(s, params), rtype))
+ else
+ v.stmt(new INative(s, params))
+ end
+ end
+end
+
+redef class AInternMethPropdef
+ redef fun fill_iroutine(v, method)
+ do
+ var p = v.iroutine.params.to_a
+ var c = method.local_class.name
+ var n = method.name
+ var s: nullable String = null
+ if c == once "Int".to_symbol then
+ if n == once "object_id".to_symbol then
+ s = "@@@"
+ else if n == once "unary -".to_symbol then
+ s = "TAG_Int(-UNTAG_Int(@@@))"
+ else if n == once "output".to_symbol then
+ s = "printf(\"%ld\\n\", UNTAG_Int(@@@));"
+ else if n == once "ascii".to_symbol then
+ s = "TAG_Char(UNTAG_Int(@@@))"
+ else if n == once "succ".to_symbol then
+ s = "TAG_Int(UNTAG_Int(@@@)+1)"
+ else if n == once "prec".to_symbol then
+ s = "TAG_Int(UNTAG_Int(@@@)-1)"
+ else if n == once "to_f".to_symbol then
+ s = "BOX_Float((float)UNTAG_Int(@@@))"
+ else if n == once "+".to_symbol then
+ s = "TAG_Int(UNTAG_Int(@@@)+UNTAG_Int(@@@))"
+ else if n == once "-".to_symbol then
+ s = "TAG_Int(UNTAG_Int(@@@)-UNTAG_Int(@@@))"
+ else if n == once "*".to_symbol then
+ s = "TAG_Int(UNTAG_Int(@@@)*UNTAG_Int(@@@))"
+ else if n == once "/".to_symbol then
+ s = "TAG_Int(UNTAG_Int(@@@)/UNTAG_Int(@@@))"
+ else if n == once "%".to_symbol then
+ s = "TAG_Int(UNTAG_Int(@@@)%UNTAG_Int(@@@))"
+ else if n == once "<".to_symbol then
+ s = "TAG_Bool(UNTAG_Int(@@@)<UNTAG_Int(@@@))"
+ else if n == once ">".to_symbol then
+ s = "TAG_Bool(UNTAG_Int(@@@)>UNTAG_Int(@@@))"
+ else if n == once "<=".to_symbol then
+ s = "TAG_Bool(UNTAG_Int(@@@)<=UNTAG_Int(@@@))"
+ else if n == once ">=".to_symbol then
+ s = "TAG_Bool(UNTAG_Int(@@@)>=UNTAG_Int(@@@))"
+ else if n == once "lshift".to_symbol then
+ s = "TAG_Int(UNTAG_Int(@@@)<<UNTAG_Int(@@@))"
+ else if n == once "rshift".to_symbol then
+ s = "TAG_Int(UNTAG_Int(@@@)>>UNTAG_Int(@@@))"
+ else if n == once "==".to_symbol then
+ s = "TAG_Bool((@@@)==(@@@))"
+ else if n == once "!=".to_symbol then
+ s = "TAG_Bool((@@@)!=(@@@))"
+ end
+ else if c == once "Float".to_symbol then
+ if n == once "object_id".to_symbol then
+ s = "TAG_Int((bigint)UNBOX_Float(@@@))"
+ else if n == once "unary -".to_symbol then
+ s = "BOX_Float(-UNBOX_Float(@@@))"
+ else if n == once "output".to_symbol then
+ s = "printf(\"%f\\n\", UNBOX_Float(@@@));"
+ else if n == once "to_i".to_symbol then
+ s = "TAG_Int((bigint)UNBOX_Float(@@@))"
+ else if n == once "+".to_symbol then
+ s = "BOX_Float(UNBOX_Float(@@@)+UNBOX_Float(@@@))"
+ else if n == once "-".to_symbol then
+ s = "BOX_Float(UNBOX_Float(@@@)-UNBOX_Float(@@@))"
+ else if n == once "*".to_symbol then
+ s = "BOX_Float(UNBOX_Float(@@@)*UNBOX_Float(@@@))"
+ else if n == once "/".to_symbol then
+ s = "BOX_Float(UNBOX_Float(@@@)/UNBOX_Float(@@@))"
+ else if n == once "<".to_symbol then
+ s = "TAG_Bool(UNBOX_Float(@@@)<UNBOX_Float(@@@))"
+ else if n == once ">".to_symbol then
+ s = "TAG_Bool(UNBOX_Float(@@@)>UNBOX_Float(@@@))"
+ else if n == once "<=".to_symbol then
+ s = "TAG_Bool(UNBOX_Float(@@@)<=UNBOX_Float(@@@))"
+ else if n == once ">=".to_symbol then
+ s = "TAG_Bool(UNBOX_Float(@@@)>=UNBOX_Float(@@@))"
+ end
+ else if c == once "Char".to_symbol then
+ if n == once "object_id".to_symbol then
+ s = "TAG_Int(UNTAG_Char(@@@))"
+ else if n == once "unary -".to_symbol then
+ s = "TAG_Char(-UNTAG_Char(@@@))"
+ else if n == once "output".to_symbol then
+ s = "printf(\"%c\", (unsigned char)UNTAG_Char(@@@));"
+ else if n == once "ascii".to_symbol then
+ s = "TAG_Int((unsigned char)UNTAG_Char(@@@))"
+ else if n == once "succ".to_symbol then
+ s = "TAG_Char(UNTAG_Char(@@@)+1)"
+ else if n == once "prec".to_symbol then
+ s = "TAG_Char(UNTAG_Char(@@@)-1)"
+ else if n == once "to_i".to_symbol then
+ s = "TAG_Int(UNTAG_Char(@@@)-'0')"
+ else if n == once "+".to_symbol then
+ s = "TAG_Char(UNTAG_Char(@@@)+UNTAG_Char(@@@))"
+ else if n == once "-".to_symbol then
+ s = "TAG_Char(UNTAG_Char(@@@)-UNTAG_Char(@@@))"
+ else if n == once "*".to_symbol then
+ s = "TAG_Char(UNTAG_Char(@@@)*UNTAG_Char(@@@))"
+ else if n == once "/".to_symbol then
+ s = "TAG_Char(UNTAG_Char(@@@)/UNTAG_Char(@@@))"
+ else if n == once "%".to_symbol then
+ s = "TAG_Char(UNTAG_Char(@@@)%UNTAG_Char(@@@))"
+ else if n == once "<".to_symbol then
+ s = "TAG_Bool(UNTAG_Char(@@@)<UNTAG_Char(@@@))"
+ else if n == once ">".to_symbol then
+ s = "TAG_Bool(UNTAG_Char(@@@)>UNTAG_Char(@@@))"
+ else if n == once "<=".to_symbol then
+ s = "TAG_Bool(UNTAG_Char(@@@)<=UNTAG_Char(@@@))"
+ else if n == once ">=".to_symbol then
+ s = "TAG_Bool(UNTAG_Char(@@@)>=UNTAG_Char(@@@))"
+ else if n == once "==".to_symbol then
+ s = "TAG_Bool((@@@)==(@@@))"
+ else if n == once "!=".to_symbol then
+ s = "TAG_Bool((@@@)!=(@@@))"
+ end
+ else if c == once "Bool".to_symbol then
+ if n == once "object_id".to_symbol then
+ s = "TAG_Int(UNTAG_Bool(@@@))"
+ else if n == once "unary -".to_symbol then
+ s = "TAG_Bool(-UNTAG_Bool(@@@))"
+ else if n == once "output".to_symbol then
+ s = "(void)printf(UNTAG_Bool(@@@)?\"true\\n\":\"false\\n\");"
+ else if n == once "ascii".to_symbol then
+ s = "TAG_Bool(UNTAG_Bool(@@@))"
+ else if n == once "to_i".to_symbol then
+ s = "TAG_Int(UNTAG_Bool(@@@))"
+ else if n == once "==".to_symbol then
+ s = "TAG_Bool((@@@)==(@@@))"
+ else if n == once "!=".to_symbol then
+ s = "TAG_Bool((@@@)!=(@@@))"
+ end
+ else if c == once "NativeArray".to_symbol then
+ if n == once "object_id".to_symbol then
+ s = "TAG_Int(UNBOX_NativeArray(@@@))"
+ else if n == once "[]".to_symbol then
+ s = "UNBOX_NativeArray(@@@)[UNTAG_Int(@@@)]"
+ else if n == once "[]=".to_symbol then
+ s = "UNBOX_NativeArray(@@@)[UNTAG_Int(@@@)]=@@@;"
+ else if n == once "copy_to".to_symbol then
+ var t = p[0]
+ p[0] = p[1]
+ p[1] = t
+ s = "(void)memcpy(UNBOX_NativeArray(@@@), UNBOX_NativeArray(@@@), UNTAG_Int(@@@)*sizeof(val_t));"
+ end
+ else if c == once "NativeString".to_symbol then
+ if n == once "object_id".to_symbol then
+ s = "TAG_Int(UNBOX_NativeString(@@@))"
+ else if n == once "atoi".to_symbol then
+ s = "TAG_Int(atoi(UNBOX_NativeString(@@@)))"
+ else if n == once "[]".to_symbol then
+ s = "TAG_Char(UNBOX_NativeString(@@@)[UNTAG_Int(@@@)])"
+ else if n == once "[]=".to_symbol then
+ s = "UNBOX_NativeString(@@@)[UNTAG_Int(@@@)]=UNTAG_Char(@@@);"
+ else if n == once "copy_to".to_symbol then
+ var t = p[0]
+ p[0] = p[1]
+ p[1] = p[4]
+ p[4] = p[2]
+ p[2] = t
+ s = "(void)memcpy(UNBOX_NativeString(@@@)+UNTAG_Int(@@@), UNBOX_NativeString(@@@)+UNTAG_Int(@@@), UNTAG_Int(@@@));"
+ end
+ else if n == once "object_id".to_symbol then
+ s = "TAG_Int((bigint)@@@)"
+ else if n == once "sys".to_symbol then
+ s = "(G_sys)"
+ else if n == once "is_same_type".to_symbol then
+ s = "TAG_Bool((VAL2VFT(@@@)==VAL2VFT(@@@)))"
+ else if n == once "exit".to_symbol then
+ p[0] = p[1]
+ s = "exit(UNTAG_Int(@@@));"
+ else if n == once "calloc_array".to_symbol then
+ p[0] = p[1]
+ s = "BOX_NativeArray((val_t*)malloc((UNTAG_Int(@@@) * sizeof(val_t))))"
+ else if n == once "calloc_string".to_symbol then
+ p[0] = p[1]
+ s = "BOX_NativeString((char*)malloc((UNTAG_Int(@@@) * sizeof(char))))"
+ end
+ if s == null then
+ v.visitor.error(self, "Fatal error: unknown intern method {method.full_name}.")
+ s = "NIT_NULL"
+ end
+ var rtype = method.signature.return_type
+ if rtype != null then
+ v.add_return_value(v.expr(new INative(s, p), rtype))
+ else
+ v.stmt(new INative(s, p))
+ end
+ end
+end
+
+###############################################################################
+
+redef class PExpr
+ redef fun accept_icode_generation(v) do end
+
+ # Generate icode sequence in the current A2IContext
+ # This method should not be called direclty: use generate_expr and generate_stmt from A2IContext instead
+ protected fun generate_icode(v: A2IContext): nullable IRegister is abstract
+end
+
+redef class AVardeclExpr
+ redef fun generate_icode(v)
+ do
+ var reg = v.variable(variable)
+ var ne = n_expr
+ if ne != null then
+ v.add_assignment(reg, v.generate_expr(ne))
+ end
+ return null
+ end
+end
+
+redef class ABlockExpr
+ redef fun generate_icode(v)
+ do
+ for ne in n_expr do v.generate_stmt(ne)
+ return null
+ end
+end
+
+redef class ADoExpr
+ redef fun generate_icode(v)
+ do
+ v.generate_stmt(n_block)
+ return null
+ end
+end
+
+redef class AReturnExpr
+ redef fun generate_icode(v)
+ do
+ var ne = n_expr
+ if ne != null then
+ v.add_assignment(v.return_value.as(not null), v.generate_expr(ne))
+ end
+ v.stmt(new IEscape(v.return_seq.as(not null)))
+ return null
+ end
+end
+
+redef class ABreakExpr
+ redef fun generate_icode(v)
+ do
+ var ne = n_expr
+ if ne != null then
+ v.add_assignment(escapable.break_value.as(not null), v.generate_expr(ne))
+ end
+ v.stmt(new IEscape(escapable.break_seq.as(not null)))
+ return null
+ end
+end
+
+redef class AContinueExpr
+ redef fun generate_icode(v)
+ do
+ var ne = n_expr
+ if ne != null then
+ v.add_assignment(escapable.continue_value.as(not null), v.generate_expr(ne))
+ end
+ v.stmt(new IEscape(escapable.continue_seq.as(not null)))
+ return null
+ end
+end
+
+redef class AAbortExpr
+ redef fun generate_icode(v)
+ do
+ v.add_abort("Aborted")
+ return null
+ end
+end
+
+redef class AIfExpr
+ redef fun generate_icode(v)
+ do
+ var iif = new IIf(v.generate_expr(n_expr))
+ v.stmt(iif)
+ var seq_old = v.seq
+
+ if n_then != null then
+ v.seq = iif.then_seq
+ v.generate_stmt(n_then)
+ end
+
+ if n_else != null then
+ v.seq = iif.else_seq
+ v.generate_stmt(n_else)
+ end
+
+ v.seq = seq_old
+ return null
+ end
+end
+
+redef class AWhileExpr
+ redef fun generate_icode(v)
+ do
+ var seq_old = v.seq
+ var iloop = new ILoop
+ v.stmt(iloop)
+ escapable.break_seq = iloop
+ v.seq = iloop
+
+ # Process condition
+ var iif = new IIf(v.generate_expr(n_expr))
+ v.stmt(iif)
+
+ # Process inside (condition is true)
+ if n_block != null then
+ v.seq = iif.then_seq
+ escapable.continue_seq = iif.then_seq
+ v.generate_stmt(n_block)
+ end
+
+ # Process escape (condition is false)
+ v.seq = iif.else_seq
+ v.stmt(new IEscape(iloop))
+
+ v.seq = seq_old
+ return null
+ end
+end
+
+redef class AForExpr
+ redef fun generate_icode(v)
+ do
+ var expr_type = n_expr.stype
+
+ # Get iterator
+ var meth_iterator = v.visitor.get_method(expr_type, once "iterator".to_symbol)
+
+ var iter_type = meth_iterator.signature_for(expr_type).return_type.as(not null)
+ var ireg_iter = v.expr(new ICall(meth_iterator, [v.generate_expr(n_expr)]), iter_type)
+
+ # Enter loop
+ var seq_old = v.seq
+ var iloop = new ILoop
+ v.stmt(iloop)
+ escapable.break_seq = iloop
+ v.seq = iloop
+
+ # Condition evaluation
+ var meth_is_ok = v.visitor.get_method(iter_type, once ("is_ok".to_symbol))
+ var ireg_isok = v.expr(new ICall(meth_is_ok, [ireg_iter]), v.visitor.type_bool)
+ var iif = new IIf(ireg_isok)
+
+ # Process insite the loop (condition is true)
+ v.stmt(iif)
+ v.seq = iif.then_seq
+ escapable.continue_seq = iif.then_seq
+
+ # Automatic variable assignment
+ var meth_item = v.visitor.get_method(iter_type, once ("item".to_symbol))
+ var va_stype = variable.stype.as(not null)
+ var ireg_item = v.expr(new ICall(meth_item, [ireg_iter]), va_stype)
+ var ireg_va = v.variable(variable)
+ v.add_assignment(ireg_va, ireg_item)
+
+ # Body evaluation
+ v.generate_stmt(n_block)
+
+ # Exit contition (condition is false)
+ v.seq = iif.else_seq
+ v.stmt(new IEscape(iloop))
+
+ # Next step
+ var meth_next = v.visitor.get_method(iter_type, once ("next".to_symbol))
+ v.seq = iloop
+ v.stmt(new ICall(meth_next, [ireg_iter]))
+
+ v.seq = seq_old
+ return null
+ end
+end
+
+redef class AAssertExpr
+ redef fun generate_icode(v)
+ do
+ var e = v.generate_expr(n_expr)
+ var iif = new IIf(e)
+ v.stmt(iif)
+ var seq_old = v.seq
+ v.seq = iif.else_seq
+ var id = n_id
+ if id == null then
+ v.add_abort("Assert failed")
+ else
+ v.add_abort("Assert %s failed", id.to_s)
+ end
+ v.seq = seq_old
+ return null
+ end
+end
+
+redef class AVarExpr
+ redef fun generate_icode(v)
+ do
+ return v.variable(variable)
+ end
+end
+
+redef class AVarAssignExpr
+ redef fun generate_icode(v)
+ do
+ var e = v.generate_expr(n_value)
+ v.add_assignment(v.variable(variable), e)
+ return null
+ end
+end
+
+redef class AVarReassignExpr
+ redef fun generate_icode(v)
+ do
+ var e1 = v.variable(variable)
+ var e2 = v.generate_expr(n_value)
+ var e3 = v.expr(new ICall(assign_method, [e1, e2]), assign_method.signature.return_type.as(not null))
+ v.add_assignment(e1, e3)
+ return null
+ end
+end
+
+redef class ASelfExpr
+ redef fun generate_icode(v)
+ do
+ return v.variable(variable)
+ end
+end
+
+redef class AIfexprExpr
+ redef fun generate_icode(v)
+ do
+ # Process condition
+ var iif = new IIf(v.generate_expr(n_expr))
+ v.stmt(iif)
+ var seq_old = v.seq
+
+ # Prepare result
+ var reg = v.new_register(stype)
+
+ # Process 'then'
+ v.seq = iif.then_seq
+ v.add_assignment(reg, v.generate_expr(n_then))
+
+ # Process 'else'
+ v.seq = iif.else_seq
+ v.add_assignment(reg, v.generate_expr(n_else))
+
+ v.seq = seq_old
+ return reg
+ end
+end
+
+redef class AEeExpr
+ redef fun generate_icode(v)
+ do
+ var e = v.generate_expr(n_expr)
+ var e2 = v.generate_expr(n_expr2)
+ return v.expr(new IIs(e, e2), stype)
+ end
+end
+
+redef class AOrExpr
+ redef fun generate_icode(v)
+ do
+ # Prepare result
+ var reg = v.new_register(stype)
+
+ # Process left operand (in a if/then)
+ var iif = new IIf(v.generate_expr(n_expr))
+ v.stmt(iif)
+ var seq_old = v.seq
+ v.seq = iif.then_seq
+ v.add_assignment(reg, v.generate_expr(n_expr))
+
+ # Process right operand (in the else)
+ v.seq = iif.else_seq
+ v.add_assignment(reg, v.generate_expr(n_expr2))
+
+ v.seq = seq_old
+ return reg
+ end
+end
+
+redef class AAndExpr
+ redef fun generate_icode(v)
+ do
+ # Prepare result
+ var reg = v.new_register(stype)
+
+ # Process left operand (in a if/else)
+ var iif = new IIf(v.generate_expr(n_expr))
+ v.stmt(iif)
+ var seq_old = v.seq
+ v.seq = iif.else_seq
+ v.add_assignment(reg, v.generate_expr(n_expr))
+
+ # Process right operand (in the then)
+ v.seq = iif.then_seq
+ v.add_assignment(reg, v.generate_expr(n_expr2))
+
+ v.seq = seq_old
+ return reg
+ end
+end
+
+redef class ANotExpr
+ redef fun generate_icode(v)
+ do
+ var e = v.generate_expr(n_expr)
+ return v.expr(new INot(e), stype)
+ end
+end
+
+redef class AIsaExpr
+ redef fun generate_icode(v)
+ do
+ var e = v.generate_expr(n_expr)
+ return v.expr(new ITypeCheck(e, n_type.stype), stype)
+ end
+end
+
+redef class AAsCastExpr
+ redef fun generate_icode(v)
+ do
+ var e = v.generate_expr(n_expr)
+ v.add_type_cast(e, stype)
+ return e
+ end
+end
+
+redef class AAsNotnullExpr
+ redef fun generate_icode(v)
+ do
+ var e = v.generate_expr(n_expr)
+ v.add_type_cast(e, stype)
+ return e
+ end
+end
+
+redef class ATrueExpr
+ redef fun generate_icode(v)
+ do
+ return v.expr(new INative("TAG_Bool(true)", null), stype)
+ end
+end
+
+redef class AFalseExpr
+ redef fun generate_icode(v)
+ do
+ return v.expr(new INative("TAG_Bool(false)", null), stype)
+ end
+end
+
+redef class AIntExpr
+ redef fun generate_icode(v)
+ do
+ return v.expr(new INative("TAG_Int({n_number.text})", null), stype)
+ end
+end
+
+redef class AFloatExpr
+ redef fun generate_icode(v)
+ do
+ return v.expr(new INative("BOX_Float({n_float.text})", null), stype)
+ end
+end
+
+redef class ACharExpr
+ redef fun generate_icode(v)
+ do
+ return v.expr(new INative("TAG_Char({n_char.text})", null), stype)
+ end
+end
+
+redef class AStringFormExpr
+ redef fun generate_icode(v)
+ do
+ compute_string_infos
+ var old_seq = v.seq
+ var ionce = new IOnce
+ var reg = v.expr(ionce, stype)
+ v.seq = ionce.body
+ var ns = v.expr(new INative("BOX_NativeString(\"{_cstring}\")", null), v.visitor.type_nativestring)
+ var ni = v.expr(new INative("TAG_Int({_cstring_length})", null), v.visitor.type_int)
+ var prop = v.visitor.get_method(stype, once "with_native".to_symbol)
+ var e = v.expr(new INew(stype, prop, [ns, ni]), stype)
+ v.add_assignment(reg, e)
+ v.seq = old_seq
+ return reg
+ end
+
+ # The raw string value
+ protected fun string_text: String is abstract
+
+ # The string in a C native format
+ protected var _cstring: nullable String
+
+ # The string length in bytes
+ protected var _cstring_length: nullable Int
+
+ # Compute _cstring and _cstring_length using string_text
+ protected fun compute_string_infos
+ do
+ var len = 0
+ var str = string_text
+ var res = new Buffer
+ var i = 0
+ while i < str.length do
+ var c = str[i]
+ if c == '\\' then
+ i = i + 1
+ var c2 = str[i]
+ if c2 != '{' and c2 != '}' then
+ res.add(c)
+ end
+ c = c2
+ end
+ len = len + 1
+ res.add(c)
+ i = i + 1
+ end
+ _cstring = res.to_s
+ _cstring_length = len
+ end
+end
+
+redef class AStringExpr
+ redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2)
+end
+redef class AStartStringExpr
+ redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2)
+end
+redef class AMidStringExpr
+ redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2)
+end
+redef class AEndStringExpr
+ redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2)
+end
+
+redef class ASuperstringExpr
+ redef fun generate_icode(v)
+ do
+ var array = v.add_new_array(atype, n_exprs.length)
+ var prop_to_s = v.visitor.get_method(v.visitor.type_object, once "to_s".to_symbol)
+ for ne in n_exprs do
+ var e = v.generate_expr(ne)
+ if ne.stype != stype then
+ e = v.expr(new ICall(prop_to_s, [e]), stype)
+ end
+ v.add_call_array_add(array, e)
+ end
+ return v.expr(new ICall(prop_to_s, [array]), stype)
+ end
+end
+
+redef class ANullExpr
+ redef fun generate_icode(v)
+ do
+ return v.new_register(stype)
+ end
+end
+
+redef class AArrayExpr
+ redef fun generate_icode(v)
+ do
+ var recv = v.add_new_array(stype, n_exprs.length)
+ for ne in n_exprs do
+ var e = v.generate_expr(ne)
+ v.add_call_array_add(recv, e)
+ end
+ return recv
+ end
+end
+
+redef class ACrangeExpr
+ redef fun generate_icode(v)
+ do
+ var e = v.generate_expr(n_expr)
+ var e2 = v.generate_expr(n_expr2)
+ var prop = v.visitor.get_method(stype, once "init".to_symbol)
+ return v.expr(new INew(stype, prop, [e, e2]), stype)
+ end
+end
+
+redef class AOrangeExpr
+ redef fun generate_icode(v)
+ do
+ var e = v.generate_expr(n_expr)
+ var e2 = v.generate_expr(n_expr2)
+ var prop = v.visitor.get_method(stype, once "without_last".to_symbol)
+ return v.expr(new INew(stype, prop, [e, e2]), stype)
+ end
+end
+
+redef class ASuperExpr
+ redef fun generate_icode(v)
+ do
+ var arity = v.iroutine.params.length - 1
+ if init_in_superclass != null then
+ arity = init_in_superclass.signature.arity
+ end
+ var args = new Array[IRegister].with_capacity(arity + 1)
+ args.add(v.iroutine.params[0])
+ if n_args.length != arity then
+ for i in [0..arity[ do
+ args.add(v.iroutine.params[i + 1])
+ end
+ else
+ for na in n_args do
+ args.add(v.generate_expr(na))
+ end
+ end
+ var p = init_in_superclass
+ if p != null then
+ var rtype = p.signature.return_type
+ if rtype != null then
+ return v.expr(new ICall(p, args), rtype)
+ else
+ v.stmt(new ICall(p, args))
+ return null
+ end
+ else
+ var p = prop
+ var rtype = p.signature.return_type
+ if rtype == null then
+ v.stmt(new ISuper(p, args))
+ return null
+ else
+ return v.expr(new ISuper(p, args), rtype)
+ end
+ end
+ end
+end
+
+redef class AAttrExpr
+ redef fun generate_icode(v)
+ do
+ var e = v.generate_expr(n_expr)
+ return v.add_attr_read(prop, e)
+ end
+end
+
+redef class AAttrAssignExpr
+ redef fun generate_icode(v)
+ do
+ var e = v.generate_expr(n_expr)
+ var e2 = v.generate_expr(n_value)
+ v.stmt(new IAttrWrite(prop, e, e2))
+ return null
+ end
+end
+redef class AAttrReassignExpr
+ redef fun generate_icode(v)
+ do
+ var e1 = v.generate_expr(n_expr)
+ var e2 = v.expr(new IAttrRead(prop, e1), attr_type)
+ var e3 = v.generate_expr(n_value)
+ var e4 = v.expr(new ICall(assign_method, [e2, e3]), attr_type)
+ v.stmt(new IAttrWrite(prop, e1, e4))
+ return null
+ end
+end
+
+redef class AIssetAttrExpr
+ redef fun generate_icode(v)
+ do
+ var e = v.generate_expr(n_expr)
+ return v.expr(new IAttrIsset(prop, e), stype)
+ end
+end
+
+redef class AAbsAbsSendExpr
+ # Compile each argument and add them to the array
+ fun generate_icode_for_arguments_in(v: A2IContext, args: Array[IRegister])
+ do
+ for a in arguments do
+ args.add(v.generate_expr(a))
+ end
+ end
+end
+
+redef class ASendExpr
+ redef fun generate_icode(v)
+ do
+ var recv = v.generate_expr(n_expr)
+ var args = new Array[IRegister]
+ args.add(recv)
+ generate_icode_for_arguments_in(v, args)
+ var prop = prop
+ var r: nullable IRegister = null # The full result of the send (raw call + breaks)
+ var r2: nullable IRegister # The raw result of the call
+
+ # Prepare closures
+ var seq_old = v.seq
+ var closcns: nullable Array[nullable IClosureDef] = null
+ if not prop_signature.closures.is_empty then
+ var rtype = prop_signature.return_type
+ if rtype != null then
+ r = v.new_register(rtype)
+ end
+ var seq = new ISeq
+ v.stmt(seq)
+ v.seq = seq
+ closcns = new Array[nullable IClosureDef]
+ var cdarity = 0
+ if closure_defs != null then cdarity = closure_defs.length
+ for i in [0..cdarity[ do
+ closure_defs[i].escapable.break_seq = seq
+ closure_defs[i].escapable.break_value = r
+ var cn = closure_defs[i].generate_iclosuredef(v)
+ closcns.add(cn)
+ end
+ for i in [cdarity..prop_signature.closures.length[ do
+ closcns.add(null)
+ end
+ end
+
+ r2 = v.add_call(prop, args, closcns)
+
+ # Closure work
+ if not prop_signature.closures.is_empty then
+ if r != null and r2 != null then v.add_assignment(r, r2)
+ v.seq = seq_old
+ else
+ r = r2
+ end
+
+ if prop.global.is_init then
+ v.invoke_super_init_calls_after(prop)
+ end
+ return r
+ end
+end
+
+redef class ASendReassignExpr
+ redef fun generate_icode(v)
+ do
+ var recv = v.generate_expr(n_expr)
+ var args = new Array[IRegister]
+ args.add(recv)
+ generate_icode_for_arguments_in(v, args)
+
+ var e2 = v.expr(new ICall(read_prop, args), read_prop.signature.return_type.as(not null))
+ var e3 = v.generate_expr(n_value)
+ var e4 = v.expr(new ICall(assign_method, [e2, e3]), assign_method.signature.return_type.as(not null))
+ var args2 = args.to_a
+ args2.add(e4)
+ v.stmt(new ICall(prop, args2))
+ return null
+ end
+end
+
+redef class ANewExpr
+ redef fun generate_icode(v)
+ do
+ var args = new Array[IRegister]
+ generate_icode_for_arguments_in(v, args)
+ return v.expr(new INew(stype, prop, args), stype)
+ end
+end
+
+redef class AProxyExpr
+ redef fun generate_icode(v)
+ do
+ return v.generate_expr(n_expr)
+ end
+end
+
+redef class AOnceExpr
+ redef fun generate_icode(v)
+ do
+ var ionce = new IOnce
+ var reg = v.expr(ionce, stype)
+ var old_seq = v.seq
+ v.seq = ionce.body
+
+ var e = v.generate_expr(n_expr)
+ v.add_assignment(reg, e)
+
+ v.seq = old_seq
+ return reg
+ end
+end
+
+
+redef class PClosureDef
+ var _iclosure_def: nullable IClosureDef
+
+ fun generate_iclosuredef(v: A2IContext): IClosureDef is abstract
+end
+
+redef class AClosureDef
+ redef fun generate_iclosuredef(v)
+ do
+ # Prepare signature
+ var args = new Array[IRegister]
+ var sig = closure.signature
+ for i in [0..sig.arity[ do
+ args.add(v.new_register(sig[i]))
+ end
+ var ret: nullable IRegister = null
+ var rtype = sig.return_type
+ if rtype != null then
+ ret = v.new_register(rtype)
+ end
+
+ var iclos = new IClosureDef(args, ret)
+ iclos.location = location
+
+ # Prepare env
+ var seq_old = v.seq
+ v.seq = iclos.body
+ escapable.continue_seq = iclos.body
+ escapable.continue_value = iclos.result
+
+ # Assign parameters
+ for i in [0..variables.length[ do
+ var res = v.variable(variables[i])
+ v.add_assignment(res, iclos.params[i])
+ end
+
+ v.generate_stmt(n_expr)
+
+ v.seq = seq_old
+ _iclosure_def = iclos
+ return iclos
+ end
+end
+
+redef class AClosureCallExpr
+ redef fun generate_icode(v)
+ do
+ # Geneate arguments
+ var args = new Array[IRegister]
+ generate_icode_for_arguments_in(v, args)
+
+ # Prepare icall
+ var closdecl = v.closurevariables[variable]
+ var icall = new IClosCall(closdecl, args)
+ var seq_old = v.seq
+
+ # Fill break of ical
+ if n_closure_defs.length == 1 then do
+ var iseq = new ISeq
+ icall.break_seq = iseq
+ v.seq = iseq
+ v.generate_stmt(n_closure_defs.first.as(AClosureDef).n_expr)
+ v.seq = seq_old
+ end
+
+ # Prepare in case of default block
+ var iif: nullable IIf = null # The iif of default block
+ var closdecl_default = closdecl.default # The default (if any)
+ if closdecl_default != null then
+ iif = new IIf(v.expr(new IHasClos(closdecl), v.visitor.type_bool))
+ v.stmt(iif)
+ v.seq = iif.then_seq
+ end
+
+ # Add the icall
+ var r2: nullable IRegister = null # the result of the icall
+ var rtype = variable.closure.signature.return_type
+ if rtype == null then
+ v.stmt(icall)
+ else
+ r2 = v.expr(icall, rtype)
+ end
+
+ # Process the case of default block
+ var r: nullable IRegister = null # the real result
+ if closdecl_default != null then
+ assert iif != null
+ if r2 != null then
+ assert rtype != null
+ r = v.new_register(rtype)
+ v.add_assignment(r, r2)
+ end
+ v.seq = iif.else_seq
+ var r3 = closdecl_default.inline_in_seq(iif.else_seq, args)
+ if r != null then
+ assert r3 != null
+ v.add_assignment(r, r3)
+ end
+ v.seq = seq_old
+ else
+ r = r2
+ end
+ return r
+ end
+end
import mmloader
import mmbuilder
import typing
+import icode_generation
# Loader of nit source files
class SrcModuleLoader
do_typing(tc)
tc.check_errors
+
+ generate_icode(tc)
+ tc.check_errors
end
end
fun attr_type: MMType is abstract
end
-redef class AStringFormExpr
- fun meth_with_native: MMMethod is abstract
-end
-
redef class ASuperstringExpr
- fun meth_with_capacity: MMMethod is abstract
- fun meth_add: MMMethod is abstract
- fun meth_to_s: MMMethod is abstract
fun atype: MMType is abstract
end
-redef class AArrayExpr
- fun meth_with_capacity: MMMethod is abstract
- fun meth_add: MMMethod is abstract
-end
-
-redef class ARangeExpr
- fun meth_init: MMMethod is abstract
-end
-
redef class AVardeclExpr
# Assiociated local variable
fun variable: VarVariable is abstract
redef class AForExpr
# Associated automatic local variable
fun variable: AutoVariable is abstract
- fun meth_iterator: MMMethod is abstract
- fun meth_is_ok: MMMethod is abstract
- fun meth_item: MMMethod is abstract
- fun meth_next: MMMethod is abstract
end
redef class ASelfExpr
# Associated local variable
fun variable: ParamVariable is abstract
- #readable writable var _variable: nullable ParamVariable
end
redef class AVarFormExpr
# Associated local variable
fun variable: Variable is abstract
- #readable writable var _variable: nullable Variable
end
redef class AClosureCallExpr
special AAbsAbsSendExpr
# Associated closure variable
fun variable: ClosureVariable is abstract
- #readable writable var _variable: nullable ClosureVariable
end
redef class PClosureDef
# Associated closure
- #readable writable var _closure: nullable MMClosure
fun closure: MMClosure is abstract
# Automatic variables
# The corresponding escapable block
readable var _escapable: nullable EscapableBlock
- var _meth_iterator: nullable MMMethod
- redef fun meth_iterator: MMMethod do return _meth_iterator.as(not null)
- var _meth_is_ok: nullable MMMethod
- redef fun meth_is_ok: MMMethod do return _meth_is_ok.as(not null)
- var _meth_item: nullable MMMethod
- redef fun meth_item: MMMethod do return _meth_item.as(not null)
- var _meth_next: nullable MMMethod
- redef fun meth_next: MMMethod do return _meth_next.as(not null)
redef fun accept_typing(v)
do
var escapable = new EscapableBlock(self)
if not v.check_conform_expr(n_expr, v.type_collection) then return
var expr_type = n_expr.stype
- _meth_iterator = expr_type.local_class.select_method(once ("iterator".to_symbol))
- if _meth_iterator == null then
- v.error(self, "Error: Collection MUST have an iterate method")
- return
- end
- var iter_type = _meth_iterator.signature_for(expr_type).return_type.as(not null)
- _meth_is_ok = iter_type.local_class.select_method(once ("is_ok".to_symbol))
- if _meth_is_ok == null then
- v.error(self, "Error: {iter_type} MUST have an is_ok method")
- return
- end
- _meth_item = iter_type.local_class.select_method(once ("item".to_symbol))
- if _meth_item == null then
- v.error(self, "Error: {iter_type} MUST have an item method")
- return
- end
- _meth_next = iter_type.local_class.select_method(once ("next".to_symbol))
- if _meth_next == null then
- v.error(self, "Error: {iter_type} MUST have a next method")
- return
- end
- var t = _meth_item.signature_for(iter_type).return_type
- if not n_expr.is_self then t = t.not_for_self
- va.stype = t
+ # Get iterator
+ var meth_iterator = v.get_method(expr_type, once "iterator".to_symbol)
+ var iter_type = meth_iterator.signature_for(expr_type).return_type.as(not null)
+ var meth_item = v.get_method(iter_type, once ("item".to_symbol))
+ var va_stype = meth_item.signature_for(iter_type).return_type.as(not null)
+ if not n_expr.is_self then va_stype = va_stype.not_for_self
+ va.stype = va_stype
# Body evaluation
if n_block != null then v.enter_visit(n_block)
end
redef class AStringFormExpr
- var _meth_with_native: nullable MMMethod
- redef fun meth_with_native: MMMethod do return _meth_with_native.as(not null)
redef fun after_typing(v)
do
_stype = v.type_string
_is_typed = true
- _meth_with_native = _stype.local_class.select_method(once "with_native".to_symbol)
- if _meth_with_native == null then v.error(self, "{_stype} MUST have a with_native method.")
end
end
redef class ASuperstringExpr
- redef fun meth_with_capacity: MMMethod do return _meth_with_capacity.as(not null)
- var _meth_with_capacity: nullable MMMethod
- redef fun meth_add: MMMethod do return _meth_add.as(not null)
- var _meth_add: nullable MMMethod
- redef fun meth_to_s: MMMethod do return _meth_to_s.as(not null)
- var _meth_to_s: nullable MMMethod
redef fun atype do return _atype.as(not null)
var _atype: nullable MMType
redef fun after_typing(v)
_stype = stype
var atype = v.type_array(stype)
_atype = atype
- _meth_with_capacity = atype.local_class.select_method(once "with_capacity".to_symbol)
- if _meth_with_capacity == null then v.error(self, "{_atype} MUST have a with_capacity method.")
- _meth_add = atype.local_class.select_method(once "add".to_symbol)
- if _meth_add == null then v.error(self, "{_atype} MUST have an add method.")
- _meth_to_s = v.type_object.local_class.select_method(once "to_s".to_symbol)
- if _meth_to_s == null then v.error(self, "Object MUST have a to_s method.")
_is_typed = true
end
end
end
redef class AArrayExpr
- redef fun meth_with_capacity: MMMethod do return _meth_with_capacity.as(not null)
- var _meth_with_capacity: nullable MMMethod
- redef fun meth_add: MMMethod do return _meth_add.as(not null)
- var _meth_add: nullable MMMethod
-
redef fun after_typing(v)
do
var stype = v.check_conform_multiexpr(null, n_exprs)
private fun do_typing(v: TypingVisitor, element_type: MMType)
do
_stype = v.type_array(element_type)
-
- _meth_with_capacity = _stype.local_class.select_method(once "with_capacity".to_symbol)
- if _meth_with_capacity == null then v.error(self, "{_stype} MUST have a with_capacity method.")
- _meth_add = _stype.local_class.select_method(once "add".to_symbol)
- if _meth_add == null then v.error(self, "{_stype} MUST have an add method.")
-
_is_typed = true
end
end
redef class ARangeExpr
- redef fun meth_init: MMMethod do return _meth_init.as(not null)
- var _meth_init: nullable MMMethod
redef fun after_typing(v)
do
if not v.check_expr(n_expr) or not v.check_expr(n_expr2) then return
end
end
-redef class ACrangeExpr
- redef fun after_typing(v)
- do
- super
- if not is_typed then return
- _meth_init = stype.local_class.select_method(once "init".to_symbol)
- end
-end
-redef class AOrangeExpr
- redef fun after_typing(v)
- do
- super
- if not is_typed then return
- _meth_init = stype.local_class.select_method(once "without_last".to_symbol)
- end
-end
-
-
redef class ASuperExpr
redef readable var _init_in_superclass: nullable MMMethod
redef fun after_typing(v)
3
true
false
-Uninitialized attribute base_attr_isset_alt3::Foo::_a2 at base_attr_isset_alt3::Foo::init for Foo.
+Uninitialized attribute _a2 (alt/base_attr_isset_alt3.nit)
,---- Stack trace -- - - -
+| check new Foo (alt/base_attr_isset_alt3.nit:0)
+| new Foo base_attr_isset_alt3::Foo::init (alt/base_attr_isset_alt3.nit:53)
| base_attr_isset_alt3::Sys::main (alt/base_attr_isset_alt3.nit:100)
`------------------- - - -
3
true
false
-Uninitialized attribute base_attr_isset_alt4::Foo::_a2 at base_attr_isset_alt4::Foo::init for Baz.
+Uninitialized attribute _a2 (alt/base_attr_isset_alt4.nit)
,---- Stack trace -- - - -
+| check new Baz (alt/base_attr_isset_alt4.nit:0)
+| new Baz base_attr_isset_alt4::Foo::init (alt/base_attr_isset_alt4.nit:53)
| base_attr_isset_alt4::Sys::main (alt/base_attr_isset_alt4.nit:99)
`------------------- - - -
,---- Stack trace -- - - -
| base_attr_nullable_alt1::Foo::run (alt/base_attr_nullable_alt1.nit:36)
| base_attr_nullable_alt1::Foo::init (alt/base_attr_nullable_alt1.nit:48)
+| new Foo base_attr_nullable_alt1::Foo::init (alt/base_attr_nullable_alt1.nit:48)
| base_attr_nullable_alt1::Sys::main (alt/base_attr_nullable_alt1.nit:81)
`------------------- - - -
1
-Uninitialized attribute _a2 (alt/base_attr_nullable_alt2.nit:35)
+Uninitialized attribute _a2 in base_attr_nullable_alt2::Foo::a2 (alt/base_attr_nullable_alt2.nit:35)
,---- Stack trace -- - - -
| base_attr_nullable_alt2::Foo::a2 (alt/base_attr_nullable_alt2.nit:35)
| base_attr_nullable_alt2::Foo::run (alt/base_attr_nullable_alt2.nit:36)
| base_attr_nullable_alt2::Foo::init (alt/base_attr_nullable_alt2.nit:48)
+| new Foo base_attr_nullable_alt2::Foo::init (alt/base_attr_nullable_alt2.nit:48)
| base_attr_nullable_alt2::Sys::main (alt/base_attr_nullable_alt2.nit:81)
`------------------- - - -
,---- Stack trace -- - - -
| base_attr_nullable_alt3::Bar::(base_attr_nullable_alt3::Foo::run) (alt/base_attr_nullable_alt3.nit:62)
| base_attr_nullable_alt3::Bar::init (alt/base_attr_nullable_alt3.nit:69)
+| new Bar base_attr_nullable_alt3::Bar::init (alt/base_attr_nullable_alt3.nit:69)
| base_attr_nullable_alt3::Sys::main (alt/base_attr_nullable_alt3.nit:81)
`------------------- - - -
,---- Stack trace -- - - -
| base_attr_nullable_alt4::Foo::run_other (alt/base_attr_nullable_alt4.nit:42)
| base_attr_nullable_alt4::Bar::init (alt/base_attr_nullable_alt4.nit:69)
+| new Bar base_attr_nullable_alt4::Bar::init (alt/base_attr_nullable_alt4.nit:69)
| base_attr_nullable_alt4::Sys::main (alt/base_attr_nullable_alt4.nit:81)
`------------------- - - -
,---- Stack trace -- - - -
| base_attr_nullable_alt5::Bar::(base_attr_nullable_alt5::Foo::run) (alt/base_attr_nullable_alt5.nit:62)
| base_attr_nullable_alt5::Bar::init (alt/base_attr_nullable_alt5.nit:69)
+| new Bar base_attr_nullable_alt5::Bar::init (alt/base_attr_nullable_alt5.nit:69)
| base_attr_nullable_alt5::Sys::main (alt/base_attr_nullable_alt5.nit:81)
`------------------- - - -
,---- Stack trace -- - - -
| base_attr_nullable_int_alt1::Foo::run (alt/base_attr_nullable_int_alt1.nit:30)
| base_attr_nullable_int_alt1::Foo::init (alt/base_attr_nullable_int_alt1.nit:42)
+| new Foo base_attr_nullable_int_alt1::Foo::init (alt/base_attr_nullable_int_alt1.nit:42)
| base_attr_nullable_int_alt1::Sys::main (alt/base_attr_nullable_int_alt1.nit:75)
`------------------- - - -
1
-Uninitialized attribute _a2 (alt/base_attr_nullable_int_alt2.nit:29)
+Uninitialized attribute _a2 in base_attr_nullable_int_alt2::Foo::a2 (alt/base_attr_nullable_int_alt2.nit:29)
,---- Stack trace -- - - -
| base_attr_nullable_int_alt2::Foo::a2 (alt/base_attr_nullable_int_alt2.nit:29)
| base_attr_nullable_int_alt2::Foo::run (alt/base_attr_nullable_int_alt2.nit:30)
| base_attr_nullable_int_alt2::Foo::init (alt/base_attr_nullable_int_alt2.nit:42)
+| new Foo base_attr_nullable_int_alt2::Foo::init (alt/base_attr_nullable_int_alt2.nit:42)
| base_attr_nullable_int_alt2::Sys::main (alt/base_attr_nullable_int_alt2.nit:75)
`------------------- - - -
,---- Stack trace -- - - -
| base_attr_nullable_int_alt3::Bar::(base_attr_nullable_int_alt3::Foo::run) (alt/base_attr_nullable_int_alt3.nit:56)
| base_attr_nullable_int_alt3::Bar::init (alt/base_attr_nullable_int_alt3.nit:63)
+| new Bar base_attr_nullable_int_alt3::Bar::init (alt/base_attr_nullable_int_alt3.nit:63)
| base_attr_nullable_int_alt3::Sys::main (alt/base_attr_nullable_int_alt3.nit:75)
`------------------- - - -
,---- Stack trace -- - - -
| base_attr_nullable_int_alt4::Foo::run_other (alt/base_attr_nullable_int_alt4.nit:36)
| base_attr_nullable_int_alt4::Bar::init (alt/base_attr_nullable_int_alt4.nit:63)
+| new Bar base_attr_nullable_int_alt4::Bar::init (alt/base_attr_nullable_int_alt4.nit:63)
| base_attr_nullable_int_alt4::Sys::main (alt/base_attr_nullable_int_alt4.nit:75)
`------------------- - - -
,---- Stack trace -- - - -
| base_attr_nullable_int_alt5::Bar::(base_attr_nullable_int_alt5::Foo::run) (alt/base_attr_nullable_int_alt5.nit:56)
| base_attr_nullable_int_alt5::Bar::init (alt/base_attr_nullable_int_alt5.nit:63)
+| new Bar base_attr_nullable_int_alt5::Bar::init (alt/base_attr_nullable_int_alt5.nit:63)
| base_attr_nullable_int_alt5::Sys::main (alt/base_attr_nullable_int_alt5.nit:75)
`------------------- - - -
-Compilation error
+0
+10
+20
+21
+40
+5
+6
true
true
-false
+true
true
true