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 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 "JSCJSValueInlines.h"
33 #include "JSFunction.h"
34 #include "JSGlobalObject.h"
35 #include "JSNotAnObject.h"
36 #include "NumberObject.h"
37 #include "StructureInlines.h"
38 #include <wtf/MathExtras.h>
39 #include <wtf/StringExtras.h>
44 double JSValue::toInteger(ExecState
* exec
) const
48 double d
= toNumber(exec
);
49 return std::isnan(d
) ? 0.0 : trunc(d
);
52 double JSValue::toIntegerPreserveNaN(ExecState
* exec
) const
56 return trunc(toNumber(exec
));
59 double JSValue::toNumberSlowCase(ExecState
* exec
) const
61 ASSERT(!isInt32() && !isDouble());
63 return asCell()->toNumber(exec
);
66 return isUndefined() ? PNaN
: 0; // null and false both convert to 0.
69 JSObject
* JSValue::toObjectSlowCase(ExecState
* exec
, JSGlobalObject
* globalObject
) const
73 if (isInt32() || isDouble())
74 return constructNumber(exec
, globalObject
, asValue());
75 if (isTrue() || isFalse())
76 return constructBooleanFromImmediateBoolean(exec
, globalObject
, asValue());
78 ASSERT(isUndefinedOrNull());
80 vm
.throwException(exec
, createNotAnObjectError(exec
, *this));
81 return JSNotAnObject::create(vm
);
84 JSValue
JSValue::toThisSlowCase(ExecState
* exec
, ECMAMode ecmaMode
) const
88 if (ecmaMode
== StrictMode
)
91 if (isInt32() || isDouble())
92 return constructNumber(exec
, exec
->lexicalGlobalObject(), asValue());
93 if (isTrue() || isFalse())
94 return constructBooleanFromImmediateBoolean(exec
, exec
->lexicalGlobalObject(), asValue());
95 ASSERT(isUndefinedOrNull());
96 return exec
->globalThisValue();
99 JSObject
* JSValue::synthesizePrototype(ExecState
* exec
) const
103 return exec
->lexicalGlobalObject()->stringPrototype();
105 return exec
->lexicalGlobalObject()->symbolPrototype();
109 return exec
->lexicalGlobalObject()->numberPrototype();
111 return exec
->lexicalGlobalObject()->booleanPrototype();
113 ASSERT(isUndefinedOrNull());
115 vm
.throwException(exec
, createNotAnObjectError(exec
, *this));
116 return JSNotAnObject::create(vm
);
120 void JSValue::putToPrimitive(ExecState
* exec
, PropertyName propertyName
, JSValue value
, PutPropertySlot
& slot
)
124 if (Optional
<uint32_t> index
= parseIndex(propertyName
)) {
125 putToPrimitiveByIndex(exec
, index
.value(), value
, slot
.isStrictMode());
129 // Check if there are any setters or getters in the prototype chain
130 JSObject
* obj
= synthesizePrototype(exec
);
132 if (propertyName
!= exec
->propertyNames().underscoreProto
) {
133 for (; !obj
->structure()->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj
= asObject(prototype
)) {
134 prototype
= obj
->prototype();
135 if (prototype
.isNull()) {
136 if (slot
.isStrictMode())
137 throwTypeError(exec
, StrictModeReadonlyPropertyWriteError
);
143 for (; ; obj
= asObject(prototype
)) {
145 PropertyOffset offset
= obj
->structure()->get(vm
, propertyName
, attributes
);
146 if (offset
!= invalidOffset
) {
147 if (attributes
& ReadOnly
) {
148 if (slot
.isStrictMode())
149 exec
->vm().throwException(exec
, createTypeError(exec
, StrictModeReadonlyPropertyWriteError
));
153 JSValue gs
= obj
->getDirect(offset
);
154 if (gs
.isGetterSetter()) {
155 callSetter(exec
, *this, gs
, value
, slot
.isStrictMode() ? StrictMode
: NotStrictMode
);
159 if (gs
.isCustomGetterSetter()) {
160 callCustomSetter(exec
, gs
, obj
, slot
.thisValue(), value
);
164 // If there's an existing property on the object or one of its
165 // prototypes it should be replaced, so break here.
169 prototype
= obj
->prototype();
170 if (prototype
.isNull())
174 if (slot
.isStrictMode())
175 throwTypeError(exec
, StrictModeReadonlyPropertyWriteError
);
179 void JSValue::putToPrimitiveByIndex(ExecState
* exec
, unsigned propertyName
, JSValue value
, bool shouldThrow
)
181 if (propertyName
> MAX_ARRAY_INDEX
) {
182 PutPropertySlot
slot(*this, shouldThrow
);
183 putToPrimitive(exec
, Identifier::from(exec
, propertyName
), value
, slot
);
187 if (synthesizePrototype(exec
)->attemptToInterceptPutByIndexOnHoleForPrototype(exec
, *this, propertyName
, value
, shouldThrow
))
191 throwTypeError(exec
, StrictModeReadonlyPropertyWriteError
);
194 void JSValue::dump(PrintStream
& out
) const
196 dumpInContext(out
, 0);
199 void JSValue::dumpInContext(PrintStream
& out
, DumpContext
* context
) const
201 dumpInContextAssumingStructure(
202 out
, context
, (!!*this && isCell()) ? asCell()->structure() : nullptr);
205 void JSValue::dumpInContextAssumingStructure(
206 PrintStream
& out
, DumpContext
* context
, Structure
* structure
) const
209 out
.print("<JSValue()>");
211 out
.printf("Int32: %d", asInt32());
212 else if (isDouble()) {
214 out
.printf("Double: %lld, %lf", (long long)reinterpretDoubleToInt64(asDouble()), asDouble());
218 uint32_t asTwoInt32s
[2];
220 u
.asDouble
= asDouble();
221 out
.printf("Double: %08x:%08x, %lf", u
.asTwoInt32s
[1], u
.asTwoInt32s
[0], asDouble());
223 } else if (isCell()) {
224 if (structure
->classInfo()->isSubClassOf(JSString::info())) {
225 JSString
* string
= jsCast
<JSString
*>(asCell());
227 if (string
->isRope())
228 out
.print(" (rope)");
229 const StringImpl
* impl
= string
->tryGetValueImpl();
231 if (impl
->isAtomic())
232 out
.print(" (atomic)");
233 if (impl
->isAtomic())
234 out
.print(" (identifier)");
235 if (impl
->isSymbol())
236 out
.print(" (symbol)");
238 out
.print(" (unresolved)");
239 out
.print(": ", impl
);
240 } else if (structure
->classInfo()->isSubClassOf(Structure::info()))
241 out
.print("Structure: ", inContext(*jsCast
<Structure
*>(asCell()), context
));
243 out
.print("Cell: ", RawPointer(asCell()));
244 out
.print(" (", inContext(*structure
, context
), ")");
247 out
.print(", ID: ", asCell()->structureID());
255 else if (isUndefined())
256 out
.print("Undefined");
258 out
.print("INVALID");
261 void JSValue::dumpForBacktrace(PrintStream
& out
) const
264 out
.print("<JSValue()>");
266 out
.printf("%d", asInt32());
268 out
.printf("%lf", asDouble());
270 if (asCell()->inherits(JSString::info())) {
271 JSString
* string
= jsCast
<JSString
*>(asCell());
272 const StringImpl
* impl
= string
->tryGetValueImpl();
274 out
.print("\"", impl
, "\"");
276 out
.print("(unresolved string)");
277 } else if (asCell()->inherits(Structure::info())) {
278 out
.print("Structure[ ", asCell()->structure()->classInfo()->className
);
280 out
.print(" ID: ", asCell()->structureID());
282 out
.print("]: ", RawPointer(asCell()));
284 out
.print("Cell[", asCell()->structure()->classInfo()->className
);
286 out
.print(" ID: ", asCell()->structureID());
288 out
.print("]: ", RawPointer(asCell()));
296 else if (isUndefined())
297 out
.print("Undefined");
299 out
.print("INVALID");
302 // This in the ToInt32 operation is defined in section 9.5 of the ECMA-262 spec.
303 // Note that this operation is identical to ToUInt32 other than to interpretation
304 // of the resulting bit-pattern (as such this metod is also called to implement
307 // The operation can be descibed as round towards zero, then select the 32 least
308 // bits of the resulting value in 2s-complement representation.
309 int32_t toInt32(double number
)
311 int64_t bits
= WTF::bitwise_cast
<int64_t>(number
);
312 int32_t exp
= (static_cast<int32_t>(bits
>> 52) & 0x7ff) - 0x3ff;
314 // If exponent < 0 there will be no bits to the left of the decimal point
315 // after rounding; if the exponent is > 83 then no bits of precision can be
316 // left in the low 32-bit range of the result (IEEE-754 doubles have 52 bits
317 // of fractional precision).
318 // Note this case handles 0, -0, and all infinte, NaN, & denormal value.
319 if (exp
< 0 || exp
> 83)
322 // Select the appropriate 32-bits from the floating point mantissa. If the
323 // exponent is 52 then the bits we need to select are already aligned to the
324 // lowest bits of the 64-bit integer representation of tghe number, no need
325 // to shift. If the exponent is greater than 52 we need to shift the value
326 // left by (exp - 52), if the value is less than 52 we need to shift right
328 int32_t result
= (exp
> 52)
329 ? static_cast<int32_t>(bits
<< (exp
- 52))
330 : static_cast<int32_t>(bits
>> (52 - exp
));
332 // IEEE-754 double precision values are stored omitting an implicit 1 before
333 // the decimal point; we need to reinsert this now. We may also the shifted
334 // invalid bits into the result that are not a part of the mantissa (the sign
335 // and exponent bits from the floatingpoint representation); mask these out.
337 int32_t missingOne
= 1 << exp
;
338 result
&= missingOne
- 1;
339 result
+= missingOne
;
342 // If the input value was negative (we could test either 'number' or 'bits',
343 // but testing 'bits' is likely faster) invert the result appropriately.
344 return bits
< 0 ? -result
: result
;
347 bool JSValue::isValidCallee()
349 return asObject(asCell())->globalObject();
352 JSString
* JSValue::toStringSlowCase(ExecState
* exec
) const
357 auto integer
= asInt32();
358 if (static_cast<unsigned>(integer
) <= 9)
359 return vm
.smallStrings
.singleCharacterString(integer
+ '0');
360 return jsNontrivialString(&vm
, vm
.numericStrings
.add(integer
));
363 return jsString(&vm
, vm
.numericStrings
.add(asDouble()));
365 return vm
.smallStrings
.trueString();
367 return vm
.smallStrings
.falseString();
369 return vm
.smallStrings
.nullString();
371 return vm
.smallStrings
.undefinedString();
373 throwTypeError(exec
);
374 return jsEmptyString(exec
);
378 JSValue value
= asCell()->toPrimitive(exec
, PreferString
);
379 if (exec
->hadException())
380 return jsEmptyString(exec
);
381 ASSERT(!value
.isObject());
382 return value
.toString(exec
);
385 String
JSValue::toWTFStringSlowCase(ExecState
* exec
) const
389 return vm
.numericStrings
.add(asInt32());
391 return vm
.numericStrings
.add(asDouble());
393 return vm
.propertyNames
->trueKeyword
.string();
395 return vm
.propertyNames
->falseKeyword
.string();
397 return vm
.propertyNames
->nullKeyword
.string();
399 return vm
.propertyNames
->undefinedKeyword
.string();
400 return toString(exec
)->value(exec
);