Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (198769 => 198770)
--- trunk/Source/_javascript_Core/ChangeLog 2016-03-29 00:58:23 UTC (rev 198769)
+++ trunk/Source/_javascript_Core/ChangeLog 2016-03-29 00:59:07 UTC (rev 198770)
@@ -1,3 +1,43 @@
+2016-03-28 Benjamin Poulain <bpoul...@apple.com>
+
+ [JSC] ArithSub should not propagate "UsesAsOther"
+ 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 "undefined" 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):
+
2016-03-28 Brian Burg <bb...@apple.com>
Web Inspector: protocol generator should generate C++ string-to-enum helper functions
Modified: trunk/Source/_javascript_Core/dfg/DFGBackwardsPropagationPhase.cpp (198769 => 198770)
--- trunk/Source/_javascript_Core/dfg/DFGBackwardsPropagationPhase.cpp 2016-03-29 00:58:23 UTC (rev 198769)
+++ trunk/Source/_javascript_Core/dfg/DFGBackwardsPropagationPhase.cpp 2016-03-29 00:59:07 UTC (rev 198770)
@@ -248,6 +248,7 @@
}
case ArithAdd: {
+ flags &= ~NodeBytecodeUsesAsOther;
if (isNotNegZero(node->child1().node()) || isNotNegZero(node->child2().node()))
flags &= ~NodeBytecodeNeedsNegZero;
if (!isWithinPowerOfTwo<32>(node->child1()) && !isWithinPowerOfTwo<32>(node->child2()))
@@ -268,6 +269,7 @@
}
case ArithSub: {
+ flags &= ~NodeBytecodeUsesAsOther;
if (isNotNegZero(node->child1().node()) || isNotPosZero(node->child2().node()))
flags &= ~NodeBytecodeNeedsNegZero;
if (!isWithinPowerOfTwo<32>(node->child1()) && !isWithinPowerOfTwo<32>(node->child2()))
Added: trunk/Source/_javascript_Core/tests/stress/arith-add-on-double-array-with-holes.js (0 => 198770)
--- trunk/Source/_javascript_Core/tests/stress/arith-add-on-double-array-with-holes.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/arith-add-on-double-array-with-holes.js 2016-03-29 00:59:07 UTC (rev 198770)
@@ -0,0 +1,100 @@
+let testCases = [
+ // Numbers
+ ['1', NaN, NaN, 2, 2],
+ ['1.5', NaN, NaN, 1 + 1.5, 1 + 1.5],
+ [NaN, NaN, NaN, NaN, NaN],
+
+ // Strings.
+ ['""', NaN, NaN, 1, 1],
+ ['new String()', NaN, NaN, 1, 1],
+ ['"WebKit!"', NaN, NaN, NaN, NaN],
+
+ // Objects.
+ ['{ }', NaN, NaN, NaN, NaN],
+ ['{ foo: 1 }', NaN, NaN, NaN, NaN],
+ ['{ toString: function() { return ""; } }', NaN, NaN, 1, 1],
+ ['{ toString: function() { return "WebKit"; } }', 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 < 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 < 1e4; ++i) {
+ let lhsResult1 = nonObservableHoleOnLhs(testArray, ${otherOperand});
+ if (!isEqual(lhsResult1, ${expectedLeftValueWithHole}))
+ throw "Error on nonObservableHoleOnLhs at i = " + i + " with operand " + ${otherOperand} + " expected " + ${expectedLeftValueWithHole} + " got " + lhsResult1;
+ let lhsResult2 = observableHoleOnLhs(testArray, ${otherOperand});
+ if (!isEqual(lhsResult2[0], ${expectedLeftValueWithHole}) || lhsResult2[1] !== undefined)
+ throw "Error on observableHoleOnLhs at i = " + i;
+
+ let rhsResult1 = nonObservableHoleOnRhs(testArray, ${otherOperand});
+ if (!isEqual(rhsResult1, ${expectedRightValueWithHole}))
+ throw "Error on nonObservableHoleOnRhs at i = " + i + " with operand " + ${otherOperand} + " expected " + ${expectedRightValueWithHole} + " got " + rhsResult1;
+ let rhsResult2 = observableHoleOnRhs(testArray, ${otherOperand});
+ if (!isEqual(rhsResult2[0], ${expectedRightValueWithHole}) || rhsResult2[1] !== undefined)
+ throw "Error on observableHoleOnRhs at i = " + i;
+ }
+
+ // Fill the hole, make sure everything still work correctly.
+ testArray[0] = 1.;
+ for (let i = 0; i < 1e4; ++i) {
+ let lhsResult1 = nonObservableHoleOnLhs(testArray, ${otherOperand});
+ if (!isEqual(lhsResult1, ${expectedLeftValue}))
+ throw "Error on non hole nonObservableHoleOnLhs at i = " + i + " expected " + ${expectedLeftValue} + " got " + lhsResult1;
+ let lhsResult2 = observableHoleOnLhs(testArray, ${otherOperand});
+ if (!isEqual(lhsResult2[0], ${expectedLeftValue}) || lhsResult2[1] !== 1)
+ throw "Error on non hole observableHoleOnLhs at i = " + i + " expected " + ${expectedLeftValue} + " got " + lhsResult2[0];
+
+ let rhsResult1 = nonObservableHoleOnRhs(testArray, ${otherOperand});
+ if (!isEqual(rhsResult1, ${expectedRightValue}))
+ throw "Error on non hole nonObservableHoleOnRhs at i = " + i + " with operand " + ${otherOperand} + " expected " + ${expectedRightValue} + " got " + rhsResult1;
+ let rhsResult2 = observableHoleOnRhs(testArray, ${otherOperand});
+ if (!isEqual(rhsResult2[0], ${expectedRightValue}) || rhsResult2[1] !== 1)
+ throw "Error on non hole observableHoleOnRhs at i = " + i;
+ }`
+ );
+}
\ No newline at end of file
Added: trunk/Source/_javascript_Core/tests/stress/arith-sub-on-double-array-with-holes.js (0 => 198770)
--- trunk/Source/_javascript_Core/tests/stress/arith-sub-on-double-array-with-holes.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/arith-sub-on-double-array-with-holes.js 2016-03-29 00:59:07 UTC (rev 198770)
@@ -0,0 +1,98 @@
+let testCases = [
+ // Numbers
+ ['1', 0, 0],
+ ['1.5', 1 - 1.5, 1.5 - 1],
+ [NaN, NaN, NaN],
+
+ // Strings.
+ ['""', 1, -1],
+ ['new String()', 1, -1],
+ ['"WebKit!"', NaN, NaN],
+
+ // Objects.
+ ['{ }', NaN, NaN],
+ ['{ foo: 1 }', NaN, NaN],
+ ['{ toString: function() { return ""; } }', 1, -1],
+ ['{ toString: function() { return "WebKit"; } }', 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 < 3; ++i) {
+ testArray[i] = i + 0.5
+ }
+
+ for (let i = 0; i < 1e4; ++i) {
+ let lhsResult1 = nonObservableHoleOnLhs(testArray, ${otherOperand});
+ if (lhsResult1 == lhsResult1)
+ throw "Error on nonObservableHoleOnLhs at i = " + i;
+ let lhsResult2 = observableHoleOnLhs(testArray, ${otherOperand});
+ if (lhsResult2[0] == lhsResult2[0] || lhsResult2[1] !== undefined)
+ throw "Error on observableHoleOnLhs at i = " + i;
+
+ let rhsResult1 = nonObservableHoleOnRhs(testArray, ${otherOperand});
+ if (rhsResult1 == rhsResult1)
+ throw "Error on nonObservableHoleOnRhs at i = " + i;
+ let rhsResult2 = observableHoleOnRhs(testArray, ${otherOperand});
+ if (rhsResult2[0] == rhsResult2[0] || rhsResult2[1] !== undefined)
+ throw "Error on observableHoleOnRhs at i = " + 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 < 1e4; ++i) {
+ let lhsResult1 = nonObservableHoleOnLhs(testArray, ${otherOperand});
+ if (!isEqual(lhsResult1, ${expectedLeftValue}))
+ throw "Error on non hole nonObservableHoleOnLhs at i = " + i + " expected " + ${expectedLeftValue} + " got " + lhsResult1;
+ let lhsResult2 = observableHoleOnLhs(testArray, ${otherOperand});
+ if (!isEqual(lhsResult2[0], ${expectedLeftValue}) || lhsResult2[1] !== 1)
+ throw "Error on non hole observableHoleOnLhs at i = " + i + " expected " + ${expectedLeftValue} + " got " + lhsResult2[0];
+
+ let rhsResult1 = nonObservableHoleOnRhs(testArray, ${otherOperand});
+ if (!isEqual(rhsResult1, ${expectedRightValue}))
+ throw "Error on non hole nonObservableHoleOnRhs at i = " + i + " expected " + ${expectedRightValue} + " got " + rhsResult1;
+ let rhsResult2 = observableHoleOnRhs(testArray, ${otherOperand});
+ if (!isEqual(rhsResult2[0], ${expectedRightValue}) || rhsResult2[1] !== 1)
+ throw "Error on non hole observableHoleOnRhs at i = " + i;
+ }`
+ );
+}
\ No newline at end of file
Added: trunk/Source/_javascript_Core/tests/stress/value-add-on-double-array-with-holes.js (0 => 198770)
--- trunk/Source/_javascript_Core/tests/stress/value-add-on-double-array-with-holes.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/value-add-on-double-array-with-holes.js 2016-03-29 00:59:07 UTC (rev 198770)
@@ -0,0 +1,100 @@
+let testCases = [
+ // Numbers
+ ['1', NaN, NaN, 2, 2],
+ ['1.5', NaN, NaN, 1 + 1.5, 1 + 1.5],
+ [NaN, NaN, NaN, NaN, NaN],
+
+ // Strings.
+ ['""', '"undefined"', '"undefined"', '"1"', '"1"'],
+ ['new String()', '"undefined"', '"undefined"', '"1"', '"1"'],
+ ['"WebKit!"', '"undefinedWebKit!"', '"WebKit!undefined"', '"1WebKit!"', '"WebKit!1"'],
+
+ // Objects.
+ ['{ }', '"undefined[object Object]"', '"[object Object]undefined"', '"1[object Object]"', '"[object Object]1"'],
+ ['{ foo: 1 }', '"undefined[object Object]"', '"[object Object]undefined"', '"1[object Object]"', '"[object Object]1"'],
+ ['{ toString: function() { return ""; } }', '"undefined"', '"undefined"', '"1"', '"1"'],
+ ['{ toString: function() { return "WebKit"; } }', '"undefinedWebKit"', '"WebKitundefined"', '"1WebKit"', '"WebKit1"'],
+
+ // 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 < 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 < 1e4; ++i) {
+ let lhsResult1 = nonObservableHoleOnLhs(testArray, ${otherOperand});
+ if (!isEqual(lhsResult1, ${expectedLeftValueWithHole}))
+ throw "Error on nonObservableHoleOnLhs at i = " + i + " with operand " + ${otherOperand} + " expected " + ${expectedLeftValueWithHole} + " got " + lhsResult1;
+ let lhsResult2 = observableHoleOnLhs(testArray, ${otherOperand});
+ if (!isEqual(lhsResult2[0], ${expectedLeftValueWithHole}) || lhsResult2[1] !== undefined)
+ throw "Error on observableHoleOnLhs at i = " + i;
+
+ let rhsResult1 = nonObservableHoleOnRhs(testArray, ${otherOperand});
+ if (!isEqual(rhsResult1, ${expectedRightValueWithHole}))
+ throw "Error on nonObservableHoleOnRhs at i = " + i + " with operand " + ${otherOperand} + " expected " + ${expectedRightValueWithHole} + " got " + rhsResult1;
+ let rhsResult2 = observableHoleOnRhs(testArray, ${otherOperand});
+ if (!isEqual(rhsResult2[0], ${expectedRightValueWithHole}) || rhsResult2[1] !== undefined)
+ throw "Error on observableHoleOnRhs at i = " + i;
+ }
+
+ // Fill the hole, make sure everything still work correctly.
+ testArray[0] = 1.;
+ for (let i = 0; i < 1e4; ++i) {
+ let lhsResult1 = nonObservableHoleOnLhs(testArray, ${otherOperand});
+ if (!isEqual(lhsResult1, ${expectedLeftValue}))
+ throw "Error on non hole nonObservableHoleOnLhs at i = " + i + " expected " + ${expectedLeftValue} + " got " + lhsResult1;
+ let lhsResult2 = observableHoleOnLhs(testArray, ${otherOperand});
+ if (!isEqual(lhsResult2[0], ${expectedLeftValue}) || lhsResult2[1] !== 1)
+ throw "Error on non hole observableHoleOnLhs at i = " + i + " expected " + ${expectedLeftValue} + " got " + lhsResult2[0];
+
+ let rhsResult1 = nonObservableHoleOnRhs(testArray, ${otherOperand});
+ if (!isEqual(rhsResult1, ${expectedRightValue}))
+ throw "Error on non hole nonObservableHoleOnRhs at i = " + i + " with operand " + ${otherOperand} + " expected " + ${expectedRightValue} + " got " + rhsResult1;
+ let rhsResult2 = observableHoleOnRhs(testArray, ${otherOperand});
+ if (!isEqual(rhsResult2[0], ${expectedRightValue}) || rhsResult2[1] !== 1)
+ throw "Error on non hole observableHoleOnRhs at i = " + i;
+ }`
+ );
+}
\ No newline at end of file