1/*
2 * Copyright (C) 2006-2019 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 JSGlobalObject* globalObject = toJS(ctx);
65 JSLockHolder locker(globalObject);
66
67 JSValue jsValue = toJS(globalObject, 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 JSGlobalObject* globalObject = toJS(ctx);
92 JSLockHolder locker(globalObject);
93
94 return toJS(globalObject, value).isUndefined();
95}
96
97bool JSValueIsNull(JSContextRef ctx, JSValueRef value)
98{
99 if (!ctx) {
100 ASSERT_NOT_REACHED();
101 return false;
102 }
103 JSGlobalObject* globalObject = toJS(ctx);
104 JSLockHolder locker(globalObject);
105
106 return toJS(globalObject, value).isNull();
107}
108
109bool JSValueIsBoolean(JSContextRef ctx, JSValueRef value)
110{
111 if (!ctx) {
112 ASSERT_NOT_REACHED();
113 return false;
114 }
115 JSGlobalObject* globalObject = toJS(ctx);
116 JSLockHolder locker(globalObject);
117
118 return toJS(globalObject, value).isBoolean();
119}
120
121bool JSValueIsNumber(JSContextRef ctx, JSValueRef value)
122{
123 if (!ctx) {
124 ASSERT_NOT_REACHED();
125 return false;
126 }
127 JSGlobalObject* globalObject = toJS(ctx);
128 JSLockHolder locker(globalObject);
129
130 return toJS(globalObject, value).isNumber();
131}
132
133bool JSValueIsString(JSContextRef ctx, JSValueRef value)
134{
135 if (!ctx) {
136 ASSERT_NOT_REACHED();
137 return false;
138 }
139 JSGlobalObject* globalObject = toJS(ctx);
140 JSLockHolder locker(globalObject);
141
142 return toJS(globalObject, value).isString();
143}
144
145bool JSValueIsObject(JSContextRef ctx, JSValueRef value)
146{
147 if (!ctx) {
148 ASSERT_NOT_REACHED();
149 return false;
150 }
151 JSGlobalObject* globalObject = toJS(ctx);
152 JSLockHolder locker(globalObject);
153
154 return toJS(globalObject, value).isObject();
155}
156
157bool JSValueIsSymbol(JSContextRef ctx, JSValueRef value)
158{
159 if (!ctx) {
160 ASSERT_NOT_REACHED();
161 return false;
162 }
163 JSGlobalObject* globalObject = toJS(ctx);
164 JSLockHolder locker(globalObject);
165
166 return toJS(globalObject, value).isSymbol();
167}
168
169bool JSValueIsArray(JSContextRef ctx, JSValueRef value)
170{
171 if (!ctx) {
172 ASSERT_NOT_REACHED();
173 return false;
174 }
175 JSGlobalObject* globalObject = toJS(ctx);
176 VM& vm = globalObject->vm();
177 JSLockHolder locker(globalObject);
178
179 return toJS(globalObject, 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 JSGlobalObject* globalObject = toJS(ctx);
189 VM& vm = globalObject->vm();
190 JSLockHolder locker(globalObject);
191
192 return toJS(globalObject, 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 JSGlobalObject* globalObject = toJS(ctx);
202 VM& vm = globalObject->vm();
203 JSLockHolder locker(globalObject);
204
205 JSValue jsValue = toJS(globalObject, 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 JSGlobalObject* globalObject = toJS(ctx);
230 VM& vm = globalObject->vm();
231 JSLockHolder locker(vm);
232 auto scope = DECLARE_CATCH_SCOPE(vm);
233
234 JSValue jsA = toJS(globalObject, a);
235 JSValue jsB = toJS(globalObject, b);
236
237 bool result = JSValue::equal(globalObject, jsA, jsB); // false if an exception is thrown
238 handleExceptionIfNeeded(scope, ctx, 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 JSGlobalObject* globalObject = toJS(ctx);
250 JSLockHolder locker(globalObject);
251
252 JSValue jsA = toJS(globalObject, a);
253 JSValue jsB = toJS(globalObject, b);
254
255 return JSValue::strictEqual(globalObject, 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 JSGlobalObject* globalObject = toJS(ctx);
265 VM& vm = globalObject->vm();
266 JSLockHolder locker(vm);
267 auto scope = DECLARE_CATCH_SCOPE(vm);
268
269 JSValue jsValue = toJS(globalObject, value);
270
271 JSObject* jsConstructor = toJS(constructor);
272 if (!jsConstructor->structure(vm)->typeInfo().implementsHasInstance())
273 return false;
274 bool result = jsConstructor->hasInstance(globalObject, jsValue); // false if an exception is thrown
275 handleExceptionIfNeeded(scope, ctx, exception);
276 return result;
277}
278
279JSValueRef JSValueMakeUndefined(JSContextRef ctx)
280{
281 if (!ctx) {
282 ASSERT_NOT_REACHED();
283 return 0;
284 }
285 JSGlobalObject* globalObject = toJS(ctx);
286 JSLockHolder locker(globalObject);
287
288 return toRef(globalObject, jsUndefined());
289}
290
291JSValueRef JSValueMakeNull(JSContextRef ctx)
292{
293 if (!ctx) {
294 ASSERT_NOT_REACHED();
295 return 0;
296 }
297 JSGlobalObject* globalObject = toJS(ctx);
298 JSLockHolder locker(globalObject);
299
300 return toRef(globalObject, jsNull());
301}
302
303JSValueRef JSValueMakeBoolean(JSContextRef ctx, bool value)
304{
305 if (!ctx) {
306 ASSERT_NOT_REACHED();
307 return 0;
308 }
309 JSGlobalObject* globalObject = toJS(ctx);
310 JSLockHolder locker(globalObject);
311
312 return toRef(globalObject, jsBoolean(value));
313}
314
315JSValueRef JSValueMakeNumber(JSContextRef ctx, double value)
316{
317 if (!ctx) {
318 ASSERT_NOT_REACHED();
319 return 0;
320 }
321 JSGlobalObject* globalObject = toJS(ctx);
322 JSLockHolder locker(globalObject);
323
324 return toRef(globalObject, jsNumber(purifyNaN(value)));
325}
326
327JSValueRef JSValueMakeSymbol(JSContextRef ctx, JSStringRef description)
328{
329 if (!ctx) {
330 ASSERT_NOT_REACHED();
331 return nullptr;
332 }
333 JSGlobalObject* globalObject = toJS(ctx);
334 VM& vm = globalObject->vm();
335 JSLockHolder locker(globalObject);
336
337 if (!description)
338 return toRef(globalObject, Symbol::create(vm));
339 return toRef(globalObject, 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 JSGlobalObject* globalObject = toJS(ctx);
349 VM& vm = globalObject->vm();
350 JSLockHolder locker(vm);
351
352 return toRef(globalObject, jsString(vm, string ? string->string() : String()));
353}
354
355JSValueRef JSValueMakeFromJSONString(JSContextRef ctx, JSStringRef string)
356{
357 if (!ctx) {
358 ASSERT_NOT_REACHED();
359 return 0;
360 }
361 JSGlobalObject* globalObject = toJS(ctx);
362 JSLockHolder locker(globalObject);
363 String str = string->string();
364 unsigned length = str.length();
365 if (!length || str.is8Bit()) {
366 LiteralParser<LChar> parser(globalObject, str.characters8(), length, StrictJSON);
367 return toRef(globalObject, parser.tryLiteralParse());
368 }
369 LiteralParser<UChar> parser(globalObject, str.characters16(), length, StrictJSON);
370 return toRef(globalObject, parser.tryLiteralParse());
371}
372
373JSStringRef JSValueCreateJSONString(JSContextRef ctx, JSValueRef apiValue, unsigned indent, JSValueRef* exception)
374{
375 if (!ctx) {
376 ASSERT_NOT_REACHED();
377 return 0;
378 }
379 JSGlobalObject* globalObject = toJS(ctx);
380 VM& vm = globalObject->vm();
381 JSLockHolder locker(vm);
382 auto scope = DECLARE_CATCH_SCOPE(vm);
383
384 JSValue value = toJS(globalObject, apiValue);
385 String result = JSONStringify(globalObject, value, indent);
386 if (exception)
387 *exception = 0;
388 if (handleExceptionIfNeeded(scope, ctx, exception) == ExceptionStatus::DidThrow)
389 return 0;
390 return OpaqueJSString::tryCreate(result).leakRef();
391}
392
393bool JSValueToBoolean(JSContextRef ctx, JSValueRef value)
394{
395 if (!ctx) {
396 ASSERT_NOT_REACHED();
397 return false;
398 }
399 JSGlobalObject* globalObject = toJS(ctx);
400 JSLockHolder locker(globalObject);
401
402 JSValue jsValue = toJS(globalObject, value);
403 return jsValue.toBoolean(globalObject);
404}
405
406double JSValueToNumber(JSContextRef ctx, JSValueRef value, JSValueRef* exception)
407{
408 if (!ctx) {
409 ASSERT_NOT_REACHED();
410 return PNaN;
411 }
412 JSGlobalObject* globalObject = toJS(ctx);
413 VM& vm = globalObject->vm();
414 JSLockHolder locker(vm);
415 auto scope = DECLARE_CATCH_SCOPE(vm);
416
417 JSValue jsValue = toJS(globalObject, value);
418
419 double number = jsValue.toNumber(globalObject);
420 if (handleExceptionIfNeeded(scope, ctx, exception) == ExceptionStatus::DidThrow)
421 number = PNaN;
422 return number;
423}
424
425JSStringRef JSValueToStringCopy(JSContextRef ctx, JSValueRef value, JSValueRef* exception)
426{
427 if (!ctx) {
428 ASSERT_NOT_REACHED();
429 return 0;
430 }
431 JSGlobalObject* globalObject = toJS(ctx);
432 VM& vm = globalObject->vm();
433 JSLockHolder locker(vm);
434 auto scope = DECLARE_CATCH_SCOPE(vm);
435
436 JSValue jsValue = toJS(globalObject, value);
437
438 auto stringRef(OpaqueJSString::tryCreate(jsValue.toWTFString(globalObject)));
439 if (handleExceptionIfNeeded(scope, ctx, exception) == ExceptionStatus::DidThrow)
440 stringRef = nullptr;
441 return stringRef.leakRef();
442}
443
444JSObjectRef JSValueToObject(JSContextRef ctx, JSValueRef value, JSValueRef* exception)
445{
446 if (!ctx) {
447 ASSERT_NOT_REACHED();
448 return 0;
449 }
450 JSGlobalObject* globalObject = toJS(ctx);
451 VM& vm = globalObject->vm();
452 JSLockHolder locker(vm);
453 auto scope = DECLARE_CATCH_SCOPE(vm);
454
455 JSValue jsValue = toJS(globalObject, value);
456
457 JSObjectRef objectRef = toRef(jsValue.toObject(globalObject));
458 if (handleExceptionIfNeeded(scope, ctx, exception) == ExceptionStatus::DidThrow)
459 objectRef = 0;
460 return objectRef;
461}
462
463void JSValueProtect(JSContextRef ctx, JSValueRef value)
464{
465 if (!ctx) {
466 ASSERT_NOT_REACHED();
467 return;
468 }
469 JSGlobalObject* globalObject = toJS(ctx);
470 JSLockHolder locker(globalObject);
471
472 JSValue jsValue = toJSForGC(globalObject, value);
473 gcProtect(jsValue);
474}
475
476void JSValueUnprotect(JSContextRef ctx, JSValueRef value)
477{
478 JSGlobalObject* globalObject = toJS(ctx);
479 JSLockHolder locker(globalObject);
480
481 JSValue jsValue = toJSForGC(globalObject, value);
482 gcUnprotect(jsValue);
483}
484