1 {
2     Copyright (c) 2009-2010 by Jonas Maebe
3 
4     This unit implements some Objective-C helper routines at the node tree
5     level.
6 
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11 
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with this program; if not, write to the Free Software
19     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21  ****************************************************************************
22 }
23 
24 {$i fpcdefs.inc}
25 
26 unit objcutil;
27 
28 interface
29 
30     uses
31       node,
32       symtype,symdef;
33 
34     { Check whether a string contains a syntactically valid selector name.  }
objcvalidselectornamenull35     function objcvalidselectorname(value_str: pchar; len: longint): boolean;
36 
37     { Generate a node loading the superclass structure necessary to call
38       an inherited Objective-C method.  }
objcsuperclassnodenull39     function objcsuperclassnode(def: tdef): tnode;
40 
41     { Encode a method's parameters and result type into the format used by the
42       run time (for generating protocol and class rtti).  }
objcencodemethodnull43     function objcencodemethod(pd: tabstractprocdef): ansistring;
44 
45     { Exports all assembler symbols related to the obj-c class }
46     procedure exportobjcclass(def: tobjectdef);
47 
48     { loads a field of an Objective-C root class (such as ISA) }
objcloadbasefieldnull49     function objcloadbasefield(n: tnode; const fieldname: string): tnode;
50 
51 
52 implementation
53 
54     uses
55       globtype,
56       cutils,
57       pass_1,
58       verbose,systems,
59       symconst,symsym,
60       objcdef,
61       defutil,paramgr,
62       nmem,ncal,nld,ncon,ncnv,
63       export;
64 
65 
66 {******************************************************************
67                        validselectorname
68 *******************************************************************}
69 
objcvalidselectornamenull70 function objcvalidselectorname(value_str: pchar; len: longint): boolean;
71   var
72     i         : longint;
73     gotcolon  : boolean;
74 begin
75   result:=false;
76   { empty name is not allowed }
77   if (len=0) then
78     exit;
79 
80   gotcolon:=false;
81 
82   { if the first character is a colon, all of them must be colons }
83   if (value_str[0] = ':') then
84     begin
85       for i:=1 to len-1 do
86         if (value_str[i]<>':') then
87           exit;
88     end
89   else
90     begin
91       { no special characters other than ':'
92       }
93       for i:=0 to len-1 do
94         if (value_str[i] = ':') then
95           gotcolon:=true
96         else if not(value_str[i] in ['_','A'..'Z','a'..'z','0'..'9',':']) then
97           exit;
98 
99       { if there is at least one colon, the final character must
100         also be a colon (in case it's only one character that is
101         a colon, this was already checked before the above loop)
102       }
103       if gotcolon and
104          (value_str[len-1] <> ':') then
105         exit;
106     end;
107 
108   result:=true;
109 end;
110 
111 {******************************************************************
112                        objcsuperclassnode
113 *******************************************************************}
114 
objcloadbasefieldnull115     function objcloadbasefield(n: tnode; const fieldname: string): tnode;
116       var
117         vs         : tsym;
118       begin
119         vs:=tsym(tabstractrecorddef(objc_objecttype).symtable.Find(fieldname));
120         if not assigned(vs) or
121            (vs.typ<>fieldvarsym) then
122           internalerror(200911301);
123         if fieldname='ISA' then
124           result:=ctypeconvnode.create_internal(
125             cderefnode.create(
126               ctypeconvnode.create_internal(n,
127                 cpointerdef.getreusable(cpointerdef.getreusable(voidpointertype))
128               )
129             ),tfieldvarsym(vs).vardef
130           )
131         else
132           begin
133             result:=cderefnode.create(ctypeconvnode.create_internal(n,objc_idtype));
134             result:=csubscriptnode.create(vs,result);
135           end;
136       end;
137 
138 
objcsuperclassnodenull139     function objcsuperclassnode(def: tdef): tnode;
140       var
141         para       : tcallparanode;
142       begin
143         { only valid for Objective-C classes and classrefs }
144         if not is_objcclass(def) and
145            not is_objcclassref(def) then
146           internalerror(2009090901);
147         { Can be done a lot more efficiently with direct symbol accesses, but
148           requires extra node types. Maybe later. }
149         if is_objcclassref(def) then
150           begin
151             if (oo_is_classhelper in tobjectdef(tclassrefdef(def).pointeddef).objectoptions) then
152               begin
153                 { in case we are in a category method, we need the metaclass of the
154                   superclass class extended by this category (= metaclass of superclass of superclass)
155                   for the fragile abi, and the metaclass of the superclass for the non-fragile ABI }
156 {$if defined(onlymacosx10_6) or defined(arm) or defined(aarch64)}
157                 { NOTE: those send2 methods are only available on Mac OS X 10.6 and later!
158                     (but also on all iPhone SDK revisions we support) }
159                 if (target_info.system in systems_objc_nfabi) then
160                   result:=cloadvmtaddrnode.create(ctypenode.create(tobjectdef(tclassrefdef(def).pointeddef).childof))
161                 else
162 {$endif onlymacosx10_6 or arm aarch64}
163                   result:=cloadvmtaddrnode.create(ctypenode.create(tobjectdef(tclassrefdef(def).pointeddef).childof.childof));
164                 tloadvmtaddrnode(result).forcall:=true;
165                 result:=cloadvmtaddrnode.create(result);
166                 typecheckpass(result);
167                 { we're done }
168                 exit;
169               end
170             else
171               begin
172                 { otherwise we need the superclass of the metaclass }
173                 para:=ccallparanode.create(cstringconstnode.createstr(tobjectdef(tclassrefdef(def).pointeddef).objextname^),nil);
174                 result:=ccallnode.createinternfromunit('OBJC','OBJC_GETMETACLASS',para);
175               end
176           end
177         else
178           begin
179             if not(oo_is_classhelper in tobjectdef(def).objectoptions) then
180               result:=cloadvmtaddrnode.create(ctypenode.create(def))
181             else
182               result:=cloadvmtaddrnode.create(ctypenode.create(tobjectdef(def).childof));
183             tloadvmtaddrnode(result).forcall:=true;
184           end;
185 
186 {$if defined(onlymacosx10_6) or defined(arm) or defined(aarch64)}
187         { For the non-fragile ABI, the superclass send2 method itself loads the
188           superclass. For the fragile ABI, we have to do this ourselves.
189 
190           NOTE: those send2 methods are only available on Mac OS X 10.6 and later!
191             (but also on all iPhone SDK revisions we support) }
192         if not(target_info.system in systems_objc_nfabi) then
193 {$endif onlymacosx10_6 or arm or aarch64}
194           result:=objcloadbasefield(result,'SUPERCLASS');
195         typecheckpass(result);
196       end;
197 
198 
199 {******************************************************************
200                           Type encoding
201 *******************************************************************}
202 
objcparasizenull203     function objcparasize(vs: tparavarsym): ptrint;
204       begin
205         result:=vs.paraloc[callerside].intsize;
206         { In Objective-C, all ordinal types are widened to at least the
207           size of the C "int" type. Assume __LP64__/4 byte ints for now. }
208         if is_ordinal(vs.vardef) and
209            (result<4) then
210           result:=4;
211       end;
212 
213 
objcencodemethodnull214     function objcencodemethod(pd: tabstractprocdef): ansistring;
215       var
216         parasize,
217         totalsize: aint;
218         vs: tparavarsym;
219         i: longint;
220         temp: ansistring;
221         founderror: tdef;
222       begin
223         result:='';
224         totalsize:=0;
225         pd.init_paraloc_info(callerside);
226 {$if defined(powerpc) and defined(dummy)}
227         { Disabled, because neither Clang nor gcc does this, and the ObjC
228           runtime contains an explicit fix to detect this error.  }
229 
230         { On ppc, the callee is responsible for removing the hidden function
231           result parameter from the stack, so it has to know. On i386, it's
232           the caller that does this.  }
233         if (pd.returndef<>voidtype) and
234             paramgr.ret_in_param(pd.returndef,pocall_cdecl) then
235           inc(totalsize,sizeof(pint));
236 {$endif}
237         for i:=0 to pd.paras.count-1 do
238           begin
239             vs:=tparavarsym(pd.paras[i]);
240             if (vo_is_funcret in vs.varoptions) then
241               continue;
242             { objcaddencodedtype always assumes a value parameter, so add
243               a pointer indirection for var/out parameters.  }
244             if not paramanager.push_addr_param(vs_value,vs.vardef,pocall_cdecl) and
245                (vs.varspez in [vs_var,vs_out,vs_constref]) then
246               result:=result+'^';
247             { Add the parameter type.  }
248             if (vo_is_parentfp in vs.varoptions) and
249                (po_is_block in pd.procoptions) then
250               { special case: self parameter of block procvars has to be @? }
251               result:=result+'@?'
252             else if not objcaddencodedtype(vs.vardef,ris_initial,false,result,founderror) then
253               { should be checked earlier on }
254               internalerror(2009081701);
255             { And the total size of the parameters coming before this one
256               (i.e., the "offset" of this parameter).  }
257             result:=result+tostr(totalsize);
258             { Update the total parameter size }
259             parasize:=objcparasize(vs);
260             inc(totalsize,parasize);
261           end;
262         { Prepend the total parameter size.  }
263         result:=tostr(totalsize)+result;
264         { And the type of the function result (void in case of a procedure).  }
265         temp:='';
266         if not objcaddencodedtype(pd.returndef,ris_initial,false,temp,founderror) then
267           internalerror(2009081801);
268         result:=temp+result;
269       end;
270 
271 
272 {******************************************************************
273                     ObjC class exporting
274 *******************************************************************}
275 
276     procedure exportobjcclassfields(objccls: tobjectdef);
277     var
278       i: longint;
279       vf: tfieldvarsym;
280       prefix: string;
281     begin
282       prefix:=target_info.cprefix+'OBJC_IVAR_$_'+objccls.objextname^+'.';
283       for i:=0 to objccls.symtable.SymList.Count-1 do
284         if tsym(objccls.symtable.SymList[i]).typ=fieldvarsym then
285           begin
286             vf:=tfieldvarsym(objccls.symtable.SymList[i]);
287             { TODO: package visibility (private_extern) -- must not be exported
288                either}
289             if not(vf.visibility in [vis_private,vis_strictprivate]) then
290               exportname(prefix+vf.RealName,[]);
291           end;
292     end;
293 
294 
295     procedure exportobjcclass(def: tobjectdef);
296       begin
297         if (target_info.system in systems_objc_nfabi) then
298           begin
299             { export class and metaclass symbols }
300             exportname(def.rtti_mangledname(objcclassrtti),[]);
301             exportname(def.rtti_mangledname(objcmetartti),[]);
302             { export public/protected instance variable offset symbols }
303             exportobjcclassfields(def);
304           end
305         else
306           begin
307              { export the class symbol }
308              exportname('.objc_class_name_'+def.objextname^,[]);
309           end;
310       end;
311 
312 end.
313