Checkpoint before integration and package validation. Assisted-by: SurmAgent:unknown [read] [edit] [bash]
1152 lines
30 KiB
JavaScript
1152 lines
30 KiB
JavaScript
// This test cannot use imports because it needs to run in non-strict mode.
|
|
|
|
function assert(actual, expected, message) {
|
|
if (arguments.length == 1)
|
|
expected = true;
|
|
|
|
if (actual === expected)
|
|
return;
|
|
|
|
if (typeof actual == 'number' && isNaN(actual)
|
|
&& typeof expected == 'number' && isNaN(expected))
|
|
return;
|
|
|
|
if (actual !== null && expected !== null
|
|
&& typeof actual == 'object' && typeof expected == 'object'
|
|
&& actual.toString() === expected.toString())
|
|
return;
|
|
|
|
var msg = message ? " (" + message + ")" : "";
|
|
throw Error("assertion failed: got |" + actual + "|" +
|
|
", expected |" + expected + "|" + msg);
|
|
}
|
|
|
|
function assert_throws(expected_error, func, message)
|
|
{
|
|
var err = false;
|
|
var msg = message ? " (" + message + ")" : "";
|
|
try {
|
|
switch (typeof func) {
|
|
case 'string':
|
|
eval(func);
|
|
break;
|
|
case 'function':
|
|
func();
|
|
break;
|
|
}
|
|
} catch(e) {
|
|
err = true;
|
|
if (!(e instanceof expected_error)) {
|
|
throw Error(`expected ${expected_error.name}, got ${e.name}${msg}`);
|
|
}
|
|
}
|
|
if (!err) {
|
|
throw Error(`expected ${expected_error.name}${msg}`);
|
|
}
|
|
}
|
|
|
|
/*----------------*/
|
|
|
|
function test_op1()
|
|
{
|
|
var r, a;
|
|
r = 1 + 2;
|
|
assert(r, 3, "1 + 2 === 3");
|
|
|
|
r = 1 - 2;
|
|
assert(r, -1, "1 - 2 === -1");
|
|
|
|
r = -1;
|
|
assert(r, -1, "-1 === -1");
|
|
|
|
r = +2;
|
|
assert(r, 2, "+2 === 2");
|
|
|
|
r = 2 * 3;
|
|
assert(r, 6, "2 * 3 === 6");
|
|
|
|
r = 4 / 2;
|
|
assert(r, 2, "4 / 2 === 2");
|
|
|
|
r = 4 % 3;
|
|
assert(r, 1, "4 % 3 === 3");
|
|
|
|
r = 4 << 2;
|
|
assert(r, 16, "4 << 2 === 16");
|
|
|
|
r = 1 << 0;
|
|
assert(r, 1, "1 << 0 === 1");
|
|
|
|
r = 1 << 31;
|
|
assert(r, -2147483648, "1 << 31 === -2147483648");
|
|
|
|
r = 1 << 32;
|
|
assert(r, 1, "1 << 32 === 1");
|
|
|
|
r = (1 << 31) < 0;
|
|
assert(r, true, "(1 << 31) < 0 === true");
|
|
|
|
r = -4 >> 1;
|
|
assert(r, -2, "-4 >> 1 === -2");
|
|
|
|
r = -4 >>> 1;
|
|
assert(r, 0x7ffffffe, "-4 >>> 1 === 0x7ffffffe");
|
|
|
|
r = 1 & 1;
|
|
assert(r, 1, "1 & 1 === 1");
|
|
|
|
r = 0 | 1;
|
|
assert(r, 1, "0 | 1 === 1");
|
|
|
|
r = 1 ^ 1;
|
|
assert(r, 0, "1 ^ 1 === 0");
|
|
|
|
r = ~1;
|
|
assert(r, -2, "~1 === -2");
|
|
|
|
r = !1;
|
|
assert(r, false, "!1 === false");
|
|
|
|
assert((1 < 2), true, "(1 < 2) === true");
|
|
|
|
assert((2 > 1), true, "(2 > 1) === true");
|
|
|
|
assert(('b' > 'a'), true, "('b' > 'a') === true");
|
|
|
|
assert(2 ** 8, 256, "2 ** 8 === 256");
|
|
}
|
|
|
|
function test_cvt()
|
|
{
|
|
assert((NaN | 0), 0);
|
|
assert((Infinity | 0), 0);
|
|
assert(((-Infinity) | 0), 0);
|
|
assert(("12345" | 0), 12345);
|
|
assert(("0x12345" | 0), 0x12345);
|
|
assert(((4294967296 * 3 - 4) | 0), -4);
|
|
|
|
assert(("12345" >>> 0), 12345);
|
|
assert(("0x12345" >>> 0), 0x12345);
|
|
assert((NaN >>> 0), 0);
|
|
assert((Infinity >>> 0), 0);
|
|
assert(((-Infinity) >>> 0), 0);
|
|
assert(((4294967296 * 3 - 4) >>> 0), (4294967296 - 4));
|
|
assert((19686109595169230000).toString(), "19686109595169230000");
|
|
}
|
|
|
|
function test_eq()
|
|
{
|
|
assert(null == undefined);
|
|
assert(undefined == null);
|
|
assert(true == 1);
|
|
assert(0 == false);
|
|
assert("" == 0);
|
|
assert("123" == 123);
|
|
assert("122" != 123);
|
|
assert((new Number(1)) == 1);
|
|
assert(2 == (new Number(2)));
|
|
assert((new String("abc")) == "abc");
|
|
assert({} != "abc");
|
|
}
|
|
|
|
function test_inc_dec()
|
|
{
|
|
var a, r;
|
|
|
|
a = 1;
|
|
r = a++;
|
|
assert(r === 1 && a === 2, true, "++");
|
|
|
|
a = 1;
|
|
r = ++a;
|
|
assert(r === 2 && a === 2, true, "++");
|
|
|
|
a = 1;
|
|
r = a--;
|
|
assert(r === 1 && a === 0, true, "--");
|
|
|
|
a = 1;
|
|
r = --a;
|
|
assert(r === 0 && a === 0, true, "--");
|
|
|
|
a = {x:true};
|
|
a.x++;
|
|
assert(a.x, 2, "++");
|
|
|
|
a = {x:true};
|
|
a.x--;
|
|
assert(a.x, 0, "--");
|
|
|
|
a = [true];
|
|
a[0]++;
|
|
assert(a[0], 2, "++");
|
|
|
|
a = {x:true};
|
|
r = a.x++;
|
|
assert(r === 1 && a.x === 2, true, "++");
|
|
|
|
a = {x:true};
|
|
r = a.x--;
|
|
assert(r === 1 && a.x === 0, true, "--");
|
|
|
|
a = [true];
|
|
r = a[0]++;
|
|
assert(r === 1 && a[0] === 2, true, "++");
|
|
|
|
a = [true];
|
|
r = a[0]--;
|
|
assert(r === 1 && a[0] === 0, true, "--");
|
|
}
|
|
|
|
function F(x)
|
|
{
|
|
this.x = x;
|
|
}
|
|
|
|
function test_op2()
|
|
{
|
|
var a, b;
|
|
a = new Object;
|
|
a.x = 1;
|
|
assert(a.x, 1, "new");
|
|
b = new F(2);
|
|
assert(b.x, 2, "new");
|
|
|
|
a = {x : 2};
|
|
assert(("x" in a), true, "in");
|
|
assert(("y" in a), false, "in");
|
|
|
|
a = {};
|
|
assert((a instanceof Object), true, "instanceof");
|
|
assert((a instanceof String), false, "instanceof");
|
|
|
|
assert((typeof 1), "number", "typeof");
|
|
assert((typeof Object), "function", "typeof");
|
|
assert((typeof null), "object", "typeof");
|
|
assert((typeof unknown_var), "undefined", "typeof");
|
|
|
|
a = {x: 1, if: 2, async: 3};
|
|
assert(a.if === 2);
|
|
assert(a.async === 3);
|
|
}
|
|
|
|
function test_delete()
|
|
{
|
|
var a, err;
|
|
|
|
a = {x: 1, y: 1};
|
|
assert((delete a.x), true, "delete");
|
|
assert(("x" in a), false, "delete");
|
|
|
|
/* the following are not tested by test262 */
|
|
assert(delete "abc"[100], true);
|
|
|
|
err = false;
|
|
try {
|
|
delete null.a;
|
|
} catch(e) {
|
|
err = (e instanceof TypeError);
|
|
}
|
|
assert(err, true, "delete");
|
|
|
|
err = false;
|
|
try {
|
|
a = { f() { delete super.a; } };
|
|
a.f();
|
|
} catch(e) {
|
|
err = (e instanceof ReferenceError);
|
|
}
|
|
assert(err, true, "delete");
|
|
}
|
|
|
|
function test_constructor()
|
|
{
|
|
function *G() {}
|
|
let ex
|
|
try { new G() } catch (ex_) { ex = ex_ }
|
|
assert(ex instanceof TypeError)
|
|
assert(ex.message, "G is not a constructor")
|
|
}
|
|
|
|
function test_prototype()
|
|
{
|
|
var f = function f() { };
|
|
assert(f.prototype.constructor, f, "prototype");
|
|
|
|
var g = function g() { };
|
|
/* QuickJS bug */
|
|
Object.defineProperty(g, "prototype", { writable: false });
|
|
assert(g.prototype.constructor, g, "prototype");
|
|
}
|
|
|
|
function test_arguments()
|
|
{
|
|
function f2() {
|
|
assert(arguments.length, 2, "arguments");
|
|
assert(arguments[0], 1, "arguments");
|
|
assert(arguments[1], 3, "arguments");
|
|
}
|
|
f2(1, 3);
|
|
|
|
/* mapped arguments with GC must not crash (non-detached var_refs) */
|
|
function f3(a) {
|
|
arguments;
|
|
gc();
|
|
}
|
|
f3(0);
|
|
}
|
|
|
|
function test_class()
|
|
{
|
|
var o;
|
|
class C {
|
|
constructor() {
|
|
this.x = 10;
|
|
}
|
|
f() {
|
|
return 1;
|
|
}
|
|
static F() {
|
|
return -1;
|
|
}
|
|
get y() {
|
|
return 12;
|
|
}
|
|
};
|
|
class D extends C {
|
|
constructor() {
|
|
super();
|
|
this.z = 20;
|
|
}
|
|
g() {
|
|
return 2;
|
|
}
|
|
static G() {
|
|
return -2;
|
|
}
|
|
h() {
|
|
return super.f();
|
|
}
|
|
static H() {
|
|
return super["F"]();
|
|
}
|
|
}
|
|
|
|
assert(C.F(), -1);
|
|
assert(Object.getOwnPropertyDescriptor(C.prototype, "y").get.name === "get y");
|
|
|
|
o = new C();
|
|
assert(o.f(), 1);
|
|
assert(o.x, 10);
|
|
|
|
assert(D.F(), -1);
|
|
assert(D.G(), -2);
|
|
assert(D.H(), -1);
|
|
|
|
o = new D();
|
|
assert(o.f(), 1);
|
|
assert(o.g(), 2);
|
|
assert(o.x, 10);
|
|
assert(o.z, 20);
|
|
assert(o.h(), 1);
|
|
|
|
/* test class name scope */
|
|
var E1 = class E { static F() { return E; } };
|
|
assert(E1, E1.F());
|
|
|
|
class S {
|
|
static x = 42;
|
|
static y = S.x;
|
|
static z = this.x;
|
|
}
|
|
assert(S.x, 42);
|
|
assert(S.y, 42);
|
|
assert(S.z, 42);
|
|
|
|
class P {
|
|
get;
|
|
set;
|
|
async;
|
|
get = () => "123";
|
|
set = () => "456";
|
|
async = () => "789";
|
|
static() { return 42; }
|
|
}
|
|
assert(new P().get(), "123");
|
|
assert(new P().set(), "456");
|
|
assert(new P().async(), "789");
|
|
assert(new P().static(), 42);
|
|
|
|
/* test that division after private field in parens is not parsed as regex */
|
|
class Q {
|
|
#x = 10;
|
|
f() { return (this.#x / 2); }
|
|
}
|
|
assert(new Q().f(), 5);
|
|
};
|
|
|
|
function test_template()
|
|
{
|
|
var a, b;
|
|
b = 123;
|
|
a = `abc${b}d`;
|
|
assert(a, "abc123d");
|
|
|
|
a = String.raw `abc${b}d`;
|
|
assert(a, "abc123d");
|
|
|
|
a = "aaa";
|
|
b = "bbb";
|
|
assert(`aaa${a, b}ccc`, "aaabbbccc");
|
|
}
|
|
|
|
function test_template_skip()
|
|
{
|
|
var a = "Bar";
|
|
var { b = `${a + `a${a}` }baz` } = {};
|
|
assert(b, "BaraBarbaz");
|
|
}
|
|
|
|
function test_template_tostring_order()
|
|
{
|
|
/* each substitution is converted with ToString right after it is
|
|
evaluated (ECMA-262 13.2.8.6), and String.prototype.concat is not
|
|
involved */
|
|
var log = [];
|
|
var o = { toString() { log.push("o"); return "O"; } };
|
|
assert(`${o}${(log.push("next"), "n")}`, "On");
|
|
assert(log.join(), "o,next");
|
|
|
|
var c = { n: 0, toString() { return "" + this.n; } };
|
|
assert(`${c} ${c.n++}`, "0 0");
|
|
|
|
var d = new Date(2024, 0, 15, 12);
|
|
assert(`${d}|${d.setFullYear(2000)}`.indexOf("2024"), 11);
|
|
|
|
var p = { [Symbol.toPrimitive](hint) { return hint; } };
|
|
assert(`${p}`, "string");
|
|
var v = { valueOf() { return 1; }, toString() { return "s"; } };
|
|
assert(`${v}`, "s");
|
|
|
|
var concat = String.prototype.concat;
|
|
String.prototype.concat = function() { return "concat"; };
|
|
try {
|
|
assert(`a${1}b${2}`, "a1b2");
|
|
} finally {
|
|
String.prototype.concat = concat;
|
|
}
|
|
|
|
var err;
|
|
try {
|
|
`${Symbol()}`;
|
|
} catch (e) {
|
|
err = e;
|
|
}
|
|
assert(err instanceof TypeError, true);
|
|
}
|
|
|
|
function test_object_literal()
|
|
{
|
|
var x = 0, get = 1, set = 2; async = 3;
|
|
a = { get: 2, set: 3, async: 4, get a(){ return this.get} };
|
|
assert(JSON.stringify(a), '{"get":2,"set":3,"async":4,"a":2}');
|
|
assert(a.a, 2);
|
|
|
|
a = { x, get, set, async };
|
|
assert(JSON.stringify(a), '{"x":0,"get":1,"set":2,"async":3}');
|
|
}
|
|
|
|
function test_regexp_skip()
|
|
{
|
|
var a, b;
|
|
[a, b = /abc\(/] = [1];
|
|
assert(a, 1);
|
|
|
|
[a, b =/abc\(/] = [2];
|
|
assert(a, 2);
|
|
}
|
|
|
|
function test_labels()
|
|
{
|
|
do x: { break x; } while(0);
|
|
if (1)
|
|
x: { break x; }
|
|
else
|
|
x: { break x; }
|
|
with ({}) x: { break x; };
|
|
while (0) x: { break x; };
|
|
}
|
|
|
|
function test_destructuring()
|
|
{
|
|
function * g () { return 0; };
|
|
var [x] = g();
|
|
assert(x, void 0);
|
|
}
|
|
|
|
function test_spread()
|
|
{
|
|
var x;
|
|
x = [1, 2, ...[3, 4]];
|
|
assert(x.toString(), "1,2,3,4");
|
|
|
|
x = [ ...[ , ] ];
|
|
assert(Object.getOwnPropertyNames(x).toString(), "0,length");
|
|
}
|
|
|
|
function test_function_length()
|
|
{
|
|
assert( ((a, b = 1, c) => {}).length, 1);
|
|
assert( (([a,b]) => {}).length, 1);
|
|
assert( (({a,b}) => {}).length, 1);
|
|
assert( ((c, [a,b] = 1, d) => {}).length, 1);
|
|
}
|
|
|
|
function test_argument_scope()
|
|
{
|
|
var f;
|
|
var c = "global";
|
|
|
|
f = function(a = eval("var arguments")) {};
|
|
// for some reason v8 does not throw an exception here
|
|
if (typeof require === 'undefined')
|
|
assert_throws(SyntaxError, f);
|
|
|
|
f = function(a = eval("1"), b = arguments[0]) { return b; };
|
|
assert(f(12), 12);
|
|
|
|
f = function(a, b = arguments[0]) { return b; };
|
|
assert(f(12), 12);
|
|
|
|
f = function(a, b = () => arguments) { return b; };
|
|
assert(f(12)()[0], 12);
|
|
|
|
f = function(a = eval("1"), b = () => arguments) { return b; };
|
|
assert(f(12)()[0], 12);
|
|
|
|
(function() {
|
|
"use strict";
|
|
f = function(a = this) { return a; };
|
|
assert(f.call(123), 123);
|
|
|
|
f = function f(a = f) { return a; };
|
|
assert(f(), f);
|
|
|
|
f = function f(a = eval("f")) { return a; };
|
|
assert(f(), f);
|
|
})();
|
|
|
|
f = (a = eval("var c = 1"), probe = () => c) => {
|
|
var c = 2;
|
|
assert(c, 2);
|
|
assert(probe(), 1);
|
|
}
|
|
f();
|
|
|
|
f = (a = eval("var arguments = 1"), probe = () => arguments) => {
|
|
var arguments = 2;
|
|
assert(arguments, 2);
|
|
assert(probe(), 1);
|
|
}
|
|
f();
|
|
|
|
f = function f(a = eval("var c = 1"), b = c, probe = () => c) {
|
|
assert(b, 1);
|
|
assert(c, 1);
|
|
assert(probe(), 1)
|
|
}
|
|
f();
|
|
|
|
assert(c, "global");
|
|
f = function f(a, b = c, probe = () => c) {
|
|
eval("var c = 1");
|
|
assert(c, 1);
|
|
assert(b, "global");
|
|
assert(probe(), "global")
|
|
}
|
|
f();
|
|
assert(c, "global");
|
|
|
|
f = function f(a = eval("var c = 1"), probe = (d = eval("c")) => d) {
|
|
assert(probe(), 1)
|
|
}
|
|
f();
|
|
}
|
|
|
|
function test_function_expr_name()
|
|
{
|
|
var f;
|
|
|
|
/* non strict mode test : assignment to the function name silently
|
|
fails */
|
|
|
|
f = function myfunc() {
|
|
myfunc = 1;
|
|
return myfunc;
|
|
};
|
|
assert(f(), f);
|
|
|
|
f = function myfunc() {
|
|
myfunc = 1;
|
|
(() => {
|
|
myfunc = 1;
|
|
})();
|
|
return myfunc;
|
|
};
|
|
assert(f(), f);
|
|
|
|
f = function myfunc() {
|
|
eval("myfunc = 1");
|
|
return myfunc;
|
|
};
|
|
assert(f(), f);
|
|
|
|
/* strict mode test : assignment to the function name raises a
|
|
TypeError exception */
|
|
|
|
f = function myfunc() {
|
|
"use strict";
|
|
myfunc = 1;
|
|
};
|
|
assert_throws(TypeError, f);
|
|
|
|
f = function myfunc() {
|
|
"use strict";
|
|
(() => {
|
|
myfunc = 1;
|
|
})();
|
|
};
|
|
assert_throws(TypeError, f);
|
|
|
|
f = function myfunc() {
|
|
"use strict";
|
|
eval("myfunc = 1");
|
|
};
|
|
assert_throws(TypeError, f);
|
|
}
|
|
|
|
function test_expr(expr, err) {
|
|
if (err)
|
|
assert_throws(err, expr, `for ${expr}`);
|
|
else
|
|
assert(1, eval(expr), `for ${expr}`);
|
|
}
|
|
|
|
function test_name(name, err)
|
|
{
|
|
test_expr(`(function() { return typeof ${name} ? 1 : 1; })()`);
|
|
test_expr(`(function() { var ${name}; ${name} = 1; return ${name}; })()`);
|
|
test_expr(`(function() { let ${name}; ${name} = 1; return ${name}; })()`, name == 'let' ? SyntaxError : undefined);
|
|
test_expr(`(function() { const ${name} = 1; return ${name}; })()`, name == 'let' ? SyntaxError : undefined);
|
|
test_expr(`(function(${name}) { ${name} = 1; return ${name}; })()`);
|
|
test_expr(`(function({${name}}) { ${name} = 1; return ${name}; })({})`);
|
|
test_expr(`(function ${name}() { return ${name} ? 1 : 0; })()`);
|
|
test_expr(`"use strict"; (function() { return typeof ${name} ? 1 : 1; })()`, err);
|
|
test_expr(`"use strict"; (function() { if (0) ${name} = 1; return 1; })()`, err);
|
|
test_expr(`"use strict"; (function() { var x; if (0) x = ${name}; return 1; })()`, err);
|
|
test_expr(`"use strict"; (function() { var ${name}; return 1; })()`, err);
|
|
test_expr(`"use strict"; (function() { let ${name}; return 1; })()`, err);
|
|
test_expr(`"use strict"; (function() { const ${name} = 1; return 1; })()`, err);
|
|
test_expr(`"use strict"; (function() { var ${name}; ${name} = 1; return 1; })()`, err);
|
|
test_expr(`"use strict"; (function() { var ${name}; ${name} = 1; return ${name}; })()`, err);
|
|
test_expr(`"use strict"; (function(${name}) { return 1; })()`, err);
|
|
test_expr(`"use strict"; (function({${name}}) { return 1; })({})`, err);
|
|
test_expr(`"use strict"; (function ${name}() { return 1; })()`, err);
|
|
test_expr(`(function() { "use strict"; return typeof ${name} ? 1 : 1; })()`, err);
|
|
test_expr(`(function() { "use strict"; if (0) ${name} = 1; return 1; })()`, err);
|
|
test_expr(`(function() { "use strict"; var x; if (0) x = ${name}; return 1; })()`, err);
|
|
test_expr(`(function() { "use strict"; var ${name}; return 1; })()`, err);
|
|
test_expr(`(function() { "use strict"; let ${name}; return 1; })()`, err);
|
|
test_expr(`(function() { "use strict"; const ${name} = 1; return 1; })()`, err);
|
|
test_expr(`(function() { "use strict"; var ${name}; ${name} = 1; return 1; })()`, err);
|
|
test_expr(`(function() { "use strict"; var ${name}; ${name} = 1; return ${name}; })()`, err);
|
|
test_expr(`(function(${name}) { "use strict"; return 1; })()`, err);
|
|
test_expr(`(function({${name}}) { "use strict"; return 1; })({})`, SyntaxError);
|
|
test_expr(`(function ${name}() { "use strict"; return 1; })()`, err);
|
|
}
|
|
|
|
function test_reserved_names()
|
|
{
|
|
test_name('await');
|
|
test_name('yield', SyntaxError);
|
|
test_name('implements', SyntaxError);
|
|
test_name('interface', SyntaxError);
|
|
test_name('let', SyntaxError);
|
|
test_name('package', SyntaxError);
|
|
test_name('private', SyntaxError);
|
|
test_name('protected', SyntaxError);
|
|
test_name('public', SyntaxError);
|
|
test_name('static', SyntaxError);
|
|
}
|
|
|
|
function test_number_literals()
|
|
{
|
|
assert(0.1.a, undefined);
|
|
assert(0x1.a, undefined);
|
|
assert(0b1.a, undefined);
|
|
assert(01.a, undefined);
|
|
assert(0o1.a, undefined);
|
|
test_expr('0.a', SyntaxError);
|
|
assert(parseInt("0_1"), 0);
|
|
assert(parseInt("1_0"), 1);
|
|
assert(parseInt("0_1", 8), 0);
|
|
assert(parseInt("1_0", 8), 1);
|
|
assert(parseFloat("0_1"), 0);
|
|
assert(parseFloat("1_0"), 1);
|
|
assert(1_0, 10);
|
|
assert(parseInt("Infinity"), NaN);
|
|
assert(parseFloat("Infinity"), Infinity);
|
|
assert(parseFloat("Infinity1"), Infinity);
|
|
assert(parseFloat("Infinity_"), Infinity);
|
|
assert(parseFloat("Infinity."), Infinity);
|
|
test_expr('0_0', SyntaxError);
|
|
test_expr('00_0', SyntaxError);
|
|
test_expr('01_0', SyntaxError);
|
|
test_expr('08_0', SyntaxError);
|
|
test_expr('09_0', SyntaxError);
|
|
}
|
|
|
|
function test_syntax()
|
|
{
|
|
assert_throws(SyntaxError, "do");
|
|
assert_throws(SyntaxError, "do;");
|
|
assert_throws(SyntaxError, "do{}");
|
|
assert_throws(SyntaxError, "if");
|
|
assert_throws(SyntaxError, "if\n");
|
|
assert_throws(SyntaxError, "if 1");
|
|
assert_throws(SyntaxError, "if \0");
|
|
assert_throws(SyntaxError, "if ;");
|
|
assert_throws(SyntaxError, "if 'abc'");
|
|
assert_throws(SyntaxError, "if `abc`");
|
|
assert_throws(SyntaxError, "if /abc/");
|
|
assert_throws(SyntaxError, "if abc");
|
|
assert_throws(SyntaxError, "if abc\u0064");
|
|
assert_throws(SyntaxError, "if abc\\u0064");
|
|
assert_throws(SyntaxError, "if \u0123");
|
|
assert_throws(SyntaxError, "if \\u0123");
|
|
}
|
|
|
|
/* optional chaining tests not present in test262 */
|
|
function test_optional_chaining()
|
|
{
|
|
var a, z;
|
|
z = null;
|
|
a = { b: { c: 2 } };
|
|
assert(delete z?.b.c, true);
|
|
assert(delete a?.b.c, true);
|
|
assert(JSON.stringify(a), '{"b":{}}', "optional chaining delete");
|
|
|
|
a = { b: { c: 2 } };
|
|
assert(delete z?.b["c"], true);
|
|
assert(delete a?.b["c"], true);
|
|
assert(JSON.stringify(a), '{"b":{}}');
|
|
|
|
a = {
|
|
b() { return this._b; },
|
|
_b: { c: 42 }
|
|
};
|
|
|
|
assert((a?.b)().c, 42);
|
|
|
|
assert((a?.["b"])().c, 42);
|
|
}
|
|
|
|
function test_parse_semicolon()
|
|
{
|
|
/* 'yield' or 'await' may not be considered as a token if the
|
|
previous ';' is missing */
|
|
function *f()
|
|
{
|
|
function func() {
|
|
}
|
|
yield 1;
|
|
var h = x => x + 1
|
|
yield 2;
|
|
}
|
|
async function g()
|
|
{
|
|
function func() {
|
|
}
|
|
await 1;
|
|
var h = x => x + 1
|
|
await 2;
|
|
}
|
|
}
|
|
|
|
function test_non_extensible_receiver_existing_property_set()
|
|
{
|
|
var proto, receiver, ret;
|
|
|
|
proto = { x: 1 };
|
|
receiver = { x: 0 };
|
|
Object.preventExtensions(receiver);
|
|
ret = Reflect.set(proto, "x", 2, receiver);
|
|
assert(ret, true, "non-extensible receiver existing property Reflect.set result");
|
|
assert(receiver.x, 2, "non-extensible receiver existing property Reflect.set value");
|
|
|
|
class A {
|
|
}
|
|
A.prototype.x = 1;
|
|
|
|
class B extends A {
|
|
setX(value) {
|
|
super.x = value;
|
|
}
|
|
}
|
|
|
|
receiver = new B();
|
|
receiver.x = 0;
|
|
Object.preventExtensions(receiver);
|
|
receiver.setX(3);
|
|
assert(receiver.x, 3, "non-extensible receiver existing property super set value");
|
|
}
|
|
|
|
function assert_array_eq(actual, expected, message)
|
|
{
|
|
assert(Array.isArray(actual), true, message);
|
|
assert(actual.length, expected.length, message);
|
|
for (var i = 0; i < expected.length; i++) {
|
|
assert(actual[i], expected[i],
|
|
(message ? message + ": " : "") + "index " + i);
|
|
}
|
|
}
|
|
|
|
/* TC39 explicit resource management: `using` declaration */
|
|
function test_using()
|
|
{
|
|
/* AddDisposableResource validates and snapshots the dispose method at
|
|
declaration; the body must not run on failure. */
|
|
|
|
/* Missing Symbol.dispose → TypeError before the body runs. */
|
|
(function() {
|
|
var bodyRan = false;
|
|
var caught;
|
|
try {
|
|
(function() { using x = {}; bodyRan = true; })();
|
|
} catch (e) { caught = e; }
|
|
assert(bodyRan, false);
|
|
assert(caught instanceof TypeError, true);
|
|
})();
|
|
|
|
/* Non-object, non-null → TypeError. */
|
|
(function() {
|
|
var bodyRan = false;
|
|
var caught;
|
|
try {
|
|
(function() { using x = 42; bodyRan = true; })();
|
|
} catch (e) { caught = e; }
|
|
assert(bodyRan, false);
|
|
assert(caught instanceof TypeError, true);
|
|
})();
|
|
|
|
/* Symbol.dispose getter throwing propagates at declaration. */
|
|
(function() {
|
|
var bodyRan = false;
|
|
var caught;
|
|
try {
|
|
(function() {
|
|
var o = {};
|
|
Object.defineProperty(o, Symbol.dispose, {
|
|
get() { throw new Error("getter"); }
|
|
});
|
|
using y = o;
|
|
bodyRan = true;
|
|
})();
|
|
} catch (e) { caught = e; }
|
|
assert(bodyRan, false);
|
|
assert(caught instanceof Error, true);
|
|
assert(caught.message, "getter");
|
|
})();
|
|
|
|
/* Non-callable dispose → TypeError. */
|
|
(function() {
|
|
var bodyRan = false;
|
|
var caught;
|
|
try {
|
|
(function() {
|
|
using x = { [Symbol.dispose]: 123 };
|
|
bodyRan = true;
|
|
})();
|
|
} catch (e) { caught = e; }
|
|
assert(bodyRan, false);
|
|
assert(caught instanceof TypeError, true);
|
|
})();
|
|
|
|
/* null / undefined are spec-permitted and must not throw. */
|
|
(function() {
|
|
var disposed = 0;
|
|
(function() {
|
|
using a = null;
|
|
using b = undefined;
|
|
using c = { [Symbol.dispose]() { disposed++; } };
|
|
})();
|
|
assert(disposed, 1);
|
|
})();
|
|
|
|
/* for-of using: validation applies per-iteration. */
|
|
(function() {
|
|
var iters = 0;
|
|
var caught;
|
|
try {
|
|
(function() {
|
|
for (using x of [{ [Symbol.dispose]() {} }, 42]) {
|
|
iters++;
|
|
}
|
|
})();
|
|
} catch (e) { caught = e; }
|
|
assert(iters, 1);
|
|
assert(caught instanceof TypeError, true);
|
|
})();
|
|
|
|
/* for-of using: normal completion must not throw (regression for
|
|
a prior bug where each iteration threw `undefined`). */
|
|
(function() {
|
|
var log = [];
|
|
(function() {
|
|
for (using x of [
|
|
{ tag: "a", [Symbol.dispose]() { log.push(this.tag); } },
|
|
{ tag: "b", [Symbol.dispose]() { log.push(this.tag); } },
|
|
{ tag: "c", [Symbol.dispose]() { log.push(this.tag); } },
|
|
]) {
|
|
log.push("iter");
|
|
}
|
|
})();
|
|
assert_array_eq(log, ["iter", "a", "iter", "b", "iter", "c"]);
|
|
})();
|
|
|
|
/* LIFO disposal order. */
|
|
(function() {
|
|
var log = [];
|
|
(function() {
|
|
using a = { [Symbol.dispose]() { log.push("a"); } };
|
|
using b = { [Symbol.dispose]() { log.push("b"); } };
|
|
using c = { [Symbol.dispose]() { log.push("c"); } };
|
|
})();
|
|
assert_array_eq(log, ["c", "b", "a"]);
|
|
})();
|
|
|
|
/* Multiple dispose errors chain as SuppressedError (error=new,
|
|
suppressed=previous). */
|
|
(function() {
|
|
var caught;
|
|
try {
|
|
(function() {
|
|
using a = { [Symbol.dispose]() { throw new Error("a"); } };
|
|
using b = { [Symbol.dispose]() { throw new Error("b"); } };
|
|
using c = { [Symbol.dispose]() { throw new Error("c"); } };
|
|
})();
|
|
} catch (e) { caught = e; }
|
|
/* LIFO: c first → error_state=c. b → Suppressed(b, c).
|
|
a → Suppressed(a, Suppressed(b, c)). */
|
|
assert(caught instanceof SuppressedError, true);
|
|
assert(caught.error.message, "a");
|
|
assert(caught.suppressed instanceof SuppressedError, true);
|
|
assert(caught.suppressed.error.message, "b");
|
|
assert(caught.suppressed.suppressed.message, "c");
|
|
})();
|
|
|
|
/* Symbol.dispose is read exactly once at declaration (snapshot). */
|
|
(function() {
|
|
var reads = 0;
|
|
var disposed = 0;
|
|
var target = {};
|
|
Object.defineProperty(target, Symbol.dispose, {
|
|
get() {
|
|
reads++;
|
|
return function() { disposed++; };
|
|
}
|
|
});
|
|
(function() { using x = target; })();
|
|
assert(reads, 1);
|
|
assert(disposed, 1);
|
|
})();
|
|
|
|
/* DisposableStack.prototype.move uses the %DisposableStack%
|
|
intrinsic; tampering with or deleting the global binding must
|
|
not redirect it. */
|
|
(function() {
|
|
var saved = globalThis.DisposableStack;
|
|
try {
|
|
var ds = new saved();
|
|
var disposed = 0;
|
|
ds.use({ [Symbol.dispose]() { disposed++; } });
|
|
globalThis.DisposableStack = class Other {};
|
|
var moved = ds.move();
|
|
assert(Object.getPrototypeOf(moved), saved.prototype);
|
|
moved.dispose();
|
|
assert(disposed, 1);
|
|
delete globalThis.DisposableStack;
|
|
var ds2 = new saved();
|
|
ds2.use({ [Symbol.dispose]() {} });
|
|
var moved2 = ds2.move();
|
|
assert(Object.getPrototypeOf(moved2), saved.prototype);
|
|
} finally {
|
|
globalThis.DisposableStack = saved;
|
|
}
|
|
})();
|
|
|
|
/* SuppressedError wrapping can't be hijacked via prototype.constructor. */
|
|
(function() {
|
|
var savedCtor = SuppressedError.prototype.constructor;
|
|
try {
|
|
var hijacked = false;
|
|
SuppressedError.prototype.constructor = function() {
|
|
hijacked = true;
|
|
throw new Error("hijack");
|
|
};
|
|
var caught;
|
|
try {
|
|
(function() {
|
|
using a = { [Symbol.dispose]() { throw new Error("a"); } };
|
|
using b = { [Symbol.dispose]() { throw new Error("b"); } };
|
|
})();
|
|
} catch (e) { caught = e; }
|
|
assert(hijacked, false);
|
|
assert(caught instanceof SuppressedError, true);
|
|
assert(caught.error.message, "a");
|
|
assert(caught.suppressed.message, "b");
|
|
} finally {
|
|
SuppressedError.prototype.constructor = savedCtor;
|
|
}
|
|
})();
|
|
}
|
|
|
|
/* Async counterpart: `await using` and AsyncDisposableStack. */
|
|
async function test_await_using()
|
|
{
|
|
/* await using validates the async/sync dispose method at declaration. */
|
|
{
|
|
var bodyRan = false;
|
|
var caught;
|
|
try {
|
|
await (async function() {
|
|
await using x = {};
|
|
bodyRan = true;
|
|
})();
|
|
} catch (e) { caught = e; }
|
|
assert(bodyRan, false);
|
|
assert(caught instanceof TypeError, true);
|
|
}
|
|
|
|
/* Falls back to Symbol.dispose when @@asyncDispose is absent. */
|
|
{
|
|
var ok = false;
|
|
await (async function() {
|
|
await using x = { [Symbol.dispose]() {} };
|
|
ok = true;
|
|
})();
|
|
assert(ok, true);
|
|
}
|
|
|
|
/* AsyncDisposableStack.disposeAsync must await each dispose's returned
|
|
promise before invoking the next one (regression for a prior bug
|
|
that called all disposes in a single synchronous pass). */
|
|
{
|
|
var log = [];
|
|
var stack = new AsyncDisposableStack();
|
|
stack.use({
|
|
async [Symbol.asyncDispose]() { log.push("outer-called"); }
|
|
});
|
|
stack.use({
|
|
async [Symbol.asyncDispose]() {
|
|
log.push("inner-called");
|
|
await null;
|
|
log.push("inner-after-await");
|
|
}
|
|
});
|
|
var p = stack.disposeAsync();
|
|
log.push("after-sync-dispose");
|
|
await p;
|
|
assert_array_eq(log, [
|
|
"inner-called",
|
|
"after-sync-dispose",
|
|
"inner-after-await",
|
|
"outer-called"
|
|
]);
|
|
}
|
|
|
|
/* disposeAsync called twice without awaiting the first must still
|
|
dispose all resources in LIFO order. */
|
|
{
|
|
var log = [];
|
|
var stack = new AsyncDisposableStack();
|
|
stack.use({ [Symbol.asyncDispose]() { log.push(42); } });
|
|
stack.use({ [Symbol.asyncDispose]() { log.push(43); } });
|
|
stack.disposeAsync();
|
|
assert(stack.disposed, true);
|
|
await stack.disposeAsync();
|
|
assert_array_eq(log, [43, 42]);
|
|
}
|
|
|
|
/* LIFO. */
|
|
{
|
|
var log = [];
|
|
var stack = new AsyncDisposableStack();
|
|
stack.use({ [Symbol.asyncDispose]() { log.push("a"); } });
|
|
stack.use({ [Symbol.asyncDispose]() { log.push("b"); } });
|
|
stack.use({ [Symbol.asyncDispose]() { log.push("c"); } });
|
|
await stack.disposeAsync();
|
|
assert_array_eq(log, ["c", "b", "a"]);
|
|
}
|
|
|
|
/* SuppressedError chaining across async dispose errors. */
|
|
{
|
|
var stack = new AsyncDisposableStack();
|
|
stack.use({
|
|
async [Symbol.asyncDispose]() { throw new Error("first"); }
|
|
});
|
|
stack.use({
|
|
async [Symbol.asyncDispose]() { throw new Error("second"); }
|
|
});
|
|
var caught;
|
|
try {
|
|
await stack.disposeAsync();
|
|
} catch (e) { caught = e; }
|
|
/* LIFO: second throws → completion=second. first throws →
|
|
Suppressed(error=first, suppressed=second). */
|
|
assert(caught instanceof SuppressedError, true);
|
|
assert(caught.error.message, "first");
|
|
assert(caught.suppressed.message, "second");
|
|
}
|
|
|
|
/* disposeAsync always resolves to undefined. */
|
|
{
|
|
var stack = new AsyncDisposableStack();
|
|
stack.use({ [Symbol.asyncDispose]() { return 42; } });
|
|
stack.use({ async [Symbol.asyncDispose]() { return "hello"; } });
|
|
var result = await stack.disposeAsync();
|
|
assert(result, undefined);
|
|
}
|
|
}
|
|
|
|
|
|
test_op1();
|
|
test_cvt();
|
|
test_eq();
|
|
test_inc_dec();
|
|
test_op2();
|
|
test_delete();
|
|
test_constructor();
|
|
test_prototype();
|
|
test_arguments();
|
|
test_class();
|
|
test_template();
|
|
test_template_skip();
|
|
test_template_tostring_order();
|
|
test_object_literal();
|
|
test_regexp_skip();
|
|
test_labels();
|
|
test_destructuring();
|
|
test_spread();
|
|
test_function_length();
|
|
test_argument_scope();
|
|
test_function_expr_name();
|
|
test_reserved_names();
|
|
test_number_literals();
|
|
test_syntax();
|
|
test_optional_chaining();
|
|
test_parse_semicolon();
|
|
test_non_extensible_receiver_existing_property_set();
|
|
test_using();
|
|
test_await_using().catch(e => { throw e; });
|