1/*
2 * Copyright (C) 1999-2001 Harri Porten ([email protected])
3 * Copyright (C) 2001 Peter Kelly ([email protected])
4 * Copyright (C) 2003-2017 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 "BooleanConstructor.h"
27#include "BooleanPrototype.h"
28#include "CustomGetterSetter.h"
29#include "Error.h"
30#include "ExceptionHelpers.h"
31#include "GetterSetter.h"
32#include "JSBigInt.h"
33#include "JSCInlines.h"
34#include "JSFunction.h"
35#include "JSGlobalObject.h"
36#include "NumberObject.h"
37#include <wtf/MathExtras.h>
38
39namespace JSC {
40
41// ECMA 9.4
42double JSValue::toInteger(ExecState* exec) const
43{
44 if (isInt32())
45 return asInt32();
46 double d = toNumber(exec);
47 return std::isnan(d) ? 0.0 : trunc(d);
48}
49
50double JSValue::toIntegerPreserveNaN(ExecState* exec) const
51{
52 if (isInt32())
53 return asInt32();
54 return trunc(toNumber(exec));
55}
56
57double JSValue::toLength(ExecState* exec) const
58{
59 // ECMA 7.1.15
60 // http://www.ecma-international.org/ecma-262/6.0/#sec-tolength
61 double d = toInteger(exec);
62 if (d <= 0)
63 return 0.0;
64 if (std::isinf(d))
65 return maxSafeInteger();
66 return std::min(d, maxSafeInteger());
67}
68
69double JSValue::toNumberSlowCase(ExecState* exec) const
70{
71 ASSERT(!isInt32() && !isDouble());
72 if (isCell())
73 return asCell()->toNumber(exec);
74 if (isTrue())
75 return 1.0;
76 return isUndefined() ? PNaN : 0; // null and false both convert to 0.
77}
78
79Optional<double> JSValue::toNumberFromPrimitive() const
80{
81 if (isEmpty())
82 return WTF::nullopt;
83 if (isNumber())
84 return asNumber();
85 if (isBoolean())
86 return asBoolean();
87 if (isUndefined())
88 return PNaN;
89 if (isNull())
90 return 0;
91 return WTF::nullopt;
92}
93
94JSObject* JSValue::toObjectSlowCase(ExecState* exec, JSGlobalObject* globalObject) const
95{
96 VM& vm = exec->vm();
97 auto scope = DECLARE_THROW_SCOPE(vm);
98 ASSERT(!isCell());
99
100 if (isInt32() || isDouble())
101 return constructNumber(exec, globalObject, asValue());
102 if (isTrue() || isFalse())
103 return constructBooleanFromImmediateBoolean(exec, globalObject, asValue());
104
105 ASSERT(isUndefinedOrNull());
106 throwException(exec, scope, createNotAnObjectError(exec, *this));
107 return nullptr;
108}
109
110JSValue JSValue::toThisSlowCase(ExecState* exec, ECMAMode ecmaMode) const
111{
112 ASSERT(!isCell());
113
114 if (ecmaMode == StrictMode)
115 return *this;
116
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();
123}
124
125JSObject* JSValue::synthesizePrototype(ExecState* exec) const
126{
127 VM& vm = exec->vm();
128 auto scope = DECLARE_THROW_SCOPE(vm);
129
130 if (isCell()) {
131 if (isString())
132 return exec->lexicalGlobalObject()->stringPrototype();
133 if (isBigInt())
134 return exec->lexicalGlobalObject()->bigIntPrototype();
135 ASSERT(isSymbol());
136 return exec->lexicalGlobalObject()->symbolPrototype();
137 }
138
139 if (isNumber())
140 return exec->lexicalGlobalObject()->numberPrototype();
141 if (isBoolean())
142 return exec->lexicalGlobalObject()->booleanPrototype();
143
144 ASSERT(isUndefinedOrNull());
145 throwException(exec, scope, createNotAnObjectError(exec, *this));
146 return nullptr;
147}
148
149// ECMA 8.7.2
150bool JSValue::putToPrimitive(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
151{
152 VM& vm = exec->vm();
153 auto scope = DECLARE_THROW_SCOPE(vm);
154
155 if (Optional<uint32_t> index = parseIndex(propertyName))
156 RELEASE_AND_RETURN(scope, putToPrimitiveByIndex(exec, index.value(), value, slot.isStrictMode()));
157
158 // Check if there are any setters or getters in the prototype chain
159 JSObject* obj = synthesizePrototype(exec);
160 EXCEPTION_ASSERT(!!scope.exception() == !obj);
161 if (UNLIKELY(!obj))
162 return false;
163 JSValue prototype;
164 if (propertyName != vm.propertyNames->underscoreProto) {
165 for (; !obj->structure(vm)->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj = asObject(prototype)) {
166 prototype = obj->getPrototype(vm, exec);
167 RETURN_IF_EXCEPTION(scope, false);
168
169 if (prototype.isNull())
170 return typeError(exec, scope, slot.isStrictMode(), ReadonlyPropertyWriteError);
171 }
172 }
173
174 for (; ; obj = asObject(prototype)) {
175 unsigned attributes;
176 PropertyOffset offset = obj->structure(vm)->get(vm, propertyName, attributes);
177 if (offset != invalidOffset) {
178 if (attributes & PropertyAttribute::ReadOnly)
179 return typeError(exec, scope, slot.isStrictMode(), ReadonlyPropertyWriteError);
180
181 JSValue gs = obj->getDirect(offset);
182 if (gs.isGetterSetter())
183 RELEASE_AND_RETURN(scope, callSetter(exec, *this, gs, value, slot.isStrictMode() ? StrictMode : NotStrictMode));
184
185 if (gs.isCustomGetterSetter())
186 return callCustomSetter(exec, gs, attributes & PropertyAttribute::CustomAccessor, obj, slot.thisValue(), value);
187
188 // If there's an existing property on the object or one of its
189 // prototypes it should be replaced, so break here.
190 break;
191 }
192
193 prototype = obj->getPrototype(vm, exec);
194 RETURN_IF_EXCEPTION(scope, false);
195 if (prototype.isNull())
196 break;
197 }
198
199 return typeError(exec, scope, slot.isStrictMode(), ReadonlyPropertyWriteError);
200}
201
202bool JSValue::putToPrimitiveByIndex(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
203{
204 VM& vm = exec->vm();
205 auto scope = DECLARE_THROW_SCOPE(vm);
206
207 if (propertyName > MAX_ARRAY_INDEX) {
208 PutPropertySlot slot(*this, shouldThrow);
209 return putToPrimitive(exec, Identifier::from(exec, propertyName), value, slot);
210 }
211
212 JSObject* prototype = synthesizePrototype(exec);
213 EXCEPTION_ASSERT(!!scope.exception() == !prototype);
214 if (UNLIKELY(!prototype))
215 return false;
216 bool putResult = false;
217 bool success = prototype->attemptToInterceptPutByIndexOnHoleForPrototype(exec, *this, propertyName, value, shouldThrow, putResult);
218 RETURN_IF_EXCEPTION(scope, false);
219 if (success)
220 return putResult;
221
222 return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
223}
224
225void JSValue::dump(PrintStream& out) const
226{
227 dumpInContext(out, 0);
228}
229
230void JSValue::dumpInContext(PrintStream& out, DumpContext* context) const
231{
232 dumpInContextAssumingStructure(
233 out, context, (!!*this && isCell()) ? asCell()->structure() : nullptr);
234}
235
236void JSValue::dumpInContextAssumingStructure(
237 PrintStream& out, DumpContext* context, Structure* structure) const
238{
239 if (!*this)
240 out.print("<JSValue()>");
241 else if (isInt32())
242 out.printf("Int32: %d", asInt32());
243 else if (isDouble()) {
244#if USE(JSVALUE64)
245 out.printf("Double: %lld, %lf", (long long)reinterpretDoubleToInt64(asDouble()), asDouble());
246#else
247 union {
248 double asDouble;
249 uint32_t asTwoInt32s[2];
250 } u;
251 u.asDouble = asDouble();
252 out.printf("Double: %08x:%08x, %lf", u.asTwoInt32s[1], u.asTwoInt32s[0], asDouble());
253#endif
254 } else if (isCell()) {
255 if (structure->classInfo()->isSubClassOf(JSString::info())) {
256 JSString* string = asString(asCell());
257 out.print("String");
258 if (string->isRope())
259 out.print(" (rope)");
260 const StringImpl* impl = string->tryGetValueImpl();
261 if (impl) {
262 if (impl->isAtom())
263 out.print(" (atomic)");
264 if (impl->isAtom())
265 out.print(" (identifier)");
266 if (impl->isSymbol())
267 out.print(" (symbol)");
268 } else
269 out.print(" (unresolved)");
270 out.print(": ", impl);
271 } else if (structure->classInfo()->isSubClassOf(RegExp::info()))
272 out.print("RegExp: ", *jsCast<RegExp*>(asCell()));
273 else if (structure->classInfo()->isSubClassOf(Symbol::info()))
274 out.print("Symbol: ", RawPointer(asCell()));
275 else if (structure->classInfo()->isSubClassOf(Structure::info()))
276 out.print("Structure: ", inContext(*jsCast<Structure*>(asCell()), context));
277 else if (structure->classInfo()->isSubClassOf(JSObject::info())) {
278 out.print("Object: ", RawPointer(asCell()));
279 out.print(" with butterfly ", RawPointer(asObject(asCell())->butterfly()));
280 out.print(" (Structure ", inContext(*structure, context), ")");
281 } else {
282 out.print("Cell: ", RawPointer(asCell()));
283 out.print(" (", inContext(*structure, context), ")");
284 }
285#if USE(JSVALUE64)
286 out.print(", StructureID: ", asCell()->structureID());
287#endif
288 } else if (isTrue())
289 out.print("True");
290 else if (isFalse())
291 out.print("False");
292 else if (isNull())
293 out.print("Null");
294 else if (isUndefined())
295 out.print("Undefined");
296 else
297 out.print("INVALID");
298}
299
300void JSValue::dumpForBacktrace(PrintStream& out) const
301{
302 if (!*this)
303 out.print("<JSValue()>");
304 else if (isInt32())
305 out.printf("%d", asInt32());
306 else if (isDouble())
307 out.printf("%lf", asDouble());
308 else if (isCell()) {
309 VM& vm = *asCell()->vm();
310 if (asCell()->inherits<JSString>(vm)) {
311 JSString* string = asString(asCell());
312 const StringImpl* impl = string->tryGetValueImpl();
313 if (impl)
314 out.print("\"", impl, "\"");
315 else
316 out.print("(unresolved string)");
317 } else if (asCell()->inherits<Structure>(vm)) {
318 out.print("Structure[ ", asCell()->structure()->classInfo()->className);
319#if USE(JSVALUE64)
320 out.print(" ID: ", asCell()->structureID());
321#endif
322 out.print("]: ", RawPointer(asCell()));
323 } else {
324 out.print("Cell[", asCell()->structure()->classInfo()->className);
325#if USE(JSVALUE64)
326 out.print(" ID: ", asCell()->structureID());
327#endif
328 out.print("]: ", RawPointer(asCell()));
329 }
330 } else if (isTrue())
331 out.print("True");
332 else if (isFalse())
333 out.print("False");
334 else if (isNull())
335 out.print("Null");
336 else if (isUndefined())
337 out.print("Undefined");
338 else
339 out.print("INVALID");
340}
341
342bool JSValue::isValidCallee()
343{
344 return asObject(asCell())->globalObject();
345}
346
347JSString* JSValue::toStringSlowCase(ExecState* exec, bool returnEmptyStringOnError) const
348{
349 VM& vm = exec->vm();
350 auto scope = DECLARE_THROW_SCOPE(vm);
351
352 auto errorValue = [&] () -> JSString* {
353 if (returnEmptyStringOnError)
354 return jsEmptyString(exec);
355 return nullptr;
356 };
357
358 ASSERT(!isString());
359 if (isInt32()) {
360 auto integer = asInt32();
361 if (static_cast<unsigned>(integer) <= 9)
362 return vm.smallStrings.singleCharacterString(integer + '0');
363 return jsNontrivialString(&vm, vm.numericStrings.add(integer));
364 }
365 if (isDouble())
366 return jsString(&vm, vm.numericStrings.add(asDouble()));
367 if (isTrue())
368 return vm.smallStrings.trueString();
369 if (isFalse())
370 return vm.smallStrings.falseString();
371 if (isNull())
372 return vm.smallStrings.nullString();
373 if (isUndefined())
374 return vm.smallStrings.undefinedString();
375 if (isSymbol()) {
376 throwTypeError(exec, scope, "Cannot convert a symbol to a string"_s);
377 return errorValue();
378 }
379 if (isBigInt()) {
380 JSBigInt* bigInt = asBigInt(*this);
381 if (auto digit = bigInt->singleDigitValueForString())
382 return vm.smallStrings.singleCharacterString(*digit + '0');
383 JSString* returnString = jsNontrivialString(&vm, bigInt->toString(exec, 10));
384 RETURN_IF_EXCEPTION(scope, errorValue());
385 return returnString;
386 }
387
388 ASSERT(isCell());
389 JSValue value = asCell()->toPrimitive(exec, PreferString);
390 RETURN_IF_EXCEPTION(scope, errorValue());
391 ASSERT(!value.isObject());
392 JSString* result = value.toString(exec);
393 RETURN_IF_EXCEPTION(scope, errorValue());
394 return result;
395}
396
397String JSValue::toWTFStringSlowCase(ExecState* exec) const
398{
399 VM& vm = exec->vm();
400 if (isInt32())
401 return vm.numericStrings.add(asInt32());
402 if (isDouble())
403 return vm.numericStrings.add(asDouble());
404 if (isTrue())
405 return vm.propertyNames->trueKeyword.string();
406 if (isFalse())
407 return vm.propertyNames->falseKeyword.string();
408 if (isNull())
409 return vm.propertyNames->nullKeyword.string();
410 if (isUndefined())
411 return vm.propertyNames->undefinedKeyword.string();
412 return toString(exec)->value(exec);
413}
414
415} // namespace JSC
416