WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
Patch
bug-194435-20210210155927.patch (text/plain), 56.16 KB, created by
Caio Lima
on 2021-02-10 10:59:29 PST
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Caio Lima
Created:
2021-02-10 10:59:29 PST
Size:
56.16 KB
patch
obsolete
>Subversion Revision: 272663 >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index 51b02fa123d89b3697ed6a5a5bb47a19f6c3d725..e7e5057471fe66b025bb553af84f7870bbc2ee96 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,104 @@ >+2021-02-10 Caio Lima <ticaiolima@gmail.com> >+ >+ [ESNext] Implement private accessors >+ https://bugs.webkit.org/show_bug.cgi?id=194435 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ This patch is implementing support for instance private getters and >+ setters following the proposal on https://tc39.es/proposal-private-methods. >+ Private accessors also use the private brand check mechanism of >+ private methods, which means that we are using both >+ `op_set_private_brand` and `op_check_private_brand` to perform brand >+ checks. Accessors are also stored on class lexical scope as a pair of >+ `getter` and `setter`. This is done creating a new `JSObject` and >+ storing the `getter` on `get` property, and `setter` on `set` >+ property. This is designed in such way that we can always hit IC fast >+ path on `get_by_id_direct` to access the property, and also to allow >+ constant folding of accessors on DFG/FTL, since acessors objects are >+ going to be constant once created. >+ In addition to private accessors, we are also moving private fields >+ and private methods to use private identifier as their name, avoiding >+ future issues if we ever allow identifiers to start with `#`. >+ >+ For reference, we have the following bytecode for a private getter >+ access: >+ >+ ``` >+ class C { >+ get #m() {...} >+ access() { >+ return this.#m; >+ } >+ } >+ ``` >+ >+ Bytecode for class declaration: >+ >+ ``` >+ ... >+ new_object dst:loc12, inlineCapacity:2 // this is the object to store getter and setter pair >+ new_func_exp dst:loc13, scope:loc4, functionDecl:"get #m() {...}" >+ put_by_id base:loc13, property:@homeObject, value:loc11, flags:Strict >+ put_by_id base:loc12, property:@get, value:loc13, flags:IsDirect|Strict >+ put_to_scope scope:loc4, var:PrivateName.#m, value:loc12 // loc4 is the class lexical scope >+ ... >+ >+ ``` >+ >+ Bytecode for `access()`: >+ >+ ``` >+ ... >+ resolve_scope dst:loc7, scope:loc4, var:"#m", resolveType:GlobalProperty, localScopeDepth:0 >+ get_from_scope dst:loc8, scope:loc7, var:@privateBrand >+ check_private_brand base:this, brand:loc8 >+ get_from_scope dst:loc8, scope:loc7, var:"#m" >+ get_by_id_direct dst:loc9, base:loc8, property:@get >+ mov dst:loc10, src:this >+ call dst:loc6, callee:loc9, argc:1, argv:16 >+ ... >+ >+ ``` >+ >+ * bytecompiler/BytecodeGenerator.cpp: >+ (JSC::BytecodeGenerator::instantiateLexicalVariables): >+ (JSC::BytecodeGenerator::isPrivateSetter): >+ (JSC::BytecodeGenerator::isPrivateGetter): >+ * bytecompiler/BytecodeGenerator.h: >+ * bytecompiler/NodesCodegen.cpp: >+ (JSC::PropertyListNode::emitBytecode): >+ (JSC::PropertyListNode::emitPutConstantProperty): >+ (JSC::BaseDotNode::emitGetPropertyValue): >+ (JSC::BaseDotNode::emitPutProperty): >+ * parser/Nodes.h: >+ * parser/Parser.cpp: >+ (JSC::Parser<LexerType>::parseClass): >+ (JSC::Parser<LexerType>::parseClassFieldInitializerSourceElements): >+ (JSC::Parser<LexerType>::parseGetterSetter): >+ (JSC::Parser<LexerType>::parseMemberExpression): >+ * parser/Parser.h: >+ (JSC::Scope::declarePrivateSetter): >+ (JSC::Scope::declarePrivateGetter): >+ * parser/ParserArena.cpp: >+ (JSC::IdentifierArena::makePrivateIdentifier): >+ * parser/ParserArena.h: >+ * parser/VariableEnvironment.cpp: >+ (JSC::VariableEnvironment::declarePrivateSetter): >+ (JSC::VariableEnvironment::declarePrivateGetter): >+ * parser/VariableEnvironment.h: >+ (JSC::VariableEnvironmentEntry::isPrivateSetter const): >+ (JSC::VariableEnvironmentEntry::isPrivateGetter const): >+ (JSC::VariableEnvironmentEntry::setIsPrivateSetter): >+ (JSC::VariableEnvironmentEntry::setIsPrivateGetter): >+ (JSC::PrivateNameEntry::isSetter const): >+ (JSC::PrivateNameEntry::isGetter const): >+ (JSC::PrivateNameEntry::isPrivateMethodOrAcessor const): >+ (JSC::VariableEnvironment::declarePrivateSetter): >+ (JSC::VariableEnvironment::declarePrivateGetter): >+ * runtime/ExceptionHelpers.cpp: >+ (JSC::createPrivateMethodAccessError): >+ > 2021-02-10 Saam Barati <sbarati@apple.com> > > Don't crash when reparsing an arrow function and the parsing invariant is broken >diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp >index 4e5719ef8258f3b29fe60d76db202d2f9b28a57e..be21e9b87b8cad21d101301b38189b25cc7e9a9b 100644 >--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp >+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp >@@ -1904,6 +1904,15 @@ bool BytecodeGenerator::instantiateLexicalVariables(const VariableEnvironment& l > symbolTable->addPrivateName(entry.key.get(), PrivateNameEntry(PrivateNameEntry::Traits::IsDeclared)); > else if (entry.value.isPrivateMethod()) > symbolTable->addPrivateName(entry.key.get(), PrivateNameEntry(PrivateNameEntry::Traits::IsDeclared | PrivateNameEntry::Traits::IsMethod)); >+ else if (entry.value.isPrivateGetter() || entry.value.isPrivateSetter()) { >+ uint16_t traits = PrivateNameEntry::Traits::IsDeclared; >+ if (entry.value.isPrivateGetter()) >+ traits |= PrivateNameEntry::Traits::IsGetter; >+ if (entry.value.isPrivateSetter()) >+ traits |= PrivateNameEntry::Traits::IsSetter; >+ >+ symbolTable->addPrivateName(entry.key.get(), PrivateNameEntry(traits)); >+ } > } > } > return hasCapturedVariables; >@@ -2943,6 +2952,30 @@ bool BytecodeGenerator::isPrivateMethod(const Identifier& ident) > return false; > } > >+bool BytecodeGenerator::isPrivateSetter(const Identifier& ident) >+{ >+ for (unsigned i = m_privateNamesStack.size(); i--; ) { >+ auto& map = m_privateNamesStack[i]; >+ auto it = map.find(ident.impl()); >+ if (it != map.end()) >+ return it->value.isSetter(); >+ } >+ >+ return false; >+} >+ >+bool BytecodeGenerator::isPrivateGetter(const Identifier& ident) >+{ >+ for (unsigned i = m_privateNamesStack.size(); i--; ) { >+ auto& map = m_privateNamesStack[i]; >+ auto it = map.find(ident.impl()); >+ if (it != map.end()) >+ return it->value.isGetter(); >+ } >+ >+ return false; >+} >+ > void BytecodeGenerator::pushPrivateAccessNames(const PrivateNameEnvironment* environment) > { > if (!environment || !environment->size()) >diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h >index bbfb568198f444557da3133c29663cd4201e7be5..8b13ec806dec495609f4c8706b87093ef64ff558 100644 >--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h >+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h >@@ -1264,6 +1264,8 @@ namespace JSC { > } > > bool isPrivateMethod(const Identifier&); >+ bool isPrivateSetter(const Identifier&); >+ bool isPrivateGetter(const Identifier&); > > void pushPrivateAccessNames(const PrivateNameEnvironment*); > void popPrivateAccessNames(); >diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp >index 4c08a33d3f87bb42be3f8f6cdba5fa0370078e5f..e41179fbb4a3ee020b1e90f5f57668e969dc1219 100644 >--- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp >+++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp >@@ -579,13 +579,65 @@ void PropertyListNode::emitDeclarePrivateFieldNames(BytecodeGenerator& generator > > RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dstOrConstructor, RegisterID* prototype, Vector<JSTextPosition>* instanceFieldLocations, Vector<JSTextPosition>* staticFieldLocations) > { >+ typedef std::pair<PropertyNode*, PropertyNode*> GetterSetterPair; >+ typedef HashMap<UniquedStringImpl*, GetterSetterPair, IdentifierRepHash> GetterSetterMap; >+ GetterSetterMap privateAccessorMap; >+ > PropertyListNode* p = this; > RegisterID* dst = nullptr; >+ for (; p; p = p->m_next) { >+ if (!(p->m_node->type() & (PropertyNode::PrivateGetter | PropertyNode::PrivateSetter))) >+ continue; >+ >+ // We group private getters and setters to store them in a object >+ GetterSetterPair pair(p->m_node, static_cast<PropertyNode*>(nullptr)); >+ GetterSetterMap::AddResult result = privateAccessorMap.add(p->m_node->name()->impl(), pair); >+ auto& resultPair = result.iterator->value; >+ // If the map already contains an element with node->name(), >+ // we need to store this node in the second part. >+ if (!result.isNewEntry) >+ resultPair.second = p->m_node; >+ continue; >+ } >+ >+ // Then we declare private accessos >+ for (auto& it : privateAccessorMap) { >+ RefPtr<RegisterID> getterSetterObj = generator.emitNewObject(generator.newTemporary()); >+ >+ GetterSetterPair pair = it.value; >+ if (pair.first) { >+ dst = pair.first->isInstanceClassProperty() ? prototype : dstOrConstructor; >+ RefPtr<RegisterID> value = generator.emitNode(pair.first->m_assign); >+ if (pair.first->needsSuperBinding()) >+ emitPutHomeObject(generator, value.get(), dst); >+ auto setterOrGetterIdent = pair.first->m_type & PropertyNode::PrivateGetter >+ ? generator.propertyNames().builtinNames().getPrivateName() >+ : generator.propertyNames().builtinNames().setPrivateName(); >+ generator.emitDirectPutById(getterSetterObj.get(), setterOrGetterIdent, value.get()); >+ } > >+ if (pair.second) { >+ dst = pair.second->isInstanceClassProperty() ? prototype : dstOrConstructor; >+ RefPtr<RegisterID> value = generator.emitNode(pair.second->m_assign); >+ if (pair.second->needsSuperBinding()) >+ emitPutHomeObject(generator, value.get(), dst); >+ auto setterOrGetterIdent = pair.second->m_type & PropertyNode::PrivateGetter >+ ? generator.propertyNames().builtinNames().getPrivateName() >+ : generator.propertyNames().builtinNames().setPrivateName(); >+ generator.emitDirectPutById(getterSetterObj.get(), setterOrGetterIdent, value.get()); >+ } >+ >+ Variable var = generator.variable(*pair.first->name()); >+ generator.emitPutToScope(generator.scopeRegister(), var, getterSetterObj.get(), DoNotThrowIfNotFound, InitializationMode::ConstInitialization); >+ } >+ > // Fast case: this loop just handles regular value properties. >- for (; p && (p->m_node->m_type & PropertyNode::Constant); p = p->m_next) { >+ for (p = this; p && (p->m_node->m_type & PropertyNode::Constant); p = p->m_next) { > dst = p->m_node->isInstanceClassProperty() ? prototype : dstOrConstructor; > >+ if (p->m_node->type() & (PropertyNode::PrivateGetter | PropertyNode::PrivateSetter)) >+ continue; >+ > if (p->isComputedClassField()) > emitSaveComputedFieldName(generator, *p->m_node); > >@@ -610,8 +662,6 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe > // a computed property or a spread, just emit everything as that may override previous values. > bool canOverrideProperties = false; > >- typedef std::pair<PropertyNode*, PropertyNode*> GetterSetterPair; >- typedef HashMap<UniquedStringImpl*, GetterSetterPair, IdentifierRepHash> GetterSetterMap; > GetterSetterMap instanceMap; > GetterSetterMap staticMap; > >@@ -651,6 +701,9 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe > if (p->isComputedClassField()) > emitSaveComputedFieldName(generator, *p->m_node); > >+ if (p->m_node->type() & (PropertyNode::PrivateGetter | PropertyNode::PrivateSetter)) >+ continue; >+ > if (p->isInstanceClassField()) { > ASSERT(instanceFieldLocations); > ASSERT(node->m_type & PropertyNode::Constant); >@@ -782,6 +835,8 @@ void PropertyListNode::emitPutConstantProperty(BytecodeGenerator& generator, Reg > > if (node.isClassProperty()) { > ASSERT(node.needsSuperBinding()); >+ ASSERT(!(node.type() & PropertyNode::PrivateSetter)); >+ ASSERT(!(node.type() & PropertyNode::PrivateGetter)); > > if (node.type() & PropertyNode::PrivateMethod) { > Variable var = generator.variable(*node.name()); >@@ -919,8 +974,9 @@ RegisterID* DotAccessorNode::emitBytecode(BytecodeGenerator& generator, Register > RegisterID* BaseDotNode::emitGetPropertyValue(BytecodeGenerator& generator, RegisterID* dst, RegisterID* base, RefPtr<RegisterID>& thisValue) > { > if (isPrivateMember()) { >- if (generator.isPrivateMethod(identifier())) { >- Variable var = generator.variable(identifier()); >+ auto identifierName = identifier(); >+ if (generator.isPrivateMethod(identifierName)) { >+ Variable var = generator.variable(identifierName); > RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var); > > RegisterID* privateBrandSymbol = generator.emitGetPrivateBrand(generator.newTemporary(), scope.get()); >@@ -929,6 +985,31 @@ RegisterID* BaseDotNode::emitGetPropertyValue(BytecodeGenerator& generator, Regi > return generator.emitGetFromScope(dst, scope.get(), var, ThrowIfNotFound); > } > >+ if (generator.isPrivateGetter(identifierName)) { >+ Variable var = generator.variable(identifierName); >+ RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var); >+ >+ RegisterID* privateBrandSymbol = generator.emitGetPrivateBrand(generator.newTemporary(), scope.get()); >+ generator.emitCheckPrivateBrand(base, privateBrandSymbol); >+ >+ RefPtr<RegisterID> getterSetterObj = generator.emitGetFromScope(generator.newTemporary(), scope.get(), var, ThrowIfNotFound); >+ RefPtr<RegisterID> getterFunction = generator.emitDirectGetById(generator.newTemporary(), getterSetterObj.get(), generator.propertyNames().builtinNames().getPrivateName()); >+ CallArguments args(generator, nullptr); >+ generator.move(args.thisRegister(), base); >+ return generator.emitCall(dst, getterFunction.get(), NoExpectedFunction, args, m_position, m_position, m_position, DebuggableCall::Yes); >+ } >+ >+ if (generator.isPrivateSetter(identifierName)) { >+ // We need to perform brand check to follow the spec >+ Variable var = generator.variable(identifierName); >+ RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var); >+ >+ RegisterID* privateBrandSymbol = generator.emitGetPrivateBrand(generator.newTemporary(), scope.get()); >+ generator.emitCheckPrivateBrand(base, privateBrandSymbol); >+ generator.emitThrowTypeError("Trying to access a not defined private getter"); >+ return dst; >+ } >+ > Variable var = generator.variable(m_ident); > ASSERT_WITH_MESSAGE(!var.local(), "Private Field names must be stored in captured variables"); > >@@ -957,7 +1038,25 @@ RegisterID* BaseDotNode::emitPutProperty(BytecodeGenerator& generator, RegisterI > { > if (isPrivateMember()) { > auto identifierName = identifier(); >- if (generator.isPrivateMethod(identifierName)) { >+ if (generator.isPrivateSetter(identifierName)) { >+ Variable var = generator.variable(identifierName); >+ RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var); >+ >+ RegisterID* privateBrandSymbol = generator.emitGetPrivateBrand(generator.newTemporary(), scope.get()); >+ generator.emitCheckPrivateBrand(base, privateBrandSymbol); >+ >+ RefPtr<RegisterID> getterSetterObj = generator.emitGetFromScope(generator.newTemporary(), scope.get(), var, ThrowIfNotFound); >+ RefPtr<RegisterID> setterFunction = generator.emitDirectGetById(generator.newTemporary(), getterSetterObj.get(), generator.propertyNames().builtinNames().setPrivateName()); >+ CallArguments args(generator, nullptr, 1); >+ generator.move(args.thisRegister(), base); >+ generator.move(args.argumentRegister(0), value); >+ generator.emitCall(generator.newTemporary(), setterFunction.get(), NoExpectedFunction, args, m_position, m_position, m_position, DebuggableCall::Yes); >+ >+ return value; >+ } >+ >+ bool isPrivateGetter = generator.isPrivateGetter(identifierName); >+ if (isPrivateGetter || generator.isPrivateMethod(identifierName)) { > Variable var = generator.variable(identifierName); > RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var); > >diff --git a/Source/JavaScriptCore/parser/Nodes.h b/Source/JavaScriptCore/parser/Nodes.h >index 4bf9386f26f02983b78869e4a25025f60ec8ab35..1430e8d445d5afe964d1c5173ada399dbd0ecfce 100644 >--- a/Source/JavaScriptCore/parser/Nodes.h >+++ b/Source/JavaScriptCore/parser/Nodes.h >@@ -721,7 +721,7 @@ namespace JSC { > enum class ClassElementTag : uint8_t { No, Instance, Static, LastTag }; > class PropertyNode final : public ParserArenaFreeable { > public: >- enum Type : uint8_t { Constant = 1, Getter = 2, Setter = 4, Computed = 8, Shorthand = 16, Spread = 32, PrivateField = 64, PrivateMethod = 128 }; >+ enum Type : uint16_t { Constant = 1, Getter = 2, Setter = 4, Computed = 8, Shorthand = 16, Spread = 32, PrivateField = 64, PrivateMethod = 128, PrivateSetter = 256, PrivateGetter = 512 }; > > PropertyNode(const Identifier&, ExpressionNode*, Type, SuperBinding, ClassElementTag); > PropertyNode(ExpressionNode*, Type, SuperBinding, ClassElementTag); >@@ -740,7 +740,7 @@ namespace JSC { > bool isInstanceClassField() const { return isInstanceClassProperty() && !needsSuperBinding(); } > bool isStaticClassField() const { return isStaticClassProperty() && !needsSuperBinding(); } > bool isOverriddenByDuplicate() const { return m_isOverriddenByDuplicate; } >- bool isPrivate() const { return m_type & (PrivateField | PrivateMethod); } >+ bool isPrivate() const { return m_type & (PrivateField | PrivateMethod | PrivateGetter | PrivateSetter); } > bool hasComputedName() const { return m_expression; } > bool isComputedClassField() const { return isClassField() && hasComputedName(); } > void setIsOverriddenByDuplicate() { m_isOverriddenByDuplicate = true; } >@@ -760,7 +760,7 @@ namespace JSC { > const Identifier* m_name; > ExpressionNode* m_expression; > ExpressionNode* m_assign; >- unsigned m_type; >+ unsigned m_type : 10; > unsigned m_needsSuperBinding : 1; > static_assert(1 << 2 > static_cast<unsigned>(ClassElementTag::LastTag), "ClassElementTag shouldn't use more than two bits"); > unsigned m_classElementTag : 2; >diff --git a/Source/JavaScriptCore/parser/Parser.cpp b/Source/JavaScriptCore/parser/Parser.cpp >index eca5bb3059596a846526dfc757111da6f5463378..16d7169223a73526c8e7a193a5f088ae86bbd90e 100644 >--- a/Source/JavaScriptCore/parser/Parser.cpp >+++ b/Source/JavaScriptCore/parser/Parser.cpp >@@ -2978,7 +2978,7 @@ parseMethod: > bool escaped = m_token.m_data.escaped; > ASSERT(ident); > next(); >- if (parseMode == SourceParseMode::MethodMode && !escaped && (matchIdentifierOrKeyword() || match(STRING) || match(DOUBLE) || match(INTEGER) || match(BIGINT) || match(OPENBRACKET))) { >+ if (parseMode == SourceParseMode::MethodMode && !escaped && (matchIdentifierOrKeyword() || match(STRING) || match(DOUBLE) || match(INTEGER) || match(BIGINT) || match(OPENBRACKET) || (Options::usePrivateMethods() && match(PRIVATENAME)))) { > isGetter = *ident == propertyNames.get; > isSetter = *ident == propertyNames.set; > } >@@ -2999,7 +2999,7 @@ parseMethod: > break; > case PRIVATENAME: { > ASSERT(Options::usePrivateClassFields()); >- ident = m_token.m_data.ident; >+ ident = &m_parserArena.identifierArena().makePrivateIdentifier(m_vm, *m_token.m_data.ident); > if (!Options::usePrivateStaticClassFields()) > failIfTrue(tag == ClassElementTag::Static, "Static class element cannot be private"); > failIfTrue(isGetter || isSetter, "Cannot parse class method with private name"); >@@ -3025,8 +3025,19 @@ parseMethod: > > TreeProperty property; > if (isGetter || isSetter) { >- type = static_cast<PropertyNode::Type>(type & ~PropertyNode::Constant); >- type = static_cast<PropertyNode::Type>(type | (isGetter ? PropertyNode::Getter : PropertyNode::Setter)); >+ if (Options::usePrivateMethods() && match(PRIVATENAME)) { >+ ident = &m_parserArena.identifierArena().makePrivateIdentifier(m_vm, *m_token.m_data.ident); >+ if (isSetter) { >+ semanticFailIfTrue(classScope->declarePrivateSetter(*ident) & DeclarationResult::InvalidDuplicateDeclaration, "Declared private setter with an already used name"); >+ type = static_cast<PropertyNode::Type>(type | PropertyNode::PrivateSetter); >+ } else { >+ semanticFailIfTrue(classScope->declarePrivateGetter(*ident) & DeclarationResult::InvalidDuplicateDeclaration, "Declared private getter with an already used name"); >+ type = static_cast<PropertyNode::Type>(type | PropertyNode::PrivateGetter); >+ } >+ } else { >+ type = static_cast<PropertyNode::Type>(type & ~PropertyNode::Constant); >+ type = static_cast<PropertyNode::Type>(type | (isGetter ? PropertyNode::Getter : PropertyNode::Setter)); >+ } > property = parseGetterSetter(context, type, methodStart, ConstructorKind::None, tag); > failIfFalse(property, "Cannot parse this method"); > } else if (!match(OPENPAREN) && (tag == ClassElementTag::Instance || Options::usePublicStaticClassFields()) && parseMode == SourceParseMode::MethodMode) { >@@ -3155,7 +3166,10 @@ template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseClassFie > switch (m_token.m_type) { > case PRIVATENAME: > type = DefineFieldNode::Type::PrivateName; >- FALLTHROUGH; >+ ident = &m_parserArena.identifierArena().makePrivateIdentifier(m_vm, *m_token.m_data.ident); >+ ASSERT(ident); >+ next(); >+ break; > case STRING: > case IDENT: > namedKeyword: >@@ -4428,12 +4442,21 @@ template <class TreeBuilder> TreeProperty Parser<LexerType>::parseGetterSetter(T > > JSTokenLocation location(tokenLocation()); > >- if (matchSpecIdentifier() || match(STRING) || m_token.m_type & KeywordTokenFlag) { >- stringPropertyName = m_token.m_data.ident; >+ bool matchesPrivateName = match(PRIVATENAME); >+ if (matchSpecIdentifier() || match(STRING) || (Options::usePrivateMethods() && matchesPrivateName) || m_token.m_type & KeywordTokenFlag) { >+ if (matchesPrivateName) >+ stringPropertyName = &m_parserArena.identifierArena().makePrivateIdentifier(m_vm, *m_token.m_data.ident); >+ else >+ stringPropertyName = m_token.m_data.ident; > semanticFailIfTrue(tag == ClassElementTag::Static && *stringPropertyName == m_vm.propertyNames->prototype, > "Cannot declare a static method named 'prototype'"); > semanticFailIfTrue(tag == ClassElementTag::Instance && *stringPropertyName == m_vm.propertyNames->constructor, > "Cannot declare a getter or setter named 'constructor'"); >+ >+ if (match(PRIVATENAME)) { >+ semanticFailIfTrue(tag == ClassElementTag::No, "Cannot declare a private setter or getter outside a class"); >+ semanticFailIfTrue(tag == ClassElementTag::Static, "Cannot declare a private setter or getter as static"); >+ } > next(); > } else if (match(DOUBLE) || match(INTEGER)) { > numericPropertyName = m_token.m_data.doubleValue; >@@ -4454,10 +4477,18 @@ template <class TreeBuilder> TreeProperty Parser<LexerType>::parseGetterSetter(T > failIfFalse(match(OPENPAREN), "Expected a parameter list for getter definition"); > SetForScope<SourceParseMode> innerParseMode(m_parseMode, SourceParseMode::GetterMode); > failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::Unnamed, false, constructorKind, SuperBinding::Needed, getterOrSetterStartOffset, info, FunctionDefinitionType::Method)), "Cannot parse getter definition"); >- } else { >+ } else if (type & PropertyNode::Setter) { > failIfFalse(match(OPENPAREN), "Expected a parameter list for setter definition"); > SetForScope<SourceParseMode> innerParseMode(m_parseMode, SourceParseMode::SetterMode); > failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::Unnamed, false, constructorKind, SuperBinding::Needed, getterOrSetterStartOffset, info, FunctionDefinitionType::Method)), "Cannot parse setter definition"); >+ } else if (type & PropertyNode::PrivateSetter) { >+ failIfFalse(match(OPENPAREN), "Expected a parameter list for private setter definition"); >+ SetForScope<SourceParseMode> innerParseMode(m_parseMode, SourceParseMode::SetterMode); >+ failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::Unnamed, false, constructorKind, SuperBinding::Needed, getterOrSetterStartOffset, info, FunctionDefinitionType::Method)), "Cannot parse private setter definition"); >+ } else if (type & PropertyNode::PrivateGetter) { >+ failIfFalse(match(OPENPAREN), "Expected a parameter list for private getter definition"); >+ SetForScope<SourceParseMode> innerParseMode(m_parseMode, SourceParseMode::GetterMode); >+ failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::Unnamed, false, constructorKind, SuperBinding::Needed, getterOrSetterStartOffset, info, FunctionDefinitionType::Method)), "Cannot parse private getter definition"); > } > > if (stringPropertyName) >@@ -5183,6 +5214,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres > const Identifier* ident = m_token.m_data.ident; > auto type = DotType::Name; > if (match(PRIVATENAME)) { >+ ident = &m_parserArena.identifierArena().makePrivateIdentifier(m_vm, *ident); > ASSERT(ident); > failIfTrue(baseIsSuper, "Cannot access private names from super"); > if (UNLIKELY(currentScope()->evalContextType() == EvalContextType::InstanceFieldEvalContext)) >diff --git a/Source/JavaScriptCore/parser/Parser.h b/Source/JavaScriptCore/parser/Parser.h >index 68efcadeaa3f60953fe96cd2ebfd0d7ba8544955..4e9664fecfb69330558e7a86746a2d2b66d9c540 100644 >--- a/Source/JavaScriptCore/parser/Parser.h >+++ b/Source/JavaScriptCore/parser/Parser.h >@@ -517,6 +517,34 @@ public: > return result; > } > >+ DeclarationResultMask declarePrivateSetter(const Identifier& ident) >+ { >+ ASSERT(m_allowsLexicalDeclarations); >+ DeclarationResultMask result = DeclarationResult::Valid; >+ bool addResult = m_lexicalVariables.declarePrivateSetter(ident); >+ >+ if (!addResult) { >+ result |= DeclarationResult::InvalidDuplicateDeclaration; >+ return result; >+ } >+ >+ return result; >+ } >+ >+ DeclarationResultMask declarePrivateGetter(const Identifier& ident) >+ { >+ ASSERT(m_allowsLexicalDeclarations); >+ DeclarationResultMask result = DeclarationResult::Valid; >+ bool addResult = m_lexicalVariables.declarePrivateGetter(ident); >+ >+ if (!addResult) { >+ result |= DeclarationResult::InvalidDuplicateDeclaration; >+ return result; >+ } >+ >+ return result; >+ } >+ > DeclarationResultMask declarePrivateField(const Identifier& ident) > { > ASSERT(m_allowsLexicalDeclarations); >diff --git a/Source/JavaScriptCore/parser/ParserArena.cpp b/Source/JavaScriptCore/parser/ParserArena.cpp >index c59e068af4833d0d35935c196d92d65ff9f80dba..e581afaffbfa8b7bb94f94d587dad93c1c0ebe7f 100644 >--- a/Source/JavaScriptCore/parser/ParserArena.cpp >+++ b/Source/JavaScriptCore/parser/ParserArena.cpp >@@ -115,4 +115,11 @@ const Identifier& IdentifierArena::makePrivateIdentifier(VM& vm, ASCIILiteral pr > return m_identifiers.last(); > } > >+const Identifier& IdentifierArena::makePrivateIdentifier(VM& vm, const Identifier& identifier) >+{ >+ auto symbol = vm.privateSymbolRegistry().symbolForKey(identifier.string()); >+ m_identifiers.append(Identifier::fromUid(symbol)); >+ return m_identifiers.last(); >+} >+ > } >diff --git a/Source/JavaScriptCore/parser/ParserArena.h b/Source/JavaScriptCore/parser/ParserArena.h >index be27f34b1c8977398e1f4331fb931a3eeccf709e..96a39b90c01097eda5cd56a4d1221fd0bfb97b8b 100644 >--- a/Source/JavaScriptCore/parser/ParserArena.h >+++ b/Source/JavaScriptCore/parser/ParserArena.h >@@ -53,6 +53,7 @@ namespace JSC { > const Identifier& makeBigIntDecimalIdentifier(VM&, const Identifier&, uint8_t radix); > const Identifier& makeNumericIdentifier(VM&, double number); > const Identifier& makePrivateIdentifier(VM&, ASCIILiteral, unsigned); >+ const Identifier& makePrivateIdentifier(VM&, const Identifier&); > > public: > static const int MaximumCachableCharacter = 128; >diff --git a/Source/JavaScriptCore/parser/VariableEnvironment.cpp b/Source/JavaScriptCore/parser/VariableEnvironment.cpp >index 3ec57c3e2aba5db37b7fb62e3c9f0a163fb5de67..a81bcdc069a6898df08d553a55957b7f668c33f4 100644 >--- a/Source/JavaScriptCore/parser/VariableEnvironment.cpp >+++ b/Source/JavaScriptCore/parser/VariableEnvironment.cpp >@@ -104,6 +104,102 @@ void VariableEnvironment::markVariableAsExported(const RefPtr<UniquedStringImpl> > findResult->value.setIsExported(); > } > >+bool VariableEnvironment::declarePrivateSetter(const RefPtr<UniquedStringImpl>& identifier) >+{ >+ if (!m_rareData) >+ m_rareData = WTF::makeUnique<VariableEnvironment::RareData>(); >+ >+ auto findResult = m_rareData->m_privateNames.find(identifier); >+ >+ if (findResult == m_rareData->m_privateNames.end()) { >+ PrivateNameEntry meta(PrivateNameEntry::Traits::IsDeclared | PrivateNameEntry::Traits::IsSetter); >+ >+ auto entry = VariableEnvironmentEntry(); >+ entry.setIsPrivateSetter(); >+ entry.setIsConst(); >+ entry.setIsCaptured(); >+ m_map.add(identifier, entry); >+ >+ auto addResult = m_rareData->m_privateNames.add(identifier, meta); >+ return addResult.isNewEntry; >+ } >+ >+ PrivateNameEntry currentEntry = findResult->value; >+ if (currentEntry.isDeclared()) { >+ if (currentEntry.isSetter() || currentEntry.isMethod() || !currentEntry.isGetter()) >+ return false; // Error: declaring a duplicate private name. >+ >+ ASSERT(currentEntry.isGetter()); >+ PrivateNameEntry meta(currentEntry.bits() | PrivateNameEntry::Traits::IsSetter); >+ m_rareData->m_privateNames.set(identifier, meta); >+ >+ auto entryIterator = m_map.find(identifier); >+ ASSERT(entryIterator != m_map.end()); >+ entryIterator->value.setIsPrivateSetter(); >+ >+ return true; >+ } >+ >+ // it was previously used, mark it as declared. >+ auto entry = VariableEnvironmentEntry(); >+ entry.setIsPrivateSetter(); >+ entry.setIsConst(); >+ entry.setIsCaptured(); >+ m_map.add(identifier, entry); >+ >+ PrivateNameEntry newEntry(currentEntry.bits() | PrivateNameEntry::Traits::IsDeclared | PrivateNameEntry::Traits::IsSetter); >+ m_rareData->m_privateNames.set(identifier, newEntry); >+ return true; >+} >+ >+bool VariableEnvironment::declarePrivateGetter(const RefPtr<UniquedStringImpl>& identifier) >+{ >+ if (!m_rareData) >+ m_rareData = WTF::makeUnique<VariableEnvironment::RareData>(); >+ >+ auto findResult = m_rareData->m_privateNames.find(identifier); >+ >+ if (findResult == m_rareData->m_privateNames.end()) { >+ PrivateNameEntry meta(PrivateNameEntry::Traits::IsDeclared | PrivateNameEntry::Traits::IsGetter); >+ >+ auto entry = VariableEnvironmentEntry(); >+ entry.setIsPrivateGetter(); >+ entry.setIsConst(); >+ entry.setIsCaptured(); >+ m_map.add(identifier, entry); >+ >+ auto addResult = m_rareData->m_privateNames.add(identifier, meta); >+ return addResult.isNewEntry; >+ } >+ >+ PrivateNameEntry currentEntry = findResult->value; >+ if (currentEntry.isDeclared()) { >+ if (currentEntry.isGetter() || currentEntry.isMethod() || !currentEntry.isSetter()) >+ return false; // Error: declaring a duplicate private name. >+ >+ ASSERT(currentEntry.isSetter()); >+ PrivateNameEntry meta(currentEntry.bits() | PrivateNameEntry::Traits::IsGetter); >+ m_rareData->m_privateNames.set(identifier, meta); >+ >+ auto entryIterator = m_map.find(identifier); >+ ASSERT(entryIterator != m_map.end()); >+ entryIterator->value.setIsPrivateGetter(); >+ >+ return true; >+ } >+ >+ // it was previously used, mark it as declared. >+ auto entry = VariableEnvironmentEntry(); >+ entry.setIsPrivateGetter(); >+ entry.setIsConst(); >+ entry.setIsCaptured(); >+ m_map.add(identifier, entry); >+ >+ PrivateNameEntry newEntry(currentEntry.bits() | PrivateNameEntry::Traits::IsDeclared | PrivateNameEntry::Traits::IsGetter); >+ m_rareData->m_privateNames.set(identifier, newEntry); >+ return true; >+} >+ > bool VariableEnvironment::declarePrivateMethod(const RefPtr<UniquedStringImpl>& identifier) > { > if (!m_rareData) >diff --git a/Source/JavaScriptCore/parser/VariableEnvironment.h b/Source/JavaScriptCore/parser/VariableEnvironment.h >index f6d358b197002d20de97f40693c33f69d557382b..a35d2f55b67f81a84561f9cacb0ae4cfd3664c70 100644 >--- a/Source/JavaScriptCore/parser/VariableEnvironment.h >+++ b/Source/JavaScriptCore/parser/VariableEnvironment.h >@@ -47,6 +47,8 @@ public: > ALWAYS_INLINE bool isSloppyModeHoistingCandidate() const { return m_bits & IsSloppyModeHoistingCandidate; } > ALWAYS_INLINE bool isPrivateField() const { return m_bits & IsPrivateField; } > ALWAYS_INLINE bool isPrivateMethod() const { return m_bits & IsPrivateMethod; } >+ ALWAYS_INLINE bool isPrivateSetter() const { return m_bits & IsPrivateSetter; } >+ ALWAYS_INLINE bool isPrivateGetter() const { return m_bits & IsPrivateGetter; } > > ALWAYS_INLINE void setIsCaptured() { m_bits |= IsCaptured; } > ALWAYS_INLINE void setIsConst() { m_bits |= IsConst; } >@@ -60,6 +62,8 @@ public: > ALWAYS_INLINE void setIsSloppyModeHoistingCandidate() { m_bits |= IsSloppyModeHoistingCandidate; } > ALWAYS_INLINE void setIsPrivateField() { m_bits |= IsPrivateField; } > ALWAYS_INLINE void setIsPrivateMethod() { m_bits |= IsPrivateMethod; } >+ ALWAYS_INLINE void setIsPrivateSetter() { m_bits |= IsPrivateSetter; } >+ ALWAYS_INLINE void setIsPrivateGetter() { m_bits |= IsPrivateGetter; } > > ALWAYS_INLINE void clearIsVar() { m_bits &= ~IsVar; } > >@@ -84,6 +88,8 @@ private: > IsSloppyModeHoistingCandidate = 1 << 9, > IsPrivateField = 1 << 10, > IsPrivateMethod = 1 << 11, >+ IsPrivateGetter = 1 << 12, >+ IsPrivateSetter = 1 << 13, > }; > uint16_t m_bits { 0 }; > }; >@@ -101,8 +107,10 @@ public: > ALWAYS_INLINE bool isUsed() const { return m_bits & IsUsed; } > ALWAYS_INLINE bool isDeclared() const { return m_bits & IsDeclared; } > ALWAYS_INLINE bool isMethod() const { return m_bits & IsMethod; } >+ ALWAYS_INLINE bool isSetter() const { return m_bits & IsSetter; } >+ ALWAYS_INLINE bool isGetter() const { return m_bits & IsGetter; } > >- bool isPrivateMethodOrAcessor() const { return isMethod(); } >+ bool isPrivateMethodOrAcessor() const { return isMethod() || isSetter() || isGetter(); } > > ALWAYS_INLINE void setIsUsed() { m_bits |= IsUsed; } > ALWAYS_INLINE void setIsDeclared() { m_bits |= IsDeclared; } >@@ -119,6 +127,8 @@ public: > IsUsed = 1 << 0, > IsDeclared = 1 << 1, > IsMethod = 1 << 2, >+ IsGetter = 1 << 3, >+ IsSetter = 1 << 4, > }; > > private: >@@ -195,6 +205,12 @@ public: > bool declarePrivateMethod(const Identifier& identifier) { return declarePrivateMethod(identifier.impl()); } > bool declarePrivateMethod(const RefPtr<UniquedStringImpl>& identifier); > >+ bool declarePrivateSetter(const Identifier& identifier) { return declarePrivateSetter(identifier.impl()); } >+ bool declarePrivateSetter(const RefPtr<UniquedStringImpl>& identifier); >+ >+ bool declarePrivateGetter(const Identifier& identifier) { return declarePrivateGetter(identifier.impl()); } >+ bool declarePrivateGetter(const RefPtr<UniquedStringImpl>& identifier); >+ > Map::AddResult declarePrivateField(const RefPtr<UniquedStringImpl>& identifier) > { > auto& meta = getOrAddPrivateName(identifier.get()); >diff --git a/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp b/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp >index d8744794bbb1ad3031fb48abc64ee55033c6bab5..2defea37b36f1dd3501e18cc0fe229b3a4e76070 100644 >--- a/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp >+++ b/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp >@@ -336,7 +336,7 @@ JSObject* createRedefinedPrivateNameError(JSGlobalObject* globalObject) > > JSObject* createPrivateMethodAccessError(JSGlobalObject* globalObject) > { >- return createTypeError(globalObject, makeString("Cannot access private method"_s), defaultSourceAppender, TypeNothing); >+ return createTypeError(globalObject, makeString("Cannot access private method or acessor"_s), defaultSourceAppender, TypeNothing); > } > > JSObject* createReinstallPrivateMethodError(JSGlobalObject* globalObject) >diff --git a/JSTests/ChangeLog b/JSTests/ChangeLog >index 441ff14275339587d1401f2189043f7f3a3a9bf7..157a449044f876c0c9791c3cded6bee5a9223654 100644 >--- a/JSTests/ChangeLog >+++ b/JSTests/ChangeLog >@@ -1,3 +1,20 @@ >+2021-02-10 Caio Lima <ticaiolima@gmail.com> >+ >+ [ESNext] Implement private accessors >+ https://bugs.webkit.org/show_bug.cgi?id=194435 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * stress/private-accesor-duplicate-name-early-errors.js: Added. >+ * stress/private-getter-brand-check.js: Added. >+ * stress/private-getter-inner-class.js: Added. >+ * stress/private-members-get-and-set.js: Added. >+ * stress/private-names-available-on-direct-eval.js: >+ * stress/private-names-available-on-eval-during-field-initialization.js: Copied from JSTests/stress/private-names-available-on-direct-eval.js. >+ * stress/private-setter-brand-check.js: Added. >+ * stress/private-setter-inner-class.js: Added. >+ * test262/config.yaml: >+ > 2021-02-09 Yusuke Suzuki <ysuzuki@apple.com> > > [JSC] C++ iteration should support fast iterator protocol >diff --git a/JSTests/stress/private-accesor-duplicate-name-early-errors.js b/JSTests/stress/private-accesor-duplicate-name-early-errors.js >new file mode 100644 >index 0000000000000000000000000000000000000000..3ca3bfb64f2c3c62c8aa11040b0c090b0f9af9b7 >--- /dev/null >+++ b/JSTests/stress/private-accesor-duplicate-name-early-errors.js >@@ -0,0 +1,92 @@ >+//@ requireOptions("--usePrivateMethods=true") >+ >+let assert = { >+ sameValue: function (lhs, rhs) { >+ if (lhs !== rhs) >+ throw new Error("Expected: " + lhs + " bug got: " + rhs); >+ }, >+ >+ throws: function (expectedError, op) { >+ try { >+ op(); >+ } catch(e) { >+ if (!(e instanceof expectedError)) >+ throw new Error("Expected to throw: " + expectedError + " but threw: " + e); >+ } >+ } >+} >+ >+assert.throws(SyntaxError, function() { >+ eval(` >+ class C { >+ set #m(v) { this._v = v; } >+ set #m(u) {} >+ } >+ `); >+}); >+ >+assert.throws(SyntaxError, function() { >+ eval(` >+ class C { >+ #m; >+ set #m(v) { this._v = v; } >+ } >+ `); >+}); >+ >+assert.throws(SyntaxError, function() { >+ eval(` >+ class C { >+ set #m(v) { this._v = v; } >+ #m; >+ } >+ `); >+}); >+ >+assert.throws(SyntaxError, function() { >+ eval(` >+ class C { >+ set #m(v) { this._v = v; } >+ #m() {} >+ } >+ `); >+}); >+ >+assert.throws(SyntaxError, function() { >+ eval(` >+ class C { >+ #m() {} >+ get #m() {} >+ } >+ `); >+}); >+ >+assert.throws(SyntaxError, function() { >+ eval(` >+ class C { >+ get #m() {} >+ get #m() {} >+ } >+ `); >+}); >+ >+assert.throws(SyntaxError, function() { >+ eval(` >+ class C { >+ get #m() {} >+ set #m() {} >+ #m; >+ } >+ `); >+}); >+ >+assert.throws(SyntaxError, function() { >+ eval(` >+ class C { >+ get #m() {} >+ set #m() {} >+ #m() {} >+ } >+ `); >+}); >+ >diff --git a/JSTests/stress/private-getter-brand-check.js b/JSTests/stress/private-getter-brand-check.js >new file mode 100644 >index 0000000000000000000000000000000000000000..cd415db8ae6bed049005b7395efbae85501921fb >--- /dev/null >+++ b/JSTests/stress/private-getter-brand-check.js >@@ -0,0 +1,91 @@ >+//@ requireOptions("--usePrivateMethods=true") >+ >+let assert = { >+ sameValue: function (lhs, rhs) { >+ if (lhs !== rhs) >+ throw new Error("Expected: " + lhs + " bug got: " + rhs); >+ }, >+ >+ throws: function (expectedError, op) { >+ try { >+ op(); >+ } catch(e) { >+ if (!(e instanceof expectedError)) >+ throw new Error("Expected to throw: " + expectedError + " but threw: " + e); >+ } >+ } >+}; >+ >+(function () { >+ let createAndInstantiateClass = function () { >+ class C { >+ get #m() { return 'test'; } >+ >+ access(o) { >+ return o.#m; >+ } >+ } >+ >+ return new C(); >+ } >+ >+ let c1 = createAndInstantiateClass(); >+ let c2 = createAndInstantiateClass(); >+ >+ assert.sameValue(c1.access(c1), 'test'); >+ assert.sameValue(c2.access(c2), 'test'); >+ >+ assert.throws(TypeError, function() { >+ c1.access(c2); >+ }); >+ >+ assert.throws(TypeError, function() { >+ c2.access(c1); >+ }); >+})(); >+ >+(function () { >+ class S { >+ get #m() { return 'super class'; } >+ >+ superAccess() { return this.#m; } >+ } >+ >+ class C extends S { >+ get #m() { return 'subclass'; } >+ >+ access() { >+ return this.#m; >+ } >+ } >+ >+ let c = new C(); >+ >+ assert.sameValue(c.access(), 'subclass'); >+ assert.sameValue(c.superAccess(), 'super class'); >+ >+ let s = new S(); >+ assert.sameValue(s.superAccess(), 'super class'); >+ assert.throws(TypeError, function() { >+ c.access.call(s); >+ }); >+})(); >+ >+(function () { >+ class C { >+ get #m() { return 'test'; } >+ >+ access(o) { >+ return o.#m; >+ } >+ } >+ >+ let c = new C(); >+ assert.sameValue(c.access(c), 'test'); >+ >+ let o = {}; >+ assert.throws(TypeError, function() { >+ c.access(o); >+ }); >+})(); >+ >diff --git a/JSTests/stress/private-getter-inner-class.js b/JSTests/stress/private-getter-inner-class.js >new file mode 100644 >index 0000000000000000000000000000000000000000..b6a98b94ac2d89a7d49a8c9a2a2835cd25fe6b85 >--- /dev/null >+++ b/JSTests/stress/private-getter-inner-class.js >@@ -0,0 +1,132 @@ >+//@ requireOptions("--usePrivateMethods=true") >+ >+let assert = { >+ sameValue: function (lhs, rhs) { >+ if (lhs !== rhs) >+ throw new Error("Expected: " + lhs + " bug got: " + rhs); >+ }, >+ >+ throws: function (expectedError, op) { >+ try { >+ op(); >+ } catch(e) { >+ if (!(e instanceof expectedError)) >+ throw new Error("Expected to throw: " + expectedError + " but threw: " + e); >+ } >+ } >+}; >+ >+(function () { >+ class C { >+ get #m() { return 'test'; } >+ >+ B = class { >+ method(o) { >+ return o.#m; >+ } >+ } >+ } >+ >+ let c = new C(); >+ let innerB = new c.B(); >+ assert.sameValue(innerB.method(c), 'test'); >+})(); >+ >+(function () { >+ class C { >+ get #m() { return 'outer class'; } >+ >+ method() { return this.#m; } >+ >+ B = class { >+ method(o) { >+ return o.#m; >+ } >+ >+ #m = 'test'; >+ } >+ } >+ >+ let c = new C(); >+ let innerB = new c.B(); >+ assert.sameValue(innerB.method(innerB), 'test'); >+ assert.sameValue(c.method(), 'outer class'); >+ assert.throws(TypeError, function() { >+ innerB.method(c); >+ }); >+})(); >+ >+(function () { >+ class C { >+ get #m() { return 'outer class'; } >+ >+ method() { return this.#m; } >+ >+ B = class { >+ method(o) { >+ return o.#m; >+ } >+ >+ get #m() { return 'test'; } >+ } >+ } >+ >+ let c = new C(); >+ let innerB = new c.B(); >+ assert.sameValue(innerB.method(innerB), 'test'); >+ assert.sameValue(c.method(), 'outer class'); >+ assert.throws(TypeError, function() { >+ innerB.method(c); >+ }); >+})(); >+ >+(function () { >+ class C { >+ get #m() { throw new Error('Should never execute'); } >+ >+ B = class { >+ method(o) { >+ return o.#m(); >+ } >+ >+ #m() { return 'test'; } >+ } >+ } >+ >+ let c = new C(); >+ let innerB = new c.B(); >+ assert.sameValue(innerB.method(innerB), 'test'); >+ assert.throws(TypeError, function() { >+ innerB.method(c); >+ }); >+})(); >+ >+(function () { >+ class C { >+ get #m() { return 'outer class'; } >+ >+ method() { return this.#m; } >+ >+ B = class { >+ method(o) { >+ return o.#m; >+ } >+ >+ set #m(v) { this._v = v; } >+ } >+ } >+ >+ let c = new C(); >+ let innerB = new c.B(); >+ >+ assert.throws(TypeError, function() { >+ innerB.method(innerB); >+ }); >+ >+ assert.sameValue(c.method(), 'outer class'); >+ >+ assert.throws(TypeError, function() { >+ innerB.method(c); >+ }); >+})(); >+ >diff --git a/JSTests/stress/private-members-get-and-set.js b/JSTests/stress/private-members-get-and-set.js >new file mode 100644 >index 0000000000000000000000000000000000000000..101f659818eefbc8ba3269b38e74a9640a55db00 >--- /dev/null >+++ b/JSTests/stress/private-members-get-and-set.js >@@ -0,0 +1,63 @@ >+//@ requireOptions("--usePrivateMethods=true") >+ >+let assert = { >+ sameValue: function (lhs, rhs) { >+ if (lhs !== rhs) >+ throw new Error("Expected: " + lhs + " bug got: " + rhs); >+ }, >+ >+ throws: function (expectedError, op) { >+ try { >+ op(); >+ } catch(e) { >+ if (!(e instanceof expectedError)) >+ throw new Error("Expected to throw: " + expectedError + " but threw: " + e); >+ } >+ } >+}; >+ >+(function () { >+ class C { >+ set #m(v) { this._v = v; } >+ >+ accessPrivateMember() { >+ return this.#m; >+ } >+ } >+ >+ let c = new C(); >+ assert.throws(TypeError, function () { >+ c.accessPrivateMember(); >+ }); >+})(); >+ >+(function () { >+ class C { >+ get #m() { return 'test'; } >+ >+ accessPrivateMember(v) { >+ this.#m = v; >+ } >+ } >+ >+ let c = new C(); >+ assert.throws(TypeError, function () { >+ c.accessPrivateMember('test'); >+ }); >+})(); >+ >+(function () { >+ class C { >+ #m() { return 'test'; } >+ >+ accessPrivateMember(v) { >+ this.#m = v; >+ } >+ } >+ >+ let c = new C(); >+ assert.throws(TypeError, function () { >+ c.accessPrivateMember('test'); >+ }); >+})(); >+ >diff --git a/JSTests/stress/private-names-available-on-direct-eval.js b/JSTests/stress/private-names-available-on-direct-eval.js >index 64ce0e0af80578e89716a1672edc177305b064f3..3ea9c7d43c3dac15e0f8d0933a925fe8165d07dc 100644 >--- a/JSTests/stress/private-names-available-on-direct-eval.js >+++ b/JSTests/stress/private-names-available-on-direct-eval.js >@@ -21,3 +21,36 @@ let assert = { > assert.sameValue(c.callMethodFromEval(), 'test'); > })(); > >+(function () { >+ class C { >+ get #m() { >+ return 'test'; >+ } >+ >+ callGetterFromEval() { >+ let self = this; >+ return eval('self.#m'); >+ } >+ } >+ >+ let c = new C(); >+ assert.sameValue(c.callGetterFromEval(), 'test'); >+})(); >+ >+(function () { >+ class C { >+ set #m(v) { >+ this._v = v; >+ } >+ >+ callSetterFromEval(v) { >+ let self = this; >+ eval('self.#m = v'); >+ } >+ } >+ >+ let c = new C(); >+ c.callSetterFromEval('test') >+ assert.sameValue(c._v, 'test'); >+})(); >+ >diff --git a/JSTests/stress/private-names-available-on-eval-during-field-initialization.js b/JSTests/stress/private-names-available-on-eval-during-field-initialization.js >new file mode 100644 >index 0000000000000000000000000000000000000000..177f0dddb19fca8fc2e26c4ea8cad4bc784e3492 >--- /dev/null >+++ b/JSTests/stress/private-names-available-on-eval-during-field-initialization.js >@@ -0,0 +1,31 @@ >+//@ requireOptions("--usePrivateMethods=true") >+ >+let assert = { >+ sameValue: function (lhs, rhs) { >+ if (lhs !== rhs) >+ throw new Error("Expected: " + rhs + " bug got: " + lhs); >+ } >+}; >+ >+(function () { >+ class C { >+ #m() { return 'test'; } >+ >+ field = eval('this.#m()'); >+ } >+ >+ let c = new C(); >+ assert.sameValue(c.field, 'test'); >+})(); >+ >+(function () { >+ class C { >+ get #m() { return 'test'; } >+ >+ field = eval('this.#m'); >+ } >+ >+ let c = new C(); >+ assert.sameValue(c.field, 'test'); >+})(); >+ >diff --git a/JSTests/stress/private-setter-brand-check.js b/JSTests/stress/private-setter-brand-check.js >new file mode 100644 >index 0000000000000000000000000000000000000000..6f23f6bf8e421ec1130391863598a4b447b598c4 >--- /dev/null >+++ b/JSTests/stress/private-setter-brand-check.js >@@ -0,0 +1,100 @@ >+//@ requireOptions("--usePrivateMethods=true") >+ >+let assert = { >+ sameValue: function (lhs, rhs) { >+ if (lhs !== rhs) >+ throw new Error("Expected: " + lhs + " bug got: " + rhs); >+ }, >+ >+ throws: function (expectedError, op) { >+ try { >+ op(); >+ } catch(e) { >+ if (!(e instanceof expectedError)) >+ throw new Error("Expected to throw: " + expectedError + " but threw: " + e); >+ } >+ } >+}; >+ >+(function () { >+ let createAndInstantiateClass = function () { >+ class C { >+ set #m(v) { this._v = v; } >+ >+ access(o, v) { >+ o.#m = v; >+ } >+ } >+ >+ let c = new C(); >+ return c; >+ }; >+ >+ let c1 = createAndInstantiateClass(); >+ let c2 = createAndInstantiateClass(); >+ >+ c1.access(c1, 'test'); >+ assert.sameValue(c1._v, 'test'); >+ c2.access(c2, 'test'); >+ assert.sameValue(c2._v, 'test'); >+ >+ assert.throws(TypeError, function() { >+ c1.access(c2, 'foo'); >+ }); >+ >+ assert.throws(TypeError, function() { >+ c2.access(c1, 'foo'); >+ }); >+})(); >+ >+(function () { >+ class S { >+ set #m(v) { this._v = v } >+ >+ superAccess(v) { this.#m = v; } >+ } >+ >+ class C extends S { >+ set #m(v) { this._u = v; } >+ >+ access(v) { >+ return this.#m = v; >+ } >+ } >+ >+ let c = new C(); >+ >+ c.access('test'); >+ assert.sameValue(c._u, 'test'); >+ >+ c.superAccess('super class'); >+ assert.sameValue(c._v, 'super class'); >+ >+ let s = new S(); >+ s.superAccess('super class') >+ assert.sameValue(s._v, 'super class'); >+ >+ assert.throws(TypeError, function() { >+ c.access.call(s, 'foo'); >+ }); >+})(); >+ >+(function () { >+ class C { >+ set #m(v) { this._v = v; } >+ >+ access(o, v) { >+ return o.#m = v; >+ } >+ } >+ >+ let c = new C(); >+ c.access(c, 'test'); >+ assert.sameValue(c._v, 'test'); >+ >+ let o = {}; >+ assert.throws(TypeError, function() { >+ c.access(o, 'foo'); >+ }); >+})(); >+ >diff --git a/JSTests/stress/private-setter-inner-class.js b/JSTests/stress/private-setter-inner-class.js >new file mode 100644 >index 0000000000000000000000000000000000000000..26db89d73f817ebaba5b1a024217ce295e797a3c >--- /dev/null >+++ b/JSTests/stress/private-setter-inner-class.js >@@ -0,0 +1,155 @@ >+//@ requireOptions("--usePrivateMethods=true") >+ >+let assert = { >+ sameValue: function (lhs, rhs) { >+ if (lhs !== rhs) >+ throw new Error("Expected: " + lhs + " bug got: " + rhs); >+ }, >+ >+ throws: function (expectedError, op) { >+ try { >+ op(); >+ } catch(e) { >+ if (!(e instanceof expectedError)) >+ throw new Error("Expected to throw: " + expectedError + " but threw: " + e); >+ } >+ } >+}; >+ >+(function () { >+ class C { >+ set #m(v) { this._v = v; } >+ >+ B = class { >+ method(o, v) { >+ o.#m = v; >+ } >+ } >+ } >+ >+ let c = new C(); >+ let innerB = new c.B(); >+ innerB.method(c, 'test'); >+ assert.sameValue(c._v, 'test'); >+})(); >+ >+(function () { >+ class C { >+ set #m(v) { this._v = v; } >+ >+ method(v) { this.#m = v; } >+ >+ B = class { >+ method(o, v) { >+ o.#m = v; >+ } >+ >+ get m() { return this.#m; } >+ >+ #m; >+ } >+ } >+ >+ let c = new C(); >+ let innerB = new c.B(); >+ >+ innerB.method(innerB, 'test'); >+ assert.sameValue(innerB.m, 'test'); >+ >+ c.method('outer class'); >+ assert.sameValue(c._v, 'outer class'); >+ >+ assert.throws(TypeError, function() { >+ innerB.method(c, 'foo'); >+ }); >+})(); >+ >+(function () { >+ class C { >+ set #m(v) { this._v = v; } >+ >+ method(v) { this.#m = v; } >+ >+ B = class { >+ method(o, v) { >+ o.#m = v; >+ } >+ >+ get #m() { return 'test'; } >+ } >+ } >+ >+ let c = new C(); >+ let innerB = new c.B(); >+ >+ assert.throws(TypeError, function() { >+ innerB.method(innerB); >+ }); >+ >+ c.method('outer class'); >+ assert.sameValue(c._v, 'outer class'); >+ >+ assert.throws(TypeError, function() { >+ innerB.method(c); >+ }); >+})(); >+ >+(function () { >+ class C { >+ set #m(v) { this._v = v; } >+ >+ method(v) { this.#m = v; } >+ >+ B = class { >+ method(o, v) { >+ o.#m = v; >+ } >+ >+ #m() { return 'test'; } >+ } >+ } >+ >+ let c = new C(); >+ let innerB = new c.B(); >+ >+ assert.throws(TypeError, function() { >+ innerB.method(innerB, 'foo'); >+ }); >+ >+ c.method('outer class'); >+ assert.sameValue(c._v, 'outer class'); >+ >+ assert.throws(TypeError, function() { >+ innerB.method(c); >+ }); >+})(); >+ >+(function () { >+ class C { >+ set #m(v) { this._v = v; } >+ >+ method(v) { this.#m = v; } >+ >+ B = class { >+ method(o, v) { >+ o.#m = v; >+ } >+ >+ set #m(v) { this._v = v; } >+ } >+ } >+ >+ let c = new C(); >+ let innerB = new c.B(); >+ >+ innerB.method(innerB, 'test262'); >+ assert.sameValue(innerB._v, 'test262'); >+ >+ c.method('outer class'); >+ assert.sameValue(c._v, 'outer class'); >+ >+ assert.throws(TypeError, function() { >+ innerB.method(c, 'foo'); >+ }); >+})(); >+ >diff --git a/JSTests/test262/config.yaml b/JSTests/test262/config.yaml >index 515da3cdb790ea7d736f86ac73f31867dbb9ee17..4116676dd1ea864315864095e5461d190d6e5ba1 100644 >--- a/JSTests/test262/config.yaml >+++ b/JSTests/test262/config.yaml >@@ -4,6 +4,7 @@ flags: > WeakRef: useWeakRefs > FinalizationRegistry: useWeakRefs > class-fields-private: usePrivateClassFields >+ class-methods-private: usePrivateMethods > class-static-fields-public: usePublicStaticClassFields > class-static-fields-private: usePrivateStaticClassFields > Intl.DateTimeFormat-dayPeriod: useIntlDateTimeFormatDayPeriod >@@ -19,7 +20,6 @@ skip: > - regexp-lookbehind > - legacy-regexp > >- - class-methods-private > - class-static-methods-private > - cleanupSome > - host-gc-required
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 194435
:
370695
|
373127
|
391861
|
391866
|
419845
|
419866
|
419911
|
419994
|
420052
|
420127
|
420350