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 == callFrame->wasmAwareLexicalGlobalObject(vm)->globalExec())) {
67 ShadowChicken::Frame emptyFrame;
68 RELEASE_ASSERT(!emptyFrame.isTailDeleted);
69 return adoptRef(*new DebuggerCallFrame(vm, callFrame, emptyFrame));
70 }
71
72 Vector<ShadowChicken::Frame> frames;
73 vm.ensureShadowChicken();
74 vm.shadowChicken()->iterate(vm, callFrame, [&] (const ShadowChicken::Frame& frame) -> bool {
75 frames.append(frame);
76 return true;
77 });
78
79 RELEASE_ASSERT(frames.size());
80 ASSERT(!frames[0].isTailDeleted); // The top frame should never be tail deleted.
81
82 RefPtr<DebuggerCallFrame> currentParent = nullptr;
83 ExecState* exec = callFrame->wasmAwareLexicalGlobalObject(vm)->globalExec();
84 // This walks the stack from the entry stack frame to the top of the stack.
85 for (unsigned i = frames.size(); i--; ) {
86 const ShadowChicken::Frame& frame = frames[i];
87 if (!frame.isTailDeleted)
88 exec = frame.frame;
89 Ref<DebuggerCallFrame> currentFrame = adoptRef(*new DebuggerCallFrame(vm, exec, frame));
90 currentFrame->m_caller = currentParent;
91 currentParent = WTFMove(currentFrame);
92 }
93 return *currentParent;
94}
95
96DebuggerCallFrame::DebuggerCallFrame(VM& vm, CallFrame* callFrame, const ShadowChicken::Frame& frame)
97 : m_validMachineFrame(callFrame)
98 , m_shadowChickenFrame(frame)
99{
100 m_position = currentPosition(vm);
101}
102
103RefPtr<DebuggerCallFrame> DebuggerCallFrame::callerFrame()
104{
105 ASSERT(isValid());
106 if (!isValid())
107 return nullptr;
108
109 return m_caller;
110}
111
112ExecState* DebuggerCallFrame::globalExec()
113{
114 return scope()->globalObject()->globalExec();
115}
116
117JSC::JSGlobalObject* DebuggerCallFrame::vmEntryGlobalObject() const
118{
119 ASSERT(isValid());
120 if (!isValid())
121 return nullptr;
122 VM& vm = m_validMachineFrame->vm();
123 return vm.vmEntryGlobalObject(m_validMachineFrame);
124}
125
126SourceID DebuggerCallFrame::sourceID() const
127{
128 ASSERT(isValid());
129 if (!isValid())
130 return noSourceID;
131 if (isTailDeleted())
132 return m_shadowChickenFrame.codeBlock->ownerExecutable()->sourceID();
133 return sourceIDForCallFrame(m_validMachineFrame);
134}
135
136String DebuggerCallFrame::functionName() const
137{
138 ASSERT(isValid());
139 if (!isValid())
140 return String();
141
142 VM& vm = m_validMachineFrame->vm();
143 if (isTailDeleted()) {
144 if (JSFunction* func = jsDynamicCast<JSFunction*>(vm, m_shadowChickenFrame.callee))
145 return func->calculatedDisplayName(vm);
146 return m_shadowChickenFrame.codeBlock->inferredName().data();
147 }
148
149 return m_validMachineFrame->friendlyFunctionName();
150}
151
152DebuggerScope* DebuggerCallFrame::scope()
153{
154 ASSERT(isValid());
155 if (!isValid())
156 return nullptr;
157
158 if (!m_scope) {
159 VM& vm = m_validMachineFrame->vm();
160 JSScope* scope;
161 CodeBlock* codeBlock = m_validMachineFrame->codeBlock();
162 if (isTailDeleted())
163 scope = m_shadowChickenFrame.scope;
164 else if (codeBlock && codeBlock->scopeRegister().isValid())
165 scope = m_validMachineFrame->scope(codeBlock->scopeRegister().offset());
166 else if (JSCallee* callee = jsDynamicCast<JSCallee*>(vm, m_validMachineFrame->jsCallee()))
167 scope = callee->scope();
168 else
169 scope = m_validMachineFrame->lexicalGlobalObject()->globalLexicalEnvironment();
170
171 m_scope.set(vm, DebuggerScope::create(vm, scope));
172 }
173 return m_scope.get();
174}
175
176DebuggerCallFrame::Type DebuggerCallFrame::type() const
177{
178 ASSERT(isValid());
179 if (!isValid())
180 return ProgramType;
181
182 if (isTailDeleted())
183 return FunctionType;
184
185 if (jsDynamicCast<JSFunction*>(m_validMachineFrame->vm(), m_validMachineFrame->jsCallee()))
186 return FunctionType;
187
188 return ProgramType;
189}
190
191JSValue DebuggerCallFrame::thisValue() const
192{
193 ASSERT(isValid());
194 if (!isValid())
195 return jsUndefined();
196
197 CodeBlock* codeBlock = nullptr;
198 JSValue thisValue;
199 if (isTailDeleted()) {
200 thisValue = m_shadowChickenFrame.thisValue;
201 codeBlock = m_shadowChickenFrame.codeBlock;
202 } else {
203 thisValue = m_validMachineFrame->thisValue();
204 codeBlock = m_validMachineFrame->codeBlock();
205 }
206
207 if (!thisValue)
208 return jsUndefined();
209
210 ECMAMode ecmaMode = NotStrictMode;
211 if (codeBlock && codeBlock->isStrictMode())
212 ecmaMode = StrictMode;
213 return thisValue.toThis(m_validMachineFrame, ecmaMode);
214}
215
216// Evaluate some JavaScript code in the scope of this frame.
217JSValue DebuggerCallFrame::evaluateWithScopeExtension(const String& script, JSObject* scopeExtensionObject, NakedPtr<Exception>& exception)
218{
219 ASSERT(isValid());
220 CallFrame* callFrame = m_validMachineFrame;
221 if (!callFrame)
222 return jsUndefined();
223
224 VM& vm = callFrame->vm();
225 JSLockHolder lock(vm);
226 auto catchScope = DECLARE_CATCH_SCOPE(vm);
227
228 CodeBlock* codeBlock = nullptr;
229 if (isTailDeleted())
230 codeBlock = m_shadowChickenFrame.codeBlock;
231 else
232 codeBlock = callFrame->codeBlock();
233 if (!codeBlock)
234 return jsUndefined();
235
236 DebuggerEvalEnabler evalEnabler(callFrame, DebuggerEvalEnabler::Mode::EvalOnCallFrameAtDebuggerEntry);
237
238 EvalContextType evalContextType;
239
240 if (isFunctionParseMode(codeBlock->unlinkedCodeBlock()->parseMode()))
241 evalContextType = EvalContextType::FunctionEvalContext;
242 else if (codeBlock->unlinkedCodeBlock()->codeType() == EvalCode)
243 evalContextType = codeBlock->unlinkedCodeBlock()->evalContextType();
244 else
245 evalContextType = EvalContextType::None;
246
247 VariableEnvironment variablesUnderTDZ;
248 JSScope::collectClosureVariablesUnderTDZ(scope()->jsScope(), variablesUnderTDZ);
249
250 auto* eval = DirectEvalExecutable::create(callFrame, makeSource(script, callFrame->callerSourceOrigin()), codeBlock->isStrictMode(), codeBlock->unlinkedCodeBlock()->derivedContextType(), codeBlock->unlinkedCodeBlock()->isArrowFunction(), evalContextType, &variablesUnderTDZ);
251 if (UNLIKELY(catchScope.exception())) {
252 exception = catchScope.exception();
253 catchScope.clearException();
254 return jsUndefined();
255 }
256
257 JSGlobalObject* globalObject = vm.vmEntryGlobalObject(callFrame);
258 if (scopeExtensionObject) {
259 JSScope* ignoredPreviousScope = globalObject->globalScope();
260 globalObject->setGlobalScopeExtension(JSWithScope::create(vm, globalObject, ignoredPreviousScope, scopeExtensionObject));
261 }
262
263 JSValue thisValue = this->thisValue();
264 JSValue result = vm.interpreter->execute(eval, callFrame, thisValue, scope()->jsScope());
265 if (UNLIKELY(catchScope.exception())) {
266 exception = catchScope.exception();
267 catchScope.clearException();
268 }
269
270 if (scopeExtensionObject)
271 globalObject->clearGlobalScopeExtension();
272
273 ASSERT(result);
274 return result;
275}
276
277void DebuggerCallFrame::invalidate()
278{
279 RefPtr<DebuggerCallFrame> frame = this;
280 while (frame) {
281 frame->m_validMachineFrame = nullptr;
282 if (frame->m_scope) {
283 frame->m_scope->invalidateChain();
284 frame->m_scope.clear();
285 }
286 frame = WTFMove(frame->m_caller);
287 }
288}
289
290TextPosition DebuggerCallFrame::currentPosition(VM& vm)
291{
292 if (!m_validMachineFrame)
293 return TextPosition();
294
295 if (isTailDeleted()) {
296 CodeBlock* codeBlock = m_shadowChickenFrame.codeBlock;
297 if (Optional<unsigned> bytecodeOffset = codeBlock->bytecodeOffsetFromCallSiteIndex(m_shadowChickenFrame.callSiteIndex)) {
298 return TextPosition(OrdinalNumber::fromOneBasedInt(codeBlock->lineNumberForBytecodeOffset(*bytecodeOffset)),
299 OrdinalNumber::fromOneBasedInt(codeBlock->columnNumberForBytecodeOffset(*bytecodeOffset)));
300 }
301 }
302
303 return positionForCallFrame(vm, m_validMachineFrame);
304}
305
306TextPosition DebuggerCallFrame::positionForCallFrame(VM& vm, CallFrame* callFrame)
307{
308 LineAndColumnFunctor functor;
309 StackVisitor::visit(callFrame, &vm, functor);
310 return TextPosition(OrdinalNumber::fromOneBasedInt(functor.line()), OrdinalNumber::fromOneBasedInt(functor.column()));
311}
312
313SourceID DebuggerCallFrame::sourceIDForCallFrame(CallFrame* callFrame)
314{
315 ASSERT(callFrame);
316 CodeBlock* codeBlock = callFrame->codeBlock();
317 if (!codeBlock)
318 return noSourceID;
319 return codeBlock->ownerExecutable()->sourceID();
320}
321
322} // namespace JSC
323