summaryrefslogtreecommitdiff
path: root/closures/compiler/objcutil.pas
diff options
context:
space:
mode:
Diffstat (limited to 'closures/compiler/objcutil.pas')
-rw-r--r--closures/compiler/objcutil.pas291
1 files changed, 291 insertions, 0 deletions
diff --git a/closures/compiler/objcutil.pas b/closures/compiler/objcutil.pas
new file mode 100644
index 0000000000..23b1bfafd6
--- /dev/null
+++ b/closures/compiler/objcutil.pas
@@ -0,0 +1,291 @@
+{
+ Copyright (c) 2009-2010 by Jonas Maebe
+
+ This unit implements some Objective-C helper routines at the node tree
+ level.
+
+ 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 objcutil;
+
+interface
+
+ uses
+ node,
+ symtype,symdef;
+
+ { Check whether a string contains a syntactically valid selector name. }
+ function objcvalidselectorname(value_str: pchar; len: longint): boolean;
+
+ { Generate a node loading the superclass structure necessary to call
+ an inherited Objective-C method. }
+ function objcsuperclassnode(def: tdef): tnode;
+
+ { Encode a method's parameters and result type into the format used by the
+ run time (for generating protocol and class rtti). }
+ function objcencodemethod(pd: tprocdef): ansistring;
+
+ { Exports all assembler symbols related to the obj-c class }
+ procedure exportobjcclass(def: tobjectdef);
+
+implementation
+
+ uses
+ globtype,
+ cutils,cclasses,
+ pass_1,
+ verbose,systems,
+ symtable,symconst,symsym,
+ objcdef,
+ defutil,paramgr,
+ nbas,nmem,ncal,nld,ncon,ncnv,
+ export;
+
+
+{******************************************************************
+ validselectorname
+*******************************************************************}
+
+function objcvalidselectorname(value_str: pchar; len: longint): boolean;
+ var
+ i : longint;
+ gotcolon : boolean;
+begin
+ result:=false;
+ { empty name is not allowed }
+ if (len=0) then
+ exit;
+
+ gotcolon:=false;
+
+ { if the first character is a colon, all of them must be colons }
+ if (value_str[0] = ':') then
+ begin
+ for i:=1 to len-1 do
+ if (value_str[i]<>':') then
+ exit;
+ end
+ else
+ begin
+ { no special characters other than ':'
+ }
+ for i:=0 to len-1 do
+ if (value_str[i] = ':') then
+ gotcolon:=true
+ else if not(value_str[i] in ['_','A'..'Z','a'..'z','0'..'9',':']) then
+ exit;
+
+ { if there is at least one colon, the final character must
+ also be a colon (in case it's only one character that is
+ a colon, this was already checked before the above loop)
+ }
+ if gotcolon and
+ (value_str[len-1] <> ':') then
+ exit;
+ end;
+
+ result:=true;
+end;
+
+{******************************************************************
+ objcsuperclassnode
+*******************************************************************}
+
+ function objcloadbasefield(n: tnode; const fieldname: string): tnode;
+ var
+ vs : tsym;
+ begin
+ result:=cderefnode.create(ctypeconvnode.create_internal(n,objc_idtype));
+ vs:=tsym(tabstractrecorddef(objc_objecttype).symtable.Find(fieldname));
+ if not assigned(vs) or
+ (vs.typ<>fieldvarsym) then
+ internalerror(200911301);
+ result:=csubscriptnode.create(vs,result);
+ end;
+
+
+ function objcsuperclassnode(def: tdef): tnode;
+ var
+ para : tcallparanode;
+ begin
+ { only valid for Objective-C classes and classrefs }
+ if not is_objcclass(def) and
+ not is_objcclassref(def) then
+ internalerror(2009090901);
+ { Can be done a lot more efficiently with direct symbol accesses, but
+ requires extra node types. Maybe later. }
+ if is_objcclassref(def) then
+ begin
+ if (oo_is_classhelper in tobjectdef(tclassrefdef(def).pointeddef).objectoptions) then
+ begin
+ { in case we are in a category method, we need the metaclass of the
+ superclass class extended by this category (= metaclass of superclass of superclass)
+ for the fragile abi, and the metaclass of the superclass for the non-fragile ABI }
+{$if defined(onlymacosx10_6) or defined(arm) }
+ { NOTE: those send2 methods are only available on Mac OS X 10.6 and later!
+ (but also on all iPhone SDK revisions we support) }
+ if (target_info.system in systems_objc_nfabi) then
+ result:=cloadvmtaddrnode.create(ctypenode.create(tobjectdef(tclassrefdef(def).pointeddef).childof))
+ else
+{$endif onlymacosx10_6 or arm}
+ result:=cloadvmtaddrnode.create(ctypenode.create(tobjectdef(tclassrefdef(def).pointeddef).childof.childof));
+ result:=objcloadbasefield(result,'ISA');
+ typecheckpass(result);
+ { we're done }
+ exit;
+ end
+ else
+ begin
+ { otherwise we need the superclass of the metaclass }
+ para:=ccallparanode.create(cstringconstnode.createstr(tobjectdef(tclassrefdef(def).pointeddef).objextname^),nil);
+ result:=ccallnode.createinternfromunit('OBJC','OBJC_GETMETACLASS',para);
+ end
+ end
+ else
+ begin
+ if not(oo_is_classhelper in tobjectdef(def).objectoptions) then
+ result:=cloadvmtaddrnode.create(ctypenode.create(def))
+ else
+ result:=cloadvmtaddrnode.create(ctypenode.create(tobjectdef(def).childof))
+ end;
+
+{$if defined(onlymacosx10_6) or defined(arm) }
+ { For the non-fragile ABI, the superclass send2 method itself loads the
+ superclass. For the fragile ABI, we have to do this ourselves.
+
+ NOTE: those send2 methods are only available on Mac OS X 10.6 and later!
+ (but also on all iPhone SDK revisions we support) }
+ if not(target_info.system in systems_objc_nfabi) then
+{$endif onlymacosx10_6 or arm}
+ result:=objcloadbasefield(result,'SUPERCLASS');
+ typecheckpass(result);
+ end;
+
+
+{******************************************************************
+ Type encoding
+*******************************************************************}
+
+ function objcparasize(vs: tparavarsym): ptrint;
+ begin
+ result:=vs.paraloc[callerside].intsize;
+ { In Objective-C, all ordinal types are widened to at least the
+ size of the C "int" type. Assume __LP64__/4 byte ints for now. }
+ if is_ordinal(vs.vardef) and
+ (result<4) then
+ result:=4;
+ end;
+
+
+ function objcencodemethod(pd: tprocdef): ansistring;
+ var
+ parasize,
+ totalsize: aint;
+ vs: tparavarsym;
+ i: longint;
+ temp: ansistring;
+ founderror: tdef;
+ begin
+ result:='';
+ totalsize:=0;
+ pd.init_paraloc_info(callerside);
+{$if defined(powerpc) and defined(dummy)}
+ { Disabled, because neither Clang nor gcc does this, and the ObjC
+ runtime contains an explicit fix to detect this error. }
+
+ { On ppc, the callee is responsible for removing the hidden function
+ result parameter from the stack, so it has to know. On i386, it's
+ the caller that does this. }
+ if (pd.returndef<>voidtype) and
+ paramgr.ret_in_param(pd.returndef,pocall_cdecl) then
+ inc(totalsize,sizeof(pint));
+{$endif}
+ for i:=0 to pd.paras.count-1 do
+ begin
+ vs:=tparavarsym(pd.paras[i]);
+ if (vo_is_funcret in vs.varoptions) then
+ continue;
+ { objcaddencodedtype always assumes a value parameter, so add
+ a pointer indirection for var/out parameters. }
+ if not paramanager.push_addr_param(vs_value,vs.vardef,pocall_cdecl) and
+ (vs.varspez in [vs_var,vs_out,vs_constref]) then
+ result:=result+'^';
+ { Add the parameter type. }
+ if not objcaddencodedtype(vs.vardef,ris_initial,false,result,founderror) then
+ { should be checked earlier on }
+ internalerror(2009081701);
+ { And the total size of the parameters coming before this one
+ (i.e., the "offset" of this parameter). }
+ result:=result+tostr(totalsize);
+ { Update the total parameter size }
+ parasize:=objcparasize(vs);
+ inc(totalsize,parasize);
+ end;
+ { Prepend the total parameter size. }
+ result:=tostr(totalsize)+result;
+ { And the type of the function result (void in case of a procedure). }
+ temp:='';
+ if not objcaddencodedtype(pd.returndef,ris_initial,false,temp,founderror) then
+ internalerror(2009081801);
+ result:=temp+result;
+ end;
+
+
+{******************************************************************
+ ObjC class exporting
+*******************************************************************}
+
+ procedure exportobjcclassfields(objccls: tobjectdef);
+ var
+ i: longint;
+ vf: tfieldvarsym;
+ prefix: string;
+ begin
+ prefix:=target_info.cprefix+'OBJC_IVAR_$_'+objccls.objextname^+'.';
+ 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]);
+ { TODO: package visibility (private_extern) -- must not be exported
+ either}
+ if not(vf.visibility in [vis_private,vis_strictprivate]) then
+ exportname(prefix+vf.RealName,0);
+ end;
+ end;
+
+
+ procedure exportobjcclass(def: tobjectdef);
+ begin
+ if (target_info.system in systems_objc_nfabi) then
+ begin
+ { export class and metaclass symbols }
+ exportname(def.rtti_mangledname(objcclassrtti),0);
+ exportname(def.rtti_mangledname(objcmetartti),0);
+ { export public/protected instance variable offset symbols }
+ exportobjcclassfields(def);
+ end
+ else
+ begin
+ { export the class symbol }
+ exportname('.objc_class_name_'+def.objextname^,0);
+ end;
+ end;
+
+end.