Browse Source

+ support for virtual constructors for the JVM platform. We simply
create a virtual class (factory) method that calls the constructor,
and then let the existing support for virtual class methods handle
everything

git-svn-id: branches/jvmbackend@18710 -

Jonas Maebe 14 years ago
parent
commit
1d0388d40a
2 changed files with 63 additions and 5 deletions
  1. 15 5
      compiler/jvm/njvmcal.pas
  2. 48 0
      compiler/pjvm.pas

+ 15 - 5
compiler/jvm/njvmcal.pas

@@ -538,26 +538,36 @@ implementation
   function tjvmcallnode.pass_1: tnode;
     var
       sym: tsym;
+      wrappername: shortstring;
     begin
       { transform procvar calls }
       if assigned(right) then
         result:=dispatch_procvar
       else
         begin
-          { replace virtual class method calls in case they may be indirect }
+          { replace virtual class method and constructor calls in case they may
+            be indirect; make sure we don't replace the callthrough to the
+            original constructor with another call to the wrapper }
           if (procdefinition.typ=procdef) and
-             ([po_classmethod,po_virtualmethod]<=procdefinition.procoptions) and
+             (current_procinfo.procdef.synthetickind<>tsk_callthrough) and
+             ((procdefinition.proctypeoption=potype_constructor) or
+              (po_classmethod in procdefinition.procoptions)) and
+             (po_virtualmethod in procdefinition.procoptions) and
              (methodpointer.nodetype<>loadvmtaddrn) then
             begin
+              wrappername:=symtableprocentry.name+'__FPCVIRTUALCLASSMETHOD__';
               sym:=
                 search_struct_member(tobjectdef(procdefinition.owner.defowner),
-                  upper(tprocdef(procdefinition).import_name^));
+                  wrappername);
               if not assigned(sym) or
                  (sym.typ<>procsym) then
                 internalerror(2011072801);
               { check whether we can simply replace the symtableprocentry, or
-                whether we have to reresolve overloads }
-              if symtableprocentry.ProcdefList.count=1 then
+                whether we have to reresolve overloads -- can never simply
+                replace in case of constructor -> class method call, because
+                constructors have a vmt parameter and class methods don't }
+              if (procdefinition.proctypeoption<>potype_constructor) and
+                 (symtableprocentry.ProcdefList.count=1) then
                 begin
                   symtableprocentry:=tprocsym(sym);
                   procdefinition:=tprocdef(symtableprocentry.ProcdefList[0]);

+ 48 - 0
compiler/pjvm.pas

@@ -545,11 +545,56 @@ implementation
       end;
 
 
+    procedure jvm_wrap_virtual_constructor(pd: tprocdef);
+      var
+        wrapperpd: tprocdef;
+      begin
+        { to avoid having to implement procvar-like support for dynamically
+          invoking constructors, call the constructors from virtual class
+          methods and replace calls to the constructors with calls to the
+          virtual class methods -> we can reuse lots of infrastructure }
+        if (po_external in pd.procoptions) or
+           (oo_is_external in pd.struct.objectoptions) then
+          exit;
+        { wrapper is part of the same symtable as the original procdef }
+        symtablestack.push(pd.owner);
+        { get a copy of the constructor }
+        wrapperpd:=tprocdef(pd.getcopyas(procdef,pc_bareproc));
+        { this one is is a class method rather than a constructor }
+        include(wrapperpd.procoptions,po_classmethod);
+        wrapperpd.proctypeoption:=potype_function;
+        wrapperpd.returndef:=tobjectdef(pd.owner.defowner);
+
+        { import/external name = name of original constructor (since
+          constructors don't have names in Java, this won't conflict with the
+          original constructor definition) }
+        stringdispose(wrapperpd.import_name);
+        wrapperpd.import_name:=stringdup(pd.procsym.realname);
+        { associate with wrapper procsym (Pascal-level name = wrapper name ->
+          in callnodes, we will have to replace the calls to virtual
+          constructors with calls to the wrappers) }
+        finish_copied_procdef(wrapperpd,pd.procsym.realname+'__fpcvirtconstrwrapper__',pd.owner,tabstractrecorddef(pd.owner.defowner));
+        { since it was a bare copy, insert the self parameter (we can't just
+          copy the vmt parameter from the constructor, that's different) }
+        insert_self_and_vmt_para(wrapperpd);
+        wrapperpd.calcparas;
+        { implementation: call through to the constructor }
+        wrapperpd.synthetickind:=tsk_callthrough;
+        wrapperpd.skpara:=pd;
+        symtablestack.pop(pd.owner);
+        { and now wrap this generated virtual static method itself as well}
+        jvm_wrap_virtual_class_method(wrapperpd);
+      end;
+
+
     procedure jvm_wrap_virtual_class_methods(obj: tobjectdef);
       var
         i: longint;
         def: tdef;
       begin
+        { new methods will be inserted while we do this, but since
+          symtable.deflist.count is evaluated at the start of the loop that
+          doesn't matter }
         for i:=0 to obj.symtable.deflist.count-1 do
           begin
             def:=tdef(obj.symtable.deflist[i]);
@@ -557,6 +602,9 @@ implementation
               continue;
             if [po_classmethod,po_virtualmethod]<=tprocdef(def).procoptions then
               jvm_wrap_virtual_class_method(tprocdef(def))
+            else if (tprocdef(def).proctypeoption=potype_constructor) and
+               (po_virtualmethod in tprocdef(def).procoptions) then
+              jvm_wrap_virtual_constructor(tprocdef(def));
           end;
       end;