{ Copyright (c) 2011 by Jonas Maebe JVM-specific code for call nodes This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. **************************************************************************** } unit njvmcal; {$i fpcdefs.inc} interface uses cgbase, symtype,symdef, ncgcal; type tjvmcallparanode = class(tcgcallparanode) protected outcopybasereg: tregister; procedure push_formal_para; override; procedure push_copyout_para; override; procedure load_arrayref_para(useparadef: tdef); end; { tjvmcallnode } tjvmcallnode = class(tcgcallnode) protected procedure extra_pre_call_code; override; procedure set_result_location(realresdef: tstoreddef); override; procedure do_release_unused_return_value;override; procedure extra_post_call_code; override; end; implementation uses verbose,globtype, symconst,defutil,ncal, cgutils,tgobj,procinfo, cpubase,aasmdata,aasmcpu, hlcgobj,hlcgcpu, node, jvmdef; {***************************************************************************** TJVMCALLPARANODE *****************************************************************************} procedure tjvmcallparanode.load_arrayref_para(useparadef: tdef); var arrayloc: tlocation; arrayref: treference; begin { cannot be a regular array or record, because those are passed by plain reference (since they are reference types at the Java level, but not at the Pascal level) -> no special initialisation necessary } outcopybasereg:=hlcg.getaddressregister(current_asmdata.CurrAsmList,java_jlobject); thlcgjvm(hlcg).a_load_stack_reg(current_asmdata.CurrAsmList,java_jlobject,outcopybasereg); reference_reset_base(arrayref,outcopybasereg,0,4); arrayref.arrayreftype:=art_indexconst; arrayref.indexoffset:=0; { load the current parameter value into the array in case it's not an out-parameter; if it's an out-parameter the contents must be nil but that's already ok, since the anewarray opcode takes care of that } if (parasym.varspez<>vs_out) then hlcg.a_load_loc_ref(current_asmdata.CurrAsmList,useparadef,useparadef,left.location,arrayref); { store the array reference into the parameter location (don't change left.location, we may need it for copy-back after the call) } location_reset(arrayloc,LOC_REGISTER,OS_ADDR); arrayloc.register:=outcopybasereg; hlcg.gen_load_loc_cgpara(current_asmdata.CurrAsmList,java_jlobject,arrayloc,tempcgpara) end; procedure tjvmcallparanode.push_formal_para; var primitivetype: boolean; begin { create an array with one element of JLObject } thlcgjvm(hlcg).a_load_const_stack(current_asmdata.CurrAsmList,s32inttype,1,R_INTREGISTER); { left is either an object-derived type, or has been boxed into one } current_asmdata.CurrAsmList.concat(taicpu.op_sym(a_anewarray,current_asmdata.RefAsmSymbol(jvmarrtype(java_jlobject,primitivetype)))); load_arrayref_para(java_jlobject); end; procedure tjvmcallparanode.push_copyout_para; var mangledname: string; primitivetype: boolean; opc: tasmop; begin { create an array with one element of the parameter type } thlcgjvm(hlcg).a_load_const_stack(current_asmdata.CurrAsmList,s32inttype,1,R_INTREGISTER); mangledname:=jvmarrtype(left.resultdef,primitivetype); if primitivetype then opc:=a_newarray else opc:=a_anewarray; { doesn't change stack height: one int replaced by one reference } current_asmdata.CurrAsmList.concat(taicpu.op_sym(opc,current_asmdata.RefAsmSymbol(mangledname))); load_arrayref_para(left.resultdef); end; {***************************************************************************** TJVMCALLNODE *****************************************************************************} procedure tjvmcallnode.extra_pre_call_code; begin { when calling a constructor, first create a new instance, except when calling it from another constructor (because then this has already been done before calling the current constructor) } if procdefinition.typ<>procdef then exit; if tabstractprocdef(procdefinition).proctypeoption<>potype_constructor then exit; if not(methodpointer.resultdef.typ in [classrefdef,recorddef]) then exit; current_asmdata.CurrAsmList.concat(taicpu.op_sym(a_new,current_asmdata.RefAsmSymbol(tabstractrecorddef(tabstractprocdef(procdefinition).owner.defowner).jvm_full_typename(true)))); { the constructor doesn't return anything, so put a duplicate of the self pointer on the evaluation stack for use as function result after the constructor has run } current_asmdata.CurrAsmList.concat(taicpu.op_none(a_dup)); thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,2); end; procedure tjvmcallnode.set_result_location(realresdef: tstoreddef); begin location_reset_ref(location,LOC_REFERENCE,def_cgsize(realresdef),1); { in case of jvmimplicitpointertype(), the function will have allocated it already and we don't have to allocate it again here } if not jvmimplicitpointertype(realresdef) then tg.gethltemp(current_asmdata.CurrAsmList,realresdef,realresdef.size,tt_normal,location.reference) else tg.gethltemp(current_asmdata.CurrAsmList,java_jlobject,java_jlobject.size,tt_normal,location.reference); end; procedure tjvmcallnode.do_release_unused_return_value; begin if (tabstractprocdef(procdefinition).proctypeoption=potype_constructor) and (current_procinfo.procdef.proctypeoption=potype_constructor) then exit; if (location.loc=LOC_REFERENCE) then tg.ungetiftemp(current_asmdata.CurrAsmList,location.reference); if assigned(funcretnode) then exit; case resultdef.size of 0: ; 1..4: begin current_asmdata.CurrAsmList.concat(taicpu.op_none(a_pop)); thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,1); end; 8: begin current_asmdata.CurrAsmList.concat(taicpu.op_none(a_pop2)); thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,2); end else internalerror(2011010305); end; end; procedure tjvmcallnode.extra_post_call_code; var totalremovesize: longint; realresdef: tdef; ppn: tjvmcallparanode; pararef: treference; begin if not assigned(typedef) then realresdef:=tstoreddef(resultdef) else realresdef:=tstoreddef(typedef); { a constructor doesn't actually return a value in the jvm } if (tabstractprocdef(procdefinition).proctypeoption=potype_constructor) then totalremovesize:=pushedparasize else { even a byte takes up a full stackslot -> align size to multiple of 4 } totalremovesize:=pushedparasize-(align(realresdef.size,4) shr 2); { remove parameters from internal evaluation stack counter (in case of e.g. no parameters and a result, it can also increase) } if totalremovesize>0 then thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,totalremovesize) else if totalremovesize<0 then thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,-totalremovesize); { if this was an inherited constructor call, initialise all fields that are wrapped types following it } if (tabstractprocdef(procdefinition).proctypeoption=potype_constructor) and (cnf_inherited in callnodeflags) then thlcgjvm(hlcg).gen_initialize_fields_code(current_asmdata.CurrAsmList); { copy back the copyout parameter values, if any } { Release temps from parameters } ppn:=tjvmcallparanode(left); while assigned(ppn) do begin if assigned(ppn.left) then begin if (ppn.outcopybasereg<>NR_NO) then begin reference_reset_base(pararef,NR_NO,0,4); pararef.arrayreftype:=art_indexconst; pararef.base:=ppn.outcopybasereg; pararef.indexoffset:=0; { the value has to be copied back into persistent storage } if (ppn.parasym.vardef.typ<>formaldef) then begin case ppn.left.location.loc of LOC_REFERENCE: hlcg.a_load_ref_ref(current_asmdata.CurrAsmList,ppn.left.resultdef,ppn.left.resultdef,pararef,ppn.left.location.reference); LOC_CREGISTER: hlcg.a_load_ref_reg(current_asmdata.CurrAsmList,ppn.left.resultdef,ppn.left.resultdef,pararef,ppn.left.location.register); else internalerror(2011051201); end; end else begin {$ifndef nounsupported} { to do: extract value from boxed parameter or load value back } {$else} internalerror(2011051901); {$endif} end; end; end; ppn:=tjvmcallparanode(ppn.right); end; end; begin ccallnode:=tjvmcallnode; ccallparanode:=tjvmcallparanode; end.