objc_utility.mm revision cad810f21b803229eb11403f9209855525a25d57
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
65namespace JSC {
66namespace 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*/
85bool 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*/
129ObjcValue 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
197JSValue 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*/
229JSValue 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
291ObjcValueType 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
361JSObject *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}
374