nobjc.pas 10 KB

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