|  | // 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. | 
|  |  | 
|  | { | 
|  | let heritageFn; | 
|  | class O { | 
|  | #f = "O.#f"; | 
|  | static C = class C extends (heritageFn = function () { | 
|  | return class D { | 
|  | exfil(obj) { return obj.#f; } | 
|  | exfilEval(obj) { return eval("obj.#f"); } | 
|  | }; | 
|  | }) { | 
|  | #f = "C.#f"; | 
|  | }; | 
|  | } | 
|  |  | 
|  | const o = new O; | 
|  | const c = new O.C; | 
|  | const D = heritageFn(); | 
|  | const d = new D; | 
|  | assertEquals(d.exfil(o), "O.#f"); | 
|  | assertEquals(d.exfilEval(o), "O.#f"); | 
|  | assertThrows(() => d.exfil(c), TypeError); | 
|  | assertThrows(() => d.exfilEval(c), TypeError); | 
|  | } | 
|  |  | 
|  | // Early errors | 
|  |  | 
|  | assertThrows(() => eval("new class extends " + | 
|  | "(class { m() { let x = this.#f; } }) " + | 
|  | "{ #f }"), SyntaxError); | 
|  |  | 
|  | assertThrows(() => eval("new class extends this.#foo { #foo }"), SyntaxError); | 
|  |  | 
|  | // Runtime errors | 
|  |  | 
|  | { | 
|  | // Test private name context chain recalc. | 
|  | let heritageFn; | 
|  | class O { | 
|  | #f = "O.#f"; | 
|  | static C = class C extends (heritageFn = function () { | 
|  | return class D { exfil(obj) { return obj.#f; } } | 
|  | }) { | 
|  | #f = "C.#f"; | 
|  | }; | 
|  | } | 
|  |  | 
|  | const o = new O; | 
|  | const c = new O.C; | 
|  | const D = heritageFn(); | 
|  | const d = new D; | 
|  | assertEquals(d.exfil(o), "O.#f"); | 
|  | assertThrows(() => d.exfil(c), TypeError); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Test private name context chain recalc with nested closures with context. | 
|  | let heritageFn; | 
|  | class O { | 
|  | #f = "O.#f"; | 
|  | static C = class C extends (heritageFn = function () { | 
|  | let forceContext = 1; | 
|  | return () => { | 
|  | assertEquals(forceContext, 1); | 
|  | return class D { exfil(obj) { return obj.#f; } } | 
|  | }; | 
|  | }) { | 
|  | #f = "C.#f"; | 
|  | }; | 
|  | } | 
|  |  | 
|  | const o = new O; | 
|  | const c = new O.C; | 
|  | const D = heritageFn()(); | 
|  | const d = new D; | 
|  | assertEquals(d.exfil(o), "O.#f"); | 
|  | assertThrows(() => d.exfil(c), TypeError); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Test private name context chain recalc where skipped class has no context. | 
|  | let heritageFn; | 
|  | class O { | 
|  | #f = "O.#f"; | 
|  | static C = class C0 extends (class C1 extends (heritageFn = function (obj) { | 
|  | if (obj) { return obj.#f; } | 
|  | }) {}) { | 
|  | #f = "C0.#f" | 
|  | } | 
|  | } | 
|  |  | 
|  | const o = new O; | 
|  | const c = new O.C; | 
|  | assertEquals(heritageFn(o), "O.#f"); | 
|  | assertThrows(() => heritageFn(c), TypeError); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Test private name context chain recalc where skipping function has no | 
|  | // context. | 
|  | let heritageFn; | 
|  | class O { | 
|  | #f = "O.#f"; | 
|  | static C = class C extends (heritageFn = function () { | 
|  | return (obj) => { return obj.#f; } | 
|  | }) { | 
|  | #f = "C.#f"; | 
|  | } | 
|  | } | 
|  |  | 
|  | const o = new O; | 
|  | const c = new O.C; | 
|  | assertEquals(heritageFn()(o), "O.#f"); | 
|  | assertThrows(() => heritageFn()(c), TypeError); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Test private name context chain recalc where neither skipped class nor | 
|  | // skipping function has contexts. | 
|  | let heritageFn; | 
|  | class O { | 
|  | #f = "O.#f"; | 
|  | static C = class C0 extends (class C1 extends (heritageFn = function () { | 
|  | return (obj) => { return obj.#f; } | 
|  | }) {}) { | 
|  | #f = "C0.#f"; | 
|  | } | 
|  | } | 
|  |  | 
|  | const o = new O; | 
|  | const c = new O.C; | 
|  | assertEquals(heritageFn()(o), "O.#f"); | 
|  | assertThrows(() => heritageFn()(c), TypeError); | 
|  | } |