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