1 | /* |
2 | * Copyright (C) 1999-2001 Harri Porten ([email protected]) |
3 | * Copyright (C) 2001 Peter Kelly ([email protected]) |
4 | * Copyright (C) 2003-2019 Apple Inc. All rights reserved. |
5 | * Copyright (C) 2007 Eric Seidel ([email protected]) |
6 | * |
7 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Library General Public |
9 | * License as published by the Free Software Foundation; either |
10 | * version 2 of the License, or (at your option) any later version. |
11 | * |
12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Library General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU Library General Public License |
18 | * along with this library; see the file COPYING.LIB. If not, write to |
19 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
20 | * Boston, MA 02110-1301, USA. |
21 | * |
22 | */ |
23 | |
24 | #include "config.h" |
25 | #include "Error.h" |
26 | |
27 | #include "ConstructData.h" |
28 | #include "ErrorConstructor.h" |
29 | #include "ExceptionHelpers.h" |
30 | #include "FunctionPrototype.h" |
31 | #include "Interpreter.h" |
32 | #include "JSArray.h" |
33 | #include "JSCInlines.h" |
34 | #include "JSFunction.h" |
35 | #include "JSGlobalObject.h" |
36 | #include "JSObject.h" |
37 | #include "JSString.h" |
38 | #include "NativeErrorConstructor.h" |
39 | #include "SourceCode.h" |
40 | #include "StackFrame.h" |
41 | #include "SuperSampler.h" |
42 | |
43 | namespace JSC { |
44 | |
45 | JSObject* createError(JSGlobalObject* globalObject, const String& message, ErrorInstance::SourceAppender appender) |
46 | { |
47 | ASSERT(!message.isEmpty()); |
48 | return ErrorInstance::create(globalObject, globalObject->vm(), globalObject->errorStructure(), message, appender, TypeNothing, true); |
49 | } |
50 | |
51 | JSObject* createEvalError(JSGlobalObject* globalObject, const String& message, ErrorInstance::SourceAppender appender) |
52 | { |
53 | ASSERT(!message.isEmpty()); |
54 | return ErrorInstance::create(globalObject, globalObject->vm(), globalObject->errorStructure(ErrorType::EvalError), message, appender, TypeNothing, true); |
55 | } |
56 | |
57 | JSObject* createRangeError(JSGlobalObject* globalObject, const String& message, ErrorInstance::SourceAppender appender) |
58 | { |
59 | ASSERT(!message.isEmpty()); |
60 | return ErrorInstance::create(globalObject, globalObject->vm(), globalObject->errorStructure(ErrorType::RangeError), message, appender, TypeNothing, true); |
61 | } |
62 | |
63 | JSObject* createReferenceError(JSGlobalObject* globalObject, const String& message, ErrorInstance::SourceAppender appender) |
64 | { |
65 | ASSERT(!message.isEmpty()); |
66 | return ErrorInstance::create(globalObject, globalObject->vm(), globalObject->errorStructure(ErrorType::ReferenceError), message, appender, TypeNothing, true); |
67 | } |
68 | |
69 | JSObject* createSyntaxError(JSGlobalObject* globalObject, const String& message, ErrorInstance::SourceAppender appender) |
70 | { |
71 | ASSERT(!message.isEmpty()); |
72 | return ErrorInstance::create(globalObject, globalObject->vm(), globalObject->errorStructure(ErrorType::SyntaxError), message, appender, TypeNothing, true); |
73 | } |
74 | |
75 | JSObject* createTypeError(JSGlobalObject* globalObject, const String& message, ErrorInstance::SourceAppender appender, RuntimeType type) |
76 | { |
77 | ASSERT(!message.isEmpty()); |
78 | return ErrorInstance::create(globalObject, globalObject->vm(), globalObject->errorStructure(ErrorType::TypeError), message, appender, type, true); |
79 | } |
80 | |
81 | JSObject* createNotEnoughArgumentsError(JSGlobalObject* globalObject, ErrorInstance::SourceAppender appender) |
82 | { |
83 | return createTypeError(globalObject, "Not enough arguments"_s , appender, TypeNothing); |
84 | } |
85 | |
86 | JSObject* createURIError(JSGlobalObject* globalObject, const String& message, ErrorInstance::SourceAppender appender) |
87 | { |
88 | ASSERT(!message.isEmpty()); |
89 | return ErrorInstance::create(globalObject, globalObject->vm(), globalObject->errorStructure(ErrorType::URIError), message, appender, TypeNothing, true); |
90 | } |
91 | |
92 | JSObject* createError(JSGlobalObject* globalObject, ErrorType errorType, const String& message) |
93 | { |
94 | switch (errorType) { |
95 | case ErrorType::Error: |
96 | return createError(globalObject, message); |
97 | case ErrorType::EvalError: |
98 | return createEvalError(globalObject, message); |
99 | case ErrorType::RangeError: |
100 | return createRangeError(globalObject, message); |
101 | case ErrorType::ReferenceError: |
102 | return createReferenceError(globalObject, message); |
103 | case ErrorType::SyntaxError: |
104 | return createSyntaxError(globalObject, message); |
105 | case ErrorType::TypeError: |
106 | return createTypeError(globalObject, message); |
107 | case ErrorType::URIError: |
108 | return createURIError(globalObject, message); |
109 | } |
110 | ASSERT_NOT_REACHED(); |
111 | return nullptr; |
112 | } |
113 | |
114 | JSObject* createGetterTypeError(JSGlobalObject* globalObject, const String& message) |
115 | { |
116 | ASSERT(!message.isEmpty()); |
117 | auto* error = ErrorInstance::create(globalObject, globalObject->vm(), globalObject->errorStructure(ErrorType::TypeError), message); |
118 | error->setNativeGetterTypeError(); |
119 | return error; |
120 | } |
121 | |
122 | class FindFirstCallerFrameWithCodeblockFunctor { |
123 | public: |
124 | FindFirstCallerFrameWithCodeblockFunctor(CallFrame* startCallFrame) |
125 | : m_startCallFrame(startCallFrame) |
126 | , m_foundCallFrame(nullptr) |
127 | , m_foundStartCallFrame(false) |
128 | , m_index(0) |
129 | { } |
130 | |
131 | StackVisitor::Status operator()(StackVisitor& visitor) const |
132 | { |
133 | if (!m_foundStartCallFrame && (visitor->callFrame() == m_startCallFrame)) |
134 | m_foundStartCallFrame = true; |
135 | |
136 | if (m_foundStartCallFrame) { |
137 | if (!visitor->isWasmFrame() && visitor->callFrame()->codeBlock()) { |
138 | m_foundCallFrame = visitor->callFrame(); |
139 | return StackVisitor::Done; |
140 | } |
141 | m_index++; |
142 | } |
143 | |
144 | return StackVisitor::Continue; |
145 | } |
146 | |
147 | CallFrame* foundCallFrame() const { return m_foundCallFrame; } |
148 | unsigned index() const { return m_index; } |
149 | |
150 | private: |
151 | CallFrame* m_startCallFrame; |
152 | mutable CallFrame* m_foundCallFrame; |
153 | mutable bool m_foundStartCallFrame; |
154 | mutable unsigned m_index; |
155 | }; |
156 | |
157 | std::unique_ptr<Vector<StackFrame>> getStackTrace(JSGlobalObject*, VM& vm, JSObject* obj, bool useCurrentFrame) |
158 | { |
159 | JSGlobalObject* globalObject = obj->globalObject(vm); |
160 | if (!globalObject->stackTraceLimit()) |
161 | return nullptr; |
162 | |
163 | size_t framesToSkip = useCurrentFrame ? 0 : 1; |
164 | std::unique_ptr<Vector<StackFrame>> stackTrace = makeUnique<Vector<StackFrame>>(); |
165 | vm.interpreter->getStackTrace(obj, *stackTrace, framesToSkip, globalObject->stackTraceLimit().value()); |
166 | return stackTrace; |
167 | } |
168 | |
169 | void getBytecodeIndex(VM& vm, CallFrame* startCallFrame, Vector<StackFrame>* stackTrace, CallFrame*& callFrame, BytecodeIndex& bytecodeIndex) |
170 | { |
171 | FindFirstCallerFrameWithCodeblockFunctor functor(startCallFrame); |
172 | StackVisitor::visit(vm.topCallFrame, vm, functor); |
173 | callFrame = functor.foundCallFrame(); |
174 | unsigned stackIndex = functor.index(); |
175 | bytecodeIndex = BytecodeIndex(0); |
176 | if (stackTrace && stackIndex < stackTrace->size() && stackTrace->at(stackIndex).hasBytecodeIndex()) |
177 | bytecodeIndex = stackTrace->at(stackIndex).bytecodeIndex(); |
178 | } |
179 | |
180 | bool getLineColumnAndSource(Vector<StackFrame>* stackTrace, unsigned& line, unsigned& column, String& sourceURL) |
181 | { |
182 | line = 0; |
183 | column = 0; |
184 | sourceURL = String(); |
185 | |
186 | if (!stackTrace) |
187 | return false; |
188 | |
189 | for (unsigned i = 0 ; i < stackTrace->size(); ++i) { |
190 | StackFrame& frame = stackTrace->at(i); |
191 | if (frame.hasLineAndColumnInfo()) { |
192 | frame.computeLineAndColumn(line, column); |
193 | sourceURL = frame.sourceURL(); |
194 | return true; |
195 | } |
196 | } |
197 | |
198 | return false; |
199 | } |
200 | |
201 | bool addErrorInfo(VM& vm, Vector<StackFrame>* stackTrace, JSObject* obj) |
202 | { |
203 | if (!stackTrace) |
204 | return false; |
205 | |
206 | if (!stackTrace->isEmpty()) { |
207 | unsigned line; |
208 | unsigned column; |
209 | String sourceURL; |
210 | getLineColumnAndSource(stackTrace, line, column, sourceURL); |
211 | obj->putDirect(vm, vm.propertyNames->line, jsNumber(line)); |
212 | obj->putDirect(vm, vm.propertyNames->column, jsNumber(column)); |
213 | if (!sourceURL.isEmpty()) |
214 | obj->putDirect(vm, vm.propertyNames->sourceURL, jsString(vm, sourceURL)); |
215 | |
216 | obj->putDirect(vm, vm.propertyNames->stack, jsString(vm, Interpreter::stackTraceAsString(vm, *stackTrace)), static_cast<unsigned>(PropertyAttribute::DontEnum)); |
217 | |
218 | return true; |
219 | } |
220 | |
221 | obj->putDirect(vm, vm.propertyNames->stack, vm.smallStrings.emptyString(), static_cast<unsigned>(PropertyAttribute::DontEnum)); |
222 | return false; |
223 | } |
224 | |
225 | void addErrorInfo(JSGlobalObject* globalObject, JSObject* obj, bool useCurrentFrame) |
226 | { |
227 | VM& vm = globalObject->vm(); |
228 | std::unique_ptr<Vector<StackFrame>> stackTrace = getStackTrace(globalObject, vm, obj, useCurrentFrame); |
229 | addErrorInfo(vm, stackTrace.get(), obj); |
230 | } |
231 | |
232 | JSObject* addErrorInfo(VM& vm, JSObject* error, int line, const SourceCode& source) |
233 | { |
234 | const String& sourceURL = source.provider()->url(); |
235 | |
236 | // The putDirect() calls below should really be put() so that they trigger materialization of |
237 | // the line/sourceURL properties. Otherwise, what we set here will just be overwritten later. |
238 | // But calling put() would be bad because we'd rather not do effectful things here. Luckily, we |
239 | // know that this will get called on some kind of error - so we can just directly ask the |
240 | // ErrorInstance to materialize whatever it needs to. There's a chance that we get passed some |
241 | // other kind of object, which also has materializable properties. But this code is heuristic-ey |
242 | // enough that if we're wrong in such corner cases, it's not the end of the world. |
243 | if (ErrorInstance* errorInstance = jsDynamicCast<ErrorInstance*>(vm, error)) |
244 | errorInstance->materializeErrorInfoIfNeeded(vm); |
245 | |
246 | // FIXME: This does not modify the column property, which confusingly continues to reflect |
247 | // the column at which the exception was thrown. |
248 | // https://bugs.webkit.org/show_bug.cgi?id=176673 |
249 | if (line != -1) |
250 | error->putDirect(vm, vm.propertyNames->line, jsNumber(line)); |
251 | if (!sourceURL.isNull()) |
252 | error->putDirect(vm, vm.propertyNames->sourceURL, jsString(vm, sourceURL)); |
253 | return error; |
254 | } |
255 | |
256 | Exception* throwConstructorCannotBeCalledAsFunctionTypeError(JSGlobalObject* globalObject, ThrowScope& scope, const char* constructorName) |
257 | { |
258 | return throwTypeError(globalObject, scope, makeString("calling " , constructorName, " constructor without new is invalid" )); |
259 | } |
260 | |
261 | Exception* throwTypeError(JSGlobalObject* globalObject, ThrowScope& scope) |
262 | { |
263 | return throwException(globalObject, scope, createTypeError(globalObject)); |
264 | } |
265 | |
266 | Exception* throwTypeError(JSGlobalObject* globalObject, ThrowScope& scope, ASCIILiteral errorMessage) |
267 | { |
268 | return throwTypeError(globalObject, scope, String(errorMessage)); |
269 | } |
270 | |
271 | Exception* throwTypeError(JSGlobalObject* globalObject, ThrowScope& scope, const String& message) |
272 | { |
273 | return throwException(globalObject, scope, createTypeError(globalObject, message)); |
274 | } |
275 | |
276 | Exception* throwSyntaxError(JSGlobalObject* globalObject, ThrowScope& scope) |
277 | { |
278 | return throwException(globalObject, scope, createSyntaxError(globalObject, "Syntax error"_s )); |
279 | } |
280 | |
281 | Exception* throwSyntaxError(JSGlobalObject* globalObject, ThrowScope& scope, const String& message) |
282 | { |
283 | return throwException(globalObject, scope, createSyntaxError(globalObject, message)); |
284 | } |
285 | |
286 | Exception* throwGetterTypeError(JSGlobalObject* globalObject, ThrowScope& scope, const String& message) |
287 | { |
288 | return throwException(globalObject, scope, createGetterTypeError(globalObject, message)); |
289 | } |
290 | |
291 | JSValue throwDOMAttributeGetterTypeError(JSGlobalObject* globalObject, ThrowScope& scope, const ClassInfo* classInfo, PropertyName propertyName) |
292 | { |
293 | return throwGetterTypeError(globalObject, scope, makeString("The " , classInfo->className, '.', String(propertyName.uid()), " getter can only be used on instances of " , classInfo->className)); |
294 | } |
295 | |
296 | JSObject* createError(JSGlobalObject* globalObject, const String& message) |
297 | { |
298 | return createError(globalObject, message, nullptr); |
299 | } |
300 | |
301 | JSObject* createEvalError(JSGlobalObject* globalObject, const String& message) |
302 | { |
303 | return createEvalError(globalObject, message, nullptr); |
304 | } |
305 | |
306 | JSObject* createRangeError(JSGlobalObject* globalObject, const String& message) |
307 | { |
308 | return createRangeError(globalObject, message, nullptr); |
309 | } |
310 | |
311 | JSObject* createReferenceError(JSGlobalObject* globalObject, const String& message) |
312 | { |
313 | return createReferenceError(globalObject, message, nullptr); |
314 | } |
315 | |
316 | JSObject* createSyntaxError(JSGlobalObject* globalObject, const String& message) |
317 | { |
318 | return createSyntaxError(globalObject, message, nullptr); |
319 | } |
320 | |
321 | JSObject* createTypeError(JSGlobalObject* globalObject) |
322 | { |
323 | return createTypeError(globalObject, "Type error"_s ); |
324 | } |
325 | |
326 | JSObject* createTypeError(JSGlobalObject* globalObject, const String& message) |
327 | { |
328 | return createTypeError(globalObject, message, nullptr, TypeNothing); |
329 | } |
330 | |
331 | JSObject* createNotEnoughArgumentsError(JSGlobalObject* globalObject) |
332 | { |
333 | return createNotEnoughArgumentsError(globalObject, nullptr); |
334 | } |
335 | |
336 | JSObject* createURIError(JSGlobalObject* globalObject, const String& message) |
337 | { |
338 | return createURIError(globalObject, message, nullptr); |
339 | } |
340 | |
341 | JSObject* createOutOfMemoryError(JSGlobalObject* globalObject) |
342 | { |
343 | auto* error = createError(globalObject, "Out of memory"_s , nullptr); |
344 | jsCast<ErrorInstance*>(error)->setOutOfMemoryError(); |
345 | return error; |
346 | } |
347 | |
348 | JSObject* createOutOfMemoryError(JSGlobalObject* globalObject, const String& message) |
349 | { |
350 | |
351 | auto* error = createError(globalObject, makeString("Out of memory: " , message), nullptr); |
352 | jsCast<ErrorInstance*>(error)->setOutOfMemoryError(); |
353 | return error; |
354 | } |
355 | |
356 | } // namespace JSC |
357 | |