Line data Source code
1 0 : /*
2 :
3 : OOJSCall.h
4 :
5 : Basic JavaScript-to-ObjC bridge implementation.
6 :
7 : Oolite
8 : Copyright (C) 2004-2013 Giles C Williams and contributors
9 :
10 : This program is free software; you can redistribute it and/or
11 : modify it under the terms of the GNU General Public License
12 : as published by the Free Software Foundation; either version 2
13 : of the License, or (at your option) any later version.
14 :
15 : This program is distributed in the hope that it will be useful,
16 : but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : GNU General Public License for more details.
19 :
20 : You should have received a copy of the GNU General Public License
21 : along with this program; if not, write to the Free Software
22 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
23 : MA 02110-1301, USA.
24 :
25 : */
26 :
27 : #ifndef NDEBUG
28 :
29 :
30 : #import "OOJSCall.h"
31 : #import "OOJavaScriptEngine.h"
32 :
33 : #import "OOFunctionAttributes.h"
34 : #import "ShipEntity.h"
35 : #import "OOCollectionExtractors.h"
36 : #import "OOShaderUniformMethodType.h"
37 : #import "OOJSVector.h"
38 : #import "OOJSQuaternion.h"
39 :
40 :
41 0 : typedef enum
42 : {
43 : kMethodTypeInvalid = kOOShaderUniformTypeInvalid,
44 :
45 : kMethodTypeCharVoid = kOOShaderUniformTypeChar,
46 : kMethodTypeUnsignedCharVoid = kOOShaderUniformTypeUnsignedChar,
47 : kMethodTypeShortVoid = kOOShaderUniformTypeShort,
48 : kMethodTypeUnsignedShortVoid = kOOShaderUniformTypeUnsignedShort,
49 : kMethodTypeIntVoid = kOOShaderUniformTypeInt,
50 : kMethodTypeUnsignedIntVoid = kOOShaderUniformTypeUnsignedInt,
51 : kMethodTypeLongVoid = kOOShaderUniformTypeLong,
52 : kMethodTypeUnsignedLongVoid = kOOShaderUniformTypeUnsignedLong,
53 : kMethodTypeFloatVoid = kOOShaderUniformTypeFloat,
54 : kMethodTypeDoubleVoid = kOOShaderUniformTypeDouble,
55 : kMethodTypeVectorVoid = kOOShaderUniformTypeVector,
56 : kMethodTypeQuaternionVoid = kOOShaderUniformTypeQuaternion,
57 : kMethodTypeMatrixVoid = kOOShaderUniformTypeMatrix,
58 : kMethodTypePointVoid = kOOShaderUniformTypePoint,
59 :
60 : kMethodTypeObjectVoid = kOOShaderUniformTypeObject,
61 : kMethodTypeObjectObject,
62 : kMethodTypeVoidVoid,
63 : kMethodTypeVoidObject
64 : } MethodType;
65 :
66 :
67 : static MethodType GetMethodType(id object, SEL selector);
68 0 : OOINLINE BOOL MethodExpectsParameter(MethodType type) { return type == kMethodTypeVoidObject || type == kMethodTypeObjectObject; }
69 :
70 :
71 0 : BOOL OOJSCallObjCObjectMethod(JSContext *context, id object, NSString *oo_jsClassName, uintN argc, jsval *argv, jsval *outResult)
72 : {
73 : OOJS_PROFILE_ENTER
74 :
75 : NSString *selectorString = nil;
76 : SEL selector = NULL;
77 : NSString *paramString = nil;
78 : MethodType type;
79 : BOOL haveParameter = NO,
80 : error = NO;
81 : id result = nil;
82 :
83 : if (argc == 0)
84 : {
85 : OOJSReportError(context, @"%@.callObjC(): no selector specified.", oo_jsClassName);
86 : return NO;
87 : }
88 :
89 : if ([object isKindOfClass:[ShipEntity class]])
90 : {
91 : [PLAYER setScriptTarget:object];
92 : }
93 :
94 : selectorString = OOStringFromJSValue(context, argv[0]);
95 :
96 : // Join all parameters together with spaces.
97 : if (1 < argc && [selectorString hasSuffix:@":"])
98 : {
99 : haveParameter = YES;
100 : paramString = [NSString concatenationOfStringsFromJavaScriptValues:argv + 1 count:argc - 1 separator:@" " inContext:context];
101 : }
102 :
103 : selector = NSSelectorFromString(selectorString);
104 :
105 : if ([object respondsToSelector:selector])
106 : {
107 : // Validate signature.
108 : type = GetMethodType(object, selector);
109 :
110 : if (MethodExpectsParameter(type) && !haveParameter)
111 : {
112 : OOJSReportError(context, @"%@.callObjC(): method %@ requires a parameter.", oo_jsClassName, selectorString);
113 : error = YES;
114 : }
115 : else
116 : {
117 : IMP method = [object methodForSelector:selector];
118 : switch (type)
119 : {
120 : case kMethodTypeVoidObject:
121 : [object performSelector:selector withObject:paramString];
122 : break;
123 :
124 : case kMethodTypeObjectObject:
125 : result = [object performSelector:selector withObject:paramString];
126 : break;
127 :
128 : case kMethodTypeObjectVoid:
129 : result = [object performSelector:selector];
130 : if ([selectorString hasSuffix:@"_bool"]) result = [NSNumber numberWithBool:OOBooleanFromObject(result, NO)];
131 : break;
132 :
133 : case kMethodTypeVoidVoid:
134 : [object performSelector:selector];
135 : break;
136 :
137 : case kMethodTypeCharVoid:
138 : case kMethodTypeUnsignedCharVoid:
139 : case kMethodTypeShortVoid:
140 : case kMethodTypeUnsignedShortVoid:
141 : case kMethodTypeIntVoid:
142 : case kMethodTypeUnsignedIntVoid:
143 : case kMethodTypeLongVoid:
144 : result = [NSNumber numberWithLongLong:OOCallIntegerMethod(object, selector, method, (OOShaderUniformType)type)];
145 : break;
146 :
147 : case kMethodTypeUnsignedLongVoid:
148 : result = [NSNumber numberWithUnsignedLongLong:OOCallIntegerMethod(object, selector, method, (OOShaderUniformType)type)];
149 : break;
150 :
151 : case kMethodTypeFloatVoid:
152 : case kMethodTypeDoubleVoid:
153 : result = [NSNumber numberWithDouble:OOCallFloatMethod(object, selector, method, (OOShaderUniformType)type)];
154 : break;
155 :
156 : case kMethodTypeVectorVoid:
157 : {
158 : Vector v = ((VectorReturnMsgSend)method)(object, selector);
159 : *outResult = OBJECT_TO_JSVAL(JSVectorWithVector(context, v));
160 : break;
161 : }
162 :
163 : case kMethodTypeQuaternionVoid:
164 : {
165 : Quaternion q = ((QuaternionReturnMsgSend)method)(object, selector);
166 : *outResult = OBJECT_TO_JSVAL(JSQuaternionWithQuaternion(context, q));
167 : break;
168 : }
169 :
170 : case kMethodTypeMatrixVoid:
171 : case kMethodTypePointVoid:
172 : case kMethodTypeInvalid:
173 : OOJSReportError(context, @"%@.callObjC(): method %@ cannot be called from JavaScript.", oo_jsClassName, selectorString);
174 : error = YES;
175 : break;
176 : }
177 : if (result != nil)
178 : {
179 : *outResult = [result oo_jsValueInContext:context];
180 : }
181 : }
182 : }
183 : else
184 : {
185 : OOJSReportError(context, @"%@.callObjC(): %@ does not respond to method %@.", oo_jsClassName, [object shortDescription], selectorString);
186 : error = YES;
187 : }
188 :
189 : return !error;
190 :
191 : OOJS_PROFILE_EXIT
192 : }
193 :
194 :
195 : // Template class providing method signature strings for the four signatures we support.
196 0 : @interface OOJSCallMethodSignatureTemplateClass: NSObject
197 :
198 0 : - (void)voidVoidMethod;
199 0 : - (void)voidObjectMethod:(id)object;
200 0 : - (id)objectObjectMethod:(id)object;
201 :
202 : @end
203 :
204 :
205 0 : static BOOL SignatureMatch(NSMethodSignature *sig, SEL selector)
206 : {
207 : NSMethodSignature *template = nil;
208 :
209 : template = [OOJSCallMethodSignatureTemplateClass instanceMethodSignatureForSelector:selector];
210 : return [sig isEqual:template];
211 : }
212 :
213 :
214 0 : static MethodType GetMethodType(id object, SEL selector)
215 : {
216 : NSMethodSignature *sig = [object methodSignatureForSelector:selector];
217 :
218 : if (SignatureMatch(sig, @selector(voidVoidMethod))) return kMethodTypeVoidVoid;
219 : if (SignatureMatch(sig, @selector(voidObjectMethod:))) return kMethodTypeVoidObject;
220 : if (SignatureMatch(sig, @selector(objectObjectMethod:))) return kMethodTypeObjectObject;
221 :
222 : MethodType type = (MethodType)OOShaderUniformTypeFromMethodSignature(sig);
223 : if (type != kMethodTypeInvalid) return type;
224 :
225 : return kMethodTypeInvalid;
226 : }
227 :
228 :
229 : @implementation OOJSCallMethodSignatureTemplateClass: NSObject
230 :
231 : - (void)voidVoidMethod {}
232 :
233 :
234 : - (void)voidObjectMethod:(id)object {}
235 :
236 :
237 : - (id)objectObjectMethod:(id)object { return nil; }
238 :
239 : @end
240 :
241 : #endif
|