Bläddra i källkod

+ 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 -

Jonas Maebe 14 år sedan
förälder
incheckning
125c0cf225

+ 1 - 0
.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

+ 11 - 6
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

+ 6 - 0
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

+ 9 - 6
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

+ 3 - 2
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;

+ 7 - 2
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)

+ 9 - 0
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);

+ 8 - 0
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%
 

+ 2 - 0
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

+ 64 - 0
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<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.
+