summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/runtime/JSCJSValue.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/runtime/JSCJSValue.cpp')
-rw-r--r--Source/JavaScriptCore/runtime/JSCJSValue.cpp282
1 files changed, 178 insertions, 104 deletions
diff --git a/Source/JavaScriptCore/runtime/JSCJSValue.cpp b/Source/JavaScriptCore/runtime/JSCJSValue.cpp
index cca6888bf..8eec50c96 100644
--- a/Source/JavaScriptCore/runtime/JSCJSValue.cpp
+++ b/Source/JavaScriptCore/runtime/JSCJSValue.cpp
@@ -1,7 +1,7 @@
/*
* Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
* Copyright (C) 2001 Peter Kelly (pmk@post.com)
- * Copyright (C) 2003, 2007, 2008, 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2003, 2007-2008, 2012, 2016 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -25,13 +25,13 @@
#include "BooleanConstructor.h"
#include "BooleanPrototype.h"
+#include "CustomGetterSetter.h"
#include "Error.h"
#include "ExceptionHelpers.h"
#include "GetterSetter.h"
-#include "JSCJSValueInlines.h"
+#include "JSCInlines.h"
#include "JSFunction.h"
#include "JSGlobalObject.h"
-#include "JSNotAnObject.h"
#include "NumberObject.h"
#include <wtf/MathExtras.h>
#include <wtf/StringExtras.h>
@@ -54,6 +54,18 @@ double JSValue::toIntegerPreserveNaN(ExecState* exec) const
return trunc(toNumber(exec));
}
+double JSValue::toLength(ExecState* exec) const
+{
+ // ECMA 7.1.15
+ // http://www.ecma-international.org/ecma-262/6.0/#sec-tolength
+ double d = toInteger(exec);
+ if (d <= 0)
+ return 0.0;
+ if (std::isinf(d))
+ return 9007199254740991.0; // 2 ** 53 - 1
+ return std::min(d, 9007199254740991.0);
+}
+
double JSValue::toNumberSlowCase(ExecState* exec) const
{
ASSERT(!isInt32() && !isDouble());
@@ -61,11 +73,28 @@ double JSValue::toNumberSlowCase(ExecState* exec) const
return asCell()->toNumber(exec);
if (isTrue())
return 1.0;
- return isUndefined() ? QNaN : 0; // null and false both convert to 0.
+ return isUndefined() ? PNaN : 0; // null and false both convert to 0.
+}
+
+std::optional<double> JSValue::toNumberFromPrimitive() const
+{
+ if (isEmpty())
+ return std::nullopt;
+ if (isNumber())
+ return asNumber();
+ if (isBoolean())
+ return asBoolean();
+ if (isUndefined())
+ return PNaN;
+ if (isNull())
+ return 0;
+ return std::nullopt;
}
JSObject* JSValue::toObjectSlowCase(ExecState* exec, JSGlobalObject* globalObject) const
{
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
ASSERT(!isCell());
if (isInt32() || isDouble())
@@ -74,9 +103,8 @@ JSObject* JSValue::toObjectSlowCase(ExecState* exec, JSGlobalObject* globalObjec
return constructBooleanFromImmediateBoolean(exec, globalObject, asValue());
ASSERT(isUndefinedOrNull());
- VM& vm = exec->vm();
- vm.throwException(exec, createNotAnObjectError(exec, *this));
- return JSNotAnObject::create(vm);
+ throwException(exec, scope, createNotAnObjectError(exec, *this));
+ return nullptr;
}
JSValue JSValue::toThisSlowCase(ExecState* exec, ECMAMode ecmaMode) const
@@ -96,9 +124,14 @@ JSValue JSValue::toThisSlowCase(ExecState* exec, ECMAMode ecmaMode) const
JSObject* JSValue::synthesizePrototype(ExecState* exec) const
{
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
if (isCell()) {
- ASSERT(isString());
- return exec->lexicalGlobalObject()->stringPrototype();
+ if (isString())
+ return exec->lexicalGlobalObject()->stringPrototype();
+ ASSERT(isSymbol());
+ return exec->lexicalGlobalObject()->symbolPrototype();
}
if (isNumber())
@@ -107,81 +140,80 @@ JSObject* JSValue::synthesizePrototype(ExecState* exec) const
return exec->lexicalGlobalObject()->booleanPrototype();
ASSERT(isUndefinedOrNull());
- VM& vm = exec->vm();
- vm.throwException(exec, createNotAnObjectError(exec, *this));
- return JSNotAnObject::create(vm);
+ throwException(exec, scope, createNotAnObjectError(exec, *this));
+ return nullptr;
}
// ECMA 8.7.2
-void JSValue::putToPrimitive(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
+bool JSValue::putToPrimitive(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
{
VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
- unsigned index = propertyName.asIndex();
- if (index != PropertyName::NotAnIndex) {
- putToPrimitiveByIndex(exec, index, value, slot.isStrictMode());
- return;
- }
+ if (std::optional<uint32_t> index = parseIndex(propertyName))
+ return putToPrimitiveByIndex(exec, index.value(), value, slot.isStrictMode());
// Check if there are any setters or getters in the prototype chain
JSObject* obj = synthesizePrototype(exec);
+ if (UNLIKELY(!obj))
+ return false;
JSValue prototype;
- if (propertyName != exec->propertyNames().underscoreProto) {
+ if (propertyName != vm.propertyNames->underscoreProto) {
for (; !obj->structure()->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj = asObject(prototype)) {
- prototype = obj->prototype();
- if (prototype.isNull()) {
- if (slot.isStrictMode())
- throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
- return;
- }
+ prototype = obj->getPrototypeDirect();
+ if (prototype.isNull())
+ return typeError(exec, scope, slot.isStrictMode(), ASCIILiteral(ReadonlyPropertyWriteError));
}
}
for (; ; obj = asObject(prototype)) {
unsigned attributes;
- JSCell* specificValue;
- PropertyOffset offset = obj->structure()->get(vm, propertyName, attributes, specificValue);
+ PropertyOffset offset = obj->structure()->get(vm, propertyName, attributes);
if (offset != invalidOffset) {
- if (attributes & ReadOnly) {
- if (slot.isStrictMode())
- exec->vm().throwException(exec, createTypeError(exec, StrictModeReadonlyPropertyWriteError));
- return;
- }
+ if (attributes & ReadOnly)
+ return typeError(exec, scope, slot.isStrictMode(), ASCIILiteral(ReadonlyPropertyWriteError));
JSValue gs = obj->getDirect(offset);
- if (gs.isGetterSetter()) {
- callSetter(exec, *this, gs, value, slot.isStrictMode() ? StrictMode : NotStrictMode);
- return;
- }
+ if (gs.isGetterSetter())
+ return callSetter(exec, *this, gs, value, slot.isStrictMode() ? StrictMode : NotStrictMode);
+
+ if (gs.isCustomGetterSetter())
+ return callCustomSetter(exec, gs, attributes & CustomAccessor, obj, slot.thisValue(), value);
// If there's an existing property on the object or one of its
// prototypes it should be replaced, so break here.
break;
}
- prototype = obj->prototype();
+ prototype = obj->getPrototype(vm, exec);
+ RETURN_IF_EXCEPTION(scope, false);
if (prototype.isNull())
break;
}
- if (slot.isStrictMode())
- throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
- return;
+ return typeError(exec, scope, slot.isStrictMode(), ASCIILiteral(ReadonlyPropertyWriteError));
}
-void JSValue::putToPrimitiveByIndex(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
+bool JSValue::putToPrimitiveByIndex(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
{
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
if (propertyName > MAX_ARRAY_INDEX) {
PutPropertySlot slot(*this, shouldThrow);
- putToPrimitive(exec, Identifier::from(exec, propertyName), value, slot);
- return;
+ return putToPrimitive(exec, Identifier::from(exec, propertyName), value, slot);
}
- if (synthesizePrototype(exec)->attemptToInterceptPutByIndexOnHoleForPrototype(exec, *this, propertyName, value, shouldThrow))
- return;
+ JSObject* prototype = synthesizePrototype(exec);
+ if (UNLIKELY(!prototype)) {
+ ASSERT(scope.exception());
+ return false;
+ }
+ bool putResult = false;
+ if (prototype->attemptToInterceptPutByIndexOnHoleForPrototype(exec, *this, propertyName, value, shouldThrow, putResult))
+ return putResult;
- if (shouldThrow)
- throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
+ return typeError(exec, scope, shouldThrow, ASCIILiteral(ReadonlyPropertyWriteError));
}
void JSValue::dump(PrintStream& out) const
@@ -191,6 +223,13 @@ void JSValue::dump(PrintStream& out) const
void JSValue::dumpInContext(PrintStream& out, DumpContext* context) const
{
+ dumpInContextAssumingStructure(
+ out, context, (!!*this && isCell()) ? asCell()->structure() : nullptr);
+}
+
+void JSValue::dumpInContextAssumingStructure(
+ PrintStream& out, DumpContext* context, Structure* structure) const
+{
if (!*this)
out.print("<JSValue()>");
else if (isInt32())
@@ -207,8 +246,8 @@ void JSValue::dumpInContext(PrintStream& out, DumpContext* context) const
out.printf("Double: %08x:%08x, %lf", u.asTwoInt32s[1], u.asTwoInt32s[0], asDouble());
#endif
} else if (isCell()) {
- if (asCell()->inherits(JSString::info())) {
- JSString* string = jsCast<JSString*>(asCell());
+ if (structure->classInfo()->isSubClassOf(JSString::info())) {
+ JSString* string = asString(asCell());
out.print("String");
if (string->isRope())
out.print(" (rope)");
@@ -216,19 +255,28 @@ void JSValue::dumpInContext(PrintStream& out, DumpContext* context) const
if (impl) {
if (impl->isAtomic())
out.print(" (atomic)");
- if (impl->isIdentifier())
+ if (impl->isAtomic())
out.print(" (identifier)");
- if (impl->isEmptyUnique())
- out.print(" (unique)");
+ if (impl->isSymbol())
+ out.print(" (symbol)");
} else
out.print(" (unresolved)");
out.print(": ", impl);
- } else if (asCell()->inherits(Structure::info()))
+ } else if (structure->classInfo()->isSubClassOf(Symbol::info()))
+ out.print("Symbol: ", RawPointer(asCell()));
+ else if (structure->classInfo()->isSubClassOf(Structure::info()))
out.print("Structure: ", inContext(*jsCast<Structure*>(asCell()), context));
- else {
+ else if (structure->classInfo()->isSubClassOf(JSObject::info())) {
+ out.print("Object: ", RawPointer(asCell()));
+ out.print(" with butterfly ", RawPointer(asObject(asCell())->butterfly()));
+ out.print(" (", inContext(*structure, context), ")");
+ } else {
out.print("Cell: ", RawPointer(asCell()));
- out.print(" (", inContext(*asCell()->structure(), context), ")");
+ out.print(" (", inContext(*structure, context), ")");
}
+#if USE(JSVALUE64)
+ out.print(", ID: ", asCell()->structureID());
+#endif
} else if (isTrue())
out.print("True");
else if (isFalse())
@@ -241,49 +289,45 @@ void JSValue::dumpInContext(PrintStream& out, DumpContext* context) const
out.print("INVALID");
}
-// This in the ToInt32 operation is defined in section 9.5 of the ECMA-262 spec.
-// Note that this operation is identical to ToUInt32 other than to interpretation
-// of the resulting bit-pattern (as such this metod is also called to implement
-// ToUInt32).
-//
-// The operation can be descibed as round towards zero, then select the 32 least
-// bits of the resulting value in 2s-complement representation.
-int32_t toInt32(double number)
+void JSValue::dumpForBacktrace(PrintStream& out) const
{
- int64_t bits = WTF::bitwise_cast<int64_t>(number);
- int32_t exp = (static_cast<int32_t>(bits >> 52) & 0x7ff) - 0x3ff;
-
- // If exponent < 0 there will be no bits to the left of the decimal point
- // after rounding; if the exponent is > 83 then no bits of precision can be
- // left in the low 32-bit range of the result (IEEE-754 doubles have 52 bits
- // of fractional precision).
- // Note this case handles 0, -0, and all infinte, NaN, & denormal value.
- if (exp < 0 || exp > 83)
- return 0;
-
- // Select the appropriate 32-bits from the floating point mantissa. If the
- // exponent is 52 then the bits we need to select are already aligned to the
- // lowest bits of the 64-bit integer representation of tghe number, no need
- // to shift. If the exponent is greater than 52 we need to shift the value
- // left by (exp - 52), if the value is less than 52 we need to shift right
- // accordingly.
- int32_t result = (exp > 52)
- ? static_cast<int32_t>(bits << (exp - 52))
- : static_cast<int32_t>(bits >> (52 - exp));
-
- // IEEE-754 double precision values are stored omitting an implicit 1 before
- // the decimal point; we need to reinsert this now. We may also the shifted
- // invalid bits into the result that are not a part of the mantissa (the sign
- // and exponent bits from the floatingpoint representation); mask these out.
- if (exp < 32) {
- int32_t missingOne = 1 << exp;
- result &= missingOne - 1;
- result += missingOne;
- }
-
- // If the input value was negative (we could test either 'number' or 'bits',
- // but testing 'bits' is likely faster) invert the result appropriately.
- return bits < 0 ? -result : result;
+ if (!*this)
+ out.print("<JSValue()>");
+ else if (isInt32())
+ out.printf("%d", asInt32());
+ else if (isDouble())
+ out.printf("%lf", asDouble());
+ else if (isCell()) {
+ if (asCell()->inherits(*asCell()->vm(), JSString::info())) {
+ JSString* string = asString(asCell());
+ const StringImpl* impl = string->tryGetValueImpl();
+ if (impl)
+ out.print("\"", impl, "\"");
+ else
+ out.print("(unresolved string)");
+ } else if (asCell()->inherits(*asCell()->vm(), Structure::info())) {
+ out.print("Structure[ ", asCell()->structure()->classInfo()->className);
+#if USE(JSVALUE64)
+ out.print(" ID: ", asCell()->structureID());
+#endif
+ out.print("]: ", RawPointer(asCell()));
+ } else {
+ out.print("Cell[", asCell()->structure()->classInfo()->className);
+#if USE(JSVALUE64)
+ out.print(" ID: ", asCell()->structureID());
+#endif
+ out.print("]: ", RawPointer(asCell()));
+ }
+ } else if (isTrue())
+ out.print("True");
+ else if (isFalse())
+ out.print("False");
+ else if (isNull())
+ out.print("Null");
+ else if (isUndefined())
+ out.print("Undefined");
+ else
+ out.print("INVALID");
}
bool JSValue::isValidCallee()
@@ -291,12 +335,24 @@ bool JSValue::isValidCallee()
return asObject(asCell())->globalObject();
}
-JSString* JSValue::toStringSlowCase(ExecState* exec) const
+JSString* JSValue::toStringSlowCase(ExecState* exec, bool returnEmptyStringOnError) const
{
VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ auto errorValue = [&] () -> JSString* {
+ if (returnEmptyStringOnError)
+ return jsEmptyString(exec);
+ return nullptr;
+ };
+
ASSERT(!isString());
- if (isInt32())
- return jsString(&vm, vm.numericStrings.add(asInt32()));
+ if (isInt32()) {
+ auto integer = asInt32();
+ if (static_cast<unsigned>(integer) <= 9)
+ return vm.smallStrings.singleCharacterString(integer + '0');
+ return jsNontrivialString(&vm, vm.numericStrings.add(integer));
+ }
if (isDouble())
return jsString(&vm, vm.numericStrings.add(asDouble()));
if (isTrue())
@@ -307,18 +363,36 @@ JSString* JSValue::toStringSlowCase(ExecState* exec) const
return vm.smallStrings.nullString();
if (isUndefined())
return vm.smallStrings.undefinedString();
+ if (isSymbol()) {
+ throwTypeError(exec, scope, ASCIILiteral("Cannot convert a symbol to a string"));
+ return errorValue();
+ }
ASSERT(isCell());
JSValue value = asCell()->toPrimitive(exec, PreferString);
- if (exec->hadException())
- return jsEmptyString(exec);
+ RETURN_IF_EXCEPTION(scope, errorValue());
ASSERT(!value.isObject());
- return value.toString(exec);
+ JSString* result = value.toString(exec);
+ RETURN_IF_EXCEPTION(scope, errorValue());
+ return result;
}
String JSValue::toWTFStringSlowCase(ExecState* exec) const
{
- return inlineJSValueNotStringtoString(*this, exec);
+ VM& vm = exec->vm();
+ if (isInt32())
+ return vm.numericStrings.add(asInt32());
+ if (isDouble())
+ return vm.numericStrings.add(asDouble());
+ if (isTrue())
+ return vm.propertyNames->trueKeyword.string();
+ if (isFalse())
+ return vm.propertyNames->falseKeyword.string();
+ if (isNull())
+ return vm.propertyNames->nullKeyword.string();
+ if (isUndefined())
+ return vm.propertyNames->undefinedKeyword.string();
+ return toString(exec)->value(exec);
}
} // namespace JSC