+ support for generics on the JVM target:

o don't try to create .class files for generic types
   o still generate all JVM-specific wrappers for generic types even though they
     won't be written out, because when specializing all the defid's have to
     match exactly
   o add synthetic routine implementations after generating the specializations,
     so that the synthetic routines for those specializations are also generated
     (we don't specialize generic versions of the synthetic generic routines
      because it's not easy or even always possible to create valid generic
      versions of synthetic routines)
   o Note: these are Pascal-style generics, not Java-style generics. The generic
     types nor their specializations are usable from Java code (specializations
     may become usable in the future)

git-svn-id: branches/jvmbackend@19047 -
This commit is contained in:
Jonas Maebe 2011-09-11 11:54:37 +00:00
parent fdab7122dd
commit 125c0cf225
10 changed files with 120 additions and 16 deletions

1
.gitattributes vendored
View File

@ -9821,6 +9821,7 @@ tests/test/jvm/tval5.pp svneol=native#text/plain
tests/test/jvm/tvalc.pp svneol=native#text/plain tests/test/jvm/tvalc.pp svneol=native#text/plain
tests/test/jvm/tvarpara.pp svneol=native#text/plain tests/test/jvm/tvarpara.pp svneol=native#text/plain
tests/test/jvm/tvirtclmeth.pp svneol=native#text/plain tests/test/jvm/tvirtclmeth.pp svneol=native#text/plain
tests/test/jvm/tw20212.pp svneol=native#text/plain
tests/test/jvm/twith.pp svneol=native#text/plain tests/test/jvm/twith.pp svneol=native#text/plain
tests/test/jvm/uenum.pp svneol=native#text/plain tests/test/jvm/uenum.pp svneol=native#text/plain
tests/test/jvm/unsupported.pp svneol=native#text/plain tests/test/jvm/unsupported.pp svneol=native#text/plain

View File

@ -624,10 +624,11 @@ implementation
{ in case of nested class: relation to parent class } { in case of nested class: relation to parent class }
if obj.owner.symtabletype in [objectsymtable,recordsymtable] then if obj.owner.symtabletype in [objectsymtable,recordsymtable] then
AsmWriteln(InnerStructDef(obj)); AsmWriteln(InnerStructDef(obj));
{ all all nested classes } { add all nested classes }
for i:=0 to obj.symtable.deflist.count-1 do for i:=0 to obj.symtable.deflist.count-1 do
if is_java_class_or_interface(tdef(obj.symtable.deflist[i])) or if (is_java_class_or_interface(tdef(obj.symtable.deflist[i])) or
(tdef(obj.symtable.deflist[i]).typ=recorddef) then (tdef(obj.symtable.deflist[i]).typ=recorddef)) and
not(df_generic in tdef(obj.symtable.deflist[i]).defoptions) then
AsmWriteln(InnerStructDef(tabstractrecorddef(obj.symtable.deflist[i]))); AsmWriteln(InnerStructDef(tabstractrecorddef(obj.symtable.deflist[i])));
end; end;
AsmLn; AsmLn;
@ -966,6 +967,7 @@ implementation
procsym: procsym:
begin begin
for j:=0 to tprocsym(sym).procdeflist.count-1 do for j:=0 to tprocsym(sym).procdeflist.count-1 do
if not(df_generic in tprocdef(tprocsym(sym).procdeflist[j]).defoptions) then
WriteSymtableVarSyms(tprocdef(tprocsym(sym).procdeflist[j]).localst); WriteSymtableVarSyms(tprocdef(tprocsym(sym).procdeflist[j]).localst);
end; end;
end; end;
@ -989,8 +991,9 @@ implementation
{ methods are also in the static/globalsymtable of the unit { methods are also in the static/globalsymtable of the unit
-> make sure they are only written for the objectdefs that -> make sure they are only written for the objectdefs that
own them } own them }
if not(st.symtabletype in [staticsymtable,globalsymtable]) or if (not(st.symtabletype in [staticsymtable,globalsymtable]) or
(def.owner=st) then (def.owner=st)) and
not(df_generic in def.defoptions) then
begin begin
WriteProcDef(tprocdef(def)); WriteProcDef(tprocdef(def));
if assigned(tprocdef(def).localst) then if assigned(tprocdef(def).localst) then
@ -1014,6 +1017,8 @@ implementation
for i:=0 to st.DefList.Count-1 do for i:=0 to st.DefList.Count-1 do
begin begin
def:=tdef(st.DefList[i]); def:=tdef(st.DefList[i]);
if df_generic in def.defoptions then
continue;
case def.typ of case def.typ of
objectdef: objectdef:
if not(oo_is_external in tobjectdef(def).objectoptions) then if not(oo_is_external in tobjectdef(def).objectoptions) then

View File

@ -139,6 +139,8 @@ implementation
topowner:=topowner.owner.defowner; topowner:=topowner.owner.defowner;
{ create procdef } { create procdef }
pd:=tprocdef.create(topowner.owner.symtablelevel+1); pd:=tprocdef.create(topowner.owner.symtablelevel+1);
if df_generic in obj.defoptions then
include(pd.defoptions,df_generic);
{ method of this objectdef } { method of this objectdef }
pd.struct:=obj; pd.struct:=obj;
{ associated procsym } { associated procsym }
@ -474,6 +476,8 @@ implementation
pvclass:=tobjectdef.create(odt_javaclass,'$'+current_module.realmodulename^+'$'+name+'$InternProcvar$'+tostr(def.defid),java_procvarbase); pvclass:=tobjectdef.create(odt_javaclass,'$'+current_module.realmodulename^+'$'+name+'$InternProcvar$'+tostr(def.defid),java_procvarbase);
tprocvardef(def).classdef:=pvclass; tprocvardef(def).classdef:=pvclass;
include(pvclass.objectoptions,oo_is_sealed); include(pvclass.objectoptions,oo_is_sealed);
if df_generic in def.defoptions then
include(pvclass.defoptions,df_generic);
{ associate typesym } { associate typesym }
pvclass.symtable.insert(ttypesym.create('__FPC_TProcVarClassAlias',pvclass)); pvclass.symtable.insert(ttypesym.create('__FPC_TProcVarClassAlias',pvclass));
{ set external name to match procvar type name } { set external name to match procvar type name }
@ -825,6 +829,8 @@ implementation
{ create procdef } { create procdef }
pd:=tprocdef.create(normal_function_level); pd:=tprocdef.create(normal_function_level);
if df_generic in obj.defoptions then
include(pd.defoptions,df_generic);
{ construct procsym name (unique for this access; reusing the same { construct procsym name (unique for this access; reusing the same
helper for multiple accesses to the same field is hard because the helper for multiple accesses to the same field is hard because the

View File

@ -936,15 +936,16 @@ implementation
init_procinfo.parse_body; init_procinfo.parse_body;
{ save file pos for debuginfo } { save file pos for debuginfo }
current_module.mainfilepos:=init_procinfo.entrypos; current_module.mainfilepos:=init_procinfo.entrypos;
{ add implementations for synthetic method declarations added by
the compiler }
add_synthetic_method_implementations(current_module.globalsymtable);
add_synthetic_method_implementations(current_module.localsymtable);
end; end;
{ Generate specializations of objectdefs methods } { Generate specializations of objectdefs methods }
generate_specialization_procs; generate_specialization_procs;
{ add implementations for synthetic method declarations added by
the compiler }
add_synthetic_method_implementations(current_module.globalsymtable);
add_synthetic_method_implementations(current_module.localsymtable);
{ if the unit contains ansi/widestrings, initialization and { if the unit contains ansi/widestrings, initialization and
finalization code must be forced } finalization code must be forced }
force_init_final:=tglobalsymtable(current_module.globalsymtable).needs_init_final or force_init_final:=tglobalsymtable(current_module.globalsymtable).needs_init_final or
@ -1874,11 +1875,13 @@ implementation
{ save file pos for debuginfo } { save file pos for debuginfo }
current_module.mainfilepos:=main_procinfo.entrypos; current_module.mainfilepos:=main_procinfo.entrypos;
add_synthetic_method_implementations(current_module.localsymtable);
{ Generate specializations of objectdefs methods } { Generate specializations of objectdefs methods }
generate_specialization_procs; generate_specialization_procs;
{ add implementations for synthetic method declarations added by
the compiler }
add_synthetic_method_implementations(current_module.localsymtable);
{ should we force unit initialization? } { should we force unit initialization? }
force_init_final:=tstaticsymtable(current_module.localsymtable).needs_init_final; force_init_final:=tstaticsymtable(current_module.localsymtable).needs_init_final;
if force_init_final or cnodeutils.force_init then if force_init_final or cnodeutils.force_init then

View File

@ -2066,7 +2066,7 @@ implementation
{ Setup symtablestack a definition time } { Setup symtablestack a definition time }
specobj:=tabstractrecorddef(ttypesym(p).typedef); specobj:=tabstractrecorddef(ttypesym(p).typedef);
if not (is_class_or_object(specobj) or is_record(specobj)) then if not (is_class_or_object(specobj) or is_record(specobj) or is_javaclass(specobj)) then
exit; exit;
oldsymtablestack:=symtablestack; oldsymtablestack:=symtablestack;
@ -2110,7 +2110,8 @@ implementation
read_proc_body(nil,tprocdef(hp)); read_proc_body(nil,tprocdef(hp));
current_filepos:=oldcurrent_filepos; current_filepos:=oldcurrent_filepos;
end end
else { synthetic routines will be implemented afterwards }
else if tprocdef(hp).synthetickind=tsk_none then
MessagePos1(tprocdef(hp).fileinfo,sym_e_forward_not_resolved,tprocdef(hp).fullprocname(false)); MessagePos1(tprocdef(hp).fileinfo,sym_e_forward_not_resolved,tprocdef(hp).fullprocname(false));
end; end;
end; end;

View File

@ -699,8 +699,10 @@ implementation
if jvmimplicitpointertype(procvar.returndef) then if jvmimplicitpointertype(procvar.returndef) then
str:=str+'type __FPC_returnptrtype = ^'+procvar.returndef.typename+';'; str:=str+'type __FPC_returnptrtype = ^'+procvar.returndef.typename+';';
str:=str+'begin '; str:=str+'begin ';
{ result handling } { result handling (skip for generic definitions, we'll generate a new
if not is_void(procvar.returndef) then version for the specialized definition) ) }
if not is_void(procvar.returndef) and
(procvar.returndef.typ<>undefineddef) then
begin begin
str:=str+'invoke:='; str:=str+'invoke:=';
if procvar.returndef.typ in [orddef,floatdef] then if procvar.returndef.typ in [orddef,floatdef] then
@ -915,6 +917,9 @@ implementation
stname: string; stname: string;
i: longint; i: longint;
begin begin
{ add generic flag if required }
if df_generic in newstruct.defoptions then
include(pd.defoptions,df_generic);
{ associate the procdef with a procsym in the owner } { associate the procdef with a procsym in the owner }
if not(pd.proctypeoption in [potype_class_constructor,potype_class_destructor]) then if not(pd.proctypeoption in [potype_class_constructor,potype_class_destructor]) then
stname:=upper(realname) stname:=upper(realname)

View File

@ -3211,6 +3211,11 @@ implementation
else else
internalerror(2011032601); internalerror(2011032601);
{ in case of specializations, add some extras to prevent name conflicts
with nested classes }
if df_specialization in defoptions then
result:='$'+result+'$specialization$';
st:=owner; st:=owner;
while assigned(st) and while assigned(st) and
(st.symtabletype in [objectsymtable,recordsymtable,localsymtable]) do (st.symtabletype in [objectsymtable,recordsymtable,localsymtable]) do
@ -3758,6 +3763,10 @@ implementation
begin begin
// ignore, reuse original constym. Should also be duplicated // ignore, reuse original constym. Should also be duplicated
// be safe though // be safe though
end;
symconst.typesym:
begin
// reuse original, part of generic declaration
end end
else else
internalerror(201160604); internalerror(201160604);

View File

@ -208,4 +208,12 @@ ppcjvm -O2 -g -B tstring9
if %errorlevel% neq 0 exit /b %errorlevel% if %errorlevel% neq 0 exit /b %errorlevel%
java -Dfile.encoding=UTF-8 -cp ..\..\..\rtl\units\jvm-java;. tstring9 java -Dfile.encoding=UTF-8 -cp ..\..\..\rtl\units\jvm-java;. tstring9
if %errorlevel% neq 0 exit /b %errorlevel% if %errorlevel% neq 0 exit /b %errorlevel%
ppcjvm -O2 -g -B tstr
if %errorlevel% neq 0 exit /b %errorlevel%
java -Dfile.encoding=UTF-8 -cp ..\..\..\rtl\units\jvm-java;. tstr
if %errorlevel% neq 0 exit /b %errorlevel%
ppcjvm -O2 -g -B tw20212
if %errorlevel% neq 0 exit /b %errorlevel%
java -Dfile.encoding=UTF-8 -cp ..\..\..\rtl\units\jvm-java;. tw20212
if %errorlevel% neq 0 exit /b %errorlevel%

View File

@ -116,3 +116,5 @@ $PPC -O2 -g -B tstring9
java -Dfile.encoding=UTF-8 -cp ../../../rtl/units/jvm-java:. tstring9 java -Dfile.encoding=UTF-8 -cp ../../../rtl/units/jvm-java:. tstring9
$PPC -O2 -g -B tstr $PPC -O2 -g -B tstr
java -Dfile.encoding=UTF-8 -cp ../../../rtl/units/jvm-java:. tstr java -Dfile.encoding=UTF-8 -cp ../../../rtl/units/jvm-java:. tstr
$PPC -O2 -g -B tw20212
java -Dfile.encoding=UTF-8 -cp ../../../rtl/units/jvm-java:. tw20212

64
tests/test/jvm/tw20212.pp Normal file
View File

@ -0,0 +1,64 @@
program tw20212;
{$mode objfpc}{$H+}
{$ifdef cpujvm}
uses
jdk15;
{$macro on}
{$define writeln:=JLSystem.fout.println}
{$endif}
type
generic TLinkedList<T> = class
private
FData: T;
FNext: TLinkedList;
public
property Data: T read FData write FData;
property Next: TLinkedList read FNext write FNext;
constructor Create(const AData: T);
procedure Append(const AData: T);
end;
constructor TLinkedList.Create(const AData: T);
begin
FData := AData;
FNext := nil;
end;
procedure TLinkedList.Append(const AData: T);
var
tmp: TLinkedList;
begin
tmp:=FNext;
while Assigned(tmp) and Assigned(tmp.Next) do
tmp := tmp.Next;
if Assigned(tmp) then
tmp.Next := TLinkedList.Create(AData)
else
FNext := TLinkedList.Create(AData);
end;
type
TIntegerLinkedList = specialize TLinkedList<Integer>;
var
L, it: TIntegerLinkedList;
begin
L := TIntegerLinkedList.Create(1);
L.Append(1);
L.Append(2);
L.Append(3);
L.Append(5);
L.Append(8);
L.Append(11);
it:=l;
while assigned(it) do
begin
writeln(it.data);
it:=it.next;
end;
end.