2 * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include "ExceptionHelpers.h"
32 #include "CodeBlock.h"
33 #include "CallFrame.h"
34 #include "ErrorHandlingScope.h"
35 #include "Exception.h"
36 #include "JSGlobalObjectFunctions.h"
37 #include "JSNotAnObject.h"
38 #include "Interpreter.h"
40 #include "JSCInlines.h"
41 #include "RuntimeType.h"
42 #include <wtf/text/StringBuilder.h>
43 #include <wtf/text/StringView.h>
47 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(TerminatedExecutionError
);
49 const ClassInfo
TerminatedExecutionError::s_info
= { "TerminatedExecutionError", &Base::s_info
, 0, CREATE_METHOD_TABLE(TerminatedExecutionError
) };
51 JSValue
TerminatedExecutionError::defaultValue(const JSObject
*, ExecState
* exec
, PreferredPrimitiveType hint
)
53 if (hint
== PreferString
)
54 return jsNontrivialString(exec
, String(ASCIILiteral("JavaScript execution terminated.")));
58 JSObject
* createTerminatedExecutionException(VM
* vm
)
60 return TerminatedExecutionError::create(*vm
);
63 bool isTerminatedExecutionException(Exception
* exception
)
65 return exception
->value().inherits(TerminatedExecutionError::info());
68 JSObject
* createStackOverflowError(ExecState
* exec
)
70 return createRangeError(exec
, ASCIILiteral("Maximum call stack size exceeded."));
73 JSObject
* createUndefinedVariableError(ExecState
* exec
, const Identifier
& ident
)
75 if (exec
->propertyNames().isPrivateName(ident
)) {
76 String
message(makeString("Can't find private variable: @", exec
->propertyNames().getPublicName(ident
).string()));
77 return createReferenceError(exec
, message
);
79 String
message(makeString("Can't find variable: ", ident
.string()));
80 return createReferenceError(exec
, message
);
83 JSString
* errorDescriptionForValue(ExecState
* exec
, JSValue v
)
86 return jsNontrivialString(exec
, makeString('"', asString(v
)->value(exec
), '"'));
89 JSObject
* object
= asObject(v
);
90 if (object
->methodTable()->getCallData(object
, callData
) != CallTypeNone
)
91 return exec
->vm().smallStrings
.functionString();
92 return jsString(exec
, JSObject::calculatedClassName(object
));
94 return v
.toString(exec
);
97 static String
defaultApproximateSourceError(const String
& originalMessage
, const String
& sourceText
)
99 return makeString(originalMessage
, " (near '...", sourceText
, "...')");
102 static String
defaultSourceAppender(const String
& originalMessage
, const String
& sourceText
, RuntimeType
, ErrorInstance::SourceTextWhereErrorOccurred occurrence
)
104 if (occurrence
== ErrorInstance::FoundApproximateSource
)
105 return defaultApproximateSourceError(originalMessage
, sourceText
);
107 ASSERT(occurrence
== ErrorInstance::FoundExactSource
);
108 return makeString(originalMessage
, " (evaluating '", sourceText
, "')");
111 static String
functionCallBase(const String
& sourceText
)
113 // This function retrieves the 'foo.bar' substring from 'foo.bar(baz)'.
114 // FIXME: This function has simple processing of /* */ style comments.
115 // It doesn't properly handle embedded comments of string literals that contain
116 // parenthesis or comment constructs, e.g. foo.bar("/abc\)*/").
117 // https://bugs.webkit.org/show_bug.cgi?id=146304
119 unsigned sourceLength
= sourceText
.length();
120 unsigned idx
= sourceLength
- 1;
121 if (sourceLength
< 2 || sourceText
[idx
] != ')') {
122 // For function calls that have many new lines in between their open parenthesis
123 // and their closing parenthesis, the text range passed into the message appender
124 // will not inlcude the text in between these parentheses, it will just be the desired
125 // text that precedes the parentheses.
129 unsigned parenStack
= 1;
130 bool isInMultiLineComment
= false;
132 // Note that we're scanning text right to left instead of the more common left to right,
133 // so syntax detection is backwards.
134 while (parenStack
> 0) {
135 UChar curChar
= sourceText
[idx
];
136 if (isInMultiLineComment
) {
137 if (idx
> 0 && curChar
== '*' && sourceText
[idx
- 1] == '/') {
138 isInMultiLineComment
= false;
141 } else if (curChar
== '(')
143 else if (curChar
== ')')
145 else if (idx
> 0 && curChar
== '/' && sourceText
[idx
- 1] == '*') {
146 isInMultiLineComment
= true;
156 return sourceText
.left(idx
+ 1);
159 static String
notAFunctionSourceAppender(const String
& originalMessage
, const String
& sourceText
, RuntimeType type
, ErrorInstance::SourceTextWhereErrorOccurred occurrence
)
161 ASSERT(type
!= TypeFunction
);
163 if (occurrence
== ErrorInstance::FoundApproximateSource
)
164 return defaultApproximateSourceError(originalMessage
, sourceText
);
166 ASSERT(occurrence
== ErrorInstance::FoundExactSource
);
167 auto notAFunctionIndex
= originalMessage
.reverseFind("is not a function");
168 RELEASE_ASSERT(notAFunctionIndex
!= notFound
);
169 StringView displayValue
;
170 if (originalMessage
.is8Bit())
171 displayValue
= StringView(originalMessage
.characters8(), notAFunctionIndex
- 1);
173 displayValue
= StringView(originalMessage
.characters16(), notAFunctionIndex
- 1);
175 String base
= functionCallBase(sourceText
);
176 StringBuilder builder
;
177 builder
.append(base
);
178 builder
.appendLiteral(" is not a function. (In '");
179 builder
.append(sourceText
);
180 builder
.appendLiteral("', '");
181 builder
.append(base
);
182 builder
.appendLiteral("' is ");
183 if (type
== TypeObject
)
184 builder
.appendLiteral("an instance of ");
185 builder
.append(displayValue
);
186 builder
.appendLiteral(")");
188 return builder
.toString();
191 static String
invalidParameterInSourceAppender(const String
& originalMessage
, const String
& sourceText
, RuntimeType type
, ErrorInstance::SourceTextWhereErrorOccurred occurrence
)
193 ASSERT_UNUSED(type
, type
!= TypeObject
);
195 if (occurrence
== ErrorInstance::FoundApproximateSource
)
196 return defaultApproximateSourceError(originalMessage
, sourceText
);
198 ASSERT(occurrence
== ErrorInstance::FoundExactSource
);
199 auto inIndex
= sourceText
.reverseFind("in");
200 RELEASE_ASSERT(inIndex
!= notFound
);
201 if (sourceText
.find("in") != inIndex
)
202 return makeString(originalMessage
, " (evaluating '", sourceText
, "')");
204 static const unsigned inLength
= 2;
205 String rightHandSide
= sourceText
.substring(inIndex
+ inLength
).simplifyWhiteSpace();
206 return makeString(rightHandSide
, " is not an Object. (evaluating '", sourceText
, "')");
209 static String
invalidParameterInstanceofSourceAppender(const String
& originalMessage
, const String
& sourceText
, RuntimeType
, ErrorInstance::SourceTextWhereErrorOccurred occurrence
)
211 if (occurrence
== ErrorInstance::FoundApproximateSource
)
212 return defaultApproximateSourceError(originalMessage
, sourceText
);
214 ASSERT(occurrence
== ErrorInstance::FoundExactSource
);
215 auto instanceofIndex
= sourceText
.reverseFind("instanceof");
216 RELEASE_ASSERT(instanceofIndex
!= notFound
);
217 if (sourceText
.find("instanceof") != instanceofIndex
)
218 return makeString(originalMessage
, " (evaluating '", sourceText
, "')");
220 static const unsigned instanceofLength
= 10;
221 String rightHandSide
= sourceText
.substring(instanceofIndex
+ instanceofLength
).simplifyWhiteSpace();
222 return makeString(rightHandSide
, " is not a function. (evaluating '", sourceText
, "')");
225 JSObject
* createError(ExecState
* exec
, JSValue value
, const String
& message
, ErrorInstance::SourceAppender appender
)
227 String errorMessage
= makeString(errorDescriptionForValue(exec
, value
)->value(exec
), ' ', message
);
228 JSObject
* exception
= createTypeError(exec
, errorMessage
, appender
, runtimeTypeForValue(value
));
229 ASSERT(exception
->isErrorInstance());
233 JSObject
* createInvalidFunctionApplyParameterError(ExecState
* exec
, JSValue value
)
235 JSObject
* exception
= createTypeError(exec
, makeString("second argument to Function.prototype.apply must be an Array-like object"), defaultSourceAppender
, runtimeTypeForValue(value
));
236 ASSERT(exception
->isErrorInstance());
240 JSObject
* createInvalidInParameterError(ExecState
* exec
, JSValue value
)
242 return createError(exec
, value
, makeString("is not an Object."), invalidParameterInSourceAppender
);
245 JSObject
* createInvalidInstanceofParameterError(ExecState
* exec
, JSValue value
)
247 return createError(exec
, value
, makeString("is not a function."), invalidParameterInstanceofSourceAppender
);
250 JSObject
* createNotAConstructorError(ExecState
* exec
, JSValue value
)
252 return createError(exec
, value
, ASCIILiteral("is not a constructor"), defaultSourceAppender
);
255 JSObject
* createNotAFunctionError(ExecState
* exec
, JSValue value
)
257 return createError(exec
, value
, ASCIILiteral("is not a function"), notAFunctionSourceAppender
);
260 JSObject
* createNotAnObjectError(ExecState
* exec
, JSValue value
)
262 return createError(exec
, value
, ASCIILiteral("is not an object"), defaultSourceAppender
);
265 JSObject
* createErrorForInvalidGlobalAssignment(ExecState
* exec
, const String
& propertyName
)
267 return createReferenceError(exec
, makeString("Strict mode forbids implicit creation of global property '", propertyName
, '\''));
270 JSObject
* throwOutOfMemoryError(ExecState
* exec
)
272 return exec
->vm().throwException(exec
, createOutOfMemoryError(exec
));
275 JSObject
* throwStackOverflowError(ExecState
* exec
)
278 ErrorHandlingScope
errorScope(vm
);
279 return vm
.throwException(exec
, createStackOverflowError(exec
));
282 JSObject
* throwTerminatedExecutionException(ExecState
* exec
)
285 ErrorHandlingScope
errorScope(vm
);
286 return vm
.throwException(exec
, createTerminatedExecutionException(&vm
));