Implement catch scope using lexical scoping constructs introduced with "let" scoping patch
https://bugs.webkit.org/show_bug.cgi?id=146979
Reviewed by Geoffrey Garen.
Now that BytecodeGenerator has a notion of local scope depth,
we can easily implement a catch scope that doesn't claim that
all variables are dynamically scoped. This means that functions
that use try/catch can have local variable resolution. This also
means that all functions that use try/catch don't have all
their variables marked as being captured.
Catch scopes now behave like a "let" scope (sans the TDZ logic) with a
single variable. Catch scopes are now just JSLexicalEnvironments and the
symbol table backing the catch scope knows that it corresponds to a catch scope.
* CMakeLists.txt:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
* bytecode/EvalCodeCache.h:
(JSC::EvalCodeCache::isCacheable):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::initializeDefaultParameterValuesAndSetupFunctionScopeStack):
(JSC::BytecodeGenerator::emitLoadGlobalObject):
(JSC::BytecodeGenerator::pushLexicalScope):
(JSC::BytecodeGenerator::pushLexicalScopeInternal):
(JSC::BytecodeGenerator::popLexicalScope):
(JSC::BytecodeGenerator::popLexicalScopeInternal):
(JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration):
(JSC::BytecodeGenerator::variable):
(JSC::BytecodeGenerator::resolveType):
(JSC::BytecodeGenerator::emitResolveScope):
(JSC::BytecodeGenerator::emitPopScope):
(JSC::BytecodeGenerator::emitPopWithScope):
(JSC::BytecodeGenerator::emitDebugHook):
(JSC::BytecodeGenerator::popScopedControlFlowContext):
(JSC::BytecodeGenerator::emitPushCatchScope):
(JSC::BytecodeGenerator::emitPopCatchScope):
(JSC::BytecodeGenerator::beginSwitch):
(JSC::BytecodeGenerator::emitPopWithOrCatchScope): Deleted.
* bytecompiler/BytecodeGenerator.h:
(JSC::BytecodeGenerator::lastOpcodeID):
* bytecompiler/NodesCodegen.cpp:
(JSC::AssignResolveNode::emitBytecode):
(JSC::WithNode::emitBytecode):
(JSC::TryNode::emitBytecode):
* debugger/DebuggerScope.cpp:
(JSC::DebuggerScope::isCatchScope):
(JSC::DebuggerScope::isFunctionNameScope):
(JSC::DebuggerScope::isFunctionOrEvalScope):
(JSC::DebuggerScope::caughtValue):
* debugger/DebuggerScope.h:
* inspector/ScriptDebugServer.cpp:
(Inspector::ScriptDebugServer::exceptionOrCaughtValue):
* interpreter/Interpreter.cpp:
(JSC::Interpreter::execute):
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_push_name_scope):
* jit/JITOpcodes32_64.cpp:
(JSC::JIT::emit_op_push_name_scope):
* jit/JITOperations.cpp:
* jit/JITOperations.h:
* parser/ASTBuilder.h:
(JSC::ASTBuilder::createContinueStatement):
(JSC::ASTBuilder::createTryStatement):
* parser/NodeConstructors.h:
(JSC::ThrowNode::ThrowNode):
(JSC::TryNode::TryNode):
(JSC::FunctionParameters::FunctionParameters):
* parser/Nodes.h:
* parser/Parser.cpp:
(JSC::Parser<LexerType>::parseTryStatement):
* parser/SyntaxChecker.h:
(JSC::SyntaxChecker::createBreakStatement):
(JSC::SyntaxChecker::createContinueStatement):
(JSC::SyntaxChecker::createTryStatement):
(JSC::SyntaxChecker::createSwitchStatement):
(JSC::SyntaxChecker::createWhileStatement):
(JSC::SyntaxChecker::createWithStatement):
* runtime/JSCatchScope.cpp:
* runtime/JSCatchScope.h:
(JSC::JSCatchScope::JSCatchScope): Deleted.
(JSC::JSCatchScope::create): Deleted.
(JSC::JSCatchScope::createStructure): Deleted.
* runtime/JSFunctionNameScope.h:
(JSC::JSFunctionNameScope::JSFunctionNameScope):
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
(JSC::JSGlobalObject::visitChildren):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::withScopeStructure):
(JSC::JSGlobalObject::strictEvalActivationStructure):
(JSC::JSGlobalObject::activationStructure):
(JSC::JSGlobalObject::functionNameScopeStructure):
(JSC::JSGlobalObject::directArgumentsStructure):
(JSC::JSGlobalObject::scopedArgumentsStructure):
(JSC::JSGlobalObject::catchScopeStructure): Deleted.
* runtime/JSNameScope.cpp:
(JSC::JSNameScope::create):
(JSC::JSNameScope::toThis):
* runtime/JSNameScope.h:
* runtime/JSObject.cpp:
(JSC::JSObject::toThis):
(JSC::JSObject::isFunctionNameScopeObject):
(JSC::JSObject::isCatchScopeObject): Deleted.
* runtime/JSObject.h:
* runtime/JSScope.cpp:
(JSC::JSScope::collectVariablesUnderTDZ):
(JSC::JSScope::isLexicalScope):
(JSC::JSScope::isCatchScope):
(JSC::resolveModeName):
* runtime/JSScope.h:
* runtime/SymbolTable.cpp:
(JSC::SymbolTable::SymbolTable):
(JSC::SymbolTable::cloneScopePart):
* runtime/SymbolTable.h:
* tests/stress/const-semantics.js:
(.):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@187515 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
index 7f31274..548ae44 100644
--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
@@ -578,7 +578,7 @@
}
// This implements step 25 of section 9.2.12.
- pushLexicalScopeInternal(environment, true, nullptr);
+ pushLexicalScopeInternal(environment, true, nullptr, TDZRequirement::UnderTDZ, ScopeType::LetConstScope);
RefPtr<RegisterID> temp = newTemporary();
for (unsigned i = 0; i < parameters.size(); i++) {
@@ -1357,7 +1357,13 @@
return m_globalObjectRegister;
}
-void BytecodeGenerator::pushLexicalScopeInternal(VariableEnvironment& environment, bool canOptimizeTDZChecks, RegisterID** constantSymbolTableResult)
+void BytecodeGenerator::pushLexicalScope(VariableEnvironmentNode* node, bool canOptimizeTDZChecks, RegisterID** constantSymbolTableResult)
+{
+ VariableEnvironment& environment = node->lexicalVariables();
+ pushLexicalScopeInternal(environment, canOptimizeTDZChecks, constantSymbolTableResult, TDZRequirement::UnderTDZ, ScopeType::LetConstScope);
+}
+
+void BytecodeGenerator::pushLexicalScopeInternal(VariableEnvironment& environment, bool canOptimizeTDZChecks, RegisterID** constantSymbolTableResult, TDZRequirement tdzRequirement, ScopeType scopeType)
{
if (!environment.size())
return;
@@ -1366,11 +1372,19 @@
environment.markAllVariablesAsCaptured();
Strong<SymbolTable> symbolTable(*m_vm, SymbolTable::create(*m_vm));
- symbolTable->setDoesCorrespondToLexicalScope();
+ switch (scopeType) {
+ case ScopeType::CatchScope:
+ symbolTable->setScopeType(SymbolTable::ScopeType::CatchScope);
+ break;
+ case ScopeType::LetConstScope:
+ symbolTable->setScopeType(SymbolTable::ScopeType::LexicalScope);
+ break;
+ }
+
bool hasCapturedVariables = false;
{
ConcurrentJITLocker locker(symbolTable->m_lock);
- for (auto entry : environment) {
+ for (auto& entry : environment) {
ASSERT(entry.value.isLet() || entry.value.isConst());
ASSERT(!entry.value.isVar());
SymbolTableEntry symbolTableEntry = symbolTable->get(locker, entry.key.get());
@@ -1408,7 +1422,7 @@
instructions().append(newScope->index());
instructions().append(scopeRegister()->index());
instructions().append(constantSymbolTable->index());
- instructions().append(addConstantValue(jsTDZValue())->index());
+ instructions().append(addConstantValue(tdzRequirement == TDZRequirement::UnderTDZ ? jsTDZValue() : jsUndefined())->index());
emitMove(scopeRegister(), newScope);
@@ -1416,31 +1430,34 @@
}
m_symbolTableStack.append(SymbolTableStackEntry{ symbolTable, newScope, false, symbolTableConstantIndex });
- m_TDZStack.append(std::make_pair(environment, canOptimizeTDZChecks));
- // Prefill stack variables with the TDZ empty value.
- // Scope variables will be initialized to the TDZ empty value when JSLexicalEnvironment is allocated.
- for (auto entry : environment) {
- SymbolTableEntry symbolTableEntry = symbolTable->get(entry.key.get());
- ASSERT(!symbolTableEntry.isNull());
- VarOffset offset = symbolTableEntry.varOffset();
- if (offset.isScope()) {
- ASSERT(newScope);
- continue;
- }
- ASSERT(offset.isStack());
- emitMoveEmptyValue(®isterFor(offset.stackOffset()));
- }
-}
+ if (tdzRequirement == TDZRequirement::UnderTDZ)
+ m_TDZStack.append(std::make_pair(environment, canOptimizeTDZChecks));
-void BytecodeGenerator::pushLexicalScope(VariableEnvironmentNode* node, bool canOptimizeTDZChecks, RegisterID** constantSymbolTableResult)
-{
- VariableEnvironment& environment = node->lexicalVariables();
- pushLexicalScopeInternal(environment, canOptimizeTDZChecks, constantSymbolTableResult);
+ if (tdzRequirement == TDZRequirement::UnderTDZ) {
+ // Prefill stack variables with the TDZ empty value.
+ // Scope variables will be initialized to the TDZ empty value when JSLexicalEnvironment is allocated.
+ for (auto& entry : environment) {
+ SymbolTableEntry symbolTableEntry = symbolTable->get(entry.key.get());
+ ASSERT(!symbolTableEntry.isNull());
+ VarOffset offset = symbolTableEntry.varOffset();
+ if (offset.isScope()) {
+ ASSERT(newScope);
+ continue;
+ }
+ ASSERT(offset.isStack());
+ emitMoveEmptyValue(®isterFor(offset.stackOffset()));
+ }
+ }
}
void BytecodeGenerator::popLexicalScope(VariableEnvironmentNode* node)
{
VariableEnvironment& environment = node->lexicalVariables();
+ popLexicalScopeInternal(environment, TDZRequirement::UnderTDZ);
+}
+
+void BytecodeGenerator::popLexicalScopeInternal(VariableEnvironment& environment, TDZRequirement tdzRequirement)
+{
if (!environment.size())
return;
@@ -1451,7 +1468,7 @@
Strong<SymbolTable> symbolTable = stackEntry.m_symbolTable;
ConcurrentJITLocker locker(symbolTable->m_lock);
bool hasCapturedVariables = false;
- for (auto entry : environment) {
+ for (auto& entry : environment) {
if (entry.value.isCaptured()) {
hasCapturedVariables = true;
continue;
@@ -1471,7 +1488,8 @@
stackEntry.m_scope->deref();
}
- m_TDZStack.removeLast();
+ if (tdzRequirement == TDZRequirement::UnderTDZ)
+ m_TDZStack.removeLast();
}
void BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration(VariableEnvironmentNode* node, RegisterID* loopSymbolTable)
@@ -1576,14 +1594,14 @@
// }
for (unsigned i = m_symbolTableStack.size(); i--; ) {
SymbolTableStackEntry& stackEntry = m_symbolTableStack[i];
- if (stackEntry.m_isWithOrCatch)
+ if (stackEntry.m_isWithScope)
return Variable(property);
Strong<SymbolTable>& symbolTable = stackEntry.m_symbolTable;
SymbolTableEntry symbolTableEntry = symbolTable->get(property.impl());
if (symbolTableEntry.isNull())
continue;
- return variableForLocalEntry(property, symbolTableEntry, stackEntry.m_symbolTableConstantIndex, symbolTable->correspondsToLexicalScope());
+ return variableForLocalEntry(property, symbolTableEntry, stackEntry.m_symbolTableConstantIndex, symbolTable->scopeType() == SymbolTable::ScopeType::LexicalScope);
}
return Variable(property);
@@ -1661,7 +1679,7 @@
ResolveType BytecodeGenerator::resolveType()
{
for (unsigned i = m_symbolTableStack.size(); i--; ) {
- if (m_symbolTableStack[i].m_isWithOrCatch)
+ if (m_symbolTableStack[i].m_isWithScope)
return Dynamic;
}
@@ -1688,9 +1706,9 @@
// that code sensible and obviate the need for us to do bad things.
for (unsigned i = m_symbolTableStack.size(); i--; ) {
SymbolTableStackEntry& stackEntry = m_symbolTableStack[i];
- // We should not resolve a variable to VarKind::Scope if a "with" or "catch" scope lies in between the current
+ // We should not resolve a variable to VarKind::Scope if a "with" scope lies in between the current
// scope and the resolved scope.
- RELEASE_ASSERT(!stackEntry.m_isWithOrCatch);
+ RELEASE_ASSERT(!stackEntry.m_isWithScope);
if (stackEntry.m_symbolTable->get(variable.ident().impl()).isNull())
continue;
@@ -2567,12 +2585,12 @@
emitMove(dst, parentScope.get());
}
-void BytecodeGenerator::emitPopWithOrCatchScope(RegisterID* srcDst)
+void BytecodeGenerator::emitPopWithScope(RegisterID* srcDst)
{
emitPopScope(srcDst, srcDst);
popScopedControlFlowContext();
SymbolTableStackEntry stackEntry = m_symbolTableStack.takeLast();
- RELEASE_ASSERT(stackEntry.m_isWithOrCatch);
+ RELEASE_ASSERT(stackEntry.m_isWithScope);
}
void BytecodeGenerator::emitDebugHook(DebugHookID debugHookID, unsigned line, unsigned charOffset, unsigned lineStart)
@@ -3007,17 +3025,19 @@
m_localScopeDepth--;
}
-void BytecodeGenerator::emitPushCatchScope(RegisterID* dst, const Identifier& property, RegisterID* value, unsigned attributes)
+void BytecodeGenerator::emitPushCatchScope(const Identifier& property, RegisterID* exceptionValue, VariableEnvironment& environment)
{
- pushScopedControlFlowContext();
+ RELEASE_ASSERT(environment.contains(property.impl()));
+ pushLexicalScopeInternal(environment, true, nullptr, TDZRequirement::NotUnderTDZ, ScopeType::CatchScope);
+ Variable exceptionVar = variable(property);
+ RELEASE_ASSERT(exceptionVar.isResolved());
+ RefPtr<RegisterID> scope = emitResolveScope(nullptr, exceptionVar);
+ emitPutToScope(scope.get(), exceptionVar, exceptionValue, ThrowIfNotFound);
+}
- emitOpcode(op_push_name_scope);
- instructions().append(dst->index());
- instructions().append(value->index());
- instructions().append(addConstantValue(SymbolTable::createNameScopeTable(*vm(), property, attributes))->index());
- instructions().append(JSNameScope::CatchScope);
-
- m_symbolTableStack.append(SymbolTableStackEntry{ Strong<SymbolTable>(), nullptr, true, 0 });
+void BytecodeGenerator::emitPopCatchScope(VariableEnvironment& environment)
+{
+ popLexicalScopeInternal(environment, TDZRequirement::NotUnderTDZ);
}
void BytecodeGenerator::beginSwitch(RegisterID* scrutineeRegister, SwitchInfo::SwitchType type)
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
index 3adc510..347f0fe 100644
--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
@@ -581,12 +581,13 @@
void emitThrowTypeError(const String& message);
void emitPushFunctionNameScope(RegisterID* dst, const Identifier& property, RegisterID* value, unsigned attributes);
- void emitPushCatchScope(RegisterID* dst, const Identifier& property, RegisterID* value, unsigned attributes);
+ void emitPushCatchScope(const Identifier& property, RegisterID* exceptionValue, VariableEnvironment&);
+ void emitPopCatchScope(VariableEnvironment&);
void emitGetScope();
RegisterID* emitPushWithScope(RegisterID* dst, RegisterID* scope);
void emitPopScope(RegisterID* dst, RegisterID* scope);
- void emitPopWithOrCatchScope(RegisterID* srcDst);
+ void emitPopWithScope(RegisterID* srcDst);
RegisterID* emitGetParentScope(RegisterID* dst, RegisterID* scope);
void emitDebugHook(DebugHookID, unsigned line, unsigned charOffset, unsigned lineStart);
@@ -622,7 +623,10 @@
OpcodeID lastOpcodeID() const { return m_lastOpcodeID; }
private:
- void pushLexicalScopeInternal(VariableEnvironment&, bool canOptimizeTDZChecks, RegisterID** constantSymbolTableResult);
+ enum class TDZRequirement { UnderTDZ, NotUnderTDZ };
+ enum class ScopeType { CatchScope, LetConstScope };
+ void pushLexicalScopeInternal(VariableEnvironment&, bool canOptimizeTDZChecks, RegisterID** constantSymbolTableResult, TDZRequirement, ScopeType);
+ void popLexicalScopeInternal(VariableEnvironment&, TDZRequirement);
public:
void pushLexicalScope(VariableEnvironmentNode*, bool canOptimizeTDZChecks, RegisterID** constantSymbolTableResult = nullptr);
void popLexicalScope(VariableEnvironmentNode*);
@@ -738,7 +742,7 @@
struct SymbolTableStackEntry {
Strong<SymbolTable> m_symbolTable;
RegisterID* m_scope;
- bool m_isWithOrCatch;
+ bool m_isWithScope;
int m_symbolTableConstantIndex;
};
Vector<SymbolTableStackEntry> m_symbolTableStack;
diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
index da46b9f..7c4ea62 100644
--- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
+++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
@@ -1858,8 +1858,8 @@
generator.emitTDZCheckIfNecessary(var, local, nullptr);
if (var.isReadOnly() && m_assignmentContext != AssignmentContext::ConstDeclarationStatement) {
+ result = generator.emitNode(dst, m_right); // Execute side effects first.
generator.emitReadOnlyExceptionIfNeeded(var);
- result = generator.emitNode(dst, m_right);
} else if (var.isSpecial() || generator.vm()->typeProfiler()) {
RefPtr<RegisterID> tempDst = generator.tempDestination(dst);
generator.emitNode(tempDst.get(), m_right);
@@ -1888,9 +1888,10 @@
dst = 0;
RefPtr<RegisterID> result = generator.emitNode(dst, m_right);
if (var.isReadOnly() && m_assignmentContext != AssignmentContext::ConstDeclarationStatement) {
+ RegisterID* result = generator.emitNode(dst, m_right); // Execute side effects first.
bool threwException = generator.emitReadOnlyExceptionIfNeeded(var);
if (threwException)
- return generator.emitNode(dst, m_right);
+ return result;
}
generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
RegisterID* returnResult = generator.emitPutToScope(scope.get(), var, result.get(), generator.isStrictMode() ? ThrowIfNotFound : DoNotThrowIfNotFound);
@@ -2683,7 +2684,7 @@
generator.emitExpressionInfo(m_divot, m_divot - m_expressionLength, m_divot);
generator.emitPushWithScope(generator.scopeRegister(), scope.get());
generator.emitNode(dst, m_statement);
- generator.emitPopWithOrCatchScope(generator.scopeRegister());
+ generator.emitPopWithScope(generator.scopeRegister());
}
// ------------------------------ CaseClauseNode --------------------------------
@@ -2933,10 +2934,10 @@
tryData = generator.pushTry(here.get());
}
- generator.emitPushCatchScope(generator.scopeRegister(), m_thrownValueIdent, thrownValueRegister.get(), DontDelete);
+ generator.emitPushCatchScope(m_thrownValueIdent, thrownValueRegister.get(), m_catchEnvironment);
generator.emitProfileControlFlow(m_tryBlock->endOffset() + 1);
generator.emitNode(dst, m_catchBlock);
- generator.emitPopWithOrCatchScope(generator.scopeRegister());
+ generator.emitPopCatchScope(m_catchEnvironment);
generator.emitLabel(catchEndLabel.get());
}