# This file is part of NIT ( http://www.nitlanguage.org ). # # Copyright 2009 Jean Privat # # 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 # The method associated to the iroutine (if any) readable var _method: nullable MMMethod init(visitor: AbsSyntaxVisitor, r: IRoutine, m: nullable MMMethod) do super(visitor.module, r) _visitor = visitor _return_seq = r.body _return_value = r.result _method = m 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 sp = n.super_init_calls[i] if sp == stop_prop then break var cargs = new Array[IRegister] if sp.signature.arity == 0 then cargs.add(iroutine.params.first) else for va in iroutine.params do cargs.add(va) end end stmt(new ICall(sp, cargs)) i += 1 end end # The current AExpr var _current_node: nullable AExpr = null # Generate icode in the current sequence from a statement fun generate_stmt(n: nullable AExpr) 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: AExpr): 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 ANode 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 ASignature fun fill_iroutine_parameters(v: A2IContext, orig_sig: MMSignature, params: Sequence[IRegister], closdecls: nullable Sequence[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(@@@)".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(@@@)<".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(@@@)".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 AExpr 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 var seq_old = v.seq var seq = new ISeq v.stmt(seq) escapable.break_seq = seq v.seq = seq v.generate_stmt(n_block) v.seq = seq_old 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 ALoopExpr 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 inside if n_block != null then var seq = new ISeq v.stmt(seq) v.seq = seq escapable.continue_seq = seq v.generate_stmt(n_block) end 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.lit_true_reg) # 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.lit_false_reg) # 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.lit_true_reg end end redef class AFalseExpr redef fun generate_icode(v) do return v.lit_false_reg 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.lit_null_reg 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 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) if n_expr.stype.is_nullable then v.add_null_reciever_check(e) 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) if n_expr.stype.is_nullable then v.add_null_reciever_check(e) 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) if n_expr.stype.is_nullable then v.add_null_reciever_check(e1) 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) if n_expr.stype.is_nullable then v.add_null_reciever_check(e) 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], signature: MMSignature) do var par_arity = signature.arity var par_vararg = signature.vararg_rank var raw_args = raw_arguments var raw_arity = raw_args.length var arg_idx = 0 for par_idx in [0..par_arity[ do var a: AExpr var par_type = signature[par_idx] if par_idx == par_vararg then var arr = v.add_new_array(v.visitor.type_array(par_type), raw_arity-par_arity) for i in [0..(raw_arity-par_arity)] do a = raw_args[arg_idx] v.add_call_array_add(arr, v.generate_expr(a)) arg_idx = arg_idx + 1 end args.add(arr) else a = raw_args[arg_idx] args.add(v.generate_expr(a)) arg_idx = arg_idx + 1 end 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) var prop = prop generate_icode_for_arguments_in(v, args, prop.signature.as(not null)) 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 var closure_defs = closure_defs for mmc in prop_signature.closures do var found = false var name = mmc.name if closure_defs != null then for cd in closure_defs do if cd.n_id.to_symbol != name then continue assert found == false found = true cd.escapable.break_seq = seq cd.escapable.break_value = r var cn = cd.generate_iclosuredef(v) closcns.add(cn) end end if not found then closcns.add(null) end 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) if n_expr.stype.is_nullable then v.add_null_reciever_check(recv) var args = new Array[IRegister] args.add(recv) generate_icode_for_arguments_in(v, args, read_prop.signature.as(not null)) 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, prop.signature.as(not null)) 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 AClosureDef var _iclosure_def: nullable IClosureDef fun generate_iclosuredef(v: A2IContext): IClosureDef 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, variable.closure.signature) # 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.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