a3c7e96891
Expressions in class heritage position do not have access to the inheriting class's private names, only its lexical bindings. The parser currently uses the same scope chain for both. This CL makes scopes in class heritage position skip their outer class when resolving private names. Whether a scope needs to skip is kept as a bit on various scope-related data structures. See implementation doc at https://docs.google.com/document/d/1d3o_SQqcICxfjLMw53OOaiIQux0ppNHQJnjZHtCQLwA Bug: v8:9177 Change-Id: I77e491a9d4a261131274f12ddf052af7ac31a921 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1769486 Commit-Queue: Shu-yu Guo <syg@chromium.org> Reviewed-by: Toon Verwaest <verwaest@chromium.org> Reviewed-by: Mathias Bynens <mathias@chromium.org> Cr-Commit-Position: refs/heads/master@{#63586}
138 lines
3.5 KiB
JavaScript
138 lines
3.5 KiB
JavaScript
// 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);
|
|
}
|