fpc/compiler/i386/n386flw.pas
Jonas Maebe 4686f61002 * keep track of the temp position separately from the offset in references,
so that they can still be freed after the reference has been changed
    (e.g. in case of array indexing or record field accesses) (mantis #33628)

git-svn-id: trunk@38814 -
2018-04-22 17:03:16 +00:00

689 lines
23 KiB
ObjectPascal

{
Copyright (c) 2011 by Free Pascal development team
Generate Win32-specific exception handling code
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 n386flw;
{$i fpcdefs.inc}
interface
uses
node,nflw,ncgflw,psub;
type
ti386raisenode=class(tcgraisenode)
function pass_1 : tnode;override;
end;
ti386onnode=class(tcgonnode)
procedure pass_generate_code;override;
end;
ti386tryexceptnode=class(tcgtryexceptnode)
procedure pass_generate_code;override;
end;
ti386tryfinallynode=class(tcgtryfinallynode)
finalizepi: tcgprocinfo;
constructor create(l,r:TNode);override;
constructor create_implicit(l,r,_t1:TNode);override;
function pass_1: tnode;override;
function simplify(forinline: boolean): tnode;override;
procedure pass_generate_code;override;
end;
implementation
uses
cutils,globtype,globals,verbose,systems,
nbas,ncal,nmem,nutils,
symconst,symbase,symtable,symsym,symdef,
cgbase,cgobj,cgcpu,cgutils,tgobj,
cpubase,htypechk,
parabase,paramgr,pdecsub,pass_1,pass_2,ncgutil,cga,
aasmbase,aasmtai,aasmdata,aasmcpu,procinfo,cpupi;
var
endexceptlabel: tasmlabel;
{ ti386raisenode }
function ti386raisenode.pass_1 : tnode;
var
statements : tstatementnode;
raisenode : tcallnode;
begin
{ difference from generic code is that address stack is not popped on reraise }
if (target_info.system<>system_i386_win32) or assigned(left) then
result:=inherited pass_1
else
begin
result:=internalstatements(statements);
raisenode:=ccallnode.createintern('fpc_reraise',nil);
include(raisenode.callnodeflags,cnf_call_never_returns);
addstatement(statements,raisenode);
end;
end;
{ ti386onnode }
procedure ti386onnode.pass_generate_code;
var
oldflowcontrol : tflowcontrol;
exceptvarsym : tlocalvarsym;
begin
if (target_info.system<>system_i386_win32) then
begin
inherited pass_generate_code;
exit;
end;
location_reset(location,LOC_VOID,OS_NO);
oldflowcontrol:=flowcontrol;
flowcontrol:=[fc_inflowcontrol];
{ RTL will put exceptobject into EAX when jumping here }
cg.a_reg_alloc(current_asmdata.CurrAsmList,NR_FUNCTION_RESULT_REG);
{ Retrieve exception variable }
if assigned(excepTSymtable) then
exceptvarsym:=tlocalvarsym(excepTSymtable.SymList[0])
else
exceptvarsym:=nil;
if assigned(exceptvarsym) then
begin
exceptvarsym.localloc.loc:=LOC_REFERENCE;
exceptvarsym.localloc.size:=OS_ADDR;
tg.GetLocal(current_asmdata.CurrAsmList,sizeof(pint),voidpointertype,exceptvarsym.localloc.reference);
cg.a_load_reg_ref(current_asmdata.CurrAsmList,OS_ADDR,OS_ADDR,NR_FUNCTION_RESULT_REG,exceptvarsym.localloc.reference);
end;
cg.a_reg_dealloc(current_asmdata.CurrAsmList,NR_FUNCTION_RESULT_REG);
if assigned(right) then
secondpass(right);
{ deallocate exception symbol }
if assigned(exceptvarsym) then
begin
tg.UngetLocal(current_asmdata.CurrAsmList,exceptvarsym.localloc.reference);
exceptvarsym.localloc.loc:=LOC_INVALID;
end;
cg.g_call(current_asmdata.CurrAsmList,'FPC_DONEEXCEPTION');
cg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel);
flowcontrol:=oldflowcontrol+(flowcontrol-[fc_inflowcontrol]);
end;
{ ti386tryfinallynode }
function reset_regvars(var n: tnode; arg: pointer): foreachnoderesult;
begin
case n.nodetype of
temprefn:
make_not_regable(n,[]);
calln:
include(tprocinfo(arg).flags,pi_do_call);
end;
result:=fen_true;
end;
function copy_parasize(var n: tnode; arg: pointer): foreachnoderesult;
begin
case n.nodetype of
calln:
tcgprocinfo(arg).allocate_push_parasize(tcallnode(n).pushed_parasize);
end;
result:=fen_true;
end;
constructor ti386tryfinallynode.create(l, r: TNode);
begin
inherited create(l,r);
if (target_info.system<>system_i386_win32) or
{ Don't create child procedures for generic methods, their nested-like
behavior causes compilation errors because real nested procedures
aren't allowed for generics. Not creating them doesn't harm because
generic node tree is discarded without generating code. }
(df_generic in current_procinfo.procdef.defoptions)
then
exit;
finalizepi:=tcgprocinfo(cprocinfo.create(current_procinfo));
finalizepi.force_nested;
finalizepi.procdef:=create_finalizer_procdef;
finalizepi.entrypos:=r.fileinfo;
finalizepi.entryswitches:=r.localswitches;
finalizepi.exitpos:=current_filepos; // last_endtoken_pos?
finalizepi.exitswitches:=current_settings.localswitches;
{ Regvar optimization for symbols is suppressed when using exceptions, but
temps may be still placed into registers. This must be fixed. }
foreachnodestatic(r,@reset_regvars,finalizepi);
include(finalizepi.flags,pi_has_assembler_block);
include(finalizepi.flags,pi_do_call);
include(finalizepi.flags,pi_uses_exceptions);
end;
constructor ti386tryfinallynode.create_implicit(l, r, _t1: TNode);
begin
inherited create_implicit(l, r, _t1);
if (target_info.system<>system_i386_win32) then
exit;
{ safecall procedures can handle implicit finalization as part of "except" flow }
if implicitframe and (current_procinfo.procdef.proccalloption=pocall_safecall) then
exit;
if df_generic in current_procinfo.procdef.defoptions then
InternalError(2013012501);
finalizepi:=tcgprocinfo(cprocinfo.create(current_procinfo));
finalizepi.force_nested;
finalizepi.procdef:=create_finalizer_procdef;
finalizepi.entrypos:=current_filepos;
finalizepi.exitpos:=current_filepos; // last_endtoken_pos?
finalizepi.entryswitches:=r.localswitches;
finalizepi.exitswitches:=current_settings.localswitches;
include(finalizepi.flags,pi_has_assembler_block);
include(finalizepi.flags,pi_do_call);
include(finalizepi.flags,pi_uses_exceptions);
end;
function ti386tryfinallynode.pass_1: tnode;
var
selfsym: tparavarsym;
begin
result:=inherited pass_1;
if (target_info.system=system_i386_win32) then
begin
{ safecall method will access 'self' from except block -> make it non-regable }
if implicitframe and (current_procinfo.procdef.proccalloption=pocall_safecall) and
is_class(current_procinfo.procdef.struct) then
begin
selfsym:=tparavarsym(current_procinfo.procdef.parast.Find('self'));
if (selfsym=nil) or (selfsym.typ<>paravarsym) then
InternalError(2011123101);
selfsym.varregable:=vr_none;
end;
end;
end;
function ti386tryfinallynode.simplify(forinline: boolean): tnode;
begin
result:=inherited simplify(forinline);
if (target_info.system<>system_i386_win32) then
exit;
if (result=nil) and assigned(finalizepi) then
begin
finalizepi.code:=right;
foreachnodestatic(right,@copy_parasize,finalizepi);
right:=ccallnode.create(nil,tprocsym(finalizepi.procdef.procsym),nil,nil,[],nil);
firstpass(right);
{ For implicit frames, no actual code is available at this time,
it is added later in assembler form. So store the nested procinfo
for later use. }
if implicitframe then
begin
current_procinfo.finalize_procinfo:=finalizepi;
{ don't leave dangling pointer }
tcgprocinfo(current_procinfo).final_asmnode:=nil;
end;
end;
end;
procedure emit_scope_start(handler,data: TAsmSymbol);
var
href: treference;
hreg: tregister;
begin
hreg:=cg.getintregister(current_asmdata.CurrAsmList,OS_ADDR);
reference_reset_base(href,hreg,0,ctempposinvalid,sizeof(pint),[]);
href.segment:=NR_FS;
emit_reg_reg(A_XOR,S_L,hreg,hreg);
emit_sym(A_PUSH,S_L,data);
emit_reg(A_PUSH,S_L,NR_FRAME_POINTER_REG);
emit_sym(A_PUSH,S_L,handler);
emit_ref(A_PUSH,S_L,href);
emit_reg_ref(A_MOV,S_L,NR_ESP,href);
end;
procedure emit_scope_end;
var
href: treference;
hreg,hreg2: tregister;
begin
hreg:=cg.getintregister(current_asmdata.CurrAsmList,OS_ADDR);
hreg2:=cg.getintregister(current_asmdata.CurrAsmList,OS_ADDR);
reference_reset_base(href,hreg,0,ctempposinvalid,sizeof(pint),[]);
href.segment:=NR_FS;
emit_reg_reg(A_XOR,S_L,hreg,hreg);
emit_reg(A_POP,S_L,hreg2);
emit_const_reg(A_ADD,S_L,3*sizeof(pint),NR_ESP);
emit_reg_ref(A_MOV,S_L,hreg2,href);
end;
procedure ti386tryfinallynode.pass_generate_code;
var
finallylabel,
exceptlabel,
safecalllabel,
endfinallylabel,
exitfinallylabel,
continuefinallylabel,
breakfinallylabel,
oldCurrExitLabel,
oldContinueLabel,
oldBreakLabel : tasmlabel;
oldflowcontrol,tryflowcontrol : tflowcontrol;
is_safecall: boolean;
hreg: tregister;
begin
if (target_info.system<>system_i386_win32) then
begin
inherited pass_generate_code;
exit;
end;
location_reset(location,LOC_VOID,OS_NO);
tryflowcontrol:=[];
oldBreakLabel:=nil;
oldContinueLabel:=nil;
continuefinallylabel:=nil;
breakfinallylabel:=nil;
exceptlabel:=nil;
safecalllabel:=nil;
hreg:=NR_NO;
is_safecall:=implicitframe and (current_procinfo.procdef.proccalloption=pocall_safecall);
{ check if child nodes do a break/continue/exit }
oldflowcontrol:=flowcontrol;
flowcontrol:=[fc_inflowcontrol];
current_asmdata.getjumplabel(finallylabel);
current_asmdata.getjumplabel(endfinallylabel);
{ the finally block must catch break, continue and exit }
{ statements }
oldCurrExitLabel:=current_procinfo.CurrExitLabel;
if implicitframe then
exitfinallylabel:=finallylabel
else
current_asmdata.getjumplabel(exitfinallylabel);
current_procinfo.CurrExitLabel:=exitfinallylabel;
if assigned(current_procinfo.CurrBreakLabel) then
begin
oldContinueLabel:=current_procinfo.CurrContinueLabel;
oldBreakLabel:=current_procinfo.CurrBreakLabel;
if implicitframe then
begin
breakfinallylabel:=finallylabel;
continuefinallylabel:=finallylabel;
end
else
begin
current_asmdata.getjumplabel(breakfinallylabel);
current_asmdata.getjumplabel(continuefinallylabel);
end;
current_procinfo.CurrContinueLabel:=continuefinallylabel;
current_procinfo.CurrBreakLabel:=breakfinallylabel;
end;
{ Start of scope }
if is_safecall then
begin
with cg.rg[R_INTREGISTER] do
used_in_proc:=used_in_proc+[RS_EBX,RS_ESI,RS_EDI];
current_asmdata.getjumplabel(exceptlabel);
emit_scope_start(
current_asmdata.RefAsmSymbol('__FPC_except_safecall',AT_FUNCTION),
exceptlabel
);
end
else
emit_scope_start(
current_asmdata.RefAsmSymbol('__FPC_finally_handler',AT_FUNCTION),
current_asmdata.RefAsmSymbol(finalizepi.procdef.mangledname,AT_FUNCTION)
);
{ try code }
if assigned(left) then
begin
secondpass(left);
tryflowcontrol:=flowcontrol;
if codegenerror then
exit;
end;
{ don't generate line info for internal cleanup }
current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
cg.a_label(current_asmdata.CurrAsmList,finallylabel);
emit_scope_end;
if is_safecall then
begin
current_asmdata.getjumplabel(safecalllabel);
hreg:=cg.GetIntRegister(current_asmdata.CurrAsmList,OS_INT);
cg.a_load_const_reg(current_asmdata.CurrAsmList,OS_INT,0,hreg);
cg.a_jmp_always(current_asmdata.CurrAsmList,safecalllabel);
{ RTL handler will jump here on exception }
cg.a_label(current_asmdata.CurrAsmList,exceptlabel);
handle_safecall_exception;
cg.a_load_reg_reg(current_asmdata.CurrAsmList,OS_INT,OS_INT,NR_FUNCTION_RESULT_REG,hreg);
cg.a_label(current_asmdata.CurrAsmList,safecalllabel);
end;
{ end cleanup }
current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
{ generate finally code as a separate procedure }
{ !!! this resets flowcontrol, how to check flow away? }
if not implicitframe then
tcgprocinfo(current_procinfo).generate_exceptfilter(finalizepi);
flowcontrol:=[fc_inflowcontrol];
{ right is a call to finalizer procedure }
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
exit;
{ don't generate line info for internal cleanup }
current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
if not implicitframe then
begin
if tryflowcontrol*[fc_exit,fc_break,fc_continue]<>[] then
cg.a_jmp_always(current_asmdata.CurrAsmList,endfinallylabel);
{ do some magic for exit,break,continue in the try block }
if fc_exit in tryflowcontrol then
begin
cg.a_label(current_asmdata.CurrAsmList,exitfinallylabel);
cg.g_call(current_asmdata.CurrAsmList,'_FPC_leave');
cg.a_jmp_always(current_asmdata.CurrAsmList,oldCurrExitLabel);
end;
if fc_break in tryflowcontrol then
begin
cg.a_label(current_asmdata.CurrAsmList,breakfinallylabel);
cg.g_call(current_asmdata.CurrAsmList,'_FPC_leave');
cg.a_jmp_always(current_asmdata.CurrAsmList,oldBreakLabel);
end;
if fc_continue in tryflowcontrol then
begin
cg.a_label(current_asmdata.CurrAsmList,continuefinallylabel);
cg.g_call(current_asmdata.CurrAsmList,'_FPC_leave');
cg.a_jmp_always(current_asmdata.CurrAsmList,oldContinueLabel);
end;
end;
if is_safecall then
cg.a_load_reg_reg(current_asmdata.CurrAsmList,OS_INT,OS_INT,hreg,NR_FUNCTION_RETURN_REG);
cg.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;
{ ti386tryexceptnode }
procedure ti386tryexceptnode.pass_generate_code;
var
exceptlabel,oldendexceptlabel,
lastonlabel,
exitexceptlabel,
continueexceptlabel,
breakexceptlabel,
exittrylabel,
continuetrylabel,
breaktrylabel,
oldCurrExitLabel,
oldContinueLabel,
oldBreakLabel : tasmlabel;
onlabel,
filterlabel: tasmlabel;
oldflowcontrol,tryflowcontrol,
exceptflowcontrol : tflowcontrol;
hnode : tnode;
hlist : tasmlist;
onnodecount : tai_const;
label
errorexit;
begin
if (target_info.system<>system_i386_win32) then
begin
inherited pass_generate_code;
exit;
end;
location_reset(location,LOC_VOID,OS_NO);
exceptflowcontrol:=[];
breakexceptlabel:=nil;
continueexceptlabel:=nil;
breaktrylabel:=nil;
continuetrylabel:=nil;
oldflowcontrol:=flowcontrol;
flowcontrol:=[fc_inflowcontrol];
{ this can be called recursivly }
oldBreakLabel:=nil;
oldContinueLabel:=nil;
oldendexceptlabel:=endexceptlabel;
{ Win32 SEH unwinding does not preserve registers. Indicate that they are
going to be destroyed. }
cg.alloccpuregisters(current_asmdata.CurrAsmList,R_INTREGISTER,[RS_EAX,RS_EBX,RS_ECX,RS_EDX,RS_ESI,RS_EDI]);
cg.dealloccpuregisters(current_asmdata.CurrAsmList,R_INTREGISTER,[RS_EAX,RS_EBX,RS_ECX,RS_EDX,RS_ESI,RS_EDI]);
{ save the old labels for control flow statements }
oldCurrExitLabel:=current_procinfo.CurrExitLabel;
if assigned(current_procinfo.CurrBreakLabel) then
begin
oldContinueLabel:=current_procinfo.CurrContinueLabel;
oldBreakLabel:=current_procinfo.CurrBreakLabel;
end;
{ get new labels for the control flow statements }
current_asmdata.getjumplabel(exittrylabel);
current_asmdata.getjumplabel(exitexceptlabel);
if assigned(current_procinfo.CurrBreakLabel) then
begin
current_asmdata.getjumplabel(breaktrylabel);
current_asmdata.getjumplabel(continuetrylabel);
current_asmdata.getjumplabel(breakexceptlabel);
current_asmdata.getjumplabel(continueexceptlabel);
end;
current_asmdata.getjumplabel(exceptlabel);
current_asmdata.getjumplabel(endexceptlabel);
current_asmdata.getjumplabel(lastonlabel);
filterlabel:=nil;
{ start of scope }
if assigned(right) then
begin
current_asmdata.getaddrlabel(filterlabel);
emit_scope_start(
current_asmdata.RefAsmSymbol('__FPC_on_handler',AT_FUNCTION),
filterlabel);
end
else
emit_scope_start(
current_asmdata.RefAsmSymbol('__FPC_except_handler',AT_FUNCTION),
exceptlabel);
{ set control flow labels for the try block }
current_procinfo.CurrExitLabel:=exittrylabel;
if assigned(oldBreakLabel) then
begin
current_procinfo.CurrContinueLabel:=continuetrylabel;
current_procinfo.CurrBreakLabel:=breaktrylabel;
end;
secondpass(left);
tryflowcontrol:=flowcontrol;
if codegenerror then
goto errorexit;
emit_scope_end;
{ jump over except handlers }
cg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel);
if fc_exit in tryflowcontrol then
begin
cg.a_label(current_asmdata.CurrAsmList,exittrylabel);
emit_scope_end;
cg.a_jmp_always(current_asmdata.CurrAsmList,oldCurrExitLabel);
end;
if fc_break in tryflowcontrol then
begin
cg.a_label(current_asmdata.CurrAsmList,breaktrylabel);
emit_scope_end;
cg.a_jmp_always(current_asmdata.CurrAsmList,oldBreakLabel);
end;
if fc_continue in tryflowcontrol then
begin
cg.a_label(current_asmdata.CurrAsmList,continuetrylabel);
emit_scope_end;
cg.a_jmp_always(current_asmdata.CurrAsmList,oldContinueLabel);
end;
{ target for catch-all handler }
cg.a_label(current_asmdata.CurrAsmList,exceptlabel);
{ set control flow labels for the except block }
{ and the on statements }
current_procinfo.CurrExitLabel:=exitexceptlabel;
if assigned(oldBreakLabel) then
begin
current_procinfo.CurrContinueLabel:=continueexceptlabel;
current_procinfo.CurrBreakLabel:=breakexceptlabel;
end;
flowcontrol:=[fc_inflowcontrol];
{ on statements }
if assigned(right) then
begin
{ emit filter table to a temporary asmlist }
hlist:=TAsmList.Create;
new_section(hlist,sec_rodata,filterlabel.name,4);
cg.a_label(hlist,filterlabel);
onnodecount:=tai_const.create_32bit(0);
hlist.concat(onnodecount);
hnode:=right;
while assigned(hnode) do
begin
if hnode.nodetype<>onn then
InternalError(2011103101);
current_asmdata.getjumplabel(onlabel);
hlist.concat(tai_const.create_sym(current_asmdata.RefAsmSymbol(tonnode(hnode).excepttype.vmt_mangledname,AT_DATA)));
hlist.concat(tai_const.create_sym(onlabel));
cg.a_label(current_asmdata.CurrAsmList,onlabel);
secondpass(hnode);
inc(onnodecount.value);
hnode:=tonnode(hnode).left;
end;
{ add 'else' node to the filter list, too }
if assigned(t1) then
begin
hlist.concat(tai_const.create_32bit(-1));
hlist.concat(tai_const.create_sym(lastonlabel));
inc(onnodecount.value);
end;
{ now move filter table to permanent list all at once }
current_procinfo.aktlocaldata.concatlist(hlist);
hlist.free;
end;
cg.a_label(current_asmdata.CurrAsmList,lastonlabel);
if assigned(t1) then
begin
{ here we don't have to reset flowcontrol }
{ the default and on flowcontrols are handled equal }
secondpass(t1);
cg.g_call(current_asmdata.CurrAsmList,'FPC_DONEEXCEPTION');
if (flowcontrol*[fc_exit,fc_break,fc_continue]<>[]) then
cg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel);
end;
exceptflowcontrol:=flowcontrol;
if fc_exit in exceptflowcontrol then
begin
{ do some magic for exit in the try block }
cg.a_label(current_asmdata.CurrAsmList,exitexceptlabel);
cg.g_call(current_asmdata.CurrAsmList,'FPC_DONEEXCEPTION');
cg.a_jmp_always(current_asmdata.CurrAsmList,oldCurrExitLabel);
end;
if fc_break in exceptflowcontrol then
begin
cg.a_label(current_asmdata.CurrAsmList,breakexceptlabel);
cg.g_call(current_asmdata.CurrAsmList,'FPC_DONEEXCEPTION');
cg.a_jmp_always(current_asmdata.CurrAsmList,oldBreakLabel);
end;
if fc_continue in exceptflowcontrol then
begin
cg.a_label(current_asmdata.CurrAsmList,continueexceptlabel);
cg.g_call(current_asmdata.CurrAsmList,'FPC_DONEEXCEPTION');
cg.a_jmp_always(current_asmdata.CurrAsmList,oldContinueLabel);
end;
cg.a_label(current_asmdata.CurrAsmList,endexceptlabel);
errorexit:
{ restore all saved labels }
endexceptlabel:=oldendexceptlabel;
{ restore the control flow labels }
current_procinfo.CurrExitLabel:=oldCurrExitLabel;
if assigned(oldBreakLabel) then
begin
current_procinfo.CurrContinueLabel:=oldContinueLabel;
current_procinfo.CurrBreakLabel:=oldBreakLabel;
end;
{ return all used control flow statements }
flowcontrol:=oldflowcontrol+(exceptflowcontrol +
tryflowcontrol - [fc_inflowcontrol]);
end;
initialization
craisenode:=ti386raisenode;
connode:=ti386onnode;
ctryexceptnode:=ti386tryexceptnode;
ctryfinallynode:=ti386tryfinallynode;
end.