"use strict" // Trivial cases: everything is monomorphic and super predictable. function compareConstants() { return (null == null) && (null == undefined) && (undefined == null); } noInline(compareConstants); for (let i = 0; i < 1e4; ++i) { if (!compareConstants()) throw "Failed to compareConstants()."; } function opaqueNull() { return null; } noInline(opaqueNull); function opaqueUndefined() { return undefined; } noInline(opaqueUndefined); function compareConstantsAndDynamicValues() { return ((null == opaqueNull()) && (opaqueNull() == null) && (undefined == opaqueNull()) && (opaqueNull() == undefined) && (null == opaqueUndefined()) && (opaqueUndefined() == null) && (undefined == opaqueUndefined()) && (opaqueUndefined() == undefined)); } noInline(compareConstantsAndDynamicValues); for (let i = 1e4; i--;) { if (!compareConstantsAndDynamicValues()) throw "Failed compareConstantsAndDynamicValues()"; } function compareDynamicValues() { return ((opaqueNull() == opaqueNull()) && (opaqueUndefined() == opaqueUndefined()) && (opaqueNull() == opaqueUndefined()) && (opaqueUndefined() == opaqueNull())); } noInline(compareDynamicValues); for (let i = 0; i < 1e4; ++i) { if (!compareDynamicValues()) throw "Failed compareDynamicValues()"; } function compareDynamicValueToItself() { const value1 = opaqueNull(); const value2 = opaqueUndefined(); return value1 == value1 && value2 == value2; } noInline(compareDynamicValueToItself); for (let i = 0; i < 1e4; ++i) { if (!compareDynamicValueToItself()) throw "Failed compareDynamicValueToItself()"; } // The case that interested us in the first place. // Accessing an array with undecided shape always return undefined. function arrayTesting() { let returnValue = true; const array1 = new Array(2); for (let i = 0; i < 3; ++i) { returnValue = returnValue && (array1[i] == null); returnValue = returnValue && (null == array1[i]); returnValue = returnValue && (array1[i] == undefined); returnValue = returnValue && (undefined == array1[i]); } const array2 = new Array(2); for (let i = 0; i < 2; ++i) { returnValue = returnValue && (array2[i] == opaqueNull()); returnValue = returnValue && (opaqueNull() == array2[i]); returnValue = returnValue && (array2[i] == opaqueUndefined()); returnValue = returnValue && (opaqueUndefined() == array2[i]); } const array3 = new Array(2); for (let i = 0; i < 3; ++i) { returnValue = returnValue && (array3[i] == array3[i]); returnValue = returnValue && (array1[i] == array3[i]); returnValue = returnValue && (array3[i] == array1[i]); returnValue = returnValue && (array2[i] == array3[i]); returnValue = returnValue && (array3[i] == array2[i]); } return returnValue; } noInline(arrayTesting); for (let i = 0; i < 1e4; ++i) { if (!arrayTesting()) throw "Failed arrayTesting()"; } // Let's make it polymorphic after optimization. We should fallback to a generic compare operation. function opaqueCompare1(a, b) { return a == b; } noInline(opaqueCompare1); function testNullComparatorUpdate() { for (let i = 0; i < 1e4; ++i) { if (!opaqueCompare1(null, null)) throw "Failed opaqueCompare1(null, null)" } // Let's change types for (let i = 0; i < 1e4; ++i) { if (opaqueCompare1("foo", null)) throw "Failed opaqueCompare1(\"foo\", null)" } } testNullComparatorUpdate(); function opaqueCompare2(a, b) { return a == b; } noInline(opaqueCompare2); function testUndefinedComparatorUpdate() { for (let i = 0; i < 1e4; ++i) { if (!opaqueCompare2(undefined, undefined)) throw "Failed opaqueCompare2(undefined, undefined)" } // Let's change types for (let i = 0; i < 1e4; ++i) { if (!opaqueCompare2("bar", "bar")) throw "Failed opaqueCompare2(\"bar\", \"bar\")" } } testUndefinedComparatorUpdate(); function opaqueCompare3(a, b) { return a == b; } noInline(opaqueCompare3); function testNullAndUndefinedComparatorUpdate() { for (let i = 0; i < 1e4; ++i) { if (!opaqueCompare3(undefined, null) || !opaqueCompare2(null, undefined)) throw "Failed opaqueCompare2(undefined/null, undefined/null)" } // Let's change types for (let i = 0; i < 1e4; ++i) { if (opaqueCompare3(undefined, "bar")) throw "Failed opaqueCompare3(undefined, \"bar\")" } } testNullAndUndefinedComparatorUpdate();