123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655 |
- {
- Copyright (c) 2010 by Jonas Maebe
- This unit implements some Objective-C type helper routines (minimal
- unit dependencies, usable in symdef).
- 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 objcdef;
- interface
- uses
- symtype;
- { The internals of Objective-C's @encode() functionality: encode a
- type into the internal format used by the run time. Returns false
- if a type is not representable by the Objective-C run time, and in
- that case also the failing definition. }
- function objctryencodetype(def: tdef; out encodedtype: ansistring; out founderror: tdef): boolean;
- { Check whether a type can be used in an Objective-C method
- signature or field declaration. }
- function objcchecktype(def: tdef; out founderror: tdef): boolean;
- { add type info for def at the end of encodedstr. recordinfostate influences
- whether a record-style type will be fully encoded, or just using its
- type name. bpacked indicates whether a record/array is bitpacked.
- On error, founderror contains the type that triggered the error. }
- type
- trecordinfostate = (ris_initial, ris_afterpointer, ris_dontprint);
- function objcaddencodedtype(def: tdef; recordinfostate: trecordinfostate; bpacked: boolean; var encodedstr: ansistring; out founderror: tdef): boolean;
- implementation
- uses
- globtype,
- cutils,cclasses,
- verbose,
- symtable,symconst,symsym,symdef,
- defutil,paramgr;
- {******************************************************************
- Type encoding
- *******************************************************************}
- function encoderecst(const recname: ansistring; recst: tabstractrecordsymtable; var encodedstr: ansistring; out founderror: tdef): boolean;
- var
- variantstarts: tfplist;
- i, varindex: longint;
- field,
- firstfield: tfieldvarsym;
- firstfieldvariant,
- bpacked: boolean;
- begin
- result:=false;
- bpacked:=recst.fieldalignment=bit_alignment;
- { Is the first field already the start of a variant? }
- firstfield:=nil;
- firstfieldvariant:=false;
- for i:=0 to recst.symlist.count-1 do
- begin
- if (tsym(recst.symlist[i]).typ<>fieldvarsym) then
- continue;
- field:=tfieldvarsym(recst.symlist[i]);
- if not assigned(firstfield) then
- firstfield:=field
- else if (vo_is_first_field in field.varoptions) then
- begin
- if (field.fieldoffset=firstfield.fieldoffset) then
- firstfieldvariant:=true;
- end;
- end;
- variantstarts:=tfplist.create;
- encodedstr:=encodedstr+'{'+recname+'=';
- for i:=0 to recst.symlist.count-1 do
- begin
- if (tsym(recst.symlist[i]).typ<>fieldvarsym) then
- continue;
- field:=tfieldvarsym(recst.symlist[i]);
- { start of a variant part? }
- if ((field=firstfield) and
- firstfieldvariant) or
- ((field<>firstfield) and
- (vo_is_first_field in field.varoptions)) then
- begin
- varindex:=variantstarts.count-1;
- if (varindex=-1) or
- (tfieldvarsym(variantstarts[varindex]).fieldoffset<field.fieldoffset) then
- begin
- { new, more deeply nested variant }
- encodedstr:=encodedstr+'(?={?=';
- variantstarts.add(field);
- end
- else
- begin
- { close existing nested variants if any }
- while (varindex>=0) and
- (tfieldvarsym(variantstarts[varindex]).fieldoffset>field.fieldoffset) do
- begin
- { close more deeply nested variants }
- encodedstr:=encodedstr+'})';
- dec(varindex);
- end;
- if (varindex<0) then
- internalerror(2009081805);
- if (tfieldvarsym(variantstarts[varindex]).fieldoffset<>field.fieldoffset) then
- internalerror(2009081804);
- { variant at the same level as a previous one }
- variantstarts.count:=varindex+1;
- { No need to add this field, it has the same offset as the
- previous one at this position. }
- if tfieldvarsym(variantstarts[varindex]).fieldoffset<>field.fieldoffset then
- internalerror(2009081601);
- { close previous variant sub-part and start new one }
- encodedstr:=encodedstr+'}{?=';
- end
- end;
- if not objcaddencodedtype(field.vardef,ris_afterpointer,bpacked,encodedstr,founderror) then
- exit;
- end;
- for i:=0 to variantstarts.count-1 do
- encodedstr:=encodedstr+'})';
- variantstarts.free;
- encodedstr:=encodedstr+'}';
- result:=true
- end;
- function objcaddencodedtype(def: tdef; recordinfostate: trecordinfostate; bpacked: boolean; var encodedstr: ansistring; out founderror: tdef): boolean;
- var
- recname: ansistring;
- recdef: trecorddef;
- objdef: tobjectdef;
- len: aint;
- c: char;
- newstate: trecordinfostate;
- addrpara: boolean;
- begin
- result:=true;
- case def.typ of
- stringdef :
- begin
- case tstringdef(def).stringtype of
- st_shortstring:
- { include length byte }
- encodedstr:=encodedstr+'['+tostr(tstringdef(def).len+1)+'C]';
- else
- { While we could handle refcounted Pascal strings correctly
- when such methods are called from Pascal code, things would
- completely break down if they were called from Objective-C
- code/reflection since the necessary refcount helper calls
- would be missing on the caller side (unless we'd
- automatically generate wrappers). }
- result:=false;
- end;
- end;
- enumdef,
- orddef :
- begin
- if bpacked and
- not is_void(def) then
- encodedstr:=encodedstr+'b'+tostr(def.packedbitsize)
- else
- begin
- if is_void(def) then
- c:='v'
- { in gcc, sizeof(_Bool) = sizeof(char) }
- else if is_boolean(def) and
- (def.size=1) then
- c:='B'
- else
- begin
- case def.size of
- 1:
- c:='c';
- 2:
- c:='s';
- 4:
- c:='i';
- 8:
- c:='q';
- else
- internalerror(2009081502);
- end;
- if not is_signed(def) then
- c:=upcase(c);
- end;
- encodedstr:=encodedstr+c;
- end;
- end;
- pointerdef :
- begin
- if is_pchar(def) then
- encodedstr:=encodedstr+'*'
- else if (def=objc_idtype) then
- encodedstr:=encodedstr+'@'
- else if (def=objc_seltype) then
- encodedstr:=encodedstr+':'
- else if (def=objc_metaclasstype) then
- encodedstr:=encodedstr+'#'
- else
- begin
- encodedstr:=encodedstr+'^';
- newstate:=recordinfostate;
- if (recordinfostate<ris_dontprint) then
- newstate:=succ(newstate);
- if not objcaddencodedtype(tpointerdef(def).pointeddef,newstate,false,encodedstr,founderror) then
- begin
- result:=false;
- { report the exact (nested) error defintion }
- exit;
- end;
- end;
- end;
- floatdef :
- begin
- case tfloatdef(def).floattype of
- s32real:
- c:='f';
- s64real:
- c:='d';
- else
- begin
- c:='!';
- result:=false;
- end;
- end;
- encodedstr:=encodedstr+c;
- end;
- filedef :
- result:=false;
- recorddef :
- begin
- if assigned(def.typesym) then
- recname:=def.typename
- else
- recname:='?';
- if (recordinfostate<>ris_dontprint) then
- begin
- if not encoderecst(recname,tabstractrecordsymtable(trecorddef(def).symtable),encodedstr,founderror) then
- begin
- result:=false;
- { report the exact (nested) error defintion }
- exit;
- end
- end
- else
- encodedstr:=encodedstr+'{'+recname+'}'
- end;
- variantdef :
- begin
- recdef:=trecorddef(search_system_type('TVARDATA').typedef);
- if (recordinfostate<>ris_dontprint) then
- begin
- if not encoderecst(recdef.typename,tabstractrecordsymtable(recdef.symtable),encodedstr,founderror) then
- begin
- result:=false;
- { report the exact (nested) error defintion }
- exit;
- end
- end
- else
- encodedstr:=encodedstr+'{'+recdef.typename+'}';
- end;
- classrefdef :
- begin
- encodedstr:=encodedstr+'^';
- newstate:=recordinfostate;
- if (recordinfostate<>ris_dontprint) then
- newstate:=succ(newstate);
- if is_objcclassref(def) then
- begin
- objdef:=tobjectdef(tclassrefdef(def).pointeddef);
- if (newstate<>ris_dontprint) then
- { anonymous (objc)class definitions do not exist }
- begin
- if not encoderecst(objdef.objextname^,tabstractrecordsymtable(objdef.symtable),encodedstr,founderror) then
- { The fields of an Objective-C class should always be
- encodeable. }
- internalerror(2009081702);
- end
- else
- encodedstr:=encodedstr+'{'+objdef.objextname^+'}'
- end
- { Object Pascal classrefdefs point to a vmt, not really useful
- to completely write those here. I'm not even sure what the
- Objective-C run time uses this information for, since in C you
- can have forward struct definitions so not all structs passed
- to functions can be written out here either -> treat
- classrefdefs the same as such forward-defined structs. }
- else
- begin
- if assigned(def.typesym) then
- recname:=def.typename
- else
- recname:='?';
- encodedstr:=encodedstr+'{'+recname;
- if (newstate<>ris_dontprint) then
- encodedstr:=encodedstr+'=';
- encodedstr:=encodedstr+'}'
- end;
- end;
- setdef :
- begin
- addrpara:=paramanager.push_addr_param(vs_value,def,pocall_cdecl);
- if not addrpara then
- { encode as an record, they are always passed by value in C. }
- encodedstr:=encodedstr+'{?=';
- { Encode the set itself as an array. Without an encompassing
- record, these are always passed by reference in C. }
- encodedstr:=encodedstr+'['+tostr(def.size)+'C]';
- if not addrpara then
- encodedstr:=encodedstr+'}';
- end;
- formaldef :
- begin
- encodedstr:=encodedstr+'^v';
- end;
- arraydef :
- begin
- if is_array_of_const(def) then
- { do nothing, varargs are ignored in signatures }
- else if is_special_array(def) then
- result:=false
- else
- begin
- len:=tarraydef(def).highrange-tarraydef(def).lowrange+1;
- if is_packed_array(def) then
- begin
- { convert from bits to bytes for bitpacked arrays }
- len:=(len+7) div 8;
- { and encode as plain array of bytes }
- encodedstr:=encodedstr+'['+tostr(len)+'C]';
- end
- else
- begin
- encodedstr:=encodedstr+'['+tostr(len);
- { Embedded structured types in the array are printed
- in full regardless of the current recordinfostate. }
- if not objcaddencodedtype(tarraydef(def).elementdef,ris_initial,false,encodedstr,founderror) then
- begin
- result:=false;
- { report the exact (nested) error defintion }
- exit;
- end;
- encodedstr:=encodedstr+']';
- end;
- end;
- end;
- procvardef :
- if not(po_is_block in tprocvardef(def).procoptions) then
- encodedstr:=encodedstr+'^?'
- else
- encodedstr:=encodedstr+'@?';
- objectdef :
- case tobjectdef(def).objecttype of
- odt_helper,
- odt_class,
- odt_object,
- odt_cppclass:
- begin
- newstate:=recordinfostate;
- { implicit pointer for classes }
- if (tobjectdef(def).objecttype in [odt_class,odt_helper]) then
- begin
- encodedstr:=encodedstr+'^';
- { make all classes opaque, so even if they contain a
- reference-counted field there is no problem. Since a
- "dereferenced class" object does not exist, this should
- not cause problems }
- newstate:=ris_dontprint;
- end;
- if newstate<>ris_dontprint then
- begin
- if not encoderecst(def.typename,tabstractrecordsymtable(tobjectdef(def).symtable),encodedstr,founderror) then
- begin
- result:=false;
- { report the exact (nested) error defintion }
- exit;
- end
- end
- else
- encodedstr:=encodedstr+'{'+def.typename+'}'
- end;
- odt_interfacecom,
- odt_interfacecom_property,
- odt_interfacecom_function,
- odt_dispinterface:
- result:=false;
- odt_interfacecorba:
- encodedstr:=encodedstr+'^{'+def.typename+'=}';
- { In Objective-C, the actual types of class instances are
- NSObject* etc, and those are encoded as "@". In FPC, to keep
- the similarity with Delphi-style Object Pascal, the type is
- NSObject and the pointer is implicit. Objective-C's "NSObject"
- has "class of NSObject" as equivalent here. }
- odt_objcclass,
- odt_objcprotocol:
- encodedstr:=encodedstr+'@';
- else
- internalerror(2009081509);
- end;
- undefineddef,
- errordef :
- result:=false;
- procdef :
- { must be done via objcencodemethod() }
- internalerror(2009081511);
- else
- internalerror(2009150812);
- end;
- if not result then
- founderror:=def;
- end;
- function objctryencodetype(def: tdef; out encodedtype: ansistring; out founderror: tdef): boolean;
- begin
- result:=objcaddencodedtype(def,ris_initial,false,encodedtype,founderror);
- end;
- {******************************************************************
- ObjC type validity checking
- *******************************************************************}
- function objcdochecktype(def: tdef; recordinfostate: trecordinfostate; out founderror: tdef): boolean; forward;
- function checkrecsttype(recst: tabstractrecordsymtable; recordinfostate: trecordinfostate; out founderror: tdef): boolean;
- var
- i: longint;
- field: tfieldvarsym;
- newstate: trecordinfostate;
- begin
- result:=false;
- newstate:=recordinfostate;
- { Although we never have to print the type info for nested
- records, check them anyway in case we're not after a pointer
- since if such records contain refcounted types then they
- can cause just as much trouble as if they were a simple
- refcounted field. }
- if (newstate=ris_afterpointer) then
- newstate:=ris_dontprint;
- for i:=0 to recst.symlist.count-1 do
- begin
- if (tsym(recst.symlist[i]).typ<>fieldvarsym) then
- continue;
- field:=tfieldvarsym(recst.symlist[i]);
- if not objcdochecktype(field.vardef,newstate,founderror) then
- exit;
- end;
- result:=true
- end;
- function objcdochecktype(def: tdef; recordinfostate: trecordinfostate; out founderror: tdef): boolean;
- var
- recdef: trecorddef;
- objdef: tobjectdef;
- newstate: trecordinfostate;
- begin
- result:=true;
- case def.typ of
- stringdef :
- begin
- case tstringdef(def).stringtype of
- st_shortstring:
- ;
- else
- { While we could handle refcounted Pascal strings correctly
- when such methods are called from Pascal code, things would
- completely break down if they were called from Objective-C
- code/reflection since the necessary refcount helper calls
- would be missing on the caller side (unless we'd
- automatically generate wrappers). }
- result:=false;
- end;
- end;
- enumdef,
- orddef :
- ;
- pointerdef :
- begin
- newstate:=recordinfostate;
- if (recordinfostate<ris_dontprint) then
- newstate:=succ(newstate);
- if not objcdochecktype(tpointerdef(def).pointeddef,newstate,founderror) then
- begin
- result:=false;
- { report the exact (nested) error defintion }
- exit;
- end;
- end;
- floatdef :
- begin
- case tfloatdef(def).floattype of
- s32real,
- s64real:
- ;
- else
- result:=false;
- end;
- end;
- filedef :
- result:=false;
- recorddef :
- begin
- if (recordinfostate<>ris_dontprint) then
- begin
- if not checkrecsttype(tabstractrecordsymtable(trecorddef(def).symtable),recordinfostate,founderror) then
- begin
- result:=false;
- { report the exact (nested) error defintion }
- exit;
- end
- end
- end;
- variantdef :
- begin
- recdef:=trecorddef(search_system_type('TVARDATA').typedef);
- if (recordinfostate<>ris_dontprint) then
- begin
- if not checkrecsttype(tabstractrecordsymtable(recdef.symtable),recordinfostate,founderror) then
- begin
- result:=false;
- { report the exact (nested) error defintion }
- exit;
- end
- end;
- end;
- classrefdef:
- begin
- if is_objcclassref(def) then
- begin
- objdef:=tobjectdef(tclassrefdef(def).pointeddef);
- newstate:=recordinfostate;
- if (recordinfostate<ris_dontprint) then
- newstate:=succ(newstate);
- if (newstate<>ris_dontprint) then
- begin
- if not checkrecsttype(tabstractrecordsymtable(objdef.symtable),recordinfostate,founderror) then
- begin
- result:=false;
- { report the exact (nested) error defintion }
- exit;
- end
- end
- end
- end;
- setdef,
- formaldef :
- ;
- arraydef :
- begin
- if is_array_of_const(def) then
- { ok, varargs are ignored in signatures }
- else if is_special_array(def) then
- result:=false
- else
- begin
- if not is_packed_array(def) then
- begin
- if not objcdochecktype(tarraydef(def).elementdef,ris_initial,founderror) then
- begin
- result:=false;
- { report the exact (nested) error defintion }
- exit;
- end;
- end;
- end;
- end;
- procvardef :
- ;
- objectdef :
- case tobjectdef(def).objecttype of
- odt_helper,
- odt_class,
- odt_object,
- odt_cppclass:
- begin
- newstate:=recordinfostate;
- { implicit pointer for classes }
- if (tobjectdef(def).objecttype in [odt_class,odt_helper]) then
- begin
- { make all classes opaque, so even if they contain a
- reference-counted field there is no problem. Since a
- "dereferenced class" object does not exist, this should
- not cause problems }
- newstate:=ris_dontprint;
- end;
- if newstate<>ris_dontprint then
- begin
- if not checkrecsttype(tabstractrecordsymtable(tobjectdef(def).symtable),newstate,founderror) then
- begin
- result:=false;
- { report the exact (nested) error defintion }
- exit;
- end
- end
- end;
- odt_interfacecom,
- odt_interfacecom_property,
- odt_interfacecom_function,
- odt_dispinterface:
- result:=false;
- odt_interfacecorba,
- odt_objcclass,
- odt_objcprotocol:
- ;
- else
- internalerror(2009081709);
- end;
- undefineddef,
- errordef :
- result:=false;
- procdef :
- result:=false;
- else
- internalerror(2009170812);
- end;
- if not result then
- founderror:=def;
- end;
- function objcchecktype(def: tdef; out founderror: tdef): boolean;
- begin
- result:=objcdochecktype(def,ris_initial,founderror);
- end;
- end.
|