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