* implemented declaring and calling constructors for Java classes

o handle them like for regular classes (return a class instance,
     although this is technically not true since they don't return
     anything; will be changed in the future)
   o because of the previous point, make sure that we handle the
     "function result" properly and don't pop too many values from
     the evaluation stack when calling one constructor from another
   o added "extra_pre_call_code" method used by njvmcal to insert
     the "new" opcode to create the new class instance before
     calling a constructor
   o when a constructor does not call any other constructor (inherited
     or otherwise), automatically insert a call to the inherited
     parameterless constructor as required by the jvm standard)
   TODO: check that *if* an inherited or other constructor is called
     from another constructor, that it does so as the first statement/
     call

git-svn-id: branches/jvmbackend@18328 -
This commit is contained in:
Jonas Maebe 2011-08-20 07:46:37 +00:00
parent e2e32fbbe9
commit eb5814a868
9 changed files with 111 additions and 14 deletions

View File

@ -81,6 +81,7 @@ uses
procedure g_proc_entry(list : TAsmList;localsize : longint;nostackframe:boolean); override; procedure g_proc_entry(list : TAsmList;localsize : longint;nostackframe:boolean); override;
procedure g_proc_exit(list : TAsmList;parasize:longint;nostackframe:boolean); override; procedure g_proc_exit(list : TAsmList;parasize:longint;nostackframe:boolean); override;
procedure gen_load_return_value(list:TAsmList);override;
procedure record_generated_code_for_procdef(pd: tprocdef; code, data: TAsmList); override; procedure record_generated_code_for_procdef(pd: tprocdef; code, data: TAsmList); override;
{ JVM-specific routines } { JVM-specific routines }
@ -788,11 +789,16 @@ implementation
procedure thlcgjvm.g_proc_exit(list: TAsmList; parasize: longint; nostackframe: boolean); procedure thlcgjvm.g_proc_exit(list: TAsmList; parasize: longint; nostackframe: boolean);
var var
retdef: tdef;
opc: tasmop; opc: tasmop;
begin begin
case current_procinfo.procdef.returndef.typ of if current_procinfo.procdef.proctypeoption=potype_constructor then
retdef:=voidtype
else
retdef:=current_procinfo.procdef.returndef;
case retdef.typ of
orddef: orddef:
case torddef(current_procinfo.procdef.returndef).ordtype of case torddef(retdef).ordtype of
uvoid: uvoid:
opc:=a_return; opc:=a_return;
s64bit, s64bit,
@ -803,7 +809,7 @@ implementation
opc:=a_ireturn; opc:=a_ireturn;
end; end;
floatdef: floatdef:
case tfloatdef(current_procinfo.procdef.returndef).floattype of case tfloatdef(retdef).floattype of
s32real: s32real:
opc:=a_freturn; opc:=a_freturn;
s64real: s64real:
@ -817,6 +823,14 @@ implementation
list.concat(taicpu.op_none(opc)); list.concat(taicpu.op_none(opc));
end; end;
procedure thlcgjvm.gen_load_return_value(list: TAsmList);
begin
{ constructors don't return anything in the jvm }
if current_procinfo.procdef.proctypeoption=potype_constructor then
exit;
inherited gen_load_return_value(list);
end;
procedure thlcgjvm.record_generated_code_for_procdef(pd: tprocdef; code, data: TAsmList); procedure thlcgjvm.record_generated_code_for_procdef(pd: tprocdef; code, data: TAsmList);
begin begin
{ add something to the al_procedures list as well, because if all al_* { add something to the al_procedures list as well, because if all al_*

View File

@ -35,6 +35,7 @@ interface
tjvmcallnode = class(tcgcallnode) tjvmcallnode = class(tcgcallnode)
protected protected
procedure extra_pre_call_code; override;
procedure set_result_location(realresdef: tstoreddef); override; procedure set_result_location(realresdef: tstoreddef); override;
procedure do_release_unused_return_value;override; procedure do_release_unused_return_value;override;
procedure extra_post_call_code; override; procedure extra_post_call_code; override;
@ -45,16 +46,37 @@ implementation
uses uses
verbose,globtype, verbose,globtype,
symtype,defutil,ncal, symconst,symtype,defutil,ncal,
cgbase,cgutils,tgobj, cgbase,cgutils,tgobj,procinfo,
cpubase,aasmdata,aasmcpu, cpubase,aasmdata,aasmcpu,
hlcgobj,hlcgcpu; hlcgobj,hlcgcpu,
jvmdef;
{***************************************************************************** {*****************************************************************************
TJVMCALLNODE 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 tprocdef(procdefinition).proctypeoption<>potype_constructor then
exit;
if (methodpointer.resultdef.typ<>classrefdef) then
exit;
current_asmdata.CurrAsmList.concat(taicpu.op_sym(a_new,current_asmdata.RefAsmSymbol(tobjectdef(tprocdef(procdefinition).owner.defowner).jvm_full_typename)));
{ 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(2);
end;
procedure tjvmcallnode.set_result_location(realresdef: tstoreddef); procedure tjvmcallnode.set_result_location(realresdef: tstoreddef);
begin begin
location_reset_ref(location,LOC_REFERENCE,def_cgsize(realresdef),1); location_reset_ref(location,LOC_REFERENCE,def_cgsize(realresdef),1);
@ -64,6 +86,9 @@ implementation
procedure tjvmcallnode.do_release_unused_return_value; procedure tjvmcallnode.do_release_unused_return_value;
begin begin
if (tprocdef(procdefinition).proctypeoption=potype_constructor) and
(current_procinfo.procdef.proctypeoption=potype_constructor) then
exit;
case resultdef.size of case resultdef.size of
0: 0:
; ;
@ -92,7 +117,11 @@ implementation
realresdef:=tstoreddef(resultdef) realresdef:=tstoreddef(resultdef)
else else
realresdef:=tstoreddef(typedef); realresdef:=tstoreddef(typedef);
totalremovesize:=pushedparasize-realresdef.size; { a constructor doesn't actually return a value in the jvm }
if (tprocdef(procdefinition).proctypeoption=potype_constructor) then
totalremovesize:=pushedparasize
else
totalremovesize:=pushedparasize-realresdef.size;
{ remove parameters from internal evaluation stack counter (in case of { remove parameters from internal evaluation stack counter (in case of
e.g. no parameters and a result, it can also increase) } e.g. no parameters and a result, it can also increase) }
if totalremovesize>0 then if totalremovesize>0 then

View File

@ -1670,7 +1670,12 @@ implementation
{ push 0 as self when allocation is needed } { push 0 as self when allocation is needed }
if (methodpointer.resultdef.typ=classrefdef) or if (methodpointer.resultdef.typ=classrefdef) or
(cnf_new_call in callnodeflags) then (cnf_new_call in callnodeflags) then
selftree:=cpointerconstnode.create(0,voidpointertype) if not is_javaclass(tdef(procdefinition.owner.defowner)) then
selftree:=cpointerconstnode.create(0,voidpointertype)
else
{ special handling for Java constructors, handled in
tjvmcallnode.extra_pre_call_code }
selftree:=cnothingnode.create
else else
begin begin
if methodpointer.nodetype=typen then if methodpointer.nodetype=typen then
@ -3340,6 +3345,15 @@ implementation
doinlinesimplify(tnode(callcleanupblock)); doinlinesimplify(tnode(callcleanupblock));
end; end;
{ If a constructor calls another constructor of the same or of an
inherited class, some targets (jvm) have to generate different
entry code for the constructor. }
if (current_procinfo.procdef.proctypeoption=potype_constructor) and
(procdefinition.typ=procdef) and
(tprocdef(procdefinition).proctypeoption=potype_constructor) and
([cnf_member_call,cnf_inherited] * callnodeflags <> []) then
current_procinfo.ConstructorCallingConstructor:=true;
{ Continue with checking a normal call or generate the inlined code } { Continue with checking a normal call or generate the inlined code }
if cnf_do_inline in callnodeflags then if cnf_do_inline in callnodeflags then
result:=pass1_inline result:=pass1_inline

View File

@ -68,6 +68,7 @@ interface
} }
procedure pop_parasize(pop_size:longint);virtual; procedure pop_parasize(pop_size:longint);virtual;
procedure extra_interrupt_code;virtual; procedure extra_interrupt_code;virtual;
procedure extra_pre_call_code;virtual;
procedure extra_call_code;virtual; procedure extra_call_code;virtual;
procedure extra_post_call_code;virtual; procedure extra_post_call_code;virtual;
procedure do_syscall;virtual;abstract; procedure do_syscall;virtual;abstract;
@ -285,6 +286,11 @@ implementation
end; end;
procedure tcgcallnode.extra_pre_call_code;
begin
end;
procedure tcgcallnode.extra_call_code; procedure tcgcallnode.extra_call_code;
begin begin
end; end;
@ -650,6 +656,8 @@ implementation
not(procdefinition.has_paraloc_info in [callerside,callbothsides]) then not(procdefinition.has_paraloc_info in [callerside,callbothsides]) then
internalerror(200305264); internalerror(200305264);
extra_pre_call_code;
if assigned(callinitblock) then if assigned(callinitblock) then
secondpass(tnode(callinitblock)); secondpass(tnode(callinitblock));

View File

@ -102,7 +102,9 @@ implementation
include(current_structdef.objectoptions,oo_has_constructor); include(current_structdef.objectoptions,oo_has_constructor);
{ Set return type, class and record constructors return the { Set return type, class and record constructors return the
created instance, object constructors return boolean } created instance, object constructors return boolean }
if is_class(pd.struct) or is_record(pd.struct) then if is_class(pd.struct) or
is_record(pd.struct) or
is_javaclass(pd.struct) then
pd.returndef:=pd.struct pd.returndef:=pd.struct
else else
{$ifdef CPU64bitaddr} {$ifdef CPU64bitaddr}

View File

@ -1402,7 +1402,9 @@ implementation
begin begin
{ Set return type, class constructors return the { Set return type, class constructors return the
created instance, object constructors return boolean } created instance, object constructors return boolean }
if is_class(pd.struct) or is_record(pd.struct) then if is_class(pd.struct) or
is_record(pd.struct) or
is_javaclass(pd.struct) then
pd.returndef:=pd.struct pd.returndef:=pd.struct
else else
{$ifdef CPU64bitaddr} {$ifdef CPU64bitaddr}
@ -2377,7 +2379,7 @@ const
mutexclpo : [] mutexclpo : []
),( ),(
idtok:_OVERLOAD; idtok:_OVERLOAD;
pd_flags : [pd_implemen,pd_interface,pd_body]; pd_flags : [pd_implemen,pd_interface,pd_body,pd_javaclass,pd_intfjava];
handler : @pd_overload; handler : @pd_overload;
pocall : pocall_none; pocall : pocall_none;
pooption : [po_overload]; pooption : [po_overload];
@ -2386,7 +2388,7 @@ const
mutexclpo : [] mutexclpo : []
),( ),(
idtok:_OVERRIDE; idtok:_OVERRIDE;
pd_flags : [pd_interface,pd_object,pd_notobjintf,pd_objcclass,pd_javaclass,pd_notrecord]; pd_flags : [pd_interface,pd_object,pd_notobjintf,pd_objcclass,pd_javaclass,pd_intfjava,pd_notrecord];
handler : @pd_override; handler : @pd_override;
pocall : pocall_none; pocall : pocall_none;
pooption : [po_overridingmethod,po_virtualmethod]; pooption : [po_overridingmethod,po_virtualmethod];

View File

@ -1257,7 +1257,9 @@ implementation
else else
begin begin
p1:=ctypenode.create(ttypesym(sym).typedef); p1:=ctypenode.create(ttypesym(sym).typedef);
if (is_class(ttypesym(sym).typedef) or is_objcclass(ttypesym(sym).typedef)) and if (is_class(ttypesym(sym).typedef) or
is_objcclass(ttypesym(sym).typedef) or
is_javaclass(ttypesym(sym).typedef)) and
not(block_type in [bt_type,bt_const_type,bt_var_type]) then not(block_type in [bt_type,bt_const_type,bt_var_type]) then
p1:=cloadvmtaddrnode.create(p1); p1:=cloadvmtaddrnode.create(p1);
end; end;
@ -1553,7 +1555,8 @@ implementation
(for "TClassHelper.Something") } (for "TClassHelper.Something") }
{ class reference ? } { class reference ? }
if is_class(hdef) or if is_class(hdef) or
is_objcclass(hdef) then is_objcclass(hdef) or
is_javaclass(hdef) then
begin begin
if getaddr and (token=_POINT) then if getaddr and (token=_POINT) then
begin begin

View File

@ -114,6 +114,11 @@ unit procinfo;
{ max. of space need for parameters } { max. of space need for parameters }
maxpushedparasize : aint; maxpushedparasize : aint;
{ is this a constructor that calls another constructor on itself
(either inherited, or another constructor of the same class)?
Requires different entry code for some targets. }
ConstructorCallingConstructor: boolean;
constructor create(aparent:tprocinfo);virtual; constructor create(aparent:tprocinfo);virtual;
destructor destroy;override; destructor destroy;override;

View File

@ -292,6 +292,7 @@ implementation
var var
srsym : tsym; srsym : tsym;
para : tcallparanode; para : tcallparanode;
call : tcallnode;
newstatement : tstatementnode; newstatement : tstatementnode;
begin begin
result:=internalstatements(newstatement); result:=internalstatements(newstatement);
@ -350,6 +351,25 @@ implementation
voidpointertype), voidpointertype),
ccallnode.createintern('fpc_help_constructor',para))); ccallnode.createintern('fpc_help_constructor',para)));
end end
else
if is_javaclass(current_structdef) then
begin
if (current_procinfo.procdef.proctypeoption=potype_constructor) and
not current_procinfo.ConstructorCallingConstructor then
begin
{ call inherited constructor }
srsym:=search_struct_member(tobjectdef(current_structdef).childof,'CREATE');
if assigned(srsym) and
(srsym.typ=procsym) then
begin
call:=ccallnode.create(nil,tprocsym(srsym),srsym.owner,load_self_node,[cnf_inherited]);
exclude(tcallnode(call).callnodeflags,cnf_return_value_used);
addstatement(newstatement,call);
end
else
internalerror(2011010312);
end;
end
else else
if not is_record(current_structdef) then if not is_record(current_structdef) then
internalerror(200305103); internalerror(200305103);