Commit f77b05d4 authored by Joyee Cheung's avatar Joyee Cheung Committed by V8 LUCI CQ

[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: 's avatarMarja Hölttä <marja@chromium.org>
Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/main@{#77797}
parent 410a5cf9
......@@ -442,7 +442,8 @@ namespace internal {
"Invalid module export name: contains unpaired surrogate") \
T(InvalidRegExpFlags, "Invalid flags supplied to RegExp constructor '%'") \
T(InvalidOrUnexpectedToken, "Invalid or unexpected token") \
T(InvalidPrivateBrand, "Object must be an instance of class %") \
T(InvalidPrivateBrandInstance, "Receiver must be an instance of class %") \
T(InvalidPrivateBrandStatic, "Receiver must be class %") \
T(InvalidPrivateBrandReinitialization, \
"Cannot initialize private methods of class % twice on the same object") \
T(InvalidPrivateFieldReinitialization, \
......
......@@ -460,7 +460,7 @@ MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<Name> name,
(name_string->length() == 0)
? isolate()->factory()->anonymous_string()
: name_string;
return TypeError(MessageTemplate::kInvalidPrivateBrand, object,
return TypeError(MessageTemplate::kInvalidPrivateBrandInstance, object,
class_name);
}
return TypeError(MessageTemplate::kInvalidPrivateMemberRead, object,
......
......@@ -3824,9 +3824,9 @@ BytecodeGenerator::AssignmentLhsData::NamedSuperProperty(
// static
BytecodeGenerator::AssignmentLhsData
BytecodeGenerator::AssignmentLhsData::PrivateMethodOrAccessor(
AssignType type, Property* property) {
return AssignmentLhsData(type, property, RegisterList(), Register(),
Register(), nullptr, nullptr);
AssignType type, Property* property, Register object, Register key) {
return AssignmentLhsData(type, property, RegisterList(), object, key, nullptr,
nullptr);
}
// static
BytecodeGenerator::AssignmentLhsData
......@@ -3864,7 +3864,14 @@ BytecodeGenerator::AssignmentLhsData BytecodeGenerator::PrepareAssignmentLhs(
case PRIVATE_SETTER_ONLY:
case PRIVATE_GETTER_AND_SETTER: {
DCHECK(!property->IsSuperAccess());
return AssignmentLhsData::PrivateMethodOrAccessor(assign_type, property);
AccumulatorPreservingScope scope(this, accumulator_preserving_mode);
Register object = VisitForRegisterValue(property->obj());
Register key =
assign_type == PRIVATE_GETTER_ONLY || assign_type == PRIVATE_METHOD
? Register()
: VisitForRegisterValue(property->key());
return AssignmentLhsData::PrivateMethodOrAccessor(assign_type, property,
object, key);
}
case NAMED_SUPER_PROPERTY: {
AccumulatorPreservingScope scope(this, accumulator_preserving_mode);
......@@ -4420,11 +4427,15 @@ void BytecodeGenerator::BuildAssignment(
break;
}
case PRIVATE_METHOD: {
Property* property = lhs_data.expr()->AsProperty();
BuildPrivateBrandCheck(property, lhs_data.object());
BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateMethodWrite,
lhs_data.expr()->AsProperty());
break;
}
case PRIVATE_GETTER_ONLY: {
Property* property = lhs_data.expr()->AsProperty();
BuildPrivateBrandCheck(property, lhs_data.object());
BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateSetterAccess,
lhs_data.expr()->AsProperty());
break;
......@@ -4434,11 +4445,8 @@ void BytecodeGenerator::BuildAssignment(
Register value = register_allocator()->NewRegister();
builder()->StoreAccumulatorInRegister(value);
Property* property = lhs_data.expr()->AsProperty();
Register object = VisitForRegisterValue(property->obj());
Register key = VisitForRegisterValue(property->key());
BuildPrivateBrandCheck(property, object,
MessageTemplate::kInvalidPrivateMemberWrite);
BuildPrivateSetterAccess(object, key, value);
BuildPrivateBrandCheck(property, lhs_data.object());
BuildPrivateSetterAccess(lhs_data.object(), lhs_data.key(), value);
if (!execution_result()->IsEffect()) {
builder()->LoadAccumulatorWithRegister(value);
}
......@@ -4493,9 +4501,7 @@ void BytecodeGenerator::VisitCompoundAssignment(CompoundAssignment* expr) {
// The property access is invalid, but if the brand check fails too, we
// need to return the error from the brand check.
Property* property = lhs_data.expr()->AsProperty();
Register object = VisitForRegisterValue(property->obj());
BuildPrivateBrandCheck(property, object,
MessageTemplate::kInvalidPrivateMemberRead);
BuildPrivateBrandCheck(property, lhs_data.object());
BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateMethodWrite,
lhs_data.expr()->AsProperty());
break;
......@@ -4504,9 +4510,7 @@ void BytecodeGenerator::VisitCompoundAssignment(CompoundAssignment* expr) {
// The property access is invalid, but if the brand check fails too, we
// need to return the error from the brand check.
Property* property = lhs_data.expr()->AsProperty();
Register object = VisitForRegisterValue(property->obj());
BuildPrivateBrandCheck(property, object,
MessageTemplate::kInvalidPrivateMemberRead);
BuildPrivateBrandCheck(property, lhs_data.object());
BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateSetterAccess,
lhs_data.expr()->AsProperty());
......@@ -4516,20 +4520,15 @@ void BytecodeGenerator::VisitCompoundAssignment(CompoundAssignment* expr) {
// The property access is invalid, but if the brand check fails too, we
// need to return the error from the brand check.
Property* property = lhs_data.expr()->AsProperty();
Register object = VisitForRegisterValue(property->obj());
BuildPrivateBrandCheck(property, object,
MessageTemplate::kInvalidPrivateMemberRead);
BuildPrivateBrandCheck(property, lhs_data.object());
BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateGetterAccess,
lhs_data.expr()->AsProperty());
break;
}
case PRIVATE_GETTER_AND_SETTER: {
Property* property = lhs_data.expr()->AsProperty();
Register object = VisitForRegisterValue(property->obj());
Register key = VisitForRegisterValue(property->key());
BuildPrivateBrandCheck(property, object,
MessageTemplate::kInvalidPrivateMemberRead);
BuildPrivateGetterAccess(object, key);
BuildPrivateBrandCheck(property, lhs_data.object());
BuildPrivateGetterAccess(lhs_data.object(), lhs_data.key());
break;
}
}
......@@ -5021,6 +5020,7 @@ void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* property) {
VisitKeyedSuperPropertyLoad(property, Register::invalid_value());
break;
case PRIVATE_SETTER_ONLY: {
BuildPrivateBrandCheck(property, obj);
BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateGetterAccess,
property);
break;
......@@ -5028,14 +5028,12 @@ void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* property) {
case PRIVATE_GETTER_ONLY:
case PRIVATE_GETTER_AND_SETTER: {
Register key = VisitForRegisterValue(property->key());
BuildPrivateBrandCheck(property, obj,
MessageTemplate::kInvalidPrivateMemberRead);
BuildPrivateBrandCheck(property, obj);
BuildPrivateGetterAccess(obj, key);
break;
}
case PRIVATE_METHOD: {
BuildPrivateBrandCheck(property, obj,
MessageTemplate::kInvalidPrivateMemberRead);
BuildPrivateBrandCheck(property, obj);
// In the case of private methods, property->key() is the function to be
// loaded (stored in a context slot), so load this directly.
VisitForAccumulatorValue(property->key());
......@@ -5134,11 +5132,11 @@ void BytecodeGenerator::BuildPrivateMethodIn(Variable* private_name,
}
void BytecodeGenerator::BuildPrivateBrandCheck(Property* property,
Register object,
MessageTemplate tmpl) {
Register object) {
Variable* private_name = property->key()->AsVariableProxy()->var();
DCHECK(IsPrivateMethodOrAccessorVariableMode(private_name->mode()));
ClassScope* scope = private_name->scope()->AsClassScope();
builder()->SetExpressionPosition(property);
if (private_name->is_static()) {
// For static private methods, the only valid receiver is the class.
// Load the class constructor.
......@@ -5168,13 +5166,21 @@ void BytecodeGenerator::BuildPrivateBrandCheck(Property* property,
BytecodeLabel return_check;
builder()->CompareReference(object).JumpIfTrue(
ToBooleanMode::kAlreadyBoolean, &return_check);
BuildInvalidPropertyAccess(tmpl, property);
const AstRawString* name = scope->class_variable()->raw_name();
RegisterList args = register_allocator()->NewRegisterList(2);
builder()
->LoadLiteral(
Smi::FromEnum(MessageTemplate::kInvalidPrivateBrandStatic))
.StoreAccumulatorInRegister(args[0])
.LoadLiteral(name)
.StoreAccumulatorInRegister(args[1])
.CallRuntime(Runtime::kNewTypeError, args)
.Throw();
builder()->Bind(&return_check);
}
} else {
BuildVariableLoadForAccumulatorValue(scope->brand(),
HoleCheckMode::kElided);
builder()->SetExpressionPosition(property);
builder()->LoadKeyedProperty(
object, feedback_index(feedback_spec()->AddKeyedLoadICSlot()));
}
......@@ -5875,16 +5881,22 @@ void BytecodeGenerator::VisitCountOperation(CountOperation* expr) {
break;
}
case PRIVATE_METHOD: {
object = VisitForRegisterValue(property->obj());
BuildPrivateBrandCheck(property, object);
BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateMethodWrite,
property);
return;
}
case PRIVATE_GETTER_ONLY: {
object = VisitForRegisterValue(property->obj());
BuildPrivateBrandCheck(property, object);
BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateSetterAccess,
property);
return;
}
case PRIVATE_SETTER_ONLY: {
object = VisitForRegisterValue(property->obj());
BuildPrivateBrandCheck(property, object);
BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateGetterAccess,
property);
return;
......@@ -5892,8 +5904,7 @@ void BytecodeGenerator::VisitCountOperation(CountOperation* expr) {
case PRIVATE_GETTER_AND_SETTER: {
object = VisitForRegisterValue(property->obj());
key = VisitForRegisterValue(property->key());
BuildPrivateBrandCheck(property, object,
MessageTemplate::kInvalidPrivateMemberRead);
BuildPrivateBrandCheck(property, object);
BuildPrivateGetterAccess(object, key);
break;
}
......
......@@ -98,7 +98,9 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
const AstRawString* name);
static AssignmentLhsData KeyedProperty(Register object, Register key);
static AssignmentLhsData PrivateMethodOrAccessor(AssignType type,
Property* property);
Property* property,
Register object,
Register key);
static AssignmentLhsData NamedSuperProperty(
RegisterList super_property_args);
static AssignmentLhsData KeyedSuperProperty(
......@@ -117,11 +119,17 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
return object_expr_;
}
Register object() const {
DCHECK(assign_type_ == NAMED_PROPERTY || assign_type_ == KEYED_PROPERTY);
DCHECK(assign_type_ == NAMED_PROPERTY || assign_type_ == KEYED_PROPERTY ||
assign_type_ == PRIVATE_METHOD ||
assign_type_ == PRIVATE_GETTER_ONLY ||
assign_type_ == PRIVATE_SETTER_ONLY ||
assign_type_ == PRIVATE_GETTER_AND_SETTER);
return object_;
}
Register key() const {
DCHECK(assign_type_ == KEYED_PROPERTY);
DCHECK(assign_type_ == KEYED_PROPERTY ||
assign_type_ == PRIVATE_SETTER_ONLY ||
assign_type_ == PRIVATE_GETTER_AND_SETTER);
return key_;
}
const AstRawString* name() const {
......@@ -311,8 +319,7 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
void VisitRestArgumentsArray(Variable* rest);
void VisitCallSuper(Call* call);
void BuildInvalidPropertyAccess(MessageTemplate tmpl, Property* property);
void BuildPrivateBrandCheck(Property* property, Register object,
MessageTemplate tmpl);
void BuildPrivateBrandCheck(Property* property, Register object);
void BuildPrivateMethodIn(Variable* private_name,
Expression* object_expression);
void BuildPrivateGetterAccess(Register obj, Register access_pair);
......
......@@ -49,9 +49,10 @@ MaybeHandle<Object> Runtime::GetObjectProperty(
if (!it.IsFound() && key->IsSymbol() &&
Symbol::cast(*key).is_private_name()) {
MessageTemplate message = Symbol::cast(*key).IsPrivateBrand()
? MessageTemplate::kInvalidPrivateBrand
: MessageTemplate::kInvalidPrivateMemberRead;
MessageTemplate message =
Symbol::cast(*key).IsPrivateBrand()
? MessageTemplate::kInvalidPrivateBrandInstance
: MessageTemplate::kInvalidPrivateMemberRead;
THROW_NEW_ERROR(isolate, NewTypeError(message, key, lookup_start_object),
Object);
}
......
......@@ -41,15 +41,15 @@ bytecodes: [
/* 83 E> */ B(CallRuntime), U16(Runtime::kLoadPrivateSetter), R(2), U8(1),
B(Star4),
B(CallProperty1), R(4), R(this), R(3), U8(7),
/* 91 S> */ B(LdaSmi), I8(1),
B(Star1),
B(LdaImmutableCurrentContextSlot), U8(2),
/* 91 S> */ B(LdaImmutableCurrentContextSlot), U8(2),
B(Star2),
B(LdaSmi), I8(1),
B(Star3),
B(LdaImmutableCurrentContextSlot), U8(3),
/* 96 E> */ B(LdaKeyedProperty), R(this), U8(9),
B(CallRuntime), U16(Runtime::kLoadPrivateSetter), R(3), U8(1),
B(CallRuntime), U16(Runtime::kLoadPrivateSetter), R(2), U8(1),
B(Star4),
B(CallProperty1), R(4), R(this), R(1), U8(11),
B(CallProperty1), R(4), R(this), R(3), U8(11),
/* 108 S> */ B(LdaImmutableCurrentContextSlot), U8(2),
B(Star2),
B(LdaImmutableCurrentContextSlot), U8(3),
......@@ -73,19 +73,21 @@ snippet: "
var test = B;
new test;
"
frame size: 3
frame size: 4
parameter count: 1
bytecode array length: 23
bytecode array length: 28
bytecodes: [
B(LdaImmutableCurrentContextSlot), U8(3),
B(Star0),
B(Ldar), R(context),
/* 48 E> */ B(StaKeyedPropertyAsDefine), R(this), R(0), U8(0),
/* 53 S> */ B(Wide), B(LdaSmi), I16(285),
B(Star1),
B(LdaConstant), U8(0),
/* 53 S> */ B(LdaImmutableCurrentContextSlot), U8(3),
/* 58 E> */ B(LdaKeyedProperty), R(this), U8(2),
B(Wide), B(LdaSmi), I16(286),
B(Star2),
B(CallRuntime), U16(Runtime::kNewTypeError), R(1), U8(2),
B(LdaConstant), U8(0),
B(Star3),
B(CallRuntime), U16(Runtime::kNewTypeError), R(2), U8(2),
B(Throw),
]
constant pool: [
......@@ -103,19 +105,21 @@ snippet: "
var test = C;
new test;
"
frame size: 3
frame size: 4
parameter count: 1
bytecode array length: 23
bytecode array length: 28
bytecodes: [
B(LdaImmutableCurrentContextSlot), U8(3),
B(Star0),
B(Ldar), R(context),
/* 41 E> */ B(StaKeyedPropertyAsDefine), R(this), R(0), U8(0),
/* 46 S> */ B(Wide), B(LdaSmi), I16(284),
B(Star1),
B(LdaConstant), U8(0),
/* 46 S> */ B(LdaImmutableCurrentContextSlot), U8(3),
/* 51 E> */ B(LdaKeyedProperty), R(this), U8(2),
B(Wide), B(LdaSmi), I16(285),
B(Star2),
B(CallRuntime), U16(Runtime::kNewTypeError), R(1), U8(2),
B(LdaConstant), U8(0),
B(Star3),
B(CallRuntime), U16(Runtime::kNewTypeError), R(2), U8(2),
B(Throw),
]
constant pool: [
......@@ -133,19 +137,21 @@ snippet: "
var test = D;
new test;
"
frame size: 3
frame size: 4
parameter count: 1
bytecode array length: 23
bytecode array length: 28
bytecodes: [
B(LdaImmutableCurrentContextSlot), U8(3),
B(Star0),
B(Ldar), R(context),
/* 48 E> */ B(StaKeyedPropertyAsDefine), R(this), R(0), U8(0),
/* 53 S> */ B(Wide), B(LdaSmi), I16(285),
B(Star1),
B(LdaConstant), U8(0),
/* 53 S> */ B(LdaImmutableCurrentContextSlot), U8(3),
/* 58 E> */ B(LdaKeyedProperty), R(this), U8(2),
B(Wide), B(LdaSmi), I16(286),
B(Star2),
/* 61 E> */ B(CallRuntime), U16(Runtime::kNewTypeError), R(1), U8(2),
B(LdaConstant), U8(0),
B(Star3),
B(CallRuntime), U16(Runtime::kNewTypeError), R(2), U8(2),
B(Throw),
]
constant pool: [
......@@ -165,13 +171,15 @@ snippet: "
"
frame size: 4
parameter count: 1
bytecode array length: 23
bytecode array length: 28
bytecodes: [
B(LdaImmutableCurrentContextSlot), U8(3),
B(Star0),
B(Ldar), R(context),
/* 41 E> */ B(StaKeyedPropertyAsDefine), R(this), R(0), U8(0),
/* 46 S> */ B(Wide), B(LdaSmi), I16(284),
/* 46 S> */ B(LdaImmutableCurrentContextSlot), U8(3),
/* 51 E> */ B(LdaKeyedProperty), R(this), U8(2),
B(Wide), B(LdaSmi), I16(285),
B(Star2),
B(LdaConstant), U8(0),
B(Star3),
......
......@@ -46,19 +46,21 @@ snippet: "
var test = B;
new test;
"
frame size: 3
frame size: 4
parameter count: 1
bytecode array length: 23
bytecode array length: 28
bytecodes: [
B(LdaImmutableCurrentContextSlot), U8(3),
B(Star0),
B(Ldar), R(context),
/* 44 E> */ B(StaKeyedPropertyAsDefine), R(this), R(0), U8(0),
/* 49 S> */ B(Wide), B(LdaSmi), I16(283),
B(Star1),
B(LdaConstant), U8(0),
/* 49 S> */ B(LdaImmutableCurrentContextSlot), U8(3),
/* 54 E> */ B(LdaKeyedProperty), R(this), U8(2),
B(Wide), B(LdaSmi), I16(284),
B(Star2),
/* 57 E> */ B(CallRuntime), U16(Runtime::kNewTypeError), R(1), U8(2),
B(LdaConstant), U8(0),
B(Star3),
B(CallRuntime), U16(Runtime::kNewTypeError), R(2), U8(2),
B(Throw),
]
constant pool: [
......@@ -77,19 +79,21 @@ snippet: "
var test = C;
new test;
"
frame size: 3
frame size: 4
parameter count: 1
bytecode array length: 23
bytecode array length: 28
bytecodes: [
B(LdaImmutableCurrentContextSlot), U8(3),
B(Star0),
B(Ldar), R(context),
/* 44 E> */ B(StaKeyedPropertyAsDefine), R(this), R(0), U8(0),
/* 49 S> */ B(Wide), B(LdaSmi), I16(283),
B(Star1),
B(LdaConstant), U8(0),
/* 49 S> */ B(LdaImmutableCurrentContextSlot), U8(3),
/* 54 E> */ B(LdaKeyedProperty), R(this), U8(2),
B(Wide), B(LdaSmi), I16(284),
B(Star2),
B(CallRuntime), U16(Runtime::kNewTypeError), R(1), U8(2),
B(LdaConstant), U8(0),
B(Star3),
B(CallRuntime), U16(Runtime::kNewTypeError), R(2), U8(2),
B(Throw),
]
constant pool: [
......
......@@ -40,10 +40,10 @@ Access A.#staticMethod() in testStatic()
Access this.#staticMethod() in testStatic()
{
exceptionDetails : {
columnNumber : 0
columnNumber : 5
exception : {
className : Error
description : Error: Unused static private method '#staticMethod' cannot be accessed at debug time at eval (eval at testStatic (:1:1), <anonymous>:1:1) at Function.testStatic (<anonymous>:6:29) at run (<anonymous>:9:7) at <anonymous>:1:1
description : Error: Unused static private method '#staticMethod' cannot be accessed at debug time at eval (eval at testStatic (:1:1), <anonymous>:1:6) at Function.testStatic (<anonymous>:6:29) at run (<anonymous>:9:7) at <anonymous>:1:1
objectId : <objectId>
subtype : error
type : object
......@@ -55,7 +55,7 @@ Access this.#staticMethod() in testStatic()
}
result : {
className : Error
description : Error: Unused static private method '#staticMethod' cannot be accessed at debug time at eval (eval at testStatic (:1:1), <anonymous>:1:1) at Function.testStatic (<anonymous>:6:29) at run (<anonymous>:9:7) at <anonymous>:1:1
description : Error: Unused static private method '#staticMethod' cannot be accessed at debug time at eval (eval at testStatic (:1:1), <anonymous>:1:6) at Function.testStatic (<anonymous>:6:29) at run (<anonymous>:9:7) at <anonymous>:1:1
objectId : <objectId>
subtype : error
type : object
......
......@@ -3,7 +3,7 @@
# found in the LICENSE file.
*%(basename)s:8: TypeError: '#foo' was defined without a getter
this.#foo++;
^
^
TypeError: '#foo' was defined without a getter
at new C (*%(basename)s:8:5)
at new C (*%(basename)s:8:10)
at *%(basename)s:12:1
# 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.
*%(basename)s:7: TypeError: Object must be an instance of class C
*%(basename)s:7: TypeError: Receiver must be an instance of class C
setA(obj, val) { obj.#a = val; }
^
TypeError: Object must be an instance of class C
TypeError: Receiver must be an instance of class C
at C.setA (*%(basename)s:7:24)
at new C (*%(basename)s:13:10)
at *%(basename)s:17:1
......@@ -3,7 +3,7 @@
# found in the LICENSE file.
*%(basename)s:8: TypeError: '#a' was defined without a getter
const a = this.#a;
^
^
TypeError: '#a' was defined without a getter
at new C (*%(basename)s:8:15)
at new C (*%(basename)s:8:20)
at *%(basename)s:11:1
......@@ -3,7 +3,7 @@
# found in the LICENSE file.
*%(basename)s:8: TypeError: '#a' was defined without a setter
this.#a = 1;
^
^
TypeError: '#a' was defined without a setter
at new C (*%(basename)s:8:13)
at new C (*%(basename)s:8:10)
at *%(basename)s:11:1
......@@ -3,7 +3,7 @@
# found in the LICENSE file.
*%(basename)s:8: TypeError: '#foo' was defined without a setter
this.#foo++;
^
^
TypeError: '#foo' was defined without a setter
at new C (*%(basename)s:8:5)
at new C (*%(basename)s:8:10)
at *%(basename)s:12:1
# 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.
*%(basename)s:7: TypeError: Object must be an instance of class C
*%(basename)s:7: TypeError: Receiver must be an instance of class C
getA(obj) { return obj.#a; }
^
TypeError: Object must be an instance of class C
TypeError: Receiver must be an instance of class C
at C.getA (*%(basename)s:7:26)
at new C (*%(basename)s:13:10)
at *%(basename)s:17:1
......@@ -3,7 +3,7 @@
# found in the LICENSE file.
*%(basename)s:8: TypeError: '#a' was defined without a setter
this.#a = 1;
^
^
TypeError: '#a' was defined without a setter
at new C (*%(basename)s:8:13)
at new C (*%(basename)s:8:10)
at *%(basename)s:11:1
# Copyright 2020 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.
*%(basename)s:7: TypeError: Object must be an instance of class anonymous
*%(basename)s:7: TypeError: Receiver must be an instance of class anonymous
test(obj) { obj.#a(); }
^
TypeError: Object must be an instance of class anonymous
TypeError: Receiver must be an instance of class anonymous
at C.test (*%(basename)s:7:19)
at *%(basename)s:9:9
# Copyright 2020 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.
*%(basename)s:7: TypeError: Object must be an instance of class C
*%(basename)s:7: TypeError: Receiver must be an instance of class C
test(obj) { obj.#a(); }
^
TypeError: Object must be an instance of class C
TypeError: Receiver must be an instance of class C
at C.test (*%(basename)s:7:19)
at *%(basename)s:9:9
*%(basename)s:8: TypeError: Private method '#a' is not writable
this.#a = 1;
^
^
TypeError: Private method '#a' is not writable
at new C (*%(basename)s:8:13)
at new C (*%(basename)s:8:10)
at *%(basename)s:11:1
// 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.
class A {
get #a() {}
test() { this.#a += 1; }
}
A.prototype.test.call({});
*%(basename)s:7: TypeError: Receiver must be an instance of class A
test() { this.#a += 1; }
^
TypeError: Receiver must be an instance of class A
at Object.test (*%(basename)s:7:17)
at *%(basename)s:10:18
// 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.
class A {
#a() {}
test() { this.#a += 1; }
}
A.prototype.test.call({});
*%(basename)s:7: TypeError: Receiver must be an instance of class A
test() { this.#a += 1; }
^
TypeError: Receiver must be an instance of class A
at Object.test (*%(basename)s:7:17)
at *%(basename)s:10:18
// 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.
class A {
set #a(val) {}
test() { return this.#a; }
}
A.prototype.test.call({});
*%(basename)s:7: TypeError: Receiver must be an instance of class A
test() { return this.#a; }
^
TypeError: Receiver must be an instance of class A
at Object.test (*%(basename)s:7:24)
at *%(basename)s:10:18
// 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.
class A {
get #a() {}
test() { this.#a++; }
}
A.prototype.test.call({});
*%(basename)s:7: TypeError: Receiver must be an instance of class A
test() { this.#a++; }
^
TypeError: Receiver must be an instance of class A
at Object.test (*%(basename)s:7:17)
at *%(basename)s:10:18
// 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.
class A {
#a() {}
test() { this.#a++; }
}
A.prototype.test.call({});
*%(basename)s:7: TypeError: Receiver must be an instance of class A
test() { this.#a++; }
^
TypeError: Receiver must be an instance of class A
at Object.test (*%(basename)s:7:17)
at *%(basename)s:10:18
// 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.
class A {
set #a(val) {}
test() { return this.#a++; }
}
A.prototype.test.call({});
*%(basename)s:7: TypeError: Receiver must be an instance of class A
test() { return this.#a++; }
^
TypeError: Receiver must be an instance of class A
at Object.test (*%(basename)s:7:24)
at *%(basename)s:10:18
// 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.
class A {
get #a() {}
test() { this.#a = 1; }
}
A.prototype.test.call({});
*%(basename)s:7: TypeError: Receiver must be an instance of class A
test() { this.#a = 1; }
^
TypeError: Receiver must be an instance of class A
at Object.test (*%(basename)s:7:17)
at *%(basename)s:10:18
// 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.
class A {
#a() {}
test() { this.#a = 1; }
}
A.prototype.test.call({});
*%(basename)s:7: TypeError: Receiver must be an instance of class A
test() { this.#a = 1; }
^
TypeError: Receiver must be an instance of class A
at Object.test (*%(basename)s:7:17)
at *%(basename)s:10:18
// 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.
class A {
set #a(val) {}
test() { this.#a = 1; }
}
A.prototype.test.call({});
*%(basename)s:7: TypeError: Receiver must be an instance of class A
test() { this.#a = 1; }
^
TypeError: Receiver must be an instance of class A
at Object.test (*%(basename)s:7:17)
at *%(basename)s:10:18
// 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.
class A {
static get #a() {}
static test() { this.#a += 1; }
}
A.test.call({});
*%(basename)s:7: TypeError: Receiver must be class A
static test() { this.#a += 1; }
^
TypeError: Receiver must be class A
at Object.test (*%(basename)s:7:24)
at *%(basename)s:10:8
// 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.
class A {
static #a() {}
static test() { this.#a += 1; }
}
A.test.call({});
*%(basename)s:7: TypeError: Receiver must be class A
static test() { this.#a += 1; }
^
TypeError: Receiver must be class A
at Object.test (*%(basename)s:7:24)
at *%(basename)s:10:8
// 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.
class A {
static set #a(val) {}
static test() { return this.#a; }
}
A.test.call({});
*%(basename)s:7: TypeError: Receiver must be class A
static test() { return this.#a; }
^
TypeError: Receiver must be class A
at Object.test (*%(basename)s:7:31)
at *%(basename)s:10:8
// 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.
class A {
static get #a() {}
static test() { this.#a++; }
}
A.test.call({});
*%(basename)s:7: TypeError: Receiver must be class A
static test() { this.#a++; }
^
TypeError: Receiver must be class A
at Object.test (*%(basename)s:7:24)
at *%(basename)s:10:8
// 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.
class A {
static #a() {}
static test() { this.#a++; }
}
A.test.call({});
*%(basename)s:7: TypeError: Receiver must be class A
static test() { this.#a++; }
^
TypeError: Receiver must be class A
at Object.test (*%(basename)s:7:24)
at *%(basename)s:10:8
// 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.
class A {
static set #a(val) {}
static test() { return this.#a++; }
}
A.test.call({});
*%(basename)s:7: TypeError: Receiver must be class A
static test() { return this.#a++; }
^
TypeError: Receiver must be class A
at Object.test (*%(basename)s:7:31)
at *%(basename)s:10:8
// 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.
class A {
static get #a() {}
static test() { this.#a = 1; }
}
A.test.call({});
*%(basename)s:7: TypeError: Receiver must be class A
static test() { this.#a = 1; }
^
TypeError: Receiver must be class A
at Object.test (*%(basename)s:7:24)
at *%(basename)s:10:8
// 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.
class A {
static #a() {}
static test() { this.#a = 1; }
}
A.test.call({});
*%(basename)s:7: TypeError: Receiver must be class A
static test() { this.#a = 1; }
^
TypeError: Receiver must be class A
at Object.test (*%(basename)s:7:24)
at *%(basename)s:10:8
// 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.
class A {
static set #a(val) {}
static test() { this.#a = 1; }
}
A.test.call({});
*%(basename)s:7: TypeError: Receiver must be class A
static test() { this.#a = 1; }
^
TypeError: Receiver must be class A
at Object.test (*%(basename)s:7:24)
at *%(basename)s:10:8
......@@ -111,3 +111,108 @@
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);
}
......@@ -148,7 +148,7 @@
}
assertThrows(() => { C.prototype.m.call({}); }, TypeError,
/Object must be an instance of class/);
/Receiver must be an instance of class/);
// It's the same error we get from this case:
class C2 {
......@@ -159,7 +159,7 @@
}
assertThrows(() => { C2.prototype.m.call({}); }, TypeError,
/Object must be an instance of class/);
/Receiver must be an instance of class/);
})();
(function TestBrandCheck_CompoundAssignmentToPrivateFieldWithOnlySetter() {
......@@ -171,7 +171,7 @@
}
assertThrows(() => { C.prototype.m.call({}); }, TypeError,
/Object must be an instance of class/);
/Receiver must be an instance of class/);
})();
(function TestBrandCheck_CompoundAssignmentToPrivateFieldWithGetterAndSetter() {
......@@ -184,7 +184,7 @@
}
assertThrows(() => { C.prototype.m.call({}); }, TypeError,
/Object must be an instance of class/);
/Receiver must be an instance of class/);
// It's the same error we get from this case:
class C2 {
......@@ -196,7 +196,7 @@
}
assertThrows(() => { C2.prototype.m.call({}); }, TypeError,
/Object must be an instance of class/);
/Receiver must be an instance of class/);
})();
(function TestBrandCheck_CompoundAssignmentToPrivateMethod() {
......@@ -208,5 +208,5 @@
}
assertThrows(() => { C.prototype.m.call({}); }, TypeError,
/Object must be an instance of class/);
/Receiver must be an instance of class/);
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment