Disable function.arguments
[WebKit-https.git] / Source / JavaScriptCore / interpreter / StackVisitor.cpp
1 /*
2  * Copyright (C) 2013 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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "StackVisitor.h"
28
29 #include "Arguments.h"
30 #include "CallFrameInlines.h"
31 #include "Executable.h"
32 #include "Interpreter.h"
33 #include "JSCInlines.h"
34 #include <wtf/DataLog.h>
35
36 namespace JSC {
37
38 StackVisitor::StackVisitor(CallFrame* startFrame)
39 {
40     m_frame.m_index = 0;
41     CallFrame* topFrame;
42     if (startFrame) {
43         m_frame.m_VMEntryFrame = startFrame->vm().topVMEntryFrame;
44         topFrame = startFrame->vm().topCallFrame;
45     } else {
46         m_frame.m_VMEntryFrame = 0;
47         topFrame = 0;
48     }
49     m_frame.m_callerIsVMEntryFrame = false;
50     readFrame(topFrame);
51
52     // Find the frame the caller wants to start unwinding from.
53     while (m_frame.callFrame() && m_frame.callFrame() != startFrame)
54         gotoNextFrame();
55 }
56
57 void StackVisitor::gotoNextFrame()
58 {
59 #if ENABLE(DFG_JIT)
60     if (m_frame.isInlinedFrame()) {
61         InlineCallFrame* inlineCallFrame = m_frame.inlineCallFrame();
62         CodeOrigin* callerCodeOrigin = &inlineCallFrame->caller;
63         readInlinedFrame(m_frame.callFrame(), callerCodeOrigin);
64         return;
65     }
66 #endif // ENABLE(DFG_JIT)
67     m_frame.m_VMEntryFrame = m_frame.m_CallerVMEntryFrame;
68     readFrame(m_frame.callerFrame());
69 }
70
71 void StackVisitor::readFrame(CallFrame* callFrame)
72 {
73     if (!callFrame) {
74         m_frame.setToEnd();
75         return;
76     }
77
78 #if !ENABLE(DFG_JIT)
79     readNonInlinedFrame(callFrame);
80
81 #else // !ENABLE(DFG_JIT)
82     // If the frame doesn't have a code block, then it's not a DFG frame.
83     // Hence, we're not at an inlined frame.
84     CodeBlock* codeBlock = callFrame->codeBlock();
85     if (!codeBlock) {
86         readNonInlinedFrame(callFrame);
87         return;
88     }
89
90     // If the code block does not have any code origins, then there's no
91     // inlining. Hence, we're not at an inlined frame.
92     if (!codeBlock->hasCodeOrigins()) {
93         readNonInlinedFrame(callFrame);
94         return;
95     }
96
97     unsigned index = callFrame->locationAsCodeOriginIndex();
98     ASSERT(codeBlock->canGetCodeOrigin(index));
99     if (!codeBlock->canGetCodeOrigin(index)) {
100         // See assertion above. In release builds, we try to protect ourselves
101         // from crashing even though stack walking will be goofed up.
102         m_frame.setToEnd();
103         return;
104     }
105
106     CodeOrigin codeOrigin = codeBlock->codeOrigin(index);
107     if (!codeOrigin.inlineCallFrame) {
108         readNonInlinedFrame(callFrame, &codeOrigin);
109         return;
110     }
111
112     readInlinedFrame(callFrame, &codeOrigin);
113 #endif // !ENABLE(DFG_JIT)
114 }
115
116 void StackVisitor::readNonInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin)
117 {
118     m_frame.m_callFrame = callFrame;
119     m_frame.m_argumentCountIncludingThis = callFrame->argumentCountIncludingThis();
120     m_frame.m_CallerVMEntryFrame = m_frame.m_VMEntryFrame;
121     m_frame.m_callerFrame = callFrame->callerFrame(m_frame.m_CallerVMEntryFrame);
122     m_frame.m_callerIsVMEntryFrame = m_frame.m_CallerVMEntryFrame != m_frame.m_VMEntryFrame;
123     m_frame.m_callee = callFrame->callee();
124     m_frame.m_scope = callFrame->scope();
125     m_frame.m_codeBlock = callFrame->codeBlock();
126     m_frame.m_bytecodeOffset = !m_frame.codeBlock() ? 0
127         : codeOrigin ? codeOrigin->bytecodeIndex
128         : callFrame->locationAsBytecodeOffset();
129 #if ENABLE(DFG_JIT)
130     m_frame.m_inlineCallFrame = 0;
131 #endif
132 }
133
134 #if ENABLE(DFG_JIT)
135 static int inlinedFrameOffset(CodeOrigin* codeOrigin)
136 {
137     InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame;
138     int frameOffset = inlineCallFrame ? inlineCallFrame->stackOffset : 0;
139     return frameOffset;
140 }
141
142 void StackVisitor::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin)
143 {
144     ASSERT(codeOrigin);
145
146     int frameOffset = inlinedFrameOffset(codeOrigin);
147     bool isInlined = !!frameOffset;
148     if (isInlined) {
149         InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame;
150
151         m_frame.m_callFrame = callFrame;
152         m_frame.m_inlineCallFrame = inlineCallFrame;
153         m_frame.m_argumentCountIncludingThis = inlineCallFrame->arguments.size();
154         m_frame.m_codeBlock = inlineCallFrame->baselineCodeBlock();
155         m_frame.m_bytecodeOffset = codeOrigin->bytecodeIndex;
156
157         JSFunction* callee = inlineCallFrame->calleeForCallFrame(callFrame);
158         m_frame.m_scope = callee->scope();
159         m_frame.m_callee = callee;
160         ASSERT(m_frame.scope());
161         ASSERT(m_frame.callee());
162
163         // The callerFrame just needs to be non-null to indicate that we
164         // haven't reached the last frame yet. Setting it to the root
165         // frame (i.e. the callFrame that this inlined frame is called from)
166         // would work just fine.
167         m_frame.m_callerFrame = callFrame;
168         return;
169     }
170
171     readNonInlinedFrame(callFrame, codeOrigin);
172 }
173 #endif // ENABLE(DFG_JIT)
174
175 StackVisitor::Frame::CodeType StackVisitor::Frame::codeType() const
176 {
177     if (!isJSFrame())
178         return CodeType::Native;
179
180     switch (codeBlock()->codeType()) {
181     case EvalCode:
182         return CodeType::Eval;
183     case FunctionCode:
184         return CodeType::Function;
185     case GlobalCode:
186         return CodeType::Global;
187     }
188     RELEASE_ASSERT_NOT_REACHED();
189     return CodeType::Global;
190 }
191
192 String StackVisitor::Frame::functionName()
193 {
194     String traceLine;
195     JSObject* callee = this->callee();
196
197     switch (codeType()) {
198     case CodeType::Eval:
199         traceLine = ASCIILiteral("eval code");
200         break;
201     case CodeType::Native:
202         if (callee)
203             traceLine = getCalculatedDisplayName(callFrame(), callee).impl();
204         break;
205     case CodeType::Function:
206         traceLine = getCalculatedDisplayName(callFrame(), callee).impl();
207         break;
208     case CodeType::Global:
209         traceLine = ASCIILiteral("global code");
210         break;
211     }
212     return traceLine.isNull() ? emptyString() : traceLine;
213 }
214
215 String StackVisitor::Frame::sourceURL()
216 {
217     String traceLine;
218
219     switch (codeType()) {
220     case CodeType::Eval:
221     case CodeType::Function:
222     case CodeType::Global: {
223         String sourceURL = codeBlock()->ownerExecutable()->sourceURL();
224         if (!sourceURL.isEmpty())
225             traceLine = sourceURL.impl();
226         break;
227     }
228     case CodeType::Native:
229         traceLine = ASCIILiteral("[native code]");
230         break;
231     }
232     return traceLine.isNull() ? emptyString() : traceLine;
233 }
234
235 String StackVisitor::Frame::toString()
236 {
237     StringBuilder traceBuild;
238     String functionName = this->functionName();
239     String sourceURL = this->sourceURL();
240     traceBuild.append(functionName);
241     if (!sourceURL.isEmpty()) {
242         if (!functionName.isEmpty())
243             traceBuild.append('@');
244         traceBuild.append(sourceURL);
245         if (isJSFrame()) {
246             unsigned line = 0;
247             unsigned column = 0;
248             computeLineAndColumn(line, column);
249             traceBuild.append(':');
250             traceBuild.appendNumber(line);
251             traceBuild.append(':');
252             traceBuild.appendNumber(column);
253         }
254     }
255     return traceBuild.toString().impl();
256 }
257
258 Arguments* StackVisitor::Frame::createArguments()
259 {
260     ASSERT(m_callFrame);
261     CallFrame* physicalFrame = m_callFrame;
262     VM& vm = physicalFrame->vm();
263     Arguments* arguments;
264     ArgumentsMode mode;
265     if (Options::enableFunctionDotArguments())
266         mode = NormalArgumentsCreationMode;
267     else
268         mode = FakeArgumentValuesCreationMode;
269 #if ENABLE(DFG_JIT)
270     if (isInlinedFrame()) {
271         ASSERT(m_inlineCallFrame);
272         arguments = Arguments::create(vm, physicalFrame, m_inlineCallFrame, mode);
273         arguments->tearOff(physicalFrame, m_inlineCallFrame);
274     } else 
275 #endif
276     {
277         arguments = Arguments::create(vm, physicalFrame, mode);
278         arguments->tearOff(physicalFrame);
279     }
280     return arguments;
281 }
282
283 Arguments* StackVisitor::Frame::existingArguments()
284 {
285     if (codeBlock()->codeType() != FunctionCode)
286         return 0;
287     if (!codeBlock()->usesArguments())
288         return 0;
289     
290     VirtualRegister reg;
291         
292 #if ENABLE(DFG_JIT)
293     if (isInlinedFrame())
294         reg = inlineCallFrame()->argumentsRegister;
295     else
296 #endif // ENABLE(DFG_JIT)
297         reg = codeBlock()->argumentsRegister();
298     
299     JSValue result = callFrame()->r(unmodifiedArgumentsRegister(reg).offset()).jsValue();
300     if (!result || !result.isCell()) // Protect against Undefined in case we throw in op_enter.
301         return 0;
302     return jsCast<Arguments*>(result);
303 }
304
305 void StackVisitor::Frame::computeLineAndColumn(unsigned& line, unsigned& column)
306 {
307     CodeBlock* codeBlock = this->codeBlock();
308     if (!codeBlock) {
309         line = 0;
310         column = 0;
311         return;
312     }
313
314     int divot = 0;
315     int unusedStartOffset = 0;
316     int unusedEndOffset = 0;
317     unsigned divotLine = 0;
318     unsigned divotColumn = 0;
319     retrieveExpressionInfo(divot, unusedStartOffset, unusedEndOffset, divotLine, divotColumn);
320
321     line = divotLine + codeBlock->ownerExecutable()->lineNo();
322     column = divotColumn + (divotLine ? 1 : codeBlock->firstLineColumnOffset());
323 }
324
325 void StackVisitor::Frame::retrieveExpressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column)
326 {
327     CodeBlock* codeBlock = this->codeBlock();
328     codeBlock->unlinkedCodeBlock()->expressionRangeForBytecodeOffset(bytecodeOffset(), divot, startOffset, endOffset, line, column);
329     divot += codeBlock->sourceOffset();
330 }
331
332 void StackVisitor::Frame::setToEnd()
333 {
334     m_callFrame = 0;
335 #if ENABLE(DFG_JIT)
336     m_inlineCallFrame = 0;
337 #endif
338 }
339
340 #ifndef NDEBUG
341
342 static const char* jitTypeName(JITCode::JITType jitType)
343 {
344     switch (jitType) {
345     case JITCode::None: return "None";
346     case JITCode::HostCallThunk: return "HostCallThunk";
347     case JITCode::InterpreterThunk: return "InterpreterThunk";
348     case JITCode::BaselineJIT: return "BaselineJIT";
349     case JITCode::DFGJIT: return "DFGJIT";
350     case JITCode::FTLJIT: return "FTLJIT";
351     }
352     return "<unknown>";
353 }
354
355 static void printIndents(int levels)
356 {
357     while (levels--)
358         dataLogFString("   ");
359 }
360
361 static void printif(int indentLevels, const char* format, ...)
362 {
363     va_list argList;
364     va_start(argList, format);
365
366     if (indentLevels)
367         printIndents(indentLevels);
368
369 #if COMPILER(CLANG) || COMPILER(GCC)
370 #pragma GCC diagnostic push
371 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
372 #pragma GCC diagnostic ignored "-Wmissing-format-attribute"
373 #endif
374
375     WTF::dataLogFV(format, argList);
376
377 #if COMPILER(CLANG) || COMPILER(GCC)
378 #pragma GCC diagnostic pop
379 #endif
380
381     va_end(argList);
382 }
383
384 void StackVisitor::Frame::print(int indentLevel)
385 {
386     int i = indentLevel;
387
388     if (!this->callFrame()) {
389         printif(i, "frame 0x0\n");
390         return;
391     }
392
393     CodeBlock* codeBlock = this->codeBlock();
394     printif(i, "frame %p {\n", this->callFrame());
395
396     CallFrame* callFrame = m_callFrame;
397     CallFrame* callerFrame = this->callerFrame();
398     void* returnPC = callFrame->hasReturnPC() ? callFrame->returnPC().value() : nullptr;
399
400     printif(i, "   name '%s'\n", functionName().utf8().data());
401     printif(i, "   sourceURL '%s'\n", sourceURL().utf8().data());
402
403 #if ENABLE(DFG_JIT)
404     printif(i, "   isInlinedFrame %d\n", isInlinedFrame());
405     if (isInlinedFrame())
406         printif(i, "   InlineCallFrame %p\n", m_inlineCallFrame);
407 #endif
408
409     printif(i, "   callee %p\n", callee());
410     printif(i, "   returnPC %p\n", returnPC);
411     printif(i, "   callerFrame %p\n", callerFrame);
412     unsigned locationRawBits = callFrame->locationAsRawBits();
413     printif(i, "   rawLocationBits %u 0x%x\n", locationRawBits, locationRawBits);
414     printif(i, "   codeBlock %p\n", codeBlock);
415     if (codeBlock) {
416         JITCode::JITType jitType = codeBlock->jitType();
417         if (callFrame->hasLocationAsBytecodeOffset()) {
418             unsigned bytecodeOffset = callFrame->locationAsBytecodeOffset();
419             printif(i, "      bytecodeOffset %u %p / %zu\n", bytecodeOffset, reinterpret_cast<void*>(bytecodeOffset), codeBlock->instructions().size());
420 #if ENABLE(DFG_JIT)
421         } else {
422             unsigned codeOriginIndex = callFrame->locationAsCodeOriginIndex();
423             printif(i, "      codeOriginIdex %u %p / %zu\n", codeOriginIndex, reinterpret_cast<void*>(codeOriginIndex), codeBlock->codeOrigins().size());
424 #endif
425         }
426         unsigned line = 0;
427         unsigned column = 0;
428         computeLineAndColumn(line, column);
429         printif(i, "      line %d\n", line);
430         printif(i, "      column %d\n", column);
431         printif(i, "      jitType %d <%s> isOptimizingJIT %d\n", jitType, jitTypeName(jitType), JITCode::isOptimizingJIT(jitType));
432 #if ENABLE(DFG_JIT)
433         printif(i, "      hasCodeOrigins %d\n", codeBlock->hasCodeOrigins());
434         if (codeBlock->hasCodeOrigins()) {
435             JITCode* jitCode = codeBlock->jitCode().get();
436             printif(i, "         jitCode %p start %p end %p\n", jitCode, jitCode->start(), jitCode->end());
437         }
438 #endif
439     }
440     printif(i, "}\n");
441 }
442
443 #endif // NDEBUG
444
445 } // namespace JSC
446
447 #ifndef NDEBUG
448 using JSC::StackVisitor;
449
450 // For debugging use
451 JS_EXPORT_PRIVATE void debugPrintCallFrame(JSC::CallFrame*);
452 JS_EXPORT_PRIVATE void debugPrintStack(JSC::CallFrame* topCallFrame);
453
454 class DebugPrintFrameFunctor {
455 public:
456     enum Action {
457         PrintOne,
458         PrintAll
459     };
460
461     DebugPrintFrameFunctor(Action action)
462         : m_action(action)
463     {
464     }
465
466     StackVisitor::Status operator()(StackVisitor& visitor)
467     {
468         visitor->print(2);
469         return m_action == PrintAll ? StackVisitor::Continue : StackVisitor::Done;
470     }
471
472 private:
473     Action m_action;
474 };
475
476 void debugPrintCallFrame(JSC::CallFrame* callFrame)
477 {
478     if (!callFrame)
479         return;
480     DebugPrintFrameFunctor functor(DebugPrintFrameFunctor::PrintOne);
481     callFrame->iterate(functor);
482 }
483
484 void debugPrintStack(JSC::CallFrame* topCallFrame)
485 {
486     if (!topCallFrame)
487         return;
488     DebugPrintFrameFunctor functor(DebugPrintFrameFunctor::PrintAll);
489     topCallFrame->iterate(functor);
490 }
491
492 #endif // !NDEBUG