1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150 |
- {
- Copyright (c) 2018 by Jonas Maebe
- Copyright (c) 2011-2021 by Blaise.ru
- This unit provides helpers for creating procdefs
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- ****************************************************************************
- }
- {$i fpcdefs.inc}
- unit procdefutil;
- interface
- uses
- globtype,procinfo,
- symconst,symtype,symdef,
- node,nbas;
- { create a nested procdef that will be used to outline code from a procedure;
- astruct should usually be nil, except in special cases like the Windows SEH
- exception handling funclets }
- function create_outline_procdef(const basesymname: string; astruct: tabstractrecorddef; potype: tproctypeoption; resultdef: tdef): tprocdef;
- procedure convert_to_funcref_intf(const n:tidstring;var def:tdef);
- function adjust_funcref(var def:tdef;sym,dummysym:tsym):boolean;
- { functionality related to capturing local variables for anonymous functions }
- function get_or_create_capturer(pd:tprocdef):tsym;
- function capturer_add_anonymous_proc(owner:tprocinfo;pd:tprocdef;out capturer:tsym):tobjectdef;
- procedure initialize_capturer(ctx:tprocinfo;var stmt:tstatementnode);
- procedure postprocess_capturer(ctx:tprocinfo);
- procedure convert_captured_syms(pd:tprocdef;tree:tnode);
- implementation
- uses
- cutils,cclasses,verbose,globals,
- fmodule,
- pass_1,
- nobj,ncal,nmem,nld,nutils,
- ngenutil,
- symbase,symsym,symtable,defutil,defcmp,
- pparautl,psub;
- function create_outline_procdef(const basesymname: string; astruct: tabstractrecorddef; potype: tproctypeoption; resultdef: tdef): tprocdef;
- var
- st:TSymTable;
- checkstack: psymtablestackitem;
- oldsymtablestack: tsymtablestack;
- sym:tprocsym;
- begin
- { get actual procedure symtable (skip withsymtables, etc.) }
- st:=nil;
- checkstack:=symtablestack.stack;
- while assigned(checkstack) do
- begin
- st:=checkstack^.symtable;
- if st.symtabletype in [staticsymtable,globalsymtable,localsymtable] then
- break;
- checkstack:=checkstack^.next;
- end;
- { Create a nested procedure, even from main_program_level.
- Furthermore, force procdef and procsym into the same symtable
- (by default, defs are registered with symtablestack.top which may be
- something temporary like exceptsymtable - in that case, procdef can be
- destroyed before procsym, leaving invalid pointers). }
- oldsymtablestack:=symtablestack;
- symtablestack:=nil;
- result:=cprocdef.create(max(normal_function_level,st.symtablelevel)+1,true);
- result.returndef:=resultdef;
- { if the parent is a generic or a specialization, the new function is one
- as well }
- if st.symtabletype=localsymtable then
- result.defoptions:=result.defoptions+(tstoreddef(st.defowner).defoptions*[df_generic,df_specialization]);
- symtablestack:=oldsymtablestack;
- st.insertdef(result);
- result.struct:=astruct;
- { tabstractprocdef constructor sets po_delphi_nested_cc whenever
- nested procvars modeswitch is active. We must be independent of this switch. }
- exclude(result.procoptions,po_delphi_nested_cc);
- result.proctypeoption:=potype;
- { always use the default calling convention }
- result.proccalloption:=pocall_default;
- include(result.procoptions,po_hascallingconvention);
- handle_calling_convention(result,hcc_default_actions_impl);
- sym:=cprocsym.create(basesymname+result.unique_id_str);
- st.insertsym(sym);
- result.procsym:=sym;
- proc_add_definition(result);
- { the code will be assigned directly to the "code" field later }
- result.forwarddef:=false;
- result.aliasnames.insert(result.mangledname);
- end;
- function fileinfo_to_suffix(const fileinfo:tfileposinfo):tsymstr;inline;
- begin
- result:=tostr(fileinfo.moduleindex)+'_'+
- tostr(fileinfo.fileindex)+'_'+
- tostr(fileinfo.line)+'_'+
- tostr(fileinfo.column);
- end;
- const
- anon_funcref_prefix='$FuncRef_';
- capturer_class_name='$CapturerClass';
- { the leading $ is only added when registering the var symbol }
- capturer_var_name='Capturer';
- keepalive_suffix='_keepalive';
- outer_self_field_name='OuterSelf';
- procedure convert_to_funcref_intf(const n:tidstring;var def:tdef);
- var
- oldsymtablestack : tsymtablestack;
- pvdef : tprocvardef absolute def;
- intfdef : tobjectdef;
- invokedef : tprocdef;
- psym : tprocsym;
- sym : tsym;
- st : tsymtable;
- i : longint;
- name : tidstring;
- begin
- if def.typ<>procvardef then
- internalerror(2021040201);
- if not (po_is_function_ref in tprocvardef(pvdef).procoptions) then
- internalerror(2021022101);
- if n='' then
- name:=anon_funcref_prefix+fileinfo_to_suffix(current_filepos)
- else
- name:=n;
- intfdef:=cobjectdef.create(odt_interfacecom,name,interface_iunknown,true);
- include(intfdef.objectoptions,oo_is_funcref);
- include(intfdef.objectoptions,oo_is_invokable);
- include(intfdef.objectoptions,oo_has_virtual);
- intfdef.typesym:=pvdef.typesym;
- pvdef.typesym:=nil;
- if cs_generate_rtti in current_settings.localswitches then
- include(intfdef.objectoptions,oo_can_have_published);
- oldsymtablestack:=symtablestack;
- symtablestack:=nil;
- invokedef:=tprocdef(pvdef.getcopyas(procdef,pc_normal_no_paras,'',false));
- invokedef.struct:=intfdef;
- invokedef.forwarddef:=false;
- include(invokedef.procoptions,po_overload);
- include(invokedef.procoptions,po_virtualmethod);
- invokedef.procsym:=cprocsym.create(method_name_funcref_invoke_decl);
- if cs_generate_rtti in current_settings.localswitches then
- invokedef.visibility:=vis_published
- else
- invokedef.visibility:=vis_public;
- intfdef.symtable.insertsym(invokedef.procsym);
- intfdef.symtable.insertdef(invokedef);
- if pvdef.is_generic or pvdef.is_specialization then
- begin
- if assigned(pvdef.genericdef) and (pvdef.genericdef.typ<>objectdef) then
- internalerror(2021040501);
- intfdef.genericdef:=pvdef.genericdef;
- intfdef.defoptions:=intfdef.defoptions+(pvdef.defoptions*[df_generic,df_specialization]);
- { in case of a generic we move all involved syms/defs to the interface }
- intfdef.genericparas:=pvdef.genericparas;
- pvdef.genericparas:=nil;
- for i:=0 to intfdef.genericparas.count-1 do
- begin
- sym:=tsym(intfdef.genericparas[i]);
- if sym.owner<>pvdef.parast then
- continue;
- sym.changeowner(intfdef.symtable);
- if (sym.typ=typesym) and (ttypesym(sym).typedef.owner=pvdef.parast) then
- ttypesym(sym).typedef.changeowner(intfdef.symtable);
- end;
- end;
- { now move the symtable over }
- invokedef.parast.free;
- invokedef.parast:=pvdef.parast;
- invokedef.parast.defowner:=invokedef;
- pvdef.parast:=nil;
- for i:=0 to invokedef.parast.symlist.count-1 do
- begin
- sym:=tsym(invokedef.parast.symlist[i]);
- if sym.typ<>paravarsym then
- continue;
- if tparavarsym(sym).vardef=pvdef then
- tparavarsym(sym).vardef:=intfdef;
- end;
- symtablestack:=oldsymtablestack;
- if invokedef.returndef=pvdef then
- invokedef.returndef:=intfdef;
- handle_calling_convention(invokedef,hcc_default_actions_intf_struct);
- proc_add_definition(invokedef);
- invokedef.calcparas;
- { def is not owned, so it can be simply freed }
- def.free;
- def:=intfdef;
- end;
- function adjust_funcref(var def:tdef;sym,dummysym:tsym):boolean;
- var
- sympos : tfileposinfo;
- name : string;
- begin
- result:=false;
- if (def.typ<>procvardef) and not is_funcref(def) then
- internalerror(2022020401);
- if assigned(sym) and not (sym.typ=typesym) then
- internalerror(2022020402);
- { these always support everything, no "of object" or
- "is_nested" is allowed }
- if is_nested_pd(tprocvardef(def)) or
- is_methodpointer(def) then
- cgmessage(type_e_function_reference_kind);
- if not (po_is_block in tprocvardef(def).procoptions) then
- begin
- if assigned(dummysym) then
- ttypesym(dummysym).typedef:=nil;
- if assigned(sym) then
- begin
- ttypesym(sym).typedef:=nil;
- name:=sym.name;
- end
- else
- name:='';
- convert_to_funcref_intf(name,def);
- if assigned(sym) then
- ttypesym(sym).typedef:=def;
- if assigned(dummysym) then
- ttypesym(dummysym).typedef:=def;
- build_vmt(tobjectdef(def));
- result:=true;
- end
- else
- begin
- if assigned(sym) and (sym.refs>0) then
- begin
- { find where the symbol was used and trigger
- a "symbol not completely defined" error }
- if not fileinfo_of_typesym_in_def(def,sym,sympos) then
- sympos:=sym.fileinfo;
- messagepos1(sympos,type_e_type_is_not_completly_defined,sym.realname);
- end;
- end;
- end;
- function funcref_intf_for_proc(pd:tabstractprocdef;const suffix:string):tobjectdef;
- var
- name : tsymstr;
- sym : tsym;
- symowner : tsymtable;
- oldsymtablestack: TSymtablestack;
- invokedef: tprocdef;
- begin
- if pd.is_generic then
- internalerror(2022010710);
- name:='funcrefintf_'+suffix;
- if pd.owner.symtabletype=globalsymtable then
- symowner:=current_module.localsymtable
- else
- symowner:=pd.owner;
- sym:=tsym(symowner.find(name));
- if assigned(sym) then
- begin
- if sym.typ<>typesym then
- internalerror(2022010708);
- if not is_funcref(ttypesym(sym).typedef) then
- internalerror(2022010709);
- result:=tobjectdef(ttypesym(sym).typedef);
- exit;
- end;
- name:='$'+name;
- result:=cobjectdef.create(odt_interfacecom,name,interface_iunknown,false);
- include(result.objectoptions,oo_is_funcref);
- include(result.objectoptions,oo_is_invokable);
- sym:=ctypesym.create(name,result);
- oldsymtablestack:=symtablestack;
- symtablestack:=nil;
- invokedef:=tprocdef(pd.getcopyas(procdef,pc_normal,'',false));
- invokedef.struct:=result;
- invokedef.visibility:=vis_public;
- invokedef.procsym:=cprocsym.create(method_name_funcref_invoke_decl);
- include(invokedef.procoptions,po_virtualmethod);
- exclude(invokedef.procoptions,po_staticmethod);
- exclude(invokedef.procoptions,po_classmethod);
- invokedef.forwarddef:=false;
- symtablestack:=oldsymtablestack;
- result.symtable.insertsym(invokedef.procsym);
- result.symtable.insertdef(invokedef);
- handle_calling_convention(invokedef,hcc_default_actions_intf_struct);
- proc_add_definition(invokedef);
- invokedef.calcparas;
- include(result.objectoptions,oo_has_virtual);
- symowner.insertsym(sym);
- symowner.insertdef(result);
- end;
- {.$define DEBUG_CAPTURER}
- function get_capturer(pd:tprocdef):tabstractvarsym;
- function getsym(st:tsymtable;typ:tsymtyp):tabstractvarsym;
- begin
- result:=tabstractvarsym(st.find(capturer_var_name));
- if not assigned(result) then
- internalerror(2022010703);
- if result.typ<>typ then
- internalerror(2022010704);
- if not is_class(result.vardef) then
- internalerror(2022010705);
- end;
- begin
- case pd.proctypeoption of
- potype_unitfinalize,
- potype_unitinit,
- potype_proginit:
- begin
- if not assigned(pd.owner) then
- internalerror(2022052401);
- if pd.owner.symtabletype<>staticsymtable then
- internalerror(2022052402);
- result:=getsym(pd.owner,staticvarsym);
- end;
- else
- begin
- if not assigned(pd.localst) then
- internalerror(2022020502);
- result:=getsym(pd.localst,localvarsym);
- end;
- end;
- end;
- function get_capturer_alive(pd:tprocdef):tabstractvarsym;
- function getsym(st:tsymtable;typ:tsymtyp):tabstractvarsym;
- begin
- result:=tabstractvarsym(st.find(capturer_var_name+keepalive_suffix));
- if not assigned(result) then
- internalerror(2022051703);
- if result.typ<>typ then
- internalerror(2022051704);
- if not is_interfacecom(result.vardef) then
- internalerror(2022051705);
- end;
- begin
- case pd.proctypeoption of
- potype_unitfinalize,
- potype_unitinit,
- potype_proginit:
- begin
- if not assigned(pd.owner) then
- internalerror(2022052403);
- if pd.owner.symtabletype<>staticsymtable then
- internalerror(2022052404);
- result:=getsym(pd.owner,staticvarsym);
- end;
- else
- begin
- if not assigned(pd.localst) then
- internalerror(2022051702);
- result:=getsym(pd.localst,localvarsym);
- end;
- end;
- end;
- function get_or_create_capturer(pd:tprocdef):tsym;
- var
- name : tsymstr;
- parent,
- def : tobjectdef;
- typesym : tsym;
- keepalive : tabstractvarsym;
- intfimpl : TImplementedInterface;
- st : tsymtable;
- begin
- if pd.has_capturer then
- begin
- result:=get_capturer(pd);
- end
- else
- begin
- parent:=tobjectdef(search_system_type('TINTERFACEDOBJECT').typedef);
- if not is_class(parent) then
- internalerror(2022010706);
- name:=capturer_class_name+'_'+fileinfo_to_suffix(pd.fileinfo);
- case pd.proctypeoption of
- potype_unitfinalize,
- potype_unitinit,
- potype_proginit:
- st:=pd.owner;
- else
- st:=pd.localst;
- end;
- def:=cobjectdef.create(odt_class,name,parent,false);
- typesym:=ctypesym.create(name,def);
- typesym.fileinfo:=pd.fileinfo;
- st.insertdef(def);
- st.insertsym(typesym);
- if df_generic in pd.defoptions then
- include(def.defoptions,df_generic);
- { don't set df_specialization as in that case genericdef needs to be
- set, but the local symtables are freed once a unit is finished }
- {if df_specialization in pd.defoptions then
- begin
- if not assigned(pd.genericdef) or (pd.genericdef.typ<>procdef) then
- internalerror(2022020501);
- def.genericdef:=tstoreddef(get_capturer(tprocdef(pd.genericdef)).vardef);
- include(def.defoptions,df_specialization);
- end;}
- if st.symtabletype=localsymtable then
- result:=clocalvarsym.create('$'+capturer_var_name,vs_value,def,[])
- else
- result:=cstaticvarsym.create('$'+capturer_var_name,vs_value,def,[]);
- result.fileinfo:=pd.fileinfo;
- st.insertsym(result);
- addsymref(result);
- if st.symtabletype=localsymtable then
- keepalive:=clocalvarsym.create('$'+capturer_var_name+keepalive_suffix,vs_value,interface_iunknown,[])
- else
- keepalive:=cstaticvarsym.create('$'+capturer_var_name+keepalive_suffix,vs_value,interface_iunknown,[]);
- keepalive.fileinfo:=pd.fileinfo;
- st.insertsym(keepalive);
- addsymref(keepalive);
- if st.symtabletype<>localsymtable then
- begin
- cnodeutils.insertbssdata(tstaticvarsym(result));
- cnodeutils.insertbssdata(tstaticvarsym(keepalive));
- end;
- { avoid warnings as these symbols are initialized using initialize_capturer
- after parsing the body }
- tabstractvarsym(result).varstate:=vs_readwritten;
- keepalive.varstate:=vs_readwritten;
- pd.has_capturer:=true;
- end;
- end;
- function can_be_captured(sym:tsym):boolean;
- begin
- result:=false;
- if not (sym.typ in [localvarsym,paravarsym]) then
- exit;
- if tabstractnormalvarsym(sym).varoptions*[vo_is_result,vo_is_funcret]<>[] then
- exit;
- if sym.typ=paravarsym then
- begin
- if (tparavarsym(sym).varspez in [vs_out,vs_var]) and
- not (vo_is_self in tparavarsym(sym).varoptions) then
- exit;
- if is_open_array(tparavarsym(sym).vardef) then
- exit;
- end;
- result:=true;
- end;
- type
- tsym_mapping = record
- oldsym:tsym;
- newsym:tsym;
- end;
- psym_mapping = ^tsym_mapping;
- function replace_self_sym(var n:tnode;arg:pointer):foreachnoderesult;
- var
- mapping : psym_mapping absolute arg;
- ld : tloadnode;
- begin
- if n.nodetype=loadn then
- begin
- ld:=tloadnode(n);
- if ld.symtableentry=mapping^.oldsym then
- begin
- ld.symtableentry:=mapping^.newsym;
- { make sure that the node is processed again }
- ld.resultdef:=nil;
- if assigned(ld.left) then
- begin
- { no longer loaded through the frame pointer }
- ld.left.free;
- ld.left:=nil;
- end;
- typecheckpass(n);
- end;
- end;
- result:=fen_true;
- end;
- procedure capture_captured_syms(pd:tprocdef;owner:tprocinfo;capturedef:tobjectdef);
- var
- curpd : tprocdef;
- subcapturer : tobjectdef;
- symstodo : TFPList;
- i : longint;
- sym : tsym;
- fieldsym : tfieldvarsym;
- fieldname : tsymstr;
- begin
- if not pd.was_anonymous or not assigned(pd.capturedsyms) or (pd.capturedsyms.count=0) then
- exit;
- { capture all variables that the original procdef captured }
- curpd:=owner.procdef;
- subcapturer:=capturedef;
- symstodo:=tfplist.create;
- for i:=0 to pd.capturedsyms.count-1 do
- if can_be_captured(pcapturedsyminfo(pd.capturedsyms[i])^.sym) then
- symstodo.add(pcapturedsyminfo(pd.capturedsyms[i])^.sym);
- while symstodo.count>0 do
- begin
- { we know we have symbols left to capture thus we either have a
- symbol that's located in the capturer of the current procdef or
- we need to put in the OuterSelf reference }
- if curpd=owner.procdef then
- subcapturer:=capturedef
- else
- subcapturer:=tobjectdef(tabstractvarsym(get_or_create_capturer(curpd)).vardef);
- i:=0;
- while i<symstodo.count do
- begin
- sym:=tsym(symstodo[i]);
- if (sym.owner=curpd.localst) or
- (sym.owner=curpd.parast) then
- begin
- {$ifdef DEBUG_CAPTURER}writeln('Symbol ',sym.name,' captured from ',curpd.procsym.name);{$endif}
- { the symbol belongs to the current procdef, so add a field to
- the capturer if it doesn't already exist }
- if vo_is_self in tabstractnormalvarsym(sym).varoptions then
- fieldname:=outer_self_field_name
- else
- fieldname:=sym.name;
- fieldsym:=tfieldvarsym(subcapturer.symtable.find(fieldname));
- if not assigned(fieldsym) then
- begin
- {$ifdef DEBUG_CAPTURER}writeln('Adding field ',fieldname,' to ',subcapturer.typesym.name);{$endif}
- if vo_is_self in tabstractnormalvarsym(sym).varoptions then
- fieldname:='$'+fieldname;
- fieldsym:=cfieldvarsym.create(fieldname,vs_value,tabstractvarsym(sym).vardef,[]);
- fieldsym.fileinfo:=sym.fileinfo;
- subcapturer.symtable.insertsym(fieldsym);
- tabstractrecordsymtable(subcapturer.symtable).addfield(fieldsym,vis_public);
- end;
- if not assigned(tabstractnormalvarsym(sym).capture_sym) then
- tabstractnormalvarsym(sym).capture_sym:=fieldsym
- else if tabstractnormalvarsym(sym).capture_sym<>fieldsym then
- internalerror(2022011602);
- symstodo.delete(i);
- end
- else
- inc(i);
- end;
- if symstodo.count>0 then
- begin
- if curpd.owner.symtabletype<>localsymtable then
- internalerror(2022011001);
- { there are still symbols left, so before we move to the parent
- procdef we add the OuterSelf field to set up the chain of
- capturers }
- {$ifdef DEBUG_CAPTURER}writeln('Initialize capturer for ',curpd.procsym.name);{$endif}
- { we no longer need the curpd, but we need the parent, so change
- curpd here }
- curpd:=tprocdef(curpd.owner.defowner);
- if curpd.typ<>procdef then
- internalerror(2022011002);
- if not assigned(subcapturer.symtable.find(outer_self_field_name)) then
- begin
- {$ifdef DEBUG_CAPTURER}writeln('Adding field OuterSelf to ',subcapturer.typesym.name);{$endif}
- if subcapturer.owner.symtablelevel>normal_function_level then
- { the outer self is the capturer of the outer procdef }
- sym:=get_or_create_capturer(curpd)
- else
- begin
- { the outer self is the self of the method }
- if not (curpd.owner.symtabletype in [objectsymtable,recordsymtable]) then
- internalerror(2022011603);
- sym:=tsym(curpd.parast.find('self'));
- if not assigned(sym) then
- internalerror(2022011604);
- end;
- { add the keep alive IUnknown symbol }
- fieldsym:=cfieldvarsym.create('$'+outer_self_field_name+keepalive_suffix,vs_value,interface_iunknown,[]);
- fieldsym.fileinfo:=sym.fileinfo;
- subcapturer.symtable.insertsym(fieldsym);
- tabstractrecordsymtable(subcapturer.symtable).addfield(fieldsym,vis_public);
- { add the capturer symbol }
- fieldsym:=cfieldvarsym.create('$'+outer_self_field_name,vs_value,tabstractvarsym(sym).vardef,[]);
- fieldsym.fileinfo:=sym.fileinfo;
- subcapturer.symtable.insertsym(fieldsym);
- tabstractrecordsymtable(subcapturer.symtable).addfield(fieldsym,vis_public);
- if (sym.typ=paravarsym) and (vo_is_self in tparavarsym(sym).varoptions) then
- begin
- if assigned(tparavarsym(sym).capture_sym) then
- internalerror(2022011705);
- tparavarsym(sym).capture_sym:=fieldsym;
- end;
- end;
- end;
- end;
- symstodo.free;
- end;
- function capturer_add_anonymous_proc(owner:tprocinfo;pd:tprocdef;out capturer:tsym):tobjectdef;
- var
- capturedef : tobjectdef;
- implintf : TImplementedInterface;
- invokename : tsymstr;
- i : longint;
- outerself,
- fpsym,
- selfsym,
- sym : tsym;
- info : pcapturedsyminfo;
- pi : tprocinfo;
- mapping : tsym_mapping;
- invokedef,
- parentdef,
- curpd : tprocdef;
- begin
- capturer:=nil;
- result:=funcref_intf_for_proc(pd,fileinfo_to_suffix(pd.fileinfo));
- if df_generic in pd.defoptions then
- begin
- if (po_anonymous in pd.procoptions) and
- assigned(pd.capturedsyms) and
- (pd.capturedsyms.count>0) then
- begin
- { only check whether the symbols can be captured, but don't
- convert anything to avoid problems }
- for i:=0 to pd.capturedsyms.count-1 do
- begin
- info:=pcapturedsyminfo(pd.capturedsyms[i]);
- if not can_be_captured(info^.sym) then
- MessagePos1(info^.fileinfo,sym_e_symbol_no_capture,info^.sym.realname)
- end;
- end;
- exit;
- end;
- capturer:=get_or_create_capturer(owner.procdef);
- if not (capturer.typ in [localvarsym,staticvarsym]) then
- internalerror(2022010711);
- capturedef:=tobjectdef(tabstractvarsym(capturer).vardef);
- if not is_class(capturedef) then
- internalerror(2022010712);
- implintf:=find_implemented_interface(capturedef,result);
- if assigned(implintf) then
- begin
- { this can only already be an implemented interface if a named procdef
- was assigned to a function ref at an earlier point, an anonymous
- function can be used only once }
- if po_anonymous in pd.procoptions then
- internalerror(2022010713);
- exit;
- end;
- implintf:=capturedef.register_implemented_interface(result,true);
- invokename:=method_name_funcref_invoke_decl+'$'+fileinfo_to_suffix(pd.fileinfo);
- if po_anonymous in pd.procoptions then
- begin
- { turn the anonymous function into a method of the capturer }
- pd.changeowner(capturedef.symtable);
- pd.struct:=capturedef;
- exclude(pd.procoptions,po_anonymous);
- exclude(pd.procoptions,po_delphi_nested_cc);
- pd.was_anonymous:=true;
- pd.procsym.ChangeOwnerAndName(capturedef.symtable,upcase(invokename));
- pd.parast.symtablelevel:=normal_function_level;
- pd.localst.symtablelevel:=normal_function_level;
- { retrieve framepointer and self parameters if any }
- fpsym:=nil;
- selfsym:=nil;
- for i:=0 to pd.parast.symlist.count-1 do
- begin
- sym:=tsym(pd.parast.symlist[i]);
- if sym.typ<>paravarsym then
- continue;
- if vo_is_parentfp in tparavarsym(sym).varoptions then
- fpsym:=sym
- else if vo_is_self in tparavarsym(sym).varoptions then
- selfsym:=sym;
- if assigned(fpsym) and assigned(selfsym) then
- break;
- end;
- { get rid of the framepointer parameter }
- if assigned(fpsym) then
- pd.parast.deletesym(fpsym);
- outerself:=nil;
- { complain about all symbols that can't be captured and add the symbols
- to this procdefs capturedsyms if it isn't a top level function }
- if assigned(pd.capturedsyms) and (pd.capturedsyms.count>0) then
- begin
- for i:=0 to pd.capturedsyms.count-1 do
- begin
- info:=pcapturedsyminfo(pd.capturedsyms[i]);
- if not can_be_captured(info^.sym) then
- MessagePos1(info^.fileinfo,sym_e_symbol_no_capture,info^.sym.realname)
- else if info^.sym=selfsym then
- begin
- { we need to replace the captured "dummy" self parameter
- with the real self parameter symbol from the surrounding
- method }
- if not assigned(outerself) then
- outerself:=tsym(owner.get_normal_proc.procdef.parast.find('self'));
- if not assigned(outerself) then
- internalerror(2022010905);
- { the anonymous function can only be a direct child of the
- owner }
- pi:=owner.get_first_nestedproc;
- while assigned(pi) do
- begin
- if pi.procdef=pd then
- break;
- pi:=tprocinfo(pi.next);
- end;
- if not assigned(pi) then
- internalerror(2022010906);
- mapping.oldsym:=selfsym;
- mapping.newsym:=outerself;
- { replace all uses of the captured Self by the new Self
- parameter }
- foreachnodestatic(pm_preprocess,tcgprocinfo(pi).code,@replace_self_sym,@mapping);
- { update the captured symbol }
- info^.sym:=outerself;
- end
- else if info^.sym.owner.defowner<>owner.procdef then
- owner.procdef.add_captured_sym(info^.sym,info^.fileinfo);
- end;
- end;
- { delete the original self parameter }
- if assigned(selfsym) then
- pd.parast.deletesym(selfsym);
- { note: don't call insert_self_and_vmt_para here, as that is later on
- done when building the VMT }
- end
- else
- internalerror(2022022201);
- implintf.AddMapping(upcase(result.objrealname^+'.')+method_name_funcref_invoke_find,upcase(invokename));
- capture_captured_syms(pd,owner,capturedef);
- end;
- function load_capturer(capturer:tabstractvarsym):tnode;inline;
- begin
- result:=cloadnode.create(capturer,capturer.owner);
- end;
- function instantiate_capturer(capturer_sym:tabstractvarsym):tnode;
- var
- capturer_def : tobjectdef;
- ctor : tprocsym;
- begin
- capturer_def:=tobjectdef(capturer_sym.vardef);
- { Neither TInterfacedObject, nor TCapturer have a custom constructor }
- ctor:=tprocsym(class_tobject.symtable.Find('CREATE'));
- if not assigned(ctor) then
- internalerror(2022010801);
- { Insert "Capturer := TCapturer.Create()" as the first statement of the routine }
- result:=cloadvmtaddrnode.create(ctypenode.create(capturer_def));
- result:=ccallnode.create(nil,ctor,capturer_def.symtable,result,[],nil);
- result:=cassignmentnode.create(load_capturer(capturer_sym),result);
- end;
- procedure initialize_captured_paras(pd:tprocdef;capturer:tabstractvarsym;var stmt:tstatementnode);
- var
- i : longint;
- psym: tparavarsym;
- n : tnode;
- begin
- for i:=0 to pd.paras.count-1 do
- begin
- psym:=tparavarsym(pd.paras[i]);
- if not psym.is_captured then
- continue;
- {$ifdef DEBUG_CAPTURER}writeln(#9'initialize captured parameter ',psym.RealName);{$endif}
- n:=cloadnode.create(psym,psym.owner);
- if psym.capture_sym.owner.defowner<>capturer.vardef then
- internalerror(2022010903);
- n:=cassignmentnode.create(
- csubscriptnode.create(psym.capture_sym,cloadnode.create(capturer,capturer.owner)),
- n
- );
- addstatement(stmt,n);
- end;
- end;
- procedure attach_outer_capturer(ctx:tprocinfo;capturer:tabstractvarsym;var stmt:tstatementnode);
- var
- alivefield,
- selffield : tfieldvarsym;
- outeralive,
- outercapturer : tabstractvarsym;
- alivenode,
- selfnode : tnode;
- begin
- if not ctx.procdef.was_anonymous and
- not (ctx.procdef.owner.symtabletype=localsymtable) then
- exit;
- selffield:=tfieldvarsym(tobjectdef(capturer.vardef).symtable.find(outer_self_field_name));
- if not assigned(selffield) then
- { we'll simply assume that we don't need the outer capturer }
- exit;
- alivefield:=tfieldvarsym(tobjectdef(capturer.vardef).symtable.find(outer_self_field_name+keepalive_suffix));
- if not assigned(alivefield) then
- internalerror(2022051701);
- if ctx.procdef.was_anonymous then
- begin
- selfnode:=load_self_node;
- alivenode:=selfnode.getcopy;
- end
- else
- begin
- outercapturer:=get_capturer(tprocdef(ctx.procdef.owner.defowner));
- if not assigned(outercapturer) then
- internalerror(2022011605);
- selfnode:=cloadnode.create(outercapturer,outercapturer.owner);
- outeralive:=get_capturer_alive(tprocdef(ctx.procdef.owner.defowner));
- if not assigned(outeralive) then
- internalerror(2022051706);
- alivenode:=cloadnode.create(outeralive,outeralive.owner);
- end;
- addstatement(stmt,cassignmentnode.create(
- csubscriptnode.create(
- selffield,
- cloadnode.create(
- capturer,
- capturer.owner
- )
- ),
- selfnode));
- addstatement(stmt,cassignmentnode.create(
- csubscriptnode.create(
- alivefield,
- cloadnode.create(
- capturer,
- capturer.owner
- )
- ),
- alivenode));
- end;
- procedure initialize_capturer(ctx:tprocinfo;var stmt:tstatementnode);
- var
- capturer_sym,
- keepalive_sym : tabstractvarsym;
- begin
- if ctx.procdef.has_capturer then
- begin
- capturer_sym:=get_capturer(ctx.procdef);
- {$ifdef DEBUG_CAPTURER}writeln('initialize_capturer @ ',ctx.procdef.procsym.RealName);{$endif}
- addstatement(stmt,instantiate_capturer(capturer_sym));
- attach_outer_capturer(ctx,capturer_sym,stmt);
- initialize_captured_paras(ctx.procdef,capturer_sym,stmt);
- keepalive_sym:=get_capturer_alive(ctx.procdef);
- if not assigned(keepalive_sym) then
- internalerror(2022010701);
- addstatement(stmt,cassignmentnode.create(cloadnode.create(keepalive_sym,keepalive_sym.owner),load_capturer(capturer_sym)));
- end;
- end;
- procedure postprocess_capturer(ctx: tprocinfo);
- var
- def: tobjectdef;
- begin
- if not ctx.procdef.has_capturer then
- exit;
- def:=tobjectdef(get_capturer(ctx.procdef).vardef);
- {$ifdef DEBUG_CAPTURER}writeln('process capturer ',def.typesym.Name);{$endif}
- { These two are delayed until this point because
- ... we have been adding fields on-the-fly }
- tabstractrecordsymtable(def.symtable).addalignmentpadding;
- { ... we have been adding interfaces on-the-fly }
- build_vmt(def);
- end;
- type
- tconvert_arg=record
- mappings:tfplist;
- end;
- pconvert_arg=^tconvert_arg;
- tconvert_mapping=record
- oldsym:tsym;
- newsym:tsym;
- selfnode:tnode;
- end;
- pconvert_mapping=^tconvert_mapping;
- function convert_captured_sym(var n:tnode;arg:pointer):foreachnoderesult;
- var
- convertarg : pconvert_arg absolute arg;
- mapping : pconvert_mapping;
- i : longint;
- old_filepos : tfileposinfo;
- begin
- result:=fen_true;
- if n.nodetype<>loadn then
- exit;
- for i:=0 to convertarg^.mappings.count-1 do
- begin
- mapping:=convertarg^.mappings[i];
- if tloadnode(n).symtableentry<>mapping^.oldsym then
- continue;
- old_filepos:=current_filepos;
- current_filepos:=n.fileinfo;
- n.free;
- n:=csubscriptnode.create(mapping^.newsym,mapping^.selfnode.getcopy);
- typecheckpass(n);
- current_filepos:=old_filepos;
- break;
- end;
- end;
- procedure convert_captured_syms(pd:tprocdef;tree:tnode);
- function self_tree_for_sym(selfsym:tsym;fieldsym:tsym):tnode;
- var
- fieldowner : tdef;
- newsym : tsym;
- begin
- result:=cloadnode.create(selfsym,selfsym.owner);
- fieldowner:=tdef(fieldsym.owner.defowner);
- newsym:=selfsym;
- while (tabstractvarsym(newsym).vardef<>fieldowner) do
- begin
- newsym:=tsym(tobjectdef(tabstractvarsym(newsym).vardef).symtable.find(outer_self_field_name));
- if not assigned(newsym) then
- internalerror(2022011101);
- result:=csubscriptnode.create(newsym,result);
- end;
- end;
- var
- i,j : longint;
- capturer : tobjectdef;
- capturedsyms : tfplist;
- convertarg : tconvert_arg;
- mapping : pconvert_mapping;
- invokepd : tprocdef;
- selfsym,
- sym : tsym;
- info: pcapturedsyminfo;
- begin
- {$ifdef DEBUG_CAPTURER}writeln('Converting captured symbols of ',pd.procsym.name);{$endif}
- convertarg.mappings:=tfplist.create;
- capturedsyms:=tfplist.create;
- if pd.was_anonymous and
- assigned(pd.capturedsyms) and
- (pd.capturedsyms.count>0) then
- begin
- {$ifdef DEBUG_CAPTURER}writeln('Converting symbols of converted anonymous function ',pd.procsym.name);{$endif}
- { this is a converted anonymous function, so rework all symbols that
- now belong to the new Self }
- selfsym:=tsym(pd.parast.find('self'));
- if not assigned(selfsym) then
- internalerror(2022010809);
- for i:=0 to pd.capturedsyms.count-1 do
- begin
- sym:=tsym(pcapturedsyminfo(pd.capturedsyms[i])^.sym);
- if not can_be_captured(sym) then
- continue;
- {$ifdef DEBUG_CAPTURER}writeln('Replacing symbol ',sym.Name);{$endif}
- new(mapping);
- mapping^.oldsym:=sym;
- mapping^.newsym:=tabstractnormalvarsym(sym).capture_sym;
- if not assigned(mapping^.newsym) then
- internalerror(2022010810);
- mapping^.selfnode:=self_tree_for_sym(selfsym,mapping^.newsym);
- convertarg.mappings.add(mapping);
- capturedsyms.add(sym);
- end;
- end;
- if (pd.parast.symtablelevel>normal_function_level) and
- assigned(pd.capturedsyms) and
- (pd.capturedsyms.count>0) then
- begin
- {$ifdef DEBUG_CAPTURER}writeln('Converting symbols of nested function ',pd.procsym.name);{$endif}
- { this is a nested function, so rework all symbols that are used from
- a parent function, but that might have been captured }
- for i:=0 to pd.capturedsyms.count-1 do
- begin
- sym:=tsym(pcapturedsyminfo(pd.capturedsyms[i])^.sym);
- if not can_be_captured(sym) or not assigned(tabstractnormalvarsym(sym).capture_sym) then
- continue;
- {$ifdef DEBUG_CAPTURER}writeln('Replacing symbol ',sym.Name);{$endif}
- new(mapping);
- mapping^.oldsym:=sym;
- mapping^.newsym:=tabstractnormalvarsym(sym).capture_sym;
- capturer:=tobjectdef(mapping^.newsym.owner.defowner);
- if not is_class(capturer) then
- internalerror(2022012701);
- if not (capturer.typesym.owner.symtabletype in [localsymtable,staticsymtable]) then
- internalerror(2022012702);
- selfsym:=tsym(capturer.typesym.owner.find(capturer_var_name));
- if not assigned(selfsym) then
- internalerror(2022012703);
- mapping^.selfnode:=self_tree_for_sym(selfsym,mapping^.newsym);
- convertarg.mappings.add(mapping);
- capturedsyms.add(sym);
- end;
- end;
- if pd.has_capturer then
- begin
- {$ifdef DEBUG_CAPTURER}writeln('Converting symbols of function ',pd.procsym.name,' with capturer');{$endif}
- { this procedure has a capturer, so rework all symbols that are
- captured in that capturer }
- selfsym:=get_capturer(pd);
- for i:=0 to pd.localst.symlist.count-1 do
- begin
- sym:=tsym(pd.localst.symlist[i]);
- if sym.typ<>localvarsym then
- continue;
- if assigned(tabstractnormalvarsym(sym).capture_sym) then
- if capturedsyms.indexof(sym)<0 then
- capturedsyms.add(sym);
- end;
- for i:=0 to pd.parast.symlist.count-1 do
- begin
- sym:=tsym(pd.parast.symlist[i]);
- if sym.typ<>paravarsym then
- continue;
- if assigned(tabstractnormalvarsym(sym).capture_sym) and
- { no need to adjust accesses to the outermost Self inside the
- outermost method }
- not (vo_is_self in tabstractvarsym(sym).varoptions) then
- if capturedsyms.indexof(sym)<0 then
- capturedsyms.add(sym);
- end;
- for i:=0 to capturedsyms.count-1 do
- begin
- new(mapping);
- mapping^.oldsym:=tsym(capturedsyms[i]);
- {$ifdef DEBUG_CAPTURER}writeln('Replacing symbol ',mapping^.oldsym.Name);{$endif}
- mapping^.newsym:=tabstractnormalvarsym(mapping^.oldsym).capture_sym;
- if not assigned(mapping^.newsym) then
- internalerror(2022010805);
- mapping^.selfnode:=self_tree_for_sym(selfsym,mapping^.newsym);
- convertarg.mappings.add(mapping);
- end;
- end;
- { not required anymore }
- capturedsyms.free;
- foreachnodestatic(pm_postprocess,tree,@convert_captured_sym,@convertarg);
- for i:=0 to convertarg.mappings.count-1 do
- begin
- mapping:=pconvert_mapping(convertarg.mappings[i]);
- mapping^.selfnode.free;
- dispose(mapping);
- end;
- convertarg.mappings.free;
- end;
- end.
|