<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[201465] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/201465">201465</a></dd>
<dt>Author</dt> <dd>sbarati@apple.com</dd>
<dt>Date</dt> <dd>2016-05-27 13:26:06 -0700 (Fri, 27 May 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>ShadowChicken/DebuggerCallFrame don't properly handle when the entry stack frame is a tail deleted frame
https://bugs.webkit.org/show_bug.cgi?id=158131

Reviewed by Yusuke Suzuki.

Source/JavaScriptCore:

There were bugs both in DebuggerCallFrame and ShadowChicken when the entry stack
frame(s) are tail deleted.

DebuggerCallFrame had an assertion saying that the entry frame shouldn't be
tail deleted. This is clearly wrong. The following program proves that this assertion
was misguided:
```
&quot;use strict&quot;;
setTimeout(function foo() { return bar(); }, 0);
```

ShadowChicken had a very subtle bug when creating the shadow stack when 
the entry frames of the stack were tail deleted. Because it places frames into its shadow
stack by walking the machine frame and looking up entries in the log,
the machine frame doesn't have any notion of those tail deleted frames
at the entry of execution. ShadowChicken would never find those frames
because it would look for tail deleted frames *before* consulting the
current machine frame. This is wrong because if the entry frames
are tail deleted, then there is no machine frame for them because there
is no machine frame before them! Therefore, we must search for tail deleted
frames *after* consulting a machine frame. This is sound because we will always
have at least one machine frame on the stack (when we are using StackVisitor on a valid ExecState).
So when we consult the machine frame that is the entry frame on the machine stack,
we will search for tail deleted frames that come before it in the shadow stack.
This will allow us to find those tail deleted frames that are the entry frames
for the shadow stack.

* debugger/DebuggerCallFrame.cpp:
(JSC::DebuggerCallFrame::create):
* interpreter/ShadowChicken.cpp:
(JSC::ShadowChicken::Packet::dump):
(JSC::ShadowChicken::update):
(JSC::ShadowChicken::dump):

LayoutTests:

* inspector/debugger/resources/tail-deleted-frames-from-vm-entry.js: Added.
(timeout):
(bar):
* inspector/debugger/tail-deleted-frames-from-vm-entry-expected.txt: Added.
* inspector/debugger/tail-deleted-frames-from-vm-entry.html: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoredebuggerDebuggerCallFramecpp">trunk/Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreinterpreterShadowChickencpp">trunk/Source/JavaScriptCore/interpreter/ShadowChicken.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsinspectordebuggerresourcestaildeletedframesfromvmentryjs">trunk/LayoutTests/inspector/debugger/resources/tail-deleted-frames-from-vm-entry.js</a></li>
<li><a href="#trunkLayoutTestsinspectordebuggertaildeletedframesfromvmentryexpectedtxt">trunk/LayoutTests/inspector/debugger/tail-deleted-frames-from-vm-entry-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectordebuggertaildeletedframesfromvmentryhtml">trunk/LayoutTests/inspector/debugger/tail-deleted-frames-from-vm-entry.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (201464 => 201465)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-05-27 20:22:12 UTC (rev 201464)
+++ trunk/LayoutTests/ChangeLog        2016-05-27 20:26:06 UTC (rev 201465)
</span><span class="lines">@@ -1,3 +1,16 @@
</span><ins>+2016-05-27  Saam barati  &lt;sbarati@apple.com&gt;
+
+        ShadowChicken/DebuggerCallFrame don't properly handle when the entry stack frame is a tail deleted frame
+        https://bugs.webkit.org/show_bug.cgi?id=158131
+
+        Reviewed by Yusuke Suzuki.
+
+        * inspector/debugger/resources/tail-deleted-frames-from-vm-entry.js: Added.
+        (timeout):
+        (bar):
+        * inspector/debugger/tail-deleted-frames-from-vm-entry-expected.txt: Added.
+        * inspector/debugger/tail-deleted-frames-from-vm-entry.html: Added.
+
</ins><span class="cx"> 2016-05-27  Joanmarie Diggs  &lt;jdiggs@igalia.com&gt;
</span><span class="cx"> 
</span><span class="cx">         AX: [ATK] accessibility/gtk/no-notification-for-unrendered-iframe-children.html began failing after r201416
</span></span></pre></div>
<a id="trunkLayoutTestsinspectordebuggerresourcestaildeletedframesfromvmentryjs"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/debugger/resources/tail-deleted-frames-from-vm-entry.js (0 => 201465)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/debugger/resources/tail-deleted-frames-from-vm-entry.js                                (rev 0)
+++ trunk/LayoutTests/inspector/debugger/resources/tail-deleted-frames-from-vm-entry.js        2016-05-27 20:26:06 UTC (rev 201465)
</span><span class="lines">@@ -0,0 +1,9 @@
</span><ins>+&quot;use strict&quot;;
+function timeout(foo = 25) {
+    return bar();
+}
+function bar(i = 9) {
+    if (i &gt; 0)
+        return bar(i - 1);
+    return 25;
+}
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectordebuggertaildeletedframesfromvmentryexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/debugger/tail-deleted-frames-from-vm-entry-expected.txt (0 => 201465)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/debugger/tail-deleted-frames-from-vm-entry-expected.txt                                (rev 0)
+++ trunk/LayoutTests/inspector/debugger/tail-deleted-frames-from-vm-entry-expected.txt        2016-05-27 20:26:06 UTC (rev 201465)
</span><span class="lines">@@ -0,0 +1,191 @@
</span><ins>+Testing that we keep around tail deleted frames that are entry frames.
+
+Starting Test
+
+
+------------------------------------
+Hit breakpoint at line: 7, column: 4
+------------------------------------
+Expected frame: {&quot;functionName&quot;:&quot;bar&quot;,&quot;scope&quot;:[&quot;i&quot;,0],&quot;isTailDeleted&quot;:false}
+PASS: Function name: bar is correct.
+PASS: Tail deleted expectation correct: false
+Expected frame: {&quot;functionName&quot;:&quot;bar&quot;,&quot;scope&quot;:[&quot;i&quot;,1],&quot;isTailDeleted&quot;:true}
+PASS: Function name: bar is correct.
+PASS: Tail deleted expectation correct: true
+Expected frame: {&quot;functionName&quot;:&quot;bar&quot;,&quot;scope&quot;:[&quot;i&quot;,2],&quot;isTailDeleted&quot;:true}
+PASS: Function name: bar is correct.
+PASS: Tail deleted expectation correct: true
+Expected frame: {&quot;functionName&quot;:&quot;bar&quot;,&quot;scope&quot;:[&quot;i&quot;,3],&quot;isTailDeleted&quot;:true}
+PASS: Function name: bar is correct.
+PASS: Tail deleted expectation correct: true
+Expected frame: {&quot;functionName&quot;:&quot;bar&quot;,&quot;scope&quot;:[&quot;i&quot;,4],&quot;isTailDeleted&quot;:true}
+PASS: Function name: bar is correct.
+PASS: Tail deleted expectation correct: true
+Expected frame: {&quot;functionName&quot;:&quot;bar&quot;,&quot;scope&quot;:[&quot;i&quot;,5],&quot;isTailDeleted&quot;:true}
+PASS: Function name: bar is correct.
+PASS: Tail deleted expectation correct: true
+Expected frame: {&quot;functionName&quot;:&quot;bar&quot;,&quot;scope&quot;:[&quot;i&quot;,6],&quot;isTailDeleted&quot;:true}
+PASS: Function name: bar is correct.
+PASS: Tail deleted expectation correct: true
+Expected frame: {&quot;functionName&quot;:&quot;bar&quot;,&quot;scope&quot;:[&quot;i&quot;,7],&quot;isTailDeleted&quot;:true}
+PASS: Function name: bar is correct.
+PASS: Tail deleted expectation correct: true
+Expected frame: {&quot;functionName&quot;:&quot;bar&quot;,&quot;scope&quot;:[&quot;i&quot;,8],&quot;isTailDeleted&quot;:true}
+PASS: Function name: bar is correct.
+PASS: Tail deleted expectation correct: true
+Expected frame: {&quot;functionName&quot;:&quot;bar&quot;,&quot;scope&quot;:[&quot;i&quot;,9],&quot;isTailDeleted&quot;:true}
+PASS: Function name: bar is correct.
+PASS: Tail deleted expectation correct: true
+Expected frame: {&quot;functionName&quot;:&quot;timeout&quot;,&quot;scope&quot;:[&quot;foo&quot;,25],&quot;isTailDeleted&quot;:true}
+PASS: Function name: timeout is correct.
+PASS: Tail deleted expectation correct: true
+Looking at frame number: 0
+    variable 'i': {&quot;_type&quot;:&quot;number&quot;,&quot;_description&quot;:&quot;0&quot;,&quot;_hasChildren&quot;:false,&quot;_value&quot;:0}
+PASS: Variable is a number.
+PASS: Found scope value: 0
+PASS: Did not find variable we were looking for: i
+Looking at frame number: 1
+    variable 'i': {&quot;_type&quot;:&quot;number&quot;,&quot;_description&quot;:&quot;1&quot;,&quot;_hasChildren&quot;:false,&quot;_value&quot;:1}
+PASS: Variable is a number.
+PASS: Found scope value: 1
+PASS: Did not find variable we were looking for: i
+Looking at frame number: 2
+    variable 'i': {&quot;_type&quot;:&quot;number&quot;,&quot;_description&quot;:&quot;2&quot;,&quot;_hasChildren&quot;:false,&quot;_value&quot;:2}
+PASS: Variable is a number.
+PASS: Found scope value: 2
+PASS: Did not find variable we were looking for: i
+Looking at frame number: 3
+    variable 'i': {&quot;_type&quot;:&quot;number&quot;,&quot;_description&quot;:&quot;3&quot;,&quot;_hasChildren&quot;:false,&quot;_value&quot;:3}
+PASS: Variable is a number.
+PASS: Found scope value: 3
+PASS: Did not find variable we were looking for: i
+Looking at frame number: 4
+    variable 'i': {&quot;_type&quot;:&quot;number&quot;,&quot;_description&quot;:&quot;4&quot;,&quot;_hasChildren&quot;:false,&quot;_value&quot;:4}
+PASS: Variable is a number.
+PASS: Found scope value: 4
+PASS: Did not find variable we were looking for: i
+Looking at frame number: 5
+    variable 'i': {&quot;_type&quot;:&quot;number&quot;,&quot;_description&quot;:&quot;5&quot;,&quot;_hasChildren&quot;:false,&quot;_value&quot;:5}
+PASS: Variable is a number.
+PASS: Found scope value: 5
+PASS: Did not find variable we were looking for: i
+Looking at frame number: 6
+    variable 'i': {&quot;_type&quot;:&quot;number&quot;,&quot;_description&quot;:&quot;6&quot;,&quot;_hasChildren&quot;:false,&quot;_value&quot;:6}
+PASS: Variable is a number.
+PASS: Found scope value: 6
+PASS: Did not find variable we were looking for: i
+Looking at frame number: 7
+    variable 'i': {&quot;_type&quot;:&quot;number&quot;,&quot;_description&quot;:&quot;7&quot;,&quot;_hasChildren&quot;:false,&quot;_value&quot;:7}
+PASS: Variable is a number.
+PASS: Found scope value: 7
+PASS: Did not find variable we were looking for: i
+Looking at frame number: 8
+    variable 'i': {&quot;_type&quot;:&quot;number&quot;,&quot;_description&quot;:&quot;8&quot;,&quot;_hasChildren&quot;:false,&quot;_value&quot;:8}
+PASS: Variable is a number.
+PASS: Found scope value: 8
+PASS: Did not find variable we were looking for: i
+Looking at frame number: 9
+    variable 'i': {&quot;_type&quot;:&quot;number&quot;,&quot;_description&quot;:&quot;9&quot;,&quot;_hasChildren&quot;:false,&quot;_value&quot;:9}
+PASS: Variable is a number.
+PASS: Found scope value: 9
+PASS: Did not find variable we were looking for: i
+Looking at frame number: 10
+    variable 'foo': {&quot;_type&quot;:&quot;number&quot;,&quot;_description&quot;:&quot;25&quot;,&quot;_hasChildren&quot;:false,&quot;_value&quot;:25}
+PASS: Variable is a number.
+PASS: Found scope value: 25
+PASS: Did not find variable we were looking for: foo
+
+
+------------------------------------
+Hit breakpoint at line: 7, column: 4
+------------------------------------
+Expected frame: {&quot;functionName&quot;:&quot;bar&quot;,&quot;scope&quot;:[&quot;i&quot;,0],&quot;isTailDeleted&quot;:false}
+PASS: Function name: bar is correct.
+PASS: Tail deleted expectation correct: false
+Expected frame: {&quot;functionName&quot;:&quot;bar&quot;,&quot;scope&quot;:[&quot;i&quot;,1],&quot;isTailDeleted&quot;:true}
+PASS: Function name: bar is correct.
+PASS: Tail deleted expectation correct: true
+Expected frame: {&quot;functionName&quot;:&quot;bar&quot;,&quot;scope&quot;:[&quot;i&quot;,2],&quot;isTailDeleted&quot;:true}
+PASS: Function name: bar is correct.
+PASS: Tail deleted expectation correct: true
+Expected frame: {&quot;functionName&quot;:&quot;bar&quot;,&quot;scope&quot;:[&quot;i&quot;,3],&quot;isTailDeleted&quot;:true}
+PASS: Function name: bar is correct.
+PASS: Tail deleted expectation correct: true
+Expected frame: {&quot;functionName&quot;:&quot;bar&quot;,&quot;scope&quot;:[&quot;i&quot;,4],&quot;isTailDeleted&quot;:true}
+PASS: Function name: bar is correct.
+PASS: Tail deleted expectation correct: true
+Expected frame: {&quot;functionName&quot;:&quot;bar&quot;,&quot;scope&quot;:[&quot;i&quot;,5],&quot;isTailDeleted&quot;:true}
+PASS: Function name: bar is correct.
+PASS: Tail deleted expectation correct: true
+Expected frame: {&quot;functionName&quot;:&quot;bar&quot;,&quot;scope&quot;:[&quot;i&quot;,6],&quot;isTailDeleted&quot;:true}
+PASS: Function name: bar is correct.
+PASS: Tail deleted expectation correct: true
+Expected frame: {&quot;functionName&quot;:&quot;bar&quot;,&quot;scope&quot;:[&quot;i&quot;,7],&quot;isTailDeleted&quot;:true}
+PASS: Function name: bar is correct.
+PASS: Tail deleted expectation correct: true
+Expected frame: {&quot;functionName&quot;:&quot;bar&quot;,&quot;scope&quot;:[&quot;i&quot;,8],&quot;isTailDeleted&quot;:true}
+PASS: Function name: bar is correct.
+PASS: Tail deleted expectation correct: true
+Expected frame: {&quot;functionName&quot;:&quot;bar&quot;,&quot;scope&quot;:[&quot;i&quot;,9],&quot;isTailDeleted&quot;:true}
+PASS: Function name: bar is correct.
+PASS: Tail deleted expectation correct: true
+Expected frame: {&quot;functionName&quot;:&quot;timeout&quot;,&quot;scope&quot;:[&quot;foo&quot;,25],&quot;isTailDeleted&quot;:true}
+PASS: Function name: timeout is correct.
+PASS: Tail deleted expectation correct: true
+Looking at frame number: 0
+    variable 'i': {&quot;_type&quot;:&quot;number&quot;,&quot;_description&quot;:&quot;0&quot;,&quot;_hasChildren&quot;:false,&quot;_value&quot;:0}
+PASS: Variable is a number.
+PASS: Found scope value: 0
+PASS: Did not find variable we were looking for: i
+Looking at frame number: 1
+    variable 'i': {&quot;_type&quot;:&quot;number&quot;,&quot;_description&quot;:&quot;1&quot;,&quot;_hasChildren&quot;:false,&quot;_value&quot;:1}
+PASS: Variable is a number.
+PASS: Found scope value: 1
+PASS: Did not find variable we were looking for: i
+Looking at frame number: 2
+    variable 'i': {&quot;_type&quot;:&quot;number&quot;,&quot;_description&quot;:&quot;2&quot;,&quot;_hasChildren&quot;:false,&quot;_value&quot;:2}
+PASS: Variable is a number.
+PASS: Found scope value: 2
+PASS: Did not find variable we were looking for: i
+Looking at frame number: 3
+    variable 'i': {&quot;_type&quot;:&quot;number&quot;,&quot;_description&quot;:&quot;3&quot;,&quot;_hasChildren&quot;:false,&quot;_value&quot;:3}
+PASS: Variable is a number.
+PASS: Found scope value: 3
+PASS: Did not find variable we were looking for: i
+Looking at frame number: 4
+    variable 'i': {&quot;_type&quot;:&quot;number&quot;,&quot;_description&quot;:&quot;4&quot;,&quot;_hasChildren&quot;:false,&quot;_value&quot;:4}
+PASS: Variable is a number.
+PASS: Found scope value: 4
+PASS: Did not find variable we were looking for: i
+Looking at frame number: 5
+    variable 'i': {&quot;_type&quot;:&quot;number&quot;,&quot;_description&quot;:&quot;5&quot;,&quot;_hasChildren&quot;:false,&quot;_value&quot;:5}
+PASS: Variable is a number.
+PASS: Found scope value: 5
+PASS: Did not find variable we were looking for: i
+Looking at frame number: 6
+    variable 'i': {&quot;_type&quot;:&quot;number&quot;,&quot;_description&quot;:&quot;6&quot;,&quot;_hasChildren&quot;:false,&quot;_value&quot;:6}
+PASS: Variable is a number.
+PASS: Found scope value: 6
+PASS: Did not find variable we were looking for: i
+Looking at frame number: 7
+    variable 'i': {&quot;_type&quot;:&quot;number&quot;,&quot;_description&quot;:&quot;7&quot;,&quot;_hasChildren&quot;:false,&quot;_value&quot;:7}
+PASS: Variable is a number.
+PASS: Found scope value: 7
+PASS: Did not find variable we were looking for: i
+Looking at frame number: 8
+    variable 'i': {&quot;_type&quot;:&quot;number&quot;,&quot;_description&quot;:&quot;8&quot;,&quot;_hasChildren&quot;:false,&quot;_value&quot;:8}
+PASS: Variable is a number.
+PASS: Found scope value: 8
+PASS: Did not find variable we were looking for: i
+Looking at frame number: 9
+    variable 'i': {&quot;_type&quot;:&quot;number&quot;,&quot;_description&quot;:&quot;9&quot;,&quot;_hasChildren&quot;:false,&quot;_value&quot;:9}
+PASS: Variable is a number.
+PASS: Found scope value: 9
+PASS: Did not find variable we were looking for: i
+Looking at frame number: 10
+    variable 'foo': {&quot;_type&quot;:&quot;number&quot;,&quot;_description&quot;:&quot;25&quot;,&quot;_hasChildren&quot;:false,&quot;_value&quot;:25}
+PASS: Variable is a number.
+PASS: Found scope value: 25
+PASS: Did not find variable we were looking for: foo
+Tests done
+
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectordebuggertaildeletedframesfromvmentryhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/debugger/tail-deleted-frames-from-vm-entry.html (0 => 201465)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/debugger/tail-deleted-frames-from-vm-entry.html                                (rev 0)
+++ trunk/LayoutTests/inspector/debugger/tail-deleted-frames-from-vm-entry.html        2016-05-27 20:26:06 UTC (rev 201465)
</span><span class="lines">@@ -0,0 +1,95 @@
</span><ins>+&lt;!doctype html&gt;
+&lt;html&gt;
+&lt;head&gt;
+&lt;script type=&quot;text/javascript&quot; src=&quot;../../http/tests/inspector/resources/inspector-test.js&quot;&gt;&lt;/script&gt;
+&lt;script type=&quot;text/javascript&quot; src=&quot;../../http/tests/inspector/debugger/debugger-test.js&quot;&gt;&lt;/script&gt;
+&lt;script type=&quot;text/javascript&quot; src=&quot;./resources/tail-deleted-frames-from-vm-entry.js&quot;&gt;&lt;/script&gt;
+&lt;script&gt;
+
+function test()
+{
+    let scriptObject;
+
+    function startTest() {
+        InspectorTest.log(&quot;Starting Test&quot;);
+        // 0 based indices.
+        let testInfo = {line: 7, column: 4};
+        let location = scriptObject.createSourceCodeLocation(testInfo.line, testInfo.column);
+        let breakpoint = new WebInspector.Breakpoint(location);
+        WebInspector.debuggerManager.addBreakpoint(breakpoint);
+        InspectorTest.evaluateInPage(&quot;setTimeout(timeout, 0);&quot;);
+    }
+
+    WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.CallFramesDidChange, function(event) {
+        var activeCallFrame = WebInspector.debuggerManager.activeCallFrame;
+
+        if (!activeCallFrame)
+            return;
+
+        var stopLocation = &quot;line: &quot; + activeCallFrame.sourceCodeLocation.lineNumber + &quot;, column: &quot; + activeCallFrame.sourceCodeLocation.columnNumber;
+
+        InspectorTest.log(&quot;\n\n------------------------------------&quot;);
+        InspectorTest.log(&quot;Hit breakpoint at &quot; + stopLocation);
+        InspectorTest.log(&quot;------------------------------------&quot;);
+
+        // top down list
+        let expectedFrames = [];
+        for (let i = 0; i &lt; 10; i++)
+            expectedFrames.push({functionName: 'bar', scope: ['i', i], isTailDeleted: i &gt; 0 ? true : false});
+        expectedFrames.push({functionName: 'timeout', scope: ['foo', 25], isTailDeleted: true});
+
+        InspectorTest.assert(WebInspector.debuggerManager.callFrames.length &gt;= expectedFrames.length);
+
+        for (let i = 0; i &lt; expectedFrames.length; i++) {
+            let callFrame = WebInspector.debuggerManager.callFrames[i];
+            let expectedFrame = expectedFrames[i];
+            InspectorTest.log(&quot;Expected frame: &quot; + JSON.stringify(expectedFrame));
+            InspectorTest.expectThat(callFrame.functionName === expectedFrame.functionName, `Function name: ${callFrame.functionName} is correct.`);
+
+            InspectorTest.expectThat(callFrame.isTailDeleted === expectedFrame.isTailDeleted, `Tail deleted expectation correct: ${callFrame.isTailDeleted}`);
+            let scope = callFrame.scopeChain[1];
+
+            scope.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(&quot;Looking at frame number: &quot; + i);
+                        InspectorTest.log(`    variable '${variableName}': ${JSON.stringify(propertyDescriptor.value)}`);
+                        InspectorTest.expectThat(propertyDescriptor.value.type === 'number', &quot;Variable is a number.&quot;);
+                        InspectorTest.expectThat(propertyDescriptor.value.value === variableValue, `Found scope value: ${variableValue}`);
+                    }
+                }
+                InspectorTest.expectThat(!!found, `Did not find variable we were looking for: ${variableName}`);
+            });
+        }
+
+        WebInspector.debuggerManager.resume();
+    });
+
+    WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Resumed, function(event) {
+        InspectorTest.log(&quot;Tests done&quot;);
+        InspectorTest.completeTest();
+    });
+
+    WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.ScriptAdded, function(event) {
+        eventScriptObject = event.data.script;
+        
+        if (/tail-deleted-frames-from-vm-entry\.js$/.test(eventScriptObject.url)) {
+            scriptObject = eventScriptObject;
+            startTest();
+            return;
+        }
+
+    });
+
+    InspectorTest.reloadPage();
+}
+&lt;/script&gt;
+&lt;/head&gt;
+&lt;body onload=&quot;runTest()&quot;&gt;
+    &lt;p&gt;Testing that we keep around tail deleted frames that are entry frames. &lt;/p&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (201464 => 201465)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2016-05-27 20:22:12 UTC (rev 201464)
+++ trunk/Source/JavaScriptCore/ChangeLog        2016-05-27 20:26:06 UTC (rev 201465)
</span><span class="lines">@@ -1,3 +1,44 @@
</span><ins>+2016-05-27  Saam barati  &lt;sbarati@apple.com&gt;
+
+        ShadowChicken/DebuggerCallFrame don't properly handle when the entry stack frame is a tail deleted frame
+        https://bugs.webkit.org/show_bug.cgi?id=158131
+
+        Reviewed by Yusuke Suzuki.
+
+        There were bugs both in DebuggerCallFrame and ShadowChicken when the entry stack
+        frame(s) are tail deleted.
+
+        DebuggerCallFrame had an assertion saying that the entry frame shouldn't be
+        tail deleted. This is clearly wrong. The following program proves that this assertion
+        was misguided:
+        ```
+        &quot;use strict&quot;;
+        setTimeout(function foo() { return bar(); }, 0);
+        ```
+
+        ShadowChicken had a very subtle bug when creating the shadow stack when 
+        the entry frames of the stack were tail deleted. Because it places frames into its shadow
+        stack by walking the machine frame and looking up entries in the log,
+        the machine frame doesn't have any notion of those tail deleted frames
+        at the entry of execution. ShadowChicken would never find those frames
+        because it would look for tail deleted frames *before* consulting the
+        current machine frame. This is wrong because if the entry frames
+        are tail deleted, then there is no machine frame for them because there
+        is no machine frame before them! Therefore, we must search for tail deleted
+        frames *after* consulting a machine frame. This is sound because we will always
+        have at least one machine frame on the stack (when we are using StackVisitor on a valid ExecState).
+        So when we consult the machine frame that is the entry frame on the machine stack,
+        we will search for tail deleted frames that come before it in the shadow stack.
+        This will allow us to find those tail deleted frames that are the entry frames
+        for the shadow stack.
+
+        * debugger/DebuggerCallFrame.cpp:
+        (JSC::DebuggerCallFrame::create):
+        * interpreter/ShadowChicken.cpp:
+        (JSC::ShadowChicken::Packet::dump):
+        (JSC::ShadowChicken::update):
+        (JSC::ShadowChicken::dump):
+
</ins><span class="cx"> 2016-05-27  Chris Dumez  &lt;cdumez@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         WorkQueue::dispatch() / RunLoop::dispatch() should not copy captured lambda variables
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredebuggerDebuggerCallFramecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp (201464 => 201465)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp        2016-05-27 20:22:12 UTC (rev 201464)
+++ trunk/Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp        2016-05-27 20:26:06 UTC (rev 201465)
</span><span class="lines">@@ -69,16 +69,15 @@
</span><span class="cx">     });
</span><span class="cx"> 
</span><span class="cx">     RELEASE_ASSERT(frames.size());
</span><del>-    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.
</del><ins>+    ASSERT(!frames[0].isTailDeleted); // The top frame should never be tail deleted.
</ins><span class="cx"> 
</span><span class="cx">     RefPtr&lt;DebuggerCallFrame&gt; currentParent = nullptr;
</span><del>-    ExecState* exec = nullptr;
</del><ins>+    ExecState* exec = callFrame-&gt;lexicalGlobalObject()-&gt;globalExec();
+    // This walks the stack from the entry stack frame to the top of the stack.
</ins><span class="cx">     for (unsigned i = frames.size(); i--; ) {
</span><span class="cx">         const ShadowChicken::Frame&amp; frame = frames[i];
</span><span class="cx">         if (!frame.isTailDeleted)
</span><span class="cx">             exec = frame.frame;
</span><del>-        ASSERT(exec);
</del><span class="cx">         Ref&lt;DebuggerCallFrame&gt; currentFrame = adoptRef(*new DebuggerCallFrame(exec, frame));
</span><span class="cx">         currentFrame-&gt;m_caller = currentParent;
</span><span class="cx">         currentParent = WTFMove(currentFrame);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreinterpreterShadowChickencpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/interpreter/ShadowChicken.cpp (201464 => 201465)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/interpreter/ShadowChicken.cpp        2016-05-27 20:22:12 UTC (rev 201464)
+++ trunk/Source/JavaScriptCore/interpreter/ShadowChicken.cpp        2016-05-27 20:26:06 UTC (rev 201465)
</span><span class="lines">@@ -50,7 +50,7 @@
</span><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     if (isTail()) {
</span><del>-        out.print(&quot;tail:{frame = &quot;, RawPointer(frame), &quot;}&quot;);
</del><ins>+        out.print(&quot;tail-packet:{frame = &quot;, RawPointer(frame), &quot;}&quot;);
</ins><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx">     
</span><span class="lines">@@ -271,15 +271,36 @@
</span><span class="cx">                 // https://bugs.webkit.org/show_bug.cgi?id=155686
</span><span class="cx">                 return StackVisitor::Continue;
</span><span class="cx">             }
</span><ins>+
</ins><span class="cx">             CallFrame* callFrame = visitor-&gt;callFrame();
</span><span class="cx">             if (verbose)
</span><span class="cx">                 dataLog(&quot;    Examining &quot;, RawPointer(callFrame), &quot;\n&quot;);
</span><del>-            if (!toPush.isEmpty() &amp;&amp; indexInLog &lt; logCursorIndex
</del><ins>+            if (callFrame == highestPointSinceLastTime) {
+                if (verbose)
+                    dataLog(&quot;    Bailing at &quot;, RawPointer(callFrame), &quot; because it's the highest point since last time.\n&quot;);
+                return StackVisitor::Done;
+            }
+
+            bool foundFrame = advanceIndexInLogTo(callFrame, callFrame-&gt;callee(), callFrame-&gt;callerFrame());
+            bool isTailDeleted = false;
+            JSScope* scope = nullptr;
+            CodeBlock* codeBlock = callFrame-&gt;codeBlock();
+            if (codeBlock &amp;&amp; codeBlock-&gt;wasCompiledWithDebuggingOpcodes() &amp;&amp; codeBlock-&gt;scopeRegister().isValid()) {
+                scope = callFrame-&gt;scope(codeBlock-&gt;scopeRegister().offset());
+                RELEASE_ASSERT(scope-&gt;inherits(JSScope::info()));
+            } else if (foundFrame) {
+                scope = m_log[indexInLog].scope;
+                if (scope)
+                    RELEASE_ASSERT(scope-&gt;inherits(JSScope::info()));
+            }
+            toPush.append(Frame(visitor-&gt;callee(), callFrame, isTailDeleted, callFrame-&gt;thisValue(), scope, codeBlock, callFrame-&gt;callSiteIndex()));
+
+            if (indexInLog &lt; logCursorIndex
</ins><span class="cx">                 // This condition protects us from the case where advanceIndexInLogTo didn't find
</span><span class="cx">                 // anything.
</span><span class="cx">                 &amp;&amp; m_log[indexInLog].frame == toPush.last().frame) {
</span><span class="cx">                 if (verbose)
</span><del>-                    dataLog(&quot;    Going to loop through things with indexInLog = &quot;, indexInLog, &quot; and push-stack top = &quot;, toPush.last(), &quot;\n&quot;);
</del><ins>+                    dataLog(&quot;    Going to loop through to find tail deleted frames with indexInLog = &quot;, indexInLog, &quot; and push-stack top = &quot;, toPush.last(), &quot;\n&quot;);
</ins><span class="cx">                 for (;;) {
</span><span class="cx">                     ASSERT(m_log[indexInLog].frame == toPush.last().frame);
</span><span class="cx">                     
</span><span class="lines">@@ -303,6 +324,8 @@
</span><span class="cx">                     indexInLog--; // Skip over the tail packet.
</span><span class="cx">                     
</span><span class="cx">                     if (!advanceIndexInLogTo(tailPacket.frame, nullptr, nullptr)) {
</span><ins>+                        if (verbose)
+                            dataLog(&quot;Can't find prologue packet for tail: &quot;, RawPointer(tailPacket.frame), &quot;\n&quot;);
</ins><span class="cx">                         // We were unable to locate the prologue packet for this tail packet.
</span><span class="cx">                         // This is rare but can happen in a situation like:
</span><span class="cx">                         // function foo() {
</span><span class="lines">@@ -317,24 +340,7 @@
</span><span class="cx">                     toPush.append(Frame(packet.callee, packet.frame, isTailDeleted, tailPacket.thisValue, tailPacket.scope, tailPacket.codeBlock, tailPacket.callSiteIndex));
</span><span class="cx">                 }
</span><span class="cx">             }
</span><del>-            if (callFrame == highestPointSinceLastTime) {
-                if (verbose)
-                    dataLog(&quot;    Bailing at &quot;, RawPointer(callFrame), &quot; because it's the highest point since last time.\n&quot;);
-                return StackVisitor::Done;
-            }
-            bool foundFrame = advanceIndexInLogTo(callFrame, callFrame-&gt;callee(), callFrame-&gt;callerFrame());
-            bool isTailDeleted = false;
-            JSScope* scope = nullptr;
-            CodeBlock* codeBlock = callFrame-&gt;codeBlock();
-            if (codeBlock &amp;&amp; codeBlock-&gt;wasCompiledWithDebuggingOpcodes() &amp;&amp; codeBlock-&gt;scopeRegister().isValid()) {
-                scope = callFrame-&gt;scope(codeBlock-&gt;scopeRegister().offset());
-                RELEASE_ASSERT(scope-&gt;inherits(JSScope::info()));
-            } else if (foundFrame) {
-                scope = m_log[indexInLog].scope;
-                if (scope)
-                    RELEASE_ASSERT(scope-&gt;inherits(JSScope::info()));
-            }
-            toPush.append(Frame(visitor-&gt;callee(), callFrame, isTailDeleted, callFrame-&gt;thisValue(), scope, codeBlock, callFrame-&gt;callSiteIndex()));
</del><ins>+
</ins><span class="cx">             return StackVisitor::Continue;
</span><span class="cx">         });
</span><span class="cx"> 
</span><span class="lines">@@ -421,8 +427,9 @@
</span><span class="cx">     
</span><span class="cx">     CommaPrinter comma;
</span><span class="cx">     unsigned limit = static_cast&lt;unsigned&gt;(m_logCursor - m_log);
</span><ins>+    out.print(&quot;\n&quot;);
</ins><span class="cx">     for (unsigned i = 0; i &lt; limit; ++i)
</span><del>-        out.print(comma, m_log[i]);
</del><ins>+        out.print(&quot;\t&quot;, comma, m_log[i], &quot;\n&quot;);
</ins><span class="cx">     out.print(&quot;]}&quot;);
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>