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 | |
39 | namespace JSC { |
40 | |
41 | // ECMA 9.4 |
42 | double 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 | |
50 | double JSValue::toIntegerPreserveNaN(ExecState* exec) const |
51 | { |
52 | if (isInt32()) |
53 | return asInt32(); |
54 | return trunc(toNumber(exec)); |
55 | } |
56 | |
57 | double 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 | |
69 | double 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 | |
79 | Optional<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 | |
94 | JSObject* 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 | |
110 | JSValue 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 | |
125 | JSObject* 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 |
150 | bool 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 | |
202 | bool 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 | |
225 | void JSValue::dump(PrintStream& out) const |
226 | { |
227 | dumpInContext(out, 0); |
228 | } |
229 | |
230 | void JSValue::dumpInContext(PrintStream& out, DumpContext* context) const |
231 | { |
232 | dumpInContextAssumingStructure( |
233 | out, context, (!!*this && isCell()) ? asCell()->structure() : nullptr); |
234 | } |
235 | |
236 | void 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 | |
300 | void 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 | |
342 | bool JSValue::isValidCallee() |
343 | { |
344 | return asObject(asCell())->globalObject(); |
345 | } |
346 | |
347 | JSString* 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 | |
397 | String 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 | |