1/*
2 * Copyright (C) 2004, 2013 Apple 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 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 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
70    JavaScript to   ObjC
71    Number          coerced to char, short, int, long, float, double, or NSNumber, as appropriate
72    String          NSString
73    wrapper         id
74    Object          WebScriptObject
75    null            NSNull
76    [], other       exception
77
78*/
79ObjcValue convertValueToObjcValue(ExecState* exec, JSValue value, ObjcValueType type)
80{
81    ObjcValue result;
82    double d = 0;
83
84    if (value.isNumber() || value.isString() || value.isBoolean())
85        d = value.toNumber(exec);
86
87    switch (type) {
88        case ObjcObjectType: {
89            JSLockHolder lock(exec);
90
91            JSGlobalObject *originGlobalObject = exec->vmEntryGlobalObject();
92            RootObject* originRootObject = findRootObject(originGlobalObject);
93
94            JSGlobalObject* globalObject = 0;
95            if (value.isObject() && asObject(value)->isGlobalObject())
96                globalObject = jsCast<JSGlobalObject*>(asObject(value));
97
98            if (!globalObject)
99                globalObject = originGlobalObject;
100
101            RootObject* rootObject = findRootObject(globalObject);
102            result.objectValue =  rootObject
103                ? [webScriptObjectClass() _convertValueToObjcValue:value originRootObject:originRootObject rootObject:rootObject]
104                : nil;
105        }
106        break;
107
108        case ObjcCharType:
109        case ObjcUnsignedCharType:
110            result.charValue = (char)d;
111            break;
112        case ObjcShortType:
113        case ObjcUnsignedShortType:
114            result.shortValue = (short)d;
115            break;
116        case ObjcBoolType:
117            result.booleanValue = (bool)d;
118            break;
119        case ObjcIntType:
120        case ObjcUnsignedIntType:
121            result.intValue = (int)d;
122            break;
123        case ObjcLongType:
124        case ObjcUnsignedLongType:
125            result.longValue = (long)d;
126            break;
127        case ObjcLongLongType:
128        case ObjcUnsignedLongLongType:
129            result.longLongValue = (long long)d;
130            break;
131        case ObjcFloatType:
132            result.floatValue = (float)d;
133            break;
134        case ObjcDoubleType:
135            result.doubleValue = (double)d;
136            break;
137        case ObjcVoidType:
138            bzero(&result, sizeof(ObjcValue));
139            break;
140
141        case ObjcInvalidType:
142        default:
143            // FIXME: throw an exception?
144            break;
145    }
146
147    return result;
148}
149
150JSValue convertNSStringToString(ExecState* exec, NSString *nsstring)
151{
152    JSLockHolder lock(exec);
153    JSValue aValue = jsString(exec, String(nsstring));
154    return aValue;
155}
156
157/*
158    ObjC      to    JavaScript
159    ----            ----------
160    char            number
161    short           number
162    int             number
163    long            number
164    float           number
165    double          number
166    NSNumber        boolean or number
167    NSString        string
168    NSArray         array
169    NSNull          null
170    WebScriptObject underlying JavaScript object
171    WebUndefined    undefined
172    id              object wrapper
173    other           should not happen
174*/
175JSValue convertObjcValueToValue(ExecState* exec, void* buffer, ObjcValueType type, RootObject* rootObject)
176{
177    JSLockHolder lock(exec);
178
179    switch (type) {
180        case ObjcObjectType: {
181            id obj = *(id*)buffer;
182            if ([obj isKindOfClass:[NSString class]])
183                return convertNSStringToString(exec, (NSString *)obj);
184            if ([obj isKindOfClass:webUndefinedClass()])
185                return jsUndefined();
186            if ((CFBooleanRef)obj == kCFBooleanTrue)
187                return jsBoolean(true);
188            if ((CFBooleanRef)obj == kCFBooleanFalse)
189                return jsBoolean(false);
190            if ([obj isKindOfClass:[NSNumber class]])
191                return jsNumber([obj doubleValue]);
192            if ([obj isKindOfClass:[NSArray class]])
193                return RuntimeArray::create(exec, new ObjcArray(obj, rootObject));
194            if ([obj isKindOfClass:webScriptObjectClass()]) {
195                JSObject* imp = [obj _imp];
196                return imp ? imp : jsUndefined();
197            }
198            if ([obj isKindOfClass:[NSNull class]])
199                return jsNull();
200            if (obj == 0)
201                return jsUndefined();
202            return ObjcInstance::create(obj, rootObject)->createRuntimeObject(exec);
203        }
204        case ObjcCharType:
205            return jsNumber(*(char*)buffer);
206        case ObjcUnsignedCharType:
207            return jsNumber(*(unsigned char*)buffer);
208        case ObjcShortType:
209            return jsNumber(*(short*)buffer);
210        case ObjcUnsignedShortType:
211            return jsNumber(*(unsigned short*)buffer);
212        case ObjcBoolType:
213            return jsBoolean(*(bool*)buffer);
214        case ObjcIntType:
215            return jsNumber(*(int*)buffer);
216        case ObjcUnsignedIntType:
217            return jsNumber(*(unsigned int*)buffer);
218        case ObjcLongType:
219            return jsNumber(*(long*)buffer);
220        case ObjcUnsignedLongType:
221            return jsNumber(*(unsigned long*)buffer);
222        case ObjcLongLongType:
223            return jsNumber(*(long long*)buffer);
224        case ObjcUnsignedLongLongType:
225            return jsNumber(*(unsigned long long*)buffer);
226        case ObjcFloatType:
227            return jsNumber(*(float*)buffer);
228        case ObjcDoubleType:
229            return jsNumber(*(double*)buffer);
230        default:
231            // Should never get here. Argument types are filtered.
232            fprintf(stderr, "%s: invalid type (%d)\n", __PRETTY_FUNCTION__, (int)type);
233            ASSERT_NOT_REACHED();
234    }
235
236    return jsUndefined();
237}
238
239ObjcValueType objcValueTypeForType(const char *type)
240{
241    int typeLength = strlen(type);
242    ObjcValueType objcValueType = ObjcInvalidType;
243
244    for (int i = 0; i < typeLength; ++i) {
245        char typeChar = type[i];
246        switch (typeChar) {
247            case _C_CONST:
248            case _C_BYCOPY:
249            case _C_BYREF:
250            case _C_ONEWAY:
251            case _C_GCINVISIBLE:
252                // skip these type modifiers
253                break;
254            case _C_ID:
255                objcValueType = ObjcObjectType;
256                break;
257            case _C_CHR:
258                objcValueType = ObjcCharType;
259                break;
260            case _C_UCHR:
261                objcValueType = ObjcUnsignedCharType;
262                break;
263            case _C_SHT:
264                objcValueType = ObjcShortType;
265                break;
266            case _C_USHT:
267                objcValueType = ObjcUnsignedShortType;
268                break;
269            case _C_BOOL:
270                objcValueType = ObjcBoolType;
271                break;
272            case _C_INT:
273                objcValueType = ObjcIntType;
274                break;
275            case _C_UINT:
276                objcValueType = ObjcUnsignedIntType;
277                break;
278            case _C_LNG:
279                objcValueType = ObjcLongType;
280                break;
281            case _C_ULNG:
282                objcValueType = ObjcUnsignedLongType;
283                break;
284            case _C_LNG_LNG:
285                objcValueType = ObjcLongLongType;
286                break;
287            case _C_ULNG_LNG:
288                objcValueType = ObjcUnsignedLongLongType;
289                break;
290            case _C_FLT:
291                objcValueType = ObjcFloatType;
292                break;
293            case _C_DBL:
294                objcValueType = ObjcDoubleType;
295                break;
296            case _C_VOID:
297                objcValueType = ObjcVoidType;
298                break;
299            default:
300                // Unhandled type. We don't handle C structs, unions, etc.
301                // FIXME: throw an exception?
302                WTFLogAlways("Unhandled ObjC type specifier: \"%c\" used in ObjC bridge.", typeChar);
303                RELEASE_ASSERT_NOT_REACHED();
304        }
305
306        if (objcValueType != ObjcInvalidType)
307            break;
308    }
309
310    return objcValueType;
311}
312
313JSObject *throwError(ExecState *exec, NSString *message)
314{
315    ASSERT(message);
316    JSObject *error = exec->vm().throwException(exec, JSC::createError(exec, String(message)));
317    return error;
318}
319
320}
321}
322