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