// Copyright 2019 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. Debug = debug.Debug const evaluate = Debug.evaluateGlobalREPL; const evaluateNonREPL = (source) => Debug.evaluateGlobal(source, false).value(); (async () => { // Declare let and get value let result; result = await evaluate("let x = 7;"); result = await evaluate("x;"); assertEquals(7, result); // Re-declare in the same script after declaration in another script. assertThrows(() => evaluate("let x = 8; let x = 9;")); result = await evaluate("x;"); assertEquals(7, result); // Re-declare let as let assertDoesNotThrow(async () => result = await evaluate("let x = 8;")); result = await evaluate("x;"); assertEquals(8, result); await evaluate("let x = 8;"); // Close over let. Inner function is only pre-parsed. result = await evaluate("function getter() { return x; }"); assertEquals(undefined, result); result = await evaluate("getter();"); assertEquals(8, result); result = await evaluate("x = 9;"); assertEquals(9, result); result = await evaluate("x;"); assertEquals(9, result); result = await evaluate("getter();"); assertEquals(9, result); // Modifies the original x; does not create a new one/shadow. result = await evaluate("let x = 10;"); assertEquals(undefined, result); result = await evaluate("x;"); assertEquals(10, result); result = await evaluate("getter();"); assertEquals(10, result); await evaluate("let x = 10"); // Check store from an inner scope. result = await evaluate("{ let z; x = 11; } x;"); assertEquals(11, result); // Check re-declare from an inner scope does nothing. result = await evaluate("{ let z; let x = 12; } x;"); assertEquals(11, result); assertThrowsAsync(evaluate("{ let qq = 10; } qq;"), ReferenceError, "qq is not defined"); // Re-declare in the same script (no previous declaration). assertThrows(() => result = evaluate("let y = 7; let y = 8;"), SyntaxError, "Identifier 'y' has already been declared"); // Check TDZ; use before initialization. // Do not check exact error message, it depends on the path taken through // the IC machinery and changes sometimes, causing the test to be flaky. assertThrowsAsync(evaluate("a; let a = 7;"), ReferenceError); assertThrowsAsync(evaluate("a;"), ReferenceError); // This is different to non-REPL mode, which throws the kUndefined error here. assertThrowsAsync(evaluate("a = 7;"), ReferenceError, "Cannot access 'a' before initialization"); result = await evaluate("let a = 8;"); assertEquals(undefined, result); result = await evaluate("a;") assertEquals(8, result); // Check TDZ; store before initialization. assertThrowsAsync(evaluate("b = 10; let b;"), ReferenceError, "Cannot access 'b' before initialization"); // Check that b is still broken. assertThrowsAsync(evaluate("b = 10; let b;"), ReferenceError, "Cannot access 'b' before initialization"); // Check that b is still broken when the let defines a value. assertThrowsAsync(evaluate("b = 10; let b = 7;"), ReferenceError, "Cannot access 'b' before initialization"); result = await evaluate("let b = 11;"); assertEquals(undefined, result); // We fixed 'b'! result = await evaluate("b;"); assertEquals(11, result); // Check that class works the same. Internally there is no difference between // class and let so we don't test as extensively as for let. result = evaluate("class K {};"); assertDoesNotThrow(() => result = evaluate("class K {};")); // many tests for normal/repl script interactions. // tests with let x = await // result = evaluate("toString;"); // Declare const and get value result = await evaluate("const c = 7;"); result = await evaluate("c;"); assertEquals(7, result); // Re-declare in the same script after declaration in another script. assertThrows(() => evaluate("let c = 8; let c = 9;")); result = await evaluate("c;"); assertEquals(7, result); // Re-declare const as const result = await evaluate("const c = 8;"); result = await evaluate("c;"); assertEquals(8, result); // Assign to const assertThrowsAsync(evaluate("c = 11;"), TypeError, "Assignment to constant variable."); result = await evaluate("c;"); assertEquals(8, result); await evaluate("const c = 8;"); // Close over const. Inner function is only pre-parsed. result = await evaluate("function getter() { return c; }"); assertEquals(undefined, result); result = await evaluate("getter();"); assertEquals(8, result); // Modifies the original c; does not create a new one/shadow. result = await evaluate("const c = 10;"); assertEquals(undefined, result); result = await evaluate("c;"); assertEquals(10, result); result = await evaluate("getter();"); assertEquals(10, result); await evaluate("const c = 10"); // Check store from an inner scope throws error. assertThrowsAsync(evaluate("{ let z; c = 11; };"), TypeError, "Assignment to constant variable."); result = await evaluate("c;"); assertEquals(10, result); // Check re-declare from an inner scope does nothing. result = await evaluate("{ let z; const c = 12; } c;"); assertEquals(10, result); assertThrowsAsync(evaluate("{ const qq = 10; } qq;"), ReferenceError, "qq is not defined"); // Const vs. const in same script. assertThrows(() => result = evaluate("const d = 9; const d = 10;"), SyntaxError, "Identifier 'd' has already been declared"); // Check TDZ; const use before initialization. assertThrowsAsync(evaluate("e; const e = 7;"), ReferenceError); assertThrowsAsync(evaluate("e;"), ReferenceError); result = await evaluate("const e = 8;"); assertEquals(undefined, result); result = await evaluate("e;") assertEquals(8, result); // f is marked as constant in TDZ assertThrowsAsync(evaluate("f = 10; const f = 7;"), TypeError, ("Assignment to constant variable.")); result = await evaluate("const f = 11;"); assertEquals(undefined, result); // We fixed 'f'! result = await evaluate("f;"); assertEquals(11, result); // Re-declare let as const evaluate("let z = 10;"); assertThrows(() => result = evaluate("const z = 9;"), SyntaxError, "Identifier 'z' has already been declared"); result = await evaluate("z;"); assertEquals(10, result) // Re-declare const as let result = await evaluate("const g = 12;"); assertThrows(() => result = evaluate("let g = 13;"), SyntaxError, "Identifier 'g' has already been declared"); result = await evaluate("g;"); assertEquals(12, result); // Let vs. const in the same script assertThrows(() => result = evaluate("let h = 13; const h = 14;"), SyntaxError, "Identifier 'h' has already been declared"); assertThrows(() => result = evaluate("const i = 13; let i = 14;"), SyntaxError, "Identifier 'i' has already been declared"); // Configurable properties of the global object can be re-declared as let. result = await evaluate(`Object.defineProperty(globalThis, 'j', { value: 1, configurable: true });`); result = await evaluate("j;"); assertEquals(1, result); result = await evaluate("let j = 2;"); result = await evaluate("j;"); assertEquals(2, result); // Non-configurable properties of the global object (also created by plain old // top-level var declarations) cannot be re-declared as let. result = await evaluate(`Object.defineProperty(globalThis, 'k', { value: 1, configurable: false });`); result = await evaluate("k;"); assertEquals(1, result); assertThrows(() => result = evaluate("let k = 2;"), SyntaxError, "Identifier 'k' has already been declared"); result = await evaluate("k;"); assertEquals(1, result); // ... Except if you do it in the same script. result = await evaluate(`Object.defineProperty(globalThis, 'k2', { value: 1, configurable: false }); let k2 = 2;`); result = await evaluate("k2;"); assertEquals(2, result); result = await evaluate("globalThis.k2;"); assertEquals(1, result); // But if the property is configurable then both versions are allowed. result = await evaluate(`Object.defineProperty(globalThis, 'k3', { value: 1, configurable: true });`); result = await evaluate("k3;"); assertEquals(1, result); result = await evaluate("let k3 = 2;"); result = await evaluate("k3;"); assertEquals(2, result); result = await evaluate("globalThis.k3;"); assertEquals(1, result); result = await evaluate(`Object.defineProperty(globalThis, 'k4', { value: 1, configurable: true }); let k4 = 2;`); result = await evaluate("k4;"); assertEquals(2, result); result = await evaluate("globalThis.k4;"); assertEquals(1, result); // Check var followed by let in the same script. assertThrows(() => result = evaluate("var k5 = 1; let k5 = 2;"), SyntaxError, "Identifier 'k5' has already been declared"); // In different scripts. result = await evaluate("var k6 = 1;"); assertThrows(() => result = evaluate("let k6 = 2;"), SyntaxError, "Identifier 'k6' has already been declared"); // Check let followed by var in the same script. assertThrows(() => result = evaluate("let k7 = 1; var k7 = 2;"), SyntaxError, "Identifier 'k7' has already been declared"); // In different scripts. result = evaluate("let k8 = 1;"); assertThrows(() => result = evaluate("var k8 = 2;"), SyntaxError, "Identifier 'k8' has already been declared"); // Check var followed by var in the same script. result = await evaluate("var k9 = 1; var k9 = 2;"); result = await evaluate("k9;"); assertEquals(2, result); // In different scripts. result = await evaluate("var k10 = 1;"); result = await evaluate("var k10 = 2;"); result = await evaluate("k10;"); assertEquals(2, result); result = await evaluate("globalThis.k10;"); assertEquals(2, result); // typeof should not throw for undeclared variables result = await evaluate("typeof k11"); assertEquals("undefined", result); // Non-configurable properties of the global object (also created by plain old // top-level var declarations) cannot be re-declared as const. result = await evaluate(`Object.defineProperty(globalThis, 'k12', { value: 1, configurable: false });`); result = await evaluate("k12;"); assertEquals(1, result); assertThrows(() => result = evaluate("const k12 = 2;"), SyntaxError, "Identifier 'k12' has already been declared"); result = await evaluate("k12;"); assertEquals(1, result); // ... Except if you do it in the same script. result = await evaluate(`Object.defineProperty(globalThis, 'k13', { value: 1, configurable: false }); const k13 = 2;`); result = await evaluate("k13;"); assertEquals(2, result); result = await evaluate("globalThis.k13;"); assertEquals(1, result); // But if the property is configurable then both versions are allowed. result = await evaluate(`Object.defineProperty(globalThis, 'k14', { value: 1, configurable: true });`); result = await evaluate("k14;"); assertEquals(1, result); result = await evaluate("const k14 = 2;"); result = await evaluate("k14;"); assertEquals(2, result); result = await evaluate("globalThis.k14;"); assertEquals(1, result); result = await evaluate(`Object.defineProperty(globalThis, 'k15', { value: 1, configurable: true }); const k15 = 2;`); result = await evaluate("k15;"); assertEquals(2, result); result = await evaluate("globalThis.k15;"); assertEquals(1, result); // Test lets with names on the object prototype e.g. toString to make sure // it only works for own properties. // result = evaluate("let valueOf;"); // REPL vs. non-REPL scripts // We can still read let values cross-mode. result = evaluateNonREPL("let l1 = 1; let l2 = 2; let l3 = 3;"); result = await evaluate("l1;"); assertEquals(1, result); // But we can't re-declare page script lets in a REPL script. We might want to // later. assertThrows(() => result = evaluate("let l1 = 2;"), SyntaxError, "Identifier 'l1' has already been declared"); assertThrows(() => result = evaluate("var l2 = 3;"), SyntaxError, "Identifier 'l2' has already been declared"); assertThrows(() => result = evaluate("const l3 = 4;"), SyntaxError, "Identifier 'l3' has already been declared"); // Re-declaring var across modes works. result = evaluateNonREPL("var l4 = 1; const l5 = 1;"); result = await evaluate("var l4 = 2;"); result = await evaluate("l4;"); assertEquals(2, result); // Const doesn't. assertThrows(() => result = evaluate("const l5 = 2;"), SyntaxError, "Identifier 'l5' has already been declared") ; result = await evaluate("l5;"); assertEquals(1, result); // Now REPL followed by non-REPL result = await evaluate("let l6 = 1; const l7 = 2; let l8 = 3;"); result = evaluateNonREPL("l7;"); assertEquals(2, result); result = evaluateNonREPL("l6;"); assertEquals(1, result); // Check that the pattern of `l9; let l9;` does not throw for REPL scripts. // If REPL scripts behaved the same as normal scripts, this case would // re-introduce the hole in 'l9's script context slot, causing the IC and feedback // to 'lie' about the current state. result = await evaluate("let l9;"); result = await evaluate("l9; let l9;"), assertEquals(undefined, await evaluate('l9;')); // Check that binding and re-declaring a function via let works. result = evaluate("let fn1 = function() { return 21; }"); assertEquals(21, fn1()); result = evaluate("let fn1 = function() { return 42; }"); assertEquals(42, fn1()); // Check that lazily parsed functions that bind a REPL-let variable work. evaluate("let l10 = 21;"); evaluate("let l10 = 42; function fn2() { return l10; }"); evaluate("let l10 = 'foo';"); assertEquals("foo", fn2()); // Check that binding and re-declaring a function via const works. result = evaluate("const fn3 = function() { return 21; }"); assertEquals(21, fn3()); result = evaluate("const fn3 = function() { return 42; }"); assertEquals(42, fn3()); // Check that lazily parsed functions that bind a REPL-const variable work. evaluate("const l11 = 21;"); evaluate("const l11 = 42; function fn4() { return l11; }"); evaluate("const l11 = 'foo1';"); assertEquals("foo1", fn4()); })().catch(e => { print(e); print(e.stack); %AbortJS("Async test is failing"); });