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(ExecState* exec, const String& message, ErrorInstance::SourceAppender appender) |
46 | { |
47 | ASSERT(!message.isEmpty()); |
48 | JSGlobalObject* globalObject = exec->lexicalGlobalObject(); |
49 | return ErrorInstance::create(exec, globalObject->vm(), globalObject->errorStructure(), message, appender, TypeNothing, true); |
50 | } |
51 | |
52 | JSObject* createEvalError(ExecState* exec, const String& message, ErrorInstance::SourceAppender appender) |
53 | { |
54 | ASSERT(!message.isEmpty()); |
55 | JSGlobalObject* globalObject = exec->lexicalGlobalObject(); |
56 | return ErrorInstance::create(exec, globalObject->vm(), globalObject->errorStructure(ErrorType::EvalError), message, appender, TypeNothing, true); |
57 | } |
58 | |
59 | JSObject* createRangeError(ExecState* exec, const String& message, ErrorInstance::SourceAppender appender) |
60 | { |
61 | JSGlobalObject* globalObject = exec->lexicalGlobalObject(); |
62 | return createRangeError(exec, globalObject, message, appender); |
63 | } |
64 | |
65 | JSObject* createRangeError(ExecState* exec, JSGlobalObject* globalObject, const String& message, ErrorInstance::SourceAppender appender) |
66 | { |
67 | ASSERT(!message.isEmpty()); |
68 | return ErrorInstance::create(exec, globalObject->vm(), globalObject->errorStructure(ErrorType::RangeError), message, appender, TypeNothing, true); |
69 | } |
70 | |
71 | JSObject* createReferenceError(ExecState* exec, const String& message, ErrorInstance::SourceAppender appender) |
72 | { |
73 | ASSERT(!message.isEmpty()); |
74 | JSGlobalObject* globalObject = exec->lexicalGlobalObject(); |
75 | return ErrorInstance::create(exec, globalObject->vm(), globalObject->errorStructure(ErrorType::ReferenceError), message, appender, TypeNothing, true); |
76 | } |
77 | |
78 | JSObject* createSyntaxError(ExecState* exec, const String& message, ErrorInstance::SourceAppender appender) |
79 | { |
80 | ASSERT(!message.isEmpty()); |
81 | JSGlobalObject* globalObject = exec->lexicalGlobalObject(); |
82 | return ErrorInstance::create(exec, globalObject->vm(), globalObject->errorStructure(ErrorType::SyntaxError), message, appender, TypeNothing, true); |
83 | } |
84 | |
85 | JSObject* createTypeError(ExecState* exec, const String& message, ErrorInstance::SourceAppender appender, RuntimeType type) |
86 | { |
87 | ASSERT(!message.isEmpty()); |
88 | JSGlobalObject* globalObject = exec->lexicalGlobalObject(); |
89 | return ErrorInstance::create(exec, globalObject->vm(), globalObject->errorStructure(ErrorType::TypeError), message, appender, type, true); |
90 | } |
91 | |
92 | JSObject* createNotEnoughArgumentsError(ExecState* exec, ErrorInstance::SourceAppender appender) |
93 | { |
94 | return createTypeError(exec, "Not enough arguments"_s , appender, TypeNothing); |
95 | } |
96 | |
97 | JSObject* createURIError(ExecState* exec, const String& message, ErrorInstance::SourceAppender appender) |
98 | { |
99 | ASSERT(!message.isEmpty()); |
100 | JSGlobalObject* globalObject = exec->lexicalGlobalObject(); |
101 | return ErrorInstance::create(exec, globalObject->vm(), globalObject->errorStructure(ErrorType::URIError), message, appender, TypeNothing, true); |
102 | } |
103 | |
104 | JSObject* createError(ExecState* exec, ErrorType errorType, const String& message) |
105 | { |
106 | switch (errorType) { |
107 | case ErrorType::Error: |
108 | return createError(exec, message); |
109 | case ErrorType::EvalError: |
110 | return createEvalError(exec, message); |
111 | case ErrorType::RangeError: |
112 | return createRangeError(exec, message); |
113 | case ErrorType::ReferenceError: |
114 | return createReferenceError(exec, message); |
115 | case ErrorType::SyntaxError: |
116 | return createSyntaxError(exec, message); |
117 | case ErrorType::TypeError: |
118 | return createTypeError(exec, message); |
119 | case ErrorType::URIError: |
120 | return createURIError(exec, message); |
121 | } |
122 | ASSERT_NOT_REACHED(); |
123 | return nullptr; |
124 | } |
125 | |
126 | JSObject* createGetterTypeError(ExecState* exec, const String& message) |
127 | { |
128 | ASSERT(!message.isEmpty()); |
129 | JSGlobalObject* globalObject = exec->lexicalGlobalObject(); |
130 | auto* error = ErrorInstance::create(exec, globalObject->vm(), globalObject->errorStructure(ErrorType::TypeError), message); |
131 | error->setNativeGetterTypeError(); |
132 | return error; |
133 | } |
134 | |
135 | class FindFirstCallerFrameWithCodeblockFunctor { |
136 | public: |
137 | FindFirstCallerFrameWithCodeblockFunctor(CallFrame* startCallFrame) |
138 | : m_startCallFrame(startCallFrame) |
139 | , m_foundCallFrame(nullptr) |
140 | , m_foundStartCallFrame(false) |
141 | , m_index(0) |
142 | { } |
143 | |
144 | StackVisitor::Status operator()(StackVisitor& visitor) const |
145 | { |
146 | if (!m_foundStartCallFrame && (visitor->callFrame() == m_startCallFrame)) |
147 | m_foundStartCallFrame = true; |
148 | |
149 | if (m_foundStartCallFrame) { |
150 | if (visitor->callFrame()->codeBlock()) { |
151 | m_foundCallFrame = visitor->callFrame(); |
152 | return StackVisitor::Done; |
153 | } |
154 | m_index++; |
155 | } |
156 | |
157 | return StackVisitor::Continue; |
158 | } |
159 | |
160 | CallFrame* foundCallFrame() const { return m_foundCallFrame; } |
161 | unsigned index() const { return m_index; } |
162 | |
163 | private: |
164 | CallFrame* m_startCallFrame; |
165 | mutable CallFrame* m_foundCallFrame; |
166 | mutable bool m_foundStartCallFrame; |
167 | mutable unsigned m_index; |
168 | }; |
169 | |
170 | std::unique_ptr<Vector<StackFrame>> getStackTrace(ExecState* exec, VM& vm, JSObject* obj, bool useCurrentFrame) |
171 | { |
172 | JSGlobalObject* globalObject = obj->globalObject(vm); |
173 | if (!globalObject->stackTraceLimit()) |
174 | return nullptr; |
175 | |
176 | size_t framesToSkip = useCurrentFrame ? 0 : 1; |
177 | std::unique_ptr<Vector<StackFrame>> stackTrace = std::make_unique<Vector<StackFrame>>(); |
178 | vm.interpreter->getStackTrace(obj, *stackTrace, framesToSkip, globalObject->stackTraceLimit().value()); |
179 | if (!stackTrace->isEmpty()) |
180 | ASSERT_UNUSED(exec, exec == vm.topCallFrame || exec->isGlobalExec()); |
181 | return stackTrace; |
182 | } |
183 | |
184 | void getBytecodeOffset(ExecState* exec, VM& vm, Vector<StackFrame>* stackTrace, CallFrame*& callFrame, unsigned& bytecodeOffset) |
185 | { |
186 | FindFirstCallerFrameWithCodeblockFunctor functor(exec); |
187 | StackVisitor::visit(vm.topCallFrame, &vm, functor); |
188 | callFrame = functor.foundCallFrame(); |
189 | unsigned stackIndex = functor.index(); |
190 | bytecodeOffset = 0; |
191 | if (stackTrace && stackIndex < stackTrace->size() && stackTrace->at(stackIndex).hasBytecodeOffset()) |
192 | bytecodeOffset = stackTrace->at(stackIndex).bytecodeOffset(); |
193 | } |
194 | |
195 | bool getLineColumnAndSource(Vector<StackFrame>* stackTrace, unsigned& line, unsigned& column, String& sourceURL) |
196 | { |
197 | line = 0; |
198 | column = 0; |
199 | sourceURL = String(); |
200 | |
201 | if (!stackTrace) |
202 | return false; |
203 | |
204 | for (unsigned i = 0 ; i < stackTrace->size(); ++i) { |
205 | StackFrame& frame = stackTrace->at(i); |
206 | if (frame.hasLineAndColumnInfo()) { |
207 | frame.computeLineAndColumn(line, column); |
208 | sourceURL = frame.sourceURL(); |
209 | return true; |
210 | } |
211 | } |
212 | |
213 | return false; |
214 | } |
215 | |
216 | bool addErrorInfo(VM& vm, Vector<StackFrame>* stackTrace, JSObject* obj) |
217 | { |
218 | if (!stackTrace) |
219 | return false; |
220 | |
221 | if (!stackTrace->isEmpty()) { |
222 | unsigned line; |
223 | unsigned column; |
224 | String sourceURL; |
225 | getLineColumnAndSource(stackTrace, line, column, sourceURL); |
226 | obj->putDirect(vm, vm.propertyNames->line, jsNumber(line)); |
227 | obj->putDirect(vm, vm.propertyNames->column, jsNumber(column)); |
228 | if (!sourceURL.isEmpty()) |
229 | obj->putDirect(vm, vm.propertyNames->sourceURL, jsString(&vm, sourceURL)); |
230 | |
231 | obj->putDirect(vm, vm.propertyNames->stack, jsString(&vm, Interpreter::stackTraceAsString(vm, *stackTrace)), static_cast<unsigned>(PropertyAttribute::DontEnum)); |
232 | |
233 | return true; |
234 | } |
235 | |
236 | obj->putDirect(vm, vm.propertyNames->stack, vm.smallStrings.emptyString(), static_cast<unsigned>(PropertyAttribute::DontEnum)); |
237 | return false; |
238 | } |
239 | |
240 | void addErrorInfo(ExecState* exec, JSObject* obj, bool useCurrentFrame) |
241 | { |
242 | VM& vm = exec->vm(); |
243 | std::unique_ptr<Vector<StackFrame>> stackTrace = getStackTrace(exec, vm, obj, useCurrentFrame); |
244 | addErrorInfo(vm, stackTrace.get(), obj); |
245 | } |
246 | |
247 | JSObject* addErrorInfo(CallFrame* callFrame, JSObject* error, int line, const SourceCode& source) |
248 | { |
249 | VM& vm = callFrame->vm(); |
250 | const String& sourceURL = source.provider()->url(); |
251 | |
252 | // The putDirect() calls below should really be put() so that they trigger materialization of |
253 | // the line/sourceURL properties. Otherwise, what we set here will just be overwritten later. |
254 | // But calling put() would be bad because we'd rather not do effectful things here. Luckily, we |
255 | // know that this will get called on some kind of error - so we can just directly ask the |
256 | // ErrorInstance to materialize whatever it needs to. There's a chance that we get passed some |
257 | // other kind of object, which also has materializable properties. But this code is heuristic-ey |
258 | // enough that if we're wrong in such corner cases, it's not the end of the world. |
259 | if (ErrorInstance* errorInstance = jsDynamicCast<ErrorInstance*>(vm, error)) |
260 | errorInstance->materializeErrorInfoIfNeeded(vm); |
261 | |
262 | // FIXME: This does not modify the column property, which confusingly continues to reflect |
263 | // the column at which the exception was thrown. |
264 | // https://bugs.webkit.org/show_bug.cgi?id=176673 |
265 | if (line != -1) |
266 | error->putDirect(vm, vm.propertyNames->line, jsNumber(line)); |
267 | if (!sourceURL.isNull()) |
268 | error->putDirect(vm, vm.propertyNames->sourceURL, jsString(&vm, sourceURL)); |
269 | return error; |
270 | } |
271 | |
272 | Exception* throwConstructorCannotBeCalledAsFunctionTypeError(ExecState* exec, ThrowScope& scope, const char* constructorName) |
273 | { |
274 | return throwTypeError(exec, scope, makeString("calling " , constructorName, " constructor without new is invalid" )); |
275 | } |
276 | |
277 | Exception* throwTypeError(ExecState* exec, ThrowScope& scope) |
278 | { |
279 | return throwException(exec, scope, createTypeError(exec)); |
280 | } |
281 | |
282 | Exception* throwTypeError(ExecState* exec, ThrowScope& scope, ASCIILiteral errorMessage) |
283 | { |
284 | return throwTypeError(exec, scope, String(errorMessage)); |
285 | } |
286 | |
287 | Exception* throwTypeError(ExecState* exec, ThrowScope& scope, const String& message) |
288 | { |
289 | return throwException(exec, scope, createTypeError(exec, message)); |
290 | } |
291 | |
292 | Exception* throwSyntaxError(ExecState* exec, ThrowScope& scope) |
293 | { |
294 | return throwException(exec, scope, createSyntaxError(exec, "Syntax error"_s )); |
295 | } |
296 | |
297 | Exception* throwSyntaxError(ExecState* exec, ThrowScope& scope, const String& message) |
298 | { |
299 | return throwException(exec, scope, createSyntaxError(exec, message)); |
300 | } |
301 | |
302 | Exception* throwGetterTypeError(ExecState* exec, ThrowScope& scope, const String& message) |
303 | { |
304 | return throwException(exec, scope, createGetterTypeError(exec, message)); |
305 | } |
306 | |
307 | JSValue throwDOMAttributeGetterTypeError(ExecState* exec, ThrowScope& scope, const ClassInfo* classInfo, PropertyName propertyName) |
308 | { |
309 | return throwGetterTypeError(exec, scope, makeString("The " , classInfo->className, '.', String(propertyName.uid()), " getter can only be used on instances of " , classInfo->className)); |
310 | } |
311 | |
312 | JSObject* createError(ExecState* exec, const String& message) |
313 | { |
314 | return createError(exec, message, nullptr); |
315 | } |
316 | |
317 | JSObject* createEvalError(ExecState* exec, const String& message) |
318 | { |
319 | return createEvalError(exec, message, nullptr); |
320 | } |
321 | |
322 | JSObject* createRangeError(ExecState* exec, const String& message) |
323 | { |
324 | return createRangeError(exec, message, nullptr); |
325 | } |
326 | |
327 | JSObject* createRangeError(ExecState* exec, JSGlobalObject* globalObject, const String& message) |
328 | { |
329 | return createRangeError(exec, globalObject, message, nullptr); |
330 | } |
331 | |
332 | JSObject* createReferenceError(ExecState* exec, const String& message) |
333 | { |
334 | return createReferenceError(exec, message, nullptr); |
335 | } |
336 | |
337 | JSObject* createSyntaxError(ExecState* exec, const String& message) |
338 | { |
339 | return createSyntaxError(exec, message, nullptr); |
340 | } |
341 | |
342 | JSObject* createTypeError(ExecState* exec) |
343 | { |
344 | return createTypeError(exec, "Type error"_s ); |
345 | } |
346 | |
347 | JSObject* createTypeError(ExecState* exec, const String& message) |
348 | { |
349 | return createTypeError(exec, message, nullptr, TypeNothing); |
350 | } |
351 | |
352 | JSObject* createNotEnoughArgumentsError(ExecState* exec) |
353 | { |
354 | return createNotEnoughArgumentsError(exec, nullptr); |
355 | } |
356 | |
357 | JSObject* createURIError(ExecState* exec, const String& message) |
358 | { |
359 | return createURIError(exec, message, nullptr); |
360 | } |
361 | |
362 | JSObject* createOutOfMemoryError(ExecState* exec) |
363 | { |
364 | auto* error = createError(exec, "Out of memory"_s , nullptr); |
365 | jsCast<ErrorInstance*>(error)->setOutOfMemoryError(); |
366 | return error; |
367 | } |
368 | |
369 | JSObject* createOutOfMemoryError(ExecState* exec, const String& message) |
370 | { |
371 | |
372 | auto* error = createError(exec, makeString("Out of memory: " , message), nullptr); |
373 | jsCast<ErrorInstance*>(error)->setOutOfMemoryError(); |
374 | return error; |
375 | } |
376 | |
377 | } // namespace JSC |
378 | |