2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2003, 2008 Apple Inc. All rights reserved.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "ErrorInstance.h"
25 #include "JSCInlines.h"
26 #include "JSGlobalObjectFunctions.h"
27 #include <wtf/Vector.h>
31 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ErrorInstance
);
33 const ClassInfo
ErrorInstance::s_info
= { "Error", &JSNonFinalObject::s_info
, 0, CREATE_METHOD_TABLE(ErrorInstance
) };
35 ErrorInstance::ErrorInstance(VM
& vm
, Structure
* structure
)
36 : JSNonFinalObject(vm
, structure
)
40 static void appendSourceToError(CallFrame
* callFrame
, ErrorInstance
* exception
, unsigned bytecodeOffset
)
42 ErrorInstance::SourceAppender appender
= exception
->sourceAppender();
43 exception
->clearSourceAppender();
44 RuntimeType type
= exception
->runtimeTypeForCause();
45 exception
->clearRuntimeTypeForCause();
47 if (!callFrame
->codeBlock()->hasExpressionInfo())
56 CodeBlock
* codeBlock
= callFrame
->codeBlock();
57 codeBlock
->expressionRangeForBytecodeOffset(bytecodeOffset
, divotPoint
, startOffset
, endOffset
, line
, column
);
59 int expressionStart
= divotPoint
- startOffset
;
60 int expressionStop
= divotPoint
+ endOffset
;
62 const String
& sourceString
= codeBlock
->source()->source();
63 if (!expressionStop
|| expressionStart
> static_cast<int>(sourceString
.length()))
66 VM
* vm
= &callFrame
->vm();
67 JSValue jsMessage
= exception
->getDirect(*vm
, vm
->propertyNames
->message
);
68 if (!jsMessage
|| !jsMessage
.isString())
71 String message
= asString(jsMessage
)->value(callFrame
);
72 if (expressionStart
< expressionStop
)
73 message
= appender(message
, codeBlock
->source()->getRange(expressionStart
, expressionStop
) , type
, ErrorInstance::FoundExactSource
);
75 // No range information, so give a few characters of context.
76 const StringImpl
* data
= sourceString
.impl();
77 int dataLength
= sourceString
.length();
78 int start
= expressionStart
;
79 int stop
= expressionStart
;
80 // Get up to 20 characters of context to the left and right of the divot, clamping to the line.
81 // Then strip whitespace.
82 while (start
> 0 && (expressionStart
- start
< 20) && (*data
)[start
- 1] != '\n')
84 while (start
< (expressionStart
- 1) && isStrWhiteSpace((*data
)[start
]))
86 while (stop
< dataLength
&& (stop
- expressionStart
< 20) && (*data
)[stop
] != '\n')
88 while (stop
> expressionStart
&& isStrWhiteSpace((*data
)[stop
- 1]))
90 message
= appender(message
, codeBlock
->source()->getRange(start
, stop
), type
, ErrorInstance::FoundApproximateSource
);
92 exception
->putDirect(*vm
, vm
->propertyNames
->message
, jsString(vm
, message
));
96 class FindFirstCallerFrameWithCodeblockFunctor
{
98 FindFirstCallerFrameWithCodeblockFunctor(CallFrame
* startCallFrame
)
99 : m_startCallFrame(startCallFrame
)
100 , m_foundCallFrame(nullptr)
101 , m_foundStartCallFrame(false)
105 StackVisitor::Status
operator()(StackVisitor
& visitor
)
107 if (!m_foundStartCallFrame
&& (visitor
->callFrame() == m_startCallFrame
))
108 m_foundStartCallFrame
= true;
110 if (m_foundStartCallFrame
) {
111 if (visitor
->callFrame()->codeBlock()) {
112 m_foundCallFrame
= visitor
->callFrame();
113 return StackVisitor::Done
;
118 return StackVisitor::Continue
;
121 CallFrame
* foundCallFrame() const { return m_foundCallFrame
; }
122 unsigned index() const { return m_index
; }
125 CallFrame
* m_startCallFrame
;
126 CallFrame
* m_foundCallFrame
;
127 bool m_foundStartCallFrame
;
131 static bool addErrorInfoAndGetBytecodeOffset(ExecState
* exec
, VM
& vm
, JSObject
* obj
, bool useCurrentFrame
, CallFrame
*& callFrame
, unsigned &bytecodeOffset
)
133 Vector
<StackFrame
> stackTrace
= Vector
<StackFrame
>();
135 if (exec
&& stackTrace
.isEmpty())
136 vm
.interpreter
->getStackTrace(stackTrace
);
138 if (!stackTrace
.isEmpty()) {
140 ASSERT(exec
== vm
.topCallFrame
|| exec
== exec
->lexicalGlobalObject()->globalExec() || exec
== exec
->vmEntryGlobalObject()->globalExec());
142 StackFrame
* stackFrame
;
143 for (unsigned i
= 0 ; i
< stackTrace
.size(); ++i
) {
144 stackFrame
= &stackTrace
.at(i
);
145 if (stackFrame
->bytecodeOffset
)
149 if (bytecodeOffset
) {
150 FindFirstCallerFrameWithCodeblockFunctor
functor(exec
);
151 vm
.topCallFrame
->iterate(functor
);
152 callFrame
= functor
.foundCallFrame();
153 unsigned stackIndex
= functor
.index();
154 bytecodeOffset
= stackTrace
.at(stackIndex
).bytecodeOffset
;
159 stackFrame
->computeLineAndColumn(line
, column
);
160 obj
->putDirect(vm
, vm
.propertyNames
->line
, jsNumber(line
), ReadOnly
| DontDelete
);
161 obj
->putDirect(vm
, vm
.propertyNames
->column
, jsNumber(column
), ReadOnly
| DontDelete
);
163 if (!stackFrame
->sourceURL
.isEmpty())
164 obj
->putDirect(vm
, vm
.propertyNames
->sourceURL
, jsString(&vm
, stackFrame
->sourceURL
), ReadOnly
| DontDelete
);
166 if (!useCurrentFrame
)
167 stackTrace
.remove(0);
168 obj
->putDirect(vm
, vm
.propertyNames
->stack
, vm
.interpreter
->stackTraceAsString(vm
.topCallFrame
, stackTrace
), DontEnum
);
175 void ErrorInstance::finishCreation(ExecState
* exec
, VM
& vm
, const String
& message
, bool useCurrentFrame
)
177 Base::finishCreation(vm
);
178 ASSERT(inherits(info()));
179 if (!message
.isNull())
180 putDirect(vm
, vm
.propertyNames
->message
, jsString(&vm
, message
), DontEnum
);
182 unsigned bytecodeOffset
= hasSourceAppender();
183 CallFrame
* callFrame
= nullptr;
184 bool hasTrace
= addErrorInfoAndGetBytecodeOffset(exec
, vm
, this, useCurrentFrame
, callFrame
, bytecodeOffset
);
186 if (hasTrace
&& callFrame
&& hasSourceAppender()) {
187 if (callFrame
&& callFrame
->codeBlock())
188 appendSourceToError(callFrame
, this, bytecodeOffset
);