WebKit Bugzilla
Attachment 348905 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]
instance class fields (09/05/18)
class-fields-big-patch.diff (text/plain), 171.95 KB, created by
Xan Lopez
on 2018-09-05 03:05:59 PDT
(
hide
)
Description:
instance class fields (09/05/18)
Filename:
MIME Type:
Creator:
Xan Lopez
Created:
2018-09-05 03:05:59 PDT
Size:
171.95 KB
patch
obsolete
>From c886c674a17dd60498388aaf1c84cd4c014d246c Mon Sep 17 00:00:00 2001 >From: =?UTF-8?q?Xan=20L=C3=B3pez?= <xan@igalia.com> >Date: Thu, 28 Jun 2018 08:02:29 +0000 >Subject: [PATCH] class fields: initial implementation of instance fields > https://bugs.webkit.org/show_bug.cgi?id=174212 >MIME-Version: 1.0 >Content-Type: text/plain; charset=UTF-8 >Content-Transfer-Encoding: 8bit > >proposal: https://tc39.github.io/proposal-class-fields/ > >Public and private instance fields are working, with minor FIXMEs and >TODOs. All in-tree test262 tests are passing. > >Static fields are also TODO, but they are a different proposal: >https://tc39.github.io/proposal-static-class-features/ > >Work by Caitlin Potter and Xan López. >--- > Source/JavaScriptCore/builtins/BuiltinNames.h | 1 + > .../bytecode/BytecodeDumper.cpp | 34 +++ > .../JavaScriptCore/bytecode/BytecodeList.json | 6 +- > .../JavaScriptCore/bytecode/BytecodeUseDef.h | 21 +- > Source/JavaScriptCore/bytecode/CodeBlock.cpp | 35 +++- > .../JavaScriptCore/bytecode/ExecutableInfo.h | 2 +- > .../bytecode/UnlinkedFunctionExecutable.cpp | 5 +- > .../bytecode/UnlinkedFunctionExecutable.h | 7 + > .../bytecompiler/BytecodeGenerator.cpp | 152 +++++++++++++- > .../bytecompiler/BytecodeGenerator.h | 15 +- > .../bytecompiler/NodesCodegen.cpp | 188 ++++++++++++++--- > .../dfg/DFGAbstractInterpreterInlines.h | 24 +++ > .../dfg/DFGBackwardsPropagationPhase.cpp | 5 + > .../JavaScriptCore/dfg/DFGByteCodeParser.cpp | 8 +- > Source/JavaScriptCore/dfg/DFGCapabilities.cpp | 4 + > Source/JavaScriptCore/dfg/DFGClobberize.h | 1 + > .../dfg/DFGConstantFoldingPhase.cpp | 9 + > Source/JavaScriptCore/dfg/DFGDoesGC.cpp | 1 + > Source/JavaScriptCore/dfg/DFGFixupPhase.cpp | 20 ++ > Source/JavaScriptCore/dfg/DFGNodeType.h | 1 + > Source/JavaScriptCore/dfg/DFGOperations.cpp | 8 + > Source/JavaScriptCore/dfg/DFGOperations.h | 1 + > .../dfg/DFGPredictionPropagationPhase.cpp | 18 +- > Source/JavaScriptCore/dfg/DFGSafeToExecute.h | 1 + > .../JavaScriptCore/dfg/DFGSpeculativeJIT.cpp | 21 ++ > Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h | 1 + > .../dfg/DFGSpeculativeJIT32_64.cpp | 5 + > .../dfg/DFGSpeculativeJIT64.cpp | 5 + > Source/JavaScriptCore/ftl/FTLCapabilities.cpp | 1 + > Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp | 40 ++++ > .../interpreter/Interpreter.cpp | 4 +- > Source/JavaScriptCore/jit/AssemblyHelpers.h | 4 + > Source/JavaScriptCore/jit/JIT.cpp | 10 + > Source/JavaScriptCore/jit/JIT.h | 4 + > Source/JavaScriptCore/jit/JITOpcodes.cpp | 29 +++ > Source/JavaScriptCore/jit/JITOpcodes32_64.cpp | 14 ++ > .../llint/LowLevelInterpreter32_64.asm | 74 +++++++ > .../llint/LowLevelInterpreter64.asm | 82 ++++++++ > Source/JavaScriptCore/parser/ASTBuilder.h | 19 +- > Source/JavaScriptCore/parser/Lexer.cpp | 59 ++++-- > .../JavaScriptCore/parser/NodeConstructors.h | 47 ++++- > Source/JavaScriptCore/parser/Nodes.cpp | 10 + > Source/JavaScriptCore/parser/Nodes.h | 87 ++++++-- > Source/JavaScriptCore/parser/Parser.cpp | 188 ++++++++++++++++- > Source/JavaScriptCore/parser/Parser.h | 121 ++++++++++- > Source/JavaScriptCore/parser/ParserModes.h | 4 +- > Source/JavaScriptCore/parser/ParserTokens.h | 1 + > Source/JavaScriptCore/parser/SyntaxChecker.h | 16 +- > .../parser/VariableEnvironment.cpp | 60 ++++++ > .../parser/VariableEnvironment.h | 106 +++++++++- > .../runtime/CommonIdentifiers.cpp | 2 + > .../runtime/CommonIdentifiers.h | 7 + > .../runtime/CommonSlowPaths.cpp | 197 ++++++++++++++++++ > .../JavaScriptCore/runtime/CommonSlowPaths.h | 4 + > 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 | 12 ++ > Source/JavaScriptCore/runtime/JSScope.h | 2 + > .../runtime/JSSymbolTableObject.h | 26 ++- > Source/JavaScriptCore/runtime/SymbolTable.cpp | 8 + > Source/JavaScriptCore/runtime/SymbolTable.h | 30 ++- > 63 files changed, 1753 insertions(+), 138 deletions(-) > >diff --git a/Source/JavaScriptCore/builtins/BuiltinNames.h b/Source/JavaScriptCore/builtins/BuiltinNames.h >index f66b8cb51eb..51cb6157b8c 100644 >--- a/Source/JavaScriptCore/builtins/BuiltinNames.h >+++ b/Source/JavaScriptCore/builtins/BuiltinNames.h >@@ -192,6 +192,7 @@ namespace JSC { > macro(meta) \ > macro(webAssemblyCompileStreamingInternal) \ > macro(webAssemblyInstantiateStreamingInternal) \ >+ macro(instanceFieldInitializer) > > namespace Symbols { > #define DECLARE_BUILTIN_STATIC_SYMBOLS(name) extern SymbolImpl::StaticSymbolImpl name##Symbol; >diff --git a/Source/JavaScriptCore/bytecode/BytecodeDumper.cpp b/Source/JavaScriptCore/bytecode/BytecodeDumper.cpp >index 70bf712a2c6..a1642900113 100644 >--- a/Source/JavaScriptCore/bytecode/BytecodeDumper.cpp >+++ b/Source/JavaScriptCore/bytecode/BytecodeDumper.cpp >@@ -1515,6 +1515,13 @@ void BytecodeDumper<Block>::dumpBytecode(PrintStream& out, const typename Block: > out.printf("%s, %s", registerName(r0).data(), registerName(r1).data()); > break; > } >+ case op_to_property_key: { >+ int r0 = (++it)->u.operand; >+ int r1 = (++it)->u.operand; >+ printLocationAndOp(out, location, it, "to_property_key"); >+ out.printf("%s, %s", registerName(r0).data(), registerName(r1).data()); >+ break; >+ } > case op_get_enumerable_length: { > int dst = it[1].u.operand; > int base = it[2].u.operand; >@@ -1744,6 +1751,33 @@ void BytecodeDumper<Block>::dumpBytecode(PrintStream& out, const typename Block: > out.printf("%s, %u, %s", registerName(r0).data(), yieldPoint, registerName(r1).data()); > break; > } >+ case op_add_private_field: { >+ int base = (++it)->u.operand; >+ int id = (++it)->u.operand; >+ int value = (++it)->u.operand; >+ int scope = (++it)->u.operand; >+ printLocationAndOp(out, location, it, "op_add_private_field"); >+ out.print(registerName(base), ", ", idName(id, identifier(id)), ", ", registerName(value), ", ", registerName(scope)); >+ break; >+ } >+ case op_get_private_field: { >+ int r0 = (++it)->u.operand; >+ int base = (++it)->u.operand; >+ int id = (++it)->u.operand; >+ int scope = (++it)->u.operand; >+ printLocationAndOp(out, location, it, "op_get_private_field"); >+ out.print(registerName(r0), ", ", registerName(base), ", ", idName(id, identifier(id)), ", ", registerName(scope)); >+ break; >+ } >+ case op_put_private_field: { >+ int base = (++it)->u.operand; >+ int id = (++it)->u.operand; >+ int value = (++it)->u.operand; >+ int scope = (++it)->u.operand; >+ printLocationAndOp(out, location, it, "op_put_private_field"); >+ out.print(registerName(base), ", ", idName(id, identifier(id)), ", ", registerName(value), ", ", registerName(scope)); >+ break; >+ } > default: > RELEASE_ASSERT_NOT_REACHED(); > } >diff --git a/Source/JavaScriptCore/bytecode/BytecodeList.json b/Source/JavaScriptCore/bytecode/BytecodeList.json >index f5bdc49a7a6..b14c4516ea9 100644 >--- a/Source/JavaScriptCore/bytecode/BytecodeList.json >+++ b/Source/JavaScriptCore/bytecode/BytecodeList.json >@@ -152,6 +152,7 @@ > { "name" : "op_construct_varargs", "length" : 9 }, > { "name" : "op_strcat", "length" : 4 }, > { "name" : "op_to_primitive", "length" : 3 }, >+ { "name" : "op_to_property_key", "length" : 3 }, > { "name" : "op_resolve_scope", "length" : 7 }, > { "name" : "op_get_from_scope", "length" : 8 }, > { "name" : "op_put_to_scope", "length" : 7 }, >@@ -186,7 +187,10 @@ > { "name" : "op_resolve_scope_for_hoisting_func_decl_in_eval", "length" : 4 }, > { "name" : "op_nop", "length" : 1 }, > { "name" : "op_super_sampler_begin", "length" : 1 }, >- { "name" : "op_super_sampler_end", "length" : 1 } >+ { "name" : "op_super_sampler_end", "length" : 1 }, >+ { "name" : "op_add_private_field", "length" : 8 }, >+ { "name" : "op_get_private_field", "length" : 8 }, >+ { "name" : "op_put_private_field", "length" : 7 } > ] > }, > { >diff --git a/Source/JavaScriptCore/bytecode/BytecodeUseDef.h b/Source/JavaScriptCore/bytecode/BytecodeUseDef.h >index 3e3771f5b77..15681c991f9 100644 >--- a/Source/JavaScriptCore/bytecode/BytecodeUseDef.h >+++ b/Source/JavaScriptCore/bytecode/BytecodeUseDef.h >@@ -180,6 +180,7 @@ void computeUsesForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, Instructi > case op_resolve_scope_for_hoisting_func_decl_in_eval: > case op_get_from_scope: > case op_to_primitive: >+ case op_to_property_key: > case op_try_get_by_id: > case op_get_by_id: > case op_get_by_id_proto_load: >@@ -322,6 +323,20 @@ void computeUsesForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, Instructi > functor(codeBlock, instruction, opcodeID, instruction[3].u.operand); > return; > } >+ case op_get_private_field: { >+ ASSERT(opcodeLengths[opcodeID] > 4); >+ functor(codeBlock, instruction, opcodeID, instruction[2].u.operand); >+ functor(codeBlock, instruction, opcodeID, instruction[4].u.operand); >+ return; >+ } >+ case op_add_private_field: >+ case op_put_private_field: { >+ ASSERT(opcodeLengths[opcodeID] > 4); >+ functor(codeBlock, instruction, opcodeID, instruction[1].u.operand); >+ functor(codeBlock, instruction, opcodeID, instruction[3].u.operand); >+ functor(codeBlock, instruction, opcodeID, instruction[4].u.operand); >+ return; >+ } > default: > RELEASE_ASSERT_NOT_REACHED(); > break; >@@ -387,6 +402,8 @@ void computeDefsForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, Instructi > 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 >@@ -409,6 +426,7 @@ void computeDefsForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, Instructi > case op_resolve_scope_for_hoisting_func_decl_in_eval: > case op_strcat: > case op_to_primitive: >+ case op_to_property_key: > case op_create_this: > case op_new_array: > case op_new_array_with_spread: >@@ -502,7 +520,8 @@ void computeDefsForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, Instructi > case op_get_from_arguments: > case op_get_argument: > case op_create_rest: >- case op_get_rest_length: { >+ case op_get_rest_length: >+ case op_get_private_field: { > ASSERT(opcodeLengths[opcodeID] > 1); > functor(codeBlock, instruction, opcodeID, instruction[1].u.operand); > return; >diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp >index 27af0fde2c0..946d7f95d87 100644 >--- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp >+++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp >@@ -576,7 +576,8 @@ bool CodeBlock::finishCreation(VM& vm, ScriptExecutable* ownerExecutable, Unlink > case op_get_from_arguments: > case op_to_number: > case op_to_object: >- case op_get_argument: { >+ case op_get_argument: >+ case op_get_private_field: { > linkValueProfile(i, opLength); > break; > } >@@ -1148,6 +1149,16 @@ void CodeBlock::propagateTransitions(const ConcurrentJSLocker&, SlotVisitor& vis > visitor.appendUnbarriered(newStructure); > break; > } >+ case op_add_private_field: { >+ StructureID newStructureID = instruction[7].u.structureID; >+ if (!newStructureID) >+ break; >+ Structure* newStructure = vm.heap.structureIDTable().get(newStructureID); >+ >+ // Cached private field transitions need to have the lifetime of the constructor. >+ visitor.appendUnbarriered(newStructure); >+ break; >+ } > default: > break; > } >@@ -1350,6 +1361,28 @@ void CodeBlock::finalizeLLIntInlineCaches() > structure.clear(); > break; > } >+ case op_add_private_field: { >+ StructureID oldStructureID = curInstruction[5].u.structureID; >+ if (!oldStructureID || Heap::isMarked(vm.heap.structureIDTable().get(oldStructureID))) >+ break; >+ if (Options::verboseOSR()) >+ dataLogF("Clearing LLInt property access (private field).\n"); >+ curInstruction[5].u.pointer = nullptr; >+ curInstruction[6].u.pointer = nullptr; >+ curInstruction[7].u.pointer = nullptr; >+ break; >+ } >+ case op_get_private_field: >+ case op_put_private_field: { >+ StructureID structureID = curInstruction[5].u.structureID; >+ if (!structureID || Heap::isMarked(vm.heap.structureIDTable().get(structureID))) >+ break; >+ if (Options::verboseOSR()) >+ dataLogF("Clearing LLInt property access (private field).\n"); >+ curInstruction[5].u.pointer = nullptr; >+ curInstruction[6].u.pointer = nullptr; >+ break; >+ } > default: > OpcodeID opcodeID = Interpreter::getOpcodeID(curInstruction[0]); > ASSERT_WITH_MESSAGE_UNUSED(opcodeID, false, "Unhandled opcode in CodeBlock::finalizeUnconditionally, %s(%d) at bc %u", opcodeNames[opcodeID], opcodeID, propertyAccessInstructions[i]); >diff --git a/Source/JavaScriptCore/bytecode/ExecutableInfo.h b/Source/JavaScriptCore/bytecode/ExecutableInfo.h >index 750900ecda4..41be844c7a5 100644 >--- a/Source/JavaScriptCore/bytecode/ExecutableInfo.h >+++ b/Source/JavaScriptCore/bytecode/ExecutableInfo.h >@@ -30,7 +30,7 @@ > namespace JSC { > > enum class DerivedContextType : uint8_t { None, DerivedConstructorContext, DerivedMethodContext }; >-enum class EvalContextType : uint8_t { None, FunctionEvalContext }; >+enum class EvalContextType : uint8_t { None, FunctionEvalContext, InstanceFieldEvalContext }; > > // FIXME: These flags, ParserModes and propagation to XXXCodeBlocks should be reorganized. > // https://bugs.webkit.org/show_bug.cgi?id=151547 >diff --git a/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp b/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp >index e540a240d8f..92c69abc507 100644 >--- a/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp >+++ b/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp >@@ -42,7 +42,8 @@ > > namespace JSC { > >-static_assert(sizeof(UnlinkedFunctionExecutable) <= 160, "UnlinkedFunctionExecutable should fit in a 160-byte cell. If you increase the size of this class, consider making a size class that perfectly fits it."); >+// FIXME: figure out something other than simply bumping the size limit. >+static_assert(sizeof(UnlinkedFunctionExecutable) <= 176, "UnlinkedFunctionExecutable should fit in a 160-byte cell. If you increase the size of this class, consider making a size class that perfectly fits it."); > > const ClassInfo UnlinkedFunctionExecutable::s_info = { "UnlinkedFunctionExecutable", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(UnlinkedFunctionExecutable) }; > >@@ -56,7 +57,7 @@ static UnlinkedFunctionCodeBlock* generateUnlinkedFunctionCodeBlock( > JSParserScriptMode scriptMode = executable->scriptMode(); > ASSERT(isFunctionParseMode(executable->parseMode())); > std::unique_ptr<FunctionNode> function = parse<FunctionNode>( >- &vm, source, executable->name(), builtinMode, strictMode, scriptMode, executable->parseMode(), executable->superBinding(), error, nullptr); >+ &vm, source, executable->name(), builtinMode, strictMode, scriptMode, executable->parseMode(), executable->superBinding(), error, nullptr, ConstructorKind::None, DerivedContextType::None, EvalContextType::None, nullptr, executable->instanceFieldLocations()); > > if (!function) { > ASSERT(error.isValid()); >diff --git a/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.h b/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.h >index f9e89399931..320ee8aa96a 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" >@@ -145,6 +146,9 @@ public: > void setSourceURLDirective(const String& sourceURL) { m_sourceURLDirective = sourceURL; } > void setSourceMappingURLDirective(const String& sourceMappingURL) { m_sourceMappingURLDirective = sourceMappingURL; } > >+ const Vector<JSTextPosition>& instanceFieldLocations() const { return m_instanceFieldLocations; } >+ void setInstanceFieldLocations(Vector<JSTextPosition>&& instanceFieldLocations) { m_instanceFieldLocations = instanceFieldLocations; } >+ > private: > UnlinkedFunctionExecutable(VM*, Structure*, const SourceCode&, FunctionMetadataNode*, UnlinkedFunctionKind, ConstructAbility, JSParserScriptMode, VariableEnvironment&, JSC::DerivedContextType, bool isBuiltinDefaultClassConstructor); > >@@ -185,6 +189,9 @@ private: > > CompactVariableMap::Handle m_parentScopeTDZVariables; > >+ // TODO: only allocate this if needed. >+ Vector<JSTextPosition> m_instanceFieldLocations; >+ > protected: > static void visitChildren(JSCell*, SlotVisitor&); > >diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp >index 00afc9f96c2..103b4e69444 100644 >--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp >+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp >@@ -309,7 +309,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, Unlinke > bool shouldCaptureSomeOfTheThings = m_shouldEmitDebugHooks || functionNode->needsActivation() || containsArrowOrEvalButNotInArrowBlock; > > bool shouldCaptureAllOfTheThings = m_shouldEmitDebugHooks || codeBlock->usesEval(); >- bool needsArguments = ((functionNode->usesArguments() && !codeBlock->isArrowFunction()) || codeBlock->usesEval() || (functionNode->usesArrowFunction() && !codeBlock->isArrowFunction() && isArgumentsUsedInInnerArrowFunction())); >+ bool needsArguments = ((functionNode->usesArguments() && !codeBlock->isArrowFunction()) || codeBlock->usesEval() || (functionNode->usesArrowFunction() && !codeBlock->isArrowFunction() && isArgumentsUsedInInnerArrowFunction())) && parseMode != SourceParseMode::InstanceFieldInitializerMode; > > if (isGeneratorOrAsyncFunctionBodyParseMode(parseMode)) { > // Generator and AsyncFunction never provides "arguments". "arguments" reference will be resolved in an upper generator function scope. >@@ -552,7 +552,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) { >@@ -647,6 +647,9 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, Unlinke > moveEmptyValue(&m_thisRegister); > } else > emitCreateThis(&m_thisRegister); >+ >+ if (constructorKind() == ConstructorKind::Base) >+ emitInstanceFieldInitializationIfNeeded(&m_thisRegister, &m_calleeRegister, m_scopeNode->position(), m_scopeNode->position(), m_scopeNode->position()); > } else if (constructorKind() != ConstructorKind::None) > emitThrowTypeError("Cannot call a class constructor without |new|"); > else { >@@ -2021,6 +2024,14 @@ 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()); >+ } >+ } > } > return hasCapturedVariables; > } >@@ -2996,6 +3007,46 @@ RegisterID* BytecodeGenerator::emitDeleteByVal(RegisterID* dst, RegisterID* base > return dst; > } > >+void BytecodeGenerator::emitPrivateFieldAdd(RegisterID* base, const Identifier& name, RegisterID* value) >+{ >+ m_codeBlock->addPropertyAccessInstruction(instructions().size()); >+ emitOpcode(op_add_private_field); >+ instructions().append(base->index()); >+ instructions().append(addConstant(name)); >+ instructions().append(value->index()); >+ instructions().append(scopeRegister()->index()); >+ instructions().append(0); // structure >+ instructions().append(0); // offset >+ instructions().append(0); // new structure >+} >+ >+RegisterID* BytecodeGenerator::emitPrivateFieldGet(RegisterID* dst, RegisterID* base, const Identifier& name) >+{ >+ m_codeBlock->addPropertyAccessInstruction(instructions().size()); >+ UnlinkedValueProfile profile = emitProfiledOpcode(op_get_private_field); >+ instructions().append(kill(dst)); >+ instructions().append(base->index()); >+ instructions().append(addConstant(name)); >+ instructions().append(scopeRegister()->index()); >+ instructions().append(0); // structure >+ instructions().append(0); // offset >+ instructions().append(profile); >+ return dst; >+} >+ >+RegisterID* BytecodeGenerator::emitPrivateFieldSet(RegisterID* base, const Identifier& name, RegisterID* value) >+{ >+ m_codeBlock->addPropertyAccessInstruction(instructions().size()); >+ emitOpcode(op_put_private_field); >+ instructions().append(base->index()); >+ instructions().append(addConstant(name)); >+ instructions().append(value->index()); >+ instructions().append(scopeRegister()->index()); >+ instructions().append(0); // structure >+ instructions().append(0); // offset >+ return value; >+} >+ > void BytecodeGenerator::emitSuperSamplerBegin() > { > emitOpcode(op_super_sampler_begin); >@@ -3043,6 +3094,20 @@ RegisterID* BytecodeGenerator::emitCreateThis(RegisterID* dst) > return dst; > } > >+RegisterID* BytecodeGenerator::emitInstanceFieldInitializationIfNeeded(RegisterID* dst, RegisterID* constructor, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) >+{ >+ Ref<Label> done = newLabel(); >+ RefPtr<RegisterID> initializer = emitDirectGetById(newTemporary(), constructor, propertyNames().builtinNames().instanceFieldInitializerPrivateName()); >+ emitJumpIfTrue(emitIsUndefined(newTemporary(), initializer.get()), done.get()); >+ >+ CallArguments args(*this, nullptr); >+ emitMove(args.thisRegister(), dst); >+ emitCall(newTemporary(), initializer.get(), NoExpectedFunction, args, divot, divotStart, divotEnd, DebuggableCall::No); >+ >+ emitLabel(done.get()); >+ return dst; >+} >+ > void BytecodeGenerator::emitTDZCheck(RegisterID* target) > { > emitOpcode(op_check_tdz); >@@ -3352,6 +3417,40 @@ RegisterID* BytecodeGenerator::emitNewDefaultConstructor(RegisterID* dst, Constr > return dst; > } > >+RegisterID* BytecodeGenerator::emitNewInstanceFieldInitializerFunction(RegisterID* dst, Vector<JSTextPosition>&& instanceFieldLocations, bool isDerived) >+{ >+ DerivedContextType newDerivedContextType; >+ SuperBinding superBinding; >+ if (!isDerived) { >+ newDerivedContextType = DerivedContextType::None; >+ superBinding = SuperBinding::NotNeeded; >+ } else { >+ newDerivedContextType = DerivedContextType::DerivedMethodContext; >+ superBinding = SuperBinding::Needed; >+ } >+ >+ VariableEnvironment variablesUnderTDZ; >+ getVariablesUnderTDZ(variablesUnderTDZ); >+ >+ SourceParseMode parseMode = SourceParseMode::InstanceFieldInitializerMode; >+ ConstructAbility constructAbility = ConstructAbility::CannotConstruct; >+ >+ const bool alwaysStrictInClass = true; >+ FunctionMetadataNode metadata(parserArena(), JSTokenLocation(), JSTokenLocation(), 0, 0, 0, 0, 0, alwaysStrictInClass, ConstructorKind::None, superBinding, 0, parseMode, false); >+ metadata.finishParsing(m_scopeNode->source(), Identifier(), FunctionMode::MethodDefinition); >+ auto initializer = UnlinkedFunctionExecutable::create(m_vm, m_scopeNode->source(), &metadata, isBuiltinFunction() ? UnlinkedBuiltinFunction : UnlinkedNormalFunction, constructAbility, scriptMode(), variablesUnderTDZ, newDerivedContextType); >+ initializer->setInstanceFieldLocations(WTFMove(instanceFieldLocations)); >+ >+ unsigned index = m_codeBlock->addFunctionExpr(initializer); >+ OpcodeID opcodeID = op_new_func_exp; >+ >+ emitOpcode(opcodeID); >+ instructions().append(dst->index()); >+ instructions().append(scopeRegister()->index()); >+ instructions().append(index); >+ return dst; >+} >+ > RegisterID* BytecodeGenerator::emitNewFunction(RegisterID* dst, FunctionMetadataNode* function) > { > unsigned index = m_codeBlock->addFunctionDecl(makeFunction(function)); >@@ -3370,28 +3469,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; > >- // FIXME: We should use an op_call to an internal function here instead. >- // https://bugs.webkit.org/show_bug.cgi?id=155547 >+ return true; >+} >+ >+void BytecodeGenerator::emitSetFunctionName(RegisterID* value, RegisterID* name) >+{ > emitOpcode(op_set_function_name); > instructions().append(value->index()); > instructions().append(name->index()); > } > >+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(op_call, dst, func, expectedFunction, callArguments, divot, divotStart, divotEnd, debuggableCall); >@@ -3793,6 +3910,15 @@ void BytecodeGenerator::emitToPrimitive(RegisterID* dst, RegisterID* src) > instructions().append(src->index()); > } > >+RegisterID* BytecodeGenerator::emitToPropertyKey(RegisterID* dst, RegisterID* src) >+{ >+ dst = tempDestination(dst); >+ emitOpcode(op_to_property_key); >+ instructions().append(dst->index()); >+ instructions().append(src->index()); >+ return dst; >+} >+ > void BytecodeGenerator::emitGetScope() > { > emitOpcode(op_get_scope); >@@ -4663,6 +4789,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()) { >diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h >index 8ac6bc1e88e..80436605686 100644 >--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h >+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h >@@ -532,14 +532,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) >@@ -662,6 +662,7 @@ namespace JSC { > RegisterID* emitUnaryNoDstOp(OpcodeID, RegisterID* src); > > 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); >@@ -676,10 +677,14 @@ namespace JSC { > RegisterID* emitNewFunction(RegisterID* dst, FunctionMetadataNode*); > RegisterID* emitNewFunctionExpression(RegisterID* dst, FuncExprNode*); > RegisterID* emitNewDefaultConstructor(RegisterID* dst, ConstructorKind, const Identifier& name, const Identifier& ecmaName, const SourceCode& classSource); >+ RegisterID* emitNewInstanceFieldInitializerFunction(RegisterID* dst, Vector<JSTextPosition>&& instanceFieldLocations, bool isDerived); > RegisterID* emitNewArrowFunctionExpression(RegisterID*, ArrowFuncExprNode*); > RegisterID* emitNewMethodDefinition(RegisterID* dst, MethodDefinitionNode*); > RegisterID* emitNewRegExp(RegisterID* dst, RegExp*); > >+ bool shouldEmitSetFunctionName(ExpressionNode* valueNode); >+ void emitSetFunctionName(RegisterID* value, RegisterID* name); >+ void emitSetFunctionName(RegisterID* value, const Identifier& name); > void emitSetFunctionNameIfNeeded(ExpressionNode* valueNode, RegisterID* value, RegisterID* name); > > RegisterID* moveLinkTimeConstant(RegisterID* dst, LinkTimeConstant); >@@ -713,6 +718,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(); > >@@ -760,6 +769,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&); >@@ -868,6 +878,7 @@ namespace JSC { > void emitPutNewTargetToArrowFunctionContextScope(); > void emitPutDerivedConstructorToArrowFunctionContextScope(); > RegisterID* emitLoadDerivedConstructorFromArrowFunctionLexicalEnvironment(); >+ RegisterID* emitLoadDerivedConstructor(); > > void emitDebugHook(DebugHookType, const JSTextPosition&); > void emitDebugHook(DebugHookType, unsigned line, unsigned charOffset, unsigned lineStart); >diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp >index 366964f1d63..63b9620c0b8 100644 >--- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp >+++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp >@@ -534,13 +534,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); > } > >@@ -588,6 +598,16 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe > PropertyNode* node = p->m_node; > dst = node->isInstanceClassProperty() ? prototype : dstOrConstructor; > >+ if (p->isComputedClassField()) >+ emitSaveComputedFieldName(generator, *p->m_node); >+ >+ if (p->isInstanceClassField()) { >+ ASSERT(instanceFieldLocations); >+ instanceFieldLocations->append(p->position()); >+ continue; >+ } >+ >+ > // Handle regular values. > if (node->m_type & PropertyNode::Constant) { > emitPutConstantProperty(generator, dst, *node); >@@ -709,6 +729,25 @@ void PropertyListNode::emitPutConstantProperty(BytecodeGenerator& generator, Reg > generator.emitDirectPutByVal(newObj, propertyName.get(), value.get()); > } > >+void PropertyListNode::emitSaveComputedFieldName(BytecodeGenerator& generator, PropertyNode& node) >+{ >+ ASSERT(node.isComputedClassField()); >+ RefPtr<RegisterID> propertyExpr; >+ const Identifier& description = *node.name(); >+ auto length = node.isPrivate() ? description.length() : 1; >+ Variable var = generator.variable(description); >+ ASSERT(!var.local()); >+ >+ propertyExpr = generator.emitNode(node.m_expression); >+ RegisterID* propertyName = generator.emitToPropertyKey(propertyExpr.get(), propertyExpr.get()); >+ >+ // TODO: add type profiling if we need it? >+ RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var); >+ generator.emitPutToScope(scope.get(), var, propertyName, ThrowIfNotFound, InitializationMode::ConstInitialization); >+ generator.emitProfileType(propertyName, var, position(), JSTextPosition(-1, position().offset + length, -1)); >+ generator.liftTDZCheckIfPossible(var); >+} >+ > // ------------------------------ BracketAccessorNode -------------------------------- > > static bool isNonIndexStringElement(ExpressionNode& element) >@@ -763,16 +802,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) >@@ -892,7 +967,11 @@ RegisterID* FunctionCallValueNode::emitBytecode(BytecodeGenerator& generator, Re > > if (generator.isDerivedConstructorContext() || doWeUseArrowFunctionInConstructor) > generator.emitPutThisToArrowFunctionContextScope(); >- >+ >+ // Initialize instance fields after super-call. >+ func = generator.emitLoadDerivedConstructor(); >+ generator.emitInstanceFieldInitializationIfNeeded(generator.thisRegister(), func.get(), divot(), divotStart(), divotEnd()); >+ > return ret; > } > RefPtr<RegisterID> func = generator.emitNode(m_expr); >@@ -2554,11 +2633,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 +2645,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; > } >@@ -3972,6 +4039,51 @@ RegisterID* AwaitExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID > return generator.move(generator.finalDestination(dst), value.get()); > } > >+// ------------------------------ DefineFieldNode --------------------------------- >+ >+void DefineFieldNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) >+{ >+ RefPtr<RegisterID> value = generator.newTemporary(); >+ >+ if (!m_assign) >+ generator.emitLoad(value.get(), jsUndefined()); >+ else { >+ generator.emitNode(value.get(), m_assign); >+ if (m_ident && generator.shouldEmitSetFunctionName(m_assign)) >+ generator.emitSetFunctionName(value.get(), *m_ident); >+ } >+ >+ switch (m_type) { >+ case DefineFieldNode::Name: { >+ std::optional<uint32_t> optionalIndex = parseIndex(*m_ident); >+ if (!optionalIndex) >+ generator.emitDirectPutById(generator.thisRegister(), *m_ident, value.get(), PropertyNode::Unknown); >+ else { >+ RefPtr<RegisterID> propertyIndex = generator.emitLoad(nullptr, jsNumber(optionalIndex.value())); >+ generator.emitDirectPutByVal(generator.thisRegister(), propertyIndex.get(), value.get()); >+ } >+ break; >+ } >+ case DefineFieldNode::PrivateName: { >+ generator.emitExpressionInfo(position(), position(), position() + m_ident->length()); >+ generator.emitPrivateFieldAdd(generator.thisRegister(), *m_ident, value.get()); >+ break; >+ } >+ case DefineFieldNode::ComputedName: { >+ Variable var = generator.variable(*m_ident); >+ ASSERT_WITH_MESSAGE(!var.local(), "Computed names must be stored in captured variables"); >+ >+ generator.emitExpressionInfo(position(), position(), position() + 1); >+ RefPtr<RegisterID> scope = generator.emitResolveScope(generator.newTemporary(), var); >+ RefPtr<RegisterID> privateName = generator.newTemporary(); >+ generator.emitGetFromScope(privateName.get(), scope.get(), var, ThrowIfNotFound); >+ generator.emitProfileType(privateName.get(), var, m_position, JSTextPosition(-1, m_position.offset + m_ident->length(), -1)); >+ generator.emitDirectPutByVal(generator.thisRegister(), privateName.get(), value.get()); >+ break; >+ } >+ } >+} >+ > // ------------------------------ ClassDeclNode --------------------------------- > > void ClassDeclNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) >@@ -3983,7 +4095,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; >@@ -4049,14 +4161,26 @@ RegisterID* ClassExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID > RefPtr<RegisterID> prototypeNameRegister = generator.emitLoad(nullptr, propertyNames.prototype); > generator.emitCallDefineProperty(constructor.get(), prototypeNameRegister.get(), prototype.get(), nullptr, nullptr, 0, m_position); > >- if (m_classElements) >- generator.emitDefineClassElements(m_classElements, constructor.get(), prototype.get()); >+ if (m_classElements) { >+ Vector<JSTextPosition> instanceFieldLocations; >+ generator.emitDefineClassElements(m_classElements, constructor.get(), prototype.get(), instanceFieldLocations); >+ if (!instanceFieldLocations.isEmpty()) { >+ RefPtr<RegisterID> instanceFieldInitializer = generator.emitNewInstanceFieldInitializerFunction(generator.newTemporary(), WTFMove(instanceFieldLocations), m_classHeritage); > >- if (!m_name.isNull()) { >- Variable classNameVar = generator.variable(m_name); >- RELEASE_ASSERT(classNameVar.isResolved()); >- RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, classNameVar); >- generator.emitPutToScope(scope.get(), classNameVar, constructor.get(), ThrowIfNotFound, InitializationMode::Initialization); >+ // FIXME: Skip this if the initializer function isn't going to need a home object (no eval or super properties) >+ emitPutHomeObject(generator, instanceFieldInitializer.get(), prototype.get()); >+ >+ generator.emitDirectPutById(constructor.get(), generator.propertyNames().builtinNames().instanceFieldInitializerPrivateName(), instanceFieldInitializer.get(), PropertyNode::Unknown); >+ } >+ } >+ >+ if (m_needsLexicalScope) { >+ if (!m_name.isNull()) { >+ Variable classNameVar = generator.variable(m_name); >+ RELEASE_ASSERT(classNameVar.isResolved()); >+ RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, classNameVar); >+ generator.emitPutToScope(scope.get(), classNameVar, constructor.get(), ThrowIfNotFound, InitializationMode::Initialization); >+ } > generator.popLexicalScope(this); > } > >diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >index 497d772a3c7..1781ce1c2fe 100644 >--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >@@ -2305,6 +2305,30 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi > break; > } > >+ case ToPropertyKey: { >+ JSValue childConst = forNode(node->child1()).value(); >+ if (childConst && (childConst.isString() || childConst.isSymbol())) { >+ didFoldClobberWorld(); >+ setConstant(node, childConst); >+ break; >+ } >+ >+ auto speculatedType = forNode(node->child1()).m_type; >+ if (!(speculatedType & ~(SpecFullNumber | SpecBoolean | SpecString | SpecSymbol | SpecBigInt))) { >+ didFoldClobberWorld(); >+ if (!(speculatedType & ~(SpecString | SpecSymbol))) { >+ m_state.setFoundConstants(true); >+ setForNode(node, forNode(node->child1())); >+ } else >+ setTypeForNode(node, SpecString | SpecSymbol); >+ } >+ >+ clobberWorld(); >+ >+ setTypeForNode(node, SpecString | SpecSymbol); >+ break; >+ } >+ > case ToNumber: { > JSValue childConst = forNode(node->child1()).value(); > if (childConst && childConst.isNumber()) { >diff --git a/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp >index 058aacf8b2a..319be337817 100644 >--- a/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp >+++ b/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp >@@ -401,6 +401,11 @@ private: > break; > } > >+ case ToPropertyKey: { >+ node->child1()->mergeFlags(NodeBytecodeUsesAsValue | NodeBytecodeUsesAsArrayIndex); >+ break; >+ } >+ > case PutByValDirect: > case PutByVal: { > m_graph.varArgChild(node, 0)->mergeFlags(NodeBytecodeUsesAsValue); >diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >index ca865ed2f70..236cd74016e 100644 >--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >@@ -5112,7 +5112,13 @@ void ByteCodeParser::parseBlock(unsigned limit) > set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(ToPrimitive, value)); > NEXT_OPCODE(op_to_primitive); > } >- >+ >+ case op_to_property_key: { >+ Node* value = get(VirtualRegister(currentInstruction[2].u.operand)); >+ set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(ToPropertyKey, value)); >+ NEXT_OPCODE(op_to_property_key); >+ } >+ > case op_strcat: { > int startOperand = currentInstruction[2].u.operand; > int numOperands = currentInstruction[3].u.operand; >diff --git a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp >index dadc92d867e..a0f2ff3a195 100644 >--- a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp >+++ b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp >@@ -214,6 +214,7 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, Instruc > case op_spread: > case op_strcat: > case op_to_primitive: >+ case op_to_property_key: > case op_throw: > case op_throw_static_error: > case op_call: >@@ -280,6 +281,9 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, Instruc > return CanCompile; > > case op_yield: >+ case op_add_private_field: // TODO: add JIT support for private field ops >+ case op_get_private_field: >+ case op_put_private_field: > case llint_program_prologue: > case llint_eval_prologue: > case llint_module_program_prologue: >diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h >index b02f2c05c2c..13e4790c04d 100644 >--- a/Source/JavaScriptCore/dfg/DFGClobberize.h >+++ b/Source/JavaScriptCore/dfg/DFGClobberize.h >@@ -632,6 +632,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu > case ConstructVarargs: > case ConstructForwardVarargs: > case ToPrimitive: >+ case ToPropertyKey: > case InByVal: > case InById: > case HasOwnProperty: >diff --git a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp >index fb1f6dc231a..d0b9cb990e5 100644 >--- a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp >+++ b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp >@@ -693,6 +693,15 @@ private: > break; > } > >+ case ToPropertyKey: { >+ if (m_state.forNode(node->child1()).m_type & ~(SpecString | SpecSymbol)) >+ break; >+ >+ node->convertToIdentity(); >+ changed = true; >+ break; >+ } >+ > case ToThis: { > ToThisResult result = isToThisAnIdentity(m_graph.m_vm, m_graph.executableFor(node->origin.semantic)->isStrictMode(), m_state.forNode(node->child1())); > if (result == ToThisResult::Identity) { >diff --git a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp >index 2b0d4df94fc..bf4b0eb1403 100644 >--- a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp >+++ b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp >@@ -189,6 +189,7 @@ bool doesGC(Graph& graph, Node* node) > case TypeOf: > case LogicalNot: > case ToPrimitive: >+ case ToPropertyKey: > case ToNumber: > case ToString: > case CallStringConstructor: >diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >index c44db1a49d2..4464a4023f6 100644 >--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >@@ -1144,6 +1144,11 @@ private: > break; > } > >+ case ToPropertyKey: { >+ fixupToPropertyKey(node); >+ break; >+ } >+ > case ToNumber: { > fixupToNumber(node); > break; >@@ -2600,6 +2605,21 @@ private: > } > } > >+ void fixupToPropertyKey(Node* node) >+ { >+ if (node->child1()->shouldSpeculateSymbol()) { >+ fixEdge<SymbolUse>(node->child1()); >+ node->convertToIdentity(); >+ return; >+ } >+ >+ if (node->child1()->shouldSpeculateString()) { >+ fixEdge<StringUse>(node->child1()); >+ node->convertToIdentity(); >+ return; >+ } >+ } >+ > void fixupToNumber(Node* node) > { > // If the prediction of the child is Number, we attempt to convert ToNumber to Identity. >diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h >index af3db342c7a..a19c69809ef 100644 >--- a/Source/JavaScriptCore/dfg/DFGNodeType.h >+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h >@@ -366,6 +366,7 @@ namespace JSC { namespace DFG { > macro(TypeOf, NodeResultJS) \ > macro(LogicalNot, NodeResultBoolean) \ > macro(ToPrimitive, NodeResultJS | NodeMustGenerate) \ >+ macro(ToPropertyKey, NodeResultJS | NodeMustGenerate) \ > macro(ToString, NodeResultJS | NodeMustGenerate) \ > macro(ToNumber, NodeResultJS | NodeMustGenerate) \ > macro(ToObject, NodeResultJS | NodeMustGenerate) \ >diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp >index 6a4c0c523a1..9ccb45d98d0 100644 >--- a/Source/JavaScriptCore/dfg/DFGOperations.cpp >+++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp >@@ -1304,6 +1304,14 @@ EncodedJSValue JIT_OPERATION operationToPrimitive(ExecState* exec, EncodedJSValu > return JSValue::encode(JSValue::decode(value).toPrimitive(exec)); > } > >+EncodedJSValue JIT_OPERATION operationToPropertyKey(ExecState* exec, EncodedJSValue value) >+{ >+ VM* vm = &exec->vm(); >+ NativeCallFrameTracer tracer(vm, exec); >+ >+ return JSValue::encode(JSValue::decode(value).toPropertyKey(exec, JSValue::Tag)); >+} >+ > EncodedJSValue JIT_OPERATION operationToNumber(ExecState* exec, EncodedJSValue value) > { > VM* vm = &exec->vm(); >diff --git a/Source/JavaScriptCore/dfg/DFGOperations.h b/Source/JavaScriptCore/dfg/DFGOperations.h >index 35bf6bceadb..ee86bf80463 100644 >--- a/Source/JavaScriptCore/dfg/DFGOperations.h >+++ b/Source/JavaScriptCore/dfg/DFGOperations.h >@@ -77,6 +77,7 @@ EncodedJSValue JIT_OPERATION operationGetByValStringInt(ExecState*, JSString*, i > EncodedJSValue JIT_OPERATION operationGetByValObjectString(ExecState*, JSCell*, JSCell* string) WTF_INTERNAL; > EncodedJSValue JIT_OPERATION operationGetByValObjectSymbol(ExecState*, JSCell*, JSCell* symbol) WTF_INTERNAL; > EncodedJSValue JIT_OPERATION operationToPrimitive(ExecState*, EncodedJSValue) WTF_INTERNAL; >+EncodedJSValue JIT_OPERATION operationToPropertyKey(ExecState*, EncodedJSValue) WTF_INTERNAL; > EncodedJSValue JIT_OPERATION operationToNumber(ExecState*, EncodedJSValue) WTF_INTERNAL; > EncodedJSValue JIT_OPERATION operationGetByValWithThis(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue) WTF_INTERNAL; > EncodedJSValue JIT_OPERATION operationGetPrototypeOf(ExecState*, EncodedJSValue) WTF_INTERNAL; >diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >index a8b92cb83aa..d962e7aa845 100644 >--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >@@ -474,6 +474,13 @@ private: > break; > } > >+ case ToPropertyKey: { >+ SpeculatedType child = node->child1()->prediction(); >+ if (child) >+ changed |= mergePrediction(resultOfToPropertyKey(child)); >+ break; >+ } >+ > case NormalizeMapKey: { > SpeculatedType prediction = node->child1()->prediction(); > if (prediction) >@@ -1068,7 +1075,8 @@ private: > case ArithAbs: > case GetByVal: > case ToThis: >- case ToPrimitive: >+ case ToPrimitive: >+ case ToPropertyKey: > case NormalizeMapKey: > case AtomicsAdd: > case AtomicsAnd: >@@ -1272,6 +1280,14 @@ private: > return type; > } > >+ SpeculatedType resultOfToPropertyKey(SpeculatedType type) >+ { >+ if (type & ~(SpecString | SpecSymbol)) >+ return SpecString | SpecSymbol; >+ >+ return type; >+ } >+ > Vector<Node*> m_dependentNodes; > Node* m_currentNode; > bool m_changed { false }; >diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h >index 4bfb61594b8..d2d01e02705 100644 >--- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h >+++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h >@@ -330,6 +330,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno > case LogicalNot: > case CallObjectConstructor: > case ToPrimitive: >+ case ToPropertyKey: > case ToString: > case ToNumber: > case ToObject: >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >index 35df5a2a456..037d6146fc9 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >@@ -12347,6 +12347,27 @@ void SpeculativeJIT::compileToPrimitive(Node* node) > jsValueResult(resultRegs, node, DataFormatJS, UseChildrenCalledExplicitly); > } > >+void SpeculativeJIT::compileToPropertyKey(Node* node) >+{ >+ DFG_ASSERT(m_jit.graph(), node, node->child1().useKind() == UntypedUse, node->child1().useKind()); >+ JSValueOperand argument(this, node->child1()); >+ JSValueRegsTemporary result(this, Reuse, argument); >+ >+ JSValueRegs argumentRegs = argument.jsValueRegs(); >+ JSValueRegs resultRegs = result.regs(); >+ >+ argument.use(); >+ >+ JITCompiler::JumpList slowCases; >+ slowCases.append(m_jit.branchIfNotCell(argumentRegs)); >+ slowCases.append(m_jit.branchIfNotPropertyKey(argumentRegs.payloadGPR())); >+ m_jit.moveValueRegs(argumentRegs, resultRegs); >+ >+ addSlowPathGenerator(slowPathCall(slowCases, this, operationToPropertyKey, resultRegs, argumentRegs)); >+ >+ jsValueResult(resultRegs, node, DataFormatJS, UseChildrenCalledExplicitly); >+} >+ > void SpeculativeJIT::compileLogShadowChickenPrologue(Node* node) > { > flushRegisters(); >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >index 7fefd12afad..48994fd3f44 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >@@ -1476,6 +1476,7 @@ public: > void compileCreateThis(Node*); > void compileNewObject(Node*); > void compileToPrimitive(Node*); >+ void compileToPropertyKey(Node*); > void compileLogShadowChickenPrologue(Node*); > void compileLogShadowChickenTail(Node*); > void compileHasIndexedProperty(Node*); >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >index 59ce3a1fad8..aab16c2a1b0 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >@@ -3034,6 +3034,11 @@ void SpeculativeJIT::compile(Node* node) > break; > } > >+ case ToPropertyKey: { >+ compileToPropertyKey(node); >+ break; >+ } >+ > case ToNumber: { > JSValueOperand argument(this, node->child1()); > GPRTemporary resultTag(this, Reuse, argument, TagWord); >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >index 025b41bfb4d..600d7823ccc 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >@@ -3291,6 +3291,11 @@ void SpeculativeJIT::compile(Node* node) > break; > } > >+ case ToPropertyKey: { >+ compileToPropertyKey(node); >+ break; >+ } >+ > case ToNumber: { > JSValueOperand argument(this, node->child1()); > GPRTemporary result(this, Reuse, argument); >diff --git a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp >index f1822a47426..12930909d2d 100644 >--- a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp >+++ b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp >@@ -205,6 +205,7 @@ inline CapabilityLevel canCompile(Node* node) > case MultiGetByOffset: > case MultiPutByOffset: > case ToPrimitive: >+ case ToPropertyKey: > case Throw: > case ThrowStaticError: > case Unreachable: >diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >index 09a73deaddb..97e79a49507 100644 >--- a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >+++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >@@ -896,6 +896,9 @@ private: > case ToPrimitive: > compileToPrimitive(); > break; >+ case ToPropertyKey: >+ compileToPropertyKey(); >+ break; > case MakeRope: > compileMakeRope(); > break; >@@ -6254,6 +6257,33 @@ private: > m_out.appendTo(continuation, lastNext); > setJSValue(m_out.phi(Int64, results)); > } >+ >+ void compileToPropertyKey() >+ { >+ LValue value = lowJSValue(m_node->child1()); >+ >+ LBasicBlock isCellCase = m_out.newBlock(); >+ LBasicBlock slowCase = m_out.newBlock(); >+ LBasicBlock continuation = m_out.newBlock(); >+ >+ Vector<ValueFromBlock, 2> results; >+ >+ m_out.branch(isCell(value, provenType(m_node->child1())), unsure(isCellCase), unsure(slowCase)); >+ >+ LBasicBlock lastNext = m_out.appendTo(isCellCase, continuation); >+ results.append(m_out.anchor(value)); >+ m_out.branch( >+ isPropertyKey(value, provenType(m_node->child1())), >+ unsure(continuation), unsure(slowCase)); >+ >+ m_out.appendTo(slowCase, continuation); >+ results.append(m_out.anchor(vmCall( >+ Int64, m_out.operation(operationToPropertyKey), m_callFrame, value))); >+ m_out.jump(continuation); >+ >+ m_out.appendTo(continuation, lastNext); >+ setJSValue(m_out.phi(Int64, results)); >+ } > > void compileMakeRope() > { >@@ -15490,6 +15520,16 @@ private: > m_out.constInt32(vm().symbolStructure->id())); > } > >+ LValue isPropertyKey(LValue cell, SpeculatedType type = SpecFullTop) >+ { >+ if (LValue proven = isProvenValue(type, SpecSymbol | SpecString)) >+ return proven; >+ ASSERT(SymbolType == 2 && StringType == 1); >+ return m_out.lessThanOrEqual( >+ m_out.load8ZeroExt32(cell, m_heaps.JSCell_typeInfoType), >+ m_out.constInt32(SymbolType)); >+ } >+ > LValue isNotBigInt(LValue cell, SpeculatedType type = SpecFullTop) > { > if (LValue proven = isProvenValue(type & SpecCell, ~SpecBigInt)) >diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp >index 7126d029f62..274a8de8855 100644 >--- a/Source/JavaScriptCore/interpreter/Interpreter.cpp >+++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp >@@ -131,7 +131,9 @@ JSValue eval(CallFrame* callFrame) > } > > EvalContextType evalContextType; >- if (isFunctionParseMode(callerUnlinkedCodeBlock->parseMode())) >+ if (callerUnlinkedCodeBlock->parseMode() == SourceParseMode::InstanceFieldInitializerMode) >+ evalContextType = EvalContextType::InstanceFieldEvalContext; >+ else if (isFunctionParseMode(callerUnlinkedCodeBlock->parseMode())) > evalContextType = EvalContextType::FunctionEvalContext; > else if (callerUnlinkedCodeBlock->codeType() == EvalCode) > evalContextType = callerUnlinkedCodeBlock->evalContextType(); >diff --git a/Source/JavaScriptCore/jit/AssemblyHelpers.h b/Source/JavaScriptCore/jit/AssemblyHelpers.h >index 01731a5b1b1..39fb5b4cd40 100644 >--- a/Source/JavaScriptCore/jit/AssemblyHelpers.h >+++ b/Source/JavaScriptCore/jit/AssemblyHelpers.h >@@ -935,6 +935,10 @@ public: > Jump branchIfNotString(GPRReg cellGPR) { return branchIfNotType(cellGPR, StringType); } > Jump branchIfSymbol(GPRReg cellGPR) { return branchIfType(cellGPR, SymbolType); } > Jump branchIfNotSymbol(GPRReg cellGPR) { return branchIfNotType(cellGPR, SymbolType); } >+ Jump branchIfNotPropertyKey(GPRReg cellGPR) >+ { >+ return branch8(Above, Address(cellGPR, JSCell::typeInfoTypeOffset()), TrustedImm32(SymbolType)); >+ } > Jump branchIfBigInt(GPRReg cellGPR) { return branchIfType(cellGPR, BigIntType); } > Jump branchIfNotBigInt(GPRReg cellGPR) { return branchIfNotType(cellGPR, BigIntType); } > Jump branchIfFunction(GPRReg cellGPR) { return branchIfType(cellGPR, JSFunctionType); } >diff --git a/Source/JavaScriptCore/jit/JIT.cpp b/Source/JavaScriptCore/jit/JIT.cpp >index bb635db2568..30ba38b6f7f 100644 >--- a/Source/JavaScriptCore/jit/JIT.cpp >+++ b/Source/JavaScriptCore/jit/JIT.cpp >@@ -428,6 +428,7 @@ void JIT::privateCompileMainPass() > DEFINE_OP(op_to_string) > DEFINE_OP(op_to_object) > DEFINE_OP(op_to_primitive) >+ DEFINE_OP(op_to_property_key) > > DEFINE_OP(op_resolve_scope) > DEFINE_OP(op_get_from_scope) >@@ -443,6 +444,11 @@ void JIT::privateCompileMainPass() > > DEFINE_OP(op_log_shadow_chicken_prologue) > DEFINE_OP(op_log_shadow_chicken_tail) >+ >+ DEFINE_OP(op_add_private_field) >+ DEFINE_OP(op_get_private_field) >+ DEFINE_OP(op_put_private_field) >+ > default: > RELEASE_ASSERT_NOT_REACHED(); > } >@@ -568,6 +574,7 @@ void JIT::privateCompileSlowCases() > DEFINE_SLOWCASE_SLOW_OP(create_this) > DEFINE_SLOWCASE_SLOW_OP(to_this) > DEFINE_SLOWCASE_SLOW_OP(to_primitive) >+ DEFINE_SLOWCASE_SLOW_OP(to_property_key) > DEFINE_SLOWCASE_SLOW_OP(to_number) > DEFINE_SLOWCASE_SLOW_OP(to_string) > DEFINE_SLOWCASE_SLOW_OP(to_object) >@@ -578,6 +585,9 @@ void JIT::privateCompileSlowCases() > DEFINE_SLOWCASE_SLOW_OP(has_structure_property) > DEFINE_SLOWCASE_SLOW_OP(resolve_scope) > DEFINE_SLOWCASE_SLOW_OP(check_tdz) >+ DEFINE_SLOWCASE_SLOW_OP(add_private_field) >+ DEFINE_SLOWCASE_SLOW_OP(get_private_field) >+ DEFINE_SLOWCASE_SLOW_OP(put_private_field) > > default: > RELEASE_ASSERT_NOT_REACHED(); >diff --git a/Source/JavaScriptCore/jit/JIT.h b/Source/JavaScriptCore/jit/JIT.h >index 593d5bf38be..cacb61d88d0 100644 >--- a/Source/JavaScriptCore/jit/JIT.h >+++ b/Source/JavaScriptCore/jit/JIT.h >@@ -582,6 +582,7 @@ namespace JSC { > void emit_op_to_string(Instruction*); > void emit_op_to_object(Instruction*); > void emit_op_to_primitive(Instruction*); >+ void emit_op_to_property_key(Instruction*); > void emit_op_unexpected_load(Instruction*); > void emit_op_unsigned(Instruction*); > void emit_op_urshift(Instruction*); >@@ -592,6 +593,9 @@ namespace JSC { > void emit_op_enumerator_generic_pname(Instruction*); > void emit_op_log_shadow_chicken_prologue(Instruction*); > void emit_op_log_shadow_chicken_tail(Instruction*); >+ void emit_op_add_private_field(Instruction*); >+ void emit_op_get_private_field(Instruction*); >+ void emit_op_put_private_field(Instruction*); > > void emitSlow_op_add(Instruction*, Vector<SlowCaseEntry>::iterator&); > void emitSlow_op_call(Instruction*, Vector<SlowCaseEntry>::iterator&); >diff --git a/Source/JavaScriptCore/jit/JITOpcodes.cpp b/Source/JavaScriptCore/jit/JITOpcodes.cpp >index 6c39fce009c..49ed4549dcc 100644 >--- a/Source/JavaScriptCore/jit/JITOpcodes.cpp >+++ b/Source/JavaScriptCore/jit/JITOpcodes.cpp >@@ -325,6 +325,20 @@ void JIT::emit_op_to_primitive(Instruction* currentInstruction) > > } > >+void JIT::emit_op_to_property_key(Instruction* currentInstruction) >+{ >+ int dst = currentInstruction[1].u.operand; >+ int src = currentInstruction[2].u.operand; >+ >+ emitGetVirtualRegister(src, regT0); >+ >+ addSlowCase(branchIfNotCell(regT0)); >+ addSlowCase(branchIfNotPropertyKey(regT0)); >+ >+ if (dst != src) >+ emitPutVirtualRegister(dst); >+} >+ > void JIT::emit_op_set_function_name(Instruction* currentInstruction) > { > emitGetVirtualRegister(currentInstruction[1].u.operand, regT0); >@@ -1464,6 +1478,21 @@ void JIT::emit_op_get_argument(Instruction* currentInstruction) > emitPutVirtualRegister(dst, resultRegs); > } > >+void JIT::emit_op_add_private_field(Instruction*) >+{ >+ addSlowCase(jump()); >+} >+ >+void JIT::emit_op_get_private_field(Instruction*) >+{ >+ addSlowCase(jump()); >+} >+ >+void JIT::emit_op_put_private_field(Instruction*) >+{ >+ addSlowCase(jump()); >+} >+ > } // namespace JSC > > #endif // ENABLE(JIT) >diff --git a/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp b/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp >index 255b057ca43..16f9db0efc5 100644 >--- a/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp >+++ b/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp >@@ -316,6 +316,20 @@ void JIT::emit_op_to_primitive(Instruction* currentInstruction) > emitStore(dst, regT1, regT0); > } > >+void JIT::emit_op_to_property_key(Instruction* currentInstruction) >+{ >+ int dst = currentInstruction[1].u.operand; >+ int src = currentInstruction[2].u.operand; >+ >+ emitLoad(src, regT1, regT0); >+ >+ addSlowCase(branchIfNotCell(regT1)); >+ addSlowCase(branchIfNotPropertyKey(regT0)); >+ >+ if (dst != src) >+ emitStore(dst, regT1, regT0); >+} >+ > void JIT::emit_op_set_function_name(Instruction* currentInstruction) > { > int func = currentInstruction[1].u.operand; >diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm >index 0bcb0541115..61fe23e0b35 100644 >--- a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm >+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm >@@ -2050,6 +2050,22 @@ _llint_op_to_primitive: > dispatch(constexpr op_to_primitive_length) > > >+_llint_op_to_property_key: >+ traceExecution() >+ loadi 8[PC], t2 >+ loadi 4[PC], t3 >+ loadConstantOrVariable(t2, t1, t0) >+ bineq t1, CellTag, .opToPropertyKeySlow >+ bbgt JSCell::m_type[t0], SymbolType, .opToPropertyKeySlow >+ storei t1, TagOffset[cfr, t3, 8] >+ storei t0, PayloadOffset[cfr, t3, 8] >+ dispatch(constexpr op_to_property_key_length) >+ >+.opToPropertyKeySlow: >+ callSlowPath(_slow_path_to_property_key) >+ dispatch(constexpr op_to_property_key_length) >+ >+ > _llint_op_catch: > # 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 >@@ -2698,3 +2714,61 @@ _llint_op_log_shadow_chicken_tail: > .opLogShadowChickenTailSlow: > callSlowPath(_llint_slow_path_log_shadow_chicken_tail) > dispatch(3) >+ >+ >+_llint_op_add_private_field: >+ traceExecution() >+ loadi 4[PC], t0 >+ loadi 20[PC], t1 >+ loadConstantOrVariablePayload(t0, CellTag, t3, .opAddPrivateFieldSlow) >+ bineq JSCell::m_structureID[t3], t1, .opAddPrivateFieldSlow >+ >+ # Transition directly to new structure >+ loadi 28[PC], t1 >+ storei t1, JSCell::m_structureID[t3] >+ writeBarrierOnOperand(1) >+ loadi 12[PC], t2 >+ loadConstantOrVariable(t2, t1, t0) >+ loadi 24[PC], t2 >+ storePropertyAtVariableOffset(t2, t3, t1, t0) >+ writeBarrierOnOperand(1) >+ dispatch(constexpr op_add_private_field_length) >+ >+.opAddPrivateFieldSlow: >+ callSlowPath(_slow_path_add_private_field) >+ dispatch(constexpr op_add_private_field_length) >+ >+_llint_op_get_private_field: >+ traceExecution() >+ loadi 8[PC], t0 >+ loadi 20[PC], t1 >+ loadConstantOrVariablePayload(t0, CellTag, t3, .opGetPrivateFieldSlow) >+ loadi 24[PC], t2 >+ bineq JSCell::m_structureID[t3], t1, .opGetPrivateFieldSlow >+ loadPropertyAtVariableOffset(t2, t3, t0, t1) >+ loadi 4[PC], t2 >+ storei t0, TagOffset[cfr, t2, 8] >+ storei t1, PayloadOffset[cfr, t2, 8] >+ valueProfile(t0, t1, 28, t2) >+ dispatch(constexpr op_get_private_field_length) >+ >+.opGetPrivateFieldSlow: >+ callSlowPath(_slow_path_get_private_field) >+ dispatch(constexpr op_get_private_field_length) >+ >+ >+_llint_op_put_private_field: >+ traceExecution() >+ loadi 4[PC], t0 >+ loadi 20[PC], t1 >+ loadConstantOrVariablePayload(t0, CellTag, t3, .opPutPrivateFieldSlow) >+ loadi 24[PC], t2 >+ bineq JSCell::m_structureID[t3], t1, .opPutPrivateFieldSlow >+ loadi 12[PC], t5 >+ loadConstantOrVariable2Reg(t5, t0, t1) >+ storePropertyAtVariableOffset(t2, t3, t0, t1) >+ dispatch(constexpr op_put_private_field_length) >+ >+.opPutPrivateFieldSlow: >+ callSlowPath(_slow_path_put_private_field) >+ dispatch(constexpr op_put_private_field_length) >diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm >index d0d49b2efd9..2b338de3712 100644 >--- a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm >+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm >@@ -2098,6 +2098,22 @@ _llint_op_to_primitive: > dispatch(constexpr op_to_primitive_length) > > >+_llint_op_to_property_key: >+ traceExecution() >+ loadisFromInstruction(2, t2) >+ loadisFromInstruction(1, t3) >+ loadConstantOrVariable(t2, t0) >+ >+ btqnz t0, tagMask, .opToPropertyKeySlow >+ bbgt JSCell::m_type[t0], SymbolType, .opToPropertyKeySlow >+ storeq t0, [cfr, t3, 8] >+ dispatch(constexpr op_to_property_key_length) >+ >+.opToPropertyKeySlow: >+ callSlowPath(_slow_path_to_property_key) >+ dispatch(constexpr op_to_property_key_length) >+ >+ > _llint_op_catch: > # 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 >@@ -2701,3 +2717,69 @@ _llint_op_log_shadow_chicken_tail: > .opLogShadowChickenTailSlow: > callSlowPath(_llint_slow_path_log_shadow_chicken_tail) > dispatch(constexpr op_log_shadow_chicken_tail_length) >+ >+ >+_llint_op_add_private_field: >+ traceExecution() >+ loadisFromInstruction(1, t0) >+ loadConstantOrVariableCell(t0, t3, .opAddPrivateFieldSlow) >+ loadi JSCell::m_structureID[t3], t1 >+ loadisFromInstruction(5, t2) >+ bineq t2, t1, .opAddPrivateFieldSlow >+ >+ # Transition directly to new structure >+ loadisFromInstruction(7, t1) >+ storei t1, JSCell::m_structureID[t3] >+ writeBarrierOnOperand(1) >+ # Reload base into t0 >+ loadisFromInstruction(1, t1) >+ loadConstantOrVariable(t1, t3) >+ >+ loadisFromInstruction(3, t2) >+ loadConstantOrVariable(t2, t0) >+ loadisFromInstruction(6, t1) >+ storePropertyAtVariableOffset(t1, t3, t0) >+ writeBarrierOnOperands(1, 3) >+ dispatch(constexpr op_add_private_field_length) >+ >+.opAddPrivateFieldSlow: >+ callSlowPath(_slow_path_add_private_field) >+ dispatch(constexpr op_add_private_field_length) >+ >+ >+_llint_op_get_private_field: >+ traceExecution() >+ loadisFromInstruction(2, t0) >+ loadConstantOrVariableCell(t0, t3, .opGetPrivateFieldSlow) >+ loadi JSCell::m_structureID[t3], t1 >+ loadisFromInstruction(5, t2) >+ bineq t2, t1, .opGetPrivateFieldSlow >+ loadisFromInstruction(6, t1) >+ loadisFromInstruction(1, t2) >+ loadPropertyAtVariableOffset(t1, t3, t0) >+ storeq t0, [cfr, t2, 8] >+ valueProfile(t0, 7, t1) >+ dispatch(constexpr op_get_private_field_length) >+ >+.opGetPrivateFieldSlow: >+ callSlowPath(_slow_path_get_private_field) >+ dispatch(constexpr op_get_private_field_length) >+ >+ >+_llint_op_put_private_field: >+ traceExecution() >+ loadisFromInstruction(1, t0) >+ loadConstantOrVariableCell(t0, t3, .opPutPrivateFieldSlow) >+ loadi JSCell::m_structureID[t3], t1 >+ loadisFromInstruction(5, t2) >+ bineq t2, t1, .opPutPrivateFieldSlow >+ loadisFromInstruction(6, t1) >+ loadisFromInstruction(3, t2) >+ loadConstantOrVariable(t2, t0) >+ storePropertyAtVariableOffset(t1, t3, t0) >+ writeBarrierOnOperands(1, 3) >+ dispatch(constexpr op_put_private_field_length) >+ >+.opPutPrivateFieldSlow: >+ callSlowPath(_slow_path_put_private_field) >+ dispatch(constexpr op_put_private_field_length) >diff --git a/Source/JavaScriptCore/parser/ASTBuilder.h b/Source/JavaScriptCore/parser/ASTBuilder.h >index 873e635463a..f3672e34d68 100644 >--- a/Source/JavaScriptCore/parser/ASTBuilder.h >+++ b/Source/JavaScriptCore/parser/ASTBuilder.h >@@ -267,12 +267,12 @@ public: > return node; > } > >- ExpressionNode* createDotAccess(const JSTokenLocation& location, ExpressionNode* base, const Identifier* property, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end) >+ ExpressionNode* createDotAccess(const JSTokenLocation& location, ExpressionNode* base, const Identifier* property, DotType type, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end) > { > if (base->isSuperNode()) > usesSuperProperty(); > >- DotAccessorNode* node = new (m_parserArena) DotAccessorNode(location, base, *property); >+ DotAccessorNode* node = new (m_parserArena) DotAccessorNode(location, base, *property, type); > setExceptionLocation(node, start, divot, end); > return node; > } >@@ -396,6 +396,11 @@ public: > return node; > } > >+ DefineFieldNode* createDefineField(const JSTokenLocation& location, const Identifier* ident, ExpressionNode* initializer, DefineFieldNode::Type type) >+ { >+ return new (m_parserArena) DefineFieldNode(location, ident, initializer, type); >+ } >+ > ClassExprNode* createClassExpr(const JSTokenLocation& location, const ParserClassInfo<ASTBuilder>& classInfo, VariableEnvironment& classEnvironment, ExpressionNode* constructor, > ExpressionNode* parentClass, PropertyListNode* classElements) > { >@@ -522,6 +527,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); } > >@@ -635,6 +641,11 @@ public: > return pattern->isAssignmentLocation(); > } > >+ bool isPrivateLocation(const Expression& node) >+ { >+ return node->isPrivateLocation(); >+ } >+ > bool isObjectLiteral(const Expression& node) > { > return node->isObjectLiteral(); >@@ -1501,10 +1512,10 @@ ExpressionNode* ASTBuilder::makeAssignNode(const JSTokenLocation& location, Expr > // function should not pick up the name of the dot->identifier(). > static_cast<BaseFuncExprNode*>(expr)->metadata()->setInferredName(dot->identifier()); > } >- return new (m_parserArena) AssignDotNode(location, dot->base(), dot->identifier(), expr, exprHasAssignments, dot->divot(), start, end); >+ return new (m_parserArena) AssignDotNode(location, dot->base(), dot->identifier(), dot->type(), expr, exprHasAssignments, dot->divot(), start, end); > } > >- ReadModifyDotNode* node = new (m_parserArena) ReadModifyDotNode(location, dot->base(), dot->identifier(), op, expr, exprHasAssignments, divot, start, end); >+ ReadModifyDotNode* node = new (m_parserArena) ReadModifyDotNode(location, dot->base(), dot->identifier(), dot->type(), op, expr, exprHasAssignments, divot, start, end); > node->setSubexpressionInfo(dot->divot(), dot->divotEnd().offset); > return node; > } >diff --git a/Source/JavaScriptCore/parser/Lexer.cpp b/Source/JavaScriptCore/parser/Lexer.cpp >index dacd7f25086..7b1fd34fd54 100644 >--- a/Source/JavaScriptCore/parser/Lexer.cpp >+++ b/Source/JavaScriptCore/parser/Lexer.cpp >@@ -90,7 +90,8 @@ enum CharacterType { > > // Other types (only one so far) > CharacterWhiteSpace, >- CharacterPrivateIdentifierStart >+ CharacterPrivateIdentifierStart, >+ CharacterPrivateNameStart > }; > > // 256 Latin-1 codes >@@ -130,7 +131,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, >@@ -945,14 +946,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(); > >@@ -967,11 +972,11 @@ template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType Lexer<LChar>::p > int identifierLength = currentSourcePtr() - identifierStart; > ident = makeIdentifier(identifierStart, identifierLength); > if (m_parsingBuiltinFunction) { >- if (!isSafeBuiltinIdentifier(*m_vm, ident) && !isPrivateName) { >+ if (!isSafeBuiltinIdentifier(*m_vm, ident) && !isPrivateIdentifier) { > m_lexErrorMessage = makeString("The use of '", ident->string(), "' is disallowed in builtin functions."); > return ERRORTOK; > } >- if (isPrivateName) >+ if (isPrivateIdentifier) > ident = m_vm->propertyNames->lookUpPrivateName(*ident); > else if (*ident == m_vm->propertyNames->undefinedKeyword) > tokenData->ident = &m_vm->propertyNames->builtinNames().undefinedPrivateName(); >@@ -982,20 +987,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 <> >@@ -1011,8 +1017,8 @@ template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType Lexer<UChar>::p > } > } > >- bool isPrivateName = m_current == '@' && m_parsingBuiltinFunction; >- if (isPrivateName) >+ bool isPrivateIdentifier = m_current == '@' && m_parsingBuiltinFunction; >+ if (isPrivateIdentifier) > shift(); > > const UChar* identifierStart = currentSourcePtr(); >@@ -1026,7 +1032,7 @@ template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType Lexer<UChar>::p > } > > if (UNLIKELY(m_current == '\\')) { >- ASSERT(!isPrivateName); >+ ASSERT(!isPrivateIdentifier); > setOffsetFromSourcePtr(identifierStart, identifierLineStart); > return parseIdentifierSlowCase<shouldCreateIdentifier>(tokenData, lexerFlags, strictMode); > } >@@ -1045,11 +1051,11 @@ template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType Lexer<UChar>::p > else > ident = makeIdentifier(identifierStart, identifierLength); > if (m_parsingBuiltinFunction) { >- if (!isSafeBuiltinIdentifier(*m_vm, ident) && !isPrivateName) { >+ if (!isSafeBuiltinIdentifier(*m_vm, ident) && !isPrivateIdentifier) { > m_lexErrorMessage = makeString("The use of '", ident->string(), "' is disallowed in builtin functions."); > return ERRORTOK; > } >- if (isPrivateName) >+ if (isPrivateIdentifier) > ident = m_vm->propertyNames->lookUpPrivateName(*ident); > else if (*ident == m_vm->propertyNames->undefinedKeyword) > tokenData->ident = &m_vm->propertyNames->builtinNames().undefinedPrivateName(); >@@ -1060,7 +1066,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); >@@ -1080,6 +1086,8 @@ template<typename CharacterType> template<bool shouldCreateIdentifier> JSTokenTy > { > tokenData->escaped = true; > auto identifierStart = currentSourcePtr(); >+ bool isPrivateName = *identifierStart == '#'; >+ auto identType = isPrivateName ? PRIVATENAME : IDENT; > bool bufferRequired = false; > > while (true) { >@@ -1130,13 +1138,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) >@@ -2307,12 +2315,21 @@ start: > m_terminator = true; > m_lineStart = m_code; > goto start; >+ case CharacterPrivateNameStart: { >+ auto next = peek(1); >+ if (isIdentStart(next) || next == '\\') { >+ lexerFlags &= ~LexexFlagsDontBuildKeywords; >+ goto parseIdent; >+ } >+ goto invalidCharacter; >+ } > case CharacterPrivateIdentifierStart: > if (m_parsingBuiltinFunction) > goto parseIdent; > > FALLTHROUGH; > 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 a56fbd756c0..120eeb09f5c 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) > { > } > >@@ -720,21 +744,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) >@@ -984,6 +1004,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) >@@ -999,6 +1027,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 95774d91c94..827e686522c 100644 >--- a/Source/JavaScriptCore/parser/Nodes.cpp >+++ b/Source/JavaScriptCore/parser/Nodes.cpp >@@ -330,6 +330,16 @@ bool PropertyListNode::hasStaticallyNamedProperty(const Identifier& propName) > return false; > } > >+bool PropertyListNode::shouldCreateLexicalScopeForClass(PropertyListNode* list) >+{ >+ while (list) { >+ if (list->m_node->isComputedClassField() || list->m_node->isPrivate()) >+ return true; >+ list = list->m_next; >+ } >+ return false; >+} >+ > VariableEnvironmentNode::VariableEnvironmentNode(VariableEnvironment& lexicalVariables) > { > m_lexicalVariables.swap(lexicalVariables); >diff --git a/Source/JavaScriptCore/parser/Nodes.h b/Source/JavaScriptCore/parser/Nodes.h >index 6c27641cee9..b740596a9ec 100644 >--- a/Source/JavaScriptCore/parser/Nodes.h >+++ b/Source/JavaScriptCore/parser/Nodes.h >@@ -181,6 +181,7 @@ namespace JSC { > virtual bool isPure(BytecodeGenerator&) const { return false; } > virtual bool isConstant() const { return false; } > virtual bool isLocation() const { return false; } >+ virtual bool isPrivateLocation() const { return false; } > virtual bool isAssignmentLocation() const { return isLocation(); } > virtual bool isResolveNode() const { return false; } > virtual bool isAssignResolveNode() const { return false; } >@@ -243,6 +244,7 @@ namespace JSC { > virtual bool isFuncDeclNode() const { return false; } > virtual bool isModuleDeclarationNode() const { return false; } > virtual bool isForOfNode() const { return false; } >+ virtual bool isDefineFieldNode() const { return false; } > > protected: > int m_lastLine { -1 }; >@@ -704,12 +706,13 @@ namespace JSC { > enum class ClassElementTag : uint8_t { No, Instance, Static, LastTag }; > class PropertyNode final : public ParserArenaFreeable { > public: >- enum Type : uint8_t { Constant = 1, Getter = 2, Setter = 4, Computed = 8, Shorthand = 16, Spread = 32 }; >+ enum Type : uint8_t { Constant = 1, Getter = 2, Setter = 4, Computed = 8, Shorthand = 16, Spread = 32, Private = 64 }; > enum PutType : uint8_t { Unknown, KnownDirect }; > > PropertyNode(const Identifier&, ExpressionNode*, Type, PutType, SuperBinding, ClassElementTag); > PropertyNode(ExpressionNode*, Type, PutType, SuperBinding, ClassElementTag); > PropertyNode(ExpressionNode* propertyName, ExpressionNode*, Type, PutType, SuperBinding, ClassElementTag); >+ PropertyNode(const Identifier&, ExpressionNode* propertyName, ExpressionNode*, Type, PutType, SuperBinding, ClassElementTag); > > ExpressionNode* expressionName() const { return m_expression; } > const Identifier* name() const { return m_name; } >@@ -719,7 +722,12 @@ namespace JSC { > bool isClassProperty() const { return static_cast<ClassElementTag>(m_classElementTag) != ClassElementTag::No; } > bool isStaticClassProperty() const { return static_cast<ClassElementTag>(m_classElementTag) == ClassElementTag::Static; } > bool isInstanceClassProperty() const { return static_cast<ClassElementTag>(m_classElementTag) == ClassElementTag::Instance; } >+ bool isClassField() const { return isClassProperty() && !needsSuperBinding(); } >+ bool isInstanceClassField() const { return isInstanceClassProperty() && !needsSuperBinding(); } > bool isOverriddenByDuplicate() const { return m_isOverriddenByDuplicate; } >+ bool isPrivate() const { return m_isPrivate; } >+ bool hasComputedName() const { return m_expression; } >+ bool isComputedClassField() const { return isClassField() && hasComputedName(); } > void setIsOverriddenByDuplicate() { m_isOverriddenByDuplicate = true; } > PutType putType() const { return static_cast<PutType>(m_putType); } > >@@ -728,12 +736,13 @@ namespace JSC { > const Identifier* m_name; > ExpressionNode* m_expression; > ExpressionNode* m_assign; >- unsigned m_type : 6; >+ unsigned m_type : 7; > unsigned m_needsSuperBinding : 1; > unsigned m_putType : 1; >+ unsigned m_isPrivate : 1; > static_assert(1 << 2 > static_cast<unsigned>(ClassElementTag::LastTag), "ClassElementTag shouldn't use more than two bits"); > unsigned m_classElementTag : 2; >- unsigned m_isOverriddenByDuplicate: 1; >+ unsigned m_isOverriddenByDuplicate : 1; > }; > > class PropertyListNode final : public ExpressionNode { >@@ -742,15 +751,26 @@ namespace JSC { > PropertyListNode(const JSTokenLocation&, PropertyNode*, PropertyListNode*); > > bool hasStaticallyNamedProperty(const Identifier& propName); >+ bool isComputedClassField() const >+ { >+ return m_node->isComputedClassField(); >+ } >+ bool isInstanceClassField() const >+ { >+ return m_node->isInstanceClassField(); >+ } > >- RegisterID* emitBytecode(BytecodeGenerator&, RegisterID*, RegisterID*); >+ static bool shouldCreateLexicalScopeForClass(PropertyListNode*); >+ >+ RegisterID* emitBytecode(BytecodeGenerator&, RegisterID*, RegisterID*, Vector<JSTextPosition>*); > > private: > RegisterID* emitBytecode(BytecodeGenerator& generator, RegisterID* dst = nullptr) override > { >- return emitBytecode(generator, dst, nullptr); >+ return emitBytecode(generator, dst, nullptr, nullptr); > } > void emitPutConstantProperty(BytecodeGenerator&, RegisterID*, PropertyNode&); >+ void emitSaveComputedFieldName(BytecodeGenerator&, PropertyNode&); > > PropertyNode* m_node; > PropertyListNode* m_next { nullptr }; >@@ -788,9 +808,30 @@ namespace JSC { > bool m_subscriptHasAssignments; > }; > >- class DotAccessorNode final : public ExpressionNode, public ThrowableExpressionData { >+ enum class DotType { Name, PrivateName }; >+ class BaseDotNode : public ExpressionNode { >+ public: >+ BaseDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, DotType); >+ >+ ExpressionNode* base() const { return m_base; } >+ const Identifier& identifier() const { return m_ident; } >+ DotType type() const { return m_type; } >+ bool isPrivateName() const { return m_type == DotType::PrivateName; } >+ >+ RegisterID* emitGetPropertyValue(BytecodeGenerator&, RegisterID* dst, RegisterID* base, RefPtr<RegisterID>& thisValue); >+ RegisterID* emitGetPropertyValue(BytecodeGenerator&, RegisterID* dst, RegisterID* base); >+ RegisterID* emitPutProperty(BytecodeGenerator&, RegisterID* base, RegisterID* value, RefPtr<RegisterID>& thisValue); >+ RegisterID* emitPutProperty(BytecodeGenerator&, RegisterID* base, RegisterID* value); >+ >+ protected: >+ ExpressionNode* m_base; >+ const Identifier& m_ident; >+ DotType m_type; >+ }; >+ >+ class DotAccessorNode final : public BaseDotNode, public ThrowableExpressionData { > public: >- DotAccessorNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&); >+ DotAccessorNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, DotType); > > ExpressionNode* base() const { return m_base; } > const Identifier& identifier() const { return m_ident; } >@@ -799,10 +840,8 @@ namespace JSC { > RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; > > bool isLocation() const override { return true; } >+ bool isPrivateLocation() const override { return m_type == DotType::PrivateName; } > bool isDotAccessorNode() const override { return true; } >- >- ExpressionNode* m_base; >- const Identifier& m_ident; > }; > > class SpreadExpressionNode final : public ExpressionNode, public ThrowableExpressionData { >@@ -1370,28 +1409,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; >@@ -2163,6 +2198,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: >@@ -2187,6 +2237,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 6aae0303d33..9f7942545d1 100644 >--- a/Source/JavaScriptCore/parser/Parser.cpp >+++ b/Source/JavaScriptCore/parser/Parser.cpp >@@ -195,7 +195,7 @@ Parser<LexerType>::~Parser() > } > > template <typename LexerType> >-String Parser<LexerType>::parseInner(const Identifier& calleeName, SourceParseMode parseMode) >+String Parser<LexerType>::parseInner(const Identifier& calleeName, SourceParseMode parseMode, const Vector<JSTextPosition>& instanceFieldLocations) > { > String parseError = String(); > >@@ -209,6 +209,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 = parseGeneratorFunctionSourceElements(context, calleeName, CheckForStrictMode); > else if (isAsyncGeneratorWrapperParseMode(parseMode)) > sourceElements = parseAsyncGeneratorFunctionSourceElements(context, parseMode, isArrowFunctionBodyExpression, CheckForStrictMode); >+ else if (parseMode == SourceParseMode::InstanceFieldInitializerMode) >+ sourceElements = parseInstanceFieldInitializerSourceElements(context, instanceFieldLocations); > else > sourceElements = parseSourceElements(context, CheckForStrictMode); > } >@@ -783,6 +787,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"); >@@ -1238,6 +1244,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"); >@@ -2062,6 +2070,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(); >@@ -2114,6 +2123,7 @@ static const char* stringArticleForFunctionMode(SourceParseMode mode) > case SourceParseMode::ProgramMode: > case SourceParseMode::ModuleAnalyzeMode: > case SourceParseMode::ModuleEvaluateMode: >+ case SourceParseMode::InstanceFieldInitializerMode: > RELEASE_ASSERT_NOT_REACHED(); > return ""; > } >@@ -2155,6 +2165,7 @@ static const char* stringForFunctionMode(SourceParseMode mode) > case SourceParseMode::ProgramMode: > case SourceParseMode::ModuleAnalyzeMode: > case SourceParseMode::ModuleEvaluateMode: >+ case SourceParseMode::InstanceFieldInitializerMode: > RELEASE_ASSERT_NOT_REACHED(); > return ""; > } >@@ -2768,6 +2779,7 @@ template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(T > classScope->setIsLexicalScope(); > classScope->preventVarDeclarations(); > classScope->setStrictMode(); >+ classScope->setIsPrivateNameScope(); > > ASSERT_WITH_MESSAGE(requirements != FunctionNameRequirements::Unnamed, "Currently, there is no caller that uses FunctionNameRequirements::Unnamed for class syntax."); > ASSERT_WITH_MESSAGE(!(requirements == FunctionNameRequirements::None && !info.className), "When specifying FunctionNameRequirements::None, we need to initialize info.className with the default value in the caller side."); >@@ -2795,6 +2807,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(); >@@ -2806,6 +2819,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(); >@@ -2867,9 +2881,27 @@ parseMethod: > case OPENBRACKET: > next(); > computedPropertyName = parseAssignmentExpression(context); >+ type = static_cast<PropertyNode::Type>(type | PropertyNode::Computed); > failIfFalse(computedPropertyName, "Cannot parse computed property name"); > handleProductionOrFail(CLOSEBRACKET, "]", "end", "computed property name"); > break; >+ case PRIVATENAME: { >+ JSToken token = m_token; >+ ident = m_token.m_data.ident; >+ failIfTrue(tag == ClassElementTag::Static, "Static class element cannot be private"); >+ failIfTrue(isGetter || isSetter, "Cannot parse class method with private name"); >+ ASSERT(ident); >+ next(); >+ failIfTrue(matchAndUpdate(OPENPAREN, token), "Cannot parse class method with private name"); >+ semanticFailIfTrue(classScope->declarePrivateName(*ident) & DeclarationResult::InvalidDuplicateDeclaration, "Cannot declare private field twice"); >+ type = static_cast<PropertyNode::Type>(type | PropertyNode::Private); >+ >+ // TODO: remove this when method for loading and storing private names has changed in followup patch. >+ classScope->declareLexicalVariable(ident, false); >+ classScope->useVariable(ident, false); >+ classScope->addClosedVariableCandidateUnconditionally(ident->impl()); >+ break; >+ } > default: > if (m_token.m_type & KeywordTokenFlag) > goto namedKeyword; >@@ -2879,9 +2911,39 @@ parseMethod: > TreeProperty property; > const bool alwaysStrictInsideClass = true; > if (isGetter || isSetter) { >- property = parseGetterSetter(context, alwaysStrictInsideClass, isGetter ? PropertyNode::Getter : PropertyNode::Setter, >- methodStart, ConstructorKind::None, tag); >+ type = static_cast<PropertyNode::Type>(type & ~PropertyNode::Constant); >+ type = static_cast<PropertyNode::Type>(type | (isGetter ? PropertyNode::Getter : PropertyNode::Setter)); >+ property = parseGetterSetter(context, alwaysStrictInsideClass, type, methodStart, ConstructorKind::None, tag); > failIfFalse(property, "Cannot parse this method"); >+ } else if (!match(OPENPAREN) && tag == ClassElementTag::Instance && parseMode == SourceParseMode::MethodMode && !isGetter && !isSetter) { >+ if (ident) { >+ semanticFailIfTrue(*ident == propertyNames.constructor, "Cannot declare class field named 'constructor'"); >+ semanticFailIfTrue(*ident == propertyNames.constructorPrivateField, "Cannot declare private class field named '#constructor'"); >+ } >+ >+ if (computedPropertyName) { >+ ident = &m_parserArena.identifierArena().makeNumericIdentifier(m_vm, numComputedFields++); >+ failIfTrue(classScope->declareLexicalVariable(ident, true) != DeclarationResult::Valid, "Failed to declare lexical variable for computed class field. FIXME: This should never happen!"); >+ classScope->useVariable(ident, false); >+ classScope->addClosedVariableCandidateUnconditionally(ident->impl()); >+ } >+ >+ TreeExpression initializer = 0; >+ if (consume(EQUAL)) { >+ SetForScope<bool> overrideParsingClassFieldInitializer(m_parserState.isParsingClassFieldInitializer, true); >+ size_t currentUsedVariablesSize = classScope->currentUsedVariablesSize(); >+ classScope->pushUsedVariableSet(); >+ initializer = parseAssignmentExpression(context); >+ failIfFalse(initializer, "Cannot parse initializer for class field"); >+ classScope->markLastUsedVariablesSetAsCaptured(); >+ classScope->revertToPreviousUsedVariables(currentUsedVariablesSize); >+ } >+ failIfFalse(autoSemiColon(), "Expected a ';' following a class field"); >+ auto inferName = initializer ? InferName::Allowed : InferName::Disallowed; >+ if (computedPropertyName) >+ property = context.createProperty(ident, computedPropertyName, initializer, type, PropertyNode::Unknown, alwaysStrictInsideClass, SuperBinding::NotNeeded, tag); >+ else >+ property = context.createProperty(ident, initializer, type, PropertyNode::Unknown, alwaysStrictInsideClass, SuperBinding::NotNeeded, inferName, tag); > } else { > ParserFunctionInfo<TreeBuilder> methodInfo; > bool isConstructor = tag == ClassElementTag::Instance && *ident == propertyNames.constructor; >@@ -2906,11 +2968,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); > } > } > >@@ -2923,11 +2983,78 @@ parseMethod: > info.endOffset = tokenLocation().endOffset - 1; > consumeOrFail(CLOSEBRACE, "Expected a closing '}' after a class body"); > >+ // FIXME: need to consider direct-eval. >+ // Fail if there are no parent private name scopes and any used-but-undeclared private names. >+ if (!tryToMigratePrivateNames()) >+ semanticFailIfTrue(currentPrivateNameScope()->hasUsedButUndeclaredPrivateNames(), "Cannot reference undeclared private names"); > auto classExpression = context.createClassExpr(location, info, classScope->finalizeLexicalEnvironment(), constructor, parentClass, classElements); > popScope(classScope, TreeBuilder::NeedsFreeVariableInfo); > return classExpression; > } > >+template <typename LexerType> >+template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseInstanceFieldInitializerSourceElements(TreeBuilder& context, const Vector<JSTextPosition>& instanceFieldLocations) >+{ >+ TreeSourceElements sourceElements = context.createSourceElements(); >+ unsigned numComputedFields = 0; >+ for (auto location : instanceFieldLocations) { >+ LexerState lexerState { location.offset, static_cast<unsigned>(location.lineStartOffset), static_cast<unsigned>(location.line), static_cast<unsigned>(location.line) }; >+ restoreLexerState(lexerState); >+ >+ JSTokenLocation fieldLocation = tokenLocation(); >+ const Identifier* ident = 0; >+ TreeExpression computedPropertyName = 0; >+ DefineFieldNode::Type type = DefineFieldNode::Name; >+ switch (m_token.m_type) { >+ case PRIVATENAME: >+ type = DefineFieldNode::PrivateName; >+ [[fallthrough]]; >+ namedKeyword: >+ case STRING: >+ case IDENT: >+ ident = m_token.m_data.ident; >+ ASSERT(ident); >+ next(); >+ break; >+ case DOUBLE: >+ case INTEGER: >+ ident = &m_parserArena.identifierArena().makeNumericIdentifier(const_cast<VM*>(m_vm), m_token.m_data.doubleValue); >+ ASSERT(ident); >+ next(); >+ break; >+ case OPENBRACKET: >+ next(); >+ computedPropertyName = parseAssignmentExpression(context); >+ failIfFalse(computedPropertyName, "Cannot parse computed property name"); >+ handleProductionOrFail(CLOSEBRACKET, "]", "end", "computed property name"); >+ ident = &m_parserArena.identifierArena().makeNumericIdentifier(m_vm, numComputedFields++); >+ type = DefineFieldNode::ComputedName; >+ break; >+ default: >+ if (m_token.m_type & KeywordTokenFlag) >+ goto namedKeyword; >+ failDueToUnexpectedToken(); >+ } >+ >+ // Only valid class fields are handled in this function. >+ ASSERT(match(EQUAL) || match(SEMICOLON) || match(CLOSEBRACE) || m_lexer->prevTerminator()); >+ >+ TreeExpression initializer = 0; >+ if (consume(EQUAL)) >+ initializer = parseAssignmentExpression(context); >+ >+ if (type == DefineFieldNode::PrivateName) >+ currentScope()->useVariable(ident, false); >+ >+ TreeStatement defineField = context.createDefineField(fieldLocation, ident, initializer, type); >+ context.appendStatement(sourceElements, defineField); >+ } >+ >+ ASSERT(!hasError()); >+ m_token.m_type = EOFTOK; // Hack to make parsing valid >+ return sourceElements; >+} >+ > struct LabelInfo { > LabelInfo(const Identifier* ident, const JSTextPosition& start, const JSTextPosition& end) > : m_ident(ident) >@@ -4469,9 +4596,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(); > >@@ -4640,6 +4771,22 @@ static inline void recordCallOrApplyDepth(ParserType*, VM&, std::optional<typena > { > } > >+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) > { >@@ -4663,10 +4810,11 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres > next(); > if (matchContextualKeyword(m_vm->propertyNames->target)) { > ScopeRef closestOrdinaryFunctionScope = closestParentOrdinaryFunctionNonLexicalScope(); >- semanticFailIfFalse(currentScope()->isFunction() || closestOrdinaryFunctionScope->evalContextType() == EvalContextType::FunctionEvalContext, "new.target is only valid inside functions"); >+ bool isFunctionEvalContextType = closestOrdinaryFunctionScope->evalContextType() == EvalContextType::FunctionEvalContext || closestOrdinaryFunctionScope->evalContextType() == EvalContextType::InstanceFieldEvalContext; >+ semanticFailIfFalse(currentScope()->isFunction() || isFunctionEvalContextType, "new.target is only valid inside functions"); > baseIsNewTarget = true; > if (currentScope()->isArrowFunction()) { >- semanticFailIfFalse(!closestOrdinaryFunctionScope->isGlobalCodeScope() || closestOrdinaryFunctionScope->evalContextType() == EvalContextType::FunctionEvalContext, "new.target is not valid inside arrow functions in global code"); >+ semanticFailIfFalse(!closestOrdinaryFunctionScope->isGlobalCodeScope() || isFunctionEvalContextType, "new.target is not valid inside arrow functions in global code"); > currentScope()->setInnerArrowFunctionUsesNewTarget(); > } > base = context.createNewTargetExpr(location); >@@ -4685,6 +4833,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres > semanticFailIfFalse(currentScope()->isFunction() || (closestOrdinaryFunctionScope->isEvalContext() && closestOrdinaryFunctionScope->expectedSuperBinding() == SuperBinding::Needed), "super is not valid in this context"); > base = context.createSuperExpr(location); > next(); >+ failIfTrue(match(OPENPAREN) && currentScope()->evalContextType() == EvalContextType::InstanceFieldEvalContext, "super call is not valid in this context"); > ScopeRef functionScope = currentFunctionScope(); > if (!functionScope->setNeedsSuperBinding()) { > // It unnecessary to check of using super during reparsing one more time. Also it can lead to syntax error >@@ -4755,6 +4904,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) { >@@ -4806,8 +4957,18 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres > m_parserState.nonTrivialExpressionCount++; > JSTextPosition expressionEnd = lastTokenEndPosition(); > nextExpectIdentifier(LexerFlagsIgnoreReservedWords | TreeBuilder::DontBuildKeywords); >+ const Identifier* ident = m_token.m_data.ident; >+ auto type = DotType::Name; >+ if (match(PRIVATENAME)) { >+ ASSERT(ident); >+ 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(); >@@ -5006,6 +5167,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: >@@ -5077,7 +5239,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 366605951eb..eb4a4451828 100644 >--- a/Source/JavaScriptCore/parser/Parser.h >+++ b/Source/JavaScriptCore/parser/Parser.h >@@ -178,6 +178,7 @@ public: > , m_hasArguments(false) > , m_isEvalContext(false) > , m_hasNonSimpleParameterList(false) >+ , m_isPrivateNameScope(false) > , m_evalContextType(EvalContextType::None) > , m_constructorKind(static_cast<unsigned>(ConstructorKind::None)) > , m_expectedSuperBinding(static_cast<unsigned>(SuperBinding::NotNeeded)) >@@ -255,6 +256,7 @@ public: > case SourceParseMode::GetterMode: > case SourceParseMode::SetterMode: > case SourceParseMode::MethodMode: >+ case SourceParseMode::InstanceFieldInitializerMode: > setIsFunction(); > break; > >@@ -284,6 +286,7 @@ public: > bool isGeneratorBoundary() const { return m_isGeneratorBoundary; } > bool isAsyncFunction() const { return m_isAsyncFunction; } > bool isAsyncFunctionBoundary() const { return m_isAsyncFunctionBoundary; } >+ bool isPrivateNameScope() const { return m_isPrivateNameScope; } > > bool hasArguments() const { return m_hasArguments; } > >@@ -295,6 +298,13 @@ public: > m_isLexicalScope = true; > m_allowsLexicalDeclarations = true; > } >+ >+ void setIsPrivateNameScope() >+ { >+ setIsFunction(); >+ m_isPrivateNameScope = true; >+ } >+ > bool isLexicalScope() { return m_isLexicalScope; } > bool usesEval() { return m_usesEval; } > >@@ -447,7 +457,51 @@ public: > { > return m_lexicalVariables.contains(ident.get()); > } >- >+ >+ bool hasPrivateName(const Identifier& ident) >+ { >+ return m_lexicalVariables.hasPrivateName(ident); >+ } >+ >+ bool movePrivateNames(Scope& other) >+ { >+ if (!m_lexicalVariables.privateNamesSize()) >+ return false; >+ >+ m_lexicalVariables.copyPrivateNames(other.m_lexicalVariables); >+ >+ return true; >+ } >+ >+ 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()); >@@ -566,6 +620,12 @@ public: > { > m_closedVariableCandidates.add(impl); > } >+ >+ void markLastUsedVariablesSetAsCaptured() >+ { >+ for (UniquedStringImpl* impl : m_usedVariables.last()) >+ m_closedVariableCandidates.add(impl); >+ } > > void collectFreeVariables(Scope* nestedScope, bool shouldTrackClosedVariables) > { >@@ -803,6 +863,7 @@ private: > bool m_hasArguments; > bool m_isEvalContext; > bool m_hasNonSimpleParameterList; >+ bool m_isPrivateNameScope; > EvalContextType m_evalContextType; > unsigned m_constructorKind; > unsigned m_expectedSuperBinding; >@@ -874,7 +935,7 @@ public: > ~Parser(); > > template <class ParsedNode> >- std::unique_ptr<ParsedNode> parse(ParserError&, const Identifier&, SourceParseMode); >+ std::unique_ptr<ParsedNode> parse(ParserError&, const Identifier&, SourceParseMode, const Vector<JSTextPosition>& = Vector<JSTextPosition>()); > > JSTextPosition positionBeforeLastNewline() const { return m_lexer->positionBeforeLastNewline(); } > JSTokenLocation locationBeforeLastToken() const { return m_lexer->lastTokenLocation(); } >@@ -1147,6 +1208,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 tryToMigratePrivateNames() >+ { >+ ScopeRef current = currentPrivateNameScope(); >+ unsigned i = current.index() - 1; >+ while (i && !m_scopeStack[i].isPrivateNameScope()) { >+ i--; >+ ASSERT(i < m_scopeStack.size()); >+ } >+ >+ if (i == 0) >+ return false; >+ >+ current->movePrivateNames(m_scopeStack[i]); >+ return true; >+ } >+ > ScopeRef closestParentOrdinaryFunctionNonLexicalScope() > { > unsigned i = m_scopeStack.size() - 1; >@@ -1312,7 +1402,7 @@ private: > } > > Parser(); >- String parseInner(const Identifier&, SourceParseMode); >+ String parseInner(const Identifier&, SourceParseMode, const Vector<JSTextPosition>& = Vector<JSTextPosition>()); > > void didFinishParsing(SourceElements*, DeclarationStacks::FunctionStack&&, VariableEnvironment&, UniquedStringImplPtrSet&&, CodeFeatures, int); > >@@ -1368,6 +1458,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) > { >@@ -1524,6 +1624,7 @@ private: > template <class TreeBuilder> TreeSourceElements parseGeneratorFunctionSourceElements(TreeBuilder&, const Identifier& name, SourceElementsMode); > template <class TreeBuilder> TreeSourceElements parseAsyncFunctionSourceElements(TreeBuilder&, SourceParseMode, bool isArrowFunctionBodyExpression, SourceElementsMode); > template <class TreeBuilder> TreeSourceElements parseAsyncGeneratorFunctionSourceElements(TreeBuilder&, SourceParseMode, bool isArrowFunctionBodyExpression, SourceElementsMode); >+ 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 }; >@@ -1615,6 +1716,7 @@ private: > > template <class TreeBuilder> NEVER_INLINE const char* metaPropertyName(TreeBuilder&, TreeExpression); > >+ ALWAYS_INLINE bool addPrivateNameUsedIfNeeded(const Identifier* ident); > ALWAYS_INLINE int isBinaryOperator(JSTokenType); > bool allowAutomaticSemicolon(); > >@@ -1708,7 +1810,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 >@@ -1848,7 +1952,7 @@ private: > > template <typename LexerType> > template <class ParsedNode> >-std::unique_ptr<ParsedNode> Parser<LexerType>::parse(ParserError& error, const Identifier& calleeName, SourceParseMode parseMode) >+std::unique_ptr<ParsedNode> Parser<LexerType>::parse(ParserError& error, const Identifier& calleeName, SourceParseMode parseMode, const Vector<JSTextPosition>& instanceFieldLocations) > { > int errLine; > String errMsg; >@@ -1865,7 +1969,7 @@ 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); >+ String parseError = parseInner(calleeName, parseMode, instanceFieldLocations); > > int lineNumber = m_lexer->lineNumber(); > bool lexError = m_lexer->sawError(); >@@ -1949,7 +2053,8 @@ std::unique_ptr<ParsedNode> parse( > ConstructorKind defaultConstructorKind = ConstructorKind::None, > DerivedContextType derivedContextType = DerivedContextType::None, > EvalContextType evalContextType = EvalContextType::None, >- DebuggerParseData* debuggerParseData = nullptr) >+ DebuggerParseData* debuggerParseData = nullptr, >+ const Vector<JSTextPosition>& instanceFieldLocations = Vector<JSTextPosition>()) > { > ASSERT(!source.provider()->source().isNull()); > >@@ -1960,7 +2065,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); >+ result = parser.parse<ParsedNode>(error, name, parseMode, instanceFieldLocations); > if (positionBeforeLastNewline) > *positionBeforeLastNewline = parser.positionBeforeLastNewline(); > if (builtinMode == JSParserBuiltinMode::Builtin) { >@@ -1973,7 +2078,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); >+ result = parser.parse<ParsedNode>(error, name, parseMode, instanceFieldLocations); > if (positionBeforeLastNewline) > *positionBeforeLastNewline = parser.positionBeforeLastNewline(); > } >diff --git a/Source/JavaScriptCore/parser/ParserModes.h b/Source/JavaScriptCore/parser/ParserModes.h >index 6960e5152cf..6c678f3239a 100644 >--- a/Source/JavaScriptCore/parser/ParserModes.h >+++ b/Source/JavaScriptCore/parser/ParserModes.h >@@ -63,6 +63,7 @@ enum class SourceParseMode : uint8_t { > AsyncGeneratorWrapperFunctionMode = 16, > AsyncGeneratorWrapperMethodMode = 17, > GeneratorWrapperMethodMode = 18, >+ InstanceFieldInitializerMode = 19, > }; > > class SourceParseModeSet { >@@ -111,7 +112,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 86926036b3e..404d768e2dd 100644 >--- a/Source/JavaScriptCore/parser/ParserTokens.h >+++ b/Source/JavaScriptCore/parser/ParserTokens.h >@@ -112,6 +112,7 @@ enum JSTokenType { > DOUBLE, > BIGINT, > IDENT, >+ PRIVATENAME, > STRING, > TEMPLATE, > REGEXP, >diff --git a/Source/JavaScriptCore/parser/SyntaxChecker.h b/Source/JavaScriptCore/parser/SyntaxChecker.h >index 97cfadb302f..4983340bc14 100644 >--- a/Source/JavaScriptCore/parser/SyntaxChecker.h >+++ b/Source/JavaScriptCore/parser/SyntaxChecker.h >@@ -72,7 +72,7 @@ public: > enum : int { NoneExpr = 0, > ResolveEvalExpr, ResolveExpr, IntegerExpr, DoubleExpr, StringExpr, BigIntExpr, > ThisExpr, NullExpr, BoolExpr, RegExpExpr, ObjectLiteralExpr, >- FunctionExpr, ClassExpr, SuperExpr, ImportExpr, BracketExpr, DotExpr, CallExpr, >+ FunctionExpr, ClassExpr, SuperExpr, ImportExpr, BracketExpr, DotExpr, PrivateDotExpr, CallExpr, > NewExpr, PreExpr, PostExpr, UnaryExpr, BinaryExpr, > ConditionalExpr, AssignmentExpr, TypeofExpr, > DeleteExpr, ArrayLiteralExpr, BindingDestructuring, RestParameter, >@@ -180,7 +180,7 @@ public: > ExpressionType createBoolean(const JSTokenLocation&, bool) { return BoolExpr; } > ExpressionType createNull(const JSTokenLocation&) { return NullExpr; } > ExpressionType createBracketAccess(const JSTokenLocation&, ExpressionType, ExpressionType, bool, int, int, int) { return BracketExpr; } >- ExpressionType createDotAccess(const JSTokenLocation&, ExpressionType, const Identifier*, int, int, int) { return DotExpr; } >+ ExpressionType createDotAccess(const JSTokenLocation&, ExpressionType, const Identifier*, DotType type, int, int, int) { return type == DotType::PrivateName ? PrivateDotExpr : DotExpr; } > ExpressionType createRegExp(const JSTokenLocation&, const Identifier& pattern, const Identifier& flags, int) { return Yarr::hasError(Yarr::checkSyntax(pattern.string(), flags.string())) ? 0 : RegExpExpr; } > ExpressionType createNewExpr(const JSTokenLocation&, ExpressionType, int, int, int, int) { return NewExpr; } > ExpressionType createNewExpr(const JSTokenLocation&, ExpressionType, int, int) { return NewExpr; } >@@ -235,6 +235,10 @@ public: > { > return Property(type); > } >+ Property createProperty(const Identifier*, int, int, PropertyNode::Type type, PropertyNode::PutType, bool, SuperBinding, ClassElementTag) >+ { >+ return Property(type); >+ } > int createPropertyList(const JSTokenLocation&, Property) { return PropertyListResult; } > int createPropertyList(const JSTokenLocation&, Property, int) { return PropertyListResult; } > int createElementList(int, int) { return ElementsListResult; } >@@ -246,6 +250,7 @@ public: > int createClauseList(int) { return ClauseListResult; } > int createClauseList(int, int) { return ClauseListResult; } > int createFuncDeclStatement(const JSTokenLocation&, const ParserFunctionInfo<SyntaxChecker>&) { return StatementResult; } >+ int createDefineField(const JSTokenLocation&, const Identifier*, int, DefineFieldNode::Type) { return 0; } > int createClassDeclStatement(const JSTokenLocation&, ClassExpression, > const JSTextPosition&, const JSTextPosition&, int, int) { return StatementResult; } > int createBlockStatement(const JSTokenLocation&, int, int, int, VariableEnvironment&, DeclarationStacks::FunctionStack&&) { return StatementResult; } >@@ -395,7 +400,12 @@ public: > > bool isAssignmentLocation(ExpressionType type) > { >- return type == ResolveExpr || type == DotExpr || type == BracketExpr; >+ return type == ResolveExpr || type == DotExpr || type == PrivateDotExpr || type == BracketExpr; >+ } >+ >+ bool isPrivateLocation(ExpressionType type) >+ { >+ return type == PrivateDotExpr; > } > > bool isObjectLiteral(ExpressionType type) >diff --git a/Source/JavaScriptCore/parser/VariableEnvironment.cpp b/Source/JavaScriptCore/parser/VariableEnvironment.cpp >index 24fd233bca1..bbeff40ba94 100644 >--- a/Source/JavaScriptCore/parser/VariableEnvironment.cpp >+++ b/Source/JavaScriptCore/parser/VariableEnvironment.cpp >@@ -29,6 +29,16 @@ > > namespace JSC { > >+VariableEnvironment& VariableEnvironment::operator=(const VariableEnvironment& other) >+{ >+ m_map = other.m_map; >+ m_isEverythingCaptured = other.m_isEverythingCaptured; >+ m_rareData.reset(); >+ if (other.m_rareData) >+ m_rareData = std::make_unique<VariableEnvironmentRareData>(*other.m_rareData); >+ return *this; >+} >+ > void VariableEnvironment::markVariableAsCapturedIfDefined(const RefPtr<UniquedStringImpl>& identifier) > { > auto findResult = m_map.find(identifier); >@@ -80,6 +90,7 @@ void VariableEnvironment::swap(VariableEnvironment& other) > { > m_map.swap(other.m_map); > m_isEverythingCaptured = other.m_isEverythingCaptured; >+ m_rareData.swap(other.m_rareData); > } > > void VariableEnvironment::markVariableAsImported(const RefPtr<UniquedStringImpl>& identifier) >@@ -96,6 +107,55 @@ void VariableEnvironment::markVariableAsExported(const RefPtr<UniquedStringImpl> > findResult->value.setIsExported(); > } > >+void VariableEnvironment::markPrivateNameAsDeclared(const Identifier& identifier) >+{ >+ if (!m_rareData) >+ return; >+ auto findResult = m_rareData->m_privateNames.find(identifier.impl()); >+ RELEASE_ASSERT(findResult != m_rareData->m_privateNames.end()); >+ findResult->value.setIsDeclared(); >+} >+ >+bool VariableEnvironment::declarePrivateName(const Identifier& identifier) >+{ >+ if (!m_rareData) >+ m_rareData = std::make_unique<VariableEnvironmentRareData>(); >+ >+ auto findResult = m_rareData->m_privateNames.find(identifier.impl()); >+ >+ if (findResult == m_rareData->m_privateNames.end()) { >+ PrivateNameEntry entry(PrivateNameEntry::Traits::IsDeclared); >+ >+ auto addResult = m_rareData->m_privateNames.add(identifier.impl(), entry); >+ return addResult.isNewEntry; >+ } else { >+ bool returnValue = true; >+ >+ if (findResult->value.isDeclared()) >+ returnValue = false; // it was previously declared, error. >+ else { // it was previously used, mark it as declared. >+ PrivateNameEntry entry(PrivateNameEntry::Traits::IsDeclared | PrivateNameEntry::Traits::IsUsed); >+ auto addResult = m_rareData->m_privateNames.set(identifier.impl(), entry); >+ returnValue = !addResult.isNewEntry; >+ } >+ >+ return returnValue; >+ } >+} >+ >+void VariableEnvironment::usePrivateName(const Identifier& identifier) >+{ >+ if (!m_rareData) >+ m_rareData = std::make_unique<VariableEnvironmentRareData>(); >+ >+ PrivateNameEntry entry(PrivateNameEntry::Traits::IsUsed); >+ auto findResult = m_rareData->m_privateNames.find(identifier.impl()); >+ >+ if (findResult != m_rareData->m_privateNames.end() && findResult->value.isDeclared()) >+ entry.setIsDeclared(); >+ m_rareData->m_privateNames.set(identifier.impl(), entry); >+} >+ > CompactVariableEnvironment::CompactVariableEnvironment(const VariableEnvironment& env) > : m_isEverythingCaptured(env.isEverythingCaptured()) > { >diff --git a/Source/JavaScriptCore/parser/VariableEnvironment.h b/Source/JavaScriptCore/parser/VariableEnvironment.h >index 129b5ea6bd9..02ae581d9e4 100644 >--- a/Source/JavaScriptCore/parser/VariableEnvironment.h >+++ b/Source/JavaScriptCore/parser/VariableEnvironment.h >@@ -27,6 +27,8 @@ > > #include "Identifier.h" > #include <wtf/HashMap.h> >+#include <wtf/HashSet.h> >+#include <wtf/IteratorRange.h> > > namespace JSC { > >@@ -83,15 +85,54 @@ struct VariableEnvironmentEntryHashTraits : HashTraits<VariableEnvironmentEntry> > static const bool needsDestruction = false; > }; > >+struct PrivateNameEntry { >+public: >+ PrivateNameEntry(uint16_t traits = 0) { m_bits = traits; } >+ >+ ALWAYS_INLINE bool isUsed() const { return m_bits & IsUsed; } >+ ALWAYS_INLINE bool isDeclared() const { return m_bits & IsDeclared; } >+ >+ ALWAYS_INLINE void setIsUsed() { m_bits |= IsUsed; } >+ ALWAYS_INLINE void setIsDeclared() { m_bits |= IsDeclared; } >+ >+ uint16_t bits() const { return m_bits; } >+ >+ bool operator==(const PrivateNameEntry& other) const >+ { >+ return m_bits == other.m_bits; >+ } >+ >+ enum Traits : uint16_t { >+ IsUsed = 1 << 0, >+ IsDeclared = 1 << 1, >+ }; >+ >+private: >+ uint16_t m_bits { 0 }; >+}; >+ >+struct PrivateNameEntryHashTraits : HashTraits<PrivateNameEntry> { >+ static const bool needsDestruction = false; >+}; >+ > class VariableEnvironment { > private: > typedef HashMap<RefPtr<UniquedStringImpl>, VariableEnvironmentEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>, VariableEnvironmentEntryHashTraits> Map; >+ typedef HashMap<RefPtr<UniquedStringImpl>, PrivateNameEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>, PrivateNameEntryHashTraits> PrivateNames; > public: >- VariableEnvironment() = default; >- VariableEnvironment(VariableEnvironment&& other) = default; >- VariableEnvironment(const VariableEnvironment&) = default; >- VariableEnvironment& operator=(const VariableEnvironment&) = default; >- VariableEnvironment& operator=(VariableEnvironment&&) = default; >+ VariableEnvironment() >+ { } >+ VariableEnvironment(VariableEnvironment&& other) >+ : m_map(WTFMove(other.m_map)) >+ , m_isEverythingCaptured(other.m_isEverythingCaptured) >+ , m_rareData(WTFMove(other.m_rareData)) >+ { } >+ VariableEnvironment(const VariableEnvironment& other) >+ : m_map(other.m_map) >+ , m_isEverythingCaptured(other.m_isEverythingCaptured) >+ , m_rareData(other.m_rareData ? std::make_unique<VariableEnvironmentRareData>(*other.m_rareData) : nullptr) >+ { } >+ VariableEnvironment& operator=(const VariableEnvironment& other); > > ALWAYS_INLINE Map::iterator begin() { return m_map.begin(); } > ALWAYS_INLINE Map::iterator end() { return m_map.end(); } >@@ -99,7 +140,7 @@ public: > ALWAYS_INLINE Map::const_iterator end() const { return m_map.end(); } > ALWAYS_INLINE Map::AddResult add(const RefPtr<UniquedStringImpl>& identifier) { return m_map.add(identifier, VariableEnvironmentEntry()); } > ALWAYS_INLINE Map::AddResult add(const Identifier& identifier) { return add(identifier.impl()); } >- ALWAYS_INLINE unsigned size() const { return m_map.size(); } >+ ALWAYS_INLINE unsigned size() const { return m_map.size() + privateNamesSize(); } > ALWAYS_INLINE bool contains(const RefPtr<UniquedStringImpl>& identifier) const { return m_map.contains(identifier); } > ALWAYS_INLINE bool remove(const RefPtr<UniquedStringImpl>& identifier) { return m_map.remove(identifier); } > ALWAYS_INLINE Map::iterator find(const RefPtr<UniquedStringImpl>& identifier) { return m_map.find(identifier); } >@@ -115,9 +156,62 @@ public: > > bool isEverythingCaptured() const { return m_isEverythingCaptured; } > >+ typedef WTF::IteratorRange<PrivateNames::iterator> PrivateNamesRange; >+ >+ bool declarePrivateName(const Identifier& identifier); >+ void usePrivateName(const Identifier& identifier); >+ >+ ALWAYS_INLINE PrivateNamesRange privateNames() const >+ { >+ ASSERT(privateNamesSize() > 0); >+ return PrivateNamesRange(m_rareData->m_privateNames.begin(), m_rareData->m_privateNames.end()); >+ } >+ >+ ALWAYS_INLINE unsigned privateNamesSize() const >+ { >+ if (!m_rareData) >+ return 0; >+ return m_rareData->m_privateNames.size(); >+ } >+ >+ ALWAYS_INLINE bool hasPrivateName(const Identifier& identifier) >+ { >+ if (!m_rareData) >+ return false; >+ return m_rareData->m_privateNames.contains(identifier.impl()); >+ } >+ >+ ALWAYS_INLINE void markPrivateNameAsDeclared(const Identifier& identifier); >+ >+ ALWAYS_INLINE void copyPrivateNames(VariableEnvironment& other) >+ { >+ 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); >+ } >+ } >+ } > private: > 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; >+ }; >+ >+ std::unique_ptr<VariableEnvironmentRareData> m_rareData; > }; > > class CompactVariableEnvironment { >diff --git a/Source/JavaScriptCore/runtime/CommonIdentifiers.cpp b/Source/JavaScriptCore/runtime/CommonIdentifiers.cpp >index 197370d110a..1d08b317cb1 100644 >--- a/Source/JavaScriptCore/runtime/CommonIdentifiers.cpp >+++ b/Source/JavaScriptCore/runtime/CommonIdentifiers.cpp >@@ -32,6 +32,7 @@ namespace JSC { > #define INITIALIZE_KEYWORD(name) , name##Keyword(Identifier::fromString(vm, #name)) > #define INITIALIZE_PRIVATE_NAME(name) , name##PrivateName(m_builtinNames->name##PrivateName()) > #define INITIALIZE_SYMBOL(name) , name##Symbol(m_builtinNames->name##Symbol()) >+#define INITIALIZE_PRIVATE_FIELD_NAME(name) , name##PrivateField(Identifier::fromString(vm, "#" #name)) > > CommonIdentifiers::CommonIdentifiers(VM* vm) > : nullIdentifier() >@@ -44,6 +45,7 @@ CommonIdentifiers::CommonIdentifiers(VM* vm) > JSC_COMMON_IDENTIFIERS_EACH_KEYWORD(INITIALIZE_KEYWORD) > JSC_COMMON_IDENTIFIERS_EACH_PROPERTY_NAME(INITIALIZE_PROPERTY_NAME) > JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(INITIALIZE_SYMBOL) >+ JSC_COMMON_IDENTIFIERS_EACH_PRIVATE_FIELD(INITIALIZE_PRIVATE_FIELD_NAME) > { > } > >diff --git a/Source/JavaScriptCore/runtime/CommonIdentifiers.h b/Source/JavaScriptCore/runtime/CommonIdentifiers.h >index 80227f77a82..2c207c57293 100644 >--- a/Source/JavaScriptCore/runtime/CommonIdentifiers.h >+++ b/Source/JavaScriptCore/runtime/CommonIdentifiers.h >@@ -222,6 +222,9 @@ > macro(writable) \ > macro(year) > >+#define JSC_COMMON_IDENTIFIERS_EACH_PRIVATE_FIELD(macro) \ >+ macro(constructor) >+ > #define JSC_COMMON_IDENTIFIERS_EACH_KEYWORD(macro) \ > macro(await) \ > macro(break) \ >@@ -321,6 +324,10 @@ namespace JSC { > JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(JSC_IDENTIFIER_DECLARE_PRIVATE_WELL_KNOWN_SYMBOL_GLOBAL) > #undef JSC_IDENTIFIER_DECLARE_PRIVATE_WELL_KNOWN_SYMBOL_GLOBAL > >+#define JSC_IDENTIFIER_DECLARE_PRIVATE_FIELD_GLOBAL(name) const Identifier name##PrivateField; >+ JSC_COMMON_IDENTIFIERS_EACH_PRIVATE_FIELD(JSC_IDENTIFIER_DECLARE_PRIVATE_FIELD_GLOBAL) >+#undef JSC_IDENTIFIER_DECLARE_PRIVATE_FIELD_GLOBAL >+ > const Identifier* lookUpPrivateName(const Identifier&) const; > Identifier lookUpPublicName(const Identifier&) const; > >diff --git a/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp b/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp >index 1c33aea6c2f..15e9efece26 100644 >--- a/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp >+++ b/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp >@@ -756,6 +756,12 @@ SLOW_PATH_DECL(slow_path_to_primitive) > RETURN(OP_C(2).jsValue().toPrimitive(exec)); > } > >+SLOW_PATH_DECL(slow_path_to_property_key) >+{ >+ BEGIN(); >+ RETURN(OP_C(2).jsValue().toPropertyKey(exec, JSValue::Tag)); >+} >+ > SLOW_PATH_DECL(slow_path_enter) > { > BEGIN(); >@@ -1212,4 +1218,195 @@ SLOW_PATH_DECL(slow_path_spread) > RETURN(JSFixedArray::createFromArray(exec, vm, array)); > } > >+SLOW_PATH_DECL(slow_path_add_private_field) >+{ >+ BEGIN(); >+ ::printf("slow_path_add_private_field\n"); >+ CodeBlock* codeBlock = exec->codeBlock(); >+ >+ JSValue baseValue = OP_C(1).jsValue(); >+ ASSERT(baseValue.isObject()); >+ const Identifier& name = codeBlock->identifier(pc[2].u.operand); >+ JSValue value = OP_C(3).jsValue(); >+ JSScope* scope = jsCast<JSScope*>(OP_C(4).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 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 = pc[5].u.structureID; >+ 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. >+ pc[5].u.pointer = nullptr; // old structure >+ pc[6].u.pointer = nullptr; // offset >+ pc[7].u.pointer = nullptr; // new structure >+ >+ 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()); >+ pc[5].u.structureID = structure->previousID()->id(); >+ pc[6].u.operand = slot.cachedOffset(); >+ pc[7].u.structureID = structure->id(); >+ } >+ } >+ } >+ >+ END(); >+} >+ >+SLOW_PATH_DECL(slow_path_get_private_field) >+{ >+ BEGIN(); >+ >+ JSValue baseValue = OP_C(2).jsValue(); >+ if (!baseValue.isObject()) >+ THROW(createTypeError(exec, "Attempted to access invalid private field")); >+ >+ const Identifier& name = exec->codeBlock()->identifier(pc[3].u.operand); >+ JSScope* scope = jsCast<JSScope*>(OP_C(4).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")); >+ >+ if (!LLINT_ALWAYS_ACCESS_SLOW && slot.isCacheable()) { >+ { >+ StructureID oldStructureID = pc[5].u.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. >+ pc[5].u.pointer = nullptr; // old structure >+ pc[6].u.pointer = nullptr; // offset >+ >+ CodeBlock* codeBlock = exec->codeBlock(); >+ if (structure->propertyAccessesAreCacheable() >+ && !structure->needImpurePropertyWatchpoint()) { >+ vm.heap.writeBarrier(codeBlock); >+ >+ ConcurrentJSLocker locker(codeBlock->m_lock); >+ >+ pc[5].u.structureID = structure->id(); >+ pc[6].u.operand = slot.cachedOffset(); >+ } >+ } >+ } >+ >+ RETURN_PROFILED(op_get_private_field, slot.getValue(exec, privateName)); >+} >+ >+SLOW_PATH_DECL(slow_path_put_private_field) >+{ >+ BEGIN(); >+ CodeBlock* codeBlock = exec->codeBlock(); >+ JSValue baseValue = OP_C(1).jsValue(); >+ if (!baseValue.isObject()) >+ THROW(createTypeError(exec, "Attempted to access invalid private field")); >+ >+ const Identifier& name = codeBlock->identifier(pc[2].u.operand); >+ JSValue value = OP_C(3).jsValue(); >+ JSScope* scope = jsCast<JSScope*>(OP_C(4).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 = pc[5].u.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. >+ pc[5].u.pointer = nullptr; // old structure >+ pc[6].u.pointer = nullptr; // offset >+ >+ 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()); >+ pc[5].u.structureID = structure->id(); >+ pc[6].u.operand = slot.cachedOffset(); >+ } >+ } >+ >+ END(); >+} >+ > } // namespace JSC >diff --git a/Source/JavaScriptCore/runtime/CommonSlowPaths.h b/Source/JavaScriptCore/runtime/CommonSlowPaths.h >index 1ece89592cd..3942a94f91c 100644 >--- a/Source/JavaScriptCore/runtime/CommonSlowPaths.h >+++ b/Source/JavaScriptCore/runtime/CommonSlowPaths.h >@@ -337,6 +337,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); >@@ -364,6 +365,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*, Instruction*); > >diff --git a/Source/JavaScriptCore/runtime/Identifier.h b/Source/JavaScriptCore/runtime/Identifier.h >index 1a308ae4297..350d743ce0e 100644 >--- a/Source/JavaScriptCore/runtime/Identifier.h >+++ b/Source/JavaScriptCore/runtime/Identifier.h >@@ -301,6 +301,11 @@ struct IdentifierMapIndexHashTraits : HashTraits<int> { > static const bool emptyValueIsZero = false; > }; > >+struct StringRepHash : PtrHash<RefPtr<UniquedStringImpl>> { >+ static unsigned hash(const RefPtr<UniquedStringImpl>& key) { return key->existingHash(); } >+ static unsigned hash(UniquedStringImpl* key) { return key->existingHash(); } >+}; >+ > typedef HashSet<RefPtr<UniquedStringImpl>, IdentifierRepHash> IdentifierSet; > typedef HashMap<RefPtr<UniquedStringImpl>, int, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>, IdentifierMapIndexHashTraits> IdentifierMap; > typedef HashMap<UniquedStringImpl*, int, IdentifierRepHash, HashTraits<UniquedStringImpl*>, IdentifierMapIndexHashTraits> BorrowedIdentifierMap; >diff --git a/Source/JavaScriptCore/runtime/JSCJSValue.h b/Source/JavaScriptCore/runtime/JSCJSValue.h >index 5442277699b..8b4d82004b1 100644 >--- a/Source/JavaScriptCore/runtime/JSCJSValue.h >+++ b/Source/JavaScriptCore/runtime/JSCJSValue.h >@@ -174,6 +174,7 @@ public: > enum JSFalseTag { JSFalse }; > enum JSCellTag { JSCellType }; > enum EncodeAsDoubleTag { EncodeAsDouble }; >+ enum JSValueTag { Tag }; > > JSValue(); > JSValue(JSNullTag); >@@ -266,6 +267,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 5aa41e4374a..231a2aca53a 100644 >--- a/Source/JavaScriptCore/runtime/JSCJSValueInlines.h >+++ b/Source/JavaScriptCore/runtime/JSCJSValueInlines.h >@@ -664,6 +664,22 @@ ALWAYS_INLINE Identifier JSValue::toPropertyKey(ExecState* exec) const > return 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 5143f7e613b..373edee29f0 100644 >--- a/Source/JavaScriptCore/runtime/JSFunction.cpp >+++ b/Source/JavaScriptCore/runtime/JSFunction.cpp >@@ -395,6 +395,7 @@ EncodedJSValue JSFunction::callerGetter(ExecState* exec, EncodedJSValue thisValu > case SourceParseMode::AsyncGeneratorWrapperFunctionMode: > case SourceParseMode::AsyncGeneratorWrapperMethodMode: > case SourceParseMode::GeneratorWrapperMethodMode: >+ case SourceParseMode::InstanceFieldInitializerMode: > if (!function->jsExecutable()->isStrictMode()) > return JSValue::encode(caller); > return JSValue::encode(throwTypeError(exec, scope, "Function.caller used to retrieve strict caller"_s)); >diff --git a/Source/JavaScriptCore/runtime/JSScope.cpp b/Source/JavaScriptCore/runtime/JSScope.cpp >index ccc00156102..6f2ba30ef83 100644 >--- a/Source/JavaScriptCore/runtime/JSScope.cpp >+++ b/Source/JavaScriptCore/runtime/JSScope.cpp >@@ -416,4 +416,16 @@ JSValue JSScope::toThis(JSCell*, ExecState* exec, ECMAMode ecmaMode) > return exec->globalThisValue(); > } > >+PrivateSymbolImpl* JSScope::getPrivateSymbol(VM& vm, JSScope* scope, const Identifier& name) >+{ >+ for (; scope; scope = scope->next()) { >+ if (JSSymbolTableObject* symbolTableObject = jsDynamicCast<JSSymbolTableObject*>(vm, scope)) { >+ if (PrivateSymbolImpl* uid = symbolTableObject->getPrivateSymbol(name)) >+ return uid; >+ } >+ } >+ >+ return nullptr; >+} >+ > } // namespace JSC >diff --git a/Source/JavaScriptCore/runtime/JSScope.h b/Source/JavaScriptCore/runtime/JSScope.h >index f6b773a7ad1..0c4c1334f98 100644 >--- a/Source/JavaScriptCore/runtime/JSScope.h >+++ b/Source/JavaScriptCore/runtime/JSScope.h >@@ -58,6 +58,8 @@ public: > > static void visitChildren(JSCell*, SlotVisitor&); > >+ static PrivateSymbolImpl* getPrivateSymbol(VM&, JSScope*, const Identifier&); >+ > bool isVarScope(); > bool isLexicalScope(); > bool isModuleScope(); >diff --git a/Source/JavaScriptCore/runtime/JSSymbolTableObject.h b/Source/JavaScriptCore/runtime/JSSymbolTableObject.h >index 0e8644eaea2..aaf2eb54c5e 100644 >--- a/Source/JavaScriptCore/runtime/JSSymbolTableObject.h >+++ b/Source/JavaScriptCore/runtime/JSSymbolTableObject.h >@@ -48,6 +48,17 @@ public: > > static ptrdiff_t offsetOfSymbolTable() { return OBJECT_OFFSETOF(JSSymbolTableObject, m_symbolTable); } > >+ PrivateSymbolImpl* getPrivateSymbol(const Identifier& name) >+ { >+ if (m_rareData) { >+ auto it = m_rareData->m_privateNames.find(name.impl()); >+ if (it != m_rareData->m_privateNames.end()) >+ return it->value.get(); >+ } >+ >+ return nullptr; >+ } >+ > DECLARE_EXPORT_INFO; > > protected: >@@ -62,18 +73,29 @@ protected: > ASSERT(symbolTable); > setSymbolTable(vm, symbolTable); > } >- >+ > void setSymbolTable(VM& vm, SymbolTable* symbolTable) > { > ASSERT(!m_symbolTable); > symbolTable->singletonScope()->notifyWrite(vm, this, "Allocated a scope"); > m_symbolTable.set(vm, this, symbolTable); >+ if (symbolTable->hasPrivateNames()) { >+ m_rareData = std::make_unique<JSSymbolTableObjectRareData>(); >+ for (auto key : symbolTable->privateNames()) >+ m_rareData->m_privateNames.set(key.get(), PrivateSymbolImpl::create(*key.get())); >+ } > } >- >+ > static void visitChildren(JSCell*, SlotVisitor&); > > private: >+ typedef HashMap<RefPtr<UniquedStringImpl>, RefPtr<PrivateSymbolImpl>, StringRepHash> PrivateNameMap; >+ struct JSSymbolTableObjectRareData { >+ PrivateNameMap m_privateNames; >+ }; >+ > WriteBarrier<SymbolTable> m_symbolTable; >+ std::unique_ptr<JSSymbolTableObjectRareData> m_rareData; > }; > > template<typename SymbolTableObjectType> >diff --git a/Source/JavaScriptCore/runtime/SymbolTable.cpp b/Source/JavaScriptCore/runtime/SymbolTable.cpp >index 6b95a0ed863..205532bba96 100644 >--- a/Source/JavaScriptCore/runtime/SymbolTable.cpp >+++ b/Source/JavaScriptCore/runtime/SymbolTable.cpp >@@ -187,6 +187,14 @@ SymbolTable* SymbolTable::cloneScopePart(VM& vm) > for (; iter != end; ++iter) > result->m_rareData->m_uniqueTypeSetMap.set(iter->key, iter->value); > } >+ >+ >+ { >+ auto iter = m_rareData->m_privateNames.begin(); >+ auto end = m_rareData->m_privateNames.end(); >+ for (; iter != end; ++iter) >+ result->m_rareData->m_privateNames.add(*iter); >+ } > } > > return result; >diff --git a/Source/JavaScriptCore/runtime/SymbolTable.h b/Source/JavaScriptCore/runtime/SymbolTable.h >index 9157d5b8292..f720dd1fb3f 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 { > >@@ -445,6 +445,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) > { >@@ -583,7 +585,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) > { >@@ -609,7 +632,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 >@@ -708,6 +731,7 @@ private: > OffsetToVariableMap m_offsetToVariableMap; > UniqueTypeSetMap m_uniqueTypeSetMap; > WriteBarrier<CodeBlock> m_codeBlock; >+ PrivateNameSet m_privateNames; > }; > std::unique_ptr<SymbolTableRareData> m_rareData; > >-- >2.17.1 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Flags:
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