blockutl.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. {
  2. Copyright (c) 2014 by Jonas Maebe, Member of the Free Pascal
  3. development team.
  4. This unit implements helper routines for "blocks" support
  5. (http://en.wikipedia.org/wiki/Blocks_(C_language_extension) )
  6. This program is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program; if not, write to the Free Software
  16. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17. ****************************************************************************
  18. }
  19. unit blockutl;
  20. {$i fpcdefs.inc}
  21. interface
  22. uses
  23. node,nld,ncnv,
  24. symtype,symdef;
  25. { accepts a loadnode for a procdef
  26. returns a node representing the converted code to implement this
  27. conversion (this node is valid both for typed constant declarations and
  28. in function bodies). The node is not reused }
  29. function generate_block_for_procaddr(procloadnode: tloadnode): tnode;
  30. { for a procdef, return a recorddef representing a block literal for this
  31. procdef
  32. for a procvardef, return a basic recorddef representing a block literal
  33. with enough info to call this procvardef }
  34. function get_block_literal_type_for_proc(pd: tabstractprocdef): trecorddef;
  35. implementation
  36. uses
  37. verbose,globtype,globals,cutils,constexp,
  38. pass_1,pparautl,fmodule,
  39. aasmdata,
  40. nbas,ncon,nmem,nutils,
  41. symbase,symconst,symtable,symsym,symcreat,objcutil,objcdef,defutil,
  42. paramgr;
  43. function get_block_literal_type_for_proc(pd: tabstractprocdef): trecorddef;
  44. begin
  45. if pd.typ=procvardef then
  46. result:=trecorddef(search_named_unit_globaltype('BLOCKRTL','FPC_BLOCK_LITERAL_BASE',true).typedef)
  47. else if pd.is_addressonly then
  48. result:=trecorddef(search_named_unit_globaltype('BLOCKRTL','FPC_BLOCK_LITERAL_STATIC',true).typedef)
  49. { todo: nested functions and Objective-C methods }
  50. else if not is_nested_pd(pd) and
  51. not is_objcclass(tdef(pd.owner.defowner)) then
  52. result:=trecorddef(search_named_unit_globaltype('BLOCKRTL','FPC_BLOCK_LITERAL_COMPLEX_PROCVAR',true).typedef)
  53. else
  54. internalerror(2014071304);
  55. end;
  56. function get_block_literal_isa(orgpd: tprocdef): tstaticvarsym;
  57. var
  58. srsym: tsym;
  59. srsymtable: tsymtable;
  60. name: tidstring;
  61. begin
  62. if orgpd.is_addressonly then
  63. name:='_NSCONCRETEGLOBALBLOCK'
  64. else
  65. name:='_NSCONCRETESTACKBLOCK';
  66. if not searchsym_in_named_module('BLOCKRTL',name,srsym,srsymtable) or
  67. (srsym.typ<>staticvarsym) then
  68. internalerror(2014071501);
  69. result:=tstaticvarsym(srsym);
  70. end;
  71. function get_block_literal_flags(orgpd, invokepd: tprocdef): longint;
  72. { BlockLiteralFlags }
  73. const
  74. BLOCK_HAS_COPY_DISPOSE = 1 shl 25;
  75. BLOCK_HAS_CXX_OBJ = 1 shl 26;
  76. BLOCK_IS_GLOBAL = 1 shl 28;
  77. BLOCK_USE_STRET = 1 shl 29;
  78. BLOCK_HAS_SIGNATURE = 1 shl 30;
  79. BLOCK_HAS_EXTENDED_LAYOUT = 1 shl 31;
  80. begin
  81. result:=0;
  82. { BLOCK_HAS_COPY_DISPOSE :
  83. copy/dispose will be necessary once we support nested procedures, in
  84. case they capture reference counted types, Objective-C class instances
  85. or block-type variables
  86. }
  87. { BLOCK_HAS_CXX_OBJ:
  88. we don't support C++ (stack-based) class instances yet
  89. }
  90. { BLOCK_IS_GLOBAL:
  91. set in case the block does not capture any local state; used for
  92. global functions and in theory also possible for nested functions that
  93. do not access any variables from their parentfp context
  94. }
  95. if orgpd.is_addressonly then
  96. result:=result or BLOCK_IS_GLOBAL;
  97. { BLOCK_USE_STRET:
  98. set in case the invoke function returns its result via a hidden
  99. parameter
  100. }
  101. if paramanager.ret_in_param(invokepd.returndef,orgpd) then
  102. result:=result or BLOCK_USE_STRET;
  103. { BLOCK_HAS_SIGNATURE:
  104. only if this bit is set, the above bit will actually be taken into
  105. account (for backward compatibility). We need it so that our invoke
  106. function isn't called as a variadic function, but on the downside this
  107. requires Mac OS X 10.7 or later
  108. }
  109. result:=result or BLOCK_HAS_SIGNATURE;
  110. { BLOCK_HAS_EXTENDED_LAYOUT:
  111. no documentation about what this means or what it's good for (clang
  112. adds it for Objective-C 1 platforms in case garbage collection is
  113. switched off, but then you also have to actually generate this layout)
  114. }
  115. end;
  116. function get_block_literal_descriptor(invokepd: tprocdef; block_literal_size: tcgint): tstaticvarsym;
  117. var
  118. descriptordef: tdef;
  119. descriptor: tstaticvarsym;
  120. name: tsymstr;
  121. srsym: tsym;
  122. srsymtable: tsymtable;
  123. begin
  124. (*
  125. FPC_Block_descriptor_simple = record
  126. reserved: culong;
  127. Block_size: culong;
  128. { signatures are only for the "ABI.2010.3.16" version, but that's all
  129. we support because otherwise the callback has to be a C-style
  130. variadic function, which we cannot (yet?) generate }
  131. signature: pchar;
  132. end;
  133. *)
  134. { must be a valid Pascal identifier, because we will reference it when
  135. constructing the block initialiser }
  136. { we don't have to include the moduleid in this mangledname, because
  137. the invokepd is a local procedure in the current unit -> defid by
  138. itself is unique }
  139. name:='__FPC_BLOCK_DESCRIPTOR_SIMPLE_'+tostr(invokepd.defid);
  140. { already exists -> return }
  141. if searchsym(name,srsym,srsymtable) then
  142. begin
  143. if srsym.typ<>staticvarsym then
  144. internalerror(2014071402);
  145. result:=tstaticvarsym(srsym);
  146. exit;
  147. end;
  148. { find the type of the descriptor structure }
  149. descriptordef:=search_named_unit_globaltype('BLOCKRTL','FPC_BLOCK_DESCRIPTOR_SIMPLE',true).typedef;
  150. { create new static variable }
  151. descriptor:=cstaticvarsym.create(name,vs_value,descriptordef,[]);
  152. symtablestack.top.insert(descriptor);
  153. include(descriptor.symoptions,sp_internal);
  154. { create typed constant for the descriptor }
  155. str_parse_typedconst(current_asmdata.AsmLists[al_const],
  156. '(reserved: 0; Block_size: '+tostr(block_literal_size)+
  157. '; signature: '''+objcencodemethod(invokepd)+''');',descriptor);
  158. result:=descriptor;
  159. end;
  160. { creates a wrapper function for pd with the C calling convention and an
  161. extra first parameter pointing to the block "self" pointer. This wrapper is
  162. what will be assigned to the "invoke" field of the block }
  163. function get_invoke_wrapper(orgpd: tprocdef; orgpv: tprocvardef): tprocdef;
  164. var
  165. wrappername: TIDString;
  166. srsym: tsym;
  167. srsymtable: tsymtable;
  168. begin
  169. { the copy() is to ensure we don't overflow the maximum identifier length;
  170. the combination of owner.moduleid and defid will make the name unique }
  171. wrappername:='__FPC_BLOCK_INVOKE_'+upper(copy(orgpd.procsym.realname,1,60))+'_'+tostr(orgpd.owner.moduleid)+'_'+tostr(orgpd.defid);
  172. { already an invoke wrapper for this procsym -> reuse }
  173. if searchsym(wrappername,srsym,srsymtable) then
  174. begin
  175. if (srsym.typ<>procsym) or
  176. (tprocsym(srsym).procdeflist.count<>1) then
  177. internalerror(2014071503);
  178. result:=tprocdef(tprocsym(srsym).procdeflist[0]);
  179. exit;
  180. end;
  181. { bare copy, so that self etc are not inserted }
  182. result:=tprocdef(orgpd.getcopyas(procdef,pc_bareproc));
  183. { will be called accoding to the ABI conventions }
  184. result.proccalloption:=pocall_cdecl;
  185. { add po_is_block so that a block "self" pointer gets added (of the type
  186. returned by get_block_literal_type_for_proc()) }
  187. include(result.procoptions,po_is_block);
  188. { now insert self/vmt/funcret according to the newly set calling
  189. convention }
  190. insert_self_and_vmt_para(result);
  191. insert_funcret_para(result);
  192. finish_copied_procdef(result,wrappername,current_module.localsymtable,nil);
  193. if orgpd.is_addressonly then
  194. begin
  195. result.synthetickind:=tsk_callthrough;
  196. result.skpara:=orgpd;
  197. end
  198. else
  199. begin
  200. { alias for the type to invoke the procvar, used in the symcreat
  201. handling of tsk_block_invoke_procvar }
  202. result.localst.insert(ctypesym.create('__FPC_BLOCK_INVOKE_PV_TYPE',orgpv));
  203. result.synthetickind:=tsk_block_invoke_procvar;
  204. end;
  205. end;
  206. { compose a block literal for a static block (one without context) }
  207. function get_global_proc_literal_sym(blockliteraldef: tdef; blockisasym: tstaticvarsym; blockflags: longint; invokepd: tprocdef; descriptor: tstaticvarsym): tstaticvarsym;
  208. var
  209. literalname: TIDString;
  210. srsym: tsym;
  211. srsymtable: tsymtable;
  212. begin
  213. literalname:='block_literal_for_'+invokepd.procsym.realname;
  214. { already exists -> return }
  215. if searchsym(literalname,srsym,srsymtable) then
  216. begin
  217. if srsym.typ<>staticvarsym then
  218. internalerror(2014071506);
  219. result:=tstaticvarsym(srsym);
  220. exit;
  221. end;
  222. { create new block literal symbol }
  223. result:=cstaticvarsym.create(
  224. '$'+literalname,
  225. vs_value,
  226. blockliteraldef,[]);
  227. include(result.symoptions,sp_internal);
  228. symtablestack.top.insert(result);
  229. { initialise it }
  230. str_parse_typedconst(current_asmdata.AsmLists[al_const],
  231. '(base: (isa : @'+blockisasym.realname+
  232. '; flags : '+tostr(blockflags)+
  233. '; reserved : 0'+
  234. '; invoke : @'+invokepd.procsym.realname+
  235. '; descriptor: @'+descriptor.realname+
  236. '));',
  237. result);
  238. end;
  239. { compose an on-stack block literal for a "procedure of object" }
  240. function get_pascal_method_literal(blockliteraldef: tdef; blockisasym: tstaticvarsym; blockflags: longint; procvarnode: tnode; invokepd: tprocdef; orgpv: tprocvardef; descriptor: tstaticvarsym): tnode;
  241. var
  242. statement: tstatementnode;
  243. literaltemp: ttempcreatenode;
  244. begin
  245. result:=internalstatements(statement);
  246. { create new block literal structure }
  247. literaltemp:=ctempcreatenode.create(blockliteraldef,blockliteraldef.size,tt_persistent,false);
  248. addstatement(statement,literaltemp);
  249. { temp.base.isa:=@blockisasym }
  250. addstatement(statement,cassignmentnode.create(
  251. genloadfield(genloadfield(ctemprefnode.create(literaltemp),'BASE'),'ISA'),
  252. caddrnode.create(cloadnode.create(blockisasym,blockisasym.owner))));
  253. { temp.base.flags:=blockflags }
  254. addstatement(statement,cassignmentnode.create(
  255. genloadfield(genloadfield(ctemprefnode.create(literaltemp),'BASE'),'FLAGS'),
  256. genintconstnode(blockflags)));
  257. { temp.base.reserved:=0 }
  258. addstatement(statement,cassignmentnode.create(
  259. genloadfield(genloadfield(ctemprefnode.create(literaltemp),'BASE'),'RESERVED'),
  260. genintconstnode(0)));
  261. { temp.base.invoke:=tmethod(@invokepd) }
  262. addstatement(statement,cassignmentnode.create(
  263. genloadfield(genloadfield(ctemprefnode.create(literaltemp),'BASE'),'INVOKE'),
  264. ctypeconvnode.create_proc_to_procvar(
  265. cloadnode.create_procvar(invokepd.procsym,invokepd,invokepd.owner))));
  266. { temp.base.descriptor:=@descriptor }
  267. addstatement(statement,cassignmentnode.create(
  268. genloadfield(genloadfield(ctemprefnode.create(literaltemp),'BASE'),'DESCRIPTOR'),
  269. caddrnode.create(cloadnode.create(descriptor,descriptor.owner))));
  270. { temp.pv:=tmethod(@orgpd) }
  271. addstatement(statement,cassignmentnode.create(
  272. ctypeconvnode.create_explicit(genloadfield(ctemprefnode.create(literaltemp),'PV'),orgpv),
  273. procvarnode.getcopy));
  274. { and return the address of the temp }
  275. addstatement(statement,caddrnode.create(ctemprefnode.create(literaltemp)));
  276. { typecheck this now, because the current source may be written in TP/
  277. Delphi/MacPas mode and the above node tree has been constructed for
  278. ObjFPC mode, which has been set by replace_scanner (in Delphi, the
  279. assignment to invoke would be without the proc_to_procvar conversion) }
  280. typecheckpass(result);
  281. end;
  282. function generate_block_for_procaddr(procloadnode: tloadnode): tnode;
  283. var
  284. procvarnode: tnode;
  285. { procvardef representing the original function we want to invoke }
  286. orgpv: tprocvardef;
  287. { procdef of the original function we want to invoke }
  288. orgpd,
  289. { procdef for the invoke-wrapper that we generated to call the original
  290. function via a procvar }
  291. invokepd: tprocdef;
  292. blockliteraldef: tdef;
  293. descriptor,
  294. blockisasym,
  295. blockliteralsym: tstaticvarsym;
  296. blockflags: longint;
  297. old_symtablestack: tsymtablestack;
  298. sstate: tscannerstate;
  299. begin
  300. result:=nil;
  301. { supported? (should be caught earlier) }
  302. if (procloadnode.resultdef.typ<>procdef) or
  303. is_nested_pd(tprocdef(procloadnode.resultdef)) or
  304. is_objcclass(tdef(procloadnode.resultdef.owner.defowner)) then
  305. internalerror(2014071401);
  306. { add every symbol that we create here to the unit-level symbol table }
  307. old_symtablestack:=symtablestack;
  308. symtablestack:=old_symtablestack.getcopyuntil(current_module.localsymtable);
  309. { save scanner state }
  310. replace_scanner('block literal creation',sstate);
  311. { def representing the original function }
  312. orgpd:=tprocdef(procloadnode.resultdef);
  313. { def representing the corresponding procvar type }
  314. procvarnode:=ctypeconvnode.create_proc_to_procvar(procloadnode.getcopy);
  315. typecheckpass(procvarnode);
  316. orgpv:=tprocvardef(procvarnode.resultdef);
  317. { get blockdef for this kind of procdef }
  318. blockliteraldef:=get_block_literal_type_for_proc(orgpd);
  319. { get the invoke wrapper }
  320. invokepd:=get_invoke_wrapper(orgpd,orgpv);
  321. { get the descriptor }
  322. descriptor:=get_block_literal_descriptor(invokepd,blockliteraldef.size);
  323. { get the ISA pointer for the literal }
  324. blockisasym:=get_block_literal_isa(orgpd);
  325. { get the flags for the block }
  326. blockflags:=get_block_literal_flags(orgpd,invokepd);
  327. { global/simple procedure -> block literal is a typed constant }
  328. if orgpd.is_addressonly then
  329. begin
  330. blockliteralsym:=get_global_proc_literal_sym(blockliteraldef,blockisasym,blockflags,invokepd,descriptor);
  331. { result: address of the block literal }
  332. result:=caddrnode.create(cloadnode.create(blockliteralsym,blockliteralsym.owner));
  333. end
  334. else
  335. begin
  336. result:=get_pascal_method_literal(blockliteraldef,blockisasym,blockflags,procvarnode,invokepd,orgpv,descriptor)
  337. end;
  338. procvarnode.free;
  339. { restore scanner }
  340. restore_scanner(sstate);
  341. { restore symtable stack }
  342. symtablestack.free;
  343. symtablestack:=old_symtablestack;
  344. end;
  345. end.