+ support for implementing Objective-C classes in Pascal,

based on patch by Dmitry Boyarintsev (mantis #14508)
   o Todo: while parsing the class declaration, check whether the
       field types are valid for use in an obj-c class
  * use a common pool for selector names generated by objcselector()
    and by the rtti info for implemented classes

git-svn-id: branches/objc@13663 -
This commit is contained in:
Jonas Maebe 2009-09-06 18:35:48 +00:00
parent 1048ac96d5
commit fc40e1fe5b
4 changed files with 441 additions and 42 deletions

View File

@ -65,6 +65,9 @@ interface
al_resourcestrings, al_resourcestrings,
{ Objective-C related sections } { Objective-C related sections }
al_objc_data, al_objc_data,
{ keep pool data separate, so we can generate new pool entries
while emitting other data }
al_objc_pools,
al_end al_end
); );
@ -79,8 +82,10 @@ interface
sp_ansistr, sp_ansistr,
sp_widestr, sp_widestr,
sp_unicodestr, sp_unicodestr,
sp_objcselector, sp_objcmetaclass,
sp_objcmetaclass sp_objcvarnames,
sp_objcvartypes,
sp_objcclassnames
); );
const const
@ -104,6 +109,7 @@ interface
'al_picdata', 'al_picdata',
'al_resourcestrings', 'al_resourcestrings',
'al_objc_data', 'al_objc_data',
'al_objc_pools',
'al_end' 'al_end'
); );

View File

@ -61,9 +61,9 @@ procedure tcgobjcselectornode.pass_generate_code;
name : pshortstring; name : pshortstring;
pc : pchar; pc : pchar;
begin begin
if current_asmdata.ConstPools[sp_objcselector]=nil then if current_asmdata.ConstPools[sp_objcvarnames]=nil then
current_asmdata.ConstPools[sp_objcselector]:=THashSet.Create(64, True, False); current_asmdata.ConstPools[sp_objcvarnames]:=THashSet.Create(64, True, False);
pool:=current_asmdata.ConstPools[sp_objcselector]; pool:=current_asmdata.ConstPools[sp_objcvarnames];
case left.nodetype of case left.nodetype of
loadn: loadn:

View File

@ -6,7 +6,7 @@
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2 of the License,or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
@ -27,21 +27,31 @@ unit objcgutl;
interface interface
uses uses
cclasses, cclasses,
aasmbase; aasmbase,
symbase;
procedure objcfinishstringrefpoolentry(entry: phashsetitem; refsec, stringsec: tasmsectiontype); procedure objcfinishstringrefpoolentry(entry: phashsetitem; refsec, stringsec: tasmsectiontype);
procedure MaybeGenerateObjectiveCImageInfo(globalst, localst: tsymtable);
implementation implementation
uses uses
globtype, globtype,globals,
aasmdata,aasmtai, systems,
cgbase,cgutils, aasmdata,aasmtai,
symsym, cgbase,cgutils,
verbose; objcutil,
symconst,symtype,symsym,symdef,symtable,
verbose;
{******************************************************************
String section helpers
*******************************************************************}
procedure objcfinishstringrefpoolentry(entry: phashsetitem; refsec, stringsec: tasmsectiontype); procedure objcfinishstringrefpoolentry(entry: phashsetitem; refsec, stringsec: tasmsectiontype);
var var
@ -62,15 +72,412 @@ procedure objcfinishstringrefpoolentry(entry: phashsetitem; refsec, stringsec: t
move(entry^.key^,pc^,entry^.keylength); move(entry^.key^,pc^,entry^.keylength);
pc[entry^.keylength]:=#0; pc[entry^.keylength]:=#0;
{ add a pointer to the message name in the string references section } { add a pointer to the message name in the string references section }
new_section(current_asmdata.asmlists[al_objc_data],refsec,reflab.name,sizeof(pint)); new_section(current_asmdata.asmlists[al_objc_pools],refsec,reflab.name,sizeof(pint));
current_asmdata.asmlists[al_objc_data].concat(Tai_label.Create(reflab)); current_asmdata.asmlists[al_objc_pools].concat(Tai_label.Create(reflab));
current_asmdata.asmlists[al_objc_data].concat(Tai_const.Create_sym(strlab)); current_asmdata.asmlists[al_objc_pools].concat(Tai_const.Create_sym(strlab));
{ and now add the message name to the associated strings section } { and now add the message name to the associated strings section }
new_section(current_asmdata.asmlists[al_objc_data],stringsec,strlab.name,1); new_section(current_asmdata.asmlists[al_objc_pools],stringsec,strlab.name,sizeof(pint));
current_asmdata.asmlists[al_objc_data].concat(Tai_label.Create(strlab)); current_asmdata.asmlists[al_objc_pools].concat(Tai_label.Create(strlab));
current_asmdata.asmlists[al_objc_data].concat(Tai_string.Create_pchar(pc,entry^.keylength+1)); current_asmdata.asmlists[al_objc_pools].concat(Tai_string.Create_pchar(pc,entry^.keylength+1));
end; end;
end; end;
function objcreatestringpoolentry(const s: string; pooltype: tconstpooltype; stringsec: tasmsectiontype): TAsmSymbol;
var
entry : PHashSetItem;
strlab : tasmlabel;
pc : pchar;
pool : THashSet;
begin
if current_asmdata.ConstPools[pooltype]=nil then
current_asmdata.ConstPools[pooltype]:=THashSet.Create(64, True, False);
pool := current_asmdata.constpools[pooltype];
entry:=pool.FindOrAdd(@s[1],length(s));
if not assigned(entry^.data) then
begin
{ create new entry }
current_asmdata.getlabel(strlab,alt_data);
entry^.Data:=strlab;
getmem(pc,entry^.keylength+1);
move(entry^.key^,pc^,entry^.keylength);
pc[entry^.keylength]:=#0;
{ add the string to the approriate section }
new_section(current_asmdata.asmlists[al_objc_pools],stringsec,strlab.name,sizeof(pint));
current_asmdata.asmlists[al_objc_pools].concat(Tai_label.Create(strlab));
current_asmdata.asmlists[al_objc_pools].concat(Tai_string.Create_pchar(pc,entry^.keylength+1));
Result := strlab;
end
else
Result := TAsmLabel(Entry^.Data);
end;
{******************************************************************
RTTI generation
*******************************************************************}
{ generate a method list, either of class methods or of instance methods,
and both for obj-c classes and categories. }
procedure gen_objc1_methods(list: tasmlist; objccls: tobjectdef; out methodslabel: tasmlabel; classmethods, iscategory: Boolean);
const
clsSectType : array [Boolean] of tasmsectiontype = (sec_objc_inst_meth, sec_objc_cls_meth);
clsSectName : array [Boolean] of string = ('_OBJC_INST_METH','_OBJC_CLS_METH');
catSectType : array [Boolean] of tasmsectiontype = (sec_objc_cat_inst_meth, sec_objc_cat_cls_meth);
catSectName : array [Boolean] of string = ('_OBJC_CAT_INST_METH','_OBJC_CAT_CLS_METH');
type
method_data = record
def : tprocdef;
selsym : TAsmSymbol;
encsym : TAsmSymbol;
end;
var
i : Integer;
def : tprocdef;
defs : array of method_data;
mcnt : integer;
begin
methodslabel:=nil;
mcnt:=0;
{ collect all instance/class methods }
SetLength(defs,objccls.vmtentries.count);
for i:=0 to objccls.vmtentries.count-1 do
begin
def:=pvmtentry(objccls.vmtentries[i])^.procdef;
if Assigned(def.procstarttai) and
(classmethods = (po_classmethod in def.procoptions)) then
begin
defs[mcnt].def:=def;
defs[mcnt].selsym:=objcreatestringpoolentry(def.messageinf.str^,sp_objcvarnames,sec_objc_meth_var_names);
defs[mcnt].encsym:=objcreatestringpoolentry(objcencodemethod(def),sp_objcvartypes,sec_objc_meth_var_types);
inc(mcnt);
end;
end;
if mcnt=0 then
exit;
if iscategory then
new_section(list,clsSectType[classmethods],clsSectName[classmethods],4)
else
new_section(list,catSectType[classmethods],catSectName[classmethods],4);
current_asmdata.getlabel(methodslabel,alt_data);
list.Concat(tai_label.Create(methodslabel));
{ not used, always zero }
list.Concat(tai_const.Create_32bit(0));
{ number of objc_method entries in the method_list array }
list.Concat(tai_const.Create_32bit(mcnt));
for i := 0 to mcnt - 1 do
begin
{ reference to the selector name }
list.Concat(tai_const.Create_sym(defs[i].selsym));
{ reference to the obj-c encoded function parameters (signature) }
list.Concat(tai_const.Create_sym(defs[i].encsym));
{ mangled name of the method }
list.Concat(tai_const.Create_sym(
current_asmdata.GetAsmSymbol(defs[i].def.objcmangledname)));
end;
end;
{ generate an instance variables list for an obj-c class. }
procedure gen_objc1_ivars(list: TAsmList; objccls: tobjectdef; out ivarslabel: TAsmLabel);
type
ivar_data = record
vf : tfieldvarsym;
namesym : TAsmSymbol;
typesym : TAsmSymbol;
end;
var
i : integer;
vf : tfieldvarsym;
vars : array of ivar_data;
vcnt : Integer;
enctype : ansistring;
encerr : tdef;
begin
ivarslabel:=nil;
vcnt:=0;
setLength(vars,objccls.symtable.SymList.Count);
for i:=0 to objccls.symtable.SymList.Count-1 do
if tsym(objccls.symtable.SymList[i]).typ=fieldvarsym then
begin
vf:=tfieldvarsym(objccls.symtable.SymList[i]);
if objctryencodetype(vf.vardef,enctype,encerr) then
begin
vars[vcnt].vf:=vf;
vars[vcnt].namesym:=objcreatestringpoolentry(vf.RealName,sp_objcvarnames,sec_objc_meth_var_names);
vars[vcnt].typesym:=objcreatestringpoolentry(enctype,sp_objcvartypes,sec_objc_meth_var_types);
inc(vcnt);
end
else
{ must be caught during parsing }
internalerror(2009090601);
end;
if vcnt=0 then
exit;
new_section(list,sec_objc_instance_vars,'_OBJC_INSTANCE_VARS',sizeof(pint));
current_asmdata.getlabel(ivarslabel,alt_data);
list.Concat(tai_label.Create(ivarslabel));
{ objc_ivar_list: first the number of elements }
list.Concat(tai_const.Create_32bit(vcnt));
for i:=0 to vcnt-1 do
begin
{ reference to the instance variable name }
list.Concat(tai_const.Create_sym(vars[i].namesym));
{ reference to the encoded type }
list.Concat(tai_const.Create_sym(vars[i].typesym));
{ and the offset of the field }
list.Concat(tai_const.Create_32bit(vars[i].vf.fieldoffset));
end;
end;
(*
From Clang:
struct _objc_class {
Class isa;
Class super_class;
const char *name;
long version;
long info;
long instance_size;
struct _objc_ivar_list *ivars;
struct _objc_method_list *methods;
struct _objc_cache *cache;
struct _objc_protocol_list *protocols;
// Objective-C 1.0 extensions (<rdr://4585769>) -- for garbage collection
const char *ivar_layout;
struct _objc_class_ext *ext;
};
*)
{ Generate rtti for an Objective-C class and its meta-class. }
procedure gen_objc1_classes_sections(list:TAsmList; objclss: tobjectdef; out classlabel: TAsmSymbol);
const
CLS_CLASS = 1;
CLS_META = 2;
META_INST_SIZE = 40+8; // sizeof(objc_class) + 8
var
root : tobjectdef;
lbl, metalbl : TAsmLabel;
superStrSym,
classStrSym,
metaisaStrSym : TAsmSymbol;
mthdlist,
ivarslist : TAsmLabel;
begin
{ generate the class methods list }
gen_objc1_methods(list,objclss,mthdlist,true,false);
{ register necessary names }
{ 1) the superclass }
if assigned(objclss.childof) then
superStrSym:=objcreatestringpoolentry(objclss.childof.objextname^,sp_objcclassnames,sec_objc_class_names)
else
{ not empty string, but nil! }
superStrSym:=nil;
{ 2) the current class }
classStrSym:=objcreatestringpoolentry(objclss.objextname^,sp_objcclassnames,sec_objc_class_names);
{ 3) the isa }
{ From Clang: The isa for the meta-class is the root of the hierarchy. }
root:=objclss;
while assigned(root.childof) do
root:=root.childof;
metaisaStrSym:=objcreatestringpoolentry(root.objextname^,sp_objcclassnames,sec_objc_class_names);
{ class declaration section }
new_section(list,sec_objc_meta_class,'_OBJC_META_CLASS',sizeof(pint));
{ 1) meta-class declaration }
current_asmdata.getlabel(metalbl,alt_data);
list.Concat(tai_label.Create(metalbl));
list.Concat(Tai_const.Create_sym(metaisaStrSym));
{ pointer to the superclass name if any, otherwise nil }
if assigned(superstrsym) then
list.Concat(Tai_const.Create_sym(superStrSym))
else
list.concat(tai_const.create_32bit(0));
{ pointer to the class name }
list.Concat(Tai_const.Create_sym(classStrSym));
{ version is always 0 currently }
list.Concat(Tai_const.Create_32bit(0));
{ CLS_META for meta-classes }
list.Concat(Tai_const.Create_32bit(CLS_META));
{ size of the meta-class instance: sizeof(objc_class) + 8 bytes }
list.Concat(Tai_const.Create_32bit(META_INST_SIZE) );
{ meta-classes don't have ivars list (=0) }
list.Concat(Tai_const.Create_32bit(0));
{ class methods list (stored in "__cls_meth" section) }
if Assigned(mthdlist) then
list.Concat(Tai_const.Create_sym(mthdlist))
else
list.Concat(Tai_const.Create_32bit(0));
{ From Clang: cache is always nil }
list.Concat(Tai_const.Create_32bit(0));
{ TODO: protocols }
list.Concat(Tai_const.Create_32bit(0));
{ From Clang: ivar_layout for meta-class is always NULL. }
list.Concat(Tai_const.Create_32bit(0));
{ From Clang: The class extension is always unused for meta-classes. }
list.Concat(Tai_const.Create_32bit(0));
{ 2) regular class declaration }
{ generate the instance methods list }
gen_objc1_methods(list,objclss,mthdlist,false,false);
{ generate the instance variables list }
gen_objc1_ivars(list,objclss,ivarslist);
new_section(list,sec_objc_class,'_OBJC_CLASS',sizeof(pint));
current_asmdata.getlabel(lbl,alt_data);
list.Concat(tai_label.Create(lbl));
{ for class declaration: the is points to the meta-class declaration }
list.Concat(Tai_const.Create_sym(metalbl));
{ pointer to the super_class name if any, nil otherwise }
if assigned(superStrSym) then
list.Concat(Tai_const.Create_sym(superStrSym))
else
list.Concat(Tai_const.Create_32bit(0));
{ pointer to the class name }
list.Concat(Tai_const.Create_sym(classStrSym));
{ version is always 0 currently }
list.Concat(Tai_const.Create_32bit(0));
{ CLS_CLASS for classes }
list.Concat(Tai_const.Create_32bit(CLS_CLASS));
{ size of instance: total size of instance variables }
list.Concat(Tai_const.Create_32bit(tobjectsymtable(objclss.symtable).datasize));
{ objc_ivar_list (stored in "__instance_vars" section) }
if assigned(ivarslist) then
list.Concat(Tai_const.Create_sym(ivarslist))
else
list.Concat(tai_const.create_32bit(0));
{ instance methods list (stored in "__inst_meth" section) }
if Assigned(mthdlist) then
list.Concat(Tai_const.Create_sym(mthdlist))
else
list.Concat(Tai_const.Create_32bit(0));
{ From Clang: cache is always NULL }
list.Concat(Tai_const.Create_32bit(0));
{ TODO: protocols }
list.Concat(Tai_const.Create_32bit(0));
{ TODO: From Clang: strong ivar_layout, necessary for garbage collection support }
list.Concat(Tai_const.Create_32bit(0));
{ TODO: From Clang: weak ivar_layout, necessary for garbage collection support }
list.Concat(Tai_const.Create_32bit(0));
classlabel:=lbl;
end;
{ Generate the rtti sections for all obj-c classes defined in st, and return
these classes in the classes list. }
procedure gen_objc1_rtti_sections(list:TAsmList; st:TSymtable; var classes: tfpobjectlist);
var
i: longint;
def: tdef;
sym : TAsmSymbol;
begin
if not Assigned(st) then
exit;
for i:=0 to st.DefList.Count-1 do
begin
def:=tdef(st.DefList[i]);
if is_objcclass(def) and
not(oo_is_external in tobjectdef(def).objectoptions) then
begin
gen_objc1_classes_sections(list,tobjectdef(def),sym);
classes.add(sym);
end;
end;
end;
{ Generate the global information sections (objc_symbols and objc_module_info)
for this module. }
procedure gen_objc1_info_sections(list: tasmlist; classes: tfpobjectlist);
var
i: longint;
sym : TAsmSymbol;
begin
if (classes.count<>0) then
begin
new_section(list,sec_objc_symbols,'_OBJC_SYMBOLS',sizeof(pint));
sym := current_asmdata.RefAsmSymbol(target_asm.labelprefix+'_OBJC_SYMBOLS');
{ symbol to refer to this information }
list.Concat(tai_symbol.Create(sym,0));
{ ??? (always 0 in Clang) }
list.Concat(Tai_const.Create_pint(0));
{ ??? (From Clang: always 0, pointer to some selector) }
list.Concat(Tai_const.Create_pint(0));
{ From Clang: number of defined classes }
list.Concat(Tai_const.Create_16bit(classes.count));
{ From Clang: number of defined categories }
list.Concat(Tai_const.Create_16bit(0));
{ first all classes }
for i:=0 to classes.count-1 do
list.Concat(Tai_const.Create_sym(tasmsymbol(classes[i])));
{ then all categories }
end
else
sym:=nil;
new_section(list,sec_objc_module_info,'_OBJC_MODULE_INFO',4);
{ version number = 7 (always, both for gcc and clang, regardless of objc-1 or 2 }
list.Concat(Tai_const.Create_pint(7));
{ sizeof(objc_module): 4 pointer-size entities }
list.Concat(Tai_const.Create_pint(sizeof(pint)*4));
{ used to be file name, now unused (points to empty string) }
list.Concat(Tai_const.Create_sym(objcreatestringpoolentry('',sp_objcclassnames,sec_objc_class_names)));
{ pointer to classes/categories list declared in this module }
if assigned(sym) then
list.Concat(Tai_const.Create_sym(sym))
else
list.concat(tai_const.create_pint(0));
end;
procedure MaybeGenerateObjectiveCImageInfo(globalst, localst: tsymtable);
var
classes: tfpobjectlist;
begin
if (m_objectivec1 in current_settings.modeswitches) then
begin
{ first 4 bytes contain version information about this section (currently version 0),
next 4 bytes contain flags (currently only regarding whether the code in the object
file supports or requires garbage collection)
}
new_section(current_asmdata.asmlists[al_objc_data],sec_objc_image_info,'_OBJC_IMAGE_INFO',sizeof(pint));
current_asmdata.asmlists[al_objc_data].concat(Tai_symbol.Createname(target_asm.labelprefix+'_OBJC_IMAGE_INFO',AT_LABEL,sizeof(pint)));
current_asmdata.asmlists[al_objc_data].concat(Tai_const.Create_64bit(0));
{ generate rtti for all obj-c classes, protocols (todo) and categories (todo)
defined in this module. }
classes:=tfpobjectlist.create(false);
gen_objc1_rtti_sections(current_asmdata.asmlists[al_objc_data],globalst,classes);
gen_objc1_rtti_sections(current_asmdata.asmlists[al_objc_data],localst,classes);
gen_objc1_info_sections(current_asmdata.asmlists[al_objc_data],classes);
classes.free;
end;
end;
end. end.

View File

@ -45,6 +45,7 @@ implementation
link,assemble,import,export,gendef,ppu,comprsrc,dbgbase, link,assemble,import,export,gendef,ppu,comprsrc,dbgbase,
cresstr,procinfo, cresstr,procinfo,
pexports, pexports,
objcgutl,
wpobase, wpobase,
scanner,pbase,pexpr,psystem,psub,pdecsub,ptype scanner,pbase,pexpr,psystem,psub,pdecsub,ptype
,cpuinfo ,cpuinfo
@ -236,21 +237,6 @@ implementation
end; end;
procedure MaybeGenerateObjectiveCImageInfo;
begin
if (m_objectivec1 in current_settings.modeswitches) then
begin
{ first 4 bytes contain version information about this section (currently version 0),
next 4 bytes contain flags (currently only regarding whether the code in the object
file supports or requires garbage collection)
}
new_section(current_asmdata.asmlists[al_objc_data],sec_objc_image_info,'_OBJC_IMAGE_INFO',4);
current_asmdata.asmlists[al_objc_data].concat(Tai_symbol.Createname(target_asm.labelprefix+'_OBJC_IMAGE_INFO',AT_LABEL,8));
current_asmdata.asmlists[al_objc_data].concat(Tai_const.Create_64bit(0));
end;
end;
Function CheckResourcesUsed : boolean; Function CheckResourcesUsed : boolean;
var var
hp : tused_unit; hp : tused_unit;
@ -1216,8 +1202,8 @@ implementation
exit; exit;
end; end;
{ if an Objective-C module, generate objc_image_info section } { if an Objective-C module, generate rtti and module info }
MaybeGenerateObjectiveCImageInfo; MaybeGenerateObjectiveCImageInfo(current_module.globalsymtable,current_module.localsymtable);
{ do we need to add the variants unit? } { do we need to add the variants unit? }
maybeloadvariantsunit; maybeloadvariantsunit;
@ -2154,9 +2140,6 @@ implementation
end; end;
end; end;
{ if an Objective-C module, generate objc_image_info section }
MaybeGenerateObjectiveCImageInfo;
{ do we need to add the variants unit? } { do we need to add the variants unit? }
maybeloadvariantsunit; maybeloadvariantsunit;
@ -2186,6 +2169,9 @@ implementation
{ generate rtti/init tables } { generate rtti/init tables }
write_persistent_type_info(current_module.localsymtable); write_persistent_type_info(current_module.localsymtable);
{ if an Objective-C module, generate rtti and module info }
MaybeGenerateObjectiveCImageInfo(nil,current_module.localsymtable);
{ generate wrappers for interfaces } { generate wrappers for interfaces }
gen_intf_wrappers(current_asmdata.asmlists[al_procedures],current_module.localsymtable); gen_intf_wrappers(current_asmdata.asmlists[al_procedures],current_module.localsymtable);