2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
4 * Copyright (C) 2003, 2007-2008, 2012, 2016 Apple Inc. All rights reserved.
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.
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.
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.
24 #include "JSCJSValue.h"
26 #include "BooleanConstructor.h"
27 #include "BooleanPrototype.h"
28 #include "CustomGetterSetter.h"
30 #include "ExceptionHelpers.h"
31 #include "GetterSetter.h"
32 #include "JSCInlines.h"
33 #include "JSFunction.h"
34 #include "JSGlobalObject.h"
35 #include "NumberObject.h"
36 #include <wtf/MathExtras.h>
37 #include <wtf/StringExtras.h>
42 double JSValue::toInteger(ExecState* exec) const
46 double d = toNumber(exec);
47 return std::isnan(d) ? 0.0 : trunc(d);
50 double JSValue::toIntegerPreserveNaN(ExecState* exec) const
54 return trunc(toNumber(exec));
57 double JSValue::toLength(ExecState* exec) const
60 // http://www.ecma-international.org/ecma-262/6.0/#sec-tolength
61 double d = toInteger(exec);
65 return 9007199254740991.0; // 2 ** 53 - 1
66 return std::min(d, 9007199254740991.0);
69 double JSValue::toNumberSlowCase(ExecState* exec) const
71 ASSERT(!isInt32() && !isDouble());
73 return asCell()->toNumber(exec);
76 return isUndefined() ? PNaN : 0; // null and false both convert to 0.
79 Optional<double> JSValue::toNumberFromPrimitive() const
94 JSObject* JSValue::toObjectSlowCase(ExecState* exec, JSGlobalObject* globalObject) const
97 auto scope = DECLARE_THROW_SCOPE(vm);
100 if (isInt32() || isDouble())
101 return constructNumber(exec, globalObject, asValue());
102 if (isTrue() || isFalse())
103 return constructBooleanFromImmediateBoolean(exec, globalObject, asValue());
105 ASSERT(isUndefinedOrNull());
106 throwException(exec, scope, createNotAnObjectError(exec, *this));
110 JSValue JSValue::toThisSlowCase(ExecState* exec, ECMAMode ecmaMode) const
114 if (ecmaMode == StrictMode)
117 if (isInt32() || isDouble())
118 return constructNumber(exec, exec->lexicalGlobalObject(), asValue());
119 if (isTrue() || isFalse())
120 return constructBooleanFromImmediateBoolean(exec, exec->lexicalGlobalObject(), asValue());
121 ASSERT(isUndefinedOrNull());
122 return exec->globalThisValue();
125 JSObject* JSValue::synthesizePrototype(ExecState* exec) const
128 auto scope = DECLARE_THROW_SCOPE(vm);
132 return exec->lexicalGlobalObject()->stringPrototype();
134 return exec->lexicalGlobalObject()->symbolPrototype();
138 return exec->lexicalGlobalObject()->numberPrototype();
140 return exec->lexicalGlobalObject()->booleanPrototype();
142 ASSERT(isUndefinedOrNull());
143 throwException(exec, scope, createNotAnObjectError(exec, *this));
148 bool JSValue::putToPrimitive(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
151 auto scope = DECLARE_THROW_SCOPE(vm);
153 if (Optional<uint32_t> index = parseIndex(propertyName))
154 return putToPrimitiveByIndex(exec, index.value(), value, slot.isStrictMode());
156 // Check if there are any setters or getters in the prototype chain
157 JSObject* obj = synthesizePrototype(exec);
161 if (propertyName != vm.propertyNames->underscoreProto) {
162 for (; !obj->structure()->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj = asObject(prototype)) {
163 prototype = obj->getPrototypeDirect();
164 if (prototype.isNull())
165 return reject(exec, scope, slot.isStrictMode(), ASCIILiteral(ReadonlyPropertyWriteError));
169 for (; ; obj = asObject(prototype)) {
171 PropertyOffset offset = obj->structure()->get(vm, propertyName, attributes);
172 if (offset != invalidOffset) {
173 if (attributes & ReadOnly)
174 return reject(exec, scope, slot.isStrictMode(), ASCIILiteral(ReadonlyPropertyWriteError));
176 JSValue gs = obj->getDirect(offset);
177 if (gs.isGetterSetter())
178 return callSetter(exec, *this, gs, value, slot.isStrictMode() ? StrictMode : NotStrictMode);
180 if (gs.isCustomGetterSetter())
181 return callCustomSetter(exec, gs, attributes & CustomAccessor, obj, slot.thisValue(), value);
183 // If there's an existing property on the object or one of its
184 // prototypes it should be replaced, so break here.
188 prototype = obj->getPrototype(vm, exec);
189 RETURN_IF_EXCEPTION(scope, false);
190 if (prototype.isNull())
194 return reject(exec, scope, slot.isStrictMode(), ASCIILiteral(ReadonlyPropertyWriteError));
197 bool JSValue::putToPrimitiveByIndex(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
200 auto scope = DECLARE_THROW_SCOPE(vm);
202 if (propertyName > MAX_ARRAY_INDEX) {
203 PutPropertySlot slot(*this, shouldThrow);
204 return putToPrimitive(exec, Identifier::from(exec, propertyName), value, slot);
207 JSObject* prototype = synthesizePrototype(exec);
208 if (UNLIKELY(!prototype)) {
209 ASSERT(scope.exception());
212 bool putResult = false;
213 if (prototype->attemptToInterceptPutByIndexOnHoleForPrototype(exec, *this, propertyName, value, shouldThrow, putResult))
216 return reject(exec, scope, shouldThrow, ASCIILiteral(ReadonlyPropertyWriteError));
219 void JSValue::dump(PrintStream& out) const
221 dumpInContext(out, 0);
224 void JSValue::dumpInContext(PrintStream& out, DumpContext* context) const
226 dumpInContextAssumingStructure(
227 out, context, (!!*this && isCell()) ? asCell()->structure() : nullptr);
230 void JSValue::dumpInContextAssumingStructure(
231 PrintStream& out, DumpContext* context, Structure* structure) const
234 out.print("<JSValue()>");
236 out.printf("Int32: %d", asInt32());
237 else if (isDouble()) {
239 out.printf("Double: %lld, %lf", (long long)reinterpretDoubleToInt64(asDouble()), asDouble());
243 uint32_t asTwoInt32s[2];
245 u.asDouble = asDouble();
246 out.printf("Double: %08x:%08x, %lf", u.asTwoInt32s[1], u.asTwoInt32s[0], asDouble());
248 } else if (isCell()) {
249 if (structure->classInfo()->isSubClassOf(JSString::info())) {
250 JSString* string = jsCast<JSString*>(asCell());
252 if (string->isRope())
253 out.print(" (rope)");
254 const StringImpl* impl = string->tryGetValueImpl();
256 if (impl->isAtomic())
257 out.print(" (atomic)");
258 if (impl->isAtomic())
259 out.print(" (identifier)");
260 if (impl->isSymbol())
261 out.print(" (symbol)");
263 out.print(" (unresolved)");
264 out.print(": ", impl);
265 } else if (structure->classInfo()->isSubClassOf(Symbol::info()))
266 out.print("Symbol: ", RawPointer(asCell()));
267 else if (structure->classInfo()->isSubClassOf(Structure::info()))
268 out.print("Structure: ", inContext(*jsCast<Structure*>(asCell()), context));
269 else if (structure->classInfo()->isSubClassOf(JSObject::info())) {
270 out.print("Object: ", RawPointer(asCell()));
271 out.print(" with butterfly ", RawPointer(asObject(asCell())->butterfly()));
272 out.print(" (", inContext(*structure, context), ")");
274 out.print("Cell: ", RawPointer(asCell()));
275 out.print(" (", inContext(*structure, context), ")");
278 out.print(", ID: ", asCell()->structureID());
286 else if (isUndefined())
287 out.print("Undefined");
289 out.print("INVALID");
292 void JSValue::dumpForBacktrace(PrintStream& out) const
295 out.print("<JSValue()>");
297 out.printf("%d", asInt32());
299 out.printf("%lf", asDouble());
301 if (asCell()->inherits(JSString::info())) {
302 JSString* string = jsCast<JSString*>(asCell());
303 const StringImpl* impl = string->tryGetValueImpl();
305 out.print("\"", impl, "\"");
307 out.print("(unresolved string)");
308 } else if (asCell()->inherits(Structure::info())) {
309 out.print("Structure[ ", asCell()->structure()->classInfo()->className);
311 out.print(" ID: ", asCell()->structureID());
313 out.print("]: ", RawPointer(asCell()));
315 out.print("Cell[", asCell()->structure()->classInfo()->className);
317 out.print(" ID: ", asCell()->structureID());
319 out.print("]: ", RawPointer(asCell()));
327 else if (isUndefined())
328 out.print("Undefined");
330 out.print("INVALID");
333 bool JSValue::isValidCallee()
335 return asObject(asCell())->globalObject();
338 JSString* JSValue::toStringSlowCase(ExecState* exec, bool returnEmptyStringOnError) const
341 auto scope = DECLARE_THROW_SCOPE(vm);
343 auto errorValue = [&] () -> JSString* {
344 if (returnEmptyStringOnError)
345 return jsEmptyString(exec);
351 auto integer = asInt32();
352 if (static_cast<unsigned>(integer) <= 9)
353 return vm.smallStrings.singleCharacterString(integer + '0');
354 return jsNontrivialString(&vm, vm.numericStrings.add(integer));
357 return jsString(&vm, vm.numericStrings.add(asDouble()));
359 return vm.smallStrings.trueString();
361 return vm.smallStrings.falseString();
363 return vm.smallStrings.nullString();
365 return vm.smallStrings.undefinedString();
367 throwTypeError(exec, scope, ASCIILiteral("Cannot convert a symbol to a string"));
372 JSValue value = asCell()->toPrimitive(exec, PreferString);
373 RETURN_IF_EXCEPTION(scope, errorValue());
374 ASSERT(!value.isObject());
375 JSString* result = value.toString(exec);
376 RETURN_IF_EXCEPTION(scope, errorValue());
380 String JSValue::toWTFStringSlowCase(ExecState* exec) const
384 return vm.numericStrings.add(asInt32());
386 return vm.numericStrings.add(asDouble());
388 return vm.propertyNames->trueKeyword.string();
390 return vm.propertyNames->falseKeyword.string();
392 return vm.propertyNames->nullKeyword.string();
394 return vm.propertyNames->undefinedKeyword.string();
395 return toString(exec)->value(exec);