a0ee39bdccaa94d4fbd35c6e50954076b0360517
[WebKit-https.git] / JavaScriptCore / bindings / 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
31 #include "runtime_array.h"
32 #include "runtime_object.h"
33
34 #include "WebScriptObject.h"
35
36 #if !defined(_C_BYCOPY)
37 #define _C_BYCOPY 'O'
38 #endif
39
40 #if !defined(_C_BYREF)
41 #define _C_BYREF 'R'
42 #endif
43
44 #if !defined(_C_ONEWAY)
45 #define _C_ONEWAY 'V'
46 #endif
47
48 #if !defined(_C_GCINVISIBLE)
49 #define _C_GCINVISIBLE '!'
50 #endif
51
52 namespace KJS {
53 namespace Bindings {
54
55 /*
56     By default, a JavaScript method name is produced by concatenating the 
57     components of an ObjectiveC method name, replacing ':' with '_', and 
58     escaping '_' and '$' with a leading '$', such that '_' becomes "$_" and 
59     '$' becomes "$$". For example:
60
61     ObjectiveC name         Default JavaScript name
62         moveTo::                moveTo__
63         moveTo_                 moveTo$_
64         moveTo$_                moveTo$$$_
65
66     This function performs the inverse of that operation.
67  
68     @result Fills 'buffer' with the ObjectiveC method name that corresponds to 'JSName'. 
69             Returns true for success, false for failure. (Failure occurs when 'buffer' 
70             is not big enough to hold the result.)
71 */
72 bool convertJSMethodNameToObjc(const char *JSName, char *buffer, size_t bufferSize)
73 {
74     assert(JSName && buffer);
75     
76     const char *sp = JSName; // source pointer
77     char *dp = buffer; // destination pointer
78         
79     char *end = buffer + bufferSize;
80     while (dp < end) {
81         if (*sp == '$') {
82             ++sp;
83             *dp = *sp;
84         } else if (*sp == '_')
85             *dp = ':';
86         else
87             *dp = *sp;
88
89         // If a future coder puts funny ++ operators above, we might write off the end 
90         // of the buffer in the middle of this loop. Let's make sure to check for that.
91         assert(dp < end);
92         
93         if (*sp == 0) { // We finished converting JSName
94             assert(strlen(JSName) < bufferSize);
95             return true;
96         }
97         
98         ++sp; 
99         ++dp;
100     }
101
102     return false; // We ran out of buffer before converting JSName
103 }
104
105 /*
106
107     JavaScript to   ObjC
108     Number          coerced to char, short, int, long, float, double, or NSNumber, as appropriate
109     String          NSString
110     wrapper         id
111     Object          WebScriptObject
112     null            NSNull
113     [], other       exception
114
115 */
116 ObjcValue convertValueToObjcValue(ExecState *exec, JSValue *value, ObjcValueType type)
117 {
118     ObjcValue result;
119     double d = 0;
120
121     if (value->isNumber() || value->isString() || value->isBoolean())
122         d = value->toNumber(exec);
123
124     switch (type) {
125         case ObjcObjectType: {
126             Interpreter *originInterpreter = exec->dynamicInterpreter();
127             const RootObject *originExecutionContext = rootForInterpreter(originInterpreter);
128
129             Interpreter *interpreter = 0;
130             if (originInterpreter->isGlobalObject(value))
131                 interpreter = originInterpreter->interpreterForGlobalObject(value);
132
133             if (!interpreter)
134                 interpreter = originInterpreter;
135                 
136             const RootObject *executionContext = rootForInterpreter(interpreter);
137             if (!executionContext) {
138                 RootObject *newExecutionContext = new RootObject(0);
139                 newExecutionContext->setInterpreter (interpreter);
140                 executionContext = newExecutionContext;
141             }
142
143             if (!webScriptObjectClass)
144                 webScriptObjectClass = NSClassFromString(@"WebScriptObject");
145             result.objectValue = [webScriptObjectClass _convertValueToObjcValue:value originExecutionContext:originExecutionContext executionContext:executionContext ];
146         }
147         break;
148
149         case ObjcCharType:
150         case ObjcUnsignedCharType:
151             result.charValue = (char)d;
152             break;
153         case ObjcShortType:
154         case ObjcUnsignedShortType:
155             result.shortValue = (short)d;
156             break;
157         case ObjcIntType:
158         case ObjcUnsignedIntType:
159             result.intValue = (int)d;
160             break;
161         case ObjcLongType:
162         case ObjcUnsignedLongType:
163             result.longValue = (long)d;
164             break;
165         case ObjcLongLongType:
166         case ObjcUnsignedLongLongType:
167             result.longValue = (long long)d;
168             break;
169         case ObjcFloatType:
170             result.floatValue = (float)d;
171             break;
172         case ObjcDoubleType:
173             result.doubleValue = (double)d;
174             break;
175         case ObjcVoidType:
176             bzero(&result, sizeof(ObjcValue));
177             break;
178
179         case ObjcInvalidType:
180         default:
181             // FIXME: throw an exception?
182             break;
183     }
184
185     return result;
186 }
187
188 JSValue *convertNSStringToString(NSString *nsstring)
189 {
190     unichar *chars;
191     unsigned int length = [nsstring length];
192     chars = (unichar *)malloc(sizeof(unichar)*length);
193     [nsstring getCharacters:chars];
194     UString u((const UChar*)chars, length);
195     JSValue *aValue = jsString(u);
196     free((void *)chars);
197     return aValue;
198 }
199
200 /*
201     ObjC      to    JavaScript
202     ----            ----------
203     char            number
204     short           number
205     int             number
206     long            number
207     float           number
208     double          number
209     NSNumber        boolean or number
210     NSString        string
211     NSArray         array
212     NSNull          null
213     WebScriptObject underlying JavaScript object
214     WebUndefined    undefined
215     id              object wrapper
216     other           should not happen
217 */
218 JSValue* convertObjcValueToValue(ExecState* exec, void* buffer, ObjcValueType type)
219 {
220     static ClassStructPtr webUndefinedClass = 0;
221     if (!webUndefinedClass)
222         webUndefinedClass = NSClassFromString(@"WebUndefined");
223     if (!webScriptObjectClass)
224         webScriptObjectClass = NSClassFromString(@"WebScriptObject");
225
226     switch (type) {
227         case ObjcObjectType: {
228             id obj = *(id*)buffer;
229             if ([obj isKindOfClass:[NSString class]])
230                 return convertNSStringToString((NSString *)obj);
231             if ([obj isKindOfClass:webUndefinedClass])
232                 return jsUndefined();
233             if ((CFBooleanRef)obj == kCFBooleanTrue)
234                 return jsBoolean(true);
235             if ((CFBooleanRef)obj == kCFBooleanFalse)
236                 return jsBoolean(false);
237             if ([obj isKindOfClass:[NSNumber class]])
238                 return jsNumber([obj doubleValue]);
239             if ([obj isKindOfClass:[NSArray class]])
240                 return new RuntimeArray(exec, new ObjcArray(obj));
241             if ([obj isKindOfClass:webScriptObjectClass])
242                 return [obj _imp];
243             if ([obj isKindOfClass:[NSNull class]])
244                 return jsNull();
245             if (obj == 0)
246                 return jsUndefined();
247             return Instance::createRuntimeObject(Instance::ObjectiveCLanguage, obj);
248         }
249         case ObjcCharType:
250             return jsNumber(*(char *)buffer);
251         case ObjcUnsignedCharType:
252             return jsNumber(*(unsigned char *)buffer);
253         case ObjcShortType:
254             return jsNumber(*(short *)buffer);
255         case ObjcUnsignedShortType:
256             return jsNumber(*(unsigned short *)buffer);
257         case ObjcIntType:
258             return jsNumber(*(int *)buffer);
259         case ObjcUnsignedIntType:
260             return jsNumber(*(unsigned int *)buffer);
261         case ObjcLongType:
262             return jsNumber(*(long *)buffer);
263         case ObjcUnsignedLongType:
264             return jsNumber(*(unsigned long *)buffer);
265         case ObjcLongLongType:
266             return jsNumber(*(long long *)buffer);
267         case ObjcUnsignedLongLongType:
268             return jsNumber(*(unsigned long long *)buffer);
269         case ObjcFloatType:
270             return jsNumber(*(float *)buffer);
271         case ObjcDoubleType:
272             return jsNumber(*(double *)buffer);
273         default:
274             // Should never get here. Argument types are filtered.
275             fprintf(stderr, "%s: invalid type (%d)\n", __PRETTY_FUNCTION__, (int)type);
276             assert(false);
277     }
278     
279     return 0;
280 }
281
282 ObjcValueType objcValueTypeForType(const char *type)
283 {
284     int typeLength = strlen(type);
285     ObjcValueType objcValueType = ObjcInvalidType;
286
287     for (int i = 0; i < typeLength; ++i) {
288         char typeChar = type[i];
289         switch (typeChar) {
290             case _C_CONST:
291             case _C_BYCOPY:
292             case _C_BYREF:
293             case _C_ONEWAY:
294             case _C_GCINVISIBLE:
295                 // skip these type modifiers
296                 break;
297             case _C_ID:
298                 objcValueType = ObjcObjectType;
299                 break;
300             case _C_CHR:
301                 objcValueType = ObjcCharType;
302                 break;
303             case _C_UCHR:
304                 objcValueType = ObjcUnsignedCharType;
305                 break;
306             case _C_SHT:
307                 objcValueType = ObjcShortType;
308                 break;
309             case _C_USHT:
310                 objcValueType = ObjcUnsignedShortType;
311                 break;
312             case _C_INT:
313                 objcValueType = ObjcIntType;
314                 break;
315             case _C_UINT:
316                 objcValueType = ObjcUnsignedIntType;
317                 break;
318             case _C_LNG:
319                 objcValueType = ObjcLongType;
320                 break;
321             case _C_ULNG:
322                 objcValueType = ObjcUnsignedLongType;
323                 break;
324             case _C_LNG_LNG:
325                 objcValueType = ObjcLongLongType;
326                 break;
327             case _C_ULNG_LNG:
328                 objcValueType = ObjcUnsignedLongLongType;
329                 break;
330             case _C_FLT:
331                 objcValueType = ObjcFloatType;
332                 break;
333             case _C_DBL:
334                 objcValueType = ObjcDoubleType;
335                 break;
336             case _C_VOID:
337                 objcValueType = ObjcVoidType;
338                 break;
339             default:
340                 // Unhandled type. We don't handle C structs, unions, etc.
341                 // FIXME: throw an exception?
342                 assert(false);
343         }
344
345         if (objcValueType != ObjcInvalidType)
346             break;
347     }
348
349     return objcValueType;
350 }
351
352 void *createObjcInstanceForValue(JSValue *value, const RootObject *origin, const RootObject *current)
353 {
354     if (!value->isObject())
355         return 0;
356     if (!webScriptObjectClass)
357         webScriptObjectClass = NSClassFromString(@"WebScriptObject");
358     JSObject *object = static_cast<JSObject *>(value);
359     return [[[webScriptObjectClass alloc] _initWithJSObject:object originExecutionContext:origin executionContext:current] autorelease];
360 }
361
362 JSObject *throwError(ExecState *exec, ErrorType type, NSString *message)
363 {
364     assert(message);
365     size_t length = [message length];
366     unichar *buffer = new unichar[length];
367     [message getCharacters:buffer];
368     JSObject *error = throwError(exec, type, UString(reinterpret_cast<UChar *>(buffer), length));
369     delete [] buffer;
370     return error;
371 }
372
373 }
374 }