mirror of
https://github.com/nodejs/node.git
synced 2025-05-21 14:02:10 +00:00

PR-URL: https://github.com/nodejs/node/pull/41610 Reviewed-By: Jiawen Geng <technicalcute@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
219 lines
4.5 KiB
JavaScript
219 lines
4.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.
|
|
|
|
"use strict";
|
|
|
|
// Complementary private accessors.
|
|
{
|
|
let store = 1;
|
|
class C {
|
|
get #a() { return store; }
|
|
set #a(val) { store = val; }
|
|
incA() { this.#a++; } // CountOperation
|
|
setA(val) { this.#a = val; }
|
|
getA() { return this.#a; }
|
|
}
|
|
|
|
const c = new C;
|
|
assertEquals(store, c.getA());
|
|
assertEquals(1, c.getA());
|
|
c.setA(2);
|
|
assertEquals(store, c.getA());
|
|
assertEquals(2, c.getA());
|
|
c.incA();
|
|
assertEquals(store, c.getA());
|
|
assertEquals(3, c.getA());
|
|
}
|
|
|
|
// Compound assignment.
|
|
{
|
|
let store;
|
|
class A {
|
|
get #a() { return store; }
|
|
set #a(val) { store = val; }
|
|
getA() { return this.#a; }
|
|
constructor(val) {
|
|
({ y: this.#a } = val);
|
|
}
|
|
}
|
|
|
|
const a = new A({y: 'test'});
|
|
assertEquals('test', a.getA());
|
|
}
|
|
|
|
// Accessing super in private accessors.
|
|
{
|
|
class A { foo(val) {} }
|
|
class C extends A {
|
|
set #a(val) { super.foo(val); }
|
|
}
|
|
new C();
|
|
|
|
class D extends A {
|
|
get #a() { return super.foo; }
|
|
}
|
|
new D();
|
|
|
|
class E extends A {
|
|
set #a(val) { super.foo(val); }
|
|
get #a() { return super.foo; }
|
|
}
|
|
new E();
|
|
}
|
|
|
|
// Nested private accessors.
|
|
{
|
|
class C {
|
|
get #a() {
|
|
let storeD = 'd';
|
|
class D {
|
|
// Shadows outer #a
|
|
get #a() { return storeD; }
|
|
getD() { return this.#a; }
|
|
}
|
|
return new D;
|
|
}
|
|
getA() {
|
|
return this.#a;
|
|
}
|
|
}
|
|
assertEquals('d', new C().getA().getD());
|
|
}
|
|
|
|
{
|
|
assertThrows(() => {
|
|
class A {
|
|
[this.#a] = 1;
|
|
get #a() {}
|
|
}
|
|
}, TypeError);
|
|
|
|
assertThrows(() => {
|
|
class A {
|
|
[this.#a] = 1;
|
|
set #a(val) {}
|
|
}
|
|
}, TypeError);
|
|
|
|
assertThrows(() => {
|
|
class A {
|
|
[this.#a] = 1;
|
|
set #a(val) {}
|
|
get #a() {}
|
|
}
|
|
}, TypeError);
|
|
}
|
|
|
|
// Duplicate private accessors.
|
|
// https://tc39.es/proposal-private-methods/#sec-static-semantics-early-errors
|
|
{
|
|
assertThrows('class C { get #a() {} get #a() {} }', SyntaxError);
|
|
assertThrows('class C { set #a(val) {} set #a(val) {} }', SyntaxError);
|
|
}
|
|
|
|
// Test that the lhs don't get re-evaluated in the assignment, and it always
|
|
// gets evaluated before the rhs.
|
|
{
|
|
let objectCount = 0;
|
|
let operations = [];
|
|
let lhsObject = {
|
|
get property() {
|
|
operations.push('lhsEvaluation');
|
|
return new Foo();
|
|
}
|
|
};
|
|
let rhs = () => {operations.push('rhsEvaluation'); return 1; };
|
|
class Foo {
|
|
id = objectCount++;
|
|
get #foo() {
|
|
operations.push('get', this.id);
|
|
return 0;
|
|
}
|
|
set #foo(x) {
|
|
operations.push('set', this.id);
|
|
}
|
|
static compound() {
|
|
lhsObject.property.#foo += rhs();
|
|
}
|
|
static assign() {
|
|
lhsObject.property.#foo = lhsObject.property.#foo + rhs();
|
|
}
|
|
}
|
|
objectCount = 0;
|
|
operations = [];
|
|
Foo.compound();
|
|
assertEquals(1, objectCount);
|
|
assertEquals(
|
|
['lhsEvaluation', 'get', 0, 'rhsEvaluation', 'set', 0],
|
|
operations);
|
|
|
|
objectCount = 0;
|
|
operations = [];
|
|
Foo.assign();
|
|
assertEquals(2, objectCount);
|
|
assertEquals(
|
|
['lhsEvaluation', 'lhsEvaluation', 'get', 1, 'rhsEvaluation', 'set', 0],
|
|
operations);
|
|
}
|
|
|
|
// Test that the brand checks are done on the lhs evaluated before the rhs.
|
|
{
|
|
let objectCount = 0;
|
|
let operations = [];
|
|
let maximumObjects = 1;
|
|
let lhsObject = {
|
|
get property() {
|
|
operations.push('lhsEvaluation');
|
|
return (objectCount >= maximumObjects) ? {id: -1} : new Foo();
|
|
}
|
|
};
|
|
let rhs = () => {operations.push('rhsEvaluation'); return 1; };
|
|
|
|
class Foo {
|
|
id = objectCount++;
|
|
|
|
set #foo(val) {
|
|
operations.push('set', this.id);
|
|
}
|
|
|
|
get #foo() {
|
|
operations.push('get', this.id);
|
|
return 0;
|
|
}
|
|
|
|
static compound() {
|
|
lhsObject.property.#foo += rhs();
|
|
}
|
|
|
|
static assign() {
|
|
lhsObject.property.#foo = lhsObject.property.#foo + rhs();
|
|
}
|
|
}
|
|
|
|
objectCount = 0;
|
|
operations = [];
|
|
maximumObjects = 1;
|
|
Foo.compound();
|
|
assertEquals(1, objectCount);
|
|
assertEquals(
|
|
['lhsEvaluation', 'get', 0, 'rhsEvaluation', 'set', 0],
|
|
operations);
|
|
|
|
objectCount = 0;
|
|
operations = [];
|
|
maximumObjects = 2;
|
|
Foo.assign();
|
|
assertEquals(2, objectCount);
|
|
assertEquals(
|
|
['lhsEvaluation', 'lhsEvaluation', 'get', 1, 'rhsEvaluation', 'set', 0],
|
|
operations);
|
|
|
|
objectCount = 0;
|
|
operations = [];
|
|
maximumObjects = 1;
|
|
assertThrows(() => Foo.assign(), TypeError, /Receiver must be an instance of class Foo/);
|
|
assertEquals(1, objectCount);
|
|
assertEquals(['lhsEvaluation', 'lhsEvaluation'], operations);
|
|
}
|