Resolve dynamic identifier assignment targets before evaluating the right hand side, then store through that saved environment record. This matches the ECMA-262 ordering for simple assignment and var initializers when a with binding is deleted or direct eval creates a nearer var. Add runtime coverage for with and direct-eval cases and refresh bytecode expectations for dynamic assignments that now snapshot the binding before storing.
90 lines
2.4 KiB
JavaScript
90 lines
2.4 KiB
JavaScript
test("basic with statement functionality", () => {
|
|
var object = { foo: 5, bar: 6, baz: 7 };
|
|
var qux = 1;
|
|
|
|
var bar = 99;
|
|
|
|
with (object) {
|
|
expect(foo).toBe(5);
|
|
expect(bar).toBe(6);
|
|
expect(baz).toBe(7);
|
|
expect(qux).toBe(1);
|
|
expect(typeof quz).toBe("undefined");
|
|
|
|
bar = 2;
|
|
}
|
|
|
|
expect(object.bar).toBe(2);
|
|
expect(() => foo).toThrowWithMessage(ReferenceError, "'foo' is not defined");
|
|
|
|
expect(bar).toBe(99);
|
|
});
|
|
|
|
test("syntax error in strict mode", () => {
|
|
expect("'use strict'; with (foo) {}").not.toEval();
|
|
});
|
|
|
|
test("restores lexical environment even when exception is thrown", () => {
|
|
var object = {
|
|
foo: 1,
|
|
get bar() {
|
|
throw Error();
|
|
},
|
|
};
|
|
|
|
try {
|
|
with (object) {
|
|
expect(foo).toBe(1);
|
|
bar;
|
|
}
|
|
expect().fail();
|
|
} catch (e) {
|
|
expect(() => foo).toThrowWithMessage(ReferenceError, "'foo' is not defined");
|
|
}
|
|
expect(() => foo).toThrowWithMessage(ReferenceError, "'foo' is not defined");
|
|
});
|
|
|
|
test("with object changes can shadow an outer binding", () => {
|
|
let outer = "outer";
|
|
let object = {};
|
|
let seen = [];
|
|
|
|
with (object) {
|
|
for (let i = 0; i < 2; ++i) {
|
|
seen.push(outer);
|
|
object.outer = "object";
|
|
}
|
|
}
|
|
|
|
expect(seen).toEqual(["outer", "object"]);
|
|
});
|
|
|
|
test("assignment keeps the resolved with binding through RHS side effects", () => {
|
|
var outer = 0;
|
|
var object = { outer: 1 };
|
|
|
|
with (object) {
|
|
outer = (delete object.outer, 2);
|
|
}
|
|
|
|
expect(object.outer).toBe(2);
|
|
expect(outer).toBe(0);
|
|
});
|
|
|
|
test("var initializer keeps the resolved with binding through initializer side effects", () => {
|
|
var object = { test262id: 1 };
|
|
|
|
with (object) {
|
|
var test262id = delete object.test262id;
|
|
}
|
|
|
|
expect(object.test262id).toBe(true);
|
|
expect(test262id).toBeUndefined();
|
|
});
|
|
|
|
test("with statement updates empty abrupt completions to undefined", () => {
|
|
expect(eval("1; do { 2; with ({}) { 3; break; } 4; } while (false);")).toBe(3);
|
|
expect(eval("5; do { 6; with ({}) { break; } 7; } while (false);")).toBeUndefined();
|
|
expect(eval("8; do { 9; with ({}) { 10; continue; } 11; } while (false);")).toBe(10);
|
|
expect(eval("12; do { 13; with ({}) { continue; } 14; } while (false);")).toBeUndefined();
|
|
});
|