| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653 | {    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      node,      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,systems,    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 :            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.
 |