* 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 -
This commit is contained in:
Jonas Maebe 2011-08-06 19:49:59 +00:00
parent f9b27806e4
commit 20c3809c3a
2 changed files with 52 additions and 1 deletions

View File

@ -1877,6 +1877,11 @@ implementation
firstpass would be called multiple times }
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 }
{ record returned via implicit pointer }
@ -1938,6 +1943,10 @@ implementation
(selftree.resultdef.typ<>classrefdef) then
begin
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);
end;
selfrestype:=selftree.resultdef;
@ -1981,6 +1990,9 @@ implementation
(methodpointer.resultdef.typ<>classrefdef)) then
begin
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);
end;
end;
@ -2013,7 +2025,10 @@ implementation
vmttree:=methodpointer.getcopy;
{ Only a typenode can be passed when it is called with <class of xx>.create }
if vmttree.nodetype=typen then
vmttree:=cloadvmtaddrnode.create(vmttree);
begin
vmttree:=cloadvmtaddrnode.create(vmttree);
tloadvmtaddrnode(vmttree).forcall:=true;
end;
end
else
begin

View File

@ -31,9 +31,17 @@ interface
type
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;
function pass_1 : tnode;override;
function pass_typecheck:tnode;override;
function docompare(p: tnode): boolean; override;
function dogetcopy: tnode; override;
end;
tloadvmtaddrnodeclass = class of tloadvmtaddrnode;
@ -190,6 +198,21 @@ implementation
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;
var
vs: tsym;
@ -229,6 +252,19 @@ implementation
else if (left.resultdef.typ=objectdef) then
tobjectdef(left.resultdef).register_maybe_created_object_type
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;