]>
Commit | Line | Data |
---|---|---|
9dae56ea A |
1 | /* |
2 | * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) | |
3 | * Copyright (C) 2001 Peter Kelly (pmk@post.com) | |
93a37866 | 4 | * Copyright (C) 2003, 2007, 2008, 2012 Apple Inc. All rights reserved. |
9dae56ea A |
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" | |
93a37866 | 24 | #include "JSCJSValue.h" |
9dae56ea | 25 | |
ba379fdc A |
26 | #include "BooleanConstructor.h" |
27 | #include "BooleanPrototype.h" | |
14957cd0 | 28 | #include "Error.h" |
ba379fdc | 29 | #include "ExceptionHelpers.h" |
6fe7ccc8 | 30 | #include "GetterSetter.h" |
93a37866 | 31 | #include "JSCJSValueInlines.h" |
9dae56ea | 32 | #include "JSFunction.h" |
93a37866 | 33 | #include "JSGlobalObject.h" |
ba379fdc A |
34 | #include "JSNotAnObject.h" |
35 | #include "NumberObject.h" | |
9dae56ea | 36 | #include <wtf/MathExtras.h> |
ba379fdc | 37 | #include <wtf/StringExtras.h> |
9dae56ea A |
38 | |
39 | namespace JSC { | |
40 | ||
41 | static const double D32 = 4294967296.0; | |
42 | ||
43 | // ECMA 9.4 | |
ba379fdc | 44 | double JSValue::toInteger(ExecState* exec) const |
9dae56ea | 45 | { |
ba379fdc A |
46 | if (isInt32()) |
47 | return asInt32(); | |
9dae56ea | 48 | double d = toNumber(exec); |
93a37866 | 49 | return std::isnan(d) ? 0.0 : trunc(d); |
9dae56ea A |
50 | } |
51 | ||
ba379fdc | 52 | double JSValue::toIntegerPreserveNaN(ExecState* exec) const |
9dae56ea | 53 | { |
ba379fdc A |
54 | if (isInt32()) |
55 | return asInt32(); | |
9dae56ea A |
56 | return trunc(toNumber(exec)); |
57 | } | |
58 | ||
6fe7ccc8 A |
59 | double JSValue::toNumberSlowCase(ExecState* exec) const |
60 | { | |
61 | ASSERT(!isInt32() && !isDouble()); | |
62 | if (isCell()) | |
63 | return asCell()->toNumber(exec); | |
64 | if (isTrue()) | |
65 | return 1.0; | |
93a37866 | 66 | return isUndefined() ? QNaN : 0; // null and false both convert to 0. |
6fe7ccc8 A |
67 | } |
68 | ||
14957cd0 | 69 | JSObject* JSValue::toObjectSlowCase(ExecState* exec, JSGlobalObject* globalObject) const |
ba379fdc A |
70 | { |
71 | ASSERT(!isCell()); | |
72 | ||
73 | if (isInt32() || isDouble()) | |
14957cd0 | 74 | return constructNumber(exec, globalObject, asValue()); |
ba379fdc | 75 | if (isTrue() || isFalse()) |
14957cd0 A |
76 | return constructBooleanFromImmediateBoolean(exec, globalObject, asValue()); |
77 | ||
ba379fdc | 78 | ASSERT(isUndefinedOrNull()); |
14957cd0 | 79 | throwError(exec, createNotAnObjectError(exec, *this)); |
6fe7ccc8 | 80 | return JSNotAnObject::create(exec); |
ba379fdc A |
81 | } |
82 | ||
83 | JSObject* JSValue::toThisObjectSlowCase(ExecState* exec) const | |
84 | { | |
85 | ASSERT(!isCell()); | |
86 | ||
87 | if (isInt32() || isDouble()) | |
14957cd0 | 88 | return constructNumber(exec, exec->lexicalGlobalObject(), asValue()); |
ba379fdc | 89 | if (isTrue() || isFalse()) |
14957cd0 | 90 | return constructBooleanFromImmediateBoolean(exec, exec->lexicalGlobalObject(), asValue()); |
ba379fdc A |
91 | ASSERT(isUndefinedOrNull()); |
92 | return exec->globalThisValue(); | |
93 | } | |
94 | ||
6fe7ccc8 | 95 | JSObject* JSValue::synthesizePrototype(ExecState* exec) const |
ba379fdc | 96 | { |
6fe7ccc8 A |
97 | if (isCell()) { |
98 | ASSERT(isString()); | |
99 | return exec->lexicalGlobalObject()->stringPrototype(); | |
100 | } | |
101 | ||
ba379fdc | 102 | if (isNumber()) |
6fe7ccc8 | 103 | return exec->lexicalGlobalObject()->numberPrototype(); |
ba379fdc | 104 | if (isBoolean()) |
6fe7ccc8 | 105 | return exec->lexicalGlobalObject()->booleanPrototype(); |
14957cd0 A |
106 | |
107 | ASSERT(isUndefinedOrNull()); | |
108 | throwError(exec, createNotAnObjectError(exec, *this)); | |
6fe7ccc8 | 109 | return JSNotAnObject::create(exec); |
ba379fdc A |
110 | } |
111 | ||
6fe7ccc8 | 112 | // ECMA 8.7.2 |
93a37866 | 113 | void JSValue::putToPrimitive(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) |
ba379fdc | 114 | { |
93a37866 A |
115 | VM& vm = exec->vm(); |
116 | ||
117 | unsigned index = propertyName.asIndex(); | |
118 | if (index != PropertyName::NotAnIndex) { | |
119 | putToPrimitiveByIndex(exec, index, value, slot.isStrictMode()); | |
120 | return; | |
121 | } | |
ba379fdc | 122 | |
6fe7ccc8 A |
123 | // Check if there are any setters or getters in the prototype chain |
124 | JSObject* obj = synthesizePrototype(exec); | |
125 | JSValue prototype; | |
126 | if (propertyName != exec->propertyNames().underscoreProto) { | |
127 | for (; !obj->structure()->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj = asObject(prototype)) { | |
128 | prototype = obj->prototype(); | |
129 | if (prototype.isNull()) { | |
130 | if (slot.isStrictMode()) | |
131 | throwTypeError(exec, StrictModeReadonlyPropertyWriteError); | |
132 | return; | |
133 | } | |
134 | } | |
135 | } | |
136 | ||
137 | for (; ; obj = asObject(prototype)) { | |
138 | unsigned attributes; | |
139 | JSCell* specificValue; | |
93a37866 A |
140 | PropertyOffset offset = obj->structure()->get(vm, propertyName, attributes, specificValue); |
141 | if (offset != invalidOffset) { | |
6fe7ccc8 A |
142 | if (attributes & ReadOnly) { |
143 | if (slot.isStrictMode()) | |
144 | throwError(exec, createTypeError(exec, StrictModeReadonlyPropertyWriteError)); | |
145 | return; | |
146 | } | |
147 | ||
93a37866 | 148 | JSValue gs = obj->getDirect(offset); |
6fe7ccc8 A |
149 | if (gs.isGetterSetter()) { |
150 | JSObject* setterFunc = asGetterSetter(gs)->setter(); | |
151 | if (!setterFunc) { | |
152 | if (slot.isStrictMode()) | |
93a37866 | 153 | throwError(exec, createTypeError(exec, ASCIILiteral("setting a property that has only a getter"))); |
6fe7ccc8 A |
154 | return; |
155 | } | |
156 | ||
157 | CallData callData; | |
158 | CallType callType = setterFunc->methodTable()->getCallData(setterFunc, callData); | |
159 | MarkedArgumentBuffer args; | |
160 | args.append(value); | |
161 | ||
162 | // If this is WebCore's global object then we need to substitute the shell. | |
163 | call(exec, setterFunc, callType, callData, *this, args); | |
164 | return; | |
165 | } | |
166 | ||
167 | // If there's an existing property on the object or one of its | |
168 | // prototypes it should be replaced, so break here. | |
169 | break; | |
170 | } | |
171 | ||
172 | prototype = obj->prototype(); | |
173 | if (prototype.isNull()) | |
174 | break; | |
175 | } | |
176 | ||
177 | if (slot.isStrictMode()) | |
178 | throwTypeError(exec, StrictModeReadonlyPropertyWriteError); | |
179 | return; | |
ba379fdc A |
180 | } |
181 | ||
93a37866 | 182 | void JSValue::putToPrimitiveByIndex(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) |
ba379fdc | 183 | { |
93a37866 A |
184 | if (propertyName > MAX_ARRAY_INDEX) { |
185 | PutPropertySlot slot(shouldThrow); | |
186 | putToPrimitive(exec, Identifier::from(exec, propertyName), value, slot); | |
187 | return; | |
188 | } | |
189 | ||
190 | if (synthesizePrototype(exec)->attemptToInterceptPutByIndexOnHoleForPrototype(exec, *this, propertyName, value, shouldThrow)) | |
191 | return; | |
192 | ||
193 | if (shouldThrow) | |
194 | throwTypeError(exec, StrictModeReadonlyPropertyWriteError); | |
195 | } | |
f9bf01c6 | 196 | |
93a37866 A |
197 | void JSValue::dump(PrintStream& out) const |
198 | { | |
f9bf01c6 | 199 | if (!*this) |
93a37866 | 200 | out.print("<JSValue()>"); |
f9bf01c6 | 201 | else if (isInt32()) |
93a37866 | 202 | out.printf("Int32: %d", asInt32()); |
6fe7ccc8 A |
203 | else if (isDouble()) { |
204 | #if USE(JSVALUE64) | |
93a37866 | 205 | out.printf("Double: %lld, %lf", (long long)reinterpretDoubleToInt64(asDouble()), asDouble()); |
6fe7ccc8 A |
206 | #else |
207 | union { | |
208 | double asDouble; | |
209 | uint32_t asTwoInt32s[2]; | |
210 | } u; | |
211 | u.asDouble = asDouble(); | |
93a37866 | 212 | out.printf("Double: %08x:%08x, %lf", u.asTwoInt32s[1], u.asTwoInt32s[0], asDouble()); |
6fe7ccc8 | 213 | #endif |
93a37866 A |
214 | } else if (isCell()) { |
215 | if (asCell()->inherits(&JSString::s_info)) { | |
216 | JSString* string = jsCast<JSString*>(asCell()); | |
217 | out.print("String: "); | |
218 | if (string->isRope()) | |
219 | out.print("(rope) "); | |
220 | out.print(string->tryGetValue()); | |
221 | } else if (asCell()->inherits(&Structure::s_info)) { | |
222 | Structure* structure = jsCast<Structure*>(asCell()); | |
223 | out.print( | |
224 | "Structure: ", RawPointer(structure), ": ", structure->classInfo()->className, | |
225 | ", ", IndexingTypeDump(structure->indexingTypeIncludingHistory())); | |
226 | } else { | |
227 | out.print("Cell: ", RawPointer(asCell())); | |
228 | if (isObject() && asObject(*this)->butterfly()) | |
229 | out.print("->", RawPointer(asObject(*this)->butterfly())); | |
230 | out.print( | |
231 | " (", RawPointer(asCell()->structure()), ": ", asCell()->structure()->classInfo()->className, | |
232 | ", ", IndexingTypeDump(asCell()->structure()->indexingTypeIncludingHistory()), ")"); | |
233 | } | |
234 | } else if (isTrue()) | |
235 | out.print("True"); | |
ba379fdc | 236 | else if (isFalse()) |
93a37866 | 237 | out.print("False"); |
ba379fdc | 238 | else if (isNull()) |
93a37866 | 239 | out.print("Null"); |
14957cd0 | 240 | else if (isUndefined()) |
93a37866 | 241 | out.print("Undefined"); |
14957cd0 | 242 | else |
93a37866 | 243 | out.print("INVALID"); |
ba379fdc | 244 | } |
ba379fdc | 245 | |
14957cd0 A |
246 | // This in the ToInt32 operation is defined in section 9.5 of the ECMA-262 spec. |
247 | // Note that this operation is identical to ToUInt32 other than to interpretation | |
248 | // of the resulting bit-pattern (as such this metod is also called to implement | |
249 | // ToUInt32). | |
250 | // | |
251 | // The operation can be descibed as round towards zero, then select the 32 least | |
252 | // bits of the resulting value in 2s-complement representation. | |
253 | int32_t toInt32(double number) | |
9dae56ea | 254 | { |
14957cd0 A |
255 | int64_t bits = WTF::bitwise_cast<int64_t>(number); |
256 | int32_t exp = (static_cast<int32_t>(bits >> 52) & 0x7ff) - 0x3ff; | |
257 | ||
258 | // If exponent < 0 there will be no bits to the left of the decimal point | |
259 | // after rounding; if the exponent is > 83 then no bits of precision can be | |
260 | // left in the low 32-bit range of the result (IEEE-754 doubles have 52 bits | |
261 | // of fractional precision). | |
262 | // Note this case handles 0, -0, and all infinte, NaN, & denormal value. | |
263 | if (exp < 0 || exp > 83) | |
9dae56ea | 264 | return 0; |
9dae56ea | 265 | |
14957cd0 A |
266 | // Select the appropriate 32-bits from the floating point mantissa. If the |
267 | // exponent is 52 then the bits we need to select are already aligned to the | |
268 | // lowest bits of the 64-bit integer representation of tghe number, no need | |
269 | // to shift. If the exponent is greater than 52 we need to shift the value | |
270 | // left by (exp - 52), if the value is less than 52 we need to shift right | |
271 | // accordingly. | |
272 | int32_t result = (exp > 52) | |
273 | ? static_cast<int32_t>(bits << (exp - 52)) | |
274 | : static_cast<int32_t>(bits >> (52 - exp)); | |
275 | ||
276 | // IEEE-754 double precision values are stored omitting an implicit 1 before | |
277 | // the decimal point; we need to reinsert this now. We may also the shifted | |
278 | // invalid bits into the result that are not a part of the mantissa (the sign | |
279 | // and exponent bits from the floatingpoint representation); mask these out. | |
280 | if (exp < 32) { | |
281 | int32_t missingOne = 1 << exp; | |
282 | result &= missingOne - 1; | |
283 | result += missingOne; | |
9dae56ea A |
284 | } |
285 | ||
14957cd0 A |
286 | // If the input value was negative (we could test either 'number' or 'bits', |
287 | // but testing 'bits' is likely faster) invert the result appropriately. | |
288 | return bits < 0 ? -result : result; | |
9dae56ea A |
289 | } |
290 | ||
6fe7ccc8 | 291 | bool JSValue::isValidCallee() |
ba379fdc | 292 | { |
6fe7ccc8 | 293 | return asObject(asCell())->globalObject(); |
ba379fdc A |
294 | } |
295 | ||
6fe7ccc8 A |
296 | JSString* JSValue::toStringSlowCase(ExecState* exec) const |
297 | { | |
93a37866 | 298 | VM& vm = exec->vm(); |
6fe7ccc8 A |
299 | ASSERT(!isString()); |
300 | if (isInt32()) | |
93a37866 | 301 | return jsString(&vm, vm.numericStrings.add(asInt32())); |
6fe7ccc8 | 302 | if (isDouble()) |
93a37866 | 303 | return jsString(&vm, vm.numericStrings.add(asDouble())); |
6fe7ccc8 | 304 | if (isTrue()) |
93a37866 | 305 | return vm.smallStrings.trueString(); |
6fe7ccc8 | 306 | if (isFalse()) |
93a37866 | 307 | return vm.smallStrings.falseString(); |
6fe7ccc8 | 308 | if (isNull()) |
93a37866 | 309 | return vm.smallStrings.nullString(); |
6fe7ccc8 | 310 | if (isUndefined()) |
93a37866 | 311 | return vm.smallStrings.undefinedString(); |
6fe7ccc8 A |
312 | |
313 | ASSERT(isCell()); | |
314 | JSValue value = asCell()->toPrimitive(exec, PreferString); | |
315 | if (exec->hadException()) | |
316 | return jsEmptyString(exec); | |
317 | ASSERT(!value.isObject()); | |
318 | return value.toString(exec); | |
319 | } | |
320 | ||
93a37866 | 321 | String JSValue::toWTFStringSlowCase(ExecState* exec) const |
14957cd0 | 322 | { |
93a37866 | 323 | return inlineJSValueNotStringtoString(*this, exec); |
14957cd0 A |
324 | } |
325 | ||
9dae56ea | 326 | } // namespace JSC |