From 125c0cf2258ddb84969fe11fd860331d8fec6917 Mon Sep 17 00:00:00 2001 From: Jonas Maebe Date: Sun, 11 Sep 2011 11:54:37 +0000 Subject: [PATCH] + 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 - --- .gitattributes | 1 + compiler/agjasmin.pas | 17 ++++++---- compiler/jvm/pjvm.pas | 6 ++++ compiler/pmodules.pas | 15 +++++---- compiler/psub.pas | 5 +-- compiler/symcreat.pas | 9 ++++-- compiler/symdef.pas | 9 ++++++ tests/test/jvm/testall.bat | 8 +++++ tests/test/jvm/testall.sh | 2 ++ tests/test/jvm/tw20212.pp | 64 ++++++++++++++++++++++++++++++++++++++ 10 files changed, 120 insertions(+), 16 deletions(-) create mode 100644 tests/test/jvm/tw20212.pp diff --git a/.gitattributes b/.gitattributes index 5ef8378ddb..6990eab7bc 100644 --- a/.gitattributes +++ b/.gitattributes @@ -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/tvarpara.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/uenum.pp svneol=native#text/plain tests/test/jvm/unsupported.pp svneol=native#text/plain diff --git a/compiler/agjasmin.pas b/compiler/agjasmin.pas index df5ed879bf..a07c26f6af 100644 --- a/compiler/agjasmin.pas +++ b/compiler/agjasmin.pas @@ -624,10 +624,11 @@ implementation { in case of nested class: relation to parent class } if obj.owner.symtabletype in [objectsymtable,recordsymtable] then AsmWriteln(InnerStructDef(obj)); - { all all nested classes } + { add all nested classes } for i:=0 to obj.symtable.deflist.count-1 do - if is_java_class_or_interface(tdef(obj.symtable.deflist[i])) or - (tdef(obj.symtable.deflist[i]).typ=recorddef) then + if (is_java_class_or_interface(tdef(obj.symtable.deflist[i])) or + (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]))); end; AsmLn; @@ -966,7 +967,8 @@ implementation procsym: begin for j:=0 to tprocsym(sym).procdeflist.count-1 do - WriteSymtableVarSyms(tprocdef(tprocsym(sym).procdeflist[j]).localst); + if not(df_generic in tprocdef(tprocsym(sym).procdeflist[j]).defoptions) then + WriteSymtableVarSyms(tprocdef(tprocsym(sym).procdeflist[j]).localst); end; end; end; @@ -989,8 +991,9 @@ implementation { methods are also in the static/globalsymtable of the unit -> make sure they are only written for the objectdefs that own them } - if not(st.symtabletype in [staticsymtable,globalsymtable]) or - (def.owner=st) then + if (not(st.symtabletype in [staticsymtable,globalsymtable]) or + (def.owner=st)) and + not(df_generic in def.defoptions) then begin WriteProcDef(tprocdef(def)); if assigned(tprocdef(def).localst) then @@ -1014,6 +1017,8 @@ implementation for i:=0 to st.DefList.Count-1 do begin def:=tdef(st.DefList[i]); + if df_generic in def.defoptions then + continue; case def.typ of objectdef: if not(oo_is_external in tobjectdef(def).objectoptions) then diff --git a/compiler/jvm/pjvm.pas b/compiler/jvm/pjvm.pas index 1592cb5be8..b2449b1a4f 100644 --- a/compiler/jvm/pjvm.pas +++ b/compiler/jvm/pjvm.pas @@ -139,6 +139,8 @@ implementation topowner:=topowner.owner.defowner; { create procdef } pd:=tprocdef.create(topowner.owner.symtablelevel+1); + if df_generic in obj.defoptions then + include(pd.defoptions,df_generic); { method of this objectdef } pd.struct:=obj; { associated procsym } @@ -474,6 +476,8 @@ implementation pvclass:=tobjectdef.create(odt_javaclass,'$'+current_module.realmodulename^+'$'+name+'$InternProcvar$'+tostr(def.defid),java_procvarbase); tprocvardef(def).classdef:=pvclass; include(pvclass.objectoptions,oo_is_sealed); + if df_generic in def.defoptions then + include(pvclass.defoptions,df_generic); { associate typesym } pvclass.symtable.insert(ttypesym.create('__FPC_TProcVarClassAlias',pvclass)); { set external name to match procvar type name } @@ -825,6 +829,8 @@ implementation { create procdef } 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 helper for multiple accesses to the same field is hard because the diff --git a/compiler/pmodules.pas b/compiler/pmodules.pas index c5a9828edd..69095efbfc 100644 --- a/compiler/pmodules.pas +++ b/compiler/pmodules.pas @@ -936,15 +936,16 @@ implementation init_procinfo.parse_body; { save file pos for debuginfo } 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; { Generate specializations of objectdefs methods } 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 finalization code must be forced } force_init_final:=tglobalsymtable(current_module.globalsymtable).needs_init_final or @@ -1874,11 +1875,13 @@ implementation { save file pos for debuginfo } current_module.mainfilepos:=main_procinfo.entrypos; - add_synthetic_method_implementations(current_module.localsymtable); - { Generate specializations of objectdefs methods } 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? } force_init_final:=tstaticsymtable(current_module.localsymtable).needs_init_final; if force_init_final or cnodeutils.force_init then diff --git a/compiler/psub.pas b/compiler/psub.pas index 69c2acfa22..cfed9f32f0 100644 --- a/compiler/psub.pas +++ b/compiler/psub.pas @@ -2066,7 +2066,7 @@ implementation { Setup symtablestack a definition time } 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; oldsymtablestack:=symtablestack; @@ -2110,7 +2110,8 @@ implementation read_proc_body(nil,tprocdef(hp)); current_filepos:=oldcurrent_filepos; 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)); end; end; diff --git a/compiler/symcreat.pas b/compiler/symcreat.pas index 95c0104ed0..1985fa417c 100644 --- a/compiler/symcreat.pas +++ b/compiler/symcreat.pas @@ -699,8 +699,10 @@ implementation if jvmimplicitpointertype(procvar.returndef) then str:=str+'type __FPC_returnptrtype = ^'+procvar.returndef.typename+';'; str:=str+'begin '; - { result handling } - if not is_void(procvar.returndef) then + { result handling (skip for generic definitions, we'll generate a new + version for the specialized definition) ) } + if not is_void(procvar.returndef) and + (procvar.returndef.typ<>undefineddef) then begin str:=str+'invoke:='; if procvar.returndef.typ in [orddef,floatdef] then @@ -915,6 +917,9 @@ implementation stname: string; i: longint; 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 } if not(pd.proctypeoption in [potype_class_constructor,potype_class_destructor]) then stname:=upper(realname) diff --git a/compiler/symdef.pas b/compiler/symdef.pas index 453d6c68b8..b4c8fc147b 100644 --- a/compiler/symdef.pas +++ b/compiler/symdef.pas @@ -3211,6 +3211,11 @@ implementation else 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; while assigned(st) and (st.symtabletype in [objectsymtable,recordsymtable,localsymtable]) do @@ -3758,6 +3763,10 @@ implementation begin // ignore, reuse original constym. Should also be duplicated // be safe though + end; + symconst.typesym: + begin + // reuse original, part of generic declaration end else internalerror(201160604); diff --git a/tests/test/jvm/testall.bat b/tests/test/jvm/testall.bat index 6af12b0688..c9cf051a79 100644 --- a/tests/test/jvm/testall.bat +++ b/tests/test/jvm/testall.bat @@ -208,4 +208,12 @@ ppcjvm -O2 -g -B tstring9 if %errorlevel% neq 0 exit /b %errorlevel% java -Dfile.encoding=UTF-8 -cp ..\..\..\rtl\units\jvm-java;. tstring9 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% diff --git a/tests/test/jvm/testall.sh b/tests/test/jvm/testall.sh index 2165f3782b..8c3b928841 100755 --- a/tests/test/jvm/testall.sh +++ b/tests/test/jvm/testall.sh @@ -116,3 +116,5 @@ $PPC -O2 -g -B tstring9 java -Dfile.encoding=UTF-8 -cp ../../../rtl/units/jvm-java:. tstring9 $PPC -O2 -g -B 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 diff --git a/tests/test/jvm/tw20212.pp b/tests/test/jvm/tw20212.pp new file mode 100644 index 0000000000..1fa67ca924 --- /dev/null +++ b/tests/test/jvm/tw20212.pp @@ -0,0 +1,64 @@ +program tw20212; + +{$mode objfpc}{$H+} + +{$ifdef cpujvm} +uses + jdk15; + +{$macro on} +{$define writeln:=JLSystem.fout.println} +{$endif} + + +type + generic TLinkedList = 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; +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. +