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