JSCJSValue.cpp
1 /* 2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) 3 * Copyright (C) 2001 Peter Kelly (pmk@post.com) 4 * Copyright (C) 2003-2020 Apple Inc. All rights reserved. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Library General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public License 17 * along with this library; see the file COPYING.LIB. If not, write to 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 * 21 */ 22 23 #include "config.h" 24 #include "JSCJSValue.h" 25 26 #include "BigIntObject.h" 27 #include "BooleanConstructor.h" 28 #include "CustomGetterSetter.h" 29 #include "GetterSetter.h" 30 #include "JSBigInt.h" 31 #include "JSCInlines.h" 32 #include "NumberObject.h" 33 #include "TypeError.h" 34 35 #ifdef DARLING 36 #include "ParseInt.h" 37 #endif 38 39 namespace JSC { 40 41 // ECMA 9.4 42 double JSValue::toInteger(JSGlobalObject* globalObject) const 43 { 44 if (isInt32()) 45 return asInt32(); 46 double d = toNumber(globalObject); 47 return std::isnan(d) ? 0.0 : trunc(d); 48 } 49 50 double JSValue::toIntegerPreserveNaN(JSGlobalObject* globalObject) const 51 { 52 if (isInt32()) 53 return asInt32(); 54 return trunc(toNumber(globalObject)); 55 } 56 57 double JSValue::toLength(JSGlobalObject* globalObject) const 58 { 59 // ECMA 7.1.15 60 // http://www.ecma-international.org/ecma-262/6.0/#sec-tolength 61 double d = toInteger(globalObject); 62 if (d <= 0) 63 return 0.0; 64 if (std::isinf(d)) 65 return maxSafeInteger(); 66 return std::min(d, maxSafeInteger()); 67 } 68 69 double JSValue::toNumberSlowCase(JSGlobalObject* globalObject) const 70 { 71 VM& vm = globalObject->vm(); 72 auto scope = DECLARE_THROW_SCOPE(vm); 73 74 ASSERT(!isInt32() && !isDouble()); 75 if (isCell()) 76 RELEASE_AND_RETURN(scope, asCell()->toNumber(globalObject)); 77 #if USE(BIGINT32) 78 if (isBigInt32()) { 79 throwTypeError(globalObject, scope, "Conversion from 'BigInt' to 'number' is not allowed."_s); 80 return 0.0; 81 } 82 #endif 83 if (isTrue()) 84 return 1.0; 85 return isUndefined() ? PNaN : 0; // null and false both convert to 0. 86 } 87 88 Optional<double> JSValue::toNumberFromPrimitive() const 89 { 90 if (isEmpty()) 91 return WTF::nullopt; 92 if (isNumber()) 93 return asNumber(); 94 if (isBoolean()) 95 return asBoolean(); 96 if (isUndefined()) 97 return PNaN; 98 if (isNull()) 99 return 0; 100 return WTF::nullopt; 101 } 102 103 // https://tc39.es/ecma262/#sec-tobigint 104 JSValue JSValue::toBigInt(JSGlobalObject* globalObject) const 105 { 106 VM& vm = globalObject->vm(); 107 auto scope = DECLARE_THROW_SCOPE(vm); 108 109 JSValue primitive = toPrimitive(globalObject); 110 RETURN_IF_EXCEPTION(scope, { }); 111 112 if (primitive.isBigInt()) 113 return primitive; 114 115 if (primitive.isBoolean()) { 116 #if USE(BIGINT32) 117 return jsBigInt32(primitive.asBoolean()); 118 #else 119 RELEASE_AND_RETURN(scope, JSBigInt::createFrom(globalObject, primitive.asBoolean())); 120 #endif 121 } 122 123 if (primitive.isString()) { 124 scope.release(); 125 return toStringView(globalObject, primitive, [&] (StringView view) { 126 return JSBigInt::parseInt(globalObject, view); 127 }); 128 } 129 130 ASSERT(primitive.isUndefinedOrNull() || primitive.isNumber() || primitive.isSymbol()); 131 throwTypeError(globalObject, scope, "Invalid argument type in ToBigInt operation"_s); 132 return jsUndefined(); 133 } 134 135 // https://tc39.es/ecma262/#sec-tobigint64 136 int64_t JSValue::toBigInt64(JSGlobalObject* globalObject) const 137 { 138 VM& vm = globalObject->vm(); 139 auto scope = DECLARE_THROW_SCOPE(vm); 140 141 JSValue value = toBigInt(globalObject); 142 RETURN_IF_EXCEPTION(scope, { }); 143 return JSBigInt::toBigInt64(value); 144 } 145 146 // https://tc39.es/ecma262/#sec-tobiguint64 147 uint64_t JSValue::toBigUInt64(JSGlobalObject* globalObject) const 148 { 149 VM& vm = globalObject->vm(); 150 auto scope = DECLARE_THROW_SCOPE(vm); 151 152 JSValue value = toBigInt(globalObject); 153 RETURN_IF_EXCEPTION(scope, { }); 154 return JSBigInt::toBigUInt64(value); 155 } 156 157 JSObject* JSValue::toObjectSlowCase(JSGlobalObject* globalObject) const 158 { 159 VM& vm = globalObject->vm(); 160 auto scope = DECLARE_THROW_SCOPE(vm); 161 ASSERT(!isCell()); 162 163 if (isInt32() || isDouble()) 164 return constructNumber(globalObject, asValue()); 165 if (isTrue() || isFalse()) 166 return constructBooleanFromImmediateBoolean(globalObject, asValue()); 167 #if USE(BIGINT32) 168 if (isBigInt32()) 169 return BigIntObject::create(vm, globalObject, *this); 170 #endif 171 172 ASSERT(isUndefinedOrNull()); 173 throwException(globalObject, scope, createNotAnObjectError(globalObject, *this)); 174 return nullptr; 175 } 176 177 JSValue JSValue::toThisSlowCase(JSGlobalObject* globalObject, ECMAMode ecmaMode) const 178 { 179 ASSERT(!isCell()); 180 181 if (ecmaMode.isStrict()) 182 return *this; 183 184 if (isInt32() || isDouble()) 185 return constructNumber(globalObject, asValue()); 186 if (isTrue() || isFalse()) 187 return constructBooleanFromImmediateBoolean(globalObject, asValue()); 188 #if USE(BIGINT32) 189 if (isBigInt32()) 190 return BigIntObject::create(globalObject->vm(), globalObject, *this); 191 #endif 192 193 ASSERT(isUndefinedOrNull()); 194 return globalObject->globalThis(); 195 } 196 197 JSObject* JSValue::synthesizePrototype(JSGlobalObject* globalObject) const 198 { 199 VM& vm = globalObject->vm(); 200 auto scope = DECLARE_THROW_SCOPE(vm); 201 202 if (isCell()) { 203 if (isString()) 204 return globalObject->stringPrototype(); 205 if (isHeapBigInt()) 206 return globalObject->bigIntPrototype(); 207 ASSERT(isSymbol()); 208 return globalObject->symbolPrototype(); 209 } 210 211 if (isNumber()) 212 return globalObject->numberPrototype(); 213 if (isBoolean()) 214 return globalObject->booleanPrototype(); 215 #if USE(BIGINT32) 216 if (isBigInt32()) 217 return globalObject->bigIntPrototype(); 218 #endif 219 220 ASSERT(isUndefinedOrNull()); 221 throwException(globalObject, scope, createNotAnObjectError(globalObject, *this)); 222 return nullptr; 223 } 224 225 // ECMA 8.7.2 226 bool JSValue::putToPrimitive(JSGlobalObject* globalObject, PropertyName propertyName, JSValue value, PutPropertySlot& slot) 227 { 228 VM& vm = globalObject->vm(); 229 auto scope = DECLARE_THROW_SCOPE(vm); 230 231 if (Optional<uint32_t> index = parseIndex(propertyName)) 232 RELEASE_AND_RETURN(scope, putToPrimitiveByIndex(globalObject, index.value(), value, slot.isStrictMode())); 233 234 // Check if there are any setters or getters in the prototype chain 235 JSObject* obj = synthesizePrototype(globalObject); 236 EXCEPTION_ASSERT(!!scope.exception() == !obj); 237 if (UNLIKELY(!obj)) 238 return false; 239 JSValue prototype; 240 if (propertyName != vm.propertyNames->underscoreProto) { 241 while (true) { 242 Structure* structure = obj->structure(vm); 243 if (structure->hasReadOnlyOrGetterSetterPropertiesExcludingProto() || structure->typeInfo().hasPutPropertySecurityCheck()) 244 break; 245 if (obj->type() == ProxyObjectType) { 246 auto* proxy = jsCast<ProxyObject*>(obj); 247 RELEASE_AND_RETURN(scope, proxy->ProxyObject::put(proxy, globalObject, propertyName, value, slot)); 248 } 249 prototype = obj->getPrototype(vm, globalObject); 250 RETURN_IF_EXCEPTION(scope, false); 251 252 if (prototype.isNull()) 253 return typeError(globalObject, scope, slot.isStrictMode(), ReadonlyPropertyWriteError); 254 obj = asObject(prototype); 255 } 256 } 257 258 for (; ; obj = asObject(prototype)) { 259 Structure* structure = obj->structure(vm); 260 if (UNLIKELY(structure->typeInfo().hasPutPropertySecurityCheck())) { 261 obj->methodTable(vm)->doPutPropertySecurityCheck(obj, globalObject, propertyName, slot); 262 RETURN_IF_EXCEPTION(scope, false); 263 } 264 unsigned attributes; 265 PropertyOffset offset = structure->get(vm, propertyName, attributes); 266 if (offset != invalidOffset) { 267 if (attributes & PropertyAttribute::ReadOnly) 268 return typeError(globalObject, scope, slot.isStrictMode(), ReadonlyPropertyWriteError); 269 270 JSValue gs = obj->getDirect(offset); 271 if (gs.isGetterSetter()) 272 RELEASE_AND_RETURN(scope, callSetter(globalObject, *this, gs, value, slot.isStrictMode() ? ECMAMode::strict() : ECMAMode::sloppy())); 273 274 if (gs.isCustomGetterSetter()) { 275 auto setter = jsCast<CustomGetterSetter*>(gs.asCell())->setter(); 276 bool isAccessor = attributes & PropertyAttribute::CustomAccessor; 277 auto result = callCustomSetter(globalObject, setter, isAccessor, obj, slot.thisValue(), value); 278 if (result != TriState::Indeterminate) 279 RELEASE_AND_RETURN(scope, result == TriState::True); 280 } 281 282 // If there's an existing property on the object or one of its 283 // prototypes it should be replaced, so break here. 284 break; 285 } 286 if (obj->type() == ProxyObjectType) { 287 auto* proxy = jsCast<ProxyObject*>(obj); 288 RELEASE_AND_RETURN(scope, proxy->ProxyObject::put(proxy, globalObject, propertyName, value, slot)); 289 } 290 prototype = obj->getPrototype(vm, globalObject); 291 RETURN_IF_EXCEPTION(scope, false); 292 if (prototype.isNull()) 293 break; 294 } 295 296 return typeError(globalObject, scope, slot.isStrictMode(), ReadonlyPropertyWriteError); 297 } 298 299 bool JSValue::putToPrimitiveByIndex(JSGlobalObject* globalObject, unsigned propertyName, JSValue value, bool shouldThrow) 300 { 301 VM& vm = globalObject->vm(); 302 auto scope = DECLARE_THROW_SCOPE(vm); 303 304 if (propertyName > MAX_ARRAY_INDEX) { 305 PutPropertySlot slot(*this, shouldThrow); 306 return putToPrimitive(globalObject, Identifier::from(vm, propertyName), value, slot); 307 } 308 309 JSObject* prototype = synthesizePrototype(globalObject); 310 EXCEPTION_ASSERT(!!scope.exception() == !prototype); 311 if (UNLIKELY(!prototype)) 312 return false; 313 bool putResult = false; 314 bool success = prototype->attemptToInterceptPutByIndexOnHoleForPrototype(globalObject, *this, propertyName, value, shouldThrow, putResult); 315 RETURN_IF_EXCEPTION(scope, false); 316 if (success) 317 return putResult; 318 319 return typeError(globalObject, scope, shouldThrow, ReadonlyPropertyWriteError); 320 } 321 322 void JSValue::dump(PrintStream& out) const 323 { 324 dumpInContext(out, nullptr); 325 } 326 327 void JSValue::dumpInContext(PrintStream& out, DumpContext* context) const 328 { 329 dumpInContextAssumingStructure( 330 out, context, (!!*this && isCell()) ? asCell()->structure() : nullptr); 331 } 332 333 void JSValue::dumpInContextAssumingStructure( 334 PrintStream& out, DumpContext* context, Structure* structure) const 335 { 336 if (!*this) 337 out.print("<JSValue()>"); 338 else if (isInt32()) 339 out.printf("Int32: %d", asInt32()); 340 else if (isDouble()) { 341 #if USE(JSVALUE64) 342 out.printf("Double: %lld, %lf", (long long)reinterpretDoubleToInt64(asDouble()), asDouble()); 343 #else 344 union { 345 double asDouble; 346 uint32_t asTwoInt32s[2]; 347 } u; 348 u.asDouble = asDouble(); 349 out.printf("Double: %08x:%08x, %lf", u.asTwoInt32s[1], u.asTwoInt32s[0], asDouble()); 350 #endif 351 } else if (isCell()) { 352 if (structure->classInfo()->isSubClassOf(JSString::info())) { 353 JSString* string = asString(asCell()); 354 out.print("String"); 355 if (string->isRope()) 356 out.print(" (rope)"); 357 const StringImpl* impl = string->tryGetValueImpl(); 358 if (impl) { 359 if (impl->isAtom()) 360 out.print(" (atomic)"); 361 if (impl->isSymbol()) 362 out.print(" (symbol)"); 363 } else 364 out.print(" (unresolved)"); 365 if (string->is8Bit()) 366 out.print(",8Bit:(1)"); 367 else 368 out.print(",8Bit:(0)"); 369 out.print(",length:(", string->length(), ")"); 370 out.print(": ", impl); 371 } else if (structure->classInfo()->isSubClassOf(RegExp::info())) 372 out.print("RegExp: ", *jsCast<RegExp*>(asCell())); 373 else if (structure->classInfo()->isSubClassOf(Symbol::info())) 374 out.print("Symbol: ", RawPointer(asCell())); 375 else if (structure->classInfo()->isSubClassOf(Structure::info())) 376 out.print("Structure: ", inContext(*jsCast<Structure*>(asCell()), context)); 377 else if (isHeapBigInt()) 378 out.print("BigInt[heap-allocated]: addr=", RawPointer(asCell()), ", length=", jsCast<JSBigInt*>(asCell())->length(), ", sign=", jsCast<JSBigInt*>(asCell())->sign()); 379 else if (structure->classInfo()->isSubClassOf(JSObject::info())) { 380 out.print("Object: ", RawPointer(asCell())); 381 out.print(" with butterfly ", RawPointer(asObject(asCell())->butterfly())); 382 out.print(" (Structure ", inContext(*structure, context), ")"); 383 } else { 384 out.print("Cell: ", RawPointer(asCell())); 385 out.print(" (", inContext(*structure, context), ")"); 386 } 387 #if USE(JSVALUE64) 388 out.print(", StructureID: ", asCell()->structureID()); 389 #endif 390 } else if (isTrue()) 391 out.print("True"); 392 else if (isFalse()) 393 out.print("False"); 394 else if (isNull()) 395 out.print("Null"); 396 else if (isUndefined()) 397 out.print("Undefined"); 398 #if USE(BIGINT32) 399 else if (isBigInt32()) 400 out.printf("BigInt[inline]: %d", bigInt32AsInt32()); 401 #endif 402 else 403 out.print("INVALID"); 404 } 405 406 void JSValue::dumpForBacktrace(PrintStream& out) const 407 { 408 if (!*this) 409 out.print("<JSValue()>"); 410 else if (isInt32()) 411 out.printf("%d", asInt32()); 412 else if (isDouble()) 413 out.printf("%lf", asDouble()); 414 else if (isCell()) { 415 VM& vm = asCell()->vm(); 416 if (asCell()->inherits<JSString>(vm)) { 417 JSString* string = asString(asCell()); 418 const StringImpl* impl = string->tryGetValueImpl(); 419 if (impl) 420 out.print("\"", impl, "\""); 421 else 422 out.print("(unresolved string)"); 423 } else if (asCell()->inherits<Structure>(vm)) { 424 out.print("Structure[ ", asCell()->structure()->classInfo()->className); 425 #if USE(JSVALUE64) 426 out.print(" ID: ", asCell()->structureID()); 427 #endif 428 out.print("]: ", RawPointer(asCell())); 429 } else { 430 out.print("Cell[", asCell()->structure()->classInfo()->className); 431 #if USE(JSVALUE64) 432 out.print(" ID: ", asCell()->structureID()); 433 #endif 434 out.print("]: ", RawPointer(asCell())); 435 } 436 } else if (isTrue()) 437 out.print("True"); 438 else if (isFalse()) 439 out.print("False"); 440 else if (isNull()) 441 out.print("Null"); 442 else if (isUndefined()) 443 out.print("Undefined"); 444 #if USE(BIGINT32) 445 else if (isBigInt32()) 446 out.printf("BigInt[inline]: %d", bigInt32AsInt32()); 447 #endif 448 else 449 out.print("INVALID"); 450 } 451 452 bool JSValue::isValidCallee() 453 { 454 return asObject(asCell())->globalObject(); 455 } 456 457 JSString* JSValue::toStringSlowCase(JSGlobalObject* globalObject, bool returnEmptyStringOnError) const 458 { 459 VM& vm = globalObject->vm(); 460 auto scope = DECLARE_THROW_SCOPE(vm); 461 462 auto errorValue = [&] () -> JSString* { 463 if (returnEmptyStringOnError) 464 return jsEmptyString(vm); 465 return nullptr; 466 }; 467 468 ASSERT(!isString()); 469 if (isInt32()) { 470 auto integer = asInt32(); 471 if (static_cast<unsigned>(integer) <= 9) 472 return vm.smallStrings.singleCharacterString(integer + '0'); 473 return jsNontrivialString(vm, vm.numericStrings.add(integer)); 474 } 475 if (isDouble()) 476 return jsString(vm, vm.numericStrings.add(asDouble())); 477 if (isTrue()) 478 return vm.smallStrings.trueString(); 479 if (isFalse()) 480 return vm.smallStrings.falseString(); 481 if (isNull()) 482 return vm.smallStrings.nullString(); 483 if (isUndefined()) 484 return vm.smallStrings.undefinedString(); 485 #if USE(BIGINT32) 486 if (isBigInt32()) { 487 auto integer = bigInt32AsInt32(); 488 if (static_cast<unsigned>(integer) <= 9) 489 return vm.smallStrings.singleCharacterString(integer + '0'); 490 return jsNontrivialString(vm, vm.numericStrings.add(integer)); 491 } 492 #endif 493 if (isHeapBigInt()) { 494 JSBigInt* bigInt = asHeapBigInt(); 495 // FIXME: we should rather have two cases here: one-character string vs jsNonTrivialString for everything else. 496 auto string = bigInt->toString(globalObject, 10); 497 RETURN_IF_EXCEPTION(scope, errorValue()); 498 JSString* returnString = JSString::create(vm, string.releaseImpl().releaseNonNull()); 499 RETURN_IF_EXCEPTION(scope, errorValue()); 500 return returnString; 501 } 502 if (isSymbol()) { 503 throwTypeError(globalObject, scope, "Cannot convert a symbol to a string"_s); 504 return errorValue(); 505 } 506 507 ASSERT(isCell()); 508 JSValue value = asCell()->toPrimitive(globalObject, PreferString); 509 RETURN_IF_EXCEPTION(scope, errorValue()); 510 ASSERT(!value.isObject()); 511 JSString* result = value.toString(globalObject); 512 RETURN_IF_EXCEPTION(scope, errorValue()); 513 return result; 514 } 515 516 String JSValue::toWTFStringSlowCase(JSGlobalObject* globalObject) const 517 { 518 VM& vm = globalObject->vm(); 519 auto scope = DECLARE_THROW_SCOPE(vm); 520 if (isInt32()) 521 return vm.numericStrings.add(asInt32()); 522 if (isDouble()) 523 return vm.numericStrings.add(asDouble()); 524 if (isTrue()) 525 return vm.propertyNames->trueKeyword.string(); 526 if (isFalse()) 527 return vm.propertyNames->falseKeyword.string(); 528 if (isNull()) 529 return vm.propertyNames->nullKeyword.string(); 530 if (isUndefined()) 531 return vm.propertyNames->undefinedKeyword.string(); 532 JSString* string = toString(globalObject); 533 RETURN_IF_EXCEPTION(scope, { }); 534 RELEASE_AND_RETURN(scope, string->value(globalObject)); 535 } 536 537 #if !COMPILER(GCC_COMPATIBLE) 538 // This makes the argument opaque from the compiler. 539 NEVER_INLINE void ensureStillAliveHere(JSValue) 540 { 541 } 542 #endif 543 544 } // namespace JSC