v8/test/mjsunit/regress/regress-v8-11360.js
Joyee Cheung f77b05d464 [class] fix evaluation order and errors in private accessor assignments
In assignments the lhs should be evaluated first and shouldn't be
re-evaluated when the value of the rhs is available. Fix it by
saving the receiver and the key registers into AssignmentLhsData
before building the assignment and use them later, instead of visiting
the AST again to retrieve the receiver.

In addition, now that we save the receiver register, use it to
perform the brand check even when we know for sure that it's
going to fail later because it's a write to a private
method or accessing the accessor in the wrong way (v8:11364),
so that the brand check error always appears first if it is present,
as specified in
https://tc39.es/proposal-private-methods/#sec-privatefieldget

Drive-by: unify the brand check error messages, and replace "Object"
with "Receiver" in the messages for clarity. The instance private
brand check now throws "Receiver must be an instance of class <name>"
and the static private brand check now throws "Receiver must be
class <name>". Also always set the expression position to the
property load position, because the brand check failure comes from
the load operation.

Bug: v8:12352, v8:11364
Change-Id: I61a8979b2e02b561dd5b2b35f9e0b6691fe07599
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3266964
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Reviewed-by: Marja Hölttä <marja@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/main@{#77797}
2021-11-09 15:36:28 +00:00

213 lines
4.3 KiB
JavaScript

// Copyright 2021 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.
(function TestCompoundAssignmentToPrivateField() {
class C {
#foo = 1;
m() {
return this.#foo += 1;
}
}
assertEquals(2, (new C()).m());
})();
(function TestCompoundAssignmentToPrivateFieldWithOnlyGetter() {
class C {
get #foo() { return 1; }
m() {
return this.#foo += 1;
}
}
assertThrows(() => { (new C()).m(); });
})();
(function TestCompoundAssignmentToPrivateFieldWithOnlySetter() {
class C {
set #foo(a) { }
m() {
return this.#foo += 1;
}
}
assertThrows(() => { (new C()).m(); });
})();
(function TestCompoundAssignmentToPrivateFieldWithGetterAndSetter() {
class C {
get #foo() { return 1; }
set #foo(a) { }
m() {
return this.#foo += 1;
}
}
assertEquals(2, (new C()).m());
})();
(function TestCompoundAssignmentToPrivateMethod() {
class C {
m() {
return this.#pm += 1;
}
#pm() {}
}
assertThrows(() => { (new O()).m(); });
})();
(function TestCompoundAssignmentToStaticPrivateField() {
class C {
static #foo = 1;
m() {
return C.#foo += 1;
}
}
assertEquals(2, (new C()).m());
})();
(function TestCompoundAssignmentToStaticPrivateFieldWithOnlyGetter() {
class C {
static get #foo() { return 1; }
m() {
return C.#foo += 1;
}
}
assertThrows(() => { (new C()).m(); });
})();
(function TestCompoundAssignmentToStaticPrivateFieldWithOnlySetter() {
class C {
static set #foo(a) { }
m() {
return C.#foo += 1;
}
}
assertThrows(() => { (new C()).m(); });
})();
(function TestCompoundAssignmentToStaticPrivateFieldWithGetterAndSetter() {
class C {
static get #foo() { return 1; }
static set #foo(a) { }
m() {
return C.#foo += 1;
}
}
assertEquals(2, (new C()).m());
})();
(function TestCompoundAssignmentToStaticPrivateMethod() {
class C {
m() {
return C.#pm += 1;
}
static #pm() {}
}
assertThrows(() => { (new O()).m(); });
})();
// The following tests test the above cases w/ brand check failures.
(function TestBrandCheck_CompoundAssignmentToPrivateField() {
class C {
#foo = 1;
m() {
return this.#foo += 1;
}
}
assertThrows(() => { C.prototype.m.call({}); }, TypeError,
/Cannot read private member/);
// It's the same error we get from this case:
class C2 {
#foo = 1;
m() {
return this.#foo;
}
}
assertThrows(() => { C2.prototype.m.call({}); }, TypeError,
/Cannot read private member/);
})();
(function TestBrandCheck_CompoundAssignmentToPrivateFieldWithOnlyGetter() {
class C {
get #foo() { return 1; }
m() {
return this.#foo += 1;
}
}
assertThrows(() => { C.prototype.m.call({}); }, TypeError,
/Receiver must be an instance of class/);
// It's the same error we get from this case:
class C2 {
get #foo() { return 1; }
m() {
return this.#foo;
}
}
assertThrows(() => { C2.prototype.m.call({}); }, TypeError,
/Receiver must be an instance of class/);
})();
(function TestBrandCheck_CompoundAssignmentToPrivateFieldWithOnlySetter() {
class C {
set #foo(a) { }
m() {
return this.#foo += 1;
}
}
assertThrows(() => { C.prototype.m.call({}); }, TypeError,
/Receiver must be an instance of class/);
})();
(function TestBrandCheck_CompoundAssignmentToPrivateFieldWithGetterAndSetter() {
class C {
get #foo() { return 1; }
set #foo(a) { }
m() {
return this.#foo += 1;
}
}
assertThrows(() => { C.prototype.m.call({}); }, TypeError,
/Receiver must be an instance of class/);
// It's the same error we get from this case:
class C2 {
get #foo() { return 1; }
set #foo(a) { }
m() {
return this.#foo;
}
}
assertThrows(() => { C2.prototype.m.call({}); }, TypeError,
/Receiver must be an instance of class/);
})();
(function TestBrandCheck_CompoundAssignmentToPrivateMethod() {
class C {
m() {
return this.#pm += 1;
}
#pm() {}
}
assertThrows(() => { C.prototype.m.call({}); }, TypeError,
/Receiver must be an instance of class/);
})();