WebKit Bugzilla
Attachment 360454 Details for
Bug 174212
: [JSC] Add support for instance class fields
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Class fields, parser + bytecode + test changes
class-fields-parser-bytecode.diff (text/plain), 189.64 KB, created by
Xan Lopez
on 2019-01-29 02:10:25 PST
(
hide
)
Description:
Class fields, parser + bytecode + test changes
Filename:
MIME Type:
Creator:
Xan Lopez
Created:
2019-01-29 02:10:25 PST
Size:
189.64 KB
patch
obsolete
>From 2ad3e6e1af5fe8c8daeadd7eb33d6c6527ef20b8 Mon Sep 17 00:00:00 2001 >From: =?UTF-8?q?Xan=20L=C3=B3pez?= <xan@igalia.com> >Date: Tue, 29 Jan 2019 10:53:11 +0100 >Subject: [PATCH] [JSC] Add support for class fields > https://bugs.webkit.org/show_bug.cgi?id=174212 > >Reviewed by NOBODY (OOPS!). > >JSTests: > >New syntax invalidates some test expectations: > >"async <linefeed> MethodDefinition" is no longer an unexpected "async" >token. It is now an instance field named "async" with no initializer, >and an automatic semicolon, followed by MethodDefinition. > >"get|set GeneratorMethodDefinition"'s error message has changed, due to "get" >being valid class field names. > >Many class-syntax tests relating to automatic semicolon insertion are >no longer valid, as a line containing nothing but an identifier is now >a valid class element. > >* stress/async-await-syntax.js: >* stress/generator-syntax.js: >* stress/method-name.js: > >Source/JavaScriptCore: > >This provides a basic implementation of public/private class instance >fields (https://tc39.github.io/proposal-class-fields/). > >Overview: > >This class field implementation relies on a synthetic function to >add fields to the class instance during construction. To do this, >a private name #instanceFieldInitializer is conditionally added to >the class during the initial evaluation if any class fields are >present. The field is always loaded during class construction or >super calls, but is not invoked if the value is undefined. This is >similar to what the v8 engine does. > >In order to generate this synthetic function, the locations of >each class field is recorded and stored in a >WTF::Vector<JSTextPosition>. These are used to reparse the class >body, and produce an AST specifically for initializing the class >body. Recording each text position allows the reparsing step to >skip over long method definitions, whitespace or comments, at the >cost of some memory. > >--- > >The spec includes a fair amount of early syntax errors for private >fields. Most of them require simple changes to the parser >(although a new EvalContextType is needed to handle the use of >fields inside eval calls), but a special type of error requires >larger changes: it is an early syntax error to use a private name >without declaring it, but a runtime error to use it before it is >declared. To accomplish this we extend VariableEnvironment to >track both uses and declarations of private names. The parser adds >the names to the set as it finds them, and when it finishes >parsing a class makes sure that either one of these holds: a) >there are no private names used without being declared b) there is >a parent class scope, so the task of balancing the set can >continue there. > >--- > >Computed property names are valid class fields -- the computed >expression is, as required by the proposal, evaluated during the >initial evaluation of the class definition. Because of this, it's >necessary to store the result of this computation for use within >the initializer function. This patch uses a hack to add these as >private variables, in such a way that it's easy to access them, >and theoretically they are not observable to JS code: > For each computed class field name, `[ {expr} ] = {initializer}`: > > <during class evaluation> > 1) fieldName = emitNode({expr}) > 2) fieldName = emitToPropertyKey(fieldName) > 3) classScope[numComputedNames++] = fieldName > > <during class field initialization> > 1) fieldName = emitGetFromScope(classScope, numComputedNames++) > 2) value = emitNode({initializer}) > 3) instance[fieldName] = value > >Although this is somewhat hacky, and should be replaced with >something better, it works and is fairly straight forward to >understand. > >Finally this introduces a new opcode (op_to_property_key). There >was no need for it before because ordinarily, the conversion >happened during get/put operations, but in this case it's >necessary to happen during class field evaluation, but storage >occurs later on in the initializer function. > >--- > >Private fields introduce 3 new opcodes: `op_add|get|put_private_field`. >This option was selected over modifying get|put_by_val for the following >reason: > >1) avoid re-ifying Symbols --- private field names are just SymbolImpls > (Caveat: this requires looking them up in scope each time > they're used --- though DFG can hopefully identify when they're > reused and only emit the load once, or replace it with a constant > in some cases). >2) avoid creating a different flavour of SymbolImpl with the specific > requirements of the proposed private fields (which behave slightly > differently from the existing PrivateName). >3) avoid adding additional slow cases to get/put operations to the common > cases, avoid adding new GetPutInfo complexity, etc > >* builtins/BuiltinNames.h: >* bytecode/BytecodeList.rb: >* bytecode/BytecodeUseDef.h: >(JSC::computeUsesForBytecodeOffset): >(JSC::computeDefsForBytecodeOffset): >* bytecode/CodeBlock.cpp: >(JSC::CodeBlock::propagateTransitions): >(JSC::CodeBlock::finalizeLLIntInlineCaches): >* bytecode/ExecutableInfo.h: >* bytecode/UnlinkedFunctionExecutable.cpp: >(JSC::generateUnlinkedFunctionCodeBlock): >* bytecode/UnlinkedFunctionExecutable.h: >* bytecompiler/BytecodeGenerator.cpp: >(JSC::BytecodeGenerator::BytecodeGenerator): >(JSC::BytecodeGenerator::instantiateLexicalVariables): >(JSC::BytecodeGenerator::popLexicalScopeInternal): >(JSC::BytecodeGenerator::emitPrivateFieldAdd): >(JSC::BytecodeGenerator::emitPrivateFieldGet): >(JSC::BytecodeGenerator::emitPrivateFieldSet): >(JSC::BytecodeGenerator::emitInstanceFieldInitializationIfNeeded): >(JSC::BytecodeGenerator::emitNewInstanceFieldInitializerFunction): >(JSC::BytecodeGenerator::shouldEmitSetFunctionName): >(JSC::BytecodeGenerator::emitSetFunctionName): >(JSC::BytecodeGenerator::emitSetFunctionNameIfNeeded): >(JSC::BytecodeGenerator::emitToPropertyKey): >(JSC::BytecodeGenerator::emitLoadDerivedConstructor): >* bytecompiler/BytecodeGenerator.h: >(JSC::BytecodeGenerator::emitDefineClassElements): >* bytecompiler/NodesCodegen.cpp: >(JSC::PropertyListNode::emitBytecode): >(JSC::PropertyListNode::emitSaveComputedFieldName): >(JSC::DotAccessorNode::emitBytecode): >(JSC::BaseDotNode::emitGetPropertyValue): >(JSC::BaseDotNode::emitPutProperty): >(JSC::FunctionCallValueNode::emitBytecode): >(JSC::AssignDotNode::emitBytecode): >(JSC::ReadModifyDotNode::emitBytecode): >(JSC::DefineFieldNode::emitBytecode): >(JSC::ClassExprNode::emitBytecode): >* dfg/DFGCapabilities.cpp: >(JSC::DFG::capabilityLevel): >* interpreter/Interpreter.cpp: >(JSC::eval): >* jit/JIT.cpp: >(JSC::JIT::privateCompileMainPass): >(JSC::JIT::privateCompileSlowCases): >* jit/JIT.h: >* jit/JITOpcodes.cpp: >(JSC::JIT::emit_op_add_private_field): >(JSC::JIT::emit_op_get_private_field): >(JSC::JIT::emit_op_put_private_field): >* llint/LowLevelInterpreter32_64.asm: >* llint/LowLevelInterpreter64.asm: >* parser/ASTBuilder.h: >(JSC::ASTBuilder::createDotAccess): >(JSC::ASTBuilder::createDefineField): >(JSC::ASTBuilder::createProperty): >(JSC::ASTBuilder::isPrivateLocation): >(JSC::ASTBuilder::makeAssignNode): >* parser/Lexer.cpp: >(JSC::Lexer<T>::Lexer): >(JSC::Lexer<LChar>::parseIdentifier): >(JSC::Lexer<UChar>::parseIdentifier): >(JSC::Lexer<CharacterType>::parseIdentifierSlowCase): >(JSC::Lexer<T>::lex): >* parser/NodeConstructors.h: >(JSC::PropertyNode::PropertyNode): >(JSC::BaseDotNode::BaseDotNode): >(JSC::DotAccessorNode::DotAccessorNode): >(JSC::AssignDotNode::AssignDotNode): >(JSC::ReadModifyDotNode::ReadModifyDotNode): >(JSC::DefineFieldNode::DefineFieldNode): >(JSC::ClassExprNode::ClassExprNode): >* parser/Nodes.cpp: >(JSC::PropertyListNode::shouldCreateLexicalScopeForClass): >* parser/Nodes.h: >(JSC::ExpressionNode::isPrivateLocation const): >(JSC::StatementNode::isDefineFieldNode const): >(JSC::BaseDotNode::base const): >(JSC::BaseDotNode::identifier const): >(JSC::BaseDotNode::type const): >(JSC::BaseDotNode::isPrivateName const): >* parser/Parser.cpp: >(JSC::Parser<LexerType>::parseInner): >(JSC::Parser<LexerType>::parseVariableDeclarationList): >(JSC::Parser<LexerType>::parseDestructuringPattern): >(JSC::Parser<LexerType>::parseFunctionBody): >(JSC::stringArticleForFunctionMode): >(JSC::stringForFunctionMode): >(JSC::Parser<LexerType>::parseClass): >(JSC::Parser<LexerType>::parseInstanceFieldInitializerSourceElements): >(JSC::Parser<LexerType>::parsePrimaryExpression): >(JSC::Parser<LexerType>::addPrivateNameUsedIfNeeded): >(JSC::Parser<LexerType>::parseMemberExpression): >(JSC::Parser<LexerType>::parseUnaryExpression): >(JSC::Parser<LexerType>::printUnexpectedTokenText): >* parser/Parser.h: >(JSC::Scope::Scope): >(JSC::Scope::setSourceParseMode): >(JSC::Scope::isPrivateNameScope const): >(JSC::Scope::setIsPrivateNameScope): >(JSC::Scope::hasPrivateName): >(JSC::Scope::copyPrivateNameUses): >(JSC::Scope::hasUsedButUndeclaredPrivateNames const): >(JSC::Scope::usePrivateName): >(JSC::Scope::declarePrivateName): >(JSC::Scope::markLastUsedVariablesSetAsCaptured): >(JSC::Parser::currentPrivateNameScope): >(JSC::Parser::copyPrivateNameUsesToOuterScope): >(JSC::Parser::matchAndUpdate): >(JSC::Parser<LexerType>::parse): >(JSC::parse): >* parser/ParserModes.h: >(JSC::isFunctionParseMode): >* parser/ParserTokens.h: >* parser/SyntaxChecker.h: >(JSC::SyntaxChecker::createDotAccess): >(JSC::SyntaxChecker::createProperty): >(JSC::SyntaxChecker::createDefineField): >(JSC::SyntaxChecker::operatorStackPop): >* parser/VariableEnvironment.cpp: >(JSC::VariableEnvironment::operator=): >(JSC::VariableEnvironment::swap): >(JSC::VariableEnvironment::markPrivateNameAsDeclared): >(JSC::VariableEnvironment::declarePrivateName): >(JSC::VariableEnvironment::usePrivateName): >* parser/VariableEnvironment.h: >(JSC::PrivateNameEntry::PrivateNameEntry): >(JSC::PrivateNameEntry::isUsed const): >(JSC::PrivateNameEntry::isDeclared const): >(JSC::PrivateNameEntry::setIsUsed): >(JSC::PrivateNameEntry::setIsDeclared): >(JSC::PrivateNameEntry::bits const): >(JSC::PrivateNameEntry::operator== const): >(JSC::VariableEnvironment::VariableEnvironment): >(JSC::VariableEnvironment::size const): >(JSC::VariableEnvironment::declarePrivateName): >(JSC::VariableEnvironment::usePrivateName): >(JSC::VariableEnvironment::privateNames const): >(JSC::VariableEnvironment::privateNamesSize const): >(JSC::VariableEnvironment::hasPrivateName): >(JSC::VariableEnvironment::copyPrivateNames const): >(JSC::VariableEnvironment::copyPrivateNameUses const): >(JSC::VariableEnvironment::VariableEnvironmentRareData::VariableEnvironmentRareData): >(JSC::VariableEnvironment::getOrAddPrivateName): >* runtime/CodeCache.h: >(JSC::generateUnlinkedCodeBlockImpl): >* runtime/CommonIdentifiers.cpp: >(JSC::CommonIdentifiers::CommonIdentifiers): >* runtime/CommonIdentifiers.h: >* runtime/CommonSlowPaths.cpp: >(JSC::SLOW_PATH_DECL): >* runtime/CommonSlowPaths.h: >* runtime/FunctionPrototype.cpp: >(JSC::functionProtoFuncToString): >* runtime/Identifier.h: >(JSC::StringRepHash::hash): >* runtime/JSCJSValue.h: >* runtime/JSCJSValueInlines.h: >(JSC::JSValue::toPropertyKey const): >* runtime/JSFunction.cpp: >(JSC::JSFunction::callerGetter): >* runtime/JSScope.cpp: >(JSC::JSScope::collectClosureVariablesUnderTDZ): >(JSC::JSScope::getPrivateSymbol): >* runtime/JSScope.h: >* runtime/JSSymbolTableObject.h: >(JSC::JSSymbolTableObject::getPrivateSymbol): >(JSC::JSSymbolTableObject::setSymbolTable): >* runtime/SymbolTable.cpp: >(JSC::SymbolTable::cloneScopePart): >* runtime/SymbolTable.h: > >LayoutTests: > >New syntax invalidates some test expectations: > >"async <linefeed> MethodDefinition" is no longer an unexpected "async" >token. It is now an instance field named "async" with no initializer, >and an automatic semicolon, followed by MethodDefinition. > >"get|set GeneratorMethodDefinition"'s error message has changed, due to "get" >being valid class field names. > >Many class-syntax tests relating to automatic semicolon insertion are >no longer valid, as a line containing nothing but an identifier is now >a valid class element. > >* js/class-syntax-semicolon-expected.txt: >* js/script-tests/class-syntax-semicolon.js: >--- > JSTests/ChangeLog | 24 ++ > JSTests/stress/async-await-syntax.js | 15 +- > JSTests/stress/generator-syntax.js | 4 +- > JSTests/stress/method-name.js | 2 +- > JSTests/test262/config.yaml | 4 + > LayoutTests/ChangeLog | 23 ++ > .../js/class-syntax-semicolon-expected.txt | 8 - > .../js/script-tests/class-syntax-semicolon.js | 9 - > Source/JavaScriptCore/ChangeLog | 265 ++++++++++++++++++ > Source/JavaScriptCore/builtins/BuiltinNames.h | 1 + > .../JavaScriptCore/bytecode/BytecodeList.rb | 44 +++ > .../JavaScriptCore/bytecode/BytecodeUseDef.h | 9 + > Source/JavaScriptCore/bytecode/CodeBlock.cpp | 45 +++ > .../JavaScriptCore/bytecode/ExecutableInfo.h | 2 +- > .../bytecode/UnlinkedFunctionExecutable.cpp | 5 +- > .../bytecode/UnlinkedFunctionExecutable.h | 7 + > .../bytecompiler/BytecodeGenerator.cpp | 128 ++++++++- > .../bytecompiler/BytecodeGenerator.h | 15 +- > .../bytecompiler/NodesCodegen.cpp | 188 ++++++++++--- > Source/JavaScriptCore/dfg/DFGCapabilities.cpp | 4 + > .../interpreter/Interpreter.cpp | 4 +- > Source/JavaScriptCore/jit/JIT.cpp | 6 + > Source/JavaScriptCore/jit/JIT.h | 3 + > Source/JavaScriptCore/jit/JITOpcodes.cpp | 13 + > .../llint/LowLevelInterpreter32_64.asm | 84 ++++++ > .../llint/LowLevelInterpreter64.asm | 85 ++++++ > Source/JavaScriptCore/parser/ASTBuilder.h | 19 +- > Source/JavaScriptCore/parser/Lexer.cpp | 73 +++-- > .../JavaScriptCore/parser/NodeConstructors.h | 47 +++- > Source/JavaScriptCore/parser/Nodes.cpp | 10 + > Source/JavaScriptCore/parser/Nodes.h | 87 ++++-- > Source/JavaScriptCore/parser/Parser.cpp | 186 +++++++++++- > Source/JavaScriptCore/parser/Parser.h | 124 +++++++- > Source/JavaScriptCore/parser/ParserModes.h | 4 +- > Source/JavaScriptCore/parser/ParserTokens.h | 1 + > Source/JavaScriptCore/parser/SyntaxChecker.h | 16 +- > .../parser/VariableEnvironment.cpp | 59 ++++ > .../parser/VariableEnvironment.h | 135 ++++++++- > Source/JavaScriptCore/runtime/CodeCache.h | 2 +- > .../runtime/CommonIdentifiers.cpp | 2 + > .../runtime/CommonIdentifiers.h | 7 + > .../runtime/CommonSlowPaths.cpp | 197 +++++++++++++ > .../JavaScriptCore/runtime/CommonSlowPaths.h | 4 + > .../runtime/FunctionPrototype.cpp | 1 + > Source/JavaScriptCore/runtime/Identifier.h | 5 + > Source/JavaScriptCore/runtime/JSCJSValue.h | 2 + > .../runtime/JSCJSValueInlines.h | 16 ++ > Source/JavaScriptCore/runtime/JSFunction.cpp | 1 + > Source/JavaScriptCore/runtime/JSScope.cpp | 17 ++ > Source/JavaScriptCore/runtime/JSScope.h | 2 + > .../runtime/JSSymbolTableObject.h | 26 +- > Source/JavaScriptCore/runtime/SymbolTable.cpp | 8 + > Source/JavaScriptCore/runtime/SymbolTable.h | 30 +- > 53 files changed, 1917 insertions(+), 161 deletions(-) > >diff --git a/JSTests/ChangeLog b/JSTests/ChangeLog >index 83833b55e93..04abe33f643 100644 >--- a/JSTests/ChangeLog >+++ b/JSTests/ChangeLog >@@ -1,3 +1,27 @@ >+2019-01-29 Xan Lopez <xan@igalia.com> >+ >+ [JSC] Add support for class fields >+ https://bugs.webkit.org/show_bug.cgi?id=174212 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ New syntax invalidates some test expectations: >+ >+ "async <linefeed> MethodDefinition" is no longer an unexpected "async" >+ token. It is now an instance field named "async" with no initializer, >+ and an automatic semicolon, followed by MethodDefinition. >+ >+ "get|set GeneratorMethodDefinition"'s error message has changed, due to "get" >+ being valid class field names. >+ >+ Many class-syntax tests relating to automatic semicolon insertion are >+ no longer valid, as a line containing nothing but an identifier is now >+ a valid class element. >+ >+ * stress/async-await-syntax.js: >+ * stress/generator-syntax.js: >+ * stress/method-name.js: >+ > 2019-01-28 Yusuke Suzuki <ysuzuki@apple.com> > > Unreviewed, fix the test after r240543 not to use @Error / Error in builtins >diff --git a/JSTests/stress/async-await-syntax.js b/JSTests/stress/async-await-syntax.js >index 6121d41268e..a28058b1571 100644 >--- a/JSTests/stress/async-await-syntax.js >+++ b/JSTests/stress/async-await-syntax.js >@@ -552,9 +552,6 @@ async function fn(b) { > // ObjectLiteral AsyncMethodDefinition > { prefix: "({ async", suffix: "method() {} }).method" }, > >- // ClassLiteral AsyncMethodDefinition >- { prefix: "(class { async", suffix: "method() {} }).prototype.method" }, >- > // AsyncArrowFunctions > { prefix: "(async", suffix: "param => 1)" }, > { prefix: "(async", suffix: "(param) => 1)" }, >@@ -570,6 +567,18 @@ async function fn(b) { > testLineFeedErrors(prefix, suffix); > } > >+ let testsClass = [ >+ // ClassLiteral AsyncMethodDefinition >+ { prefix: "(class { async", suffix: "method() {} }).prototype.method" }, >+ ]; >+ >+ for (let { prefix, suffix } of testsClass) { >+ testSyntax(`${prefix} ${suffix}`); >+ testSyntax(`"use strict";${prefix} ${suffix}`); >+ shouldBe("function", typeof eval(`${prefix} ${suffix}`)); >+ shouldBe("function", typeof eval(`"use strict";${prefix} ${suffix}`)); >+ } >+ > // AsyncFunctionDeclaration > testSyntax("async function foo() {}"); > testLineFeeds("async", "function foo() {}"); >diff --git a/JSTests/stress/generator-syntax.js b/JSTests/stress/generator-syntax.js >index 862c2a59364..81e13a5c8d4 100644 >--- a/JSTests/stress/generator-syntax.js >+++ b/JSTests/stress/generator-syntax.js >@@ -26,7 +26,7 @@ class Hello { > get *gen() { > } > } >-`, `SyntaxError: Unexpected token '*'. Expected an opening '(' before a method's parameter list.`); >+`, `SyntaxError: Unexpected token '*'. Expected a ';' following a class field.`); > > > testSyntaxError(` >@@ -34,7 +34,7 @@ class Hello { > set *gen(value) { > } > } >-`, `SyntaxError: Unexpected token '*'. Expected an opening '(' before a method's parameter list.`); >+`, `SyntaxError: Unexpected token '*'. Expected a ';' following a class field.`); > > testSyntaxError(` > function ** gen() { } >diff --git a/JSTests/stress/method-name.js b/JSTests/stress/method-name.js >index 45ee5ba4897..0a2b6ca25a1 100644 >--- a/JSTests/stress/method-name.js >+++ b/JSTests/stress/method-name.js >@@ -25,7 +25,7 @@ testSyntaxError(` > class Hello { > hello hello() { } > } >-`, `SyntaxError: Unexpected identifier 'hello'. Expected an opening '(' before a method's parameter list.`); >+`, `SyntaxError: Unexpected identifier 'hello'. Expected a ';' following a class field.`); > > testSyntaxError(` > let obj = { >diff --git a/JSTests/test262/config.yaml b/JSTests/test262/config.yaml >index 0bf6d95438e..2d742dd9e45 100644 >--- a/JSTests/test262/config.yaml >+++ b/JSTests/test262/config.yaml >@@ -17,6 +17,10 @@ skip: > - Symbol.matchAll > # https://bugs.webkit.org/show_bug.cgi?id=174931 > - regexp-lookbehind >+ - class-methods-private >+ - class-static-methods-private >+ - class-static-fields-private >+ - class-static-fields-public > files: > - test/built-ins/Array/prototype/reverse/length-exceeding-integer-limit-with-object.js > - test/built-ins/Array/prototype/unshift/length-near-integer-limit.js >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index 9261b6b474a..a6cc3a0ff31 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,26 @@ >+2019-01-29 Xan Lopez <xan@igalia.com> >+ >+ [JSC] Add support for class fields >+ https://bugs.webkit.org/show_bug.cgi?id=174212 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ New syntax invalidates some test expectations: >+ >+ "async <linefeed> MethodDefinition" is no longer an unexpected "async" >+ token. It is now an instance field named "async" with no initializer, >+ and an automatic semicolon, followed by MethodDefinition. >+ >+ "get|set GeneratorMethodDefinition"'s error message has changed, due to "get" >+ being valid class field names. >+ >+ Many class-syntax tests relating to automatic semicolon insertion are >+ no longer valid, as a line containing nothing but an identifier is now >+ a valid class element. >+ >+ * js/class-syntax-semicolon-expected.txt: >+ * js/script-tests/class-syntax-semicolon.js: >+ > 2019-01-28 Devin Rousso <drousso@apple.com> > > Web Inspector: provide a way to edit page WebRTC settings on a remote target >diff --git a/LayoutTests/js/class-syntax-semicolon-expected.txt b/LayoutTests/js/class-syntax-semicolon-expected.txt >index 8d34286132f..72be3227fb1 100644 >--- a/LayoutTests/js/class-syntax-semicolon-expected.txt >+++ b/LayoutTests/js/class-syntax-semicolon-expected.txt >@@ -3,14 +3,6 @@ Tests for ES6 class syntax containing semicolon in the class body > On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". > > >-PASS class A { foo;() { } } threw exception SyntaxError: Unexpected token ';'. Expected an opening '(' before a method's parameter list.. >-PASS class A { foo() ; { } } threw exception SyntaxError: Unexpected token ';'. Expected an opening '{' at the start of a method body.. >-PASS class A { get ; foo() { } } threw exception SyntaxError: Unexpected token ';'. Expected an opening '(' before a method's parameter list.. >-PASS class A { get foo;() { } } threw exception SyntaxError: Unexpected token ';'. Expected a parameter list for getter definition.. >-PASS class A { get foo() ; { } } threw exception SyntaxError: Unexpected token ';'. Expected an opening '{' at the start of a getter body.. >-PASS class A { set ; foo(x) { } } threw exception SyntaxError: Unexpected token ';'. Expected an opening '(' before a method's parameter list.. >-PASS class A { set foo;(x) { } } threw exception SyntaxError: Unexpected token ';'. Expected a parameter list for setter definition.. >-PASS class A { set foo(x) ; { } } threw exception SyntaxError: Unexpected token ';'. Expected an opening '{' at the start of a setter body.. > PASS class A { ; } did not throw exception. > PASS class A { foo() { } ; } did not throw exception. > PASS class A { get foo() { } ; } did not throw exception. >diff --git a/LayoutTests/js/script-tests/class-syntax-semicolon.js b/LayoutTests/js/script-tests/class-syntax-semicolon.js >index 86b3d56ac3a..0a4284361ba 100644 >--- a/LayoutTests/js/script-tests/class-syntax-semicolon.js >+++ b/LayoutTests/js/script-tests/class-syntax-semicolon.js >@@ -1,14 +1,5 @@ > description('Tests for ES6 class syntax containing semicolon in the class body'); > >-shouldThrow("class A { foo;() { } }", "'SyntaxError: Unexpected token \\';\\'. Expected an opening \\'(\\' before a method\\'s parameter list.'"); >-shouldThrow("class A { foo() ; { } }", "'SyntaxError: Unexpected token \\\';\\'. Expected an opening \\'{\\' at the start of a method body.'"); >-shouldThrow("class A { get ; foo() { } }", "'SyntaxError: Unexpected token \\';\\'. Expected an opening \\'(\\' before a method\\'s parameter list.'"); >-shouldThrow("class A { get foo;() { } }", "'SyntaxError: Unexpected token \\\';\\'. Expected a parameter list for getter definition.'"); >-shouldThrow("class A { get foo() ; { } }", "'SyntaxError: Unexpected token \\\';\\'. Expected an opening \\'{\\' at the start of a getter body.'"); >-shouldThrow("class A { set ; foo(x) { } }", "'SyntaxError: Unexpected token \\';\\'. Expected an opening \\'(\\' before a method\\'s parameter list.'"); >-shouldThrow("class A { set foo;(x) { } }", "'SyntaxError: Unexpected token \\\';\\'. Expected a parameter list for setter definition.'"); >-shouldThrow("class A { set foo(x) ; { } }", "'SyntaxError: Unexpected token \\\';\\'. Expected an opening \\'{\\' at the start of a setter body.'"); >- > shouldNotThrow("class A { ; }"); > shouldNotThrow("class A { foo() { } ; }"); > shouldNotThrow("class A { get foo() { } ; }"); >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index ad050a70c2e..d7536983562 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,268 @@ >+2019-01-29 Xan Lopez <xan@igalia.com> >+ >+ [JSC] Add support for class fields >+ https://bugs.webkit.org/show_bug.cgi?id=174212 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ This provides a basic implementation of public/private class instance >+ fields (https://tc39.github.io/proposal-class-fields/). >+ >+ Overview: >+ >+ This class field implementation relies on a synthetic function to >+ add fields to the class instance during construction. To do this, >+ a private name #instanceFieldInitializer is conditionally added to >+ the class during the initial evaluation if any class fields are >+ present. The field is always loaded during class construction or >+ super calls, but is not invoked if the value is undefined. This is >+ similar to what the v8 engine does. >+ >+ In order to generate this synthetic function, the locations of >+ each class field is recorded and stored in a >+ WTF::Vector<JSTextPosition>. These are used to reparse the class >+ body, and produce an AST specifically for initializing the class >+ body. Recording each text position allows the reparsing step to >+ skip over long method definitions, whitespace or comments, at the >+ cost of some memory. >+ >+ --- >+ >+ The spec includes a fair amount of early syntax errors for private >+ fields. Most of them require simple changes to the parser >+ (although a new EvalContextType is needed to handle the use of >+ fields inside eval calls), but a special type of error requires >+ larger changes: it is an early syntax error to use a private name >+ without declaring it, but a runtime error to use it before it is >+ declared. To accomplish this we extend VariableEnvironment to >+ track both uses and declarations of private names. The parser adds >+ the names to the set as it finds them, and when it finishes >+ parsing a class makes sure that either one of these holds: a) >+ there are no private names used without being declared b) there is >+ a parent class scope, so the task of balancing the set can >+ continue there. >+ >+ --- >+ >+ Computed property names are valid class fields -- the computed >+ expression is, as required by the proposal, evaluated during the >+ initial evaluation of the class definition. Because of this, it's >+ necessary to store the result of this computation for use within >+ the initializer function. This patch uses a hack to add these as >+ private variables, in such a way that it's easy to access them, >+ and theoretically they are not observable to JS code: >+ For each computed class field name, `[ {expr} ] = {initializer}`: >+ >+ <during class evaluation> >+ 1) fieldName = emitNode({expr}) >+ 2) fieldName = emitToPropertyKey(fieldName) >+ 3) classScope[numComputedNames++] = fieldName >+ >+ <during class field initialization> >+ 1) fieldName = emitGetFromScope(classScope, numComputedNames++) >+ 2) value = emitNode({initializer}) >+ 3) instance[fieldName] = value >+ >+ Although this is somewhat hacky, and should be replaced with >+ something better, it works and is fairly straight forward to >+ understand. >+ >+ Finally this introduces a new opcode (op_to_property_key). There >+ was no need for it before because ordinarily, the conversion >+ happened during get/put operations, but in this case it's >+ necessary to happen during class field evaluation, but storage >+ occurs later on in the initializer function. >+ >+ --- >+ >+ Private fields introduce 3 new opcodes: `op_add|get|put_private_field`. >+ This option was selected over modifying get|put_by_val for the following >+ reason: >+ >+ 1) avoid re-ifying Symbols --- private field names are just SymbolImpls >+ (Caveat: this requires looking them up in scope each time >+ they're used --- though DFG can hopefully identify when they're >+ reused and only emit the load once, or replace it with a constant >+ in some cases). >+ 2) avoid creating a different flavour of SymbolImpl with the specific >+ requirements of the proposed private fields (which behave slightly >+ differently from the existing PrivateName). >+ 3) avoid adding additional slow cases to get/put operations to the common >+ cases, avoid adding new GetPutInfo complexity, etc >+ >+ * builtins/BuiltinNames.h: >+ * bytecode/BytecodeList.rb: >+ * bytecode/BytecodeUseDef.h: >+ (JSC::computeUsesForBytecodeOffset): >+ (JSC::computeDefsForBytecodeOffset): >+ * bytecode/CodeBlock.cpp: >+ (JSC::CodeBlock::propagateTransitions): >+ (JSC::CodeBlock::finalizeLLIntInlineCaches): >+ * bytecode/ExecutableInfo.h: >+ * bytecode/UnlinkedFunctionExecutable.cpp: >+ (JSC::generateUnlinkedFunctionCodeBlock): >+ * bytecode/UnlinkedFunctionExecutable.h: >+ * bytecompiler/BytecodeGenerator.cpp: >+ (JSC::BytecodeGenerator::BytecodeGenerator): >+ (JSC::BytecodeGenerator::instantiateLexicalVariables): >+ (JSC::BytecodeGenerator::popLexicalScopeInternal): >+ (JSC::BytecodeGenerator::emitPrivateFieldAdd): >+ (JSC::BytecodeGenerator::emitPrivateFieldGet): >+ (JSC::BytecodeGenerator::emitPrivateFieldSet): >+ (JSC::BytecodeGenerator::emitInstanceFieldInitializationIfNeeded): >+ (JSC::BytecodeGenerator::emitNewInstanceFieldInitializerFunction): >+ (JSC::BytecodeGenerator::shouldEmitSetFunctionName): >+ (JSC::BytecodeGenerator::emitSetFunctionName): >+ (JSC::BytecodeGenerator::emitSetFunctionNameIfNeeded): >+ (JSC::BytecodeGenerator::emitToPropertyKey): >+ (JSC::BytecodeGenerator::emitLoadDerivedConstructor): >+ * bytecompiler/BytecodeGenerator.h: >+ (JSC::BytecodeGenerator::emitDefineClassElements): >+ * bytecompiler/NodesCodegen.cpp: >+ (JSC::PropertyListNode::emitBytecode): >+ (JSC::PropertyListNode::emitSaveComputedFieldName): >+ (JSC::DotAccessorNode::emitBytecode): >+ (JSC::BaseDotNode::emitGetPropertyValue): >+ (JSC::BaseDotNode::emitPutProperty): >+ (JSC::FunctionCallValueNode::emitBytecode): >+ (JSC::AssignDotNode::emitBytecode): >+ (JSC::ReadModifyDotNode::emitBytecode): >+ (JSC::DefineFieldNode::emitBytecode): >+ (JSC::ClassExprNode::emitBytecode): >+ * dfg/DFGCapabilities.cpp: >+ (JSC::DFG::capabilityLevel): >+ * interpreter/Interpreter.cpp: >+ (JSC::eval): >+ * jit/JIT.cpp: >+ (JSC::JIT::privateCompileMainPass): >+ (JSC::JIT::privateCompileSlowCases): >+ * jit/JIT.h: >+ * jit/JITOpcodes.cpp: >+ (JSC::JIT::emit_op_add_private_field): >+ (JSC::JIT::emit_op_get_private_field): >+ (JSC::JIT::emit_op_put_private_field): >+ * llint/LowLevelInterpreter32_64.asm: >+ * llint/LowLevelInterpreter64.asm: >+ * parser/ASTBuilder.h: >+ (JSC::ASTBuilder::createDotAccess): >+ (JSC::ASTBuilder::createDefineField): >+ (JSC::ASTBuilder::createProperty): >+ (JSC::ASTBuilder::isPrivateLocation): >+ (JSC::ASTBuilder::makeAssignNode): >+ * parser/Lexer.cpp: >+ (JSC::Lexer<T>::Lexer): >+ (JSC::Lexer<LChar>::parseIdentifier): >+ (JSC::Lexer<UChar>::parseIdentifier): >+ (JSC::Lexer<CharacterType>::parseIdentifierSlowCase): >+ (JSC::Lexer<T>::lex): >+ * parser/NodeConstructors.h: >+ (JSC::PropertyNode::PropertyNode): >+ (JSC::BaseDotNode::BaseDotNode): >+ (JSC::DotAccessorNode::DotAccessorNode): >+ (JSC::AssignDotNode::AssignDotNode): >+ (JSC::ReadModifyDotNode::ReadModifyDotNode): >+ (JSC::DefineFieldNode::DefineFieldNode): >+ (JSC::ClassExprNode::ClassExprNode): >+ * parser/Nodes.cpp: >+ (JSC::PropertyListNode::shouldCreateLexicalScopeForClass): >+ * parser/Nodes.h: >+ (JSC::ExpressionNode::isPrivateLocation const): >+ (JSC::StatementNode::isDefineFieldNode const): >+ (JSC::BaseDotNode::base const): >+ (JSC::BaseDotNode::identifier const): >+ (JSC::BaseDotNode::type const): >+ (JSC::BaseDotNode::isPrivateName const): >+ * parser/Parser.cpp: >+ (JSC::Parser<LexerType>::parseInner): >+ (JSC::Parser<LexerType>::parseVariableDeclarationList): >+ (JSC::Parser<LexerType>::parseDestructuringPattern): >+ (JSC::Parser<LexerType>::parseFunctionBody): >+ (JSC::stringArticleForFunctionMode): >+ (JSC::stringForFunctionMode): >+ (JSC::Parser<LexerType>::parseClass): >+ (JSC::Parser<LexerType>::parseInstanceFieldInitializerSourceElements): >+ (JSC::Parser<LexerType>::parsePrimaryExpression): >+ (JSC::Parser<LexerType>::addPrivateNameUsedIfNeeded): >+ (JSC::Parser<LexerType>::parseMemberExpression): >+ (JSC::Parser<LexerType>::parseUnaryExpression): >+ (JSC::Parser<LexerType>::printUnexpectedTokenText): >+ * parser/Parser.h: >+ (JSC::Scope::Scope): >+ (JSC::Scope::setSourceParseMode): >+ (JSC::Scope::isPrivateNameScope const): >+ (JSC::Scope::setIsPrivateNameScope): >+ (JSC::Scope::hasPrivateName): >+ (JSC::Scope::copyPrivateNameUses): >+ (JSC::Scope::hasUsedButUndeclaredPrivateNames const): >+ (JSC::Scope::usePrivateName): >+ (JSC::Scope::declarePrivateName): >+ (JSC::Scope::markLastUsedVariablesSetAsCaptured): >+ (JSC::Parser::currentPrivateNameScope): >+ (JSC::Parser::copyPrivateNameUsesToOuterScope): >+ (JSC::Parser::matchAndUpdate): >+ (JSC::Parser<LexerType>::parse): >+ (JSC::parse): >+ * parser/ParserModes.h: >+ (JSC::isFunctionParseMode): >+ * parser/ParserTokens.h: >+ * parser/SyntaxChecker.h: >+ (JSC::SyntaxChecker::createDotAccess): >+ (JSC::SyntaxChecker::createProperty): >+ (JSC::SyntaxChecker::createDefineField): >+ (JSC::SyntaxChecker::operatorStackPop): >+ * parser/VariableEnvironment.cpp: >+ (JSC::VariableEnvironment::operator=): >+ (JSC::VariableEnvironment::swap): >+ (JSC::VariableEnvironment::markPrivateNameAsDeclared): >+ (JSC::VariableEnvironment::declarePrivateName): >+ (JSC::VariableEnvironment::usePrivateName): >+ * parser/VariableEnvironment.h: >+ (JSC::PrivateNameEntry::PrivateNameEntry): >+ (JSC::PrivateNameEntry::isUsed const): >+ (JSC::PrivateNameEntry::isDeclared const): >+ (JSC::PrivateNameEntry::setIsUsed): >+ (JSC::PrivateNameEntry::setIsDeclared): >+ (JSC::PrivateNameEntry::bits const): >+ (JSC::PrivateNameEntry::operator== const): >+ (JSC::VariableEnvironment::VariableEnvironment): >+ (JSC::VariableEnvironment::size const): >+ (JSC::VariableEnvironment::declarePrivateName): >+ (JSC::VariableEnvironment::usePrivateName): >+ (JSC::VariableEnvironment::privateNames const): >+ (JSC::VariableEnvironment::privateNamesSize const): >+ (JSC::VariableEnvironment::hasPrivateName): >+ (JSC::VariableEnvironment::copyPrivateNames const): >+ (JSC::VariableEnvironment::copyPrivateNameUses const): >+ (JSC::VariableEnvironment::VariableEnvironmentRareData::VariableEnvironmentRareData): >+ (JSC::VariableEnvironment::getOrAddPrivateName): >+ * runtime/CodeCache.h: >+ (JSC::generateUnlinkedCodeBlockImpl): >+ * runtime/CommonIdentifiers.cpp: >+ (JSC::CommonIdentifiers::CommonIdentifiers): >+ * runtime/CommonIdentifiers.h: >+ * runtime/CommonSlowPaths.cpp: >+ (JSC::SLOW_PATH_DECL): >+ * runtime/CommonSlowPaths.h: >+ * runtime/FunctionPrototype.cpp: >+ (JSC::functionProtoFuncToString): >+ * runtime/Identifier.h: >+ (JSC::StringRepHash::hash): >+ * runtime/JSCJSValue.h: >+ * runtime/JSCJSValueInlines.h: >+ (JSC::JSValue::toPropertyKey const): >+ * runtime/JSFunction.cpp: >+ (JSC::JSFunction::callerGetter): >+ * runtime/JSScope.cpp: >+ (JSC::JSScope::collectClosureVariablesUnderTDZ): >+ (JSC::JSScope::getPrivateSymbol): >+ * runtime/JSScope.h: >+ * runtime/JSSymbolTableObject.h: >+ (JSC::JSSymbolTableObject::getPrivateSymbol): >+ (JSC::JSSymbolTableObject::setSymbolTable): >+ * runtime/SymbolTable.cpp: >+ (JSC::SymbolTable::cloneScopePart): >+ * runtime/SymbolTable.h: >+ > 2019-01-28 Devin Rousso <drousso@apple.com> > > Web Inspector: provide a way to edit page WebRTC settings on a remote target >diff --git a/Source/JavaScriptCore/builtins/BuiltinNames.h b/Source/JavaScriptCore/builtins/BuiltinNames.h >index 4a701448e2a..93b0c4a4ec0 100644 >--- a/Source/JavaScriptCore/builtins/BuiltinNames.h >+++ b/Source/JavaScriptCore/builtins/BuiltinNames.h >@@ -190,6 +190,7 @@ namespace JSC { > macro(meta) \ > macro(webAssemblyCompileStreamingInternal) \ > macro(webAssemblyInstantiateStreamingInternal) \ >+ macro(instanceFieldInitializer) > > namespace Symbols { > #define DECLARE_BUILTIN_STATIC_SYMBOLS(name) extern SymbolImpl::StaticSymbolImpl name##Symbol; >diff --git a/Source/JavaScriptCore/bytecode/BytecodeList.rb b/Source/JavaScriptCore/bytecode/BytecodeList.rb >index 115bd3b065d..b6bf976b0c8 100644 >--- a/Source/JavaScriptCore/bytecode/BytecodeList.rb >+++ b/Source/JavaScriptCore/bytecode/BytecodeList.rb >@@ -822,6 +822,12 @@ op :to_primitive, > src: VirtualRegister, > } > >+op :to_property_key, >+ args: { >+ dst: VirtualRegister, >+ src: VirtualRegister, >+ } >+ > op :resolve_scope, > args: { > dst: VirtualRegister, # offset 1 >@@ -1103,6 +1109,44 @@ op :super_sampler_begin > > op :super_sampler_end > >+op :add_private_field, >+ args: { >+ base: VirtualRegister, >+ fieldName: unsigned, >+ value: VirtualRegister, >+ scope: VirtualRegister, >+ }, >+ metadata: { >+ oldStructureID: StructureID, >+ offset: unsigned, >+ newStructureID: StructureID, >+ } >+ >+op :get_private_field, >+ args: { >+ dst: VirtualRegister, >+ base: VirtualRegister, >+ fieldName: unsigned, >+ scope: VirtualRegister, >+ }, >+ metadata: { >+ structureID: StructureID, >+ offset: unsigned, >+ profile: ValueProfile, >+ } >+ >+op :put_private_field, >+ args: { >+ base: VirtualRegister, >+ fieldName: unsigned, >+ value: VirtualRegister, >+ scope: VirtualRegister, >+ }, >+ metadata: { >+ structureID: StructureID, >+ offset: unsigned, >+ } >+ > end_section :Bytecodes > > begin_section :CLoopHelpers, >diff --git a/Source/JavaScriptCore/bytecode/BytecodeUseDef.h b/Source/JavaScriptCore/bytecode/BytecodeUseDef.h >index 1eee5844607..d1e8d66a9af 100644 >--- a/Source/JavaScriptCore/bytecode/BytecodeUseDef.h >+++ b/Source/JavaScriptCore/bytecode/BytecodeUseDef.h >@@ -161,6 +161,7 @@ void computeUsesForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, const Ins > USES(OpResolveScopeForHoistingFuncDeclInEval, scope) > USES(OpGetFromScope, scope) > USES(OpToPrimitive, src) >+ USES(OpToPropertyKey, src) > USES(OpTryGetById, base) > USES(OpGetById, base) > USES(OpGetByIdDirect, base) >@@ -249,6 +250,10 @@ void computeUsesForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, const Ins > > USES(OpYield, generator, argument) > >+ USES(OpAddPrivateField, base, value, scope) >+ USES(OpGetPrivateField, base, scope) >+ USES(OpPutPrivateField, base, value, scope) >+ > case op_new_array_with_spread: > handleNewArrayLike(instruction->as<OpNewArrayWithSpread>()); > return; >@@ -345,6 +350,8 @@ void computeDefsForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, const Ins > case op_unreachable: > case op_super_sampler_begin: > case op_super_sampler_end: >+ case op_add_private_field: >+ case op_put_private_field: > #define LLINT_HELPER_OPCODES(opcode, length) case opcode: > FOR_EACH_LLINT_OPCODE_EXTENSION(LLINT_HELPER_OPCODES); > #undef LLINT_HELPER_OPCODES >@@ -367,6 +374,7 @@ void computeDefsForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, const Ins > DEFS(OpResolveScopeForHoistingFuncDeclInEval, dst) > DEFS(OpStrcat, dst) > DEFS(OpToPrimitive, dst) >+ DEFS(OpToPropertyKey, dst) > DEFS(OpCreateThis, dst) > DEFS(OpNewArray, dst) > DEFS(OpNewArrayWithSpread, dst) >@@ -460,6 +468,7 @@ void computeDefsForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, const Ins > DEFS(OpGetArgument, dst) > DEFS(OpCreateRest, dst) > DEFS(OpGetRestLength, dst) >+ DEFS(OpGetPrivateField, dst) > > DEFS(OpCatch, exception, thrownValue) > >diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp >index 40dac73badd..ebd760ee2d9 100644 >--- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp >+++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp >@@ -1107,6 +1107,18 @@ void CodeBlock::propagateTransitions(const ConcurrentJSLocker&, SlotVisitor& vis > visitor.appendUnbarriered(newStructure); > continue; > } >+ if (instruction->is<OpAddPrivateField>()) { >+ auto& metadata = instruction->as<OpAddPrivateField>().metadata(this); >+ StructureID oldStructureID = metadata.m_oldStructureID; >+ StructureID newStructureID = metadata.m_newStructureID; >+ if (!oldStructureID || !newStructureID) >+ continue; >+ Structure* oldStructure = vm.heap.structureIDTable().get(oldStructureID); >+ Structure* newStructure = vm.heap.structureIDTable().get(newStructureID); >+ if (Heap::isMarked(oldStructure)) >+ visitor.appendUnbarriered(newStructure); >+ continue; >+ } > } > } > >@@ -1308,6 +1320,39 @@ void CodeBlock::finalizeLLIntInlineCaches() > case op_put_to_scope: > handleGetPutFromScope(curInstruction->as<OpPutToScope>().metadata(this)); > break; >+ case op_add_private_field: { >+ auto& metadata = curInstruction->as<OpAddPrivateField>().metadata(this); >+ StructureID oldStructureID = metadata.m_oldStructureID; >+ if (!oldStructureID || Heap::isMarked(vm.heap.structureIDTable().get(oldStructureID))) >+ break; >+ if (Options::verboseOSR()) >+ dataLogF("Clearing LLInt property access (private field).\n"); >+ metadata.m_oldStructureID = metadata.m_newStructureID = 0; >+ metadata.m_offset = 0; >+ break; >+ } >+ case op_get_private_field: { >+ auto& metadata = curInstruction->as<OpGetPrivateField>().metadata(this); >+ StructureID structureID = metadata.m_structureID; >+ if (!structureID || Heap::isMarked(vm.heap.structureIDTable().get(structureID))) >+ break; >+ if (Options::verboseOSR()) >+ dataLogF("Clearing LLInt property access (private field).\n"); >+ metadata.m_structureID = 0; >+ metadata.m_offset = 0; >+ break; >+ } >+ case op_put_private_field: { >+ auto& metadata = curInstruction->as<OpPutPrivateField>().metadata(this); >+ StructureID structureID = metadata.m_structureID; >+ if (!structureID || Heap::isMarked(vm.heap.structureIDTable().get(structureID))) >+ break; >+ if (Options::verboseOSR()) >+ dataLogF("Clearing LLInt property access (private field).\n"); >+ metadata.m_structureID = 0; >+ metadata.m_offset = 0; >+ break; >+ } > default: > OpcodeID opcodeID = curInstruction->opcodeID(); > ASSERT_WITH_MESSAGE_UNUSED(opcodeID, false, "Unhandled opcode in CodeBlock::finalizeUnconditionally, %s(%d) at bc %u", opcodeNames[opcodeID], opcodeID, propertyAccessInstructions[i]); >diff --git a/Source/JavaScriptCore/bytecode/ExecutableInfo.h b/Source/JavaScriptCore/bytecode/ExecutableInfo.h >index 750900ecda4..41be844c7a5 100644 >--- a/Source/JavaScriptCore/bytecode/ExecutableInfo.h >+++ b/Source/JavaScriptCore/bytecode/ExecutableInfo.h >@@ -30,7 +30,7 @@ > namespace JSC { > > enum class DerivedContextType : uint8_t { None, DerivedConstructorContext, DerivedMethodContext }; >-enum class EvalContextType : uint8_t { None, FunctionEvalContext }; >+enum class EvalContextType : uint8_t { None, FunctionEvalContext, InstanceFieldEvalContext }; > > // FIXME: These flags, ParserModes and propagation to XXXCodeBlocks should be reorganized. > // https://bugs.webkit.org/show_bug.cgi?id=151547 >diff --git a/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp b/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp >index 7b29fc31077..3e895837787 100644 >--- a/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp >+++ b/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp >@@ -42,7 +42,8 @@ > > namespace JSC { > >-static_assert(sizeof(UnlinkedFunctionExecutable) <= 160, "UnlinkedFunctionExecutable should fit in a 160-byte cell. If you increase the size of this class, consider making a size class that perfectly fits it."); >+// FIXME: figure out something other than simply bumping the size limit. >+static_assert(sizeof(UnlinkedFunctionExecutable) <= 176, "UnlinkedFunctionExecutable should fit in a 160-byte cell. If you increase the size of this class, consider making a size class that perfectly fits it."); > > const ClassInfo UnlinkedFunctionExecutable::s_info = { "UnlinkedFunctionExecutable", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(UnlinkedFunctionExecutable) }; > >@@ -56,7 +57,7 @@ static UnlinkedFunctionCodeBlock* generateUnlinkedFunctionCodeBlock( > JSParserScriptMode scriptMode = executable->scriptMode(); > ASSERT(isFunctionParseMode(executable->parseMode())); > std::unique_ptr<FunctionNode> function = parse<FunctionNode>( >- &vm, source, executable->name(), builtinMode, strictMode, scriptMode, executable->parseMode(), executable->superBinding(), error, nullptr); >+ &vm, source, executable->name(), builtinMode, strictMode, scriptMode, executable->parseMode(), executable->superBinding(), error, nullptr, ConstructorKind::None, DerivedContextType::None, EvalContextType::None, nullptr, nullptr, executable->instanceFieldLocations()); > > if (!function) { > ASSERT(error.isValid()); >diff --git a/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.h b/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.h >index d637d68af5a..db9fe90ad5e 100644 >--- a/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.h >+++ b/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.h >@@ -33,6 +33,7 @@ > #include "Intrinsic.h" > #include "JSCast.h" > #include "ParserModes.h" >+#include "ParserTokens.h" > #include "RegExp.h" > #include "SourceCode.h" > #include "VariableEnvironment.h" >@@ -150,6 +151,9 @@ public: > void setSourceURLDirective(const String& sourceURL) { m_sourceURLDirective = sourceURL; } > void setSourceMappingURLDirective(const String& sourceMappingURL) { m_sourceMappingURLDirective = sourceMappingURL; } > >+ const Vector<JSTextPosition>& instanceFieldLocations() const { return m_instanceFieldLocations; } >+ void setInstanceFieldLocations(Vector<JSTextPosition>&& instanceFieldLocations) { m_instanceFieldLocations = instanceFieldLocations; } >+ > private: > UnlinkedFunctionExecutable(VM*, Structure*, const SourceCode&, FunctionMetadataNode*, UnlinkedFunctionKind, ConstructAbility, JSParserScriptMode, VariableEnvironment&, JSC::DerivedContextType, bool isBuiltinDefaultClassConstructor); > UnlinkedFunctionExecutable(Decoder&, VariableEnvironment&, const CachedFunctionExecutable&); >@@ -191,6 +195,9 @@ private: > > CompactVariableMap::Handle m_parentScopeTDZVariables; > >+ // TODO: only allocate this if needed. >+ Vector<JSTextPosition> m_instanceFieldLocations; >+ > protected: > static void visitChildren(JSCell*, SlotVisitor&); > >diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp >index d6e131bf813..aead2c4bfbc 100644 >--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp >+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp >@@ -397,7 +397,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, Unlinke > bool shouldCaptureSomeOfTheThings = m_shouldEmitDebugHooks || functionNode->needsActivation() || containsArrowOrEvalButNotInArrowBlock; > > bool shouldCaptureAllOfTheThings = m_shouldEmitDebugHooks || codeBlock->usesEval(); >- bool needsArguments = ((functionNode->usesArguments() && !codeBlock->isArrowFunction()) || codeBlock->usesEval() || (functionNode->usesArrowFunction() && !codeBlock->isArrowFunction() && isArgumentsUsedInInnerArrowFunction())); >+ bool needsArguments = ((functionNode->usesArguments() && !codeBlock->isArrowFunction()) || codeBlock->usesEval() || (functionNode->usesArrowFunction() && !codeBlock->isArrowFunction() && isArgumentsUsedInInnerArrowFunction())) && parseMode != SourceParseMode::InstanceFieldInitializerMode; > > if (isGeneratorOrAsyncFunctionBodyParseMode(parseMode)) { > // Generator and AsyncFunction never provides "arguments". "arguments" reference will be resolved in an upper generator function scope. >@@ -624,7 +624,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, Unlinke > } > > bool shouldCreateArgumensVariable = !haveParameterNamedArguments >- && !SourceParseModeSet(SourceParseMode::ArrowFunctionMode, SourceParseMode::AsyncArrowFunctionMode).contains(m_codeBlock->parseMode()); >+ && !SourceParseModeSet(SourceParseMode::ArrowFunctionMode, SourceParseMode::AsyncArrowFunctionMode, SourceParseMode::InstanceFieldInitializerMode).contains(m_codeBlock->parseMode()); > shouldCreateArgumentsVariableInParameterScope = shouldCreateArgumensVariable && !isSimpleParameterList; > // Do not create arguments variable in case of Arrow function. Value will be loaded from parent scope > if (shouldCreateArgumensVariable && !shouldCreateArgumentsVariableInParameterScope) { >@@ -720,6 +720,9 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, Unlinke > moveEmptyValue(&m_thisRegister); > } else > emitCreateThis(&m_thisRegister); >+ >+ if (constructorKind() == ConstructorKind::Base) >+ emitInstanceFieldInitializationIfNeeded(&m_thisRegister, &m_calleeRegister, m_scopeNode->position(), m_scopeNode->position(), m_scopeNode->position()); > } else if (constructorKind() != ConstructorKind::None) > emitThrowTypeError("Cannot call a class constructor without |new|"); > else { >@@ -1936,6 +1939,17 @@ bool BytecodeGenerator::instantiateLexicalVariables(const VariableEnvironment& l > SymbolTableEntry newEntry(varOffset, static_cast<unsigned>(entry.value.isConst() ? PropertyAttribute::ReadOnly : PropertyAttribute::None)); > symbolTable->add(NoLockingNecessary, entry.key.get(), newEntry); > } >+ >+ // Add private names to SymbolTable, if any >+ if (lexicalVariables.privateNamesSize()) { >+ for (const auto& privateName : lexicalVariables.privateNames()) { >+ if (privateName.value.isDeclared()) >+ symbolTable->addPrivateName(privateName.key.get()); >+ } >+ >+ // FIXME: determine if private names are captured in a smarter way. >+ hasCapturedVariables = true; >+ } > } > return hasCapturedVariables; > } >@@ -2184,7 +2198,8 @@ void BytecodeGenerator::popLexicalScopeInternal(VariableEnvironment& environment > > auto stackEntry = m_lexicalScopeStack.takeLast(); > SymbolTable* symbolTable = stackEntry.m_symbolTable; >- bool hasCapturedVariables = false; >+ // Private names are always treated as captured, to ensure the presence of the lexical scope. >+ bool hasCapturedVariables = symbolTable->hasPrivateNames(); > for (auto& entry : environment) { > if (entry.value.isCaptured()) { > hasCapturedVariables = true; >@@ -2753,6 +2768,24 @@ RegisterID* BytecodeGenerator::emitDeleteByVal(RegisterID* dst, RegisterID* base > return dst; > } > >+void BytecodeGenerator::emitPrivateFieldAdd(RegisterID* base, const Identifier& name, RegisterID* value) >+{ >+ m_codeBlock->addPropertyAccessInstruction(instructions().size()); >+ OpAddPrivateField::emit(this, base, addConstant(name), value, scopeRegister()); >+} >+ >+RegisterID* BytecodeGenerator::emitPrivateFieldGet(RegisterID* dst, RegisterID* base, const Identifier& name) >+{ >+ OpGetPrivateField::emit(this, kill(dst), base, addConstant(name), scopeRegister()); >+ return dst; >+} >+ >+RegisterID* BytecodeGenerator::emitPrivateFieldSet(RegisterID* base, const Identifier& name, RegisterID* value) >+{ >+ OpPutPrivateField::emit(this, base, addConstant(name), value, scopeRegister()); >+ return value; >+} >+ > void BytecodeGenerator::emitSuperSamplerBegin() > { > OpSuperSamplerBegin::emit(this); >@@ -2789,6 +2822,20 @@ RegisterID* BytecodeGenerator::emitCreateThis(RegisterID* dst) > return dst; > } > >+RegisterID* BytecodeGenerator::emitInstanceFieldInitializationIfNeeded(RegisterID* dst, RegisterID* constructor, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) >+{ >+ Ref<Label> done = newLabel(); >+ RefPtr<RegisterID> initializer = emitDirectGetById(newTemporary(), constructor, propertyNames().builtinNames().instanceFieldInitializerPrivateName()); >+ emitJumpIfTrue(emitIsUndefined(newTemporary(), initializer.get()), done.get()); >+ >+ CallArguments args(*this, nullptr); >+ emitMove(args.thisRegister(), dst); >+ emitCall(newTemporary(), initializer.get(), NoExpectedFunction, args, divot, divotStart, divotEnd, DebuggableCall::No); >+ >+ emitLabel(done.get()); >+ return dst; >+} >+ > void BytecodeGenerator::emitTDZCheck(RegisterID* target) > { > OpCheckTdz::emit(this, target); >@@ -3064,6 +3111,35 @@ RegisterID* BytecodeGenerator::emitNewDefaultConstructor(RegisterID* dst, Constr > return dst; > } > >+RegisterID* BytecodeGenerator::emitNewInstanceFieldInitializerFunction(RegisterID* dst, Vector<JSTextPosition>&& instanceFieldLocations, bool isDerived) >+{ >+ DerivedContextType newDerivedContextType; >+ SuperBinding superBinding; >+ if (!isDerived) { >+ newDerivedContextType = DerivedContextType::None; >+ superBinding = SuperBinding::NotNeeded; >+ } else { >+ newDerivedContextType = DerivedContextType::DerivedMethodContext; >+ superBinding = SuperBinding::Needed; >+ } >+ >+ VariableEnvironment variablesUnderTDZ; >+ getVariablesUnderTDZ(variablesUnderTDZ); >+ >+ SourceParseMode parseMode = SourceParseMode::InstanceFieldInitializerMode; >+ ConstructAbility constructAbility = ConstructAbility::CannotConstruct; >+ >+ const bool alwaysStrictInClass = true; >+ FunctionMetadataNode metadata(parserArena(), JSTokenLocation(), JSTokenLocation(), 0, 0, 0, 0, 0, alwaysStrictInClass, ConstructorKind::None, superBinding, 0, parseMode, false); >+ metadata.finishParsing(m_scopeNode->source(), Identifier(), FunctionMode::MethodDefinition); >+ auto initializer = UnlinkedFunctionExecutable::create(m_vm, m_scopeNode->source(), &metadata, isBuiltinFunction() ? UnlinkedBuiltinFunction : UnlinkedNormalFunction, constructAbility, scriptMode(), variablesUnderTDZ, newDerivedContextType); >+ initializer->setInstanceFieldLocations(WTFMove(instanceFieldLocations)); >+ >+ unsigned index = m_codeBlock->addFunctionExpr(initializer); >+ OpNewFuncExp::emit(this, dst, scopeRegister(), index); >+ return dst; >+} >+ > RegisterID* BytecodeGenerator::emitNewFunction(RegisterID* dst, FunctionMetadataNode* function) > { > unsigned index = m_codeBlock->addFunctionDecl(makeFunction(function)); >@@ -3078,26 +3154,46 @@ RegisterID* BytecodeGenerator::emitNewFunction(RegisterID* dst, FunctionMetadata > return dst; > } > >-void BytecodeGenerator::emitSetFunctionNameIfNeeded(ExpressionNode* valueNode, RegisterID* value, RegisterID* name) >+bool BytecodeGenerator::shouldEmitSetFunctionName(ExpressionNode* valueNode) > { >+ if (!valueNode) >+ return false; > if (valueNode->isBaseFuncExprNode()) { > FunctionMetadataNode* metadata = static_cast<BaseFuncExprNode*>(valueNode)->metadata(); > if (!metadata->ecmaName().isNull()) >- return; >+ return false; > } else if (valueNode->isClassExprNode()) { > ClassExprNode* classExprNode = static_cast<ClassExprNode*>(valueNode); > if (!classExprNode->ecmaName().isNull()) >- return; >+ return false; > if (classExprNode->hasStaticProperty(m_vm->propertyNames->name)) >- return; >+ return false; > } else >- return; >+ return false; > >+ return true; >+} >+ >+void BytecodeGenerator::emitSetFunctionName(RegisterID* value, RegisterID* name) >+{ > // FIXME: We should use an op_call to an internal function here instead. > // https://bugs.webkit.org/show_bug.cgi?id=155547 > OpSetFunctionName::emit(this, value, name); > } > >+void BytecodeGenerator::emitSetFunctionName(RegisterID* value, const Identifier& ident) >+{ >+ emitSetFunctionName(value, emitLoad(newTemporary(), ident)); >+} >+ >+void BytecodeGenerator::emitSetFunctionNameIfNeeded(ExpressionNode* valueNode, RegisterID* value, RegisterID* name) >+{ >+ if (!shouldEmitSetFunctionName(valueNode)) >+ return; >+ >+ emitSetFunctionName(value, name); >+} >+ > RegisterID* BytecodeGenerator::emitCall(RegisterID* dst, RegisterID* func, ExpectedFunction expectedFunction, CallArguments& callArguments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, DebuggableCall debuggableCall) > { > return emitCall<OpCall>(dst, func, expectedFunction, callArguments, divot, divotStart, divotEnd, debuggableCall); >@@ -3439,6 +3535,13 @@ void BytecodeGenerator::emitToPrimitive(RegisterID* dst, RegisterID* src) > OpToPrimitive::emit(this, dst, src); > } > >+RegisterID* BytecodeGenerator::emitToPropertyKey(RegisterID* dst, RegisterID* src) >+{ >+ dst = tempDestination(dst); >+ OpToPropertyKey::emit(this, dst, src); >+ return dst; >+} >+ > void BytecodeGenerator::emitGetScope() > { > OpGetScope::emit(this, scopeRegister()); >@@ -4299,6 +4402,14 @@ RegisterID* BytecodeGenerator::emitLoadDerivedConstructorFromArrowFunctionLexica > return emitGetFromScope(newTemporary(), emitLoadArrowFunctionLexicalEnvironment(propertyNames().builtinNames().derivedConstructorPrivateName()), protoScopeVar, ThrowIfNotFound); > } > >+RegisterID* BytecodeGenerator::emitLoadDerivedConstructor() >+{ >+ ASSERT(constructorKind() == ConstructorKind::Extends || isDerivedConstructorContext()); >+ if (constructorKind() == ConstructorKind::Extends) >+ return &m_calleeRegister; >+ return emitLoadDerivedConstructorFromArrowFunctionLexicalEnvironment(); >+} >+ > RegisterID* BytecodeGenerator::ensureThis() > { > if (constructorKind() == ConstructorKind::Extends || isDerivedConstructorContext()) { >@@ -4935,4 +5046,3 @@ void printInternal(PrintStream& out, JSC::Variable::VariableKind kind) > } > > } // namespace WTF >- >diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h >index 6ad716eebd2..349040ebad3 100644 >--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h >+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h >@@ -551,14 +551,14 @@ namespace JSC { > return emitNodeInTailPosition(nullptr, n); > } > >- RegisterID* emitDefineClassElements(PropertyListNode* n, RegisterID* constructor, RegisterID* prototype) >+ RegisterID* emitDefineClassElements(PropertyListNode* n, RegisterID* constructor, RegisterID* prototype, Vector<JSTextPosition>& instanceFieldLocations) > { > ASSERT(constructor->refCount() && prototype->refCount()); > if (UNLIKELY(!m_vm->isSafeToRecurse())) > return emitThrowExpressionTooDeepException(); > if (UNLIKELY(n->needsDebugHook())) > emitDebugHook(n); >- return n->emitBytecode(*this, constructor, prototype); >+ return n->emitBytecode(*this, constructor, prototype, &instanceFieldLocations); > } > > RegisterID* emitNodeForProperty(RegisterID* dst, ExpressionNode* node) >@@ -713,6 +713,7 @@ namespace JSC { > template<typename EqOp> > RegisterID* emitEqualityOp(RegisterID* dst, RegisterID* src1, RegisterID* src2); > RegisterID* emitCreateThis(RegisterID* dst); >+ RegisterID* emitInstanceFieldInitializationIfNeeded(RegisterID* dst, RegisterID* constructor, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); > void emitTDZCheck(RegisterID* target); > bool needsTDZCheck(const Variable&); > void emitTDZCheckIfNecessary(const Variable&, RegisterID* target, RegisterID* scope); >@@ -727,10 +728,14 @@ namespace JSC { > RegisterID* emitNewFunction(RegisterID* dst, FunctionMetadataNode*); > RegisterID* emitNewFunctionExpression(RegisterID* dst, FuncExprNode*); > RegisterID* emitNewDefaultConstructor(RegisterID* dst, ConstructorKind, const Identifier& name, const Identifier& ecmaName, const SourceCode& classSource); >+ RegisterID* emitNewInstanceFieldInitializerFunction(RegisterID* dst, Vector<JSTextPosition>&& instanceFieldLocations, bool isDerived); > RegisterID* emitNewArrowFunctionExpression(RegisterID*, ArrowFuncExprNode*); > RegisterID* emitNewMethodDefinition(RegisterID* dst, MethodDefinitionNode*); > RegisterID* emitNewRegExp(RegisterID* dst, RegExp*); > >+ bool shouldEmitSetFunctionName(ExpressionNode* valueNode); >+ void emitSetFunctionName(RegisterID* value, RegisterID* name); >+ void emitSetFunctionName(RegisterID* value, const Identifier& name); > void emitSetFunctionNameIfNeeded(ExpressionNode* valueNode, RegisterID* value, RegisterID* name); > > RegisterID* moveLinkTimeConstant(RegisterID* dst, LinkTimeConstant); >@@ -764,6 +769,10 @@ namespace JSC { > RegisterID* emitDirectPutByVal(RegisterID* base, RegisterID* property, RegisterID* value); > RegisterID* emitDeleteByVal(RegisterID* dst, RegisterID* base, RegisterID* property); > >+ void emitPrivateFieldAdd(RegisterID* base, const Identifier& name, RegisterID* value); >+ RegisterID* emitPrivateFieldGet(RegisterID* dst, RegisterID* base, const Identifier& name); >+ RegisterID* emitPrivateFieldSet(RegisterID* base, const Identifier& name, RegisterID* value); >+ > void emitSuperSamplerBegin(); > void emitSuperSamplerEnd(); > >@@ -811,6 +820,7 @@ namespace JSC { > RegisterID* emitConstruct(RegisterID* dst, RegisterID* func, RegisterID* lazyThis, ExpectedFunction, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); > RegisterID* emitStrcat(RegisterID* dst, RegisterID* src, int count); > void emitToPrimitive(RegisterID* dst, RegisterID* src); >+ RegisterID* emitToPropertyKey(RegisterID* dst, RegisterID* src); > > ResolveType resolveType(); > RegisterID* emitResolveConstantLocal(RegisterID* dst, const Variable&); >@@ -923,6 +933,7 @@ namespace JSC { > void emitPutNewTargetToArrowFunctionContextScope(); > void emitPutDerivedConstructorToArrowFunctionContextScope(); > RegisterID* emitLoadDerivedConstructorFromArrowFunctionLexicalEnvironment(); >+ RegisterID* emitLoadDerivedConstructor(); > > void emitDebugHook(DebugHookType, const JSTextPosition&); > void emitDebugHook(DebugHookType, unsigned line, unsigned charOffset, unsigned lineStart); >diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp >index 6d5045b831b..94868178117 100644 >--- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp >+++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp >@@ -532,13 +532,23 @@ static inline void emitPutHomeObject(BytecodeGenerator& generator, RegisterID* f > generator.emitPutById(function, generator.propertyNames().builtinNames().homeObjectPrivateName(), homeObject); > } > >-RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dstOrConstructor, RegisterID* prototype) >+RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dstOrConstructor, RegisterID* prototype, Vector<JSTextPosition>* instanceFieldLocations) > { > // Fast case: this loop just handles regular value properties. > PropertyListNode* p = this; > RegisterID* dst = nullptr; > for (; p && (p->m_node->m_type & PropertyNode::Constant); p = p->m_next) { > dst = p->m_node->isInstanceClassProperty() ? prototype : dstOrConstructor; >+ >+ if (p->isComputedClassField()) >+ emitSaveComputedFieldName(generator, *p->m_node); >+ >+ if (p->isInstanceClassField()) { >+ ASSERT(instanceFieldLocations); >+ instanceFieldLocations->append(p->position()); >+ continue; >+ } >+ > emitPutConstantProperty(generator, dst, *p->m_node); > } > >@@ -586,6 +596,16 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe > PropertyNode* node = p->m_node; > dst = node->isInstanceClassProperty() ? prototype : dstOrConstructor; > >+ if (p->isComputedClassField()) >+ emitSaveComputedFieldName(generator, *p->m_node); >+ >+ if (p->isInstanceClassField()) { >+ ASSERT(instanceFieldLocations); >+ instanceFieldLocations->append(p->position()); >+ continue; >+ } >+ >+ > // Handle regular values. > if (node->m_type & PropertyNode::Constant) { > emitPutConstantProperty(generator, dst, *node); >@@ -707,6 +727,25 @@ void PropertyListNode::emitPutConstantProperty(BytecodeGenerator& generator, Reg > generator.emitDirectPutByVal(newObj, propertyName.get(), value.get()); > } > >+void PropertyListNode::emitSaveComputedFieldName(BytecodeGenerator& generator, PropertyNode& node) >+{ >+ ASSERT(node.isComputedClassField()); >+ RefPtr<RegisterID> propertyExpr; >+ const Identifier& description = *node.name(); >+ auto length = node.isPrivate() ? description.length() : 1; >+ Variable var = generator.variable(description); >+ ASSERT(!var.local()); >+ >+ propertyExpr = generator.emitNode(node.m_expression); >+ RegisterID* propertyName = generator.emitToPropertyKey(propertyExpr.get(), propertyExpr.get()); >+ >+ // TODO: add type profiling if we need it? >+ RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var); >+ generator.emitPutToScope(scope.get(), var, propertyName, ThrowIfNotFound, InitializationMode::ConstInitialization); >+ generator.emitProfileType(propertyName, var, position(), JSTextPosition(-1, position().offset + length, -1)); >+ generator.liftTDZCheckIfPossible(var); >+} >+ > // ------------------------------ BracketAccessorNode -------------------------------- > > static bool isNonIndexStringElement(ExpressionNode& element) >@@ -761,16 +800,52 @@ RegisterID* DotAccessorNode::emitBytecode(BytecodeGenerator& generator, Register > RefPtr<RegisterID> base = baseIsSuper ? emitSuperBaseForCallee(generator) : generator.emitNode(m_base); > generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); > RegisterID* finalDest = generator.finalDestination(dst); >- RegisterID* ret; >- if (baseIsSuper) { >- RefPtr<RegisterID> thisValue = generator.ensureThis(); >- ret = generator.emitGetById(finalDest, base.get(), thisValue.get(), m_ident); >- } else >- ret = generator.emitGetById(finalDest, base.get(), m_ident); >+ RegisterID* ret = emitGetPropertyValue(generator, finalDest, base.get()); >+ > generator.emitProfileType(finalDest, divotStart(), divotEnd()); > return ret; > } > >+RegisterID* BaseDotNode::emitGetPropertyValue(BytecodeGenerator& generator, RegisterID* dst, RegisterID* base, RefPtr<RegisterID>& thisValue) >+{ >+ if (isPrivateName()) >+ return generator.emitPrivateFieldGet(dst, base, identifier()); >+ >+ if (m_base->isSuperNode()) { >+ if (!thisValue) >+ thisValue = generator.ensureThis(); >+ return generator.emitGetById(dst, base, thisValue.get(), m_ident); >+ } >+ >+ return generator.emitGetById(dst, base, m_ident); >+} >+ >+RegisterID* BaseDotNode::emitGetPropertyValue(BytecodeGenerator& generator, RegisterID* dst, RegisterID* base) >+{ >+ RefPtr<RegisterID> thisValue; >+ return emitGetPropertyValue(generator, dst, base, thisValue); >+} >+ >+RegisterID* BaseDotNode::emitPutProperty(BytecodeGenerator& generator, RegisterID* base, RegisterID* value, RefPtr<RegisterID>& thisValue) >+{ >+ if (isPrivateName()) >+ return generator.emitPrivateFieldSet(base, m_ident, value); >+ >+ if (m_base->isSuperNode()) { >+ if (!thisValue) >+ thisValue = generator.ensureThis(); >+ return generator.emitPutById(base, thisValue.get(), m_ident, value); >+ } >+ >+ return generator.emitPutById(base, m_ident, value); >+} >+ >+RegisterID* BaseDotNode::emitPutProperty(BytecodeGenerator& generator, RegisterID* base, RegisterID* value) >+{ >+ RefPtr<RegisterID> thisValue; >+ return emitPutProperty(generator, base, value, thisValue); >+} >+ > // ------------------------------ ArgumentListNode ----------------------------- > > RegisterID* ArgumentListNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) >@@ -890,7 +965,11 @@ RegisterID* FunctionCallValueNode::emitBytecode(BytecodeGenerator& generator, Re > > if (generator.isDerivedConstructorContext() || doWeUseArrowFunctionInConstructor) > generator.emitPutThisToArrowFunctionContextScope(); >- >+ >+ // Initialize instance fields after super-call. >+ func = generator.emitLoadDerivedConstructor(); >+ generator.emitInstanceFieldInitializationIfNeeded(generator.thisRegister(), func.get(), divot(), divotStart(), divotEnd()); >+ > return ret; > } > RefPtr<RegisterID> func = generator.emitNode(m_expr); >@@ -2546,11 +2625,7 @@ RegisterID* AssignDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID > RefPtr<RegisterID> result = generator.emitNode(value.get(), m_right); > generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); > RefPtr<RegisterID> forwardResult = (dst == generator.ignoredResult()) ? result.get() : generator.move(generator.tempDestination(result.get()), result.get()); >- if (m_base->isSuperNode()) { >- RefPtr<RegisterID> thisValue = generator.ensureThis(); >- generator.emitPutById(base.get(), thisValue.get(), m_ident, forwardResult.get()); >- } else >- generator.emitPutById(base.get(), m_ident, forwardResult.get()); >+ emitPutProperty(generator, base.get(), forwardResult.get()); > generator.emitProfileType(forwardResult.get(), divotStart(), divotEnd()); > return generator.move(dst, forwardResult.get()); > } >@@ -2562,21 +2637,13 @@ RegisterID* ReadModifyDotNode::emitBytecode(BytecodeGenerator& generator, Regist > RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_rightHasAssignments, m_right->isPure(generator)); > > generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd()); >- RefPtr<RegisterID> value; > RefPtr<RegisterID> thisValue; >- if (m_base->isSuperNode()) { >- thisValue = generator.ensureThis(); >- value = generator.emitGetById(generator.tempDestination(dst), base.get(), thisValue.get(), m_ident); >- } else >- value = generator.emitGetById(generator.tempDestination(dst), base.get(), m_ident); >+ RefPtr<RegisterID> value = emitGetPropertyValue(generator, generator.tempDestination(dst), base.get(), thisValue); >+ > RegisterID* updatedValue = emitReadModifyAssignment(generator, generator.finalDestination(dst, value.get()), value.get(), m_right, static_cast<JSC::Operator>(m_operator), OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); > > generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); >- RegisterID* ret; >- if (m_base->isSuperNode()) >- ret = generator.emitPutById(base.get(), thisValue.get(), m_ident, updatedValue); >- else >- ret = generator.emitPutById(base.get(), m_ident, updatedValue); >+ RegisterID* ret = emitPutProperty(generator, base.get(), updatedValue, thisValue); > generator.emitProfileType(updatedValue, divotStart(), divotEnd()); > return ret; > } >@@ -3960,6 +4027,51 @@ RegisterID* AwaitExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID > return generator.move(generator.finalDestination(dst), value.get()); > } > >+// ------------------------------ DefineFieldNode --------------------------------- >+ >+void DefineFieldNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) >+{ >+ RefPtr<RegisterID> value = generator.newTemporary(); >+ >+ if (!m_assign) >+ generator.emitLoad(value.get(), jsUndefined()); >+ else { >+ generator.emitNode(value.get(), m_assign); >+ if (m_ident && generator.shouldEmitSetFunctionName(m_assign)) >+ generator.emitSetFunctionName(value.get(), *m_ident); >+ } >+ >+ switch (m_type) { >+ case DefineFieldNode::Name: { >+ Optional<uint32_t> optionalIndex = parseIndex(*m_ident); >+ if (!optionalIndex) >+ generator.emitDirectPutById(generator.thisRegister(), *m_ident, value.get(), PropertyNode::Unknown); >+ else { >+ RefPtr<RegisterID> propertyIndex = generator.emitLoad(nullptr, jsNumber(optionalIndex.value())); >+ generator.emitDirectPutByVal(generator.thisRegister(), propertyIndex.get(), value.get()); >+ } >+ break; >+ } >+ case DefineFieldNode::PrivateName: { >+ generator.emitExpressionInfo(position(), position(), position() + m_ident->length()); >+ generator.emitPrivateFieldAdd(generator.thisRegister(), *m_ident, value.get()); >+ break; >+ } >+ case DefineFieldNode::ComputedName: { >+ Variable var = generator.variable(*m_ident); >+ ASSERT_WITH_MESSAGE(!var.local(), "Computed names must be stored in captured variables"); >+ >+ generator.emitExpressionInfo(position(), position(), position() + 1); >+ RefPtr<RegisterID> scope = generator.emitResolveScope(generator.newTemporary(), var); >+ RefPtr<RegisterID> privateName = generator.newTemporary(); >+ generator.emitGetFromScope(privateName.get(), scope.get(), var, ThrowIfNotFound); >+ generator.emitProfileType(privateName.get(), var, m_position, JSTextPosition(-1, m_position.offset + m_ident->length(), -1)); >+ generator.emitDirectPutByVal(generator.thisRegister(), privateName.get(), value.get()); >+ break; >+ } >+ } >+} >+ > // ------------------------------ ClassDeclNode --------------------------------- > > void ClassDeclNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) >@@ -3971,7 +4083,7 @@ void ClassDeclNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) > > RegisterID* ClassExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) > { >- if (!m_name.isNull()) >+ if (m_needsLexicalScope) > generator.pushLexicalScope(this, BytecodeGenerator::TDZCheckOptimization::Optimize, BytecodeGenerator::NestedScopeType::IsNested); > > RefPtr<RegisterID> superclass; >@@ -4037,14 +4149,26 @@ RegisterID* ClassExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID > RefPtr<RegisterID> prototypeNameRegister = generator.emitLoad(nullptr, propertyNames.prototype); > generator.emitCallDefineProperty(constructor.get(), prototypeNameRegister.get(), prototype.get(), nullptr, nullptr, 0, m_position); > >- if (m_classElements) >- generator.emitDefineClassElements(m_classElements, constructor.get(), prototype.get()); >+ if (m_classElements) { >+ Vector<JSTextPosition> instanceFieldLocations; >+ generator.emitDefineClassElements(m_classElements, constructor.get(), prototype.get(), instanceFieldLocations); >+ if (!instanceFieldLocations.isEmpty()) { >+ RefPtr<RegisterID> instanceFieldInitializer = generator.emitNewInstanceFieldInitializerFunction(generator.newTemporary(), WTFMove(instanceFieldLocations), m_classHeritage); > >- if (!m_name.isNull()) { >- Variable classNameVar = generator.variable(m_name); >- RELEASE_ASSERT(classNameVar.isResolved()); >- RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, classNameVar); >- generator.emitPutToScope(scope.get(), classNameVar, constructor.get(), ThrowIfNotFound, InitializationMode::Initialization); >+ // FIXME: Skip this if the initializer function isn't going to need a home object (no eval or super properties) >+ emitPutHomeObject(generator, instanceFieldInitializer.get(), prototype.get()); >+ >+ generator.emitDirectPutById(constructor.get(), generator.propertyNames().builtinNames().instanceFieldInitializerPrivateName(), instanceFieldInitializer.get(), PropertyNode::Unknown); >+ } >+ } >+ >+ if (m_needsLexicalScope) { >+ if (!m_name.isNull()) { >+ Variable classNameVar = generator.variable(m_name); >+ RELEASE_ASSERT(classNameVar.isResolved()); >+ RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, classNameVar); >+ generator.emitPutToScope(scope.get(), classNameVar, constructor.get(), ThrowIfNotFound, InitializationMode::Initialization); >+ } > generator.popLexicalScope(this); > } > >diff --git a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp >index 40533638426..4498989c1d9 100644 >--- a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp >+++ b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp >@@ -281,6 +281,10 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, const I > return CanCompile; > > case op_yield: >+ case op_add_private_field: // TODO: add JIT support for private field ops >+ case op_get_private_field: >+ case op_put_private_field: >+ case op_to_property_key: > case llint_program_prologue: > case llint_eval_prologue: > case llint_module_program_prologue: >diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp >index 8e8256028b1..ac3d1561c4e 100644 >--- a/Source/JavaScriptCore/interpreter/Interpreter.cpp >+++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp >@@ -133,7 +133,9 @@ JSValue eval(CallFrame* callFrame) > } > > EvalContextType evalContextType; >- if (isFunctionParseMode(callerUnlinkedCodeBlock->parseMode())) >+ if (callerUnlinkedCodeBlock->parseMode() == SourceParseMode::InstanceFieldInitializerMode) >+ evalContextType = EvalContextType::InstanceFieldEvalContext; >+ else if (isFunctionParseMode(callerUnlinkedCodeBlock->parseMode())) > evalContextType = EvalContextType::FunctionEvalContext; > else if (callerUnlinkedCodeBlock->codeType() == EvalCode) > evalContextType = callerUnlinkedCodeBlock->evalContextType(); >diff --git a/Source/JavaScriptCore/jit/JIT.cpp b/Source/JavaScriptCore/jit/JIT.cpp >index 5d6e07773dc..40c2afd5afc 100644 >--- a/Source/JavaScriptCore/jit/JIT.cpp >+++ b/Source/JavaScriptCore/jit/JIT.cpp >@@ -419,6 +419,9 @@ void JIT::privateCompileMainPass() > DEFINE_OP(op_put_getter_setter_by_id) > DEFINE_OP(op_put_getter_by_val) > DEFINE_OP(op_put_setter_by_val) >+ DEFINE_OP(op_get_private_field) >+ DEFINE_OP(op_add_private_field) >+ DEFINE_OP(op_put_private_field) > > DEFINE_OP(op_ret) > DEFINE_OP(op_rshift) >@@ -584,6 +587,9 @@ void JIT::privateCompileSlowCases() > DEFINE_SLOWCASE_SLOW_OP(has_structure_property) > DEFINE_SLOWCASE_SLOW_OP(resolve_scope) > DEFINE_SLOWCASE_SLOW_OP(check_tdz) >+ DEFINE_SLOWCASE_SLOW_OP(add_private_field) >+ DEFINE_SLOWCASE_SLOW_OP(get_private_field) >+ DEFINE_SLOWCASE_SLOW_OP(put_private_field) > > default: > RELEASE_ASSERT_NOT_REACHED(); >diff --git a/Source/JavaScriptCore/jit/JIT.h b/Source/JavaScriptCore/jit/JIT.h >index a6aa326b9a2..b3d66731b60 100644 >--- a/Source/JavaScriptCore/jit/JIT.h >+++ b/Source/JavaScriptCore/jit/JIT.h >@@ -634,6 +634,9 @@ namespace JSC { > void emit_op_enumerator_generic_pname(const Instruction*); > void emit_op_log_shadow_chicken_prologue(const Instruction*); > void emit_op_log_shadow_chicken_tail(const Instruction*); >+ void emit_op_add_private_field(const Instruction*); >+ void emit_op_get_private_field(const Instruction*); >+ void emit_op_put_private_field(const Instruction*); > > void emitSlow_op_add(const Instruction*, Vector<SlowCaseEntry>::iterator&); > void emitSlow_op_call(const Instruction*, Vector<SlowCaseEntry>::iterator&); >diff --git a/Source/JavaScriptCore/jit/JITOpcodes.cpp b/Source/JavaScriptCore/jit/JITOpcodes.cpp >index 378dc505f70..186c52a131a 100644 >--- a/Source/JavaScriptCore/jit/JITOpcodes.cpp >+++ b/Source/JavaScriptCore/jit/JITOpcodes.cpp >@@ -1566,6 +1566,19 @@ void JIT::emit_op_get_argument(const Instruction* currentInstruction) > emitPutVirtualRegister(dst, resultRegs); > } > >+void JIT::emit_op_add_private_field(const Instruction*) >+{ >+ addSlowCase(jump()); >+} >+void JIT::emit_op_get_private_field(const Instruction*) >+{ >+ addSlowCase(jump()); >+} >+void JIT::emit_op_put_private_field(const Instruction*) >+{ >+ addSlowCase(jump()); >+} >+ > } // namespace JSC > > #endif // ENABLE(JIT) >diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm >index bf2bc2dd181..402ebedd131 100644 >--- a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm >+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm >@@ -1876,6 +1876,19 @@ llintOpWithReturn(op_to_primitive, OpToPrimitive, macro (size, get, dispatch, re > end) > > >+llintOpWithReturn(op_to_property_key, OpToPropertyKey, macro (size, get, dispatch, return) >+ get(m_src, t2) >+ loadConstantOrVariable(size, t2, t1, t0) >+ bineq t1, CellTag, .opToPropertyKeySlow >+ bbgt JSCell::m_type[t0], SymbolType, .opToPropertyKeySlow >+ return(t1, t0) >+ >+.opToPropertyKeySlow: >+ callSlowPath(_slow_path_to_property_key) >+ dispatch() >+end) >+ >+ > commonOp(llint_op_catch, macro() end, macro (size) > # This is where we end up from the JIT's throw trampoline (because the > # machine code return address will be set to _llint_op_catch), and from >@@ -2515,3 +2528,74 @@ llintOp(op_log_shadow_chicken_tail, OpLogShadowChickenTail, macro (size, get, di > callSlowPath(_llint_slow_path_log_shadow_chicken_tail) > dispatch() > end) >+ >+ >+llintOpWithMetadata(op_add_private_field, OpAddPrivateField, macro (size, get, dispatch, metadata, return) >+ get(m_base, t0) >+ loadConstantOrVariablePayload(size, t0, CellTag, t3, .opAddPrivateFieldSlow) >+ >+ # Take the slow path if the cached structure != the current object's structure >+ metadata(t5, t0) >+ loadi OpAddPrivateField::Metadata::m_oldStructureID[t5], t0 >+ bineq JSCell::m_structureID[t3], t0, .opAddPrivateFieldSlow >+ >+ # Transition to new structure >+ # (Can we be sure there's room in the existing backing store for non-inline fields?) >+ loadi OpAddPrivateField::Metadata::m_newStructureID[t5], t0 >+ storei t0, JSCell::m_structureID[t3] >+ writeBarrierOnOperand(size, get, m_base) >+ >+ # Store {value} in the cached offset >+ get(m_value, t1) >+ loadConstantOrVariable(size, t1, t2, t0) >+ loadi OpAddPrivateField::Metadata::m_offset[t5], t1 >+ storePropertyAtVariableOffset(t1, t3, t2, t0) >+ writeBarrierOnOperand(size, get, m_base) >+ dispatch() >+ >+.opAddPrivateFieldSlow: >+ callSlowPath(_slow_path_add_private_field) >+ dispatch() >+end) >+ >+ >+llintOpWithMetadata(op_get_private_field, OpGetPrivateField, macro (size, get, dispatch, metadata, return) >+ get(m_base, t0) >+ loadConstantOrVariablePayload(size, t0, CellTag, t3, .opGetPrivateFieldSlow) >+ >+ # Take the slow path if the cached structure != the current object's structure >+ metadata(t5, t0) >+ loadi OpGetPrivateField::Metadata::m_structureID[t5], t0 >+ bineq JSCell::m_structureID[t3], t0, .opGetPrivateFieldSlow >+ >+ # Load the field at {offset} >+ loadi OpGetPrivateField::Metadata::m_offset[t5], t0 >+ loadPropertyAtVariableOffset(t0, t3, t1, t2) >+ valueProfile(OpGetPrivateField, t5, t1, t2) >+ return(t1, t2) >+ >+.opGetPrivateFieldSlow: >+ callSlowPath(_slow_path_get_private_field) >+ dispatch() >+end) >+ >+ >+llintOpWithMetadata(op_put_private_field, OpPutPrivateField, macro (size, get, dispatch, metadata, return) >+ get(m_base, t0) >+ loadConstantOrVariablePayload(size, t0, CellTag, t3, .opPutPrivateFieldSlow) >+ >+ # Take the slow path if the cached structure != the current object's structure >+ metadata(t5, t0) >+ loadi OpGetPrivateField::Metadata::m_structureID[t5], t0 >+ bineq JSCell::m_structureID[t3], t0, .opPutPrivateFieldSlow >+ >+ get(m_value, t0) >+ loadConstantOrVariable(size, t0, t1, t2) >+ loadi OpGetPrivateField::Metadata::m_offset[t5], t0 >+ storePropertyAtVariableOffset(t0, t3, t1, t2) >+ dispatch() >+ >+.opPutPrivateFieldSlow: >+ callSlowPath(_slow_path_put_private_field) >+ dispatch() >+end) >diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm >index c29717dec9d..7250029e091 100644 >--- a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm >+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm >@@ -1971,6 +1971,20 @@ llintOpWithReturn(op_to_primitive, OpToPrimitive, macro (size, get, dispatch, re > end) > > >+llintOpWithReturn(op_to_property_key, OpToPropertyKey, macro (size, get, dispatch, return) >+ get(m_src, t2) >+ loadConstantOrVariable(size, t2, t0) >+ >+ btqnz t0, tagMask, .opToPropertyKeySlow >+ bbgt JSCell::m_type[t0], SymbolType, .opToPropertyKeySlow >+ return(t0) >+ >+.opToPropertyKeySlow: >+ callSlowPath(_slow_path_to_property_key) >+ dispatch() >+end) >+ >+ > commonOp(llint_op_catch, macro() end, macro (size) > # This is where we end up from the JIT's throw trampoline (because the > # machine code return address will be set to _llint_op_catch), and from >@@ -2567,3 +2581,74 @@ llintOp(op_log_shadow_chicken_tail, OpLogShadowChickenTail, macro (size, get, di > callSlowPath(_llint_slow_path_log_shadow_chicken_tail) > dispatch() > end) >+ >+ >+llintOpWithMetadata(op_add_private_field, OpAddPrivateField, macro (size, get, dispatch, metadata, return) >+ get(m_base, t0) >+ loadConstantOrVariableCell(size, t0, t3, .opAddPrivateFieldSlow) >+ >+ # Take the slow path if the cached structure != the current object's structure >+ metadata(t5, t0) >+ loadi OpAddPrivateField::Metadata::m_oldStructureID[t5], t0 >+ bineq JSCell::m_structureID[t3], t0, .opAddPrivateFieldSlow >+ >+ # Transition to new structure >+ # (Can we be sure there's room in the existing backing store for non-inline fields?) >+ loadi OpAddPrivateField::Metadata::m_newStructureID[t5], t0 >+ storei t0, JSCell::m_structureID[t3] >+ writeBarrierOnOperand(size, get, m_base) >+ >+ # Store {value} in the cached offset >+ get(m_value, t1) >+ loadConstantOrVariable(size, t1, t2) >+ loadi OpAddPrivateField::Metadata::m_offset[t5], t1 >+ storePropertyAtVariableOffset(t1, t3, t2) >+ writeBarrierOnOperand(size, get, m_base) >+ dispatch() >+ >+.opAddPrivateFieldSlow: >+ callSlowPath(_slow_path_add_private_field) >+ dispatch() >+end) >+ >+ >+llintOpWithMetadata(op_get_private_field, OpGetPrivateField, macro (size, get, dispatch, metadata, return) >+ get(m_base, t0) >+ loadConstantOrVariableCell(size, t0, t3, .opGetPrivateFieldSlow) >+ >+ # Take the slow path if the cached structure != the current object's structure >+ metadata(t5, t0) >+ loadi OpGetPrivateField::Metadata::m_structureID[t5], t0 >+ bineq JSCell::m_structureID[t3], t0, .opGetPrivateFieldSlow >+ >+ # Load the field at {offset} >+ loadi OpGetPrivateField::Metadata::m_offset[t5], t0 >+ loadPropertyAtVariableOffset(t0, t3, t1) >+ valueProfile(OpGetPrivateField, t5, t1) >+ return(t1) >+ >+.opGetPrivateFieldSlow: >+ callSlowPath(_slow_path_get_private_field) >+ dispatch() >+end) >+ >+ >+llintOpWithMetadata(op_put_private_field, OpPutPrivateField, macro (size, get, dispatch, metadata, return) >+ get(m_base, t0) >+ loadConstantOrVariableCell(size, t0, t3, .opPutPrivateFieldSlow) >+ >+ # Take the slow path if the cached structure != the current object's structure >+ metadata(t5, t0) >+ loadi OpGetPrivateField::Metadata::m_structureID[t5], t0 >+ bineq JSCell::m_structureID[t3], t0, .opPutPrivateFieldSlow >+ >+ get(m_value, t0) >+ loadConstantOrVariable(size, t0, t1) >+ loadi OpGetPrivateField::Metadata::m_offset[t5], t0 >+ storePropertyAtVariableOffset(t0, t3, t1) >+ dispatch() >+ >+.opPutPrivateFieldSlow: >+ callSlowPath(_slow_path_put_private_field) >+ dispatch() >+end) >diff --git a/Source/JavaScriptCore/parser/ASTBuilder.h b/Source/JavaScriptCore/parser/ASTBuilder.h >index 622d291842a..b470cd3f438 100644 >--- a/Source/JavaScriptCore/parser/ASTBuilder.h >+++ b/Source/JavaScriptCore/parser/ASTBuilder.h >@@ -267,12 +267,12 @@ public: > return node; > } > >- ExpressionNode* createDotAccess(const JSTokenLocation& location, ExpressionNode* base, const Identifier* property, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end) >+ ExpressionNode* createDotAccess(const JSTokenLocation& location, ExpressionNode* base, const Identifier* property, DotType type, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end) > { > if (base->isSuperNode()) > usesSuperProperty(); > >- DotAccessorNode* node = new (m_parserArena) DotAccessorNode(location, base, *property); >+ DotAccessorNode* node = new (m_parserArena) DotAccessorNode(location, base, *property, type); > setExceptionLocation(node, start, divot, end); > return node; > } >@@ -396,6 +396,11 @@ public: > return node; > } > >+ DefineFieldNode* createDefineField(const JSTokenLocation& location, const Identifier* ident, ExpressionNode* initializer, DefineFieldNode::Type type) >+ { >+ return new (m_parserArena) DefineFieldNode(location, ident, initializer, type); >+ } >+ > ClassExprNode* createClassExpr(const JSTokenLocation& location, const ParserClassInfo<ASTBuilder>& classInfo, VariableEnvironment& classEnvironment, ExpressionNode* constructor, > ExpressionNode* parentClass, PropertyListNode* classElements) > { >@@ -520,6 +525,7 @@ public: > return new (m_parserArena) PropertyNode(parserArena.identifierArena().makeNumericIdentifier(vm, propertyName), node, type, putType, superBinding, tag); > } > PropertyNode* createProperty(ExpressionNode* propertyName, ExpressionNode* node, PropertyNode::Type type, PropertyNode::PutType putType, bool, SuperBinding superBinding, ClassElementTag tag) { return new (m_parserArena) PropertyNode(propertyName, node, type, putType, superBinding, tag); } >+ PropertyNode* createProperty(const Identifier* identifier, ExpressionNode* propertyName, ExpressionNode* node, PropertyNode::Type type, PropertyNode::PutType putType, bool, SuperBinding superBinding, ClassElementTag tag) { return new (m_parserArena) PropertyNode(*identifier, propertyName, node, type, putType, superBinding, tag); } > PropertyListNode* createPropertyList(const JSTokenLocation& location, PropertyNode* property) { return new (m_parserArena) PropertyListNode(location, property); } > PropertyListNode* createPropertyList(const JSTokenLocation& location, PropertyNode* property, PropertyListNode* tail) { return new (m_parserArena) PropertyListNode(location, property, tail); } > >@@ -633,6 +639,11 @@ public: > return pattern->isAssignmentLocation(); > } > >+ bool isPrivateLocation(const Expression& node) >+ { >+ return node->isPrivateLocation(); >+ } >+ > bool isObjectLiteral(const Expression& node) > { > return node->isObjectLiteral(); >@@ -1503,10 +1514,10 @@ ExpressionNode* ASTBuilder::makeAssignNode(const JSTokenLocation& location, Expr > // function should not pick up the name of the dot->identifier(). > static_cast<BaseFuncExprNode*>(expr)->metadata()->setInferredName(dot->identifier()); > } >- return new (m_parserArena) AssignDotNode(location, dot->base(), dot->identifier(), expr, exprHasAssignments, dot->divot(), start, end); >+ return new (m_parserArena) AssignDotNode(location, dot->base(), dot->identifier(), dot->type(), expr, exprHasAssignments, dot->divot(), start, end); > } > >- ReadModifyDotNode* node = new (m_parserArena) ReadModifyDotNode(location, dot->base(), dot->identifier(), op, expr, exprHasAssignments, divot, start, end); >+ ReadModifyDotNode* node = new (m_parserArena) ReadModifyDotNode(location, dot->base(), dot->identifier(), dot->type(), op, expr, exprHasAssignments, divot, start, end); > node->setSubexpressionInfo(dot->divot(), dot->divotEnd().offset); > return node; > } >diff --git a/Source/JavaScriptCore/parser/Lexer.cpp b/Source/JavaScriptCore/parser/Lexer.cpp >index 122c4d91d4b..890e0eb6ad5 100644 >--- a/Source/JavaScriptCore/parser/Lexer.cpp >+++ b/Source/JavaScriptCore/parser/Lexer.cpp >@@ -94,7 +94,8 @@ enum CharacterType { > > // Other types (only one so far) > CharacterWhiteSpace, >- CharacterPrivateIdentifierStart >+ CharacterPrivateIdentifierStart, >+ CharacterPrivateNameStart > }; > > // 256 Latin-1 codes >@@ -134,7 +135,7 @@ static constexpr const unsigned short typesOfLatin1Characters[256] = { > /* 32 - Space */ CharacterWhiteSpace, > /* 33 - ! */ CharacterExclamationMark, > /* 34 - " */ CharacterQuote, >-/* 35 - # */ CharacterInvalid, >+/* 35 - # */ CharacterPrivateNameStart, > /* 36 - $ */ CharacterIdentifierStart, > /* 37 - % */ CharacterModulo, > /* 38 - & */ CharacterAnd, >@@ -948,14 +949,18 @@ template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType Lexer<LChar>::p > return keyword == RESERVED_IF_STRICT && !strictMode ? IDENT : keyword; > } > } >- >- bool isPrivateName = m_current == '@' && m_parsingBuiltinFunction; >- if (isPrivateName) >+ >+ bool isPrivateName = m_current == '#'; >+ bool isPrivateIdentifier = m_current == '@' && m_parsingBuiltinFunction; >+ if (isPrivateIdentifier) > shift(); > > const LChar* identifierStart = currentSourcePtr(); > unsigned identifierLineStart = currentLineStartOffset(); >- >+ >+ if (isPrivateName) >+ shift(); >+ > while (isIdentPart(m_current)) > shift(); > >@@ -970,11 +975,11 @@ template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType Lexer<LChar>::p > int identifierLength = currentSourcePtr() - identifierStart; > ident = makeIdentifier(identifierStart, identifierLength); > if (m_parsingBuiltinFunction) { >- if (!isSafeBuiltinIdentifier(*m_vm, ident) && !isPrivateName) { >+ if (!isSafeBuiltinIdentifier(*m_vm, ident) && !isPrivateIdentifier) { > m_lexErrorMessage = makeString("The use of '", ident->string(), "' is disallowed in builtin functions."); > return ERRORTOK; > } >- if (isPrivateName) >+ if (isPrivateIdentifier) > ident = m_vm->propertyNames->lookUpPrivateName(*ident); > else if (*ident == m_vm->propertyNames->undefinedKeyword) > tokenData->ident = &m_vm->propertyNames->builtinNames().undefinedPrivateName(); >@@ -985,20 +990,21 @@ template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType Lexer<LChar>::p > } else > tokenData->ident = nullptr; > >- if (UNLIKELY((remaining < maxTokenLength) && !(lexerFlags & LexerFlagsIgnoreReservedWords)) && !isPrivateName) { >+ auto identType = isPrivateName ? PRIVATENAME : IDENT; >+ if (UNLIKELY((remaining < maxTokenLength) && !(lexerFlags & LexerFlagsIgnoreReservedWords)) && !isPrivateIdentifier) { > ASSERT(shouldCreateIdentifier); > if (remaining < maxTokenLength) { > const HashTableValue* entry = JSC::mainTable.entry(*ident); > ASSERT((remaining < maxTokenLength) || !entry); > if (!entry) >- return IDENT; >+ return identType; > JSTokenType token = static_cast<JSTokenType>(entry->lexerValue()); >- return (token != RESERVED_IF_STRICT) || strictMode ? token : IDENT; >+ return (token != RESERVED_IF_STRICT) || strictMode ? token : identType; > } >- return IDENT; >+ return identType; > } > >- return IDENT; >+ return identType; > } > > template <> >@@ -1013,23 +1019,27 @@ template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType Lexer<UChar>::p > return keyword == RESERVED_IF_STRICT && !strictMode ? IDENT : keyword; > } > } >- >- bool isPrivateName = m_current == '@' && m_parsingBuiltinFunction; >- if (isPrivateName) >+ >+ bool isPrivateName = m_current == '#'; >+ bool isPrivateIdentifier = m_current == '@' && m_parsingBuiltinFunction; >+ if (isPrivateIdentifier) > shift(); > > const UChar* identifierStart = currentSourcePtr(); > int identifierLineStart = currentLineStartOffset(); > > UChar orAllChars = 0; >- >+ >+ if (isPrivateName) >+ shift(); >+ > while (isIdentPart(m_current)) { > orAllChars |= m_current; > shift(); > } > > if (UNLIKELY(m_current == '\\')) { >- ASSERT(!isPrivateName); >+ ASSERT(!isPrivateIdentifier); > setOffsetFromSourcePtr(identifierStart, identifierLineStart); > return parseIdentifierSlowCase<shouldCreateIdentifier>(tokenData, lexerFlags, strictMode); > } >@@ -1048,11 +1058,11 @@ template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType Lexer<UChar>::p > else > ident = makeIdentifier(identifierStart, identifierLength); > if (m_parsingBuiltinFunction) { >- if (!isSafeBuiltinIdentifier(*m_vm, ident) && !isPrivateName) { >+ if (!isSafeBuiltinIdentifier(*m_vm, ident) && !isPrivateIdentifier) { > m_lexErrorMessage = makeString("The use of '", ident->string(), "' is disallowed in builtin functions."); > return ERRORTOK; > } >- if (isPrivateName) >+ if (isPrivateIdentifier) > ident = m_vm->propertyNames->lookUpPrivateName(*ident); > else if (*ident == m_vm->propertyNames->undefinedKeyword) > tokenData->ident = &m_vm->propertyNames->builtinNames().undefinedPrivateName(); >@@ -1063,7 +1073,7 @@ template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType Lexer<UChar>::p > } else > tokenData->ident = nullptr; > >- if (UNLIKELY((remaining < maxTokenLength) && !(lexerFlags & LexerFlagsIgnoreReservedWords)) && !isPrivateName) { >+ if (UNLIKELY((remaining < maxTokenLength) && !(lexerFlags & LexerFlagsIgnoreReservedWords)) && !isPrivateIdentifier) { > ASSERT(shouldCreateIdentifier); > if (remaining < maxTokenLength) { > const HashTableValue* entry = JSC::mainTable.entry(*ident); >@@ -1083,8 +1093,13 @@ template<typename CharacterType> template<bool shouldCreateIdentifier> JSTokenTy > { > tokenData->escaped = true; > auto identifierStart = currentSourcePtr(); >+ bool isPrivateName = *identifierStart == '#'; >+ auto identType = isPrivateName ? PRIVATENAME : IDENT; > bool bufferRequired = false; > >+ if (isPrivateName) >+ shift(); >+ > while (true) { > if (LIKELY(isIdentPart(m_current))) { > shift(); >@@ -1104,7 +1119,8 @@ template<typename CharacterType> template<bool shouldCreateIdentifier> JSTokenTy > auto character = parseUnicodeEscape(); > if (UNLIKELY(!character.isValid())) > return character.isIncomplete() ? UNTERMINATED_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK : INVALID_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK; >- if (UNLIKELY(m_buffer16.size() ? !isIdentPart(character.value()) : !isIdentStart(character.value()))) >+ bool isIdentifierStart = isPrivateName ? m_buffer16.size() == 1: !m_buffer16.size(); >+ if (UNLIKELY(isIdentifierStart ? !isIdentStart(character.value()) : !isIdentPart(character.value()))) > return INVALID_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK; > if (shouldCreateIdentifier) > recordUnicodeCodePoint(character.value()); >@@ -1133,13 +1149,13 @@ template<typename CharacterType> template<bool shouldCreateIdentifier> JSTokenTy > ASSERT(shouldCreateIdentifier); > const HashTableValue* entry = JSC::mainTable.entry(*ident); > if (!entry) >- return IDENT; >+ return identType; > JSTokenType token = static_cast<JSTokenType>(entry->lexerValue()); > if ((token != RESERVED_IF_STRICT) || strictMode) > return bufferRequired ? UNEXPECTED_ESCAPE_ERRORTOK : token; > } > >- return IDENT; >+ return identType; > } > > static ALWAYS_INLINE bool characterRequiresParseStringSlowCase(LChar character) >@@ -2310,6 +2326,14 @@ start: > m_terminator = true; > m_lineStart = m_code; > goto start; >+ case CharacterPrivateNameStart: { >+ auto next = peek(1); >+ if (isIdentStart(next) || next == '\\') { >+ lexerFlags &= ~LexexFlagsDontBuildKeywords; >+ goto parseIdent; >+ } >+ goto invalidCharacter; >+ } > case CharacterPrivateIdentifierStart: > if (m_parsingBuiltinFunction) > goto parseIdent; >@@ -2317,6 +2341,7 @@ start: > FALLTHROUGH; > case CharacterOtherIdentifierPart: > case CharacterInvalid: >+ invalidCharacter: > m_lexErrorMessage = invalidCharacterMessage(); > token = ERRORTOK; > goto returnError; >diff --git a/Source/JavaScriptCore/parser/NodeConstructors.h b/Source/JavaScriptCore/parser/NodeConstructors.h >index a2b8c5f0fc9..daa8a773668 100644 >--- a/Source/JavaScriptCore/parser/NodeConstructors.h >+++ b/Source/JavaScriptCore/parser/NodeConstructors.h >@@ -248,10 +248,12 @@ namespace JSC { > > inline PropertyNode::PropertyNode(const Identifier& name, ExpressionNode* assign, Type type, PutType putType, SuperBinding superBinding, ClassElementTag tag) > : m_name(&name) >+ , m_expression(nullptr) > , m_assign(assign) > , m_type(type) > , m_needsSuperBinding(superBinding == SuperBinding::Needed) > , m_putType(putType) >+ , m_isPrivate(!!(type & Private)) > , m_classElementTag(static_cast<unsigned>(tag)) > , m_isOverriddenByDuplicate(false) > { >@@ -259,10 +261,12 @@ namespace JSC { > > inline PropertyNode::PropertyNode(ExpressionNode* assign, Type type, PutType putType, SuperBinding superBinding, ClassElementTag tag) > : m_name(nullptr) >+ , m_expression(nullptr) > , m_assign(assign) > , m_type(type) > , m_needsSuperBinding(superBinding == SuperBinding::Needed) > , m_putType(putType) >+ , m_isPrivate(!!(type & Private)) > , m_classElementTag(static_cast<unsigned>(tag)) > , m_isOverriddenByDuplicate(false) > { >@@ -275,6 +279,20 @@ namespace JSC { > , m_type(type) > , m_needsSuperBinding(superBinding == SuperBinding::Needed) > , m_putType(putType) >+ , m_isPrivate(!!(type & Private)) >+ , m_classElementTag(static_cast<unsigned>(tag)) >+ , m_isOverriddenByDuplicate(false) >+ { >+ } >+ >+ inline PropertyNode::PropertyNode(const Identifier& ident, ExpressionNode* name, ExpressionNode* assign, Type type, PutType putType, SuperBinding superBinding, ClassElementTag tag) >+ : m_name(&ident) >+ , m_expression(name) >+ , m_assign(assign) >+ , m_type(type) >+ , m_needsSuperBinding(superBinding == SuperBinding::Needed) >+ , m_putType(putType) >+ , m_isPrivate(!!(type & Private)) > , m_classElementTag(static_cast<unsigned>(tag)) > , m_isOverriddenByDuplicate(false) > { >@@ -313,10 +331,16 @@ namespace JSC { > { > } > >- inline DotAccessorNode::DotAccessorNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident) >+ inline BaseDotNode::BaseDotNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident, DotType type) > : ExpressionNode(location) > , m_base(base) > , m_ident(ident) >+ , m_type(type) >+ { >+ } >+ >+ inline DotAccessorNode::DotAccessorNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident, DotType type) >+ : BaseDotNode(location, base, ident, type) > { > } > >@@ -719,21 +743,17 @@ namespace JSC { > { > } > >- inline AssignDotNode::AssignDotNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident, ExpressionNode* right, bool rightHasAssignments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) >- : ExpressionNode(location) >+ inline AssignDotNode::AssignDotNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident, DotType type, ExpressionNode* right, bool rightHasAssignments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) >+ : BaseDotNode(location, base, ident, type) > , ThrowableExpressionData(divot, divotStart, divotEnd) >- , m_base(base) >- , m_ident(ident) > , m_right(right) > , m_rightHasAssignments(rightHasAssignments) > { > } > >- inline ReadModifyDotNode::ReadModifyDotNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident, Operator oper, ExpressionNode* right, bool rightHasAssignments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) >- : ExpressionNode(location) >+ inline ReadModifyDotNode::ReadModifyDotNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident, DotType type, Operator oper, ExpressionNode* right, bool rightHasAssignments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) >+ : BaseDotNode(location, base, ident, type) > , ThrowableSubExpressionData(divot, divotStart, divotEnd) >- , m_base(base) >- , m_ident(ident) > , m_right(right) > , m_operator(oper) > , m_rightHasAssignments(rightHasAssignments) >@@ -983,6 +1003,14 @@ namespace JSC { > { > } > >+ inline DefineFieldNode::DefineFieldNode(const JSTokenLocation& location, const Identifier* ident, ExpressionNode* assign, Type type) >+ : StatementNode(location) >+ , m_ident(ident) >+ , m_assign(assign) >+ , m_type(type) >+ { >+ } >+ > inline ClassDeclNode::ClassDeclNode(const JSTokenLocation& location, ExpressionNode* classDeclaration) > : StatementNode(location) > , m_classDeclaration(classDeclaration) >@@ -998,6 +1026,7 @@ namespace JSC { > , m_constructorExpression(constructorExpression) > , m_classHeritage(classHeritage) > , m_classElements(classElements) >+ , m_needsLexicalScope(!name.isNull() || PropertyListNode::shouldCreateLexicalScopeForClass(classElements)) > { > } > >diff --git a/Source/JavaScriptCore/parser/Nodes.cpp b/Source/JavaScriptCore/parser/Nodes.cpp >index 6143efd6233..33bbc143db9 100644 >--- a/Source/JavaScriptCore/parser/Nodes.cpp >+++ b/Source/JavaScriptCore/parser/Nodes.cpp >@@ -328,6 +328,16 @@ bool PropertyListNode::hasStaticallyNamedProperty(const Identifier& propName) > return false; > } > >+bool PropertyListNode::shouldCreateLexicalScopeForClass(PropertyListNode* list) >+{ >+ while (list) { >+ if (list->m_node->isComputedClassField() || list->m_node->isPrivate()) >+ return true; >+ list = list->m_next; >+ } >+ return false; >+} >+ > VariableEnvironmentNode::VariableEnvironmentNode(VariableEnvironment& lexicalVariables) > { > m_lexicalVariables.swap(lexicalVariables); >diff --git a/Source/JavaScriptCore/parser/Nodes.h b/Source/JavaScriptCore/parser/Nodes.h >index 45e7fb17141..b1e29038fd6 100644 >--- a/Source/JavaScriptCore/parser/Nodes.h >+++ b/Source/JavaScriptCore/parser/Nodes.h >@@ -181,6 +181,7 @@ namespace JSC { > virtual bool isPure(BytecodeGenerator&) const { return false; } > virtual bool isConstant() const { return false; } > virtual bool isLocation() const { return false; } >+ virtual bool isPrivateLocation() const { return false; } > virtual bool isAssignmentLocation() const { return isLocation(); } > virtual bool isResolveNode() const { return false; } > virtual bool isAssignResolveNode() const { return false; } >@@ -243,6 +244,7 @@ namespace JSC { > virtual bool isFuncDeclNode() const { return false; } > virtual bool isModuleDeclarationNode() const { return false; } > virtual bool isForOfNode() const { return false; } >+ virtual bool isDefineFieldNode() const { return false; } > > protected: > int m_lastLine { -1 }; >@@ -704,12 +706,13 @@ 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 }; >+ enum Type : uint8_t { Constant = 1, Getter = 2, Setter = 4, Computed = 8, Shorthand = 16, Spread = 32, Private = 64 }; > enum PutType : uint8_t { Unknown, KnownDirect }; > > PropertyNode(const Identifier&, ExpressionNode*, Type, PutType, SuperBinding, ClassElementTag); > PropertyNode(ExpressionNode*, Type, PutType, SuperBinding, ClassElementTag); > PropertyNode(ExpressionNode* propertyName, ExpressionNode*, Type, PutType, SuperBinding, ClassElementTag); >+ PropertyNode(const Identifier&, ExpressionNode* propertyName, ExpressionNode*, Type, PutType, SuperBinding, ClassElementTag); > > ExpressionNode* expressionName() const { return m_expression; } > const Identifier* name() const { return m_name; } >@@ -719,7 +722,12 @@ namespace JSC { > bool isClassProperty() const { return static_cast<ClassElementTag>(m_classElementTag) != ClassElementTag::No; } > bool isStaticClassProperty() const { return static_cast<ClassElementTag>(m_classElementTag) == ClassElementTag::Static; } > bool isInstanceClassProperty() const { return static_cast<ClassElementTag>(m_classElementTag) == ClassElementTag::Instance; } >+ bool isClassField() const { return isClassProperty() && !needsSuperBinding(); } >+ bool isInstanceClassField() const { return isInstanceClassProperty() && !needsSuperBinding(); } > bool isOverriddenByDuplicate() const { return m_isOverriddenByDuplicate; } >+ bool isPrivate() const { return m_isPrivate; } >+ bool hasComputedName() const { return m_expression; } >+ bool isComputedClassField() const { return isClassField() && hasComputedName(); } > void setIsOverriddenByDuplicate() { m_isOverriddenByDuplicate = true; } > PutType putType() const { return static_cast<PutType>(m_putType); } > >@@ -728,12 +736,13 @@ namespace JSC { > const Identifier* m_name; > ExpressionNode* m_expression; > ExpressionNode* m_assign; >- unsigned m_type : 6; >+ unsigned m_type : 7; > unsigned m_needsSuperBinding : 1; > unsigned m_putType : 1; >+ unsigned m_isPrivate : 1; > static_assert(1 << 2 > static_cast<unsigned>(ClassElementTag::LastTag), "ClassElementTag shouldn't use more than two bits"); > unsigned m_classElementTag : 2; >- unsigned m_isOverriddenByDuplicate: 1; >+ unsigned m_isOverriddenByDuplicate : 1; > }; > > class PropertyListNode final : public ExpressionNode { >@@ -742,15 +751,26 @@ namespace JSC { > PropertyListNode(const JSTokenLocation&, PropertyNode*, PropertyListNode*); > > bool hasStaticallyNamedProperty(const Identifier& propName); >+ bool isComputedClassField() const >+ { >+ return m_node->isComputedClassField(); >+ } >+ bool isInstanceClassField() const >+ { >+ return m_node->isInstanceClassField(); >+ } > >- RegisterID* emitBytecode(BytecodeGenerator&, RegisterID*, RegisterID*); >+ static bool shouldCreateLexicalScopeForClass(PropertyListNode*); >+ >+ RegisterID* emitBytecode(BytecodeGenerator&, RegisterID*, RegisterID*, Vector<JSTextPosition>*); > > private: > RegisterID* emitBytecode(BytecodeGenerator& generator, RegisterID* dst = nullptr) override > { >- return emitBytecode(generator, dst, nullptr); >+ return emitBytecode(generator, dst, nullptr, nullptr); > } > void emitPutConstantProperty(BytecodeGenerator&, RegisterID*, PropertyNode&); >+ void emitSaveComputedFieldName(BytecodeGenerator&, PropertyNode&); > > PropertyNode* m_node; > PropertyListNode* m_next { nullptr }; >@@ -788,9 +808,30 @@ namespace JSC { > bool m_subscriptHasAssignments; > }; > >- class DotAccessorNode final : public ExpressionNode, public ThrowableExpressionData { >+ enum class DotType { Name, PrivateName }; >+ class BaseDotNode : public ExpressionNode { >+ public: >+ BaseDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, DotType); >+ >+ ExpressionNode* base() const { return m_base; } >+ const Identifier& identifier() const { return m_ident; } >+ DotType type() const { return m_type; } >+ bool isPrivateName() const { return m_type == DotType::PrivateName; } >+ >+ RegisterID* emitGetPropertyValue(BytecodeGenerator&, RegisterID* dst, RegisterID* base, RefPtr<RegisterID>& thisValue); >+ RegisterID* emitGetPropertyValue(BytecodeGenerator&, RegisterID* dst, RegisterID* base); >+ RegisterID* emitPutProperty(BytecodeGenerator&, RegisterID* base, RegisterID* value, RefPtr<RegisterID>& thisValue); >+ RegisterID* emitPutProperty(BytecodeGenerator&, RegisterID* base, RegisterID* value); >+ >+ protected: >+ ExpressionNode* m_base; >+ const Identifier& m_ident; >+ DotType m_type; >+ }; >+ >+ class DotAccessorNode final : public BaseDotNode, public ThrowableExpressionData { > public: >- DotAccessorNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&); >+ DotAccessorNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, DotType); > > ExpressionNode* base() const { return m_base; } > const Identifier& identifier() const { return m_ident; } >@@ -799,10 +840,8 @@ namespace JSC { > RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; > > bool isLocation() const override { return true; } >+ bool isPrivateLocation() const override { return m_type == DotType::PrivateName; } > bool isDotAccessorNode() const override { return true; } >- >- ExpressionNode* m_base; >- const Identifier& m_ident; > }; > > class SpreadExpressionNode final : public ExpressionNode, public ThrowableExpressionData { >@@ -1361,28 +1400,24 @@ namespace JSC { > bool m_rightHasAssignments : 1; > }; > >- class AssignDotNode final : public ExpressionNode, public ThrowableExpressionData { >+ class AssignDotNode final : public BaseDotNode, public ThrowableExpressionData { > public: >- AssignDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, ExpressionNode* right, bool rightHasAssignments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); >+ AssignDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, DotType, ExpressionNode* right, bool rightHasAssignments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); > > private: > RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; > >- ExpressionNode* m_base; >- const Identifier& m_ident; > ExpressionNode* m_right; > bool m_rightHasAssignments; > }; > >- class ReadModifyDotNode final : public ExpressionNode, public ThrowableSubExpressionData { >+ class ReadModifyDotNode final : public BaseDotNode, public ThrowableSubExpressionData { > public: >- ReadModifyDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, Operator, ExpressionNode* right, bool rightHasAssignments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); >+ ReadModifyDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, DotType, Operator, ExpressionNode* right, bool rightHasAssignments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); > > private: > RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; > >- ExpressionNode* m_base; >- const Identifier& m_ident; > ExpressionNode* m_right; > unsigned m_operator : 31; > bool m_rightHasAssignments : 1; >@@ -2154,6 +2189,21 @@ namespace JSC { > ExpressionNode* m_argument; > }; > >+ class DefineFieldNode final : public StatementNode { >+ public: >+ enum Type { Name, PrivateName, ComputedName }; >+ DefineFieldNode(const JSTokenLocation&, const Identifier*, ExpressionNode*, Type); >+ >+ private: >+ void emitBytecode(BytecodeGenerator&, RegisterID* destination = 0) override; >+ >+ bool isDefineFieldNode() const override { return true; } >+ >+ const Identifier* m_ident; >+ ExpressionNode* m_assign; >+ Type m_type; >+ }; >+ > class ClassExprNode final : public ExpressionNode, public VariableEnvironmentNode { > JSC_MAKE_PARSER_ARENA_DELETABLE_ALLOCATED(ClassExprNode); > public: >@@ -2178,6 +2228,7 @@ namespace JSC { > ExpressionNode* m_constructorExpression; > ExpressionNode* m_classHeritage; > PropertyListNode* m_classElements; >+ bool m_needsLexicalScope; > }; > > class DestructuringPatternNode : public ParserArenaFreeable { >diff --git a/Source/JavaScriptCore/parser/Parser.cpp b/Source/JavaScriptCore/parser/Parser.cpp >index 1d223bc3a12..b3cb6879238 100644 >--- a/Source/JavaScriptCore/parser/Parser.cpp >+++ b/Source/JavaScriptCore/parser/Parser.cpp >@@ -193,7 +193,7 @@ Parser<LexerType>::~Parser() > } > > template <typename LexerType> >-String Parser<LexerType>::parseInner(const Identifier& calleeName, SourceParseMode parseMode, ParsingContext parsingContext, Optional<int> functionConstructorParametersEndPosition) >+String Parser<LexerType>::parseInner(const Identifier& calleeName, SourceParseMode parseMode, ParsingContext parsingContext, Optional<int> functionConstructorParametersEndPosition, const Vector<JSTextPosition>& instanceFieldLocations) > { > String parseError = String(); > >@@ -207,6 +207,8 @@ String Parser<LexerType>::parseInner(const Identifier& calleeName, SourceParseMo > ParserFunctionInfo<ASTBuilder> functionInfo; > if (isGeneratorOrAsyncFunctionBodyParseMode(parseMode)) > m_parameters = createGeneratorParameters(context, functionInfo.parameterCount); >+ else if (parseMode == SourceParseMode::InstanceFieldInitializerMode) >+ m_parameters = context.createFormalParameterList(); > else > m_parameters = parseFunctionParameters(context, parseMode, functionInfo); > >@@ -239,6 +241,8 @@ String Parser<LexerType>::parseInner(const Identifier& calleeName, SourceParseMo > sourceElements = parseAsyncGeneratorFunctionSourceElements(context, parseMode, isArrowFunctionBodyExpression, CheckForStrictMode); > else if (parsingContext == ParsingContext::FunctionConstructor) > sourceElements = parseSingleFunction(context, functionConstructorParametersEndPosition); >+ else if (parseMode == SourceParseMode::InstanceFieldInitializerMode) >+ sourceElements = parseInstanceFieldInitializerSourceElements(context, instanceFieldLocations); > else > sourceElements = parseSourceElements(context, CheckForStrictMode); > } >@@ -816,6 +820,8 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseVariableDecl > TreeExpression node = 0; > declarations++; > bool hasInitializer = false; >+ >+ failIfTrue(match(PRIVATENAME), "Cannot parse variable declaration"); > if (matchSpecIdentifier()) { > failIfTrue(match(LET) && (declarationType == DeclarationType::LetDeclaration || declarationType == DeclarationType::ConstDeclaration), > "Cannot use 'let' as an identifier name for a LexicalDeclaration"); >@@ -1271,6 +1277,8 @@ template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDe > if (kind == DestructuringKind::DestructureToExpressions) > return 0; > semanticFailureDueToKeyword(destructuringKindToVariableKindName(kind)); >+ if (kind != DestructuringKind::DestructureToParameters) >+ failIfTrue(match(PRIVATENAME), "Cannot parse this destructuring pattern"); > failWithMessage("Expected a parameter pattern or a ')' in parameter list"); > } > failIfTrue(match(LET) && (kind == DestructuringKind::DestructureToLet || kind == DestructuringKind::DestructureToConst), "Cannot use 'let' as an identifier name for a LexicalDeclaration"); >@@ -2096,6 +2104,7 @@ template <class TreeBuilder> TreeFunctionBody Parser<LexerType>::parseFunctionBo > TreeBuilder& context, SyntaxChecker& syntaxChecker, const JSTokenLocation& startLocation, int startColumn, int functionKeywordStart, int functionNameStart, int parametersStart, > ConstructorKind constructorKind, SuperBinding superBinding, FunctionBodyType bodyType, unsigned parameterCount, SourceParseMode parseMode) > { >+ SetForScope<bool> overrideParsingClassFieldInitializer(m_parserState.isParsingClassFieldInitializer, bodyType == StandardFunctionBodyBlock ? false : m_parserState.isParsingClassFieldInitializer); > bool isArrowFunctionBodyExpression = bodyType == ArrowFunctionBodyExpression; > if (!isArrowFunctionBodyExpression) { > next(); >@@ -2148,6 +2157,7 @@ static const char* stringArticleForFunctionMode(SourceParseMode mode) > case SourceParseMode::ProgramMode: > case SourceParseMode::ModuleAnalyzeMode: > case SourceParseMode::ModuleEvaluateMode: >+ case SourceParseMode::InstanceFieldInitializerMode: > RELEASE_ASSERT_NOT_REACHED(); > return ""; > } >@@ -2189,6 +2199,7 @@ static const char* stringForFunctionMode(SourceParseMode mode) > case SourceParseMode::ProgramMode: > case SourceParseMode::ModuleAnalyzeMode: > case SourceParseMode::ModuleEvaluateMode: >+ case SourceParseMode::InstanceFieldInitializerMode: > RELEASE_ASSERT_NOT_REACHED(); > return ""; > } >@@ -2806,6 +2817,7 @@ template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(T > classScope->setIsLexicalScope(); > classScope->preventVarDeclarations(); > classScope->setStrictMode(); >+ classScope->setIsPrivateNameScope(); > > ASSERT_WITH_MESSAGE(requirements != FunctionNameRequirements::Unnamed, "Currently, there is no caller that uses FunctionNameRequirements::Unnamed for class syntax."); > ASSERT_WITH_MESSAGE(!(requirements == FunctionNameRequirements::None && !info.className), "When specifying FunctionNameRequirements::None, we need to initialize info.className with the default value in the caller side."); >@@ -2833,6 +2845,7 @@ template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(T > TreeExpression constructor = 0; > TreePropertyList classElements = 0; > TreePropertyList classElementsTail = 0; >+ unsigned numComputedFields = 0; > while (!match(CLOSEBRACE)) { > if (match(SEMICOLON)) { > next(); >@@ -2844,6 +2857,7 @@ template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(T > > // For backwards compatibility, "static" is a non-reserved keyword in non-strict mode. > ClassElementTag tag = ClassElementTag::Instance; >+ auto type = PropertyNode::Constant; > if (match(RESERVED_IF_STRICT) && *m_token.m_data.ident == m_vm->propertyNames->staticKeyword) { > SavePoint savePoint = createSavePoint(); > next(); >@@ -2877,7 +2891,8 @@ parseMethod: > if (!isGeneratorMethodParseMode(parseMode) && !isAsyncMethodParseMode(parseMode)) { > ident = m_token.m_data.ident; > next(); >- if (match(OPENPAREN) || match(COLON) || match(EQUAL) || m_lexer->prevTerminator()) >+ // We match SEMICOLON as a special case for a field called 'async' without initializer. >+ if (match(OPENPAREN) || match(COLON) || match(SEMICOLON) || match(EQUAL) || m_lexer->prevTerminator()) > break; > if (UNLIKELY(consume(TIMES))) > parseMode = SourceParseMode::AsyncGeneratorWrapperMethodMode; >@@ -2905,9 +2920,22 @@ parseMethod: > case OPENBRACKET: > next(); > computedPropertyName = parseAssignmentExpression(context); >+ type = static_cast<PropertyNode::Type>(type | PropertyNode::Computed); > failIfFalse(computedPropertyName, "Cannot parse computed property name"); > handleProductionOrFail(CLOSEBRACKET, "]", "end", "computed property name"); > break; >+ case PRIVATENAME: { >+ JSToken token = m_token; >+ ident = m_token.m_data.ident; >+ failIfTrue(tag == ClassElementTag::Static, "Static class element cannot be private"); >+ failIfTrue(isGetter || isSetter, "Cannot parse class method with private name"); >+ ASSERT(ident); >+ next(); >+ failIfTrue(matchAndUpdate(OPENPAREN, token), "Cannot parse class method with private name"); >+ semanticFailIfTrue(classScope->declarePrivateName(*ident) & DeclarationResult::InvalidDuplicateDeclaration, "Cannot declare private field twice"); >+ type = static_cast<PropertyNode::Type>(type | PropertyNode::Private); >+ break; >+ } > default: > if (m_token.m_type & KeywordTokenFlag) > goto namedKeyword; >@@ -2917,9 +2945,39 @@ parseMethod: > TreeProperty property; > const bool alwaysStrictInsideClass = true; > if (isGetter || isSetter) { >- property = parseGetterSetter(context, alwaysStrictInsideClass, isGetter ? PropertyNode::Getter : PropertyNode::Setter, >- methodStart, ConstructorKind::None, tag); >+ type = static_cast<PropertyNode::Type>(type & ~PropertyNode::Constant); >+ type = static_cast<PropertyNode::Type>(type | (isGetter ? PropertyNode::Getter : PropertyNode::Setter)); >+ property = parseGetterSetter(context, alwaysStrictInsideClass, type, methodStart, ConstructorKind::None, tag); > failIfFalse(property, "Cannot parse this method"); >+ } else if (!match(OPENPAREN) && tag == ClassElementTag::Instance && parseMode == SourceParseMode::MethodMode && !isGetter && !isSetter) { >+ if (ident) { >+ semanticFailIfTrue(*ident == propertyNames.constructor, "Cannot declare class field named 'constructor'"); >+ semanticFailIfTrue(*ident == propertyNames.constructorPrivateField, "Cannot declare private class field named '#constructor'"); >+ } >+ >+ if (computedPropertyName) { >+ ident = &m_parserArena.identifierArena().makeNumericIdentifier(m_vm, numComputedFields++); >+ failIfTrue(classScope->declareLexicalVariable(ident, true) != DeclarationResult::Valid, "Failed to declare lexical variable for computed class field. FIXME: This should never happen!"); >+ classScope->useVariable(ident, false); >+ classScope->addClosedVariableCandidateUnconditionally(ident->impl()); >+ } >+ >+ TreeExpression initializer = 0; >+ if (consume(EQUAL)) { >+ SetForScope<bool> overrideParsingClassFieldInitializer(m_parserState.isParsingClassFieldInitializer, true); >+ size_t currentUsedVariablesSize = classScope->currentUsedVariablesSize(); >+ classScope->pushUsedVariableSet(); >+ initializer = parseAssignmentExpression(context); >+ failIfFalse(initializer, "Cannot parse initializer for class field"); >+ classScope->markLastUsedVariablesSetAsCaptured(); >+ classScope->revertToPreviousUsedVariables(currentUsedVariablesSize); >+ } >+ failIfFalse(autoSemiColon(), "Expected a ';' following a class field"); >+ auto inferName = initializer ? InferName::Allowed : InferName::Disallowed; >+ if (computedPropertyName) >+ property = context.createProperty(ident, computedPropertyName, initializer, type, PropertyNode::Unknown, alwaysStrictInsideClass, SuperBinding::NotNeeded, tag); >+ else >+ property = context.createProperty(ident, initializer, type, PropertyNode::Unknown, alwaysStrictInsideClass, SuperBinding::NotNeeded, inferName, tag); > } else { > ParserFunctionInfo<TreeBuilder> methodInfo; > bool isConstructor = tag == ClassElementTag::Instance && *ident == propertyNames.constructor; >@@ -2944,11 +3002,9 @@ parseMethod: > "Cannot declare a static method named 'prototype'"); > > if (computedPropertyName) { >- property = context.createProperty(computedPropertyName, method, static_cast<PropertyNode::Type>(PropertyNode::Constant | PropertyNode::Computed), >- PropertyNode::Unknown, alwaysStrictInsideClass, SuperBinding::Needed, tag); >+ property = context.createProperty(computedPropertyName, method, type, PropertyNode::Unknown, alwaysStrictInsideClass, SuperBinding::Needed, tag); > } else { >- property = context.createProperty(methodInfo.name, method, PropertyNode::Constant, >- PropertyNode::Unknown, alwaysStrictInsideClass, SuperBinding::Needed, InferName::Allowed, tag); >+ property = context.createProperty(methodInfo.name, method, type, PropertyNode::Unknown, alwaysStrictInsideClass, SuperBinding::Needed, InferName::Allowed, tag); > } > } > >@@ -2961,11 +3017,76 @@ parseMethod: > info.endOffset = tokenLocation().endOffset - 1; > consumeOrFail(CLOSEBRACE, "Expected a closing '}' after a class body"); > >+ // Fail if there are no parent private name scopes and any used-but-undeclared private names. >+ semanticFailIfFalse(copyPrivateNameUsesToOuterScope(), "Cannot reference undeclared private names"); > auto classExpression = context.createClassExpr(location, info, classScope->finalizeLexicalEnvironment(), constructor, parentClass, classElements); > popScope(classScope, TreeBuilder::NeedsFreeVariableInfo); > return classExpression; > } > >+template <typename LexerType> >+template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseInstanceFieldInitializerSourceElements(TreeBuilder& context, const Vector<JSTextPosition>& instanceFieldLocations) >+{ >+ TreeSourceElements sourceElements = context.createSourceElements(); >+ unsigned numComputedFields = 0; >+ for (auto location : instanceFieldLocations) { >+ LexerState lexerState { location.offset, static_cast<unsigned>(location.lineStartOffset), static_cast<unsigned>(location.line), static_cast<unsigned>(location.line) }; >+ restoreLexerState(lexerState); >+ >+ JSTokenLocation fieldLocation = tokenLocation(); >+ const Identifier* ident = 0; >+ TreeExpression computedPropertyName = 0; >+ DefineFieldNode::Type type = DefineFieldNode::Name; >+ switch (m_token.m_type) { >+ case PRIVATENAME: >+ type = DefineFieldNode::PrivateName; >+ FALLTHROUGH; >+ case STRING: >+ case IDENT: >+ namedKeyword: >+ ident = m_token.m_data.ident; >+ ASSERT(ident); >+ next(); >+ break; >+ case DOUBLE: >+ case INTEGER: >+ ident = &m_parserArena.identifierArena().makeNumericIdentifier(const_cast<VM*>(m_vm), m_token.m_data.doubleValue); >+ ASSERT(ident); >+ next(); >+ break; >+ case OPENBRACKET: >+ next(); >+ computedPropertyName = parseAssignmentExpression(context); >+ failIfFalse(computedPropertyName, "Cannot parse computed property name"); >+ handleProductionOrFail(CLOSEBRACKET, "]", "end", "computed property name"); >+ ident = &m_parserArena.identifierArena().makeNumericIdentifier(m_vm, numComputedFields++); >+ type = DefineFieldNode::ComputedName; >+ break; >+ default: >+ if (m_token.m_type & KeywordTokenFlag) >+ goto namedKeyword; >+ failDueToUnexpectedToken(); >+ } >+ >+ // Only valid class fields are handled in this function. >+ ASSERT(match(EQUAL) || match(SEMICOLON) || match(CLOSEBRACE) || m_lexer->prevTerminator()); >+ >+ TreeExpression initializer = 0; >+ if (consume(EQUAL)) >+ initializer = parseAssignmentExpression(context); >+ >+ if (type == DefineFieldNode::PrivateName) >+ currentScope()->useVariable(ident, false); >+ >+ TreeStatement defineField = context.createDefineField(fieldLocation, ident, initializer, type); >+ context.appendStatement(sourceElements, defineField); >+ } >+ >+ ASSERT(!hasError()); >+ m_token.m_type = EOFTOK; // Hack to make parsing valid >+ return sourceElements; >+} >+ > struct LabelInfo { > LabelInfo(const Identifier* ident, const JSTextPosition& start, const JSTextPosition& end) > : m_ident(ident) >@@ -4496,9 +4617,13 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePrimaryExpre > const bool isEval = false; > return createResolveAndUseVariable(context, ident, isEval, start, location); > } >+ if (UNLIKELY(m_parserState.isParsingClassFieldInitializer)) >+ failIfTrue(*m_token.m_data.ident == m_vm->propertyNames->arguments, "Cannot reference 'arguments' in class field initializer"); > identifierExpression: > JSTextPosition start = tokenStartPosition(); > const Identifier* ident = m_token.m_data.ident; >+ if (UNLIKELY(currentScope()->evalContextType() == EvalContextType::InstanceFieldEvalContext)) >+ failIfTrue(*ident == m_vm->propertyNames->arguments, "arguments is not valid in this context"); > JSTokenLocation location(tokenLocation()); > next(); > >@@ -4668,6 +4793,22 @@ static inline void recordCallOrApplyDepth(ParserType*, VM&, Optional<typename Pa > { > } > >+template <typename LexerType> >+bool Parser<LexerType>::addPrivateNameUsedIfNeeded(const Identifier* ident) >+{ >+ if (m_lexer->isReparsingFunction()) >+ return true; >+ >+ bool found = false; >+ ScopeRef current = currentPrivateNameScope(&found); >+ if (!found) >+ return false; >+ if (!current->hasPrivateName(*ident)) >+ current->usePrivateName(*ident); >+ >+ return true; >+} >+ > template <typename LexerType> > template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpression(TreeBuilder& context) > { >@@ -4691,10 +4832,11 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres > next(); > if (matchContextualKeyword(m_vm->propertyNames->target)) { > ScopeRef closestOrdinaryFunctionScope = closestParentOrdinaryFunctionNonLexicalScope(); >- semanticFailIfFalse(currentScope()->isFunction() || closestOrdinaryFunctionScope->evalContextType() == EvalContextType::FunctionEvalContext, "new.target is only valid inside functions"); >+ bool isFunctionEvalContextType = closestOrdinaryFunctionScope->evalContextType() == EvalContextType::FunctionEvalContext || closestOrdinaryFunctionScope->evalContextType() == EvalContextType::InstanceFieldEvalContext; >+ semanticFailIfFalse(currentScope()->isFunction() || isFunctionEvalContextType, "new.target is only valid inside functions"); > baseIsNewTarget = true; > if (currentScope()->isArrowFunction()) { >- semanticFailIfFalse(!closestOrdinaryFunctionScope->isGlobalCodeScope() || closestOrdinaryFunctionScope->evalContextType() == EvalContextType::FunctionEvalContext, "new.target is not valid inside arrow functions in global code"); >+ semanticFailIfFalse(!closestOrdinaryFunctionScope->isGlobalCodeScope() || isFunctionEvalContextType, "new.target is not valid inside arrow functions in global code"); > currentScope()->setInnerArrowFunctionUsesNewTarget(); > } > base = context.createNewTargetExpr(location); >@@ -4713,6 +4855,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres > semanticFailIfFalse(currentScope()->isFunction() || (closestOrdinaryFunctionScope->isEvalContext() && closestOrdinaryFunctionScope->expectedSuperBinding() == SuperBinding::Needed), "super is not valid in this context"); > base = context.createSuperExpr(location); > next(); >+ failIfTrue(match(OPENPAREN) && currentScope()->evalContextType() == EvalContextType::InstanceFieldEvalContext, "super call is not valid in this context"); > ScopeRef functionScope = currentFunctionScope(); > if (!functionScope->setNeedsSuperBinding()) { > // It unnecessary to check of using super during reparsing one more time. Also it can lead to syntax error >@@ -4783,6 +4926,8 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres > break; > } > case OPENPAREN: { >+ if (baseIsSuper) >+ failIfTrue(m_parserState.isParsingClassFieldInitializer, "super call is not valid in class field initializer context"); > m_parserState.nonTrivialExpressionCount++; > int nonLHSCount = m_parserState.nonLHSCount; > if (newCount) { >@@ -4834,8 +4979,20 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres > m_parserState.nonTrivialExpressionCount++; > JSTextPosition expressionEnd = lastTokenEndPosition(); > nextExpectIdentifier(LexerFlagsIgnoreReservedWords | TreeBuilder::DontBuildKeywords); >+ const Identifier* ident = m_token.m_data.ident; >+ auto type = DotType::Name; >+ if (match(PRIVATENAME)) { >+ ASSERT(ident); >+ if (UNLIKELY(currentScope()->evalContextType() == EvalContextType::InstanceFieldEvalContext)) >+ semanticFailIfFalse(currentScope()->hasPrivateName(*ident), "Cannot reference undeclared private field '", ident->impl(), "'"); >+ semanticFailIfFalse(addPrivateNameUsedIfNeeded(ident), "Cannot reference private names outside of class"); >+ m_parserState.lastPrivateName = ident; >+ currentScope()->useVariable(ident, false); >+ type = DotType::PrivateName; >+ m_token.m_type = IDENT; >+ } > matchOrFail(IDENT, "Expected a property name after '.'"); >- base = context.createDotAccess(startLocation, base, m_token.m_data.ident, expressionStart, expressionEnd, tokenEndPosition()); >+ base = context.createDotAccess(startLocation, base, ident, type, expressionStart, expressionEnd, tokenEndPosition()); > if (UNLIKELY(baseIsSuper && currentScope()->isArrowFunction())) > currentFunctionScope()->setInnerArrowFunctionUsesSuperProperty(); > next(); >@@ -5030,6 +5187,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseUnaryExpress > break; > case DELETETOKEN: > failIfTrueIfStrict(context.isResolve(expr), "Cannot delete unqualified property '", m_parserState.lastIdentifier->impl(), "' in strict mode"); >+ semanticFailIfTrue(context.isPrivateLocation(expr), "Cannot delete private field ", m_parserState.lastPrivateName->impl()); > expr = context.makeDeleteNode(location, expr, context.unaryTokenStackLastStart(tokenStackDepth), end, end); > break; > default: >@@ -5101,7 +5259,11 @@ template <typename LexerType> void Parser<LexerType>::printUnexpectedTokenText(W > case INVALID_PRIVATE_NAME_ERRORTOK: > out.print("Invalid private name '", getToken(), "'"); > return; >- >+ >+ case PRIVATENAME: >+ out.print("Unexpected private name ", getToken()); >+ return; >+ > case AWAIT: > case IDENT: > out.print("Unexpected identifier '", getToken(), "'"); >diff --git a/Source/JavaScriptCore/parser/Parser.h b/Source/JavaScriptCore/parser/Parser.h >index 4dc4fa7336c..2a30f43629c 100644 >--- a/Source/JavaScriptCore/parser/Parser.h >+++ b/Source/JavaScriptCore/parser/Parser.h >@@ -179,6 +179,7 @@ public: > , m_hasArguments(false) > , m_isEvalContext(false) > , m_hasNonSimpleParameterList(false) >+ , m_isPrivateNameScope(false) > , m_evalContextType(EvalContextType::None) > , m_constructorKind(static_cast<unsigned>(ConstructorKind::None)) > , m_expectedSuperBinding(static_cast<unsigned>(SuperBinding::NotNeeded)) >@@ -256,6 +257,7 @@ public: > case SourceParseMode::GetterMode: > case SourceParseMode::SetterMode: > case SourceParseMode::MethodMode: >+ case SourceParseMode::InstanceFieldInitializerMode: > setIsFunction(); > break; > >@@ -285,6 +287,7 @@ public: > bool isGeneratorBoundary() const { return m_isGeneratorBoundary; } > bool isAsyncFunction() const { return m_isAsyncFunction; } > bool isAsyncFunctionBoundary() const { return m_isAsyncFunctionBoundary; } >+ bool isPrivateNameScope() const { return m_isPrivateNameScope; } > > bool hasArguments() const { return m_hasArguments; } > >@@ -299,6 +302,13 @@ public: > m_isLexicalScope = true; > m_allowsLexicalDeclarations = true; > } >+ >+ void setIsPrivateNameScope() >+ { >+ setIsFunction(); >+ m_isPrivateNameScope = true; >+ } >+ > bool isLexicalScope() { return m_isLexicalScope; } > bool usesEval() { return m_usesEval; } > >@@ -460,7 +470,46 @@ public: > { > return m_lexicalVariables.contains(ident.get()); > } >- >+ >+ bool hasPrivateName(const Identifier& ident) >+ { >+ return m_lexicalVariables.hasPrivateName(ident); >+ } >+ >+ void copyPrivateNameUses(Scope& other) >+ { >+ m_lexicalVariables.copyPrivateNameUses(other.m_lexicalVariables); >+ } >+ >+ bool hasUsedButUndeclaredPrivateNames() const >+ { >+ if (m_lexicalVariables.privateNamesSize() > 0) { >+ for (auto entry : m_lexicalVariables.privateNames()) { >+ if (entry.value.isUsed() && !entry.value.isDeclared()) >+ return true; >+ } >+ } >+ return false; >+ } >+ >+ void usePrivateName(const Identifier& ident) >+ { >+ ASSERT(m_allowsLexicalDeclarations); >+ m_lexicalVariables.usePrivateName(ident); >+ } >+ >+ DeclarationResultMask declarePrivateName(const Identifier& ident) >+ { >+ ASSERT(m_allowsLexicalDeclarations); >+ DeclarationResultMask result = DeclarationResult::Valid; >+ bool addResult = m_lexicalVariables.declarePrivateName(ident); >+ >+ if (!addResult) >+ result |= DeclarationResult::InvalidDuplicateDeclaration; >+ >+ return result; >+ } >+ > ALWAYS_INLINE bool hasDeclaredParameter(const Identifier& ident) > { > return hasDeclaredParameter(ident.impl()); >@@ -579,6 +628,12 @@ public: > { > m_closedVariableCandidates.add(impl); > } >+ >+ void markLastUsedVariablesSetAsCaptured() >+ { >+ for (UniquedStringImpl* impl : m_usedVariables.last()) >+ m_closedVariableCandidates.add(impl); >+ } > > void collectFreeVariables(Scope* nestedScope, bool shouldTrackClosedVariables) > { >@@ -817,6 +872,7 @@ private: > bool m_hasArguments; > bool m_isEvalContext; > bool m_hasNonSimpleParameterList; >+ bool m_isPrivateNameScope; > EvalContextType m_evalContextType; > unsigned m_constructorKind; > unsigned m_expectedSuperBinding; >@@ -887,7 +943,7 @@ public: > ~Parser(); > > template <class ParsedNode> >- std::unique_ptr<ParsedNode> parse(ParserError&, const Identifier&, SourceParseMode, ParsingContext, Optional<int> functionConstructorParametersEndPosition = WTF::nullopt); >+ std::unique_ptr<ParsedNode> parse(ParserError&, const Identifier&, SourceParseMode, ParsingContext, Optional<int> functionConstructorParametersEndPosition = WTF::nullopt, const VariableEnvironment* = nullptr, const Vector<JSTextPosition>& = Vector<JSTextPosition>()); > > JSTextPosition positionBeforeLastNewline() const { return m_lexer->positionBeforeLastNewline(); } > JSTokenLocation locationBeforeLastToken() const { return m_lexer->lastTokenLocation(); } >@@ -1160,6 +1216,35 @@ private: > return ScopeRef(&m_scopeStack, i); > } > >+ ScopeRef currentPrivateNameScope(bool *found = 0) >+ { >+ unsigned i = m_scopeStack.size() - 1; >+ ASSERT(i < m_scopeStack.size()); >+ while (i && !m_scopeStack[i].isPrivateNameScope()) { >+ i--; >+ ASSERT(i < m_scopeStack.size()); >+ } >+ if (found) >+ *found = m_scopeStack[i].isPrivateNameScope(); >+ return ScopeRef(&m_scopeStack, i); >+ } >+ >+ bool copyPrivateNameUsesToOuterScope() >+ { >+ ScopeRef current = currentPrivateNameScope(); >+ unsigned i = current.index() - 1; >+ while (i && !m_scopeStack[i].isPrivateNameScope()) { >+ i--; >+ ASSERT(i < m_scopeStack.size()); >+ } >+ >+ if (!i) >+ return !current->hasUsedButUndeclaredPrivateNames(); >+ >+ current->copyPrivateNameUses(m_scopeStack[i]); >+ return true; >+ } >+ > ScopeRef closestParentOrdinaryFunctionNonLexicalScope() > { > unsigned i = m_scopeStack.size() - 1; >@@ -1346,7 +1431,7 @@ private: > > Parser(); > >- String parseInner(const Identifier&, SourceParseMode, ParsingContext, Optional<int> functionConstructorParametersEndPosition = WTF::nullopt); >+ String parseInner(const Identifier&, SourceParseMode, ParsingContext, Optional<int> functionConstructorParametersEndPosition = WTF::nullopt, const Vector<JSTextPosition>& = Vector<JSTextPosition>()); > > void didFinishParsing(SourceElements*, DeclarationStacks::FunctionStack&&, VariableEnvironment&, UniquedStringImplPtrSet&&, CodeFeatures, int); > >@@ -1402,6 +1487,16 @@ private: > { > return m_token.m_type == expected; > } >+ >+ ALWAYS_INLINE bool matchAndUpdate(JSTokenType expected, const JSToken& token) >+ { >+ if (match(expected)) { >+ m_token = token; >+ return true; >+ } >+ >+ return false; >+ } > > ALWAYS_INLINE bool matchContextualKeyword(const Identifier& identifier) > { >@@ -1559,6 +1654,7 @@ private: > template <class TreeBuilder> TreeSourceElements parseAsyncFunctionSourceElements(TreeBuilder&, SourceParseMode, bool isArrowFunctionBodyExpression, SourceElementsMode); > template <class TreeBuilder> TreeSourceElements parseAsyncGeneratorFunctionSourceElements(TreeBuilder&, SourceParseMode, bool isArrowFunctionBodyExpression, SourceElementsMode); > template <class TreeBuilder> TreeSourceElements parseSingleFunction(TreeBuilder&, Optional<int> functionConstructorParametersEndPosition); >+ template <class TreeBuilder> TreeSourceElements parseInstanceFieldInitializerSourceElements(TreeBuilder&, const Vector<JSTextPosition>&); > template <class TreeBuilder> TreeStatement parseStatementListItem(TreeBuilder&, const Identifier*& directive, unsigned* directiveLiteralLength); > template <class TreeBuilder> TreeStatement parseStatement(TreeBuilder&, const Identifier*& directive, unsigned* directiveLiteralLength = 0); > enum class ExportType { Exported, NotExported }; >@@ -1650,6 +1746,7 @@ private: > > template <class TreeBuilder> NEVER_INLINE const char* metaPropertyName(TreeBuilder&, TreeExpression); > >+ ALWAYS_INLINE bool addPrivateNameUsedIfNeeded(const Identifier*); > ALWAYS_INLINE int isBinaryOperator(JSTokenType); > bool allowAutomaticSemicolon(); > >@@ -1743,7 +1840,9 @@ private: > FunctionParsePhase functionParsePhase { FunctionParsePhase::Body }; > const Identifier* lastIdentifier { nullptr }; > const Identifier* lastFunctionName { nullptr }; >+ const Identifier* lastPrivateName { nullptr }; > bool allowAwait { true }; >+ bool isParsingClassFieldInitializer { false }; > }; > > // If you're using this directly, you probably should be using >@@ -1881,7 +1980,7 @@ private: > > template <typename LexerType> > template <class ParsedNode> >-std::unique_ptr<ParsedNode> Parser<LexerType>::parse(ParserError& error, const Identifier& calleeName, SourceParseMode parseMode, ParsingContext parsingContext, Optional<int> functionConstructorParametersEndPosition) >+std::unique_ptr<ParsedNode> Parser<LexerType>::parse(ParserError& error, const Identifier& calleeName, SourceParseMode parseMode, ParsingContext parsingContext, Optional<int> functionConstructorParametersEndPosition, const VariableEnvironment* variablesUnderTDZ, const Vector<JSTextPosition>& instanceFieldLocations) > { > int errLine; > String errMsg; >@@ -1898,7 +1997,14 @@ std::unique_ptr<ParsedNode> Parser<LexerType>::parse(ParserError& error, const I > ASSERT(m_source->startColumn() > OrdinalNumber::beforeFirst()); > unsigned startColumn = m_source->startColumn().zeroBasedInt(); > >- String parseError = parseInner(calleeName, parseMode, parsingContext, functionConstructorParametersEndPosition); >+ if (isEvalNode<ParsedNode>() && variablesUnderTDZ) { >+ if (variablesUnderTDZ->privateNamesSize()) { >+ currentScope()->setIsPrivateNameScope(); >+ variablesUnderTDZ->copyPrivateNames(currentScope()->lexicalVariables()); >+ } >+ } >+ >+ String parseError = parseInner(calleeName, parseMode, parsingContext, functionConstructorParametersEndPosition, instanceFieldLocations); > > int lineNumber = m_lexer->lineNumber(); > bool lexError = m_lexer->sawError(); >@@ -1982,7 +2088,9 @@ std::unique_ptr<ParsedNode> parse( > ConstructorKind defaultConstructorKind = ConstructorKind::None, > DerivedContextType derivedContextType = DerivedContextType::None, > EvalContextType evalContextType = EvalContextType::None, >- DebuggerParseData* debuggerParseData = nullptr) >+ DebuggerParseData* debuggerParseData = nullptr, >+ const VariableEnvironment* variablesUnderTDZ = nullptr, >+ const Vector<JSTextPosition>& instanceFieldLocations = Vector<JSTextPosition>()) > { > ASSERT(!source.provider()->source().isNull()); > >@@ -1993,7 +2101,7 @@ std::unique_ptr<ParsedNode> parse( > std::unique_ptr<ParsedNode> result; > if (source.provider()->source().is8Bit()) { > Parser<Lexer<LChar>> parser(vm, source, builtinMode, strictMode, scriptMode, parseMode, superBinding, defaultConstructorKind, derivedContextType, isEvalNode<ParsedNode>(), evalContextType, debuggerParseData); >- result = parser.parse<ParsedNode>(error, name, parseMode, isEvalNode<ParsedNode>() ? ParsingContext::Eval : ParsingContext::Program); >+ result = parser.parse<ParsedNode>(error, name, parseMode, isEvalNode<ParsedNode>() ? ParsingContext::Eval : ParsingContext::Program, WTF::nullopt, variablesUnderTDZ, instanceFieldLocations); > if (positionBeforeLastNewline) > *positionBeforeLastNewline = parser.positionBeforeLastNewline(); > if (builtinMode == JSParserBuiltinMode::Builtin) { >@@ -2006,7 +2114,7 @@ std::unique_ptr<ParsedNode> parse( > } else { > ASSERT_WITH_MESSAGE(defaultConstructorKind == ConstructorKind::None, "BuiltinExecutables::createDefaultConstructor should always use a 8-bit string"); > Parser<Lexer<UChar>> parser(vm, source, builtinMode, strictMode, scriptMode, parseMode, superBinding, defaultConstructorKind, derivedContextType, isEvalNode<ParsedNode>(), evalContextType, debuggerParseData); >- result = parser.parse<ParsedNode>(error, name, parseMode, isEvalNode<ParsedNode>() ? ParsingContext::Eval : ParsingContext::Program); >+ result = parser.parse<ParsedNode>(error, name, parseMode, isEvalNode<ParsedNode>() ? ParsingContext::Eval : ParsingContext::Program, WTF::nullopt, variablesUnderTDZ, instanceFieldLocations); > if (positionBeforeLastNewline) > *positionBeforeLastNewline = parser.positionBeforeLastNewline(); > } >diff --git a/Source/JavaScriptCore/parser/ParserModes.h b/Source/JavaScriptCore/parser/ParserModes.h >index 9e637c4aa09..2e44a10d37a 100644 >--- a/Source/JavaScriptCore/parser/ParserModes.h >+++ b/Source/JavaScriptCore/parser/ParserModes.h >@@ -62,6 +62,7 @@ enum class SourceParseMode : uint8_t { > AsyncGeneratorWrapperFunctionMode = 16, > AsyncGeneratorWrapperMethodMode = 17, > GeneratorWrapperMethodMode = 18, >+ InstanceFieldInitializerMode = 19, > }; > > class SourceParseModeSet { >@@ -110,7 +111,8 @@ ALWAYS_INLINE bool isFunctionParseMode(SourceParseMode parseMode) > SourceParseMode::AsyncArrowFunctionBodyMode, > SourceParseMode::AsyncGeneratorBodyMode, > SourceParseMode::AsyncGeneratorWrapperFunctionMode, >- SourceParseMode::AsyncGeneratorWrapperMethodMode).contains(parseMode); >+ SourceParseMode::AsyncGeneratorWrapperMethodMode, >+ SourceParseMode::InstanceFieldInitializerMode).contains(parseMode); > } > > ALWAYS_INLINE bool isAsyncFunctionParseMode(SourceParseMode parseMode) >diff --git a/Source/JavaScriptCore/parser/ParserTokens.h b/Source/JavaScriptCore/parser/ParserTokens.h >index 59cb0fb4c86..9ed903cdbae 100644 >--- a/Source/JavaScriptCore/parser/ParserTokens.h >+++ b/Source/JavaScriptCore/parser/ParserTokens.h >@@ -112,6 +112,7 @@ enum JSTokenType { > DOUBLE, > BIGINT, > IDENT, >+ PRIVATENAME, > STRING, > TEMPLATE, > REGEXP, >diff --git a/Source/JavaScriptCore/parser/SyntaxChecker.h b/Source/JavaScriptCore/parser/SyntaxChecker.h >index 97cfadb302f..4983340bc14 100644 >--- a/Source/JavaScriptCore/parser/SyntaxChecker.h >+++ b/Source/JavaScriptCore/parser/SyntaxChecker.h >@@ -72,7 +72,7 @@ public: > enum : int { NoneExpr = 0, > ResolveEvalExpr, ResolveExpr, IntegerExpr, DoubleExpr, StringExpr, BigIntExpr, > ThisExpr, NullExpr, BoolExpr, RegExpExpr, ObjectLiteralExpr, >- FunctionExpr, ClassExpr, SuperExpr, ImportExpr, BracketExpr, DotExpr, CallExpr, >+ FunctionExpr, ClassExpr, SuperExpr, ImportExpr, BracketExpr, DotExpr, PrivateDotExpr, CallExpr, > NewExpr, PreExpr, PostExpr, UnaryExpr, BinaryExpr, > ConditionalExpr, AssignmentExpr, TypeofExpr, > DeleteExpr, ArrayLiteralExpr, BindingDestructuring, RestParameter, >@@ -180,7 +180,7 @@ public: > ExpressionType createBoolean(const JSTokenLocation&, bool) { return BoolExpr; } > ExpressionType createNull(const JSTokenLocation&) { return NullExpr; } > ExpressionType createBracketAccess(const JSTokenLocation&, ExpressionType, ExpressionType, bool, int, int, int) { return BracketExpr; } >- ExpressionType createDotAccess(const JSTokenLocation&, ExpressionType, const Identifier*, int, int, int) { return DotExpr; } >+ ExpressionType createDotAccess(const JSTokenLocation&, ExpressionType, const Identifier*, DotType type, int, int, int) { return type == DotType::PrivateName ? PrivateDotExpr : DotExpr; } > ExpressionType createRegExp(const JSTokenLocation&, const Identifier& pattern, const Identifier& flags, int) { return Yarr::hasError(Yarr::checkSyntax(pattern.string(), flags.string())) ? 0 : RegExpExpr; } > ExpressionType createNewExpr(const JSTokenLocation&, ExpressionType, int, int, int, int) { return NewExpr; } > ExpressionType createNewExpr(const JSTokenLocation&, ExpressionType, int, int) { return NewExpr; } >@@ -235,6 +235,10 @@ public: > { > return Property(type); > } >+ Property createProperty(const Identifier*, int, int, PropertyNode::Type type, PropertyNode::PutType, bool, SuperBinding, ClassElementTag) >+ { >+ return Property(type); >+ } > int createPropertyList(const JSTokenLocation&, Property) { return PropertyListResult; } > int createPropertyList(const JSTokenLocation&, Property, int) { return PropertyListResult; } > int createElementList(int, int) { return ElementsListResult; } >@@ -246,6 +250,7 @@ public: > int createClauseList(int) { return ClauseListResult; } > int createClauseList(int, int) { return ClauseListResult; } > int createFuncDeclStatement(const JSTokenLocation&, const ParserFunctionInfo<SyntaxChecker>&) { return StatementResult; } >+ int createDefineField(const JSTokenLocation&, const Identifier*, int, DefineFieldNode::Type) { return 0; } > int createClassDeclStatement(const JSTokenLocation&, ClassExpression, > const JSTextPosition&, const JSTextPosition&, int, int) { return StatementResult; } > int createBlockStatement(const JSTokenLocation&, int, int, int, VariableEnvironment&, DeclarationStacks::FunctionStack&&) { return StatementResult; } >@@ -395,7 +400,12 @@ public: > > bool isAssignmentLocation(ExpressionType type) > { >- return type == ResolveExpr || type == DotExpr || type == BracketExpr; >+ return type == ResolveExpr || type == DotExpr || type == PrivateDotExpr || type == BracketExpr; >+ } >+ >+ bool isPrivateLocation(ExpressionType type) >+ { >+ return type == PrivateDotExpr; > } > > bool isObjectLiteral(ExpressionType type) >diff --git a/Source/JavaScriptCore/parser/VariableEnvironment.cpp b/Source/JavaScriptCore/parser/VariableEnvironment.cpp >index a395ff585b6..10650c14bca 100644 >--- a/Source/JavaScriptCore/parser/VariableEnvironment.cpp >+++ b/Source/JavaScriptCore/parser/VariableEnvironment.cpp >@@ -29,6 +29,16 @@ > > namespace JSC { > >+VariableEnvironment& VariableEnvironment::operator=(const VariableEnvironment& other) >+{ >+ m_map = other.m_map; >+ m_isEverythingCaptured = other.m_isEverythingCaptured; >+ m_rareData.reset(); >+ if (other.m_rareData) >+ m_rareData = std::make_unique<VariableEnvironmentRareData>(*other.m_rareData); >+ return *this; >+} >+ > void VariableEnvironment::markVariableAsCapturedIfDefined(const RefPtr<UniquedStringImpl>& identifier) > { > auto findResult = m_map.find(identifier); >@@ -80,6 +90,7 @@ void VariableEnvironment::swap(VariableEnvironment& other) > { > m_map.swap(other.m_map); > m_isEverythingCaptured = other.m_isEverythingCaptured; >+ m_rareData.swap(other.m_rareData); > } > > void VariableEnvironment::markVariableAsImported(const RefPtr<UniquedStringImpl>& identifier) >@@ -96,6 +107,54 @@ void VariableEnvironment::markVariableAsExported(const RefPtr<UniquedStringImpl> > findResult->value.setIsExported(); > } > >+void VariableEnvironment::markPrivateNameAsDeclared(const Identifier& identifier) >+{ >+ if (!m_rareData) >+ return; >+ auto findResult = m_rareData->m_privateNames.find(identifier.impl()); >+ RELEASE_ASSERT(findResult != m_rareData->m_privateNames.end()); >+ findResult->value.setIsDeclared(); >+} >+ >+bool VariableEnvironment::declarePrivateName(const RefPtr<UniquedStringImpl>& identifier) >+{ >+ if (!m_rareData) >+ m_rareData = std::make_unique<VariableEnvironmentRareData>(); >+ >+ auto findResult = m_rareData->m_privateNames.find(identifier); >+ >+ if (findResult == m_rareData->m_privateNames.end()) { >+ PrivateNameEntry entry(PrivateNameEntry::Traits::IsDeclared); >+ >+ auto addResult = m_rareData->m_privateNames.add(identifier, entry); >+ return addResult.isNewEntry; >+ } >+ >+ >+ if (findResult->value.isDeclared()) >+ return false; // Error: declaring a duplicate private name. >+ >+ // it was previously used, mark it as declared. >+ PrivateNameEntry entry(PrivateNameEntry::Traits::IsDeclared | PrivateNameEntry::Traits::IsUsed); >+ auto addResult = m_rareData->m_privateNames.set(identifier, entry); >+ return !addResult.isNewEntry; >+} >+ >+void VariableEnvironment::usePrivateName(const RefPtr<UniquedStringImpl>& identifier) >+{ >+ if (!m_rareData) >+ m_rareData = std::make_unique<VariableEnvironmentRareData>(); >+ >+ auto findResult = m_rareData->m_privateNames.find(identifier); >+ >+ if (findResult != m_rareData->m_privateNames.end()) >+ findResult->value.setIsUsed(); >+ else { >+ PrivateNameEntry entry(PrivateNameEntry::Traits::IsUsed); >+ m_rareData->m_privateNames.set(identifier, entry); >+ } >+} >+ > CompactVariableEnvironment::CompactVariableEnvironment(const VariableEnvironment& env) > : m_isEverythingCaptured(env.isEverythingCaptured()) > { >diff --git a/Source/JavaScriptCore/parser/VariableEnvironment.h b/Source/JavaScriptCore/parser/VariableEnvironment.h >index d84b2e1fb21..0326231f00d 100644 >--- a/Source/JavaScriptCore/parser/VariableEnvironment.h >+++ b/Source/JavaScriptCore/parser/VariableEnvironment.h >@@ -27,6 +27,8 @@ > > #include "Identifier.h" > #include <wtf/HashMap.h> >+#include <wtf/HashSet.h> >+#include <wtf/IteratorRange.h> > > namespace JSC { > >@@ -83,15 +85,54 @@ struct VariableEnvironmentEntryHashTraits : HashTraits<VariableEnvironmentEntry> > static const bool needsDestruction = false; > }; > >+struct PrivateNameEntry { >+public: >+ PrivateNameEntry(uint16_t traits = 0) { m_bits = traits; } >+ >+ ALWAYS_INLINE bool isUsed() const { return m_bits & IsUsed; } >+ ALWAYS_INLINE bool isDeclared() const { return m_bits & IsDeclared; } >+ >+ ALWAYS_INLINE void setIsUsed() { m_bits |= IsUsed; } >+ ALWAYS_INLINE void setIsDeclared() { m_bits |= IsDeclared; } >+ >+ uint16_t bits() const { return m_bits; } >+ >+ bool operator==(const PrivateNameEntry& other) const >+ { >+ return m_bits == other.m_bits; >+ } >+ >+ enum Traits : uint16_t { >+ IsUsed = 1 << 0, >+ IsDeclared = 1 << 1, >+ }; >+ >+private: >+ uint16_t m_bits { 0 }; >+}; >+ >+struct PrivateNameEntryHashTraits : HashTraits<PrivateNameEntry> { >+ static const bool needsDestruction = false; >+}; >+ > class VariableEnvironment { > private: > typedef HashMap<RefPtr<UniquedStringImpl>, VariableEnvironmentEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>, VariableEnvironmentEntryHashTraits> Map; >+ typedef HashMap<RefPtr<UniquedStringImpl>, PrivateNameEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>, PrivateNameEntryHashTraits> PrivateNames; > public: >- VariableEnvironment() = default; >- VariableEnvironment(VariableEnvironment&& other) = default; >- VariableEnvironment(const VariableEnvironment&) = default; >- VariableEnvironment& operator=(const VariableEnvironment&) = default; >- VariableEnvironment& operator=(VariableEnvironment&&) = default; >+ VariableEnvironment() >+ { } >+ VariableEnvironment(VariableEnvironment&& other) >+ : m_map(WTFMove(other.m_map)) >+ , m_isEverythingCaptured(other.m_isEverythingCaptured) >+ , m_rareData(WTFMove(other.m_rareData)) >+ { } >+ VariableEnvironment(const VariableEnvironment& other) >+ : m_map(other.m_map) >+ , m_isEverythingCaptured(other.m_isEverythingCaptured) >+ , m_rareData(other.m_rareData ? std::make_unique<VariableEnvironmentRareData>(*other.m_rareData) : nullptr) >+ { } >+ VariableEnvironment& operator=(const VariableEnvironment& other); > > ALWAYS_INLINE Map::iterator begin() { return m_map.begin(); } > ALWAYS_INLINE Map::iterator end() { return m_map.end(); } >@@ -99,7 +140,7 @@ public: > ALWAYS_INLINE Map::const_iterator end() const { return m_map.end(); } > ALWAYS_INLINE Map::AddResult add(const RefPtr<UniquedStringImpl>& identifier) { return m_map.add(identifier, VariableEnvironmentEntry()); } > ALWAYS_INLINE Map::AddResult add(const Identifier& identifier) { return add(identifier.impl()); } >- ALWAYS_INLINE unsigned size() const { return m_map.size(); } >+ ALWAYS_INLINE unsigned size() const { return m_map.size() + privateNamesSize(); } > ALWAYS_INLINE bool contains(const RefPtr<UniquedStringImpl>& identifier) const { return m_map.contains(identifier); } > ALWAYS_INLINE bool remove(const RefPtr<UniquedStringImpl>& identifier) { return m_map.remove(identifier); } > ALWAYS_INLINE Map::iterator find(const RefPtr<UniquedStringImpl>& identifier) { return m_map.find(identifier); } >@@ -115,11 +156,93 @@ public: > > bool isEverythingCaptured() const { return m_isEverythingCaptured; } > >+ typedef WTF::IteratorRange<PrivateNames::iterator> PrivateNamesRange; >+ >+ bool declarePrivateName(const Identifier& identifier) { return declarePrivateName(identifier.impl()); } >+ bool declarePrivateName(const RefPtr<UniquedStringImpl>& identifier); >+ void usePrivateName(const RefPtr<UniquedStringImpl>& identifier); >+ ALWAYS_INLINE void usePrivateName(const Identifier& identifier) >+ { >+ usePrivateName(identifier.impl()); >+ } >+ >+ ALWAYS_INLINE PrivateNamesRange privateNames() const >+ { >+ ASSERT(privateNamesSize() > 0); >+ return PrivateNamesRange(m_rareData->m_privateNames.begin(), m_rareData->m_privateNames.end()); >+ } >+ >+ ALWAYS_INLINE unsigned privateNamesSize() const >+ { >+ if (!m_rareData) >+ return 0; >+ return m_rareData->m_privateNames.size(); >+ } >+ >+ ALWAYS_INLINE bool hasPrivateName(const Identifier& identifier) >+ { >+ if (!m_rareData) >+ return false; >+ return m_rareData->m_privateNames.contains(identifier.impl()); >+ } >+ >+ ALWAYS_INLINE void markPrivateNameAsDeclared(const Identifier&); >+ >+ ALWAYS_INLINE void copyPrivateNames(VariableEnvironment& other) const >+ { >+ if (!m_rareData) >+ return; >+ if (!other.m_rareData) >+ other.m_rareData = std::make_unique<VariableEnvironmentRareData>(); >+ if (privateNamesSize() > 0) { >+ for (auto entry : privateNames()) { >+ if (!(entry.value.isUsed() && entry.value.isDeclared())) >+ other.m_rareData->m_privateNames.add(entry.key, entry.value); >+ } >+ } >+ } >+ >+ ALWAYS_INLINE void copyPrivateNameUses(VariableEnvironment& outer) const { >+ // Used by the Parser to transfer recorded uses of PrivateNames from an >+ // inner PrivateNameEnvironment into an outer one, in case a PNE is used >+ // earlier in the source code than it is defined. >+ if (privateNamesSize() > 0) { >+ for (auto entry : privateNames()) { >+ if (entry.value.isUsed() && !entry.value.isDeclared()) >+ outer.getOrAddPrivateName(entry.key.get()).setIsUsed(); >+ } >+ } >+ } >+ > private: > friend class CachedVariableEnvironment; > > Map m_map; > bool m_isEverythingCaptured { false }; >+ >+ struct VariableEnvironmentRareData { >+ VariableEnvironmentRareData() >+ { } >+ VariableEnvironmentRareData(VariableEnvironmentRareData&& other) >+ : m_privateNames(WTFMove(other.m_privateNames)) >+ { } >+ VariableEnvironmentRareData(const VariableEnvironmentRareData&) = default; >+ VariableEnvironmentRareData& operator=(const VariableEnvironmentRareData&) = default; >+ PrivateNames m_privateNames; >+ }; >+ >+ PrivateNameEntry& getOrAddPrivateName(UniquedStringImpl* impl) >+ { >+ if (!m_rareData) >+ m_rareData = std::make_unique<VariableEnvironmentRareData>(); >+ >+ auto it = m_rareData->m_privateNames.find(impl); >+ if (it == m_rareData->m_privateNames.end()) >+ it = m_rareData->m_privateNames.add(impl, PrivateNameEntry(0)).iterator; >+ return it->value; >+ } >+ >+ std::unique_ptr<VariableEnvironmentRareData> m_rareData; > }; > > class CompactVariableEnvironment { >diff --git a/Source/JavaScriptCore/runtime/CodeCache.h b/Source/JavaScriptCore/runtime/CodeCache.h >index 80c0d95f9ef..8a047bbbf87 100644 >--- a/Source/JavaScriptCore/runtime/CodeCache.h >+++ b/Source/JavaScriptCore/runtime/CodeCache.h >@@ -322,7 +322,7 @@ UnlinkedCodeBlockType* generateUnlinkedCodeBlockImpl(VM& vm, const SourceCode& s > { > typedef typename CacheTypes<UnlinkedCodeBlockType>::RootNode RootNode; > std::unique_ptr<RootNode> rootNode = parse<RootNode>( >- &vm, source, Identifier(), JSParserBuiltinMode::NotBuiltin, strictMode, scriptMode, CacheTypes<UnlinkedCodeBlockType>::parseMode, SuperBinding::NotNeeded, error, nullptr, ConstructorKind::None, derivedContextType, evalContextType); >+ &vm, source, Identifier(), JSParserBuiltinMode::NotBuiltin, strictMode, scriptMode, CacheTypes<UnlinkedCodeBlockType>::parseMode, SuperBinding::NotNeeded, error, nullptr, ConstructorKind::None, derivedContextType, evalContextType, nullptr, variablesUnderTDZ); > if (!rootNode) > return nullptr; > >diff --git a/Source/JavaScriptCore/runtime/CommonIdentifiers.cpp b/Source/JavaScriptCore/runtime/CommonIdentifiers.cpp >index 197370d110a..1d08b317cb1 100644 >--- a/Source/JavaScriptCore/runtime/CommonIdentifiers.cpp >+++ b/Source/JavaScriptCore/runtime/CommonIdentifiers.cpp >@@ -32,6 +32,7 @@ namespace JSC { > #define INITIALIZE_KEYWORD(name) , name##Keyword(Identifier::fromString(vm, #name)) > #define INITIALIZE_PRIVATE_NAME(name) , name##PrivateName(m_builtinNames->name##PrivateName()) > #define INITIALIZE_SYMBOL(name) , name##Symbol(m_builtinNames->name##Symbol()) >+#define INITIALIZE_PRIVATE_FIELD_NAME(name) , name##PrivateField(Identifier::fromString(vm, "#" #name)) > > CommonIdentifiers::CommonIdentifiers(VM* vm) > : nullIdentifier() >@@ -44,6 +45,7 @@ CommonIdentifiers::CommonIdentifiers(VM* vm) > JSC_COMMON_IDENTIFIERS_EACH_KEYWORD(INITIALIZE_KEYWORD) > JSC_COMMON_IDENTIFIERS_EACH_PROPERTY_NAME(INITIALIZE_PROPERTY_NAME) > JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(INITIALIZE_SYMBOL) >+ JSC_COMMON_IDENTIFIERS_EACH_PRIVATE_FIELD(INITIALIZE_PRIVATE_FIELD_NAME) > { > } > >diff --git a/Source/JavaScriptCore/runtime/CommonIdentifiers.h b/Source/JavaScriptCore/runtime/CommonIdentifiers.h >index 80227f77a82..2c207c57293 100644 >--- a/Source/JavaScriptCore/runtime/CommonIdentifiers.h >+++ b/Source/JavaScriptCore/runtime/CommonIdentifiers.h >@@ -222,6 +222,9 @@ > macro(writable) \ > macro(year) > >+#define JSC_COMMON_IDENTIFIERS_EACH_PRIVATE_FIELD(macro) \ >+ macro(constructor) >+ > #define JSC_COMMON_IDENTIFIERS_EACH_KEYWORD(macro) \ > macro(await) \ > macro(break) \ >@@ -321,6 +324,10 @@ namespace JSC { > JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(JSC_IDENTIFIER_DECLARE_PRIVATE_WELL_KNOWN_SYMBOL_GLOBAL) > #undef JSC_IDENTIFIER_DECLARE_PRIVATE_WELL_KNOWN_SYMBOL_GLOBAL > >+#define JSC_IDENTIFIER_DECLARE_PRIVATE_FIELD_GLOBAL(name) const Identifier name##PrivateField; >+ JSC_COMMON_IDENTIFIERS_EACH_PRIVATE_FIELD(JSC_IDENTIFIER_DECLARE_PRIVATE_FIELD_GLOBAL) >+#undef JSC_IDENTIFIER_DECLARE_PRIVATE_FIELD_GLOBAL >+ > const Identifier* lookUpPrivateName(const Identifier&) const; > Identifier lookUpPublicName(const Identifier&) const; > >diff --git a/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp b/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp >index bbbadab67fd..6a3a7098105 100644 >--- a/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp >+++ b/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp >@@ -874,6 +874,13 @@ SLOW_PATH_DECL(slow_path_to_primitive) > RETURN(GET_C(bytecode.m_src).jsValue().toPrimitive(exec)); > } > >+SLOW_PATH_DECL(slow_path_to_property_key) >+{ >+ BEGIN(); >+ auto bytecode = pc->as<OpToPropertyKey>(); >+ RETURN(GET_C(bytecode.m_src).jsValue().toPropertyKey(exec, JSValue::Tag)); >+} >+ > SLOW_PATH_DECL(slow_path_enter) > { > BEGIN(); >@@ -1359,4 +1366,194 @@ SLOW_PATH_DECL(slow_path_spread) > RETURN(JSFixedArray::createFromArray(exec, vm, array)); > } > >+SLOW_PATH_DECL(slow_path_add_private_field) >+{ >+ BEGIN(); >+ CodeBlock* codeBlock = exec->codeBlock(); >+ auto bytecode = pc->as<OpAddPrivateField>(); >+ auto& metadata = bytecode.metadata(exec); >+ >+ JSValue baseValue = GET(bytecode.m_base).jsValue(); >+ ASSERT(baseValue.isObject()); >+ const Identifier& name = codeBlock->identifier(bytecode.m_fieldName); >+ JSValue value = GET_C(bytecode.m_value).jsValue(); >+ JSScope* scope = jsCast<JSScope*>(GET(bytecode.m_scope).jsValue()); >+ >+ auto uid = JSScope::getPrivateSymbol(vm, scope, name); >+ ASSERT(uid); >+ >+ auto privateName = Identifier::fromUid(&vm, uid); >+ { >+ PropertySlot slot(baseValue, PropertySlot::PropertySlot::InternalMethodType::VMInquiry); >+ >+ bool found = baseValue.getOwnPropertySlot(exec, privateName, slot); >+ if (found) >+ THROW(createTypeError(exec, "Attempted to redeclare private field")); >+ } >+ >+ PutPropertySlot slot(baseValue, codeBlock->isStrictMode()); >+ asObject(baseValue)->putDirect(vm, privateName, value, slot); >+ CHECK_EXCEPTION(); >+ >+ if (!LLINT_ALWAYS_ACCESS_SLOW && slot.isCacheablePut()) { >+ StructureID oldStructureID = metadata.m_oldStructureID; >+ if (oldStructureID) { >+ Structure* a = vm.heap.structureIDTable().get(oldStructureID); >+ Structure* b = baseValue.asCell()->structure(vm)->previousID(); >+ >+ if (Structure::shouldConvertToPolyProto(a, b)) { >+ a->rareData()->sharedPolyProtoWatchpoint()->invalidate(vm, StringFireDetail("Detected poly proto opportunity.")); >+ b->rareData()->sharedPolyProtoWatchpoint()->invalidate(vm, StringFireDetail("Detected poly proto opportunity.")); >+ } >+ } >+ >+ // Start out by clearing out the old cache. >+ metadata.m_oldStructureID = metadata.m_newStructureID = 0; >+ metadata.m_offset = 0; >+ >+ JSCell* baseCell = baseValue.asCell(); >+ Structure* structure = baseCell->structure(vm); >+ >+ if (!structure->isUncacheableDictionary() >+ && !structure->typeInfo().prohibitsPropertyCaching() >+ && baseCell == slot.base()) { >+ >+ vm.heap.writeBarrier(codeBlock); >+ GCSafeConcurrentJSLocker locker(codeBlock->m_lock, vm.heap); >+ >+ if (!structure->isDictionary() && structure->previousID()->outOfLineCapacity() == structure->outOfLineCapacity()) { >+ ASSERT(structure->previousID()->transitionWatchpointSetHasBeenInvalidated()); >+ ASSERT(structure->previousID()->isObject()); >+ metadata.m_oldStructureID = structure->previousID()->id(); >+ metadata.m_offset = slot.cachedOffset(); >+ metadata.m_newStructureID = structure->id(); >+ } >+ } >+ } >+ >+ END(); >+} >+ >+SLOW_PATH_DECL(slow_path_get_private_field) >+{ >+ BEGIN(); >+ auto bytecode = pc->as<OpGetPrivateField>(); >+ auto& metadata = bytecode.metadata(exec); >+ >+ JSValue baseValue = GET(bytecode.m_base).jsValue(); >+ if (!baseValue.isObject()) >+ THROW(createTypeError(exec, "Attempted to access invalid private field")); >+ >+ const Identifier& name = exec->codeBlock()->identifier(bytecode.m_fieldName); >+ JSScope* scope = jsCast<JSScope*>(GET(bytecode.m_scope).jsValue()); >+ >+ auto uid = JSScope::getPrivateSymbol(vm, scope, name); >+ ASSERT(uid); >+ >+ auto privateName = Identifier::fromUid(&vm, uid); >+ PropertySlot slot(baseValue, PropertySlot::PropertySlot::InternalMethodType::VMInquiry); >+ >+ bool found = baseValue.getOwnPropertySlot(exec, privateName, slot); >+ if (!found) >+ THROW(createTypeError(exec, "Attempted to access invalid private field")); >+ >+ if (!LLINT_ALWAYS_ACCESS_SLOW && slot.isCacheable()) { >+ StructureID oldStructureID = metadata.m_structureID; >+ if (oldStructureID) { >+ Structure* a = vm.heap.structureIDTable().get(oldStructureID); >+ Structure* b = baseValue.asCell()->structure(vm); >+ >+ if (Structure::shouldConvertToPolyProto(a, b)) { >+ ASSERT(a->rareData()->sharedPolyProtoWatchpoint().get() == b->rareData()->sharedPolyProtoWatchpoint().get()); >+ a->rareData()->sharedPolyProtoWatchpoint()->invalidate(vm, StringFireDetail("Detected poly proto opportunity.")); >+ } >+ } >+ >+ JSCell* baseCell = baseValue.asCell(); >+ Structure* structure = baseCell->structure(vm); >+ if (slot.isValue()) { >+ // Start out by clearing out the old cache. >+ metadata.m_structureID = 0; >+ metadata.m_offset = 0; >+ >+ CodeBlock* codeBlock = exec->codeBlock(); >+ if (structure->propertyAccessesAreCacheable() >+ && !structure->needImpurePropertyWatchpoint()) { >+ vm.heap.writeBarrier(codeBlock); >+ >+ ConcurrentJSLocker locker(codeBlock->m_lock); >+ >+ metadata.m_structureID = structure->id(); >+ metadata.m_offset = slot.cachedOffset(); >+ } >+ } >+ } >+ >+ RETURN_PROFILED(slot.getValue(exec, privateName)); >+} >+ >+SLOW_PATH_DECL(slow_path_put_private_field) >+{ >+ BEGIN(); >+ CodeBlock* codeBlock = exec->codeBlock(); >+ auto bytecode = pc->as<OpPutPrivateField>(); >+ auto& metadata = bytecode.metadata(exec); >+ >+ JSValue baseValue = GET(bytecode.m_base).jsValue(); >+ if (!baseValue.isObject()) >+ THROW(createTypeError(exec, "Attempted to access invalid private field")); >+ >+ const Identifier& name = codeBlock->identifier(bytecode.m_fieldName); >+ JSValue value = GET_C(bytecode.m_value).jsValue(); >+ JSScope* scope = jsCast<JSScope*>(GET(bytecode.m_scope).jsValue()); >+ >+ auto uid = JSScope::getPrivateSymbol(vm, scope, name); >+ ASSERT(uid); >+ >+ auto privateName = Identifier::fromUid(&vm, uid); >+ { >+ PropertySlot slot(baseValue, PropertySlot::PropertySlot::InternalMethodType::GetOwnProperty); >+ >+ bool found = baseValue.getOwnPropertySlot(exec, privateName, slot); >+ if (!found) >+ THROW(createTypeError(exec, "Attempted to access invalid private field")); >+ } >+ >+ PutPropertySlot slot(baseValue, codeBlock->isStrictMode()); >+ asObject(baseValue)->putDirect(vm, privateName, value, slot); >+ CHECK_EXCEPTION(); >+ >+ if (!LLINT_ALWAYS_ACCESS_SLOW && slot.isCacheablePut()) { >+ StructureID oldStructureID = metadata.m_structureID; >+ if (oldStructureID) { >+ Structure* a = vm.heap.structureIDTable().get(oldStructureID); >+ Structure* b = baseValue.asCell()->structure(vm); >+ >+ if (Structure::shouldConvertToPolyProto(a, b)) { >+ a->rareData()->sharedPolyProtoWatchpoint()->invalidate(vm, StringFireDetail("Detected poly proto opportunity.")); >+ b->rareData()->sharedPolyProtoWatchpoint()->invalidate(vm, StringFireDetail("Detected poly proto opportunity.")); >+ } >+ } >+ >+ // Start out by clearing out the old cache. >+ metadata.m_structureID = 0; >+ metadata.m_offset = 0; >+ >+ JSCell* baseCell = baseValue.asCell(); >+ Structure* structure = baseCell->structure(vm); >+ >+ if (!structure->isUncacheableDictionary() >+ && !structure->typeInfo().prohibitsPropertyCaching() >+ && baseCell == slot.base()) { >+ >+ vm.heap.writeBarrier(codeBlock); >+ structure->didCachePropertyReplacement(vm, slot.cachedOffset()); >+ metadata.m_structureID = structure->id(); >+ metadata.m_offset = slot.cachedOffset(); >+ } >+ } >+ >+ END(); >+} >+ > } // namespace JSC >diff --git a/Source/JavaScriptCore/runtime/CommonSlowPaths.h b/Source/JavaScriptCore/runtime/CommonSlowPaths.h >index 7eeaf704b43..9ded7017b9c 100644 >--- a/Source/JavaScriptCore/runtime/CommonSlowPaths.h >+++ b/Source/JavaScriptCore/runtime/CommonSlowPaths.h >@@ -367,6 +367,7 @@ SLOW_PATH_HIDDEN_DECL(slow_path_in_by_val); > SLOW_PATH_HIDDEN_DECL(slow_path_del_by_val); > SLOW_PATH_HIDDEN_DECL(slow_path_strcat); > SLOW_PATH_HIDDEN_DECL(slow_path_to_primitive); >+SLOW_PATH_HIDDEN_DECL(slow_path_to_property_key); > SLOW_PATH_HIDDEN_DECL(slow_path_get_enumerable_length); > SLOW_PATH_HIDDEN_DECL(slow_path_has_generic_property); > SLOW_PATH_HIDDEN_DECL(slow_path_has_structure_property); >@@ -394,6 +395,9 @@ SLOW_PATH_HIDDEN_DECL(slow_path_throw_static_error); > SLOW_PATH_HIDDEN_DECL(slow_path_new_array_with_spread); > SLOW_PATH_HIDDEN_DECL(slow_path_new_array_buffer); > SLOW_PATH_HIDDEN_DECL(slow_path_spread); >+SLOW_PATH_HIDDEN_DECL(slow_path_add_private_field); >+SLOW_PATH_HIDDEN_DECL(slow_path_get_private_field); >+SLOW_PATH_HIDDEN_DECL(slow_path_put_private_field); > > using SlowPathFunction = SlowPathReturnType(SLOW_PATH *)(ExecState*, const Instruction*); > >diff --git a/Source/JavaScriptCore/runtime/FunctionPrototype.cpp b/Source/JavaScriptCore/runtime/FunctionPrototype.cpp >index 6105e0430bb..869b12845cb 100644 >--- a/Source/JavaScriptCore/runtime/FunctionPrototype.cpp >+++ b/Source/JavaScriptCore/runtime/FunctionPrototype.cpp >@@ -116,6 +116,7 @@ EncodedJSValue JSC_HOST_CALL functionProtoFuncToString(ExecState* exec) > break; > > case SourceParseMode::ArrowFunctionMode: >+ case SourceParseMode::InstanceFieldInitializerMode: > functionHeader = ""; > break; > >diff --git a/Source/JavaScriptCore/runtime/Identifier.h b/Source/JavaScriptCore/runtime/Identifier.h >index 54e84631d76..f992a553f43 100644 >--- a/Source/JavaScriptCore/runtime/Identifier.h >+++ b/Source/JavaScriptCore/runtime/Identifier.h >@@ -301,6 +301,11 @@ struct IdentifierMapIndexHashTraits : HashTraits<int> { > static const bool emptyValueIsZero = false; > }; > >+struct StringRepHash : PtrHash<RefPtr<UniquedStringImpl>> { >+ static unsigned hash(const RefPtr<UniquedStringImpl>& key) { return key->existingHash(); } >+ static unsigned hash(UniquedStringImpl* key) { return key->existingHash(); } >+}; >+ > typedef HashSet<RefPtr<UniquedStringImpl>, IdentifierRepHash> IdentifierSet; > typedef HashMap<RefPtr<UniquedStringImpl>, int, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>, IdentifierMapIndexHashTraits> IdentifierMap; > typedef HashMap<UniquedStringImpl*, int, IdentifierRepHash, HashTraits<UniquedStringImpl*>, IdentifierMapIndexHashTraits> BorrowedIdentifierMap; >diff --git a/Source/JavaScriptCore/runtime/JSCJSValue.h b/Source/JavaScriptCore/runtime/JSCJSValue.h >index 1957528f260..1965b9a4b9e 100644 >--- a/Source/JavaScriptCore/runtime/JSCJSValue.h >+++ b/Source/JavaScriptCore/runtime/JSCJSValue.h >@@ -174,6 +174,7 @@ public: > enum JSFalseTag { JSFalse }; > enum JSCellTag { JSCellType }; > enum EncodeAsDoubleTag { EncodeAsDouble }; >+ enum JSValueTag { Tag }; > > JSValue(); > JSValue(JSNullTag); >@@ -267,6 +268,7 @@ public: > JSString* toString(ExecState*) const; // On exception, this returns the empty string. > JSString* toStringOrNull(ExecState*) const; // On exception, this returns null, to make exception checks faster. > Identifier toPropertyKey(ExecState*) const; >+ JSValue toPropertyKey(ExecState*, JSValueTag) const; > WTF::String toWTFString(ExecState*) const; > JSObject* toObject(ExecState*) const; > JSObject* toObject(ExecState*, JSGlobalObject*) const; >diff --git a/Source/JavaScriptCore/runtime/JSCJSValueInlines.h b/Source/JavaScriptCore/runtime/JSCJSValueInlines.h >index 6cf3a223c2d..f2b6f971a3d 100644 >--- a/Source/JavaScriptCore/runtime/JSCJSValueInlines.h >+++ b/Source/JavaScriptCore/runtime/JSCJSValueInlines.h >@@ -657,6 +657,22 @@ ALWAYS_INLINE Identifier JSValue::toPropertyKey(ExecState* exec) const > RELEASE_AND_RETURN(scope, primitive.toString(exec)->toIdentifier(exec)); > } > >+ALWAYS_INLINE JSValue JSValue::toPropertyKey(ExecState* exec, JSValueTag) const >+{ >+ VM& vm = exec->vm(); >+ auto scope = DECLARE_THROW_SCOPE(vm); >+ >+ if (isString() || isSymbol()) >+ return *this; >+ >+ JSValue primitive = toPrimitive(exec, PreferString); >+ RETURN_IF_EXCEPTION(scope, JSValue()); >+ if (primitive.isSymbol()) >+ return primitive; >+ scope.release(); >+ return primitive.toString(exec); >+} >+ > inline JSValue JSValue::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const > { > return isCell() ? asCell()->toPrimitive(exec, preferredType) : asValue(); >diff --git a/Source/JavaScriptCore/runtime/JSFunction.cpp b/Source/JavaScriptCore/runtime/JSFunction.cpp >index 50568e846b6..61b41719efa 100644 >--- a/Source/JavaScriptCore/runtime/JSFunction.cpp >+++ b/Source/JavaScriptCore/runtime/JSFunction.cpp >@@ -395,6 +395,7 @@ EncodedJSValue JSFunction::callerGetter(ExecState* exec, EncodedJSValue thisValu > case SourceParseMode::AsyncGeneratorWrapperFunctionMode: > case SourceParseMode::AsyncGeneratorWrapperMethodMode: > case SourceParseMode::GeneratorWrapperMethodMode: >+ case SourceParseMode::InstanceFieldInitializerMode: > if (!function->jsExecutable()->isStrictMode()) > return JSValue::encode(caller); > return JSValue::encode(throwTypeError(exec, scope, "Function.caller used to retrieve strict caller"_s)); >diff --git a/Source/JavaScriptCore/runtime/JSScope.cpp b/Source/JavaScriptCore/runtime/JSScope.cpp >index ccc00156102..ed2a9a2a6b0 100644 >--- a/Source/JavaScriptCore/runtime/JSScope.cpp >+++ b/Source/JavaScriptCore/runtime/JSScope.cpp >@@ -339,6 +339,11 @@ void JSScope::collectClosureVariablesUnderTDZ(JSScope* scope, VariableEnvironmen > ConcurrentJSLocker locker(symbolTable->m_lock); > for (auto end = symbolTable->end(locker), iter = symbolTable->begin(locker); iter != end; ++iter) > result.add(iter->key); >+ if (symbolTable->hasPrivateNames()) { >+ auto privateNames = symbolTable->privateNames(); >+ for (auto end = privateNames.end(), iter = privateNames.begin(); iter != end; ++iter) >+ result.usePrivateName(*iter); >+ } > } > } > >@@ -416,4 +421,16 @@ JSValue JSScope::toThis(JSCell*, ExecState* exec, ECMAMode ecmaMode) > return exec->globalThisValue(); > } > >+PrivateSymbolImpl* JSScope::getPrivateSymbol(VM& vm, JSScope* scope, const Identifier& name) >+{ >+ for (; scope; scope = scope->next()) { >+ if (JSSymbolTableObject* symbolTableObject = jsDynamicCast<JSSymbolTableObject*>(vm, scope)) { >+ if (PrivateSymbolImpl* uid = symbolTableObject->getPrivateSymbol(name)) >+ return uid; >+ } >+ } >+ >+ return nullptr; >+} >+ > } // namespace JSC >diff --git a/Source/JavaScriptCore/runtime/JSScope.h b/Source/JavaScriptCore/runtime/JSScope.h >index f6b773a7ad1..0c4c1334f98 100644 >--- a/Source/JavaScriptCore/runtime/JSScope.h >+++ b/Source/JavaScriptCore/runtime/JSScope.h >@@ -58,6 +58,8 @@ public: > > static void visitChildren(JSCell*, SlotVisitor&); > >+ static PrivateSymbolImpl* getPrivateSymbol(VM&, JSScope*, const Identifier&); >+ > bool isVarScope(); > bool isLexicalScope(); > bool isModuleScope(); >diff --git a/Source/JavaScriptCore/runtime/JSSymbolTableObject.h b/Source/JavaScriptCore/runtime/JSSymbolTableObject.h >index 0e8644eaea2..aaf2eb54c5e 100644 >--- a/Source/JavaScriptCore/runtime/JSSymbolTableObject.h >+++ b/Source/JavaScriptCore/runtime/JSSymbolTableObject.h >@@ -48,6 +48,17 @@ public: > > static ptrdiff_t offsetOfSymbolTable() { return OBJECT_OFFSETOF(JSSymbolTableObject, m_symbolTable); } > >+ PrivateSymbolImpl* getPrivateSymbol(const Identifier& name) >+ { >+ if (m_rareData) { >+ auto it = m_rareData->m_privateNames.find(name.impl()); >+ if (it != m_rareData->m_privateNames.end()) >+ return it->value.get(); >+ } >+ >+ return nullptr; >+ } >+ > DECLARE_EXPORT_INFO; > > protected: >@@ -62,18 +73,29 @@ protected: > ASSERT(symbolTable); > setSymbolTable(vm, symbolTable); > } >- >+ > void setSymbolTable(VM& vm, SymbolTable* symbolTable) > { > ASSERT(!m_symbolTable); > symbolTable->singletonScope()->notifyWrite(vm, this, "Allocated a scope"); > m_symbolTable.set(vm, this, symbolTable); >+ if (symbolTable->hasPrivateNames()) { >+ m_rareData = std::make_unique<JSSymbolTableObjectRareData>(); >+ for (auto key : symbolTable->privateNames()) >+ m_rareData->m_privateNames.set(key.get(), PrivateSymbolImpl::create(*key.get())); >+ } > } >- >+ > static void visitChildren(JSCell*, SlotVisitor&); > > private: >+ typedef HashMap<RefPtr<UniquedStringImpl>, RefPtr<PrivateSymbolImpl>, StringRepHash> PrivateNameMap; >+ struct JSSymbolTableObjectRareData { >+ PrivateNameMap m_privateNames; >+ }; >+ > WriteBarrier<SymbolTable> m_symbolTable; >+ std::unique_ptr<JSSymbolTableObjectRareData> m_rareData; > }; > > template<typename SymbolTableObjectType> >diff --git a/Source/JavaScriptCore/runtime/SymbolTable.cpp b/Source/JavaScriptCore/runtime/SymbolTable.cpp >index 6b95a0ed863..205532bba96 100644 >--- a/Source/JavaScriptCore/runtime/SymbolTable.cpp >+++ b/Source/JavaScriptCore/runtime/SymbolTable.cpp >@@ -187,6 +187,14 @@ SymbolTable* SymbolTable::cloneScopePart(VM& vm) > for (; iter != end; ++iter) > result->m_rareData->m_uniqueTypeSetMap.set(iter->key, iter->value); > } >+ >+ >+ { >+ auto iter = m_rareData->m_privateNames.begin(); >+ auto end = m_rareData->m_privateNames.end(); >+ for (; iter != end; ++iter) >+ result->m_rareData->m_privateNames.add(*iter); >+ } > } > > return result; >diff --git a/Source/JavaScriptCore/runtime/SymbolTable.h b/Source/JavaScriptCore/runtime/SymbolTable.h >index 59691b2ec41..614e6798229 100644 >--- a/Source/JavaScriptCore/runtime/SymbolTable.h >+++ b/Source/JavaScriptCore/runtime/SymbolTable.h >@@ -38,7 +38,7 @@ > #include "Watchpoint.h" > #include <memory> > #include <wtf/HashTraits.h> >-#include <wtf/text/UniquedStringImpl.h> >+#include <wtf/text/SymbolImpl.h> > > namespace JSC { > >@@ -449,6 +449,8 @@ public: > typedef HashMap<RefPtr<UniquedStringImpl>, RefPtr<TypeSet>, IdentifierRepHash> UniqueTypeSetMap; > typedef HashMap<VarOffset, RefPtr<UniquedStringImpl>> OffsetToVariableMap; > typedef Vector<SymbolTableEntry*> LocalToEntryVec; >+ typedef HashSet<RefPtr<UniquedStringImpl>, StringRepHash> PrivateNameSet; >+ typedef WTF::IteratorRange<typename PrivateNameSet::iterator> PrivateNameIteratorRange; > > static SymbolTable* create(VM& vm) > { >@@ -587,7 +589,28 @@ public: > ConcurrentJSLocker locker(m_lock); > add(locker, key, std::forward<Entry>(entry)); > } >- >+ >+ bool hasPrivateNames() const { return m_rareData && m_rareData->m_privateNames.size(); } >+ ALWAYS_INLINE PrivateNameIteratorRange privateNames() >+ { >+ PrivateNameSet::iterator begin, end; >+ if (m_rareData) { >+ begin = m_rareData->m_privateNames.begin(); >+ end = m_rareData->m_privateNames.end(); >+ } >+ return PrivateNameIteratorRange(begin, end); >+ } >+ >+ void addPrivateName(UniquedStringImpl* key) >+ { >+ ASSERT(key && !key->isSymbol()); >+ if (!m_rareData) >+ m_rareData = std::make_unique<SymbolTableRareData>(); >+ >+ ASSERT(!m_rareData->m_privateNames.contains(key)); >+ m_rareData->m_privateNames.add(key); >+ } >+ > template<typename Entry> > void set(const ConcurrentJSLocker&, UniquedStringImpl* key, Entry&& entry) > { >@@ -613,7 +636,7 @@ public: > ConcurrentJSLocker locker(m_lock); > return contains(locker, key); > } >- >+ > // The principle behind ScopedArgumentsTable modifications is that we will create one and > // leave it unlocked - thereby allowing in-place changes - until someone asks for a pointer to > // the table. Then, we will lock it. Then both our future changes and their future changes >@@ -712,6 +735,7 @@ private: > OffsetToVariableMap m_offsetToVariableMap; > UniqueTypeSetMap m_uniqueTypeSetMap; > WriteBarrier<CodeBlock> m_codeBlock; >+ PrivateNameSet m_privateNames; > }; > std::unique_ptr<SymbolTableRareData> m_rareData; > >-- >2.20.1 >
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
Flags:
saam
:
review-
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 174212
:
314736
|
327359
|
335960
|
344172
|
344176
|
348905
|
348907
|
350893
|
350896
|
350917
|
350923
|
350924
|
350925
|
350935
|
350962
|
351261
|
351286
|
351354
|
353696
|
353698
|
353699
|
353848
|
353849
|
353857
|
353954
|
356594
|
356595
|
360454
|
366588
|
367749
|
368117
|
369295
|
369383
|
371258
|
371265
|
371266
|
371267
|
371268
|
371270
|
371272
|
371273
|
371276
|
371277
|
371278
|
371383
|
371384
|
371387
|
371392
|
373112
|
373117
|
373142
|
373153
|
380353
|
380552
|
380560
|
380757
|
380826
|
381427
|
381457
|
381711
|
382314
|
382318
|
383468
|
384358
|
384359
|
384927
|
384930
|
385494
|
385496
|
387356
|
387361
|
387363
|
387818
|
387827
|
387927