debug-evaluate-repl-mode.js 13.6 KB
Newer Older
Simon Zünd's avatar
Simon Zünd committed
1 2 3 4 5 6 7 8 9
// 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();

10 11
(async () => {

Simon Zünd's avatar
Simon Zünd committed
12 13
// Declare let and get value
let result;
14 15
result = await evaluate("let x = 7;");
result = await evaluate("x;");
Simon Zünd's avatar
Simon Zünd committed
16 17 18
assertEquals(7, result);

// Re-declare in the same script after declaration in another script.
19 20
assertThrows(() => evaluate("let x = 8; let x = 9;"));
result = await evaluate("x;");
Simon Zünd's avatar
Simon Zünd committed
21 22 23
assertEquals(7, result);

// Re-declare let as let
24 25
assertDoesNotThrow(async () => result = await evaluate("let x = 8;"));
result = await evaluate("x;");
Simon Zünd's avatar
Simon Zünd committed
26 27
assertEquals(8, result);

28
await evaluate("let x = 8;");
Simon Zünd's avatar
Simon Zünd committed
29 30

// Close over let. Inner function is only pre-parsed.
31
result = await evaluate("function getter() { return x; }");
Simon Zünd's avatar
Simon Zünd committed
32
assertEquals(undefined, result);
33
result = await evaluate("getter();");
Simon Zünd's avatar
Simon Zünd committed
34
assertEquals(8, result);
35
result = await evaluate("x = 9;");
Simon Zünd's avatar
Simon Zünd committed
36
assertEquals(9, result);
37
result = await evaluate("x;");
Simon Zünd's avatar
Simon Zünd committed
38
assertEquals(9, result);
39
result = await evaluate("getter();");
Simon Zünd's avatar
Simon Zünd committed
40 41
assertEquals(9, result);
// Modifies the original x; does not create a new one/shadow.
42
result = await evaluate("let x = 10;");
Simon Zünd's avatar
Simon Zünd committed
43
assertEquals(undefined, result);
44
result = await evaluate("x;");
Simon Zünd's avatar
Simon Zünd committed
45
assertEquals(10, result);
46
result = await evaluate("getter();");
Simon Zünd's avatar
Simon Zünd committed
47 48
assertEquals(10, result);

49
await evaluate("let x = 10");
Simon Zünd's avatar
Simon Zünd committed
50 51

// Check store from an inner scope.
52
result = await evaluate("{ let z; x = 11; } x;");
Simon Zünd's avatar
Simon Zünd committed
53 54 55
assertEquals(11, result);

// Check re-declare from an inner scope does nothing.
56
result = await evaluate("{ let z; let x = 12; } x;");
Simon Zünd's avatar
Simon Zünd committed
57 58
assertEquals(11, result);

59
assertThrowsAsync(evaluate("{ let qq = 10; } qq;"),
Simon Zünd's avatar
Simon Zünd committed
60 61 62 63 64 65 66 67 68
    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.
69 70
assertThrowsAsync(evaluate("a; let a = 7;"), ReferenceError);
assertThrowsAsync(evaluate("a;"), ReferenceError);
Simon Zünd's avatar
Simon Zünd committed
71
// This is different to non-REPL mode, which throws the kUndefined error here.
72
assertThrowsAsync(evaluate("a = 7;"),
Simon Zünd's avatar
Simon Zünd committed
73 74
    ReferenceError, "Cannot access 'a' before initialization");

75
result = await evaluate("let a = 8;");
Simon Zünd's avatar
Simon Zünd committed
76
assertEquals(undefined, result);
77
result = await evaluate("a;")
Simon Zünd's avatar
Simon Zünd committed
78 79 80
assertEquals(8, result);

// Check TDZ; store before initialization.
81
assertThrowsAsync(evaluate("b = 10; let b;"),
Simon Zünd's avatar
Simon Zünd committed
82 83
    ReferenceError, "Cannot access 'b' before initialization");
// Check that b is still broken.
84
assertThrowsAsync(evaluate("b = 10; let b;"),
Simon Zünd's avatar
Simon Zünd committed
85 86
    ReferenceError, "Cannot access 'b' before initialization");
// Check that b is still broken when the let defines a value.
87
assertThrowsAsync(evaluate("b = 10; let b = 7;"),
Simon Zünd's avatar
Simon Zünd committed
88
    ReferenceError, "Cannot access 'b' before initialization");
89
result = await evaluate("let b = 11;");
Simon Zünd's avatar
Simon Zünd committed
90 91
assertEquals(undefined, result);
// We fixed 'b'!
92
result = await evaluate("b;");
Simon Zünd's avatar
Simon Zünd committed
93 94 95 96 97 98 99 100 101 102 103 104 105
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;");

106 107 108 109 110 111 112 113 114
// 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);
Simon Zünd's avatar
Simon Zünd committed
115 116

// Re-declare const as const
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
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.
135
result = await evaluate("const c = 10;");
136 137 138 139 140 141 142 143 144 145 146
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.");
147
result = await evaluate("c;");
Simon Zünd's avatar
Simon Zünd committed
148 149
assertEquals(10, result);

150 151 152 153 154 155 156
// 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");

Simon Zünd's avatar
Simon Zünd committed
157 158 159 160
// Const vs. const in same script.
assertThrows(() => result = evaluate("const d = 9; const d = 10;"),
SyntaxError, "Identifier 'd' has already been declared");

161 162 163
// Check TDZ; const use before initialization.
assertThrowsAsync(evaluate("e; const e = 7;"), ReferenceError);
assertThrowsAsync(evaluate("e;"), ReferenceError);
Simon Zünd's avatar
Simon Zünd committed
164

165 166 167 168
result = await evaluate("const e = 8;");
assertEquals(undefined, result);
result = await evaluate("e;")
assertEquals(8, result);
Simon Zünd's avatar
Simon Zünd committed
169

170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
// 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)
Simon Zünd's avatar
Simon Zünd committed
185 186

// Re-declare const as let
187
result = await evaluate("const g = 12;");
Simon Zünd's avatar
Simon Zünd committed
188 189
assertThrows(() => result = evaluate("let g = 13;"),
    SyntaxError, "Identifier 'g' has already been declared");
190
result = await evaluate("g;");
Simon Zünd's avatar
Simon Zünd committed
191 192 193 194 195 196 197 198 199
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.
200
result = await evaluate(`Object.defineProperty(globalThis, 'j', {
Simon Zünd's avatar
Simon Zünd committed
201 202 203
  value: 1,
  configurable: true
});`);
204
result = await evaluate("j;");
Simon Zünd's avatar
Simon Zünd committed
205
assertEquals(1, result);
206 207
result = await evaluate("let j = 2;");
result = await evaluate("j;");
Simon Zünd's avatar
Simon Zünd committed
208 209 210 211
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.
212
result = await evaluate(`Object.defineProperty(globalThis, 'k', {
Simon Zünd's avatar
Simon Zünd committed
213 214 215
  value: 1,
  configurable: false
});`);
216
result = await evaluate("k;");
Simon Zünd's avatar
Simon Zünd committed
217 218 219
assertEquals(1, result);
assertThrows(() => result = evaluate("let k = 2;"),
    SyntaxError, "Identifier 'k' has already been declared");
220
result = await evaluate("k;");
Simon Zünd's avatar
Simon Zünd committed
221 222 223
assertEquals(1, result);

// ... Except if you do it in the same script.
224
result = await evaluate(`Object.defineProperty(globalThis, 'k2', {
Simon Zünd's avatar
Simon Zünd committed
225 226 227 228
  value: 1,
  configurable: false
});
let k2 = 2;`);
229
result = await evaluate("k2;");
Simon Zünd's avatar
Simon Zünd committed
230
assertEquals(2, result);
231
result = await evaluate("globalThis.k2;");
Simon Zünd's avatar
Simon Zünd committed
232 233 234
assertEquals(1, result);

// But if the property is configurable then both versions are allowed.
235
result = await evaluate(`Object.defineProperty(globalThis, 'k3', {
Simon Zünd's avatar
Simon Zünd committed
236 237 238
  value: 1,
  configurable: true
});`);
239
result = await evaluate("k3;");
Simon Zünd's avatar
Simon Zünd committed
240
assertEquals(1, result);
241 242
result = await evaluate("let k3 = 2;");
result = await evaluate("k3;");
Simon Zünd's avatar
Simon Zünd committed
243
assertEquals(2, result);
244
result = await evaluate("globalThis.k3;");
Simon Zünd's avatar
Simon Zünd committed
245 246
assertEquals(1, result);

247
result = await evaluate(`Object.defineProperty(globalThis, 'k4', {
Simon Zünd's avatar
Simon Zünd committed
248 249 250 251
  value: 1,
  configurable: true
});
let k4 = 2;`);
252
result = await evaluate("k4;");
Simon Zünd's avatar
Simon Zünd committed
253
assertEquals(2, result);
254
result = await evaluate("globalThis.k4;");
Simon Zünd's avatar
Simon Zünd committed
255 256 257 258 259 260 261
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.
262
result = await evaluate("var k6 = 1;");
Simon Zünd's avatar
Simon Zünd committed
263 264 265 266 267 268 269 270 271 272 273 274 275
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.
276 277
result = await evaluate("var k9 = 1; var k9 = 2;");
result = await evaluate("k9;");
Simon Zünd's avatar
Simon Zünd committed
278 279 280
assertEquals(2, result);

// In different scripts.
281 282 283
result = await evaluate("var k10 = 1;");
result = await evaluate("var k10 = 2;");
result = await evaluate("k10;");
Simon Zünd's avatar
Simon Zünd committed
284
assertEquals(2, result);
285
result = await evaluate("globalThis.k10;");
Simon Zünd's avatar
Simon Zünd committed
286 287 288
assertEquals(2, result);

// typeof should not throw for undeclared variables
289
result = await evaluate("typeof k11");
Simon Zünd's avatar
Simon Zünd committed
290 291
assertEquals("undefined", result);

292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
// 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);

Simon Zünd's avatar
Simon Zünd committed
339 340 341 342 343 344 345 346
// 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;");
347
result = await evaluate("l1;");
Simon Zünd's avatar
Simon Zünd committed
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
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;");
363 364
result = await evaluate("var l4 = 2;");
result = await evaluate("l4;");
Simon Zünd's avatar
Simon Zünd committed
365 366 367 368 369
assertEquals(2, result);

// Const doesn't.
assertThrows(() => result = evaluate("const l5 = 2;"),
    SyntaxError, "Identifier 'l5' has already been declared") ;
370
result = await evaluate("l5;");
Simon Zünd's avatar
Simon Zünd committed
371 372 373
assertEquals(1, result);

// Now REPL followed by non-REPL
374
result = await evaluate("let l6 = 1; const l7 = 2; let l8 = 3;");
Simon Zünd's avatar
Simon Zünd committed
375 376
result = evaluateNonREPL("l7;");
assertEquals(2, result);
377 378
result = evaluateNonREPL("l6;");
assertEquals(1, result);
Simon Zünd's avatar
Simon Zünd committed
379 380 381 382 383

// 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.
384 385 386
result = await evaluate("let l9;");
result = await evaluate("l9; let l9;"),
assertEquals(undefined, await evaluate('l9;'));
Simon Zünd's avatar
Simon Zünd committed
387 388 389 390 391 392 393 394 395 396 397 398

// 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());
399

400 401 402 403 404 405 406 407 408 409 410 411
// 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());

412 413 414 415 416
})().catch(e => {
    print(e);
    print(e.stack);
    %AbortJS("Async test is failing");
});