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