procdefutil.pas 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. {
  2. Copyright (c) 2018 by Jonas Maebe
  3. This unit provides helpers for creating procdefs
  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. {$i fpcdefs.inc}
  18. unit procdefutil;
  19. interface
  20. uses
  21. symconst,symtype,symdef,globtype;
  22. { create a nested procdef that will be used to outline code from a procedure;
  23. astruct should usually be nil, except in special cases like the Windows SEH
  24. exception handling funclets }
  25. function create_outline_procdef(const basesymname: string; astruct: tabstractrecorddef; potype: tproctypeoption; resultdef: tdef): tprocdef;
  26. procedure convert_to_funcref_intf(const n:tidstring;var def:tdef);
  27. function adjust_funcref(var def:tdef;sym,dummysym:tsym):boolean;
  28. implementation
  29. uses
  30. cutils,cclasses,verbose,globals,
  31. nobj,
  32. symbase,symsym,symtable,defutil,pparautl;
  33. function create_outline_procdef(const basesymname: string; astruct: tabstractrecorddef; potype: tproctypeoption; resultdef: tdef): tprocdef;
  34. var
  35. st:TSymTable;
  36. checkstack: psymtablestackitem;
  37. oldsymtablestack: tsymtablestack;
  38. sym:tprocsym;
  39. begin
  40. { get actual procedure symtable (skip withsymtables, etc.) }
  41. st:=nil;
  42. checkstack:=symtablestack.stack;
  43. while assigned(checkstack) do
  44. begin
  45. st:=checkstack^.symtable;
  46. if st.symtabletype in [staticsymtable,globalsymtable,localsymtable] then
  47. break;
  48. checkstack:=checkstack^.next;
  49. end;
  50. { Create a nested procedure, even from main_program_level.
  51. Furthermore, force procdef and procsym into the same symtable
  52. (by default, defs are registered with symtablestack.top which may be
  53. something temporary like exceptsymtable - in that case, procdef can be
  54. destroyed before procsym, leaving invalid pointers). }
  55. oldsymtablestack:=symtablestack;
  56. symtablestack:=nil;
  57. result:=cprocdef.create(max(normal_function_level,st.symtablelevel)+1,true);
  58. result.returndef:=resultdef;
  59. { if the parent is a generic or a specialization, the new function is one
  60. as well }
  61. if st.symtabletype=localsymtable then
  62. result.defoptions:=result.defoptions+(tstoreddef(st.defowner).defoptions*[df_generic,df_specialization]);
  63. symtablestack:=oldsymtablestack;
  64. st.insertdef(result);
  65. result.struct:=astruct;
  66. { tabstractprocdef constructor sets po_delphi_nested_cc whenever
  67. nested procvars modeswitch is active. We must be independent of this switch. }
  68. exclude(result.procoptions,po_delphi_nested_cc);
  69. result.proctypeoption:=potype;
  70. { always use the default calling convention }
  71. result.proccalloption:=pocall_default;
  72. include(result.procoptions,po_hascallingconvention);
  73. handle_calling_convention(result,hcc_default_actions_impl);
  74. sym:=cprocsym.create(basesymname+result.unique_id_str);
  75. st.insertsym(sym);
  76. result.procsym:=sym;
  77. proc_add_definition(result);
  78. { the code will be assigned directly to the "code" field later }
  79. result.forwarddef:=false;
  80. result.aliasnames.insert(result.mangledname);
  81. end;
  82. function fileinfo_to_suffix(const fileinfo:tfileposinfo):tsymstr;inline;
  83. begin
  84. result:=tostr(fileinfo.moduleindex)+'_'+
  85. tostr(fileinfo.fileindex)+'_'+
  86. tostr(fileinfo.line)+'_'+
  87. tostr(fileinfo.column);
  88. end;
  89. const
  90. anon_funcref_prefix='$FuncRef_';
  91. procedure convert_to_funcref_intf(const n:tidstring;var def:tdef);
  92. var
  93. oldsymtablestack : tsymtablestack;
  94. pvdef : tprocvardef absolute def;
  95. intfdef : tobjectdef;
  96. invokedef : tprocdef;
  97. psym : tprocsym;
  98. sym : tsym;
  99. st : tsymtable;
  100. i : longint;
  101. name : tidstring;
  102. begin
  103. if def.typ<>procvardef then
  104. internalerror(2021040201);
  105. if not (po_is_function_ref in tprocvardef(pvdef).procoptions) then
  106. internalerror(2021022101);
  107. if n='' then
  108. name:=anon_funcref_prefix+fileinfo_to_suffix(current_filepos)
  109. else
  110. name:=n;
  111. intfdef:=cobjectdef.create(odt_interfacecom,name,interface_iunknown,true);
  112. include(intfdef.objectoptions,oo_is_funcref);
  113. include(intfdef.objectoptions,oo_is_invokable);
  114. include(intfdef.objectoptions,oo_has_virtual);
  115. intfdef.typesym:=pvdef.typesym;
  116. pvdef.typesym:=nil;
  117. if cs_generate_rtti in current_settings.localswitches then
  118. include(intfdef.objectoptions,oo_can_have_published);
  119. oldsymtablestack:=symtablestack;
  120. symtablestack:=nil;
  121. invokedef:=tprocdef(pvdef.getcopyas(procdef,pc_normal_no_paras,'',false));
  122. invokedef.struct:=intfdef;
  123. invokedef.forwarddef:=false;
  124. include(invokedef.procoptions,po_overload);
  125. include(invokedef.procoptions,po_virtualmethod);
  126. invokedef.procsym:=cprocsym.create(method_name_funcref_invoke_decl);
  127. if cs_generate_rtti in current_settings.localswitches then
  128. invokedef.visibility:=vis_published
  129. else
  130. invokedef.visibility:=vis_public;
  131. intfdef.symtable.insertsym(invokedef.procsym);
  132. intfdef.symtable.insertdef(invokedef);
  133. if pvdef.is_generic or pvdef.is_specialization then
  134. begin
  135. if assigned(pvdef.genericdef) and (pvdef.genericdef.typ<>objectdef) then
  136. internalerror(2021040501);
  137. intfdef.genericdef:=pvdef.genericdef;
  138. intfdef.defoptions:=intfdef.defoptions+(pvdef.defoptions*[df_generic,df_specialization]);
  139. { in case of a generic we move all involved syms/defs to the interface }
  140. intfdef.genericparas:=pvdef.genericparas;
  141. pvdef.genericparas:=nil;
  142. for i:=0 to intfdef.genericparas.count-1 do
  143. begin
  144. sym:=tsym(intfdef.genericparas[i]);
  145. if sym.owner<>pvdef.parast then
  146. continue;
  147. sym.changeowner(intfdef.symtable);
  148. if (sym.typ=typesym) and (ttypesym(sym).typedef.owner=pvdef.parast) then
  149. ttypesym(sym).typedef.changeowner(intfdef.symtable);
  150. end;
  151. end;
  152. { now move the symtable over }
  153. invokedef.parast.free;
  154. invokedef.parast:=pvdef.parast;
  155. invokedef.parast.defowner:=invokedef;
  156. pvdef.parast:=nil;
  157. for i:=0 to invokedef.parast.symlist.count-1 do
  158. begin
  159. sym:=tsym(invokedef.parast.symlist[i]);
  160. if sym.typ<>paravarsym then
  161. continue;
  162. if tparavarsym(sym).vardef=pvdef then
  163. tparavarsym(sym).vardef:=intfdef;
  164. end;
  165. symtablestack:=oldsymtablestack;
  166. if invokedef.returndef=pvdef then
  167. invokedef.returndef:=intfdef;
  168. handle_calling_convention(invokedef,hcc_default_actions_intf_struct);
  169. proc_add_definition(invokedef);
  170. invokedef.calcparas;
  171. { def is not owned, so it can be simply freed }
  172. def.free;
  173. def:=intfdef;
  174. end;
  175. function adjust_funcref(var def:tdef;sym,dummysym:tsym):boolean;
  176. var
  177. sympos : tfileposinfo;
  178. name : string;
  179. begin
  180. result:=false;
  181. if (def.typ<>procvardef) and not is_funcref(def) then
  182. internalerror(2022020401);
  183. if assigned(sym) and not (sym.typ=typesym) then
  184. internalerror(2022020402);
  185. { these always support everything, no "of object" or
  186. "is_nested" is allowed }
  187. if is_nested_pd(tprocvardef(def)) or
  188. is_methodpointer(def) then
  189. cgmessage(type_e_function_reference_kind);
  190. if not (po_is_block in tprocvardef(def).procoptions) then
  191. begin
  192. if assigned(dummysym) then
  193. ttypesym(dummysym).typedef:=nil;
  194. if assigned(sym) then
  195. begin
  196. ttypesym(sym).typedef:=nil;
  197. name:=sym.name;
  198. end
  199. else
  200. name:='';
  201. convert_to_funcref_intf(name,def);
  202. if assigned(sym) then
  203. ttypesym(sym).typedef:=def;
  204. if assigned(dummysym) then
  205. ttypesym(dummysym).typedef:=def;
  206. build_vmt(tobjectdef(def));
  207. result:=true;
  208. end
  209. else
  210. begin
  211. if assigned(sym) and (sym.refs>0) then
  212. begin
  213. { find where the symbol was used and trigger
  214. a "symbol not completely defined" error }
  215. if not fileinfo_of_typesym_in_def(def,sym,sympos) then
  216. sympos:=sym.fileinfo;
  217. messagepos1(sympos,type_e_type_is_not_completly_defined,sym.realname);
  218. end;
  219. end;
  220. end;
  221. end.