v8/test/mjsunit/harmony/private-name-scopes.js
Shu-yu Guo a3c7e96891 [class] Fix private name scope chain
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}
2019-09-06 00:52:07 +00:00

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);
}