123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385 |
- {
- Copyright (c) 2014 by Jonas Maebe, Member of the Free Pascal
- development team.
- This unit implements helper routines for "blocks" support
- (http://en.wikipedia.org/wiki/Blocks_(C_language_extension) )
- 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.
- ****************************************************************************
- }
- unit blockutl;
- {$i fpcdefs.inc}
- interface
- uses
- node,nld,ncnv,
- symtype,symdef;
- { accepts a loadnode for a procdef
- returns a node representing the converted code to implement this
- conversion (this node is valid both for typed constant declarations and
- in function bodies). The node is not reused }
- function generate_block_for_procaddr(procloadnode: tloadnode): tnode;
- { for a procdef, return a recorddef representing a block literal for this
- procdef
- for a procvardef, return a basic recorddef representing a block literal
- with enough info to call this procvardef }
- function get_block_literal_type_for_proc(pd: tabstractprocdef): trecorddef;
- implementation
- uses
- verbose,globtype,cutils,constexp,
- pass_1,pparautl,fmodule,
- aasmdata,
- nbas,ncon,nmem,nutils,
- symbase,symconst,symtable,symsym,symcreat,objcutil,defutil,
- paramgr;
- function get_block_literal_type_for_proc(pd: tabstractprocdef): trecorddef;
- begin
- if pd.typ=procvardef then
- result:=trecorddef(search_named_unit_globaltype('BLOCKRTL','FPC_BLOCK_LITERAL_BASE',true).typedef)
- else if pd.is_addressonly then
- result:=trecorddef(search_named_unit_globaltype('BLOCKRTL','FPC_BLOCK_LITERAL_STATIC',true).typedef)
- { todo: nested functions and Objective-C methods }
- else if not is_nested_pd(pd) and
- not is_objcclass(tdef(pd.owner.defowner)) then
- result:=trecorddef(search_named_unit_globaltype('BLOCKRTL','FPC_BLOCK_LITERAL_COMPLEX_PROCVAR',true).typedef)
- else
- internalerror(2014071304);
- end;
- function get_block_literal_isa(orgpd: tprocdef): tstaticvarsym;
- var
- srsym: tsym;
- srsymtable: tsymtable;
- name: tidstring;
- begin
- if orgpd.is_addressonly then
- name:='_NSCONCRETEGLOBALBLOCK'
- else
- name:='_NSCONCRETESTACKBLOCK';
- if not searchsym_in_named_module('BLOCKRTL',name,srsym,srsymtable) or
- (srsym.typ<>staticvarsym) then
- internalerror(2014071501);
- result:=tstaticvarsym(srsym);
- end;
- function get_block_literal_flags(orgpd, invokepd: tprocdef): longint;
- { BlockLiteralFlags }
- const
- BLOCK_HAS_COPY_DISPOSE = 1 shl 25;
- BLOCK_HAS_CXX_OBJ = 1 shl 26;
- BLOCK_IS_GLOBAL = 1 shl 28;
- BLOCK_USE_STRET = 1 shl 29;
- BLOCK_HAS_SIGNATURE = 1 shl 30;
- BLOCK_HAS_EXTENDED_LAYOUT = 1 shl 31;
- begin
- result:=0;
- { BLOCK_HAS_COPY_DISPOSE :
- copy/dispose will be necessary once we support nested procedures, in
- case they capture reference counted types, Objective-C class instances
- or block-type variables
- }
- { BLOCK_HAS_CXX_OBJ:
- we don't support C++ (stack-based) class instances yet
- }
- { BLOCK_IS_GLOBAL:
- set in case the block does not capture any local state; used for
- global functions and in theory also possible for nested functions that
- do not access any variables from their parentfp context
- }
- if orgpd.is_addressonly then
- result:=result or BLOCK_IS_GLOBAL;
- { BLOCK_USE_STRET:
- set in case the invoke function returns its result via a hidden
- parameter
- }
- if paramanager.ret_in_param(invokepd.returndef,orgpd) then
- result:=result or BLOCK_USE_STRET;
- { BLOCK_HAS_SIGNATURE:
- only if this bit is set, the above bit will actually be taken into
- account (for backward compatibility). We need it so that our invoke
- function isn't called as a variadic function, but on the downside this
- requires Mac OS X 10.7 or later
- }
- result:=result or BLOCK_HAS_SIGNATURE;
- { BLOCK_HAS_EXTENDED_LAYOUT:
- no documentation about what this means or what it's good for (clang
- adds it for Objective-C 1 platforms in case garbage collection is
- switched off, but then you also have to actually generate this layout)
- }
- end;
- function get_block_literal_descriptor(invokepd: tprocdef; block_literal_size: tcgint): tstaticvarsym;
- var
- descriptordef: tdef;
- descriptor: tstaticvarsym;
- name: tsymstr;
- srsym: tsym;
- srsymtable: tsymtable;
- begin
- (*
- FPC_Block_descriptor_simple = record
- reserved: culong;
- Block_size: culong;
- { signatures are only for the "ABI.2010.3.16" version, but that's all
- we support because otherwise the callback has to be a C-style
- variadic function, which we cannot (yet?) generate }
- signature: pchar;
- end;
- *)
- { must be a valid Pascal identifier, because we will reference it when
- constructing the block initialiser }
- { we don't have to include the moduleid in this mangledname, because
- the invokepd is a local procedure in the current unit -> unique_id_str
- by itself is unique }
- name:='__FPC_BLOCK_DESCRIPTOR_SIMPLE_'+invokepd.unique_id_str;
- { already exists -> return }
- if searchsym(name,srsym,srsymtable) then
- begin
- if srsym.typ<>staticvarsym then
- internalerror(2014071402);
- result:=tstaticvarsym(srsym);
- exit;
- end;
- { find the type of the descriptor structure }
- descriptordef:=search_named_unit_globaltype('BLOCKRTL','FPC_BLOCK_DESCRIPTOR_SIMPLE',true).typedef;
- { create new static variable }
- descriptor:=cstaticvarsym.create(name,vs_value,descriptordef,[]);
- symtablestack.top.insert(descriptor);
- include(descriptor.symoptions,sp_internal);
- { create typed constant for the descriptor }
- str_parse_typedconst(current_asmdata.AsmLists[al_const],
- '(reserved: 0; Block_size: '+tostr(block_literal_size)+
- '; signature: '''+objcencodemethod(invokepd)+''');',descriptor);
- result:=descriptor;
- end;
- { creates a wrapper function for pd with the C calling convention and an
- extra first parameter pointing to the block "self" pointer. This wrapper is
- what will be assigned to the "invoke" field of the block }
- function get_invoke_wrapper(orgpd: tprocdef; orgpv: tprocvardef): tprocdef;
- var
- wrappername: TIDString;
- srsym: tsym;
- srsymtable: tsymtable;
- begin
- { the copy() is to ensure we don't overflow the maximum identifier length;
- the combination of owner.moduleid and defid will make the name unique }
- wrappername:='__FPC_BLOCK_INVOKE_'+upper(copy(orgpd.procsym.realname,1,60))+'_'+tostr(orgpd.owner.moduleid)+'_'+orgpd.unique_id_str;
- { already an invoke wrapper for this procsym -> reuse }
- if searchsym(wrappername,srsym,srsymtable) then
- begin
- if (srsym.typ<>procsym) or
- (tprocsym(srsym).procdeflist.count<>1) then
- internalerror(2014071503);
- result:=tprocdef(tprocsym(srsym).procdeflist[0]);
- exit;
- end;
- { bare copy, so that self etc are not inserted }
- result:=tprocdef(orgpd.getcopyas(procdef,pc_bareproc,''));
- { will be called accoding to the ABI conventions }
- result.proccalloption:=pocall_cdecl;
- { add po_is_block so that a block "self" pointer gets added (of the type
- returned by get_block_literal_type_for_proc()) }
- include(result.procoptions,po_is_block);
- { now insert self/vmt/funcret according to the newly set calling
- convention }
- insert_self_and_vmt_para(result);
- insert_funcret_para(result);
- finish_copied_procdef(result,wrappername,current_module.localsymtable,nil);
- if orgpd.is_addressonly then
- begin
- result.synthetickind:=tsk_callthrough;
- result.skpara:=orgpd;
- end
- else
- begin
- { alias for the type to invoke the procvar, used in the symcreat
- handling of tsk_block_invoke_procvar }
- result.localst.insert(ctypesym.create('__FPC_BLOCK_INVOKE_PV_TYPE',orgpv));
- result.synthetickind:=tsk_block_invoke_procvar;
- end;
- end;
- { compose a block literal for a static block (one without context) }
- function get_global_proc_literal_sym(blockliteraldef: tdef; blockisasym: tstaticvarsym; blockflags: longint; invokepd: tprocdef; descriptor: tstaticvarsym): tstaticvarsym;
- var
- literalname: TIDString;
- srsym: tsym;
- srsymtable: tsymtable;
- begin
- literalname:='block_literal_for_'+invokepd.procsym.realname;
- { already exists -> return }
- if searchsym(literalname,srsym,srsymtable) then
- begin
- if srsym.typ<>staticvarsym then
- internalerror(2014071506);
- result:=tstaticvarsym(srsym);
- exit;
- end;
- { create new block literal symbol }
- result:=cstaticvarsym.create(
- '$'+literalname,
- vs_value,
- blockliteraldef,[]);
- include(result.symoptions,sp_internal);
- symtablestack.top.insert(result);
- { initialise it }
- str_parse_typedconst(current_asmdata.AsmLists[al_const],
- '(base: (isa : @'+blockisasym.realname+
- '; flags : '+tostr(blockflags)+
- '; reserved : 0'+
- '; invoke : @'+invokepd.procsym.realname+
- '; descriptor: @'+descriptor.realname+
- '));',
- result);
- end;
- { compose an on-stack block literal for a "procedure of object" }
- function get_pascal_method_literal(blockliteraldef: tdef; blockisasym: tstaticvarsym; blockflags: longint; procvarnode: tnode; invokepd: tprocdef; orgpv: tprocvardef; descriptor: tstaticvarsym): tnode;
- var
- statement: tstatementnode;
- literaltemp: ttempcreatenode;
- begin
- result:=internalstatements(statement);
- { create new block literal structure }
- literaltemp:=ctempcreatenode.create(blockliteraldef,blockliteraldef.size,tt_persistent,false);
- addstatement(statement,literaltemp);
- { temp.base.isa:=@blockisasym }
- addstatement(statement,cassignmentnode.create(
- genloadfield(genloadfield(ctemprefnode.create(literaltemp),'BASE'),'ISA'),
- caddrnode.create(cloadnode.create(blockisasym,blockisasym.owner))));
- { temp.base.flags:=blockflags }
- addstatement(statement,cassignmentnode.create(
- genloadfield(genloadfield(ctemprefnode.create(literaltemp),'BASE'),'FLAGS'),
- genintconstnode(blockflags)));
- { temp.base.reserved:=0 }
- addstatement(statement,cassignmentnode.create(
- genloadfield(genloadfield(ctemprefnode.create(literaltemp),'BASE'),'RESERVED'),
- genintconstnode(0)));
- { temp.base.invoke:=tmethod(@invokepd) }
- addstatement(statement,cassignmentnode.create(
- genloadfield(genloadfield(ctemprefnode.create(literaltemp),'BASE'),'INVOKE'),
- ctypeconvnode.create_proc_to_procvar(
- cloadnode.create_procvar(invokepd.procsym,invokepd,invokepd.owner))));
- { temp.base.descriptor:=@descriptor }
- addstatement(statement,cassignmentnode.create(
- genloadfield(genloadfield(ctemprefnode.create(literaltemp),'BASE'),'DESCRIPTOR'),
- caddrnode.create(cloadnode.create(descriptor,descriptor.owner))));
- { temp.pv:=tmethod(@orgpd) }
- addstatement(statement,cassignmentnode.create(
- ctypeconvnode.create_explicit(genloadfield(ctemprefnode.create(literaltemp),'PV'),orgpv),
- procvarnode.getcopy));
- { and return the address of the temp }
- addstatement(statement,caddrnode.create(ctemprefnode.create(literaltemp)));
- { typecheck this now, because the current source may be written in TP/
- Delphi/MacPas mode and the above node tree has been constructed for
- ObjFPC mode, which has been set by replace_scanner (in Delphi, the
- assignment to invoke would be without the proc_to_procvar conversion) }
- typecheckpass(result);
- end;
- function generate_block_for_procaddr(procloadnode: tloadnode): tnode;
- var
- procvarnode: tnode;
- { procvardef representing the original function we want to invoke }
- orgpv: tprocvardef;
- { procdef of the original function we want to invoke }
- orgpd,
- { procdef for the invoke-wrapper that we generated to call the original
- function via a procvar }
- invokepd: tprocdef;
- blockliteraldef: tdef;
- descriptor,
- blockisasym,
- blockliteralsym: tstaticvarsym;
- blockflags: longint;
- old_symtablestack: tsymtablestack;
- sstate: tscannerstate;
- begin
- result:=nil;
- { supported? (should be caught earlier) }
- if (procloadnode.resultdef.typ<>procdef) or
- is_nested_pd(tprocdef(procloadnode.resultdef)) or
- is_objcclass(tdef(procloadnode.resultdef.owner.defowner)) then
- internalerror(2014071401);
- { add every symbol that we create here to the unit-level symbol table }
- old_symtablestack:=symtablestack;
- symtablestack:=old_symtablestack.getcopyuntil(current_module.localsymtable);
- { save scanner state }
- replace_scanner('block literal creation',sstate);
- { def representing the original function }
- orgpd:=tprocdef(procloadnode.resultdef);
- { def representing the corresponding procvar type }
- procvarnode:=ctypeconvnode.create_proc_to_procvar(procloadnode.getcopy);
- typecheckpass(procvarnode);
- orgpv:=tprocvardef(procvarnode.resultdef);
- { get blockdef for this kind of procdef }
- blockliteraldef:=get_block_literal_type_for_proc(orgpd);
- { get the invoke wrapper }
- invokepd:=get_invoke_wrapper(orgpd,orgpv);
- { get the descriptor }
- descriptor:=get_block_literal_descriptor(invokepd,blockliteraldef.size);
- { get the ISA pointer for the literal }
- blockisasym:=get_block_literal_isa(orgpd);
- { get the flags for the block }
- blockflags:=get_block_literal_flags(orgpd,invokepd);
- { global/simple procedure -> block literal is a typed constant }
- if orgpd.is_addressonly then
- begin
- blockliteralsym:=get_global_proc_literal_sym(blockliteraldef,blockisasym,blockflags,invokepd,descriptor);
- { result: address of the block literal }
- result:=caddrnode.create(cloadnode.create(blockliteralsym,blockliteralsym.owner));
- end
- else
- begin
- result:=get_pascal_method_literal(blockliteraldef,blockisasym,blockflags,procvarnode,invokepd,orgpv,descriptor)
- end;
- procvarnode.free;
- { restore scanner }
- restore_scanner(sstate);
- { restore symtable stack }
- symtablestack.free;
- symtablestack:=old_symtablestack;
- end;
- end.
|