1/*
2 * Copyright (C) 2006, 2007 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 "JSValueRef.h"
28
29#include "APICast.h"
30#include "JSAPIWrapperObject.h"
31#include "JSCJSValue.h"
32#include "JSCallbackObject.h"
33#include "JSGlobalObject.h"
34#include "JSONObject.h"
35#include "JSString.h"
36#include "LiteralParser.h"
37#include "JSCInlines.h"
38#include "Protect.h"
39
40#include <wtf/Assertions.h>
41#include <wtf/text/StringHash.h>
42#include <wtf/text/WTFString.h>
43
44#include <algorithm> // for std::min
45
46#if PLATFORM(MAC)
47#include <mach-o/dyld.h>
48#endif
49
50#if ENABLE(REMOTE_INSPECTOR)
51#include "JSGlobalObjectInspectorController.h"
52#endif
53
54using namespace JSC;
55
56#if PLATFORM(MAC)
57static bool evernoteHackNeeded()
58{
59    static const int32_t webkitLastVersionWithEvernoteHack = 35133959;
60    static bool hackNeeded = CFEqual(CFBundleGetIdentifier(CFBundleGetMainBundle()), CFSTR("com.evernote.Evernote"))
61        && NSVersionOfLinkTimeLibrary("JavaScriptCore") <= webkitLastVersionWithEvernoteHack;
62
63    return hackNeeded;
64}
65#endif
66
67::JSType JSValueGetType(JSContextRef ctx, JSValueRef value)
68{
69    if (!ctx) {
70        ASSERT_NOT_REACHED();
71        return kJSTypeUndefined;
72    }
73    ExecState* exec = toJS(ctx);
74    JSLockHolder locker(exec);
75
76    JSValue jsValue = toJS(exec, value);
77
78    if (jsValue.isUndefined())
79        return kJSTypeUndefined;
80    if (jsValue.isNull())
81        return kJSTypeNull;
82    if (jsValue.isBoolean())
83        return kJSTypeBoolean;
84    if (jsValue.isNumber())
85        return kJSTypeNumber;
86    if (jsValue.isString())
87        return kJSTypeString;
88    ASSERT(jsValue.isObject());
89    return kJSTypeObject;
90}
91
92bool JSValueIsUndefined(JSContextRef ctx, JSValueRef value)
93{
94    if (!ctx) {
95        ASSERT_NOT_REACHED();
96        return false;
97    }
98    ExecState* exec = toJS(ctx);
99    JSLockHolder locker(exec);
100
101    JSValue jsValue = toJS(exec, value);
102    return jsValue.isUndefined();
103}
104
105bool JSValueIsNull(JSContextRef ctx, JSValueRef value)
106{
107    if (!ctx) {
108        ASSERT_NOT_REACHED();
109        return false;
110    }
111    ExecState* exec = toJS(ctx);
112    JSLockHolder locker(exec);
113
114    JSValue jsValue = toJS(exec, value);
115    return jsValue.isNull();
116}
117
118bool JSValueIsBoolean(JSContextRef ctx, JSValueRef value)
119{
120    if (!ctx) {
121        ASSERT_NOT_REACHED();
122        return false;
123    }
124    ExecState* exec = toJS(ctx);
125    JSLockHolder locker(exec);
126
127    JSValue jsValue = toJS(exec, value);
128    return jsValue.isBoolean();
129}
130
131bool JSValueIsNumber(JSContextRef ctx, JSValueRef value)
132{
133    if (!ctx) {
134        ASSERT_NOT_REACHED();
135        return false;
136    }
137    ExecState* exec = toJS(ctx);
138    JSLockHolder locker(exec);
139
140    JSValue jsValue = toJS(exec, value);
141    return jsValue.isNumber();
142}
143
144bool JSValueIsString(JSContextRef ctx, JSValueRef value)
145{
146    if (!ctx) {
147        ASSERT_NOT_REACHED();
148        return false;
149    }
150    ExecState* exec = toJS(ctx);
151    JSLockHolder locker(exec);
152
153    JSValue jsValue = toJS(exec, value);
154    return jsValue.isString();
155}
156
157bool JSValueIsObject(JSContextRef ctx, JSValueRef value)
158{
159    if (!ctx) {
160        ASSERT_NOT_REACHED();
161        return false;
162    }
163    ExecState* exec = toJS(ctx);
164    JSLockHolder locker(exec);
165
166    JSValue jsValue = toJS(exec, value);
167    return jsValue.isObject();
168}
169
170bool JSValueIsObjectOfClass(JSContextRef ctx, JSValueRef value, JSClassRef jsClass)
171{
172    if (!ctx || !jsClass) {
173        ASSERT_NOT_REACHED();
174        return false;
175    }
176    ExecState* exec = toJS(ctx);
177    JSLockHolder locker(exec);
178
179    JSValue jsValue = toJS(exec, value);
180
181    if (JSObject* o = jsValue.getObject()) {
182        if (o->inherits(JSProxy::info()))
183            o = jsCast<JSProxy*>(o)->target();
184
185        if (o->inherits(JSCallbackObject<JSGlobalObject>::info()))
186            return jsCast<JSCallbackObject<JSGlobalObject>*>(o)->inherits(jsClass);
187        if (o->inherits(JSCallbackObject<JSDestructibleObject>::info()))
188            return jsCast<JSCallbackObject<JSDestructibleObject>*>(o)->inherits(jsClass);
189#if JSC_OBJC_API_ENABLED
190        if (o->inherits(JSCallbackObject<JSAPIWrapperObject>::info()))
191            return jsCast<JSCallbackObject<JSAPIWrapperObject>*>(o)->inherits(jsClass);
192#endif
193    }
194    return false;
195}
196
197bool JSValueIsEqual(JSContextRef ctx, JSValueRef a, JSValueRef b, JSValueRef* exception)
198{
199    if (!ctx) {
200        ASSERT_NOT_REACHED();
201        return false;
202    }
203    ExecState* exec = toJS(ctx);
204    JSLockHolder locker(exec);
205
206    JSValue jsA = toJS(exec, a);
207    JSValue jsB = toJS(exec, b);
208
209    bool result = JSValue::equal(exec, jsA, jsB); // false if an exception is thrown
210    if (exec->hadException()) {
211        JSValue exceptionValue = exec->exception();
212        if (exception)
213            *exception = toRef(exec, exceptionValue);
214        exec->clearException();
215#if ENABLE(REMOTE_INSPECTOR)
216        exec->vmEntryGlobalObject()->inspectorController().reportAPIException(exec, exceptionValue);
217#endif
218    }
219    return result;
220}
221
222bool JSValueIsStrictEqual(JSContextRef ctx, JSValueRef a, JSValueRef b)
223{
224    if (!ctx) {
225        ASSERT_NOT_REACHED();
226        return false;
227    }
228    ExecState* exec = toJS(ctx);
229    JSLockHolder locker(exec);
230
231    JSValue jsA = toJS(exec, a);
232    JSValue jsB = toJS(exec, b);
233
234    return JSValue::strictEqual(exec, jsA, jsB);
235}
236
237bool JSValueIsInstanceOfConstructor(JSContextRef ctx, JSValueRef value, JSObjectRef constructor, JSValueRef* exception)
238{
239    if (!ctx) {
240        ASSERT_NOT_REACHED();
241        return false;
242    }
243    ExecState* exec = toJS(ctx);
244    JSLockHolder locker(exec);
245
246    JSValue jsValue = toJS(exec, value);
247
248    JSObject* jsConstructor = toJS(constructor);
249    if (!jsConstructor->structure()->typeInfo().implementsHasInstance())
250        return false;
251    bool result = jsConstructor->hasInstance(exec, jsValue); // false if an exception is thrown
252    if (exec->hadException()) {
253        JSValue exceptionValue = exec->exception();
254        if (exception)
255            *exception = toRef(exec, exceptionValue);
256        exec->clearException();
257#if ENABLE(REMOTE_INSPECTOR)
258        exec->vmEntryGlobalObject()->inspectorController().reportAPIException(exec, exceptionValue);
259#endif
260    }
261    return result;
262}
263
264JSValueRef JSValueMakeUndefined(JSContextRef ctx)
265{
266    if (!ctx) {
267        ASSERT_NOT_REACHED();
268        return 0;
269    }
270    ExecState* exec = toJS(ctx);
271    JSLockHolder locker(exec);
272
273    return toRef(exec, jsUndefined());
274}
275
276JSValueRef JSValueMakeNull(JSContextRef ctx)
277{
278    if (!ctx) {
279        ASSERT_NOT_REACHED();
280        return 0;
281    }
282    ExecState* exec = toJS(ctx);
283    JSLockHolder locker(exec);
284
285    return toRef(exec, jsNull());
286}
287
288JSValueRef JSValueMakeBoolean(JSContextRef ctx, bool value)
289{
290    if (!ctx) {
291        ASSERT_NOT_REACHED();
292        return 0;
293    }
294    ExecState* exec = toJS(ctx);
295    JSLockHolder locker(exec);
296
297    return toRef(exec, jsBoolean(value));
298}
299
300JSValueRef JSValueMakeNumber(JSContextRef ctx, double value)
301{
302    if (!ctx) {
303        ASSERT_NOT_REACHED();
304        return 0;
305    }
306    ExecState* exec = toJS(ctx);
307    JSLockHolder locker(exec);
308
309    return toRef(exec, jsNumber(purifyNaN(value)));
310}
311
312JSValueRef JSValueMakeString(JSContextRef ctx, JSStringRef string)
313{
314    if (!ctx) {
315        ASSERT_NOT_REACHED();
316        return 0;
317    }
318    ExecState* exec = toJS(ctx);
319    JSLockHolder locker(exec);
320
321    return toRef(exec, jsString(exec, string->string()));
322}
323
324JSValueRef JSValueMakeFromJSONString(JSContextRef ctx, JSStringRef string)
325{
326    if (!ctx) {
327        ASSERT_NOT_REACHED();
328        return 0;
329    }
330    ExecState* exec = toJS(ctx);
331    JSLockHolder locker(exec);
332    String str = string->string();
333    unsigned length = str.length();
334    if (!length || str.is8Bit()) {
335        LiteralParser<LChar> parser(exec, str.characters8(), length, StrictJSON);
336        return toRef(exec, parser.tryLiteralParse());
337    }
338    LiteralParser<UChar> parser(exec, str.characters16(), length, StrictJSON);
339    return toRef(exec, parser.tryLiteralParse());
340}
341
342JSStringRef JSValueCreateJSONString(JSContextRef ctx, JSValueRef apiValue, unsigned indent, JSValueRef* exception)
343{
344    if (!ctx) {
345        ASSERT_NOT_REACHED();
346        return 0;
347    }
348    ExecState* exec = toJS(ctx);
349    JSLockHolder locker(exec);
350    JSValue value = toJS(exec, apiValue);
351    String result = JSONStringify(exec, value, indent);
352    if (exception)
353        *exception = 0;
354    if (exec->hadException()) {
355        JSValue exceptionValue = exec->exception();
356        if (exception)
357            *exception = toRef(exec, exceptionValue);
358        exec->clearException();
359#if ENABLE(REMOTE_INSPECTOR)
360        exec->vmEntryGlobalObject()->inspectorController().reportAPIException(exec, exceptionValue);
361#endif
362        return 0;
363    }
364    return OpaqueJSString::create(result).leakRef();
365}
366
367bool JSValueToBoolean(JSContextRef ctx, JSValueRef value)
368{
369    if (!ctx) {
370        ASSERT_NOT_REACHED();
371        return false;
372    }
373    ExecState* exec = toJS(ctx);
374    JSLockHolder locker(exec);
375
376    JSValue jsValue = toJS(exec, value);
377    return jsValue.toBoolean(exec);
378}
379
380double JSValueToNumber(JSContextRef ctx, JSValueRef value, JSValueRef* exception)
381{
382    if (!ctx) {
383        ASSERT_NOT_REACHED();
384        return PNaN;
385    }
386    ExecState* exec = toJS(ctx);
387    JSLockHolder locker(exec);
388
389    JSValue jsValue = toJS(exec, value);
390
391    double number = jsValue.toNumber(exec);
392    if (exec->hadException()) {
393        JSValue exceptionValue = exec->exception();
394        if (exception)
395            *exception = toRef(exec, exceptionValue);
396        exec->clearException();
397#if ENABLE(REMOTE_INSPECTOR)
398        exec->vmEntryGlobalObject()->inspectorController().reportAPIException(exec, exceptionValue);
399#endif
400        number = PNaN;
401    }
402    return number;
403}
404
405JSStringRef JSValueToStringCopy(JSContextRef ctx, JSValueRef value, JSValueRef* exception)
406{
407    if (!ctx) {
408        ASSERT_NOT_REACHED();
409        return 0;
410    }
411    ExecState* exec = toJS(ctx);
412    JSLockHolder locker(exec);
413
414    JSValue jsValue = toJS(exec, value);
415
416    RefPtr<OpaqueJSString> stringRef(OpaqueJSString::create(jsValue.toString(exec)->value(exec)));
417    if (exec->hadException()) {
418        JSValue exceptionValue = exec->exception();
419        if (exception)
420            *exception = toRef(exec, exceptionValue);
421        exec->clearException();
422#if ENABLE(REMOTE_INSPECTOR)
423        exec->vmEntryGlobalObject()->inspectorController().reportAPIException(exec, exceptionValue);
424#endif
425        stringRef.clear();
426    }
427    return stringRef.release().leakRef();
428}
429
430JSObjectRef JSValueToObject(JSContextRef ctx, JSValueRef value, JSValueRef* exception)
431{
432    if (!ctx) {
433        ASSERT_NOT_REACHED();
434        return 0;
435    }
436    ExecState* exec = toJS(ctx);
437    JSLockHolder locker(exec);
438
439    JSValue jsValue = toJS(exec, value);
440
441    JSObjectRef objectRef = toRef(jsValue.toObject(exec));
442    if (exec->hadException()) {
443        JSValue exceptionValue = exec->exception();
444        if (exception)
445            *exception = toRef(exec, exceptionValue);
446        exec->clearException();
447#if ENABLE(REMOTE_INSPECTOR)
448        exec->vmEntryGlobalObject()->inspectorController().reportAPIException(exec, exceptionValue);
449#endif
450        objectRef = 0;
451    }
452    return objectRef;
453}
454
455void JSValueProtect(JSContextRef ctx, JSValueRef value)
456{
457    if (!ctx) {
458        ASSERT_NOT_REACHED();
459        return;
460    }
461    ExecState* exec = toJS(ctx);
462    JSLockHolder locker(exec);
463
464    JSValue jsValue = toJSForGC(exec, value);
465    gcProtect(jsValue);
466}
467
468void JSValueUnprotect(JSContextRef ctx, JSValueRef value)
469{
470#if PLATFORM(MAC)
471    if ((!value || !ctx) && evernoteHackNeeded())
472        return;
473#endif
474
475    ExecState* exec = toJS(ctx);
476    JSLockHolder locker(exec);
477
478    JSValue jsValue = toJSForGC(exec, value);
479    gcUnprotect(jsValue);
480}
481