WebKit Bugzilla
Attachment 371258 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-040619.diff (text/plain), 266.30 KB, created by
Xan Lopez
on 2019-06-04 02:39:42 PDT
(
hide
)
Description:
Class fields, parser + bytecode + test changes
Filename:
MIME Type:
Creator:
Xan Lopez
Created:
2019-06-04 02:39:42 PDT
Size:
266.30 KB
patch
obsolete
>From 99dd8635464bb0edf2622c1fd732ec6c01b934c3 Mon Sep 17 00:00:00 2001 >From: =?UTF-8?q?Xan=20L=C3=B3pez?= <xan@igalia.com> >Date: Thu, 11 Oct 2018 18:58:14 +0200 >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): >* 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::generateUnlinkedCodeBlock): >* 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/class-fields-bytecode-cache.js | 19 + > JSTests/stress/class-fields-function-name.js | 17 + > JSTests/stress/class-fields-harmony.js | 880 ++++++++++++++++++ > .../class-fields-private-as-function.js | 18 + > .../class-fields-private-cached-bytecode.js | 18 + > .../stress/class-fields-private-on-proxy.js | 39 + > .../class-fields-private-out-of-line.js | 28 + > .../stress/class-fields-private-use-eval.js | 15 + > .../class-fields-proxy-define-property.js | 40 + > .../stress/class-fields-stress-instance.js | 21 + > JSTests/stress/generator-syntax.js | 4 +- > JSTests/stress/method-name.js | 2 +- > JSTests/test262/config.yaml | 3 - > LayoutTests/ChangeLog | 23 + > .../js/class-syntax-semicolon-expected.txt | 5 - > .../js/script-tests/class-syntax-semicolon.js | 5 - > Source/JavaScriptCore/ChangeLog | 300 ++++++ > .../builtins/BuiltinExecutableCreator.cpp | 2 +- > .../builtins/BuiltinExecutables.cpp | 12 +- > .../builtins/BuiltinExecutables.h | 5 +- > Source/JavaScriptCore/builtins/BuiltinNames.h | 1 + > .../JavaScriptCore/bytecode/BytecodeList.rb | 44 + > .../JavaScriptCore/bytecode/BytecodeUseDef.h | 9 + > Source/JavaScriptCore/bytecode/CodeBlock.cpp | 49 + > .../JavaScriptCore/bytecode/ExecutableInfo.h | 8 +- > .../bytecode/UnlinkedCodeBlock.cpp | 1 + > .../bytecode/UnlinkedCodeBlock.h | 4 + > .../bytecode/UnlinkedFunctionExecutable.cpp | 10 +- > .../bytecode/UnlinkedFunctionExecutable.h | 25 +- > .../bytecompiler/BytecodeGenerator.cpp | 136 ++- > .../bytecompiler/BytecodeGenerator.h | 25 +- > .../bytecompiler/NodesCodegen.cpp | 204 +++- > .../debugger/DebuggerCallFrame.cpp | 2 +- > Source/JavaScriptCore/dfg/DFGCapabilities.cpp | 4 + > .../interpreter/Interpreter.cpp | 6 +- > Source/JavaScriptCore/jit/JIT.cpp | 8 + > Source/JavaScriptCore/jit/JIT.h | 4 + > Source/JavaScriptCore/jit/JITOpcodes.cpp | 17 + > .../llint/LowLevelInterpreter32_64.asm | 83 ++ > .../llint/LowLevelInterpreter64.asm | 90 ++ > Source/JavaScriptCore/parser/ASTBuilder.h | 33 +- > Source/JavaScriptCore/parser/Lexer.cpp | 75 +- > .../JavaScriptCore/parser/NodeConstructors.h | 61 +- > Source/JavaScriptCore/parser/Nodes.cpp | 23 + > Source/JavaScriptCore/parser/Nodes.h | 102 +- > Source/JavaScriptCore/parser/Parser.cpp | 201 +++- > Source/JavaScriptCore/parser/Parser.h | 145 ++- > Source/JavaScriptCore/parser/ParserModes.h | 4 +- > Source/JavaScriptCore/parser/ParserTokens.h | 1 + > Source/JavaScriptCore/parser/SyntaxChecker.h | 16 +- > .../parser/VariableEnvironment.cpp | 52 +- > .../parser/VariableEnvironment.h | 134 ++- > Source/JavaScriptCore/runtime/CachedTypes.cpp | 22 + > Source/JavaScriptCore/runtime/CodeCache.cpp | 2 +- > Source/JavaScriptCore/runtime/CodeCache.h | 16 +- > .../runtime/CommonIdentifiers.cpp | 2 + > .../runtime/CommonIdentifiers.h | 7 + > .../runtime/CommonSlowPaths.cpp | 197 ++++ > .../JavaScriptCore/runtime/CommonSlowPaths.h | 4 + > .../runtime/DirectEvalExecutable.cpp | 8 +- > .../runtime/DirectEvalExecutable.h | 5 +- > .../JavaScriptCore/runtime/EvalExecutable.h | 6 +- > .../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 +- > .../runtime/ModuleProgramExecutable.h | 3 +- > Source/JavaScriptCore/runtime/Options.h | 2 +- > .../runtime/ProgramExecutable.h | 2 +- > Source/JavaScriptCore/runtime/SymbolTable.cpp | 7 + > Source/JavaScriptCore/runtime/SymbolTable.h | 42 +- > 77 files changed, 3240 insertions(+), 227 deletions(-) > create mode 100644 JSTests/stress/class-fields-bytecode-cache.js > create mode 100644 JSTests/stress/class-fields-function-name.js > create mode 100644 JSTests/stress/class-fields-harmony.js > create mode 100644 JSTests/stress/class-fields-private-as-function.js > create mode 100644 JSTests/stress/class-fields-private-cached-bytecode.js > create mode 100644 JSTests/stress/class-fields-private-on-proxy.js > create mode 100644 JSTests/stress/class-fields-private-out-of-line.js > create mode 100644 JSTests/stress/class-fields-private-use-eval.js > create mode 100644 JSTests/stress/class-fields-proxy-define-property.js > create mode 100644 JSTests/stress/class-fields-stress-instance.js > >diff --git a/JSTests/ChangeLog b/JSTests/ChangeLog >index bb678862475..35c21ae3c50 100644 >--- a/JSTests/ChangeLog >+++ b/JSTests/ChangeLog >@@ -1,3 +1,27 @@ >+2018-11-02 Xan López <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-06-03 Caio Lima <ticaiolima@gmail.com> > > [ESNext][BigInt] Implement support for "**" >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/class-fields-bytecode-cache.js b/JSTests/stress/class-fields-bytecode-cache.js >new file mode 100644 >index 00000000000..1d4fcfa07cb >--- /dev/null >+++ b/JSTests/stress/class-fields-bytecode-cache.js >@@ -0,0 +1,19 @@ >+//@ runBytecodeCache("--useClassFields=true") >+ >+function assert(a, e) { >+ if (a !== e) >+ throw new Erro("Expected: " + e + " but got: " + a); >+} >+ >+class C{ >+ foo() { >+ return this.f; >+ } >+ >+ f = 15; >+} >+ >+let c = new C(); >+let r = c.foo(); >+assert(r, 15); >+ >diff --git a/JSTests/stress/class-fields-function-name.js b/JSTests/stress/class-fields-function-name.js >new file mode 100644 >index 00000000000..c229a61d70f >--- /dev/null >+++ b/JSTests/stress/class-fields-function-name.js >@@ -0,0 +1,17 @@ >+//@ requireOptions("--useClassFields=true") >+ >+function assert(a, message) { >+ if (!a) >+ throw new Error(message); >+} >+ >+class A { >+ foo = function() { }; >+ bar = class {}; >+ baz = "test"; >+} >+ >+var a = new A(); >+assert(a.foo.name == "foo"); >+assert(a.bar.name == "bar"); >+assert(a.baz.name == undefined); >diff --git a/JSTests/stress/class-fields-harmony.js b/JSTests/stress/class-fields-harmony.js >new file mode 100644 >index 00000000000..55bbc8297a8 >--- /dev/null >+++ b/JSTests/stress/class-fields-harmony.js >@@ -0,0 +1,880 @@ >+// Copyright 2017 the V8 project authors. All rights reserved. >+// Use of this source code is governed by a BSD-style license that can be >+// found in the LICENSE file. >+"use strict"; >+ >+function classOf(object) { >+ // Argument must not be null or undefined. >+ var string = Object.prototype.toString.call(object); >+ // String has format [object <ClassName>]. >+ return string.substring(8, string.length - 1); >+} >+ >+/** >+ * Compares two objects for key/value equality. >+ * Returns true if they are equal, false otherwise. >+ */ >+function deepObjectEquals(a, b) { >+ var aProps = Object.keys(a); >+ aProps.sort(); >+ var bProps = Object.keys(b); >+ bProps.sort(); >+ if (!deepEquals(aProps, bProps)) { >+ return false; >+ } >+ for (var i = 0; i < aProps.length; i++) { >+ if (!deepEquals(a[aProps[i]], b[aProps[i]])) { >+ return false; >+ } >+ } >+ return true; >+} >+ >+/** >+ * Compares two JavaScript values for type and value equality. >+ * It checks internals of arrays and objects. >+ */ >+function deepEquals(a, b) { >+ if (a === b) { >+ // Check for -0. >+ if (a === 0) return (1 / a) === (1 / b); >+ return true; >+ } >+ if (typeof a != typeof b) return false; >+ if (typeof a == 'number') return isNaN(a) && isNaN(b); >+ if (typeof a !== 'object' && typeof a !== 'function') return false; >+ // Neither a nor b is primitive. >+ var objectClass = classOf(a); >+ if (objectClass !== classOf(b)) return false; >+ if (objectClass === 'RegExp') { >+ // For RegExp, just compare pattern and flags using its toString. >+ return (a.toString() === b.toString()); >+ } >+ // Functions are only identical to themselves. >+ if (objectClass === 'Function') return false; >+ if (objectClass === 'Array') { >+ var elementCount = 0; >+ if (a.length != b.length) { >+ return false; >+ } >+ for (var i = 0; i < a.length; i++) { >+ if (!deepEquals(a[i], b[i])) return false; >+ } >+ return true; >+ } >+ if (objectClass == 'String' || objectClass == 'Number' || >+ objectClass == 'Boolean' || objectClass == 'Date') { >+ if (a.valueOf() !== b.valueOf()) return false; >+ } >+ return deepObjectEquals(a, b); >+} >+ >+/** >+ * Throws an exception containing the user_message (if any) and the values. >+ */ >+function fail(expected, found, user_message = '') { >+ // TODO(cira): Replace String with PrettyPrint for objects and arrays. >+ var message = 'Failure' + (user_message ? ' (' + user_message + ')' : '') + >+ ': expected <' + String(expected) + '>, found <' + String(found) + '>.'; >+ throw new Error(message); >+} >+ >+/** >+ * Throws if obj is not of given type. >+ */ >+function assertInstanceof(obj, type) { >+ if (!(obj instanceof type)) { >+ var actualTypeName = null; >+ var actualConstructor = Object.getPrototypeOf(obj).constructor; >+ if (typeof actualConstructor == "function") { >+ actualTypeName = actualConstructor.name || String(actualConstructor); >+ } >+ throw new Error('Object <' + obj + '> is not an instance of <' + >+ (type.name || type) + '>' + >+ (actualTypeName ? ' but of < ' + actualTypeName + '>' : '')); >+ } >+} >+ >+/** >+ * Throws if obj is not the same as expected. >+ */ >+function assertSame(expected, found, name_opt) { >+ if (found === expected) { >+ if (expected !== 0 || (1 / expected) === (1 / found)) >+ return; >+ } else if ((expected !== expected) && (found !== found)) { >+ return; >+ } >+ fail(expected, found, name_opt); >+} >+ >+/** >+ * Throws if two variables have different types or values. >+ */ >+ >+function assertEquals(expected, found, user_message = '') { >+ if (!deepEquals(expected, found)) { >+ fail(expected, found, user_message); >+ } >+} >+ >+/** >+ * Throws if two variables have equal types or values. >+ */ >+ >+function assertNotEquals(expected, found, user_message = '') { >+ if (deepEquals(expected, found)) { >+ fail(expected, found, user_message); >+ } >+} >+ >+/** >+ * Throws if value is false. >+ */ >+function assertTrue(value, user_message = '') { >+ assertEquals(true, value, user_message); >+} >+ >+ >+/** >+ * Throws if value is true. >+ */ >+function assertFalse(value, user_message = '') { >+ assertEquals(false, value, user_message); >+} >+ >+/** >+ * Runs code() and asserts that it throws the specified exception. >+ */ >+function assertThrows(code, type_opt, cause_opt) { >+ try { >+ if (typeof code == 'function') { >+ code(); >+ } else { >+ eval(code); >+ } >+ } catch (e) { >+ if (typeof type_opt == 'function') { >+ assertInstanceof(e, type_opt); >+ } >+ if (arguments.length >= 3) { >+ assertEquals(cause_opt, e.type, 'thrown exception type mismatch'); >+ } >+ // Success. >+ return; >+ } >+ var expected = arguments.length >= 3 ? cause_opt : >+ typeof type_opt == 'function' ? type_opt : 'any exception'; >+ fail(expected, 'no exception', 'expected thrown exception'); >+} >+ >+{ >+ class C { >+ a; >+ } >+ >+ assertEquals(undefined, C.a); >+ >+ let c = new C; >+ let descriptor = Object.getOwnPropertyDescriptor(c, 'a'); >+ assertTrue(c.hasOwnProperty('a')); >+ assertTrue(descriptor.writable); >+ assertTrue(descriptor.enumerable); >+ assertTrue(descriptor.configurable); >+ assertEquals(undefined, c.a); >+} >+ >+{ >+ class C { >+ x = 1; >+ constructor() {} >+ } >+ >+ let c = new C; >+ assertEquals(1, c.x); >+} >+ >+{ >+ function t() { >+ class X { >+ x = 1; >+ constructor() {} >+ } >+ >+ var x = new X; >+ return x.x; >+ } >+ >+ assertEquals(1, t()); >+} >+ >+{ >+ let x = 'a'; >+ class C { >+ a; >+ b = x; >+ c = 1; >+ hasOwnProperty() { return 1;} >+ } >+ >+ let c = new C; >+ assertEquals(undefined, c.a); >+ assertEquals('a', c.b); >+ assertEquals(1, c.c); >+ assertEquals(undefined, c.d); >+ assertEquals(1, c.hasOwnProperty()); >+} >+ >+{ >+ class C { >+ x = Object.freeze(this); >+ c = 42; >+ } >+ assertThrows(() => { new C; }, TypeError); >+} >+ >+{ >+ class C { >+ c = this; >+ d = () => this; >+ } >+ >+ let c = new C; >+ assertEquals(c, c.c); >+ assertEquals(c, c.d()); >+ >+ assertEquals(undefined, C.c); >+ assertEquals(undefined, C.d); >+} >+ >+{ >+ class C { >+ c = 1; >+ d = this.c; >+ } >+ >+ let c = new C; >+ assertEquals(1, c.c); >+ assertEquals(1, c.d); >+ >+ assertEquals(undefined, C.c); >+ assertEquals(undefined, C.d); >+} >+ >+{ >+ class C { >+ b = 1; >+ c = () => this.b; >+ } >+ >+ let c = new C; >+ assertEquals(1, c.b); >+ assertEquals(1, c.c()); >+ >+ assertEquals(undefined, C.c); >+ assertEquals(undefined, C.b); >+} >+ >+{ >+ let x = 'a'; >+ class C { >+ b = 1; >+ c = () => this.b; >+ e = () => x; >+ } >+ >+ let c = new C; >+ assertEquals(1, c.b); >+ assertEquals('a', c.e()); >+ >+ let a = {b : 2 }; >+ assertEquals(1, c.c.call(a)); >+ >+ assertEquals(undefined, C.b); >+ assertEquals(undefined, C.c); >+} >+ >+{ >+ let x = 'a'; >+ class C { >+ c = 1; >+ d = function() { return this.c; }; >+ e = function() { return x; }; >+ } >+ >+ let c = new C; >+ assertEquals(1, c.c); >+ assertEquals(1, c.d()); >+ assertEquals('a', c.e()); >+ >+ c.c = 2; >+ assertEquals(2, c.d()); >+ >+ let a = {c : 3 }; >+ assertEquals(3, c.d.call(a)); >+ >+ assertThrows(c.d.bind(undefined)); >+ >+ assertEquals(undefined, C.c); >+ assertEquals(undefined, C.d); >+ assertEquals(undefined, C.e); >+} >+ >+{ >+ class C { >+ c = function() { return 1 }; >+ } >+ >+ let c = new C; >+ assertEquals('c', c.c.name); >+} >+ >+{ >+ let d = function() { return new.target; } >+ class C { >+ c = d; >+ } >+ >+ let c = new C; >+ assertEquals(undefined, c.c()); >+ assertEquals(new d, new c.c()); >+} >+ >+{ >+ class C { >+ c = () => new.target; >+ } >+ >+ let c = new C; >+ assertEquals(undefined, c.c()); >+} >+ >+{ >+ let run = false; >+ class C { >+ c = () => { >+ let b; >+ class A { >+ constructor() { >+ b = new.target; >+ } >+ }; >+ new A; >+ run = true; >+ assertEquals(A, b); >+ } >+ } >+ >+ let c = new C; >+ c.c(); >+ assertTrue(run); >+} >+ >+{ >+ class C { >+ c = new.target; >+ } >+ >+ let c = new C; >+ assertEquals(undefined, c.c); >+} >+ >+{ >+ class B { >+ c = 1; >+ } >+ >+ class C extends B {} >+ >+ let c = new C; >+ assertEquals(1, c.c); >+} >+ >+{ >+ assertThrows(() => { >+ class C { >+ c = new C; >+ } >+ let c = new C; >+ }); >+} >+ >+(function test() { >+ function makeC() { >+ var x = 1; >+ >+ return class { >+ a = () => () => x; >+ } >+ } >+ >+ let C = makeC(); >+ let c = new C; >+ let f = c.a(); >+ assertEquals(1, f()); >+})() >+ >+{ >+ let c1 = "c"; >+ class C { >+ ["a"] = 1; >+ ["b"]; >+ [c1]; >+ } >+ >+ let c = new C; >+ assertEquals(1, c.a); >+ assertEquals(undefined, c.b); >+ assertEquals(undefined, c[c1]); >+} >+ >+{ >+ let log = []; >+ function run(i) { >+ log.push(i); >+ return i; >+ } >+ >+ class C { >+ [run(1)] = run(6); >+ [run(2)] = run(7); >+ [run(3)]() { run(9);} >+ [run(4)] = run(8); >+ [run(5)]() { throw new Error('should not execute');}; >+ } >+ >+ let c = new C; >+ c[3](); >+ assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], log); >+} >+ >+function x() { >+ // This tests lazy parsing. >+ return function() { >+ let log = []; >+ function run(i) { >+ log.push(i); >+ return i; >+ } >+ >+ class C { >+ [run(1)] = run(6); >+ [run(2)] = run(7); >+ [run(3)]() { run(9);} >+ [run(4)] = run(8); >+ [run(5)]() { throw new Error('should not execute');}; >+ } >+ >+ let c = new C; >+ c[3](); >+ assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], log); >+ } >+} >+x()(); >+ >+{ >+ class C {} >+ class D { >+ [C]; >+ } >+ >+ let d = new D; >+ assertThrows(() => { class X { [X] } let x = new X;}); >+ assertEquals(undefined, d[C]); >+} >+ >+{ >+ class B { >+ a = 1; >+ } >+ >+ class C extends B { >+ b = 2; >+ constructor() { >+ super(); >+ } >+ } >+ >+ let c = new C; >+ assertEquals(1, c.a); >+ assertEquals(2, c.b); >+} >+ >+{ >+ var log = []; >+ function addToLog(item) { log.push(item); } >+ >+ class B { >+ a = 1; >+ constructor() { >+ addToLog("base constructor"); >+ } >+ } >+ >+ function initF() { >+ addToLog("init f"); >+ return 1; >+ } >+ >+ class C extends B { >+ f = initF(); >+ >+ constructor() { >+ addToLog("derived constructor"); >+ var t = () => { >+ addToLog("t"); >+ if (1==-1) { >+ super(); >+ } else { >+ super(); >+ } >+ } >+ (() => { >+ addToLog("anon"); >+ t(); >+ })(); >+ } >+ } >+ >+ let c = new C; >+ assertEquals(1, c.f); >+ assertEquals(1, c.a); >+ assertEquals(["derived constructor","anon","t","base constructor","init f"], >+ log); >+} >+ >+{ >+ class B { >+ a = 1; >+ returnA = () => this.a; >+ } >+ >+ class C extends B { >+ c = this.a; >+ d = 2; >+ returnC = () => this.c; >+ returnD = () => this.d; >+ } >+ >+ let c = new C; >+ assertEquals(1, c.a); >+ assertEquals(1, c.returnA()); >+ assertEquals(1, c.c); >+ assertEquals(1, c.returnA()); >+ assertEquals(1, c.returnC()); >+ assertEquals(2, c.d); >+ assertEquals(2, c.returnD()); >+ >+ let c2 = new C; >+ assertNotEquals(c2.returnA, c.returnA); >+ assertNotEquals(c2.returnC, c.returnC); >+ assertNotEquals(c2.returnD, c.returnD); >+} >+ >+{ >+ let foo = undefined; >+ class B { >+ set d(x) { >+ foo = x; >+ } >+ } >+ >+ class C extends B { >+ d = 2; >+ } >+ >+ let c = new C; >+ assertEquals(undefined, foo); >+ assertEquals(2, c.d); >+} >+ >+{ >+ class B {} >+ class C extends B { >+ constructor() { >+ super(); >+ } >+ >+ c = 1; >+ } >+ >+ let c = new C; >+ assertEquals(1, c.c); >+} >+ >+{ >+ class B {} >+ class C extends B { >+ constructor() { >+ let t = () => { >+ super(); >+ } >+ t(); >+ } >+ >+ c = 1; >+ } >+ >+ let c = new C; >+ assertEquals(1, c.c); >+} >+ >+{ >+ let log = []; >+ >+ class B {} >+ >+ class C extends B { >+ >+ x = (log.push(1), 1); >+ >+ constructor() { >+ let t = () => { >+ class D extends B { >+ >+ x = (log.push(2), 2); >+ >+ constructor() { >+ let p = () => { >+ super(); >+ } >+ >+ p(); >+ } >+ } >+ >+ let d = new D(); >+ assertEquals(2, d.x); >+ super(); >+ } >+ >+ t(); >+ } >+ } >+ >+ >+ let c = new C; >+ assertEquals(1, c.x); >+ assertEquals([2, 1], log); >+} >+ >+{ >+ let log = []; >+ class C1 extends class {} { >+ x = log.push(1); >+ constructor() { >+ var t = () => super(); >+ super(); >+ t(); >+ } >+ } >+ >+ assertThrows(() => new C1, ReferenceError); >+ assertEquals([1], log); >+ >+ log = []; >+ class C2 extends class {} { >+ x = log.push(1); >+ constructor() { >+ var t = () => super(); >+ t(); >+ super(); >+ } >+ } >+ >+ assertThrows(() => new C2, ReferenceError); >+ assertEquals([1], log); >+} >+ >+{ >+ class C1 extends class {} { >+ x = 1 >+ constructor() { >+ eval("super()"); >+ } >+ } >+ >+ let c = new C1; >+ assertEquals(1, c.x); >+ >+ class C2 extends class {} { >+ x = 1 >+ constructor() { >+ var t = () => { >+ eval("super()"); >+ } >+ t(); >+ } >+ } >+ >+ c = new C2; >+ assertEquals(1, c.x); >+} >+ >+{ >+ class C { >+ ['x'] = 1; >+ ['y'] = 2; >+ } >+ >+ class C1 extends C { >+ ['x'] = 3; >+ ['z'] = 4; >+ } >+ >+ let c = new C1; >+ assertEquals(3, c.x); >+ assertEquals(2, c.y); >+ assertEquals(4, c.z); >+} >+ >+{ >+ class X extends class {} { >+ c = 1; >+ >+ constructor() { >+ let t = () => { >+ >+ class P extends class {} { >+ constructor() { >+ let t = () => { super(); }; >+ t(); >+ } >+ } >+ >+ let p = new P; >+ assertEquals(undefined, p.c); >+ super(); >+ } >+ >+ t(); >+ } >+ } >+ >+ let x = new X; >+ assertEquals(1, x.c); >+} >+ >+{ >+ class A { >+ a() { return 1; } >+ } >+ >+ class C extends A { >+ b = super.a(); >+ c = () => super.a; >+ d = () => super.a(); >+ e = super.a; >+ f = super.b; >+ } >+ >+ let c = new C; >+ assertEquals(1, c.a()); >+ assertEquals(1, c.b); >+ assertEquals(1, c.c()()); >+ assertEquals(1, c.d()); >+ assertEquals(1, c.e()); >+ assertFalse(Object.hasOwnProperty(c, 'a')); >+ assertEquals(c.a, c.e); >+ assertEquals(undefined, c.f); >+} >+ >+{ >+ function t() { >+ return class { >+ ['x'] = 1; >+ } >+ } >+ >+ let klass = t(); >+ let obj = new klass; >+ assertEquals(1, obj.x); >+} >+ >+{ >+ new class { >+ t = 1; >+ constructor(t = this.t) { >+ assertEquals(1, t); >+ } >+ } >+ >+ new class extends class {} { >+ t = 1; >+ constructor(t = (super(), this.t)) { >+ assertEquals(1, t); >+ } >+ } >+ >+ assertThrows(() => { >+ new class extends class {} { >+ t = 1; >+ constructor(t = this.t) { >+ super(); >+ } >+ } >+ }, ReferenceError); >+} >+ >+{ >+ class X { >+ p = function() { return arguments[0]; } >+ } >+ >+ let x = new X; >+ assertEquals(1, x.p(1)); >+} >+ >+{ >+ class X { >+ t = () => { >+ function p() { return arguments[0]; }; >+ return p; >+ } >+ } >+ >+ let x = new X; >+ let p = x.t(); >+ assertEquals(1, p(1)); >+} >+ >+{ >+ class X { >+ t = () => { >+ function p() { return eval("arguments[0]"); }; >+ return p; >+ } >+ } >+ >+ let x = new X; >+ let p = x.t(); >+ assertEquals(1, p(1)); >+} >+ >+{ >+ class X { >+ p = eval("(function() { return arguments[0]; })(1)"); >+ } >+ >+ let x = new X; >+ assertEquals(1, x.p); >+} >+ >+{ >+ let thisInInitializer, thisInConstructor, thisFromArrowFn, arrowFn; >+ let C = class extends class {} { >+ field = (thisInInitializer = this, thisFromArrowFn = arrowFn()); >+ constructor() { >+ arrowFn = () => this; >+ super(); >+ thisInConstructor = this; >+ } >+ }; >+ >+ let c = new C(); >+ >+ assertSame(thisInInitializer, c); >+ assertSame(thisFromArrowFn, c); >+ assertSame(thisInConstructor, c); >+} >+ >diff --git a/JSTests/stress/class-fields-private-as-function.js b/JSTests/stress/class-fields-private-as-function.js >new file mode 100644 >index 00000000000..ffaad718e48 >--- /dev/null >+++ b/JSTests/stress/class-fields-private-as-function.js >@@ -0,0 +1,18 @@ >+//@ run('--useClassFields=true') >+ >+function assert(a, e) { >+ if (a !== e) >+ throw new Error("Expected: " + e + " but got: " + a); >+} >+ >+class C { >+ #m = () => 25; >+ >+ method() { >+ return this.#m(); >+ } >+} >+ >+let c = new C(); >+assert(c.method(), 25); >+ >diff --git a/JSTests/stress/class-fields-private-cached-bytecode.js b/JSTests/stress/class-fields-private-cached-bytecode.js >new file mode 100644 >index 00000000000..4708698f651 >--- /dev/null >+++ b/JSTests/stress/class-fields-private-cached-bytecode.js >@@ -0,0 +1,18 @@ >+//@ runBytecodeCache("--useClassFields=true") >+ >+function assert(a, e) { >+ if (a !== e) >+ throw new Error("Expected: " + e + " but got: " + a); >+} >+ >+class C { >+ #f = 3; >+ >+ method() { >+ return this.#f; >+ } >+} >+ >+let c = new C(); >+assert(c.method(), 3); >+ >diff --git a/JSTests/stress/class-fields-private-on-proxy.js b/JSTests/stress/class-fields-private-on-proxy.js >new file mode 100644 >index 00000000000..b952697b408 >--- /dev/null >+++ b/JSTests/stress/class-fields-private-on-proxy.js >@@ -0,0 +1,39 @@ >+//@ run("--useClassFields=true") >+ >+function assert(a, e, m) { >+ if (a !== e) >+ throw new Error(m); >+} >+ >+function assertArrayContent(a, e) { >+ assert(a.length, e.length, "Size of arrays doesn't match"); >+ for (var i = 0; i < a.length; i++) >+ assert(a[i], e[i], "a[" + i + "] = " + a[i] + " but e[" + i + "] = " + e[i]); >+} >+ >+let arr = []; >+ >+class ProxyBase { >+ constructor() { >+ return new Proxy(this, { >+ get: function (obj, prop) { >+ arr.push(prop); >+ return obj[prop]; >+ } >+ }); >+ } >+} >+ >+class Test extends ProxyBase { >+ #f = 3; >+ method() { >+ return this.#f; >+ } >+} >+ >+let t = new Test(); >+let r = t.method(); >+assert(r, 3, "Expected: 3 but got: " + r); >+ >+assertArrayContent(arr, ['method']); >+ >diff --git a/JSTests/stress/class-fields-private-out-of-line.js b/JSTests/stress/class-fields-private-out-of-line.js >new file mode 100644 >index 00000000000..6644d6b213b >--- /dev/null >+++ b/JSTests/stress/class-fields-private-out-of-line.js >@@ -0,0 +1,28 @@ >+//@ requireOptions("--useClassFields=true") >+ >+function assert(a, e) { >+ if (a !== e) >+ throw new Error("Expected: " + e + " but got: " + a); >+} >+ >+let klassSource = ` >+var C = class { >+ assertOutOfLineValue() { >+ assert(this.#f200, "foo"); >+ } >+`; >+ >+ >+for (let i = 0; i < 200; i++) { >+ klassSource += " #f" + i + ";\n"; >+} >+ >+klassSource += " #f200 = \"foo\";\n}"; >+ >+eval(klassSource); >+ >+for (let i = 0; i < 2; i++) { >+ let c = new C(); >+ c.assertOutOfLineValue(); >+} >+ >diff --git a/JSTests/stress/class-fields-private-use-eval.js b/JSTests/stress/class-fields-private-use-eval.js >new file mode 100644 >index 00000000000..ea0696baccf >--- /dev/null >+++ b/JSTests/stress/class-fields-private-use-eval.js >@@ -0,0 +1,15 @@ >+//@ requireOptions("--useClassFields=true") >+ >+function assert(a, message) { >+ if (!a) >+ throw new Error(message); >+} >+ >+let inst = new class { >+ a = eval("(i) => i.#b"); >+ >+ #b = {}; >+}; >+ >+inst.a(inst).x = 1; >+assert(inst.a(inst).x == 1); >diff --git a/JSTests/stress/class-fields-proxy-define-property.js b/JSTests/stress/class-fields-proxy-define-property.js >new file mode 100644 >index 00000000000..9dfee4f5d75 >--- /dev/null >+++ b/JSTests/stress/class-fields-proxy-define-property.js >@@ -0,0 +1,40 @@ >+//@ run("--useClassFields=true") >+ >+function assert(a, e, m) { >+ m = m || "Expected: " + e + " but got: " + a; >+ if (a !== e) >+ throw new Error(m); >+} >+ >+function assertArrayContent(a, e) { >+ assert(a.length, e.length, "Size of arrays doesn't match"); >+ for (var i = 0; i < a.length; i++) >+ assert(a[i], e[i], "a[" + i + "] = " + a[i] + " but e[" + i + "] = " + e[i]); >+} >+ >+let arr = []; >+ >+class ProxyBase { >+ constructor() { >+ return new Proxy(this, { >+ defineProperty: function (target, key, descriptor) { >+ arr.push(key); >+ assert(descriptor.enumerable, true); >+ assert(descriptor.configurable, true); >+ assert(descriptor.writable, true); >+ return Reflect.defineProperty(target, key, descriptor); >+ } >+ }); >+ } >+} >+ >+class Test extends ProxyBase { >+ f = 3; >+ g = "test"; >+} >+ >+let t = new Test(); >+assert(t.f, 3); >+assert(t.g, "test"); >+ >+assertArrayContent(arr, ["f", "g"]); >diff --git a/JSTests/stress/class-fields-stress-instance.js b/JSTests/stress/class-fields-stress-instance.js >new file mode 100644 >index 00000000000..3c9b588ebec >--- /dev/null >+++ b/JSTests/stress/class-fields-stress-instance.js >@@ -0,0 +1,21 @@ >+//@ requireOptions("--useClassFields=true") >+ >+class A { >+ a = 0; >+ b = 1; >+ #c = 0; >+ #d = 1; >+ e = function () { return 0; }; >+ #f = function() { return 1; }; >+ ["g"] = 0; >+ h = eval("true"); >+ #i = eval("false"); >+} >+ >+for (var i = 0; i < 10000; i++) >+ new A(); >+ >+fullGC(); >+ >+for (var i = 0; i < 10000; i++) >+ new A(); >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 df1bbc79f3d..35ce787e1cc 100644 >--- a/JSTests/test262/config.yaml >+++ b/JSTests/test262/config.yaml >@@ -17,9 +17,6 @@ skip: > - Symbol.matchAll > # https://bugs.webkit.org/show_bug.cgi?id=174931 > - regexp-lookbehind >- >- - class-fields-public >- - class-fields-private > - class-methods-private > - class-static-fields-public > - class-static-fields-private >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index 0beab4416bf..853c03deb67 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,26 @@ >+2018-11-02 Xan López <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-06-04 Cathie Chen <cathiechen@igalia.com> > > JS wrapper of target in ResizeObserverEntry/ResizeObserver shouldn't get collected ahead >diff --git a/LayoutTests/js/class-syntax-semicolon-expected.txt b/LayoutTests/js/class-syntax-semicolon-expected.txt >index 8d34286132f..af5adc6afe5 100644 >--- a/LayoutTests/js/class-syntax-semicolon-expected.txt >+++ b/LayoutTests/js/class-syntax-semicolon-expected.txt >@@ -3,13 +3,8 @@ 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. >diff --git a/LayoutTests/js/script-tests/class-syntax-semicolon.js b/LayoutTests/js/script-tests/class-syntax-semicolon.js >index 86b3d56ac3a..160ee9e6550 100644 >--- a/LayoutTests/js/script-tests/class-syntax-semicolon.js >+++ b/LayoutTests/js/script-tests/class-syntax-semicolon.js >@@ -1,12 +1,7 @@ > 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 { ; }"); >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index 593fb21182e..d407d479489 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,303 @@ >+2018-12-05 Xan Lopez <xan@igalia.com> >+ >+ [JSC] Add support for class fields >+ https://bugs.webkit.org/show_bug.cgi?id=174212 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Add placeholder baseline JIT implementations for the private field >+ opcodes. This avoids a crash when the first JIT compilation for >+ any of them is triggered. >+ >+ * 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): >+ >+2018-11-02 Xan López <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. >+ >+ Private fields are stored in a separate hash inside >+ VariableEnvironment instead of extending the >+ VariableEnvironmentEntry Traits. This is done for several reasons: >+ >+ 1) Make private names first class in scopes (different from the >+ string naming mechanics). >+ 2) Easily determine if a scope is a PrivateNameEnvironment by the >+ presence of the private name's hash. >+ 3) In a future patch for private methods also allow to easily >+ determine if a private name access is happening from a field or >+ from an accessor/method. >+ >+ Slightly related to the previous, there are also private name >+ hashes in both SymbolTable and JSSymbolTableObject. The main >+ reason for this is that while SymbolTables are shared between >+ multiple evaluations of the same scope, JSSymbolTableObjects are >+ not, since they are newly created during evaluation. We thus store >+ the string form of the private names in SymbolTable, and the >+ actual SymbolImpl in each JSSymbolTableObject. This matches the >+ expected semantics of private names, where two instances of the >+ same class with a private field should not collide. >+ >+ --- >+ >+ 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). Previously, 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): >+ * 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::generateUnlinkedCodeBlock): >+ * 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-06-03 Caio Lima <ticaiolima@gmail.com> > > [ESNext][BigInt] Implement support for "**" >diff --git a/Source/JavaScriptCore/builtins/BuiltinExecutableCreator.cpp b/Source/JavaScriptCore/builtins/BuiltinExecutableCreator.cpp >index 2b79e7e6ef9..7abd1786ecc 100644 >--- a/Source/JavaScriptCore/builtins/BuiltinExecutableCreator.cpp >+++ b/Source/JavaScriptCore/builtins/BuiltinExecutableCreator.cpp >@@ -32,7 +32,7 @@ namespace JSC { > > UnlinkedFunctionExecutable* createBuiltinExecutable(VM& vm, const SourceCode& source, const Identifier& ident, ConstructorKind kind, ConstructAbility ability) > { >- return BuiltinExecutables::createExecutable(vm, source, ident, kind, ability); >+ return BuiltinExecutables::createExecutable(vm, source, ident, kind, ability, ClassFieldsInitializer::NotNeeded); > } > > } // namespace JSC >diff --git a/Source/JavaScriptCore/builtins/BuiltinExecutables.cpp b/Source/JavaScriptCore/builtins/BuiltinExecutables.cpp >index fef4122603b..c8c5a100182 100644 >--- a/Source/JavaScriptCore/builtins/BuiltinExecutables.cpp >+++ b/Source/JavaScriptCore/builtins/BuiltinExecutables.cpp >@@ -58,14 +58,14 @@ SourceCode BuiltinExecutables::defaultConstructorSourceCode(ConstructorKind cons > return SourceCode(); > } > >-UnlinkedFunctionExecutable* BuiltinExecutables::createDefaultConstructor(ConstructorKind constructorKind, const Identifier& name) >+UnlinkedFunctionExecutable* BuiltinExecutables::createDefaultConstructor(ConstructorKind constructorKind, const Identifier& name, ClassFieldsInitializer classFieldsInitializer) > { > switch (constructorKind) { > case ConstructorKind::None: > break; > case ConstructorKind::Base: > case ConstructorKind::Extends: >- return createExecutable(m_vm, defaultConstructorSourceCode(constructorKind), name, constructorKind, ConstructAbility::CanConstruct); >+ return createExecutable(m_vm, defaultConstructorSourceCode(constructorKind), name, constructorKind, ConstructAbility::CanConstruct, classFieldsInitializer); > } > ASSERT_NOT_REACHED(); > return nullptr; >@@ -73,15 +73,15 @@ UnlinkedFunctionExecutable* BuiltinExecutables::createDefaultConstructor(Constru > > UnlinkedFunctionExecutable* BuiltinExecutables::createBuiltinExecutable(const SourceCode& code, const Identifier& name, ConstructAbility constructAbility) > { >- return createExecutable(m_vm, code, name, ConstructorKind::None, constructAbility); >+ return createExecutable(m_vm, code, name, ConstructorKind::None, constructAbility, ClassFieldsInitializer::NotNeeded); > } > > UnlinkedFunctionExecutable* createBuiltinExecutable(VM& vm, const SourceCode& code, const Identifier& name, ConstructAbility constructAbility) > { >- return BuiltinExecutables::createExecutable(vm, code, name, ConstructorKind::None, constructAbility); >+ return BuiltinExecutables::createExecutable(vm, code, name, ConstructorKind::None, constructAbility, ClassFieldsInitializer::NotNeeded); > } > >-UnlinkedFunctionExecutable* BuiltinExecutables::createExecutable(VM& vm, const SourceCode& source, const Identifier& name, ConstructorKind constructorKind, ConstructAbility constructAbility) >+UnlinkedFunctionExecutable* BuiltinExecutables::createExecutable(VM& vm, const SourceCode& source, const Identifier& name, ConstructorKind constructorKind, ConstructAbility constructAbility, ClassFieldsInitializer classFieldsInitializer) > { > // FIXME: Can we just make MetaData computation be constexpr and have the compiler do this for us? > // https://bugs.webkit.org/show_bug.cgi?id=193272 >@@ -254,7 +254,7 @@ UnlinkedFunctionExecutable* BuiltinExecutables::createExecutable(VM& vm, const S > } > } > >- UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&vm, source, &metadata, kind, constructAbility, JSParserScriptMode::Classic, WTF::nullopt, DerivedContextType::None, isBuiltinDefaultClassConstructor); >+ UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&vm, source, &metadata, kind, constructAbility, JSParserScriptMode::Classic, WTF::nullopt, DerivedContextType::None, classFieldsInitializer, isBuiltinDefaultClassConstructor); > return functionExecutable; > } > >diff --git a/Source/JavaScriptCore/builtins/BuiltinExecutables.h b/Source/JavaScriptCore/builtins/BuiltinExecutables.h >index aa4433c6cec..174c4b19fc2 100644 >--- a/Source/JavaScriptCore/builtins/BuiltinExecutables.h >+++ b/Source/JavaScriptCore/builtins/BuiltinExecutables.h >@@ -25,6 +25,7 @@ > > #pragma once > >+#include "ExecutableInfo.h" > #include "JSCBuiltins.h" > #include "ParserModes.h" > #include "SourceCode.h" >@@ -57,9 +58,9 @@ SourceCode name##Source(); > #undef EXPOSE_BUILTIN_EXECUTABLES > > static SourceCode defaultConstructorSourceCode(ConstructorKind); >- UnlinkedFunctionExecutable* createDefaultConstructor(ConstructorKind, const Identifier& name); >+ UnlinkedFunctionExecutable* createDefaultConstructor(ConstructorKind, const Identifier& name, ClassFieldsInitializer); > >- static UnlinkedFunctionExecutable* createExecutable(VM&, const SourceCode&, const Identifier&, ConstructorKind, ConstructAbility); >+ static UnlinkedFunctionExecutable* createExecutable(VM&, const SourceCode&, const Identifier&, ConstructorKind, ConstructAbility, ClassFieldsInitializer); > > void finalizeUnconditionally(); > >diff --git a/Source/JavaScriptCore/builtins/BuiltinNames.h b/Source/JavaScriptCore/builtins/BuiltinNames.h >index dd0d673fe68..b8fa7061455 100644 >--- a/Source/JavaScriptCore/builtins/BuiltinNames.h >+++ b/Source/JavaScriptCore/builtins/BuiltinNames.h >@@ -174,6 +174,7 @@ namespace JSC { > macro(meta) \ > macro(webAssemblyCompileStreamingInternal) \ > macro(webAssemblyInstantiateStreamingInternal) \ >+ macro(instanceFieldInitializer) > > namespace Symbols { > #define DECLARE_BUILTIN_STATIC_SYMBOLS(name) extern JS_EXPORT_PRIVATE SymbolImpl::StaticSymbolImpl name##Symbol; >diff --git a/Source/JavaScriptCore/bytecode/BytecodeList.rb b/Source/JavaScriptCore/bytecode/BytecodeList.rb >index ea1bbe2d68d..0ea3f71e3a9 100644 >--- a/Source/JavaScriptCore/bytecode/BytecodeList.rb >+++ b/Source/JavaScriptCore/bytecode/BytecodeList.rb >@@ -818,6 +818,12 @@ op :to_primitive, > src: VirtualRegister, > } > >+op :to_property_key, >+ args: { >+ dst: VirtualRegister, >+ src: VirtualRegister, >+ } >+ > op :resolve_scope, > args: { > dst: VirtualRegister, # offset 1 >@@ -1107,6 +1113,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 4962c761804..124f29710d6 100644 >--- a/Source/JavaScriptCore/bytecode/BytecodeUseDef.h >+++ b/Source/JavaScriptCore/bytecode/BytecodeUseDef.h >@@ -164,6 +164,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) >@@ -252,6 +253,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; >@@ -350,6 +355,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 >@@ -373,6 +380,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) >@@ -465,6 +473,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 ade2ace3d1a..3a93d892149 100644 >--- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp >+++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp >@@ -1083,6 +1083,22 @@ 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 (vm.heap.isMarked(oldStructure)) >+ visitor.appendUnbarriered(newStructure); >+ else >+ // If the old structure isn't retained, we'll take the slow path on the >+ // next call. >+ metadata.m_newStructureID = metadata.m_oldStructureID = 0; >+ continue; >+ } > } > } > >@@ -1290,6 +1306,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 || vm.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 || vm.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 || vm.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..46594da0cb4 100644 >--- a/Source/JavaScriptCore/bytecode/ExecutableInfo.h >+++ b/Source/JavaScriptCore/bytecode/ExecutableInfo.h >@@ -30,12 +30,13 @@ > 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 }; >+enum class ClassFieldsInitializer : uint8_t { NotNeeded, Needed }; > > // FIXME: These flags, ParserModes and propagation to XXXCodeBlocks should be reorganized. > // https://bugs.webkit.org/show_bug.cgi?id=151547 > struct ExecutableInfo { >- ExecutableInfo(bool usesEval, bool isStrictMode, bool isConstructor, bool isBuiltinFunction, ConstructorKind constructorKind, JSParserScriptMode scriptMode, SuperBinding superBinding, SourceParseMode parseMode, DerivedContextType derivedContextType, bool isArrowFunctionContext, bool isClassContext, EvalContextType evalContextType) >+ ExecutableInfo(bool usesEval, bool isStrictMode, bool isConstructor, bool isBuiltinFunction, ConstructorKind constructorKind, JSParserScriptMode scriptMode, SuperBinding superBinding, SourceParseMode parseMode, DerivedContextType derivedContextType, ClassFieldsInitializer classFieldsInitializer, bool isArrowFunctionContext, bool isClassContext, EvalContextType evalContextType) > : m_usesEval(usesEval) > , m_isStrictMode(isStrictMode) > , m_isConstructor(isConstructor) >@@ -45,6 +46,7 @@ struct ExecutableInfo { > , m_scriptMode(static_cast<unsigned>(scriptMode)) > , m_parseMode(parseMode) > , m_derivedContextType(static_cast<unsigned>(derivedContextType)) >+ , m_classFieldsInitializer(static_cast<unsigned>(classFieldsInitializer)) > , m_isArrowFunctionContext(isArrowFunctionContext) > , m_isClassContext(isClassContext) > , m_evalContextType(static_cast<unsigned>(evalContextType)) >@@ -66,6 +68,7 @@ struct ExecutableInfo { > EvalContextType evalContextType() const { return static_cast<EvalContextType>(m_evalContextType); } > bool isArrowFunctionContext() const { return m_isArrowFunctionContext; } > bool isClassContext() const { return m_isClassContext; } >+ ClassFieldsInitializer classFieldsInitializer() const { return static_cast<ClassFieldsInitializer>(m_classFieldsInitializer); } > > private: > unsigned m_usesEval : 1; >@@ -77,6 +80,7 @@ private: > unsigned m_scriptMode: 1; > SourceParseMode m_parseMode; > unsigned m_derivedContextType : 2; >+ unsigned m_classFieldsInitializer : 1; > unsigned m_isArrowFunctionContext : 1; > unsigned m_isClassContext : 1; > unsigned m_evalContextType : 2; >diff --git a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp >index f9c3ba9d716..6b0d1b91d9d 100644 >--- a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp >+++ b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp >@@ -68,6 +68,7 @@ UnlinkedCodeBlock::UnlinkedCodeBlock(VM* vm, Structure* structure, CodeType code > , m_hasTailCalls(false) > , m_constructorKind(static_cast<unsigned>(info.constructorKind())) > , m_derivedContextType(static_cast<unsigned>(info.derivedContextType())) >+ , m_classFieldsInitializer(static_cast<unsigned>(info.classFieldsInitializer())) > , m_evalContextType(static_cast<unsigned>(info.evalContextType())) > , m_codeType(static_cast<unsigned>(codeType)) > , m_didOptimize(static_cast<unsigned>(MixedTriState)) >diff --git a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h >index 2a350a1f11c..84817004184 100644 >--- a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h >+++ b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h >@@ -339,6 +339,8 @@ public: > TriState didOptimize() const { return static_cast<TriState>(m_didOptimize); } > void setDidOptimize(TriState didOptimize) { m_didOptimize = static_cast<unsigned>(didOptimize); } > >+ ClassFieldsInitializer classFieldsInitializer() const { return static_cast<ClassFieldsInitializer>(m_classFieldsInitializer); } >+ > void dump(PrintStream&) const; > > BytecodeLivenessAnalysis& livenessAnalysis(CodeBlock* codeBlock) >@@ -421,9 +423,11 @@ private: > unsigned m_hasTailCalls : 1; > unsigned m_constructorKind : 2; > unsigned m_derivedContextType : 2; >+ unsigned m_classFieldsInitializer : 1; > unsigned m_evalContextType : 2; > unsigned m_codeType : 2; > unsigned m_didOptimize : 2; >+ > public: > ConcurrentJSLock m_lock; > private: >diff --git a/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp b/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp >index 14a069c5e07..7755f9995c9 100644 >--- a/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp >+++ b/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp >@@ -57,8 +57,10 @@ static UnlinkedFunctionCodeBlock* generateUnlinkedFunctionCodeBlock( > JSParserStrictMode strictMode = executable->isInStrictContext() ? JSParserStrictMode::Strict : JSParserStrictMode::NotStrict; > JSParserScriptMode scriptMode = executable->scriptMode(); > ASSERT(isFunctionParseMode(executable->parseMode())); >+ auto optionalInstanceFieldLocations = executable->instanceFieldLocations(); >+ const auto& instanceFieldLocations = optionalInstanceFieldLocations ? *optionalInstanceFieldLocations : Vector<JSTextPosition>(); > 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, instanceFieldLocations); > > if (!function) { > ASSERT(error.isValid()); >@@ -70,7 +72,7 @@ static UnlinkedFunctionCodeBlock* generateUnlinkedFunctionCodeBlock( > > bool isClassContext = executable->superBinding() == SuperBinding::Needed; > >- UnlinkedFunctionCodeBlock* result = UnlinkedFunctionCodeBlock::create(&vm, FunctionCode, ExecutableInfo(function->usesEval(), function->isStrictMode(), kind == CodeForConstruct, functionKind == UnlinkedBuiltinFunction, executable->constructorKind(), scriptMode, executable->superBinding(), parseMode, executable->derivedContextType(), false, isClassContext, EvalContextType::FunctionEvalContext), codeGenerationMode); >+ UnlinkedFunctionCodeBlock* result = UnlinkedFunctionCodeBlock::create(&vm, FunctionCode, ExecutableInfo(function->usesEval(), function->isStrictMode(), kind == CodeForConstruct, functionKind == UnlinkedBuiltinFunction, executable->constructorKind(), scriptMode, executable->superBinding(), parseMode, executable->derivedContextType(), executable->classFieldsInitializer(), false, isClassContext, EvalContextType::FunctionEvalContext), codeGenerationMode); > > VariableEnvironment parentScopeTDZVariables = executable->parentScopeTDZVariables(); > error = BytecodeGenerator::generate(vm, function.get(), source, result, codeGenerationMode, &parentScopeTDZVariables); >@@ -81,7 +83,7 @@ static UnlinkedFunctionCodeBlock* generateUnlinkedFunctionCodeBlock( > return result; > } > >-UnlinkedFunctionExecutable::UnlinkedFunctionExecutable(VM* vm, Structure* structure, const SourceCode& parentSource, FunctionMetadataNode* node, UnlinkedFunctionKind kind, ConstructAbility constructAbility, JSParserScriptMode scriptMode, Optional<CompactVariableMap::Handle> parentScopeTDZVariables, DerivedContextType derivedContextType, bool isBuiltinDefaultClassConstructor) >+UnlinkedFunctionExecutable::UnlinkedFunctionExecutable(VM* vm, Structure* structure, const SourceCode& parentSource, FunctionMetadataNode* node, UnlinkedFunctionKind kind, ConstructAbility constructAbility, JSParserScriptMode scriptMode, Optional<CompactVariableMap::Handle> parentScopeTDZVariables, DerivedContextType derivedContextType, ClassFieldsInitializer classFieldsInitializer, bool isBuiltinDefaultClassConstructor) > : Base(*vm, structure) > , m_firstLineOffset(node->firstLine() - parentSource.firstLine().oneBasedInt()) > , m_isInStrictContext(node->isInStrictContext()) >@@ -98,6 +100,7 @@ UnlinkedFunctionExecutable::UnlinkedFunctionExecutable(VM* vm, Structure* struct > , m_sourceLength(node->source().length()) > , m_superBinding(static_cast<unsigned>(node->superBinding())) > , m_parametersStartOffset(node->parametersStart()) >+ , m_classFieldsInitializer(static_cast<unsigned>(classFieldsInitializer)) > , m_isCached(false) > , m_typeProfilingStartOffset(node->functionKeywordStart()) > , m_typeProfilingEndOffset(node->startStartOffset() + node->source().length() - 1) >@@ -120,6 +123,7 @@ UnlinkedFunctionExecutable::UnlinkedFunctionExecutable(VM* vm, Structure* struct > ASSERT(m_superBinding == static_cast<unsigned>(node->superBinding())); > ASSERT(m_derivedContextType == static_cast<unsigned>(derivedContextType)); > ASSERT(!(m_isBuiltinDefaultClassConstructor && constructorKind() == ConstructorKind::None)); >+ ASSERT(!m_classFieldsInitializer || (isClassConstructorFunction() || derivedContextType == DerivedContextType::DerivedConstructorContext)); > if (!node->classSource().isNull()) > setClassSource(node->classSource()); > if (parentScopeTDZVariables) >diff --git a/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.h b/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.h >index 0c7f89cd4eb..76c63ad35c1 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" >@@ -68,10 +69,10 @@ public: > return &vm.unlinkedFunctionExecutableSpace.space; > } > >- static UnlinkedFunctionExecutable* create(VM* vm, const SourceCode& source, FunctionMetadataNode* node, UnlinkedFunctionKind unlinkedFunctionKind, ConstructAbility constructAbility, JSParserScriptMode scriptMode, Optional<CompactVariableMap::Handle> parentScopeTDZVariables, DerivedContextType derivedContextType, bool isBuiltinDefaultClassConstructor = false) >+ static UnlinkedFunctionExecutable* create(VM* vm, const SourceCode& source, FunctionMetadataNode* node, UnlinkedFunctionKind unlinkedFunctionKind, ConstructAbility constructAbility, JSParserScriptMode scriptMode, Optional<CompactVariableMap::Handle> parentScopeTDZVariables, DerivedContextType derivedContextType, ClassFieldsInitializer classFieldsInitializer, bool isBuiltinDefaultClassConstructor = false) > { > UnlinkedFunctionExecutable* instance = new (NotNull, allocateCell<UnlinkedFunctionExecutable>(vm->heap)) >- UnlinkedFunctionExecutable(vm, vm->unlinkedFunctionExecutableStructure.get(), source, node, unlinkedFunctionKind, constructAbility, scriptMode, WTFMove(parentScopeTDZVariables), derivedContextType, isBuiltinDefaultClassConstructor); >+ UnlinkedFunctionExecutable(vm, vm->unlinkedFunctionExecutableStructure.get(), source, node, unlinkedFunctionKind, constructAbility, scriptMode, WTFMove(parentScopeTDZVariables), derivedContextType, classFieldsInitializer, isBuiltinDefaultClassConstructor); > instance->finishCreation(*vm); > return instance; > } >@@ -196,10 +197,27 @@ public: > String m_sourceURLDirective; > String m_sourceMappingURLDirective; > CompactVariableMap::Handle m_parentScopeTDZVariables; >+ Vector<JSTextPosition> m_instanceFieldLocations; > }; > >+ ClassFieldsInitializer classFieldsInitializer() const { return static_cast<ClassFieldsInitializer>(m_classFieldsInitializer); } >+ >+ Optional<Vector<JSTextPosition>> instanceFieldLocations() const >+ { >+ if (m_rareData) >+ return m_rareData->m_instanceFieldLocations; >+ return WTF::nullopt; >+ } >+ >+ void setInstanceFieldLocations(Vector<JSTextPosition>&& instanceFieldLocations) >+ { >+ if (instanceFieldLocations.isEmpty()) >+ return; >+ ensureRareData().m_instanceFieldLocations = instanceFieldLocations; >+ } >+ > private: >- UnlinkedFunctionExecutable(VM*, Structure*, const SourceCode&, FunctionMetadataNode*, UnlinkedFunctionKind, ConstructAbility, JSParserScriptMode, Optional<CompactVariableMap::Handle>, JSC::DerivedContextType, bool isBuiltinDefaultClassConstructor); >+ UnlinkedFunctionExecutable(VM*, Structure*, const SourceCode&, FunctionMetadataNode*, UnlinkedFunctionKind, ConstructAbility, JSParserScriptMode, Optional<CompactVariableMap::Handle>, JSC::DerivedContextType, JSC::ClassFieldsInitializer, bool isBuiltinDefaultClassConstructor); > UnlinkedFunctionExecutable(Decoder&, const CachedFunctionExecutable&); > > void decodeCachedCodeBlocks(); >@@ -219,6 +237,7 @@ private: > unsigned m_sourceLength : 31; > unsigned m_superBinding : 1; > unsigned m_parametersStartOffset : 31; >+ unsigned m_classFieldsInitializer : 1; > unsigned m_isCached : 1; > unsigned m_typeProfilingStartOffset; > unsigned m_typeProfilingEndOffset; >diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp >index abe4166c5b8..0182f408b7c 100644 >--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp >+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp >@@ -415,7 +415,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, Unlinke > bool shouldCaptureSomeOfTheThings = shouldEmitDebugHooks() || functionNode->needsActivation() || containsArrowOrEvalButNotInArrowBlock; > > bool shouldCaptureAllOfTheThings = 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. >@@ -639,7 +639,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) { >@@ -735,6 +735,9 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, Unlinke > moveEmptyValue(&m_thisRegister); > } else > emitCreateThis(&m_thisRegister); >+ >+ if (Options::useClassFields() && 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 { >@@ -1957,6 +1960,16 @@ 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()); >+ } >+ >+ hasCapturedVariables = true; >+ } > } > return hasCapturedVariables; > } >@@ -2205,10 +2218,11 @@ 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 needsToPopScope = symbolTable->hasPrivateNames(); > for (auto& entry : environment) { > if (entry.value.isCaptured()) { >- hasCapturedVariables = true; >+ needsToPopScope = true; > continue; > } > SymbolTableEntry symbolTableEntry = symbolTable->get(NoLockingNecessary, entry.key.get()); >@@ -2219,7 +2233,7 @@ void BytecodeGenerator::popLexicalScopeInternal(VariableEnvironment& environment > local->deref(); > } > >- if (hasCapturedVariables) { >+ if (needsToPopScope) { > RELEASE_ASSERT(stackEntry.m_scope); > emitPopScope(scopeRegister(), stackEntry.m_scope); > popLocalControlFlowScope(); >@@ -2782,6 +2796,26 @@ 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) >+{ >+ m_codeBlock->addPropertyAccessInstruction(instructions().size()); >+ OpGetPrivateField::emit(this, kill(dst), base, addConstant(name), scopeRegister()); >+ return dst; >+} >+ >+RegisterID* BytecodeGenerator::emitPrivateFieldSet(RegisterID* base, const Identifier& name, RegisterID* value) >+{ >+ m_codeBlock->addPropertyAccessInstruction(instructions().size()); >+ OpPutPrivateField::emit(this, base, addConstant(name), value, scopeRegister()); >+ return value; >+} >+ > void BytecodeGenerator::emitSuperSamplerBegin() > { > OpSuperSamplerBegin::emit(this); >@@ -2818,6 +2852,19 @@ RegisterID* BytecodeGenerator::emitCreateThis(RegisterID* dst) > return dst; > } > >+RegisterID* BytecodeGenerator::emitInstanceFieldInitializationIfNeeded(RegisterID* dst, RegisterID* constructor, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) >+{ >+ if (!(isConstructor() || isDerivedConstructorContext()) || classFieldsInitializer() == ClassFieldsInitializer::NotNeeded) >+ return dst; >+ >+ RefPtr<RegisterID> initializer = emitDirectGetById(newTemporary(), constructor, propertyNames().builtinNames().instanceFieldInitializerPrivateName()); >+ CallArguments args(*this, nullptr); >+ emitMove(args.thisRegister(), dst); >+ emitCall(newTemporary(), initializer.get(), NoExpectedFunction, args, divot, divotStart, divotEnd, DebuggableCall::No); >+ >+ return dst; >+} >+ > void BytecodeGenerator::emitTDZCheck(RegisterID* target) > { > OpCheckTdz::emit(this, target); >@@ -3099,9 +3146,9 @@ RegisterID* BytecodeGenerator::emitNewMethodDefinition(RegisterID* dst, MethodDe > } > > RegisterID* BytecodeGenerator::emitNewDefaultConstructor(RegisterID* dst, ConstructorKind constructorKind, const Identifier& name, >- const Identifier& ecmaName, const SourceCode& classSource) >+ const Identifier& ecmaName, const SourceCode& classSource, ClassFieldsInitializer classFieldsInitializer) > { >- UnlinkedFunctionExecutable* executable = m_vm->builtinExecutables()->createDefaultConstructor(constructorKind, name); >+ UnlinkedFunctionExecutable* executable = m_vm->builtinExecutables()->createDefaultConstructor(constructorKind, name, classFieldsInitializer); > executable->setInvalidTypeProfilingOffsets(); > executable->setEcmaName(ecmaName); > executable->setClassSource(classSource); >@@ -3112,6 +3159,33 @@ 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; >+ } >+ >+ Optional<CompactVariableMap::Handle> optionalVariablesUnderTDZ = getVariablesUnderTDZ(); >+ 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(), WTFMove(optionalVariablesUnderTDZ), newDerivedContextType, ClassFieldsInitializer::NotNeeded); >+ 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)); >@@ -3126,26 +3200,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); >@@ -3488,6 +3582,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()); >@@ -4335,7 +4436,7 @@ void BytecodeGenerator::popIndexedForInScope(RegisterID* localRegister) > > RegisterID* BytecodeGenerator::emitLoadArrowFunctionLexicalEnvironment(const Identifier& identifier) > { >- ASSERT(m_codeBlock->isArrowFunction() || m_codeBlock->isArrowFunctionContext() || constructorKind() == ConstructorKind::Extends || m_codeType == EvalCode); >+ ASSERT(m_codeBlock->isArrowFunction() || m_codeBlock->isArrowFunctionContext() || constructorKind() == ConstructorKind::Extends || m_codeType == EvalCode || m_codeBlock->parseMode() == SourceParseMode::InstanceFieldInitializerMode); > > return emitResolveScope(nullptr, variable(identifier, ThisResolutionType::Scoped)); > } >@@ -4359,6 +4460,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()) { >@@ -5084,4 +5193,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 e97686aa3ed..d32fb68e06e 100644 >--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h >+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h >@@ -392,6 +392,7 @@ namespace JSC { > ConstructorKind constructorKind() const { return m_codeBlock->constructorKind(); } > SuperBinding superBinding() const { return m_codeBlock->superBinding(); } > JSParserScriptMode scriptMode() const { return m_codeBlock->scriptMode(); } >+ ClassFieldsInitializer classFieldsInitializer() const { return m_codeBlock->classFieldsInitializer(); } > > template<typename Node, typename UnlinkedCodeBlock> > static ParserError generate(VM& vm, Node* node, const SourceCode& sourceCode, UnlinkedCodeBlock* unlinkedCodeBlock, OptionSet<CodeGenerationMode> codeGenerationMode, const VariableEnvironment* environment) >@@ -553,14 +554,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) >@@ -722,6 +723,7 @@ namespace JSC { > bool emitEqualityOpImpl(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); >@@ -735,11 +737,15 @@ 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* emitNewDefaultConstructor(RegisterID* dst, ConstructorKind, const Identifier& name, const Identifier& ecmaName, const SourceCode& classSource, ClassFieldsInitializer); >+ 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); >@@ -773,6 +779,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(); > >@@ -820,6 +830,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&); >@@ -936,6 +947,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); >@@ -1111,9 +1123,12 @@ namespace JSC { > { > DerivedContextType newDerivedContextType = DerivedContextType::None; > >+ ClassFieldsInitializer classFieldsInitializer = metadata->classFieldsInitializer(); > if (SourceParseModeSet(SourceParseMode::ArrowFunctionMode, SourceParseMode::AsyncArrowFunctionMode, SourceParseMode::AsyncArrowFunctionBodyMode).contains(metadata->parseMode())) { >- if (constructorKind() == ConstructorKind::Extends || isDerivedConstructorContext()) >+ if (constructorKind() == ConstructorKind::Extends || isDerivedConstructorContext()) { > newDerivedContextType = DerivedContextType::DerivedConstructorContext; >+ classFieldsInitializer = m_codeBlock->classFieldsInitializer(); >+ } > else if (m_codeBlock->isClassContext() || isDerivedClassContext()) > newDerivedContextType = DerivedContextType::DerivedMethodContext; > } >@@ -1127,7 +1142,7 @@ namespace JSC { > if (parseMode == SourceParseMode::MethodMode && metadata->constructorKind() != ConstructorKind::None) > constructAbility = ConstructAbility::CanConstruct; > >- return UnlinkedFunctionExecutable::create(m_vm, m_scopeNode->source(), metadata, isBuiltinFunction() ? UnlinkedBuiltinFunction : UnlinkedNormalFunction, constructAbility, scriptMode(), WTFMove(optionalVariablesUnderTDZ), newDerivedContextType); >+ return UnlinkedFunctionExecutable::create(m_vm, m_scopeNode->source(), metadata, isBuiltinFunction() ? UnlinkedBuiltinFunction : UnlinkedNormalFunction, constructAbility, scriptMode(), WTFMove(optionalVariablesUnderTDZ), newDerivedContextType, classFieldsInitializer); > } > > Optional<CompactVariableMap::Handle> getVariablesUnderTDZ(); >diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp >index 629d416bf94..7203997ba5c 100644 >--- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp >+++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp >@@ -174,7 +174,7 @@ RegisterID* ThisNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst > > static RegisterID* emitHomeObjectForCallee(BytecodeGenerator& generator) > { >- if (generator.isDerivedClassContext() || generator.isDerivedConstructorContext()) { >+ if ((generator.isDerivedClassContext() || generator.isDerivedConstructorContext()) && generator.parseMode() != SourceParseMode::InstanceFieldInitializerMode) { > RegisterID* derivedConstructor = generator.emitLoadDerivedConstructorFromArrowFunctionLexicalEnvironment(); > return generator.emitGetById(generator.newTemporary(), derivedConstructor, generator.propertyNames().builtinNames().homeObjectPrivateName()); > } >@@ -538,13 +538,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); > } > >@@ -592,6 +602,15 @@ 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); >@@ -713,6 +732,23 @@ 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()); >+ >+ 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)); >+} >+ > // ------------------------------ BracketAccessorNode -------------------------------- > > static bool isNonIndexStringElement(ExpressionNode& element) >@@ -767,16 +803,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) >@@ -896,7 +968,13 @@ RegisterID* FunctionCallValueNode::emitBytecode(BytecodeGenerator& generator, Re > > if (generator.isDerivedConstructorContext() || doWeUseArrowFunctionInConstructor) > generator.emitPutThisToArrowFunctionContextScope(); >- >+ >+ // Initialize instance fields after super-call. >+ if (Options::useClassFields()) { >+ func = generator.emitLoadDerivedConstructor(); >+ generator.emitInstanceFieldInitializationIfNeeded(generator.thisRegister(), func.get(), divot(), divotStart(), divotEnd()); >+ } >+ > return ret; > } > RefPtr<RegisterID> func = generator.emitNode(m_expr); >@@ -1334,11 +1412,10 @@ RegisterID* FunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, Regi > else > generator.emitNode(callArguments.thisRegister(), m_base); > generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd()); >- if (baseIsSuper) { >- RefPtr<RegisterID> superBase = emitSuperBaseForCallee(generator); >- generator.emitGetById(function.get(), superBase.get(), callArguments.thisRegister(), m_ident); >- } else >- generator.emitGetById(function.get(), callArguments.thisRegister(), m_ident); >+ >+ RefPtr<RegisterID> base = baseIsSuper ? emitSuperBaseForCallee(generator) : callArguments.thisRegister(); >+ emitGetPropertyValue(generator, function.get(), base.get()); >+ > RegisterID* ret = generator.emitCallInTailPosition(returnValue.get(), function.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd(), DebuggableCall::Yes); > generator.emitProfileType(returnValue.get(), divotStart(), divotEnd()); > return ret; >@@ -2554,11 +2631,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()); > } >@@ -2570,21 +2643,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; > } >@@ -3959,6 +4024,50 @@ 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: { >+ // FIXME: Improve performance of public class fields >+ // https://bugs.webkit.org/show_bug.cgi?id=198330 >+ RefPtr<RegisterID> propertyName = generator.emitLoad(nullptr, *m_ident); >+ generator.emitCallDefineProperty(generator.thisRegister(), propertyName.get(), value.get(), nullptr, nullptr, BytecodeGenerator::PropertyConfigurable | BytecodeGenerator::PropertyWritable | BytecodeGenerator::PropertyEnumerable, m_position); >+ break; >+ } >+ case DefineFieldNode::PrivateName: { >+ generator.emitExpressionInfo(position(), position(), position() + m_ident->length()); >+ generator.emitPrivateFieldAdd(generator.thisRegister(), *m_ident, value.get()); >+ break; >+ } >+ case DefineFieldNode::ComputedName: { >+ // FIXME: Improve performance of public class fields >+ // https://bugs.webkit.org/show_bug.cgi?id=198330 >+ 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.emitCallDefineProperty(generator.thisRegister(), privateName.get(), value.get(), nullptr, nullptr, BytecodeGenerator::PropertyConfigurable | BytecodeGenerator::PropertyWritable | BytecodeGenerator::PropertyEnumerable, m_position); >+ break; >+ } >+ } >+} >+ > // ------------------------------ ClassDeclNode --------------------------------- > > void ClassDeclNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) >@@ -3970,7 +4079,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; >@@ -3982,15 +4091,19 @@ RegisterID* ClassExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID > RefPtr<RegisterID> constructor; > bool needsHomeObject = false; > >+ bool hasInstanceFields = this->hasInstanceFields(); >+ > if (m_constructorExpression) { > ASSERT(m_constructorExpression->isFuncExprNode()); > FunctionMetadataNode* metadata = static_cast<FuncExprNode*>(m_constructorExpression)->metadata(); > metadata->setEcmaName(ecmaName()); > metadata->setClassSource(m_classSource); >+ if (hasInstanceFields) >+ metadata->setClassFieldsInitializer(ClassFieldsInitializer::Needed); > constructor = generator.emitNode(dst, m_constructorExpression); > needsHomeObject = m_classHeritage || metadata->superBinding() == SuperBinding::Needed; > } else >- constructor = generator.emitNewDefaultConstructor(generator.finalDestination(dst), m_classHeritage ? ConstructorKind::Extends : ConstructorKind::Base, m_name, ecmaName(), m_classSource); >+ constructor = generator.emitNewDefaultConstructor(generator.finalDestination(dst), m_classHeritage ? ConstructorKind::Extends : ConstructorKind::Base, m_name, ecmaName(), m_classSource, static_cast<ClassFieldsInitializer>(hasInstanceFields)); > > const auto& propertyNames = generator.propertyNames(); > RefPtr<RegisterID> prototype = generator.emitNewObject(generator.newTemporary()); >@@ -4036,14 +4149,27 @@ 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) >+ // https://bugs.webkit.org/show_bug.cgi?id=196867 >+ 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/debugger/DebuggerCallFrame.cpp b/Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp >index e9dfbc2c088..6179acc5ec9 100644 >--- a/Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp >+++ b/Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp >@@ -247,7 +247,7 @@ JSValue DebuggerCallFrame::evaluateWithScopeExtension(const String& script, JSOb > VariableEnvironment variablesUnderTDZ; > JSScope::collectClosureVariablesUnderTDZ(scope()->jsScope(), variablesUnderTDZ); > >- auto* eval = DirectEvalExecutable::create(callFrame, makeSource(script, callFrame->callerSourceOrigin()), codeBlock->isStrictMode(), codeBlock->unlinkedCodeBlock()->derivedContextType(), codeBlock->unlinkedCodeBlock()->isArrowFunction(), evalContextType, &variablesUnderTDZ); >+ auto* eval = DirectEvalExecutable::create(callFrame, makeSource(script, callFrame->callerSourceOrigin()), codeBlock->isStrictMode(), codeBlock->unlinkedCodeBlock()->derivedContextType(), codeBlock->unlinkedCodeBlock()->classFieldsInitializer(), codeBlock->unlinkedCodeBlock()->isArrowFunction(), evalContextType, &variablesUnderTDZ); > if (UNLIKELY(catchScope.exception())) { > exception = catchScope.exception(); > catchScope.clearException(); >diff --git a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp >index dfe6c16d51a..85c5ac22132 100644 >--- a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp >+++ b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp >@@ -282,6 +282,10 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, const I > > case op_yield: > case op_create_generator_frame_environment: >+ case op_add_private_field: // FIXME: add JIT support for private field ops. https://bugs.webkit.org/show_bug.cgi?id=195619 >+ 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 c1f2f1f445e..a75d1bec009 100644 >--- a/Source/JavaScriptCore/interpreter/Interpreter.cpp >+++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp >@@ -138,7 +138,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(); >@@ -164,7 +166,7 @@ JSValue eval(CallFrame* callFrame) > > VariableEnvironment variablesUnderTDZ; > JSScope::collectClosureVariablesUnderTDZ(callerScopeChain, variablesUnderTDZ); >- eval = DirectEvalExecutable::create(callFrame, makeSource(programSource, callerCodeBlock->source().provider()->sourceOrigin()), callerCodeBlock->isStrictMode(), derivedContextType, isArrowFunctionContext, evalContextType, &variablesUnderTDZ); >+ eval = DirectEvalExecutable::create(callFrame, makeSource(programSource, callerCodeBlock->source().provider()->sourceOrigin()), callerCodeBlock->isStrictMode(), derivedContextType, callerUnlinkedCodeBlock->classFieldsInitializer(), isArrowFunctionContext, evalContextType, &variablesUnderTDZ); > EXCEPTION_ASSERT(!!scope.exception() == !eval); > if (!eval) > return jsUndefined(); >diff --git a/Source/JavaScriptCore/jit/JIT.cpp b/Source/JavaScriptCore/jit/JIT.cpp >index f1975bfb014..35cdeb1d29c 100644 >--- a/Source/JavaScriptCore/jit/JIT.cpp >+++ b/Source/JavaScriptCore/jit/JIT.cpp >@@ -419,6 +419,10 @@ 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_to_property_key) > > DEFINE_OP(op_ret) > DEFINE_OP(op_rshift) >@@ -584,6 +588,10 @@ 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) >+ DEFINE_SLOWCASE_SLOW_OP(to_property_key) > > default: > RELEASE_ASSERT_NOT_REACHED(); >diff --git a/Source/JavaScriptCore/jit/JIT.h b/Source/JavaScriptCore/jit/JIT.h >index 698963637fd..ddffdac1a1c 100644 >--- a/Source/JavaScriptCore/jit/JIT.h >+++ b/Source/JavaScriptCore/jit/JIT.h >@@ -634,6 +634,10 @@ 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 emit_op_to_property_key(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 f05dba964b2..89ac3c43633 100644 >--- a/Source/JavaScriptCore/jit/JITOpcodes.cpp >+++ b/Source/JavaScriptCore/jit/JITOpcodes.cpp >@@ -1562,6 +1562,23 @@ 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()); >+} >+void JIT::emit_op_to_property_key(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 c350c018e50..61075ed7d49 100644 >--- a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm >+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm >@@ -1893,6 +1893,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 >@@ -2545,3 +2558,73 @@ 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 >+ 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 6aaf0dd10c3..e8b92cdfa14 100644 >--- a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm >+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm >@@ -1998,6 +1998,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 >@@ -2585,3 +2599,79 @@ 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 >+ loadi OpAddPrivateField::Metadata::m_newStructureID[t5], t0 >+ storei t0, JSCell::m_structureID[t3] >+ writeBarrierOnOperandWithReload(size, get, m_base, macro () >+ # Reload metadata into t5 >+ metadata(t5, t1) >+ # Reload base into t3 >+ get(m_base, t1) >+ loadConstantOrVariable(size, t1, t3) >+ end) >+ >+ # 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 dc878c58dac..104d5ec3062 100644 >--- a/Source/JavaScriptCore/parser/ASTBuilder.h >+++ b/Source/JavaScriptCore/parser/ASTBuilder.h >@@ -268,12 +268,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; > } >@@ -397,6 +397,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) > { >@@ -519,6 +524,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); } > >@@ -637,6 +643,11 @@ public: > return node->isAssignmentLocation(); > } > >+ bool isPrivateLocation(const Expression& node) >+ { >+ return node->isPrivateLocation(); >+ } >+ > bool isObjectLiteral(const Expression& node) > { > return node->isObjectLiteral(); >@@ -1377,15 +1388,15 @@ ExpressionNode* ASTBuilder::makeFunctionCallNode(const JSTokenLocation& location > DotAccessorNode* dot = static_cast<DotAccessorNode*>(func); > FunctionCallDotNode* node = nullptr; > if (!previousBaseWasSuper && (dot->identifier() == m_vm->propertyNames->builtinNames().callPublicName() || dot->identifier() == m_vm->propertyNames->builtinNames().callPrivateName())) >- node = new (m_parserArena) CallFunctionCallDotNode(location, dot->base(), dot->identifier(), args, divot, divotStart, divotEnd, callOrApplyChildDepth); >+ node = new (m_parserArena) CallFunctionCallDotNode(location, dot->base(), dot->identifier(), dot->type(), args, divot, divotStart, divotEnd, callOrApplyChildDepth); > else if (!previousBaseWasSuper && (dot->identifier() == m_vm->propertyNames->builtinNames().applyPublicName() || dot->identifier() == m_vm->propertyNames->builtinNames().applyPrivateName())) { > // FIXME: This check is only needed because we haven't taught the bytecode generator to inline > // Reflect.apply yet. See https://bugs.webkit.org/show_bug.cgi?id=190668. > if (!dot->base()->isResolveNode() || static_cast<ResolveNode*>(dot->base())->identifier() != "Reflect") >- node = new (m_parserArena) ApplyFunctionCallDotNode(location, dot->base(), dot->identifier(), args, divot, divotStart, divotEnd, callOrApplyChildDepth); >+ node = new (m_parserArena) ApplyFunctionCallDotNode(location, dot->base(), dot->identifier(), dot->type(), args, divot, divotStart, divotEnd, callOrApplyChildDepth); > } > if (!node) >- node = new (m_parserArena) FunctionCallDotNode(location, dot->base(), dot->identifier(), args, divot, divotStart, divotEnd); >+ node = new (m_parserArena) FunctionCallDotNode(location, dot->base(), dot->identifier(), dot->type(), args, divot, divotStart, divotEnd); > node->setSubexpressionInfo(dot->divot(), dot->divotEnd().offset); > return node; > } >@@ -1506,10 +1517,16 @@ ExpressionNode* ASTBuilder::makeAssignNode(const JSTokenLocation& location, Expr > } > ASSERT(loc->isDotAccessorNode()); > DotAccessorNode* dot = static_cast<DotAccessorNode*>(loc); >- if (op == OpEqual) >- return new (m_parserArena) AssignDotNode(location, dot->base(), dot->identifier(), expr, exprHasAssignments, dot->divot(), start, end); >+ if (op == OpEqual) { >+ if (expr->isBaseFuncExprNode()) { >+ // We don't also set the ecma name here because ES6 specifies that the >+ // function should not pick up the name of the dot->identifier(). >+ static_cast<BaseFuncExprNode*>(expr)->metadata()->setEcmaName(dot->identifier()); >+ } >+ 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 4662c54f2a0..e3be096d894 100644 >--- a/Source/JavaScriptCore/parser/Lexer.cpp >+++ b/Source/JavaScriptCore/parser/Lexer.cpp >@@ -95,7 +95,8 @@ enum CharacterType { > > // Other types (only one so far) > CharacterWhiteSpace, >- CharacterPrivateIdentifierStart >+ CharacterPrivateIdentifierStart, >+ CharacterPrivateNameStart > }; > > // 256 Latin-1 codes >@@ -135,7 +136,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, >@@ -958,14 +959,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(); > >@@ -980,11 +985,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_arena->makeIdentifier(m_vm, m_vm->propertyNames->lookUpPrivateName(*ident)); > else if (*ident == m_vm->propertyNames->undefinedKeyword) > tokenData->ident = &m_vm->propertyNames->undefinedPrivateName; >@@ -995,20 +1000,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 <> >@@ -1023,23 +1029,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); > } >@@ -1058,11 +1068,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_arena->makeIdentifier(m_vm, m_vm->propertyNames->lookUpPrivateName(*ident)); > else if (*ident == m_vm->propertyNames->undefinedKeyword) > tokenData->ident = &m_vm->propertyNames->undefinedPrivateName; >@@ -1073,7 +1083,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); >@@ -1093,8 +1103,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(); >@@ -1114,7 +1129,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()); >@@ -1143,13 +1159,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) >@@ -2414,6 +2430,16 @@ start: > m_hasLineTerminatorBeforeToken = true; > m_lineStart = m_code; > goto start; >+ case CharacterPrivateNameStart: { >+ if (Options::useClassFields()) { >+ auto next = peek(1); >+ if (isIdentStart(next) || next == '\\') { >+ lexerFlags &= ~LexexFlagsDontBuildKeywords; >+ goto parseIdent; >+ } >+ } >+ goto invalidCharacter; >+ } > case CharacterPrivateIdentifierStart: > if (m_parsingBuiltinFunction) > goto parseIdent; >@@ -2421,6 +2447,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..f86e1a6d292 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) > { > } > >@@ -404,11 +428,9 @@ namespace JSC { > { > } > >- inline FunctionCallDotNode::FunctionCallDotNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident, ArgumentsNode* args, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) >- : ExpressionNode(location) >+ inline FunctionCallDotNode::FunctionCallDotNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident, DotType type, ArgumentsNode* args, 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_args(args) > { > } >@@ -423,14 +445,14 @@ namespace JSC { > { > } > >- inline CallFunctionCallDotNode::CallFunctionCallDotNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident, ArgumentsNode* args, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, size_t distanceToInnermostCallOrApply) >- : FunctionCallDotNode(location, base, ident, args, divot, divotStart, divotEnd) >+ inline CallFunctionCallDotNode::CallFunctionCallDotNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident, DotType type, ArgumentsNode* args, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, size_t distanceToInnermostCallOrApply) >+ : FunctionCallDotNode(location, base, ident, type, args, divot, divotStart, divotEnd) > , m_distanceToInnermostCallOrApply(distanceToInnermostCallOrApply) > { > } > >- inline ApplyFunctionCallDotNode::ApplyFunctionCallDotNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident, ArgumentsNode* args, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, size_t distanceToInnermostCallOrApply) >- : FunctionCallDotNode(location, base, ident, args, divot, divotStart, divotEnd) >+ inline ApplyFunctionCallDotNode::ApplyFunctionCallDotNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident, DotType type, ArgumentsNode* args, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, size_t distanceToInnermostCallOrApply) >+ : FunctionCallDotNode(location, base, ident, type, args, divot, divotStart, divotEnd) > , m_distanceToInnermostCallOrApply(distanceToInnermostCallOrApply) > { > } >@@ -719,21 +741,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 +1001,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 +1024,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 15bdb56dab6..e64b6232c15 100644 >--- a/Source/JavaScriptCore/parser/Nodes.cpp >+++ b/Source/JavaScriptCore/parser/Nodes.cpp >@@ -198,6 +198,7 @@ FunctionMetadataNode::FunctionMetadataNode( > , m_isInStrictContext(isInStrictContext) > , m_superBinding(static_cast<unsigned>(superBinding)) > , m_constructorKind(static_cast<unsigned>(constructorKind)) >+ , m_classFieldsInitializer(static_cast<unsigned>(ClassFieldsInitializer::NotNeeded)) > , m_isArrowFunctionBodyExpression(isArrowFunctionBodyExpression) > , m_parseMode(mode) > , m_startColumn(startColumn) >@@ -221,6 +222,7 @@ FunctionMetadataNode::FunctionMetadataNode( > , m_isInStrictContext(isInStrictContext) > , m_superBinding(static_cast<unsigned>(superBinding)) > , m_constructorKind(static_cast<unsigned>(constructorKind)) >+ , m_classFieldsInitializer(static_cast<unsigned>(ClassFieldsInitializer::NotNeeded)) > , m_isArrowFunctionBodyExpression(isArrowFunctionBodyExpression) > , m_parseMode(mode) > , m_startColumn(startColumn) >@@ -326,6 +328,27 @@ 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; >+} >+ >+// ------------------------------ ClassExprNode ----------------------------- >+ >+bool PropertyListNode::hasInstanceFields() const >+{ >+ for (auto list = this; list; list = list->m_next) { >+ if (list->m_node->isInstanceClassField()) >+ return true; >+ } >+ 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 60ba5eea2d5..3ca42f8bbaa 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; } >@@ -244,6 +245,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 }; >@@ -705,12 +707,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; } >@@ -720,7 +723,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); } > >@@ -729,12 +737,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 { >@@ -743,15 +752,27 @@ 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(); >+ } >+ bool hasInstanceFields() const; >+ >+ static bool shouldCreateLexicalScopeForClass(PropertyListNode*); > >- RegisterID* emitBytecode(BytecodeGenerator&, RegisterID*, RegisterID*); >+ 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 }; >@@ -789,9 +810,30 @@ namespace JSC { > bool m_subscriptHasAssignments; > }; > >- class DotAccessorNode final : public ExpressionNode, public ThrowableExpressionData { >+ enum class DotType { Name, PrivateName }; >+ class BaseDotNode : public ExpressionNode { > public: >- DotAccessorNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&); >+ 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&, DotType); > > ExpressionNode* base() const { return m_base; } > const Identifier& identifier() const { return m_ident; } >@@ -800,10 +842,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 { >@@ -916,9 +956,9 @@ namespace JSC { > bool m_subscriptHasAssignments; > }; > >- class FunctionCallDotNode : public ExpressionNode, public ThrowableSubExpressionData { >+ class FunctionCallDotNode : public BaseDotNode, public ThrowableSubExpressionData { > public: >- FunctionCallDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); >+ FunctionCallDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, DotType, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); > > private: > RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; >@@ -926,8 +966,6 @@ namespace JSC { > protected: > bool isFunctionCall() const override { return true; } > >- ExpressionNode* m_base; >- const Identifier& m_ident; > ArgumentsNode* m_args; > }; > >@@ -966,7 +1004,7 @@ namespace JSC { > > class CallFunctionCallDotNode final : public FunctionCallDotNode { > public: >- CallFunctionCallDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, size_t distanceToInnermostCallOrApply); >+ CallFunctionCallDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, DotType, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, size_t distanceToInnermostCallOrApply); > > private: > RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; >@@ -975,7 +1013,7 @@ namespace JSC { > > class ApplyFunctionCallDotNode final : public FunctionCallDotNode { > public: >- ApplyFunctionCallDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, size_t distanceToInnermostCallOrApply); >+ ApplyFunctionCallDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, DotType, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, size_t distanceToInnermostCallOrApply); > > private: > RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; >@@ -1374,28 +1412,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; >@@ -2028,6 +2062,8 @@ namespace JSC { > bool isInStrictContext() const { return m_isInStrictContext; } > SuperBinding superBinding() { return static_cast<SuperBinding>(m_superBinding); } > ConstructorKind constructorKind() { return static_cast<ConstructorKind>(m_constructorKind); } >+ ClassFieldsInitializer classFieldsInitializer() const { return static_cast<ClassFieldsInitializer>(m_classFieldsInitializer); } >+ void setClassFieldsInitializer(ClassFieldsInitializer value) { m_classFieldsInitializer = static_cast<unsigned>(value); } > bool isArrowFunctionBodyExpression() const { return m_isArrowFunctionBodyExpression; } > > void setLoc(unsigned firstLine, unsigned lastLine, int startOffset, int lineStartOffset) >@@ -2048,6 +2084,7 @@ namespace JSC { > unsigned m_isInStrictContext : 1; > unsigned m_superBinding : 1; > unsigned m_constructorKind : 2; >+ unsigned m_classFieldsInitializer : 1; > unsigned m_isArrowFunctionBodyExpression : 1; > SourceParseMode m_parseMode; > FunctionMode m_functionMode; >@@ -2164,6 +2201,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: >@@ -2176,6 +2228,7 @@ namespace JSC { > void setEcmaName(const Identifier& name) { m_ecmaName = m_name.isNull() ? &name : &m_name; } > > bool hasStaticProperty(const Identifier& propName) { return m_classElements ? m_classElements->hasStaticallyNamedProperty(propName) : false; } >+ bool hasInstanceFields() const { return m_classElements ? m_classElements->hasInstanceFields() : false; } > > private: > RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; >@@ -2188,6 +2241,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 45ac90801ec..566b4807565 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); > } >@@ -817,6 +821,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"); >@@ -1272,6 +1278,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"); >@@ -2097,6 +2105,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(); >@@ -2149,6 +2158,7 @@ static const char* stringArticleForFunctionMode(SourceParseMode mode) > case SourceParseMode::ProgramMode: > case SourceParseMode::ModuleAnalyzeMode: > case SourceParseMode::ModuleEvaluateMode: >+ case SourceParseMode::InstanceFieldInitializerMode: > RELEASE_ASSERT_NOT_REACHED(); > return ""; > } >@@ -2190,6 +2200,7 @@ static const char* stringForFunctionMode(SourceParseMode mode) > case SourceParseMode::ProgramMode: > case SourceParseMode::ModuleAnalyzeMode: > case SourceParseMode::ModuleEvaluateMode: >+ case SourceParseMode::InstanceFieldInitializerMode: > RELEASE_ASSERT_NOT_REACHED(); > return ""; > } >@@ -2837,6 +2848,7 @@ template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(T > parentClass = parseMemberExpression(context); > failIfFalse(parentClass, "Cannot parse the parent class name"); > } >+ classScope->setIsClassScope(); > const ConstructorKind constructorKind = parentClass ? ConstructorKind::Extends : ConstructorKind::Base; > > consumeOrFail(OPENBRACE, "Expected opening '{' at the start of a class body"); >@@ -2844,6 +2856,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(); >@@ -2855,6 +2868,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(); >@@ -2888,7 +2902,8 @@ parseMethod: > if (!isGeneratorMethodParseMode(parseMode) && !isAsyncMethodParseMode(parseMode)) { > ident = m_token.m_data.ident; > next(); >- if (match(OPENPAREN) || match(COLON) || match(EQUAL) || m_lexer->hasLineTerminatorBeforeToken()) >+ // 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->hasLineTerminatorBeforeToken()) > break; > if (UNLIKELY(consume(TIMES))) > parseMode = SourceParseMode::AsyncGeneratorWrapperMethodMode; >@@ -2916,9 +2931,23 @@ 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: { >+ ASSERT(Options::useClassFields()); >+ 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; >@@ -2928,9 +2957,38 @@ 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 (Options::useClassFields() && !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); >+ classScope->setExpectedSuperBinding(SuperBinding::Needed); >+ initializer = parseAssignmentExpression(context); >+ classScope->setExpectedSuperBinding(SuperBinding::NotNeeded); >+ failIfFalse(initializer, "Cannot parse initializer for class field"); >+ classScope->markLastUsedVariablesSetAsCaptured(); >+ } >+ 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; >@@ -2955,11 +3013,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); > } > } > >@@ -2972,11 +3028,82 @@ parseMethod: > info.endOffset = tokenLocation().endOffset - 1; > consumeOrFail(CLOSEBRACE, "Expected a closing '}' after a class body"); > >+ if (Options::useClassFields()) { >+ // 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(); >+ currentScope()->setIsClassScope(); >+ >+ unsigned numComputedFields = 0; >+ for (auto location : instanceFieldLocations) { >+ // We don't need to worry about hasLineTerminatorBeforeToken >+ // on class fields, so we set this value to false. >+ LexerState lexerState { location.offset, static_cast<unsigned>(location.lineStartOffset), static_cast<unsigned>(location.line), static_cast<unsigned>(location.line), false }; >+ 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->hasLineTerminatorBeforeToken()); >+ >+ 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) >@@ -4515,9 +4642,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(); > >@@ -4687,6 +4818,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) > { >@@ -4712,10 +4859,13 @@ 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"); >+ ScopeRef classScope = closestClassScopeOrTopLevelScope(); >+ bool isClassFieldInitializer = classScope.index() > closestOrdinaryFunctionScope.index(); >+ bool isFunctionEvalContextType = closestOrdinaryFunctionScope->evalContextType() == EvalContextType::FunctionEvalContext || closestOrdinaryFunctionScope->evalContextType() == EvalContextType::InstanceFieldEvalContext; >+ semanticFailIfFalse(currentScope()->isFunction() || isFunctionEvalContextType || isClassFieldInitializer, "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 || isClassFieldInitializer, "new.target is not valid inside arrow functions in global code"); > currentScope()->setInnerArrowFunctionUsesNewTarget(); > } > ASSERT(lastNewTokenLocation.line); >@@ -4732,9 +4882,12 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres > > if (baseIsSuper) { > ScopeRef closestOrdinaryFunctionScope = closestParentOrdinaryFunctionNonLexicalScope(); >- semanticFailIfFalse(currentScope()->isFunction() || (closestOrdinaryFunctionScope->isEvalContext() && closestOrdinaryFunctionScope->expectedSuperBinding() == SuperBinding::Needed), "super is not valid in this context"); >+ ScopeRef classScope = closestClassScopeOrTopLevelScope(); >+ bool isClassFieldInitializer = classScope.index() > closestOrdinaryFunctionScope.index(); >+ semanticFailIfFalse(currentScope()->isFunction() || isClassFieldInitializer || (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 >@@ -4744,7 +4897,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres > SuperBinding functionSuperBinding = !functionScope->isArrowFunction() && !closestOrdinaryFunctionScope->isEvalContext() > ? functionScope->expectedSuperBinding() > : closestOrdinaryFunctionScope->expectedSuperBinding(); >- semanticFailIfTrue(functionSuperBinding == SuperBinding::NotNeeded, "super is not valid in this context"); >+ semanticFailIfTrue(functionSuperBinding == SuperBinding::NotNeeded && !isClassFieldInitializer, "super is not valid in this context"); > } > } > } else if (baseIsImport) { >@@ -4803,6 +4956,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) { >@@ -4854,8 +5009,21 @@ 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 (Options::useClassFields() && match(PRIVATENAME)) { >+ ASSERT(ident); >+ failIfTrue(baseIsSuper, "Cannot access private names from super"); >+ 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(); >@@ -5042,6 +5210,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: >@@ -5113,7 +5282,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 10a312112ff..da8779c240b 100644 >--- a/Source/JavaScriptCore/parser/Parser.h >+++ b/Source/JavaScriptCore/parser/Parser.h >@@ -179,6 +179,8 @@ public: > , m_hasArguments(false) > , m_isEvalContext(false) > , m_hasNonSimpleParameterList(false) >+ , m_isPrivateNameScope(false) >+ , m_isClassScope(false) > , m_evalContextType(EvalContextType::None) > , m_constructorKind(static_cast<unsigned>(ConstructorKind::None)) > , m_expectedSuperBinding(static_cast<unsigned>(SuperBinding::NotNeeded)) >@@ -256,6 +258,7 @@ public: > case SourceParseMode::GetterMode: > case SourceParseMode::SetterMode: > case SourceParseMode::MethodMode: >+ case SourceParseMode::InstanceFieldInitializerMode: > setIsFunction(); > break; > >@@ -285,6 +288,8 @@ 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 isClassScope() const { return m_isClassScope; } > > bool hasArguments() const { return m_hasArguments; } > >@@ -299,6 +304,19 @@ public: > m_isLexicalScope = true; > m_allowsLexicalDeclarations = true; > } >+ >+ void setIsPrivateNameScope() >+ { >+ setIsFunction(); >+ m_isPrivateNameScope = true; >+ } >+ >+ void setIsClassScope() >+ { >+ setIsPrivateNameScope(); >+ m_isClassScope = true; >+ } >+ > bool isLexicalScope() { return m_isLexicalScope; } > bool usesEval() { return m_usesEval; } > >@@ -460,7 +478,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 +636,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 +880,8 @@ private: > bool m_hasArguments; > bool m_isEvalContext; > bool m_hasNonSimpleParameterList; >+ bool m_isPrivateNameScope; >+ bool m_isClassScope; > EvalContextType m_evalContextType; > unsigned m_constructorKind; > unsigned m_expectedSuperBinding; >@@ -887,7 +952,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 +1225,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; >@@ -1169,7 +1263,16 @@ private: > // When reaching the top level scope (it can be non ordinary function scope), we return it. > return ScopeRef(&m_scopeStack, i); > } >- >+ >+ ScopeRef closestClassScopeOrTopLevelScope() >+ { >+ unsigned i = m_scopeStack.size() - 1; >+ ASSERT(i < m_scopeStack.size() && m_scopeStack.size()); >+ while (i && !m_scopeStack[i].isClassScope()) >+ i--; >+ return ScopeRef(&m_scopeStack, i); >+ } >+ > ScopeRef pushScope() > { > bool isFunction = false; >@@ -1345,7 +1448,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); > >@@ -1417,6 +1520,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) > { >@@ -1574,6 +1687,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 }; >@@ -1667,6 +1781,8 @@ private: > > template <class TreeBuilder> ALWAYS_INLINE bool isSimpleAssignmentTarget(TreeBuilder&, TreeExpression); > >+ ALWAYS_INLINE bool addPrivateNameUsedIfNeeded(const Identifier*); >+ > ALWAYS_INLINE int isBinaryOperator(JSTokenType); > bool allowAutomaticSemicolon(); > >@@ -1760,7 +1876,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 >@@ -1901,7 +2019,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; >@@ -1918,7 +2036,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(); >@@ -2002,7 +2127,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()); > >@@ -2013,7 +2140,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) { >@@ -2026,7 +2153,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 e26d9f7bccb..5d5882c7571 100644 >--- a/Source/JavaScriptCore/parser/ParserModes.h >+++ b/Source/JavaScriptCore/parser/ParserModes.h >@@ -66,6 +66,7 @@ enum class SourceParseMode : uint8_t { > AsyncGeneratorWrapperFunctionMode = 16, > AsyncGeneratorWrapperMethodMode = 17, > GeneratorWrapperMethodMode = 18, >+ InstanceFieldInitializerMode = 19, > }; > > class SourceParseModeSet { >@@ -114,7 +115,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 cfee33798c2..a632bd43d6f 100644 >--- a/Source/JavaScriptCore/parser/ParserTokens.h >+++ b/Source/JavaScriptCore/parser/ParserTokens.h >@@ -114,6 +114,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 419958bedb4..89debf1243d 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 isLocation(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 isAssignmentLocation(ExpressionType type) >diff --git a/Source/JavaScriptCore/parser/VariableEnvironment.cpp b/Source/JavaScriptCore/parser/VariableEnvironment.cpp >index 8e69151c7b6..e32e9263df2 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,11 +107,50 @@ void VariableEnvironment::markVariableAsExported(const RefPtr<UniquedStringImpl> > findResult->value.setIsExported(); > } > >+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()) > { > Vector<std::pair<UniquedStringImpl*, VariableEnvironmentEntry>, 32> sortedEntries; >- sortedEntries.reserveInitialCapacity(env.size()); >+ sortedEntries.reserveInitialCapacity(env.mapSize()); > for (auto& pair : env) > sortedEntries.append({ pair.key.get(), pair.value }); > >diff --git a/Source/JavaScriptCore/parser/VariableEnvironment.h b/Source/JavaScriptCore/parser/VariableEnvironment.h >index 96372c5899f..58f1a0b5623 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,8 @@ 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 unsigned mapSize() const { return m_map.size(); } > 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); } >@@ -116,11 +158,91 @@ public: > bool isEverythingCaptured() const { return m_isEverythingCaptured; } > bool isEmpty() const { return !m_map.size(); } > >+ 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 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/CachedTypes.cpp b/Source/JavaScriptCore/runtime/CachedTypes.cpp >index e0b4cbfb9a5..197a1a4517c 100644 >--- a/Source/JavaScriptCore/runtime/CachedTypes.cpp >+++ b/Source/JavaScriptCore/runtime/CachedTypes.cpp >@@ -1076,6 +1076,24 @@ private: > intptr_t m_bits; > }; > >+class CachedSymbolTableRareData : public CachedObject<SymbolTable::SymbolTableRareData> { >+public: >+ void encode(Encoder& encoder, const SymbolTable::SymbolTableRareData& rareData) >+ { >+ m_privateNames.encode(encoder, rareData.m_privateNames); >+ } >+ >+ SymbolTable::SymbolTableRareData* decode(Decoder& decoder) const >+ { >+ SymbolTable::SymbolTableRareData* rareData = new SymbolTable::SymbolTableRareData { }; >+ m_privateNames.decode(decoder, rareData->m_privateNames); >+ return rareData; >+ } >+ >+private: >+ CachedHashSet<CachedRefPtr<CachedUniquedStringImpl>, StringRepHash> m_privateNames; >+}; >+ > class CachedSymbolTable : public CachedObject<SymbolTable> { > public: > void encode(Encoder& encoder, const SymbolTable& symbolTable) >@@ -1086,6 +1104,7 @@ public: > m_nestedLexicalScope = symbolTable.m_nestedLexicalScope; > m_scopeType = symbolTable.m_scopeType; > m_arguments.encode(encoder, symbolTable.m_arguments.get()); >+ m_rareData.encode(encoder, symbolTable.m_rareData); > } > > SymbolTable* decode(Decoder& decoder) const >@@ -1096,6 +1115,8 @@ public: > symbolTable->m_usesNonStrictEval = m_usesNonStrictEval; > symbolTable->m_nestedLexicalScope = m_nestedLexicalScope; > symbolTable->m_scopeType = m_scopeType; >+ std::unique_ptr<SymbolTable::SymbolTableRareData> rareData(m_rareData.decodeAsPtr(decoder)); >+ symbolTable->m_rareData = WTFMove(rareData); > ScopedArgumentsTable* scopedArgumentsTable = m_arguments.decode(decoder); > if (scopedArgumentsTable) > symbolTable->m_arguments.set(decoder.vm(), symbolTable, scopedArgumentsTable); >@@ -1108,6 +1129,7 @@ private: > unsigned m_usesNonStrictEval : 1; > unsigned m_nestedLexicalScope : 1; > unsigned m_scopeType : 3; >+ CachedOptional<CachedSymbolTableRareData> m_rareData; > CachedPtr<CachedScopedArgumentsTable> m_arguments; > }; > >diff --git a/Source/JavaScriptCore/runtime/CodeCache.cpp b/Source/JavaScriptCore/runtime/CodeCache.cpp >index 03a92ee64a3..a8ef6403225 100644 >--- a/Source/JavaScriptCore/runtime/CodeCache.cpp >+++ b/Source/JavaScriptCore/runtime/CodeCache.cpp >@@ -151,7 +151,7 @@ UnlinkedFunctionExecutable* CodeCache::getUnlinkedGlobalFunctionExecutable(VM& v > // The Function constructor only has access to global variables, so no variables will be under TDZ unless they're > // in the global lexical environment, which we always TDZ check accesses from. > ConstructAbility constructAbility = constructAbilityForParseMode(metadata->parseMode()); >- UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&vm, source, metadata, UnlinkedNormalFunction, constructAbility, JSParserScriptMode::Classic, WTF::nullopt, DerivedContextType::None); >+ UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&vm, source, metadata, UnlinkedNormalFunction, constructAbility, JSParserScriptMode::Classic, WTF::nullopt, DerivedContextType::None, ClassFieldsInitializer::NotNeeded); > > if (!source.provider()->sourceURLDirective().isNull()) > functionExecutable->setSourceURLDirective(source.provider()->sourceURLDirective()); >diff --git a/Source/JavaScriptCore/runtime/CodeCache.h b/Source/JavaScriptCore/runtime/CodeCache.h >index b067f847dae..405a07d4820 100644 >--- a/Source/JavaScriptCore/runtime/CodeCache.h >+++ b/Source/JavaScriptCore/runtime/CodeCache.h >@@ -259,12 +259,24 @@ template <> struct CacheTypes<UnlinkedModuleProgramCodeBlock> { > static const SourceParseMode parseMode = SourceParseMode::ModuleEvaluateMode; > }; > >+template <class ExecutableType> >+inline ClassFieldsInitializer classFieldsInitializer(ExecutableType*) >+{ >+ return ClassFieldsInitializer::NotNeeded; >+} >+ >+template <> >+inline ClassFieldsInitializer classFieldsInitializer<DirectEvalExecutable>(DirectEvalExecutable* executable) >+{ >+ return executable->classFieldsInitializer(); >+} >+ > template <class UnlinkedCodeBlockType, class ExecutableType = ScriptExecutable> > UnlinkedCodeBlockType* generateUnlinkedCodeBlockImpl(VM& vm, const SourceCode& source, JSParserStrictMode strictMode, JSParserScriptMode scriptMode, OptionSet<CodeGenerationMode> codeGenerationMode, ParserError& error, EvalContextType evalContextType, DerivedContextType derivedContextType, bool isArrowFunctionContext, const VariableEnvironment* variablesUnderTDZ, ExecutableType* executable = nullptr) > { > 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; > >@@ -279,7 +291,7 @@ UnlinkedCodeBlockType* generateUnlinkedCodeBlockImpl(VM& vm, const SourceCode& s > > bool usesEval = rootNode->features() & EvalFeature; > bool isStrictMode = rootNode->features() & StrictModeFeature; >- ExecutableInfo executableInfo(usesEval, isStrictMode, false, false, ConstructorKind::None, scriptMode, SuperBinding::NotNeeded, CacheTypes<UnlinkedCodeBlockType>::parseMode, derivedContextType, isArrowFunctionContext, false, evalContextType); >+ ExecutableInfo executableInfo(usesEval, isStrictMode, false, false, ConstructorKind::None, scriptMode, SuperBinding::NotNeeded, CacheTypes<UnlinkedCodeBlockType>::parseMode, derivedContextType, classFieldsInitializer(executable), isArrowFunctionContext, false, evalContextType); > > UnlinkedCodeBlockType* unlinkedCodeBlock = UnlinkedCodeBlockType::create(&vm, executableInfo, codeGenerationMode); > unlinkedCodeBlock->recordParse(rootNode->features(), rootNode->hasCapturedVariables(), lineCount, unlinkedEndColumn); >diff --git a/Source/JavaScriptCore/runtime/CommonIdentifiers.cpp b/Source/JavaScriptCore/runtime/CommonIdentifiers.cpp >index fae47242c9a..78a0239f27f 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() >@@ -45,6 +46,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 c06f7ec75c8..65c94ffb1f2 100644 >--- a/Source/JavaScriptCore/runtime/CommonIdentifiers.h >+++ b/Source/JavaScriptCore/runtime/CommonIdentifiers.h >@@ -199,6 +199,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) \ >@@ -312,6 +315,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 >+ > SymbolImpl* lookUpPrivateName(const Identifier&) const; > Identifier getPublicName(VM&, SymbolImpl*) const; > >diff --git a/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp b/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp >index 29b2022338a..27a65fe848c 100644 >--- a/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp >+++ b/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp >@@ -888,6 +888,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(); >@@ -1380,4 +1387,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 = JSObject::getOwnPropertySlot(asObject(baseValue), 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/DirectEvalExecutable.cpp b/Source/JavaScriptCore/runtime/DirectEvalExecutable.cpp >index 73309ae1b16..a88d31963fd 100644 >--- a/Source/JavaScriptCore/runtime/DirectEvalExecutable.cpp >+++ b/Source/JavaScriptCore/runtime/DirectEvalExecutable.cpp >@@ -35,7 +35,7 @@ > > namespace JSC { > >-DirectEvalExecutable* DirectEvalExecutable::create(ExecState* exec, const SourceCode& source, bool isInStrictContext, DerivedContextType derivedContextType, bool isArrowFunctionContext, EvalContextType evalContextType, const VariableEnvironment* variablesUnderTDZ) >+DirectEvalExecutable* DirectEvalExecutable::create(ExecState* exec, const SourceCode& source, bool isInStrictContext, DerivedContextType derivedContextType, ClassFieldsInitializer classFieldsInitializer, bool isArrowFunctionContext, EvalContextType evalContextType, const VariableEnvironment* variablesUnderTDZ) > { > VM& vm = exec->vm(); > auto scope = DECLARE_THROW_SCOPE(vm); >@@ -46,7 +46,7 @@ DirectEvalExecutable* DirectEvalExecutable::create(ExecState* exec, const Source > return 0; > } > >- auto* executable = new (NotNull, allocateCell<DirectEvalExecutable>(vm.heap)) DirectEvalExecutable(exec, source, isInStrictContext, derivedContextType, isArrowFunctionContext, evalContextType); >+ auto* executable = new (NotNull, allocateCell<DirectEvalExecutable>(vm.heap)) DirectEvalExecutable(exec, source, isInStrictContext, derivedContextType, classFieldsInitializer, isArrowFunctionContext, evalContextType); > executable->finishCreation(vm); > > ParserError error; >@@ -70,9 +70,11 @@ DirectEvalExecutable* DirectEvalExecutable::create(ExecState* exec, const Source > return executable; > } > >-DirectEvalExecutable::DirectEvalExecutable(ExecState* exec, const SourceCode& source, bool inStrictContext, DerivedContextType derivedContextType, bool isArrowFunctionContext, EvalContextType evalContextType) >+DirectEvalExecutable::DirectEvalExecutable(ExecState* exec, const SourceCode& source, bool inStrictContext, DerivedContextType derivedContextType, ClassFieldsInitializer classFieldsInitializer, bool isArrowFunctionContext, EvalContextType evalContextType) > : EvalExecutable(exec, source, inStrictContext, derivedContextType, isArrowFunctionContext, evalContextType) > { >+ ASSERT(classFieldsInitializer == ClassFieldsInitializer::NotNeeded || derivedContextType == DerivedContextType::DerivedConstructorContext); >+ m_classFieldsInitializer = static_cast<unsigned>(classFieldsInitializer); > } > > } // namespace JSC >diff --git a/Source/JavaScriptCore/runtime/DirectEvalExecutable.h b/Source/JavaScriptCore/runtime/DirectEvalExecutable.h >index 8948d45b582..35208a7e6b5 100644 >--- a/Source/JavaScriptCore/runtime/DirectEvalExecutable.h >+++ b/Source/JavaScriptCore/runtime/DirectEvalExecutable.h >@@ -31,9 +31,10 @@ namespace JSC { > > class DirectEvalExecutable final : public EvalExecutable { > public: >- static DirectEvalExecutable* create(ExecState*, const SourceCode&, bool isInStrictContext, DerivedContextType, bool isArrowFunctionContext, EvalContextType, const VariableEnvironment*); >+ static DirectEvalExecutable* create(ExecState*, const SourceCode&, bool isInStrictContext, DerivedContextType, ClassFieldsInitializer, bool isArrowFunctionContext, EvalContextType, const VariableEnvironment*); >+ > private: >- DirectEvalExecutable(ExecState*, const SourceCode&, bool inStrictContext, DerivedContextType, bool isArrowFunctionContext, EvalContextType); >+ DirectEvalExecutable(ExecState*, const SourceCode&, bool inStrictContext, DerivedContextType, ClassFieldsInitializer, bool isArrowFunctionContext, EvalContextType); > }; > > static_assert(sizeof(DirectEvalExecutable) == sizeof(EvalExecutable), ""); >diff --git a/Source/JavaScriptCore/runtime/EvalExecutable.h b/Source/JavaScriptCore/runtime/EvalExecutable.h >index 57b7a35f432..7fbea7b7c05 100644 >--- a/Source/JavaScriptCore/runtime/EvalExecutable.h >+++ b/Source/JavaScriptCore/runtime/EvalExecutable.h >@@ -62,13 +62,13 @@ public: > > DECLARE_INFO; > >- ExecutableInfo executableInfo() const { return ExecutableInfo(usesEval(), isStrictMode(), false, false, ConstructorKind::None, JSParserScriptMode::Classic, SuperBinding::NotNeeded, SourceParseMode::ProgramMode, derivedContextType(), isArrowFunctionContext(), false, evalContextType()); } >+ ExecutableInfo executableInfo() const { return ExecutableInfo(usesEval(), isStrictMode(), false, false, ConstructorKind::None, JSParserScriptMode::Classic, SuperBinding::NotNeeded, SourceParseMode::ProgramMode, derivedContextType(), classFieldsInitializer(), isArrowFunctionContext(), false, evalContextType()); } > > unsigned numVariables() { return m_unlinkedEvalCodeBlock->numVariables(); } > unsigned numFunctionHoistingCandidates() { return m_unlinkedEvalCodeBlock->numFunctionHoistingCandidates(); } > unsigned numTopLevelFunctionDecls() { return m_unlinkedEvalCodeBlock->numberOfFunctionDecls(); } > bool allowDirectEvalCache() const { return m_unlinkedEvalCodeBlock->allowDirectEvalCache(); } >- >+ ClassFieldsInitializer classFieldsInitializer() const { return static_cast<ClassFieldsInitializer>(m_classFieldsInitializer); } > TemplateObjectMap& ensureTemplateObjectMap(VM&); > > protected: >@@ -80,6 +80,8 @@ protected: > > static void visitChildren(JSCell*, SlotVisitor&); > >+ unsigned m_classFieldsInitializer : 1; >+ > WriteBarrier<ExecutableToCodeBlockEdge> m_evalCodeBlock; > WriteBarrier<UnlinkedEvalCodeBlock> m_unlinkedEvalCodeBlock; > std::unique_ptr<TemplateObjectMap> m_templateObjectMap; >diff --git a/Source/JavaScriptCore/runtime/FunctionPrototype.cpp b/Source/JavaScriptCore/runtime/FunctionPrototype.cpp >index 2cec7d67717..f7a8ec88477 100644 >--- a/Source/JavaScriptCore/runtime/FunctionPrototype.cpp >+++ b/Source/JavaScriptCore/runtime/FunctionPrototype.cpp >@@ -113,6 +113,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 fa8d3421c41..bef6e097be2 100644 >--- a/Source/JavaScriptCore/runtime/Identifier.h >+++ b/Source/JavaScriptCore/runtime/Identifier.h >@@ -302,6 +302,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 ef7033e591c..f3c24917fdf 100644 >--- a/Source/JavaScriptCore/runtime/JSCJSValue.h >+++ b/Source/JavaScriptCore/runtime/JSCJSValue.h >@@ -175,6 +175,7 @@ public: > enum JSFalseTag { JSFalse }; > enum JSCellTag { JSCellType }; > enum EncodeAsDoubleTag { EncodeAsDouble }; >+ enum JSValueTag { Tag }; > > JSValue(); > JSValue(JSNullTag); >@@ -272,6 +273,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 0d3324ae546..40e203edd27 100644 >--- a/Source/JavaScriptCore/runtime/JSCJSValueInlines.h >+++ b/Source/JavaScriptCore/runtime/JSCJSValueInlines.h >@@ -689,6 +689,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 f3a96d8556b..d377e406c8d 100644 >--- a/Source/JavaScriptCore/runtime/JSFunction.cpp >+++ b/Source/JavaScriptCore/runtime/JSFunction.cpp >@@ -396,6 +396,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 5aa6d49f8a1..83f233249e3 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,19 +73,30 @@ protected: > ASSERT(symbolTable); > setSymbolTable(vm, symbolTable); > } >- >+ > void setSymbolTable(VM& vm, SymbolTable* symbolTable) > { > ASSERT(!m_symbolTable); > if (auto* singletonScope = symbolTable->singletonScope()) > 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/ModuleProgramExecutable.h b/Source/JavaScriptCore/runtime/ModuleProgramExecutable.h >index f9b7e2adbfe..859e1c1d2e1 100644 >--- a/Source/JavaScriptCore/runtime/ModuleProgramExecutable.h >+++ b/Source/JavaScriptCore/runtime/ModuleProgramExecutable.h >@@ -1,3 +1,4 @@ >+ > /* > * Copyright (C) 2009-2018 Apple Inc. All rights reserved. > * >@@ -63,7 +64,7 @@ public: > > DECLARE_INFO; > >- ExecutableInfo executableInfo() const { return ExecutableInfo(usesEval(), isStrictMode(), false, false, ConstructorKind::None, JSParserScriptMode::Module, SuperBinding::NotNeeded, SourceParseMode::ModuleEvaluateMode, derivedContextType(), isArrowFunctionContext(), false, EvalContextType::None); } >+ ExecutableInfo executableInfo() const { return ExecutableInfo(usesEval(), isStrictMode(), false, false, ConstructorKind::None, JSParserScriptMode::Module, SuperBinding::NotNeeded, SourceParseMode::ModuleEvaluateMode, derivedContextType(), ClassFieldsInitializer::NotNeeded, isArrowFunctionContext(), false, EvalContextType::None); } > > UnlinkedModuleProgramCodeBlock* unlinkedModuleProgramCodeBlock() { return m_unlinkedModuleProgramCodeBlock.get(); } > >diff --git a/Source/JavaScriptCore/runtime/Options.h b/Source/JavaScriptCore/runtime/Options.h >index 14c9b5663e5..18771a2b96b 100644 >--- a/Source/JavaScriptCore/runtime/Options.h >+++ b/Source/JavaScriptCore/runtime/Options.h >@@ -520,7 +520,7 @@ constexpr bool enableWebAssemblyStreamingApi = false; > v(double, validateAbstractInterpreterStateProbability, 0.5, Normal, nullptr) \ > v(optionString, dumpJITMemoryPath, nullptr, Restricted, nullptr) \ > v(double, dumpJITMemoryFlushInterval, 10, Restricted, "Maximum time in between flushes of the JIT memory dump in seconds.") \ >- >+ v(bool, useClassFields, true, Normal, "If true, the parser will understand data fields inside classes.") \ > > enum OptionEquivalence { > SameOption, >diff --git a/Source/JavaScriptCore/runtime/ProgramExecutable.h b/Source/JavaScriptCore/runtime/ProgramExecutable.h >index b96844456a3..23639570d76 100644 >--- a/Source/JavaScriptCore/runtime/ProgramExecutable.h >+++ b/Source/JavaScriptCore/runtime/ProgramExecutable.h >@@ -71,7 +71,7 @@ public: > > DECLARE_INFO; > >- ExecutableInfo executableInfo() const { return ExecutableInfo(usesEval(), isStrictMode(), false, false, ConstructorKind::None, JSParserScriptMode::Classic, SuperBinding::NotNeeded, SourceParseMode::ProgramMode, derivedContextType(), isArrowFunctionContext(), false, EvalContextType::None); } >+ ExecutableInfo executableInfo() const { return ExecutableInfo(usesEval(), isStrictMode(), false, false, ConstructorKind::None, JSParserScriptMode::Classic, SuperBinding::NotNeeded, SourceParseMode::ProgramMode, derivedContextType(), ClassFieldsInitializer::NotNeeded, isArrowFunctionContext(), false, EvalContextType::None); } > > TemplateObjectMap& ensureTemplateObjectMap(VM&); > >diff --git a/Source/JavaScriptCore/runtime/SymbolTable.cpp b/Source/JavaScriptCore/runtime/SymbolTable.cpp >index c93607e8914..860c91be994 100644 >--- a/Source/JavaScriptCore/runtime/SymbolTable.cpp >+++ b/Source/JavaScriptCore/runtime/SymbolTable.cpp >@@ -183,6 +183,13 @@ 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 3fd1c46e1ad..eef04a0f01b 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 >@@ -703,17 +726,20 @@ private: > ScopeOffset m_maxScopeOffset; > public: > mutable ConcurrentJSLock m_lock; >-private: >- unsigned m_usesNonStrictEval : 1; >- unsigned m_nestedLexicalScope : 1; // Non-function LexicalScope. >- unsigned m_scopeType : 3; // ScopeType >- >+ > struct SymbolTableRareData { > UniqueIDMap m_uniqueIDMap; > OffsetToVariableMap m_offsetToVariableMap; > UniqueTypeSetMap m_uniqueTypeSetMap; > WriteBarrier<CodeBlock> m_codeBlock; >+ PrivateNameSet m_privateNames; > }; >+ >+private: >+ unsigned m_usesNonStrictEval : 1; >+ unsigned m_nestedLexicalScope : 1; // Non-function LexicalScope. >+ unsigned m_scopeType : 3; // ScopeType >+ > std::unique_ptr<SymbolTableRareData> m_rareData; > > WriteBarrier<ScopedArgumentsTable> m_arguments; >-- >2.21.0 >
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:
ews-watchlist
:
commit-queue-
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