[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/+/1695205
Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Cr-Commit-Position: refs/heads/master@{#63474}
This commit is contained in:
Joyee Cheung 2019-08-30 09:09:00 +08:00 committed by Commit Bot
parent bdcc7502cb
commit df12eb194e
29 changed files with 793 additions and 185 deletions

View File

@ -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 %") \

View File

@ -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);
}

View File

@ -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;
}
case PRIVATE_GETTER_ONLY:
case PRIVATE_SETTER_ONLY:
case PRIVATE_GETTER_AND_SETTER: {
// TODO(joyee): implement private accessors.
BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateMethodWrite,
property);
return;
}
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,17 +5166,20 @@ void BytecodeGenerator::VisitCountOperation(CountOperation* expr) {
.CallRuntime(Runtime::kStoreKeyedToSuper, super_property_args);
break;
}
case PRIVATE_METHOD: {
BuildThrowPrivateMethodWriteError(
property->key()->AsVariableProxy()->raw_name());
break;
}
case PRIVATE_GETTER_ONLY:
case PRIVATE_SETTER_ONLY:
case PRIVATE_GETTER_AND_SETTER: {
// TODO(joyee): implement private accessors.
case PRIVATE_GETTER_ONLY:
case PRIVATE_METHOD: {
UNREACHABLE();
}
case PRIVATE_GETTER_AND_SETTER: {
Register value = register_allocator()->NewRegister();
builder()->StoreAccumulatorInRegister(value);
BuildPrivateSetterAccess(object, key, value);
if (!execution_result()->IsEffect()) {
builder()->LoadAccumulatorWithRegister(value);
}
break;
}
}
// Restore old value for postfix expressions.

View File

@ -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);

View File

@ -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);
}

View File

@ -0,0 +1,192 @@
#
# 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: [
]

View File

@ -0,0 +1,104 @@
#
# 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: [
]

View File

@ -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(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(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(Mov), R(5), R(7),
B(CallRuntime), U16(Runtime::kDefineClass), R(6), U8(3),
B(Star), R(6),
B(LdaConstant), U8(7),
B(Mov), R(7), R(3),
B(LdaConstant), U8(4),
B(Star), R(7),
B(CreateClosure), U8(9), U8(4), 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(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(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),
/* 232 E> */ B(CallProperty0), R(5), R(2), U8(4),
B(LdaConstant), U8(6),
B(Star), R(6),
B(CreateClosure), U8(8), U8(3), U8(2),
B(StaCurrentContextSlot), 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(CallRuntime), U16(Runtime::kCreatePrivateNameSymbol), R(7), U8(1),
B(StaCurrentContextSlot), U8(5),
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: [
]

View File

@ -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(PrivateAccessors) {
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(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;
}

View File

@ -0,0 +1,14 @@
// 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();

View File

@ -0,0 +1,9 @@
# 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

View File

@ -0,0 +1,19 @@
// 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;

View File

@ -0,0 +1,10 @@
# 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

View File

@ -0,0 +1,13 @@
// 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;

View File

@ -0,0 +1,9 @@
# 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

View File

@ -0,0 +1,13 @@
// 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;

View File

@ -0,0 +1,9 @@
# 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

View File

@ -0,0 +1,14 @@
// 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();

View File

@ -0,0 +1,9 @@
# 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

View File

@ -0,0 +1,19 @@
// 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;

View File

@ -0,0 +1,10 @@
# 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

View File

@ -0,0 +1,13 @@
// 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;

View File

@ -0,0 +1,9 @@
# 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

View File

@ -1,5 +1,5 @@
*%(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

View File

@ -1,6 +1,6 @@
*%(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

View File

@ -1,6 +1,6 @@
*%(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

View File

@ -1,6 +1,6 @@
*%(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

View 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.