cred: isolate scripts in WASI and move RSA signing to AWS-LC

This commit is contained in:
2026-11-06 22:22:49 +00:00
parent 5b3e1393f5
commit 86c5d8a4e4
217 changed files with 155392 additions and 391 deletions
@@ -0,0 +1,49 @@
export function assert(actual, expected, message) {
if (arguments.length === 1)
expected = true;
if (typeof actual === typeof expected) {
if (actual === expected) {
if (actual !== 0 || (1 / actual) === (1 / expected))
return;
}
if (typeof actual === 'number') {
if (isNaN(actual) && isNaN(expected))
return;
}
if (typeof actual === 'object') {
if (actual !== null && expected !== null
&& actual.constructor === expected.constructor
&& actual.toString() === expected.toString())
return;
}
}
throw Error("assertion failed: got |" + actual + "|" +
", expected |" + expected + "|" +
(message ? " (" + message + ")" : ""));
}
export function assertThrows(err, func)
{
var ex;
ex = false;
try {
func();
} catch(e) {
ex = true;
assert(e instanceof err);
}
assert(ex, true, "exception expected");
}
export function assertArrayEquals(a, b)
{
if (!Array.isArray(a) || !Array.isArray(b))
return assert(false);
assert(a.length, b.length);
a.forEach((value, idx) => {
assert(b[idx], value);
});
}
@@ -0,0 +1,16 @@
import {assert} from "./assert.js"
let caught = 0
for (let i = 1; i <= 32; i++) {
const prefix = "(:?".repeat(i)
const suffix = "|)+".repeat(i)
const between = "(?:a|)+"
try {
new RegExp(prefix + between + suffix)
} catch (e) {
assert(e instanceof SyntaxError)
assert(e.message, "out of memory")
caught++
}
}
assert(caught, 12) // subject to change
@@ -0,0 +1,6 @@
/*---
flags: [qjs:track-promise-rejections]
---*/
Promise.reject().catch(() => print('oops'))
Promise.resolve().then(() => print('ok'))
@@ -0,0 +1,6 @@
/*---
flags: [qjs:track-promise-rejections]
---*/
const error = await Promise.resolve().then(() => Promise.reject('reject')).catch(e => e)
print('Got this error:', error)
@@ -0,0 +1,7 @@
/*---
flags: [qjs:track-promise-rejections]
---*/
const promise = Promise.reject('reject')
const error = await Promise.resolve().then(() => promise).catch(e => e)
print('Got this error:', error)
@@ -0,0 +1,7 @@
/*---
flags: [qjs:no-detect-module]
negative:
phase: parse
type: SyntaxError
---*/
const undefined = 42 // SyntaxError at global scope
@@ -0,0 +1,4 @@
/*---
flags: [qjs:no-detect-module, module]
---*/
const undefined = 42 // not a SyntaxError at toplevel module scope
@@ -0,0 +1,4 @@
/*---
flags: [qjs:no-detect-module]
---*/
{ const undefined = 42 } // not a SyntaxError, not at global scope
@@ -0,0 +1,4 @@
/*---
flags: [qjs:no-detect-module]
---*/
;(function() { const undefined = 42 })() // not a SyntaxError, not at global scope
@@ -0,0 +1,4 @@
"use strict";
const u8 = new Uint8Array(1);
u8[100] = 123; // Should not throw.
@@ -0,0 +1,9 @@
import { assert, assertThrows } from "../assert.js";
const ab = new ArrayBuffer(1);
const u8 = new Uint8Array(ab);
assert(!ab.detached);
// Detach the ArrayBuffer.
ab.transfer();
assert(ab.detached);
u8[100] = 123; // Doesn't throw.
assertThrows(TypeError, () => Object.defineProperty(u8, "100", { value: 123 }));
@@ -0,0 +1,7 @@
import { assert, assertThrows } from "../assert.js";
const ab = new ArrayBuffer(16, { maxByteLength: 32 });
const u8 = new Uint8Array(ab, 16);
ab.resize(8);
assert(ab.byteLength, 8);
u8[1] = 123; // Doesn't throw.
assertThrows(TypeError, () => Object.defineProperty(u8, "1", { value: 123 }));
@@ -0,0 +1,13 @@
/*---
negative:
phase: runtime
type: Error
---*/
let finrec = new FinalizationRegistry(v => {})
let object = {object:"object"}
finrec.register(object, {held:"held"}, {token:"token"})
object = undefined
// abrupt termination should not leak |held|
// unfortunately only shows up in qjs, not run-test262,
// but still good to have a regression test
throw Error("ok")
@@ -0,0 +1,4 @@
import { assert } from "./assert.js"
const ref = new WeakRef({})
const val = ref.deref() // should not throw
assert(val, undefined)
@@ -0,0 +1,19 @@
import {assert} from "./assert.js"
while (1) label: break
var i = 0
while (i < 3) label: {
if (i > 0)
break
i++
}
assert(i, 1)
for (;;) label: break
for (i = 0; i < 3; i++) label: {
if (i > 0)
break
}
assert(i, 1)
@@ -0,0 +1,7 @@
/*---
negative:
phase: runtime
type: RangeError
---*/
function f() { f() } // was problematic under ASan
f()
@@ -0,0 +1,7 @@
/*---
negative:
phase: runtime
type: RangeError
---*/
function f() { f.apply(null) } // was problematic under ASan
f()
@@ -0,0 +1,2 @@
const m = await import("./empty.js")
print(m) // should not throw
@@ -0,0 +1,26 @@
import {assert} from "./assert.js";
Error.stackTraceLimit = Infinity;
assert(Error.stackTraceLimit === Infinity);
assert(new Error("error").stack.includes("bug858.js")); // stack should not be empty
Error.stackTraceLimit = -Infinity;
assert(Error.stackTraceLimit === -Infinity);
assert(!new Error("error").stack.includes("bug858.js")); // stack should be empty
Error.stackTraceLimit = NaN;
assert(Number.isNaN(Error.stackTraceLimit));
assert(!new Error("error").stack.includes("bug858.js")); // stack should be empty
Error.stackTraceLimit = -0;
assert(Object.is(Error.stackTraceLimit, -0));
assert(!new Error("error").stack.includes("bug858.js")); // stack should be empty
const obj = { valueOf() { throw "evil" } };
Error.stackTraceLimit = obj;
assert(Error.stackTraceLimit === obj);
try {
throw new Error("fail")
} catch (e) {
assert(e.message.includes("fail"));
}
@@ -0,0 +1,6 @@
import {assert, assertThrows} from "./assert.js"
let calls = 0
Error.prepareStackTrace = function() { calls++ }
function f() { f() }
assertThrows(RangeError, f)
assert(calls, 0)
@@ -0,0 +1,7 @@
import {assert} from "./assert.js"
const expected = [97,98,99]
const ab = new ArrayBuffer(0, {maxByteLength:3})
const dv = new DataView(ab)
ab.resize(3)
for (const [i,v] of Object.entries(expected)) dv.setUint8(i, v)
for (const [i,v] of Object.entries(expected)) assert(v, dv.getUint8(i))
@@ -0,0 +1,3 @@
function* f(r){ return r } // must return r
[...f({})]
@@ -0,0 +1,8 @@
import { assert, assertArrayEquals } from "./assert.js";
import * as mod from "./destructured-export.js";
export const { a, b, c } = { a: 1, b: 2, c: 3 };
export const d = 4;
assert(typeof mod === 'object');
assertArrayEquals(Object.keys(mod), ["a", "b", "c", "d"]);
@@ -0,0 +1 @@
await undefined
@@ -0,0 +1,2 @@
const p = Promise.resolve(42)
await p
@@ -0,0 +1 @@
await = 42 // parsed as classic script
@@ -0,0 +1,8 @@
/*---
negative:
phase: parse
type: SyntaxError
---*/
// the import statement makes it a module but `await = 42` is a SyntaxError
import * as _ from "dummy"
await = 42
@@ -0,0 +1,3 @@
// imports should classify it as a module, even when not at the top
os.now()
import * as os from "qjs:os"
@@ -0,0 +1,2 @@
import * as a from "./test_cyclic_import.js"
export function f(x) { return 2 * a.g(x) }
@@ -0,0 +1,12 @@
// ES2020 string export names test fixture
export const regularExport = "regular";
const value1 = "value-1";
const value2 = "value-2";
// String export names (ES2020)
export { value1 as "string-export-1" };
export { value2 as "string-export-2" };
// Mixed: regular and string exports
const mixed = "mixed-value";
export { mixed as normalName, mixed as "string-name" };
@@ -0,0 +1,14 @@
"use strict"
const expect = "function f() { return 42 }"
function f() { return 42 }
{
const actual = f.toString()
if (actual !== expect) throw Error(actual)
}
{
const f = eval(expect + "f")
const actual = f.toString()
if (actual !== expect) throw Error(actual)
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,38 @@
import {assert} from "./assert.js"
let ex
try {
null.x
} catch (e) {
ex = e
}
assert(ex instanceof TypeError)
assert(ex.message, "cannot read property 'x' of null")
ex = undefined
try {
null["x"]
} catch (e) {
ex = e
}
assert(ex instanceof TypeError)
assert(ex.message, "cannot read property 'x' of null")
ex = undefined
try {
undefined.x
} catch (e) {
ex = e
}
assert(ex instanceof TypeError)
assert(ex.message, "cannot read property 'x' of undefined")
ex = undefined
try {
undefined["x"]
} catch (e) {
ex = e
}
assert(ex instanceof TypeError)
assert(ex.message, "cannot read property 'x' of undefined")
ex = undefined
@@ -0,0 +1,5 @@
var v1 = '';
try {
var v2 = v1.padEnd(2147483620, 0);
} catch(_) {}
@@ -0,0 +1,107 @@
import { assert, assertThrows } from "./assert.js";
function bigint_pow(a, n)
{
var r, i;
r = 1n;
for(i = 0n; i < n; i++)
r *= a;
return r;
}
/* a must be < b */
function test_less(a, b)
{
assert(a < b);
assert(!(b < a));
assert(a <= b);
assert(!(b <= a));
assert(b > a);
assert(!(a > b));
assert(b >= a);
assert(!(a >= b));
assert(a != b);
assert(!(a == b));
}
/* a must be numerically equal to b */
function test_eq(a, b)
{
assert(a == b);
assert(b == a);
assert(!(a != b));
assert(!(b != a));
assert(a <= b);
assert(b <= a);
assert(!(a < b));
assert(a >= b);
assert(b >= a);
assert(!(a > b));
}
function test_bigint1()
{
var a, r;
test_less(2n, 3n);
test_eq(3n, 3n);
test_less(2, 3n);
test_eq(3, 3n);
test_less(2.1, 3n);
test_eq(Math.sqrt(4), 2n);
a = bigint_pow(3n, 100n);
assert((a - 1n) != a);
assert(a, 515377520732011331036461129765621272702107522001n);
assert(a, 0x5a4653ca673768565b41f775d6947d55cf3813d1n);
r = 1n << 31n;
assert(r, 2147483648n, "1 << 31n === 2147483648n");
r = 1n << 32n;
assert(r, 4294967296n, "1 << 32n === 4294967296n");
assert(String(-9223372036854775808n), "-9223372036854775808");
}
function test_bigint2()
{
assert(BigInt(""), 0n);
assert(BigInt(" 123"), 123n);
assert(BigInt(" 123 "), 123n);
assertThrows(SyntaxError, () => { BigInt("+") } );
assertThrows(SyntaxError, () => { BigInt("-") } );
assertThrows(SyntaxError, () => { BigInt("\x00a") } );
assertThrows(SyntaxError, () => { BigInt(" 123 r") } );
}
function test_bigint_map()
{
var m = new Map();
assert(m.size, 0);
for (let i = 0n; i < 1337n; i++) {
const r = m.set(i, i.toString());
assert(r, m);
}
assert(m.size, 1337);
for (let i = 0n; i < 1337n; i++) {
const r = m.get(i);
assert(r, i.toString());
}
assert(m.get(1337n), undefined);
assert(m.size, 1337);
for (let i = 0n; i < 1337n; i++)
assert(m.delete(i));
assert(!m.delete(1337n));
assert(m.size, 0);
}
test_bigint1();
test_bigint2();
test_bigint_map();
@@ -0,0 +1,341 @@
import * as std from "qjs:std";
import * as bjson from "qjs:bjson";
import { assert } from "./assert.js";
function base64decode(s) {
var A = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var n = s.indexOf("=");
if (n < 0) n = s.length;
if (n & 3 === 1) throw Error("bad base64"); // too much padding
var r = new Uint8Array(3 * (n>>2) + (n>>1 & 1) + (n & 1));
var a, b, c, d, i, j;
a = b = c = d = i = j = 0;
while (i+3 < n) {
a = A.indexOf(s[i++]);
b = A.indexOf(s[i++]);
c = A.indexOf(s[i++]);
d = A.indexOf(s[i++]);
if (~63 & (a|b|c|d)) throw Error("bad base64");
r[j++] = a<<2 | b>>4;
r[j++] = 255 & b<<4 | c>>2;
r[j++] = 255 & c<<6 | d;
}
switch (n & 3) {
case 2:
a = A.indexOf(s[i++]);
b = A.indexOf(s[i++]);
if (~63 & (a|b)) throw Error("bad base64");
if (b & 15) throw Error("bad base64");
r[j++] = a<<2 | b>>4;
break;
case 3:
a = A.indexOf(s[i++]);
b = A.indexOf(s[i++]);
c = A.indexOf(s[i++]);
if (~63 & (a|b|c)) throw Error("bad base64");
if (c & 3) throw Error("bad base64");
r[j++] = a<<2 | b>>4;
r[j++] = 255 & b<<4 | c>>2;
break;
}
return r.buffer;
}
function toHex(a)
{
var i, s = "", tab, v;
tab = new Uint8Array(a);
for(i = 0; i < tab.length; i++) {
v = tab[i].toString(16);
if (v.length < 2)
v = "0" + v;
if (i !== 0)
s += " ";
s += v;
}
return s;
}
function isArrayLike(a)
{
return Array.isArray(a) ||
(a instanceof Uint8ClampedArray) ||
(a instanceof Uint8Array) ||
(a instanceof Uint16Array) ||
(a instanceof Uint32Array) ||
(a instanceof Int8Array) ||
(a instanceof Int16Array) ||
(a instanceof Int32Array) ||
(a instanceof Float16Array) ||
(a instanceof Float32Array) ||
(a instanceof Float64Array);
}
function toStr(a)
{
var s, i, props, prop;
switch(typeof(a)) {
case "object":
if (a === null)
return "null";
if (a instanceof Date) {
s = "Date(" + toStr(a.valueOf()) + ")";
} else if (a instanceof Number) {
s = "Number(" + toStr(a.valueOf()) + ")";
} else if (a instanceof String) {
s = "String(" + toStr(a.valueOf()) + ")";
} else if (a instanceof Boolean) {
s = "Boolean(" + toStr(a.valueOf()) + ")";
} else if (isArrayLike(a)) {
s = "[";
for(i = 0; i < a.length; i++) {
if (i != 0)
s += ",";
s += toStr(a[i]);
}
s += "]";
} else {
props = Object.keys(a);
s = "{";
for(i = 0; i < props.length; i++) {
if (i != 0)
s += ",";
prop = props[i];
s += prop + ":" + toStr(a[prop]);
}
s += "}";
}
return s;
case "undefined":
return "undefined";
case "string":
return JSON.stringify(a);
case "number":
if (a == 0 && 1 / a < 0)
return "-0";
else
return a.toString();
break;
default:
return a.toString();
}
}
function bjson_test(a)
{
var buf, r, a_str, r_str;
a_str = toStr(a);
buf = bjson.write(a);
if (0) {
print(a_str, "->", toHex(buf));
}
r = bjson.read(buf, 0, buf.byteLength);
r_str = toStr(r);
if (a_str != r_str) {
print(a_str);
print(r_str);
assert(false);
}
}
function bjson_test_arraybuffer()
{
var buf, array_buffer;
array_buffer = new ArrayBuffer(4);
assert(array_buffer.byteLength, 4);
assert(array_buffer.maxByteLength, 4);
assert(array_buffer.resizable, false);
buf = bjson.write(array_buffer);
array_buffer = bjson.read(buf, 0, buf.byteLength);
assert(array_buffer.byteLength, 4);
assert(array_buffer.maxByteLength, 4);
assert(array_buffer.resizable, false);
array_buffer = new ArrayBuffer(4, {maxByteLength: 4});
assert(array_buffer.byteLength, 4);
assert(array_buffer.maxByteLength, 4);
assert(array_buffer.resizable, true);
buf = bjson.write(array_buffer);
array_buffer = bjson.read(buf, 0, buf.byteLength);
assert(array_buffer.byteLength, 4);
assert(array_buffer.maxByteLength, 4);
assert(array_buffer.resizable, true);
array_buffer = new ArrayBuffer(4, {maxByteLength: 8});
assert(array_buffer.byteLength, 4);
assert(array_buffer.maxByteLength, 8);
assert(array_buffer.resizable, true);
buf = bjson.write(array_buffer);
array_buffer = bjson.read(buf, 0, buf.byteLength);
assert(array_buffer.byteLength, 4);
assert(array_buffer.maxByteLength, 8);
assert(array_buffer.resizable, true);
}
/* test multiple references to an object including circular
references */
function bjson_test_reference()
{
var array, buf, i, n, array_buffer;
n = 16;
array = [];
for(i = 0; i < n; i++)
array[i] = {};
array_buffer = new ArrayBuffer(n);
for(i = 0; i < n; i++) {
array[i].next = array[(i + 1) % n];
array[i].idx = i;
array[i].typed_array = new Uint8Array(array_buffer, i, 1);
}
buf = bjson.write(array, bjson.WRITE_OBJ_REFERENCE);
array = bjson.read(buf, 0, buf.byteLength, bjson.READ_OBJ_REFERENCE);
/* check the result */
for(i = 0; i < n; i++) {
assert(array[i].next, array[(i + 1) % n]);
assert(array[i].idx, i);
assert(array[i].typed_array.buffer, array_buffer);
assert(array[i].typed_array.length, 1);
assert(array[i].typed_array.byteOffset, i);
}
}
function bjson_test_regexp()
{
var buf, r;
bjson_test(/xyzzy/);
bjson_test(/xyzzy/digu);
buf = bjson.write(/(?<𝓓𝓸𝓰>dog)/);
r = bjson.read(buf, 0, buf.byteLength);
assert("sup dog".match(r).groups["𝓓𝓸𝓰"], "dog");
}
function bjson_test_map()
{
var buf, r, xs;
xs = [["key", "value"]];
buf = bjson.write(new Map(xs));
r = bjson.read(buf, 0, buf.byteLength);
assert(r instanceof Map);
assert([...r].toString(), xs.toString());
}
function bjson_test_set()
{
var buf, r, xs;
xs = ["one", "two", "three"];
buf = bjson.write(new Set(xs));
r = bjson.read(buf, 0, buf.byteLength);
assert(r instanceof Set);
assert([...r].toString(), xs.toString());
}
function bjson_test_symbol()
{
var buf, r, o;
o = {[Symbol.toStringTag]: "42"};
buf = bjson.write(o);
r = bjson.read(buf, 0, buf.byteLength);
assert(o.toString(), r.toString());
o = Symbol('foo');
buf = bjson.write(o);
r = bjson.read(buf, 0, buf.byteLength);
assert(o.toString(), r.toString());
assert(o !== r);
o = Symbol.for('foo');
buf = bjson.write(o);
r = bjson.read(buf, 0, buf.byteLength);
assert(o, r);
o = Symbol.toStringTag;
buf = bjson.write(o);
r = bjson.read(buf, 0, buf.byteLength);
assert(o, r);
}
function bjson_test_bytecode()
{
var buf, o, r, e, i;
o = std.evalScript(";(function f(o){ return o.i })", {compile_only: true});
buf = bjson.write(o, /*JS_WRITE_OBJ_BYTECODE*/(1 << 0));
try {
bjson.read(buf, 0, buf.byteLength);
} catch (_e) {
e = _e;
}
assert(String(e), "SyntaxError: no bytecode allowed");
o = bjson.read(buf, 0, buf.byteLength, /*JS_READ_OBJ_BYTECODE*/(1 << 0));
assert(String(o), "[function bytecode]");
o = std.evalScript(o, {eval_function: true});
for (i = 0; i < 42; i++) o({i}); // exercise o.i IC
}
function bjson_test_fuzz()
{
var corpus = [
"FRAAAAAABGA=",
"Febm5oIt",
"FQARABMGBgYGBgYGBgYGBv////8QABEALxH/vy8R/78=",
"FQAIfwAK/////3//////////////////////////////3/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAAAAAD5+fn5+fn5+fn5+fkAAAAAAAYAqw==",
];
for (var input of corpus) {
var buf = base64decode(input);
try {
bjson.read(buf, 0, buf.byteLength);
} catch (e) {
if (/invalid version/.test(e.message)) throw e; // corpus needs update
}
}
}
function bjson_test_all()
{
var obj;
bjson_test({x:1, y:2, if:3});
bjson_test([1, 2, 3]);
bjson_test([1.0, "aa", true, false, undefined, null, NaN, -Infinity, -0.0]);
if (typeof BigInt !== "undefined") {
bjson_test([BigInt("1"), -BigInt("0x123456789"),
BigInt("0x123456789abcdef123456789abcdef")]);
}
bjson_test([new Date(1234), new String("abc"), new Number(-12.1), new Boolean(true)]);
bjson_test(new Int32Array([123123, 222111, -32222]));
bjson_test(new Float16Array([1024, 1024.5]));
bjson_test(new Float64Array([123123, 222111.5]));
/* tested with a circular reference */
obj = {};
obj.x = obj;
try {
bjson.write(obj);
assert(false);
} catch(e) {
assert(e instanceof TypeError);
}
bjson_test_arraybuffer();
bjson_test_reference();
bjson_test_regexp();
bjson_test_map();
bjson_test_set();
bjson_test_symbol();
bjson_test_bytecode();
bjson_test_fuzz();
}
bjson_test_all();
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,220 @@
// 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 (actual !== null && expected !== null
&& typeof actual == 'object' && typeof expected == 'object'
&& actual.toString() === expected.toString())
return;
throw Error("assertion failed: got |" + actual + "|" +
", expected |" + expected + "|" +
(message ? " (" + message + ")" : ""));
}
/*----------------*/
var log_str = "";
function log(str)
{
log_str += str + ",";
}
function f(a, b, c)
{
var x = 10;
log("a="+a);
function g(d) {
function h() {
log("d=" + d);
log("x=" + x);
}
log("b=" + b);
log("c=" + c);
h();
}
g(4);
return g;
}
var g1 = f(1, 2, 3);
g1(5);
assert(log_str, "a=1,b=2,c=3,d=4,x=10,b=2,c=3,d=5,x=10,", "closure1");
function test_closure1()
{
function f2()
{
var val = 1;
function set(a) {
val = a;
}
function get(a) {
return val;
}
return { "set": set, "get": get };
}
var obj = f2();
obj.set(10);
var r;
r = obj.get();
assert(r, 10, "closure2");
}
function test_closure2()
{
var expr_func = function myfunc1(n) {
function myfunc2(n) {
return myfunc1(n - 1);
}
if (n == 0)
return 0;
else
return myfunc2(n);
};
var r;
r = expr_func(1);
assert(r, 0, "expr_func");
}
function test_closure3()
{
function fib(n)
{
if (n <= 0)
return 0;
else if (n == 1)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
var fib_func = function fib1(n)
{
if (n <= 0)
return 0;
else if (n == 1)
return 1;
else
return fib1(n - 1) + fib1(n - 2);
};
assert(fib(6), 8, "fib");
assert(fib_func(6), 8, "fib_func");
}
function test_arrow_function()
{
"use strict";
function f1() {
return (() => arguments)();
}
function f2() {
return (() => this)();
}
function f3() {
return (() => eval("this"))();
}
function f4() {
return (() => eval("new.target"))();
}
var a;
a = f1(1, 2);
assert(a.length, 2);
assert(a[0] === 1 && a[1] === 2);
assert(f2.call("this_val"), "this_val");
assert(f3.call("this_val"), "this_val");
assert(new f4(), f4);
var o1 = { f() { return this; } };
var o2 = { f() {
return (() => eval("super.f()"))();
} };
o2.__proto__ = o1;
assert(o2.f(), o2);
}
function test_with()
{
var o1 = { x: "o1", y: "o1" };
var x = "local";
eval('var z="var_obj";');
assert(z, "var_obj");
with (o1) {
assert(x, "o1");
assert(eval("x"), "o1");
var f = function () {
o2 = { x: "o2" };
with (o2) {
assert(x, "o2");
assert(y, "o1");
assert(z, "var_obj");
assert(eval("x"), "o2");
assert(eval("y"), "o1");
assert(eval("z"), "var_obj");
assert(eval('eval("x")'), "o2");
}
};
f();
}
}
function test_eval_closure()
{
var tab;
tab = [];
for(let i = 0; i < 3; i++) {
eval("tab.push(function g1() { return i; })");
}
for(let i = 0; i < 3; i++) {
assert(tab[i](), i);
}
tab = [];
for(let i = 0; i < 3; i++) {
let f = function f() {
eval("tab.push(function g2() { return i; })");
};
f();
}
for(let i = 0; i < 3; i++) {
assert(tab[i](), i);
}
}
function test_eval_const()
{
const a = 1;
var success = false;
var f = function () {
eval("a = 1");
};
try {
f();
} catch(e) {
success = (e instanceof TypeError);
}
assert(success);
}
test_closure1();
test_closure2();
test_closure3();
test_arrow_function();
test_with();
test_eval_closure();
test_eval_const();
@@ -0,0 +1,12 @@
/*---
negative:
phase: resolution
type: SyntaxError
---*/
// FIXME(bnoordhuis) shouldn't throw SyntaxError but that's still better
// than segfaulting, see https://github.com/quickjs-ng/quickjs/issues/567
import {assert} from "./assert.js"
import {f} from "./fixture_cyclic_import.js"
export {f}
export function g(x) { return x + 1 }
assert(f(1), 4)
@@ -0,0 +1,35 @@
import { assert, assertThrows } from "./assert.js";
function test_code() {
let ex = new DOMException();
assert(ex.code, 0);
ex = new DOMException("", "HierarchyRequestError\0test");
assert(ex.code, 0);
ex = new DOMException("test", "HierarchyRequestError");
assert(ex.code, 3);
assert(ex.code, ex.HIERARCHY_REQUEST_ERR);
assert(ex.code, DOMException.HIERARCHY_REQUEST_ERR);
ex = new DOMException("", "DataCloneError");
assert(ex.code, ex.DATA_CLONE_ERR);
assert(ex.code, 25);
ex = new DOMException("", "IndexSizeError");
assert(ex.code, ex.INDEX_SIZE_ERR);
assert(ex.code, 1);
}
function test_properties() {
let ex = new DOMException("test");
assert(ex.message, "test");
assert(ex.name, "Error");
ex = new DOMException("test", "InvalidCharacterError");
assert(ex.name, "InvalidCharacterError");
assertThrows(TypeError, () => ex.message = "");
assertThrows(TypeError, () => ex.name = "test");
assert(ex.__proto__, DOMException.prototype);
/* Note: browsers set "stack" on the prototype, not the object.
* This follows node. */
assert(Object.getOwnPropertyNames(ex), ["stack"]);
}
test_code();
test_properties();
@@ -0,0 +1,748 @@
// 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);
}
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);
};
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_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;
}
}
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_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();
@@ -0,0 +1,367 @@
// 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 (actual !== null && expected !== null
&& typeof actual == 'object' && typeof expected == 'object'
&& actual.toString() === expected.toString())
return;
throw Error("assertion failed: got |" + actual + "|" +
", expected |" + expected + "|" +
(message ? " (" + message + ")" : ""));
}
/*----------------*/
function test_while()
{
var i, c;
i = 0;
c = 0;
while (i < 3) {
c++;
i++;
}
assert(c, 3);
}
function test_while_break()
{
var i, c;
i = 0;
c = 0;
while (i < 3) {
c++;
if (i == 1)
break;
i++;
}
assert(c === 2 && i === 1);
}
function test_do_while()
{
var i, c;
i = 0;
c = 0;
do {
c++;
i++;
} while (i < 3);
assert(c === 3 && i === 3);
}
function test_for()
{
var i, c;
c = 0;
for(i = 0; i < 3; i++) {
c++;
}
assert(c === 3 && i === 3);
c = 0;
for(var j = 0; j < 3; j++) {
c++;
}
assert(c === 3 && j === 3);
}
function test_for_in()
{
var i, tab, a, b;
tab = [];
for(i in {x:1, y: 2}) {
tab.push(i);
}
assert(tab.toString(), "x,y", "for_in");
/* prototype chain test */
a = {x:2, y: 2, "1": 3};
b = {"4" : 3 };
Object.setPrototypeOf(a, b);
tab = [];
for(i in a) {
tab.push(i);
}
assert(tab.toString(), "1,x,y,4", "for_in");
/* non enumerable properties hide enumerables ones in the
prototype chain */
a = {y: 2, "1": 3};
Object.defineProperty(a, "x", { value: 1 });
b = {"x" : 3 };
Object.setPrototypeOf(a, b);
tab = [];
for(i in a) {
tab.push(i);
}
assert(tab.toString(), "1,y", "for_in");
/* array optimization */
a = [];
for(i = 0; i < 10; i++)
a.push(i);
tab = [];
for(i in a) {
tab.push(i);
}
assert(tab.toString(), "0,1,2,3,4,5,6,7,8,9", "for_in");
/* iterate with a field */
a={x:0};
tab = [];
for(a.x in {x:1, y: 2}) {
tab.push(a.x);
}
assert(tab.toString(), "x,y", "for_in");
/* iterate with a variable field */
a=[0];
tab = [];
for(a[0] in {x:1, y: 2}) {
tab.push(a[0]);
}
assert(tab.toString(), "x,y", "for_in");
/* variable definition in the for in */
tab = [];
for(var j in {x:1, y: 2}) {
tab.push(j);
}
assert(tab.toString(), "x,y", "for_in");
/* variable assigment in the for in */
tab = [];
for(var k = 2 in {x:1, y: 2}) {
tab.push(k);
}
assert(tab.toString(), "x,y", "for_in");
}
function test_for_in2()
{
var i;
tab = [];
for(i in {x:1, y: 2, z:3}) {
if (i === "y")
continue;
tab.push(i);
}
assert(tab.toString() == "x,z");
tab = [];
for(i in {x:1, y: 2, z:3}) {
if (i === "z")
break;
tab.push(i);
}
assert(tab.toString() == "x,y");
}
function test_for_break()
{
var i, c;
c = 0;
L1: for(i = 0; i < 3; i++) {
c++;
if (i == 0)
continue;
while (1) {
break L1;
}
}
assert(c === 2 && i === 1);
}
function test_switch1()
{
var i, a, s;
s = "";
for(i = 0; i < 3; i++) {
a = "?";
switch(i) {
case 0:
a = "a";
break;
case 1:
a = "b";
break;
default:
a = "c";
break;
}
s += a;
}
assert(s === "abc" && i === 3);
}
function test_switch2()
{
var i, a, s;
s = "";
for(i = 0; i < 4; i++) {
a = "?";
switch(i) {
case 0:
a = "a";
break;
case 1:
a = "b";
break;
case 2:
continue;
default:
a = "" + i;
break;
}
s += a;
}
assert(s === "ab3" && i === 4);
}
function test_try_catch1()
{
try {
throw "hello";
} catch (e) {
assert(e, "hello", "catch");
return;
}
assert(false, "catch");
}
function test_try_catch2()
{
var a;
try {
a = 1;
} catch (e) {
a = 2;
}
assert(a, 1, "catch");
}
function test_try_catch3()
{
var s;
s = "";
try {
s += "t";
} catch (e) {
s += "c";
} finally {
s += "f";
}
assert(s, "tf", "catch");
}
function test_try_catch4()
{
var s;
s = "";
try {
s += "t";
throw "c";
} catch (e) {
s += e;
} finally {
s += "f";
}
assert(s, "tcf", "catch");
}
function test_try_catch5()
{
var s;
s = "";
for(;;) {
try {
s += "t";
break;
s += "b";
} finally {
s += "f";
}
}
assert(s, "tf", "catch");
}
function test_try_catch6()
{
function f() {
try {
s += 't';
return 1;
} finally {
s += "f";
}
}
var s = "";
assert(f() === 1);
assert(s, "tf", "catch6");
}
function test_try_catch7()
{
var s;
s = "";
try {
try {
s += "t";
throw "a";
} finally {
s += "f";
}
} catch(e) {
s += e;
} finally {
s += "g";
}
assert(s, "tfag", "catch");
}
function test_try_catch8()
{
var i, s;
s = "";
for(var i in {x:1, y:2}) {
try {
s += i;
throw "a";
} catch (e) {
s += e;
} finally {
s += "f";
}
}
assert(s === "xafyaf");
}
test_while();
test_while_break();
test_do_while();
test_for();
test_for_break();
test_switch1();
test_switch2();
test_for_in();
test_for_in2();
test_try_catch1();
test_try_catch2();
test_try_catch3();
test_try_catch4();
test_try_catch5();
test_try_catch6();
test_try_catch7();
test_try_catch8();
@@ -0,0 +1,39 @@
import { assert, assertArrayEquals, assertThrows } from "./assert.js";
function test_types() {
assertThrows(TypeError, () => queueMicrotask(), "no argument");
assertThrows(TypeError, () => queueMicrotask(undefined), "undefined");
assertThrows(TypeError, () => queueMicrotask(null), "null");
assertThrows(TypeError, () => queueMicrotask(0), "0");
assertThrows(TypeError, () => queueMicrotask({ handleEvent() { } }), "an event handler object");
assertThrows(TypeError, () => queueMicrotask("window.x = 5;"), "a string");
}
function test_async() {
let called = false;
queueMicrotask(() => {
called = true;
});
assert(!called);
}
function test_arguments() {
queueMicrotask(function () { // note: intentionally not an arrow function
assert(arguments.length === 0);
}, "x", "y");
};
function test_async_order() {
const happenings = [];
Promise.resolve().then(() => happenings.push("a"));
queueMicrotask(() => happenings.push("b"));
Promise.reject().catch(() => happenings.push("c"));
queueMicrotask(() => {
assertArrayEquals(happenings, ["a", "b", "c"]);
});
}
test_types();
test_async();
test_arguments();
test_async_order();
@@ -0,0 +1,314 @@
import * as std from "qjs:std";
import * as os from "qjs:os";
import { assert } from "./assert.js";
const isWin = os.platform === 'win32';
const isCygwin = os.platform === 'cygwin';
function test_printf()
{
assert(std.sprintf("a=%d s=%s", 123, "abc"), "a=123 s=abc");
assert(std.sprintf("%010d", 123), "0000000123");
assert(std.sprintf("%x", -2), "fffffffe");
assert(std.sprintf("%lx", -2), "fffffffffffffffe");
assert(std.sprintf("%10.1f", 2.1), " 2.1");
assert(std.sprintf("%*.*f", 10, 2, -2.13), " -2.13");
assert(std.sprintf("%#lx", 0x7fffffffffffffffn), "0x7fffffffffffffff");
}
function test_file1()
{
var f, len, str, size, buf, ret, i, str1, ab;
f = std.tmpfile();
str = "hello world\n";
f.puts(str);
f.seek(0, std.SEEK_SET);
ab = f.readAsArrayBuffer();
assert([...new Uint8Array(ab)], str.split("").map(c => c.charCodeAt(0)));
f.seek(0, std.SEEK_SET);
str1 = f.readAsString();
assert(str1, str);
f.seek(0, std.SEEK_END);
size = f.tell();
assert(size, str.length);
f.seek(0, std.SEEK_SET);
buf = new Uint8Array(size);
ret = f.read(buf.buffer, 0, size);
assert(ret, size);
for(i = 0; i < size; i++)
assert(buf[i], str.charCodeAt(i));
f.close();
}
function test_file2()
{
var f, str, i, size;
f = std.tmpfile();
str = "hello world\n";
size = str.length;
for(i = 0; i < size; i++)
f.putByte(str.charCodeAt(i));
f.seek(0, std.SEEK_SET);
for(i = 0; i < size; i++) {
assert(str.charCodeAt(i), f.getByte());
}
assert(f.getByte(), -1);
f.close();
}
function test_getline()
{
var f, line, line_count, lines, i;
lines = ["hello world", "line 1", "line 2" ];
f = std.tmpfile();
for(i = 0; i < lines.length; i++) {
f.puts(lines[i], "\n");
}
f.seek(0, std.SEEK_SET);
assert(!f.eof());
line_count = 0;
for(;;) {
line = f.getline();
if (line === null)
break;
assert(line, lines[line_count]);
line_count++;
}
assert(f.eof());
assert(line_count, lines.length);
f.close();
}
function test_popen()
{
var str, f, fname = "tmp_file.txt";
var ta, content = "hello world";
var cmd = isWin ? "type" : "cat";
ta = new Uint8Array([...content].map(c => c.charCodeAt(0)));
std.writeFile(fname, ta);
assert(std.loadFile(fname), content);
std.writeFile(fname, ta.buffer);
assert(std.loadFile(fname), content);
std.writeFile(fname, content);
assert(std.loadFile(fname), content);
// popen pipe is unidirectional so mode should
// be either read or write but not both
let caught = false;
try {
std.popen(cmd, "rw");
} catch (e) {
assert(/invalid file mode/.test(e.message));
caught = true;
}
assert(caught);
/* execute shell command */
f = std.popen(cmd + " " + fname, "r");
str = f.readAsString();
f.close();
assert(str, content);
os.remove(fname);
}
function test_os()
{
var fd, fpath, fname, fdir, buf, buf2, i, files, err, fdate, st, link_path;
// XXX(bnoordhuis) disabled because stdio is not a tty on CI
//assert(os.isatty(0));
fdir = "test_tmp_dir";
fname = "tmp_file.txt";
fpath = fdir + "/" + fname;
link_path = fdir + "/test_link";
os.remove(link_path);
os.remove(fpath);
os.remove(fdir);
err = os.mkdir(fdir, 0o755);
assert(err, 0);
fd = os.open(fpath, os.O_RDWR | os.O_CREAT | os.O_TRUNC);
assert(fd >= 0);
buf = new Uint8Array(10);
for(i = 0; i < buf.length; i++)
buf[i] = i;
assert(os.write(fd, buf.buffer, 0, buf.length), buf.length);
assert(os.seek(fd, 0, std.SEEK_SET), 0);
buf2 = new Uint8Array(buf.length);
assert(os.read(fd, buf2.buffer, 0, buf2.length), buf2.length);
for(i = 0; i < buf.length; i++)
assert(buf[i] == buf2[i]);
if (typeof BigInt !== "undefined") {
assert(os.seek(fd, BigInt(6), std.SEEK_SET), BigInt(6));
assert(os.read(fd, buf2.buffer, 0, 1), 1);
assert(buf[6] == buf2[0]);
}
assert(os.close(fd), 0);
[files, err] = os.readdir(fdir);
assert(err, 0);
assert(files.length >= 3);
assert(files.includes(fname));
assert(files.includes("."));
assert(files.includes(".."));
fdate = 10000;
err = os.utimes(fpath, fdate, fdate);
assert(err, 0);
[st, err] = os.stat(fpath);
assert(err, 0);
assert(st.mode & os.S_IFMT, os.S_IFREG);
assert(st.mtime, fdate);
if (!isWin) {
err = os.symlink(fname, link_path);
assert(err, 0);
[st, err] = os.lstat(link_path);
assert(err, 0);
assert(st.mode & os.S_IFMT, os.S_IFLNK);
[buf, err] = os.readlink(link_path);
assert(err, 0);
assert(buf, fname);
assert(os.remove(link_path) === 0);
}
[buf, err] = os.getcwd();
assert(err, 0);
[buf2, err] = os.realpath(".");
assert(err, 0);
assert(buf, buf2);
assert(os.remove(fpath) === 0);
fd = os.open(fpath, os.O_RDONLY);
assert(fd < 0);
assert(os.remove(fdir) === 0);
}
function test_os_exec()
{
var ret, fds, pid, f, status;
ret = os.exec(["true"]);
assert(ret, 0);
ret = os.exec(["/bin/sh", "-c", "exit 1"], { usePath: false });
assert(ret, 1);
fds = os.pipe();
pid = os.exec(["sh", "-c", "echo $FOO"], {
stdout: fds[1],
block: false,
env: { FOO: "hello" },
} );
assert(pid >= 0);
os.close(fds[1]); /* close the write end (as it is only in the child) */
f = std.fdopen(fds[0], "r");
assert(f.getline(), "hello");
assert(f.getline(), null);
f.close();
[ret, status] = os.waitpid(pid, 0);
assert(ret, pid);
assert(status & 0x7f, 0); /* exited */
assert(status >> 8, 0); /* exit code */
pid = os.exec(["cat"], { block: false } );
assert(pid >= 0);
os.kill(pid, os.SIGTERM);
[ret, status] = os.waitpid(pid, 0);
assert(ret, pid);
// Flaky on cygwin for unclear reasons, see
// https://github.com/quickjs-ng/quickjs/issues/184
if (!isCygwin) {
assert(status & 0x7f, os.SIGTERM);
}
}
function test_interval()
{
var t = os.setInterval(f, 1);
function f() {
if (++f.count === 3) os.clearInterval(t);
}
f.count = 0;
}
function test_timeout()
{
var th, i;
/* just test that a timer can be inserted and removed */
th = [];
for(i = 0; i < 3; i++)
th[i] = os.setTimeout(function () { }, 1000);
for(i = 0; i < 3; i++)
os.clearTimeout(th[i]);
}
function test_timeout_order()
{
var s = "";
os.setTimeout(a, 0);
os.setTimeout(b, 100);
os.setTimeout(d, 700);
function a() { s += "a"; os.setTimeout(c, 300); }
function b() { s += "b"; }
function c() { s += "c"; }
function d() { assert(s, "abc"); } // not "acb"
}
function test_stdio_close()
{
for (const f of [std.in, std.out, std.err]) {
let caught = false;
try {
f.close();
} catch (e) {
assert(/cannot close stdio/.test(e.message));
caught = true;
}
assert(caught);
}
}
test_printf();
test_file1();
test_file2();
test_getline();
test_popen();
test_os();
!isWin && test_os_exec();
test_interval();
test_timeout();
test_timeout_order();
test_stdio_close();
@@ -0,0 +1,25 @@
// Test ES2020 string export/import names
import { assert } from "./assert.js";
import * as mod from "./fixture_string_exports.js";
// Test string import names
import { "string-export-1" as str1 } from "./fixture_string_exports.js";
import { "string-export-2" as str2 } from "./fixture_string_exports.js";
import { "string-name" as strMixed } from "./fixture_string_exports.js";
// Test regular imports still work
import { regularExport, normalName } from "./fixture_string_exports.js";
// Verify values
assert(str1, "value-1");
assert(str2, "value-2");
assert(strMixed, "mixed-value");
assert(regularExport, "regular");
assert(normalName, "mixed-value");
// Verify module namespace has string-named exports
assert(mod["string-export-1"], "value-1");
assert(mod["string-export-2"], "value-2");
assert(mod["string-name"], "mixed-value");
assert(mod.regularExport, "regular");
assert(mod.normalName, "mixed-value");
@@ -0,0 +1,43 @@
import * as os from "qjs:os";
import { assert } from "./assert.js";
var worker;
function test_worker()
{
var counter;
worker = new os.Worker("./test_worker_module.js");
counter = 0;
worker.onmessage = function (e) {
var ev = e.data;
// print("recv", JSON.stringify(ev));
switch(ev.type) {
case "num":
assert(ev.num, counter);
counter++;
if (counter == 10) {
/* test SharedArrayBuffer modification */
let sab = new SharedArrayBuffer(10);
let buf = new Uint8Array(sab);
worker.postMessage({ type: "sab", buf: buf });
}
break;
case "sab_done":
{
let buf = ev.buf;
/* check that the SharedArrayBuffer was modified */
assert(buf[2], 10);
worker.postMessage({ type: "abort" });
}
break;
case "done":
/* terminate */
worker.onmessage = null;
break;
}
};
}
test_worker();
@@ -0,0 +1,30 @@
/* Worker code for test_worker.js */
import * as os from "qjs:os";
var parent = os.Worker.parent;
function handle_msg(e) {
var ev = e.data;
// print("child_recv", JSON.stringify(ev));
switch(ev.type) {
case "abort":
parent.postMessage({ type: "done" });
break;
case "sab":
/* modify the SharedArrayBuffer */
ev.buf[2] = 10;
parent.postMessage({ type: "sab_done", buf: ev.buf });
break;
}
}
function worker_main() {
var i;
parent.onmessage = handle_msg;
for(i = 0; i < 10; i++) {
parent.postMessage({ type: "num", num: i });
}
}
worker_main();