<!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>[198770] trunk/Source/JavaScriptCore</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/198770">198770</a></dd>
<dt>Author</dt> <dd>commit-queue@webkit.org</dd>
<dt>Date</dt> <dd>2016-03-28 17:59:07 -0700 (Mon, 28 Mar 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>[JSC] ArithSub should not propagate &quot;UsesAsOther&quot;
https://bugs.webkit.org/show_bug.cgi?id=155932

Patch by Benjamin Poulain &lt;bpoulain@apple.com&gt; on 2016-03-28
Reviewed by Mark Lam.

The node ArithSub was backpropagating UsesAsOther.
This causes any GetByVal on a Double Array to have an extra
hole check if it flows into an ArithSub.

The definition of ArithSub (12.8.4.1) has both operands go
through ToNumber(). ToNumber() on &quot;undefined&quot; always produces
NaN. It is safe to ignore the NaN marker from hole when
the DAG flows into ArithSub.

This patch also adds this change and test coverage to ArithAdd.
ArithAdd was not a problem in practice because it is only
generated before Fixup if both operands are known to be numerical.
The change to ArithAdd is there to protect us of the ArithSub-like
problems if we ever improve our support of arithmetic operators.

* dfg/DFGBackwardsPropagationPhase.cpp:
(JSC::DFG::BackwardsPropagationPhase::propagate):
* tests/stress/arith-add-on-double-array-with-holes.js: Added.
(let.testCase.of.testCases.eval.nonObservableHoleOnLhs):
(let.testCase.of.testCases.observableHoleOnLhs):
(let.testCase.of.testCases.nonObservableHoleOnRhs):
(let.testCase.of.testCases.observableHoleOnRhs):
* tests/stress/arith-sub-on-double-array-with-holes.js: Added.
(let.testCase.of.testCases.eval.nonObservableHoleOnLhs):
(let.testCase.of.testCases.observableHoleOnLhs):
(let.testCase.of.testCases.nonObservableHoleOnRhs):
(let.testCase.of.testCases.observableHoleOnRhs):
* tests/stress/value-add-on-double-array-with-holes.js: Added.
(let.testCase.of.testCases.eval.nonObservableHoleOnLhs):
(let.testCase.of.testCases.observableHoleOnLhs):
(let.testCase.of.testCases.nonObservableHoleOnRhs):
(let.testCase.of.testCases.observableHoleOnRhs):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGBackwardsPropagationPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoretestsstressarithaddondoublearraywithholesjs">trunk/Source/JavaScriptCore/tests/stress/arith-add-on-double-array-with-holes.js</a></li>
<li><a href="#trunkSourceJavaScriptCoretestsstressarithsubondoublearraywithholesjs">trunk/Source/JavaScriptCore/tests/stress/arith-sub-on-double-array-with-holes.js</a></li>
<li><a href="#trunkSourceJavaScriptCoretestsstressvalueaddondoublearraywithholesjs">trunk/Source/JavaScriptCore/tests/stress/value-add-on-double-array-with-holes.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (198769 => 198770)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2016-03-29 00:58:23 UTC (rev 198769)
+++ trunk/Source/JavaScriptCore/ChangeLog        2016-03-29 00:59:07 UTC (rev 198770)
</span><span class="lines">@@ -1,3 +1,43 @@
</span><ins>+2016-03-28  Benjamin Poulain  &lt;bpoulain@apple.com&gt;
+
+        [JSC] ArithSub should not propagate &quot;UsesAsOther&quot;
+        https://bugs.webkit.org/show_bug.cgi?id=155932
+
+        Reviewed by Mark Lam.
+
+        The node ArithSub was backpropagating UsesAsOther.
+        This causes any GetByVal on a Double Array to have an extra
+        hole check if it flows into an ArithSub.
+
+        The definition of ArithSub (12.8.4.1) has both operands go
+        through ToNumber(). ToNumber() on &quot;undefined&quot; always produces
+        NaN. It is safe to ignore the NaN marker from hole when
+        the DAG flows into ArithSub.
+
+        This patch also adds this change and test coverage to ArithAdd.
+        ArithAdd was not a problem in practice because it is only
+        generated before Fixup if both operands are known to be numerical.
+        The change to ArithAdd is there to protect us of the ArithSub-like
+        problems if we ever improve our support of arithmetic operators.
+
+        * dfg/DFGBackwardsPropagationPhase.cpp:
+        (JSC::DFG::BackwardsPropagationPhase::propagate):
+        * tests/stress/arith-add-on-double-array-with-holes.js: Added.
+        (let.testCase.of.testCases.eval.nonObservableHoleOnLhs):
+        (let.testCase.of.testCases.observableHoleOnLhs):
+        (let.testCase.of.testCases.nonObservableHoleOnRhs):
+        (let.testCase.of.testCases.observableHoleOnRhs):
+        * tests/stress/arith-sub-on-double-array-with-holes.js: Added.
+        (let.testCase.of.testCases.eval.nonObservableHoleOnLhs):
+        (let.testCase.of.testCases.observableHoleOnLhs):
+        (let.testCase.of.testCases.nonObservableHoleOnRhs):
+        (let.testCase.of.testCases.observableHoleOnRhs):
+        * tests/stress/value-add-on-double-array-with-holes.js: Added.
+        (let.testCase.of.testCases.eval.nonObservableHoleOnLhs):
+        (let.testCase.of.testCases.observableHoleOnLhs):
+        (let.testCase.of.testCases.nonObservableHoleOnRhs):
+        (let.testCase.of.testCases.observableHoleOnRhs):
+
</ins><span class="cx"> 2016-03-28  Brian Burg  &lt;bburg@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Web Inspector: protocol generator should generate C++ string-to-enum helper functions
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGBackwardsPropagationPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp (198769 => 198770)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp        2016-03-29 00:58:23 UTC (rev 198769)
+++ trunk/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp        2016-03-29 00:59:07 UTC (rev 198770)
</span><span class="lines">@@ -248,6 +248,7 @@
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         case ArithAdd: {
</span><ins>+            flags &amp;= ~NodeBytecodeUsesAsOther;
</ins><span class="cx">             if (isNotNegZero(node-&gt;child1().node()) || isNotNegZero(node-&gt;child2().node()))
</span><span class="cx">                 flags &amp;= ~NodeBytecodeNeedsNegZero;
</span><span class="cx">             if (!isWithinPowerOfTwo&lt;32&gt;(node-&gt;child1()) &amp;&amp; !isWithinPowerOfTwo&lt;32&gt;(node-&gt;child2()))
</span><span class="lines">@@ -268,6 +269,7 @@
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         case ArithSub: {
</span><ins>+            flags &amp;= ~NodeBytecodeUsesAsOther;
</ins><span class="cx">             if (isNotNegZero(node-&gt;child1().node()) || isNotPosZero(node-&gt;child2().node()))
</span><span class="cx">                 flags &amp;= ~NodeBytecodeNeedsNegZero;
</span><span class="cx">             if (!isWithinPowerOfTwo&lt;32&gt;(node-&gt;child1()) &amp;&amp; !isWithinPowerOfTwo&lt;32&gt;(node-&gt;child2()))
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstressarithaddondoublearraywithholesjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/arith-add-on-double-array-with-holes.js (0 => 198770)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/arith-add-on-double-array-with-holes.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/arith-add-on-double-array-with-holes.js        2016-03-29 00:59:07 UTC (rev 198770)
</span><span class="lines">@@ -0,0 +1,100 @@
</span><ins>+let testCases = [
+    // Numbers
+    ['1', NaN, NaN, 2, 2],
+    ['1.5', NaN, NaN, 1 + 1.5, 1 + 1.5],
+    [NaN, NaN, NaN, NaN, NaN],
+
+    // Strings.
+    ['&quot;&quot;', NaN, NaN, 1, 1],
+    ['new String()', NaN, NaN, 1, 1],
+    ['&quot;WebKit!&quot;', NaN, NaN, NaN, NaN],
+
+    // Objects.
+    ['{ }', NaN, NaN, NaN, NaN],
+    ['{ foo: 1 }', NaN, NaN, NaN, NaN],
+    ['{ toString: function() { return &quot;&quot;; } }', NaN, NaN, 1, 1],
+    ['{ toString: function() { return &quot;WebKit&quot;; } }', NaN, NaN, NaN, NaN],
+
+    // Others.
+    ['null', NaN, NaN, 1, 1],
+    ['undefined', NaN, NaN, NaN, NaN]
+];
+
+for (let testCase of testCases) {
+    let otherOperand = testCase[0];
+    let expectedLeftValueWithHole = testCase[1];
+    let expectedRightValueWithHole = testCase[2];
+    let expectedLeftValue = testCase[3];
+    let expectedRightValue = testCase[4];
+    eval(
+        `// Those holes are not observable by arithmetic operation.
+        // The return value is always going to be NaN.
+        function nonObservableHoleOnLhs(array, otherValue) {
+            return Math.min(array[0]) + Math.min(otherValue);
+        }
+        noInline(nonObservableHoleOnLhs);
+
+        function observableHoleOnLhs(array, otherValue) {
+            let value = array[0];
+            return [Math.min(value) + Math.min(otherValue), value];
+        }
+        noInline(observableHoleOnLhs);
+
+        function nonObservableHoleOnRhs(array, otherValue) {
+            return Math.min(otherValue) + Math.min(array[0]);
+        }
+        noInline(nonObservableHoleOnRhs);
+
+        function observableHoleOnRhs(array, otherValue) {
+            let value = array[0];
+            return [Math.min(otherValue) + Math.min(value), value];
+        }
+        noInline(observableHoleOnRhs);
+
+        let testArray = new Array;
+        for (let i = 1; i &lt; 3; ++i) {
+            testArray[i] = i + 0.5
+        }
+
+        let isEqual = function(a, b) {
+            if (a === a) {
+                return a === b;
+            }
+            return b !== b;
+        }
+
+        for (let i = 0; i &lt; 1e4; ++i) {
+            let lhsResult1 = nonObservableHoleOnLhs(testArray, ${otherOperand});
+            if (!isEqual(lhsResult1, ${expectedLeftValueWithHole}))
+                throw &quot;Error on nonObservableHoleOnLhs at i = &quot; + i + &quot; with operand &quot; + ${otherOperand} + &quot; expected &quot; + ${expectedLeftValueWithHole} + &quot; got &quot; + lhsResult1;
+            let lhsResult2 = observableHoleOnLhs(testArray, ${otherOperand});
+            if (!isEqual(lhsResult2[0], ${expectedLeftValueWithHole}) || lhsResult2[1] !== undefined)
+                throw &quot;Error on observableHoleOnLhs at i = &quot; + i;
+
+            let rhsResult1 = nonObservableHoleOnRhs(testArray, ${otherOperand});
+            if (!isEqual(rhsResult1, ${expectedRightValueWithHole}))
+                throw &quot;Error on nonObservableHoleOnRhs at i = &quot; + i + &quot; with operand &quot; + ${otherOperand} + &quot; expected &quot; + ${expectedRightValueWithHole} + &quot; got &quot; + rhsResult1;
+            let rhsResult2 = observableHoleOnRhs(testArray, ${otherOperand});
+            if (!isEqual(rhsResult2[0], ${expectedRightValueWithHole}) || rhsResult2[1] !== undefined)
+                throw &quot;Error on observableHoleOnRhs at i = &quot; + i;
+        }
+
+        // Fill the hole, make sure everything still work correctly.
+        testArray[0] = 1.;
+        for (let i = 0; i &lt; 1e4; ++i) {
+            let lhsResult1 = nonObservableHoleOnLhs(testArray, ${otherOperand});
+            if (!isEqual(lhsResult1, ${expectedLeftValue}))
+                throw &quot;Error on non hole nonObservableHoleOnLhs at i = &quot; + i + &quot; expected &quot; + ${expectedLeftValue} + &quot; got &quot; + lhsResult1;
+            let lhsResult2 = observableHoleOnLhs(testArray, ${otherOperand});
+            if (!isEqual(lhsResult2[0], ${expectedLeftValue}) || lhsResult2[1] !== 1)
+                throw &quot;Error on non hole observableHoleOnLhs at i = &quot; + i + &quot; expected &quot; + ${expectedLeftValue} + &quot; got &quot; + lhsResult2[0];
+
+            let rhsResult1 = nonObservableHoleOnRhs(testArray, ${otherOperand});
+            if (!isEqual(rhsResult1, ${expectedRightValue}))
+                throw &quot;Error on non hole nonObservableHoleOnRhs at i = &quot; + i + &quot; with operand &quot; + ${otherOperand} + &quot; expected &quot; + ${expectedRightValue} + &quot; got &quot; + rhsResult1;
+            let rhsResult2 = observableHoleOnRhs(testArray, ${otherOperand});
+            if (!isEqual(rhsResult2[0], ${expectedRightValue}) || rhsResult2[1] !== 1)
+                throw &quot;Error on non hole observableHoleOnRhs at i = &quot; + i;
+        }`
+    );
+}
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstressarithsubondoublearraywithholesjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/arith-sub-on-double-array-with-holes.js (0 => 198770)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/arith-sub-on-double-array-with-holes.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/arith-sub-on-double-array-with-holes.js        2016-03-29 00:59:07 UTC (rev 198770)
</span><span class="lines">@@ -0,0 +1,98 @@
</span><ins>+let testCases = [
+    // Numbers
+    ['1', 0, 0],
+    ['1.5', 1 - 1.5, 1.5 - 1],
+    [NaN, NaN, NaN],
+
+    // Strings.
+    ['&quot;&quot;', 1, -1],
+    ['new String()', 1, -1],
+    ['&quot;WebKit!&quot;', NaN, NaN],
+
+    // Objects.
+    ['{ }', NaN, NaN],
+    ['{ foo: 1 }', NaN, NaN],
+    ['{ toString: function() { return &quot;&quot;; } }', 1, -1],
+    ['{ toString: function() { return &quot;WebKit&quot;; } }', NaN, NaN],
+
+    // Others.
+    ['null', 1, -1],
+    ['undefined', NaN, NaN]
+];
+
+for (let testCase of testCases) {
+    let otherOperand = testCase[0];
+    let expectedLeftValue = testCase[1];
+    let expectedRightValue = testCase[2];
+    eval(
+        `// Those holes are not observable by arithmetic operation.
+        // The return value is always going to be NaN.
+        function nonObservableHoleOnLhs(array, otherValue) {
+            return array[0] - otherValue;
+        }
+        noInline(nonObservableHoleOnLhs);
+
+        function observableHoleOnLhs(array, otherValue) {
+            let value = array[0];
+            return [value - otherValue, value];
+        }
+        noInline(observableHoleOnLhs);
+
+        function nonObservableHoleOnRhs(array, otherValue) {
+            return otherValue - array[0];
+        }
+        noInline(nonObservableHoleOnRhs);
+
+        function observableHoleOnRhs(array, otherValue) {
+            let value = array[0];
+            return [otherValue - value, value];
+        }
+        noInline(observableHoleOnRhs);
+
+        let testArray = new Array;
+        for (let i = 1; i &lt; 3; ++i) {
+            testArray[i] = i + 0.5
+        }
+
+        for (let i = 0; i &lt; 1e4; ++i) {
+            let lhsResult1 = nonObservableHoleOnLhs(testArray, ${otherOperand});
+            if (lhsResult1 == lhsResult1)
+                throw &quot;Error on nonObservableHoleOnLhs at i = &quot; + i;
+            let lhsResult2 = observableHoleOnLhs(testArray, ${otherOperand});
+            if (lhsResult2[0] == lhsResult2[0] || lhsResult2[1] !== undefined)
+                throw &quot;Error on observableHoleOnLhs at i = &quot; + i;
+
+            let rhsResult1 = nonObservableHoleOnRhs(testArray, ${otherOperand});
+            if (rhsResult1 == rhsResult1)
+                throw &quot;Error on nonObservableHoleOnRhs at i = &quot; + i;
+            let rhsResult2 = observableHoleOnRhs(testArray, ${otherOperand});
+            if (rhsResult2[0] == rhsResult2[0] || rhsResult2[1] !== undefined)
+                throw &quot;Error on observableHoleOnRhs at i = &quot; + i;
+        }
+
+        let isEqual = function(a, b) {
+            if (a === a) {
+                return a === b;
+            }
+            return b !== b;
+        }
+
+        // Fill the hole, make sure everything still work correctly.
+        testArray[0] = 1.;
+        for (let i = 0; i &lt; 1e4; ++i) {
+            let lhsResult1 = nonObservableHoleOnLhs(testArray, ${otherOperand});
+            if (!isEqual(lhsResult1, ${expectedLeftValue}))
+                throw &quot;Error on non hole nonObservableHoleOnLhs at i = &quot; + i + &quot; expected &quot; + ${expectedLeftValue} + &quot; got &quot; + lhsResult1;
+            let lhsResult2 = observableHoleOnLhs(testArray, ${otherOperand});
+            if (!isEqual(lhsResult2[0], ${expectedLeftValue}) || lhsResult2[1] !== 1)
+                throw &quot;Error on non hole observableHoleOnLhs at i = &quot; + i + &quot; expected &quot; + ${expectedLeftValue} + &quot; got &quot; + lhsResult2[0];
+
+            let rhsResult1 = nonObservableHoleOnRhs(testArray, ${otherOperand});
+            if (!isEqual(rhsResult1, ${expectedRightValue}))
+                throw &quot;Error on non hole nonObservableHoleOnRhs at i = &quot; + i + &quot; expected &quot; + ${expectedRightValue} + &quot; got &quot; + rhsResult1;
+            let rhsResult2 = observableHoleOnRhs(testArray, ${otherOperand});
+            if (!isEqual(rhsResult2[0], ${expectedRightValue}) || rhsResult2[1] !== 1)
+                throw &quot;Error on non hole observableHoleOnRhs at i = &quot; + i;
+        }`
+    );
+}
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstressvalueaddondoublearraywithholesjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/value-add-on-double-array-with-holes.js (0 => 198770)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/value-add-on-double-array-with-holes.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/value-add-on-double-array-with-holes.js        2016-03-29 00:59:07 UTC (rev 198770)
</span><span class="lines">@@ -0,0 +1,100 @@
</span><ins>+let testCases = [
+    // Numbers
+    ['1', NaN, NaN, 2, 2],
+    ['1.5', NaN, NaN, 1 + 1.5, 1 + 1.5],
+    [NaN, NaN, NaN, NaN, NaN],
+
+    // Strings.
+    ['&quot;&quot;', '&quot;undefined&quot;', '&quot;undefined&quot;', '&quot;1&quot;', '&quot;1&quot;'],
+    ['new String()', '&quot;undefined&quot;', '&quot;undefined&quot;', '&quot;1&quot;', '&quot;1&quot;'],
+    ['&quot;WebKit!&quot;', '&quot;undefinedWebKit!&quot;', '&quot;WebKit!undefined&quot;', '&quot;1WebKit!&quot;', '&quot;WebKit!1&quot;'],
+
+    // Objects.
+    ['{ }', '&quot;undefined[object Object]&quot;', '&quot;[object Object]undefined&quot;', '&quot;1[object Object]&quot;', '&quot;[object Object]1&quot;'],
+    ['{ foo: 1 }', '&quot;undefined[object Object]&quot;', '&quot;[object Object]undefined&quot;', '&quot;1[object Object]&quot;', '&quot;[object Object]1&quot;'],
+    ['{ toString: function() { return &quot;&quot;; } }', '&quot;undefined&quot;', '&quot;undefined&quot;', '&quot;1&quot;', '&quot;1&quot;'],
+    ['{ toString: function() { return &quot;WebKit&quot;; } }', '&quot;undefinedWebKit&quot;', '&quot;WebKitundefined&quot;', '&quot;1WebKit&quot;', '&quot;WebKit1&quot;'],
+
+    // Others.
+    ['null', NaN, NaN, 1, 1],
+    ['undefined', NaN, NaN, NaN, NaN]
+];
+
+for (let testCase of testCases) {
+    let otherOperand = testCase[0];
+    let expectedLeftValueWithHole = testCase[1];
+    let expectedRightValueWithHole = testCase[2];
+    let expectedLeftValue = testCase[3];
+    let expectedRightValue = testCase[4];
+    eval(
+        `// Those holes are not observable by arithmetic operation.
+        // The return value is always going to be NaN.
+        function nonObservableHoleOnLhs(array, otherValue) {
+            return array[0] + otherValue;
+        }
+        noInline(nonObservableHoleOnLhs);
+
+        function observableHoleOnLhs(array, otherValue) {
+            let value = array[0];
+            return [value + otherValue, value];
+        }
+        noInline(observableHoleOnLhs);
+
+        function nonObservableHoleOnRhs(array, otherValue) {
+            return otherValue + array[0];
+        }
+        noInline(nonObservableHoleOnRhs);
+
+        function observableHoleOnRhs(array, otherValue) {
+            let value = array[0];
+            return [otherValue + value, value];
+        }
+        noInline(observableHoleOnRhs);
+
+        let testArray = new Array;
+        for (let i = 1; i &lt; 3; ++i) {
+            testArray[i] = i + 0.5
+        }
+
+        let isEqual = function(a, b) {
+            if (a === a) {
+                return a === b;
+            }
+            return b !== b;
+        }
+
+        for (let i = 0; i &lt; 1e4; ++i) {
+            let lhsResult1 = nonObservableHoleOnLhs(testArray, ${otherOperand});
+            if (!isEqual(lhsResult1, ${expectedLeftValueWithHole}))
+                throw &quot;Error on nonObservableHoleOnLhs at i = &quot; + i + &quot; with operand &quot; + ${otherOperand} + &quot; expected &quot; + ${expectedLeftValueWithHole} + &quot; got &quot; + lhsResult1;
+            let lhsResult2 = observableHoleOnLhs(testArray, ${otherOperand});
+            if (!isEqual(lhsResult2[0], ${expectedLeftValueWithHole}) || lhsResult2[1] !== undefined)
+                throw &quot;Error on observableHoleOnLhs at i = &quot; + i;
+
+            let rhsResult1 = nonObservableHoleOnRhs(testArray, ${otherOperand});
+            if (!isEqual(rhsResult1, ${expectedRightValueWithHole}))
+                throw &quot;Error on nonObservableHoleOnRhs at i = &quot; + i + &quot; with operand &quot; + ${otherOperand} + &quot; expected &quot; + ${expectedRightValueWithHole} + &quot; got &quot; + rhsResult1;
+            let rhsResult2 = observableHoleOnRhs(testArray, ${otherOperand});
+            if (!isEqual(rhsResult2[0], ${expectedRightValueWithHole}) || rhsResult2[1] !== undefined)
+                throw &quot;Error on observableHoleOnRhs at i = &quot; + i;
+        }
+
+        // Fill the hole, make sure everything still work correctly.
+        testArray[0] = 1.;
+        for (let i = 0; i &lt; 1e4; ++i) {
+            let lhsResult1 = nonObservableHoleOnLhs(testArray, ${otherOperand});
+            if (!isEqual(lhsResult1, ${expectedLeftValue}))
+                throw &quot;Error on non hole nonObservableHoleOnLhs at i = &quot; + i + &quot; expected &quot; + ${expectedLeftValue} + &quot; got &quot; + lhsResult1;
+            let lhsResult2 = observableHoleOnLhs(testArray, ${otherOperand});
+            if (!isEqual(lhsResult2[0], ${expectedLeftValue}) || lhsResult2[1] !== 1)
+                throw &quot;Error on non hole observableHoleOnLhs at i = &quot; + i + &quot; expected &quot; + ${expectedLeftValue} + &quot; got &quot; + lhsResult2[0];
+
+            let rhsResult1 = nonObservableHoleOnRhs(testArray, ${otherOperand});
+            if (!isEqual(rhsResult1, ${expectedRightValue}))
+                throw &quot;Error on non hole nonObservableHoleOnRhs at i = &quot; + i + &quot; with operand &quot; + ${otherOperand} + &quot; expected &quot; + ${expectedRightValue} + &quot; got &quot; + rhsResult1;
+            let rhsResult2 = observableHoleOnRhs(testArray, ${otherOperand});
+            if (!isEqual(rhsResult2[0], ${expectedRightValue}) || rhsResult2[1] !== 1)
+                throw &quot;Error on non hole observableHoleOnRhs at i = &quot; + i;
+        }`
+    );
+}
</ins><span class="cx">\ No newline at end of file
</span></span></pre>
</div>
</div>

</body>
</html>