blob: ed81bb303877a29db4325546e9dfa45bbd8c7e8f [file] [log] [blame]
// 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.
// Flags: --harmony-private-methods
"use strict";
// Static private methods
{
let store = 1;
class C {
static #a() { return store; }
static a() { return this.#a(); }
}
assertEquals(C.a(), store);
assertThrows(() => C.a.call(new C), TypeError);
}
// Complementary static private accessors.
{
let store = 1;
class C {
static get #a() { return store; }
static set #a(val) { store = val; }
static incA() { this.#a++; }
static getA() { return this.#a; }
static setA(val) { this.#a = val; }
}
assertEquals(C.getA(), 1);
C.incA();
assertEquals(store, 2);
C.setA(3);
assertEquals(store, 3);
assertThrows(() => C.incA.call(new C), TypeError);
assertThrows(() => C.getA.call(new C), TypeError);
assertThrows(() => C.setA.call(new C), TypeError);
assertThrows(() => { const incA = C.incA; incA(); }, TypeError);
assertThrows(() => { const getA = C.getA; getA(); }, TypeError);
assertThrows(() => { const setA = C.setA; setA(); }, TypeError);
}
// Static private methods accessed explicitly in an anonymous nested class.
{
class Outer {
#a() { return 'Outer'; }
a() { return this.#a(); }
test() {
return class {
static #a() { return 'Inner'; }
static a() { return this.#a(); }
};
}
}
const obj = new Outer;
const C = obj.test();
assertEquals(C.a(), 'Inner');
assertThrows(() => obj.a.call(C), TypeError);
assertThrows(() => obj.a.call(new C), TypeError);
}
// Static private methods accessed explicitly in a named nested class.
{
class Outer {
#a() { return 'Outer'; }
a() { return this.#a(); }
test() {
return class Inner {
static #a() { return 'Inner'; }
static a() { return this.#a(); }
};
}
}
const obj = new Outer;
const C = obj.test();
assertEquals(C.a(), 'Inner');
assertThrows(() => obj.a.call(C), TypeError);
assertThrows(() => obj.a.call(new C), TypeError);
}
// Static private methods accessed through eval in an anonymous nested class.
{
class Outer {
#a() { return 'Outer'; }
a() { return this.#a(); }
test() {
return class {
static #a() { return 'Inner'; }
static a(str) { return eval(str); }
};
}
}
const obj = new Outer;
const C = obj.test();
assertEquals(C.a('this.#a()'), 'Inner');
assertThrows(() => C.a('Outer.#a()'), TypeError);
}
// Static private methods accessed through eval in a named nested class.
{
class Outer {
#a() { return 'Outer'; }
a() { return this.#a(); }
test() {
return class Inner {
static #a() { return 'Inner'; }
static a(str) { return eval(str); }
};
}
}
const obj = new Outer;
const C = obj.test();
assertEquals(C.a('this.#a()'), 'Inner');
assertEquals(C.a('Inner.#a()'), 'Inner');
assertThrows(() => C.a('Outer.#a()'), TypeError);
assertThrows(() => C.run('(new Outer).#a()'), TypeError);
}
// Static private methods in the outer class accessed through eval
// in a named nested class.
{
class Outer {
static #a() { return 'Outer'; }
static test() {
return class Inner {
static run(str) { return eval(str); }
};
}
}
const C = Outer.test();
assertEquals(C.run('Outer.#a()'), 'Outer');
assertThrows(() => C.run('this.#a()'), TypeError);
assertThrows(() => C.run('Inner.#a()'), TypeError);
assertThrows(() => C.run('(new Outer).#a()'), TypeError);
}
// Static private methods in the outer class accessed explicitly
// in a named nested class.
{
class Outer {
static #a() { return 'Outer'; }
static test() {
return class Inner {
static getA(klass) { return klass.#a(); }
};
}
}
const C = Outer.test();
assertEquals(C.getA(Outer), 'Outer');
assertThrows(() => C.getA.call(C), TypeError);
assertThrows(() => C.getA.call(new Outer), TypeError);
}
// Static private methods in the outer class accessed explicitly
// in an anonymous nested class.
{
class Outer {
static #a() { return 'Outer'; }
static test() {
return class {
static getA(klass) { return klass.#a(); }
};
}
}
const C = Outer.test();
assertEquals(C.getA(Outer), 'Outer');
assertThrows(() => C.getA.call(C), TypeError);
assertThrows(() => C.getA.call(new Outer), TypeError);
}
// Super property access in static private methods
{
class A {
static a = 1;
}
class B extends A {
static #a() { return super.a; }
static getA() { return this.#a(); }
}
assertEquals(B.getA(), 1);
}
// Invalid super property access in static private methods
{
class A {
static #a() { return 1; }
static getA() { return this.#a(); }
}
class B extends A {
static getA() { return super.getA(); }
}
assertThrows(() => B.getA(), TypeError);
}
// Static private methods accessed in eval.
{
class C {
static #m(v) { return v; }
static test(str) {
return eval(str);
}
}
assertEquals(C.test('this.#m(1)'), 1);
}
// Test that the receiver is checked during run time.
{
const C = class {
static #a() { }
static test(klass) { return klass.#a; }
};
const test = C.test;
assertThrows(test, TypeError);
}
// Duplicate static private accessors and methods.
{
assertThrows('class C { static get #a() {} static get #a() {} }', SyntaxError);
assertThrows('class C { static get #a() {} static #a() {} }', SyntaxError);
assertThrows('class C { static get #a() {} get #a() {} }', SyntaxError);
assertThrows('class C { static get #a() {} set #a(val) {} }', SyntaxError);
assertThrows('class C { static get #a() {} #a() {} }', SyntaxError);
assertThrows('class C { static set #a(val) {} static set #a(val) {} }', SyntaxError);
assertThrows('class C { static set #a(val) {} static #a() {} }', SyntaxError);
assertThrows('class C { static set #a(val) {} get #a() {} }', SyntaxError);
assertThrows('class C { static set #a(val) {} set #a(val) {} }', SyntaxError);
assertThrows('class C { static set #a(val) {} #a() {} }', SyntaxError);
assertThrows('class C { static #a() {} static #a() {} }', SyntaxError);
assertThrows('class C { static #a() {} #a(val) {} }', SyntaxError);
assertThrows('class C { static #a() {} set #a(val) {} }', SyntaxError);
assertThrows('class C { static #a() {} get #a() {} }', SyntaxError);
}