Commit df12eb19 authored by Joyee Cheung's avatar Joyee Cheung Committed by Commit Bot

[class] implement private accessors

This patch implements the access of private accessors by loading the
referenced component from the AccessorPair associated with private
name variables. It also makes the error messages for invalid kind
of private accessor access more specific.

Bug: v8:8330
Design doc: https://docs.google.com/document/d/10W4begYfs7lmldSqBoQBBt_BKamgT8igqxF9u50RGrI/edit

Change-Id: I6d441cffb85f8d9cd0417ec9b6ae20f3e34ef418
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1695205Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Cr-Commit-Position: refs/heads/master@{#63474}
parent bdcc7502
......@@ -413,11 +413,15 @@ namespace internal {
T(InvalidOrUnexpectedToken, "Invalid or unexpected token") \
T(InvalidPrivateFieldResolution, \
"Private field '%' must be declared in an enclosing class") \
T(InvalidPrivateFieldRead, \
"Read of private field % from an object which did not contain the field") \
T(InvalidPrivateFieldWrite, \
"Write of private field % to an object which did not contain the field") \
T(InvalidPrivateMemberRead, \
"Cannot read private member % from an object whose class did not declare " \
"it") \
T(InvalidPrivateMemberWrite, \
"Cannot write private member % to an object whose class did not declare " \
"it") \
T(InvalidPrivateMethodWrite, "Private method '%' is not writable") \
T(InvalidPrivateGetterAccess, "'%' was defined without a getter") \
T(InvalidPrivateSetterAccess, "'%' was defined without a setter") \
T(JsonParseUnexpectedEOS, "Unexpected end of JSON input") \
T(JsonParseUnexpectedToken, "Unexpected token % in JSON at position %") \
T(JsonParseUnexpectedTokenNumber, "Unexpected number in JSON at position %") \
......
......@@ -412,7 +412,7 @@ MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<Name> name) {
if (name->IsPrivateName() && !it.IsFound()) {
Handle<String> name_string(String::cast(Symbol::cast(*name).name()),
isolate());
return TypeError(MessageTemplate::kInvalidPrivateFieldRead, object,
return TypeError(MessageTemplate::kInvalidPrivateMemberRead, object,
name_string);
}
......@@ -1411,7 +1411,7 @@ MaybeHandle<Object> StoreIC::Store(Handle<Object> object, Handle<Name> name,
if (name->IsPrivateName() && !it.IsFound()) {
Handle<String> name_string(String::cast(Symbol::cast(*name).name()),
isolate());
return TypeError(MessageTemplate::kInvalidPrivateFieldWrite, object,
return TypeError(MessageTemplate::kInvalidPrivateMemberWrite, object,
name_string);
}
......
......@@ -2329,12 +2329,13 @@ void BytecodeGenerator::VisitInitializeClassMembersStatement(
}
}
void BytecodeGenerator::BuildThrowPrivateMethodWriteError(
const AstRawString* name) {
void BytecodeGenerator::BuildInvalidPropertyAccess(MessageTemplate tmpl,
Property* property) {
RegisterAllocationScope register_scope(this);
const AstRawString* name = property->key()->AsVariableProxy()->raw_name();
RegisterList args = register_allocator()->NewRegisterList(2);
builder()
->LoadLiteral(Smi::FromEnum(MessageTemplate::kInvalidPrivateMethodWrite))
->LoadLiteral(Smi::FromEnum(tmpl))
.StoreAccumulatorInRegister(args[0])
.LoadLiteral(name)
.StoreAccumulatorInRegister(args[1])
......@@ -3887,15 +3888,28 @@ void BytecodeGenerator::BuildAssignment(
break;
}
case PRIVATE_METHOD: {
BuildThrowPrivateMethodWriteError(
lhs_data.expr()->AsProperty()->key()->AsVariableProxy()->raw_name());
BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateMethodWrite,
lhs_data.expr()->AsProperty());
break;
}
case PRIVATE_GETTER_ONLY: {
BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateSetterAccess,
lhs_data.expr()->AsProperty());
break;
}
case PRIVATE_GETTER_ONLY:
case PRIVATE_SETTER_ONLY:
case PRIVATE_GETTER_AND_SETTER: {
// TODO(joyee): implement private accessors.
return;
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);
BuildPrivateSetterAccess(object, key, value);
if (!execution_result()->IsEffect()) {
builder()->LoadAccumulatorWithRegister(value);
}
break;
}
}
}
......@@ -3942,16 +3956,13 @@ void BytecodeGenerator::VisitCompoundAssignment(CompoundAssignment* expr) {
lhs_data.super_property_args().Truncate(3));
break;
}
case PRIVATE_METHOD: {
BuildThrowPrivateMethodWriteError(
lhs_data.expr()->AsProperty()->key()->AsVariableProxy()->raw_name());
break;
}
case PRIVATE_SETTER_ONLY:
case PRIVATE_METHOD:
case PRIVATE_GETTER_ONLY:
case PRIVATE_SETTER_ONLY:
case PRIVATE_GETTER_AND_SETTER: {
// TODO(joyee): implement private accessors.
return;
// ({ #foo: name } = obj) is currently syntactically invalid.
UNREACHABLE();
break;
}
}
......@@ -4418,32 +4429,70 @@ void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* property) {
case KEYED_SUPER_PROPERTY:
VisitKeyedSuperPropertyLoad(property, Register::invalid_value());
break;
case PRIVATE_SETTER_ONLY: {
BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateGetterAccess,
property);
break;
}
case PRIVATE_GETTER_ONLY:
case PRIVATE_GETTER_AND_SETTER: {
Register key = VisitForRegisterValue(property->key());
BuildPrivateBrandCheck(property, obj);
BuildPrivateGetterAccess(obj, key);
break;
}
case PRIVATE_METHOD: {
Variable* private_name = property->key()->AsVariableProxy()->var();
// Perform the brand check.
DCHECK(private_name->requires_brand_check());
ClassScope* scope = private_name->scope()->AsClassScope();
Variable* brand = scope->brand();
BuildVariableLoadForAccumulatorValue(brand, HoleCheckMode::kElided);
builder()->SetExpressionPosition(property);
builder()->LoadKeyedProperty(
obj, feedback_index(feedback_spec()->AddKeyedLoadICSlot()));
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());
break;
}
case PRIVATE_SETTER_ONLY:
case PRIVATE_GETTER_ONLY:
case PRIVATE_GETTER_AND_SETTER: {
// TODO(joyee): implement private accessors.
return;
}
}
}
void BytecodeGenerator::BuildPrivateGetterAccess(Register object,
Register accessor_pair) {
RegisterAllocationScope scope(this);
Register accessor = register_allocator()->NewRegister();
RegisterList args = register_allocator()->NewRegisterList(1);
builder()
->CallRuntime(Runtime::kLoadPrivateGetter, accessor_pair)
.StoreAccumulatorInRegister(accessor)
.MoveRegister(object, args[0])
.CallProperty(accessor, args,
feedback_index(feedback_spec()->AddCallICSlot()));
}
void BytecodeGenerator::BuildPrivateSetterAccess(Register object,
Register accessor_pair,
Register value) {
RegisterAllocationScope scope(this);
Register accessor = register_allocator()->NewRegister();
RegisterList args = register_allocator()->NewRegisterList(2);
builder()
->CallRuntime(Runtime::kLoadPrivateSetter, accessor_pair)
.StoreAccumulatorInRegister(accessor)
.MoveRegister(object, args[0])
.MoveRegister(value, args[1])
.CallProperty(accessor, args,
feedback_index(feedback_spec()->AddCallICSlot()));
}
void BytecodeGenerator::BuildPrivateBrandCheck(Property* property,
Register object) {
Variable* private_name = property->key()->AsVariableProxy()->var();
DCHECK(private_name->requires_brand_check());
ClassScope* scope = private_name->scope()->AsClassScope();
Variable* brand = scope->brand();
BuildVariableLoadForAccumulatorValue(brand, HoleCheckMode::kElided);
builder()->SetExpressionPosition(property);
builder()->LoadKeyedProperty(
object, feedback_index(feedback_spec()->AddKeyedLoadICSlot()));
}
void BytecodeGenerator::VisitPropertyLoadForRegister(Register obj,
Property* expr,
Register destination) {
......@@ -5030,16 +5079,27 @@ void BytecodeGenerator::VisitCountOperation(CountOperation* expr) {
break;
}
case PRIVATE_METHOD: {
BuildThrowPrivateMethodWriteError(
property->key()->AsVariableProxy()->raw_name());
break;
BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateMethodWrite,
property);
return;
}
case PRIVATE_GETTER_ONLY:
case PRIVATE_SETTER_ONLY:
case PRIVATE_GETTER_AND_SETTER: {
// TODO(joyee): implement private accessors.
case PRIVATE_GETTER_ONLY: {
BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateSetterAccess,
property);
return;
}
case PRIVATE_SETTER_ONLY: {
BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateGetterAccess,
property);
return;
}
case PRIVATE_GETTER_AND_SETTER: {
object = VisitForRegisterValue(property->obj());
key = VisitForRegisterValue(property->key());
BuildPrivateBrandCheck(property, object);
BuildPrivateGetterAccess(object, key);
break;
}
}
// Save result for postfix expressions.
......@@ -5106,16 +5166,19 @@ void BytecodeGenerator::VisitCountOperation(CountOperation* expr) {
.CallRuntime(Runtime::kStoreKeyedToSuper, super_property_args);
break;
}
case PRIVATE_SETTER_ONLY:
case PRIVATE_GETTER_ONLY:
case PRIVATE_METHOD: {
BuildThrowPrivateMethodWriteError(
property->key()->AsVariableProxy()->raw_name());
break;
UNREACHABLE();
}
case PRIVATE_GETTER_ONLY:
case PRIVATE_SETTER_ONLY:
case PRIVATE_GETTER_AND_SETTER: {
// TODO(joyee): implement private accessors.
UNREACHABLE();
Register value = register_allocator()->NewRegister();
builder()->StoreAccumulatorInRegister(value);
BuildPrivateSetterAccess(object, key, value);
if (!execution_result()->IsEffect()) {
builder()->LoadAccumulatorWithRegister(value);
}
break;
}
}
......
......@@ -302,7 +302,11 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
void VisitArgumentsObject(Variable* variable);
void VisitRestArgumentsArray(Variable* rest);
void VisitCallSuper(Call* call);
void BuildThrowPrivateMethodWriteError(const AstRawString* name);
void BuildInvalidPropertyAccess(MessageTemplate tmpl, Property* property);
void BuildPrivateBrandCheck(Property* property, Register object);
void BuildPrivateGetterAccess(Register obj, Register access_pair);
void BuildPrivateSetterAccess(Register obj, Register access_pair,
Register value);
void BuildClassLiteral(ClassLiteral* expr, Register name);
void VisitClassLiteral(ClassLiteral* expr, Register name);
void VisitNewTargetVariable(Variable* variable);
......
......@@ -46,7 +46,7 @@ MaybeHandle<Object> Runtime::GetObjectProperty(Isolate* isolate,
Handle<Object> name_string(Symbol::cast(*key).name(), isolate);
DCHECK(name_string->IsString());
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kInvalidPrivateFieldRead,
NewTypeError(MessageTemplate::kInvalidPrivateMemberRead,
name_string, object),
Object);
}
......@@ -413,7 +413,7 @@ MaybeHandle<Object> Runtime::SetObjectProperty(
Handle<Object> name_string(Symbol::cast(*key).name(), isolate);
DCHECK(name_string->IsString());
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kInvalidPrivateFieldWrite,
NewTypeError(MessageTemplate::kInvalidPrivateMemberWrite,
name_string, object),
Object);
}
......
#
# Autogenerated by generate-bytecode-expectations.
#
---
wrap: no
test function name: test
private methods: yes
---
snippet: "
class A {
get #a() { return 1; }
set #a(val) { }
constructor() {
this.#a++;
this.#a = 1;
return this.#a;
}
}
var test = A;
new test;
"
frame size: 6
parameter count: 1
bytecode array length: 95
bytecodes: [
/* 67 E> */ B(StackCheck),
B(LdaCurrentContextSlot), U8(5),
B(Star), R(1),
B(Mov), R(this), R(0),
B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(2),
/* 76 S> */ B(LdaCurrentContextSlot), U8(4),
B(Star), R(3),
B(LdaCurrentContextSlot), U8(5),
/* 81 E> */ B(LdaKeyedProperty), R(this), U8(0),
B(CallRuntime), U16(Runtime::kLoadPrivateGetter), R(3), U8(1),
B(Star), R(4),
B(CallProperty0), R(4), R(this), U8(2),
B(Inc), U8(4),
B(Star), R(4),
/* 83 E> */ B(CallRuntime), U16(Runtime::kLoadPrivateSetter), R(3), U8(1),
B(Star), R(5),
B(CallProperty1), R(5), R(this), R(4), U8(5),
/* 91 S> */ B(LdaSmi), I8(1),
B(Star), R(2),
B(LdaCurrentContextSlot), U8(4),
B(Star), R(4),
B(LdaCurrentContextSlot), U8(5),
/* 96 E> */ B(LdaKeyedProperty), R(this), U8(7),
B(CallRuntime), U16(Runtime::kLoadPrivateSetter), R(4), U8(1),
B(Star), R(5),
B(CallProperty1), R(5), R(this), R(2), U8(9),
/* 108 S> */ B(LdaCurrentContextSlot), U8(4),
B(Star), R(3),
B(LdaCurrentContextSlot), U8(5),
/* 120 E> */ B(LdaKeyedProperty), R(this), U8(11),
B(CallRuntime), U16(Runtime::kLoadPrivateGetter), R(3), U8(1),
B(Star), R(4),
B(CallProperty0), R(4), R(this), U8(13),
/* 123 S> */ B(Return),
]
constant pool: [
]
handlers: [
]
---
snippet: "
class B {
get #b() { return 1; }
constructor() { this.#b++; }
}
var test = B;
new test;
"
frame size: 4
parameter count: 1
bytecode array length: 29
bytecodes: [
/* 48 E> */ B(StackCheck),
B(LdaCurrentContextSlot), U8(5),
B(Star), R(1),
B(Mov), R(this), R(0),
B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(2),
/* 53 S> */ B(Wide), B(LdaSmi), I16(263),
B(Star), R(2),
B(LdaConstant), U8(0),
B(Star), R(3),
B(CallRuntime), U16(Runtime::kNewTypeError), R(2), U8(2),
B(Throw),
]
constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["#b"],
]
handlers: [
]
---
snippet: "
class C {
set #c(val) { }
constructor() { this.#c++; }
}
var test = C;
new test;
"
frame size: 4
parameter count: 1
bytecode array length: 29
bytecodes: [
/* 41 E> */ B(StackCheck),
B(LdaCurrentContextSlot), U8(5),
B(Star), R(1),
B(Mov), R(this), R(0),
B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(2),
/* 46 S> */ B(Wide), B(LdaSmi), I16(262),
B(Star), R(2),
B(LdaConstant), U8(0),
B(Star), R(3),
B(CallRuntime), U16(Runtime::kNewTypeError), R(2), U8(2),
B(Throw),
]
constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["#c"],
]
handlers: [
]
---
snippet: "
class D {
get #d() { return 1; }
constructor() { this.#d = 1; }
}
var test = D;
new test;
"
frame size: 4
parameter count: 1
bytecode array length: 29
bytecodes: [
/* 48 E> */ B(StackCheck),
B(LdaCurrentContextSlot), U8(5),
B(Star), R(1),
B(Mov), R(this), R(0),
B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(2),
/* 53 S> */ B(Wide), B(LdaSmi), I16(263),
B(Star), R(2),
B(LdaConstant), U8(0),
B(Star), R(3),
/* 61 E> */ B(CallRuntime), U16(Runtime::kNewTypeError), R(2), U8(2),
B(Throw),
]
constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["#d"],
]
handlers: [
]
---
snippet: "
class E {
set #e(val) { }
constructor() { this.#e; }
}
var test = E;
new test;
"
frame size: 5
parameter count: 1
bytecode array length: 29
bytecodes: [
/* 41 E> */ B(StackCheck),
B(LdaCurrentContextSlot), U8(5),
B(Star), R(1),
B(Mov), R(this), R(0),
B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(2),
/* 46 S> */ B(Wide), B(LdaSmi), I16(262),
B(Star), R(3),
B(LdaConstant), U8(0),
B(Star), R(4),
B(CallRuntime), U16(Runtime::kNewTypeError), R(3), U8(2),
B(Throw),
]
constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["#e"],
]
handlers: [
]
#
# Autogenerated by generate-bytecode-expectations.
#
---
wrap: no
test function name: test
private methods: yes
---
snippet: "
class A {
#a() { return 1; }
constructor() { return this.#a(); }
}
var test = A;
new A;
"
frame size: 3
parameter count: 1
bytecode array length: 28
bytecodes: [
/* 44 E> */ B(StackCheck),
B(LdaCurrentContextSlot), U8(5),
B(Star), R(1),
B(Mov), R(this), R(0),
B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(2),
/* 49 S> */ B(LdaCurrentContextSlot), U8(5),
/* 61 E> */ B(LdaKeyedProperty), R(this), U8(0),
B(LdaCurrentContextSlot), U8(4),
B(Star), R(2),
/* 63 E> */ B(CallAnyReceiver), R(2), R(this), U8(1), U8(2),
/* 66 S> */ B(Return),
]
constant pool: [
]
handlers: [
]
---
snippet: "
class B {
#b() { return 1; }
constructor() { this.#b = 1; }
}
var test = B;
new test;
"
frame size: 4
parameter count: 1
bytecode array length: 29
bytecodes: [
/* 44 E> */ B(StackCheck),
B(LdaCurrentContextSlot), U8(5),
B(Star), R(1),
B(Mov), R(this), R(0),
B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(2),
/* 49 S> */ B(Wide), B(LdaSmi), I16(261),
B(Star), R(2),
B(LdaConstant), U8(0),
B(Star), R(3),
/* 57 E> */ B(CallRuntime), U16(Runtime::kNewTypeError), R(2), U8(2),
B(Throw),
]
constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["#b"],
]
handlers: [
]
---
snippet: "
class C {
#c() { return 1; }
constructor() { this.#c++; }
}
var test = C;
new test;
"
frame size: 4
parameter count: 1
bytecode array length: 29
bytecodes: [
/* 44 E> */ B(StackCheck),
B(LdaCurrentContextSlot), U8(5),
B(Star), R(1),
B(Mov), R(this), R(0),
B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(2),
/* 49 S> */ B(Wide), B(LdaSmi), I16(261),
B(Star), R(2),
B(LdaConstant), U8(0),
B(Star), R(3),
B(CallRuntime), U16(Runtime::kNewTypeError), R(2), U8(2),
B(Throw),
]
constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["#c"],
]
handlers: [
]
......@@ -11,57 +11,43 @@ snippet: "
{
class A {
#a() { return 1; }
callA() { return this.#a(); }
}
const a = new A;
a.callA();
}
"
frame size: 9
frame size: 7
parameter count: 1
bytecode array length: 80
bytecode array length: 55
bytecodes: [
/* 30 E> */ B(StackCheck),
B(CreateBlockContext), U8(0),
B(PushContext), R(3),
B(PushContext), R(2),
B(LdaTheHole),
B(Star), R(7),
B(Star), R(6),
B(CreateClosure), U8(2), U8(0), U8(2),
B(Star), R(4),
B(Star), R(3),
B(LdaConstant), U8(1),
B(Star), R(5),
B(Star), R(4),
B(CreateClosure), U8(3), U8(1), U8(2),
B(StaCurrentContextSlot), U8(4),
B(CreateClosure), U8(4), U8(2), U8(2),
B(Star), R(8),
B(Mov), R(4), R(6),
B(CallRuntime), U16(Runtime::kDefineClass), R(5), U8(4),
B(Mov), R(3), R(5),
B(CallRuntime), U16(Runtime::kDefineClass), R(4), U8(3),
B(Star), R(4),
B(Mov), R(5), R(1),
B(LdaConstant), U8(4),
B(Star), R(5),
B(Mov), R(6), R(2),
B(LdaConstant), U8(5),
B(Star), R(6),
B(CallRuntime), U16(Runtime::kCreatePrivateNameSymbol), R(6), U8(1),
B(CallRuntime), U16(Runtime::kCreatePrivateNameSymbol), R(5), U8(1),
B(StaCurrentContextSlot), U8(5),
B(PopContext), R(3),
B(Mov), R(2), R(0),
/* 122 S> */ B(Ldar), R(0),
/* 122 E> */ B(Construct), R(0), R(0), U8(0), U8(0),
B(Star), R(1),
/* 133 S> */ B(LdaNamedProperty), R(1), U8(6), U8(2),
B(Star), R(3),
/* 133 E> */ B(CallProperty0), R(3), R(1), U8(4),
B(PopContext), R(2),
B(Mov), R(1), R(0),
B(LdaUndefined),
/* 144 S> */ B(Return),
/* 77 S> */ B(Return),
]
constant pool: [
SCOPE_INFO_TYPE,
FIXED_ARRAY_TYPE,
SHARED_FUNCTION_INFO_TYPE,
SHARED_FUNCTION_INFO_TYPE,
SHARED_FUNCTION_INFO_TYPE,
ONE_BYTE_INTERNALIZED_STRING_TYPE ["A"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["callA"],
]
handlers: [
]
......@@ -71,89 +57,70 @@ snippet: "
{
class D {
#d() { return 1; }
callD() { return this.#d(); }
}
class E extends D {
#e() { return 2; }
callE() { return this.callD() + this.#e(); }
}
const e = new E;
e.callE();
}
"
frame size: 11
frame size: 9
parameter count: 1
bytecode array length: 138
bytecode array length: 107
bytecodes: [
/* 30 E> */ B(StackCheck),
B(CreateBlockContext), U8(0),
B(PushContext), R(5),
B(PushContext), R(4),
B(LdaTheHole),
B(Star), R(9),
B(Star), R(8),
B(CreateClosure), U8(2), U8(0), U8(2),
B(Star), R(6),
B(Star), R(5),
B(LdaConstant), U8(1),
B(Star), R(7),
B(Star), R(6),
B(CreateClosure), U8(3), U8(1), U8(2),
B(StaCurrentContextSlot), U8(4),
B(CreateClosure), U8(4), U8(2), U8(2),
B(Star), R(10),
B(Mov), R(6), R(8),
B(CallRuntime), U16(Runtime::kDefineClass), R(7), U8(4),
B(Mov), R(5), R(7),
B(CallRuntime), U16(Runtime::kDefineClass), R(6), U8(3),
B(Star), R(6),
B(Mov), R(7), R(3),
B(LdaConstant), U8(4),
B(Star), R(7),
B(Mov), R(8), R(4),
B(LdaConstant), U8(5),
B(Star), R(8),
B(CallRuntime), U16(Runtime::kCreatePrivateNameSymbol), R(8), U8(1),
B(CallRuntime), U16(Runtime::kCreatePrivateNameSymbol), R(7), U8(1),
B(StaCurrentContextSlot), U8(5),
B(PopContext), R(5),
B(Mov), R(4), R(0),
/* 38 E> */ B(CreateBlockContext), U8(6),
B(PushContext), R(5),
/* 128 E> */ B(CreateClosure), U8(8), U8(3), U8(2),
B(PopContext), R(4),
B(Mov), R(3), R(0),
/* 38 E> */ B(CreateBlockContext), U8(5),
B(PushContext), R(4),
/* 93 E> */ B(CreateClosure), U8(7), U8(2), U8(2),
B(Star), R(5),
B(LdaConstant), U8(6),
B(Star), R(6),
B(LdaConstant), U8(7),
B(Star), R(7),
B(CreateClosure), U8(9), U8(4), U8(2),
B(CreateClosure), U8(8), U8(3), U8(2),
B(StaCurrentContextSlot), U8(4),
B(CreateClosure), U8(10), U8(5), U8(2),
B(Star), R(10),
B(Mov), R(6), R(8),
B(Mov), R(4), R(9),
B(CallRuntime), U16(Runtime::kDefineClass), R(7), U8(4),
B(Mov), R(5), R(7),
B(Mov), R(3), R(8),
B(CallRuntime), U16(Runtime::kDefineClass), R(6), U8(3),
B(Star), R(6),
B(Mov), R(7), R(2),
B(LdaConstant), U8(9),
B(Star), R(7),
B(Mov), R(8), R(3),
B(LdaConstant), U8(11),
B(Star), R(8),
B(CallRuntime), U16(Runtime::kCreatePrivateNameSymbol), R(8), U8(1),
B(CallRuntime), U16(Runtime::kCreatePrivateNameSymbol), R(7), U8(1),
B(StaCurrentContextSlot), U8(5),
B(PopContext), R(5),
B(Mov), R(3), R(1),
/* 221 S> */ B(Ldar), R(1),
/* 221 E> */ B(Construct), R(1), R(0), U8(0), U8(0),
B(Star), R(2),
/* 232 S> */ B(LdaNamedProperty), R(2), U8(12), U8(2),
B(Star), R(5),
/* 232 E> */ B(CallProperty0), R(5), R(2), U8(4),
B(PopContext), R(4),
B(Mov), R(2), R(1),
B(LdaUndefined),
/* 243 S> */ B(Return),
/* 126 S> */ B(Return),
]
constant pool: [
SCOPE_INFO_TYPE,
FIXED_ARRAY_TYPE,
SHARED_FUNCTION_INFO_TYPE,
SHARED_FUNCTION_INFO_TYPE,
SHARED_FUNCTION_INFO_TYPE,
ONE_BYTE_INTERNALIZED_STRING_TYPE ["D"],
SCOPE_INFO_TYPE,
FIXED_ARRAY_TYPE,
SHARED_FUNCTION_INFO_TYPE,
SHARED_FUNCTION_INFO_TYPE,
SHARED_FUNCTION_INFO_TYPE,
ONE_BYTE_INTERNALIZED_STRING_TYPE ["E"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["callE"],
]
handlers: [
]
......@@ -164,14 +131,12 @@ snippet: "
class A { foo() {} }
class C extends A {
#m() { return super.foo; }
fn() { return this.#m(); }
}
new C().fn();
}
"
frame size: 10
parameter count: 1
bytecode array length: 131
bytecode array length: 106
bytecodes: [
/* 30 E> */ B(StackCheck),
B(CreateBlockContext), U8(0),
......@@ -198,31 +163,23 @@ bytecodes: [
B(Star), R(6),
B(CreateClosure), U8(7), U8(3), U8(2),
B(StaCurrentContextSlot), U8(4),
B(CreateClosure), U8(8), U8(4), U8(2),
B(Star), R(9),
B(Mov), R(5), R(7),
B(Mov), R(3), R(8),
B(CallRuntime), U16(Runtime::kDefineClass), R(6), U8(4),
B(CallRuntime), U16(Runtime::kDefineClass), R(6), U8(3),
B(Star), R(6),
B(Mov), R(7), R(2),
B(LdaConstant), U8(9),
B(LdaConstant), U8(8),
B(Star), R(7),
B(CallRuntime), U16(Runtime::kCreatePrivateNameSymbol), R(7), U8(1),
B(StaCurrentContextSlot), U8(5),
B(LdaCurrentContextSlot), U8(4),
B(Star), R(8),
B(Ldar), R(6),
B(StaNamedProperty), R(8), U8(10), U8(0),
B(StaNamedProperty), R(8), U8(9), U8(0),
B(PopContext), R(4),
B(Mov), R(2), R(1),
/* 149 S> */ B(Ldar), R(1),
/* 149 E> */ B(Construct), R(5), R(0), U8(0), U8(2),
B(Star), R(5),
/* 157 E> */ B(LdaNamedProperty), R(5), U8(11), U8(4),
B(Star), R(4),
/* 157 E> */ B(CallProperty0), R(4), R(5), U8(6),
B(LdaUndefined),
/* 165 S> */ B(Return),
/* 118 S> */ B(Return),
]
constant pool: [
SCOPE_INFO_TYPE,
......@@ -233,10 +190,8 @@ constant pool: [
FIXED_ARRAY_TYPE,
SHARED_FUNCTION_INFO_TYPE,
SHARED_FUNCTION_INFO_TYPE,
SHARED_FUNCTION_INFO_TYPE,
ONE_BYTE_INTERNALIZED_STRING_TYPE ["C"],
SYMBOL_TYPE,
ONE_BYTE_INTERNALIZED_STRING_TYPE ["fn"],
]
handlers: [
]
......
......@@ -2758,7 +2758,7 @@ TEST(PrivateClassFields) {
LoadGolden("PrivateClassFields.golden")));
}
TEST(PrivateMethods) {
TEST(PrivateMethodDeclaration) {
bool old_methods_flag = i::FLAG_harmony_private_methods;
i::FLAG_harmony_private_methods = true;
InitializedIgnitionHandleScope scope;
......@@ -2768,43 +2768,124 @@ TEST(PrivateMethods) {
"{\n"
" class A {\n"
" #a() { return 1; }\n"
" callA() { return this.#a(); }\n"
" }\n"
"\n"
" const a = new A;\n"
" a.callA();\n"
"}\n",
"{\n"
" class D {\n"
" #d() { return 1; }\n"
" callD() { return this.#d(); }\n"
" }\n"
"\n"
" class E extends D {\n"
" #e() { return 2; }\n"
" callE() { return this.callD() + this.#e(); }\n"
" }\n"
"\n"
" const e = new E;\n"
" e.callE();\n"
"}\n",
"{\n"
" class A { foo() {} }\n"
" class C extends A {\n"
" #m() { return super.foo; }\n"
" fn() { return this.#m(); }\n"
" }\n"
" new C().fn();\n"
"}\n"};
CHECK(CompareTexts(BuildActual(printer, snippets),
LoadGolden("PrivateMethods.golden")));
LoadGolden("PrivateMethodDeclaration.golden")));
i::FLAG_harmony_private_methods = old_methods_flag;
}
TEST(PrivateMethodAccess) {
bool old_methods_flag = i::FLAG_harmony_private_methods;
i::FLAG_harmony_private_methods = true;
InitializedIgnitionHandleScope scope;
BytecodeExpectationsPrinter printer(CcTest::isolate());
printer.set_wrap(false);
printer.set_test_function_name("test");
const char* snippets[] = {
"class A {\n"
" #a() { return 1; }\n"
" constructor() { return this.#a(); }\n"
"}\n"
"\n"
"var test = A;\n"
"new A;\n",
"class B {\n"
" #b() { return 1; }\n"
" constructor() { this.#b = 1; }\n"
"}\n"
"\n"
"var test = B;\n"
"new test;\n",
"class C {\n"
" #c() { return 1; }\n"
" constructor() { this.#c++; }\n"
"}\n"
"\n"
"var test = C;\n"
"new test;\n"};
CHECK(CompareTexts(BuildActual(printer, snippets),
LoadGolden("PrivateMethodAccess.golden")));
i::FLAG_harmony_private_methods = old_methods_flag;
}
TEST(PrivateAccessorAccess) {
bool old_methods_flag = i::FLAG_harmony_private_methods;
i::FLAG_harmony_private_methods = true;
InitializedIgnitionHandleScope scope;
BytecodeExpectationsPrinter printer(CcTest::isolate());
printer.set_wrap(false);
printer.set_test_function_name("test");
const char* snippets[] = {
"class A {\n"
" get #a() { return 1; }\n"
" set #a(val) { }\n"
"\n"
" constructor() {\n"
" this.#a++;\n"
" this.#a = 1;\n"
" return this.#a;\n"
" }\n"
"}\n"
"var test = A;\n"
"new test;\n",
"class B {\n"
" get #b() { return 1; }\n"
" constructor() { this.#b++; }\n"
"}\n"
"var test = B;\n"
"new test;\n",
"class C {\n"
" set #c(val) { }\n"
" constructor() { this.#c++; }\n"
"}\n"
"var test = C;\n"
"new test;\n",
"class D {\n"
" get #d() { return 1; }\n"
" constructor() { this.#d = 1; }\n"
"}\n"
"var test = D;\n"
"new test;\n",
"class E {\n"
" set #e(val) { }\n"
" constructor() { this.#e; }\n"
"}\n"
"var test = E;\n"
"new test;\n"};
CHECK(CompareTexts(BuildActual(printer, snippets),
LoadGolden("PrivateAccessorAccess.golden")));
i::FLAG_harmony_private_methods = old_methods_flag;
}
TEST(PrivateAccessors) {
TEST(PrivateAccessorDeclaration) {
bool old_methods_flag = i::FLAG_harmony_private_methods;
i::FLAG_harmony_private_methods = true;
InitializedIgnitionHandleScope scope;
......@@ -2859,7 +2940,7 @@ TEST(PrivateAccessors) {
"}\n"};
CHECK(CompareTexts(BuildActual(printer, snippets),
LoadGolden("PrivateAccessors.golden")));
LoadGolden("PrivateAccessorDeclaration.golden")));
i::FLAG_harmony_private_methods = old_methods_flag;
}
......
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-private-methods
class C {
set #foo(val) {}
constructor() {
this.#foo++;
}
}
new C();
# 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:10: TypeError: '#foo' was defined without a getter
this.#foo++;
^
TypeError: '#foo' was defined without a getter
at new C (*%(basename)s:10:5)
at *%(basename)s:14: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.
// Flags: --harmony-private-methods
class C {
set #a(val) {}
setA(obj, val) { obj.#a = val; }
constructor() {
class D {
get #a() {}
}
this.setA(new D(), 1);
}
}
new C;
# 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:9: TypeError: Cannot read private member C from an object whose class did not declare it
setA(obj, val) { obj.#a = val; }
^
TypeError: Cannot read private member C from an object whose class did not declare it
at C.setA (*%(basename)s:9:24)
at new C (*%(basename)s:15:10)
at *%(basename)s:19:1
\ No newline at end of file
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-private-methods
class C {
set #a(val) {}
constructor() {
const a = this.#a;
}
}
new C;
# 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:10: TypeError: '#a' was defined without a getter
const a = this.#a;
^
TypeError: '#a' was defined without a getter
at new C (*%(basename)s:10:15)
at *%(basename)s:13: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.
// Flags: --harmony-private-methods
class C {
get #a() {}
constructor() {
this.#a = 1;
}
}
new C;
# 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:10: TypeError: '#a' was defined without a setter
this.#a = 1;
^
TypeError: '#a' was defined without a setter
at new C (*%(basename)s:10:13)
at *%(basename)s:13: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.
// Flags: --harmony-private-methods
class C {
get #foo() {}
constructor() {
this.#foo++;
}
}
new C();
# 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:10: TypeError: '#foo' was defined without a setter
this.#foo++;
^
TypeError: '#foo' was defined without a setter
at new C (*%(basename)s:10:5)
at *%(basename)s:14: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.
// Flags: --harmony-private-methods
class C {
get #a() {}
getA(obj) { return obj.#a; }
constructor() {
class D {
set #a(val) {}
}
this.getA(new D());
}
}
new C;
# 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:9: TypeError: Cannot read private member C from an object whose class did not declare it
getA(obj) { return obj.#a; }
^
TypeError: Cannot read private member C from an object whose class did not declare it
at C.getA (*%(basename)s:9:26)
at new C (*%(basename)s:15:10)
at *%(basename)s:19: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.
// Flags: --harmony-private-methods
class C {
get #a() {}
constructor() {
this.#a = 1;
}
}
new C;
# 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:10: TypeError: '#a' was defined without a setter
this.#a = 1;
^
TypeError: '#a' was defined without a setter
at new C (*%(basename)s:10:13)
at *%(basename)s:13:1
\ No newline at end of file
*%(basename)s:9: TypeError: Read of private field #a from an object which did not contain the field
*%(basename)s:9: TypeError: Cannot read private member #a from an object whose class did not declare it
[o.#a](){}
^
TypeError: Read of private field #a from an object which did not contain the field
TypeError: Cannot read private member #a from an object whose class did not declare it
at *%(basename)s:9:8
*%(basename)s:8: TypeError: Write of private field #x to an object which did not contain the field
*%(basename)s:8: TypeError: Cannot write private member #x to an object whose class did not declare it
({}).#x = 1;
^
TypeError: Write of private field #x to an object which did not contain the field
TypeError: Cannot write private member #x to an object whose class did not declare it
at new X (*%(basename)s:8:13)
at *%(basename)s:12:1
\ No newline at end of file
*%(basename)s:7: TypeError: Read of private field #x from an object which did not contain the field
*%(basename)s:7: TypeError: Cannot read private member #x from an object whose class did not declare it
eq(o) { return this.#x === o.#x; }
^
TypeError: Read of private field #x from an object which did not contain the field
TypeError: Cannot read private member #x from an object whose class did not declare it
at X.eq (*%(basename)s:7:32)
at *%(basename)s:10:9
\ No newline at end of file
*%(basename)s:7: TypeError: Write of private field #x to an object which did not contain the field
*%(basename)s:7: TypeError: Cannot write private member #x to an object whose class did not declare it
setX(o, val) { o.#x = val; }
^
TypeError: Write of private field #x to an object which did not contain the field
TypeError: Cannot write private member #x to an object whose class did not declare it
at X.setX (*%(basename)s:7:23)
at *%(basename)s:10:9
\ No newline at end of file
......@@ -8,12 +8,40 @@
// Complementary private accessors.
{
let store = 1;
class C {
get #a() { }
set #a(val) { }
get #a() { return store; }
set #a(val) { store = val; }
incA() { this.#a++; } // CountOperation
setA(val) { this.#a = val; }
getA() { return this.#a; }
}
new C;
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.
......@@ -39,13 +67,20 @@
// Nested private accessors.
{
class C {
a() { this.#a; }
get #a() {
class D { 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;
}
}
new C().a();
assertEquals('d', new C().getA().getD());
}
// Duplicate private accessors.
......
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