- Private names:
- Improve error messages for symbols. Symbols should be statically known, so we should be able to throw at link-time if a private name can't be found. This may involve new bytecodes for loading private names + fixing the IC property loads to throw the right error for private names --- it may also involve creating a new flag for SymbolImpl to indicate that a name is really an ECMA262 "PrivateName" and not a WebKit/JSC "PrivateName" (the lexer patch renames some things to call those "PrivateIdentifiers" for this purpose)
- SetFunctionName with a PrivateName is very likely not doing the right thing at all, though this has not been tested yet.
- Computed property names:
- Computed properties have not been worked on at all for class fields. They should be a little bit simpler than PrivateNames, but they still need to be stored somewhere for use within the initializer function.
- The initializer function:
- We may want to inline this inside the constructor and super calls at bytecode generation, rather than a separate function call. Re-parsing each class field is probably not significantly faster (possibly slower) than the cost of a Generatorification-like pass to insert field initialization during bytecode generation. The difficulty is related to scoping, but it's surely doable. 1 Optimization: v8 has added a "boilerplate" system for class literals, similar to their boilerplates for Object and Array literals --- this allows automatic instantiation of compile-time constants, including methods, primitive properties, nested class/object/array literals, etc. We could do something similar in JSC, but there's no point starting this until we have the basics 100% working.
Last active
April 19, 2018 21:53
-
-
Save caitp/21bdd80467b6673fd8a32a05e768b509 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 4cb549619bcf39b16575f4a5bebf264a8e1b8238 Mon Sep 17 00:00:00 2001 | |
From: Caitlin Potter <[email protected]> | |
Date: Fri, 13 Apr 2018 16:59:01 -0400 | |
Subject: [PATCH 1/3] [JSC] add limited support for class fields | |
supports a very basic subset of the class fields proposal | |
--- | |
Source/JavaScriptCore/bytecode/ExecutableInfo.h | 5 +- | |
.../JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp | 1 + | |
Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h | 2 + | |
.../bytecode/UnlinkedFunctionExecutable.cpp | 5 +- | |
.../bytecode/UnlinkedFunctionExecutable.h | 7 ++ | |
.../bytecompiler/BytecodeGenerator.cpp | 81 ++++++++++++++++++++-- | |
.../bytecompiler/BytecodeGenerator.h | 10 ++- | |
.../JavaScriptCore/bytecompiler/NodesCodegen.cpp | 51 ++++++++++++-- | |
Source/JavaScriptCore/parser/ASTBuilder.h | 5 ++ | |
Source/JavaScriptCore/parser/NodeConstructors.h | 7 ++ | |
Source/JavaScriptCore/parser/Nodes.h | 18 ++++- | |
Source/JavaScriptCore/parser/Parser.cpp | 77 +++++++++++++++++++- | |
Source/JavaScriptCore/parser/Parser.h | 17 +++-- | |
Source/JavaScriptCore/parser/ParserModes.h | 4 +- | |
Source/JavaScriptCore/parser/SyntaxChecker.h | 1 + | |
Source/JavaScriptCore/runtime/CommonIdentifiers.h | 3 +- | |
Source/JavaScriptCore/runtime/EvalExecutable.h | 2 +- | |
.../runtime/ModuleProgramExecutable.h | 2 +- | |
Source/JavaScriptCore/runtime/ProgramExecutable.h | 2 +- | |
19 files changed, 269 insertions(+), 31 deletions(-) | |
diff --git a/Source/JavaScriptCore/bytecode/ExecutableInfo.h b/Source/JavaScriptCore/bytecode/ExecutableInfo.h | |
index 750900ecda4..ca9682f5aba 100644 | |
--- a/Source/JavaScriptCore/bytecode/ExecutableInfo.h | |
+++ b/Source/JavaScriptCore/bytecode/ExecutableInfo.h | |
@@ -35,7 +35,7 @@ enum class EvalContextType : uint8_t { None, FunctionEvalContext }; | |
// FIXME: These flags, ParserModes and propagation to XXXCodeBlocks should be reorganized. | |
// https://bugs.webkit.org/show_bug.cgi?id=151547 | |
struct ExecutableInfo { | |
- ExecutableInfo(bool usesEval, bool isStrictMode, bool isConstructor, bool isBuiltinFunction, ConstructorKind constructorKind, JSParserScriptMode scriptMode, SuperBinding superBinding, SourceParseMode parseMode, DerivedContextType derivedContextType, bool isArrowFunctionContext, bool isClassContext, EvalContextType evalContextType) | |
+ ExecutableInfo(bool usesEval, bool isStrictMode, bool isConstructor, bool isBuiltinFunction, ConstructorKind constructorKind, JSParserScriptMode scriptMode, SuperBinding superBinding, SourceParseMode parseMode, DerivedContextType derivedContextType, bool isArrowFunctionContext, bool isClassContext, EvalContextType evalContextType, bool requiresInstanceFieldInitialization) | |
: m_usesEval(usesEval) | |
, m_isStrictMode(isStrictMode) | |
, m_isConstructor(isConstructor) | |
@@ -48,6 +48,7 @@ struct ExecutableInfo { | |
, m_isArrowFunctionContext(isArrowFunctionContext) | |
, m_isClassContext(isClassContext) | |
, m_evalContextType(static_cast<unsigned>(evalContextType)) | |
+ , m_requiresInstanceFieldInitialization(static_cast<unsigned>(requiresInstanceFieldInitialization)) | |
{ | |
ASSERT(m_constructorKind == static_cast<unsigned>(constructorKind)); | |
ASSERT(m_superBinding == static_cast<unsigned>(superBinding)); | |
@@ -66,6 +67,7 @@ struct ExecutableInfo { | |
EvalContextType evalContextType() const { return static_cast<EvalContextType>(m_evalContextType); } | |
bool isArrowFunctionContext() const { return m_isArrowFunctionContext; } | |
bool isClassContext() const { return m_isClassContext; } | |
+ bool requiresInstanceFieldInitialization() const { return m_requiresInstanceFieldInitialization; } | |
private: | |
unsigned m_usesEval : 1; | |
@@ -80,6 +82,7 @@ private: | |
unsigned m_isArrowFunctionContext : 1; | |
unsigned m_isClassContext : 1; | |
unsigned m_evalContextType : 2; | |
+ unsigned m_requiresInstanceFieldInitialization : 1; | |
}; | |
} // namespace JSC | |
diff --git a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp | |
index cefe3ce4188..ed4d566a92e 100644 | |
--- a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp | |
+++ b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp | |
@@ -72,6 +72,7 @@ UnlinkedCodeBlock::UnlinkedCodeBlock(VM* vm, Structure* structure, CodeType code | |
, m_derivedContextType(static_cast<unsigned>(info.derivedContextType())) | |
, m_evalContextType(static_cast<unsigned>(info.evalContextType())) | |
, m_hasTailCalls(false) | |
+ , m_requiresInstanceFieldInitialization(static_cast<unsigned>(info.requiresInstanceFieldInitialization())) | |
, m_lineCount(0) | |
, m_endColumn(UINT_MAX) | |
, m_didOptimize(MixedTriState) | |
diff --git a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h | |
index 6acb500f89d..14bcdd2b1e2 100644 | |
--- a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h | |
+++ b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h | |
@@ -134,6 +134,7 @@ public: | |
bool isClassContext() const { return m_isClassContext; } | |
bool hasTailCalls() const { return m_hasTailCalls; } | |
void setHasTailCalls() { m_hasTailCalls = true; } | |
+ bool requiresInstanceFieldInitialization() const { return m_requiresInstanceFieldInitialization; } | |
bool allowDirectEvalCache() const { return !(m_features & NoEvalCacheFeature); } | |
void addExpressionInfo(unsigned instructionOffset, int divot, | |
@@ -454,6 +455,7 @@ private: | |
unsigned m_derivedContextType : 2; | |
unsigned m_evalContextType : 2; | |
unsigned m_hasTailCalls : 1; | |
+ unsigned m_requiresInstanceFieldInitialization : 1; | |
unsigned m_lineCount; | |
unsigned m_endColumn; | |
diff --git a/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp b/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp | |
index cff726a4f86..12f02dc10da 100644 | |
--- a/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp | |
+++ b/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp | |
@@ -54,7 +54,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()); | |
@@ -65,8 +65,9 @@ static UnlinkedFunctionCodeBlock* generateUnlinkedFunctionCodeBlock( | |
executable->recordParse(function->features(), function->hasCapturedVariables()); | |
bool isClassContext = executable->superBinding() == SuperBinding::Needed; | |
+ bool requiresInstanceFieldInitialization = false; // TODO(caitp): fix this | |
- UnlinkedFunctionCodeBlock* result = UnlinkedFunctionCodeBlock::create(&vm, FunctionCode, ExecutableInfo(function->usesEval(), function->isStrictMode(), kind == CodeForConstruct, functionKind == UnlinkedBuiltinFunction, executable->constructorKind(), scriptMode, executable->superBinding(), parseMode, executable->derivedContextType(), false, isClassContext, EvalContextType::FunctionEvalContext), debuggerMode); | |
+ UnlinkedFunctionCodeBlock* result = UnlinkedFunctionCodeBlock::create(&vm, FunctionCode, ExecutableInfo(function->usesEval(), function->isStrictMode(), kind == CodeForConstruct, functionKind == UnlinkedBuiltinFunction, executable->constructorKind(), scriptMode, executable->superBinding(), parseMode, executable->derivedContextType(), false, isClassContext, EvalContextType::FunctionEvalContext, requiresInstanceFieldInitialization), debuggerMode); | |
error = BytecodeGenerator::generate(vm, function.get(), source, result, debuggerMode, executable->parentScopeTDZVariables()); | |
diff --git a/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.h b/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.h | |
index de0bfecb5c9..dfb31171aca 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" | |
@@ -138,6 +139,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&, SourceCode&& parentSourceOverride, FunctionMetadataNode*, UnlinkedFunctionKind, ConstructAbility, JSParserScriptMode, VariableEnvironment&, JSC::DerivedContextType); | |
@@ -178,6 +182,9 @@ private: | |
VariableEnvironment 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 d1d4192a8ff..92b8cd27a74 100644 | |
--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp | |
+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp | |
@@ -648,6 +648,9 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, Unlinke | |
emitMoveEmptyValue(&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 { | |
@@ -3038,6 +3041,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 = emitGetById(newTemporary(), constructor, propertyNames().instanceFieldInitializerSymbol); | |
+ 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); | |
@@ -3336,6 +3353,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)); | |
@@ -3354,28 +3405,44 @@ RegisterID* BytecodeGenerator::emitNewFunction(RegisterID* dst, FunctionMetadata | |
return dst; | |
} | |
-void BytecodeGenerator::emitSetFunctionNameIfNeeded(ExpressionNode* valueNode, RegisterID* value, RegisterID* name) | |
+bool BytecodeGenerator::shouldEmitSetFunctionName(ExpressionNode* valueNode) | |
{ | |
if (valueNode->isBaseFuncExprNode()) { | |
FunctionMetadataNode* metadata = static_cast<BaseFuncExprNode*>(valueNode)->metadata(); | |
if (!metadata->ecmaName().isNull()) | |
- return; | |
+ return false; | |
} else if (valueNode->isClassExprNode()) { | |
ClassExprNode* classExprNode = static_cast<ClassExprNode*>(valueNode); | |
if (!classExprNode->ecmaName().isNull()) | |
- return; | |
+ return false; | |
if (classExprNode->hasStaticProperty(m_vm->propertyNames->name)) | |
- return; | |
+ return false; | |
} else | |
- return; | |
+ return false; | |
+ | |
+ return true; | |
+} | |
- // FIXME: We should use an op_call to an internal function here instead. | |
- // https://bugs.webkit.org/show_bug.cgi?id=155547 | |
+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); | |
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h | |
index b7255ac6216..f651efb481c 100644 | |
--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h | |
+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h | |
@@ -377,6 +377,7 @@ namespace JSC { | |
ConstructorKind constructorKind() const { return m_codeBlock->constructorKind(); } | |
SuperBinding superBinding() const { return m_codeBlock->superBinding(); } | |
JSParserScriptMode scriptMode() const { return m_codeBlock->scriptMode(); } | |
+ bool requiresInstanceFieldInitialization() const { return m_codeBlock->requiresInstanceFieldInitialization(); } | |
template<typename Node, typename UnlinkedCodeBlock> | |
static ParserError generate(VM& vm, Node* node, const SourceCode& sourceCode, UnlinkedCodeBlock* unlinkedCodeBlock, DebuggerMode debuggerMode, const VariableEnvironment* environment) | |
@@ -531,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) | |
@@ -661,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); | |
@@ -674,10 +676,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* emitMoveLinkTimeConstant(RegisterID* dst, LinkTimeConstant); | |
diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp | |
index 07a9c907a7e..5f5d5df1b39 100644 | |
--- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp | |
+++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp | |
@@ -526,13 +526,17 @@ 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 (instanceFieldLocations && dst == prototype && !p->m_node->needsSuperBinding()) { | |
+ instanceFieldLocations->append(p->position()); | |
+ continue; | |
+ } | |
emitPutConstantProperty(generator, dst, *p->m_node); | |
} | |
@@ -579,6 +583,11 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe | |
for (; p; p = p->m_next) { | |
PropertyNode* node = p->m_node; | |
dst = node->isInstanceClassProperty() ? prototype : dstOrConstructor; | |
+ if (instanceFieldLocations && dst == prototype && !p->m_node->needsSuperBinding()) { | |
+ instanceFieldLocations->append(p->position()); | |
+ continue; | |
+ } | |
+ | |
// Handle regular values. | |
if (node->m_type & PropertyNode::Constant) { | |
@@ -884,7 +893,18 @@ RegisterID* FunctionCallValueNode::emitBytecode(BytecodeGenerator& generator, Re | |
if (generator.isDerivedConstructorContext() || doWeUseArrowFunctionInConstructor) | |
generator.emitPutThisToArrowFunctionContextScope(); | |
- | |
+ | |
+ if (generator.isDerivedConstructorContext() || generator.requiresInstanceFieldInitialization()) { | |
+ RegisterID callee; | |
+ callee.setIndex(CallFrameSlot::callee); | |
+ | |
+ RefPtr<RegisterID> constructor = &callee; | |
+ if (generator.isDerivedConstructorContext()) | |
+ constructor = generator.emitLoadDerivedConstructorFromArrowFunctionLexicalEnvironment(); | |
+ | |
+ generator.emitInstanceFieldInitializationIfNeeded(ret, constructor.get(), divot(), divotStart(), divotEnd()); | |
+ } | |
+ | |
return ret; | |
} | |
RefPtr<RegisterID> func = generator.emitNode(m_expr); | |
@@ -3953,6 +3973,23 @@ RegisterID* AwaitExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID | |
return generator.emitMove(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 (generator.shouldEmitSetFunctionName(m_assign)) | |
+ generator.emitSetFunctionName(value.get(), *m_ident); | |
+ } | |
+ | |
+ generator.emitDirectPutById(generator.thisRegister(), *m_ident, value.get(), PropertyNode::Unknown); | |
+} | |
+ | |
// ------------------------------ ClassDeclNode --------------------------------- | |
void ClassDeclNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) | |
@@ -4030,8 +4067,14 @@ 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); | |
+ generator.emitDirectPutById(constructor.get(), generator.propertyNames().instanceFieldInitializerSymbol, instanceFieldInitializer.get(), PropertyNode::Unknown); | |
+ } | |
+ } | |
if (!m_name.isNull()) { | |
Variable classNameVar = generator.variable(m_name); | |
diff --git a/Source/JavaScriptCore/parser/ASTBuilder.h b/Source/JavaScriptCore/parser/ASTBuilder.h | |
index 5c6dd33198c..f9b43c6536c 100644 | |
--- a/Source/JavaScriptCore/parser/ASTBuilder.h | |
+++ b/Source/JavaScriptCore/parser/ASTBuilder.h | |
@@ -396,6 +396,11 @@ public: | |
return node; | |
} | |
+ DefineFieldNode* createDefineField(const JSTokenLocation& location, const Identifier* ident, ExpressionNode* initializer) | |
+ { | |
+ return new (m_parserArena) DefineFieldNode(location, ident, initializer); | |
+ } | |
+ | |
ClassExprNode* createClassExpr(const JSTokenLocation& location, const ParserClassInfo<ASTBuilder>& classInfo, VariableEnvironment& classEnvironment, ExpressionNode* constructor, | |
ExpressionNode* parentClass, PropertyListNode* classElements) | |
{ | |
diff --git a/Source/JavaScriptCore/parser/NodeConstructors.h b/Source/JavaScriptCore/parser/NodeConstructors.h | |
index e6f03b65b75..937e057f858 100644 | |
--- a/Source/JavaScriptCore/parser/NodeConstructors.h | |
+++ b/Source/JavaScriptCore/parser/NodeConstructors.h | |
@@ -987,6 +987,13 @@ namespace JSC { | |
{ | |
} | |
+ inline DefineFieldNode::DefineFieldNode(const JSTokenLocation& location, const Identifier* ident, ExpressionNode* assign) | |
+ : StatementNode(location) | |
+ , m_ident(ident) | |
+ , m_assign(assign) | |
+ { | |
+ } | |
+ | |
inline ClassDeclNode::ClassDeclNode(const JSTokenLocation& location, ExpressionNode* classDeclaration) | |
: StatementNode(location) | |
, m_classDeclaration(classDeclaration) | |
diff --git a/Source/JavaScriptCore/parser/Nodes.h b/Source/JavaScriptCore/parser/Nodes.h | |
index 6c0f2dbd0c1..8a767c03b8c 100644 | |
--- a/Source/JavaScriptCore/parser/Nodes.h | |
+++ b/Source/JavaScriptCore/parser/Nodes.h | |
@@ -243,6 +243,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: | |
StatementNode* m_next; | |
@@ -737,12 +738,12 @@ namespace JSC { | |
bool hasStaticallyNamedProperty(const Identifier& propName); | |
- RegisterID* emitBytecode(BytecodeGenerator&, RegisterID*, RegisterID*); | |
+ RegisterID* emitBytecode(BytecodeGenerator&, RegisterID*, RegisterID*, Vector<JSTextPosition>*); | |
private: | |
RegisterID* emitBytecode(BytecodeGenerator& generator, RegisterID* dst = nullptr) override | |
{ | |
- return emitBytecode(generator, dst, nullptr); | |
+ return emitBytecode(generator, dst, nullptr, nullptr); | |
} | |
void emitPutConstantProperty(BytecodeGenerator&, RegisterID*, PropertyNode&); | |
@@ -2143,6 +2144,19 @@ namespace JSC { | |
ExpressionNode* m_argument; | |
}; | |
+ class DefineFieldNode final : public StatementNode { | |
+ public: | |
+ DefineFieldNode(const JSTokenLocation&, const Identifier*, ExpressionNode*); | |
+ | |
+ private: | |
+ void emitBytecode(BytecodeGenerator&, RegisterID* destination = 0) override; | |
+ | |
+ bool isDefineFieldNode() const override { return true; } | |
+ | |
+ const Identifier* m_ident; | |
+ ExpressionNode* m_assign; | |
+ }; | |
+ | |
class ClassExprNode final : public ExpressionNode, public VariableEnvironmentNode { | |
JSC_MAKE_PARSER_ARENA_DELETABLE_ALLOCATED(ClassExprNode); | |
public: | |
diff --git a/Source/JavaScriptCore/parser/Parser.cpp b/Source/JavaScriptCore/parser/Parser.cpp | |
index 6fa3ad1fa76..7d50bbd97a8 100644 | |
--- a/Source/JavaScriptCore/parser/Parser.cpp | |
+++ b/Source/JavaScriptCore/parser/Parser.cpp | |
@@ -197,7 +197,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(); | |
@@ -211,6 +211,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); | |
@@ -241,6 +243,8 @@ String Parser<LexerType>::parseInner(const Identifier& calleeName, SourceParseMo | |
sourceElements = parseGeneratorFunctionSourceElements(context, calleeName, CheckForStrictMode); | |
else if (isAsyncGeneratorFunctionParseMode(parseMode)) | |
sourceElements = parseAsyncGeneratorFunctionSourceElements(context, parseMode, isArrowFunctionBodyExpression, CheckForStrictMode); | |
+ else if (parseMode == SourceParseMode::InstanceFieldInitializerMode) | |
+ sourceElements = parseInstanceFieldInitializerSourceElements(context, instanceFieldLocations); | |
else | |
sourceElements = parseSourceElements(context, CheckForStrictMode); | |
} | |
@@ -2116,6 +2120,7 @@ static const char* stringArticleForFunctionMode(SourceParseMode mode) | |
case SourceParseMode::ProgramMode: | |
case SourceParseMode::ModuleAnalyzeMode: | |
case SourceParseMode::ModuleEvaluateMode: | |
+ case SourceParseMode::InstanceFieldInitializerMode: | |
RELEASE_ASSERT_NOT_REACHED(); | |
return ""; | |
} | |
@@ -2157,6 +2162,7 @@ static const char* stringForFunctionMode(SourceParseMode mode) | |
case SourceParseMode::ProgramMode: | |
case SourceParseMode::ModuleAnalyzeMode: | |
case SourceParseMode::ModuleEvaluateMode: | |
+ case SourceParseMode::InstanceFieldInitializerMode: | |
RELEASE_ASSERT_NOT_REACHED(); | |
return ""; | |
} | |
@@ -2884,6 +2890,15 @@ parseMethod: | |
property = parseGetterSetter(context, alwaysStrictInsideClass, isGetter ? PropertyNode::Getter : PropertyNode::Setter, | |
methodStart, ConstructorKind::None, tag); | |
failIfFalse(property, "Cannot parse this method"); | |
+ } else if (!match(OPENPAREN) && tag == ClassElementTag::Instance && parseMode == SourceParseMode::MethodMode && !isGetter && !isSetter) { | |
+ TreeExpression initializer = 0; | |
+ if (consume(EQUAL)) { | |
+ // TODO: Use SyntaxChecker if this is an instance field initializer. | |
+ initializer = parseAssignmentExpression(context); | |
+ failIfFalse(initializer, "Cannot parse initializer for class field"); | |
+ } | |
+ failIfFalse(autoSemiColon(), "Expected a ';' following a class field"); | |
+ property = context.createProperty(ident, initializer, PropertyNode::Constant, PropertyNode::Unknown, alwaysStrictInsideClass, SuperBinding::NotNeeded, InferName::Allowed, tag); | |
} else { | |
ParserFunctionInfo<TreeBuilder> methodInfo; | |
bool isConstructor = tag == ClassElementTag::Instance && *ident == propertyNames.constructor; | |
@@ -2930,6 +2945,66 @@ parseMethod: | |
return classExpression; | |
} | |
+template <typename LexerType> | |
+template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseInstanceFieldInitializerSourceElements(TreeBuilder& context, const Vector<JSTextPosition>& instanceFieldLocations) | |
+{ | |
+ TreeSourceElements sourceElements = context.createSourceElements(); | |
+ | |
+ 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); | |
+ | |
+ const Identifier* ident = 0; | |
+ TreeExpression computedPropertyName = 0; | |
+ switch (m_token.m_type) { | |
+ 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"); | |
+ | |
+ // TODO: handle computed property names properly. | |
+ continue; | |
+ | |
+ 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 (computedPropertyName) | |
+ continue; | |
+ | |
+ TreeStatement defineField = context.createDefineField(JSTokenLocation(), ident, initializer); | |
+ 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) | |
diff --git a/Source/JavaScriptCore/parser/Parser.h b/Source/JavaScriptCore/parser/Parser.h | |
index ab0e74d40a5..28966b9aae9 100644 | |
--- a/Source/JavaScriptCore/parser/Parser.h | |
+++ b/Source/JavaScriptCore/parser/Parser.h | |
@@ -255,6 +255,7 @@ public: | |
case SourceParseMode::GetterMode: | |
case SourceParseMode::SetterMode: | |
case SourceParseMode::MethodMode: | |
+ case SourceParseMode::InstanceFieldInitializerMode: | |
setIsFunction(); | |
break; | |
@@ -874,7 +875,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(); } | |
@@ -1312,7 +1313,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); | |
@@ -1524,6 +1525,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 }; | |
@@ -1848,7 +1850,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 +1867,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 +1951,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 +1963,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) { | |
@@ -1970,7 +1973,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 2cea8829804..d604fd83827 100644 | |
--- a/Source/JavaScriptCore/parser/ParserModes.h | |
+++ b/Source/JavaScriptCore/parser/ParserModes.h | |
@@ -62,6 +62,7 @@ enum class SourceParseMode : uint32_t { | |
AsyncGeneratorWrapperFunctionMode = 0b00000000000000010000000000000000, | |
AsyncGeneratorWrapperMethodMode = 0b00000000000000100000000000000000, | |
GeneratorWrapperMethodMode = 0b00000000000001000000000000000000, | |
+ InstanceFieldInitializerMode = 0b00000000000010000000000000000000, | |
}; | |
class SourceParseModeSet { | |
@@ -110,7 +111,8 @@ ALWAYS_INLINE bool isFunctionParseMode(SourceParseMode parseMode) | |
SourceParseMode::AsyncArrowFunctionBodyMode, | |
SourceParseMode::AsyncGeneratorBodyMode, | |
SourceParseMode::AsyncGeneratorWrapperFunctionMode, | |
- SourceParseMode::AsyncGeneratorWrapperMethodMode).contains(parseMode); | |
+ SourceParseMode::AsyncGeneratorWrapperMethodMode, | |
+ SourceParseMode::InstanceFieldInitializerMode).contains(parseMode); | |
} | |
ALWAYS_INLINE bool isAsyncFunctionParseMode(SourceParseMode parseMode) | |
diff --git a/Source/JavaScriptCore/parser/SyntaxChecker.h b/Source/JavaScriptCore/parser/SyntaxChecker.h | |
index 97cfadb302f..47c3a3ffc14 100644 | |
--- a/Source/JavaScriptCore/parser/SyntaxChecker.h | |
+++ b/Source/JavaScriptCore/parser/SyntaxChecker.h | |
@@ -246,6 +246,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*, ExpressionNode*) { 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; } | |
diff --git a/Source/JavaScriptCore/runtime/CommonIdentifiers.h b/Source/JavaScriptCore/runtime/CommonIdentifiers.h | |
index d64766a304f..8f972425301 100644 | |
--- a/Source/JavaScriptCore/runtime/CommonIdentifiers.h | |
+++ b/Source/JavaScriptCore/runtime/CommonIdentifiers.h | |
@@ -281,7 +281,8 @@ | |
macro(split) \ | |
macro(toPrimitive) \ | |
macro(toStringTag) \ | |
- macro(unscopables) | |
+ macro(unscopables) \ | |
+ macro(instanceFieldInitializer) | |
namespace JSC { | |
diff --git a/Source/JavaScriptCore/runtime/EvalExecutable.h b/Source/JavaScriptCore/runtime/EvalExecutable.h | |
index b834a7c981e..d3492c8a573 100644 | |
--- a/Source/JavaScriptCore/runtime/EvalExecutable.h | |
+++ b/Source/JavaScriptCore/runtime/EvalExecutable.h | |
@@ -56,7 +56,7 @@ public: | |
DECLARE_INFO; | |
- ExecutableInfo executableInfo() const { return ExecutableInfo(usesEval(), isStrictMode(), false, false, ConstructorKind::None, JSParserScriptMode::Classic, SuperBinding::NotNeeded, SourceParseMode::ProgramMode, derivedContextType(), isArrowFunctionContext(), false, evalContextType()); } | |
+ ExecutableInfo executableInfo() const { return ExecutableInfo(usesEval(), isStrictMode(), false, false, ConstructorKind::None, JSParserScriptMode::Classic, SuperBinding::NotNeeded, SourceParseMode::ProgramMode, derivedContextType(), isArrowFunctionContext(), false, evalContextType(), false); } | |
unsigned numVariables() { return m_unlinkedEvalCodeBlock->numVariables(); } | |
unsigned numFunctionHoistingCandidates() { return m_unlinkedEvalCodeBlock->numFunctionHoistingCandidates(); } | |
diff --git a/Source/JavaScriptCore/runtime/ModuleProgramExecutable.h b/Source/JavaScriptCore/runtime/ModuleProgramExecutable.h | |
index a1fe8644f0f..fd10e805b82 100644 | |
--- a/Source/JavaScriptCore/runtime/ModuleProgramExecutable.h | |
+++ b/Source/JavaScriptCore/runtime/ModuleProgramExecutable.h | |
@@ -63,7 +63,7 @@ public: | |
DECLARE_INFO; | |
- ExecutableInfo executableInfo() const { return ExecutableInfo(usesEval(), isStrictMode(), false, false, ConstructorKind::None, JSParserScriptMode::Module, SuperBinding::NotNeeded, SourceParseMode::ModuleEvaluateMode, derivedContextType(), isArrowFunctionContext(), false, EvalContextType::None); } | |
+ ExecutableInfo executableInfo() const { return ExecutableInfo(usesEval(), isStrictMode(), false, false, ConstructorKind::None, JSParserScriptMode::Module, SuperBinding::NotNeeded, SourceParseMode::ModuleEvaluateMode, derivedContextType(), isArrowFunctionContext(), false, EvalContextType::None, false); } | |
UnlinkedModuleProgramCodeBlock* unlinkedModuleProgramCodeBlock() { return m_unlinkedModuleProgramCodeBlock.get(); } | |
diff --git a/Source/JavaScriptCore/runtime/ProgramExecutable.h b/Source/JavaScriptCore/runtime/ProgramExecutable.h | |
index 9837c884c89..46adf4cbf13 100644 | |
--- a/Source/JavaScriptCore/runtime/ProgramExecutable.h | |
+++ b/Source/JavaScriptCore/runtime/ProgramExecutable.h | |
@@ -73,7 +73,7 @@ public: | |
DECLARE_INFO; | |
- ExecutableInfo executableInfo() const { return ExecutableInfo(usesEval(), isStrictMode(), false, false, ConstructorKind::None, JSParserScriptMode::Classic, SuperBinding::NotNeeded, SourceParseMode::ProgramMode, derivedContextType(), isArrowFunctionContext(), false, EvalContextType::None); } | |
+ ExecutableInfo executableInfo() const { return ExecutableInfo(usesEval(), isStrictMode(), false, false, ConstructorKind::None, JSParserScriptMode::Classic, SuperBinding::NotNeeded, SourceParseMode::ProgramMode, derivedContextType(), isArrowFunctionContext(), false, EvalContextType::None, false); } | |
private: | |
friend class ExecutableBase; | |
-- | |
2.15.1 (Apple Git-101) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 25d7ca9ea9637133f0250a7e1da634062dc2b911 Mon Sep 17 00:00:00 2001 | |
From: Caitlin Potter <[email protected]> | |
Date: Mon, 16 Apr 2018 00:32:24 -0400 | |
Subject: [PATCH 2/3] [JSC] add support for lexing PrivateName productions | |
--- | |
Source/JavaScriptCore/ChangeLog | 33 +++++++++++++++++ | |
Source/JavaScriptCore/parser/Lexer.cpp | 57 ++++++++++++++++++----------- | |
Source/JavaScriptCore/parser/Parser.cpp | 10 ++++- | |
Source/JavaScriptCore/parser/ParserTokens.h | 1 + | |
4 files changed, 79 insertions(+), 22 deletions(-) | |
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog | |
index 5c5b54b3e61..0cd20d2dc01 100644 | |
--- a/Source/JavaScriptCore/ChangeLog | |
+++ b/Source/JavaScriptCore/ChangeLog | |
@@ -1,3 +1,36 @@ | |
+2018-03-21 Caitlin Potter <[email protected]> | |
+ | |
+ [JSC] add support for lexing PrivateName productions | |
+ https://bugs.webkit.org/show_bug.cgi?id=183793 | |
+ | |
+ Reviewed by NOBODY (OOPS!). | |
+ | |
+ https://tc39.github.io/proposal-class-fields/#prod-PrivateName | |
+ introduces a new production, which is an identifier with a leading `#`. | |
+ | |
+ This change adds support for scanning these names, with a new token type | |
+ so that they aren't conflated with identifiers. | |
+ | |
+ A lone `#` with no valid identifier following it, will always produce | |
+ the error "Invalid character: '#'" --- In cases where the PRIVATENAME | |
+ token type is not handled, the error message is | |
+ "Unexpected private name #name". | |
+ | |
+ This will likely have a small impact on code load performance, but is | |
+ not expected to be substantial. | |
+ | |
+ * parser/Lexer.cpp: | |
+ (JSC::Lexer<T>::Lexer): | |
+ (JSC::Lexer<LChar>::parseIdentifier): | |
+ (JSC::Lexer<UChar>::parseIdentifier): | |
+ (JSC::Lexer<CharacterType>::parseIdentifierSlowCase): | |
+ (JSC::Lexer<T>::lex): | |
+ * parser/Parser.cpp: | |
+ (JSC::Parser<LexerType>::parseVariableDeclarationList): | |
+ (JSC::Parser<LexerType>::parseDestructuringPattern): | |
+ (JSC::Parser<LexerType>::printUnexpectedTokenText): | |
+ * parser/ParserTokens.h: | |
+ | |
2018-04-11 Yusuke Suzuki <[email protected]> | |
Unreviewed, build fix for 32bit | |
diff --git a/Source/JavaScriptCore/parser/Lexer.cpp b/Source/JavaScriptCore/parser/Lexer.cpp | |
index 7b8e97e4bec..ef73d731d78 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 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,19 @@ start: | |
m_terminator = true; | |
m_lineStart = m_code; | |
goto start; | |
+ case CharacterPrivateNameStart: { | |
+ auto next = peek(1); | |
+ if (isIdentStart(next) || next == '\\') | |
+ 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/Parser.cpp b/Source/JavaScriptCore/parser/Parser.cpp | |
index 7d50bbd97a8..c1d8010014f 100644 | |
--- a/Source/JavaScriptCore/parser/Parser.cpp | |
+++ b/Source/JavaScriptCore/parser/Parser.cpp | |
@@ -789,6 +789,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"); | |
@@ -1244,6 +1246,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"); | |
@@ -5154,7 +5158,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/ParserTokens.h b/Source/JavaScriptCore/parser/ParserTokens.h | |
index c2058aeeb0d..2691852d2c8 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, | |
-- | |
2.15.1 (Apple Git-101) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 3f18db0531e1ba206ac5e9a8376de3da2b170035 Mon Sep 17 00:00:00 2001 | |
From: Caitlin Potter <[email protected]> | |
Date: Tue, 17 Apr 2018 14:36:51 -0400 | |
Subject: [PATCH 3/4] Private fields are "working", but could be done better! | |
--- | |
.../JavaScriptCore/bytecompiler/NodesCodegen.cpp | 201 ++++++++++++++++++--- | |
Source/JavaScriptCore/parser/ASTBuilder.h | 12 +- | |
Source/JavaScriptCore/parser/NodeConstructors.h | 27 ++- | |
Source/JavaScriptCore/parser/Nodes.h | 68 +++++-- | |
Source/JavaScriptCore/parser/Parser.cpp | 48 ++++- | |
Source/JavaScriptCore/parser/SyntaxChecker.h | 4 +- | |
6 files changed, 290 insertions(+), 70 deletions(-) | |
diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp | |
index 5f5d5df1b39..5cb84675665 100644 | |
--- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp | |
+++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp | |
@@ -533,10 +533,16 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe | |
RegisterID* dst = nullptr; | |
for (; p && (p->m_node->m_type & PropertyNode::Constant); p = p->m_next) { | |
dst = p->m_node->isInstanceClassProperty() ? prototype : dstOrConstructor; | |
- if (instanceFieldLocations && dst == prototype && !p->m_node->needsSuperBinding()) { | |
+ | |
+ if (p->isComputedOrPrivateClassField()) | |
+ emitPutComputedOrPrivateFieldName(generator, *p->m_node); | |
+ | |
+ if (p->isInstanceClassField()) { | |
+ ASSERT(instanceFieldLocations); | |
instanceFieldLocations->append(p->position()); | |
continue; | |
} | |
+ | |
emitPutConstantProperty(generator, dst, *p->m_node); | |
} | |
@@ -583,7 +589,12 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe | |
for (; p; p = p->m_next) { | |
PropertyNode* node = p->m_node; | |
dst = node->isInstanceClassProperty() ? prototype : dstOrConstructor; | |
- if (instanceFieldLocations && dst == prototype && !p->m_node->needsSuperBinding()) { | |
+ | |
+ if (p->isComputedOrPrivateClassField()) | |
+ emitPutComputedOrPrivateFieldName(generator, *p->m_node); | |
+ | |
+ if (p->isInstanceClassField()) { | |
+ ASSERT(instanceFieldLocations); | |
instanceFieldLocations->append(p->position()); | |
continue; | |
} | |
@@ -710,6 +721,62 @@ void PropertyListNode::emitPutConstantProperty(BytecodeGenerator& generator, Reg | |
generator.emitDirectPutByVal(newObj, propertyName.get(), value.get()); | |
} | |
+void PropertyListNode::emitPutComputedOrPrivateFieldName(BytecodeGenerator& generator, PropertyNode& node) | |
+{ | |
+ ASSERT(node.isComputedOrPrivateClassField()); | |
+ if (node.isPrivate()) { | |
+ ASSERT(node.name()); | |
+ const Identifier& description = *node.name(); | |
+ | |
+ // The variable must have been declared in the Class's lexical scope. | |
+ Variable var = generator.variable(description); | |
+ ASSERT(!var.local()); | |
+ | |
+/* | |
+ | |
+ if (generator.isStrictMode()) | |
+ generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); | |
+ RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var); | |
+ if (m_assignmentContext == AssignmentContext::AssignmentExpression) | |
+ generator.emitTDZCheckIfNecessary(var, nullptr, scope.get()); | |
+ if (dst == generator.ignoredResult()) | |
+ dst = 0; | |
+ RefPtr<RegisterID> result = generator.emitNode(dst, m_right); | |
+ if (isReadOnly) { | |
+ RegisterID* result = generator.emitNode(dst, m_right); // Execute side effects first. | |
+ bool threwException = generator.emitReadOnlyExceptionIfNeeded(var); | |
+ if (threwException) | |
+ return result; | |
+ } | |
+ generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); | |
+ RegisterID* returnResult = result.get(); | |
+ if (!isReadOnly) { | |
+ returnResult = generator.emitPutToScope(scope.get(), var, result.get(), generator.isStrictMode() ? ThrowIfNotFound : DoNotThrowIfNotFound, initializationModeForAssignmentContext(m_assignmentContext)); | |
+ generator.emitProfileType(result.get(), var, divotStart(), divotEnd()); | |
+ } | |
+ | |
+ if (m_assignmentContext == AssignmentContext::DeclarationStatement || m_assignmentContext == AssignmentContext::ConstDeclarationStatement) | |
+ generator.liftTDZCheckIfPossible(var); | |
+ return returnResult; | |
+ */ | |
+ | |
+ | |
+ auto privateName = PrivateName(PrivateName::PrivateSymbol, description.string()); | |
+ JSValue privateSymbol = Symbol::create(*generator.vm(), privateName.uid()); | |
+ | |
+ // TODO: add type profiling if we need it? | |
+ RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var); | |
+ RefPtr<RegisterID> result = generator.emitLoad(generator.newTemporary(), privateSymbol); | |
+ generator.emitPutToScope(scope.get(), var, result.get(), ThrowIfNotFound, InitializationMode::ConstInitialization); | |
+ generator.emitProfileType(result.get(), var, position(), JSTextPosition(-1, position().offset + description.length(), -1)); | |
+ generator.liftTDZCheckIfPossible(var); | |
+ return; | |
+ } | |
+ | |
+ // TODO: compute property name and store in a private variable | |
+ ASSERT_NOT_REACHED(); | |
+} | |
+ | |
// ------------------------------ BracketAccessorNode -------------------------------- | |
static bool isNonIndexStringElement(ExpressionNode& element) | |
@@ -764,16 +831,89 @@ 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::emitResolvePrivateName(BytecodeGenerator& generator, RegisterID* dst) | |
+{ | |
+ ASSERT(isPrivateName()); | |
+ Variable var = generator.variable(m_ident); | |
+ ASSERT(!var.local()); | |
+ /*if (RegisterID* local = var.local()) { | |
+ generator.emitTDZCheckIfNecessary(var, local, nullptr); | |
+ if (dst == generator.ignoredResult()) | |
+ return nullptr; | |
+ | |
+ generator.emitProfileType(local, var, m_position, JSTextPosition(-1, m_position.offset + m_ident.length(), -1)); | |
+ return generator.moveToDestinationIfNeeded(dst, local); | |
+ }*/ | |
+ | |
+ JSTextPosition divot = m_position + m_ident.length(); | |
+ generator.emitExpressionInfo(divot, m_position, divot); | |
+ RefPtr<RegisterID> scope = generator.emitResolveScope(dst, var); | |
+ RegisterID* finalDest = generator.finalDestination(dst); | |
+ RefPtr<RegisterID> uncheckedResult = generator.newTemporary(); | |
+ generator.emitGetFromScope(uncheckedResult.get(), scope.get(), var, ThrowIfNotFound); | |
+ generator.emitTDZCheckIfNecessary(var, uncheckedResult.get(), nullptr); | |
+ generator.emitMove(finalDest, uncheckedResult.get()); | |
+ generator.emitProfileType(finalDest, var, m_position, JSTextPosition(-1, m_position.offset + m_ident.length(), -1)); | |
+ return finalDest; | |
+} | |
+ | |
+RegisterID* BaseDotNode::emitGetPropertyValue(BytecodeGenerator& generator, RegisterID* dst, RegisterID* base, RefPtr<RegisterID>& thisValue, RefPtr<RegisterID>& privateName) | |
+{ | |
+ if (m_base->isSuperNode()) | |
+ thisValue = generator.ensureThis(); | |
+ | |
+ if (isPrivateName()){ | |
+ privateName = emitResolvePrivateName(generator, generator.newTemporary()); | |
+ if (m_base->isSuperNode()) | |
+ return generator.emitGetByVal(dst, base, thisValue.get(), privateName.get()); | |
+ return generator.emitGetByVal(dst, base, privateName.get()); | |
+ } | |
+ | |
+ if (m_base->isSuperNode()) | |
+ 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; | |
+ RefPtr<RegisterID> privateName; | |
+ return emitGetPropertyValue(generator, dst, base, thisValue, privateName); | |
+} | |
+ | |
+RegisterID* BaseDotNode::emitPutProperty(BytecodeGenerator& generator, RegisterID* base, RegisterID* thisValue, RegisterID* privateName, RegisterID* value) | |
+{ | |
+ if (isPrivateName()) { | |
+ ASSERT(privateName); | |
+ if (thisValue) | |
+ return generator.emitPutByVal(base, thisValue, privateName, value); | |
+ return generator.emitPutByVal(base, privateName, value); | |
+ } | |
+ | |
+ if (thisValue) | |
+ return generator.emitPutById(base, thisValue, m_ident, value); | |
+ return generator.emitPutById(base, m_ident, value); | |
+} | |
+ | |
+RegisterID* BaseDotNode::emitPutProperty(BytecodeGenerator& generator, RegisterID* base, RegisterID* value) | |
+{ | |
+ RefPtr<RegisterID> thisValue; | |
+ RefPtr<RegisterID> privateName; | |
+ if (m_base->isSuperNode()) | |
+ thisValue = generator.ensureThis(); | |
+ | |
+ if (isPrivateName()) | |
+ privateName = emitResolvePrivateName(generator, generator.newTemporary()); | |
+ | |
+ return emitPutProperty(generator, base, thisValue.get(), privateName.get(), value); | |
+} | |
+ | |
// ------------------------------ ArgumentListNode ----------------------------- | |
RegisterID* ArgumentListNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) | |
@@ -2560,11 +2700,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.moveToDestinationIfNeeded(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.moveToDestinationIfNeeded(dst, forwardResult.get()); | |
} | |
@@ -2576,21 +2712,14 @@ 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> privateName; | |
+ RefPtr<RegisterID> value = emitGetPropertyValue(generator, generator.tempDestination(dst), base.get(), thisValue, privateName); | |
+ | |
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(), thisValue.get(), privateName.get(), updatedValue); | |
generator.emitProfileType(updatedValue, divotStart(), divotEnd()); | |
return ret; | |
} | |
@@ -3983,11 +4112,31 @@ void DefineFieldNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) | |
generator.emitLoad(value.get(), jsUndefined()); | |
else { | |
generator.emitNode(value.get(), m_assign); | |
- if (generator.shouldEmitSetFunctionName(m_assign)) | |
+ if (m_ident && generator.shouldEmitSetFunctionName(m_assign)) | |
generator.emitSetFunctionName(value.get(), *m_ident); | |
} | |
- generator.emitDirectPutById(generator.thisRegister(), *m_ident, value.get(), PropertyNode::Unknown); | |
+ if (m_type == DefineFieldNode::Name) | |
+ generator.emitDirectPutById(generator.thisRegister(), *m_ident, value.get(), PropertyNode::Unknown); | |
+ else if (m_type == DefineFieldNode::PrivateName) { | |
+ Variable var = generator.variable(*m_ident); | |
+ ASSERT(!var.local()); | |
+ | |
+ generator.emitExpressionInfo(position(), position(), position() + m_ident->length()); | |
+ RefPtr<RegisterID> scope = generator.emitResolveScope(generator.newTemporary(), var); | |
+ RefPtr<RegisterID> privateName = generator.newTemporary(); | |
+ generator.emitGetFromScope(privateName.get(), scope.get(), var, ThrowIfNotFound); | |
+ generator.emitTDZCheckIfNecessary(var, privateName.get(), nullptr); | |
+ //generator.emitMove(finalDest, uncheckedResult.get()); | |
+ generator.emitProfileType(privateName.get(), var, m_position, JSTextPosition(-1, m_position.offset + m_ident->length(), -1)); | |
+ | |
+ generator.emitDirectPutByVal(generator.thisRegister(), privateName.get(), value.get()); | |
+ } | |
+ else { | |
+ if (m_expression) return; | |
+ //RefPtr<RegisterID> name = generator.emitNodeForProperty(m_expression); | |
+ //generator.emitDirectPutByVal(generator.thisRegister(), name.get(), value.get()); | |
+ } | |
} | |
// ------------------------------ ClassDeclNode --------------------------------- | |
diff --git a/Source/JavaScriptCore/parser/ASTBuilder.h b/Source/JavaScriptCore/parser/ASTBuilder.h | |
index f9b43c6536c..8c8a247f373 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,9 +396,9 @@ public: | |
return node; | |
} | |
- DefineFieldNode* createDefineField(const JSTokenLocation& location, const Identifier* ident, ExpressionNode* initializer) | |
+ DefineFieldNode* createDefineField(const JSTokenLocation& location, const Identifier* ident, ExpressionNode* expression, ExpressionNode* initializer, DefineFieldNode::Type type) | |
{ | |
- return new (m_parserArena) DefineFieldNode(location, ident, initializer); | |
+ return new (m_parserArena) DefineFieldNode(location, ident, expression, initializer, type); | |
} | |
ClassExprNode* createClassExpr(const JSTokenLocation& location, const ParserClassInfo<ASTBuilder>& classInfo, VariableEnvironment& classEnvironment, ExpressionNode* constructor, | |
@@ -1493,10 +1493,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/NodeConstructors.h b/Source/JavaScriptCore/parser/NodeConstructors.h | |
index 937e057f858..e3cd32bc854 100644 | |
--- a/Source/JavaScriptCore/parser/NodeConstructors.h | |
+++ b/Source/JavaScriptCore/parser/NodeConstructors.h | |
@@ -248,6 +248,7 @@ namespace JSC { | |
, m_type(type) | |
, m_needsSuperBinding(superBinding == SuperBinding::Needed) | |
, m_putType(putType) | |
+ , m_isPrivate((type & Private) != 0) | |
, m_classElementTag(static_cast<unsigned>(tag)) | |
, m_isOverriddenByDuplicate(false) | |
{ | |
@@ -259,6 +260,7 @@ namespace JSC { | |
, m_type(type) | |
, m_needsSuperBinding(superBinding == SuperBinding::Needed) | |
, m_putType(putType) | |
+ , m_isPrivate((type & Private) != 0) | |
, m_classElementTag(static_cast<unsigned>(tag)) | |
, m_isOverriddenByDuplicate(false) | |
{ | |
@@ -271,6 +273,7 @@ namespace JSC { | |
, m_type(type) | |
, m_needsSuperBinding(superBinding == SuperBinding::Needed) | |
, m_putType(putType) | |
+ , m_isPrivate((type & Private) != 0) | |
, m_classElementTag(static_cast<unsigned>(tag)) | |
, m_isOverriddenByDuplicate(false) | |
{ | |
@@ -311,10 +314,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 +729,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) | |
@@ -987,10 +992,12 @@ namespace JSC { | |
{ | |
} | |
- inline DefineFieldNode::DefineFieldNode(const JSTokenLocation& location, const Identifier* ident, ExpressionNode* assign) | |
+ inline DefineFieldNode::DefineFieldNode(const JSTokenLocation& location, const Identifier* ident, ExpressionNode* expression, ExpressionNode* assign, Type type) | |
: StatementNode(location) | |
, m_ident(ident) | |
+ , m_expression(expression) | |
, m_assign(assign) | |
+ , m_type(type) | |
{ | |
} | |
diff --git a/Source/JavaScriptCore/parser/Nodes.h b/Source/JavaScriptCore/parser/Nodes.h | |
index 8a767c03b8c..48d19defa6e 100644 | |
--- a/Source/JavaScriptCore/parser/Nodes.h | |
+++ b/Source/JavaScriptCore/parser/Nodes.h | |
@@ -699,7 +699,7 @@ namespace JSC { | |
enum class ClassElementTag { No, Instance, Static, LastTag }; | |
class PropertyNode : public ParserArenaFreeable { | |
public: | |
- enum Type { Constant = 1, Getter = 2, Setter = 4, Computed = 8, Shorthand = 16, Spread = 32 }; | |
+ enum Type { Constant = 1, Getter = 2, Setter = 4, Computed = 8, Shorthand = 16, Spread = 32, Private = 64 }; | |
enum PutType { Unknown, KnownDirect }; | |
PropertyNode(const Identifier&, ExpressionNode*, Type, PutType, SuperBinding, ClassElementTag); | |
@@ -714,7 +714,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 != 0; } | |
+ bool isComputedOrPrivateClassField() const { return isClassField() && (hasComputedName() || isPrivate());} | |
void setIsOverriddenByDuplicate() { m_isOverriddenByDuplicate = true; } | |
PutType putType() const { return static_cast<PutType>(m_putType); } | |
@@ -723,12 +728,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 : public ExpressionNode { | |
@@ -737,6 +743,15 @@ namespace JSC { | |
PropertyListNode(const JSTokenLocation&, PropertyNode*, PropertyListNode*); | |
bool hasStaticallyNamedProperty(const Identifier& propName); | |
+ bool isComputedOrPrivateClassField() const | |
+ { | |
+ return m_node->isComputedOrPrivateClassField(); | |
+ } | |
+ bool isInstanceClassField() const | |
+ { | |
+ return m_node->isInstanceClassField(); | |
+ } | |
+ | |
RegisterID* emitBytecode(BytecodeGenerator&, RegisterID*, RegisterID*, Vector<JSTextPosition>*); | |
@@ -746,6 +761,7 @@ namespace JSC { | |
return emitBytecode(generator, dst, nullptr, nullptr); | |
} | |
void emitPutConstantProperty(BytecodeGenerator&, RegisterID*, PropertyNode&); | |
+ void emitPutComputedOrPrivateFieldName(BytecodeGenerator&, PropertyNode&); | |
PropertyNode* m_node; | |
PropertyListNode* m_next; | |
@@ -783,9 +799,31 @@ namespace JSC { | |
bool m_subscriptHasAssignments; | |
}; | |
- class DotAccessorNode : public ExpressionNode, public ThrowableExpressionData { | |
+ enum class DotType { Name, PrivateName }; | |
+ class BaseDotNode : public ExpressionNode { | |
public: | |
- DotAccessorNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&); | |
+ BaseDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, DotType type); | |
+ | |
+ 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* emitResolvePrivateName(BytecodeGenerator& generator, RegisterID* dst); | |
+ RegisterID* emitGetPropertyValue(BytecodeGenerator& generator, RegisterID* dst, RegisterID* base, RefPtr<RegisterID>& thisValue, RefPtr<RegisterID>& privateName); | |
+ RegisterID* emitGetPropertyValue(BytecodeGenerator& generator, RegisterID* dst, RegisterID* base); | |
+ RegisterID* emitPutProperty(BytecodeGenerator& generator, RegisterID* base, RegisterID* thisValue, RegisterID* privateName, RegisterID* value); | |
+ RegisterID* emitPutProperty(BytecodeGenerator& generator, RegisterID* base, RegisterID* value); | |
+ | |
+ protected: | |
+ ExpressionNode* m_base; | |
+ const Identifier& m_ident; | |
+ DotType m_type; | |
+ }; | |
+ | |
+ class DotAccessorNode : public BaseDotNode, public ThrowableExpressionData { | |
+ public: | |
+ DotAccessorNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, DotType type); | |
ExpressionNode* base() const { return m_base; } | |
const Identifier& identifier() const { return m_ident; } | |
@@ -795,9 +833,6 @@ namespace JSC { | |
bool isLocation() const override { return true; } | |
bool isDotAccessorNode() const override { return true; } | |
- | |
- ExpressionNode* m_base; | |
- const Identifier& m_ident; | |
}; | |
class SpreadExpressionNode : public ExpressionNode, public ThrowableExpressionData { | |
@@ -1365,28 +1400,24 @@ namespace JSC { | |
bool m_rightHasAssignments : 1; | |
}; | |
- class AssignDotNode : public ExpressionNode, public ThrowableExpressionData { | |
+ class AssignDotNode : 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 type, 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 : public ExpressionNode, public ThrowableSubExpressionData { | |
+ class ReadModifyDotNode : 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 type, 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; | |
@@ -2146,7 +2177,8 @@ namespace JSC { | |
class DefineFieldNode final : public StatementNode { | |
public: | |
- DefineFieldNode(const JSTokenLocation&, const Identifier*, ExpressionNode*); | |
+ enum Type { Name, PrivateName, ComputedName }; | |
+ DefineFieldNode(const JSTokenLocation&, const Identifier*, ExpressionNode*, ExpressionNode*, Type type); | |
private: | |
void emitBytecode(BytecodeGenerator&, RegisterID* destination = 0) override; | |
@@ -2154,7 +2186,9 @@ namespace JSC { | |
bool isDefineFieldNode() const override { return true; } | |
const Identifier* m_ident; | |
+ ExpressionNode* m_expression; | |
ExpressionNode* m_assign; | |
+ Type m_type; | |
}; | |
class ClassExprNode final : public ExpressionNode, public VariableEnvironmentNode { | |
diff --git a/Source/JavaScriptCore/parser/Parser.cpp b/Source/JavaScriptCore/parser/Parser.cpp | |
index c1d8010014f..34cfa86a29f 100644 | |
--- a/Source/JavaScriptCore/parser/Parser.cpp | |
+++ b/Source/JavaScriptCore/parser/Parser.cpp | |
@@ -2818,6 +2818,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(); | |
@@ -2879,9 +2880,19 @@ 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: { | |
+ ident = m_token.m_data.ident; | |
+ ASSERT(ident); | |
+ failIfTrue(classScope->declareLexicalVariable(ident, true) & DeclarationResult::InvalidDuplicateDeclaration, "Cannot declare private field twice"); | |
+ classScope->addClosedVariableCandidateUnconditionally(ident->impl()); | |
+ type = static_cast<PropertyNode::Type>(type | PropertyNode::Private); | |
+ next(); | |
+ } | |
+ break; | |
default: | |
if (m_token.m_type & KeywordTokenFlag) | |
goto namedKeyword; | |
@@ -2891,8 +2902,9 @@ 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) { | |
TreeExpression initializer = 0; | |
@@ -2902,7 +2914,11 @@ parseMethod: | |
failIfFalse(initializer, "Cannot parse initializer for class field"); | |
} | |
failIfFalse(autoSemiColon(), "Expected a ';' following a class field"); | |
- property = context.createProperty(ident, initializer, PropertyNode::Constant, PropertyNode::Unknown, alwaysStrictInsideClass, SuperBinding::NotNeeded, InferName::Allowed, tag); | |
+ auto inferName = initializer ? InferName::Allowed : InferName::Disallowed; | |
+ if (computedPropertyName) | |
+ property = context.createProperty(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; | |
@@ -2927,11 +2943,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); | |
} | |
} | |
@@ -2958,9 +2972,13 @@ template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseInstance | |
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; | |
namedKeyword: | |
case STRING: | |
case IDENT: | |
@@ -2979,6 +2997,7 @@ template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseInstance | |
computedPropertyName = parseAssignmentExpression(context); | |
failIfFalse(computedPropertyName, "Cannot parse computed property name"); | |
handleProductionOrFail(CLOSEBRACKET, "]", "end", "computed property name"); | |
+ type = DefineFieldNode::ComputedName; | |
// TODO: handle computed property names properly. | |
continue; | |
@@ -3000,7 +3019,10 @@ template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseInstance | |
if (computedPropertyName) | |
continue; | |
- TreeStatement defineField = context.createDefineField(JSTokenLocation(), ident, initializer); | |
+ if (type == DefineFieldNode::PrivateName) | |
+ currentScope()->useVariable(ident, false); | |
+ | |
+ TreeStatement defineField = context.createDefineField(fieldLocation, ident, computedPropertyName, initializer, type); | |
context.appendStatement(sourceElements, defineField); | |
} | |
@@ -4887,8 +4909,16 @@ 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)) { | |
+ if (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(); | |
diff --git a/Source/JavaScriptCore/parser/SyntaxChecker.h b/Source/JavaScriptCore/parser/SyntaxChecker.h | |
index 47c3a3ffc14..172bd0a28e6 100644 | |
--- a/Source/JavaScriptCore/parser/SyntaxChecker.h | |
+++ b/Source/JavaScriptCore/parser/SyntaxChecker.h | |
@@ -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, int, int, int) { return 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; } | |
@@ -246,7 +246,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*, ExpressionNode*) { return 0; } | |
+ int createDefineField(const JSTokenLocation&, const Identifier*, int, 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; } | |
-- | |
2.15.1 (Apple Git-101) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From f5628e03d8f7a3fc97fed695ff625631451878b2 Mon Sep 17 00:00:00 2001 | |
From: Caitlin Potter <[email protected]> | |
Date: Thu, 19 Apr 2018 17:49:05 -0400 | |
Subject: [PATCH 4/4] Fix DefineField for indexed properties | |
--- | |
.../JavaScriptCore/bytecompiler/NodesCodegen.cpp | 26 ++++++++++++++-------- | |
1 file changed, 17 insertions(+), 9 deletions(-) | |
diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp | |
index 5cb84675665..e0dd1264aaa 100644 | |
--- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp | |
+++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp | |
@@ -4116,26 +4116,34 @@ void DefineFieldNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) | |
generator.emitSetFunctionName(value.get(), *m_ident); | |
} | |
- if (m_type == DefineFieldNode::Name) | |
- generator.emitDirectPutById(generator.thisRegister(), *m_ident, value.get(), PropertyNode::Unknown); | |
- else if (m_type == DefineFieldNode::PrivateName) { | |
+ 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: { | |
Variable var = generator.variable(*m_ident); | |
- ASSERT(!var.local()); | |
+ ASSERT_WITH_MESSAGE(!var.local(), "Private names must be captured variabbles"); | |
generator.emitExpressionInfo(position(), position(), position() + m_ident->length()); | |
RefPtr<RegisterID> scope = generator.emitResolveScope(generator.newTemporary(), var); | |
RefPtr<RegisterID> privateName = generator.newTemporary(); | |
generator.emitGetFromScope(privateName.get(), scope.get(), var, ThrowIfNotFound); | |
generator.emitTDZCheckIfNecessary(var, privateName.get(), nullptr); | |
- //generator.emitMove(finalDest, uncheckedResult.get()); | |
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; | |
} | |
- else { | |
+ case DefineFieldNode::ComputedName: | |
+ // TODO: Support computed field names | |
if (m_expression) return; | |
- //RefPtr<RegisterID> name = generator.emitNodeForProperty(m_expression); | |
- //generator.emitDirectPutByVal(generator.thisRegister(), name.get(), value.get()); | |
+ break; | |
} | |
} | |
-- | |
2.15.1 (Apple Git-101) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment