Browse Source

* when a constant Objective-C class reference is used for anything but
the methodpointer of a call, transform it into a call to the classclass
method because otherwise it can be used before the first call to any
method of the class (such as in the packages/cocoaint/src/IvarSize test)
and this can result in crashes

git-svn-id: trunk@18124 -

Jonas Maebe 14 years ago
parent
commit
20c3809c3a
2 changed files with 52 additions and 1 deletions
  1. 16 1
      compiler/ncal.pas
  2. 36 0
      compiler/nmem.pas

+ 16 - 1
compiler/ncal.pas

@@ -1877,6 +1877,11 @@ implementation
           firstpass would be called multiple times }
           firstpass would be called multiple times }
         include(callnodeflags,cnf_objc_processed);
         include(callnodeflags,cnf_objc_processed);
 
 
+        { make sure the methodpointer doesn't get translated into a call
+          as well (endless loop) }
+        if methodpointer.nodetype=loadvmtaddrn then
+          tloadvmtaddrnode(methodpointer).forcall:=true;
+
         { A) set the appropriate objc_msgSend* variant to call }
         { A) set the appropriate objc_msgSend* variant to call }
 
 
         { record returned via implicit pointer }
         { record returned via implicit pointer }
@@ -1938,6 +1943,10 @@ implementation
                 (selftree.resultdef.typ<>classrefdef) then
                 (selftree.resultdef.typ<>classrefdef) then
                begin
                begin
                  selftree:=cloadvmtaddrnode.create(selftree);
                  selftree:=cloadvmtaddrnode.create(selftree);
+                 { since we're in a class method of the current class, its
+                   information has already been initialized (and that of all of
+                   its parent classes too) }
+                 tloadvmtaddrnode(selftree).forcall:=true;
                  typecheckpass(selftree);
                  typecheckpass(selftree);
                end;
                end;
              selfrestype:=selftree.resultdef;
              selfrestype:=selftree.resultdef;
@@ -1981,6 +1990,9 @@ implementation
                 (methodpointer.resultdef.typ<>classrefdef)) then
                 (methodpointer.resultdef.typ<>classrefdef)) then
               begin
               begin
                 methodpointer:=cloadvmtaddrnode.create(methodpointer);
                 methodpointer:=cloadvmtaddrnode.create(methodpointer);
+                { no need to obtain the class ref by calling class(), sending
+                  this message will initialize it if necessary }
+                tloadvmtaddrnode(methodpointer).forcall:=true;
                 firstpass(methodpointer);
                 firstpass(methodpointer);
               end;
               end;
           end;
           end;
@@ -2013,7 +2025,10 @@ implementation
                 vmttree:=methodpointer.getcopy;
                 vmttree:=methodpointer.getcopy;
                 { Only a typenode can be passed when it is called with <class of xx>.create }
                 { Only a typenode can be passed when it is called with <class of xx>.create }
                 if vmttree.nodetype=typen then
                 if vmttree.nodetype=typen then
-                  vmttree:=cloadvmtaddrnode.create(vmttree);
+                  begin
+                    vmttree:=cloadvmtaddrnode.create(vmttree);
+                    tloadvmtaddrnode(vmttree).forcall:=true;
+                  end;
               end
               end
             else
             else
               begin
               begin

+ 36 - 0
compiler/nmem.pas

@@ -31,9 +31,17 @@ interface
 
 
     type
     type
        tloadvmtaddrnode = class(tunarynode)
        tloadvmtaddrnode = class(tunarynode)
+          { unless this is for a call, we have to send the "class" message to
+            the objctype because the type information only gets initialized
+            after the first message has been sent -> crash if you pass an
+            uninitialized type to e.g. class_getInstanceSize() or so. No need
+            to save to/restore from ppu. }
+          forcall: boolean;
           constructor create(l : tnode);virtual;
           constructor create(l : tnode);virtual;
           function pass_1 : tnode;override;
           function pass_1 : tnode;override;
           function pass_typecheck:tnode;override;
           function pass_typecheck:tnode;override;
+          function docompare(p: tnode): boolean; override;
+          function dogetcopy: tnode; override;
        end;
        end;
        tloadvmtaddrnodeclass = class of tloadvmtaddrnode;
        tloadvmtaddrnodeclass = class of tloadvmtaddrnode;
 
 
@@ -190,6 +198,21 @@ implementation
       end;
       end;
 
 
 
 
+    function tloadvmtaddrnode.docompare(p: tnode): boolean;
+      begin
+        result:=inherited docompare(p);
+        if result then
+          result:=forcall=tloadvmtaddrnode(p).forcall;
+      end;
+
+
+    function tloadvmtaddrnode.dogetcopy: tnode;
+      begin
+        result:=inherited dogetcopy;
+        tloadvmtaddrnode(result).forcall:=forcall;
+      end;
+
+
     function tloadvmtaddrnode.pass_1 : tnode;
     function tloadvmtaddrnode.pass_1 : tnode;
       var
       var
         vs: tsym;
         vs: tsym;
@@ -229,6 +252,19 @@ implementation
                else if (left.resultdef.typ=objectdef) then
                else if (left.resultdef.typ=objectdef) then
                  tobjectdef(left.resultdef).register_maybe_created_object_type
                  tobjectdef(left.resultdef).register_maybe_created_object_type
              end
              end
+           end
+         else if is_objcclass(left.resultdef) and
+              not(forcall) then
+           begin
+             { call "class" method (= "classclass" in FPC), because otherwise
+               we may use the class information before it has been
+               initialized }
+             vs:=search_struct_member(tobjectdef(left.resultdef),'CLASSCLASS');
+             if not assigned(vs) or
+                (vs.typ<>procsym) then
+               internalerror(2011080601);
+             { can't reuse "self", because it will be freed when we return }
+             result:=ccallnode.create(nil,tprocsym(vs),vs.owner,self.getcopy,[]);
            end;
            end;
       end;
       end;