nobjc.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. {
  2. Copyright (c) 2009 by Jonas Maebe
  3. This unit implements Objective-C nodes
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. ****************************************************************************
  16. }
  17. { @abstract(This unit implements Objective-C nodes)
  18. This unit contains various nodes to implement Objective-Pascal and to
  19. interface with the Objective-C runtime.
  20. }
  21. unit nobjc;
  22. {$i fpcdefs.inc}
  23. interface
  24. uses
  25. node;
  26. type
  27. tobjcselectornode = class(tunarynode)
  28. public
  29. constructor create(formethod: tnode);
  30. function pass_typecheck: tnode;override;
  31. function pass_1: tnode;override;
  32. end;
  33. tobjcselectornodeclass = class of tobjcselectornode;
  34. tobjcprotocolnode = class(tunarynode)
  35. public
  36. constructor create(forprotocol: tnode);
  37. function pass_typecheck: tnode;override;
  38. function pass_1: tnode;override;
  39. end;
  40. tobjcprotocolnodeclass = class of tobjcprotocolnode;
  41. tobjcmessagesendnode = class(tunarynode)
  42. public
  43. constructor create(forcall: tnode);
  44. function pass_typecheck: tnode;override;
  45. function pass_1: tnode;override;
  46. end;
  47. tobjcmessagesendnodeclass = class of tobjcmessagesendnode;
  48. var
  49. cobjcselectornode : tobjcselectornodeclass;
  50. cobjcmessagesendnode : tobjcmessagesendnodeclass;
  51. cobjcprotocolnode : tobjcprotocolnodeclass;
  52. implementation
  53. uses
  54. sysutils,
  55. globtype,cclasses,
  56. verbose,pass_1,
  57. defutil,
  58. symtype,symtable,symdef,symconst,symsym,
  59. paramgr,
  60. nutils,
  61. nbas,nld,ncnv,ncon,ncal,nmem,
  62. objcutil,
  63. cgbase;
  64. {*****************************************************************************
  65. TOBJCSELECTORNODE
  66. *****************************************************************************}
  67. constructor tobjcselectornode.create(formethod: tnode);
  68. begin
  69. inherited create(objcselectorn,formethod);
  70. end;
  71. function tobjcselectornode.pass_typecheck: tnode;
  72. var
  73. len: longint;
  74. s: shortstring;
  75. begin
  76. result:=nil;
  77. typecheckpass(left);
  78. { argument can be
  79. a) an objc method
  80. b) a pchar, zero-based chararray or ansistring
  81. }
  82. case left.nodetype of
  83. loadn:
  84. begin
  85. if (left.resultdef.typ=procdef) and
  86. (po_objc in tprocdef(left.resultdef).procoptions) then
  87. begin
  88. { ok }
  89. end
  90. else
  91. CGMessage1(type_e_expected_objc_method_but_got,left.resultdef.typename);
  92. end;
  93. stringconstn:
  94. begin
  95. if not objcvalidselectorname(tstringconstnode(left).value_str,
  96. tstringconstnode(left).len) then
  97. begin
  98. len:=tstringconstnode(left).len;
  99. if (len>255) then
  100. len:=255;
  101. setlength(s,len);
  102. move(tstringconstnode(left).value_str^,s[1],len);
  103. CGMessage1(type_e_invalid_objc_selector_name,s);
  104. exit;
  105. end;
  106. end
  107. else
  108. CGMessage(type_e_expected_objc_method);
  109. end;
  110. resultdef:=objc_seltype;
  111. end;
  112. function tobjcselectornode.pass_1: tnode;
  113. begin
  114. result:=nil;
  115. expectloc:=LOC_CREFERENCE;
  116. end;
  117. {*****************************************************************************
  118. TOBJPROTOCOLNODE
  119. *****************************************************************************}
  120. constructor tobjcprotocolnode.create(forprotocol: tnode);
  121. begin
  122. inherited create(objcprotocoln,forprotocol);
  123. end;
  124. function tobjcprotocolnode.pass_typecheck: tnode;
  125. begin
  126. result:=nil;
  127. typecheckpass(left);
  128. if (left.nodetype<>typen) then
  129. MessagePos(left.fileinfo,type_e_type_id_expected)
  130. else if not is_objcprotocol(left.resultdef) then
  131. MessagePos2(left.fileinfo,type_e_incompatible_types,left.resultdef.typename,'ObjCProtocol');
  132. resultdef:=objc_protocoltype;
  133. end;
  134. function tobjcprotocolnode.pass_1: tnode;
  135. begin
  136. result:=ccallnode.createinternresfromunit('OBJC1','OBJC_GETPROTOCOL',
  137. ccallparanode.create(cstringconstnode.createstr(tobjectdef(left.resultdef).objextname^),nil),
  138. resultdef
  139. );
  140. typecheckpass(result);
  141. end;
  142. {*****************************************************************************
  143. TOBJCMESSAGESENDNODE
  144. *****************************************************************************}
  145. constructor tobjcmessagesendnode.create(forcall: tnode);
  146. begin
  147. if (forcall.nodetype<>calln) then
  148. internalerror(2009032502);
  149. { typecheck pass must already have run on the call node,
  150. because pass1 of the callnode creates this node right
  151. at the beginning
  152. }
  153. inherited create(objcmessagesendn,forcall);
  154. end;
  155. function tobjcmessagesendnode.pass_typecheck: tnode;
  156. begin
  157. { typecheckpass of left has already run, see constructor }
  158. resultdef:=left.resultdef;
  159. result:=nil;
  160. expectloc:=left.expectloc;
  161. end;
  162. function tobjcmessagesendnode.pass_1: tnode;
  163. var
  164. msgsendname: string;
  165. newparas,
  166. para: tcallparanode;
  167. block,
  168. selftree : tnode;
  169. statements: tstatementnode;
  170. temp,
  171. tempresult: ttempcreatenode;
  172. objcsupertype: tdef;
  173. field: tfieldvarsym;
  174. selfpara,
  175. msgselpara,
  176. respara,
  177. prerespara,
  178. prevpara: tcallparanode;
  179. begin
  180. { typecheckpass of left has already run, see constructor }
  181. { default behaviour: call objc_msgSend and friends;
  182. ppc64 and x86_64 for Mac OS X have to override this as they
  183. call messages via an indirect function call similar to
  184. dynamically linked functions, ARM maybe as well (not checked)
  185. Which variant of objc_msgSend is used depends on the
  186. result type, and on whether or not it's an inherited call.
  187. }
  188. tempresult:=nil;
  189. newparas:=tcallparanode(tcallnode(left).left);
  190. { Find the self and msgsel parameters. }
  191. para:=newparas;
  192. selfpara:=nil;
  193. msgselpara:=nil;
  194. respara:=nil;
  195. prevpara:=nil;
  196. while assigned(para) do
  197. begin
  198. if (vo_is_self in para.parasym.varoptions) then
  199. selfpara:=para
  200. else if (vo_is_msgsel in para.parasym.varoptions) then
  201. msgselpara:=para
  202. else if (vo_is_funcret in para.parasym.varoptions) then
  203. begin
  204. prerespara:=prevpara;
  205. respara:=para;
  206. end;
  207. prevpara:=para;
  208. para:=tcallparanode(para.right);
  209. end;
  210. if not assigned(selfpara) then
  211. internalerror(2009051801);
  212. if not assigned(msgselpara) then
  213. internalerror(2009051802);
  214. { record returned via implicit pointer }
  215. if paramanager.ret_in_param(left.resultdef,tcallnode(left).procdefinition.proccalloption) then
  216. begin
  217. if not assigned(respara) then
  218. internalerror(2009091101);
  219. { Since the result parameter is also hidden in the routine we'll
  220. call now, it will be inserted again by the callnode. So we have to
  221. remove the old one, otherwise we'll have two result parameters.
  222. }
  223. if (tcallparanode(respara).left.nodetype<>nothingn) then
  224. internalerror(2009091102);
  225. if assigned(prerespara) then
  226. tcallparanode(prerespara).right:=tcallparanode(respara).right
  227. else
  228. begin
  229. tcallnode(left).left:=tcallparanode(respara).right;
  230. newparas:=tcallparanode(tcallnode(left).left);
  231. end;
  232. tcallparanode(respara).right:=nil;
  233. respara.free;
  234. if not(cnf_inherited in tcallnode(left).callnodeflags) then
  235. msgsendname:='OBJC_MSGSEND_STRET'
  236. else
  237. msgsendname:='OBJC_MSGSENDSUPER_STRET'
  238. end
  239. {$ifdef i386}
  240. { special case for fpu results on i386 for non-inherited calls }
  241. else if (left.resultdef.typ=floatdef) and
  242. not(cnf_inherited in tcallnode(left).callnodeflags) then
  243. msgsendname:='OBJC_MSGSEND_FPRET'
  244. {$endif}
  245. { default }
  246. else if not(cnf_inherited in tcallnode(left).callnodeflags) then
  247. msgsendname:='OBJC_MSGSEND'
  248. else
  249. msgsendname:='OBJC_MSGSENDSUPER';
  250. { Handle self }
  251. { 1) in case of sending a message to a superclass, self is a pointer to
  252. an objc_super record
  253. }
  254. if (cnf_inherited in tcallnode(left).callnodeflags) then
  255. begin
  256. block:=internalstatements(statements);
  257. objcsupertype:=search_named_unit_globaltype('OBJC1','OBJC_SUPER').typedef;
  258. if (objcsupertype.typ<>recorddef) then
  259. internalerror(2009032901);
  260. { temp for the for the objc_super record }
  261. temp:=ctempcreatenode.create(objcsupertype,objcsupertype.size,tt_persistent,false);
  262. addstatement(statements,temp);
  263. { initialize objc_super record }
  264. selftree:=load_self_node;
  265. { we can call an inherited class static/method from a regular method
  266. -> self node must change from instance pointer to vmt pointer)
  267. }
  268. if (po_classmethod in tcallnode(left).procdefinition.procoptions) and
  269. (selftree.resultdef.typ<>classrefdef) then
  270. begin
  271. selftree:=cloadvmtaddrnode.create(selftree);
  272. typecheckpass(selftree);
  273. end;
  274. field:=tfieldvarsym(trecorddef(objcsupertype).symtable.find('RECEIVER'));
  275. if not assigned(field) then
  276. internalerror(2009032902);
  277. { first the destination object/class instance }
  278. addstatement(statements,
  279. cassignmentnode.create(
  280. csubscriptnode.create(field,ctemprefnode.create(temp)),
  281. selftree
  282. )
  283. );
  284. { and secondly, the class type in which the selector must be looked
  285. up (the parent class in case of an instance method, the parent's
  286. metaclass in case of a class method) }
  287. field:=tfieldvarsym(trecorddef(objcsupertype).symtable.find('_CLASS'));
  288. if not assigned(field) then
  289. internalerror(2009032903);
  290. addstatement(statements,
  291. cassignmentnode.create(
  292. csubscriptnode.create(field,ctemprefnode.create(temp)),
  293. objcsuperclassnode(selftree.resultdef)
  294. )
  295. );
  296. { result of this block is the address of this temp }
  297. addstatement(statements,caddrnode.create_internal(ctemprefnode.create(temp)));
  298. { replace the method pointer with the address of this temp }
  299. tcallnode(left).methodpointer.free;
  300. tcallnode(left).methodpointer:=block;
  301. typecheckpass(block);
  302. end
  303. else
  304. { 2) regular call (not inherited) }
  305. begin
  306. { a) If we're calling a class method, use a class ref. }
  307. if (po_classmethod in tcallnode(left).procdefinition.procoptions) and
  308. ((tcallnode(left).methodpointer.nodetype=typen) or
  309. (tcallnode(left).methodpointer.resultdef.typ<>classrefdef)) then
  310. begin
  311. tcallnode(left).methodpointer:=cloadvmtaddrnode.create(tcallnode(left).methodpointer);
  312. firstpass(tcallnode(left).methodpointer);
  313. end;
  314. { b) convert methodpointer parameter to match objc_MsgSend* signatures }
  315. inserttypeconv_internal(tcallnode(left).methodpointer,objc_idtype);
  316. end;
  317. { replace self parameter }
  318. selfpara.left.free;
  319. selfpara.left:=tcallnode(left).methodpointer;
  320. { replace selector parameter }
  321. msgselpara.left.free;
  322. msgselpara.left:=
  323. cobjcselectornode.create(
  324. cstringconstnode.createstr(tprocdef(tcallnode(left).procdefinition).messageinf.str^)
  325. );
  326. { parameters are reused -> make sure they don't get freed }
  327. tcallnode(left).left:=nil;
  328. { methodpointer is also reused }
  329. tcallnode(left).methodpointer:=nil;
  330. { and now the call to the Objective-C rtl }
  331. result:=ccallnode.createinternresfromunit('OBJC1',msgsendname,newparas,left.resultdef);
  332. { record whether or not the function result is used (remains
  333. the same for the new call).
  334. }
  335. if not(cnf_return_value_used in tcallnode(left).callnodeflags) then
  336. exclude(tcallnode(result).callnodeflags,cnf_return_value_used);
  337. { in case an explicit function result was specified, keep it }
  338. tcallnode(result).funcretnode:=tcallnode(left).funcretnode;
  339. tcallnode(left).funcretnode:=nil;
  340. { keep variable paras }
  341. tcallnode(result).varargsparas:=tcallnode(left).varargsparas;
  342. tcallnode(left).varargsparas:=nil;
  343. if (cnf_inherited in tcallnode(left).callnodeflags) then
  344. begin
  345. block:=internalstatements(statements);
  346. { temp for the result of the inherited call }
  347. if not is_void(left.resultdef) and
  348. (cnf_return_value_used in tcallnode(left).callnodeflags) then
  349. begin
  350. tempresult:=ctempcreatenode.create(left.resultdef,left.resultdef.size,tt_persistent,true);
  351. addstatement(statements,tempresult);
  352. end;
  353. { make sure we return the result, if any }
  354. if not assigned(tempresult) then
  355. addstatement(statements,result)
  356. else
  357. addstatement(statements,
  358. cassignmentnode.create(ctemprefnode.create(tempresult),result));
  359. { free the objc_super temp after the call. We cannot use
  360. ctempdeletenode.create_normal_temp before the call, because then
  361. the temp will be released while evaluating the parameters, and thus
  362. may be reused while evaluating another parameter
  363. }
  364. addstatement(statements,ctempdeletenode.create(temp));
  365. if assigned(tempresult) then
  366. begin
  367. { mark the result temp as "free after next use" and return it }
  368. addstatement(statements,
  369. ctempdeletenode.create_normal_temp(tempresult));
  370. addstatement(statements,ctemprefnode.create(tempresult));
  371. end;
  372. typecheckpass(block);
  373. result:=block;
  374. end;
  375. end;
  376. begin
  377. cobjcmessagesendnode:=tobjcmessagesendnode;
  378. end.