1/*
2 * Copyright (C) 2008, 2013-2014, 2016 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
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.
16 *
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.
27 */
28
29#include "config.h"
30#include "DebuggerCallFrame.h"
31
32#include "CatchScope.h"
33#include "CodeBlock.h"
34#include "DebuggerEvalEnabler.h"
35#include "DebuggerScope.h"
36#include "Interpreter.h"
37#include "JSCInlines.h"
38#include "JSFunction.h"
39#include "JSLexicalEnvironment.h"
40#include "JSWithScope.h"
41#include "Parser.h"
42#include "ShadowChickenInlines.h"
43#include "StackVisitor.h"
44#include "StrongInlines.h"
45
46namespace JSC {
47
48class LineAndColumnFunctor {
49public:
50 StackVisitor::Status operator()(StackVisitor& visitor) const
51 {
52 visitor->computeLineAndColumn(m_line, m_column);
53 return StackVisitor::Done;
54 }
55
56 unsigned line() const { return m_line; }
57 unsigned column() const { return m_column; }
58
59private:
60 mutable unsigned m_line { 0 };
61 mutable unsigned m_column { 0 };
62};
63
64Ref<DebuggerCallFrame> DebuggerCallFrame::create(VM& vm, CallFrame* callFrame)
65{
66 if (UNLIKELY(!callFrame)) {
67 ShadowChicken::Frame emptyFrame;
68 RELEASE_ASSERT(!emptyFrame.isTailDeleted);
69 return adoptRef(*new DebuggerCallFrame(vm, callFrame, emptyFrame));
70 }
71
72 if (callFrame->isDeprecatedCallFrameForDebugger()) {
73 ShadowChicken::Frame emptyFrame;
74 RELEASE_ASSERT(!emptyFrame.isTailDeleted);
75 return adoptRef(*new DebuggerCallFrame(vm, callFrame, emptyFrame));
76 }
77
78 Vector<ShadowChicken::Frame> frames;
79 vm.ensureShadowChicken();
80 vm.shadowChicken()->iterate(vm, callFrame, [&] (const ShadowChicken::Frame& frame) -> bool {
81 frames.append(frame);
82 return true;
83 });
84
85 RELEASE_ASSERT(frames.size());
86 ASSERT(!frames[0].isTailDeleted); // The top frame should never be tail deleted.
87
88 RefPtr<DebuggerCallFrame> currentParent = nullptr;
89 // This walks the stack from the entry stack frame to the top of the stack.
90 for (unsigned i = frames.size(); i--; ) {
91 const ShadowChicken::Frame& frame = frames[i];
92 if (!frame.isTailDeleted)
93 callFrame = frame.frame;
94 Ref<DebuggerCallFrame> currentFrame = adoptRef(*new DebuggerCallFrame(vm, callFrame, frame));
95 currentFrame->m_caller = currentParent;
96 currentParent = WTFMove(currentFrame);
97 }
98 return *currentParent;
99}
100
101DebuggerCallFrame::DebuggerCallFrame(VM& vm, CallFrame* callFrame, const ShadowChicken::Frame& frame)
102 : m_validMachineFrame(callFrame)
103 , m_shadowChickenFrame(frame)
104{
105 m_position = currentPosition(vm);
106}
107
108RefPtr<DebuggerCallFrame> DebuggerCallFrame::callerFrame()
109{
110 ASSERT(isValid());
111 if (!isValid())
112 return nullptr;
113
114 return m_caller;
115}
116
117JSGlobalObject* DebuggerCallFrame::globalObject()
118{
119 return scope()->globalObject();
120}
121
122JSC::JSGlobalObject* DebuggerCallFrame::deprecatedVMEntryGlobalObject() const
123{
124 ASSERT(isValid());
125 if (!isValid())
126 return nullptr;
127 VM& vm = m_validMachineFrame->deprecatedVM();
128 return vm.deprecatedVMEntryGlobalObject(m_validMachineFrame->lexicalGlobalObject(vm));
129}
130
131SourceID DebuggerCallFrame::sourceID() const
132{
133 ASSERT(isValid());
134 if (!isValid())
135 return noSourceID;
136 if (isTailDeleted())
137 return m_shadowChickenFrame.codeBlock->ownerExecutable()->sourceID();
138 return sourceIDForCallFrame(m_validMachineFrame);
139}
140
141String DebuggerCallFrame::functionName() const
142{
143 ASSERT(isValid());
144 if (!isValid())
145 return String();
146
147 VM& vm = m_validMachineFrame->deprecatedVM();
148 if (isTailDeleted()) {
149 if (JSFunction* func = jsDynamicCast<JSFunction*>(vm, m_shadowChickenFrame.callee))
150 return func->calculatedDisplayName(vm);
151 return m_shadowChickenFrame.codeBlock->inferredName().data();
152 }
153
154 return m_validMachineFrame->friendlyFunctionName();
155}
156
157DebuggerScope* DebuggerCallFrame::scope()
158{
159 ASSERT(isValid());
160 if (!isValid())
161 return nullptr;
162
163 if (!m_scope) {
164 VM& vm = m_validMachineFrame->deprecatedVM();
165 JSScope* scope;
166 CodeBlock* codeBlock = m_validMachineFrame->codeBlock();
167 if (isTailDeleted())
168 scope = m_shadowChickenFrame.scope;
169 else if (codeBlock && codeBlock->scopeRegister().isValid())
170 scope = m_validMachineFrame->scope(codeBlock->scopeRegister().offset());
171 else if (JSCallee* callee = jsDynamicCast<JSCallee*>(vm, m_validMachineFrame->jsCallee()))
172 scope = callee->scope();
173 else
174 scope = m_validMachineFrame->lexicalGlobalObject(vm)->globalLexicalEnvironment();
175
176 m_scope.set(vm, DebuggerScope::create(vm, scope));
177 }
178 return m_scope.get();
179}
180
181DebuggerCallFrame::Type DebuggerCallFrame::type() const
182{
183 ASSERT(isValid());
184 if (!isValid())
185 return ProgramType;
186
187 if (isTailDeleted())
188 return FunctionType;
189
190 if (jsDynamicCast<JSFunction*>(m_validMachineFrame->deprecatedVM(), m_validMachineFrame->jsCallee()))
191 return FunctionType;
192
193 return ProgramType;
194}
195
196JSValue DebuggerCallFrame::thisValue(VM& vm) const
197{
198 ASSERT(isValid());
199 if (!isValid())
200 return jsUndefined();
201
202 CodeBlock* codeBlock = nullptr;
203 JSValue thisValue;
204 if (isTailDeleted()) {
205 thisValue = m_shadowChickenFrame.thisValue;
206 codeBlock = m_shadowChickenFrame.codeBlock;
207 } else {
208 thisValue = m_validMachineFrame->thisValue();
209 codeBlock = m_validMachineFrame->codeBlock();
210 }
211
212 if (!thisValue)
213 return jsUndefined();
214
215 ECMAMode ecmaMode = NotStrictMode;
216 if (codeBlock && codeBlock->isStrictMode())
217 ecmaMode = StrictMode;
218 return thisValue.toThis(m_validMachineFrame->lexicalGlobalObject(vm), ecmaMode);
219}
220
221// Evaluate some JavaScript code in the scope of this frame.
222JSValue DebuggerCallFrame::evaluateWithScopeExtension(const String& script, JSObject* scopeExtensionObject, NakedPtr<Exception>& exception)
223{
224 ASSERT(isValid());
225 CallFrame* callFrame = m_validMachineFrame;
226 if (!callFrame)
227 return jsUndefined();
228
229 VM& vm = callFrame->deprecatedVM();
230 JSLockHolder lock(vm);
231 auto catchScope = DECLARE_CATCH_SCOPE(vm);
232
233 CodeBlock* codeBlock = nullptr;
234 if (isTailDeleted())
235 codeBlock = m_shadowChickenFrame.codeBlock;
236 else
237 codeBlock = callFrame->codeBlock();
238 if (!codeBlock)
239 return jsUndefined();
240
241 JSGlobalObject* globalObject = codeBlock->globalObject();
242 DebuggerEvalEnabler evalEnabler(globalObject, DebuggerEvalEnabler::Mode::EvalOnGlobalObjectAtDebuggerEntry);
243
244 EvalContextType evalContextType;
245
246 if (isFunctionParseMode(codeBlock->unlinkedCodeBlock()->parseMode()))
247 evalContextType = EvalContextType::FunctionEvalContext;
248 else if (codeBlock->unlinkedCodeBlock()->codeType() == EvalCode)
249 evalContextType = codeBlock->unlinkedCodeBlock()->evalContextType();
250 else
251 evalContextType = EvalContextType::None;
252
253 VariableEnvironment variablesUnderTDZ;
254 JSScope::collectClosureVariablesUnderTDZ(scope()->jsScope(), variablesUnderTDZ);
255
256 auto* eval = DirectEvalExecutable::create(globalObject, makeSource(script, callFrame->callerSourceOrigin(vm)), codeBlock->isStrictMode(), codeBlock->unlinkedCodeBlock()->derivedContextType(), codeBlock->unlinkedCodeBlock()->isArrowFunction(), evalContextType, &variablesUnderTDZ);
257 if (UNLIKELY(catchScope.exception())) {
258 exception = catchScope.exception();
259 catchScope.clearException();
260 return jsUndefined();
261 }
262
263 if (scopeExtensionObject) {
264 JSScope* ignoredPreviousScope = globalObject->globalScope();
265 globalObject->setGlobalScopeExtension(JSWithScope::create(vm, globalObject, ignoredPreviousScope, scopeExtensionObject));
266 }
267
268 JSValue thisValue = this->thisValue(vm);
269 JSValue result = vm.interpreter->execute(eval, globalObject, thisValue, scope()->jsScope());
270 if (UNLIKELY(catchScope.exception())) {
271 exception = catchScope.exception();
272 catchScope.clearException();
273 }
274
275 if (scopeExtensionObject)
276 globalObject->clearGlobalScopeExtension();
277
278 ASSERT(result);
279 return result;
280}
281
282void DebuggerCallFrame::invalidate()
283{
284 RefPtr<DebuggerCallFrame> frame = this;
285 while (frame) {
286 frame->m_validMachineFrame = nullptr;
287 if (frame->m_scope) {
288 frame->m_scope->invalidateChain();
289 frame->m_scope.clear();
290 }
291 frame = WTFMove(frame->m_caller);
292 }
293}
294
295TextPosition DebuggerCallFrame::currentPosition(VM& vm)
296{
297 if (!m_validMachineFrame)
298 return TextPosition();
299
300 if (isTailDeleted()) {
301 CodeBlock* codeBlock = m_shadowChickenFrame.codeBlock;
302 if (Optional<BytecodeIndex> bytecodeIndex = codeBlock->bytecodeIndexFromCallSiteIndex(m_shadowChickenFrame.callSiteIndex)) {
303 return TextPosition(OrdinalNumber::fromOneBasedInt(codeBlock->lineNumberForBytecodeIndex(*bytecodeIndex)),
304 OrdinalNumber::fromOneBasedInt(codeBlock->columnNumberForBytecodeIndex(*bytecodeIndex)));
305 }
306 }
307
308 return positionForCallFrame(vm, m_validMachineFrame);
309}
310
311TextPosition DebuggerCallFrame::positionForCallFrame(VM& vm, CallFrame* callFrame)
312{
313 LineAndColumnFunctor functor;
314 StackVisitor::visit(callFrame, vm, functor);
315 return TextPosition(OrdinalNumber::fromOneBasedInt(functor.line()), OrdinalNumber::fromOneBasedInt(functor.column()));
316}
317
318SourceID DebuggerCallFrame::sourceIDForCallFrame(CallFrame* callFrame)
319{
320 if (!callFrame)
321 return noSourceID;
322 CodeBlock* codeBlock = callFrame->codeBlock();
323 if (!codeBlock || callFrame->callee().isWasm())
324 return noSourceID;
325 return codeBlock->ownerExecutable()->sourceID();
326}
327
328} // namespace JSC
329