{ Copyright (c) 1998-2011 by Florian Klaempfl and Jonas Maebe Generate assembler for nodes that influence the flow for the JVM 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 njvmflw; {$i fpcdefs.inc} interface uses aasmbase,node,nflw,ncgflw; type tjvmfornode = class(tcgfornode) function pass_1: tnode; override; end; tjvmraisenode = class(traisenode) function pass_typecheck: tnode; override; function pass_1: tnode; override; procedure pass_generate_code;override; end; tjvmtryexceptnode = class(ttryexceptnode) procedure pass_generate_code;override; end; tjvmtryfinallynode = class(ttryfinallynode) procedure pass_generate_code;override; end; tjvmonnode = class(tonnode) procedure pass_generate_code;override; end; implementation uses verbose,globals,systems,globtype,constexp, symconst,symdef,symsym,aasmtai,aasmdata,aasmcpu,defutil,jvmdef, procinfo,cgbase,pass_1,pass_2,parabase, cpubase,cpuinfo, nbas,nld,ncon,ncnv, tgobj,paramgr, cgutils,hlcgobj,hlcgcpu ; {***************************************************************************** TFJVMFORNODE *****************************************************************************} function tjvmfornode.pass_1: tnode; var iteratortmp: ttempcreatenode; olditerator: tnode; block, newbody: tblocknode; stat, newbodystat: tstatementnode; begin { transform for-loops with enums to: for tempint:=ord(lowval) to ord(upperval) do begin originalctr:=tenum(tempint); end; enums are class instances in Java and hence can't be increased or so. The type conversion consists of an array lookup in a final method, so it shouldn't be too expensive. } if left.resultdef.typ=enumdef then begin block:=internalstatements(stat); iteratortmp:=ctempcreatenode.create(s32inttype,left.resultdef.size,tt_persistent,true); addstatement(stat,iteratortmp); olditerator:=left; left:=ctemprefnode.create(iteratortmp); inserttypeconv_explicit(right,s32inttype); inserttypeconv_explicit(t1,s32inttype); newbody:=internalstatements(newbodystat); addstatement(newbodystat,cassignmentnode.create(olditerator, ctypeconvnode.create_explicit(ctemprefnode.create(iteratortmp), olditerator.resultdef))); addstatement(newbodystat,t2); addstatement(stat,cfornode.create(left,right,t1,newbody,lnf_backward in loopflags)); addstatement(stat,ctempdeletenode.create(iteratortmp)); left:=nil; right:=nil; t1:=nil; t2:=nil; result:=block end else result:=inherited pass_1; end; {***************************************************************************** SecondRaise *****************************************************************************} var current_except_loc: tlocation; function tjvmraisenode.pass_typecheck: tnode; begin Result:=inherited pass_typecheck; if codegenerror then exit; { Java exceptions must descend from java.lang.Throwable } if assigned(left) and not(left.resultdef).is_related(java_jlthrowable) then MessagePos2(left.fileinfo,type_e_incompatible_types,left.resultdef.typename,'class(JLThrowable)'); { Java exceptions cannot be raised "at" a specific location } if assigned(right) then MessagePos(right.fileinfo,parser_e_illegal_expression); end; function tjvmraisenode.pass_1: tnode; begin result:=nil; expectloc:=LOC_VOID; if assigned(left) then firstpass(left); end; procedure tjvmraisenode.pass_generate_code; begin if assigned(left) then begin secondpass(left); thlcgjvm(hlcg).a_load_loc_stack(current_asmdata.CurrAsmList,left.resultdef,left.location); end else thlcgjvm(hlcg).a_load_loc_stack(current_asmdata.CurrAsmList,java_jlthrowable,current_except_loc); current_asmdata.CurrAsmList.Concat(taicpu.op_none(a_athrow)); thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,1); end; {***************************************************************************** SecondTryExcept *****************************************************************************} var begintrylabel, endtrylabel: tasmlabel; endexceptlabel : tasmlabel; procedure tjvmtryexceptnode.pass_generate_code; var oldendexceptlabel, oldbegintrylabel, oldendtrylabel, defaultcatchlabel: tasmlabel; oldflowcontrol,tryflowcontrol, exceptflowcontrol : tflowcontrol; prev_except_loc: tlocation; begin location_reset(location,LOC_VOID,OS_NO); oldflowcontrol:=flowcontrol; flowcontrol:=[fc_inflowcontrol]; { this can be called recursivly } oldbegintrylabel:=begintrylabel; oldendtrylabel:=endtrylabel; oldendexceptlabel:=endexceptlabel; { get new labels for the control flow statements } current_asmdata.getaddrlabel(begintrylabel); current_asmdata.getaddrlabel(endtrylabel); current_asmdata.getjumplabel(endexceptlabel); { try block } { set control flow labels for the try block } hlcg.a_label(current_asmdata.CurrAsmList,begintrylabel); secondpass(left); hlcg.a_label(current_asmdata.CurrAsmList,endtrylabel); tryflowcontrol:=flowcontrol; { jump over exception handling blocks } current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart)); hlcg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel); current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd)); { set control flow labels for the except block } { and the on statements } flowcontrol:=[fc_inflowcontrol]; { on-statements } if assigned(right) then secondpass(right); { default handling except handling } if assigned(t1) then begin current_asmdata.getaddrlabel(defaultcatchlabel); current_asmdata.CurrAsmList.concat(tai_jcatch.create( 'all',begintrylabel,endtrylabel,defaultcatchlabel)); hlcg.a_label(current_asmdata.CurrAsmList,defaultcatchlabel); { here we don't have to reset flowcontrol } { the default and on flowcontrols are handled equal } { get the exception object from the stack and store it for use by the exception code (in case of an anonymous "raise") } current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart)); prev_except_loc:=current_except_loc; location_reset_ref(current_except_loc,LOC_REFERENCE,OS_ADDR,4); tg.GetLocal(current_asmdata.CurrAsmList,sizeof(pint),java_jlthrowable,current_except_loc.reference); thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,1); thlcgjvm(hlcg).a_load_stack_loc(current_asmdata.CurrAsmList,java_jlthrowable,current_except_loc); current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd)); { and generate the exception handling code } secondpass(t1); { free the temp containing the exception and invalidate } tg.UngetLocal(current_asmdata.CurrAsmList,current_except_loc.reference); current_except_loc:=prev_except_loc; exceptflowcontrol:=flowcontrol; end else exceptflowcontrol:=flowcontrol; hlcg.a_label(current_asmdata.CurrAsmList,endexceptlabel); { restore all saved labels } begintrylabel:=oldbegintrylabel; endtrylabel:=oldendtrylabel; endexceptlabel:=oldendexceptlabel; { return all used control flow statements } flowcontrol:=oldflowcontrol+(exceptflowcontrol + tryflowcontrol - [fc_inflowcontrol]); end; {***************************************************************************** SecondOn *****************************************************************************} procedure tjvmonnode.pass_generate_code; var thisonlabel : tasmlabel; oldflowcontrol : tflowcontrol; exceptvarsym : tlocalvarsym; prev_except_loc : tlocation; begin location_reset(location,LOC_VOID,OS_NO); oldflowcontrol:=flowcontrol; flowcontrol:=[fc_inflowcontrol]; current_asmdata.getjumplabel(thisonlabel); hlcg.a_label(current_asmdata.CurrAsmList,thisonlabel); if assigned(excepTSymtable) then exceptvarsym:=tlocalvarsym(excepTSymtable.SymList[0]) else internalerror(2011020402); { add exception catching information for the JVM: exception type (will have to be adjusted if/when support for catching class reference types is added), begin/end of code in which the exception can be raised, and start of this exception handling code } current_asmdata.CurrAsmList.concat(tai_jcatch.create( tobjectdef(exceptvarsym.vardef).jvm_full_typename(true), begintrylabel,endtrylabel,thisonlabel)); { Retrieve exception variable } { 1) prepare the location where we'll store it } location_reset_ref(exceptvarsym.localloc,LOC_REFERENCE,OS_ADDR,sizeof(pint)); tg.GetLocal(current_asmdata.CurrAsmList,sizeof(pint),exceptvarsym.vardef,exceptvarsym.localloc.reference); prev_except_loc:=current_except_loc; current_except_loc:=exceptvarsym.localloc; { 2) the exception variable is at the top of the evaluation stack (placed there by the JVM) -> adjust stack count, then store it } thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,1); thlcgjvm(hlcg).a_load_stack_loc(current_asmdata.CurrAsmList,exceptvarsym.vardef,current_except_loc); if assigned(right) then secondpass(right); { clear some stuff } tg.UngetLocal(current_asmdata.CurrAsmList,exceptvarsym.localloc.reference); exceptvarsym.localloc.loc:=LOC_INVALID; current_except_loc:=prev_except_loc; hlcg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel); flowcontrol:=oldflowcontrol+(flowcontrol-[fc_inflowcontrol]); { next on node } if assigned(left) then secondpass(left); end; {***************************************************************************** SecondTryFinally *****************************************************************************} procedure tjvmtryfinallynode.pass_generate_code; var begintrylabel, endtrylabel, reraiselabel, finallylabel, finallyexceptlabel, endfinallylabel, exitfinallylabel, continuefinallylabel, breakfinallylabel, oldCurrExitLabel, oldContinueLabel, oldBreakLabel : tasmlabel; oldflowcontrol,tryflowcontrol : tflowcontrol; finallycodecopy: tnode; reasonbuf, exceptreg: tregister; begin { not necessary on a garbage-collected platform } if implicitframe then internalerror(2011031803); location_reset(location,LOC_VOID,OS_NO); { check if child nodes do a break/continue/exit } oldflowcontrol:=flowcontrol; flowcontrol:=[fc_inflowcontrol]; current_asmdata.getjumplabel(finallylabel); current_asmdata.getjumplabel(endfinallylabel); current_asmdata.getjumplabel(reraiselabel); { the finally block must catch break, continue and exit } { statements } oldCurrExitLabel:=current_procinfo.CurrExitLabel; current_asmdata.getjumplabel(exitfinallylabel); current_procinfo.CurrExitLabel:=exitfinallylabel; if assigned(current_procinfo.CurrBreakLabel) then begin oldContinueLabel:=current_procinfo.CurrContinueLabel; oldBreakLabel:=current_procinfo.CurrBreakLabel; current_asmdata.getjumplabel(breakfinallylabel); current_asmdata.getjumplabel(continuefinallylabel); current_procinfo.CurrContinueLabel:=continuefinallylabel; current_procinfo.CurrBreakLabel:=breakfinallylabel; end; { allocate reg to store the reason why the finally block was entered (no exception, break, continue, exit), so we can continue to the right label afterwards. In case of an exception, we use a separate (duplicate) finally block because otherwise the JVM's bytecode verification cannot statically prove that the exception reraise code will only execute in case an exception actually happened } reasonbuf:=hlcg.getaddressregister(current_asmdata.CurrAsmList,s32inttype); { try code } begintrylabel:=nil; endtrylabel:=nil; if assigned(left) then begin current_asmdata.getaddrlabel(begintrylabel); current_asmdata.getaddrlabel(endtrylabel); hlcg.a_label(current_asmdata.CurrAsmList,begintrylabel); secondpass(left); hlcg.a_label(current_asmdata.CurrAsmList,endtrylabel); tryflowcontrol:=flowcontrol; if codegenerror then exit; { reason: no exception occurred } hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,0,reasonbuf); end else tryflowcontrol:=[fc_inflowcontrol]; { begin of the finally code } hlcg.a_label(current_asmdata.CurrAsmList,finallylabel); { finally code } flowcontrol:=[fc_inflowcontrol]; { duplicate finally code for case when exception happened } if assigned(begintrylabel) then finallycodecopy:=right.getcopy; secondpass(right); { goto is allowed if it stays inside the finally block, this is checked using the exception block number } if (flowcontrol-[fc_gotolabel])<>[fc_inflowcontrol] then CGMessage(cg_e_control_flow_outside_finally); if codegenerror then begin if assigned(begintrylabel) then finallycodecopy.free; exit; end; { don't generate line info for internal cleanup } current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart)); { the reasonbuf holds the reason why this (non-exception) finally code was executed: 0 = try code simply finished 1 = (unused) exception raised 2 = exit called 3 = break called 4 = continue called } hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,s32inttype,OC_EQ,0,reasonbuf,endfinallylabel); if fc_exit in tryflowcontrol then if ([fc_break,fc_continue]*tryflowcontrol)<>[] then hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,s32inttype,OC_EQ,2,reasonbuf,oldCurrExitLabel) else hlcg.a_jmp_always(current_asmdata.CurrAsmList,oldCurrExitLabel); if fc_break in tryflowcontrol then if fc_continue in tryflowcontrol then hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,s32inttype,OC_EQ,3,reasonbuf,oldBreakLabel) else hlcg.a_jmp_always(current_asmdata.CurrAsmList,oldBreakLabel); if fc_continue in tryflowcontrol then hlcg.a_jmp_always(current_asmdata.CurrAsmList,oldContinueLabel); { now generate the trampolines for exit/break/continue to load the reasonbuf } if fc_exit in tryflowcontrol then begin hlcg.a_label(current_asmdata.CurrAsmList,exitfinallylabel); hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,2,reasonbuf); hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallylabel); end; if fc_break in tryflowcontrol then begin hlcg.a_label(current_asmdata.CurrAsmList,breakfinallylabel); hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,3,reasonbuf); hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallylabel); end; if fc_continue in tryflowcontrol then begin hlcg.a_label(current_asmdata.CurrAsmList,continuefinallylabel); hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,4,reasonbuf); hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallylabel); end; { jump over finally-code-in-case-an-exception-happened } hlcg.a_jmp_always(current_asmdata.CurrAsmList,endfinallylabel); { generate finally code in case an exception occurred } if assigned(begintrylabel) then begin current_asmdata.getaddrlabel(finallyexceptlabel); hlcg.a_label(current_asmdata.CurrAsmList,finallyexceptlabel); { catch the exceptions } current_asmdata.CurrAsmList.concat(tai_jcatch.create( 'all',begintrylabel,endtrylabel,finallyexceptlabel)); { store the generated exception object to a temp } exceptreg:=hlcg.getaddressregister(current_asmdata.CurrAsmList,java_jlthrowable); thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,1); thlcgjvm(hlcg).a_load_stack_reg(current_asmdata.CurrAsmList,java_jlthrowable,exceptreg); { generate the finally code again } secondpass(finallycodecopy); finallycodecopy.free; { reraise the exception } thlcgjvm(hlcg).a_load_reg_stack(current_asmdata.CurrAsmList,java_jlthrowable,exceptreg); current_asmdata.CurrAsmList.Concat(taicpu.op_none(a_athrow)); thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,1); end; hlcg.a_label(current_asmdata.CurrAsmList,endfinallylabel); { end cleanup } current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd)); current_procinfo.CurrExitLabel:=oldCurrExitLabel; if assigned(current_procinfo.CurrBreakLabel) then begin current_procinfo.CurrContinueLabel:=oldContinueLabel; current_procinfo.CurrBreakLabel:=oldBreakLabel; end; flowcontrol:=oldflowcontrol+(tryflowcontrol-[fc_inflowcontrol]); end; begin cfornode:=tjvmfornode; craisenode:=tjvmraisenode; ctryexceptnode:=tjvmtryexceptnode; ctryfinallynode:=tjvmtryfinallynode; connode:=tjvmonnode; end.