Source code
Revision control
Copy as Markdown
Other Tools
const testCasesAnd = [];
const testCasesOr = [];
const testCasesCoalesce = [];
// Assignment to a global variable (JSOp::SetGName).
var globalVar;
function testAnd_GlobalVar(init, value) {
globalVar = init;
return [(globalVar &&= value), globalVar];
}
testCasesAnd.push(testAnd_GlobalVar);
function testOr_GlobalVar(init, value) {
globalVar = init;
return [(globalVar ||= value), globalVar];
}
testCasesOr.push(testOr_GlobalVar);
function testCoalesce_GlobalVar(init, value) {
globalVar = init;
return [(globalVar ??= value), globalVar];
}
testCasesCoalesce.push(testCoalesce_GlobalVar);
// Assignment to a local variable (JSOp::SetLocal).
function testAnd_LocalVar(init, value) {
let v = init;
return [(v &&= value), v];
}
testCasesAnd.push(testAnd_LocalVar);
function testOr_LocalVar(init, value) {
let v = init;
return [(v ||= value), v];
}
testCasesOr.push(testOr_LocalVar);
function testCoalesce_LocalVar(init, value) {
let v = init;
return [(v ??= value), v];
}
testCasesCoalesce.push(testCoalesce_LocalVar);
// Assignment to a parameter (JSOp::SetArg).
function testAnd_Arg(init, value) {
function f(v) {
return [(v &&= value), v];
}
return f(init);
}
testCasesAnd.push(testAnd_Arg);
function testOr_Arg(init, value) {
function f(v) {
return [(v ||= value), v];
}
return f(init);
}
testCasesOr.push(testOr_Arg);
function testCoalesce_Arg(init, value) {
function f(v) {
return [(v ??= value), v];
}
return f(init);
}
testCasesCoalesce.push(testCoalesce_Arg);
// Assignment to a closed over variable (JSOp::SetAliasedVar).
function testAnd_ClosedOver(init, value) {
let v = init;
function f() {
return (v &&= value);
}
return [f(), v];
}
testCasesAnd.push(testAnd_ClosedOver);
function testOr_ClosedOver(init, value) {
let v = init;
function f() {
return (v ||= value);
}
return [f(), v];
}
testCasesOr.push(testOr_ClosedOver);
function testCoalesce_ClosedOver(init, value) {
let v = init;
function f() {
return (v ??= value);
}
return [f(), v];
}
testCasesCoalesce.push(testCoalesce_ClosedOver);
// Assignment to a dynamic name (JSOp::SetName).
function testAnd_DynamicName(init, value) {
eval("var v = init;");
return [(v &&= value), v];
}
testCasesAnd.push(testAnd_DynamicName);
function testOr_DynamicName(init, value) {
eval("var v = init;");
return [(v ||= value), v];
}
testCasesOr.push(testOr_DynamicName);
function testCoalesce_DynamicName(init, value) {
eval("var v = init;");
return [(v ??= value), v];
}
testCasesCoalesce.push(testCoalesce_DynamicName);
// Assignment to a property.
function testAnd_Property(init, value) {
let obj = {p: init};
return [(obj.p &&= value), obj.p];
}
testCasesAnd.push(testAnd_Property);
function testOr_Property(init, value) {
let obj = {p: init};
return [(obj.p ||= value), obj.p];
}
testCasesOr.push(testOr_Property);
function testCoalesce_Property(init, value) {
let obj = {p: init};
return [(obj.p ??= value), obj.p];
}
testCasesCoalesce.push(testCoalesce_Property);
// Assignment to a super property.
function testAnd_SuperProperty(init, value) {
let proto = {p: init};
let obj = {__proto__: proto, m() { return (super.p &&= value); }};
return [obj.m(), obj.p];
}
testCasesAnd.push(testAnd_SuperProperty);
function testOr_SuperProperty(init, value) {
let proto = {p: init};
let obj = {__proto__: proto, m() { return (super.p ||= value); }};
return [obj.m(), obj.p];
}
testCasesOr.push(testOr_SuperProperty);
function testCoalesce_SuperProperty(init, value) {
let proto = {p: init};
let obj = {__proto__: proto, m() { return (super.p ??= value); }};
return [obj.m(), obj.p];
}
testCasesCoalesce.push(testCoalesce_SuperProperty);
// Assignment to an element.
function testAnd_Element(init, value) {
let p = 123;
let obj = {[p]: init};
return [(obj[p] &&= value), obj[p]];
}
testCasesAnd.push(testAnd_Element);
function testOr_Element(init, value) {
let p = 123;
let obj = {[p]: init};
return [(obj[p] ||= value), obj[p]];
}
testCasesOr.push(testOr_Element);
function testCoalesce_Element(init, value) {
let p = 123;
let obj = {[p]: init};
return [(obj[p] ??= value), obj[p]];
}
testCasesCoalesce.push(testCoalesce_Element);
// Assignment to a super element.
function testAnd_SuperElement(init, value) {
let p = 123;
let proto = {[p]: init};
let obj = {__proto__: proto, m() { return (super[p] &&= value); }};
return [obj.m(), obj[p]];
}
testCasesAnd.push(testAnd_SuperElement);
function testOr_SuperElement(init, value) {
let p = 123;
let proto = {[p]: init};
let obj = {__proto__: proto, m() { return (super[p] ||= value); }};
return [obj.m(), obj[p]];
}
testCasesOr.push(testOr_SuperElement);
function testCoalesce_SuperElement(init, value) {
let p = 123;
let proto = {[p]: init};
let obj = {__proto__: proto, m() { return (super[p] ??= value); }};
return [obj.m(), obj[p]];
}
testCasesCoalesce.push(testCoalesce_SuperElement);
// Run the actual tests.
function runTest(testCases, init, value, expected) {
for (let f of testCases) {
let [result, newValue] = f(init, value);
assertEq(result, expected);
assertEq(newValue, expected);
}
}
function testAnd(init, value) {
const expected = init ? value : init;
runTest(testCasesAnd, init, value, expected);
}
function testOr(init, value) {
const expected = !init ? value : init;
runTest(testCasesOr, init, value, expected);
}
function testCoalesce(init, value) {
const expected = init === undefined || init === null ? value : init;
runTest(testCasesCoalesce, init, value, expected);
}
// Repeat a number of times to ensure JITs can kick in, too.
for (let i = 0; i < 50; ++i) {
for (let thruthy of [true, 123, 123n, "hi", [], Symbol()]) {
testAnd(thruthy, "pass");
testOr(thruthy, "fail");
testCoalesce(thruthy, "fail");
}
for (let falsy of [false, 0, NaN, 0n, ""]) {
testAnd(falsy, "fail");
testOr(falsy, "pass");
testCoalesce(falsy, "fail");
}
for (let nullOrUndefined of [null, undefined]) {
testAnd(nullOrUndefined, "fail");
testOr(nullOrUndefined, "pass");
testCoalesce(nullOrUndefined, "pass");
}
}
if (typeof reportCompare === "function")
reportCompare(0, 0);