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