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

View File

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

View File

@ -6,7 +6,7 @@
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
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,
@ -27,21 +27,31 @@ unit objcgutl;
interface
uses
cclasses,
aasmbase;
uses
cclasses,
aasmbase,
symbase;
procedure objcfinishstringrefpoolentry(entry: phashsetitem; refsec, stringsec: tasmsectiontype);
procedure objcfinishstringrefpoolentry(entry: phashsetitem; refsec, stringsec: tasmsectiontype);
procedure MaybeGenerateObjectiveCImageInfo(globalst, localst: tsymtable);
implementation
uses
globtype,
aasmdata,aasmtai,
cgbase,cgutils,
symsym,
verbose;
uses
globtype,globals,
systems,
aasmdata,aasmtai,
cgbase,cgutils,
objcutil,
symconst,symtype,symsym,symdef,symtable,
verbose;
{******************************************************************
String section helpers
*******************************************************************}
procedure objcfinishstringrefpoolentry(entry: phashsetitem; refsec, stringsec: tasmsectiontype);
var
@ -62,15 +72,412 @@ procedure objcfinishstringrefpoolentry(entry: phashsetitem; refsec, stringsec: t
move(entry^.key^,pc^,entry^.keylength);
pc[entry^.keylength]:=#0;
{ 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));
current_asmdata.asmlists[al_objc_data].concat(Tai_label.Create(reflab));
current_asmdata.asmlists[al_objc_data].concat(Tai_const.Create_sym(strlab));
new_section(current_asmdata.asmlists[al_objc_pools],refsec,reflab.name,sizeof(pint));
current_asmdata.asmlists[al_objc_pools].concat(Tai_label.Create(reflab));
current_asmdata.asmlists[al_objc_pools].concat(Tai_const.Create_sym(strlab));
{ and now add the message name to the associated strings section }
new_section(current_asmdata.asmlists[al_objc_data],stringsec,strlab.name,1);
current_asmdata.asmlists[al_objc_data].concat(Tai_label.Create(strlab));
current_asmdata.asmlists[al_objc_data].concat(Tai_string.Create_pchar(pc,entry^.keylength+1));
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));
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.

View File

@ -45,6 +45,7 @@ implementation
link,assemble,import,export,gendef,ppu,comprsrc,dbgbase,
cresstr,procinfo,
pexports,
objcgutl,
wpobase,
scanner,pbase,pexpr,psystem,psub,pdecsub,ptype
,cpuinfo
@ -236,21 +237,6 @@ implementation
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;
var
hp : tused_unit;
@ -1216,8 +1202,8 @@ implementation
exit;
end;
{ if an Objective-C module, generate objc_image_info section }
MaybeGenerateObjectiveCImageInfo;
{ if an Objective-C module, generate rtti and module info }
MaybeGenerateObjectiveCImageInfo(current_module.globalsymtable,current_module.localsymtable);
{ do we need to add the variants unit? }
maybeloadvariantsunit;
@ -2154,9 +2140,6 @@ implementation
end;
end;
{ if an Objective-C module, generate objc_image_info section }
MaybeGenerateObjectiveCImageInfo;
{ do we need to add the variants unit? }
maybeloadvariantsunit;
@ -2186,6 +2169,9 @@ implementation
{ generate rtti/init tables }
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 }
gen_intf_wrappers(current_asmdata.asmlists[al_procedures],current_module.localsymtable);