5901 lines
98 KiB
Go
5901 lines
98 KiB
Go
package goja
|
|
|
|
import (
|
|
"os"
|
|
"sync"
|
|
"testing"
|
|
"unsafe"
|
|
|
|
"apigo.cc/ai/ai/goja/unistring"
|
|
)
|
|
|
|
const TESTLIB = `
|
|
function $ERROR(message) {
|
|
throw new Error(message);
|
|
}
|
|
|
|
function Test262Error(message) {
|
|
this.message = message || "";
|
|
}
|
|
|
|
Test262Error.prototype.toString = function () {
|
|
return "Test262Error: " + this.message;
|
|
};
|
|
|
|
Test262Error.thrower = (message) => {
|
|
throw new Test262Error(message);
|
|
};
|
|
|
|
function assert(mustBeTrue, message) {
|
|
if (mustBeTrue === true) {
|
|
return;
|
|
}
|
|
|
|
if (message === undefined) {
|
|
message = 'Expected true but got ' + String(mustBeTrue);
|
|
}
|
|
$ERROR(message);
|
|
}
|
|
|
|
assert._isSameValue = function (a, b) {
|
|
if (a === b) {
|
|
// Handle +/-0 vs. -/+0
|
|
return a !== 0 || 1 / a === 1 / b;
|
|
}
|
|
|
|
// Handle NaN vs. NaN
|
|
return a !== a && b !== b;
|
|
};
|
|
|
|
assert.sameValue = function (actual, expected, message) {
|
|
if (assert._isSameValue(actual, expected)) {
|
|
return;
|
|
}
|
|
|
|
if (message === undefined) {
|
|
message = '';
|
|
} else {
|
|
message += ' ';
|
|
}
|
|
|
|
message += 'Expected SameValue(«' + String(actual) + '», «' + String(expected) + '») to be true';
|
|
|
|
$ERROR(message);
|
|
};
|
|
|
|
assert.throws = function (expectedErrorConstructor, func, message) {
|
|
if (typeof func !== "function") {
|
|
$ERROR('assert.throws requires two arguments: the error constructor ' +
|
|
'and a function to run');
|
|
return;
|
|
}
|
|
if (message === undefined) {
|
|
message = '';
|
|
} else {
|
|
message += ' ';
|
|
}
|
|
|
|
try {
|
|
func();
|
|
} catch (thrown) {
|
|
if (typeof thrown !== 'object' || thrown === null) {
|
|
message += 'Thrown value was not an object!';
|
|
$ERROR(message);
|
|
} else if (thrown.constructor !== expectedErrorConstructor) {
|
|
message += 'Expected a ' + expectedErrorConstructor.name + ' but got a ' + thrown.constructor.name;
|
|
$ERROR(message);
|
|
}
|
|
return;
|
|
}
|
|
|
|
message += 'Expected a ' + expectedErrorConstructor.name + ' to be thrown but no exception was thrown at all';
|
|
$ERROR(message);
|
|
};
|
|
|
|
function compareArray(a, b) {
|
|
if (b.length !== a.length) {
|
|
return false;
|
|
}
|
|
|
|
for (var i = 0; i < a.length; i++) {
|
|
if (b[i] !== a[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
`
|
|
|
|
const TESTLIBX = `
|
|
function looksNative(fn) {
|
|
return /native code/.test(Function.prototype.toString.call(fn));
|
|
}
|
|
|
|
function deepEqual(a, b) {
|
|
if (typeof a === "object") {
|
|
if (typeof b === "object") {
|
|
if (a === b) {
|
|
return true;
|
|
}
|
|
if (Reflect.getPrototypeOf(a) !== Reflect.getPrototypeOf(b)) {
|
|
return false;
|
|
}
|
|
var keysA = Object.keys(a);
|
|
var keysB = Object.keys(b);
|
|
if (keysA.length !== keysB.length) {
|
|
return false;
|
|
}
|
|
if (!compareArray(keysA.sort(), keysB.sort())) {
|
|
return false;
|
|
}
|
|
for (var i = 0; i < keysA.length; i++) {
|
|
var key = keysA[i];
|
|
if (!deepEqual(a[key], b[key])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
return assert._isSameValue(a, b);
|
|
}
|
|
|
|
function assertStack(e, expected) {
|
|
const lines = e.stack.split('\n');
|
|
assert.sameValue(lines.length, expected.length + 2, "Stack lengths mismatch");
|
|
let lnum = 1;
|
|
for (const [file, func, line, col] of expected) {
|
|
const expLine = func === "" ?
|
|
"\tat " + file + ":" + line + ":" + col + "(" :
|
|
"\tat " + func + " (" + file + ":" + line + ":" + col + "(";
|
|
assert.sameValue(lines[lnum].substring(0, expLine.length), expLine, "line " + lnum);
|
|
lnum++;
|
|
}
|
|
}
|
|
`
|
|
|
|
var (
|
|
// The reason it's implemented this way rather than just as _testLib = MustCompile(...)
|
|
// is because when you try to debug the compiler and set a breakpoint it gets triggered during the
|
|
// initialisation which is annoying.
|
|
_testLib, _testLibX *Program
|
|
testLibOnce, testLibXOnce sync.Once
|
|
)
|
|
|
|
func testLib() *Program {
|
|
testLibOnce.Do(func() {
|
|
_testLib = MustCompile("testlib.js", TESTLIB, false)
|
|
})
|
|
return _testLib
|
|
}
|
|
|
|
func testLibX() *Program {
|
|
testLibXOnce.Do(func() {
|
|
_testLibX = MustCompile("testlibx.js", TESTLIBX, false)
|
|
})
|
|
return _testLibX
|
|
}
|
|
|
|
func (r *Runtime) testPrg(p *Program, expectedResult Value, t *testing.T) {
|
|
p.dumpCode(t.Logf)
|
|
v, err := r.RunProgram(p)
|
|
if err != nil {
|
|
if ex, ok := err.(*Exception); ok {
|
|
t.Fatalf("Exception: %v", ex.String())
|
|
}
|
|
}
|
|
vm := r.vm
|
|
t.Logf("stack size: %d", len(vm.stack))
|
|
t.Logf("stashAllocs: %d", vm.stashAllocs)
|
|
|
|
if v == nil && expectedResult != nil || !v.SameAs(expectedResult) {
|
|
t.Fatalf("Result: %+v, expected: %+v", v, expectedResult)
|
|
}
|
|
|
|
if vm.sp != 0 {
|
|
t.Fatalf("sp: %d", vm.sp)
|
|
}
|
|
|
|
if l := len(vm.iterStack); l > 0 {
|
|
t.Fatalf("iter stack is not empty: %d", l)
|
|
}
|
|
}
|
|
|
|
func (r *Runtime) testScriptWithTestLib(script string, expectedResult Value, t *testing.T) {
|
|
_, err := r.RunProgram(testLib())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
r.testScript(script, expectedResult, t)
|
|
}
|
|
|
|
func (r *Runtime) testScriptWithTestLibX(script string, expectedResult Value, t *testing.T) {
|
|
_, err := r.RunProgram(testLib())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, err = r.RunProgram(testLibX())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
r.testScript(script, expectedResult, t)
|
|
}
|
|
|
|
func (r *Runtime) testScript(script string, expectedResult Value, t *testing.T) {
|
|
r.testPrg(MustCompile("test.js", script, false), expectedResult, t)
|
|
}
|
|
|
|
func testScript(script string, expectedResult Value, t *testing.T) {
|
|
New().testScript(script, expectedResult, t)
|
|
}
|
|
|
|
func testScriptWithTestLib(script string, expectedResult Value, t *testing.T) {
|
|
New().testScriptWithTestLib(script, expectedResult, t)
|
|
}
|
|
|
|
func testScriptWithTestLibX(script string, expectedResult Value, t *testing.T) {
|
|
New().testScriptWithTestLibX(script, expectedResult, t)
|
|
}
|
|
|
|
func (r *Runtime) testAsyncFunc(src string, expectedResult Value, t *testing.T) {
|
|
v, err := r.RunScript("test.js", "(async function test() {"+src+"\n})()")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
promise := v.Export().(*Promise)
|
|
switch s := promise.State(); s {
|
|
case PromiseStateFulfilled:
|
|
if res := promise.Result(); res == nil && expectedResult != nil || !res.SameAs(expectedResult) {
|
|
t.Fatalf("Result: %+v, expected: %+v", res, expectedResult)
|
|
}
|
|
case PromiseStateRejected:
|
|
res := promise.Result()
|
|
if resObj, ok := res.(*Object); ok {
|
|
if stack := resObj.Get("stack"); stack != nil {
|
|
t.Fatal(stack.String())
|
|
}
|
|
}
|
|
t.Fatal(res.String())
|
|
default:
|
|
t.Fatalf("Unexpected promise state: %v", s)
|
|
}
|
|
}
|
|
|
|
func (r *Runtime) testAsyncFuncWithTestLib(src string, expectedResult Value, t *testing.T) {
|
|
_, err := r.RunProgram(testLib())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
r.testAsyncFunc(src, expectedResult, t)
|
|
}
|
|
|
|
func (r *Runtime) testAsyncFuncWithTestLibX(src string, expectedResult Value, t *testing.T) {
|
|
_, err := r.RunProgram(testLib())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, err = r.RunProgram(testLibX())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
r.testAsyncFunc(src, expectedResult, t)
|
|
}
|
|
|
|
func testAsyncFunc(src string, expectedResult Value, t *testing.T) {
|
|
New().testAsyncFunc(src, expectedResult, t)
|
|
}
|
|
|
|
func testAsyncFuncWithTestLib(src string, expectedResult Value, t *testing.T) {
|
|
New().testAsyncFuncWithTestLib(src, expectedResult, t)
|
|
}
|
|
|
|
func testAsyncFuncWithTestLibX(src string, expectedResult Value, t *testing.T) {
|
|
New().testAsyncFuncWithTestLibX(src, expectedResult, t)
|
|
}
|
|
|
|
func TestEmptyProgram(t *testing.T) {
|
|
const SCRIPT = `
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestResultEmptyBlock(t *testing.T) {
|
|
const SCRIPT = `
|
|
undefined;
|
|
{}
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestResultVarDecl(t *testing.T) {
|
|
const SCRIPT = `
|
|
7; var x = 1;
|
|
`
|
|
testScript(SCRIPT, valueInt(7), t)
|
|
}
|
|
|
|
func TestResultLexDecl(t *testing.T) {
|
|
const SCRIPT = `
|
|
7; {let x = 1};
|
|
`
|
|
testScript(SCRIPT, valueInt(7), t)
|
|
}
|
|
|
|
func TestResultLexDeclBreak(t *testing.T) {
|
|
const SCRIPT = `
|
|
L:{ 7; {let x = 1; break L;}};
|
|
`
|
|
testScript(SCRIPT, valueInt(7), t)
|
|
}
|
|
|
|
func TestResultLexDeclNested(t *testing.T) {
|
|
const SCRIPT = `
|
|
7; {let x = (function() { return eval("8; {let y = 9}")})()};
|
|
`
|
|
testScript(SCRIPT, valueInt(7), t)
|
|
}
|
|
|
|
func TestErrorProto(t *testing.T) {
|
|
const SCRIPT = `
|
|
var e = new TypeError();
|
|
e.name;
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("TypeError"), t)
|
|
}
|
|
|
|
func TestThis1(t *testing.T) {
|
|
const SCRIPT = `
|
|
function independent() {
|
|
return this.prop;
|
|
}
|
|
var o = {};
|
|
o.b = {g: independent, prop: 42};
|
|
|
|
o.b.g();
|
|
`
|
|
testScript(SCRIPT, intToValue(42), t)
|
|
}
|
|
|
|
func TestThis2(t *testing.T) {
|
|
const SCRIPT = `
|
|
var o = {
|
|
prop: 37,
|
|
f: function() {
|
|
return this.prop;
|
|
}
|
|
};
|
|
|
|
o.f();
|
|
`
|
|
|
|
testScript(SCRIPT, intToValue(37), t)
|
|
}
|
|
|
|
func TestThisStrict(t *testing.T) {
|
|
const SCRIPT = `
|
|
"use strict";
|
|
|
|
Object.defineProperty(Object.prototype, "x", { get: function () { return this; } });
|
|
|
|
(5).x === 5;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestThisNoStrict(t *testing.T) {
|
|
const SCRIPT = `
|
|
Object.defineProperty(Object.prototype, "x", { get: function () { return this; } });
|
|
|
|
(5).x == 5;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestNestedFuncVarResolution(t *testing.T) {
|
|
const SCRIPT = `
|
|
(function outer() {
|
|
var v = 42;
|
|
function inner() {
|
|
return v;
|
|
}
|
|
return inner();
|
|
})();
|
|
`
|
|
testScript(SCRIPT, valueInt(42), t)
|
|
}
|
|
|
|
func TestNestedFuncVarResolution1(t *testing.T) {
|
|
const SCRIPT = `
|
|
function outer(argOuter) {
|
|
var called = 0;
|
|
var inner = function(argInner) {
|
|
if (arguments.length !== 1) {
|
|
throw new Error();
|
|
}
|
|
called++;
|
|
if (argOuter !== 1) {
|
|
throw new Error("argOuter");
|
|
}
|
|
if (argInner !== 2) {
|
|
throw new Error("argInner");
|
|
}
|
|
};
|
|
inner(2);
|
|
}
|
|
outer(1);
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestCallFewerArgs(t *testing.T) {
|
|
const SCRIPT = `
|
|
function A(a, b, c) {
|
|
return String(a) + " " + String(b) + " " + String(c);
|
|
}
|
|
|
|
A(1, 2);
|
|
`
|
|
testScript(SCRIPT, asciiString("1 2 undefined"), t)
|
|
}
|
|
|
|
func TestCallFewerArgsClosureNoArgs(t *testing.T) {
|
|
const SCRIPT = `
|
|
var x;
|
|
function A(a, b, c) {
|
|
var y = a;
|
|
x = function() { return " " + y };
|
|
return String(a) + " " + String(b) + " " + String(c);
|
|
}
|
|
|
|
A(1, 2) + x();
|
|
`
|
|
testScript(SCRIPT, asciiString("1 2 undefined 1"), t)
|
|
}
|
|
|
|
func TestCallFewerArgsClosureArgs(t *testing.T) {
|
|
const SCRIPT = `
|
|
var x;
|
|
function A(a, b, c) {
|
|
var y = b;
|
|
x = function() { return " " + a + " " + y };
|
|
return String(a) + " " + String(b) + " " + String(c);
|
|
}
|
|
|
|
A(1, 2) + x();
|
|
`
|
|
testScript(SCRIPT, asciiString("1 2 undefined 1 2"), t)
|
|
}
|
|
|
|
func TestCallMoreArgs(t *testing.T) {
|
|
const SCRIPT = `
|
|
function A(a, b) {
|
|
var c = 4;
|
|
return a - b + c;
|
|
}
|
|
|
|
A(1, 2, 3);
|
|
`
|
|
testScript(SCRIPT, intToValue(3), t)
|
|
}
|
|
|
|
func TestCallMoreArgsDynamic(t *testing.T) {
|
|
const SCRIPT = `
|
|
function A(a, b) {
|
|
var c = 4;
|
|
if (false) {
|
|
eval("");
|
|
}
|
|
return a - b + c;
|
|
}
|
|
|
|
A(1, 2, 3);
|
|
`
|
|
testScript(SCRIPT, intToValue(3), t)
|
|
}
|
|
|
|
func TestCallLessArgsDynamic(t *testing.T) {
|
|
const SCRIPT = `
|
|
function A(a, b, c) {
|
|
// Make it stashful
|
|
function B() {
|
|
return a;
|
|
}
|
|
return String(a) + " " + String(b) + " " + String(c);
|
|
}
|
|
|
|
A(1, 2);
|
|
`
|
|
testScript(SCRIPT, asciiString("1 2 undefined"), t)
|
|
}
|
|
|
|
func TestCallLessArgsDynamicLocalVar(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f(param) {
|
|
var a = 42;
|
|
if (false) {
|
|
eval("");
|
|
}
|
|
return a;
|
|
}
|
|
f();
|
|
`
|
|
|
|
testScript(SCRIPT, intToValue(42), t)
|
|
}
|
|
|
|
/*
|
|
func TestFib(t *testing.T) {
|
|
testScript(TEST_FIB, valueInt(9227465), t)
|
|
}
|
|
*/
|
|
|
|
func TestNativeCall(t *testing.T) {
|
|
const SCRIPT = `
|
|
var o = Object(1);
|
|
Object.defineProperty(o, "test", {value: 42});
|
|
o.test;
|
|
`
|
|
testScript(SCRIPT, intToValue(42), t)
|
|
}
|
|
|
|
func TestJSCall(t *testing.T) {
|
|
const SCRIPT = `
|
|
function getter() {
|
|
return this.x;
|
|
}
|
|
var o = Object(1);
|
|
o.x = 42;
|
|
Object.defineProperty(o, "test", {get: getter});
|
|
o.test;
|
|
`
|
|
testScript(SCRIPT, intToValue(42), t)
|
|
|
|
}
|
|
|
|
func TestLoop1(t *testing.T) {
|
|
const SCRIPT = `
|
|
function A() {
|
|
var x = 1;
|
|
for (var i = 0; i < 1; i++) {
|
|
var x = 2;
|
|
}
|
|
return x;
|
|
}
|
|
|
|
A();
|
|
`
|
|
testScript(SCRIPT, intToValue(2), t)
|
|
}
|
|
|
|
func TestLoopBreak(t *testing.T) {
|
|
const SCRIPT = `
|
|
function A() {
|
|
var x = 1;
|
|
for (var i = 0; i < 1; i++) {
|
|
break;
|
|
var x = 2;
|
|
}
|
|
return x;
|
|
}
|
|
|
|
A();
|
|
`
|
|
testScript(SCRIPT, intToValue(1), t)
|
|
}
|
|
|
|
func TestForLoopOptionalExpr(t *testing.T) {
|
|
const SCRIPT = `
|
|
function A() {
|
|
var x = 1;
|
|
for (;;) {
|
|
break;
|
|
var x = 2;
|
|
}
|
|
return x;
|
|
}
|
|
|
|
A();
|
|
`
|
|
testScript(SCRIPT, intToValue(1), t)
|
|
}
|
|
|
|
func TestBlockBreak(t *testing.T) {
|
|
const SCRIPT = `
|
|
var rv = 0;
|
|
B1: {
|
|
rv = 1;
|
|
B2: {
|
|
rv = 2;
|
|
break B1;
|
|
}
|
|
rv = 3;
|
|
}
|
|
rv;
|
|
`
|
|
testScript(SCRIPT, intToValue(2), t)
|
|
|
|
}
|
|
|
|
func TestTry(t *testing.T) {
|
|
const SCRIPT = `
|
|
function A() {
|
|
var x = 1;
|
|
try {
|
|
x = 2;
|
|
} catch(e) {
|
|
x = 3;
|
|
} finally {
|
|
x = 4;
|
|
}
|
|
return x;
|
|
}
|
|
|
|
A();
|
|
`
|
|
testScript(SCRIPT, intToValue(4), t)
|
|
}
|
|
|
|
func TestTryOptionalCatchBinding(t *testing.T) {
|
|
const SCRIPT = `
|
|
try {
|
|
throw null;
|
|
} catch {
|
|
}
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestTryCatch(t *testing.T) {
|
|
const SCRIPT = `
|
|
function A() {
|
|
var x;
|
|
try {
|
|
throw 4;
|
|
} catch(e) {
|
|
x = e;
|
|
}
|
|
return x;
|
|
}
|
|
|
|
A();
|
|
`
|
|
testScript(SCRIPT, intToValue(4), t)
|
|
}
|
|
|
|
func TestTryCatchDirectEval(t *testing.T) {
|
|
const SCRIPT = `
|
|
function A() {
|
|
var x;
|
|
try {
|
|
throw 4;
|
|
} catch(e) {
|
|
eval("x = e");
|
|
}
|
|
return x;
|
|
}
|
|
|
|
A();
|
|
`
|
|
testScript(SCRIPT, intToValue(4), t)
|
|
}
|
|
|
|
func TestTryExceptionInCatch(t *testing.T) {
|
|
const SCRIPT = `
|
|
function A() {
|
|
var x;
|
|
try {
|
|
throw 4;
|
|
} catch(e) {
|
|
throw 5;
|
|
}
|
|
return x;
|
|
}
|
|
|
|
var rv;
|
|
try {
|
|
A();
|
|
} catch (e) {
|
|
rv = e;
|
|
}
|
|
rv;
|
|
`
|
|
testScript(SCRIPT, intToValue(5), t)
|
|
}
|
|
|
|
func TestTryContinueInCatch(t *testing.T) {
|
|
const SCRIPT = `
|
|
var c3 = 0, fin3 = 0;
|
|
while (c3 < 2) {
|
|
try {
|
|
throw "ex1";
|
|
} catch(er1) {
|
|
c3 += 1;
|
|
continue;
|
|
} finally {
|
|
fin3 = 1;
|
|
}
|
|
fin3 = 0;
|
|
}
|
|
|
|
fin3;
|
|
`
|
|
testScript(SCRIPT, intToValue(1), t)
|
|
}
|
|
|
|
func TestContinueInWith(t *testing.T) {
|
|
const SCRIPT = `
|
|
var x;
|
|
var o = {x: 0};
|
|
for (var i = 0; i < 2; i++) {
|
|
with(o) {
|
|
x = i;
|
|
if (i === 0) {
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
x;
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestTryContinueInFinally(t *testing.T) {
|
|
const SCRIPT = `
|
|
var c3 = 0, fin3 = 0;
|
|
while (c3 < 2) {
|
|
try {
|
|
throw "ex1";
|
|
} catch(er1) {
|
|
c3 += 1;
|
|
} finally {
|
|
fin3 = 1;
|
|
continue;
|
|
}
|
|
fin3 = 0;
|
|
}
|
|
|
|
fin3;
|
|
`
|
|
testScript(SCRIPT, intToValue(1), t)
|
|
}
|
|
|
|
func TestTryBreakFinallyContinue(t *testing.T) {
|
|
const SCRIPT = `
|
|
for (var i = 0; i < 3; i++) {
|
|
try {
|
|
break;
|
|
} finally {
|
|
continue;
|
|
}
|
|
}
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestTryBreakFinallyContinueWithResult(t *testing.T) {
|
|
const SCRIPT = `
|
|
for (var i = 0; i < 3; i++) {
|
|
try {
|
|
true;
|
|
break;
|
|
} finally {
|
|
continue;
|
|
}
|
|
}
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestTryBreakFinallyContinueWithResult1(t *testing.T) {
|
|
const SCRIPT = `
|
|
for (var i = 0; i < 3; i++) {
|
|
try {
|
|
true;
|
|
break;
|
|
} finally {
|
|
var x = 1;
|
|
continue;
|
|
}
|
|
}
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestTryBreakFinallyContinueWithResultNested(t *testing.T) {
|
|
const SCRIPT = `
|
|
LOOP:
|
|
for (var i = 0; i < 3; i++) {
|
|
try {
|
|
if (true) {
|
|
false; break;
|
|
}
|
|
} finally {
|
|
if (true) {
|
|
true; continue;
|
|
}
|
|
}
|
|
}
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestTryBreakOuterFinallyContinue(t *testing.T) {
|
|
const SCRIPT = `
|
|
let iCount = 0, jCount = 0;
|
|
OUTER: for (let i = 0; i < 1; i++) {
|
|
iCount++;
|
|
for (let j = 0; j < 2; j++) {
|
|
jCount++;
|
|
try {
|
|
break OUTER;
|
|
} finally {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
""+iCount+jCount;
|
|
`
|
|
testScript(SCRIPT, asciiString("12"), t)
|
|
}
|
|
|
|
func TestTryIllegalContinueWithFinallyOverride(t *testing.T) {
|
|
const SCRIPT = `
|
|
L: {
|
|
while (Math.random() > 0.5) {
|
|
try {
|
|
continue L;
|
|
} finally {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
`
|
|
_, err := Compile("", SCRIPT, false)
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
}
|
|
|
|
func TestTryIllegalContinueWithFinallyOverrideNoLabel(t *testing.T) {
|
|
const SCRIPT = `
|
|
L: {
|
|
try {
|
|
continue;
|
|
} finally {
|
|
break L;
|
|
}
|
|
}
|
|
`
|
|
_, err := Compile("", SCRIPT, false)
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
}
|
|
|
|
func TestTryIllegalContinueWithFinallyOverrideDummy(t *testing.T) {
|
|
const SCRIPT = `
|
|
L: {
|
|
while (false) {
|
|
try {
|
|
continue L;
|
|
} finally {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
`
|
|
_, err := Compile("", SCRIPT, false)
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
}
|
|
|
|
func TestTryNoResult(t *testing.T) {
|
|
const SCRIPT = `
|
|
true;
|
|
L:
|
|
try {
|
|
break L;
|
|
} finally {
|
|
}
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestCatchLexicalEnv(t *testing.T) {
|
|
const SCRIPT = `
|
|
function F() {
|
|
try {
|
|
throw 1;
|
|
} catch (e) {
|
|
var x = e;
|
|
}
|
|
return x;
|
|
}
|
|
|
|
F();
|
|
`
|
|
testScript(SCRIPT, intToValue(1), t)
|
|
}
|
|
|
|
func TestThrowType(t *testing.T) {
|
|
const SCRIPT = `
|
|
function Exception(message) {
|
|
this.message = message;
|
|
}
|
|
|
|
|
|
function A() {
|
|
try {
|
|
throw new Exception("boo!");
|
|
} catch(e) {
|
|
return e;
|
|
}
|
|
}
|
|
var thrown = A();
|
|
thrown !== null && typeof thrown === "object" && thrown.constructor === Exception;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestThrowConstructorName(t *testing.T) {
|
|
const SCRIPT = `
|
|
function Exception(message) {
|
|
this.message = message;
|
|
}
|
|
|
|
|
|
function A() {
|
|
try {
|
|
throw new Exception("boo!");
|
|
} catch(e) {
|
|
return e;
|
|
}
|
|
}
|
|
A().constructor.name;
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("Exception"), t)
|
|
}
|
|
|
|
func TestThrowNativeConstructorName(t *testing.T) {
|
|
const SCRIPT = `
|
|
|
|
|
|
function A() {
|
|
try {
|
|
throw new TypeError();
|
|
} catch(e) {
|
|
return e;
|
|
}
|
|
}
|
|
A().constructor.name;
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("TypeError"), t)
|
|
}
|
|
|
|
func TestEmptyTryNoCatch(t *testing.T) {
|
|
const SCRIPT = `
|
|
var called = false;
|
|
try {
|
|
} finally {
|
|
called = true;
|
|
}
|
|
called;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestTryReturnFromCatch(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f(o) {
|
|
var x = 42;
|
|
|
|
function innerf(o) {
|
|
try {
|
|
throw o;
|
|
} catch (e) {
|
|
return x;
|
|
}
|
|
}
|
|
|
|
return innerf(o);
|
|
}
|
|
f({});
|
|
`
|
|
|
|
testScript(SCRIPT, valueInt(42), t)
|
|
}
|
|
|
|
func TestTryCompletionResult(t *testing.T) {
|
|
const SCRIPT = `
|
|
99; do { -99; try { 39 } catch (e) { -1 } finally { break; -2 }; } while (false);
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestIfElse(t *testing.T) {
|
|
const SCRIPT = `
|
|
var rv;
|
|
if (rv === undefined) {
|
|
rv = "passed";
|
|
} else {
|
|
rv = "failed";
|
|
}
|
|
rv;
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("passed"), t)
|
|
}
|
|
|
|
func TestIfElseRetVal(t *testing.T) {
|
|
const SCRIPT = `
|
|
var x;
|
|
if (x === undefined) {
|
|
"passed";
|
|
} else {
|
|
"failed";
|
|
}
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("passed"), t)
|
|
}
|
|
|
|
func TestWhileReturnValue(t *testing.T) {
|
|
const SCRIPT = `
|
|
var x = 0;
|
|
while(true) {
|
|
x = 1;
|
|
break;
|
|
}
|
|
`
|
|
testScript(SCRIPT, intToValue(1), t)
|
|
}
|
|
|
|
func TestIfElseLabel(t *testing.T) {
|
|
const SCRIPT = `
|
|
var x = 0;
|
|
abc: if (true) {
|
|
x = 1;
|
|
break abc;
|
|
}
|
|
`
|
|
testScript(SCRIPT, intToValue(1), t)
|
|
}
|
|
|
|
func TestIfMultipleLabels(t *testing.T) {
|
|
const SCRIPT = `
|
|
var x = 0;
|
|
xyz:abc: if (true) {
|
|
break xyz;
|
|
}
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestBreakOutOfTry(t *testing.T) {
|
|
const SCRIPT = `
|
|
function A() {
|
|
var x = 1;
|
|
B: {
|
|
try {
|
|
x = 2;
|
|
} catch(e) {
|
|
x = 3;
|
|
} finally {
|
|
break B;
|
|
x = 4;
|
|
}
|
|
}
|
|
return x;
|
|
}
|
|
|
|
A();
|
|
`
|
|
testScript(SCRIPT, intToValue(2), t)
|
|
}
|
|
|
|
func TestReturnOutOfTryNested(t *testing.T) {
|
|
const SCRIPT = `
|
|
function A() {
|
|
function nested() {
|
|
try {
|
|
return 1;
|
|
} catch(e) {
|
|
return 2;
|
|
}
|
|
}
|
|
return nested();
|
|
}
|
|
|
|
A();
|
|
`
|
|
testScript(SCRIPT, intToValue(1), t)
|
|
}
|
|
|
|
func TestReturnOutOfTryWithFinally(t *testing.T) {
|
|
const SCRIPT = `
|
|
function test() {
|
|
try {
|
|
return 'Hello, world!';
|
|
} finally {
|
|
const dummy = 'unexpected';
|
|
}
|
|
}
|
|
test();
|
|
`
|
|
testScript(SCRIPT, asciiString("Hello, world!"), t)
|
|
}
|
|
|
|
func TestContinueLoop(t *testing.T) {
|
|
const SCRIPT = `
|
|
function A() {
|
|
var r = 0;
|
|
for (var i = 0; i < 5; i++) {
|
|
if (i > 1) {
|
|
continue;
|
|
}
|
|
r++;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
A();
|
|
`
|
|
testScript(SCRIPT, intToValue(2), t)
|
|
}
|
|
|
|
func TestContinueOutOfTry(t *testing.T) {
|
|
const SCRIPT = `
|
|
function A() {
|
|
var r = 0;
|
|
for (var i = 0; i < 5; i++) {
|
|
try {
|
|
if (i > 1) {
|
|
continue;
|
|
}
|
|
} catch(e) {
|
|
return 99;
|
|
}
|
|
r++;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
A();
|
|
`
|
|
testScript(SCRIPT, intToValue(2), t)
|
|
}
|
|
|
|
func TestThisInCatch(t *testing.T) {
|
|
const SCRIPT = `
|
|
function O() {
|
|
try {
|
|
f();
|
|
} catch (e) {
|
|
this.value = e.toString();
|
|
}
|
|
}
|
|
|
|
function f() {
|
|
throw "ex";
|
|
}
|
|
|
|
var o = new O();
|
|
o.value;
|
|
`
|
|
testScript(SCRIPT, asciiString("ex"), t)
|
|
}
|
|
|
|
func TestNestedTry(t *testing.T) {
|
|
const SCRIPT = `
|
|
var ex;
|
|
try {
|
|
throw "ex1";
|
|
} catch (er1) {
|
|
try {
|
|
throw "ex2";
|
|
} catch (er1) {
|
|
ex = er1;
|
|
}
|
|
}
|
|
ex;
|
|
`
|
|
testScript(SCRIPT, asciiString("ex2"), t)
|
|
}
|
|
|
|
func TestNestedTryInStashlessFunc(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f() {
|
|
var ex1, ex2;
|
|
try {
|
|
throw "ex1";
|
|
} catch (er1) {
|
|
try {
|
|
throw "ex2";
|
|
} catch (er1) {
|
|
ex2 = er1;
|
|
}
|
|
ex1 = er1;
|
|
}
|
|
return ex1 == "ex1" && ex2 == "ex2";
|
|
}
|
|
f();
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestEvalLexicalDecl(t *testing.T) {
|
|
const SCRIPT = `
|
|
eval("let x = true; x;");
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestEvalInCatchInStashlessFunc(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f() {
|
|
var ex;
|
|
try {
|
|
throw "ex1";
|
|
} catch (er1) {
|
|
eval("ex = er1");
|
|
}
|
|
return ex;
|
|
}
|
|
f();
|
|
`
|
|
testScript(SCRIPT, asciiString("ex1"), t)
|
|
}
|
|
|
|
func TestCatchClosureInStashlessFunc(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f() {
|
|
var ex;
|
|
try {
|
|
throw "ex1";
|
|
} catch (er1) {
|
|
return function() {
|
|
return er1;
|
|
}
|
|
}
|
|
}
|
|
f()();
|
|
`
|
|
testScript(SCRIPT, asciiString("ex1"), t)
|
|
}
|
|
|
|
func TestCatchVarNotUsedInStashlessFunc(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f() {
|
|
var ex;
|
|
try {
|
|
throw "ex1";
|
|
} catch (er1) {
|
|
ex = "ok";
|
|
}
|
|
return ex;
|
|
}
|
|
f();
|
|
`
|
|
testScript(SCRIPT, asciiString("ok"), t)
|
|
}
|
|
|
|
func TestNew(t *testing.T) {
|
|
const SCRIPT = `
|
|
function O() {
|
|
this.x = 42;
|
|
}
|
|
|
|
new O().x;
|
|
`
|
|
|
|
testScript(SCRIPT, intToValue(42), t)
|
|
}
|
|
|
|
func TestStringConstructor(t *testing.T) {
|
|
const SCRIPT = `
|
|
function F() {
|
|
return String(33) + " " + String("cows");
|
|
}
|
|
|
|
F();
|
|
`
|
|
testScript(SCRIPT, asciiString("33 cows"), t)
|
|
}
|
|
|
|
func TestError(t *testing.T) {
|
|
const SCRIPT = `
|
|
function F() {
|
|
return new Error("test");
|
|
}
|
|
|
|
var e = F();
|
|
e.message == "test" && e.name == "Error";
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestTypeError(t *testing.T) {
|
|
const SCRIPT = `
|
|
function F() {
|
|
return new TypeError("test");
|
|
}
|
|
|
|
var e = F();
|
|
e.message == "test" && e.name == "TypeError";
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestToString(t *testing.T) {
|
|
const SCRIPT = `
|
|
var o = {x: 42};
|
|
o.toString = function() {
|
|
return String(this.x);
|
|
}
|
|
|
|
var o1 = {};
|
|
o.toString() + " ### " + o1.toString();
|
|
`
|
|
testScript(SCRIPT, asciiString("42 ### [object Object]"), t)
|
|
}
|
|
|
|
func TestEvalOrder(t *testing.T) {
|
|
const SCRIPT = `
|
|
var o = {f: function() {return 42}, x: 0};
|
|
var trace = "";
|
|
|
|
function F1() {
|
|
trace += "First!";
|
|
return o;
|
|
}
|
|
|
|
function F2() {
|
|
trace += "Second!";
|
|
return "f";
|
|
}
|
|
|
|
function F3() {
|
|
trace += "Third!";
|
|
}
|
|
|
|
var rv = F1()[F2()](F3());
|
|
rv += trace;
|
|
rv;
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("42First!Second!Third!"), t)
|
|
}
|
|
|
|
func TestPostfixIncBracket(t *testing.T) {
|
|
const SCRIPT = `
|
|
var o = {x: 42};
|
|
var trace = "";
|
|
|
|
function F1() {
|
|
trace += "First!";
|
|
return o;
|
|
}
|
|
|
|
function F2() {
|
|
trace += "Second!";
|
|
return "x";
|
|
}
|
|
|
|
|
|
var rv = F1()[F2()]++;
|
|
rv + trace + o.x;
|
|
`
|
|
testScript(SCRIPT, asciiString("42First!Second!43"), t)
|
|
}
|
|
|
|
func TestPostfixIncDot(t *testing.T) {
|
|
const SCRIPT = `
|
|
var o = {x: 42};
|
|
var trace = "";
|
|
|
|
function F1() {
|
|
trace += "First!";
|
|
return o;
|
|
}
|
|
|
|
var rv = F1().x++;
|
|
rv + trace + o.x;
|
|
`
|
|
testScript(SCRIPT, asciiString("42First!43"), t)
|
|
}
|
|
|
|
func TestPrefixIncBracket(t *testing.T) {
|
|
const SCRIPT = `
|
|
var o = {x: 42};
|
|
var trace = "";
|
|
|
|
function F1() {
|
|
trace += "First!";
|
|
return o;
|
|
}
|
|
|
|
function F2() {
|
|
trace += "Second!";
|
|
return "x";
|
|
}
|
|
|
|
|
|
var rv = ++F1()[F2()];
|
|
rv + trace + o.x;
|
|
`
|
|
testScript(SCRIPT, asciiString("43First!Second!43"), t)
|
|
}
|
|
|
|
func TestPrefixIncDot(t *testing.T) {
|
|
const SCRIPT = `
|
|
var o = {x: 42};
|
|
var trace = "";
|
|
|
|
function F1() {
|
|
trace += "First!";
|
|
return o;
|
|
}
|
|
|
|
var rv = ++F1().x;
|
|
rv + trace + o.x;
|
|
`
|
|
testScript(SCRIPT, asciiString("43First!43"), t)
|
|
}
|
|
|
|
func TestPostDecObj(t *testing.T) {
|
|
const SCRIPT = `
|
|
var object = {valueOf: function() {return 1}};
|
|
var y = object--;
|
|
var ok = false;
|
|
if (y === 1) {
|
|
ok = true;
|
|
}
|
|
ok;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestPropAcc1(t *testing.T) {
|
|
const SCRIPT = `
|
|
1..toString()
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("1"), t)
|
|
}
|
|
|
|
func TestEvalDirect(t *testing.T) {
|
|
const SCRIPT = `
|
|
var rv = false;
|
|
function foo(){ rv = true; }
|
|
|
|
var o = { };
|
|
function f() {
|
|
try {
|
|
eval("o.bar( foo() );");
|
|
} catch (e) {
|
|
}
|
|
}
|
|
f();
|
|
rv;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestEvalRet(t *testing.T) {
|
|
const SCRIPT = `
|
|
eval("for (var i = 0; i < 3; i++) {i}")
|
|
`
|
|
|
|
testScript(SCRIPT, valueInt(2), t)
|
|
}
|
|
|
|
func TestEvalFunctionDecl(t *testing.T) {
|
|
const SCRIPT = `
|
|
eval("function F() {}")
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestEvalFunctionExpr(t *testing.T) {
|
|
const SCRIPT = `
|
|
eval("(function F() {return 42;})")()
|
|
`
|
|
|
|
testScript(SCRIPT, intToValue(42), t)
|
|
}
|
|
|
|
func TestEvalDirectScope(t *testing.T) {
|
|
const SCRIPT = `
|
|
var __10_4_2_1_3 = "str";
|
|
function testcase() {
|
|
var __10_4_2_1_3 = "str1";
|
|
try {
|
|
throw "error";
|
|
} catch (e) {
|
|
var __10_4_2_1_3 = "str2";
|
|
return eval("__10_4_2_1_3");
|
|
}
|
|
}
|
|
testcase();
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("str2"), t)
|
|
}
|
|
|
|
func TestEvalDirectScope1(t *testing.T) {
|
|
const SCRIPT = `
|
|
'use strict';
|
|
var __10_4_2_1_5 = "str";
|
|
function testcase() {
|
|
var __10_4_2_1_5 = "str1";
|
|
var r = eval("\
|
|
var __10_4_2_1_5 = \'str2\'; \
|
|
eval(\"\'str2\' === __10_4_2_1_5\")\
|
|
");
|
|
return r;
|
|
}
|
|
testcase();
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestEvalDirectCreateBinding(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f() {
|
|
eval("var x = true");
|
|
return x;
|
|
}
|
|
var res = f();
|
|
var thrown = false;
|
|
try {
|
|
x;
|
|
} catch(e) {
|
|
if (e instanceof ReferenceError) {
|
|
thrown = true;
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
res && thrown;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestEvalDirectCreateBinding1(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f() {
|
|
eval("let x = 1; var y = 2; function f1() {return x};");
|
|
assert.throws(ReferenceError, function() { x });
|
|
return ""+y+f1();
|
|
}
|
|
f();
|
|
`
|
|
|
|
testScriptWithTestLib(SCRIPT, asciiString("21"), t)
|
|
}
|
|
|
|
func TestEvalDirectCreateBinding3(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f() {
|
|
let x;
|
|
try {
|
|
eval("var y=1, x=2");
|
|
} catch(e) {}
|
|
return y;
|
|
}
|
|
assert.throws(ReferenceError, f);
|
|
`
|
|
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestEvalGlobalStrict(t *testing.T) {
|
|
const SCRIPT = `
|
|
'use strict';
|
|
var evalStr =
|
|
'for (var x in this) {\n'+
|
|
' if ( x === \'Math\' ) {\n'+
|
|
' }\n'+
|
|
'}\n';
|
|
|
|
eval(evalStr);
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestEvalEmptyStrict(t *testing.T) {
|
|
const SCRIPT = `
|
|
'use strict';
|
|
eval("");
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestEvalFuncDecl(t *testing.T) {
|
|
const SCRIPT = `
|
|
'use strict';
|
|
var funcA = eval("function __funcA(__arg){return __arg;}; __funcA");
|
|
typeof funcA;
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("function"), t)
|
|
}
|
|
|
|
func TestGetAfterSet(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f() {
|
|
var x = 1;
|
|
return x;
|
|
}
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestForLoopRet(t *testing.T) {
|
|
const SCRIPT = `
|
|
for (var i = 0; i < 20; i++) { if (i > 2) {break;} else { i }}
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestForLoopRet1(t *testing.T) {
|
|
const SCRIPT = `
|
|
for (var i = 0; i < 20; i++) { if (i > 2) {42;; {L:{break;}}} else { i }}
|
|
`
|
|
|
|
testScript(SCRIPT, intToValue(42), t)
|
|
}
|
|
|
|
func TestForInLoopRet(t *testing.T) {
|
|
const SCRIPT = `
|
|
var o = [1, 2, 3, 4];
|
|
for (var i in o) { if (i > 2) {break;} else { i }}
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestForInLoopRet1(t *testing.T) {
|
|
const SCRIPT = `
|
|
var o = {};
|
|
o.x = 1;
|
|
o.y = 2;
|
|
for (var i in o) {
|
|
true;
|
|
}
|
|
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestDoWhileLoopRet(t *testing.T) {
|
|
const SCRIPT = `
|
|
var i = 0;
|
|
do {
|
|
if (i > 2) {
|
|
break;
|
|
} else {
|
|
i;
|
|
}
|
|
} while (i++ < 20);
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestDoWhileContinueRet(t *testing.T) {
|
|
const SCRIPT = `
|
|
var i = 0;
|
|
do {
|
|
if (i > 2) {
|
|
true;
|
|
continue;
|
|
} else {
|
|
i;
|
|
}
|
|
} while (i++ < 20);
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestWhileLoopRet(t *testing.T) {
|
|
const SCRIPT = `
|
|
var i; while (i < 20) { if (i > 2) {break;} else { i++ }}
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestLoopRet1(t *testing.T) {
|
|
const SCRIPT = `
|
|
for (var i = 0; i < 20; i++) { }
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestInstanceof(t *testing.T) {
|
|
const SCRIPT = `
|
|
var rv;
|
|
try {
|
|
true();
|
|
} catch (e) {
|
|
rv = e instanceof TypeError;
|
|
}
|
|
rv;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestStrictAssign(t *testing.T) {
|
|
const SCRIPT = `
|
|
'use strict';
|
|
var rv;
|
|
var called = false;
|
|
function F() {
|
|
called = true;
|
|
return 1;
|
|
}
|
|
try {
|
|
x = F();
|
|
} catch (e) {
|
|
rv = e instanceof ReferenceError;
|
|
}
|
|
rv + " " + called;
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("true true"), t)
|
|
}
|
|
|
|
func TestStrictScope(t *testing.T) {
|
|
const SCRIPT = `
|
|
var rv;
|
|
var called = false;
|
|
function F() {
|
|
'use strict';
|
|
x = 1;
|
|
}
|
|
try {
|
|
F();
|
|
} catch (e) {
|
|
rv = e instanceof ReferenceError;
|
|
}
|
|
x = 1;
|
|
rv + " " + x;
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("true 1"), t)
|
|
}
|
|
|
|
func TestStringObj(t *testing.T) {
|
|
const SCRIPT = `
|
|
var s = new String("test");
|
|
s[0] + s[2] + s[1];
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("tse"), t)
|
|
}
|
|
|
|
func TestStringPrimitive(t *testing.T) {
|
|
const SCRIPT = `
|
|
var s = "test";
|
|
s[0] + s[2] + s[1];
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("tse"), t)
|
|
}
|
|
|
|
func TestCallGlobalObject(t *testing.T) {
|
|
const SCRIPT = `
|
|
var rv;
|
|
try {
|
|
this();
|
|
} catch (e) {
|
|
rv = e instanceof TypeError
|
|
}
|
|
rv;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestFuncLength(t *testing.T) {
|
|
const SCRIPT = `
|
|
function F(x, y) {
|
|
|
|
}
|
|
F.length
|
|
`
|
|
|
|
testScript(SCRIPT, intToValue(2), t)
|
|
}
|
|
|
|
func TestNativeFuncLength(t *testing.T) {
|
|
const SCRIPT = `
|
|
eval.length + Object.defineProperty.length + String.length
|
|
`
|
|
|
|
testScript(SCRIPT, intToValue(5), t)
|
|
}
|
|
|
|
func TestArguments(t *testing.T) {
|
|
const SCRIPT = `
|
|
function F() {
|
|
return arguments.length + " " + arguments[1];
|
|
}
|
|
|
|
F(1,2,3)
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("3 2"), t)
|
|
}
|
|
|
|
func TestArgumentsPut(t *testing.T) {
|
|
const SCRIPT = `
|
|
function F(x, y) {
|
|
arguments[0] -= arguments[1];
|
|
return x;
|
|
}
|
|
|
|
F(5, 2)
|
|
`
|
|
|
|
testScript(SCRIPT, intToValue(3), t)
|
|
}
|
|
|
|
func TestArgumentsPutStrict(t *testing.T) {
|
|
const SCRIPT = `
|
|
function F(x, y) {
|
|
'use strict';
|
|
arguments[0] -= arguments[1];
|
|
return x;
|
|
}
|
|
|
|
F(5, 2)
|
|
`
|
|
|
|
testScript(SCRIPT, intToValue(5), t)
|
|
}
|
|
|
|
func TestArgumentsExtra(t *testing.T) {
|
|
const SCRIPT = `
|
|
function F(x, y) {
|
|
return arguments[2];
|
|
}
|
|
|
|
F(1, 2, 42)
|
|
`
|
|
|
|
testScript(SCRIPT, intToValue(42), t)
|
|
}
|
|
|
|
func TestArgumentsExist(t *testing.T) {
|
|
const SCRIPT = `
|
|
function F(x, arguments) {
|
|
return arguments;
|
|
}
|
|
|
|
F(1, 42)
|
|
`
|
|
|
|
testScript(SCRIPT, intToValue(42), t)
|
|
}
|
|
|
|
func TestArgumentsDelete(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f(x) {
|
|
delete arguments[0];
|
|
arguments[0] = 42;
|
|
return x;
|
|
}
|
|
f(1)
|
|
`
|
|
|
|
testScript(SCRIPT, intToValue(1), t)
|
|
}
|
|
|
|
func TestArgumentsInEval(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f() {
|
|
return eval("arguments");
|
|
}
|
|
f(1)[0];
|
|
`
|
|
|
|
testScript(SCRIPT, intToValue(1), t)
|
|
}
|
|
|
|
func TestArgumentsRedeclareInEval(t *testing.T) {
|
|
const SCRIPT = `
|
|
assert.sameValue("arguments" in this, false, "No global 'arguments' binding");
|
|
|
|
function f(p = eval("var arguments = 'param'"), arguments) {}
|
|
assert.throws(SyntaxError, f);
|
|
|
|
assert.sameValue("arguments" in this, false, "No global 'arguments' binding");
|
|
`
|
|
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestArgumentsRedeclareArrow(t *testing.T) {
|
|
const SCRIPT = `
|
|
const oldArguments = globalThis.arguments;
|
|
let count = 0;
|
|
const f = (p = eval("var arguments = 'param'"), q = () => arguments) => {
|
|
var arguments = "local";
|
|
assert.sameValue(arguments, "local", "arguments");
|
|
assert.sameValue(q(), "param", "q");
|
|
count++;
|
|
}
|
|
f();
|
|
assert.sameValue(count, 1);
|
|
assert.sameValue(globalThis.arguments, oldArguments, "globalThis.arguments unchanged");
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestEvalParamWithDef(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f(param = 0) {
|
|
eval("var param = 1");
|
|
return param;
|
|
}
|
|
f();
|
|
`
|
|
|
|
testScript(SCRIPT, valueInt(1), t)
|
|
}
|
|
|
|
func TestArgumentsRedefinedAsLetDyn(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f() {
|
|
let arguments;
|
|
eval(""); // force dynamic scope
|
|
return arguments;
|
|
}
|
|
|
|
f(1,2);
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestWith(t *testing.T) {
|
|
const SCRIPT = `
|
|
var b = 1;
|
|
var o = {a: 41};
|
|
with(o) {
|
|
a += b;
|
|
}
|
|
o.a;
|
|
|
|
`
|
|
|
|
testScript(SCRIPT, intToValue(42), t)
|
|
}
|
|
|
|
func TestWithInFunc(t *testing.T) {
|
|
const SCRIPT = `
|
|
function F() {
|
|
var b = 1;
|
|
var c = 0;
|
|
var o = {a: 40, c: 1};
|
|
with(o) {
|
|
a += b + c;
|
|
}
|
|
return o.a;
|
|
}
|
|
|
|
F();
|
|
`
|
|
|
|
testScript(SCRIPT, intToValue(42), t)
|
|
}
|
|
|
|
func TestAssignNonExtendable(t *testing.T) {
|
|
const SCRIPT = `
|
|
'use strict';
|
|
|
|
function F() {
|
|
this.x = 1;
|
|
}
|
|
|
|
var o = new F();
|
|
Object.preventExtensions(o);
|
|
o.x = 42;
|
|
o.x;
|
|
`
|
|
|
|
testScript(SCRIPT, intToValue(42), t)
|
|
}
|
|
|
|
func TestAssignNonExtendable1(t *testing.T) {
|
|
const SCRIPT = `
|
|
'use strict';
|
|
|
|
function F() {
|
|
}
|
|
|
|
var o = new F();
|
|
var rv;
|
|
|
|
Object.preventExtensions(o);
|
|
try {
|
|
o.x = 42;
|
|
} catch (e) {
|
|
rv = e.constructor === TypeError;
|
|
}
|
|
|
|
rv += " " + o.x;
|
|
rv;
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("true undefined"), t)
|
|
}
|
|
|
|
func TestAssignStrict(t *testing.T) {
|
|
const SCRIPT = `
|
|
'use strict';
|
|
|
|
try {
|
|
eval("eval = 42");
|
|
} catch(e) {
|
|
var rv = e instanceof SyntaxError
|
|
}
|
|
rv;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestIllegalArgmentName(t *testing.T) {
|
|
const SCRIPT = `
|
|
'use strict';
|
|
|
|
try {
|
|
eval("function F(eval) {}");
|
|
} catch (e) {
|
|
var rv = e instanceof SyntaxError
|
|
}
|
|
rv;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestFunction(t *testing.T) {
|
|
const SCRIPT = `
|
|
|
|
var f0 = Function("");
|
|
var f1 = Function("return ' one'");
|
|
var f2 = Function("arg", "return ' ' + arg");
|
|
f0() + f1() + f2("two");
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("undefined one two"), t)
|
|
}
|
|
|
|
func TestFunction1(t *testing.T) {
|
|
const SCRIPT = `
|
|
|
|
var f = function f1(count) {
|
|
if (count == 0) {
|
|
return true;
|
|
}
|
|
return f1(count-1);
|
|
}
|
|
|
|
f(1);
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestFunction2(t *testing.T) {
|
|
const SCRIPT = `
|
|
var trace = "";
|
|
function f(count) {
|
|
trace += "f("+count+")";
|
|
if (count == 0) {
|
|
return;
|
|
}
|
|
return f(count-1);
|
|
}
|
|
|
|
function f1() {
|
|
trace += "f1";
|
|
}
|
|
|
|
var f2 = f;
|
|
f = f1;
|
|
f2(1);
|
|
trace;
|
|
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("f(1)f1"), t)
|
|
}
|
|
|
|
func TestFunctionToString(t *testing.T) {
|
|
const SCRIPT = `
|
|
|
|
Function("arg1", "arg2", "return 42").toString();
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("function anonymous(arg1,arg2\n) {\nreturn 42\n}"), t)
|
|
}
|
|
|
|
func TestObjectLiteral(t *testing.T) {
|
|
const SCRIPT = `
|
|
var getterCalled = false;
|
|
var setterCalled = false;
|
|
|
|
var o = {get x() {getterCalled = true}, set x(_) {setterCalled = true}};
|
|
|
|
o.x;
|
|
o.x = 42;
|
|
|
|
getterCalled && setterCalled;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestConst(t *testing.T) {
|
|
const SCRIPT = `
|
|
|
|
var v1 = true && true;
|
|
var v2 = 1/(-1 * 0);
|
|
var v3 = 1 == 2 || v1;
|
|
var v4 = true && false
|
|
v1 === true && v2 === -Infinity && v3 === v1 && v4 === false;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestConstWhile(t *testing.T) {
|
|
const SCRIPT = `
|
|
var c = 0;
|
|
while (2 + 2 === 4) {
|
|
if (++c > 9) {
|
|
break;
|
|
}
|
|
}
|
|
c === 10;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestConstWhileThrow(t *testing.T) {
|
|
const SCRIPT = `
|
|
var thrown = false;
|
|
try {
|
|
while ('s' in true) {
|
|
break;
|
|
}
|
|
} catch (e) {
|
|
thrown = e instanceof TypeError
|
|
}
|
|
thrown;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestDupParams(t *testing.T) {
|
|
const SCRIPT = `
|
|
function F(x, y, x) {
|
|
return x;
|
|
}
|
|
|
|
F(1, 2);
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestUseUnsuppliedParam(t *testing.T) {
|
|
const SCRIPT = `
|
|
function getMessage(message) {
|
|
if (message === undefined) {
|
|
message = '';
|
|
}
|
|
message += " 123 456";
|
|
return message;
|
|
}
|
|
|
|
getMessage();
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString(" 123 456"), t)
|
|
}
|
|
|
|
func TestForInLetWithInitializer(t *testing.T) {
|
|
const SCRIPT = `for (let x = 3 in {}) { }`
|
|
_, err := Compile("", SCRIPT, false)
|
|
if err == nil {
|
|
t.Fatal("Expected error")
|
|
}
|
|
}
|
|
|
|
func TestForInLoop(t *testing.T) {
|
|
const SCRIPT = `
|
|
function Proto() {}
|
|
Proto.prototype.x = 42;
|
|
var o = new Proto();
|
|
o.y = 44;
|
|
o.x = 45;
|
|
var hasX = false;
|
|
var hasY = false;
|
|
|
|
for (var i in o) {
|
|
switch(i) {
|
|
case "x":
|
|
if (hasX) {
|
|
throw new Error("Already has X");
|
|
}
|
|
hasX = true;
|
|
break;
|
|
case "y":
|
|
if (hasY) {
|
|
throw new Error("Already has Y");
|
|
}
|
|
hasY = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
hasX && hasY;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestWhileLoopResult(t *testing.T) {
|
|
const SCRIPT = `
|
|
while(false);
|
|
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestEmptySwitch(t *testing.T) {
|
|
const SCRIPT = `
|
|
switch(1){}
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestEmptyDoWhile(t *testing.T) {
|
|
const SCRIPT = `
|
|
do {} while(false)
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestSwitch(t *testing.T) {
|
|
const SCRIPT = `
|
|
function F(x) {
|
|
var i = 0;
|
|
switch (x) {
|
|
case 0:
|
|
i++;
|
|
case 1:
|
|
i++;
|
|
default:
|
|
i++;
|
|
case 2:
|
|
i++;
|
|
break;
|
|
case 3:
|
|
i++;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
F(0) + F(1) + F(2) + F(4);
|
|
|
|
`
|
|
|
|
testScript(SCRIPT, intToValue(10), t)
|
|
}
|
|
|
|
func TestSwitchDefFirst(t *testing.T) {
|
|
const SCRIPT = `
|
|
function F(x) {
|
|
var i = 0;
|
|
switch (x) {
|
|
default:
|
|
i++;
|
|
case 0:
|
|
i++;
|
|
case 1:
|
|
i++;
|
|
case 2:
|
|
i++;
|
|
break;
|
|
case 3:
|
|
i++;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
F(0) + F(1) + F(2) + F(4);
|
|
|
|
`
|
|
|
|
testScript(SCRIPT, intToValue(10), t)
|
|
}
|
|
|
|
func TestSwitchResult(t *testing.T) {
|
|
const SCRIPT = `
|
|
var x = 2;
|
|
|
|
switch (x) {
|
|
case 0:
|
|
"zero";
|
|
case 1:
|
|
"one";
|
|
case 2:
|
|
"two";
|
|
break;
|
|
case 3:
|
|
"three";
|
|
default:
|
|
"default";
|
|
}
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("two"), t)
|
|
}
|
|
|
|
func TestSwitchResult1(t *testing.T) {
|
|
const SCRIPT = `
|
|
var x = 0;
|
|
switch (x) { case 0: "two"; case 1: break}
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("two"), t)
|
|
}
|
|
|
|
func TestSwitchResult2(t *testing.T) {
|
|
const SCRIPT = `
|
|
6; switch ("a") { case "a": 7; case "b": }
|
|
`
|
|
|
|
testScript(SCRIPT, valueInt(7), t)
|
|
}
|
|
|
|
func TestSwitchResultJumpIntoEmptyEval(t *testing.T) {
|
|
const SCRIPT = `
|
|
function t(x) {
|
|
return eval("switch(x) { case 1: 2; break; case 2: let x = 1; case 3: x+2; break; case 4: default: 9}");
|
|
}
|
|
""+t(2)+t();
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("39"), t)
|
|
}
|
|
|
|
func TestSwitchResultJumpIntoEmpty(t *testing.T) {
|
|
const SCRIPT = `
|
|
switch(2) { case 1: 2; break; case 2: let x = 1; case 3: x+2; case 4: {let y = 2}; break; default: 9};
|
|
`
|
|
|
|
testScript(SCRIPT, valueInt(3), t)
|
|
}
|
|
|
|
func TestSwitchLexical(t *testing.T) {
|
|
const SCRIPT = `
|
|
switch (true) { case true: let x = 1; }
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestSwitchBreakOuter(t *testing.T) {
|
|
const SCRIPT = `
|
|
LOOP:
|
|
for (let i = 0; i < 10; i++) {
|
|
switch (i) {
|
|
case 0:
|
|
continue;
|
|
case 1:
|
|
let x = 1;
|
|
continue;
|
|
case 2:
|
|
try {
|
|
x++;
|
|
} catch (e) {
|
|
if (e instanceof ReferenceError) {
|
|
break LOOP;
|
|
}
|
|
}
|
|
throw new Error("Exception was not thrown");
|
|
}
|
|
}
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestIfBreakResult(t *testing.T) {
|
|
const SCRIPT = `
|
|
L: {if (true) {42;} break L;}
|
|
`
|
|
|
|
testScript(SCRIPT, intToValue(42), t)
|
|
}
|
|
|
|
func TestSwitchNoMatch(t *testing.T) {
|
|
const SCRIPT = `
|
|
var result;
|
|
var x;
|
|
switch (x) {
|
|
case 0:
|
|
result = "2";
|
|
break;
|
|
}
|
|
|
|
result;
|
|
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestSwitchNoMatchNoDefault(t *testing.T) {
|
|
const SCRIPT = `
|
|
switch (1) {
|
|
case 0:
|
|
}
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestSwitchNoMatchNoDefaultNoResult(t *testing.T) {
|
|
const SCRIPT = `
|
|
switch (1) {
|
|
case 0:
|
|
}
|
|
42;
|
|
`
|
|
|
|
testScript(SCRIPT, intToValue(42), t)
|
|
}
|
|
|
|
func TestSwitchNoMatchNoDefaultNoResultMatch(t *testing.T) {
|
|
const SCRIPT = `
|
|
switch (1) {
|
|
case 1:
|
|
}
|
|
42;
|
|
`
|
|
|
|
testScript(SCRIPT, intToValue(42), t)
|
|
}
|
|
|
|
func TestEmptySwitchNoResult(t *testing.T) {
|
|
const SCRIPT = `
|
|
switch (1) {}
|
|
42;
|
|
`
|
|
|
|
testScript(SCRIPT, intToValue(42), t)
|
|
}
|
|
|
|
func TestGetOwnPropertyNames(t *testing.T) {
|
|
const SCRIPT = `
|
|
var o = {
|
|
prop1: 42,
|
|
prop2: "test"
|
|
}
|
|
|
|
var hasProp1 = false;
|
|
var hasProp2 = false;
|
|
|
|
var names = Object.getOwnPropertyNames(o);
|
|
for (var i in names) {
|
|
var p = names[i];
|
|
switch(p) {
|
|
case "prop1":
|
|
hasProp1 = true;
|
|
break;
|
|
case "prop2":
|
|
hasProp2 = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
hasProp1 && hasProp2;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestArrayLiteral(t *testing.T) {
|
|
const SCRIPT = `
|
|
|
|
var f1Called = false;
|
|
var f2Called = false;
|
|
var f3Called = false;
|
|
var errorThrown = false;
|
|
|
|
function F1() {
|
|
f1Called = true;
|
|
}
|
|
|
|
function F2() {
|
|
f2Called = true;
|
|
}
|
|
|
|
function F3() {
|
|
f3Called = true;
|
|
}
|
|
|
|
|
|
try {
|
|
var a = [F1(), x(F3()), F2()];
|
|
} catch(e) {
|
|
if (e instanceof ReferenceError) {
|
|
errorThrown = true;
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
f1Called && !f2Called && f3Called && errorThrown && a === undefined;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestJumpOutOfReturn(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f() {
|
|
var a;
|
|
if (a == 0) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
f();
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestSwitchJumpOutOfReturn(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f(x) {
|
|
switch(x) {
|
|
case 0:
|
|
break;
|
|
default:
|
|
return x;
|
|
}
|
|
}
|
|
|
|
f(0);
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestSetToReadOnlyPropertyStrictBracket(t *testing.T) {
|
|
const SCRIPT = `
|
|
'use strict';
|
|
|
|
var o = {};
|
|
var thrown = false;
|
|
Object.defineProperty(o, "test", {value: 42, configurable: true});
|
|
try {
|
|
o["test"] = 43;
|
|
} catch (e) {
|
|
thrown = e instanceof TypeError;
|
|
}
|
|
|
|
thrown;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestSetToReadOnlyPropertyStrictDot(t *testing.T) {
|
|
const SCRIPT = `
|
|
'use strict';
|
|
|
|
var o = {};
|
|
var thrown = false;
|
|
Object.defineProperty(o, "test", {value: 42, configurable: true});
|
|
try {
|
|
o.test = 43;
|
|
} catch (e) {
|
|
thrown = e instanceof TypeError;
|
|
}
|
|
|
|
thrown;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestDeleteNonConfigurablePropertyStrictBracket(t *testing.T) {
|
|
const SCRIPT = `
|
|
'use strict';
|
|
|
|
var o = {};
|
|
var thrown = false;
|
|
Object.defineProperty(o, "test", {value: 42});
|
|
try {
|
|
delete o["test"];
|
|
} catch (e) {
|
|
thrown = e instanceof TypeError;
|
|
}
|
|
|
|
thrown;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestDeleteNonConfigurablePropertyStrictDot(t *testing.T) {
|
|
const SCRIPT = `
|
|
'use strict';
|
|
|
|
var o = {};
|
|
var thrown = false;
|
|
Object.defineProperty(o, "test", {value: 42});
|
|
try {
|
|
delete o.test;
|
|
} catch (e) {
|
|
thrown = e instanceof TypeError;
|
|
}
|
|
|
|
thrown;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestCompound1(t *testing.T) {
|
|
const SCRIPT = `
|
|
var x = 0;
|
|
var scope = {x: 1};
|
|
var f;
|
|
with (scope) {
|
|
f = function() {
|
|
x *= (delete scope.x, 2);
|
|
}
|
|
}
|
|
f();
|
|
|
|
scope.x === 2 && x === 0;
|
|
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestCompound2(t *testing.T) {
|
|
const SCRIPT = `
|
|
|
|
var x;
|
|
x = "x";
|
|
x ^= "1";
|
|
|
|
`
|
|
testScript(SCRIPT, intToValue(1), t)
|
|
}
|
|
|
|
func TestDeleteArguments(t *testing.T) {
|
|
defer func() {
|
|
if _, ok := recover().(*CompilerSyntaxError); !ok {
|
|
t.Fatal("Expected syntax error")
|
|
}
|
|
}()
|
|
const SCRIPT = `
|
|
'use strict';
|
|
|
|
function f() {
|
|
delete arguments;
|
|
}
|
|
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestReturnUndefined(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f() {
|
|
return x;
|
|
}
|
|
|
|
var thrown = false;
|
|
try {
|
|
f();
|
|
} catch (e) {
|
|
thrown = e instanceof ReferenceError;
|
|
}
|
|
|
|
thrown;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestForBreak(t *testing.T) {
|
|
const SCRIPT = `
|
|
var supreme, count;
|
|
supreme = 5;
|
|
var __evaluated = eval("for(count=0;;) {if (count===supreme)break;else count++; }");
|
|
if (__evaluated !== void 0) {
|
|
throw new Error('#1: __evaluated === 4. Actual: __evaluated ==='+ __evaluated );
|
|
}
|
|
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestLargeNumberLiteral(t *testing.T) {
|
|
const SCRIPT = `
|
|
var x = 0x800000000000000000000;
|
|
x.toString();
|
|
`
|
|
testScript(SCRIPT, asciiString("9.671406556917033e+24"), t)
|
|
}
|
|
|
|
func TestIncDelete(t *testing.T) {
|
|
const SCRIPT = `
|
|
var o = {x: 1};
|
|
o.x += (delete o.x, 1);
|
|
o.x;
|
|
`
|
|
testScript(SCRIPT, intToValue(2), t)
|
|
}
|
|
|
|
func TestCompoundAssignRefError(t *testing.T) {
|
|
const SCRIPT = `
|
|
var thrown = false;
|
|
try {
|
|
a *= 1;
|
|
} catch (e) {
|
|
if (e instanceof ReferenceError) {
|
|
thrown = true;
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
thrown;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestObjectLiteral__Proto__(t *testing.T) {
|
|
const SCRIPT = `
|
|
var o = {
|
|
__proto__: null,
|
|
test: 42
|
|
}
|
|
|
|
Object.getPrototypeOf(o);
|
|
`
|
|
|
|
testScript(SCRIPT, _null, t)
|
|
}
|
|
|
|
func TestEmptyCodeError(t *testing.T) {
|
|
if _, err := New().RunString(`i`); err == nil {
|
|
t.Fatal("Expected an error")
|
|
} else {
|
|
if e := err.Error(); e != "ReferenceError: i is not defined at <eval>:1:1(0)" {
|
|
t.Fatalf("Unexpected error: '%s'", e)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestForOfArray(t *testing.T) {
|
|
const SCRIPT = `
|
|
var array = [0, 'a', true, false, null, /* hole */, undefined, NaN];
|
|
var i = 0;
|
|
|
|
for (var value of array) {
|
|
assert.sameValue(value, array[i], 'element at index ' + i);
|
|
i++;
|
|
}
|
|
|
|
assert.sameValue(i, 8, 'Visits all elements');
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestForOfReturn(t *testing.T) {
|
|
const SCRIPT = `
|
|
var callCount = 0;
|
|
var iterationCount = 0;
|
|
var iterable = {};
|
|
var x = {
|
|
set attr(_) {
|
|
throw new Test262Error();
|
|
}
|
|
};
|
|
|
|
iterable[Symbol.iterator] = function() {
|
|
return {
|
|
next: function() {
|
|
return { done: false, value: 0 };
|
|
},
|
|
return: function() {
|
|
callCount += 1;
|
|
}
|
|
}
|
|
};
|
|
|
|
assert.throws(Test262Error, function() {
|
|
for (x.attr of iterable) {
|
|
iterationCount += 1;
|
|
}
|
|
});
|
|
|
|
assert.sameValue(iterationCount, 0, 'The loop body is not evaluated');
|
|
assert.sameValue(callCount, 1, 'Iterator is closed');
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestForOfReturn1(t *testing.T) {
|
|
const SCRIPT = `
|
|
var iterable = {};
|
|
var iterationCount = 0;
|
|
|
|
iterable[Symbol.iterator] = function() {
|
|
return {
|
|
next: function() {
|
|
return { done: false, value: null };
|
|
},
|
|
get return() {
|
|
throw new Test262Error();
|
|
}
|
|
};
|
|
};
|
|
|
|
assert.throws(Test262Error, function() {
|
|
for (var x of iterable) {
|
|
iterationCount += 1;
|
|
break;
|
|
}
|
|
});
|
|
|
|
assert.sameValue(iterationCount, 1, 'The loop body is evaluated');
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestForOfLet(t *testing.T) {
|
|
const SCRIPT = `
|
|
var iterCount = 0;
|
|
function f() {}
|
|
for (var let of [23]) {
|
|
f(let);
|
|
if (let != 23) {
|
|
throw new Error("");
|
|
}
|
|
iterCount += 1;
|
|
}
|
|
|
|
iterCount;
|
|
`
|
|
testScript(SCRIPT, valueInt(1), t)
|
|
}
|
|
|
|
func TestForOfLetLet(t *testing.T) {
|
|
const SCRIPT = `
|
|
for (let let of [23]) {
|
|
}
|
|
`
|
|
_, err := Compile("", SCRIPT, false)
|
|
if err == nil {
|
|
t.Fatal("Expected error")
|
|
}
|
|
}
|
|
|
|
func TestForHeadLet(t *testing.T) {
|
|
const SCRIPT = `
|
|
for (let = 0; let < 2; let++);
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestLhsLet(t *testing.T) {
|
|
const SCRIPT = `
|
|
let = 1;
|
|
let;
|
|
`
|
|
testScript(SCRIPT, valueInt(1), t)
|
|
}
|
|
|
|
func TestLetPostfixASI(t *testing.T) {
|
|
const SCRIPT = `
|
|
let
|
|
++
|
|
`
|
|
_, err := Compile("", SCRIPT, false)
|
|
if err == nil {
|
|
t.Fatal("Expected error")
|
|
}
|
|
}
|
|
|
|
func TestIteratorReturnNormal(t *testing.T) {
|
|
const SCRIPT = `
|
|
var iterable = {};
|
|
var iterationCount = 0;
|
|
|
|
iterable[Symbol.iterator] = function() {
|
|
return {
|
|
next: function() {
|
|
return { done: ++iterationCount > 2, value: null };
|
|
},
|
|
get return() {
|
|
throw new Test262Error();
|
|
}
|
|
};
|
|
};
|
|
|
|
for (var x of iterable) {
|
|
}
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestIteratorReturnErrorNested(t *testing.T) {
|
|
const SCRIPT = `
|
|
var returnCalled = {};
|
|
function iter(id) {
|
|
return function() {
|
|
var count = 0;
|
|
return {
|
|
next: function () {
|
|
return {
|
|
value: null,
|
|
done: ++count > 2
|
|
};
|
|
},
|
|
return: function () {
|
|
returnCalled[id] = true;
|
|
throw new Error(id);
|
|
}
|
|
};
|
|
}
|
|
}
|
|
var iterable1 = {};
|
|
iterable1[Symbol.iterator] = iter("1");
|
|
var iterable2 = {};
|
|
iterable2[Symbol.iterator] = iter("2");
|
|
|
|
try {
|
|
for (var i of iterable1) {
|
|
for (var j of iterable2) {
|
|
break;
|
|
}
|
|
}
|
|
throw new Error("no exception was thrown");
|
|
} catch (e) {
|
|
if (e.message !== "2") {
|
|
throw e;
|
|
}
|
|
}
|
|
if (!returnCalled["1"]) {
|
|
throw new Error("no return 1");
|
|
}
|
|
if (!returnCalled["2"]) {
|
|
throw new Error("no return 2");
|
|
}
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestReturnFromForInLoop(t *testing.T) {
|
|
const SCRIPT = `
|
|
(function f() {
|
|
for (var i in {a: 1}) {
|
|
return true;
|
|
}
|
|
})();
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestReturnFromForOfLoop(t *testing.T) {
|
|
const SCRIPT = `
|
|
(function f() {
|
|
for (var i of [1]) {
|
|
return true;
|
|
}
|
|
})();
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestIfStackLeaks(t *testing.T) {
|
|
const SCRIPT = `
|
|
var t = 0;
|
|
if (t === 0) {
|
|
t;
|
|
}
|
|
`
|
|
testScript(SCRIPT, _positiveZero, t)
|
|
}
|
|
|
|
func TestWithCallee(t *testing.T) {
|
|
const SCRIPT = `
|
|
function O() {
|
|
var that = this;
|
|
this.m = function() {
|
|
return this === that;
|
|
}
|
|
}
|
|
with(new O()) {
|
|
m();
|
|
}
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestWithScope(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f(o) {
|
|
var x = 42;
|
|
|
|
function innerf(o) {
|
|
with (o) {
|
|
return x;
|
|
}
|
|
}
|
|
|
|
return innerf(o);
|
|
}
|
|
f({});
|
|
`
|
|
testScript(SCRIPT, valueInt(42), t)
|
|
}
|
|
|
|
func TestEvalCallee(t *testing.T) {
|
|
const SCRIPT = `
|
|
(function () {
|
|
'use strict';
|
|
var v = function() {
|
|
return this === undefined;
|
|
};
|
|
return eval('v()');
|
|
})();
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestEvalBindingDeleteVar(t *testing.T) {
|
|
const SCRIPT = `
|
|
(function () {
|
|
eval("var x = 1");
|
|
return x === 1 && delete x;
|
|
})();
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestEvalBindingDeleteFunc(t *testing.T) {
|
|
const SCRIPT = `
|
|
(function () {
|
|
eval("function x(){}");
|
|
return typeof x === "function" && delete x;
|
|
})();
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestDeleteGlobalLexical(t *testing.T) {
|
|
const SCRIPT = `
|
|
let x;
|
|
delete x;
|
|
`
|
|
testScript(SCRIPT, valueFalse, t)
|
|
}
|
|
|
|
func TestDeleteGlobalEval(t *testing.T) {
|
|
const SCRIPT = `
|
|
eval("var x");
|
|
delete x;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestTryResultEmpty(t *testing.T) {
|
|
const SCRIPT = `
|
|
1; try { } finally { }
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestTryResultEmptyCatch(t *testing.T) {
|
|
const SCRIPT = `
|
|
1; try { throw null } catch(e) { }
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestTryResultEmptyContinueLoop(t *testing.T) {
|
|
const SCRIPT = `
|
|
for (var i = 0; i < 2; i++) { try {throw null;} catch(e) {continue;} 'bad'}
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestTryEmptyCatchStackLeak(t *testing.T) {
|
|
const SCRIPT = `
|
|
(function() {
|
|
var f;
|
|
// Make sure the outer function is not stashless.
|
|
(function() {
|
|
f++;
|
|
})();
|
|
try {
|
|
throw new Error();
|
|
} catch(e) {}
|
|
})();
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestTryThrowEmptyCatch(t *testing.T) {
|
|
const SCRIPT = `
|
|
try {
|
|
throw new Error();
|
|
}
|
|
catch (e) {}
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestFalsyLoopBreak(t *testing.T) {
|
|
const SCRIPT = `
|
|
while(false) {
|
|
break;
|
|
}
|
|
for(;false;) {
|
|
break;
|
|
}
|
|
undefined;
|
|
`
|
|
MustCompile("", SCRIPT, false)
|
|
}
|
|
|
|
func TestFalsyLoopBreakWithResult(t *testing.T) {
|
|
const SCRIPT = `
|
|
while(false) {
|
|
break;
|
|
}
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestDummyCompile(t *testing.T) {
|
|
const SCRIPT = `
|
|
'use strict';
|
|
|
|
for (;false;) {
|
|
eval = 1;
|
|
}
|
|
`
|
|
|
|
_, err := Compile("", SCRIPT, false)
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
}
|
|
|
|
func TestDummyCompileForUpdate(t *testing.T) {
|
|
const SCRIPT = `
|
|
'use strict';
|
|
|
|
for (;false;eval=1) {
|
|
}
|
|
`
|
|
|
|
_, err := Compile("", SCRIPT, false)
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
}
|
|
|
|
func TestObjectLiteralWithNumericKeys(t *testing.T) {
|
|
const SCRIPT = `
|
|
var o = {1e3: true};
|
|
var keys = Object.keys(o);
|
|
var o1 = {get 1e3() {return true;}};
|
|
var keys1 = Object.keys(o1);
|
|
var o2 = {1e21: true};
|
|
var keys2 = Object.keys(o2);
|
|
let o3 = {0(){return true}};
|
|
keys.length === 1 && keys[0] === "1000" &&
|
|
keys1.length === 1 && keys1[0] === "1000" && o1[1e3] === true &&
|
|
keys2.length === 1 && keys2[0] === "1e+21" && o3[0]();
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestEscapedObjectPropertyKeys(t *testing.T) {
|
|
const SCRIPT = `
|
|
var obj = {
|
|
w\u0069th: 42
|
|
};
|
|
var obj = {
|
|
with() {42}
|
|
};
|
|
`
|
|
|
|
_, err := Compile("", SCRIPT, false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestEscapedKeywords(t *testing.T) {
|
|
const SCRIPT = `r\u0065turn;`
|
|
_, err := Compile("", SCRIPT, false)
|
|
if err == nil {
|
|
t.Fatal("Expected error")
|
|
}
|
|
}
|
|
|
|
func TestEscapedLet(t *testing.T) {
|
|
const SCRIPT = `
|
|
this.let = 0;
|
|
|
|
l\u0065t // ASI
|
|
a;
|
|
|
|
// If the parser treated the previous escaped "let" as a lexical declaration,
|
|
// this variable declaration will result an early syntax error.
|
|
var a;
|
|
`
|
|
_, err := Compile("", SCRIPT, false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestObjectLiteralFuncProps(t *testing.T) {
|
|
const SCRIPT = `
|
|
(function() {
|
|
'use strict';
|
|
var o = {
|
|
eval: function() {return 1;},
|
|
arguments() {return 2;},
|
|
test: function test1() {}
|
|
}
|
|
assert.sameValue(o.eval.name, "eval");
|
|
assert.sameValue(o.arguments.name, "arguments");
|
|
assert.sameValue(o.eval(), 1);
|
|
assert.sameValue(o.arguments(), 2);
|
|
assert.sameValue(o.test.name, "test1");
|
|
})();
|
|
`
|
|
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestFuncName(t *testing.T) {
|
|
const SCRIPT = `
|
|
var method = 1;
|
|
var o = {
|
|
method: function() {
|
|
return method;
|
|
},
|
|
method1: function method() {
|
|
return method;
|
|
}
|
|
}
|
|
o.method() === 1 && o.method1() === o.method1;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestFuncNameAssign(t *testing.T) {
|
|
const SCRIPT = `
|
|
var f = function() {};
|
|
var f1;
|
|
f1 = function() {};
|
|
let f2 = function() {};
|
|
|
|
f.name === "f" && f1.name === "f1" && f2.name === "f2";
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestLexicalDeclGlobal(t *testing.T) {
|
|
const SCRIPT = `
|
|
if (true) {
|
|
let it = "be";
|
|
if (it !== "be") {
|
|
throw new Error(it);
|
|
}
|
|
}
|
|
let thrown = false;
|
|
try {
|
|
it;
|
|
} catch(e) {
|
|
if (e instanceof ReferenceError) {
|
|
thrown = true;
|
|
}
|
|
}
|
|
thrown;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestLexicalDeclFunction(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f() {
|
|
if (true) {
|
|
let it = "be";
|
|
if (it !== "be") {
|
|
throw new Error(it);
|
|
}
|
|
}
|
|
let thrown = false;
|
|
try {
|
|
it;
|
|
} catch(e) {
|
|
if (e instanceof ReferenceError) {
|
|
thrown = true;
|
|
}
|
|
}
|
|
return thrown;
|
|
}
|
|
f();
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestLexicalDynamicScope(t *testing.T) {
|
|
const SCRIPT = `
|
|
const global = 1;
|
|
function f() {
|
|
const func = global + 1;
|
|
function inner() {
|
|
function assertThrows(fn) {
|
|
let thrown = false;
|
|
try {
|
|
fn();
|
|
} catch (e) {
|
|
if (e instanceof TypeError) {
|
|
thrown = true;
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
if (!thrown) {
|
|
throw new Error("Did not throw");
|
|
}
|
|
}
|
|
|
|
assertThrows(function() {
|
|
func++;
|
|
});
|
|
assertThrows(function() {
|
|
global++;
|
|
});
|
|
|
|
assertThrows(function() {
|
|
eval("func++");
|
|
});
|
|
assertThrows(function() {
|
|
eval("global++");
|
|
});
|
|
|
|
return eval("func + 1");
|
|
}
|
|
return inner();
|
|
}
|
|
f();
|
|
`
|
|
testScript(SCRIPT, valueInt(3), t)
|
|
}
|
|
|
|
func TestLexicalDynamicScope1(t *testing.T) {
|
|
const SCRIPT = `
|
|
(function() {
|
|
const x = 1 * 4;
|
|
return (function() {
|
|
eval("");
|
|
return x;
|
|
})();
|
|
})();
|
|
`
|
|
testScript(SCRIPT, intToValue(4), t)
|
|
}
|
|
|
|
func TestLexicalDynamicScope2(t *testing.T) {
|
|
const SCRIPT = `
|
|
(function() {
|
|
const x = 1 + 3;
|
|
var y = 2 * 2;
|
|
eval("");
|
|
return x;
|
|
})();
|
|
`
|
|
testScript(SCRIPT, intToValue(4), t)
|
|
}
|
|
|
|
func TestNonStrictLet(t *testing.T) {
|
|
const SCRIPT = `
|
|
var let = 1;
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestStrictLet(t *testing.T) {
|
|
const SCRIPT = `
|
|
var let = 1;
|
|
`
|
|
|
|
_, err := Compile("", SCRIPT, true)
|
|
if err == nil {
|
|
t.Fatal("Expected an error")
|
|
}
|
|
}
|
|
|
|
func TestLetLet(t *testing.T) {
|
|
const SCRIPT = `
|
|
let let = 1;
|
|
`
|
|
|
|
_, err := Compile("", SCRIPT, false)
|
|
if err == nil {
|
|
t.Fatal("Expected an error")
|
|
}
|
|
}
|
|
|
|
func TestLetASI(t *testing.T) {
|
|
const SCRIPT = `
|
|
while (false) let // ASI
|
|
x = 1;
|
|
`
|
|
|
|
_, err := Compile("", SCRIPT, false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestLetASI1(t *testing.T) {
|
|
const SCRIPT = `
|
|
let
|
|
x = 1;
|
|
`
|
|
|
|
_, err := Compile("", SCRIPT, true)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestLetNoASI(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f() {}let
|
|
x = 1;
|
|
`
|
|
|
|
_, err := Compile("", SCRIPT, true)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestLetNoASI1(t *testing.T) {
|
|
const SCRIPT = `
|
|
let
|
|
let = 1;
|
|
`
|
|
|
|
_, err := Compile("", SCRIPT, false)
|
|
if err == nil {
|
|
t.Fatal("Expected error")
|
|
}
|
|
}
|
|
|
|
func TestLetArrayWithNewline(t *testing.T) {
|
|
const SCRIPT = `
|
|
with ({}) let
|
|
[a] = 0;
|
|
`
|
|
|
|
_, err := Compile("", SCRIPT, false)
|
|
if err == nil {
|
|
t.Fatal("Expected error")
|
|
}
|
|
}
|
|
|
|
func TestDynamicUninitedVarAccess(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f() {
|
|
var x;
|
|
return eval("x");
|
|
}
|
|
f();
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestLexicalForLoopNoClosure(t *testing.T) {
|
|
const SCRIPT = `
|
|
let sum = 0;
|
|
for (let i = 0; i < 3; i++) {
|
|
sum += i;
|
|
}
|
|
sum;
|
|
`
|
|
testScript(SCRIPT, valueInt(3), t)
|
|
}
|
|
|
|
func TestLexicalForLoopClosure(t *testing.T) {
|
|
const SCRIPT = `
|
|
var f = [];
|
|
for (let i = 0; i < 3; i++) {
|
|
f.push(function() {
|
|
return i;
|
|
});
|
|
}
|
|
f.length === 3 && f[0]() === 0 && f[1]() === 1 && f[2]() === 2;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestLexicalForLoopClosureInNext(t *testing.T) {
|
|
const SCRIPT = `
|
|
const a = [];
|
|
for (let i = 0; i < 5; a.push(function () { return i; }), ++i) { }
|
|
let res = "";
|
|
for (let k = 0; k < 5; ++k) {
|
|
res += ""+a[k]();
|
|
}
|
|
res;
|
|
`
|
|
testScript(SCRIPT, asciiString("12345"), t)
|
|
}
|
|
|
|
func TestVarForLoop(t *testing.T) {
|
|
const SCRIPT = `
|
|
var f = [];
|
|
for (var i = 0, j = 0; i < 3; i++) {
|
|
f.push(function() {
|
|
return i;
|
|
});
|
|
}
|
|
f.length === 3 && f[0]() === 3 && f[1]() === 3 && f[2]() === 3;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestLexicalForOfLoop(t *testing.T) {
|
|
const SCRIPT = `
|
|
var f = [];
|
|
for (let i of [0, 1, 2]) {
|
|
f.push(function() {
|
|
return i;
|
|
});
|
|
}
|
|
f.length === 3 && f[0]() === 0 && f[1]() === 1 && f[2]() === 2;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestLexicalForOfLoopContBreak(t *testing.T) {
|
|
const SCRIPT = `
|
|
const f = [];
|
|
for (let i of [0, 1, 2, 3, 4, 5]) {
|
|
if (i % 2) continue;
|
|
f.push(function() {
|
|
return i;
|
|
});
|
|
if (i > 2) break;
|
|
}
|
|
let res = "";
|
|
f.forEach(function(item) {res += item()});
|
|
f.length === 3 && res === "024";
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestVarBlockConflict(t *testing.T) {
|
|
const SCRIPT = `
|
|
let x;
|
|
{
|
|
if (false) {
|
|
var x;
|
|
}
|
|
}
|
|
`
|
|
_, err := Compile("", SCRIPT, false)
|
|
if err == nil {
|
|
t.Fatal("Expected an error")
|
|
}
|
|
}
|
|
|
|
func TestVarBlockConflictEval(t *testing.T) {
|
|
const SCRIPT = `
|
|
assert.throws(SyntaxError, function() {
|
|
let x;
|
|
{
|
|
if (true) {
|
|
eval("var x");
|
|
}
|
|
}
|
|
});
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestVarBlockNoConflict(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f() {
|
|
let x;
|
|
function ff() {
|
|
{
|
|
var x = 3;
|
|
}
|
|
}
|
|
ff();
|
|
}
|
|
f();
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestVarBlockNoConflictEval(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f() {
|
|
let x;
|
|
function ff() {
|
|
{
|
|
eval("var x = 3");
|
|
}
|
|
}
|
|
ff();
|
|
}
|
|
f();
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestVarDeclCorrectScope(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f() {
|
|
{
|
|
let z;
|
|
eval("var x = 3");
|
|
}
|
|
return x;
|
|
}
|
|
f();
|
|
`
|
|
testScript(SCRIPT, valueInt(3), t)
|
|
}
|
|
|
|
func TestLexicalCatch(t *testing.T) {
|
|
const SCRIPT = `
|
|
try {
|
|
throw null;
|
|
} catch (e) {
|
|
let x = 1;
|
|
function f() {}
|
|
e;
|
|
}
|
|
`
|
|
testScript(SCRIPT, _null, t)
|
|
}
|
|
|
|
func TestArgumentsLexicalDecl(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f1() {
|
|
let arguments;
|
|
return arguments;
|
|
}
|
|
f1(42);
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestArgumentsLexicalDeclAssign(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f1() {
|
|
let arguments = arguments;
|
|
return a;
|
|
}
|
|
assert.throws(ReferenceError, function() {
|
|
f1(42);
|
|
});
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestLexicalConstModifyFromEval(t *testing.T) {
|
|
const SCRIPT = `
|
|
const x = 1;
|
|
function f() {
|
|
eval("x = 2");
|
|
}
|
|
assert.throws(TypeError, function() {
|
|
f();
|
|
});
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestLexicalStrictNames(t *testing.T) {
|
|
const SCRIPT = `let eval = 1;`
|
|
|
|
_, err := Compile("", SCRIPT, true)
|
|
if err == nil {
|
|
t.Fatal("Expected an error")
|
|
}
|
|
}
|
|
|
|
func TestAssignAfterStackExpand(t *testing.T) {
|
|
// make sure the reference to the variable x does not remain stale after the stack is copied
|
|
const SCRIPT = `
|
|
function f() {
|
|
let sum = 0;
|
|
for (let i = 0; i < arguments.length; i++) {
|
|
sum += arguments[i];
|
|
}
|
|
return sum;
|
|
}
|
|
function testAssignment() {
|
|
var x = 0;
|
|
var scope = {};
|
|
|
|
with (scope) {
|
|
x = (scope.x = f(0, 0, 0, 0, 0, 0, 1, 1), 1);
|
|
}
|
|
|
|
if (scope.x !== 2) {
|
|
throw new Error('#1: scope.x === 2. Actual: ' + (scope.x));
|
|
}
|
|
if (x !== 1) {
|
|
throw new Error('#2: x === 1. Actual: ' + (x));
|
|
}
|
|
}
|
|
testAssignment();
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestArgAccessFromDynamicStash(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f(arg) {
|
|
function test() {
|
|
eval("");
|
|
return a;
|
|
}
|
|
return arg;
|
|
}
|
|
f(true);
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestLoadMixedLex(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f() {
|
|
let a = 1;
|
|
{
|
|
function inner() {
|
|
eval("var a = true");
|
|
return a;
|
|
}
|
|
return inner();
|
|
}
|
|
}
|
|
f();
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestObjectLiteralSpread(t *testing.T) {
|
|
const SCRIPT = `
|
|
let src = {prop1: 1};
|
|
Object.defineProperty(src, "prop2", {value: 2, configurable: true});
|
|
Object.defineProperty(src, "prop3", {value: 3, enumerable: true, configurable: true});
|
|
let target = {prop4: 4, ...src};
|
|
assert(deepEqual(target, {prop1: 1, prop3: 3, prop4: 4}));
|
|
`
|
|
testScriptWithTestLibX(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestArrayLiteralSpread(t *testing.T) {
|
|
const SCRIPT = `
|
|
let a1 = [1, 2];
|
|
let a2 = [3, 4];
|
|
let a = [...a1, 0, ...a2, 1];
|
|
assert(compareArray(a, [1, 2, 0, 3, 4, 1]));
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestObjectAssignmentPattern(t *testing.T) {
|
|
const SCRIPT = `
|
|
let a, b, c;
|
|
({a, b, c=3} = {a: 1, b: 2});
|
|
assert.sameValue(a, 1, "a");
|
|
assert.sameValue(b, 2, "b");
|
|
assert.sameValue(c, 3, "c");
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestObjectAssignmentPatternNoDyn(t *testing.T) {
|
|
const SCRIPT = `
|
|
(function() {
|
|
let a, b, c;
|
|
({a, b, c=3} = {a: 1, b: 2});
|
|
assert.sameValue(a, 1, "a");
|
|
assert.sameValue(b, 2, "b");
|
|
assert.sameValue(c, 3, "c");
|
|
})();
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestObjectAssignmentPatternNested(t *testing.T) {
|
|
const SCRIPT = `
|
|
let a, b, c, d;
|
|
({a, b, c: {d} = 3} = {a: 1, b: 2, c: {d: 4}});
|
|
assert.sameValue(a, 1, "a");
|
|
assert.sameValue(b, 2, "b");
|
|
assert.sameValue(c, undefined, "c");
|
|
assert.sameValue(d, 4, "d");
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestObjectAssignmentPatternEvalOrder(t *testing.T) {
|
|
const SCRIPT = `
|
|
let trace = "";
|
|
let target_obj = {};
|
|
|
|
function src() {
|
|
trace += "src(),";
|
|
return {
|
|
get a() {
|
|
trace += "get a,";
|
|
return "a";
|
|
}
|
|
}
|
|
}
|
|
|
|
function prop1() {
|
|
trace += "prop1(),"
|
|
return {
|
|
toString: function() {
|
|
trace += "prop1-to-string(),";
|
|
return "a";
|
|
}
|
|
}
|
|
}
|
|
|
|
function prop2() {
|
|
trace += "prop2(),";
|
|
return {
|
|
toString: function() {
|
|
trace += "prop2-to-string(),";
|
|
return "b";
|
|
}
|
|
}
|
|
}
|
|
|
|
function target() {
|
|
trace += "target(),"
|
|
return target_obj;
|
|
}
|
|
|
|
let a, b;
|
|
|
|
({[prop1()]: target().a, [prop2()]: b} = src());
|
|
if (target_obj.a !== "a") {
|
|
throw new Error("target_obj.a="+target_obj.a);
|
|
}
|
|
trace;
|
|
`
|
|
testScript(SCRIPT, asciiString("src(),prop1(),prop1-to-string(),target(),get a,prop2(),prop2-to-string(),"), t)
|
|
}
|
|
|
|
func TestArrayAssignmentPatternEvalOrder(t *testing.T) {
|
|
const SCRIPT = `
|
|
let trace = "";
|
|
|
|
let src_arr = {
|
|
[Symbol.iterator]: function() {
|
|
let done = false;
|
|
return {
|
|
next: function() {
|
|
trace += "next,";
|
|
if (!done) {
|
|
done = true;
|
|
return {value: 0};
|
|
}
|
|
return {done: true};
|
|
},
|
|
return: function() {
|
|
trace += "return,";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function src() {
|
|
trace += "src(),";
|
|
return src_arr;
|
|
}
|
|
|
|
let tgt = {
|
|
get a() {
|
|
trace += "get a,";
|
|
return "a";
|
|
},
|
|
get b() {
|
|
trace += "get b,";
|
|
return "b";
|
|
}
|
|
}
|
|
|
|
function target() {
|
|
trace += "target(),";
|
|
return tgt;
|
|
}
|
|
|
|
function default_a() {
|
|
trace += "default a,";
|
|
return "def_a";
|
|
}
|
|
|
|
function default_b() {
|
|
trace += "default b,";
|
|
return "def_b";
|
|
}
|
|
|
|
([target().a = default_a(), target().b = default_b()] = src());
|
|
trace;
|
|
`
|
|
testScript(SCRIPT, asciiString("src(),target(),next,target(),next,default b,"), t)
|
|
}
|
|
|
|
func TestObjectAssignPatternRest(t *testing.T) {
|
|
const SCRIPT = `
|
|
let a, b, c, d;
|
|
({a, b, c, ...d} = {a: 1, b: 2, d: 4});
|
|
assert.sameValue(a, 1, "a");
|
|
assert.sameValue(b, 2, "b");
|
|
assert.sameValue(c, undefined, "c");
|
|
assert(deepEqual(d, {d: 4}), "d");
|
|
`
|
|
testScriptWithTestLibX(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestObjectBindPattern(t *testing.T) {
|
|
const SCRIPT = `
|
|
let {a, b, c, ...d} = {a: 1, b: 2, d: 4};
|
|
assert.sameValue(a, 1, "a");
|
|
assert.sameValue(b, 2, "b");
|
|
assert.sameValue(c, undefined, "c");
|
|
assert(deepEqual(d, {d: 4}), "d");
|
|
|
|
var { x: y, } = { x: 23 };
|
|
|
|
assert.sameValue(y, 23);
|
|
|
|
assert.throws(ReferenceError, function() {
|
|
x;
|
|
});
|
|
`
|
|
testScriptWithTestLibX(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestObjLiteralShorthandWithInitializer(t *testing.T) {
|
|
const SCRIPT = `
|
|
o = {a=1};
|
|
`
|
|
_, err := Compile("", SCRIPT, false)
|
|
if err == nil {
|
|
t.Fatal("Expected an error")
|
|
}
|
|
}
|
|
|
|
func TestObjLiteralShorthandLetStringLit(t *testing.T) {
|
|
const SCRIPT = `
|
|
o = {"let"};
|
|
`
|
|
_, err := Compile("", SCRIPT, false)
|
|
if err == nil {
|
|
t.Fatal("Expected an error")
|
|
}
|
|
}
|
|
|
|
func TestObjLiteralComputedKeys(t *testing.T) {
|
|
const SCRIPT = `
|
|
let o = {
|
|
get [Symbol.toString]() {
|
|
}
|
|
}
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestObjLiteralComputedKeysEvalOrder(t *testing.T) {
|
|
const SCRIPT = `
|
|
let trace = [];
|
|
function key() {
|
|
trace.push("key");
|
|
return {
|
|
toString: function() {
|
|
trace.push("key-toString");
|
|
return "key";
|
|
}
|
|
}
|
|
}
|
|
function val() {
|
|
trace.push("val");
|
|
return "val";
|
|
}
|
|
|
|
const _ = {
|
|
[key()]: val(),
|
|
}
|
|
|
|
trace.join(",");
|
|
`
|
|
testScript(SCRIPT, asciiString("key,key-toString,val"), t)
|
|
}
|
|
|
|
func TestArrayAssignPattern(t *testing.T) {
|
|
const SCRIPT = `
|
|
let a, b;
|
|
([a, b] = [1, 2]);
|
|
a === 1 && b === 2;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestArrayAssignPattern1(t *testing.T) {
|
|
const SCRIPT = `
|
|
let a, b;
|
|
([a = 3, b = 2] = [1]);
|
|
a === 1 && b === 2;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestArrayAssignPatternLHS(t *testing.T) {
|
|
const SCRIPT = `
|
|
let a = {};
|
|
[ a.b, a['c'] = 2 ] = [1];
|
|
a.b === 1 && a.c === 2;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestArrayAssignPatternElision(t *testing.T) {
|
|
const SCRIPT = `
|
|
let a, b;
|
|
([a,, b] = [1, 4, 2]);
|
|
a === 1 && b === 2;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestArrayAssignPatternRestPattern(t *testing.T) {
|
|
const SCRIPT = `
|
|
let a, b, z;
|
|
[ z, ...[a, b] ] = [0, 1, 2];
|
|
z === 0 && a === 1 && b === 2;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestArrayBindingPattern(t *testing.T) {
|
|
const SCRIPT = `
|
|
let [a, b] = [1, 2];
|
|
a === 1 && b === 2;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestObjectPatternShorthandInit(t *testing.T) {
|
|
const SCRIPT = `
|
|
[...{ x = 1 }] = [];
|
|
x;
|
|
`
|
|
testScript(SCRIPT, valueInt(1), t)
|
|
}
|
|
|
|
func TestArrayBindingPatternRestPattern(t *testing.T) {
|
|
const SCRIPT = `
|
|
const [a, b, ...[c, d]] = [1, 2, 3, 4];
|
|
a === 1 && b === 2 && c === 3 && d === 4;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestForVarPattern(t *testing.T) {
|
|
const SCRIPT = `
|
|
var o = {a: 1};
|
|
var trace = "";
|
|
for (var [key, value] of Object.entries(o)) {
|
|
trace += key+":"+value;
|
|
}
|
|
trace;
|
|
`
|
|
testScript(SCRIPT, asciiString("a:1"), t)
|
|
}
|
|
|
|
func TestForLexPattern(t *testing.T) {
|
|
const SCRIPT = `
|
|
var o = {a: 1};
|
|
var trace = "";
|
|
for (const [key, value] of Object.entries(o)) {
|
|
trace += key+":"+value;
|
|
}
|
|
trace;
|
|
`
|
|
testScript(SCRIPT, asciiString("a:1"), t)
|
|
}
|
|
|
|
func TestBindingPatternRestTrailingComma(t *testing.T) {
|
|
const SCRIPT = `
|
|
const [a, b, ...rest,] = [];
|
|
`
|
|
_, err := Compile("", SCRIPT, false)
|
|
if err == nil {
|
|
t.Fatal("Expected an error")
|
|
}
|
|
}
|
|
|
|
func TestAssignPatternRestTrailingComma(t *testing.T) {
|
|
const SCRIPT = `
|
|
([a, b, ...rest,] = []);
|
|
`
|
|
_, err := Compile("", SCRIPT, false)
|
|
if err == nil {
|
|
t.Fatal("Expected an error")
|
|
}
|
|
}
|
|
|
|
func TestFuncParamInitializerSimple(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f(a = 1) {
|
|
return a;
|
|
}
|
|
""+f()+f(2);
|
|
`
|
|
testScript(SCRIPT, asciiString("12"), t)
|
|
}
|
|
|
|
func TestFuncParamObjectPatternSimple(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f({a, b} = {a: 1, b: 2}) {
|
|
return "" + a + b;
|
|
}
|
|
""+f()+" "+f({a: 3, b: 4});
|
|
`
|
|
testScript(SCRIPT, asciiString("12 34"), t)
|
|
}
|
|
|
|
func TestFuncParamRestStackSimple(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f(arg1, ...rest) {
|
|
return rest;
|
|
}
|
|
let ar = f(1, 2, 3);
|
|
ar.join(",");
|
|
`
|
|
testScript(SCRIPT, asciiString("2,3"), t)
|
|
}
|
|
|
|
func TestFuncParamRestStashSimple(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f(arg1, ...rest) {
|
|
eval("true");
|
|
return rest;
|
|
}
|
|
let ar = f(1, 2, 3);
|
|
ar.join(",");
|
|
`
|
|
testScript(SCRIPT, asciiString("2,3"), t)
|
|
}
|
|
|
|
func TestRestArgsNotInStash(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f(...rest) {
|
|
() => rest;
|
|
return rest.length;
|
|
}
|
|
f(1,2);
|
|
`
|
|
testScript(SCRIPT, valueInt(2), t)
|
|
}
|
|
|
|
func TestRestArgsInStash(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f(first, ...rest) {
|
|
() => first;
|
|
() => rest;
|
|
return rest.length;
|
|
}
|
|
f(1,2);
|
|
`
|
|
testScript(SCRIPT, valueInt(1), t)
|
|
}
|
|
|
|
func TestRestArgsInStashFwdRef(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f(first = eval(), ...rest) {
|
|
() => first;
|
|
() => rest;
|
|
return rest.length === 1 && rest[0] === 2;
|
|
}
|
|
f(1,2);
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestFuncParamRestPattern(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f(arg1, ...{0: rest1, 1: rest2}) {
|
|
return ""+arg1+" "+rest1+" "+rest2;
|
|
}
|
|
f(1, 2, 3);
|
|
`
|
|
testScript(SCRIPT, asciiString("1 2 3"), t)
|
|
}
|
|
|
|
func TestFuncParamForwardRef(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f(a = b + 1, b) {
|
|
return ""+a+" "+b;
|
|
}
|
|
f(1, 2);
|
|
`
|
|
testScript(SCRIPT, asciiString("1 2"), t)
|
|
}
|
|
|
|
func TestFuncParamForwardRefMissing(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f(a = b + 1, b) {
|
|
return ""+a+" "+b;
|
|
}
|
|
assert.throws(ReferenceError, function() {
|
|
f();
|
|
});
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestFuncParamInnerRef(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f(a = inner) {
|
|
var inner = 42;
|
|
return a;
|
|
}
|
|
assert.throws(ReferenceError, function() {
|
|
f();
|
|
});
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestFuncParamInnerRefEval(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f(a = eval("inner")) {
|
|
var inner = 42;
|
|
return a;
|
|
}
|
|
assert.throws(ReferenceError, function() {
|
|
f();
|
|
});
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestFuncParamCalleeName(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f(a = f) {
|
|
var f;
|
|
return f;
|
|
}
|
|
typeof f();
|
|
`
|
|
testScript(SCRIPT, asciiString("undefined"), t)
|
|
}
|
|
|
|
func TestFuncParamVarCopy(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f(a = f) {
|
|
var a;
|
|
return a;
|
|
}
|
|
typeof f();
|
|
`
|
|
testScript(SCRIPT, asciiString("function"), t)
|
|
}
|
|
|
|
func TestFuncParamScope(t *testing.T) {
|
|
const SCRIPT = `
|
|
var x = 'outside';
|
|
var probe1, probe2;
|
|
|
|
function f(
|
|
_ = probe1 = function() { return x; },
|
|
__ = (eval('var x = "inside";'), probe2 = function() { return x; })
|
|
) {
|
|
}
|
|
f();
|
|
probe1()+" "+probe2();
|
|
`
|
|
testScript(SCRIPT, asciiString("inside inside"), t)
|
|
}
|
|
|
|
func TestDefParamsStackPtr(t *testing.T) {
|
|
const SCRIPT = `
|
|
function A() {};
|
|
A.B = function () {};
|
|
function D(message = '') {
|
|
var C = A.B;
|
|
C([1,2,3]);
|
|
};
|
|
|
|
D();
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestNestedVariadicCalls(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f() {
|
|
return Array.prototype.join.call(arguments, ",");
|
|
}
|
|
f(...[1], "a", f(...[2]));
|
|
`
|
|
testScript(SCRIPT, asciiString("1,a,2"), t)
|
|
}
|
|
|
|
func TestVariadicNew(t *testing.T) {
|
|
const SCRIPT = `
|
|
function C() {
|
|
this.res = Array.prototype.join.call(arguments, ",");
|
|
}
|
|
var c = new C(...[1], "a", new C(...[2]).res);
|
|
c.res;
|
|
`
|
|
testScript(SCRIPT, asciiString("1,a,2"), t)
|
|
}
|
|
|
|
func TestVariadicUseStackVars(t *testing.T) {
|
|
const SCRIPT = `
|
|
function A(message) { return message; }
|
|
function B(...args){
|
|
return A(...args);
|
|
}
|
|
B("C");
|
|
`
|
|
testScript(SCRIPT, asciiString("C"), t)
|
|
}
|
|
|
|
func TestCatchParamPattern(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f() {
|
|
let x = 3;
|
|
try {
|
|
throw {a: 1, b: 2};
|
|
} catch ({a, b, c = x}) {
|
|
let x = 99;
|
|
return ""+a+" "+b+" "+c;
|
|
}
|
|
}
|
|
f();
|
|
`
|
|
testScript(SCRIPT, asciiString("1 2 3"), t)
|
|
}
|
|
|
|
func TestArrowUseStrict(t *testing.T) {
|
|
// simple parameter list -- ok
|
|
_, err := Compile("", "(a) => {'use strict';}", false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// non-simple parameter list -- syntax error
|
|
_, err = Compile("", "(a=0) => {'use strict';}", false)
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
}
|
|
|
|
func TestArrowBoxedThis(t *testing.T) {
|
|
const SCRIPT = `
|
|
var context;
|
|
fn = function() {
|
|
return (arg) => { var local; context = this; };
|
|
};
|
|
|
|
fn()();
|
|
context === this;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestParameterOverride(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f(arg) {
|
|
var arg = arg || "default"
|
|
return arg
|
|
}
|
|
f()
|
|
`
|
|
testScript(SCRIPT, asciiString("default"), t)
|
|
}
|
|
|
|
func TestEvalInIterScope(t *testing.T) {
|
|
const SCRIPT = `
|
|
for (let a = 0; a < 1; a++) {
|
|
eval("a");
|
|
}
|
|
`
|
|
|
|
testScript(SCRIPT, valueInt(0), t)
|
|
}
|
|
|
|
func TestTemplateLiterals(t *testing.T) {
|
|
vm := New()
|
|
_, err := vm.RunString("const a = 1, b = 'b';")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
f := func(t *testing.T, template, expected string) {
|
|
res, err := vm.RunString(template)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if actual := res.Export(); actual != expected {
|
|
t.Fatalf("Expected: %q, actual: %q", expected, actual)
|
|
}
|
|
}
|
|
t.Run("empty", func(t *testing.T) {
|
|
f(t, "``", "")
|
|
})
|
|
t.Run("noSub", func(t *testing.T) {
|
|
f(t, "`test`", "test")
|
|
})
|
|
t.Run("emptyTail", func(t *testing.T) {
|
|
f(t, "`a=${a},b=${b}`", "a=1,b=b")
|
|
})
|
|
t.Run("emptyHead", func(t *testing.T) {
|
|
f(t, "`${a},b=${b}$`", "1,b=b$")
|
|
})
|
|
t.Run("headAndTail", func(t *testing.T) {
|
|
f(t, "`a=${a},b=${b}$`", "a=1,b=b$")
|
|
})
|
|
}
|
|
|
|
func TestTaggedTemplate(t *testing.T) {
|
|
const SCRIPT = `
|
|
let res;
|
|
const o = {
|
|
tmpl() {
|
|
res = this;
|
|
return () => {};
|
|
}
|
|
}
|
|
` +
|
|
"o.tmpl()`test`;" + `
|
|
res === o;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestDuplicateGlobalFunc(t *testing.T) {
|
|
const SCRIPT = `
|
|
function a(){}
|
|
function b(){ return "b" }
|
|
function c(){ return "c" }
|
|
function a(){}
|
|
b();
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("b"), t)
|
|
}
|
|
|
|
func TestDuplicateFunc(t *testing.T) {
|
|
const SCRIPT = `
|
|
function f() {
|
|
function a(){}
|
|
function b(){ return "b" }
|
|
function c(){ return "c" }
|
|
function a(){}
|
|
return b();
|
|
}
|
|
f();
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("b"), t)
|
|
}
|
|
|
|
func TestSrcLocations(t *testing.T) {
|
|
// Do not reformat, assertions depend on the line and column numbers
|
|
const SCRIPT = `
|
|
let i = {
|
|
valueOf() {
|
|
throw new Error();
|
|
}
|
|
};
|
|
try {
|
|
i++;
|
|
} catch(e) {
|
|
assertStack(e, [["test.js", "valueOf", 4, 10],
|
|
["test.js", "", 8, 3]
|
|
]);
|
|
}
|
|
|
|
Object.defineProperty(globalThis, "x", {
|
|
get() {
|
|
throw new Error();
|
|
},
|
|
set() {
|
|
throw new Error();
|
|
}
|
|
});
|
|
|
|
try {
|
|
x;
|
|
} catch(e) {
|
|
assertStack(e, [["test.js", "get", 17, 10],
|
|
["test.js", "", 25, 3]
|
|
]);
|
|
}
|
|
|
|
try {
|
|
x++;
|
|
} catch(e) {
|
|
assertStack(e, [["test.js", "get", 17, 10],
|
|
["test.js", "", 33, 3]
|
|
]);
|
|
}
|
|
|
|
try {
|
|
x = 2;
|
|
} catch(e) {
|
|
assertStack(e, [["test.js", "set", 20, 10],
|
|
["test.js", "", 41, 3]
|
|
]);
|
|
}
|
|
|
|
try {
|
|
+i;
|
|
} catch(e) {
|
|
assertStack(e, [["test.js", "valueOf", 4, 10],
|
|
["test.js", "", 49, 4]
|
|
]);
|
|
}
|
|
|
|
try {
|
|
let n;
|
|
n.field = {
|
|
"key1": "test",
|
|
"key2": {},
|
|
}
|
|
} catch(e) {
|
|
assertStack(e, [["test.js", "", 58, 5]
|
|
]);
|
|
}
|
|
`
|
|
testScriptWithTestLibX(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestSrcLocationThrowLiteral(t *testing.T) {
|
|
vm := New()
|
|
_, err := vm.RunString(`
|
|
const z = 1;
|
|
throw "";
|
|
`)
|
|
if ex, ok := err.(*Exception); ok {
|
|
pos := ex.stack[0].Position()
|
|
if pos.Line != 3 {
|
|
t.Fatal(pos)
|
|
}
|
|
} else {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestSrcLocation(t *testing.T) {
|
|
prg := MustCompile("test.js", `
|
|
f();
|
|
var x = 1;
|
|
let y = 1;
|
|
let [z1, z2] = [0, 0];
|
|
|
|
var [z3, z4] = [0, 0];
|
|
`, false)
|
|
const (
|
|
varLine = 3
|
|
letLine = 4
|
|
dstrLetLine = 5
|
|
dstrVarLine = 7
|
|
)
|
|
linesOfInterest := map[int]string{
|
|
varLine: "var",
|
|
letLine: "let",
|
|
dstrLetLine: "destruct let",
|
|
dstrVarLine: "destruct var",
|
|
}
|
|
for i := range prg.code {
|
|
loc := prg.src.Position(prg.sourceOffset(i))
|
|
delete(linesOfInterest, loc.Line)
|
|
if len(linesOfInterest) == 0 {
|
|
break
|
|
}
|
|
}
|
|
for _, v := range linesOfInterest {
|
|
t.Fatalf("no %s line", v)
|
|
}
|
|
}
|
|
|
|
func TestBadObjectKey(t *testing.T) {
|
|
_, err := Compile("", "({!:0})", false)
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
}
|
|
|
|
func TestConstantFolding(t *testing.T) {
|
|
testValues := func(prg *Program, result Value, t *testing.T) {
|
|
values := make(map[unistring.String]struct{})
|
|
for _, ins := range prg.code {
|
|
if lv, ok := ins.(loadVal); ok {
|
|
values[lv.v.string()] = struct{}{}
|
|
}
|
|
}
|
|
if len(values) != 1 {
|
|
prg.dumpCode(t.Logf)
|
|
t.Fatalf("values: %v", values)
|
|
}
|
|
}
|
|
f := func(src string, result Value, t *testing.T) {
|
|
prg := MustCompile("test.js", src, false)
|
|
testValues(prg, result, t)
|
|
New().testPrg(prg, result, t)
|
|
}
|
|
ff := func(src string, result Value, t *testing.T) {
|
|
prg := MustCompile("test.js", src, false)
|
|
fl := prg.code[0].(*newFunc)
|
|
testValues(fl.prg, result, t)
|
|
New().testPrg(prg, result, t)
|
|
}
|
|
|
|
t.Run("lexical binding", func(t *testing.T) {
|
|
f("const x = 1 + 2; x", valueInt(3), t)
|
|
})
|
|
t.Run("var binding", func(t *testing.T) {
|
|
f("var x = 1 + 2; x", valueInt(3), t)
|
|
})
|
|
t.Run("assignment", func(t *testing.T) {
|
|
f("x = 1 + 2; x", valueInt(3), t)
|
|
})
|
|
t.Run("object pattern", func(t *testing.T) {
|
|
f("const {x = 1 + 2} = {}; x", valueInt(3), t)
|
|
})
|
|
t.Run("array pattern", func(t *testing.T) {
|
|
f("const [x = 1 + 2] = []; x", valueInt(3), t)
|
|
})
|
|
t.Run("object literal", func(t *testing.T) {
|
|
f("var o = {x: 1 + 2}; o.x", valueInt(3), t)
|
|
})
|
|
t.Run("array literal", func(t *testing.T) {
|
|
f("var a = [3, 3, 3, 1 + 2]; a[3]", valueInt(3), t)
|
|
})
|
|
t.Run("default function parameter", func(t *testing.T) {
|
|
ff("function f(arg = 1 + 2) {return arg}; f()", valueInt(3), t)
|
|
})
|
|
t.Run("return", func(t *testing.T) {
|
|
ff("function f() {return 1 + 2}; f()", valueInt(3), t)
|
|
})
|
|
}
|
|
|
|
func TestStringInterning(t *testing.T) {
|
|
const SCRIPT = `
|
|
const str1 = "Test";
|
|
function f() {
|
|
return "Test";
|
|
}
|
|
[str1, f()];
|
|
`
|
|
vm := New()
|
|
res, err := vm.RunString(SCRIPT)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
str1 := res.(*Object).Get("0").String()
|
|
str2 := res.(*Object).Get("1").String()
|
|
if unsafe.StringData(str1) != unsafe.StringData(str2) {
|
|
t.Fatal("not interned")
|
|
}
|
|
}
|
|
|
|
func TestAssignBeforeInit(t *testing.T) {
|
|
const SCRIPT = `
|
|
assert.throws(ReferenceError, () => {
|
|
a = 1;
|
|
let a;
|
|
});
|
|
|
|
assert.throws(ReferenceError, () => {
|
|
({a, b} = {a: 1, b: 2});
|
|
let a, b;
|
|
});
|
|
|
|
assert.throws(ReferenceError, () => {
|
|
(function() {
|
|
eval("");
|
|
({a} = {a: 1});
|
|
})();
|
|
let a;
|
|
});
|
|
|
|
assert.throws(ReferenceError, () => {
|
|
const ctx = {x: 1};
|
|
function t() {
|
|
delete ctx.x;
|
|
return 42;
|
|
}
|
|
with(ctx) {
|
|
(function() {
|
|
'use strict';
|
|
({x} = {x: t()});
|
|
})();
|
|
}
|
|
return ctx.x;
|
|
});
|
|
|
|
assert.throws(ReferenceError, () => {
|
|
const ctx = {x: 1};
|
|
function t() {
|
|
delete ctx.x;
|
|
return 42;
|
|
}
|
|
with(ctx) {
|
|
(function() {
|
|
'use strict';
|
|
const src = {};
|
|
Object.defineProperty(src, "x", {
|
|
get() {
|
|
return t();
|
|
}
|
|
});
|
|
({x} = src);
|
|
})();
|
|
}
|
|
return ctx.x;
|
|
});
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestOptChainCallee(t *testing.T) {
|
|
const SCRIPT = `
|
|
var a;
|
|
assert.sameValue(a?.(true), undefined);
|
|
a = null;
|
|
assert.sameValue(a?.(), undefined);
|
|
var o = {n: null};
|
|
assert.sameValue(o.m?.(true), undefined);
|
|
assert.sameValue(o.n?.(true), undefined);
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestObjectLiteralSuper(t *testing.T) {
|
|
const SCRIPT = `
|
|
const proto = {
|
|
m() {
|
|
return 40;
|
|
}
|
|
}
|
|
const o = {
|
|
m() {
|
|
return super.m() + 2;
|
|
}
|
|
}
|
|
o.__proto__ = proto;
|
|
o.m();
|
|
`
|
|
testScript(SCRIPT, intToValue(42), t)
|
|
}
|
|
|
|
func TestClassCaptureThisInFieldInit(t *testing.T) {
|
|
const SCRIPT = `
|
|
let capture;
|
|
|
|
class C {
|
|
a = () => this
|
|
}
|
|
|
|
let c = new C();
|
|
c.a() === c;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestClassUseThisInFieldInit(t *testing.T) {
|
|
const SCRIPT = `
|
|
let capture;
|
|
|
|
class C {
|
|
a = this
|
|
}
|
|
|
|
let c = new C();
|
|
c.a === c;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestClassCaptureThisInStaticFieldInit(t *testing.T) {
|
|
const SCRIPT = `
|
|
let capture;
|
|
|
|
class C {
|
|
static a = (capture = () => this, 0)
|
|
}
|
|
|
|
let c = new C();
|
|
capture() === C;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestClassDynCaptureThisInStaticFieldInit(t *testing.T) {
|
|
const SCRIPT = `
|
|
class C {
|
|
static a = eval("this")
|
|
}
|
|
|
|
C.a === C;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestCompileClass(t *testing.T) {
|
|
const SCRIPT = `
|
|
class C extends Error {
|
|
a = true;
|
|
b = 1;
|
|
["b".toUpperCase()] = 2
|
|
static A = Math.random() < 1
|
|
constructor(message = "My Error") {
|
|
super(message);
|
|
}
|
|
static M() {
|
|
}
|
|
static M1() {
|
|
}
|
|
m() {
|
|
//return C.a;
|
|
}
|
|
m1() {
|
|
return true;
|
|
}
|
|
static {
|
|
this.supername = super.name;
|
|
}
|
|
}
|
|
let c = new C();
|
|
c.a === true && c.b === 1 && c.B === 2 && c.m1() && C.A && C.supername === "Error";
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestSuperInEval(t *testing.T) {
|
|
const SCRIPT = `
|
|
class C extends Error {
|
|
constructor() {
|
|
eval("super()");
|
|
}
|
|
m() {
|
|
return eval("super.name");
|
|
}
|
|
}
|
|
let c = new C();
|
|
c.m() === "Error";
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestSuperRefDot(t *testing.T) {
|
|
const SCRIPT = `
|
|
let thisGet, thisSet;
|
|
class P {
|
|
_p = 0
|
|
get p() {
|
|
thisGet = this;
|
|
return this._p;
|
|
}
|
|
set p(v) {
|
|
thisSet = this;
|
|
this._p = v;
|
|
}
|
|
}
|
|
|
|
class C extends P {
|
|
g() {
|
|
return super.p;
|
|
}
|
|
s(v) {
|
|
super.p = v;
|
|
}
|
|
|
|
inc() {
|
|
super.p++;
|
|
}
|
|
incR() {
|
|
return super.p++;
|
|
}
|
|
|
|
inc1() {
|
|
++super.p;
|
|
}
|
|
|
|
inc1R() {
|
|
return ++super.p;
|
|
}
|
|
unary() {
|
|
return +super.p;
|
|
}
|
|
pattern() {
|
|
[super.p] = [9];
|
|
}
|
|
}
|
|
|
|
let o = new C();
|
|
assert.sameValue(o.g(), 0, "get value");
|
|
assert.sameValue(thisGet, o, "get this");
|
|
o.s(1);
|
|
assert.sameValue(o._p, 1, "set value");
|
|
assert.sameValue(thisSet, o, "set this");
|
|
|
|
thisGet = undefined;
|
|
thisSet = undefined;
|
|
o.inc();
|
|
assert.sameValue(o._p, 2, "inc value");
|
|
assert.sameValue(thisGet, o, "inc thisGet");
|
|
assert.sameValue(thisSet, o, "inc thisSet");
|
|
|
|
thisGet = undefined;
|
|
thisSet = undefined;
|
|
assert.sameValue(o.incR(), 2, "incR result");
|
|
assert.sameValue(o._p, 3, "incR value");
|
|
assert.sameValue(thisGet, o, "incR thisGet");
|
|
assert.sameValue(thisSet, o, "incR thisSet");
|
|
|
|
thisGet = undefined;
|
|
thisSet = undefined;
|
|
o.inc1();
|
|
assert.sameValue(o._p, 4, "inc1 value");
|
|
assert.sameValue(thisGet, o, "inc1 thisGet");
|
|
assert.sameValue(thisSet, o, "inc1 thisSet");
|
|
|
|
thisGet = undefined;
|
|
thisSet = undefined;
|
|
assert.sameValue(o.inc1R(), 5, "inc1R result");
|
|
assert.sameValue(o._p, 5, "inc1R value");
|
|
assert.sameValue(thisGet, o, "inc1R thisGet");
|
|
assert.sameValue(thisSet, o, "inc1R thisSet");
|
|
|
|
assert.sameValue(o.unary(), 5, "unary");
|
|
|
|
o.pattern();
|
|
assert.sameValue(o._p, 9, "pattern");
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestPrivateRefDot(t *testing.T) {
|
|
const SCRIPT = `
|
|
class C {
|
|
#p = 0;
|
|
g() {
|
|
return this.#p;
|
|
}
|
|
s(v) {
|
|
this.#p = v;
|
|
}
|
|
|
|
inc() {
|
|
this.#p++;
|
|
}
|
|
incR() {
|
|
return this.#p++;
|
|
}
|
|
|
|
inc1() {
|
|
++this.#p;
|
|
}
|
|
|
|
inc1R() {
|
|
return ++this.#p;
|
|
}
|
|
pattern() {
|
|
[this.#p] = [9];
|
|
}
|
|
}
|
|
|
|
let o = new C();
|
|
assert.sameValue(o.g(), 0, "get value");
|
|
o.s(1);
|
|
assert.sameValue(o.g(), 1, "set value");
|
|
|
|
o.inc();
|
|
assert.sameValue(o.g(), 2, "inc value");
|
|
|
|
assert.sameValue(o.incR(), 2, "incR result");
|
|
assert.sameValue(o.g(), 3, "incR value");
|
|
|
|
o.inc1();
|
|
assert.sameValue(o.g(), 4, "inc1 value");
|
|
|
|
assert.sameValue(o.inc1R(), 5, "inc1R result");
|
|
assert.sameValue(o.g(), 5, "inc1R value");
|
|
|
|
o.pattern();
|
|
assert.sameValue(o.g(), 9, "pattern");
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestPrivateRefDotEval(t *testing.T) {
|
|
const SCRIPT = `
|
|
class C {
|
|
#p = 0;
|
|
g() {
|
|
return eval("this.#p");
|
|
}
|
|
s(v) {
|
|
eval("this.#p = v");
|
|
}
|
|
|
|
incR() {
|
|
return eval("this.#p++");
|
|
}
|
|
|
|
inc1R() {
|
|
return eval("++this.#p");
|
|
}
|
|
|
|
pattern() {
|
|
eval("[this.#p] = [9]");
|
|
}
|
|
}
|
|
|
|
let o = new C();
|
|
assert.sameValue(o.g(), 0, "get value");
|
|
o.s(1);
|
|
assert.sameValue(o.g(), 1, "set value");
|
|
|
|
assert.sameValue(o.incR(), 1, "incR result");
|
|
assert.sameValue(o.g(), 2, "incR value");
|
|
|
|
assert.sameValue(o.inc1R(), 3, "inc1R result");
|
|
assert.sameValue(o.g(), 3, "inc1R value");
|
|
|
|
o.pattern();
|
|
assert.sameValue(o.g(), 9, "pattern");
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestSuperRefDotCallee(t *testing.T) {
|
|
const SCRIPT = `
|
|
class P {
|
|
get p() {
|
|
return function() {
|
|
return this;
|
|
};
|
|
}
|
|
}
|
|
|
|
class C extends P {
|
|
m() {
|
|
return super.p();
|
|
}
|
|
}
|
|
|
|
let o = new C();
|
|
o.m() === o;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestSuperRefBracket(t *testing.T) {
|
|
const SCRIPT = `
|
|
let PROP = "p";
|
|
let thisGet, thisSet;
|
|
class P {
|
|
_p = 0
|
|
get p() {
|
|
thisGet = this;
|
|
return this._p;
|
|
}
|
|
set p(v) {
|
|
thisSet = this;
|
|
this._p = v;
|
|
}
|
|
}
|
|
|
|
class C extends P {
|
|
g() {
|
|
return super[PROP];
|
|
}
|
|
s(v) {
|
|
super[PROP] = v;
|
|
}
|
|
|
|
inc() {
|
|
super[PROP]++;
|
|
}
|
|
incR() {
|
|
return super[PROP]++;
|
|
}
|
|
|
|
inc1() {
|
|
++super[PROP];
|
|
}
|
|
|
|
inc1R() {
|
|
return ++super[PROP];
|
|
}
|
|
pattern() {
|
|
[super[PROP]] = [9];
|
|
}
|
|
}
|
|
|
|
let o = new C();
|
|
assert.sameValue(o.g(), 0, "get value");
|
|
assert.sameValue(thisGet, o, "get this");
|
|
o.s(1);
|
|
assert.sameValue(o._p, 1, "set value");
|
|
assert.sameValue(thisSet, o, "set this");
|
|
|
|
thisGet = undefined;
|
|
thisSet = undefined;
|
|
o.inc();
|
|
assert.sameValue(o._p, 2, "inc value");
|
|
assert.sameValue(thisGet, o, "inc thisGet");
|
|
assert.sameValue(thisSet, o, "inc thisSet");
|
|
|
|
thisGet = undefined;
|
|
thisSet = undefined;
|
|
assert.sameValue(o.incR(), 2, "incR result");
|
|
assert.sameValue(o._p, 3, "incR value");
|
|
assert.sameValue(thisGet, o, "incR thisGet");
|
|
assert.sameValue(thisSet, o, "incR thisSet");
|
|
|
|
thisGet = undefined;
|
|
thisSet = undefined;
|
|
o.inc1();
|
|
assert.sameValue(o._p, 4, "inc1 value");
|
|
assert.sameValue(thisGet, o, "inc1 thisGet");
|
|
assert.sameValue(thisSet, o, "inc1 thisSet");
|
|
|
|
thisGet = undefined;
|
|
thisSet = undefined;
|
|
assert.sameValue(o.inc1R(), 5, "inc1R result");
|
|
assert.sameValue(o._p, 5, "inc1R value");
|
|
assert.sameValue(thisGet, o, "inc1R thisGet");
|
|
assert.sameValue(thisSet, o, "inc1R thisSet");
|
|
|
|
o.pattern();
|
|
assert.sameValue(o._p, 9, "pattern");
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestSuperRefBracketEvalOrder(t *testing.T) {
|
|
const SCRIPT = `
|
|
let keyCallCount = 0;
|
|
|
|
function key() {
|
|
keyCallCount++;
|
|
C.prototype.__proto__ = null;
|
|
return "k";
|
|
}
|
|
|
|
class C {
|
|
constructor() {
|
|
super[key()]++;
|
|
}
|
|
}
|
|
|
|
assert.throws(TypeError, () => new C());
|
|
assert.sameValue(keyCallCount, 1);
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestSuperRefBracketCallee(t *testing.T) {
|
|
const SCRIPT = `
|
|
let PROP = "p";
|
|
class P {
|
|
get p() {
|
|
return function() {
|
|
return this;
|
|
};
|
|
}
|
|
}
|
|
|
|
class C extends P {
|
|
m() {
|
|
return super[PROP]();
|
|
}
|
|
}
|
|
|
|
let o = new C();
|
|
o.m() === o;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestSuperBaseInCtor(t *testing.T) {
|
|
const SCRIPT = `
|
|
let result;
|
|
class Derived extends Object {
|
|
constructor() {
|
|
super();
|
|
result = super.constructor === Object;
|
|
}
|
|
}
|
|
new Derived();
|
|
result;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestClassNamedEval(t *testing.T) {
|
|
const SCRIPT = `
|
|
const C = class {
|
|
}
|
|
|
|
C.name === "C";
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestClassNonDerived(t *testing.T) {
|
|
const SCRIPT = `
|
|
function initF() {
|
|
}
|
|
class C {
|
|
f = initF()
|
|
}
|
|
let c = new C();
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestClassExpr(t *testing.T) {
|
|
const SCRIPT = `
|
|
typeof Object.getOwnPropertyDescriptor(class {get f() {}}.prototype, "f").get === "function";
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestClassSuperInHeritage(t *testing.T) {
|
|
const SCRIPT = `
|
|
class P {
|
|
a() {
|
|
return Error;
|
|
}
|
|
}
|
|
|
|
class C extends P {
|
|
m() {
|
|
class Inner extends super.a() {
|
|
}
|
|
return new Inner();
|
|
}
|
|
}
|
|
|
|
new C().m() instanceof Error;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestClassSuperInHeritageExpr(t *testing.T) {
|
|
const SCRIPT = `
|
|
class P {
|
|
a() {
|
|
return Error;
|
|
}
|
|
}
|
|
|
|
class C extends P {
|
|
m() {
|
|
function f(cls) {
|
|
return new cls();
|
|
}
|
|
return f(class Inner extends super.a() {
|
|
})
|
|
}
|
|
}
|
|
|
|
new C().m() instanceof Error;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestClassReferToBinding(t *testing.T) {
|
|
const SCRIPT = `
|
|
const CC = class C {
|
|
static T = 40
|
|
f = C.T + 2
|
|
}
|
|
let c = new CC();
|
|
c.f;
|
|
`
|
|
|
|
testScript(SCRIPT, intToValue(42), t)
|
|
}
|
|
|
|
func TestClassReferToBindingInStaticDecl(t *testing.T) {
|
|
const SCRIPT = `
|
|
class C {
|
|
static T = C.name
|
|
}
|
|
C.T;
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("C"), t)
|
|
}
|
|
|
|
func TestClassReferToBindingInStaticEval(t *testing.T) {
|
|
const SCRIPT = `
|
|
const CC = class C {
|
|
static T = eval("C.name")
|
|
}
|
|
CC.T;
|
|
`
|
|
|
|
testScript(SCRIPT, asciiString("C"), t)
|
|
}
|
|
|
|
func TestClassReferToBindingFromHeritage(t *testing.T) {
|
|
const SCRIPT = `
|
|
assert.throws(ReferenceError, () => {
|
|
class C extends C {
|
|
}
|
|
});
|
|
`
|
|
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestClassCaptureSuperCallInArrowFunc(t *testing.T) {
|
|
const SCRIPT = `
|
|
let f;
|
|
class C extends class {} {
|
|
constructor() {
|
|
f = () => super();
|
|
f();
|
|
}
|
|
}
|
|
let c = new C();
|
|
`
|
|
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestClassCaptureSuperCallInNestedArrowFunc(t *testing.T) {
|
|
const SCRIPT = `
|
|
let f;
|
|
class P {
|
|
}
|
|
class C extends P {
|
|
constructor() {
|
|
f = () => () => super();
|
|
f()();
|
|
}
|
|
}
|
|
new C() instanceof P;
|
|
`
|
|
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestThisInEval(t *testing.T) {
|
|
const SCRIPT = `
|
|
assert.sameValue(eval("this"), this, "global");
|
|
|
|
let o = {
|
|
f() {
|
|
return eval("this");
|
|
}
|
|
}
|
|
assert.sameValue(o.f(), o, "obj literal");
|
|
`
|
|
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestStaticAsBindingTarget(t *testing.T) {
|
|
const SCRIPT = `
|
|
let [static] = [];
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestEvalInStaticFieldInit(t *testing.T) {
|
|
const SCRIPT = `
|
|
var C = class {
|
|
static f = 'test';
|
|
static g = this.f + '262';
|
|
static h = eval('this.g') + 'test';
|
|
}
|
|
C.f === "test" && C.g === "test262" && C.h === "test262test";
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestClassPrivateElemInEval(t *testing.T) {
|
|
const SCRIPT = `
|
|
let f1, f2;
|
|
|
|
class C extends (f1 = o => eval("o.#a"), Object) {
|
|
static #a = 42;
|
|
static {
|
|
f2 = o => eval("o.#a");
|
|
assert.sameValue(C.#a, 42);
|
|
assert.sameValue((() => C.#a)(), 42);
|
|
}
|
|
}
|
|
|
|
assert.throws(SyntaxError, () => f1(C));
|
|
assert.sameValue(f2(C), 42);
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestClassPrivateElemInIndirectEval(t *testing.T) {
|
|
const SCRIPT = `
|
|
let f1, f2;
|
|
|
|
class C extends (f1 = o => (0, eval)("o.#a"), Object) {
|
|
static #a = 42;
|
|
static {
|
|
f2 = o => (0, eval)("o.#a");
|
|
assert.throws(SyntaxError, () => (0, eval)("C.#a"));
|
|
}
|
|
}
|
|
|
|
assert.throws(SyntaxError, () => f1(C));
|
|
assert.throws(SyntaxError, () => f2(C));
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestClassPrivateElemInFunction(t *testing.T) {
|
|
const SCRIPT = `
|
|
assert.throws(SyntaxError, () => {
|
|
class C {
|
|
static #a = 42;
|
|
static {
|
|
Function("o", "return o.#a");
|
|
}
|
|
}
|
|
});
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestClassPrivateElementsDecl(t *testing.T) {
|
|
const SCRIPT = `
|
|
class C {
|
|
#a = 42;
|
|
get #b() {}
|
|
set #b(_) {}
|
|
get c() {
|
|
return this.#a;
|
|
}
|
|
#m() {
|
|
return this.#a;
|
|
}
|
|
static getter(inst) {
|
|
return inst.#m();
|
|
}
|
|
}
|
|
let c = new C();
|
|
c.c + C.getter(c);
|
|
`
|
|
testScript(SCRIPT, intToValue(84), t)
|
|
}
|
|
|
|
func TestPrivateIn(t *testing.T) {
|
|
const SCRIPT = `
|
|
class C {
|
|
#a = 42;
|
|
static check(inst) {
|
|
return #a in inst;
|
|
}
|
|
}
|
|
let c = new C();
|
|
C.check(c);
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestDeletePropOfNonObject(t *testing.T) {
|
|
const SCRIPT = `
|
|
delete 'Test262'[100] && delete 'Test262'.a && delete 'Test262'['@'];
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestKeywordsAsLabels(t *testing.T) {
|
|
const SCRIPT = `
|
|
let: for (let i = 0; i < 2; i++) {
|
|
if (i === 0) continue let;
|
|
break let;
|
|
}
|
|
|
|
\u006Cet: for (let i = 0; i < 2; i++) {
|
|
if (i === 0) continue \u006Cet;
|
|
break \u006Cet;
|
|
}
|
|
|
|
yield: for (let i = 0; i < 2; i++) {
|
|
if (i === 0) continue yield;
|
|
break yield;
|
|
}
|
|
|
|
yi\u0065ld: for (let i = 0; i < 2; i++) {
|
|
if (i === 0) continue yi\u0065ld;
|
|
break yi\u0065ld;
|
|
}
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestThisResolutionWithArg(t *testing.T) {
|
|
const SCRIPT = `
|
|
let capture;
|
|
function f(arg) {
|
|
capture = () => this; // move 'this' to stash
|
|
return [this, arg];
|
|
}
|
|
const _this = {};
|
|
const arg = {};
|
|
const [_this1, arg1] = f.call(_this, arg);
|
|
_this1 === _this && arg1 === arg;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestThisResolutionArgInStash(t *testing.T) {
|
|
const SCRIPT = `
|
|
let capture;
|
|
function f(arg) {
|
|
capture = () => this + arg; // move 'this' and arguments to stash
|
|
return [this, arg];
|
|
}
|
|
const _this = {};
|
|
const arg = {};
|
|
const [_this1, arg1] = f.call(_this, arg);
|
|
_this1 === _this && arg1 === arg;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestThisResolutionWithStackVar(t *testing.T) {
|
|
const SCRIPT = `
|
|
let capture;
|
|
function f(arg) {
|
|
const _ = 1; // a stack variable
|
|
capture = () => this + arg; // move 'this' and arguments to stash
|
|
return [this, arg];
|
|
}
|
|
const _this = {};
|
|
const arg = {};
|
|
const [_this1, arg1] = f.call(_this, arg);
|
|
_this1 === _this && arg1 === arg;
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestForInLoopContinue(t *testing.T) {
|
|
const SCRIPT = `
|
|
var globalSink;
|
|
(function() {
|
|
const data = [{disabled: true}, {}];
|
|
function dummy() {}
|
|
function f1() {}
|
|
|
|
function f() {
|
|
dummy(); // move dummy to stash (so that f1 is at index 1)
|
|
for (const d of data) {
|
|
if (d.disabled) continue;
|
|
globalSink = () => d; // move d to stash
|
|
f1();
|
|
}
|
|
}
|
|
|
|
f();
|
|
})();
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestForInLoopContinueOuter(t *testing.T) {
|
|
const SCRIPT = `
|
|
var globalSink;
|
|
(function() {
|
|
const data = [{disabled: true}, {}];
|
|
function dummy1() {}
|
|
function f1() {}
|
|
|
|
function f() {
|
|
dummy1();
|
|
let counter = 0;
|
|
OUTER: for (let i = 0; i < 1; i++) {
|
|
for (const d of data) {
|
|
if (d.disabled) continue OUTER;
|
|
globalSink = () => d;
|
|
}
|
|
counter++;
|
|
}
|
|
f1();
|
|
if (counter !== 0) {
|
|
throw new Error(counter);
|
|
}
|
|
}
|
|
|
|
f();
|
|
})();
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestLexicalDeclInSwitch(t *testing.T) {
|
|
const SCRIPT = `
|
|
switch(0) {
|
|
case 1:
|
|
if (false) b = 3;
|
|
case 2:
|
|
const c = 1;
|
|
}
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestClassFieldSpecial(t *testing.T) {
|
|
const SCRIPT = `
|
|
class C {
|
|
get;
|
|
set;
|
|
async;
|
|
static;
|
|
}
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestClassMethodSpecial(t *testing.T) {
|
|
const SCRIPT = `
|
|
class C {
|
|
get() {}
|
|
set() {}
|
|
async() {}
|
|
static() {}
|
|
}
|
|
`
|
|
testScript(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestClassMethodNumLiteral(t *testing.T) {
|
|
const SCRIPT = `
|
|
class C {
|
|
0() {
|
|
return true;
|
|
}
|
|
}
|
|
new C()[0]();
|
|
`
|
|
testScript(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestAsyncFunc(t *testing.T) {
|
|
const SCRIPT = `
|
|
async (x = true, y) => {};
|
|
async x => {};
|
|
let passed = false;
|
|
async function f() {
|
|
return true;
|
|
}
|
|
async function f1(arg = true) {
|
|
passed = await f();
|
|
}
|
|
await f1();
|
|
return passed;
|
|
`
|
|
testAsyncFunc(SCRIPT, valueTrue, t)
|
|
}
|
|
|
|
func TestObjectLiteralComputedMethodKeys(t *testing.T) {
|
|
_, err := Compile("", `
|
|
({
|
|
["__proto__"]() {},
|
|
["__proto__"]() {}
|
|
})
|
|
`, false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, err = Compile("", `
|
|
({
|
|
get ["__proto__"]() {},
|
|
get ["__proto__"]() {}
|
|
})
|
|
`, false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestGeneratorFunc(t *testing.T) {
|
|
const SCRIPT = `
|
|
let trace = "";
|
|
function defParam() {
|
|
trace += "1";
|
|
return "def";
|
|
}
|
|
function* g(param = defParam()) {
|
|
const THREE = 3;
|
|
trace += "2";
|
|
assert.sameValue(Math.floor(yield 1), THREE);
|
|
return 42;
|
|
}
|
|
let iter = g();
|
|
assert.sameValue(trace, "1");
|
|
|
|
let next = iter.next();
|
|
assert.sameValue(next.value, 1);
|
|
assert.sameValue(next.done, false);
|
|
|
|
next = iter.next(Math.PI);
|
|
assert.sameValue(next.value, 42);
|
|
assert.sameValue(next.done, true);
|
|
|
|
assert.sameValue(trace, "12");
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestGeneratorMethods(t *testing.T) {
|
|
const SCRIPT = `
|
|
class C {
|
|
*g(param) {
|
|
yield 1;
|
|
yield 2;
|
|
}
|
|
}
|
|
let c = new C();
|
|
let iter = c.g();
|
|
let res = iter.next();
|
|
assert.sameValue(res.value, 1);
|
|
assert.sameValue(res.done, false);
|
|
|
|
res = iter.next();
|
|
assert.sameValue(res.value, 2);
|
|
assert.sameValue(res.done, false);
|
|
|
|
res = iter.next();
|
|
assert.sameValue(res.value, undefined);
|
|
assert.sameValue(res.done, true);
|
|
`
|
|
testScriptWithTestLib(SCRIPT, _undefined, t)
|
|
}
|
|
|
|
func TestFunctionBodyClassDecl(t *testing.T) {
|
|
const SCRIPT = `
|
|
function as(requiredArgument = {}) {
|
|
class something { }
|
|
};
|
|
`
|
|
_, err := Compile("", SCRIPT, false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
/*
|
|
func TestBabel(t *testing.T) {
|
|
src, err := os.ReadFile("babel7.js")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
vm := New()
|
|
_, err = vm.RunString(string(src))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = vm.RunString(`var result = Babel.transform("", {presets: ["es2015"]});`)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}*/
|
|
|
|
func BenchmarkCompile(b *testing.B) {
|
|
data, err := os.ReadFile("testdata/S15.10.2.12_A1_T1.js")
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
src := string(data)
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := Compile("test.js", src, false)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|