nobjc.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  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. nbas,nld,ncnv,ncon,ncal,nmem,
  61. objcutil,
  62. cgbase;
  63. {*****************************************************************************
  64. TOBJCSELECTORNODE
  65. *****************************************************************************}
  66. constructor tobjcselectornode.create(formethod: tnode);
  67. begin
  68. inherited create(objcselectorn,formethod);
  69. end;
  70. function validselectorname(value_str: pchar; len: longint): boolean;
  71. var
  72. i : longint;
  73. gotcolon : boolean;
  74. begin
  75. result:=false;
  76. { empty name is not allowed }
  77. if (len=0) then
  78. exit;
  79. gotcolon:=false;
  80. { if the first character is a colon, all of them must be colons }
  81. if (value_str[0] = ':') then
  82. begin
  83. for i:=1 to len-1 do
  84. if (value_str[i]<>':') then
  85. exit;
  86. end
  87. else
  88. begin
  89. { no special characters other than ':'
  90. (already checked character 0, so start checking from 1)
  91. }
  92. for i:=1 to len-1 do
  93. if (value_str[i] = ':') then
  94. gotcolon:=true
  95. else if not(value_str[i] in ['_','A'..'Z','a'..'z','0'..'9',':']) then
  96. exit;
  97. { if there is at least one colon, the final character must
  98. also be a colon (in case it's only one character that is
  99. a colon, this was already checked before the above loop)
  100. }
  101. if gotcolon and
  102. (value_str[len-1] <> ':') then
  103. exit;
  104. end;
  105. result:=true;
  106. end;
  107. function tobjcselectornode.pass_typecheck: tnode;
  108. begin
  109. result:=nil;
  110. typecheckpass(left);
  111. { argument can be
  112. a) an objc method
  113. b) a pchar, zero-based chararray or ansistring
  114. }
  115. case left.nodetype of
  116. loadn:
  117. begin
  118. if (left.resultdef.typ=procdef) and
  119. (po_objc in tprocdef(left.resultdef).procoptions) then
  120. begin
  121. { ok }
  122. end
  123. else
  124. CGMessage1(type_e_expected_objc_method_but_got,left.resultdef.typename);
  125. end;
  126. stringconstn:
  127. begin
  128. if not validselectorname(tstringconstnode(left).value_str,
  129. tstringconstnode(left).len) then
  130. begin
  131. CGMessage(type_e_invalid_objc_selector_name);
  132. exit;
  133. end;
  134. end
  135. else
  136. CGMessage(type_e_expected_objc_method);
  137. end;
  138. resultdef:=objc_seltype;
  139. end;
  140. function tobjcselectornode.pass_1: tnode;
  141. begin
  142. result:=nil;
  143. expectloc:=LOC_CREFERENCE;
  144. end;
  145. {*****************************************************************************
  146. TOBJPROTOCOLNODE
  147. *****************************************************************************}
  148. constructor tobjcprotocolnode.create(forprotocol: tnode);
  149. begin
  150. inherited create(objcprotocoln,forprotocol);
  151. end;
  152. function tobjcprotocolnode.pass_typecheck: tnode;
  153. begin
  154. result:=nil;
  155. typecheckpass(left);
  156. if (left.nodetype<>typen) then
  157. MessagePos(left.fileinfo,type_e_type_id_expected)
  158. else if not is_objcprotocol(left.resultdef) then
  159. MessagePos2(left.fileinfo,type_e_incompatible_types,left.resultdef.typename,'ObjCProtocol');
  160. resultdef:=objc_protocoltype;
  161. end;
  162. function tobjcprotocolnode.pass_1: tnode;
  163. begin
  164. result:=nil;
  165. expectloc:=LOC_CREFERENCE;
  166. end;
  167. {*****************************************************************************
  168. TOBJCMESSAGESENDNODE
  169. *****************************************************************************}
  170. constructor tobjcmessagesendnode.create(forcall: tnode);
  171. begin
  172. if (forcall.nodetype<>calln) then
  173. internalerror(2009032502);
  174. { typecheck pass (and pass1) must already have run on the call node,
  175. because pass1 of the callnode creates this node
  176. }
  177. inherited create(objcmessagesendn,forcall);
  178. end;
  179. function tobjcmessagesendnode.pass_typecheck: tnode;
  180. begin
  181. { typecheckpass of left has already run, see constructor }
  182. resultdef:=left.resultdef;
  183. result:=nil;
  184. expectloc:=left.expectloc;
  185. end;
  186. function tobjcmessagesendnode.pass_1: tnode;
  187. var
  188. msgsendname: string;
  189. newparas,
  190. para: tcallparanode;
  191. block: tblocknode;
  192. statements: tstatementnode;
  193. temp: ttempcreatenode;
  194. objcsupertype: tdef;
  195. field: tfieldvarsym;
  196. selfpara,
  197. msgselpara: tcallparanode;
  198. begin
  199. { pass1 of left has already run, see constructor }
  200. { default behaviour: call objc_msgSend and friends;
  201. ppc64 and x86_64 for Mac OS X have to override this as they
  202. call messages via an indirect function call similar to
  203. dynamically linked functions, ARM maybe as well (not checked)
  204. Which variant of objc_msgSend is used depends on the
  205. result type, and on whether or not it's an inherited call.
  206. }
  207. { record returned via implicit pointer }
  208. if paramanager.ret_in_param(left.resultdef,tcallnode(left).procdefinition.proccalloption) then
  209. if not(cnf_inherited in tcallnode(left).callnodeflags) then
  210. msgsendname:='OBJC_MSGSEND_STRET'
  211. else
  212. msgsendname:='OBJC_MSGSENDSUPER_STRET'
  213. {$ifdef i386}
  214. { special case for fpu results on i386 for non-inherited calls }
  215. else if (left.resultdef.typ=floatdef) and
  216. not(cnf_inherited in tcallnode(left).callnodeflags) then
  217. msgsendname:='OBJC_MSGSENF_FPRET'
  218. {$endif}
  219. { default }
  220. else if not(cnf_inherited in tcallnode(left).callnodeflags) then
  221. msgsendname:='OBJC_MSGSEND'
  222. else
  223. msgsendname:='OBJC_MSGSENDSUPER';
  224. newparas:=tcallparanode(tcallnode(left).left);
  225. { Find the self and msgsel parameters. }
  226. para:=newparas;
  227. selfpara:=nil;
  228. msgselpara:=nil;
  229. while assigned(para) do
  230. begin
  231. if (vo_is_self in para.parasym.varoptions) then
  232. selfpara:=para
  233. else if (vo_is_msgsel in para.parasym.varoptions) then
  234. msgselpara:=para;
  235. para:=tcallparanode(para.right);
  236. end;
  237. if not assigned(selfpara) then
  238. internalerror(2009051801);
  239. if not assigned(msgselpara) then
  240. internalerror(2009051802);
  241. { Handle self }
  242. { 1) If we're calling a class method, use a class ref. }
  243. if (po_classmethod in tcallnode(left).procdefinition.procoptions) and
  244. ((tcallnode(left).methodpointer.nodetype=typen) or
  245. (tcallnode(left).methodpointer.resultdef.typ<>classrefdef)) then
  246. begin
  247. tcallnode(left).methodpointer:=cloadvmtaddrnode.create(tcallnode(left).methodpointer);
  248. firstpass(tcallnode(left).methodpointer);
  249. end;
  250. { 2) convert parameter to id to match objc_MsgSend* signatures }
  251. inserttypeconv_internal(tcallnode(left).methodpointer,objc_idtype);
  252. { in case of sending a message to a superclass, self is a pointer to
  253. an objc_super record
  254. }
  255. if (cnf_inherited in tcallnode(left).callnodeflags) then
  256. begin
  257. block:=internalstatements(statements);
  258. objcsupertype:=search_named_unit_globaltype('OBJC1','OBJC_SUPER').typedef;
  259. if (objcsupertype.typ<>recorddef) then
  260. internalerror(2009032901);
  261. { temp for the for the objc_super record }
  262. temp:=ctempcreatenode.create(objcsupertype,objcsupertype.size,tt_persistent,false);
  263. addstatement(statements,temp);
  264. { initialize objc_super record: first the destination object instance }
  265. field:=tfieldvarsym(trecorddef(objcsupertype).symtable.find('RECEIVER'));
  266. if not assigned(field) then
  267. internalerror(2009032902);
  268. addstatement(statements,
  269. cassignmentnode.create(
  270. csubscriptnode.create(field,ctemprefnode.create(temp)),
  271. tcallnode(left).methodpointer
  272. )
  273. );
  274. { and secondly, the destination class type }
  275. field:=tfieldvarsym(trecorddef(objcsupertype).symtable.find('_CLASS'));
  276. if not assigned(field) then
  277. internalerror(2009032903);
  278. addstatement(statements,
  279. cassignmentnode.create(
  280. csubscriptnode.create(field,ctemprefnode.create(temp)),
  281. objcsuperclassnode(tobjectdef(tcallnode(left).methodpointer.resultdef))
  282. )
  283. );
  284. { result of this block is the address of this temp }
  285. addstatement(statements,caddrnode.create_internal(ctemprefnode.create(temp)));
  286. { replace the method pointer with the address of this temp }
  287. tcallnode(left).methodpointer:=block;
  288. typecheckpass(block);
  289. end;
  290. { replace self parameter }
  291. selfpara.left.free;
  292. selfpara.left:=tcallnode(left).methodpointer;
  293. { replace selector parameter }
  294. msgselpara.left.Free;
  295. msgselpara.left:=
  296. cobjcselectornode.create(
  297. cstringconstnode.createstr(tprocdef(tcallnode(left).procdefinition).messageinf.str^)
  298. );
  299. { parameters are reused -> make sure they don't get freed }
  300. tcallnode(left).left:=nil;
  301. { methodpointer is also reused }
  302. tcallnode(left).methodpointer:=nil;
  303. { and now the call to the Objective-C rtl }
  304. result:=ccallnode.createinternresfromunit('OBJC1',msgsendname,newparas,left.resultdef);
  305. if (cnf_inherited in tcallnode(left).callnodeflags) then
  306. begin
  307. { free the objc_super temp after the call. We cannout use
  308. ctempdeletenode.create_normal_temp before the call, because then
  309. the temp will be released while evaluating the parameters, and thus
  310. may be reused while evaluating another parameter
  311. }
  312. block:=internalstatements(statements);
  313. addstatement(statements,result);
  314. addstatement(statements,ctempdeletenode.create(temp));
  315. typecheckpass(block);
  316. result:=block;
  317. end;
  318. end;
  319. begin
  320. cobjcmessagesendnode:=tobjcmessagesendnode;
  321. end.