// Test case |typeof| folding in simple comparison contexts.

// Create functions to test if |typeof x| is equal to a constant string.
// - Covers all possible |typeof| results, plus the invalid string "bad".
// - Covers all four possible equality comparison operators.
function createFunctions() {
  return [
    "undefined",
    "object",
    "function",
    "string",
    "number",
    "boolean",
    "symbol",
    "bigint",
    "bad",
  ].flatMap(type => [
    "==",
    "===",
    "!=",
    "!=="
  ].map(op => ({
    type,
    equal: op[0] === "=",
    fn: Function("thing", `return typeof thing ${op} "${type}"`)
  })));
}

let functions = createFunctions();

function test() {
  const ccwGlobal = newGlobal({newCompartment: true});
  const xs = [
    // "undefined"
    // Plain undefined and object emulating undefined, including various proxy wrapper cases.
    undefined,
    createIsHTMLDDA(),
    wrapWithProto(createIsHTMLDDA(), null),
    ccwGlobal.eval("createIsHTMLDDA()"),

    // "object"
    // Plain object and various proxy wrapper cases.
    {},
    this,
    new Proxy({}, {}),
    wrapWithProto({}, null),
    transplantableObject({proxy: true}).object,
    ccwGlobal.Object(),

    // "function"
    // Plain function and various proxy wrapper cases.
    function(){},
    new Proxy(function(){}, {}),
    new Proxy(createIsHTMLDDA(), {}),
    wrapWithProto(function(){}, null),
    ccwGlobal.Function(),

    // "string"
    "",

    // "number"
    // Int32 and Double numbers.
    0,
    1.23,

    // "boolean"
    true,

    // "symbol"
    Symbol(),

    // "bigint"
    0n,
  ];

  for (let i = 0; i < 200; ++i) {
    let x = xs[i % xs.length];
    for (let {type, equal, fn} of functions) {
      assertEq(fn(x), (typeof x === type) === equal);
    }
  }
}
for (let i = 0; i < 2; ++i) test();

// Fresh set of functions to gather new type info.
let functionsObject = createFunctions();

// Additional test when the input is always an object.
function testObject() {
  const ccwGlobal = newGlobal({newCompartment: true});

  // All elements of this array are objects to cover the case when |MTypeOf| has
  // a |MIRType::Object| input.
  const xs = [
    // "undefined"
    // Object emulating undefined, including various proxy wrapper cases.
    createIsHTMLDDA(),
    wrapWithProto(createIsHTMLDDA(), null),
    ccwGlobal.eval("createIsHTMLDDA()"),

    // "object"
    // Plain object and various proxy wrapper cases.
    {},
    this,
    new Proxy({}, {}),
    wrapWithProto({}, null),
    transplantableObject({proxy: true}).object,
    ccwGlobal.Object(),

    // "function"
    // Plain function and various proxy wrapper cases.
    function(){},
    new Proxy(function(){}, {}),
    new Proxy(createIsHTMLDDA(), {}),
    wrapWithProto(function(){}, null),
    ccwGlobal.Function(),
  ];

  for (let i = 0; i < 200; ++i) {
    let x = xs[i % xs.length];
    for (let {type, equal, fn} of functionsObject) {
      assertEq(fn(x), (typeof x === type) === equal);
    }
  }
}
for (let i = 0; i < 2; ++i) testObject();
