Hook up ShadowChicken to the debugger to show tail deleted frames
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 16 May 2016 23:31:39 +0000 (23:31 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 16 May 2016 23:31:39 +0000 (23:31 +0000)
https://bugs.webkit.org/show_bug.cgi?id=156685
<rdar://problem/25770521>

Reviewed by Filip Pizlo and Mark Lam and Joseph Pecoraro.

Source/JavaScriptCore:

The heart of this patch hooks up ShadowChicken to DebuggerCallFrame to
allow the Web Inspector to display the ShadowChicken's shadow stack.
This means the Web Inspector can now display tail deleted frames.
To make this work, I made the necessary changes to ShadowChicken and
DebuggerCallFrame to allow DebuggerCallFrame to keep the same API
when representing both machine frames and tail deleted frames.

- ShadowChicken prologue packets now log the current scope. Tail packets
  log the current scope, the 'this' value, the CodeBlock, and the
  CallSiteIndex. This allows the inspector to not only show the
  tail deleted frame, but also show exactly where the tail call happened (line and column numbers),
  with which scope it executed, and with which 'this' value. This
  patch also allows DebuggerCallFrame to execute console statements
  in a tail deleted frame.

- I changed ShadowChicken's stack resizing algorithm. ShadowChicken
  now only keeps a maximum number of tail deleted frames in its shadow stack.
  It will happily represent all machine frames without limit. Right now, the
  maximum number of tail deleted frames I chose to keep alive is 128.
  We will keep frames alive starting from the top of the stack. This
  allows us to have a strong defense against runaway memory usage. We will only
  keep around at most 128 "shadow" frames that wouldn't have naturally been kept
  alive by the executing program. We can play around with this number
  if we find that 128 is either too many or too few frames.

- DebuggerCallFrame is no longer a cheap class to create. When it is created,
  we will eagerly create the entire virtual debugger stack. So I modified the
  existing code to lazily create DebuggerCallFrames only when necessary. We
  used to eagerly create them at each op_debug statement even though we would
  just throw them away if we didn't hit a breakpoint.

- A valid DebuggerCallFrame will always have a valid CallFrame* pointer
  into the stack. This pointer won't always refer to the logical frame
  that the DebuggerCallFrame represents because a DebuggerCallFrame can
  now represent a tail deleted frame. To do this, DebuggerCallFrame now
  has a ShadowChicken::Frame member variable. This allows DebuggerCallFrame
  to know when it represents a tail deleted frame and gives DebuggerCallFrame
  a mechanism to ask the tail deleted frame for interesting information
  (like its 'this' value, scope, CodeBlock, etc). A tail deleted frame's
  machine frame pointer will be the machine caller of the tail deleted frame
  (or the machine caller of the first of a series of consecutive tail calls).

- I added a new flag to UnlinkedCodeBlock to indicate when it is compiled
  with debugging opcodes. I did this because ShadowChicken may read a JSScope
  from the machine stack. This is only safe if the machine CodeBlock was
  compiled with debugging opcodes. This is safer than asking if the
  CodeBlock's global object has an interactive debugger enabled because
  it's theoretically possible for the debugger to be enabled while code
  compiled without a debugger is still live on the stack. This field is
  also now used to indicate to the DFGGraph that the interactive debugger
  is enabled.

- Finally, this patch adds a new field to the Inspector's CallFrame protocol
  object called 'isTailDeleted' to allow the Inspector to know when a
  CallFrame represents a tail deleted frame.

* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/BytecodeList.json:
* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
(JSC::CodeBlock::findPC):
(JSC::CodeBlock::bytecodeOffsetFromCallSiteIndex):
* bytecode/CodeBlock.h:
(JSC::CodeBlock::clearDebuggerRequests):
(JSC::CodeBlock::wasCompiledWithDebuggingOpcodes):
* bytecode/UnlinkedCodeBlock.cpp:
(JSC::UnlinkedCodeBlock::UnlinkedCodeBlock):
* bytecode/UnlinkedCodeBlock.h:
(JSC::UnlinkedCodeBlock::wasCompiledWithDebuggingOpcodes):
(JSC::UnlinkedCodeBlock::finishCreation):
(JSC::UnlinkedGlobalCodeBlock::UnlinkedGlobalCodeBlock):
* bytecode/UnlinkedFunctionExecutable.cpp:
(JSC::generateUnlinkedFunctionCodeBlock):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::generate):
(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::emitEnter):
(JSC::BytecodeGenerator::emitLogShadowChickenPrologueIfNecessary):
(JSC::BytecodeGenerator::emitLogShadowChickenTailIfNecessary):
(JSC::BytecodeGenerator::emitCallDefineProperty):
* debugger/Debugger.cpp:
(JSC::DebuggerPausedScope::DebuggerPausedScope):
(JSC::DebuggerPausedScope::~DebuggerPausedScope):
(JSC::Debugger::didReachBreakpoint):
(JSC::Debugger::currentDebuggerCallFrame):
* debugger/Debugger.h:
* debugger/DebuggerCallFrame.cpp:
(JSC::LineAndColumnFunctor::operator()):
(JSC::DebuggerCallFrame::create):
(JSC::DebuggerCallFrame::DebuggerCallFrame):
(JSC::DebuggerCallFrame::callerFrame):
(JSC::DebuggerCallFrame::globalExec):
(JSC::DebuggerCallFrame::vmEntryGlobalObject):
(JSC::DebuggerCallFrame::sourceID):
(JSC::DebuggerCallFrame::functionName):
(JSC::DebuggerCallFrame::scope):
(JSC::DebuggerCallFrame::type):
(JSC::DebuggerCallFrame::thisValue):
(JSC::DebuggerCallFrame::evaluateWithScopeExtension):
(JSC::DebuggerCallFrame::invalidate):
(JSC::DebuggerCallFrame::currentPosition):
(JSC::DebuggerCallFrame::positionForCallFrame):
(JSC::DebuggerCallFrame::sourceIDForCallFrame):
(JSC::FindCallerMidStackFunctor::FindCallerMidStackFunctor): Deleted.
(JSC::FindCallerMidStackFunctor::operator()): Deleted.
(JSC::FindCallerMidStackFunctor::getCallerFrame): Deleted.
(JSC::DebuggerCallFrame::thisValueForCallFrame): Deleted.
* debugger/DebuggerCallFrame.h:
(JSC::DebuggerCallFrame::isValid):
(JSC::DebuggerCallFrame::isTailDeleted):
(JSC::DebuggerCallFrame::create): Deleted.
(JSC::DebuggerCallFrame::exec): Deleted.
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::Graph):
(JSC::DFG::Graph::~Graph):
* dfg/DFGJITCompiler.h:
(JSC::DFG::JITCompiler::addCallSite):
(JSC::DFG::JITCompiler::emitStoreCodeOrigin):
(JSC::DFG::JITCompiler::emitStoreCallSiteIndex):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLAbstractHeapRepository.h:
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileLogShadowChickenPrologue):
(JSC::FTL::DFG::LowerDFGToB3::compileLogShadowChickenTail):
(JSC::FTL::DFG::LowerDFGToB3::compileRecordRegExpCachedResult):
(JSC::FTL::DFG::LowerDFGToB3::allocateJSArray):
(JSC::FTL::DFG::LowerDFGToB3::ensureShadowChickenPacket):
(JSC::FTL::DFG::LowerDFGToB3::setupShadowChickenPacket): Deleted.
* inspector/InjectedScriptSource.js:
(InjectedScript.CallFrameProxy):
* inspector/JSJavaScriptCallFrame.cpp:
(Inspector::JSJavaScriptCallFrame::thisObject):
(Inspector::JSJavaScriptCallFrame::isTailDeleted):
(Inspector::JSJavaScriptCallFrame::type):
* inspector/JSJavaScriptCallFrame.h:
* inspector/JSJavaScriptCallFramePrototype.cpp:
(Inspector::JSJavaScriptCallFramePrototype::finishCreation):
(Inspector::jsJavaScriptCallFramePrototypeFunctionEvaluateWithScopeExtension):
(Inspector::jsJavaScriptCallFrameAttributeType):
(Inspector::jsJavaScriptCallFrameIsTailDeleted):
* inspector/JavaScriptCallFrame.h:
(Inspector::JavaScriptCallFrame::type):
(Inspector::JavaScriptCallFrame::scopeChain):
(Inspector::JavaScriptCallFrame::vmEntryGlobalObject):
(Inspector::JavaScriptCallFrame::isTailDeleted):
(Inspector::JavaScriptCallFrame::thisValue):
(Inspector::JavaScriptCallFrame::evaluateWithScopeExtension):
* inspector/ScriptDebugServer.cpp:
(Inspector::ScriptDebugServer::evaluateBreakpointAction):
* inspector/protocol/Debugger.json:
* interpreter/ShadowChicken.cpp:
(JSC::ShadowChicken::update):
(JSC::ShadowChicken::visitChildren):
(JSC::ShadowChicken::reset):
* interpreter/ShadowChicken.h:
(JSC::ShadowChicken::Packet::throwMarker):
(JSC::ShadowChicken::Packet::prologue):
(JSC::ShadowChicken::Packet::tail):
(JSC::ShadowChicken::Frame::Frame):
(JSC::ShadowChicken::Frame::operator==):
* jit/CCallHelpers.cpp:
(JSC::CCallHelpers::logShadowChickenProloguePacket):
(JSC::CCallHelpers::logShadowChickenTailPacket):
(JSC::CCallHelpers::ensureShadowChickenPacket):
(JSC::CCallHelpers::setupShadowChickenPacket): Deleted.
* jit/CCallHelpers.h:
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_profile_type):
(JSC::JIT::emit_op_log_shadow_chicken_prologue):
(JSC::JIT::emit_op_log_shadow_chicken_tail):
(JSC::JIT::emit_op_get_enumerable_length):
(JSC::JIT::emit_op_resume):
* jit/JITOpcodes32_64.cpp:
(JSC::JIT::emit_op_profile_type):
(JSC::JIT::emit_op_log_shadow_chicken_prologue):
(JSC::JIT::emit_op_log_shadow_chicken_tail):
* jit/RegisterSet.cpp:
(JSC::RegisterSet::webAssemblyCalleeSaveRegisters):
(JSC::RegisterSet::argumentGPRS):
(JSC::RegisterSet::registersToNotSaveForJSCall):
* jit/RegisterSet.h:
* llint/LLIntData.cpp:
(JSC::LLInt::Data::performAssertions):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* llint/LowLevelInterpreter.asm:
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/CodeCache.cpp:
(JSC::CodeCache::getGlobalCodeBlock):
* runtime/Options.h:
* tests/stress/shadow-chicken-enabled.js:
(test5a.foo):
(test5a):
(test5b.foo):
(test5b):
(test6.foo):
(test6):

Source/WebCore:

Tests: inspector/debugger/tail-deleted-frames-this-value.html
       inspector/debugger/tail-deleted-frames.html
       inspector/debugger/tail-recursion.html

* ForwardingHeaders/interpreter/ShadowChicken.h: Added.

Source/WebInspectorUI:

This patch makes the WebInspector display tail deleted frames.
We show tail deleted frames with a gray [f] instead of a green
[f]. We also put text in the tooltip to indicate that the frame
is tail deleted. Other than that, tail deleted frames behave like
normal frames. You can evaluate in them, inspect their scope, etc.

* Localizations/en.lproj/localizedStrings.js:
* UserInterface/Images/TailDeletedFunction.svg: Added.
* UserInterface/Images/gtk/TailDeletedFunction.svg: Added.
* UserInterface/Models/CallFrame.js:
* UserInterface/Views/CallFrameIcons.css:
* UserInterface/Views/CallFrameTreeElement.js:
* UserInterface/Views/CallFrameView.js:

LayoutTests:

* inspector/debugger/resources/tail-deleted-frames-this-value.js: Added.
(a):
(b):
* inspector/debugger/resources/tail-deleted-frames.js: Added.
(a):
(b):
(c):
(startABC):
* inspector/debugger/resources/tail-recursion.js: Added.
(recurse):
(startRecurse):
* inspector/debugger/tail-deleted-frames-expected.txt: Added.
* inspector/debugger/tail-deleted-frames-this-value-expected.txt: Added.
* inspector/debugger/tail-deleted-frames-this-value.html: Added.
* inspector/debugger/tail-deleted-frames.html: Added.
* inspector/debugger/tail-recursion-expected.txt: Added.
* inspector/debugger/tail-recursion.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@200981 268f45cc-cd09-0410-ab3c-d52691b4dbfc

65 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector/debugger/resources/tail-deleted-frames-this-value.js [new file with mode: 0644]
LayoutTests/inspector/debugger/resources/tail-deleted-frames.js [new file with mode: 0644]
LayoutTests/inspector/debugger/resources/tail-recursion.js [new file with mode: 0644]
LayoutTests/inspector/debugger/tail-deleted-frames-expected.txt [new file with mode: 0644]
LayoutTests/inspector/debugger/tail-deleted-frames-this-value-expected.txt [new file with mode: 0644]
LayoutTests/inspector/debugger/tail-deleted-frames-this-value.html [new file with mode: 0644]
LayoutTests/inspector/debugger/tail-deleted-frames.html [new file with mode: 0644]
LayoutTests/inspector/debugger/tail-recursion-expected.txt [new file with mode: 0644]
LayoutTests/inspector/debugger/tail-recursion.html [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/bytecode/BytecodeList.json
Source/JavaScriptCore/bytecode/BytecodeUseDef.h
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/CodeBlock.h
Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp
Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h
Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/debugger/Debugger.cpp
Source/JavaScriptCore/debugger/Debugger.h
Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp
Source/JavaScriptCore/debugger/DebuggerCallFrame.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGGraph.cpp
Source/JavaScriptCore/dfg/DFGJITCompiler.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/inspector/InjectedScriptSource.js
Source/JavaScriptCore/inspector/JSJavaScriptCallFrame.cpp
Source/JavaScriptCore/inspector/JSJavaScriptCallFrame.h
Source/JavaScriptCore/inspector/JSJavaScriptCallFramePrototype.cpp
Source/JavaScriptCore/inspector/JavaScriptCallFrame.h
Source/JavaScriptCore/inspector/ScriptDebugServer.cpp
Source/JavaScriptCore/inspector/protocol/Debugger.json
Source/JavaScriptCore/interpreter/ShadowChicken.cpp
Source/JavaScriptCore/interpreter/ShadowChicken.h
Source/JavaScriptCore/jit/CCallHelpers.cpp
Source/JavaScriptCore/jit/CCallHelpers.h
Source/JavaScriptCore/jit/JITOpcodes.cpp
Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
Source/JavaScriptCore/jit/RegisterSet.cpp
Source/JavaScriptCore/jit/RegisterSet.h
Source/JavaScriptCore/llint/LLIntData.cpp
Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
Source/JavaScriptCore/llint/LowLevelInterpreter.asm
Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/runtime/CodeCache.cpp
Source/JavaScriptCore/runtime/Options.h
Source/JavaScriptCore/tests/stress/shadow-chicken-enabled.js
Source/WebCore/ChangeLog
Source/WebCore/ForwardingHeaders/interpreter/ShadowChicken.h [new file with mode: 0644]
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
Source/WebInspectorUI/UserInterface/Images/TailDeletedFunction.svg [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Images/gtk/TailDeletedFunction.svg [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Models/CallFrame.js
Source/WebInspectorUI/UserInterface/Views/CallFrameIcons.css
Source/WebInspectorUI/UserInterface/Views/CallFrameTreeElement.js
Source/WebInspectorUI/UserInterface/Views/CallFrameView.js

index d11206e..dc06fa3 100644 (file)
@@ -1,3 +1,29 @@
+2016-05-16  Saam barati  <sbarati@apple.com>
+
+        Hook up ShadowChicken to the debugger to show tail deleted frames
+        https://bugs.webkit.org/show_bug.cgi?id=156685
+        <rdar://problem/25770521>
+
+        Reviewed by Filip Pizlo and Mark Lam and Joseph Pecoraro.
+
+        * inspector/debugger/resources/tail-deleted-frames-this-value.js: Added.
+        (a):
+        (b):
+        * inspector/debugger/resources/tail-deleted-frames.js: Added.
+        (a):
+        (b):
+        (c):
+        (startABC):
+        * inspector/debugger/resources/tail-recursion.js: Added.
+        (recurse):
+        (startRecurse):
+        * inspector/debugger/tail-deleted-frames-expected.txt: Added.
+        * inspector/debugger/tail-deleted-frames-this-value-expected.txt: Added.
+        * inspector/debugger/tail-deleted-frames-this-value.html: Added.
+        * inspector/debugger/tail-deleted-frames.html: Added.
+        * inspector/debugger/tail-recursion-expected.txt: Added.
+        * inspector/debugger/tail-recursion.html: Added.
+
 2016-05-16  Aaron Chu  <aaron_chu@apple.com>
 
         REGRESSION (r200441): Yahoo sports, finance and news pages automatically scroll
diff --git a/LayoutTests/inspector/debugger/resources/tail-deleted-frames-this-value.js b/LayoutTests/inspector/debugger/resources/tail-deleted-frames-this-value.js
new file mode 100644 (file)
index 0000000..741c5ca
--- /dev/null
@@ -0,0 +1,17 @@
+"use strict";
+function a() {
+    let x = 20;
+    x;
+    return x;
+}
+function b() {
+    let y = 40;
+    return a.call({aThis: 2});
+}
+function c() {
+    let z = 60;
+    return b.call({bThis: 1}); 
+}
+function startABC() {
+    c.call({cThis: 0});
+}
diff --git a/LayoutTests/inspector/debugger/resources/tail-deleted-frames.js b/LayoutTests/inspector/debugger/resources/tail-deleted-frames.js
new file mode 100644 (file)
index 0000000..af0374c
--- /dev/null
@@ -0,0 +1,17 @@
+"use strict";
+function a() {
+    let x = 20;
+    x;
+    return x;
+}
+function b() {
+    let y = 40;
+    return a();
+}
+function c() {
+    let z = 60;
+    return b(); 
+}
+function startABC() {
+    c();
+}
diff --git a/LayoutTests/inspector/debugger/resources/tail-recursion.js b/LayoutTests/inspector/debugger/resources/tail-recursion.js
new file mode 100644 (file)
index 0000000..91c353f
--- /dev/null
@@ -0,0 +1,14 @@
+"use strict";
+function recurse(i) {
+    if (i <= 0) {
+        i++;
+        i++;
+        return;
+    }
+
+    return recurse(i - 1);
+}
+
+function startRecurse() {
+    recurse(1000);
+}
diff --git a/LayoutTests/inspector/debugger/tail-deleted-frames-expected.txt b/LayoutTests/inspector/debugger/tail-deleted-frames-expected.txt
new file mode 100644 (file)
index 0000000..5451131
--- /dev/null
@@ -0,0 +1,19 @@
+Testing that we keep around tail deleted frames in the inspector.
+
+Starting Test
+
+
+------------------------------------
+Hit breakpoint at line: 3, column: 4
+------------------------------------
+Expected frame: {"functionName":"a","scope":["x",20],"isTailDeleted":false}
+Expected frame: {"functionName":"b","scope":["y",40],"isTailDeleted":true}
+Expected frame: {"functionName":"c","scope":["z",60],"isTailDeleted":true}
+Looking at frame number: 0
+    variable 'x': {"_type":"number","_description":"20","_hasChildren":false,"_value":20}
+Looking at frame number: 1
+    variable 'y': {"_type":"number","_description":"40","_hasChildren":false,"_value":40}
+Looking at frame number: 2
+    variable 'z': {"_type":"number","_description":"60","_hasChildren":false,"_value":60}
+Tests done
+
diff --git a/LayoutTests/inspector/debugger/tail-deleted-frames-this-value-expected.txt b/LayoutTests/inspector/debugger/tail-deleted-frames-this-value-expected.txt
new file mode 100644 (file)
index 0000000..2b9cbeb
--- /dev/null
@@ -0,0 +1,19 @@
+Testing that we keep around tail deleted frames in the inspector and their this values.
+
+Starting Test
+
+
+------------------------------------
+Hit breakpoint at line: 3, column: 4
+------------------------------------
+Expected frame: {"functionName":"a","thisValue":["aThis",2],"isTailDeleted":false}
+PASS: 'this' value should have expected property: aThis
+PASS: Call Frame 0 'this' value is correct.
+Expected frame: {"functionName":"b","thisValue":["bThis",1],"isTailDeleted":true}
+PASS: 'this' value should have expected property: bThis
+PASS: Call Frame 1 'this' value is correct.
+Expected frame: {"functionName":"c","thisValue":["cThis",0],"isTailDeleted":true}
+PASS: 'this' value should have expected property: cThis
+PASS: Call Frame 2 'this' value is correct.
+Tests done
+
diff --git a/LayoutTests/inspector/debugger/tail-deleted-frames-this-value.html b/LayoutTests/inspector/debugger/tail-deleted-frames-this-value.html
new file mode 100644 (file)
index 0000000..45e2efc
--- /dev/null
@@ -0,0 +1,86 @@
+<!doctype html>
+<html>
+<head>
+<script type="text/javascript" src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script type="text/javascript" src="../../http/tests/inspector/debugger/debugger-test.js"></script>
+<script type="text/javascript" src="./resources/tail-deleted-frames-this-value.js"></script>
+<script>
+
+function test()
+{
+    var scriptObject;
+
+    function startTest() {
+        InspectorTest.log("Starting Test");
+        // 0 based indices.
+        let testInfo = {line: 3, column: 4};
+        let location = scriptObject.createSourceCodeLocation(testInfo.line, testInfo.column);
+        let breakpoint = new WebInspector.Breakpoint(location);
+        WebInspector.debuggerManager.addBreakpoint(breakpoint);
+        InspectorTest.evaluateInPage("startABC()");
+    }
+
+    WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.CallFramesDidChange, function(event) {
+        var activeCallFrame = WebInspector.debuggerManager.activeCallFrame;
+
+        if (!activeCallFrame)
+            return;
+
+        var stopLocation = "line: " + activeCallFrame.sourceCodeLocation.lineNumber + ", column: " + activeCallFrame.sourceCodeLocation.columnNumber;
+
+        InspectorTest.log("\n\n------------------------------------");
+        InspectorTest.log("Hit breakpoint at " + stopLocation);
+        InspectorTest.log("------------------------------------");
+
+        // top down list
+        let expectedFrames = [
+            {functionName: 'a', thisValue: ['aThis', 2], isTailDeleted: false},
+            {functionName: 'b', thisValue: ['bThis', 1], isTailDeleted: true},
+            {functionName: 'c', thisValue: ['cThis', 0], isTailDeleted: true}
+        ];
+
+        InspectorTest.assert(WebInspector.debuggerManager.callFrames.length >= expectedFrames.length);
+
+        for (let i = 0; i < expectedFrames.length; i++) {
+            let callFrame = WebInspector.debuggerManager.callFrames[i];
+            let expectedFrame = expectedFrames[i];
+            InspectorTest.log("Expected frame: " + JSON.stringify(expectedFrame));
+            InspectorTest.assert(callFrame.functionName === expectedFrame.functionName);
+            InspectorTest.assert(callFrame.isTailDeleted === expectedFrame.isTailDeleted);
+
+            let thisObject = callFrame.thisObject;
+            let properties = thisObject.preview.propertyPreviews;
+            InspectorTest.assert(properties.length === 1);
+            let prop = properties[0];
+            InspectorTest.expectThat(expectedFrame.thisValue[0] === prop.name, `'this' value should have expected property: ${expectedFrame.thisValue[0]}`);
+            InspectorTest.assert('' + expectedFrame.thisValue[1] === prop.value, `'this' value object should have expected property value: ${expectedFrame.thisValue[1]}`);
+            InspectorTest.pass(`Call Frame ${i} 'this' value is correct.`);
+        }
+
+        WebInspector.debuggerManager.resume();
+    });
+
+    WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Resumed, function(event) {
+        InspectorTest.log("Tests done");
+        InspectorTest.completeTest();
+    });
+
+    WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.ScriptAdded, function(event) {
+        eventScriptObject = event.data.script;
+        
+        if (/tail-deleted-frames-this-value\.js$/.test(eventScriptObject.url)) {
+            scriptObject = eventScriptObject;
+            startTest();
+            return;
+        }
+
+    });
+
+    InspectorTest.reloadPage();
+}
+</script>
+</head>
+<body onload="runTest()">
+    <p>Testing that we keep around tail deleted frames in the inspector and their this values.</p>
+</body>
+</html>
diff --git a/LayoutTests/inspector/debugger/tail-deleted-frames.html b/LayoutTests/inspector/debugger/tail-deleted-frames.html
new file mode 100644 (file)
index 0000000..7f50745
--- /dev/null
@@ -0,0 +1,96 @@
+<!doctype html>
+<html>
+<head>
+<script type="text/javascript" src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script type="text/javascript" src="../../http/tests/inspector/debugger/debugger-test.js"></script>
+<script type="text/javascript" src="./resources/tail-deleted-frames.js"></script>
+<script>
+
+function test()
+{
+    var scriptObject;
+
+    function startTest() {
+        InspectorTest.log("Starting Test");
+        // 0 based indices.
+        let testInfo = {line: 3, column: 4};
+        let location = scriptObject.createSourceCodeLocation(testInfo.line, testInfo.column);
+        let breakpoint = new WebInspector.Breakpoint(location);
+        WebInspector.debuggerManager.addBreakpoint(breakpoint);
+        InspectorTest.evaluateInPage("startABC()");
+    }
+
+    WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.CallFramesDidChange, function(event) {
+        var activeCallFrame = WebInspector.debuggerManager.activeCallFrame;
+
+        if (!activeCallFrame)
+            return;
+
+        var stopLocation = "line: " + activeCallFrame.sourceCodeLocation.lineNumber + ", column: " + activeCallFrame.sourceCodeLocation.columnNumber;
+
+        InspectorTest.log("\n\n------------------------------------");
+        InspectorTest.log("Hit breakpoint at " + stopLocation);
+        InspectorTest.log("------------------------------------");
+
+        // top down list
+        let expectedFrames = [
+            {functionName: 'a', scope: ['x', 20], isTailDeleted: false},
+            {functionName: 'b', scope: ['y', 40], isTailDeleted: true},
+            {functionName: 'c', scope: ['z', 60], isTailDeleted: true}
+        ];
+
+        InspectorTest.assert(WebInspector.debuggerManager.callFrames.length >= expectedFrames.length);
+
+        for (let i = 0; i < expectedFrames.length; i++) {
+            let callFrame = WebInspector.debuggerManager.callFrames[i];
+            let expectedFrame = expectedFrames[i];
+            InspectorTest.log("Expected frame: " + JSON.stringify(expectedFrame));
+            InspectorTest.assert(callFrame.functionName === expectedFrame.functionName);
+
+            InspectorTest.assert(callFrame.isTailDeleted === expectedFrame.isTailDeleted);
+            let topScope = callFrame.scopeChain[0];
+
+            topScope.objects[0].getAllPropertyDescriptors(function(properties) {
+                let found = false;
+                let variableName = expectedFrame.scope[0];
+                let variableValue = expectedFrame.scope[1];
+                for (let propertyDescriptor of properties) {
+                    if (propertyDescriptor.name === variableName) {
+                        found = true;
+                        InspectorTest.log("Looking at frame number: " + i);
+                        InspectorTest.log(`    variable '${variableName}': ${JSON.stringify(propertyDescriptor.value)}`);
+                        InspectorTest.assert(propertyDescriptor.value.type === 'number');
+                        InspectorTest.assert(propertyDescriptor.value.value === variableValue);
+                    }
+                }
+                InspectorTest.assert(found);
+            });
+        }
+
+        WebInspector.debuggerManager.resume();
+    });
+
+    WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Resumed, function(event) {
+        InspectorTest.log("Tests done");
+        InspectorTest.completeTest();
+    });
+
+    WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.ScriptAdded, function(event) {
+        eventScriptObject = event.data.script;
+        
+        if (/tail-deleted-frames\.js$/.test(eventScriptObject.url)) {
+            scriptObject = eventScriptObject;
+            startTest();
+            return;
+        }
+
+    });
+
+    InspectorTest.reloadPage();
+}
+</script>
+</head>
+<body onload="runTest()">
+    <p>Testing that we keep around tail deleted frames in the inspector. </p>
+</body>
+</html>
diff --git a/LayoutTests/inspector/debugger/tail-recursion-expected.txt b/LayoutTests/inspector/debugger/tail-recursion-expected.txt
new file mode 100644 (file)
index 0000000..066ae9b
--- /dev/null
@@ -0,0 +1,219 @@
+Testing that we keep around tail deleted frames in the inspector.
+
+Starting Test
+
+
+------------------------------------
+Hit breakpoint at line: 3, column: 8
+numBreakpointHits: 1
+Going to look at the top 50 frames.
+------------------------------------
+Looking at frame number: 0
+    variable 'i': {"_type":"number","_description":"0","_hasChildren":false,"_value":0}
+Looking at frame number: 1
+    variable 'i': {"_type":"number","_description":"1","_hasChildren":false,"_value":1}
+Looking at frame number: 2
+    variable 'i': {"_type":"number","_description":"2","_hasChildren":false,"_value":2}
+Looking at frame number: 3
+    variable 'i': {"_type":"number","_description":"3","_hasChildren":false,"_value":3}
+Looking at frame number: 4
+    variable 'i': {"_type":"number","_description":"4","_hasChildren":false,"_value":4}
+Looking at frame number: 5
+    variable 'i': {"_type":"number","_description":"5","_hasChildren":false,"_value":5}
+Looking at frame number: 6
+    variable 'i': {"_type":"number","_description":"6","_hasChildren":false,"_value":6}
+Looking at frame number: 7
+    variable 'i': {"_type":"number","_description":"7","_hasChildren":false,"_value":7}
+Looking at frame number: 8
+    variable 'i': {"_type":"number","_description":"8","_hasChildren":false,"_value":8}
+Looking at frame number: 9
+    variable 'i': {"_type":"number","_description":"9","_hasChildren":false,"_value":9}
+Looking at frame number: 10
+    variable 'i': {"_type":"number","_description":"10","_hasChildren":false,"_value":10}
+Looking at frame number: 11
+    variable 'i': {"_type":"number","_description":"11","_hasChildren":false,"_value":11}
+Looking at frame number: 12
+    variable 'i': {"_type":"number","_description":"12","_hasChildren":false,"_value":12}
+Looking at frame number: 13
+    variable 'i': {"_type":"number","_description":"13","_hasChildren":false,"_value":13}
+Looking at frame number: 14
+    variable 'i': {"_type":"number","_description":"14","_hasChildren":false,"_value":14}
+Looking at frame number: 15
+    variable 'i': {"_type":"number","_description":"15","_hasChildren":false,"_value":15}
+Looking at frame number: 16
+    variable 'i': {"_type":"number","_description":"16","_hasChildren":false,"_value":16}
+Looking at frame number: 17
+    variable 'i': {"_type":"number","_description":"17","_hasChildren":false,"_value":17}
+Looking at frame number: 18
+    variable 'i': {"_type":"number","_description":"18","_hasChildren":false,"_value":18}
+Looking at frame number: 19
+    variable 'i': {"_type":"number","_description":"19","_hasChildren":false,"_value":19}
+Looking at frame number: 20
+    variable 'i': {"_type":"number","_description":"20","_hasChildren":false,"_value":20}
+Looking at frame number: 21
+    variable 'i': {"_type":"number","_description":"21","_hasChildren":false,"_value":21}
+Looking at frame number: 22
+    variable 'i': {"_type":"number","_description":"22","_hasChildren":false,"_value":22}
+Looking at frame number: 23
+    variable 'i': {"_type":"number","_description":"23","_hasChildren":false,"_value":23}
+Looking at frame number: 24
+    variable 'i': {"_type":"number","_description":"24","_hasChildren":false,"_value":24}
+Looking at frame number: 25
+    variable 'i': {"_type":"number","_description":"25","_hasChildren":false,"_value":25}
+Looking at frame number: 26
+    variable 'i': {"_type":"number","_description":"26","_hasChildren":false,"_value":26}
+Looking at frame number: 27
+    variable 'i': {"_type":"number","_description":"27","_hasChildren":false,"_value":27}
+Looking at frame number: 28
+    variable 'i': {"_type":"number","_description":"28","_hasChildren":false,"_value":28}
+Looking at frame number: 29
+    variable 'i': {"_type":"number","_description":"29","_hasChildren":false,"_value":29}
+Looking at frame number: 30
+    variable 'i': {"_type":"number","_description":"30","_hasChildren":false,"_value":30}
+Looking at frame number: 31
+    variable 'i': {"_type":"number","_description":"31","_hasChildren":false,"_value":31}
+Looking at frame number: 32
+    variable 'i': {"_type":"number","_description":"32","_hasChildren":false,"_value":32}
+Looking at frame number: 33
+    variable 'i': {"_type":"number","_description":"33","_hasChildren":false,"_value":33}
+Looking at frame number: 34
+    variable 'i': {"_type":"number","_description":"34","_hasChildren":false,"_value":34}
+Looking at frame number: 35
+    variable 'i': {"_type":"number","_description":"35","_hasChildren":false,"_value":35}
+Looking at frame number: 36
+    variable 'i': {"_type":"number","_description":"36","_hasChildren":false,"_value":36}
+Looking at frame number: 37
+    variable 'i': {"_type":"number","_description":"37","_hasChildren":false,"_value":37}
+Looking at frame number: 38
+    variable 'i': {"_type":"number","_description":"38","_hasChildren":false,"_value":38}
+Looking at frame number: 39
+    variable 'i': {"_type":"number","_description":"39","_hasChildren":false,"_value":39}
+Looking at frame number: 40
+    variable 'i': {"_type":"number","_description":"40","_hasChildren":false,"_value":40}
+Looking at frame number: 41
+    variable 'i': {"_type":"number","_description":"41","_hasChildren":false,"_value":41}
+Looking at frame number: 42
+    variable 'i': {"_type":"number","_description":"42","_hasChildren":false,"_value":42}
+Looking at frame number: 43
+    variable 'i': {"_type":"number","_description":"43","_hasChildren":false,"_value":43}
+Looking at frame number: 44
+    variable 'i': {"_type":"number","_description":"44","_hasChildren":false,"_value":44}
+Looking at frame number: 45
+    variable 'i': {"_type":"number","_description":"45","_hasChildren":false,"_value":45}
+Looking at frame number: 46
+    variable 'i': {"_type":"number","_description":"46","_hasChildren":false,"_value":46}
+Looking at frame number: 47
+    variable 'i': {"_type":"number","_description":"47","_hasChildren":false,"_value":47}
+Looking at frame number: 48
+    variable 'i': {"_type":"number","_description":"48","_hasChildren":false,"_value":48}
+Looking at frame number: 49
+    variable 'i': {"_type":"number","_description":"49","_hasChildren":false,"_value":49}
+
+
+------------------------------------
+Hit breakpoint at line: 4, column: 8
+numBreakpointHits: 2
+Going to look at the top 50 frames.
+------------------------------------
+Looking at frame number: 0
+    variable 'i': {"_type":"number","_description":"1","_hasChildren":false,"_value":1}
+Looking at frame number: 1
+    variable 'i': {"_type":"number","_description":"1","_hasChildren":false,"_value":1}
+Looking at frame number: 2
+    variable 'i': {"_type":"number","_description":"2","_hasChildren":false,"_value":2}
+Looking at frame number: 3
+    variable 'i': {"_type":"number","_description":"3","_hasChildren":false,"_value":3}
+Looking at frame number: 4
+    variable 'i': {"_type":"number","_description":"4","_hasChildren":false,"_value":4}
+Looking at frame number: 5
+    variable 'i': {"_type":"number","_description":"5","_hasChildren":false,"_value":5}
+Looking at frame number: 6
+    variable 'i': {"_type":"number","_description":"6","_hasChildren":false,"_value":6}
+Looking at frame number: 7
+    variable 'i': {"_type":"number","_description":"7","_hasChildren":false,"_value":7}
+Looking at frame number: 8
+    variable 'i': {"_type":"number","_description":"8","_hasChildren":false,"_value":8}
+Looking at frame number: 9
+    variable 'i': {"_type":"number","_description":"9","_hasChildren":false,"_value":9}
+Looking at frame number: 10
+    variable 'i': {"_type":"number","_description":"10","_hasChildren":false,"_value":10}
+Looking at frame number: 11
+    variable 'i': {"_type":"number","_description":"11","_hasChildren":false,"_value":11}
+Looking at frame number: 12
+    variable 'i': {"_type":"number","_description":"12","_hasChildren":false,"_value":12}
+Looking at frame number: 13
+    variable 'i': {"_type":"number","_description":"13","_hasChildren":false,"_value":13}
+Looking at frame number: 14
+    variable 'i': {"_type":"number","_description":"14","_hasChildren":false,"_value":14}
+Looking at frame number: 15
+    variable 'i': {"_type":"number","_description":"15","_hasChildren":false,"_value":15}
+Looking at frame number: 16
+    variable 'i': {"_type":"number","_description":"16","_hasChildren":false,"_value":16}
+Looking at frame number: 17
+    variable 'i': {"_type":"number","_description":"17","_hasChildren":false,"_value":17}
+Looking at frame number: 18
+    variable 'i': {"_type":"number","_description":"18","_hasChildren":false,"_value":18}
+Looking at frame number: 19
+    variable 'i': {"_type":"number","_description":"19","_hasChildren":false,"_value":19}
+Looking at frame number: 20
+    variable 'i': {"_type":"number","_description":"20","_hasChildren":false,"_value":20}
+Looking at frame number: 21
+    variable 'i': {"_type":"number","_description":"21","_hasChildren":false,"_value":21}
+Looking at frame number: 22
+    variable 'i': {"_type":"number","_description":"22","_hasChildren":false,"_value":22}
+Looking at frame number: 23
+    variable 'i': {"_type":"number","_description":"23","_hasChildren":false,"_value":23}
+Looking at frame number: 24
+    variable 'i': {"_type":"number","_description":"24","_hasChildren":false,"_value":24}
+Looking at frame number: 25
+    variable 'i': {"_type":"number","_description":"25","_hasChildren":false,"_value":25}
+Looking at frame number: 26
+    variable 'i': {"_type":"number","_description":"26","_hasChildren":false,"_value":26}
+Looking at frame number: 27
+    variable 'i': {"_type":"number","_description":"27","_hasChildren":false,"_value":27}
+Looking at frame number: 28
+    variable 'i': {"_type":"number","_description":"28","_hasChildren":false,"_value":28}
+Looking at frame number: 29
+    variable 'i': {"_type":"number","_description":"29","_hasChildren":false,"_value":29}
+Looking at frame number: 30
+    variable 'i': {"_type":"number","_description":"30","_hasChildren":false,"_value":30}
+Looking at frame number: 31
+    variable 'i': {"_type":"number","_description":"31","_hasChildren":false,"_value":31}
+Looking at frame number: 32
+    variable 'i': {"_type":"number","_description":"32","_hasChildren":false,"_value":32}
+Looking at frame number: 33
+    variable 'i': {"_type":"number","_description":"33","_hasChildren":false,"_value":33}
+Looking at frame number: 34
+    variable 'i': {"_type":"number","_description":"34","_hasChildren":false,"_value":34}
+Looking at frame number: 35
+    variable 'i': {"_type":"number","_description":"35","_hasChildren":false,"_value":35}
+Looking at frame number: 36
+    variable 'i': {"_type":"number","_description":"36","_hasChildren":false,"_value":36}
+Looking at frame number: 37
+    variable 'i': {"_type":"number","_description":"37","_hasChildren":false,"_value":37}
+Looking at frame number: 38
+    variable 'i': {"_type":"number","_description":"38","_hasChildren":false,"_value":38}
+Looking at frame number: 39
+    variable 'i': {"_type":"number","_description":"39","_hasChildren":false,"_value":39}
+Looking at frame number: 40
+    variable 'i': {"_type":"number","_description":"40","_hasChildren":false,"_value":40}
+Looking at frame number: 41
+    variable 'i': {"_type":"number","_description":"41","_hasChildren":false,"_value":41}
+Looking at frame number: 42
+    variable 'i': {"_type":"number","_description":"42","_hasChildren":false,"_value":42}
+Looking at frame number: 43
+    variable 'i': {"_type":"number","_description":"43","_hasChildren":false,"_value":43}
+Looking at frame number: 44
+    variable 'i': {"_type":"number","_description":"44","_hasChildren":false,"_value":44}
+Looking at frame number: 45
+    variable 'i': {"_type":"number","_description":"45","_hasChildren":false,"_value":45}
+Looking at frame number: 46
+    variable 'i': {"_type":"number","_description":"46","_hasChildren":false,"_value":46}
+Looking at frame number: 47
+    variable 'i': {"_type":"number","_description":"47","_hasChildren":false,"_value":47}
+Looking at frame number: 48
+    variable 'i': {"_type":"number","_description":"48","_hasChildren":false,"_value":48}
+Looking at frame number: 49
+    variable 'i': {"_type":"number","_description":"49","_hasChildren":false,"_value":49}
+Tests done
+
diff --git a/LayoutTests/inspector/debugger/tail-recursion.html b/LayoutTests/inspector/debugger/tail-recursion.html
new file mode 100644 (file)
index 0000000..e6014e6
--- /dev/null
@@ -0,0 +1,97 @@
+<!doctype html>
+<html>
+<head>
+<script type="text/javascript" src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script type="text/javascript" src="../../http/tests/inspector/debugger/debugger-test.js"></script>
+<script type="text/javascript" src="./resources/tail-recursion.js"></script>
+<script>
+
+function test()
+{
+    var scriptObject;
+
+    function startTest() {
+        InspectorTest.log("Starting Test");
+        // 0 based indices.
+        var testInfos = [{line : 3, column : 8}, {line : 4, column : 8}];
+        for (let testInfo of testInfos) {
+            let location = scriptObject.createSourceCodeLocation(testInfo.line, testInfo.column);
+            let breakpoint = new WebInspector.Breakpoint(location);
+            WebInspector.debuggerManager.addBreakpoint(breakpoint);
+        }
+        InspectorTest.evaluateInPage("startRecurse()");
+    }
+
+    let numBreakpointHits = 0;
+    WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.CallFramesDidChange, function(event) {
+        var activeCallFrame = WebInspector.debuggerManager.activeCallFrame;
+
+        if (!activeCallFrame)
+            return;
+
+        numBreakpointHits++;
+        var stopLocation = "line: " + activeCallFrame.sourceCodeLocation.lineNumber + ", column: " + activeCallFrame.sourceCodeLocation.columnNumber;
+
+        InspectorTest.log("\n\n------------------------------------");
+        InspectorTest.log("Hit breakpoint at " + stopLocation);
+        InspectorTest.log("numBreakpointHits: " + numBreakpointHits);
+        const numFramesToInspect = 50;
+        InspectorTest.log("Going to look at the top " + numFramesToInspect + " frames.");
+        InspectorTest.log("------------------------------------");
+
+        InspectorTest.assert(WebInspector.debuggerManager.callFrames.length > numFramesToInspect); // We just look at top 50. This isn't a precise number. But it gets at the gist of the debugging experience.
+        for (let i = 0; i < numFramesToInspect; i++) {
+            let callFrame = WebInspector.debuggerManager.callFrames[i];
+            InspectorTest.assert(callFrame.functionName === "recurse");
+            let topScope = callFrame.scopeChain[0];
+
+            if (i > 0)
+                InspectorTest.assert(callFrame.isTailDeleted);
+            else
+                InspectorTest.assert(!callFrame.isTailDeleted);
+
+            topScope.objects[0].getAllPropertyDescriptors(function(properties) {
+                let found = false;
+                for (let propertyDescriptor of properties) {
+                    if (propertyDescriptor.name === 'i') {
+                        found = true;
+                        InspectorTest.log("Looking at frame number: " + i);
+                        InspectorTest.log("    variable 'i': " + JSON.stringify(propertyDescriptor.value));
+                        InspectorTest.assert(propertyDescriptor.value.type === 'number');
+                        if (numBreakpointHits === 2 && i === 0)
+                            InspectorTest.assert(propertyDescriptor.value.value === i + 1); // this tests the i++ inside tail-recurse.js
+                        else
+                            InspectorTest.assert(propertyDescriptor.value.value === i);
+                    }
+                }
+                InspectorTest.assert(found);
+            });
+        }
+
+        WebInspector.debuggerManager.resume();
+    });
+
+    WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Resumed, function(event) {
+        InspectorTest.log("Tests done");
+        InspectorTest.completeTest();
+    });
+
+    WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.ScriptAdded, function(event) {
+        eventScriptObject = event.data.script;
+        
+        if (/tail-recursion\.js$/.test(eventScriptObject.url)) {
+            scriptObject = eventScriptObject;
+            startTest();
+            return;
+        }
+
+    });
+
+    InspectorTest.reloadPage();
+}
+</script>
+</head>
+<body onload="runTest()">
+    <p>Testing that we keep around tail deleted frames in the inspector. </p>
+</body>
+</html>
index 806cb08..8bdc266 100644 (file)
@@ -1,5 +1,221 @@
 2016-05-16  Saam barati  <sbarati@apple.com>
 
+        Hook up ShadowChicken to the debugger to show tail deleted frames
+        https://bugs.webkit.org/show_bug.cgi?id=156685
+        <rdar://problem/25770521>
+
+        Reviewed by Filip Pizlo and Mark Lam and Joseph Pecoraro.
+
+        The heart of this patch hooks up ShadowChicken to DebuggerCallFrame to
+        allow the Web Inspector to display the ShadowChicken's shadow stack.
+        This means the Web Inspector can now display tail deleted frames.
+        To make this work, I made the necessary changes to ShadowChicken and
+        DebuggerCallFrame to allow DebuggerCallFrame to keep the same API
+        when representing both machine frames and tail deleted frames.
+
+        - ShadowChicken prologue packets now log the current scope. Tail packets
+          log the current scope, the 'this' value, the CodeBlock, and the
+          CallSiteIndex. This allows the inspector to not only show the
+          tail deleted frame, but also show exactly where the tail call happened (line and column numbers),
+          with which scope it executed, and with which 'this' value. This
+          patch also allows DebuggerCallFrame to execute console statements
+          in a tail deleted frame.
+
+        - I changed ShadowChicken's stack resizing algorithm. ShadowChicken
+          now only keeps a maximum number of tail deleted frames in its shadow stack.
+          It will happily represent all machine frames without limit. Right now, the
+          maximum number of tail deleted frames I chose to keep alive is 128.
+          We will keep frames alive starting from the top of the stack. This
+          allows us to have a strong defense against runaway memory usage. We will only
+          keep around at most 128 "shadow" frames that wouldn't have naturally been kept
+          alive by the executing program. We can play around with this number
+          if we find that 128 is either too many or too few frames.
+
+        - DebuggerCallFrame is no longer a cheap class to create. When it is created,
+          we will eagerly create the entire virtual debugger stack. So I modified the
+          existing code to lazily create DebuggerCallFrames only when necessary. We
+          used to eagerly create them at each op_debug statement even though we would
+          just throw them away if we didn't hit a breakpoint.
+
+        - A valid DebuggerCallFrame will always have a valid CallFrame* pointer
+          into the stack. This pointer won't always refer to the logical frame
+          that the DebuggerCallFrame represents because a DebuggerCallFrame can
+          now represent a tail deleted frame. To do this, DebuggerCallFrame now
+          has a ShadowChicken::Frame member variable. This allows DebuggerCallFrame
+          to know when it represents a tail deleted frame and gives DebuggerCallFrame
+          a mechanism to ask the tail deleted frame for interesting information
+          (like its 'this' value, scope, CodeBlock, etc). A tail deleted frame's
+          machine frame pointer will be the machine caller of the tail deleted frame
+          (or the machine caller of the first of a series of consecutive tail calls).
+
+        - I added a new flag to UnlinkedCodeBlock to indicate when it is compiled
+          with debugging opcodes. I did this because ShadowChicken may read a JSScope
+          from the machine stack. This is only safe if the machine CodeBlock was
+          compiled with debugging opcodes. This is safer than asking if the
+          CodeBlock's global object has an interactive debugger enabled because
+          it's theoretically possible for the debugger to be enabled while code
+          compiled without a debugger is still live on the stack. This field is
+          also now used to indicate to the DFGGraph that the interactive debugger
+          is enabled.
+
+        - Finally, this patch adds a new field to the Inspector's CallFrame protocol
+          object called 'isTailDeleted' to allow the Inspector to know when a
+          CallFrame represents a tail deleted frame.
+
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * bytecode/BytecodeList.json:
+        * bytecode/BytecodeUseDef.h:
+        (JSC::computeUsesForBytecodeOffset):
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::dumpBytecode):
+        (JSC::CodeBlock::findPC):
+        (JSC::CodeBlock::bytecodeOffsetFromCallSiteIndex):
+        * bytecode/CodeBlock.h:
+        (JSC::CodeBlock::clearDebuggerRequests):
+        (JSC::CodeBlock::wasCompiledWithDebuggingOpcodes):
+        * bytecode/UnlinkedCodeBlock.cpp:
+        (JSC::UnlinkedCodeBlock::UnlinkedCodeBlock):
+        * bytecode/UnlinkedCodeBlock.h:
+        (JSC::UnlinkedCodeBlock::wasCompiledWithDebuggingOpcodes):
+        (JSC::UnlinkedCodeBlock::finishCreation):
+        (JSC::UnlinkedGlobalCodeBlock::UnlinkedGlobalCodeBlock):
+        * bytecode/UnlinkedFunctionExecutable.cpp:
+        (JSC::generateUnlinkedFunctionCodeBlock):
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::generate):
+        (JSC::BytecodeGenerator::BytecodeGenerator):
+        (JSC::BytecodeGenerator::emitEnter):
+        (JSC::BytecodeGenerator::emitLogShadowChickenPrologueIfNecessary):
+        (JSC::BytecodeGenerator::emitLogShadowChickenTailIfNecessary):
+        (JSC::BytecodeGenerator::emitCallDefineProperty):
+        * debugger/Debugger.cpp:
+        (JSC::DebuggerPausedScope::DebuggerPausedScope):
+        (JSC::DebuggerPausedScope::~DebuggerPausedScope):
+        (JSC::Debugger::didReachBreakpoint):
+        (JSC::Debugger::currentDebuggerCallFrame):
+        * debugger/Debugger.h:
+        * debugger/DebuggerCallFrame.cpp:
+        (JSC::LineAndColumnFunctor::operator()):
+        (JSC::DebuggerCallFrame::create):
+        (JSC::DebuggerCallFrame::DebuggerCallFrame):
+        (JSC::DebuggerCallFrame::callerFrame):
+        (JSC::DebuggerCallFrame::globalExec):
+        (JSC::DebuggerCallFrame::vmEntryGlobalObject):
+        (JSC::DebuggerCallFrame::sourceID):
+        (JSC::DebuggerCallFrame::functionName):
+        (JSC::DebuggerCallFrame::scope):
+        (JSC::DebuggerCallFrame::type):
+        (JSC::DebuggerCallFrame::thisValue):
+        (JSC::DebuggerCallFrame::evaluateWithScopeExtension):
+        (JSC::DebuggerCallFrame::invalidate):
+        (JSC::DebuggerCallFrame::currentPosition):
+        (JSC::DebuggerCallFrame::positionForCallFrame):
+        (JSC::DebuggerCallFrame::sourceIDForCallFrame):
+        (JSC::FindCallerMidStackFunctor::FindCallerMidStackFunctor): Deleted.
+        (JSC::FindCallerMidStackFunctor::operator()): Deleted.
+        (JSC::FindCallerMidStackFunctor::getCallerFrame): Deleted.
+        (JSC::DebuggerCallFrame::thisValueForCallFrame): Deleted.
+        * debugger/DebuggerCallFrame.h:
+        (JSC::DebuggerCallFrame::isValid):
+        (JSC::DebuggerCallFrame::isTailDeleted):
+        (JSC::DebuggerCallFrame::create): Deleted.
+        (JSC::DebuggerCallFrame::exec): Deleted.
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGGraph.cpp:
+        (JSC::DFG::Graph::Graph):
+        (JSC::DFG::Graph::~Graph):
+        * dfg/DFGJITCompiler.h:
+        (JSC::DFG::JITCompiler::addCallSite):
+        (JSC::DFG::JITCompiler::emitStoreCodeOrigin):
+        (JSC::DFG::JITCompiler::emitStoreCallSiteIndex):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLAbstractHeapRepository.h:
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileLogShadowChickenPrologue):
+        (JSC::FTL::DFG::LowerDFGToB3::compileLogShadowChickenTail):
+        (JSC::FTL::DFG::LowerDFGToB3::compileRecordRegExpCachedResult):
+        (JSC::FTL::DFG::LowerDFGToB3::allocateJSArray):
+        (JSC::FTL::DFG::LowerDFGToB3::ensureShadowChickenPacket):
+        (JSC::FTL::DFG::LowerDFGToB3::setupShadowChickenPacket): Deleted.
+        * inspector/InjectedScriptSource.js:
+        (InjectedScript.CallFrameProxy):
+        * inspector/JSJavaScriptCallFrame.cpp:
+        (Inspector::JSJavaScriptCallFrame::thisObject):
+        (Inspector::JSJavaScriptCallFrame::isTailDeleted):
+        (Inspector::JSJavaScriptCallFrame::type):
+        * inspector/JSJavaScriptCallFrame.h:
+        * inspector/JSJavaScriptCallFramePrototype.cpp:
+        (Inspector::JSJavaScriptCallFramePrototype::finishCreation):
+        (Inspector::jsJavaScriptCallFramePrototypeFunctionEvaluateWithScopeExtension):
+        (Inspector::jsJavaScriptCallFrameAttributeType):
+        (Inspector::jsJavaScriptCallFrameIsTailDeleted):
+        * inspector/JavaScriptCallFrame.h:
+        (Inspector::JavaScriptCallFrame::type):
+        (Inspector::JavaScriptCallFrame::scopeChain):
+        (Inspector::JavaScriptCallFrame::vmEntryGlobalObject):
+        (Inspector::JavaScriptCallFrame::isTailDeleted):
+        (Inspector::JavaScriptCallFrame::thisValue):
+        (Inspector::JavaScriptCallFrame::evaluateWithScopeExtension):
+        * inspector/ScriptDebugServer.cpp:
+        (Inspector::ScriptDebugServer::evaluateBreakpointAction):
+        * inspector/protocol/Debugger.json:
+        * interpreter/ShadowChicken.cpp:
+        (JSC::ShadowChicken::update):
+        (JSC::ShadowChicken::visitChildren):
+        (JSC::ShadowChicken::reset):
+        * interpreter/ShadowChicken.h:
+        (JSC::ShadowChicken::Packet::throwMarker):
+        (JSC::ShadowChicken::Packet::prologue):
+        (JSC::ShadowChicken::Packet::tail):
+        (JSC::ShadowChicken::Frame::Frame):
+        (JSC::ShadowChicken::Frame::operator==):
+        * jit/CCallHelpers.cpp:
+        (JSC::CCallHelpers::logShadowChickenProloguePacket):
+        (JSC::CCallHelpers::logShadowChickenTailPacket):
+        (JSC::CCallHelpers::ensureShadowChickenPacket):
+        (JSC::CCallHelpers::setupShadowChickenPacket): Deleted.
+        * jit/CCallHelpers.h:
+        * jit/JITOpcodes.cpp:
+        (JSC::JIT::emit_op_profile_type):
+        (JSC::JIT::emit_op_log_shadow_chicken_prologue):
+        (JSC::JIT::emit_op_log_shadow_chicken_tail):
+        (JSC::JIT::emit_op_get_enumerable_length):
+        (JSC::JIT::emit_op_resume):
+        * jit/JITOpcodes32_64.cpp:
+        (JSC::JIT::emit_op_profile_type):
+        (JSC::JIT::emit_op_log_shadow_chicken_prologue):
+        (JSC::JIT::emit_op_log_shadow_chicken_tail):
+        * jit/RegisterSet.cpp:
+        (JSC::RegisterSet::webAssemblyCalleeSaveRegisters):
+        (JSC::RegisterSet::argumentGPRS):
+        (JSC::RegisterSet::registersToNotSaveForJSCall):
+        * jit/RegisterSet.h:
+        * llint/LLIntData.cpp:
+        (JSC::LLInt::Data::performAssertions):
+        * llint/LLIntSlowPaths.cpp:
+        (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+        * llint/LowLevelInterpreter.asm:
+        * llint/LowLevelInterpreter32_64.asm:
+        * llint/LowLevelInterpreter64.asm:
+        * runtime/CodeCache.cpp:
+        (JSC::CodeCache::getGlobalCodeBlock):
+        * runtime/Options.h:
+        * tests/stress/shadow-chicken-enabled.js:
+        (test5a.foo):
+        (test5a):
+        (test5b.foo):
+        (test5b):
+        (test6.foo):
+        (test6):
+
+2016-05-16  Saam barati  <sbarati@apple.com>
+
         TypeSet/StructureShape have a flawed sense of JS prototype chains
         https://bugs.webkit.org/show_bug.cgi?id=157760
 
index bd9a7bb..cbc2150 100644 (file)
                C4F4B6F61A05C984005CAB76 /* objc_generator_templates.py in Headers */ = {isa = PBXBuildFile; fileRef = C4F4B6D81A05C76F005CAB76 /* objc_generator_templates.py */; settings = {ATTRIBUTES = (Private, ); }; };
                DC00039319D8BE6F00023EB0 /* DFGPreciseLocalClobberize.h in Headers */ = {isa = PBXBuildFile; fileRef = DC00039019D8BE6F00023EB0 /* DFGPreciseLocalClobberize.h */; };
                DC17E8171C9C91D6008A6AB3 /* ShadowChicken.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DC17E8131C9C7FD4008A6AB3 /* ShadowChicken.cpp */; };
-               DC17E8181C9C91D9008A6AB3 /* ShadowChicken.h in Headers */ = {isa = PBXBuildFile; fileRef = DC17E8141C9C7FD4008A6AB3 /* ShadowChicken.h */; };
-               DC17E8191C9C91DB008A6AB3 /* ShadowChickenInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = DC17E8151C9C7FD4008A6AB3 /* ShadowChickenInlines.h */; };
+               DC17E8181C9C91D9008A6AB3 /* ShadowChicken.h in Headers */ = {isa = PBXBuildFile; fileRef = DC17E8141C9C7FD4008A6AB3 /* ShadowChicken.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               DC17E8191C9C91DB008A6AB3 /* ShadowChickenInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = DC17E8151C9C7FD4008A6AB3 /* ShadowChickenInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
                DC17E81A1C9C91E9008A6AB3 /* CCallHelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DC17E8161C9C802B008A6AB3 /* CCallHelpers.cpp */; };
                DC2143071CA32E55000A8869 /* ICStats.h in Headers */ = {isa = PBXBuildFile; fileRef = DC2143061CA32E52000A8869 /* ICStats.h */; };
                DC2143081CA32E58000A8869 /* ICStats.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DC2143051CA32E52000A8869 /* ICStats.cpp */; };
                                0F4570391BE44C910062A629 /* AirEliminateDeadCode.h in Headers */,
                                79B00CBD1C6AB07E0088C65D /* ProxyConstructor.h in Headers */,
                                990DA67F1C8E316A00295159 /* generate_objc_protocol_type_conversions_implementation.py in Headers */,
+                               DC17E8191C9C91DB008A6AB3 /* ShadowChickenInlines.h in Headers */,
+                               DC17E8181C9C91D9008A6AB3 /* ShadowChicken.h in Headers */,
                                799EF7C41C56ED96002B0534 /* B3PCToOriginMap.h in Headers */,
                                79B00CBF1C6AB07E0088C65D /* ProxyObject.h in Headers */,
                                79160DBE1C8E3EC8008C085A /* ProxyRevoke.h in Headers */,
                                BC18C41E0E16F5CD00B34460 /* JSContextRef.h in Headers */,
                                A5EA70EE19F5B5C40098F5EC /* JSContextRefInspectorSupport.h in Headers */,
                                A5D2E665195E174000A518E7 /* JSContextRefInternal.h in Headers */,
-                               DC17E8191C9C91DB008A6AB3 /* ShadowChickenInlines.h in Headers */,
                                A5AB49DD1BEC8086007020FB /* PerGlobalObjectWrapperWorld.h in Headers */,
                                148CD1D8108CF902008163C6 /* JSContextRefPrivate.h in Headers */,
                                A72028B81797601E0098028C /* JSCTestRunnerUtils.h in Headers */,
                                A7CA3AE617DA41AE006538AF /* WeakMapPrototype.h in Headers */,
                                0F242DA713F3B1E8007ADD4C /* WeakReferenceHarvester.h in Headers */,
                                14E84FA114EE1ACC00D6D5D4 /* WeakSet.h in Headers */,
-                               DC17E8181C9C91D9008A6AB3 /* ShadowChicken.h in Headers */,
                                709FB86A1AE335C60039D069 /* WeakSetConstructor.h in Headers */,
                                14150133154BB13F005D8C98 /* WeakSetInlines.h in Headers */,
                                709FB86C1AE335C60039D069 /* WeakSetPrototype.h in Headers */,
index 7b49c64..ba18a7e 100644 (file)
             { "name" : "op_save", "length" : 4 },
             { "name" : "op_resume", "length" : 3 },
             { "name" : "op_watchdog", "length" : 1 },
-            { "name" : "op_log_shadow_chicken_prologue", "length" : 1},
-            { "name" : "op_log_shadow_chicken_tail", "length" : 1}
+            { "name" : "op_log_shadow_chicken_prologue", "length" : 2},
+            { "name" : "op_log_shadow_chicken_tail", "length" : 3}
         ]
     },
     {
index 4a1672e..14e26b8 100644 (file)
@@ -55,8 +55,6 @@ void computeUsesForBytecodeOffset(
     case op_create_cloned_arguments:
     case op_get_rest_length:
     case op_watchdog:
-    case op_log_shadow_chicken_prologue:
-    case op_log_shadow_chicken_tail:
         return;
     case op_assert:
     case op_get_scope:
@@ -74,6 +72,7 @@ void computeUsesForBytecodeOffset(
     case op_jneq_null:
     case op_dec:
     case op_inc:
+    case op_log_shadow_chicken_prologue:
     case op_resume: {
         ASSERT(opcodeLengths[opcodeID] > 1);
         functor(codeBlock, instruction, opcodeID, instruction[1].u.operand);
@@ -88,6 +87,7 @@ void computeUsesForBytecodeOffset(
     case op_jngreatereq:
     case op_jless:
     case op_set_function_name:
+    case op_log_shadow_chicken_tail:
     case op_copy_rest: {
         ASSERT(opcodeLengths[opcodeID] > 2);
         functor(codeBlock, instruction, opcodeID, instruction[1].u.operand);
index 3a808cb..240527c 100644 (file)
@@ -1360,11 +1360,16 @@ void CodeBlock::dumpBytecode(
             break;
         }
         case op_log_shadow_chicken_prologue: {
+            int r0 = (++it)->u.operand;
             printLocationAndOp(out, exec, location, it, "log_shadow_chicken_prologue");
+            out.printf("%s", registerName(r0).data());
             break;
         }
         case op_log_shadow_chicken_tail: {
+            int r0 = (++it)->u.operand;
+            int r1 = (++it)->u.operand;
             printLocationAndOp(out, exec, location, it, "log_shadow_chicken_tail");
+            out.printf("%s, %s", registerName(r0).data(), registerName(r1).data());
             break;
         }
         case op_switch_imm: {
@@ -4387,4 +4392,24 @@ Optional<CodeOrigin> CodeBlock::findPC(void* pc)
 }
 #endif // ENABLE(JIT)
 
+Optional<unsigned> CodeBlock::bytecodeOffsetFromCallSiteIndex(CallSiteIndex callSiteIndex)
+{
+    Optional<unsigned> bytecodeOffset;
+    JITCode::JITType jitType = this->jitType();
+    if (jitType == JITCode::InterpreterThunk || jitType == JITCode::BaselineJIT) {
+#if USE(JSVALUE64)
+        bytecodeOffset = callSiteIndex.bits();
+#else
+        Instruction* instruction = bitwise_cast<Instruction*>(callSiteIndex.bits());
+        bytecodeOffset = instruction - instructions().begin();
+#endif
+    } else if (jitType == JITCode::DFGJIT || jitType == JITCode::FTLJIT) {
+        RELEASE_ASSERT(canGetCodeOrigin(callSiteIndex));
+        CodeOrigin origin = codeOrigin(callSiteIndex);
+        bytecodeOffset = origin.bytecodeIndex;
+    }
+
+    return bytecodeOffset;
+}
+
 } // namespace JSC
index d201f39..b159789 100644 (file)
@@ -231,6 +231,8 @@ public:
     void expressionRangeForBytecodeOffset(unsigned bytecodeOffset, int& divot,
                                           int& startOffset, int& endOffset, unsigned& line, unsigned& column);
 
+    Optional<unsigned> bytecodeOffsetFromCallSiteIndex(CallSiteIndex);
+
     void getStubInfoMap(const ConcurrentJITLocker&, StubInfoMap& result);
     void getStubInfoMap(StubInfoMap& result);
     
@@ -833,6 +835,8 @@ public:
         m_steppingMode = SteppingModeDisabled;
         m_numBreakpoints = 0;
     }
+
+    bool wasCompiledWithDebuggingOpcodes() const { return m_unlinkedCode->wasCompiledWithDebuggingOpcodes(); }
     
     // FIXME: Make these remaining members private.
 
index 291065a..a7976a4 100644 (file)
@@ -51,7 +51,7 @@ const ClassInfo UnlinkedModuleProgramCodeBlock::s_info = { "UnlinkedModuleProgra
 const ClassInfo UnlinkedEvalCodeBlock::s_info = { "UnlinkedEvalCodeBlock", &Base::s_info, 0, CREATE_METHOD_TABLE(UnlinkedEvalCodeBlock) };
 const ClassInfo UnlinkedFunctionCodeBlock::s_info = { "UnlinkedFunctionCodeBlock", &Base::s_info, 0, CREATE_METHOD_TABLE(UnlinkedFunctionCodeBlock) };
 
-UnlinkedCodeBlock::UnlinkedCodeBlock(VM* vm, Structure* structure, CodeType codeType, const ExecutableInfo& info)
+UnlinkedCodeBlock::UnlinkedCodeBlock(VM* vm, Structure* structure, CodeType codeType, const ExecutableInfo& info, DebuggerMode debuggerMode)
     : Base(*vm, structure)
     , m_numVars(0)
     , m_numCalleeLocals(0)
@@ -68,6 +68,7 @@ UnlinkedCodeBlock::UnlinkedCodeBlock(VM* vm, Structure* structure, CodeType code
     , m_evalContextType(static_cast<unsigned>(info.evalContextType()))
     , m_isArrowFunctionContext(info.isArrowFunctionContext())
     , m_isClassContext(info.isClassContext())
+    , m_wasCompiledWithDebuggingOpcodes(debuggerMode == DebuggerMode::DebuggerOn)
     , m_firstLine(0)
     , m_lineCount(0)
     , m_endColumn(UINT_MAX)
index d265bb1..2e0e293 100644 (file)
@@ -368,8 +368,10 @@ public:
 
     void dumpExpressionRangeInfo(); // For debugging purpose only.
 
+    bool wasCompiledWithDebuggingOpcodes() const { return m_wasCompiledWithDebuggingOpcodes; }
+
 protected:
-    UnlinkedCodeBlock(VM*, Structure*, CodeType, const ExecutableInfo&);
+    UnlinkedCodeBlock(VM*, Structure*, CodeType, const ExecutableInfo&, DebuggerMode);
     ~UnlinkedCodeBlock();
 
     void finishCreation(VM& vm)
@@ -409,6 +411,7 @@ private:
     unsigned m_evalContextType : 2;
     unsigned m_isArrowFunctionContext : 1;
     unsigned m_isClassContext : 1;
+    unsigned m_wasCompiledWithDebuggingOpcodes : 1;
     unsigned m_firstLine;
     unsigned m_lineCount;
     unsigned m_endColumn;
@@ -479,8 +482,8 @@ public:
     typedef UnlinkedCodeBlock Base;
 
 protected:
-    UnlinkedGlobalCodeBlock(VM* vm, Structure* structure, CodeType codeType, const ExecutableInfo& info)
-        : Base(vm, structure, codeType, info)
+    UnlinkedGlobalCodeBlock(VM* vm, Structure* structure, CodeType codeType, const ExecutableInfo& info, DebuggerMode debuggerMode)
+        : Base(vm, structure, codeType, info, debuggerMode)
     {
     }
 
@@ -490,9 +493,9 @@ protected:
 class UnlinkedProgramCodeBlock final : public UnlinkedGlobalCodeBlock {
 private:
     friend class CodeCache;
-    static UnlinkedProgramCodeBlock* create(VM* vm, const ExecutableInfo& info)
+    static UnlinkedProgramCodeBlock* create(VM* vm, const ExecutableInfo& info, DebuggerMode debuggerMode)
     {
-        UnlinkedProgramCodeBlock* instance = new (NotNull, allocateCell<UnlinkedProgramCodeBlock>(vm->heap)) UnlinkedProgramCodeBlock(vm, vm->unlinkedProgramCodeBlockStructure.get(), info);
+        UnlinkedProgramCodeBlock* instance = new (NotNull, allocateCell<UnlinkedProgramCodeBlock>(vm->heap)) UnlinkedProgramCodeBlock(vm, vm->unlinkedProgramCodeBlockStructure.get(), info, debuggerMode);
         instance->finishCreation(*vm);
         return instance;
     }
@@ -512,8 +515,8 @@ public:
     static void visitChildren(JSCell*, SlotVisitor&);
 
 private:
-    UnlinkedProgramCodeBlock(VM* vm, Structure* structure, const ExecutableInfo& info)
-        : Base(vm, structure, GlobalCode, info)
+    UnlinkedProgramCodeBlock(VM* vm, Structure* structure, const ExecutableInfo& info, DebuggerMode debuggerMode)
+        : Base(vm, structure, GlobalCode, info, debuggerMode)
     {
     }
 
@@ -532,9 +535,9 @@ public:
 class UnlinkedModuleProgramCodeBlock final : public UnlinkedGlobalCodeBlock {
 private:
     friend class CodeCache;
-    static UnlinkedModuleProgramCodeBlock* create(VM* vm, const ExecutableInfo& info)
+    static UnlinkedModuleProgramCodeBlock* create(VM* vm, const ExecutableInfo& info, DebuggerMode debuggerMode)
     {
-        UnlinkedModuleProgramCodeBlock* instance = new (NotNull, allocateCell<UnlinkedModuleProgramCodeBlock>(vm->heap)) UnlinkedModuleProgramCodeBlock(vm, vm->unlinkedModuleProgramCodeBlockStructure.get(), info);
+        UnlinkedModuleProgramCodeBlock* instance = new (NotNull, allocateCell<UnlinkedModuleProgramCodeBlock>(vm->heap)) UnlinkedModuleProgramCodeBlock(vm, vm->unlinkedModuleProgramCodeBlockStructure.get(), info, debuggerMode);
         instance->finishCreation(*vm);
         return instance;
     }
@@ -578,8 +581,8 @@ public:
     }
 
 private:
-    UnlinkedModuleProgramCodeBlock(VM* vm, Structure* structure, const ExecutableInfo& info)
-        : Base(vm, structure, ModuleCode, info)
+    UnlinkedModuleProgramCodeBlock(VM* vm, Structure* structure, const ExecutableInfo& info, DebuggerMode debuggerMode)
+        : Base(vm, structure, ModuleCode, info, debuggerMode)
     {
     }
 
@@ -598,9 +601,9 @@ class UnlinkedEvalCodeBlock final : public UnlinkedGlobalCodeBlock {
 private:
     friend class CodeCache;
 
-    static UnlinkedEvalCodeBlock* create(VM* vm, const ExecutableInfo& info)
+    static UnlinkedEvalCodeBlock* create(VM* vm, const ExecutableInfo& info, DebuggerMode debuggerMode)
     {
-        UnlinkedEvalCodeBlock* instance = new (NotNull, allocateCell<UnlinkedEvalCodeBlock>(vm->heap)) UnlinkedEvalCodeBlock(vm, vm->unlinkedEvalCodeBlockStructure.get(), info);
+        UnlinkedEvalCodeBlock* instance = new (NotNull, allocateCell<UnlinkedEvalCodeBlock>(vm->heap)) UnlinkedEvalCodeBlock(vm, vm->unlinkedEvalCodeBlockStructure.get(), info, debuggerMode);
         instance->finishCreation(*vm);
         return instance;
     }
@@ -620,8 +623,8 @@ public:
     }
 
 private:
-    UnlinkedEvalCodeBlock(VM* vm, Structure* structure, const ExecutableInfo& info)
-        : Base(vm, structure, EvalCode, info)
+    UnlinkedEvalCodeBlock(VM* vm, Structure* structure, const ExecutableInfo& info, DebuggerMode debuggerMode)
+        : Base(vm, structure, EvalCode, info, debuggerMode)
     {
     }
 
@@ -641,9 +644,9 @@ public:
     typedef UnlinkedCodeBlock Base;
     static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
 
-    static UnlinkedFunctionCodeBlock* create(VM* vm, CodeType codeType, const ExecutableInfo& info)
+    static UnlinkedFunctionCodeBlock* create(VM* vm, CodeType codeType, const ExecutableInfo& info, DebuggerMode debuggerMode)
     {
-        UnlinkedFunctionCodeBlock* instance = new (NotNull, allocateCell<UnlinkedFunctionCodeBlock>(vm->heap)) UnlinkedFunctionCodeBlock(vm, vm->unlinkedFunctionCodeBlockStructure.get(), codeType, info);
+        UnlinkedFunctionCodeBlock* instance = new (NotNull, allocateCell<UnlinkedFunctionCodeBlock>(vm->heap)) UnlinkedFunctionCodeBlock(vm, vm->unlinkedFunctionCodeBlockStructure.get(), codeType, info, debuggerMode);
         instance->finishCreation(*vm);
         return instance;
     }
@@ -651,8 +654,8 @@ public:
     static void destroy(JSCell*);
 
 private:
-    UnlinkedFunctionCodeBlock(VM* vm, Structure* structure, CodeType codeType, const ExecutableInfo& info)
-        : Base(vm, structure, codeType, info)
+    UnlinkedFunctionCodeBlock(VM* vm, Structure* structure, CodeType codeType, const ExecutableInfo& info, DebuggerMode debuggerMode)
+        : Base(vm, structure, codeType, info, debuggerMode)
     {
     }
     
index 6627884..7eb1eb6 100644 (file)
@@ -68,7 +68,7 @@ static UnlinkedFunctionCodeBlock* generateUnlinkedFunctionCodeBlock(
 
     bool isClassContext = executable->superBinding() == SuperBinding::Needed;
 
-    UnlinkedFunctionCodeBlock* result = UnlinkedFunctionCodeBlock::create(&vm, FunctionCode, ExecutableInfo(function->usesEval(), function->isStrictMode(), kind == CodeForConstruct, functionKind == UnlinkedBuiltinFunction, executable->constructorKind(), executable->superBinding(), parseMode, executable->derivedContextType(), false, isClassContext, EvalContextType::FunctionEvalContext));
+    UnlinkedFunctionCodeBlock* result = UnlinkedFunctionCodeBlock::create(&vm, FunctionCode, ExecutableInfo(function->usesEval(), function->isStrictMode(), kind == CodeForConstruct, functionKind == UnlinkedBuiltinFunction, executable->constructorKind(), executable->superBinding(), parseMode, executable->derivedContextType(), false, isClassContext, EvalContextType::FunctionEvalContext), debuggerMode);
 
     error = BytecodeGenerator::generate(vm, function.get(), result, debuggerMode, profilerMode, executable->parentScopeTDZVariables());
 
index ec110f9..dceb82a 100644 (file)
@@ -64,6 +64,8 @@ void Label::setLocation(unsigned location)
 ParserError BytecodeGenerator::generate()
 {
     m_codeBlock->setThisRegister(m_thisRegister.virtualRegister());
+
+    emitLogShadowChickenPrologueIfNecessary();
     
     // If we have declared a variable named "arguments" and we are using arguments then we should
     // perform that assignment now.
@@ -291,7 +293,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, Unlinke
     emitEnter();
 
     allocateAndEmitScope();
-    
+
     m_calleeRegister.setIndex(JSStack::Callee);
 
     initializeParameters(parameters);
@@ -1105,7 +1107,6 @@ UnlinkedValueProfile BytecodeGenerator::emitProfiledOpcode(OpcodeID opcodeID)
 void BytecodeGenerator::emitEnter()
 {
     emitOpcode(op_enter);
-    emitLogShadowChickenPrologueIfNecessary();
     emitWatchdog();
 }
 
@@ -3167,6 +3168,7 @@ void BytecodeGenerator::emitLogShadowChickenPrologueIfNecessary()
     if (!m_shouldEmitDebugHooks && !Options::alwaysUseShadowChicken())
         return;
     emitOpcode(op_log_shadow_chicken_prologue);
+    instructions().append(scopeRegister()->index());
 }
 
 void BytecodeGenerator::emitLogShadowChickenTailIfNecessary()
@@ -3174,6 +3176,8 @@ void BytecodeGenerator::emitLogShadowChickenTailIfNecessary()
     if (!m_shouldEmitDebugHooks && !Options::alwaysUseShadowChicken())
         return;
     emitOpcode(op_log_shadow_chicken_tail);
+    instructions().append(thisRegister()->index());
+    instructions().append(scopeRegister()->index());
 }
 
 void BytecodeGenerator::emitCallDefineProperty(RegisterID* newObj, RegisterID* propertyNameRegister,
index e2f0205..f88933c 100644 (file)
@@ -77,8 +77,6 @@ public:
         : m_debugger(debugger)
     {
         ASSERT(!m_debugger.m_currentDebuggerCallFrame);
-        if (m_debugger.m_currentCallFrame)
-            m_debugger.m_currentDebuggerCallFrame = DebuggerCallFrame::create(debugger.m_currentCallFrame);
     }
 
     ~DebuggerPausedScope()
@@ -770,9 +768,10 @@ void Debugger::didReachBreakpoint(CallFrame* callFrame)
     updateCallFrameAndPauseIfNeeded(callFrame);
 }
 
-DebuggerCallFrame* Debugger::currentDebuggerCallFrame() const
+DebuggerCallFrame* Debugger::currentDebuggerCallFrame()
 {
-    ASSERT(m_currentDebuggerCallFrame);
+    if (!m_currentDebuggerCallFrame)
+        m_currentDebuggerCallFrame = DebuggerCallFrame::create(m_currentCallFrame);
     return m_currentDebuggerCallFrame.get();
 }
 
index 00ece4a..6ce0d52 100644 (file)
@@ -50,7 +50,7 @@ public:
 
     VM& vm() { return m_vm; }
 
-    JSC::DebuggerCallFrame* currentDebuggerCallFrame() const;
+    JSC::DebuggerCallFrame* currentDebuggerCallFrame();
     bool hasHandlerForExceptionCallback() const
     {
         ASSERT(m_reasonForPause == PausedForException);
index 4b34bf3..8dc7e8f 100644 (file)
 #include "JSLexicalEnvironment.h"
 #include "JSWithScope.h"
 #include "Parser.h"
+#include "ShadowChickenInlines.h"
 #include "StackVisitor.h"
 #include "StrongInlines.h"
 
 namespace JSC {
 
-// FIXME: Make this use ShadowChicken so that it sees tail-deleted frames.
-// https://bugs.webkit.org/show_bug.cgi?id=155690
-
 class LineAndColumnFunctor {
 public:
     StackVisitor::Status operator()(StackVisitor& visitor) const
@@ -62,61 +60,59 @@ private:
     mutable unsigned m_column;
 };
 
-class FindCallerMidStackFunctor {
-public:
-    FindCallerMidStackFunctor(CallFrame* callFrame)
-        : m_callFrame(callFrame)
-        , m_callerFrame(nullptr)
-    { }
-
-    StackVisitor::Status operator()(StackVisitor& visitor) const
-    {
-        if (visitor->callFrame() == m_callFrame) {
-            m_callerFrame = visitor->callerFrame();
-            return StackVisitor::Done;
-        }
-        return StackVisitor::Continue;
+Ref<DebuggerCallFrame> DebuggerCallFrame::create(CallFrame* callFrame)
+{
+    Vector<ShadowChicken::Frame> frames;
+    callFrame->vm().shadowChicken().iterate(callFrame->vm(), callFrame, [&] (const ShadowChicken::Frame& frame) -> bool {
+        frames.append(frame);
+        return true;
+    });
+
+    RELEASE_ASSERT(frames.size());
+    RELEASE_ASSERT(!frames[0].isTailDeleted); // The top frame should never be tail deleted.
+    RELEASE_ASSERT(!frames[frames.size() - 1].isTailDeleted); // The first frame should never be tail deleted.
+
+    RefPtr<DebuggerCallFrame> currentParent = nullptr;
+    ExecState* exec = nullptr;
+    for (unsigned i = frames.size(); i--; ) {
+        const ShadowChicken::Frame& frame = frames[i];
+        if (!frame.isTailDeleted)
+            exec = frame.frame;
+        ASSERT(exec);
+        Ref<DebuggerCallFrame> currentFrame = adoptRef(*new DebuggerCallFrame(exec, frame));
+        currentFrame->m_caller = currentParent;
+        currentParent = WTFMove(currentFrame);
     }
+    return *currentParent;
+}
 
-    CallFrame* getCallerFrame() const { return m_callerFrame; }
-
-private:
-    CallFrame* m_callFrame;
-    mutable CallFrame* m_callerFrame;
-};
-
-DebuggerCallFrame::DebuggerCallFrame(CallFrame* callFrame)
-    : m_callFrame(callFrame)
+DebuggerCallFrame::DebuggerCallFrame(CallFrame* callFrame, const ShadowChicken::Frame& frame)
+    : m_validMachineFrame(callFrame)
+    , m_shadowChickenFrame(frame)
 {
-    m_position = positionForCallFrame(m_callFrame);
+    m_position = currentPosition();
 }
 
 RefPtr<DebuggerCallFrame> DebuggerCallFrame::callerFrame()
 {
     ASSERT(isValid());
     if (!isValid())
-        return 0;
-
-    if (m_caller)
-        return m_caller;
-
-    FindCallerMidStackFunctor functor(m_callFrame);
-    m_callFrame->vm().topCallFrame->iterate(functor);
-
-    CallFrame* callerFrame = functor.getCallerFrame();
-    if (!callerFrame)
         return nullptr;
 
-    m_caller = DebuggerCallFrame::create(callerFrame);
     return m_caller;
 }
 
+ExecState* DebuggerCallFrame::globalExec()
+{
+    return scope()->globalObject()->globalExec();
+}
+
 JSC::JSGlobalObject* DebuggerCallFrame::vmEntryGlobalObject() const
 {
     ASSERT(isValid());
     if (!isValid())
-        return 0;
-    return m_callFrame->vmEntryGlobalObject();
+        return nullptr;
+    return m_validMachineFrame->vmEntryGlobalObject();
 }
 
 SourceID DebuggerCallFrame::sourceID() const
@@ -124,7 +120,9 @@ SourceID DebuggerCallFrame::sourceID() const
     ASSERT(isValid());
     if (!isValid())
         return noSourceID;
-    return sourceIDForCallFrame(m_callFrame);
+    if (isTailDeleted())
+        return m_shadowChickenFrame.codeBlock->ownerScriptExecutable()->sourceID();
+    return sourceIDForCallFrame(m_validMachineFrame);
 }
 
 String DebuggerCallFrame::functionName() const
@@ -132,25 +130,34 @@ String DebuggerCallFrame::functionName() const
     ASSERT(isValid());
     if (!isValid())
         return String();
-    return m_callFrame->friendlyFunctionName();
+
+    if (isTailDeleted()) {
+        if (JSFunction* func = jsDynamicCast<JSFunction*>(m_shadowChickenFrame.callee))
+            return func->calculatedDisplayName(m_validMachineFrame);
+        return m_shadowChickenFrame.codeBlock->inferredName().data();
+    }
+
+    return m_validMachineFrame->friendlyFunctionName();
 }
 
 DebuggerScope* DebuggerCallFrame::scope()
 {
     ASSERT(isValid());
     if (!isValid())
-        return 0;
+        return nullptr;
 
     if (!m_scope) {
-        VM& vm = m_callFrame->vm();
+        VM& vm = m_validMachineFrame->vm();
         JSScope* scope;
-        CodeBlock* codeBlock = m_callFrame->codeBlock();
-        if (codeBlock && codeBlock->scopeRegister().isValid())
-            scope = m_callFrame->scope(codeBlock->scopeRegister().offset());
-        else if (JSCallee* callee = jsDynamicCast<JSCallee*>(m_callFrame->callee()))
+        CodeBlock* codeBlock = m_validMachineFrame->codeBlock();
+        if (isTailDeleted())
+            scope = m_shadowChickenFrame.scope;
+        else if (codeBlock && codeBlock->scopeRegister().isValid())
+            scope = m_validMachineFrame->scope(codeBlock->scopeRegister().offset());
+        else if (JSCallee* callee = jsDynamicCast<JSCallee*>(m_validMachineFrame->callee()))
             scope = callee->scope();
         else
-            scope = m_callFrame->lexicalGlobalObject();
+            scope = m_validMachineFrame->lexicalGlobalObject()->globalLexicalEnvironment();
 
         m_scope.set(vm, DebuggerScope::create(vm, scope));
     }
@@ -163,7 +170,10 @@ DebuggerCallFrame::Type DebuggerCallFrame::type() const
     if (!isValid())
         return ProgramType;
 
-    if (jsDynamicCast<JSFunction*>(m_callFrame->callee()))
+    if (isTailDeleted())
+        return FunctionType;
+
+    if (jsDynamicCast<JSFunction*>(m_validMachineFrame->callee()))
         return FunctionType;
 
     return ProgramType;
@@ -172,40 +182,63 @@ DebuggerCallFrame::Type DebuggerCallFrame::type() const
 JSValue DebuggerCallFrame::thisValue() const
 {
     ASSERT(isValid());
-    return thisValueForCallFrame(m_callFrame);
+    if (!isValid())
+        return jsUndefined();
+
+    CodeBlock* codeBlock = nullptr;
+    JSValue thisValue;
+    if (isTailDeleted()) {
+        thisValue = m_shadowChickenFrame.thisValue;
+        codeBlock = m_shadowChickenFrame.codeBlock;
+    } else {
+        thisValue = m_validMachineFrame->thisValue();
+        codeBlock = m_validMachineFrame->codeBlock();
+    }
+
+    if (!thisValue)
+        return jsUndefined();
+
+    ECMAMode ecmaMode = NotStrictMode;
+    if (codeBlock && codeBlock->isStrictMode())
+        ecmaMode = StrictMode;
+    return thisValue.toThis(m_validMachineFrame, ecmaMode);
 }
 
 // Evaluate some JavaScript code in the scope of this frame.
 JSValue DebuggerCallFrame::evaluateWithScopeExtension(const String& script, JSObject* scopeExtensionObject, NakedPtr<Exception>& exception)
 {
     ASSERT(isValid());
-    CallFrame* callFrame = m_callFrame;
+    CallFrame* callFrame = m_validMachineFrame;
     if (!callFrame)
         return jsUndefined();
 
     JSLockHolder lock(callFrame);
 
-    if (!callFrame->codeBlock())
+    CodeBlock* codeBlock = nullptr;
+    if (isTailDeleted())
+        codeBlock = m_shadowChickenFrame.codeBlock;
+    else
+        codeBlock = callFrame->codeBlock();
+    if (!codeBlock)
         return jsUndefined();
     
     DebuggerEvalEnabler evalEnabler(callFrame);
     VM& vm = callFrame->vm();
-    auto& codeBlock = *callFrame->codeBlock();
-    ThisTDZMode thisTDZMode = codeBlock.unlinkedCodeBlock()->constructorKind() == ConstructorKind::Derived ? ThisTDZMode::AlwaysCheck : ThisTDZMode::CheckIfNeeded;
+    ThisTDZMode thisTDZMode = codeBlock->unlinkedCodeBlock()->constructorKind() == ConstructorKind::Derived ? ThisTDZMode::AlwaysCheck : ThisTDZMode::CheckIfNeeded;
 
     EvalContextType evalContextType;
     
-    if (isFunctionParseMode(codeBlock.unlinkedCodeBlock()->parseMode()))
+    if (isFunctionParseMode(codeBlock->unlinkedCodeBlock()->parseMode()))
         evalContextType = EvalContextType::FunctionEvalContext;
-    else if (codeBlock.unlinkedCodeBlock()->codeType() == EvalCode)
-        evalContextType = codeBlock.unlinkedCodeBlock()->evalContextType();
+    else if (codeBlock->unlinkedCodeBlock()->codeType() == EvalCode)
+        evalContextType = codeBlock->unlinkedCodeBlock()->evalContextType();
     else 
         evalContextType = EvalContextType::None;
 
     VariableEnvironment variablesUnderTDZ;
     JSScope::collectVariablesUnderTDZ(scope()->jsScope(), variablesUnderTDZ);
 
-    EvalExecutable* eval = EvalExecutable::create(callFrame, makeSource(script), codeBlock.isStrictMode(), thisTDZMode, codeBlock.unlinkedCodeBlock()->derivedContextType(), codeBlock.unlinkedCodeBlock()->isArrowFunction(), evalContextType, &variablesUnderTDZ);
+    EvalExecutable* eval = EvalExecutable::create(callFrame, makeSource(script), codeBlock->isStrictMode(), thisTDZMode, codeBlock->unlinkedCodeBlock()->derivedContextType(), codeBlock->unlinkedCodeBlock()->isArrowFunction(), evalContextType, &variablesUnderTDZ);
     if (vm.exception()) {
         exception = vm.exception();
         vm.clearException();
@@ -218,7 +251,7 @@ JSValue DebuggerCallFrame::evaluateWithScopeExtension(const String& script, JSOb
         globalObject->setGlobalScopeExtension(JSWithScope::create(vm, globalObject, scopeExtensionObject, ignoredPreviousScope));
     }
 
-    JSValue thisValue = thisValueForCallFrame(callFrame);
+    JSValue thisValue = this->thisValue();
     JSValue result = vm.interpreter->execute(eval, callFrame, thisValue, scope()->jsScope());
     if (vm.exception()) {
         exception = vm.exception();
@@ -236,7 +269,7 @@ void DebuggerCallFrame::invalidate()
 {
     RefPtr<DebuggerCallFrame> frame = this;
     while (frame) {
-        frame->m_callFrame = nullptr;
+        frame->m_validMachineFrame = nullptr;
         if (frame->m_scope) {
             frame->m_scope->invalidateChain();
             frame->m_scope.clear();
@@ -245,11 +278,24 @@ void DebuggerCallFrame::invalidate()
     }
 }
 
-TextPosition DebuggerCallFrame::positionForCallFrame(CallFrame* callFrame)
+TextPosition DebuggerCallFrame::currentPosition()
 {
-    if (!callFrame)
+    if (!m_validMachineFrame)
         return TextPosition();
 
+    if (isTailDeleted()) {
+        CodeBlock* codeBlock = m_shadowChickenFrame.codeBlock;
+        if (Optional<unsigned> bytecodeOffset = codeBlock->bytecodeOffsetFromCallSiteIndex(m_shadowChickenFrame.callSiteIndex)) {
+            return TextPosition(OrdinalNumber::fromOneBasedInt(codeBlock->lineNumberForBytecodeOffset(*bytecodeOffset)),
+                OrdinalNumber::fromOneBasedInt(codeBlock->columnNumberForBytecodeOffset(*bytecodeOffset)));
+        }
+    }
+
+    return positionForCallFrame(m_validMachineFrame);
+}
+
+TextPosition DebuggerCallFrame::positionForCallFrame(CallFrame* callFrame)
+{
     LineAndColumnFunctor functor;
     callFrame->iterate(functor);
     return TextPosition(OrdinalNumber::fromOneBasedInt(functor.line()), OrdinalNumber::fromOneBasedInt(functor.column()));
@@ -264,20 +310,4 @@ SourceID DebuggerCallFrame::sourceIDForCallFrame(CallFrame* callFrame)
     return codeBlock->ownerScriptExecutable()->sourceID();
 }
 
-JSValue DebuggerCallFrame::thisValueForCallFrame(CallFrame* callFrame)
-{
-    if (!callFrame)
-        return jsUndefined();
-
-    if (!callFrame->thisValue())
-        return jsUndefined();
-
-    ECMAMode ecmaMode = NotStrictMode;
-    CodeBlock* codeBlock = callFrame->codeBlock();
-    if (codeBlock && codeBlock->isStrictMode())
-        ecmaMode = StrictMode;
-
-    return callFrame->thisValue().toThis(callFrame, ecmaMode);
-}
-
 } // namespace JSC
index 40e141e..7983191 100644 (file)
@@ -29,7 +29,9 @@
 #ifndef DebuggerCallFrame_h
 #define DebuggerCallFrame_h
 
+#include "CallFrame.h"
 #include "DebuggerPrimitives.h"
+#include "ShadowChicken.h"
 #include "Strong.h"
 #include <wtf/NakedPtr.h>
 #include <wtf/PassRefPtr.h>
@@ -40,22 +42,15 @@ namespace JSC {
 
 class DebuggerScope;
 class Exception;
-class ExecState;
-typedef ExecState CallFrame;
 
 class DebuggerCallFrame : public RefCounted<DebuggerCallFrame> {
 public:
     enum Type { ProgramType, FunctionType };
 
-    static Ref<DebuggerCallFrame> create(CallFrame* callFrame)
-    {
-        return adoptRef(*new DebuggerCallFrame(callFrame));
-    }
-
-    JS_EXPORT_PRIVATE explicit DebuggerCallFrame(CallFrame*);
+    static Ref<DebuggerCallFrame> create(CallFrame*);
 
     JS_EXPORT_PRIVATE RefPtr<DebuggerCallFrame> callerFrame();
-    ExecState* exec() const { return m_callFrame; }
+    ExecState* globalExec();
     JS_EXPORT_PRIVATE SourceID sourceID() const;
 
     // line and column are in base 0 e.g. the first line is line 0.
@@ -70,23 +65,28 @@ public:
     JS_EXPORT_PRIVATE JSValue thisValue() const;
     JSValue evaluateWithScopeExtension(const String&, JSObject* scopeExtensionObject, NakedPtr<Exception>&);
 
-    bool isValid() const { return !!m_callFrame; }
+    bool isValid() const { return !!m_validMachineFrame || isTailDeleted(); }
     JS_EXPORT_PRIVATE void invalidate();
 
     // The following are only public for the Debugger's use only. They will be
     // made private soon. Other clients should not use these.
 
+    JS_EXPORT_PRIVATE TextPosition currentPosition();
     JS_EXPORT_PRIVATE static TextPosition positionForCallFrame(CallFrame*);
     JS_EXPORT_PRIVATE static SourceID sourceIDForCallFrame(CallFrame*);
-    static JSValue thisValueForCallFrame(CallFrame*);
+
+    bool isTailDeleted() const { return m_shadowChickenFrame.isTailDeleted; }
 
 private:
-    CallFrame* m_callFrame;
+    DebuggerCallFrame(CallFrame*, const ShadowChicken::Frame&);
+
+    CallFrame* m_validMachineFrame;
     RefPtr<DebuggerCallFrame> m_caller;
     TextPosition m_position;
     // The DebuggerPausedScope is responsible for calling invalidate() which,
     // in turn, will clear this strong ref.
     Strong<DebuggerScope> m_scope;
+    ShadowChicken::Frame m_shadowChickenFrame;
 };
 
 } // namespace JSC
index 6d079f6..d6913c1 100644 (file)
@@ -5109,7 +5109,7 @@ bool ByteCodeParser::parseBlock(unsigned limit)
             
         case op_log_shadow_chicken_prologue: {
             if (!m_inlineStackTop->m_inlineCallFrame)
-                addToGraph(LogShadowChickenPrologue);
+                addToGraph(LogShadowChickenPrologue, get(VirtualRegister(currentInstruction[1].u.operand)));
             NEXT_OPCODE(op_log_shadow_chicken_prologue);
         }
 
@@ -5118,7 +5118,7 @@ bool ByteCodeParser::parseBlock(unsigned limit)
                 // FIXME: The right solution for inlining is to elide these whenever the tail call
                 // ends up being inlined.
                 // https://bugs.webkit.org/show_bug.cgi?id=155686
-                addToGraph(LogShadowChickenTail);
+                addToGraph(LogShadowChickenTail, get(VirtualRegister(currentInstruction[1].u.operand)), get(VirtualRegister(currentInstruction[2].u.operand)));
             }
             NEXT_OPCODE(op_log_shadow_chicken_tail);
         }
index ff26441..0e336ee 100644 (file)
@@ -1501,6 +1501,16 @@ private:
             break;
         }
 
+        case LogShadowChickenPrologue: {
+            fixEdge<KnownCellUse>(node->child1());
+            break;
+        }
+        case LogShadowChickenTail: {
+            fixEdge<UntypedUse>(node->child1());
+            fixEdge<KnownCellUse>(node->child2());
+            break;
+        }
+
 #if !ASSERT_DISABLED
         // Have these no-op cases here to ensure that nobody forgets to add handlers for new opcodes.
         case SetArgument:
@@ -1561,8 +1571,6 @@ private:
         case CheckBadCell:
         case CheckNotEmpty:
         case CheckWatchdogTimer:
-        case LogShadowChickenPrologue:
-        case LogShadowChickenTail:
         case Unreachable:
         case ExtractOSREntryLocal:
         case LoopHint:
index 10c96f9..06681bd 100644 (file)
@@ -79,8 +79,7 @@ Graph::Graph(VM& vm, Plan& plan, LongLivedState& longLivedState)
 {
     ASSERT(m_profiledBlock);
     
-    m_hasDebuggerEnabled = m_profiledBlock->globalObject()->hasInteractiveDebugger()
-        || Options::forceDebuggerBytecodeGeneration();
+    m_hasDebuggerEnabled = m_profiledBlock->wasCompiledWithDebuggingOpcodes() || Options::forceDebuggerBytecodeGeneration();
 }
 
 Graph::~Graph()
index 71f4141..604b6a1 100644 (file)
@@ -147,10 +147,11 @@ public:
         return m_jitCode->common.addCodeOrigin(codeOrigin);
     }
 
-    void emitStoreCodeOrigin(CodeOrigin codeOrigin)
+    CallSiteIndex emitStoreCodeOrigin(CodeOrigin codeOrigin)
     {
         CallSiteIndex callSite = addCallSite(codeOrigin);
         emitStoreCallSiteIndex(callSite);
+        return callSite;
     }
 
     void emitStoreCallSiteIndex(CallSiteIndex callSite)
index d740d77..1f8ee0d 100644 (file)
@@ -5152,7 +5152,20 @@ void SpeculativeJIT::compile(Node* node)
         flushRegisters();
         prepareForExternalCall();
         m_jit.emitStoreCodeOrigin(node->origin.semantic);
-        m_jit.logShadowChickenProloguePacket();
+
+        GPRTemporary scratch1(this, GPRInfo::nonArgGPR0); // This must be a non-argument GPR.
+        GPRReg scratch1Reg = scratch1.gpr();
+        GPRTemporary shadowPacket(this);
+        GPRReg shadowPacketReg = shadowPacket.gpr();
+        GPRTemporary scratch2(this);
+        GPRReg scratch2Reg = scratch2.gpr();
+
+        m_jit.ensureShadowChickenPacket(shadowPacketReg, scratch1Reg, scratch2Reg);
+
+        SpeculateCellOperand scope(this, node->child1());
+        GPRReg scopeReg = scope.gpr();
+
+        m_jit.logShadowChickenProloguePacket(shadowPacketReg, scratch1Reg, scopeReg);
         noResult(node);
         break;
     }
@@ -5160,8 +5173,23 @@ void SpeculativeJIT::compile(Node* node)
     case LogShadowChickenTail: {
         flushRegisters();
         prepareForExternalCall();
-        m_jit.emitStoreCodeOrigin(node->origin.semantic);
-        m_jit.logShadowChickenTailPacket();
+        CallSiteIndex callSiteIndex = m_jit.emitStoreCodeOrigin(node->origin.semantic);
+
+        GPRTemporary scratch1(this, GPRInfo::nonArgGPR0); // This must be a non-argument GPR.
+        GPRReg scratch1Reg = scratch1.gpr();
+        GPRTemporary shadowPacket(this);
+        GPRReg shadowPacketReg = shadowPacket.gpr();
+        GPRTemporary scratch2(this);
+        GPRReg scratch2Reg = scratch2.gpr();
+
+        m_jit.ensureShadowChickenPacket(shadowPacketReg, scratch1Reg, scratch2Reg);
+
+        JSValueOperand thisValue(this, node->child1());
+        JSValueRegs thisRegs = thisValue.jsValueRegs();
+        SpeculateCellOperand scope(this, node->child2());
+        GPRReg scopeReg = scope.gpr();
+
+        m_jit.logShadowChickenTailPacket(shadowPacketReg, thisRegs, scopeReg, m_jit.codeBlock(), callSiteIndex);
         noResult(node);
         break;
     }
index 1400ea5..56804bc 100644 (file)
@@ -5146,7 +5146,20 @@ void SpeculativeJIT::compile(Node* node)
         flushRegisters();
         prepareForExternalCall();
         m_jit.emitStoreCodeOrigin(node->origin.semantic);
-        m_jit.logShadowChickenProloguePacket();
+
+        GPRTemporary scratch1(this, GPRInfo::nonArgGPR0); // This must be a non-argument GPR.
+        GPRReg scratch1Reg = scratch1.gpr();
+        GPRTemporary scratch2(this);
+        GPRReg scratch2Reg = scratch2.gpr();
+        GPRTemporary shadowPacket(this);
+        GPRReg shadowPacketReg = shadowPacket.gpr();
+
+        m_jit.ensureShadowChickenPacket(shadowPacketReg, scratch1Reg, scratch2Reg);
+
+        SpeculateCellOperand scope(this, node->child1());
+        GPRReg scopeReg = scope.gpr();
+
+        m_jit.logShadowChickenProloguePacket(shadowPacketReg, scratch1Reg, scopeReg);
         noResult(node);
         break;
     }
@@ -5154,8 +5167,23 @@ void SpeculativeJIT::compile(Node* node)
     case LogShadowChickenTail: {
         flushRegisters();
         prepareForExternalCall();
-        m_jit.emitStoreCodeOrigin(node->origin.semantic);
-        m_jit.logShadowChickenTailPacket();
+        CallSiteIndex callSiteIndex = m_jit.emitStoreCodeOrigin(node->origin.semantic);
+
+        GPRTemporary scratch1(this, GPRInfo::nonArgGPR0); // This must be a non-argument GPR.
+        GPRReg scratch1Reg = scratch1.gpr();
+        GPRTemporary scratch2(this);
+        GPRReg scratch2Reg = scratch2.gpr();
+        GPRTemporary shadowPacket(this);
+        GPRReg shadowPacketReg = shadowPacket.gpr();
+
+        m_jit.ensureShadowChickenPacket(shadowPacketReg, scratch1Reg, scratch2Reg);
+
+        JSValueOperand thisValue(this, node->child1());
+        JSValueRegs thisRegs = JSValueRegs(thisValue.gpr());
+        SpeculateCellOperand scope(this, node->child2());
+        GPRReg scopeReg = scope.gpr();
+
+        m_jit.logShadowChickenTailPacket(shadowPacketReg, thisRegs, scopeReg, m_jit.codeBlock(), callSiteIndex);
         noResult(node);
         break;
     }
index b11ade4..212580f 100644 (file)
@@ -87,6 +87,10 @@ namespace JSC { namespace FTL {
     macro(ShadowChicken_Packet_callee, OBJECT_OFFSETOF(ShadowChicken::Packet, callee)) \
     macro(ShadowChicken_Packet_frame, OBJECT_OFFSETOF(ShadowChicken::Packet, frame)) \
     macro(ShadowChicken_Packet_callerFrame, OBJECT_OFFSETOF(ShadowChicken::Packet, callerFrame)) \
+    macro(ShadowChicken_Packet_thisValue, OBJECT_OFFSETOF(ShadowChicken::Packet, thisValue)) \
+    macro(ShadowChicken_Packet_scope, OBJECT_OFFSETOF(ShadowChicken::Packet, scope)) \
+    macro(ShadowChicken_Packet_codeBlock, OBJECT_OFFSETOF(ShadowChicken::Packet, codeBlock)) \
+    macro(ShadowChicken_Packet_callSiteIndex, OBJECT_OFFSETOF(ShadowChicken::Packet, callSiteIndex)) \
     macro(ScopedArguments_overrodeThings, ScopedArguments::offsetOfOverrodeThings()) \
     macro(ScopedArguments_scope, ScopedArguments::offsetOfScope()) \
     macro(ScopedArguments_table, ScopedArguments::offsetOfTable()) \
index 75adbc8..3307a17 100644 (file)
@@ -7094,19 +7094,28 @@ private:
     
     void compileLogShadowChickenPrologue()
     {
-        LValue packet = setupShadowChickenPacket();
-        
+        LValue packet = ensureShadowChickenPacket();
+        LValue scope = lowCell(m_node->child1());
+
         m_out.storePtr(m_callFrame, packet, m_heaps.ShadowChicken_Packet_frame);
         m_out.storePtr(m_out.loadPtr(addressFor(0)), packet, m_heaps.ShadowChicken_Packet_callerFrame);
         m_out.storePtr(m_out.loadPtr(payloadFor(JSStack::Callee)), packet, m_heaps.ShadowChicken_Packet_callee);
+        m_out.storePtr(scope, packet, m_heaps.ShadowChicken_Packet_scope);
     }
     
     void compileLogShadowChickenTail()
     {
-        LValue packet = setupShadowChickenPacket();
+        LValue packet = ensureShadowChickenPacket();
+        LValue thisValue = lowJSValue(m_node->child1());
+        LValue scope = lowCell(m_node->child2());
+        CallSiteIndex callSiteIndex = m_ftlState.jitCode->common.addCodeOrigin(m_node->origin.semantic);
         
         m_out.storePtr(m_callFrame, packet, m_heaps.ShadowChicken_Packet_frame);
         m_out.storePtr(m_out.constIntPtr(ShadowChicken::Packet::tailMarker()), packet, m_heaps.ShadowChicken_Packet_callee);
+        m_out.store64(thisValue, packet, m_heaps.ShadowChicken_Packet_thisValue);
+        m_out.storePtr(scope, packet, m_heaps.ShadowChicken_Packet_scope);
+        m_out.storePtr(m_out.constIntPtr(codeBlock()), packet, m_heaps.ShadowChicken_Packet_codeBlock);
+        m_out.store32(m_out.constInt32(callSiteIndex.bits()), packet, m_heaps.ShadowChicken_Packet_callSiteIndex);
     }
 
     void compileRecordRegExpCachedResult()
@@ -8380,7 +8389,7 @@ private:
             m_out.phi(m_out.intPtr, fastButterfly, slowButterfly));
     }
     
-    LValue setupShadowChickenPacket()
+    LValue ensureShadowChickenPacket()
     {
         LBasicBlock slowCase = m_out.newBlock();
         LBasicBlock continuation = m_out.newBlock();
index c338fec..68a1f03 100644 (file)
@@ -1299,6 +1299,7 @@ InjectedScript.CallFrameProxy = function(ordinal, callFrame)
     this.location = {scriptId: String(callFrame.sourceID), lineNumber: callFrame.line, columnNumber: callFrame.column};
     this.scopeChain = this._wrapScopeChain(callFrame);
     this.this = injectedScript._wrapObject(callFrame.thisObject, "backtrace", false, true);
+    this.isTailDeleted = callFrame.isTailDeleted;
 }
 
 InjectedScript.CallFrameProxy.prototype = {
index 79332e4..10b2cd0 100644 (file)
@@ -184,6 +184,11 @@ JSValue JSJavaScriptCallFrame::thisObject(ExecState*) const
     return impl().thisValue();
 }
 
+JSValue JSJavaScriptCallFrame::isTailDeleted(JSC::ExecState*) const
+{
+    return jsBoolean(impl().isTailDeleted());
+}
+
 JSValue JSJavaScriptCallFrame::type(ExecState* exec) const
 {
     switch (impl().type()) {
index 351d3a2..c4b0b36 100644 (file)
@@ -69,6 +69,7 @@ public:
     JSC::JSValue scopeChain(JSC::ExecState*) const;
     JSC::JSValue thisObject(JSC::ExecState*) const;
     JSC::JSValue type(JSC::ExecState*) const;
+    JSC::JSValue isTailDeleted(JSC::ExecState*) const;
 
     // Constants.
     static const unsigned short GLOBAL_SCOPE = 0;
index 21840c3..3af39e5 100644 (file)
@@ -50,6 +50,7 @@ static EncodedJSValue JSC_HOST_CALL jsJavaScriptCallFrameAttributeFunctionName(E
 static EncodedJSValue JSC_HOST_CALL jsJavaScriptCallFrameAttributeScopeChain(ExecState*);
 static EncodedJSValue JSC_HOST_CALL jsJavaScriptCallFrameAttributeThisObject(ExecState*);
 static EncodedJSValue JSC_HOST_CALL jsJavaScriptCallFrameAttributeType(ExecState*);
+static EncodedJSValue JSC_HOST_CALL jsJavaScriptCallFrameIsTailDeleted(ExecState*);
 
 const ClassInfo JSJavaScriptCallFramePrototype::s_info = { "JavaScriptCallFrame", &Base::s_info, 0, CREATE_METHOD_TABLE(JSJavaScriptCallFramePrototype) };
 
@@ -70,6 +71,7 @@ void JSJavaScriptCallFramePrototype::finishCreation(VM& vm, JSGlobalObject* glob
     JSC_NATIVE_GETTER("scopeChain", jsJavaScriptCallFrameAttributeScopeChain, DontEnum | Accessor);
     JSC_NATIVE_GETTER("thisObject", jsJavaScriptCallFrameAttributeThisObject, DontEnum | Accessor);
     JSC_NATIVE_GETTER("type", jsJavaScriptCallFrameAttributeType, DontEnum | Accessor);
+    JSC_NATIVE_GETTER("isTailDeleted", jsJavaScriptCallFrameIsTailDeleted, DontEnum | Accessor);
 }
 
 EncodedJSValue JSC_HOST_CALL jsJavaScriptCallFramePrototypeFunctionEvaluateWithScopeExtension(ExecState* exec)
@@ -172,4 +174,14 @@ EncodedJSValue JSC_HOST_CALL jsJavaScriptCallFrameAttributeType(ExecState* exec)
     return JSValue::encode(castedThis->type(exec));
 }
 
+EncodedJSValue JSC_HOST_CALL jsJavaScriptCallFrameIsTailDeleted(ExecState* exec)
+{
+    JSValue thisValue = exec->thisValue();
+    JSJavaScriptCallFrame* castedThis = jsDynamicCast<JSJavaScriptCallFrame*>(thisValue);
+    if (!castedThis)
+        return throwVMTypeError(exec);
+
+    return JSValue::encode(castedThis->isTailDeleted(exec));
+}
+
 } // namespace Inspector
index c8abe9c..8e6eae4 100644 (file)
@@ -53,6 +53,7 @@ public:
     JSC::DebuggerCallFrame::Type type() const { return m_debuggerCallFrame->type(); }
     JSC::DebuggerScope* scopeChain() const { return m_debuggerCallFrame->scope(); }
     JSC::JSGlobalObject* vmEntryGlobalObject() const { return m_debuggerCallFrame->vmEntryGlobalObject(); }
+    bool isTailDeleted() const { return m_debuggerCallFrame->isTailDeleted(); }
 
     JSC::JSValue thisValue() const { return m_debuggerCallFrame->thisValue(); }
     JSC::JSValue evaluateWithScopeExtension(const String& script, JSC::JSObject* scopeExtension, NakedPtr<JSC::Exception>& exception) const { return m_debuggerCallFrame->evaluateWithScopeExtension(script, scopeExtension, exception); }
index 9cc150b..4c40737 100644 (file)
@@ -89,7 +89,7 @@ bool ScriptDebugServer::evaluateBreakpointAction(const ScriptBreakpointAction& b
 
     switch (breakpointAction.type) {
     case ScriptBreakpointActionTypeLog: {
-        dispatchBreakpointActionLog(debuggerCallFrame->exec(), breakpointAction.data);
+        dispatchBreakpointActionLog(debuggerCallFrame->globalExec(), breakpointAction.data);
         break;
     }
     case ScriptBreakpointActionTypeEvaluate: {
@@ -97,21 +97,21 @@ bool ScriptDebugServer::evaluateBreakpointAction(const ScriptBreakpointAction& b
         JSObject* scopeExtensionObject = nullptr;
         debuggerCallFrame->evaluateWithScopeExtension(breakpointAction.data, scopeExtensionObject, exception);
         if (exception)
-            reportException(debuggerCallFrame->exec(), exception);
+            reportException(debuggerCallFrame->globalExec(), exception);
         break;
     }
     case ScriptBreakpointActionTypeSound:
-        dispatchBreakpointActionSound(debuggerCallFrame->exec(), breakpointAction.identifier);
+        dispatchBreakpointActionSound(debuggerCallFrame->globalExec(), breakpointAction.identifier);
         break;
     case ScriptBreakpointActionTypeProbe: {
         NakedPtr<Exception> exception;
         JSObject* scopeExtensionObject = nullptr;
         JSValue result = debuggerCallFrame->evaluateWithScopeExtension(breakpointAction.data, scopeExtensionObject, exception);
+        JSC::ExecState* exec = debuggerCallFrame->globalExec();
         if (exception)
-            reportException(debuggerCallFrame->exec(), exception);
+            reportException(exec, exception);
         
-        JSC::ExecState* state = debuggerCallFrame->scope()->globalObject()->globalExec();
-        dispatchBreakpointActionProbe(state, breakpointAction, exception ? exception->value() : result);
+        dispatchBreakpointActionProbe(exec, breakpointAction, exception ? exception->value() : result);
         break;
     }
     default:
index 692f1f0..949418f 100644 (file)
@@ -72,7 +72,8 @@
                 { "name": "functionName", "type": "string", "description": "Name of the JavaScript function called on this call frame." },
                 { "name": "location", "$ref": "Location", "description": "Location in the source code." },
                 { "name": "scopeChain", "type": "array", "items": { "$ref": "Scope" }, "description": "Scope chain for this call frame." },
-                { "name": "this", "$ref": "Runtime.RemoteObject", "description": "<code>this</code> object for this call frame." }
+                { "name": "this", "$ref": "Runtime.RemoteObject", "description": "<code>this</code> object for this call frame." },
+                { "name": "isTailDeleted", "type": "boolean", "description": "Is the current frame tail deleted from a tail call." }
             ],
             "description": "JavaScript call frame. Array of call frames form the call stack."
         },
index 8934691..b9c04b8 100644 (file)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "ShadowChicken.h"
 
+#include "CodeBlock.h"
 #include "JSCInlines.h"
 #include "ShadowChickenInlines.h"
 #include <wtf/ListDump.h>
@@ -112,7 +113,7 @@ void ShadowChicken::update(VM&, ExecState* exec)
     }
     
     if (verbose)
-        dataLog("    Highest point since last time: ", RawPointer(highestPointSinceLastTime), "\n");
+        dataLog("Highest point since last time: ", RawPointer(highestPointSinceLastTime), "\n");
     
     while (!m_stack.isEmpty() && (m_stack.last().frame < highestPointSinceLastTime || m_stack.last().isTailDeleted))
         m_stack.removeLast();
@@ -128,11 +129,17 @@ void ShadowChicken::update(VM&, ExecState* exec)
         for (unsigned i = 0; i < logCursorIndex; ++i) {
             Packet& packet = m_log[i];
             if (packet.isTail() && packet.frame == m_stack.last().frame) {
-                m_stack.last().isTailDeleted = true;
+                Frame& frame = m_stack.last();
+                frame.thisValue = packet.thisValue;
+                frame.scope = packet.scope;
+                frame.codeBlock = packet.codeBlock;
+                frame.callSiteIndex = packet.callSiteIndex;
+                frame.isTailDeleted = true;
                 break;
             }
         }
     }
+
     
     if (verbose)
         dataLog("    Revised stack: ", listDump(m_stack), "\n");
@@ -164,7 +171,13 @@ void ShadowChicken::update(VM&, ExecState* exec)
                 shadowIndex++;
                 continue;
             }
-            if (m_stack[shadowIndex] == stackRightNow[rightNowIndex]) {
+
+            // We specifically don't use operator== here because we are using a less
+            // strict filter on equality of frames. For example, the scope pointer
+            // could change, but we wouldn't want to consider the frames different entities
+            // because of that because it's natural for the program to change scopes.
+            if (m_stack[shadowIndex].frame == stackRightNow[rightNowIndex].frame
+                && m_stack[shadowIndex].callee == stackRightNow[rightNowIndex].callee) {
                 shadowIndex++;
                 rightNowIndex++;
                 continue;
@@ -193,8 +206,7 @@ void ShadowChicken::update(VM&, ExecState* exec)
     // Set everything up so that we know where the top frame is in the log.
     unsigned indexInLog = logCursorIndex;
     
-    auto advanceIndexInLogTo =
-        [&] (CallFrame* frame, JSObject* callee, CallFrame* callerFrame) -> bool {
+    auto advanceIndexInLogTo = [&] (CallFrame* frame, JSObject* callee, CallFrame* callerFrame) -> bool {
         if (verbose)
             dataLog("    Advancing to frame = ", RawPointer(frame), " from indexInLog = ", indexInLog, "\n");
         if (indexInLog > logCursorIndex) {
@@ -278,8 +290,8 @@ void ShadowChicken::update(VM&, ExecState* exec)
                     
                     if (!indexInLog)
                         break;
-                    Packet lastPacket = m_log[indexInLog - 1];
-                    if (!lastPacket.isTail()) {
+                    Packet tailPacket = m_log[indexInLog - 1];
+                    if (!tailPacket.isTail()) {
                         // Last frame that we recorded was not the outcome of a tail call. So, there
                         // will not be any more deleted frames.
                         // FIXME: We might want to have a filter here. Consider that this was a tail
@@ -290,14 +302,19 @@ void ShadowChicken::update(VM&, ExecState* exec)
                     }
                     indexInLog--; // Skip over the tail packet.
                     
-                    if (!advanceIndexInLogTo(lastPacket.frame, nullptr, nullptr)) {
-                        // We were unable to locate the prologue packet for this tail packet. That's
-                        // quite suspect, so give up.
+                    if (!advanceIndexInLogTo(tailPacket.frame, nullptr, nullptr)) {
+                        // We were unable to locate the prologue packet for this tail packet.
+                        // This is rare but can happen in a situation like:
+                        // function foo() {
+                        //     ... call some deeply tail-recursive function, causing a random number of log processings.
+                        //     return bar(); // tail call
+                        // }
                         break;
                     }
                     Packet packet = m_log[indexInLog];
                     bool isTailDeleted = true;
-                    toPush.append(Frame(packet.callee, packet.frame, isTailDeleted));
+                    RELEASE_ASSERT(tailPacket.scope->inherits(JSScope::info()));
+                    toPush.append(Frame(packet.callee, packet.frame, isTailDeleted, tailPacket.thisValue, tailPacket.scope, tailPacket.codeBlock, tailPacket.callSiteIndex));
                 }
             }
             if (callFrame == highestPointSinceLastTime) {
@@ -305,12 +322,22 @@ void ShadowChicken::update(VM&, ExecState* exec)
                     dataLog("    Bailing at ", RawPointer(callFrame), " because it's the highest point since last time.\n");
                 return StackVisitor::Done;
             }
-            advanceIndexInLogTo(callFrame, callFrame->callee(), callFrame->callerFrame());
+            bool foundFrame = advanceIndexInLogTo(callFrame, callFrame->callee(), callFrame->callerFrame());
             bool isTailDeleted = false;
-            toPush.append(Frame(visitor->callee(), callFrame, isTailDeleted));
+            JSScope* scope = nullptr;
+            CodeBlock* codeBlock = callFrame->codeBlock();
+            if (codeBlock && codeBlock->wasCompiledWithDebuggingOpcodes() && codeBlock->scopeRegister().isValid()) {
+                scope = callFrame->scope(codeBlock->scopeRegister().offset());
+                RELEASE_ASSERT(scope->inherits(JSScope::info()));
+            } else if (foundFrame) {
+                scope = m_log[indexInLog].scope;
+                if (scope)
+                    RELEASE_ASSERT(scope->inherits(JSScope::info()));
+            }
+            toPush.append(Frame(visitor->callee(), callFrame, isTailDeleted, callFrame->thisValue(), scope, codeBlock, callFrame->callSiteIndex()));
             return StackVisitor::Continue;
         });
-    
+
     if (verbose)
         dataLog("    Pushing: ", listDump(toPush), "\n");
     
@@ -329,23 +356,28 @@ void ShadowChicken::update(VM&, ExecState* exec)
 
     if (verbose)
         dataLog("    After pushing: ", *this, "\n");
-    
-    // Remove tail frames until the stack is small enough again.
-    const unsigned stackSizeLimit = Options::shadowChickenStackSizeLimit();
-    if (m_stack.size() > stackSizeLimit) {
-        unsigned dstIndex = 0;
-        unsigned srcIndex = 0;
-        unsigned size = m_stack.size();
-        while (srcIndex < m_stack.size()) {
-            Frame frame = m_stack[srcIndex++];
-            if (size > stackSizeLimit && frame.isTailDeleted) {
-                size--;
-                continue;
+
+    // Remove tail frames until the number of tail deleted frames is small enough.
+    const unsigned maxTailDeletedFrames = Options::shadowChickenMaxTailDeletedFramesSize();
+    if (m_stack.size() > maxTailDeletedFrames) {
+        unsigned numberOfTailDeletedFrames = 0;
+        for (const Frame& frame : m_stack) {
+            if (frame.isTailDeleted)
+                numberOfTailDeletedFrames++;
+        }
+        if (numberOfTailDeletedFrames > maxTailDeletedFrames) {
+            unsigned dstIndex = 0;
+            unsigned srcIndex = 0;
+            while (srcIndex < m_stack.size()) {
+                Frame frame = m_stack[srcIndex++];
+                if (numberOfTailDeletedFrames > maxTailDeletedFrames && frame.isTailDeleted) {
+                    numberOfTailDeletedFrames--;
+                    continue;
+                }
+                m_stack[dstIndex++] = frame;
             }
-            m_stack[dstIndex++] = frame;
+            m_stack.resize(dstIndex);
         }
-        RELEASE_ASSERT(dstIndex == size);
-        m_stack.resize(size);
     }
 
     if (verbose)
@@ -358,10 +390,23 @@ void ShadowChicken::visitChildren(SlotVisitor& visitor)
         JSObject* callee = m_log[i].callee;
         if (callee != Packet::tailMarker() && callee != Packet::throwMarker())
             visitor.appendUnbarrieredReadOnlyPointer(callee);
+        if (callee != Packet::throwMarker())
+            visitor.appendUnbarrieredReadOnlyPointer(m_log[i].scope);
+        if (callee == Packet::tailMarker()) {
+            visitor.appendUnbarrieredValue(&m_log[i].thisValue);
+            visitor.appendUnbarrieredReadOnlyPointer(m_log[i].codeBlock);
+        }
     }
     
-    for (Frame& frame : m_stack)
+    for (unsigned i = m_stack.size(); i--; ) {
+        Frame& frame = m_stack[i];
+        visitor.appendUnbarrieredValue(&frame.thisValue);
         visitor.appendUnbarrieredReadOnlyPointer(frame.callee);
+        if (frame.scope)
+            visitor.appendUnbarrieredReadOnlyPointer(frame.scope);
+        if (frame.codeBlock)
+            visitor.appendUnbarrieredReadOnlyPointer(frame.codeBlock);
+    }
 }
 
 void ShadowChicken::reset()
index e9d7149..d7455a6 100644 (file)
@@ -25,6 +25,8 @@
 
 #pragma once
 
+#include "CallFrame.h"
+#include "JSCJSValue.h"
 #include <wtf/FastMalloc.h>
 #include <wtf/Noncopyable.h>
 #include <wtf/PrintStream.h>
 
 namespace JSC {
 
+class CodeBlock;
 class ExecState;
 class JSArray;
 class JSObject;
+class JSScope;
 class LLIntOffsetsExtractor;
 class SlotVisitor;
 class VM;
@@ -85,23 +89,25 @@ public:
             return bitwise_cast<JSObject*>(static_cast<intptr_t>(unlikelyValue + 1));
         }
         
-        static Packet prologue(JSObject* callee, CallFrame* frame, CallFrame* callerFrame)
+        static Packet prologue(JSObject* callee, CallFrame* frame, CallFrame* callerFrame, JSScope* scope)
         {
             Packet result;
             result.callee = callee;
             result.frame = frame;
             result.callerFrame = callerFrame;
+            result.scope = scope;
             return result;
         }
         
-        // FIXME: Tail packets should hold currentScope so that the inspector can look at local
-        // variables in tail-deleted frames.
-        // https://bugs.webkit.org/show_bug.cgi?id=155722
-        static Packet tail(CallFrame* frame)
+        static Packet tail(CallFrame* frame, JSValue thisValue, JSScope* scope, CodeBlock* codeBlock, CallSiteIndex callSiteIndex)
         {
             Packet result;
             result.callee = tailMarker();
             result.frame = frame;
+            result.thisValue = thisValue;
+            result.scope = scope;
+            result.codeBlock = codeBlock;
+            result.callSiteIndex = callSiteIndex;
             return result;
         }
         
@@ -120,9 +126,14 @@ public:
         
         void dump(PrintStream&) const;
         
+        // Only tail packets have a valid thisValue, CodeBlock*, and CallSiteIndex. We grab 'this' and CodeBlock* from non tail-deleted frames from the machine frame.
+        JSValue thisValue { JSValue() };
         JSObject* callee { nullptr };
         CallFrame* frame { nullptr };
         CallFrame* callerFrame { nullptr };
+        JSScope* scope { nullptr };
+        CodeBlock* codeBlock { nullptr };
+        CallSiteIndex callSiteIndex;
     };
     
     struct Frame {
@@ -130,9 +141,13 @@ public:
         {
         }
         
-        Frame(JSObject* callee, CallFrame* frame, bool isTailDeleted)
+        Frame(JSObject* callee, CallFrame* frame, bool isTailDeleted, JSValue thisValue = JSValue(), JSScope* scope = nullptr, CodeBlock* codeBlock = nullptr, CallSiteIndex callSiteIndex = CallSiteIndex())
             : callee(callee)
             , frame(frame)
+            , thisValue(thisValue)
+            , scope(scope)
+            , codeBlock(codeBlock)
+            , callSiteIndex(callSiteIndex)
             , isTailDeleted(isTailDeleted)
         {
         }
@@ -141,6 +156,10 @@ public:
         {
             return callee == other.callee
                 && frame == other.frame
+                && thisValue == other.thisValue
+                && scope == other.scope
+                && codeBlock == other.codeBlock
+                && callSiteIndex.bits() == other.callSiteIndex.bits()
                 && isTailDeleted == other.isTailDeleted;
         }
         
@@ -156,6 +175,10 @@ public:
         // https://bugs.webkit.org/show_bug.cgi?id=155686
         JSObject* callee { nullptr };
         CallFrame* frame { nullptr };
+        JSValue thisValue { JSValue() };
+        JSScope* scope { nullptr };
+        CodeBlock* codeBlock { nullptr };
+        CallSiteIndex callSiteIndex;
         bool isTailDeleted { false };
     };
     
@@ -166,7 +189,7 @@ public:
     
     void update(VM&, ExecState*);
     
-    // Expects this signature: (const Frame& frame) -> bool.
+    // Expects this signature: (const Frame& frame) -> bool. Return true to keep iterating. Return false to stop iterating.
     // Note that this only works right with inlining disabled, but that's OK since for now we
     // disable inlining when the inspector is attached. It would be easy to make this work with
     // inlining, and would mostly require that we can request that StackVisitor doesn't skip tail
index ae189f2..d773dbe 100644 (file)
 
 namespace JSC {
 
-void CCallHelpers::logShadowChickenProloguePacket()
+void CCallHelpers::logShadowChickenProloguePacket(GPRReg shadowPacket, GPRReg scratch1, GPRReg scope)
 {
-    setupShadowChickenPacket();
-    storePtr(GPRInfo::callFrameRegister, Address(GPRInfo::regT1, OBJECT_OFFSETOF(ShadowChicken::Packet, frame)));
-    loadPtr(Address(GPRInfo::callFrameRegister, OBJECT_OFFSETOF(CallerFrameAndPC, callerFrame)), GPRInfo::regT0);
-    storePtr(GPRInfo::regT0, Address(GPRInfo::regT1, OBJECT_OFFSETOF(ShadowChicken::Packet, callerFrame)));
-    loadPtr(addressFor(JSStack::Callee), GPRInfo::regT0);
-    storePtr(GPRInfo::regT0, Address(GPRInfo::regT1, OBJECT_OFFSETOF(ShadowChicken::Packet, callee)));
+    storePtr(GPRInfo::callFrameRegister, Address(shadowPacket, OBJECT_OFFSETOF(ShadowChicken::Packet, frame)));
+    loadPtr(Address(GPRInfo::callFrameRegister, OBJECT_OFFSETOF(CallerFrameAndPC, callerFrame)), scratch1);
+    storePtr(scratch1, Address(shadowPacket, OBJECT_OFFSETOF(ShadowChicken::Packet, callerFrame)));
+    loadPtr(addressFor(JSStack::Callee), scratch1);
+    storePtr(scratch1, Address(shadowPacket, OBJECT_OFFSETOF(ShadowChicken::Packet, callee)));
+    storePtr(scope, Address(shadowPacket, OBJECT_OFFSETOF(ShadowChicken::Packet, scope)));
 }
 
-void CCallHelpers::logShadowChickenTailPacket()
+void CCallHelpers::logShadowChickenTailPacket(GPRReg shadowPacket, JSValueRegs thisRegs, GPRReg scope, CodeBlock* codeBlock, CallSiteIndex callSiteIndex)
 {
-    setupShadowChickenPacket();
-    storePtr(GPRInfo::callFrameRegister, Address(GPRInfo::regT1, OBJECT_OFFSETOF(ShadowChicken::Packet, frame)));
-    storePtr(TrustedImmPtr(ShadowChicken::Packet::tailMarker()), Address(GPRInfo::regT1, OBJECT_OFFSETOF(ShadowChicken::Packet, callee)));
+    storePtr(GPRInfo::callFrameRegister, Address(shadowPacket, OBJECT_OFFSETOF(ShadowChicken::Packet, frame)));
+    storePtr(TrustedImmPtr(ShadowChicken::Packet::tailMarker()), Address(shadowPacket, OBJECT_OFFSETOF(ShadowChicken::Packet, callee)));
+    storeValue(thisRegs, Address(shadowPacket, OBJECT_OFFSETOF(ShadowChicken::Packet, thisValue)));
+    storePtr(scope, Address(shadowPacket, OBJECT_OFFSETOF(ShadowChicken::Packet, scope)));
+    storePtr(TrustedImmPtr(codeBlock), Address(shadowPacket, OBJECT_OFFSETOF(ShadowChicken::Packet, codeBlock)));
+    store32(TrustedImm32(callSiteIndex.bits()), Address(shadowPacket, OBJECT_OFFSETOF(ShadowChicken::Packet, callSiteIndex)));
 }
 
-void CCallHelpers::setupShadowChickenPacket()
+void CCallHelpers::ensureShadowChickenPacket(GPRReg shadowPacket, GPRReg scratch1NonArgGPR, GPRReg scratch2)
 {
-    move(TrustedImmPtr(vm()->shadowChicken().addressOfLogCursor()), GPRInfo::regT0);
-    loadPtr(Address(GPRInfo::regT0), GPRInfo::regT1);
-    Jump ok = branchPtr(Below, GPRInfo::regT1, TrustedImmPtr(vm()->shadowChicken().logEnd()));
+    ASSERT(!RegisterSet::argumentGPRS().get(scratch1NonArgGPR));
+    move(TrustedImmPtr(vm()->shadowChicken().addressOfLogCursor()), scratch1NonArgGPR);
+    loadPtr(Address(scratch1NonArgGPR), shadowPacket);
+    Jump ok = branchPtr(Below, shadowPacket, TrustedImmPtr(vm()->shadowChicken().logEnd()));
     setupArgumentsExecState();
-    move(TrustedImmPtr(bitwise_cast<void*>(operationProcessShadowChickenLog)), GPRInfo::nonArgGPR0);
-    call(GPRInfo::nonArgGPR0);
-    move(TrustedImmPtr(vm()->shadowChicken().addressOfLogCursor()), GPRInfo::regT0);
-    loadPtr(Address(GPRInfo::regT0), GPRInfo::regT1);
+    move(TrustedImmPtr(bitwise_cast<void*>(operationProcessShadowChickenLog)), scratch1NonArgGPR);
+    call(scratch1NonArgGPR);
+    move(TrustedImmPtr(vm()->shadowChicken().addressOfLogCursor()), scratch1NonArgGPR);
+    loadPtr(Address(scratch1NonArgGPR), shadowPacket);
     ok.link(this);
-    addPtr(TrustedImm32(sizeof(ShadowChicken::Packet)), GPRInfo::regT1, GPRInfo::regT2);
-    storePtr(GPRInfo::regT2, Address(GPRInfo::regT0));
+    addPtr(TrustedImm32(sizeof(ShadowChicken::Packet)), shadowPacket, scratch2);
+    storePtr(scratch2, Address(scratch1NonArgGPR));
 }
 
 #if NUMBER_OF_ARGUMENT_REGISTERS >= 4
index 2426abd..bb248b6 100644 (file)
@@ -2330,12 +2330,10 @@ public:
     
     // These operations clobber all volatile registers. They assume that there is room on the top of
     // stack to marshall call arguments.
-    void logShadowChickenProloguePacket();
-    void logShadowChickenTailPacket();
-
-private:
-    // Leaves behind a pointer to the Packet we should write to in regT1.
-    void setupShadowChickenPacket();
+    void logShadowChickenProloguePacket(GPRReg shadowPacket, GPRReg scratch1, GPRReg scope);
+    void logShadowChickenTailPacket(GPRReg shadowPacket, JSValueRegs thisRegs, GPRReg scope, CodeBlock*, CallSiteIndex);
+    // Leaves behind a pointer to the Packet we should write to in shadowPacket.
+    void ensureShadowChickenPacket(GPRReg shadowPacket, GPRReg scratch1NonArgGPR, GPRReg scratch2);
 };
 
 } // namespace JSC
index f0a7519..1995801 100644 (file)
@@ -1352,6 +1352,31 @@ void JIT::emit_op_profile_type(Instruction* currentInstruction)
     jumpToEnd.link(this);
 }
 
+void JIT::emit_op_log_shadow_chicken_prologue(Instruction* currentInstruction)
+{
+    updateTopCallFrame();
+    static_assert(nonArgGPR0 != regT0 && nonArgGPR0 != regT2, "we will have problems if this is true.");
+    GPRReg shadowPacketReg = regT0;
+    GPRReg scratch1Reg = nonArgGPR0; // This must be a non-argument register.
+    GPRReg scratch2Reg = regT2;
+    ensureShadowChickenPacket(shadowPacketReg, scratch1Reg, scratch2Reg);
+    emitGetVirtualRegister(currentInstruction[1].u.operand, regT3);
+    logShadowChickenProloguePacket(shadowPacketReg, scratch1Reg, regT3);
+}
+
+void JIT::emit_op_log_shadow_chicken_tail(Instruction* currentInstruction)
+{
+    updateTopCallFrame();
+    static_assert(nonArgGPR0 != regT0 && nonArgGPR0 != regT2, "we will have problems if this is true.");
+    GPRReg shadowPacketReg = regT0;
+    GPRReg scratch1Reg = nonArgGPR0; // This must be a non-argument register.
+    GPRReg scratch2Reg = regT2;
+    ensureShadowChickenPacket(shadowPacketReg, scratch1Reg, scratch2Reg);
+    emitGetVirtualRegister(currentInstruction[1].u.operand, regT2);
+    emitGetVirtualRegister(currentInstruction[2].u.operand, regT3);
+    logShadowChickenTailPacket(shadowPacketReg, JSValueRegs(regT2), regT3, m_codeBlock, CallSiteIndex(m_bytecodeOffset));
+}
+
 #endif // USE(JSVALUE64)
 
 void JIT::emit_op_get_enumerable_length(Instruction* currentInstruction)
@@ -1462,18 +1487,6 @@ void JIT::emit_op_resume(Instruction* currentInstruction)
     slowPathCall.call();
 }
 
-void JIT::emit_op_log_shadow_chicken_prologue(Instruction*)
-{
-    updateTopCallFrame();
-    logShadowChickenProloguePacket();
-}
-
-void JIT::emit_op_log_shadow_chicken_tail(Instruction*)
-{
-    updateTopCallFrame();
-    logShadowChickenTailPacket();
-}
-
 } // namespace JSC
 
 #endif // ENABLE(JIT)
index cf04ca9..8d88b21 100644 (file)
@@ -1354,6 +1354,36 @@ void JIT::emit_op_profile_type(Instruction* currentInstruction)
     jumpToEnd.link(this);
 }
 
+void JIT::emit_op_log_shadow_chicken_prologue(Instruction* currentInstruction)
+{
+    updateTopCallFrame();
+    static_assert(nonArgGPR0 != regT0 && nonArgGPR0 != regT2, "we will have problems if this is true.");
+    GPRReg shadowPacketReg = regT0;
+    GPRReg scratch1Reg = nonArgGPR0; // This must be a non-argument register.
+    GPRReg scratch2Reg = regT2;
+    ensureShadowChickenPacket(shadowPacketReg, scratch1Reg, scratch2Reg);
+
+    scratch1Reg = regT4;
+    emitLoadPayload(currentInstruction[1].u.operand, regT3);
+    logShadowChickenProloguePacket(shadowPacketReg, scratch1Reg, regT3);
+}
+
+void JIT::emit_op_log_shadow_chicken_tail(Instruction* currentInstruction)
+{
+    updateTopCallFrame();
+    static_assert(nonArgGPR0 != regT0 && nonArgGPR0 != regT2, "we will have problems if this is true.");
+    GPRReg shadowPacketReg = regT0;
+    GPRReg scratch1Reg = nonArgGPR0; // This must be a non-argument register.
+    GPRReg scratch2Reg = regT2;
+    ensureShadowChickenPacket(shadowPacketReg, scratch1Reg, scratch2Reg);
+
+    emitLoadPayload(currentInstruction[1].u.operand, regT2);
+    emitLoadTag(currentInstruction[1].u.operand, regT1);
+    JSValueRegs thisRegs(regT1, regT2);
+    emitLoadPayload(currentInstruction[2].u.operand, regT3);
+    logShadowChickenTailPacket(shadowPacketReg, thisRegs, regT3, m_codeBlock, CallSiteIndex(currentInstruction));
+}
+
 } // namespace JSC
 
 #endif // USE(JSVALUE32_64)
index 5418400..59ff082 100644 (file)
@@ -339,6 +339,16 @@ RegisterSet RegisterSet::webAssemblyCalleeSaveRegisters()
 }
 #endif
 
+RegisterSet RegisterSet::argumentGPRS()
+{
+    RegisterSet result;
+#if NUMBER_OF_ARGUMENT_REGISTERS
+    for (unsigned i = 0; i < GPRInfo::numberOfArgumentRegisters; i++)
+        result.set(GPRInfo::toArgumentRegister(i));
+#endif
+    return result;
+}
+
 RegisterSet RegisterSet::registersToNotSaveForJSCall()
 {
     return RegisterSet(RegisterSet::vmCalleeSaveRegisters(), RegisterSet::stackRegisters(), RegisterSet::reservedHardwareRegisters());
index a95583e..7d96fbf 100644 (file)
@@ -63,6 +63,7 @@ public:
     JS_EXPORT_PRIVATE static RegisterSet allGPRs();
     JS_EXPORT_PRIVATE static RegisterSet allFPRs();
     static RegisterSet allRegisters();
+    static RegisterSet argumentGPRS();
 
     static RegisterSet registersToNotSaveForJSCall();
     static RegisterSet registersToNotSaveForCCall();
index 8a480d6..ee5f984 100644 (file)
@@ -34,6 +34,7 @@
 #include "MaxFrameExtentForSlowPathCall.h"
 #include "Opcode.h"
 #include "PropertyOffset.h"
+#include "ShadowChicken.h"
 #include "WriteBarrier.h"
 
 #define STATIC_ASSERT(cond) static_assert(cond, "LLInt assumes " #cond)
@@ -191,6 +192,8 @@ void Data::performAssertions(VM& vm)
 
     STATIC_ASSERT(MarkedBlock::blockMask == ~static_cast<decltype(MarkedBlock::blockMask)>(0x3fff));
 
+    ASSERT(bitwise_cast<uintptr_t>(ShadowChicken::Packet::tailMarker()) == static_cast<uintptr_t>(0x7a11));
+
     // FIXME: make these assertions less horrible.
 #if !ASSERT_DISABLED
     Vector<int> testVector;
index ccc58d1..3759113 100644 (file)
@@ -1542,8 +1542,8 @@ LLINT_SLOW_PATH_DECL(slow_path_log_shadow_chicken_prologue)
 {
     LLINT_BEGIN();
     
-    vm.shadowChicken().log(
-        vm, exec, ShadowChicken::Packet::prologue(exec->callee(), exec, exec->callerFrame()));
+    JSScope* scope = exec->uncheckedR(pc[1].u.operand).Register::scope();
+    vm.shadowChicken().log(vm, exec, ShadowChicken::Packet::prologue(exec->callee(), exec, exec->callerFrame(), scope));
     
     LLINT_END();
 }
@@ -1551,8 +1551,16 @@ LLINT_SLOW_PATH_DECL(slow_path_log_shadow_chicken_prologue)
 LLINT_SLOW_PATH_DECL(slow_path_log_shadow_chicken_tail)
 {
     LLINT_BEGIN();
+
+    JSValue thisValue = LLINT_OP(1).jsValue();
+    JSScope* scope = exec->uncheckedR(pc[2].u.operand).Register::scope();
     
-    vm.shadowChicken().log(vm, exec, ShadowChicken::Packet::tail(exec));
+#if USE(JSVALUE64)
+    CallSiteIndex callSiteIndex(exec->codeBlock()->bytecodeOffset(pc));
+#else
+    CallSiteIndex callSiteIndex(pc);
+#endif
+    vm.shadowChicken().log(vm, exec, ShadowChicken::Packet::tail(exec, thisValue, scope, exec->codeBlock(), callSiteIndex));
     
     LLINT_END();
 }
index 43bf77e..678910c 100644 (file)
@@ -249,6 +249,9 @@ const ClearWatchpoint = 0
 const IsWatched = 1
 const IsInvalidated = 2
 
+# ShadowChicken data
+const ShadowChickenTailMarker = 0x7a11
+
 # Some register conventions.
 if JSVALUE64
     # - Use a pair of registers to represent the PC: one register for the
@@ -1471,30 +1474,6 @@ macro acquireShadowChickenPacket(slow)
     storep t1, ShadowChicken::m_logCursor[t2]
 end
 
-_llint_op_log_shadow_chicken_prologue:
-    traceExecution()
-    acquireShadowChickenPacket(.opLogShadowChickenPrologueSlow)
-    storep cfr, ShadowChicken::Packet::frame[t0]
-    loadp CallerFrame[cfr], t1
-    storep t1, ShadowChicken::Packet::callerFrame[t0]
-    loadp Callee + PayloadOffset[cfr], t1
-    storep t1, ShadowChicken::Packet::callee[t0]
-    dispatch(1)
-.opLogShadowChickenPrologueSlow:
-    callSlowPath(_llint_slow_path_log_shadow_chicken_prologue)
-    dispatch(1)
-
-
-_llint_op_log_shadow_chicken_tail:
-    traceExecution()
-    acquireShadowChickenPacket(.opLogShadowChickenTailSlow)
-    storep cfr, ShadowChicken::Packet::frame[t0]
-    storep 0x7a11, ShadowChicken::Packet::callee[t0]
-    dispatch(1)
-.opLogShadowChickenTailSlow:
-    callSlowPath(_llint_slow_path_log_shadow_chicken_tail)
-    dispatch(1)
-
 
 _llint_op_switch_string:
     traceExecution()
index 05325c2..52e0b45 100644 (file)
@@ -2444,3 +2444,40 @@ _llint_op_get_rest_length:
     storei t0, PayloadOffset[cfr, t1, 8]
     storei Int32Tag, TagOffset[cfr, t1, 8]
     dispatch(3)
+
+
+_llint_op_log_shadow_chicken_prologue:
+    traceExecution()
+    acquireShadowChickenPacket(.opLogShadowChickenPrologueSlow)
+    storep cfr, ShadowChicken::Packet::frame[t0]
+    loadp CallerFrame[cfr], t1
+    storep t1, ShadowChicken::Packet::callerFrame[t0]
+    loadp Callee + PayloadOffset[cfr], t1
+    storep t1, ShadowChicken::Packet::callee[t0]
+    loadisFromInstruction(1, t1)
+    loadi PayloadOffset[cfr, t1, 8], t1
+    storep t1, ShadowChicken::Packet::scope[t0]
+    dispatch(2)
+.opLogShadowChickenPrologueSlow:
+    callSlowPath(_llint_slow_path_log_shadow_chicken_prologue)
+    dispatch(2)
+
+
+_llint_op_log_shadow_chicken_tail:
+    traceExecution()
+    acquireShadowChickenPacket(.opLogShadowChickenTailSlow)
+    storep cfr, ShadowChicken::Packet::frame[t0]
+    storep ShadowChickenTailMarker, ShadowChicken::Packet::callee[t0]
+    loadVariable(1, t3, t2, t1)
+    storei t2, TagOffset + ShadowChicken::Packet::thisValue[t0]
+    storei t1, PayloadOffset + ShadowChicken::Packet::thisValue[t0]
+    loadisFromInstruction(2, t1)
+    loadi PayloadOffset[cfr, t1, 8], t1
+    storep t1, ShadowChicken::Packet::scope[t0]
+    loadp CodeBlock[cfr], t1
+    storep t1, ShadowChicken::Packet::codeBlock[t0]
+    storei PC, ShadowChicken::Packet::callSiteIndex[t0]
+    dispatch(3)
+.opLogShadowChickenTailSlow:
+    callSlowPath(_llint_slow_path_log_shadow_chicken_tail)
+    dispatch(3)
index 82fcb17..ea5e28b 100644 (file)
@@ -2301,3 +2301,37 @@ _llint_op_get_rest_length:
     loadisFromInstruction(1, t1)
     storeq t0, [cfr, t1, 8]
     dispatch(3)
+
+
+_llint_op_log_shadow_chicken_prologue:
+    traceExecution()
+    acquireShadowChickenPacket(.opLogShadowChickenPrologueSlow)
+    storep cfr, ShadowChicken::Packet::frame[t0]
+    loadp CallerFrame[cfr], t1
+    storep t1, ShadowChicken::Packet::callerFrame[t0]
+    loadp Callee + PayloadOffset[cfr], t1
+    storep t1, ShadowChicken::Packet::callee[t0]
+    loadVariable(1, t1)
+    storep t1, ShadowChicken::Packet::scope[t0]
+    dispatch(2)
+.opLogShadowChickenPrologueSlow:
+    callSlowPath(_llint_slow_path_log_shadow_chicken_prologue)
+    dispatch(2)
+
+
+_llint_op_log_shadow_chicken_tail:
+    traceExecution()
+    acquireShadowChickenPacket(.opLogShadowChickenTailSlow)
+    storep cfr, ShadowChicken::Packet::frame[t0]
+    storep ShadowChickenTailMarker, ShadowChicken::Packet::callee[t0]
+    loadVariable(1, t1)
+    storep t1, ShadowChicken::Packet::thisValue[t0]
+    loadVariable(2, t1)
+    storep t1, ShadowChicken::Packet::scope[t0]
+    loadp CodeBlock[cfr], t1
+    storep t1, ShadowChicken::Packet::codeBlock[t0]
+    storei PC, ShadowChicken::Packet::callSiteIndex[t0]
+    dispatch(3)
+.opLogShadowChickenTailSlow:
+    callSlowPath(_llint_slow_path_log_shadow_chicken_tail)
+    dispatch(3)
index 281273e..0b65797 100644 (file)
@@ -115,7 +115,7 @@ UnlinkedCodeBlockType* CodeCache::getGlobalCodeBlock(VM& vm, ExecutableType* exe
     unsigned arrowContextFeature = executable->isArrowFunctionContext() ? ArrowFunctionContextFeature : 0;
     executable->recordParse(rootNode->features() | arrowContextFeature, rootNode->hasCapturedVariables(), rootNode->firstLine(), rootNode->lastLine(), startColumn, endColumn);
 
-    UnlinkedCodeBlockType* unlinkedCodeBlock = UnlinkedCodeBlockType::create(&vm, executable->executableInfo());
+    UnlinkedCodeBlockType* unlinkedCodeBlock = UnlinkedCodeBlockType::create(&vm, executable->executableInfo(), debuggerMode);
     unlinkedCodeBlock->recordParse(rootNode->features(), rootNode->hasCapturedVariables(), rootNode->firstLine() - source.firstLine(), lineCount, unlinkedEndColumn);
     unlinkedCodeBlock->setSourceURLDirective(source.provider()->sourceURL());
     unlinkedCodeBlock->setSourceMappingURLDirective(source.provider()->sourceMappingURL());
index 1d0e564..c754e81 100644 (file)
@@ -137,7 +137,7 @@ typedef const char* optionString;
     v(bool, useTailCalls, true, Normal, nullptr) \
     v(bool, alwaysUseShadowChicken, false, Normal, nullptr) \
     v(unsigned, shadowChickenLogSize, 1000, Normal, nullptr) \
-    v(unsigned, shadowChickenStackSizeLimit, 100000, Normal, nullptr) \
+    v(unsigned, shadowChickenMaxTailDeletedFramesSize, 128, Normal, nullptr) \
     \
     /* dumpDisassembly implies dumpDFGDisassembly. */ \
     v(bool, dumpDisassembly, false, Normal, "dumps disassembly of all JIT compiled code upon compilation") \
index ed56fdb..93e7156 100644 (file)
@@ -121,7 +121,7 @@ initialize();
 (function test5a() {
     if (verbose)
         print("In test5a:");
-    var foos = 990;
+    var foos = 50;
     
     function foo(ttl) {
         if (ttl <= 1) {
@@ -141,7 +141,7 @@ initialize();
 (function test5b() {
     if (verbose)
         print("In test5b:");
-    var foos = 9990;
+    var foos = 100;
     
     function foo(ttl) {
         if (ttl <= 1) {
@@ -165,7 +165,7 @@ initialize();
         print("array.push = " + describe([].push));
     }
     
-    var foos = 99990;
+    var foos = 128;
     
     function foo(ttl) {
         if (ttl <= 1) {
index 8c62afc..6994aca 100644 (file)
@@ -1,3 +1,17 @@
+2016-05-16  Saam barati  <sbarati@apple.com>
+
+        Hook up ShadowChicken to the debugger to show tail deleted frames
+        https://bugs.webkit.org/show_bug.cgi?id=156685
+        <rdar://problem/25770521>
+
+        Reviewed by Filip Pizlo and Mark Lam and Joseph Pecoraro.
+
+        Tests: inspector/debugger/tail-deleted-frames-this-value.html
+               inspector/debugger/tail-deleted-frames.html
+               inspector/debugger/tail-recursion.html
+
+        * ForwardingHeaders/interpreter/ShadowChicken.h: Added.
+
 2016-05-16  Alex Christensen  <achristensen@webkit.org>
 
         Don't include CSSParser.h from other headers
diff --git a/Source/WebCore/ForwardingHeaders/interpreter/ShadowChicken.h b/Source/WebCore/ForwardingHeaders/interpreter/ShadowChicken.h
new file mode 100644 (file)
index 0000000..ce1181c
--- /dev/null
@@ -0,0 +1,4 @@
+#ifndef WebCore_FWD_ShadowChicken_h
+#define WebCore_FWD_ShadowChicken_h
+#include <JavaScriptCore/ShadowChicken.h>
+#endif
index 9522c06..cf097da 100644 (file)
@@ -1,3 +1,25 @@
+2016-05-16  Saam barati  <sbarati@apple.com>
+
+        Hook up ShadowChicken to the debugger to show tail deleted frames
+        https://bugs.webkit.org/show_bug.cgi?id=156685
+        <rdar://problem/25770521>
+
+        Reviewed by Filip Pizlo and Mark Lam and Joseph Pecoraro.
+
+        This patch makes the WebInspector display tail deleted frames.
+        We show tail deleted frames with a gray [f] instead of a green
+        [f]. We also put text in the tooltip to indicate that the frame
+        is tail deleted. Other than that, tail deleted frames behave like
+        normal frames. You can evaluate in them, inspect their scope, etc.
+
+        * Localizations/en.lproj/localizedStrings.js:
+        * UserInterface/Images/TailDeletedFunction.svg: Added.
+        * UserInterface/Images/gtk/TailDeletedFunction.svg: Added.
+        * UserInterface/Models/CallFrame.js:
+        * UserInterface/Views/CallFrameIcons.css:
+        * UserInterface/Views/CallFrameTreeElement.js:
+        * UserInterface/Views/CallFrameView.js:
+
 2016-05-16  Nikita Vasilyev  <nvasilyev@apple.com>
 
         Web Inspector: Contents of Duration column are covered by always on (legacy) scroll bars
index 303925e..a43c8ae 100644 (file)
Binary files a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js and b/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js differ
diff --git a/Source/WebInspectorUI/UserInterface/Images/TailDeletedFunction.svg b/Source/WebInspectorUI/UserInterface/Images/TailDeletedFunction.svg
new file mode 100644 (file)
index 0000000..4633e74
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright © 2013 Apple Inc. All rights reserved. -->
+<svg xmlns="http://www.w3.org/2000/svg" id="root" version="1.1" viewBox="0 0 16 16">
+    <path fill="rgb(203, 203, 203)" d="M 13 1 L 3 1 C 1.898438 1 1 1.898438 1 3 L 1 13 C 1 14.101562 1.898438 15 3 15 L 13 15 C 14.101562 15 15 14.101562 15 13 L 15 3 C 15 1.898438 14.101562 1 13 1 Z"/>
+    <path fill="rgb(153, 153, 153)" d="M 13 1 L 3 1 C 1.898438 1 1 1.898438 1 3 L 1 13 C 1 14.101562 1.898438 15 3 15 L 13 15 C 14.101562 15 15 14.101562 15 13 L 15 3 C 15 1.898438 14.101562 1 13 1 M 13 2 C 13.550781 2 14 2.449219 14 3 L 14 13 C 14 13.550781 13.550781 14 13 14 L 3 14 C 2.449219 14 2 13.550781 2 13 L 2 3 C 2 2.449219 2.449219 2 3 2 L 13 2"/>
+    <path fill="rgb(166, 166, 166)" d="M 10.503906 2.558594 C 8.164062 2.558594 6.574219 3.742188 5.882812 5.988281 L 4.445312 6 L 3.449219 6.007812 L 3.449219 9 L 5.15625 9 L 4.21875 12.925781 L 3.921875 14.15625 L 7.964844 14.15625 L 8.148438 13.386719 L 9.191406 9 L 11.300781 9 L 11.300781 6 L 9.902344 6 C 10.070312 5.589844 10.296875 5.589844 10.480469 5.589844 C 10.800781 5.589844 11.191406 5.652344 11.644531 5.769531 L 12.796875 6.074219 L 12.898438 4.890625 L 12.996094 3.757812 L 13.078125 2.828125 L 12.15625 2.683594 C 11.636719 2.597656 11.082031 2.558594 10.503906 2.558594 M 10.503906 3.558594 C 11.03125 3.558594 11.53125 3.59375 12 3.667969 L 11.902344 4.804688 C 11.363281 4.660156 10.890625 4.589844 10.480469 4.589844 C 9.605469 4.589844 9.050781 5.101562 8.820312 6.128906 L 8.625 7 L 10.300781 7 L 10.300781 8 L 8.398438 8 L 7.175781 13.15625 L 5.191406 13.15625 L 6.425781 8 L 4.449219 8 L 4.449219 7 L 6.613281 6.984375 L 6.742188 6.617188 C 7.257812 4.578125 8.507812 3.558594 10.503906 3.558594"/>
+    <path fill="white" d="M 5.191406 13.15625 L 6.425781 8 L 4.449219 8 L 4.449219 7 L 6.613281 6.984375 L 6.742188 6.617188 C 7.257812 4.578125 8.507812 3.558594 10.5 3.558594 C 11.03125 3.558594 11.53125 3.59375 12 3.667969 L 11.902344 4.804688 C 11.363281 4.660156 10.890625 4.589844 10.484375 4.589844 C 9.605469 4.589844 9.050781 5.101562 8.820312 6.128906 L 8.625 7 L 10.300781 7 L 10.300781 8 L 8.398438 8 L 7.175781 13.15625 Z"/>
+</svg>
diff --git a/Source/WebInspectorUI/UserInterface/Images/gtk/TailDeletedFunction.svg b/Source/WebInspectorUI/UserInterface/Images/gtk/TailDeletedFunction.svg
new file mode 100644 (file)
index 0000000..be363b6
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Licensed under the Creative Commons Attribution-Share Alike 3.0 United States License (http://creativecommons.org/licenses/by-sa/3.0/) -->
+<svg xmlns="http://www.w3.org/2000/svg" id="root" version="1.1" viewBox="0 0 16 16">
+ <defs>
+  <radialGradient id="b" cx="301" cy="51.5" r="19.5" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.97063 1.9684e-8 0 .28611 -291.16 -14.63)">
+   <stop stop-color="#fff" offset="0"/>
+   <stop stop-color="#fff" stop-opacity="0" offset="1"/>
+  </radialGradient>
+  <radialGradient id="a" cx="24.446" cy="35.878" r="20.531" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.85696 3.2529e-7 -1.253e-7 .33010 -12.949 .77181)">
+   <stop stop-color="#fff" offset="0"/>
+   <stop stop-color="#dcdcdc" offset="1"/>
+  </radialGradient>
+  <linearGradient id="c" x1="321.57" x2="311.65" y1="145.52" y2="118.5" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.55569 0 0 .55568 -169.27 -70.906)">
+   <stop stop-color="#9b9b9b" offset="0"/>
+   <stop stop-color="#d3d7cf" offset="1"/>
+  </linearGradient>
+  <linearGradient id="d" x1="491.22" x2="491.22" y1="506.65" y2="732.05" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.093294 0 0 .093294 -35.871 -43.769)">
+   <stop stop-color="#888a85" offset="0"/>
+   <stop stop-color="#d3d7cf" offset="1"/>
+  </linearGradient>
+ </defs>
+ <rect width="15.004" height="15.003" x=".49815" y=".49833" fill="url(#a)" fill-rule="evenodd" stroke="url(#c)" stroke-miterlimit="10" rx="1.5143" ry="1.5143" stroke-linejoin="bevel"/>
+ <rect width="12.997" height="13" x="1.5014" y="1.5016" fill="none" stroke="#fff" stroke-miterlimit="10" ry=".50810" stroke-linejoin="bevel" rx=".50812"/>
+ <rect width="14" height="14" x="1.0017" y="1.0016" fill="url(#b)" fill-rule="evenodd" rx="1.9062" ry="1.9062"/>
+ <path fill="#fff" d="m9.0593 4.8342c-1.0619 0-1.8016 0.3977-2.25 0.875-0.44838 0.4773-0.61719 1.005-0.6875 1.3438l-0.0625 0.25h-0.71875a0.25002 0.25002 0 0 0 -0.0625 0 0.25002 0.25002 0 0 0 -0.09375 0.0625 0.25002 0.25002 0 0 0 -0.03125 0.03125 0.25002 0.25002 0 0 0 -0.03125 0.03125 0.25002 0.25002 0 0 0 -0.03125 0.09375l-0.1875 1.0938a0.25002 0.25002 0 0 0 0 0.03125 0.25002 0.25002 0 0 0 0 0.0625 0.25002 0.25002 0 0 0 0.125 0.15625 0.25002 0.25002 0 0 0 0.125 0.03125h0.59375l-0.84375 4.1563a0.25002 0.25002 0 0 0 0 0.0625 0.25002 0.25002 0 0 0 0 0.0625 0.25002 0.25002 0 0 0 0.09375 0.125 0.25002 0.25002 0 0 0 0.03125 0.03125 0.25002 0.25002 0 0 0 0.09375 0.03125 0.25002 0.25002 0 0 0 0.03125 0h1.4062a0.25002 0.25002 0 0 0 0.03125 0 0.25002 0.25002 0 0 0 0.09375 -0.03125 0.25002 0.25002 0 0 0 0.09375 -0.09375 0.25002 0.25002 0 0 0 0.03125 -0.0625l0.84375-4.2813h1.875a0.25002 0.25002 0 0 0 0.03125 0 0.25002 0.25002 0 0 0 0.09375 -0.03125 0.25002 0.25002 0 0 0 0.09375 -0.09375 0.25002 0.25002 0 0 0 0.03125 -0.0625l0.21875-1.0938a0.25002 0.25002 0 0 0 0 -0.0625 0.25002 0.25002 0 0 0 0 -0.0625 0.25002 0.25002 0 0 0 -0.0625 -0.09375 0.25002 0.25002 0 0 0 -0.03125 -0.03125 0.25002 0.25002 0 0 0 -0.0625 -0.03125 0.25002 0.25002 0 0 0 -0.09375 -0.03125h-1.7812l0.03125-0.15625c0.037755-0.19297 0.14732-0.34648 0.34375-0.46875 0.19642-0.12227 0.4762-0.21875 0.8125-0.21875 0.30479 0 0.6559 0.064823 0.78125 0.09375l0.21875 0.0625a0.25002 0.25002 0 0 0 0.0625 0 0.25002 0.25002 0 0 0 0.0625 0 0.25002 0.25002 0 0 0 0.125 -0.09375 0.25002 0.25002 0 0 0 0.03125 -0.03125 0.25002 0.25002 0 0 0 0 -0.03125l0.40625-1a0.25002 0.25002 0 0 0 0.03125 -0.03125 0.25002 0.25002 0 0 0 0 -0.0625 0.25002 0.25002 0 0 0 -0.03125 -0.09375 0.25002 0.25002 0 0 0 -0.03125 -0.0625 0.25002 0.25002 0 0 0 -0.03125 -0.03125 0.25002 0.25002 0 0 0 -0.0625 -0.03125 0.25002 0.25002 0 0 0 -0.03125 -0.03125l-0.1875-0.0625c-0.673-0.2163-1.1307-0.2185-1.4367-0.2185z" display="block"/>
+ <path fill="url(#d)" d="m9.1111 3.8039c-1.0619 0-1.8016 0.3977-2.25 0.875-0.44838 0.4773-0.61719 1.005-0.6875 1.3438l-0.0625 0.25h-0.71875a0.25002 0.25002 0 0 0 -0.0625 0 0.25002 0.25002 0 0 0 -0.09375 0.0625 0.25002 0.25002 0 0 0 -0.03125 0.03125 0.25002 0.25002 0 0 0 -0.03125 0.03125 0.25002 0.25002 0 0 0 -0.03125 0.09375l-0.1875 1.0938a0.25002 0.25002 0 0 0 0 0.03125 0.25002 0.25002 0 0 0 0 0.0625 0.25002 0.25002 0 0 0 0.125 0.15625 0.25002 0.25002 0 0 0 0.125 0.03125h0.59375l-0.84375 4.1563a0.25002 0.25002 0 0 0 0 0.0625 0.25002 0.25002 0 0 0 0 0.0625 0.25002 0.25002 0 0 0 0.09375 0.125 0.25002 0.25002 0 0 0 0.03125 0.03125 0.25002 0.25002 0 0 0 0.09375 0.03125 0.25002 0.25002 0 0 0 0.03125 0h1.4062a0.25002 0.25002 0 0 0 0.03125 0 0.25002 0.25002 0 0 0 0.09375 -0.03125 0.25002 0.25002 0 0 0 0.09375 -0.09375 0.25002 0.25002 0 0 0 0.03125 -0.0625l0.84375-4.2813h1.875a0.25002 0.25002 0 0 0 0.03125 0 0.25002 0.25002 0 0 0 0.09375 -0.03125 0.25002 0.25002 0 0 0 0.09375 -0.09375 0.25002 0.25002 0 0 0 0.03125 -0.0625l0.2191-1.0941a0.25002 0.25002 0 0 0 0 -0.0625 0.25002 0.25002 0 0 0 0 -0.0625 0.25002 0.25002 0 0 0 -0.0625 -0.09375 0.25002 0.25002 0 0 0 -0.03125 -0.03125 0.25002 0.25002 0 0 0 -0.0625 -0.03125 0.25002 0.25002 0 0 0 -0.09375 -0.03125h-1.7812l0.03125-0.15625c0.037755-0.19297 0.14732-0.34648 0.34375-0.46875 0.19642-0.12227 0.4762-0.21875 0.8125-0.21875 0.30479 0 0.6559 0.064823 0.78125 0.09375l0.21875 0.0625a0.25002 0.25002 0 0 0 0.0625 0 0.25002 0.25002 0 0 0 0.0625 0 0.25002 0.25002 0 0 0 0.125 -0.09375 0.25002 0.25002 0 0 0 0.03125 -0.03125 0.25002 0.25002 0 0 0 0 -0.03125l0.40625-1a0.25002 0.25002 0 0 0 0.03125 -0.03125 0.25002 0.25002 0 0 0 0 -0.0625 0.25002 0.25002 0 0 0 -0.03125 -0.09375 0.25002 0.25002 0 0 0 -0.03125 -0.0625 0.25002 0.25002 0 0 0 -0.03125 -0.03125 0.25002 0.25002 0 0 0 -0.0625 -0.03125 0.25002 0.25002 0 0 0 -0.03125 -0.03125l-0.1875-0.0625c-0.6732-0.2164-1.1309-0.2186-1.4369-0.2186z" display="block"/>
+</svg>
index 3afeeac..fef7895 100644 (file)
@@ -25,7 +25,7 @@
 
 WebInspector.CallFrame = class CallFrame extends WebInspector.Object
 {
-    constructor(id, sourceCodeLocation, functionName, thisObject, scopeChain, nativeCode, programCode)
+    constructor(id, sourceCodeLocation, functionName, thisObject, scopeChain, nativeCode, programCode, isTailDeleted)
     {
         super();
 
@@ -40,6 +40,7 @@ WebInspector.CallFrame = class CallFrame extends WebInspector.Object
         this._scopeChain = scopeChain || [];
         this._nativeCode = nativeCode || false;
         this._programCode = programCode || false;
+        this._isTailDeleted = isTailDeleted || false;
     }
 
     // Public
@@ -79,6 +80,11 @@ WebInspector.CallFrame = class CallFrame extends WebInspector.Object
         return this._scopeChain;
     }
 
+    get isTailDeleted()
+    {
+        return this._isTailDeleted;
+    }
+
     saveIdentityToCookie()
     {
         // Do nothing. The call frame is torn down when the inspector closes, and
@@ -132,13 +138,14 @@ WebInspector.CallFrame = class CallFrame extends WebInspector.Object
         let functionName = WebInspector.CallFrame.functionNameFromPayload(payload);
         let nativeCode = false;
         let programCode = WebInspector.CallFrame.programCodeFromPayload(payload);
+        let isTailDeleted = payload.isTailDeleted;
 
         if (sourceCodeLocation && isWebInspectorConsoleEvaluationScript(sourceCodeLocation.sourceCode.sourceURL)) {
             functionName = WebInspector.UIString("Console Evaluation");
             programCode = true;
         }
 
-        return new WebInspector.CallFrame(id, sourceCodeLocation, functionName, thisObject, scopeChain, nativeCode, programCode);
+        return new WebInspector.CallFrame(id, sourceCodeLocation, functionName, thisObject, scopeChain, nativeCode, programCode, isTailDeleted);
     }
 
     static fromPayload(payload)
index 012e281..236a07c 100644 (file)
@@ -38,3 +38,7 @@
 .native-icon .icon {
     content: url(../Images/Native.svg);
 }
+
+.tail-deleted .icon {
+    content: url(../Images/TailDeletedFunction.svg);
+}
index 5da914b..0076138 100644 (file)
@@ -29,13 +29,13 @@ WebInspector.CallFrameTreeElement = class CallFrameTreeElement extends WebInspec
     {
         console.assert(callFrame instanceof WebInspector.CallFrame);
 
-        var className = WebInspector.CallFrameView.iconClassNameForCallFrame(callFrame);
-        var title = callFrame.functionName || WebInspector.UIString("(anonymous function)");
+        let className = WebInspector.CallFrameView.iconClassNameForCallFrame(callFrame);
+        let title = callFrame.functionName || WebInspector.UIString("(anonymous function)");
 
         super(className, title, null, callFrame, false);
 
         if (!callFrame.nativeCode && callFrame.sourceCodeLocation) {
-            var displayScriptURL = callFrame.sourceCodeLocation.displaySourceCode.url;
+            let displayScriptURL = callFrame.sourceCodeLocation.displaySourceCode.url;
             if (displayScriptURL) {
                 this.subtitle = document.createElement("span");
                 callFrame.sourceCodeLocation.populateLiveDisplayLocationString(this.subtitle, "textContent");
@@ -63,7 +63,10 @@ WebInspector.CallFrameTreeElement = class CallFrameTreeElement extends WebInspec
         console.assert(this.element);
 
         if (this.tooltipHandledSeparately) {
-            var tooltipPrefix = this.mainTitle + "\n";
+            let tailCallSuffix = "";
+            if (this._callFrame.isTailDeleted)
+                tailCallSuffix = " " + WebInspector.UIString("(Tail Call)");
+            let tooltipPrefix = this.mainTitle + tailCallSuffix + "\n";
             this._callFrame.sourceCodeLocation.populateLiveDisplayLocationTooltip(this.element, tooltipPrefix);
         }
     }
index 26cc7da..e7ac854 100644 (file)
@@ -73,6 +73,9 @@ WebInspector.CallFrameView = class CallFrameView extends WebInspector.Object
 
     static iconClassNameForCallFrame(callFrame)
     {
+        if (callFrame.isTailDeleted)
+            return WebInspector.CallFrameView.TailDeletedIcon;
+
         if (callFrame.programCode)
             return WebInspector.CallFrameView.ProgramIconStyleClassName;
 
@@ -92,3 +95,4 @@ WebInspector.CallFrameView.ProgramIconStyleClassName = "program-icon";
 WebInspector.CallFrameView.FunctionIconStyleClassName = "function-icon";
 WebInspector.CallFrameView.EventListenerIconStyleClassName = "event-listener-icon";
 WebInspector.CallFrameView.NativeIconStyleClassName = "native-icon";
+WebInspector.CallFrameView.TailDeletedIcon = "tail-deleted";