Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save caitp/21bdd80467b6673fd8a32a05e768b509 to your computer and use it in GitHub Desktop.
Save caitp/21bdd80467b6673fd8a32a05e768b509 to your computer and use it in GitHub Desktop.
From 4cb549619bcf39b16575f4a5bebf264a8e1b8238 Mon Sep 17 00:00:00 2001
From: Caitlin Potter <caitp@igalia.com>
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)
From 25d7ca9ea9637133f0250a7e1da634062dc2b911 Mon Sep 17 00:00:00 2001
From: Caitlin Potter <caitp@igalia.com>
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 <caitp@igalia.com>
+
+ [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 <utatane.tea@gmail.com>
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)
From 3f18db0531e1ba206ac5e9a8376de3da2b170035 Mon Sep 17 00:00:00 2001
From: Caitlin Potter <caitp@igalia.com>
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)
From f5628e03d8f7a3fc97fed695ff625631451878b2 Mon Sep 17 00:00:00 2001
From: Caitlin Potter <caitp@igalia.com>
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)
  1. Private names:
    1. 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)
    2. SetFunctionName with a PrivateName is very likely not doing the right thing at all, though this has not been tested yet.
  2. Computed property names:
    1. 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.
  3. The initializer function:
    1. 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment