2 * Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
3 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2012 Apple Inc. All rights reserved.
5 * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca)
6 * Copyright (C) 2007 Maks Orlovich
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
26 #include "JSGlobalObjectFunctions.h"
28 #include "CallFrame.h"
29 #include "Interpreter.h"
30 #include "JSFunction.h"
31 #include "JSGlobalObject.h"
33 #include "JSStringBuilder.h"
35 #include "LiteralParser.h"
37 #include "JSCInlines.h"
39 #include "StackVisitor.h"
43 #include <wtf/ASCIICType.h>
44 #include <wtf/Assertions.h>
45 #include <wtf/HexNumber.h>
46 #include <wtf/MathExtras.h>
47 #include <wtf/StringExtras.h>
48 #include <wtf/text/StringBuilder.h>
49 #include <wtf/unicode/UTF8.h>
52 using namespace Unicode
;
56 template<unsigned charactersCount
>
57 static Bitmap
<256> makeCharacterBitmap(const char (&characters
)[charactersCount
])
60 for (unsigned i
= 0; i
< charactersCount
; ++i
)
61 bitmap
.set(characters
[i
]);
65 static JSValue
encode(ExecState
* exec
, const Bitmap
<256>& doNotEscape
)
67 CString cstr
= exec
->argument(0).toString(exec
)->view(exec
).get().utf8(StrictConversion
);
69 return exec
->vm().throwException(exec
, createURIError(exec
, ASCIILiteral("String contained an illegal UTF-16 sequence.")));
71 JSStringBuilder builder
;
72 const char* p
= cstr
.data();
73 for (size_t k
= 0; k
< cstr
.length(); k
++, p
++) {
75 if (c
&& doNotEscape
.get(static_cast<LChar
>(c
)))
76 builder
.append(static_cast<LChar
>(c
));
78 builder
.append(static_cast<LChar
>('%'));
79 appendByteAsHex(c
, builder
);
82 return builder
.build(exec
);
85 template <typename CharType
>
87 static JSValue
decode(ExecState
* exec
, const CharType
* characters
, int length
, const Bitmap
<256>& doNotUnescape
, bool strict
)
89 JSStringBuilder builder
;
93 const CharType
* p
= characters
+ k
;
97 if (k
<= length
- 3 && isASCIIHexDigit(p
[1]) && isASCIIHexDigit(p
[2])) {
98 const char b0
= Lexer
<CharType
>::convertHex(p
[1], p
[2]);
99 const int sequenceLen
= UTF8SequenceLength(b0
);
100 if (sequenceLen
&& k
<= length
- sequenceLen
* 3) {
101 charLen
= sequenceLen
* 3;
104 for (int i
= 1; i
< sequenceLen
; ++i
) {
105 const CharType
* q
= p
+ i
* 3;
106 if (q
[0] == '%' && isASCIIHexDigit(q
[1]) && isASCIIHexDigit(q
[2]))
107 sequence
[i
] = Lexer
<CharType
>::convertHex(q
[1], q
[2]);
114 sequence
[sequenceLen
] = 0;
115 const int character
= decodeUTF8Sequence(sequence
);
116 if (character
< 0 || character
>= 0x110000)
118 else if (character
>= 0x10000) {
119 // Convert to surrogate pair.
120 builder
.append(static_cast<UChar
>(0xD800 | ((character
- 0x10000) >> 10)));
121 u
= static_cast<UChar
>(0xDC00 | ((character
- 0x10000) & 0x3FF));
123 u
= static_cast<UChar
>(character
);
129 return exec
->vm().throwException(exec
, createURIError(exec
, ASCIILiteral("URI error")));
130 // The only case where we don't use "strict" mode is the "unescape" function.
131 // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE.
132 if (k
<= length
- 6 && p
[1] == 'u'
133 && isASCIIHexDigit(p
[2]) && isASCIIHexDigit(p
[3])
134 && isASCIIHexDigit(p
[4]) && isASCIIHexDigit(p
[5])) {
136 u
= Lexer
<UChar
>::convertUnicode(p
[2], p
[3], p
[4], p
[5]);
139 if (charLen
&& (u
== 0 || u
>= 128 || !doNotUnescape
.get(static_cast<LChar
>(u
)))) {
148 return builder
.build(exec
);
151 static JSValue
decode(ExecState
* exec
, const Bitmap
<256>& doNotUnescape
, bool strict
)
153 StringView str
= exec
->argument(0).toString(exec
)->view(exec
);
156 return decode(exec
, str
.characters8(), str
.length(), doNotUnescape
, strict
);
157 return decode(exec
, str
.characters16(), str
.length(), doNotUnescape
, strict
);
160 bool isStrWhiteSpace(UChar c
)
163 // ECMA-262-5th 7.2 & 7.3
171 case 0x180E: // This character used to be in Zs category before Unicode 6.3, and EcmaScript says that we should keep treating it as such.
177 return c
> 0xFF && u_charType(c
) == U_SPACE_SEPARATOR
;
181 static int parseDigit(unsigned short c
, int radix
)
185 if (c
>= '0' && c
<= '9')
187 else if (c
>= 'A' && c
<= 'Z')
188 digit
= c
- 'A' + 10;
189 else if (c
>= 'a' && c
<= 'z')
190 digit
= c
- 'a' + 10;
197 double parseIntOverflow(const LChar
* s
, unsigned length
, int radix
)
200 double radixMultiplier
= 1.0;
202 for (const LChar
* p
= s
+ length
- 1; p
>= s
; p
--) {
203 if (radixMultiplier
== std::numeric_limits
<double>::infinity()) {
205 number
= std::numeric_limits
<double>::infinity();
209 int digit
= parseDigit(*p
, radix
);
210 number
+= digit
* radixMultiplier
;
213 radixMultiplier
*= radix
;
219 static double parseIntOverflow(const UChar
* s
, unsigned length
, int radix
)
222 double radixMultiplier
= 1.0;
224 for (const UChar
* p
= s
+ length
- 1; p
>= s
; p
--) {
225 if (radixMultiplier
== std::numeric_limits
<double>::infinity()) {
227 number
= std::numeric_limits
<double>::infinity();
231 int digit
= parseDigit(*p
, radix
);
232 number
+= digit
* radixMultiplier
;
235 radixMultiplier
*= radix
;
241 static double parseIntOverflow(StringView string
, int radix
)
244 return parseIntOverflow(string
.characters8(), string
.length(), radix
);
245 return parseIntOverflow(string
.characters16(), string
.length(), radix
);
249 template <typename CharType
>
251 static double parseInt(StringView s
, const CharType
* data
, int radix
)
253 // 1. Let inputString be ToString(string).
254 // 2. Let S be a newly created substring of inputString consisting of the first character that is not a
255 // StrWhiteSpaceChar and all characters following that character. (In other words, remove leading white
256 // space.) If inputString does not contain any such characters, let S be the empty string.
257 int length
= s
.length();
259 while (p
< length
&& isStrWhiteSpace(data
[p
]))
263 // 4. If S is not empty and the first character of S is a minus sign -, let sign be -1.
264 // 5. If S is not empty and the first character of S is a plus sign + or a minus sign -, then remove the first character from S.
269 else if (data
[p
] == '-') {
275 // 6. Let R = ToInt32(radix).
276 // 7. Let stripPrefix be true.
278 // b. If R != 16, let stripPrefix be false.
281 // 10. If stripPrefix is true, then
282 // a. If the length of S is at least 2 and the first two characters of S are either ―0x or ―0X,
283 // then remove the first two characters from S and let R = 16.
284 // 11. If S contains any character that is not a radix-R digit, then let Z be the substring of S
285 // consisting of all characters before the first such character; otherwise, let Z be S.
286 if ((radix
== 0 || radix
== 16) && length
- p
>= 2 && data
[p
] == '0' && (data
[p
+ 1] == 'x' || data
[p
+ 1] == 'X')) {
289 } else if (radix
== 0)
292 // 8.a If R < 2 or R > 36, then return NaN.
293 if (radix
< 2 || radix
> 36)
296 // 13. Let mathInt be the mathematical integer value that is represented by Z in radix-R notation, using the letters
297 // A-Z and a-z for digits with values 10 through 35. (However, if R is 10 and Z contains more than 20 significant
298 // digits, every significant digit after the 20th may be replaced by a 0 digit, at the option of the implementation;
299 // and if R is not 2, 4, 8, 10, 16, or 32, then mathInt may be an implementation-dependent approximation to the
300 // mathematical integer value that is represented by Z in radix-R notation.)
301 // 14. Let number be the Number value for mathInt.
302 int firstDigitPosition
= p
;
303 bool sawDigit
= false;
306 int digit
= parseDigit(data
[p
], radix
);
315 // 12. If Z is empty, return NaN.
319 // Alternate code path for certain large numbers.
320 if (number
>= mantissaOverflowLowerBound
) {
323 number
= parseDouble(s
.substring(firstDigitPosition
, p
- firstDigitPosition
), parsedLength
);
324 } else if (radix
== 2 || radix
== 4 || radix
== 8 || radix
== 16 || radix
== 32)
325 number
= parseIntOverflow(s
.substring(firstDigitPosition
, p
- firstDigitPosition
), radix
);
328 // 15. Return sign x number.
329 return sign
* number
;
332 static double parseInt(StringView s
, int radix
)
335 return parseInt(s
, s
.characters8(), radix
);
336 return parseInt(s
, s
.characters16(), radix
);
339 static const int SizeOfInfinity
= 8;
341 template <typename CharType
>
342 static bool isInfinity(const CharType
* data
, const CharType
* end
)
344 return (end
- data
) >= SizeOfInfinity
355 // See ecma-262 6th 11.8.3
356 template <typename CharType
>
357 static double jsBinaryIntegerLiteral(const CharType
*& data
, const CharType
* end
)
361 const CharType
* firstDigitPosition
= data
;
364 number
= number
* 2 + (*data
- '0');
368 if (!isASCIIBinaryDigit(*data
))
371 if (number
>= mantissaOverflowLowerBound
)
372 number
= parseIntOverflow(firstDigitPosition
, data
- firstDigitPosition
, 2);
377 // See ecma-262 6th 11.8.3
378 template <typename CharType
>
379 static double jsOctalIntegerLiteral(const CharType
*& data
, const CharType
* end
)
383 const CharType
* firstDigitPosition
= data
;
386 number
= number
* 8 + (*data
- '0');
390 if (!isASCIIOctalDigit(*data
))
393 if (number
>= mantissaOverflowLowerBound
)
394 number
= parseIntOverflow(firstDigitPosition
, data
- firstDigitPosition
, 8);
399 // See ecma-262 6th 11.8.3
400 template <typename CharType
>
401 static double jsHexIntegerLiteral(const CharType
*& data
, const CharType
* end
)
405 const CharType
* firstDigitPosition
= data
;
408 number
= number
* 16 + toASCIIHexValue(*data
);
412 if (!isASCIIHexDigit(*data
))
415 if (number
>= mantissaOverflowLowerBound
)
416 number
= parseIntOverflow(firstDigitPosition
, data
- firstDigitPosition
, 16);
421 // See ecma-262 6th 11.8.3
422 template <typename CharType
>
423 static double jsStrDecimalLiteral(const CharType
*& data
, const CharType
* end
)
425 RELEASE_ASSERT(data
< end
);
428 double number
= parseDouble(data
, end
- data
, parsedLength
);
430 data
+= parsedLength
;
434 // Check for [+-]?Infinity
437 if (isInfinity(data
, end
)) {
438 data
+= SizeOfInfinity
;
439 return std::numeric_limits
<double>::infinity();
444 if (isInfinity(data
+ 1, end
)) {
445 data
+= SizeOfInfinity
+ 1;
446 return std::numeric_limits
<double>::infinity();
451 if (isInfinity(data
+ 1, end
)) {
452 data
+= SizeOfInfinity
+ 1;
453 return -std::numeric_limits
<double>::infinity();
462 template <typename CharType
>
463 static double toDouble(const CharType
* characters
, unsigned size
)
465 const CharType
* endCharacters
= characters
+ size
;
467 // Skip leading white space.
468 for (; characters
< endCharacters
; ++characters
) {
469 if (!isStrWhiteSpace(*characters
))
474 if (characters
== endCharacters
)
478 if (characters
[0] == '0' && characters
+ 2 < endCharacters
) {
479 if ((characters
[1] | 0x20) == 'x' && isASCIIHexDigit(characters
[2]))
480 number
= jsHexIntegerLiteral(characters
, endCharacters
);
481 else if ((characters
[1] | 0x20) == 'o' && isASCIIOctalDigit(characters
[2]))
482 number
= jsOctalIntegerLiteral(characters
, endCharacters
);
483 else if ((characters
[1] | 0x20) == 'b' && isASCIIBinaryDigit(characters
[2]))
484 number
= jsBinaryIntegerLiteral(characters
, endCharacters
);
486 number
= jsStrDecimalLiteral(characters
, endCharacters
);
488 number
= jsStrDecimalLiteral(characters
, endCharacters
);
490 // Allow trailing white space.
491 for (; characters
< endCharacters
; ++characters
) {
492 if (!isStrWhiteSpace(*characters
))
495 if (characters
!= endCharacters
)
501 // See ecma-262 6th 11.8.3
502 double jsToNumber(StringView s
)
504 unsigned size
= s
.length();
510 if (isStrWhiteSpace(c
))
516 return toDouble(s
.characters8(), size
);
517 return toDouble(s
.characters16(), size
);
520 static double parseFloat(StringView s
)
522 unsigned size
= s
.length();
532 const LChar
* data
= s
.characters8();
533 const LChar
* end
= data
+ size
;
535 // Skip leading white space.
536 for (; data
< end
; ++data
) {
537 if (!isStrWhiteSpace(*data
))
545 return jsStrDecimalLiteral(data
, end
);
548 const UChar
* data
= s
.characters16();
549 const UChar
* end
= data
+ size
;
551 // Skip leading white space.
552 for (; data
< end
; ++data
) {
553 if (!isStrWhiteSpace(*data
))
561 return jsStrDecimalLiteral(data
, end
);
564 EncodedJSValue JSC_HOST_CALL
globalFuncEval(ExecState
* exec
)
566 JSValue x
= exec
->argument(0);
568 return JSValue::encode(x
);
570 String s
= x
.toString(exec
)->value(exec
);
573 LiteralParser
<LChar
> preparser(exec
, s
.characters8(), s
.length(), NonStrictJSON
);
574 if (JSValue parsedObject
= preparser
.tryLiteralParse())
575 return JSValue::encode(parsedObject
);
577 LiteralParser
<UChar
> preparser(exec
, s
.characters16(), s
.length(), NonStrictJSON
);
578 if (JSValue parsedObject
= preparser
.tryLiteralParse())
579 return JSValue::encode(parsedObject
);
582 JSGlobalObject
* calleeGlobalObject
= exec
->callee()->globalObject();
583 EvalExecutable
* eval
= EvalExecutable::create(exec
, makeSource(s
), false, ThisTDZMode::CheckIfNeeded
);
585 return JSValue::encode(jsUndefined());
587 return JSValue::encode(exec
->interpreter()->execute(eval
, exec
, calleeGlobalObject
->globalThis(), calleeGlobalObject
));
590 EncodedJSValue JSC_HOST_CALL
globalFuncParseInt(ExecState
* exec
)
592 JSValue value
= exec
->argument(0);
593 JSValue radixValue
= exec
->argument(1);
595 // Optimized handling for numbers:
596 // If the argument is 0 or a number in range 10^-6 <= n < INT_MAX+1, then parseInt
597 // results in a truncation to integer. In the case of -0, this is converted to 0.
599 // This is also a truncation for values in the range INT_MAX+1 <= n < 10^21,
600 // however these values cannot be trivially truncated to int since 10^21 exceeds
601 // even the int64_t range. Negative numbers are a little trickier, the case for
602 // values in the range -10^21 < n <= -1 are similar to those for integer, but
603 // values in the range -1 < n <= -10^-6 need to truncate to -0, not 0.
604 static const double tenToTheMinus6
= 0.000001;
605 static const double intMaxPlusOne
= 2147483648.0;
606 if (value
.isNumber()) {
607 double n
= value
.asNumber();
608 if (((n
< intMaxPlusOne
&& n
>= tenToTheMinus6
) || !n
) && radixValue
.isUndefinedOrNull())
609 return JSValue::encode(jsNumber(static_cast<int32_t>(n
)));
612 // If ToString throws, we shouldn't call ToInt32.
613 StringView s
= value
.toString(exec
)->view(exec
);
614 if (exec
->hadException())
615 return JSValue::encode(jsUndefined());
617 return JSValue::encode(jsNumber(parseInt(s
, radixValue
.toInt32(exec
))));
620 EncodedJSValue JSC_HOST_CALL
globalFuncParseFloat(ExecState
* exec
)
622 return JSValue::encode(jsNumber(parseFloat(exec
->argument(0).toString(exec
)->view(exec
))));
625 EncodedJSValue JSC_HOST_CALL
globalFuncIsNaN(ExecState
* exec
)
627 return JSValue::encode(jsBoolean(std::isnan(exec
->argument(0).toNumber(exec
))));
630 EncodedJSValue JSC_HOST_CALL
globalFuncIsFinite(ExecState
* exec
)
632 double n
= exec
->argument(0).toNumber(exec
);
633 return JSValue::encode(jsBoolean(std::isfinite(n
)));
636 EncodedJSValue JSC_HOST_CALL
globalFuncDecodeURI(ExecState
* exec
)
638 static Bitmap
<256> doNotUnescapeWhenDecodingURI
= makeCharacterBitmap(
642 return JSValue::encode(decode(exec
, doNotUnescapeWhenDecodingURI
, true));
645 EncodedJSValue JSC_HOST_CALL
globalFuncDecodeURIComponent(ExecState
* exec
)
647 static Bitmap
<256> emptyBitmap
;
648 return JSValue::encode(decode(exec
, emptyBitmap
, true));
651 EncodedJSValue JSC_HOST_CALL
globalFuncEncodeURI(ExecState
* exec
)
653 static Bitmap
<256> doNotEscapeWhenEncodingURI
= makeCharacterBitmap(
654 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
655 "abcdefghijklmnopqrstuvwxyz"
657 "!#$&'()*+,-./:;=?@_~"
660 return JSValue::encode(encode(exec
, doNotEscapeWhenEncodingURI
));
663 EncodedJSValue JSC_HOST_CALL
globalFuncEncodeURIComponent(ExecState
* exec
)
665 static Bitmap
<256> doNotEscapeWhenEncodingURIComponent
= makeCharacterBitmap(
666 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
667 "abcdefghijklmnopqrstuvwxyz"
672 return JSValue::encode(encode(exec
, doNotEscapeWhenEncodingURIComponent
));
675 EncodedJSValue JSC_HOST_CALL
globalFuncEscape(ExecState
* exec
)
677 static Bitmap
<256> doNotEscape
= makeCharacterBitmap(
678 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
679 "abcdefghijklmnopqrstuvwxyz"
684 JSStringBuilder builder
;
685 StringView str
= exec
->argument(0).toString(exec
)->view(exec
);
687 const LChar
* c
= str
.characters8();
688 for (unsigned k
= 0; k
< str
.length(); k
++, c
++) {
690 if (u
&& doNotEscape
.get(static_cast<LChar
>(u
)))
693 builder
.append(static_cast<LChar
>('%'));
694 appendByteAsHex(static_cast<LChar
>(u
), builder
);
698 return JSValue::encode(builder
.build(exec
));
701 const UChar
* c
= str
.characters16();
702 for (unsigned k
= 0; k
< str
.length(); k
++, c
++) {
705 builder
.append(static_cast<LChar
>('%'));
706 builder
.append(static_cast<LChar
>('u'));
707 appendByteAsHex(u
>> 8, builder
);
708 appendByteAsHex(u
& 0xFF, builder
);
709 } else if (u
!= 0 && doNotEscape
.get(static_cast<LChar
>(u
)))
712 builder
.append(static_cast<LChar
>('%'));
713 appendByteAsHex(u
, builder
);
717 return JSValue::encode(builder
.build(exec
));
720 EncodedJSValue JSC_HOST_CALL
globalFuncUnescape(ExecState
* exec
)
722 StringBuilder builder
;
723 StringView str
= exec
->argument(0).toString(exec
)->view(exec
);
725 int len
= str
.length();
728 const LChar
* characters
= str
.characters8();
729 LChar convertedLChar
;
731 const LChar
* c
= characters
+ k
;
732 if (c
[0] == '%' && k
<= len
- 6 && c
[1] == 'u') {
733 if (isASCIIHexDigit(c
[2]) && isASCIIHexDigit(c
[3]) && isASCIIHexDigit(c
[4]) && isASCIIHexDigit(c
[5])) {
734 builder
.append(Lexer
<UChar
>::convertUnicode(c
[2], c
[3], c
[4], c
[5]));
738 } else if (c
[0] == '%' && k
<= len
- 3 && isASCIIHexDigit(c
[1]) && isASCIIHexDigit(c
[2])) {
739 convertedLChar
= LChar(Lexer
<LChar
>::convertHex(c
[1], c
[2]));
747 const UChar
* characters
= str
.characters16();
750 const UChar
* c
= characters
+ k
;
751 UChar convertedUChar
;
752 if (c
[0] == '%' && k
<= len
- 6 && c
[1] == 'u') {
753 if (isASCIIHexDigit(c
[2]) && isASCIIHexDigit(c
[3]) && isASCIIHexDigit(c
[4]) && isASCIIHexDigit(c
[5])) {
754 convertedUChar
= Lexer
<UChar
>::convertUnicode(c
[2], c
[3], c
[4], c
[5]);
758 } else if (c
[0] == '%' && k
<= len
- 3 && isASCIIHexDigit(c
[1]) && isASCIIHexDigit(c
[2])) {
759 convertedUChar
= UChar(Lexer
<UChar
>::convertHex(c
[1], c
[2]));
768 return JSValue::encode(jsString(exec
, builder
.toString()));
771 EncodedJSValue JSC_HOST_CALL
globalFuncThrowTypeError(ExecState
* exec
)
773 return throwVMTypeError(exec
);
776 class GlobalFuncProtoGetterFunctor
{
778 GlobalFuncProtoGetterFunctor(JSObject
* thisObject
)
779 : m_hasSkippedFirstFrame(false)
780 , m_thisObject(thisObject
)
781 , m_result(JSValue::encode(jsUndefined()))
785 EncodedJSValue
result() { return m_result
; }
787 StackVisitor::Status
operator()(StackVisitor
& visitor
)
789 if (!m_hasSkippedFirstFrame
) {
790 m_hasSkippedFirstFrame
= true;
791 return StackVisitor::Continue
;
794 if (m_thisObject
->allowsAccessFrom(visitor
->callFrame()))
795 m_result
= JSValue::encode(m_thisObject
->prototype());
797 return StackVisitor::Done
;
801 bool m_hasSkippedFirstFrame
;
802 JSObject
* m_thisObject
;
803 EncodedJSValue m_result
;
806 EncodedJSValue JSC_HOST_CALL
globalFuncProtoGetter(ExecState
* exec
)
808 if (exec
->thisValue().isUndefinedOrNull())
809 return throwVMError(exec
, createTypeError(exec
, "Can't convert undefined or null to object"));
811 JSObject
* thisObject
= jsDynamicCast
<JSObject
*>(exec
->thisValue().toThis(exec
, NotStrictMode
));
814 return JSValue::encode(exec
->thisValue().synthesizePrototype(exec
));
816 GlobalFuncProtoGetterFunctor
functor(thisObject
);
817 exec
->iterate(functor
);
818 return functor
.result();
821 class GlobalFuncProtoSetterFunctor
{
823 GlobalFuncProtoSetterFunctor(JSObject
* thisObject
)
824 : m_hasSkippedFirstFrame(false)
825 , m_allowsAccess(false)
826 , m_thisObject(thisObject
)
830 bool allowsAccess() const { return m_allowsAccess
; }
832 StackVisitor::Status
operator()(StackVisitor
& visitor
)
834 if (!m_hasSkippedFirstFrame
) {
835 m_hasSkippedFirstFrame
= true;
836 return StackVisitor::Continue
;
839 m_allowsAccess
= m_thisObject
->allowsAccessFrom(visitor
->callFrame());
840 return StackVisitor::Done
;
844 bool m_hasSkippedFirstFrame
;
846 JSObject
* m_thisObject
;
849 bool checkProtoSetterAccessAllowed(ExecState
* exec
, JSObject
* object
)
851 GlobalFuncProtoSetterFunctor
functor(object
);
852 exec
->iterate(functor
);
853 return functor
.allowsAccess();
856 EncodedJSValue JSC_HOST_CALL
globalFuncProtoSetter(ExecState
* exec
)
858 if (exec
->thisValue().isUndefinedOrNull())
859 return throwVMError(exec
, createTypeError(exec
, "Can't convert undefined or null to object"));
861 JSValue value
= exec
->argument(0);
863 JSObject
* thisObject
= jsDynamicCast
<JSObject
*>(exec
->thisValue().toThis(exec
, NotStrictMode
));
865 // Setting __proto__ of a primitive should have no effect.
867 return JSValue::encode(jsUndefined());
869 if (!checkProtoSetterAccessAllowed(exec
, thisObject
))
870 return JSValue::encode(jsUndefined());
872 // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
873 if (!value
.isObject() && !value
.isNull())
874 return JSValue::encode(jsUndefined());
876 if (!thisObject
->isExtensible())
877 return throwVMError(exec
, createTypeError(exec
, StrictModeReadonlyPropertyWriteError
));
879 if (!thisObject
->setPrototypeWithCycleCheck(exec
, value
))
880 exec
->vm().throwException(exec
, createError(exec
, ASCIILiteral("cyclic __proto__ value")));
881 return JSValue::encode(jsUndefined());
884 EncodedJSValue JSC_HOST_CALL
globalFuncBuiltinLog(ExecState
* exec
)
886 dataLog(exec
->argument(0).toWTFString(exec
), "\n");
887 return JSValue::encode(jsUndefined());