2011-01-07 Adam Barth <abarth@webkit.org>
[WebKit-https.git] / WebCore / bridge / objc / objc_utility.mm
1 /*
2  * Copyright (C) 2004 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "objc_utility.h"
28
29 #include "objc_instance.h"
30 #include "runtime_array.h"
31 #include "runtime_object.h"
32 #include "WebScriptObject.h"
33 #include <runtime/JSGlobalObject.h>
34 #include <runtime/JSLock.h>
35 #include <wtf/Assertions.h>
36
37 #if !defined(_C_LNG_LNG)
38 #define _C_LNG_LNG 'q'
39 #endif
40
41 #if !defined(_C_ULNG_LNG)
42 #define _C_ULNG_LNG 'Q'
43 #endif
44
45 #if !defined(_C_CONST)
46 #define _C_CONST 'r'
47 #endif
48
49 #if !defined(_C_BYCOPY)
50 #define _C_BYCOPY 'O'
51 #endif
52
53 #if !defined(_C_BYREF)
54 #define _C_BYREF 'R'
55 #endif
56
57 #if !defined(_C_ONEWAY)
58 #define _C_ONEWAY 'V'
59 #endif
60
61 #if !defined(_C_GCINVISIBLE)
62 #define _C_GCINVISIBLE '!'
63 #endif
64
65 namespace JSC {
66 namespace Bindings {
67
68 /*
69     By default, a JavaScript method name is produced by concatenating the 
70     components of an ObjectiveC method name, replacing ':' with '_', and 
71     escaping '_' and '$' with a leading '$', such that '_' becomes "$_" and 
72     '$' becomes "$$". For example:
73
74     ObjectiveC name         Default JavaScript name
75         moveTo::                moveTo__
76         moveTo_                 moveTo$_
77         moveTo$_                moveTo$$$_
78
79     This function performs the inverse of that operation.
80  
81     @result Fills 'buffer' with the ObjectiveC method name that corresponds to 'JSName'. 
82             Returns true for success, false for failure. (Failure occurs when 'buffer' 
83             is not big enough to hold the result.)
84 */
85 bool convertJSMethodNameToObjc(const char *JSName, char *buffer, size_t bufferSize)
86 {
87     ASSERT(JSName && buffer);
88     
89     const char *sp = JSName; // source pointer
90     char *dp = buffer; // destination pointer
91         
92     char *end = buffer + bufferSize;
93     while (dp < end) {
94         if (*sp == '$') {
95             ++sp;
96             *dp = *sp;
97         } else if (*sp == '_')
98             *dp = ':';
99         else
100             *dp = *sp;
101
102         // If a future coder puts funny ++ operators above, we might write off the end 
103         // of the buffer in the middle of this loop. Let's make sure to check for that.
104         ASSERT(dp < end);
105         
106         if (*sp == 0) { // We finished converting JSName
107             ASSERT(strlen(JSName) < bufferSize);
108             return true;
109         }
110         
111         ++sp; 
112         ++dp;
113     }
114
115     return false; // We ran out of buffer before converting JSName
116 }
117
118 /*
119
120     JavaScript to   ObjC
121     Number          coerced to char, short, int, long, float, double, or NSNumber, as appropriate
122     String          NSString
123     wrapper         id
124     Object          WebScriptObject
125     null            NSNull
126     [], other       exception
127
128 */
129 ObjcValue convertValueToObjcValue(ExecState* exec, JSValue value, ObjcValueType type)
130 {
131     ObjcValue result;
132     double d = 0;
133
134     if (value.isNumber() || value.isString() || value.isBoolean())
135         d = value.toNumber(exec);
136
137     switch (type) {
138         case ObjcObjectType: {
139             JSLock lock(SilenceAssertionsOnly);
140             
141             JSGlobalObject *originGlobalObject = exec->dynamicGlobalObject();
142             RootObject* originRootObject = findRootObject(originGlobalObject);
143
144             JSGlobalObject* globalObject = 0;
145             if (value.isObject() && asObject(value)->isGlobalObject())
146                 globalObject = static_cast<JSGlobalObject*>(asObject(value));
147
148             if (!globalObject)
149                 globalObject = originGlobalObject;
150                 
151             RootObject* rootObject = findRootObject(globalObject);
152             result.objectValue =  rootObject
153                 ? [webScriptObjectClass() _convertValueToObjcValue:value originRootObject:originRootObject rootObject:rootObject]
154                 : nil;
155         }
156         break;
157
158         case ObjcCharType:
159         case ObjcUnsignedCharType:
160             result.charValue = (char)d;
161             break;
162         case ObjcShortType:
163         case ObjcUnsignedShortType:
164             result.shortValue = (short)d;
165             break;
166         case ObjcIntType:
167         case ObjcUnsignedIntType:
168             result.intValue = (int)d;
169             break;
170         case ObjcLongType:
171         case ObjcUnsignedLongType:
172             result.longValue = (long)d;
173             break;
174         case ObjcLongLongType:
175         case ObjcUnsignedLongLongType:
176             result.longLongValue = (long long)d;
177             break;
178         case ObjcFloatType:
179             result.floatValue = (float)d;
180             break;
181         case ObjcDoubleType:
182             result.doubleValue = (double)d;
183             break;
184         case ObjcVoidType:
185             bzero(&result, sizeof(ObjcValue));
186             break;
187
188         case ObjcInvalidType:
189         default:
190             // FIXME: throw an exception?
191             break;
192     }
193
194     return result;
195 }
196
197 JSValue convertNSStringToString(ExecState* exec, NSString *nsstring)
198 {
199     JSLock lock(SilenceAssertionsOnly);
200     
201     unichar *chars;
202     unsigned int length = [nsstring length];
203     chars = (unichar *)malloc(sizeof(unichar)*length);
204     [nsstring getCharacters:chars];
205     UString u((const UChar*)chars, length);
206     JSValue aValue = jsString(exec, u);
207     free((void *)chars);
208     return aValue;
209 }
210
211 /*
212     ObjC      to    JavaScript
213     ----            ----------
214     char            number
215     short           number
216     int             number
217     long            number
218     float           number
219     double          number
220     NSNumber        boolean or number
221     NSString        string
222     NSArray         array
223     NSNull          null
224     WebScriptObject underlying JavaScript object
225     WebUndefined    undefined
226     id              object wrapper
227     other           should not happen
228 */
229 JSValue convertObjcValueToValue(ExecState* exec, void* buffer, ObjcValueType type, RootObject* rootObject)
230 {
231     JSLock lock(SilenceAssertionsOnly);
232     
233     switch (type) {
234         case ObjcObjectType: {
235             id obj = *(id*)buffer;
236             if ([obj isKindOfClass:[NSString class]])
237                 return convertNSStringToString(exec, (NSString *)obj);
238             if ([obj isKindOfClass:webUndefinedClass()])
239                 return jsUndefined();
240             if ((CFBooleanRef)obj == kCFBooleanTrue)
241                 return jsBoolean(true);
242             if ((CFBooleanRef)obj == kCFBooleanFalse)
243                 return jsBoolean(false);
244             if ([obj isKindOfClass:[NSNumber class]])
245                 return jsNumber([obj doubleValue]);
246             if ([obj isKindOfClass:[NSArray class]])
247                 return new (exec) RuntimeArray(exec, new ObjcArray(obj, rootObject));
248             if ([obj isKindOfClass:webScriptObjectClass()]) {
249                 JSObject* imp = [obj _imp];
250                 return imp ? imp : jsUndefined();
251             }
252             if ([obj isKindOfClass:[NSNull class]])
253                 return jsNull();
254             if (obj == 0)
255                 return jsUndefined();
256             return ObjcInstance::create(obj, rootObject)->createRuntimeObject(exec);
257         }
258         case ObjcCharType:
259             return jsNumber(*(char*)buffer);
260         case ObjcUnsignedCharType:
261             return jsNumber(*(unsigned char*)buffer);
262         case ObjcShortType:
263             return jsNumber(*(short*)buffer);
264         case ObjcUnsignedShortType:
265             return jsNumber(*(unsigned short*)buffer);
266         case ObjcIntType:
267             return jsNumber(*(int*)buffer);
268         case ObjcUnsignedIntType:
269             return jsNumber(*(unsigned int*)buffer);
270         case ObjcLongType:
271             return jsNumber(*(long*)buffer);
272         case ObjcUnsignedLongType:
273             return jsNumber(*(unsigned long*)buffer);
274         case ObjcLongLongType:
275             return jsNumber(*(long long*)buffer);
276         case ObjcUnsignedLongLongType:
277             return jsNumber(*(unsigned long long*)buffer);
278         case ObjcFloatType:
279             return jsNumber(*(float*)buffer);
280         case ObjcDoubleType:
281             return jsNumber(*(double*)buffer);
282         default:
283             // Should never get here. Argument types are filtered.
284             fprintf(stderr, "%s: invalid type (%d)\n", __PRETTY_FUNCTION__, (int)type);
285             ASSERT(false);
286     }
287     
288     return jsUndefined();
289 }
290
291 ObjcValueType objcValueTypeForType(const char *type)
292 {
293     int typeLength = strlen(type);
294     ObjcValueType objcValueType = ObjcInvalidType;
295
296     for (int i = 0; i < typeLength; ++i) {
297         char typeChar = type[i];
298         switch (typeChar) {
299             case _C_CONST:
300             case _C_BYCOPY:
301             case _C_BYREF:
302             case _C_ONEWAY:
303             case _C_GCINVISIBLE:
304                 // skip these type modifiers
305                 break;
306             case _C_ID:
307                 objcValueType = ObjcObjectType;
308                 break;
309             case _C_CHR:
310                 objcValueType = ObjcCharType;
311                 break;
312             case _C_UCHR:
313                 objcValueType = ObjcUnsignedCharType;
314                 break;
315             case _C_SHT:
316                 objcValueType = ObjcShortType;
317                 break;
318             case _C_USHT:
319                 objcValueType = ObjcUnsignedShortType;
320                 break;
321             case _C_INT:
322                 objcValueType = ObjcIntType;
323                 break;
324             case _C_UINT:
325                 objcValueType = ObjcUnsignedIntType;
326                 break;
327             case _C_LNG:
328                 objcValueType = ObjcLongType;
329                 break;
330             case _C_ULNG:
331                 objcValueType = ObjcUnsignedLongType;
332                 break;
333             case _C_LNG_LNG:
334                 objcValueType = ObjcLongLongType;
335                 break;
336             case _C_ULNG_LNG:
337                 objcValueType = ObjcUnsignedLongLongType;
338                 break;
339             case _C_FLT:
340                 objcValueType = ObjcFloatType;
341                 break;
342             case _C_DBL:
343                 objcValueType = ObjcDoubleType;
344                 break;
345             case _C_VOID:
346                 objcValueType = ObjcVoidType;
347                 break;
348             default:
349                 // Unhandled type. We don't handle C structs, unions, etc.
350                 // FIXME: throw an exception?
351                 ASSERT(false);
352         }
353
354         if (objcValueType != ObjcInvalidType)
355             break;
356     }
357
358     return objcValueType;
359 }
360
361 JSObject *throwError(ExecState *exec, NSString *message)
362 {
363     ASSERT(message);
364     size_t length = [message length];
365     unichar *buffer = new unichar[length];
366     [message getCharacters:buffer];
367     JSObject *error = JSC::throwError(exec, JSC::createError(exec, UString(buffer, length)));
368     delete [] buffer;
369     return error;
370 }
371
372 }
373 }