[WebAssembly] Inspector's DebuggerCallFrame should be aware of Wasm frames
[WebKit-https.git] / Source / JavaScriptCore / debugger / DebuggerCallFrame.cpp
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
46 namespace JSC {
47
48 class LineAndColumnFunctor {
49 public:
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
59 private:
60     mutable unsigned m_line { 0 };
61     mutable unsigned m_column { 0 };
62 };
63
64 Ref<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
101 DebuggerCallFrame::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
108 RefPtr<DebuggerCallFrame> DebuggerCallFrame::callerFrame()
109 {
110     ASSERT(isValid());
111     if (!isValid())
112         return nullptr;
113
114     return m_caller;
115 }
116
117 JSGlobalObject* DebuggerCallFrame::globalObject()
118 {
119     return scope()->globalObject();
120 }
121
122 JSC::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
131 SourceID 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
141 String 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
157 DebuggerScope* 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
181 DebuggerCallFrame::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
196 JSValue 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.
222 JSValue 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
282 void 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
295 TextPosition 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
311 TextPosition 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
318 SourceID 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