1 | /* |
2 | * Copyright (C) 2011-2017 Apple Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #pragma once |
27 | |
28 | #include "CatchScope.h" |
29 | #include "Error.h" |
30 | #include "ExceptionHelpers.h" |
31 | #include "Identifier.h" |
32 | #include "InternalFunction.h" |
33 | #include "JSBigInt.h" |
34 | #include "JSCJSValue.h" |
35 | #include "JSCellInlines.h" |
36 | #include "JSFunction.h" |
37 | #include "JSObject.h" |
38 | #include "JSProxy.h" |
39 | #include "JSStringInlines.h" |
40 | #include "MathCommon.h" |
41 | #include <wtf/Variant.h> |
42 | #include <wtf/text/StringImpl.h> |
43 | |
44 | namespace JSC { |
45 | |
46 | ALWAYS_INLINE int32_t JSValue::toInt32(ExecState* exec) const |
47 | { |
48 | if (isInt32()) |
49 | return asInt32(); |
50 | return JSC::toInt32(toNumber(exec)); |
51 | } |
52 | |
53 | inline uint32_t JSValue::toUInt32(ExecState* exec) const |
54 | { |
55 | // See comment on JSC::toUInt32, in JSCJSValue.h. |
56 | return toInt32(exec); |
57 | } |
58 | |
59 | inline uint32_t JSValue::toIndex(ExecState* exec, const char* errorName) const |
60 | { |
61 | VM& vm = exec->vm(); |
62 | auto scope = DECLARE_THROW_SCOPE(vm); |
63 | |
64 | double d = toNumber(exec); |
65 | RETURN_IF_EXCEPTION(scope, 0); |
66 | if (d <= -1) { |
67 | throwException(exec, scope, createRangeError(exec, makeString(errorName, " cannot be negative" ))); |
68 | return 0; |
69 | } |
70 | if (d > std::numeric_limits<unsigned>::max()) { |
71 | throwException(exec, scope, createRangeError(exec, makeString(errorName, " too large" ))); |
72 | return 0; |
73 | } |
74 | |
75 | if (isInt32()) |
76 | return asInt32(); |
77 | RELEASE_AND_RETURN(scope, JSC::toInt32(d)); |
78 | } |
79 | |
80 | inline bool JSValue::isUInt32() const |
81 | { |
82 | return isInt32() && asInt32() >= 0; |
83 | } |
84 | |
85 | inline uint32_t JSValue::asUInt32() const |
86 | { |
87 | ASSERT(isUInt32()); |
88 | return asInt32(); |
89 | } |
90 | |
91 | inline double JSValue::asNumber() const |
92 | { |
93 | ASSERT(isNumber()); |
94 | return isInt32() ? asInt32() : asDouble(); |
95 | } |
96 | |
97 | inline JSValue jsNaN() |
98 | { |
99 | return JSValue(JSValue::EncodeAsDouble, PNaN); |
100 | } |
101 | |
102 | inline JSValue::JSValue(char i) |
103 | { |
104 | *this = JSValue(static_cast<int32_t>(i)); |
105 | } |
106 | |
107 | inline JSValue::JSValue(unsigned char i) |
108 | { |
109 | *this = JSValue(static_cast<int32_t>(i)); |
110 | } |
111 | |
112 | inline JSValue::JSValue(short i) |
113 | { |
114 | *this = JSValue(static_cast<int32_t>(i)); |
115 | } |
116 | |
117 | inline JSValue::JSValue(unsigned short i) |
118 | { |
119 | *this = JSValue(static_cast<int32_t>(i)); |
120 | } |
121 | |
122 | inline JSValue::JSValue(unsigned i) |
123 | { |
124 | if (static_cast<int32_t>(i) < 0) { |
125 | *this = JSValue(EncodeAsDouble, static_cast<double>(i)); |
126 | return; |
127 | } |
128 | *this = JSValue(static_cast<int32_t>(i)); |
129 | } |
130 | |
131 | inline JSValue::JSValue(long i) |
132 | { |
133 | if (static_cast<int32_t>(i) != i) { |
134 | *this = JSValue(EncodeAsDouble, static_cast<double>(i)); |
135 | return; |
136 | } |
137 | *this = JSValue(static_cast<int32_t>(i)); |
138 | } |
139 | |
140 | inline JSValue::JSValue(unsigned long i) |
141 | { |
142 | if (static_cast<uint32_t>(i) != i) { |
143 | *this = JSValue(EncodeAsDouble, static_cast<double>(i)); |
144 | return; |
145 | } |
146 | *this = JSValue(static_cast<uint32_t>(i)); |
147 | } |
148 | |
149 | inline JSValue::JSValue(long long i) |
150 | { |
151 | if (static_cast<int32_t>(i) != i) { |
152 | *this = JSValue(EncodeAsDouble, static_cast<double>(i)); |
153 | return; |
154 | } |
155 | *this = JSValue(static_cast<int32_t>(i)); |
156 | } |
157 | |
158 | inline JSValue::JSValue(unsigned long long i) |
159 | { |
160 | if (static_cast<uint32_t>(i) != i) { |
161 | *this = JSValue(EncodeAsDouble, static_cast<double>(i)); |
162 | return; |
163 | } |
164 | *this = JSValue(static_cast<uint32_t>(i)); |
165 | } |
166 | |
167 | inline JSValue::JSValue(double d) |
168 | { |
169 | if (canBeStrictInt32(d)) { |
170 | *this = JSValue(static_cast<int32_t>(d)); |
171 | return; |
172 | } |
173 | *this = JSValue(EncodeAsDouble, d); |
174 | } |
175 | |
176 | inline EncodedJSValue JSValue::encode(JSValue value) |
177 | { |
178 | return value.u.asInt64; |
179 | } |
180 | |
181 | inline JSValue JSValue::decode(EncodedJSValue encodedJSValue) |
182 | { |
183 | JSValue v; |
184 | v.u.asInt64 = encodedJSValue; |
185 | return v; |
186 | } |
187 | |
188 | #if USE(JSVALUE32_64) |
189 | inline JSValue::JSValue() |
190 | { |
191 | u.asBits.tag = EmptyValueTag; |
192 | u.asBits.payload = 0; |
193 | } |
194 | |
195 | inline JSValue::JSValue(JSNullTag) |
196 | { |
197 | u.asBits.tag = NullTag; |
198 | u.asBits.payload = 0; |
199 | } |
200 | |
201 | inline JSValue::JSValue(JSUndefinedTag) |
202 | { |
203 | u.asBits.tag = UndefinedTag; |
204 | u.asBits.payload = 0; |
205 | } |
206 | |
207 | inline JSValue::JSValue(JSTrueTag) |
208 | { |
209 | u.asBits.tag = BooleanTag; |
210 | u.asBits.payload = 1; |
211 | } |
212 | |
213 | inline JSValue::JSValue(JSFalseTag) |
214 | { |
215 | u.asBits.tag = BooleanTag; |
216 | u.asBits.payload = 0; |
217 | } |
218 | |
219 | inline JSValue::JSValue(HashTableDeletedValueTag) |
220 | { |
221 | u.asBits.tag = DeletedValueTag; |
222 | u.asBits.payload = 0; |
223 | } |
224 | |
225 | inline JSValue::JSValue(JSCell* ptr) |
226 | { |
227 | if (ptr) |
228 | u.asBits.tag = CellTag; |
229 | else |
230 | u.asBits.tag = EmptyValueTag; |
231 | u.asBits.payload = reinterpret_cast<int32_t>(ptr); |
232 | } |
233 | |
234 | inline JSValue::JSValue(const JSCell* ptr) |
235 | { |
236 | if (ptr) |
237 | u.asBits.tag = CellTag; |
238 | else |
239 | u.asBits.tag = EmptyValueTag; |
240 | u.asBits.payload = reinterpret_cast<int32_t>(const_cast<JSCell*>(ptr)); |
241 | } |
242 | |
243 | inline JSValue::operator bool() const |
244 | { |
245 | ASSERT(tag() != DeletedValueTag); |
246 | return tag() != EmptyValueTag; |
247 | } |
248 | |
249 | inline bool JSValue::operator==(const JSValue& other) const |
250 | { |
251 | return u.asInt64 == other.u.asInt64; |
252 | } |
253 | |
254 | inline bool JSValue::operator!=(const JSValue& other) const |
255 | { |
256 | return u.asInt64 != other.u.asInt64; |
257 | } |
258 | |
259 | inline bool JSValue::isEmpty() const |
260 | { |
261 | return tag() == EmptyValueTag; |
262 | } |
263 | |
264 | inline bool JSValue::isUndefined() const |
265 | { |
266 | return tag() == UndefinedTag; |
267 | } |
268 | |
269 | inline bool JSValue::isNull() const |
270 | { |
271 | return tag() == NullTag; |
272 | } |
273 | |
274 | inline bool JSValue::isUndefinedOrNull() const |
275 | { |
276 | return isUndefined() || isNull(); |
277 | } |
278 | |
279 | inline bool JSValue::isCell() const |
280 | { |
281 | return tag() == CellTag; |
282 | } |
283 | |
284 | inline bool JSValue::isInt32() const |
285 | { |
286 | return tag() == Int32Tag; |
287 | } |
288 | |
289 | inline bool JSValue::isDouble() const |
290 | { |
291 | return tag() < LowestTag; |
292 | } |
293 | |
294 | inline bool JSValue::isTrue() const |
295 | { |
296 | return tag() == BooleanTag && payload(); |
297 | } |
298 | |
299 | inline bool JSValue::isFalse() const |
300 | { |
301 | return tag() == BooleanTag && !payload(); |
302 | } |
303 | |
304 | inline uint32_t JSValue::tag() const |
305 | { |
306 | return u.asBits.tag; |
307 | } |
308 | |
309 | inline int32_t JSValue::payload() const |
310 | { |
311 | return u.asBits.payload; |
312 | } |
313 | |
314 | inline int32_t JSValue::asInt32() const |
315 | { |
316 | ASSERT(isInt32()); |
317 | return u.asBits.payload; |
318 | } |
319 | |
320 | inline double JSValue::asDouble() const |
321 | { |
322 | ASSERT(isDouble()); |
323 | return u.asDouble; |
324 | } |
325 | |
326 | ALWAYS_INLINE JSCell* JSValue::asCell() const |
327 | { |
328 | ASSERT(isCell()); |
329 | return reinterpret_cast<JSCell*>(u.asBits.payload); |
330 | } |
331 | |
332 | ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, double d) |
333 | { |
334 | ASSERT(!isImpureNaN(d)); |
335 | u.asDouble = d; |
336 | } |
337 | |
338 | inline JSValue::JSValue(int i) |
339 | { |
340 | u.asBits.tag = Int32Tag; |
341 | u.asBits.payload = i; |
342 | } |
343 | |
344 | inline JSValue::JSValue(int32_t tag, int32_t payload) |
345 | { |
346 | u.asBits.tag = tag; |
347 | u.asBits.payload = payload; |
348 | } |
349 | |
350 | inline bool JSValue::isNumber() const |
351 | { |
352 | return isInt32() || isDouble(); |
353 | } |
354 | |
355 | inline bool JSValue::isBoolean() const |
356 | { |
357 | return tag() == BooleanTag; |
358 | } |
359 | |
360 | inline bool JSValue::asBoolean() const |
361 | { |
362 | ASSERT(isBoolean()); |
363 | return payload(); |
364 | } |
365 | |
366 | #else // !USE(JSVALUE32_64) i.e. USE(JSVALUE64) |
367 | |
368 | // 0x0 can never occur naturally because it has a tag of 00, indicating a pointer value, but a payload of 0x0, which is in the (invalid) zero page. |
369 | inline JSValue::JSValue() |
370 | { |
371 | u.asInt64 = ValueEmpty; |
372 | } |
373 | |
374 | // 0x4 can never occur naturally because it has a tag of 00, indicating a pointer value, but a payload of 0x4, which is in the (invalid) zero page. |
375 | inline JSValue::JSValue(HashTableDeletedValueTag) |
376 | { |
377 | u.asInt64 = ValueDeleted; |
378 | } |
379 | |
380 | inline JSValue::JSValue(JSCell* ptr) |
381 | { |
382 | u.asInt64 = reinterpret_cast<uintptr_t>(ptr); |
383 | } |
384 | |
385 | inline JSValue::JSValue(const JSCell* ptr) |
386 | { |
387 | u.asInt64 = reinterpret_cast<uintptr_t>(const_cast<JSCell*>(ptr)); |
388 | } |
389 | |
390 | inline JSValue::operator bool() const |
391 | { |
392 | return u.asInt64; |
393 | } |
394 | |
395 | inline bool JSValue::operator==(const JSValue& other) const |
396 | { |
397 | return u.asInt64 == other.u.asInt64; |
398 | } |
399 | |
400 | inline bool JSValue::operator!=(const JSValue& other) const |
401 | { |
402 | return u.asInt64 != other.u.asInt64; |
403 | } |
404 | |
405 | inline bool JSValue::isEmpty() const |
406 | { |
407 | return u.asInt64 == ValueEmpty; |
408 | } |
409 | |
410 | inline bool JSValue::isUndefined() const |
411 | { |
412 | return asValue() == JSValue(JSUndefined); |
413 | } |
414 | |
415 | inline bool JSValue::isNull() const |
416 | { |
417 | return asValue() == JSValue(JSNull); |
418 | } |
419 | |
420 | inline bool JSValue::isTrue() const |
421 | { |
422 | return asValue() == JSValue(JSTrue); |
423 | } |
424 | |
425 | inline bool JSValue::isFalse() const |
426 | { |
427 | return asValue() == JSValue(JSFalse); |
428 | } |
429 | |
430 | inline bool JSValue::asBoolean() const |
431 | { |
432 | ASSERT(isBoolean()); |
433 | return asValue() == JSValue(JSTrue); |
434 | } |
435 | |
436 | inline int32_t JSValue::asInt32() const |
437 | { |
438 | ASSERT(isInt32()); |
439 | return static_cast<int32_t>(u.asInt64); |
440 | } |
441 | |
442 | inline bool JSValue::isDouble() const |
443 | { |
444 | return isNumber() && !isInt32(); |
445 | } |
446 | |
447 | inline JSValue::JSValue(JSNullTag) |
448 | { |
449 | u.asInt64 = ValueNull; |
450 | } |
451 | |
452 | inline JSValue::JSValue(JSUndefinedTag) |
453 | { |
454 | u.asInt64 = ValueUndefined; |
455 | } |
456 | |
457 | inline JSValue::JSValue(JSTrueTag) |
458 | { |
459 | u.asInt64 = ValueTrue; |
460 | } |
461 | |
462 | inline JSValue::JSValue(JSFalseTag) |
463 | { |
464 | u.asInt64 = ValueFalse; |
465 | } |
466 | |
467 | inline bool JSValue::isUndefinedOrNull() const |
468 | { |
469 | // Undefined and null share the same value, bar the 'undefined' bit in the extended tag. |
470 | return (u.asInt64 & ~TagBitUndefined) == ValueNull; |
471 | } |
472 | |
473 | inline bool JSValue::isBoolean() const |
474 | { |
475 | return (u.asInt64 & ~1) == ValueFalse; |
476 | } |
477 | |
478 | inline bool JSValue::isCell() const |
479 | { |
480 | return !(u.asInt64 & TagMask); |
481 | } |
482 | |
483 | inline bool JSValue::isInt32() const |
484 | { |
485 | return (u.asInt64 & TagTypeNumber) == TagTypeNumber; |
486 | } |
487 | |
488 | inline int64_t reinterpretDoubleToInt64(double value) |
489 | { |
490 | return bitwise_cast<int64_t>(value); |
491 | } |
492 | inline double reinterpretInt64ToDouble(int64_t value) |
493 | { |
494 | return bitwise_cast<double>(value); |
495 | } |
496 | |
497 | ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, double d) |
498 | { |
499 | ASSERT(!isImpureNaN(d)); |
500 | u.asInt64 = reinterpretDoubleToInt64(d) + DoubleEncodeOffset; |
501 | } |
502 | |
503 | inline JSValue::JSValue(int i) |
504 | { |
505 | u.asInt64 = TagTypeNumber | static_cast<uint32_t>(i); |
506 | } |
507 | |
508 | inline double JSValue::asDouble() const |
509 | { |
510 | ASSERT(isDouble()); |
511 | return reinterpretInt64ToDouble(u.asInt64 - DoubleEncodeOffset); |
512 | } |
513 | |
514 | inline bool JSValue::isNumber() const |
515 | { |
516 | return u.asInt64 & TagTypeNumber; |
517 | } |
518 | |
519 | ALWAYS_INLINE JSCell* JSValue::asCell() const |
520 | { |
521 | ASSERT(isCell()); |
522 | return u.ptr; |
523 | } |
524 | |
525 | #endif // USE(JSVALUE64) |
526 | |
527 | inline int64_t tryConvertToInt52(double number) |
528 | { |
529 | if (number != number) |
530 | return JSValue::notInt52; |
531 | #if OS(WINDOWS) && CPU(X86) |
532 | // The VS Compiler for 32-bit builds generates a floating point error when attempting to cast |
533 | // from an infinity to a 64-bit integer. We leave this routine with the floating point error |
534 | // left in a register, causing undefined behavior in later floating point operations. |
535 | // |
536 | // To avoid this issue, we check for infinity here, and return false in that case. |
537 | if (std::isinf(number)) |
538 | return JSValue::notInt52; |
539 | #endif |
540 | int64_t asInt64 = static_cast<int64_t>(number); |
541 | if (asInt64 != number) |
542 | return JSValue::notInt52; |
543 | if (!asInt64 && std::signbit(number)) |
544 | return JSValue::notInt52; |
545 | if (asInt64 >= (static_cast<int64_t>(1) << (JSValue::numberOfInt52Bits - 1))) |
546 | return JSValue::notInt52; |
547 | if (asInt64 < -(static_cast<int64_t>(1) << (JSValue::numberOfInt52Bits - 1))) |
548 | return JSValue::notInt52; |
549 | return asInt64; |
550 | } |
551 | |
552 | inline bool isInt52(double number) |
553 | { |
554 | return tryConvertToInt52(number) != JSValue::notInt52; |
555 | } |
556 | |
557 | inline bool JSValue::isAnyInt() const |
558 | { |
559 | if (isInt32()) |
560 | return true; |
561 | if (!isNumber()) |
562 | return false; |
563 | return isInt52(asDouble()); |
564 | } |
565 | |
566 | inline int64_t JSValue::asAnyInt() const |
567 | { |
568 | ASSERT(isAnyInt()); |
569 | if (isInt32()) |
570 | return asInt32(); |
571 | return static_cast<int64_t>(asDouble()); |
572 | } |
573 | |
574 | inline bool JSValue::isInt32AsAnyInt() const |
575 | { |
576 | if (!isAnyInt()) |
577 | return false; |
578 | int64_t value = asAnyInt(); |
579 | return value >= INT32_MIN && value <= INT32_MAX; |
580 | } |
581 | |
582 | inline int32_t JSValue::asInt32AsAnyInt() const |
583 | { |
584 | ASSERT(isInt32AsAnyInt()); |
585 | if (isInt32()) |
586 | return asInt32(); |
587 | return static_cast<int32_t>(asDouble()); |
588 | } |
589 | |
590 | inline bool JSValue::isUInt32AsAnyInt() const |
591 | { |
592 | if (!isAnyInt()) |
593 | return false; |
594 | int64_t value = asAnyInt(); |
595 | return value >= 0 && value <= UINT32_MAX; |
596 | } |
597 | |
598 | inline uint32_t JSValue::asUInt32AsAnyInt() const |
599 | { |
600 | ASSERT(isUInt32AsAnyInt()); |
601 | if (isUInt32()) |
602 | return asUInt32(); |
603 | return static_cast<uint32_t>(asDouble()); |
604 | } |
605 | |
606 | inline bool JSValue::isString() const |
607 | { |
608 | return isCell() && asCell()->isString(); |
609 | } |
610 | |
611 | inline bool JSValue::isBigInt() const |
612 | { |
613 | return isCell() && asCell()->isBigInt(); |
614 | } |
615 | |
616 | inline bool JSValue::isSymbol() const |
617 | { |
618 | return isCell() && asCell()->isSymbol(); |
619 | } |
620 | |
621 | inline bool JSValue::isPrimitive() const |
622 | { |
623 | return !isCell() || asCell()->isString() || asCell()->isSymbol() || asCell()->isBigInt(); |
624 | } |
625 | |
626 | inline bool JSValue::isGetterSetter() const |
627 | { |
628 | return isCell() && asCell()->isGetterSetter(); |
629 | } |
630 | |
631 | inline bool JSValue::isCustomGetterSetter() const |
632 | { |
633 | return isCell() && asCell()->isCustomGetterSetter(); |
634 | } |
635 | |
636 | inline bool JSValue::isObject() const |
637 | { |
638 | return isCell() && asCell()->isObject(); |
639 | } |
640 | |
641 | inline bool JSValue::getString(ExecState* exec, String& s) const |
642 | { |
643 | return isCell() && asCell()->getString(exec, s); |
644 | } |
645 | |
646 | inline String JSValue::getString(ExecState* exec) const |
647 | { |
648 | return isCell() ? asCell()->getString(exec) : String(); |
649 | } |
650 | |
651 | template <typename Base> String HandleConverter<Base, Unknown>::getString(ExecState* exec) const |
652 | { |
653 | return jsValue().getString(exec); |
654 | } |
655 | |
656 | inline JSObject* JSValue::getObject() const |
657 | { |
658 | return isCell() ? asCell()->getObject() : 0; |
659 | } |
660 | |
661 | ALWAYS_INLINE bool JSValue::getUInt32(uint32_t& v) const |
662 | { |
663 | if (isInt32()) { |
664 | int32_t i = asInt32(); |
665 | v = static_cast<uint32_t>(i); |
666 | return i >= 0; |
667 | } |
668 | if (isDouble()) { |
669 | double d = asDouble(); |
670 | v = static_cast<uint32_t>(d); |
671 | return v == d; |
672 | } |
673 | return false; |
674 | } |
675 | |
676 | ALWAYS_INLINE Identifier JSValue::toPropertyKey(ExecState* exec) const |
677 | { |
678 | VM& vm = exec->vm(); |
679 | auto scope = DECLARE_THROW_SCOPE(vm); |
680 | |
681 | if (isString()) |
682 | RELEASE_AND_RETURN(scope, asString(*this)->toIdentifier(exec)); |
683 | |
684 | JSValue primitive = toPrimitive(exec, PreferString); |
685 | RETURN_IF_EXCEPTION(scope, vm.propertyNames->emptyIdentifier); |
686 | if (primitive.isSymbol()) |
687 | RELEASE_AND_RETURN(scope, Identifier::fromUid(asSymbol(primitive)->privateName())); |
688 | |
689 | RELEASE_AND_RETURN(scope, primitive.toString(exec)->toIdentifier(exec)); |
690 | } |
691 | |
692 | inline JSValue JSValue::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const |
693 | { |
694 | return isCell() ? asCell()->toPrimitive(exec, preferredType) : asValue(); |
695 | } |
696 | |
697 | inline PreferredPrimitiveType toPreferredPrimitiveType(ExecState* exec, JSValue value) |
698 | { |
699 | VM& vm = exec->vm(); |
700 | auto scope = DECLARE_THROW_SCOPE(vm); |
701 | |
702 | if (!value.isString()) { |
703 | throwTypeError(exec, scope, "Primitive hint is not a string."_s ); |
704 | return NoPreference; |
705 | } |
706 | |
707 | StringImpl* hintString = asString(value)->value(exec).impl(); |
708 | RETURN_IF_EXCEPTION(scope, NoPreference); |
709 | |
710 | if (WTF::equal(hintString, "default" )) |
711 | return NoPreference; |
712 | if (WTF::equal(hintString, "number" )) |
713 | return PreferNumber; |
714 | if (WTF::equal(hintString, "string" )) |
715 | return PreferString; |
716 | |
717 | throwTypeError(exec, scope, "Expected primitive hint to match one of 'default', 'number', 'string'."_s ); |
718 | return NoPreference; |
719 | } |
720 | |
721 | inline bool JSValue::getPrimitiveNumber(ExecState* exec, double& number, JSValue& value) |
722 | { |
723 | if (isInt32()) { |
724 | number = asInt32(); |
725 | value = *this; |
726 | return true; |
727 | } |
728 | if (isDouble()) { |
729 | number = asDouble(); |
730 | value = *this; |
731 | return true; |
732 | } |
733 | if (isCell()) |
734 | return asCell()->getPrimitiveNumber(exec, number, value); |
735 | if (isTrue()) { |
736 | number = 1.0; |
737 | value = *this; |
738 | return true; |
739 | } |
740 | if (isFalse() || isNull()) { |
741 | number = 0.0; |
742 | value = *this; |
743 | return true; |
744 | } |
745 | ASSERT(isUndefined()); |
746 | number = PNaN; |
747 | value = *this; |
748 | return true; |
749 | } |
750 | |
751 | ALWAYS_INLINE double JSValue::toNumber(ExecState* exec) const |
752 | { |
753 | if (isInt32()) |
754 | return asInt32(); |
755 | if (isDouble()) |
756 | return asDouble(); |
757 | return toNumberSlowCase(exec); |
758 | } |
759 | |
760 | ALWAYS_INLINE Variant<JSBigInt*, double> JSValue::toNumeric(ExecState* exec) const |
761 | { |
762 | if (isInt32()) |
763 | return asInt32(); |
764 | if (isDouble()) |
765 | return asDouble(); |
766 | if (isBigInt()) |
767 | return asBigInt(*this); |
768 | |
769 | VM& vm = exec->vm(); |
770 | auto scope = DECLARE_THROW_SCOPE(vm); |
771 | JSValue primValue = this->toPrimitive(exec, PreferNumber); |
772 | RETURN_IF_EXCEPTION(scope, 0); |
773 | if (primValue.isBigInt()) |
774 | return asBigInt(primValue); |
775 | double value = primValue.toNumber(exec); |
776 | RETURN_IF_EXCEPTION(scope, 0); |
777 | return value; |
778 | } |
779 | |
780 | ALWAYS_INLINE Variant<JSBigInt*, int32_t> JSValue::toBigIntOrInt32(ExecState* exec) const |
781 | { |
782 | if (isInt32()) |
783 | return asInt32(); |
784 | if (isDouble() && canBeInt32(asDouble())) |
785 | return static_cast<int32_t>(asDouble()); |
786 | if (isBigInt()) |
787 | return asBigInt(*this); |
788 | |
789 | VM& vm = exec->vm(); |
790 | auto scope = DECLARE_THROW_SCOPE(vm); |
791 | JSValue primValue = this->toPrimitive(exec, PreferNumber); |
792 | RETURN_IF_EXCEPTION(scope, 0); |
793 | if (primValue.isBigInt()) |
794 | return asBigInt(primValue); |
795 | int32_t value = primValue.toInt32(exec); |
796 | RETURN_IF_EXCEPTION(scope, 0); |
797 | return value; |
798 | } |
799 | |
800 | inline JSObject* JSValue::toObject(ExecState* exec) const |
801 | { |
802 | return isCell() ? asCell()->toObject(exec, exec->lexicalGlobalObject()) : toObjectSlowCase(exec, exec->lexicalGlobalObject()); |
803 | } |
804 | |
805 | inline JSObject* JSValue::toObject(ExecState* exec, JSGlobalObject* globalObject) const |
806 | { |
807 | return isCell() ? asCell()->toObject(exec, globalObject) : toObjectSlowCase(exec, globalObject); |
808 | } |
809 | |
810 | inline bool JSValue::isFunction(VM& vm) const |
811 | { |
812 | if (!isCell()) |
813 | return false; |
814 | return asCell()->isFunction(vm); |
815 | } |
816 | |
817 | inline bool JSValue::isCallable(VM& vm, CallType& callType, CallData& callData) const |
818 | { |
819 | if (!isCell()) |
820 | return false; |
821 | return asCell()->isCallable(vm, callType, callData); |
822 | } |
823 | |
824 | inline bool JSValue::isConstructor(VM& vm) const |
825 | { |
826 | if (!isCell()) |
827 | return false; |
828 | return asCell()->isConstructor(vm); |
829 | } |
830 | |
831 | inline bool JSValue::isConstructor(VM& vm, ConstructType& constructType, ConstructData& constructData) const |
832 | { |
833 | if (!isCell()) |
834 | return false; |
835 | return asCell()->isConstructor(vm, constructType, constructData); |
836 | } |
837 | |
838 | // this method is here to be after the inline declaration of JSCell::inherits |
839 | inline bool JSValue::inherits(VM& vm, const ClassInfo* classInfo) const |
840 | { |
841 | return isCell() && asCell()->inherits(vm, classInfo); |
842 | } |
843 | |
844 | template<typename Target> |
845 | inline bool JSValue::inherits(VM& vm) const |
846 | { |
847 | return isCell() && asCell()->inherits<Target>(vm); |
848 | } |
849 | |
850 | inline const ClassInfo* JSValue::classInfoOrNull(VM& vm) const |
851 | { |
852 | return isCell() ? asCell()->classInfo(vm) : nullptr; |
853 | } |
854 | |
855 | inline JSValue JSValue::toThis(ExecState* exec, ECMAMode ecmaMode) const |
856 | { |
857 | return isCell() ? asCell()->methodTable(exec->vm())->toThis(asCell(), exec, ecmaMode) : toThisSlowCase(exec, ecmaMode); |
858 | } |
859 | |
860 | ALWAYS_INLINE JSValue JSValue::get(ExecState* exec, PropertyName propertyName) const |
861 | { |
862 | PropertySlot slot(asValue(), PropertySlot::InternalMethodType::Get); |
863 | return get(exec, propertyName, slot); |
864 | } |
865 | |
866 | ALWAYS_INLINE JSValue JSValue::get(ExecState* exec, PropertyName propertyName, PropertySlot& slot) const |
867 | { |
868 | auto scope = DECLARE_THROW_SCOPE(exec->vm()); |
869 | bool hasSlot = getPropertySlot(exec, propertyName, slot); |
870 | EXCEPTION_ASSERT(!scope.exception() || !hasSlot); |
871 | if (!hasSlot) |
872 | return jsUndefined(); |
873 | RELEASE_AND_RETURN(scope, slot.getValue(exec, propertyName)); |
874 | } |
875 | |
876 | template<typename CallbackWhenNoException> |
877 | ALWAYS_INLINE typename std::result_of<CallbackWhenNoException(bool, PropertySlot&)>::type JSValue::getPropertySlot(ExecState* exec, PropertyName propertyName, CallbackWhenNoException callback) const |
878 | { |
879 | PropertySlot slot(asValue(), PropertySlot::InternalMethodType::Get); |
880 | return getPropertySlot(exec, propertyName, slot, callback); |
881 | } |
882 | |
883 | template<typename CallbackWhenNoException> |
884 | ALWAYS_INLINE typename std::result_of<CallbackWhenNoException(bool, PropertySlot&)>::type JSValue::getPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot, CallbackWhenNoException callback) const |
885 | { |
886 | auto scope = DECLARE_THROW_SCOPE(exec->vm()); |
887 | bool found = getPropertySlot(exec, propertyName, slot); |
888 | RETURN_IF_EXCEPTION(scope, { }); |
889 | RELEASE_AND_RETURN(scope, callback(found, slot)); |
890 | } |
891 | |
892 | ALWAYS_INLINE bool JSValue::getPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot) const |
893 | { |
894 | auto scope = DECLARE_THROW_SCOPE(exec->vm()); |
895 | // If this is a primitive, we'll need to synthesize the prototype - |
896 | // and if it's a string there are special properties to check first. |
897 | JSObject* object; |
898 | if (UNLIKELY(!isObject())) { |
899 | if (isString()) { |
900 | bool hasProperty = asString(*this)->getStringPropertySlot(exec, propertyName, slot); |
901 | RETURN_IF_EXCEPTION(scope, false); |
902 | if (hasProperty) |
903 | return true; |
904 | } |
905 | object = synthesizePrototype(exec); |
906 | EXCEPTION_ASSERT(!!scope.exception() == !object); |
907 | if (UNLIKELY(!object)) |
908 | return false; |
909 | } else |
910 | object = asObject(asCell()); |
911 | |
912 | RELEASE_AND_RETURN(scope, object->getPropertySlot(exec, propertyName, slot)); |
913 | } |
914 | |
915 | ALWAYS_INLINE bool JSValue::getOwnPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot) const |
916 | { |
917 | // If this is a primitive, we'll need to synthesize the prototype - |
918 | // and if it's a string there are special properties to check first. |
919 | auto scope = DECLARE_THROW_SCOPE(exec->vm()); |
920 | if (UNLIKELY(!isObject())) { |
921 | if (isString()) |
922 | RELEASE_AND_RETURN(scope, asString(*this)->getStringPropertySlot(exec, propertyName, slot)); |
923 | |
924 | if (isUndefinedOrNull()) |
925 | throwException(exec, scope, createNotAnObjectError(exec, *this)); |
926 | return false; |
927 | } |
928 | RELEASE_AND_RETURN(scope, asObject(asCell())->getOwnPropertySlotInline(exec, propertyName, slot)); |
929 | } |
930 | |
931 | ALWAYS_INLINE JSValue JSValue::get(ExecState* exec, unsigned propertyName) const |
932 | { |
933 | PropertySlot slot(asValue(), PropertySlot::InternalMethodType::Get); |
934 | return get(exec, propertyName, slot); |
935 | } |
936 | |
937 | ALWAYS_INLINE JSValue JSValue::get(ExecState* exec, unsigned propertyName, PropertySlot& slot) const |
938 | { |
939 | auto scope = DECLARE_THROW_SCOPE(exec->vm()); |
940 | // If this is a primitive, we'll need to synthesize the prototype - |
941 | // and if it's a string there are special properties to check first. |
942 | JSObject* object; |
943 | if (UNLIKELY(!isObject())) { |
944 | if (isString()) { |
945 | bool hasProperty = asString(*this)->getStringPropertySlot(exec, propertyName, slot); |
946 | RETURN_IF_EXCEPTION(scope, { }); |
947 | if (hasProperty) |
948 | RELEASE_AND_RETURN(scope, slot.getValue(exec, propertyName)); |
949 | } |
950 | object = synthesizePrototype(exec); |
951 | EXCEPTION_ASSERT(!!scope.exception() == !object); |
952 | if (UNLIKELY(!object)) |
953 | return JSValue(); |
954 | } else |
955 | object = asObject(asCell()); |
956 | |
957 | bool hasSlot = object->getPropertySlot(exec, propertyName, slot); |
958 | EXCEPTION_ASSERT(!scope.exception() || !hasSlot); |
959 | if (!hasSlot) |
960 | return jsUndefined(); |
961 | RELEASE_AND_RETURN(scope, slot.getValue(exec, propertyName)); |
962 | } |
963 | |
964 | ALWAYS_INLINE JSValue JSValue::get(ExecState* exec, uint64_t propertyName) const |
965 | { |
966 | if (LIKELY(propertyName <= std::numeric_limits<unsigned>::max())) |
967 | return get(exec, static_cast<unsigned>(propertyName)); |
968 | return get(exec, Identifier::from(exec, static_cast<double>(propertyName))); |
969 | } |
970 | |
971 | inline bool JSValue::put(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) |
972 | { |
973 | if (UNLIKELY(!isCell())) |
974 | return putToPrimitive(exec, propertyName, value, slot); |
975 | |
976 | return asCell()->methodTable(exec->vm())->put(asCell(), exec, propertyName, value, slot); |
977 | } |
978 | |
979 | ALWAYS_INLINE bool JSValue::putInline(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) |
980 | { |
981 | if (UNLIKELY(!isCell())) |
982 | return putToPrimitive(exec, propertyName, value, slot); |
983 | return asCell()->putInline(exec, propertyName, value, slot); |
984 | } |
985 | |
986 | inline bool JSValue::putByIndex(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) |
987 | { |
988 | if (UNLIKELY(!isCell())) |
989 | return putToPrimitiveByIndex(exec, propertyName, value, shouldThrow); |
990 | |
991 | return asCell()->methodTable(exec->vm())->putByIndex(asCell(), exec, propertyName, value, shouldThrow); |
992 | } |
993 | |
994 | inline Structure* JSValue::structureOrNull() const |
995 | { |
996 | if (isCell()) |
997 | return asCell()->structure(); |
998 | return nullptr; |
999 | } |
1000 | |
1001 | inline JSValue JSValue::structureOrUndefined() const |
1002 | { |
1003 | if (isCell()) |
1004 | return JSValue(asCell()->structure()); |
1005 | return jsUndefined(); |
1006 | } |
1007 | |
1008 | // ECMA 11.9.3 |
1009 | inline bool JSValue::equal(ExecState* exec, JSValue v1, JSValue v2) |
1010 | { |
1011 | if (v1.isInt32() && v2.isInt32()) |
1012 | return v1 == v2; |
1013 | |
1014 | return equalSlowCase(exec, v1, v2); |
1015 | } |
1016 | |
1017 | ALWAYS_INLINE bool JSValue::equalSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2) |
1018 | { |
1019 | VM& vm = exec->vm(); |
1020 | auto scope = DECLARE_THROW_SCOPE(vm); |
1021 | do { |
1022 | if (v1.isNumber() && v2.isNumber()) |
1023 | return v1.asNumber() == v2.asNumber(); |
1024 | |
1025 | bool s1 = v1.isString(); |
1026 | bool s2 = v2.isString(); |
1027 | if (s1 && s2) |
1028 | RELEASE_AND_RETURN(scope, asString(v1)->equal(exec, asString(v2))); |
1029 | |
1030 | if (v1.isBigInt() && s2) { |
1031 | JSBigInt* n = JSBigInt::stringToBigInt(exec, asString(v2)->value(exec)); |
1032 | RETURN_IF_EXCEPTION(scope, false); |
1033 | if (!n) |
1034 | return false; |
1035 | |
1036 | v2 = JSValue(n); |
1037 | continue; |
1038 | } |
1039 | |
1040 | if (s1 && v2.isBigInt()) { |
1041 | JSBigInt* n = JSBigInt::stringToBigInt(exec, asString(v1)->value(exec)); |
1042 | RETURN_IF_EXCEPTION(scope, false); |
1043 | if (!n) |
1044 | return false; |
1045 | |
1046 | v1 = JSValue(n); |
1047 | continue; |
1048 | } |
1049 | |
1050 | if (v1.isUndefinedOrNull()) { |
1051 | if (v2.isUndefinedOrNull()) |
1052 | return true; |
1053 | if (!v2.isCell()) |
1054 | return false; |
1055 | return v2.asCell()->structure(vm)->masqueradesAsUndefined(exec->lexicalGlobalObject()); |
1056 | } |
1057 | |
1058 | if (v2.isUndefinedOrNull()) { |
1059 | if (!v1.isCell()) |
1060 | return false; |
1061 | return v1.asCell()->structure(vm)->masqueradesAsUndefined(exec->lexicalGlobalObject()); |
1062 | } |
1063 | |
1064 | if (v1.isObject()) { |
1065 | if (v2.isObject()) |
1066 | return v1 == v2; |
1067 | JSValue p1 = v1.toPrimitive(exec); |
1068 | RETURN_IF_EXCEPTION(scope, false); |
1069 | v1 = p1; |
1070 | if (v1.isInt32() && v2.isInt32()) |
1071 | return v1 == v2; |
1072 | continue; |
1073 | } |
1074 | |
1075 | if (v2.isObject()) { |
1076 | JSValue p2 = v2.toPrimitive(exec); |
1077 | RETURN_IF_EXCEPTION(scope, false); |
1078 | v2 = p2; |
1079 | if (v1.isInt32() && v2.isInt32()) |
1080 | return v1 == v2; |
1081 | continue; |
1082 | } |
1083 | |
1084 | bool sym1 = v1.isSymbol(); |
1085 | bool sym2 = v2.isSymbol(); |
1086 | if (sym1 || sym2) { |
1087 | if (sym1 && sym2) |
1088 | return asSymbol(v1) == asSymbol(v2); |
1089 | return false; |
1090 | } |
1091 | |
1092 | if (s1 || s2) { |
1093 | double d1 = v1.toNumber(exec); |
1094 | RETURN_IF_EXCEPTION(scope, false); |
1095 | double d2 = v2.toNumber(exec); |
1096 | RETURN_IF_EXCEPTION(scope, false); |
1097 | return d1 == d2; |
1098 | } |
1099 | |
1100 | if (v1.isBoolean()) { |
1101 | if (v2.isNumber()) |
1102 | return static_cast<double>(v1.asBoolean()) == v2.asNumber(); |
1103 | else if (v2.isBigInt()) { |
1104 | v1 = JSValue(v1.toNumber(exec)); |
1105 | continue; |
1106 | } |
1107 | } else if (v2.isBoolean()) { |
1108 | if (v1.isNumber()) |
1109 | return v1.asNumber() == static_cast<double>(v2.asBoolean()); |
1110 | else if (v1.isBigInt()) { |
1111 | v2 = JSValue(v2.toNumber(exec)); |
1112 | continue; |
1113 | } |
1114 | } |
1115 | |
1116 | if (v1.isBigInt() && v2.isBigInt()) |
1117 | return JSBigInt::equals(asBigInt(v1), asBigInt(v2)); |
1118 | |
1119 | if (v1.isBigInt() && v2.isNumber()) |
1120 | return asBigInt(v1)->equalsToNumber(v2); |
1121 | |
1122 | if (v2.isBigInt() && v1.isNumber()) |
1123 | return asBigInt(v2)->equalsToNumber(v1); |
1124 | |
1125 | return v1 == v2; |
1126 | } while (true); |
1127 | } |
1128 | |
1129 | // ECMA 11.9.3 |
1130 | ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2) |
1131 | { |
1132 | ASSERT(v1.isCell() && v2.isCell()); |
1133 | |
1134 | if (v1.asCell()->isString() && v2.asCell()->isString()) |
1135 | return asString(v1)->equal(exec, asString(v2)); |
1136 | if (v1.isBigInt() && v2.isBigInt()) |
1137 | return JSBigInt::equals(asBigInt(v1), asBigInt(v2)); |
1138 | return v1 == v2; |
1139 | } |
1140 | |
1141 | inline bool JSValue::strictEqual(ExecState* exec, JSValue v1, JSValue v2) |
1142 | { |
1143 | if (v1.isInt32() && v2.isInt32()) |
1144 | return v1 == v2; |
1145 | |
1146 | if (v1.isNumber() && v2.isNumber()) |
1147 | return v1.asNumber() == v2.asNumber(); |
1148 | |
1149 | if (!v1.isCell() || !v2.isCell()) |
1150 | return v1 == v2; |
1151 | |
1152 | return strictEqualSlowCaseInline(exec, v1, v2); |
1153 | } |
1154 | |
1155 | inline int32_t JSValue::asInt32ForArithmetic() const |
1156 | { |
1157 | if (isBoolean()) |
1158 | return asBoolean(); |
1159 | return asInt32(); |
1160 | } |
1161 | |
1162 | inline TriState JSValue::pureStrictEqual(JSValue v1, JSValue v2) |
1163 | { |
1164 | if (v1.isInt32() && v2.isInt32()) |
1165 | return triState(v1 == v2); |
1166 | |
1167 | if (v1.isNumber() && v2.isNumber()) |
1168 | return triState(v1.asNumber() == v2.asNumber()); |
1169 | |
1170 | if (!v1.isCell() || !v2.isCell()) |
1171 | return triState(v1 == v2); |
1172 | |
1173 | if (v1.asCell()->isString() && v2.asCell()->isString()) { |
1174 | const StringImpl* v1String = asString(v1)->tryGetValueImpl(); |
1175 | const StringImpl* v2String = asString(v2)->tryGetValueImpl(); |
1176 | if (!v1String || !v2String) |
1177 | return MixedTriState; |
1178 | return triState(WTF::equal(*v1String, *v2String)); |
1179 | } |
1180 | |
1181 | return triState(v1 == v2); |
1182 | } |
1183 | |
1184 | inline TriState JSValue::pureToBoolean() const |
1185 | { |
1186 | if (isInt32()) |
1187 | return asInt32() ? TrueTriState : FalseTriState; |
1188 | if (isDouble()) |
1189 | return isNotZeroAndOrdered(asDouble()) ? TrueTriState : FalseTriState; // false for NaN |
1190 | if (isCell()) |
1191 | return asCell()->pureToBoolean(); |
1192 | return isTrue() ? TrueTriState : FalseTriState; |
1193 | } |
1194 | |
1195 | ALWAYS_INLINE bool JSValue::requireObjectCoercible(ExecState* exec) const |
1196 | { |
1197 | VM& vm = exec->vm(); |
1198 | auto scope = DECLARE_THROW_SCOPE(vm); |
1199 | |
1200 | if (!isUndefinedOrNull()) |
1201 | return true; |
1202 | throwException(exec, scope, createNotAnObjectError(exec, *this)); |
1203 | return false; |
1204 | } |
1205 | |
1206 | ALWAYS_INLINE bool isThisValueAltered(const PutPropertySlot& slot, JSObject* baseObject) |
1207 | { |
1208 | JSValue thisValue = slot.thisValue(); |
1209 | if (LIKELY(thisValue == baseObject)) |
1210 | return false; |
1211 | |
1212 | if (!thisValue.isObject()) |
1213 | return true; |
1214 | JSObject* thisObject = asObject(thisValue); |
1215 | // Only PureForwardingProxyType can be seen as the same to the original target object. |
1216 | if (thisObject->type() == PureForwardingProxyType && jsCast<JSProxy*>(thisObject)->target() == baseObject) |
1217 | return false; |
1218 | return true; |
1219 | } |
1220 | |
1221 | // See section 7.2.9: https://tc39.github.io/ecma262/#sec-samevalue |
1222 | ALWAYS_INLINE bool sameValue(ExecState* exec, JSValue a, JSValue b) |
1223 | { |
1224 | if (!a.isNumber()) |
1225 | return JSValue::strictEqual(exec, a, b); |
1226 | if (!b.isNumber()) |
1227 | return false; |
1228 | double x = a.asNumber(); |
1229 | double y = b.asNumber(); |
1230 | bool xIsNaN = std::isnan(x); |
1231 | bool yIsNaN = std::isnan(y); |
1232 | if (xIsNaN || yIsNaN) |
1233 | return xIsNaN && yIsNaN; |
1234 | return bitwise_cast<uint64_t>(x) == bitwise_cast<uint64_t>(y); |
1235 | } |
1236 | |
1237 | } // namespace JSC |
1238 | |