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(&registerFor(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(&registerFor(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());
     }