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