mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-22 00:29:33 +02:00
* integrated the transformation of an Objective-C method call into a call
to objc_msgSend* into the callnode. This allows reusing the current call node rather than having to create a new one, and is in particular necessary because even though the objc_msgSend* functions are declared as varargs, you're supposed to typecast them to the function type describing the method before calling them (so they should *not* use varargs calling conventions!) * for the above, a field called fobjcforcedprocname has been added to the callnode, which can be set to a string that will be used as the (mangled) name of the function to call instead of the mangled name of the procsym -> fixes calling obj-c methods with floating point arguments on ppc git-svn-id: branches/objc@13783 -
This commit is contained in:
parent
97ba8de56c
commit
3660bf7f98
@ -46,7 +46,8 @@ interface
|
||||
cnf_dispose_call,
|
||||
cnf_member_call, { called with implicit methodpointer tree }
|
||||
cnf_uses_varargs, { varargs are used in the declaration }
|
||||
cnf_create_failed { exception thrown in constructor -> don't call beforedestruction }
|
||||
cnf_create_failed, { exception thrown in constructor -> don't call beforedestruction }
|
||||
cnf_objc_processed { the procedure name has been set to the appropriate objc_msgSend* variant -> don't process again }
|
||||
);
|
||||
tcallnodeflags = set of tcallnodeflag;
|
||||
|
||||
@ -73,7 +74,8 @@ interface
|
||||
procedure check_inlining;
|
||||
function pass1_normal:tnode;
|
||||
procedure register_created_object_types;
|
||||
|
||||
protected
|
||||
procedure objc_convert_to_message_send;virtual;
|
||||
|
||||
private
|
||||
{ inlining support }
|
||||
@ -87,6 +89,10 @@ interface
|
||||
function pass1_inline:tnode;
|
||||
protected
|
||||
pushedparasize : longint;
|
||||
{ Objective-C support: force the call node to call the routine with
|
||||
this name rather than the name of symtableprocentry (don't store
|
||||
to ppu, is set while processing the node) }
|
||||
fobjcforcedprocname: pshortstring;
|
||||
public
|
||||
{ the symbol containing the definition of the procedure }
|
||||
{ to call }
|
||||
@ -150,6 +156,8 @@ interface
|
||||
{ checks if there are any parameters which end up at the stack, i.e.
|
||||
which have LOC_REFERENCE and set pi_has_stackparameter if this applies }
|
||||
procedure check_stack_parameters;
|
||||
{ force the name of the to-be-called routine to a particular string,
|
||||
used for Objective-C message sending. }
|
||||
property parameters : tnode read left write left;
|
||||
private
|
||||
AbstractMethodsList : TFPHashList;
|
||||
@ -211,6 +219,7 @@ implementation
|
||||
symconst,defutil,defcmp,
|
||||
htypechk,pass_1,
|
||||
ncnv,nld,ninl,nadd,ncon,nmem,nset,nobjc,
|
||||
objcutil,
|
||||
procinfo,cpuinfo,
|
||||
cgbase,
|
||||
wpobase
|
||||
@ -996,6 +1005,7 @@ implementation
|
||||
funcretnode.free;
|
||||
if assigned(varargsparas) then
|
||||
varargsparas.free;
|
||||
stringdispose(fobjcforcedprocname);
|
||||
inherited destroy;
|
||||
end;
|
||||
|
||||
@ -1275,7 +1285,7 @@ implementation
|
||||
(hp.nodetype=typeconvn) and
|
||||
(ttypeconvnode(hp).convtype=tc_equal) do
|
||||
hp:=tunarynode(hp).left;
|
||||
result:=(hp.nodetype in [typen,loadvmtaddrn,loadn,temprefn,arrayconstructorn]);
|
||||
result:=(hp.nodetype in [typen,loadvmtaddrn,loadn,temprefn,arrayconstructorn,addrn]);
|
||||
if result and
|
||||
not(may_be_in_reg) then
|
||||
case hp.nodetype of
|
||||
@ -1499,12 +1509,17 @@ implementation
|
||||
selftree:=nil;
|
||||
|
||||
{ When methodpointer was a callnode we must load it first into a
|
||||
temp to prevent the processing callnode twice }
|
||||
temp to prevent processing the callnode twice }
|
||||
if (methodpointer.nodetype=calln) then
|
||||
internalerror(200405121);
|
||||
|
||||
{ Objective-C: objc_convert_to_message_send() already did all necessary
|
||||
transformation on the methodpointer }
|
||||
if (procdefinition.typ=procdef) and
|
||||
(po_objc in tprocdef(procdefinition).procoptions) then
|
||||
selftree:=methodpointer.getcopy
|
||||
{ inherited }
|
||||
if (cnf_inherited in callnodeflags) then
|
||||
else if (cnf_inherited in callnodeflags) then
|
||||
begin
|
||||
selftree:=load_self_node;
|
||||
{ we can call an inherited class static/method from a regular method
|
||||
@ -1670,6 +1685,146 @@ implementation
|
||||
end;
|
||||
|
||||
|
||||
procedure tcallnode.objc_convert_to_message_send;
|
||||
var
|
||||
block,
|
||||
selftree : tnode;
|
||||
statements : tstatementnode;
|
||||
field : tfieldvarsym;
|
||||
temp : ttempcreatenode;
|
||||
selfrestype,
|
||||
objcsupertype : tdef;
|
||||
srsym : tsym;
|
||||
srsymtable : tsymtable;
|
||||
msgsendname : string;
|
||||
begin
|
||||
{ typecheck pass must already have run on the call node,
|
||||
because pass1 calls this method
|
||||
}
|
||||
|
||||
{ default behaviour: call objc_msgSend and friends;
|
||||
64 bit targets for Mac OS X can override this as they
|
||||
can call messages via an indirect function call similar to
|
||||
dynamically linked functions, ARM maybe as well (not checked)
|
||||
|
||||
Which variant of objc_msgSend is used depends on the
|
||||
result type, and on whether or not it's an inherited call.
|
||||
}
|
||||
|
||||
{ make sure we don't perform this transformation twice in case
|
||||
firstpass would be called multiple times }
|
||||
include(callnodeflags,cnf_objc_processed);
|
||||
|
||||
{ A) set the appropriate objc_msgSend* variant to call }
|
||||
|
||||
{ record returned via implicit pointer }
|
||||
if paramanager.ret_in_param(resultdef,procdefinition.proccalloption) then
|
||||
begin
|
||||
if not(cnf_inherited in callnodeflags) then
|
||||
msgsendname:='OBJC_MSGSEND_STRET'
|
||||
{$if defined(onlymacosx10_6) or defined(arm) }
|
||||
else if (target_info.system in system_objc_nfabi) then
|
||||
msgsendname:='OBJC_MSGSENDSUPER2_STRET'
|
||||
{$endif onlymacosx10_6 or arm}
|
||||
else
|
||||
msgsendname:='OBJC_MSGSENDSUPER_STRET'
|
||||
end
|
||||
{$ifdef i386}
|
||||
{ special case for fpu results on i386 for non-inherited calls }
|
||||
{ TODO: also for x86_64 "extended" results }
|
||||
else if (resultdef.typ=floatdef) and
|
||||
not(cnf_inherited in callnodeflags) then
|
||||
msgsendname:='OBJC_MSGSEND_FPRET'
|
||||
{$endif}
|
||||
{ default }
|
||||
else if not(cnf_inherited in callnodeflags) then
|
||||
msgsendname:='OBJC_MSGSEND'
|
||||
{$if defined(onlymacosx10_6) or defined(arm) }
|
||||
else if (target_info.system in system_objc_nfabi) then
|
||||
msgsendname:='OBJC_MSGSENDSUPER2'
|
||||
{$endif onlymacosx10_6 or arm}
|
||||
else
|
||||
msgsendname:='OBJC_MSGSENDSUPER';
|
||||
|
||||
{ get the mangled name }
|
||||
if not searchsym_in_named_module('OBJC',msgsendname,srsym,srsymtable) or
|
||||
(srsym.typ<>procsym) or
|
||||
(tprocsym(srsym).ProcdefList.count<>1) then
|
||||
Message1(cg_f_unknown_compilerproc,'objc.'+msgsendname);
|
||||
fobjcforcedprocname:=stringdup(tprocdef(tprocsym(srsym).ProcdefList[0]).mangledname);
|
||||
|
||||
{ B) Handle self }
|
||||
{ 1) in case of sending a message to a superclass, self is a pointer to
|
||||
an objc_super record
|
||||
}
|
||||
if (cnf_inherited in callnodeflags) then
|
||||
begin
|
||||
block:=internalstatements(statements);
|
||||
objcsupertype:=search_named_unit_globaltype('OBJC','OBJC_SUPER').typedef;
|
||||
if (objcsupertype.typ<>recorddef) then
|
||||
internalerror(2009032901);
|
||||
{ temp for the for the objc_super record }
|
||||
temp:=ctempcreatenode.create(objcsupertype,objcsupertype.size,tt_persistent,false);
|
||||
addstatement(statements,temp);
|
||||
{ initialize objc_super record }
|
||||
selftree:=load_self_node;
|
||||
|
||||
{ we can call an inherited class static/method from a regular method
|
||||
-> self node must change from instance pointer to vmt pointer)
|
||||
}
|
||||
if (po_classmethod in procdefinition.procoptions) and
|
||||
(selftree.resultdef.typ<>classrefdef) then
|
||||
begin
|
||||
selftree:=cloadvmtaddrnode.create(selftree);
|
||||
typecheckpass(selftree);
|
||||
end;
|
||||
selfrestype:=selftree.resultdef;
|
||||
field:=tfieldvarsym(trecorddef(objcsupertype).symtable.find('RECEIVER'));
|
||||
if not assigned(field) then
|
||||
internalerror(2009032902);
|
||||
{ first the destination object/class instance }
|
||||
addstatement(statements,
|
||||
cassignmentnode.create(
|
||||
csubscriptnode.create(field,ctemprefnode.create(temp)),
|
||||
selftree
|
||||
)
|
||||
);
|
||||
{ and secondly, the class type in which the selector must be looked
|
||||
up (the parent class in case of an instance method, the parent's
|
||||
metaclass in case of a class method) }
|
||||
field:=tfieldvarsym(trecorddef(objcsupertype).symtable.find('_CLASS'));
|
||||
if not assigned(field) then
|
||||
internalerror(2009032903);
|
||||
addstatement(statements,
|
||||
cassignmentnode.create(
|
||||
csubscriptnode.create(field,ctemprefnode.create(temp)),
|
||||
objcsuperclassnode(selftree.resultdef)
|
||||
)
|
||||
);
|
||||
{ result of this block is the address of this temp }
|
||||
addstatement(statements,ctypeconvnode.create_internal(
|
||||
caddrnode.create_internal(ctemprefnode.create(temp)),selfrestype)
|
||||
);
|
||||
{ replace the method pointer with the address of this temp }
|
||||
methodpointer.free;
|
||||
methodpointer:=block;
|
||||
typecheckpass(block);
|
||||
end
|
||||
else
|
||||
{ 2) regular call (not inherited) }
|
||||
begin
|
||||
{ a) If we're calling a class method, use a class ref. }
|
||||
if (po_classmethod in procdefinition.procoptions) and
|
||||
((methodpointer.nodetype=typen) or
|
||||
(methodpointer.resultdef.typ<>classrefdef)) then
|
||||
begin
|
||||
methodpointer:=cloadvmtaddrnode.create(methodpointer);
|
||||
firstpass(methodpointer);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
function tcallnode.gen_vmt_tree:tnode;
|
||||
var
|
||||
vmttree : tnode;
|
||||
@ -2029,7 +2184,12 @@ implementation
|
||||
if vo_is_overflow_check in para.parasym.varoptions then
|
||||
begin
|
||||
para.left:=cordconstnode.create(Ord(cs_check_overflow in current_settings.localswitches),booltype,false);
|
||||
end;
|
||||
end
|
||||
else
|
||||
if vo_is_msgsel in para.parasym.varoptions then
|
||||
begin
|
||||
para.left:=cobjcselectornode.create(cstringconstnode.createstr(tprocdef(procdefinition).messageinf.str^));
|
||||
end;
|
||||
end;
|
||||
if not assigned(para.left) then
|
||||
internalerror(200709084);
|
||||
@ -2849,19 +3009,26 @@ implementation
|
||||
if (procdefinition.typ=procdef) and
|
||||
(po_objc in tprocdef(procdefinition).procoptions) then
|
||||
begin
|
||||
result:=cobjcmessagesendnode.create(self.getcopy);
|
||||
exit;
|
||||
if not(cnf_objc_processed in callnodeflags) then
|
||||
objc_convert_to_message_send;
|
||||
end
|
||||
else
|
||||
begin
|
||||
{ The following don't apply to obj-c: obj-c methods can never be
|
||||
inlined because they're always virtual and the destination can
|
||||
change at run, and for the same reason we also can't perform
|
||||
WPO on them (+ they have no constructors) }
|
||||
|
||||
{ Check if the call can be inlined, sets the cnf_do_inline flag }
|
||||
check_inlining;
|
||||
|
||||
{ must be called before maybe_load_in_temp(methodpointer), because
|
||||
it converts the methodpointer into a temp in case it's a call
|
||||
(and we want to know the original call)
|
||||
}
|
||||
register_created_object_types;
|
||||
end;
|
||||
|
||||
{ Check if the call can be inlined, sets the cnf_do_inline flag }
|
||||
check_inlining;
|
||||
|
||||
{ must be called before maybe_load_in_temp(methodpointer), because
|
||||
it converts the methodpointer into a temp in case it's a call
|
||||
(and we want to know the original call)
|
||||
}
|
||||
register_created_object_types;
|
||||
|
||||
{ Maybe optimize the loading of the methodpointer using a temp. When the methodpointer
|
||||
is a calln this is even required to not execute the calln twice.
|
||||
This needs to be done after the resulttype pass, because in the resulttype we can still convert the
|
||||
|
@ -1024,9 +1024,12 @@ implementation
|
||||
{$endif vtentry}
|
||||
|
||||
name_to_call:='';
|
||||
if assigned(fobjcforcedprocname) then
|
||||
name_to_call:=fobjcforcedprocname^;
|
||||
{ When methodpointer is typen we don't need (and can't) load
|
||||
a pointer. We can directly call the correct procdef (PFV) }
|
||||
if (po_virtualmethod in procdefinition.procoptions) and
|
||||
if (name_to_call='') and
|
||||
(po_virtualmethod in procdefinition.procoptions) and
|
||||
assigned(methodpointer) and
|
||||
(methodpointer.nodetype<>typen) and
|
||||
not wpoinfomanager.can_be_devirtualized(methodpointer.resultdef,procdefinition,name_to_call) then
|
||||
|
@ -50,17 +50,8 @@ type
|
||||
end;
|
||||
tobjcprotocolnodeclass = class of tobjcprotocolnode;
|
||||
|
||||
tobjcmessagesendnode = class(tunarynode)
|
||||
public
|
||||
constructor create(forcall: tnode);
|
||||
function pass_typecheck: tnode;override;
|
||||
function pass_1: tnode;override;
|
||||
end;
|
||||
tobjcmessagesendnodeclass = class of tobjcmessagesendnode;
|
||||
|
||||
var
|
||||
cobjcselectornode : tobjcselectornodeclass;
|
||||
cobjcmessagesendnode : tobjcmessagesendnodeclass;
|
||||
cobjcprotocolnode : tobjcprotocolnodeclass;
|
||||
|
||||
implementation
|
||||
@ -170,272 +161,5 @@ function tobjcprotocolnode.pass_1: tnode;
|
||||
end;
|
||||
|
||||
|
||||
{*****************************************************************************
|
||||
TOBJCMESSAGESENDNODE
|
||||
*****************************************************************************}
|
||||
|
||||
constructor tobjcmessagesendnode.create(forcall: tnode);
|
||||
begin
|
||||
if (forcall.nodetype<>calln) then
|
||||
internalerror(2009032502);
|
||||
{ typecheck pass must already have run on the call node,
|
||||
because pass1 of the callnode creates this node right
|
||||
at the beginning
|
||||
}
|
||||
inherited create(objcmessagesendn,forcall);
|
||||
end;
|
||||
|
||||
|
||||
function tobjcmessagesendnode.pass_typecheck: tnode;
|
||||
begin
|
||||
{ typecheckpass of left has already run, see constructor }
|
||||
resultdef:=left.resultdef;
|
||||
result:=nil;
|
||||
expectloc:=left.expectloc;
|
||||
end;
|
||||
|
||||
|
||||
function tobjcmessagesendnode.pass_1: tnode;
|
||||
var
|
||||
msgsendname: string;
|
||||
newparas,
|
||||
para: tcallparanode;
|
||||
block,
|
||||
selftree : tnode;
|
||||
statements: tstatementnode;
|
||||
temp,
|
||||
tempresult: ttempcreatenode;
|
||||
objcsupertype: tdef;
|
||||
field: tfieldvarsym;
|
||||
selfpara,
|
||||
msgselpara,
|
||||
respara,
|
||||
|
||||
prerespara,
|
||||
prevpara: tcallparanode;
|
||||
begin
|
||||
{ typecheckpass of left has already run, see constructor }
|
||||
|
||||
{ default behaviour: call objc_msgSend and friends;
|
||||
ppc64 and x86_64 for Mac OS X have to override this as they
|
||||
call messages via an indirect function call similar to
|
||||
dynamically linked functions, ARM maybe as well (not checked)
|
||||
|
||||
Which variant of objc_msgSend is used depends on the
|
||||
result type, and on whether or not it's an inherited call.
|
||||
}
|
||||
|
||||
tempresult:=nil;
|
||||
newparas:=tcallparanode(tcallnode(left).left);
|
||||
{ Find the self and msgsel parameters, and if we have var/out parameters
|
||||
that normally aren't passed by reference in C, add addrnodes
|
||||
}
|
||||
para:=newparas;
|
||||
selfpara:=nil;
|
||||
msgselpara:=nil;
|
||||
respara:=nil;
|
||||
prevpara:=nil;
|
||||
while assigned(para) do
|
||||
begin
|
||||
if (vo_is_self in para.parasym.varoptions) then
|
||||
selfpara:=para
|
||||
else if (vo_is_msgsel in para.parasym.varoptions) then
|
||||
msgselpara:=para
|
||||
else if (vo_is_funcret in para.parasym.varoptions) then
|
||||
begin
|
||||
prerespara:=prevpara;
|
||||
respara:=para;
|
||||
end
|
||||
{ All parameters will be passed as varargs to objc_msg*, so make
|
||||
sure that in case of var/out parameters, the address is passed. }
|
||||
else if (para.parasym.varspez in [vs_var,vs_out]) and
|
||||
not paramanager.push_addr_param(vs_value,para.parasym.vardef,pocall_cdecl) then
|
||||
para.left:=caddrnode.create(para.left);
|
||||
prevpara:=para;
|
||||
para:=tcallparanode(para.right);
|
||||
end;
|
||||
if not assigned(selfpara) then
|
||||
internalerror(2009051801);
|
||||
if not assigned(msgselpara) then
|
||||
internalerror(2009051802);
|
||||
|
||||
{ record returned via implicit pointer }
|
||||
if paramanager.ret_in_param(left.resultdef,tcallnode(left).procdefinition.proccalloption) then
|
||||
begin
|
||||
if not assigned(respara) then
|
||||
internalerror(2009091101);
|
||||
{ Since the result parameter is also hidden in the routine we'll
|
||||
call now, it will be inserted again by the callnode. So we have to
|
||||
remove the old one, otherwise we'll have two result parameters.
|
||||
}
|
||||
if (tcallparanode(respara).left.nodetype<>nothingn) then
|
||||
internalerror(2009091102);
|
||||
if assigned(prerespara) then
|
||||
tcallparanode(prerespara).right:=tcallparanode(respara).right
|
||||
else
|
||||
begin
|
||||
tcallnode(left).left:=tcallparanode(respara).right;
|
||||
newparas:=tcallparanode(tcallnode(left).left);
|
||||
end;
|
||||
tcallparanode(respara).right:=nil;
|
||||
respara.free;
|
||||
if not(cnf_inherited in tcallnode(left).callnodeflags) then
|
||||
msgsendname:='OBJC_MSGSEND_STRET'
|
||||
{$if defined(onlymacosx10_6) or defined(arm) }
|
||||
else if (target_info.system in system_objc_nfabi) then
|
||||
msgsendname:='OBJC_MSGSENDSUPER2_STRET'
|
||||
{$endif onlymacosx10_6 or arm}
|
||||
else
|
||||
msgsendname:='OBJC_MSGSENDSUPER_STRET'
|
||||
end
|
||||
{$ifdef i386}
|
||||
{ special case for fpu results on i386 for non-inherited calls }
|
||||
else if (left.resultdef.typ=floatdef) and
|
||||
not(cnf_inherited in tcallnode(left).callnodeflags) then
|
||||
msgsendname:='OBJC_MSGSEND_FPRET'
|
||||
{$endif}
|
||||
{ default }
|
||||
else if not(cnf_inherited in tcallnode(left).callnodeflags) then
|
||||
msgsendname:='OBJC_MSGSEND'
|
||||
{$if defined(onlymacosx10_6) or defined(arm) }
|
||||
else if (target_info.system in system_objc_nfabi) then
|
||||
msgsendname:='OBJC_MSGSENDSUPER2'
|
||||
{$endif onlymacosx10_6 or arm}
|
||||
else
|
||||
msgsendname:='OBJC_MSGSENDSUPER';
|
||||
|
||||
|
||||
{ Handle self }
|
||||
{ 1) in case of sending a message to a superclass, self is a pointer to
|
||||
an objc_super record
|
||||
}
|
||||
if (cnf_inherited in tcallnode(left).callnodeflags) then
|
||||
begin
|
||||
block:=internalstatements(statements);
|
||||
objcsupertype:=search_named_unit_globaltype('OBJC','OBJC_SUPER').typedef;
|
||||
if (objcsupertype.typ<>recorddef) then
|
||||
internalerror(2009032901);
|
||||
{ temp for the for the objc_super record }
|
||||
temp:=ctempcreatenode.create(objcsupertype,objcsupertype.size,tt_persistent,false);
|
||||
addstatement(statements,temp);
|
||||
{ initialize objc_super record }
|
||||
selftree:=load_self_node;
|
||||
|
||||
{ we can call an inherited class static/method from a regular method
|
||||
-> self node must change from instance pointer to vmt pointer)
|
||||
}
|
||||
if (po_classmethod in tcallnode(left).procdefinition.procoptions) and
|
||||
(selftree.resultdef.typ<>classrefdef) then
|
||||
begin
|
||||
selftree:=cloadvmtaddrnode.create(selftree);
|
||||
typecheckpass(selftree);
|
||||
end;
|
||||
field:=tfieldvarsym(trecorddef(objcsupertype).symtable.find('RECEIVER'));
|
||||
if not assigned(field) then
|
||||
internalerror(2009032902);
|
||||
{ first the destination object/class instance }
|
||||
addstatement(statements,
|
||||
cassignmentnode.create(
|
||||
csubscriptnode.create(field,ctemprefnode.create(temp)),
|
||||
selftree
|
||||
)
|
||||
);
|
||||
{ and secondly, the class type in which the selector must be looked
|
||||
up (the parent class in case of an instance method, the parent's
|
||||
metaclass in case of a class method) }
|
||||
field:=tfieldvarsym(trecorddef(objcsupertype).symtable.find('_CLASS'));
|
||||
if not assigned(field) then
|
||||
internalerror(2009032903);
|
||||
addstatement(statements,
|
||||
cassignmentnode.create(
|
||||
csubscriptnode.create(field,ctemprefnode.create(temp)),
|
||||
objcsuperclassnode(selftree.resultdef)
|
||||
)
|
||||
);
|
||||
{ result of this block is the address of this temp }
|
||||
addstatement(statements,caddrnode.create_internal(ctemprefnode.create(temp)));
|
||||
{ replace the method pointer with the address of this temp }
|
||||
tcallnode(left).methodpointer.free;
|
||||
tcallnode(left).methodpointer:=block;
|
||||
typecheckpass(block);
|
||||
end
|
||||
else
|
||||
{ 2) regular call (not inherited) }
|
||||
begin
|
||||
{ a) If we're calling a class method, use a class ref. }
|
||||
if (po_classmethod in tcallnode(left).procdefinition.procoptions) and
|
||||
((tcallnode(left).methodpointer.nodetype=typen) or
|
||||
(tcallnode(left).methodpointer.resultdef.typ<>classrefdef)) then
|
||||
begin
|
||||
tcallnode(left).methodpointer:=cloadvmtaddrnode.create(tcallnode(left).methodpointer);
|
||||
firstpass(tcallnode(left).methodpointer);
|
||||
end;
|
||||
{ b) convert methodpointer parameter to match objc_MsgSend* signatures }
|
||||
inserttypeconv_internal(tcallnode(left).methodpointer,objc_idtype);
|
||||
end;
|
||||
{ replace self parameter }
|
||||
selfpara.left.free;
|
||||
selfpara.left:=tcallnode(left).methodpointer;
|
||||
{ replace selector parameter }
|
||||
msgselpara.left.free;
|
||||
msgselpara.left:=
|
||||
cobjcselectornode.create(
|
||||
cstringconstnode.createstr(tprocdef(tcallnode(left).procdefinition).messageinf.str^)
|
||||
);
|
||||
{ parameters are reused -> make sure they don't get freed }
|
||||
tcallnode(left).left:=nil;
|
||||
{ methodpointer is also reused }
|
||||
tcallnode(left).methodpointer:=nil;
|
||||
{ and now the call to the Objective-C rtl }
|
||||
result:=ccallnode.createinternresfromunit('OBJC',msgsendname,newparas,left.resultdef);
|
||||
{ record whether or not the function result is used (remains
|
||||
the same for the new call).
|
||||
}
|
||||
if not(cnf_return_value_used in tcallnode(left).callnodeflags) then
|
||||
exclude(tcallnode(result).callnodeflags,cnf_return_value_used);
|
||||
{ in case an explicit function result was specified, keep it }
|
||||
tcallnode(result).funcretnode:=tcallnode(left).funcretnode;
|
||||
tcallnode(left).funcretnode:=nil;
|
||||
{ keep variable paras }
|
||||
tcallnode(result).varargsparas:=tcallnode(left).varargsparas;
|
||||
tcallnode(left).varargsparas:=nil;
|
||||
|
||||
if (cnf_inherited in tcallnode(left).callnodeflags) then
|
||||
begin
|
||||
block:=internalstatements(statements);
|
||||
{ temp for the result of the inherited call }
|
||||
if not is_void(left.resultdef) and
|
||||
(cnf_return_value_used in tcallnode(left).callnodeflags) then
|
||||
begin
|
||||
tempresult:=ctempcreatenode.create(left.resultdef,left.resultdef.size,tt_persistent,true);
|
||||
addstatement(statements,tempresult);
|
||||
end;
|
||||
|
||||
{ make sure we return the result, if any }
|
||||
if not assigned(tempresult) then
|
||||
addstatement(statements,result)
|
||||
else
|
||||
addstatement(statements,
|
||||
cassignmentnode.create(ctemprefnode.create(tempresult),result));
|
||||
{ free the objc_super temp after the call. We cannot use
|
||||
ctempdeletenode.create_normal_temp before the call, because then
|
||||
the temp will be released while evaluating the parameters, and thus
|
||||
may be reused while evaluating another parameter
|
||||
}
|
||||
addstatement(statements,ctempdeletenode.create(temp));
|
||||
if assigned(tempresult) then
|
||||
begin
|
||||
{ mark the result temp as "free after next use" and return it }
|
||||
addstatement(statements,
|
||||
ctempdeletenode.create_normal_temp(tempresult));
|
||||
addstatement(statements,ctemprefnode.create(tempresult));
|
||||
end;
|
||||
typecheckpass(block);
|
||||
result:=block;
|
||||
end;
|
||||
end;
|
||||
|
||||
begin
|
||||
cobjcmessagesendnode:=tobjcmessagesendnode;
|
||||
end.
|
||||
|
||||
|
@ -111,7 +111,6 @@ interface
|
||||
loadparentfpn, { Load the framepointer of the parent for nested procedures }
|
||||
dataconstn, { node storing some binary data }
|
||||
objcselectorn, { node for an Objective-C message selector }
|
||||
objcmessagesendn, { node for message sent to an Objective-C instance (similar to a method call) }
|
||||
objcprotocoln { node for an Objective-C @protocol() expression (returns metaclass associated with protocol) }
|
||||
);
|
||||
|
||||
@ -195,7 +194,6 @@ interface
|
||||
'loadparentfpn',
|
||||
'dataconstn',
|
||||
'objcselectorn',
|
||||
'objcmessagesendn',
|
||||
'objcprotocoln');
|
||||
|
||||
type
|
||||
|
@ -43,7 +43,7 @@ type
|
||||
{$endif Test_Double_checksum}
|
||||
|
||||
const
|
||||
CurrentPPUVersion = 102;
|
||||
CurrentPPUVersion = 103;
|
||||
|
||||
{ buffer sizes }
|
||||
maxentrysize = 1024;
|
||||
|
@ -19,7 +19,7 @@ type
|
||||
function getss: shortstring ; message 'getss';
|
||||
function getsspara(l1,l2: longint): shortstring ; message 'getss:l1:';
|
||||
function getsingle(l1,l2: longint): single; message 'getsingle:l1:';
|
||||
function getdouble(l1,l2: longint): double; message 'getdouble:l1:';
|
||||
function getdouble(l1,l2: longint; d: double): double; message 'getdouble:l1:l2:';
|
||||
|
||||
function getbool: boolean; message 'getbool';
|
||||
end;
|
||||
@ -48,10 +48,12 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
function MyObject.getdouble(l1,l2: longint): double;
|
||||
function MyObject.getdouble(l1,l2: longint; d: double): double;
|
||||
begin
|
||||
writeln(d);
|
||||
if (l1<>1) or
|
||||
(l2<>2) then
|
||||
(l2<>2) or
|
||||
(d<>1.5) then
|
||||
halt(3);
|
||||
result:=fdouble;
|
||||
end;
|
||||
@ -78,7 +80,7 @@ begin
|
||||
halt(5);
|
||||
if m.getsingle(1,2)<>123.625 then
|
||||
halt(6);
|
||||
if m.getdouble(1,2)<>9876.0625 then
|
||||
if m.getdouble(1,2,1.5)<>9876.0625 then
|
||||
halt(7);
|
||||
|
||||
m.fbool:=true;
|
||||
|
Loading…
Reference in New Issue
Block a user