Title: [202778] trunk/Source
Revision
202778
Author
[email protected]
Date
2016-07-02 10:43:56 -0700 (Sat, 02 Jul 2016)

Log Message

Scopes that are not under TDZ should still push their variables onto the TDZ stack so that lifting TDZ doesn't bypass that scope
https://bugs.webkit.org/show_bug.cgi?id=159332
rdar://problem/27018958

Reviewed by Saam Barati.
        
This fixes an instacrash in this code:
        
    try{}catch(e){}print(e);let e;
        
We lift TDZ for "e" in "catch (e){}", but since that scope doesn't push anything onto the
TDZ stack, we lift TDZ from "let e".
        
The problem is that we weren't tracking the set of variables that do not have TDZ. We need
to track them to "block" the traversal that lifts TDZ. This change fixes this issue by
using a map that tracks all known variables, and tells you if they are under TDZ or not.

* bytecode/CodeBlock.h:
(JSC::CodeBlock::numParameters):
* bytecode/CodeOrigin.h:
* bytecompiler/BytecodeGenerator.cpp:
(JSC::Label::setLocation):
(JSC::Variable::dump):
(JSC::BytecodeGenerator::generate):
(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::pushLexicalScopeInternal):
(JSC::BytecodeGenerator::popLexicalScope):
(JSC::BytecodeGenerator::popLexicalScopeInternal):
(JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration):
(JSC::BytecodeGenerator::variable):
(JSC::BytecodeGenerator::needsTDZCheck):
(JSC::BytecodeGenerator::liftTDZCheckIfPossible):
(JSC::BytecodeGenerator::pushTDZVariables):
(JSC::BytecodeGenerator::getVariablesUnderTDZ):
(JSC::BytecodeGenerator::endGenerator):
(WTF::printInternal):
* bytecompiler/BytecodeGenerator.h:
(JSC::Variable::isConst):
(JSC::Variable::setIsReadOnly):
* interpreter/CallFrame.h:
(JSC::ExecState::topOfFrame):
* tests/stress/lift-tdz-bypass-catch.js: Added.
(foo):
(catch):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (202777 => 202778)


--- trunk/Source/_javascript_Core/ChangeLog	2016-07-02 11:29:42 UTC (rev 202777)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-07-02 17:43:56 UTC (rev 202778)
@@ -1,3 +1,50 @@
+2016-06-30  Filip Pizlo  <[email protected]>
+
+        Scopes that are not under TDZ should still push their variables onto the TDZ stack so that lifting TDZ doesn't bypass that scope
+        https://bugs.webkit.org/show_bug.cgi?id=159332
+        rdar://problem/27018958
+
+        Reviewed by Saam Barati.
+        
+        This fixes an instacrash in this code:
+        
+            try{}catch(e){}print(e);let e;
+        
+        We lift TDZ for "e" in "catch (e){}", but since that scope doesn't push anything onto the
+        TDZ stack, we lift TDZ from "let e".
+        
+        The problem is that we weren't tracking the set of variables that do not have TDZ. We need
+        to track them to "block" the traversal that lifts TDZ. This change fixes this issue by
+        using a map that tracks all known variables, and tells you if they are under TDZ or not.
+
+        * bytecode/CodeBlock.h:
+        (JSC::CodeBlock::numParameters):
+        * bytecode/CodeOrigin.h:
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::Label::setLocation):
+        (JSC::Variable::dump):
+        (JSC::BytecodeGenerator::generate):
+        (JSC::BytecodeGenerator::BytecodeGenerator):
+        (JSC::BytecodeGenerator::pushLexicalScopeInternal):
+        (JSC::BytecodeGenerator::popLexicalScope):
+        (JSC::BytecodeGenerator::popLexicalScopeInternal):
+        (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration):
+        (JSC::BytecodeGenerator::variable):
+        (JSC::BytecodeGenerator::needsTDZCheck):
+        (JSC::BytecodeGenerator::liftTDZCheckIfPossible):
+        (JSC::BytecodeGenerator::pushTDZVariables):
+        (JSC::BytecodeGenerator::getVariablesUnderTDZ):
+        (JSC::BytecodeGenerator::endGenerator):
+        (WTF::printInternal):
+        * bytecompiler/BytecodeGenerator.h:
+        (JSC::Variable::isConst):
+        (JSC::Variable::setIsReadOnly):
+        * interpreter/CallFrame.h:
+        (JSC::ExecState::topOfFrame):
+        * tests/stress/lift-tdz-bypass-catch.js: Added.
+        (foo):
+        (catch):
+
 2016-07-01  Benjamin Poulain  <[email protected]>
 
         [JSC] RegExp.compile is not returning the regexp when it succeed

Modified: trunk/Source/_javascript_Core/bytecode/CodeBlock.h (202777 => 202778)


--- trunk/Source/_javascript_Core/bytecode/CodeBlock.h	2016-07-02 11:29:42 UTC (rev 202777)
+++ trunk/Source/_javascript_Core/bytecode/CodeBlock.h	2016-07-02 17:43:56 UTC (rev 202778)
@@ -141,7 +141,7 @@
     CString sourceCodeForTools() const; // Not quite the actual source we parsed; this will do things like prefix the source for a function with a reified signature.
     CString sourceCodeOnOneLine() const; // As sourceCodeForTools(), but replaces all whitespace runs with a single space.
     void dumpAssumingJITType(PrintStream&, JITCode::JITType) const;
-    void dump(PrintStream&) const;
+    JS_EXPORT_PRIVATE void dump(PrintStream&) const;
 
     int numParameters() const { return m_numParameters; }
     void setNumParameters(int newValue);

Modified: trunk/Source/_javascript_Core/bytecode/CodeOrigin.h (202777 => 202778)


--- trunk/Source/_javascript_Core/bytecode/CodeOrigin.h	2016-07-02 11:29:42 UTC (rev 202777)
+++ trunk/Source/_javascript_Core/bytecode/CodeOrigin.h	2016-07-02 17:43:56 UTC (rev 202778)
@@ -109,7 +109,7 @@
     // Get the inline stack. This is slow, and is intended for debugging only.
     Vector<CodeOrigin> inlineStack() const;
     
-    void dump(PrintStream&) const;
+    JS_EXPORT_PRIVATE void dump(PrintStream&) const;
     void dumpInContext(PrintStream&, DumpContext*) const;
 
 private:

Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp (202777 => 202778)


--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp	2016-07-02 11:29:42 UTC (rev 202777)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp	2016-07-02 17:43:56 UTC (rev 202778)
@@ -45,6 +45,7 @@
 #include "StrongInlines.h"
 #include "UnlinkedCodeBlock.h"
 #include "UnlinkedInstructionStream.h"
+#include <wtf/CommaPrinter.h>
 #include <wtf/StdLibExtras.h>
 #include <wtf/text/WTFString.h>
 
@@ -61,6 +62,18 @@
         m_generator.instructions()[m_unresolvedJumps[i].second].u.operand = m_location - m_unresolvedJumps[i].first;
 }
 
+void Variable::dump(PrintStream& out) const
+{
+    out.print(
+        "{ident = ", m_ident,
+        ", offset = ", m_offset,
+        ", local = ", RawPointer(m_local),
+        ", attributes = ", m_attributes,
+        ", kind = ", m_kind,
+        ", symbolTableConstantIndex = ", m_symbolTableConstantIndex,
+        ", isLexicallyScoped = ", m_isLexicallyScoped, "}");
+}
+
 ParserError BytecodeGenerator::generate()
 {
     m_codeBlock->setThisRegister(m_thisRegister.virtualRegister());
@@ -603,7 +616,7 @@
 
     // All "addVar()"s needs to happen before "initializeDefaultParameterValuesAndSetupFunctionScopeStack()" is called
     // because a function's default parameter ExpressionNodes will use temporary registers.
-    pushTDZVariables(*parentScopeTDZVariables, TDZCheckOptimization::DoNotOptimize);
+    pushTDZVariables(*parentScopeTDZVariables, TDZCheckOptimization::DoNotOptimize, TDZRequirement::UnderTDZ);
     initializeDefaultParameterValuesAndSetupFunctionScopeStack(parameters, isSimpleParameterList, functionNode, functionSymbolTable, symbolTableConstantIndex, captures, shouldCreateArgumentsVariableInParameterScope);
     
     // If we don't have  default parameter _expression_, then loading |this| inside an arrow function must be done
@@ -639,7 +652,7 @@
 
     m_codeBlock->setNumParameters(1);
 
-    pushTDZVariables(*parentScopeTDZVariables, TDZCheckOptimization::DoNotOptimize);
+    pushTDZVariables(*parentScopeTDZVariables, TDZCheckOptimization::DoNotOptimize, TDZRequirement::UnderTDZ);
 
     emitEnter();
 
@@ -756,7 +769,7 @@
     else
         constantSymbolTable = addConstantValue(moduleEnvironmentSymbolTable->cloneScopePart(*m_vm));
 
-    pushTDZVariables(lexicalVariables, TDZCheckOptimization::Optimize);
+    pushTDZVariables(lexicalVariables, TDZCheckOptimization::Optimize, TDZRequirement::UnderTDZ);
     bool isWithScope = false;
     m_symbolTableStack.append(SymbolTableStackEntry { moduleEnvironmentSymbolTable, m_topMostScope, isWithScope, constantSymbolTable->index() });
     emitPrefillStackTDZVariables(lexicalVariables, moduleEnvironmentSymbolTable);
@@ -1933,8 +1946,7 @@
 
     bool isWithScope = false;
     m_symbolTableStack.append(SymbolTableStackEntry{ symbolTable, newScope, isWithScope, symbolTableConstantIndex });
-    if (tdzRequirement == TDZRequirement::UnderTDZ)
-        pushTDZVariables(environment, tdzCheckOptimization);
+    pushTDZVariables(environment, tdzCheckOptimization, tdzRequirement);
 
     if (tdzRequirement == TDZRequirement::UnderTDZ)
         emitPrefillStackTDZVariables(environment, symbolTable);
@@ -2021,10 +2033,10 @@
 void BytecodeGenerator::popLexicalScope(VariableEnvironmentNode* node)
 {
     VariableEnvironment& environment = node->lexicalVariables();
-    popLexicalScopeInternal(environment, TDZRequirement::UnderTDZ);
+    popLexicalScopeInternal(environment);
 }
 
-void BytecodeGenerator::popLexicalScopeInternal(VariableEnvironment& environment, TDZRequirement tdzRequirement)
+void BytecodeGenerator::popLexicalScopeInternal(VariableEnvironment& environment)
 {
     // NOTE: This function only makes sense for scopes that aren't ScopeRegisterType::Var (only function name scope right now is ScopeRegisterType::Var).
     // This doesn't make sense for ScopeRegisterType::Var because we deref RegisterIDs here.
@@ -2057,8 +2069,7 @@
         stackEntry.m_scope->deref();
     }
 
-    if (tdzRequirement == TDZRequirement::UnderTDZ)
-        m_TDZStack.removeLast();
+    m_TDZStack.removeLast();
 }
 
 void BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration(VariableEnvironmentNode* node, RegisterID* loopSymbolTable)
@@ -2182,7 +2193,7 @@
             result.setIsReadOnly();
         return result;
     }
-
+    
     return Variable(property);
 }
 
@@ -2722,9 +2733,10 @@
 bool BytecodeGenerator::needsTDZCheck(const Variable& variable)
 {
     for (unsigned i = m_TDZStack.size(); i--;) {
-        VariableEnvironment& identifiers = m_TDZStack[i].first;
-        if (identifiers.contains(variable.ident().impl()))
-            return true;
+        auto iter = m_TDZStack[i].find(variable.ident().impl());
+        if (iter == m_TDZStack[i].end())
+            continue;
+        return iter->value != TDZNecessityLevel::NotNeeded;
     }
 
     return false;
@@ -2747,41 +2759,54 @@
 {
     RefPtr<UniquedStringImpl> identifier(variable.ident().impl());
     for (unsigned i = m_TDZStack.size(); i--;) {
-        VariableEnvironment& environment = m_TDZStack[i].first;
-        if (environment.contains(identifier)) {
-            TDZCheckOptimization tdzCheckOptimizationCapability = m_TDZStack[i].second;
-            if (tdzCheckOptimizationCapability == TDZCheckOptimization::Optimize) {
-                bool wasRemoved = environment.remove(identifier);
-                RELEASE_ASSERT(wasRemoved);
-            }
+        auto iter = m_TDZStack[i].find(identifier);
+        if (iter != m_TDZStack[i].end()) {
+            if (iter->value == TDZNecessityLevel::Optimize)
+                iter->value = TDZNecessityLevel::NotNeeded;
             break;
         }
     }
 }
 
-void BytecodeGenerator::pushTDZVariables(VariableEnvironment environment, TDZCheckOptimization optimization)
+void BytecodeGenerator::pushTDZVariables(const VariableEnvironment& environment, TDZCheckOptimization optimization, TDZRequirement requirement)
 {
     if (!environment.size())
         return;
+    
+    TDZNecessityLevel level;
+    if (requirement == TDZRequirement::UnderTDZ) {
+        if (optimization == TDZCheckOptimization::Optimize)
+            level = TDZNecessityLevel::Optimize;
+        else
+            level = TDZNecessityLevel::DoNotOptimize;
+    } else
+        level = TDZNecessityLevel::NotNeeded;
+    
+    TDZMap map;
+    for (const auto& entry : environment)
+        map.add(entry.key, entry.value.isFunction() ? TDZNecessityLevel::NotNeeded : level);
 
-    Vector<UniquedStringImpl*, 4> functionsToRemove;
-    for (const auto& entry : environment) {
-        if (entry.value.isFunction())
-            functionsToRemove.append(entry.key.get());
-    }
-
-    for (UniquedStringImpl* function : functionsToRemove)
-        environment.remove(function);
-
-    m_TDZStack.append(std::make_pair(WTFMove(environment), optimization));
+    m_TDZStack.append(WTFMove(map));
 }
 
 void BytecodeGenerator::getVariablesUnderTDZ(VariableEnvironment& result)
 {
-    for (auto& pair : m_TDZStack) {
-        VariableEnvironment& environment = pair.first;
-        for (auto entry : environment)
-            result.add(entry.key.get());
+    // NOTE: This is conservative. If called at "...", it will report "x" as being under TDZ:
+    //
+    //     {
+    //         {
+    //             let x;
+    //             ...
+    //         }
+    //         let x;
+    //     }
+    //
+    // FIXME: https://bugs.webkit.org/show_bug.cgi?id=159387
+    for (auto& map : m_TDZStack) {
+        for (auto& entry : map)  {
+            if (entry.value != TDZNecessityLevel::NotNeeded)
+                result.add(entry.key.get());
+        }
     }
 }
 
@@ -3836,7 +3861,7 @@
 
 void BytecodeGenerator::emitPopCatchScope(VariableEnvironment& environment) 
 {
-    popLexicalScopeInternal(environment, TDZRequirement::NotUnderTDZ);
+    popLexicalScopeInternal(environment);
 }
 
 void BytecodeGenerator::beginSwitch(RegisterID* scrutineeRegister, SwitchInfo::SwitchType type)
@@ -4631,3 +4656,21 @@
 }
 
 } // namespace JSC
+
+namespace WTF {
+
+void printInternal(PrintStream& out, JSC::Variable::VariableKind kind)
+{
+    switch (kind) {
+    case JSC::Variable::NormalVariable:
+        out.print("Normal");
+        return;
+    case JSC::Variable::SpecialVariable:
+        out.print("Special");
+        return;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+} // namespace WTF
+

Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h (202777 => 202778)


--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h	2016-07-02 11:29:42 UTC (rev 202777)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h	2016-07-02 17:43:56 UTC (rev 202778)
@@ -238,6 +238,8 @@
         bool isConst() const { return isReadOnly() && m_isLexicallyScoped; }
         void setIsReadOnly() { m_attributes |= ReadOnly; }
 
+        void dump(PrintStream&) const;
+
     private:
         Identifier m_ident;
         VarOffset m_offset;
@@ -728,7 +730,7 @@
         enum class ScopeRegisterType { Var, Block };
         void pushLexicalScopeInternal(VariableEnvironment&, TDZCheckOptimization, NestedScopeType, RegisterID** constantSymbolTableResult, TDZRequirement, ScopeType, ScopeRegisterType);
         void initializeBlockScopedFunctions(VariableEnvironment&, FunctionStack&, RegisterID* constantSymbolTable);
-        void popLexicalScopeInternal(VariableEnvironment&, TDZRequirement);
+        void popLexicalScopeInternal(VariableEnvironment&);
         template<typename LookUpVarKindFunctor>
         bool instantiateLexicalVariables(const VariableEnvironment&, SymbolTable*, ScopeRegisterType, LookUpVarKindFunctor);
         void emitPrefillStackTDZVariables(const VariableEnvironment&, SymbolTable*);
@@ -881,9 +883,15 @@
             int m_symbolTableConstantIndex;
         };
         Vector<SymbolTableStackEntry> m_symbolTableStack;
-        Vector<std::pair<VariableEnvironment, TDZCheckOptimization>> m_TDZStack;
+        enum class TDZNecessityLevel {
+            NotNeeded,
+            Optimize,
+            DoNotOptimize
+        };
+        typedef HashMap<RefPtr<UniquedStringImpl>, TDZNecessityLevel, IdentifierRepHash> TDZMap;
+        Vector<TDZMap> m_TDZStack;
         Optional<size_t> m_varScopeSymbolTableIndex;
-        void pushTDZVariables(VariableEnvironment, TDZCheckOptimization);
+        void pushTDZVariables(const VariableEnvironment&, TDZCheckOptimization, TDZRequirement);
 
         ScopeNode* const m_scopeNode;
         Strong<UnlinkedCodeBlock> m_codeBlock;
@@ -965,4 +973,10 @@
 
 }
 
+namespace WTF {
+
+void printInternal(PrintStream&, JSC::Variable::VariableKind);
+
+} // namespace WTF
+
 #endif // BytecodeGenerator_h

Modified: trunk/Source/_javascript_Core/interpreter/CallFrame.h (202777 => 202778)


--- trunk/Source/_javascript_Core/interpreter/CallFrame.h	2016-07-02 11:29:42 UTC (rev 202777)
+++ trunk/Source/_javascript_Core/interpreter/CallFrame.h	2016-07-02 17:43:56 UTC (rev 202778)
@@ -149,7 +149,7 @@
         
         // This will get you a CodeOrigin. It will always succeed. May return
         // CodeOrigin(0) if we're in native code.
-        CodeOrigin codeOrigin();
+        JS_EXPORT_PRIVATE CodeOrigin codeOrigin();
 
         Register* topOfFrame()
         {

Added: trunk/Source/_javascript_Core/tests/stress/lift-tdz-bypass-catch.js (0 => 202778)


--- trunk/Source/_javascript_Core/tests/stress/lift-tdz-bypass-catch.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/lift-tdz-bypass-catch.js	2016-07-02 17:43:56 UTC (rev 202778)
@@ -0,0 +1,10 @@
+//@ runDefault
+
+function foo () {
+try{}catch(e){}print(e);let e;
+}
+
+try {
+    foo();
+} catch (e) {}
+

Modified: trunk/Source/WTF/benchmarks/LockFairnessTest.cpp (202777 => 202778)


--- trunk/Source/WTF/benchmarks/LockFairnessTest.cpp	2016-07-02 11:29:42 UTC (rev 202777)
+++ trunk/Source/WTF/benchmarks/LockFairnessTest.cpp	2016-07-02 17:43:56 UTC (rev 202778)
@@ -48,12 +48,13 @@
 
 NO_RETURN void usage()
 {
-    printf("Usage: LockFairnessTest yieldspinlock|pausespinlock|wordlock|lock|barginglock|bargingwordlock|thunderlock|thunderwordlock|cascadelock|cascadewordlockhandofflock|mutex|all <num threads> <seconds per test>\n");
+    printf("Usage: LockFairnessTest yieldspinlock|pausespinlock|wordlock|lock|barginglock|bargingwordlock|thunderlock|thunderwordlock|cascadelock|cascadewordlockhandofflock|mutex|all <num threads> <seconds per test> <microseconds in critical section>\n");
     exit(1);
 }
 
 unsigned numThreads;
 double secondsPerTest;
+unsigned microsecondsInCriticalSection;
 
 struct Benchmark {
     template<typename LockType>
@@ -72,9 +73,19 @@
             threads[threadIndex] = createThread(
                 "Benchmark Thread",
                 [&, threadIndex] () {
+                    if (!microsecondsInCriticalSection) {
+                        while (keepGoing) {
+                            lock.lock();
+                            counts[threadIndex]++;
+                            lock.unlock();
+                        }
+                        return;
+                    }
+                    
                     while (keepGoing) {
                         lock.lock();
                         counts[threadIndex]++;
+                        usleep(microsecondsInCriticalSection);
                         lock.unlock();
                     }
                 });
@@ -106,9 +117,10 @@
 {
     WTF::initializeThreading();
     
-    if (argc != 4
+    if (argc != 5
         || sscanf(argv[2], "%u", &numThreads) != 1
-        || sscanf(argv[3], "%lf", &secondsPerTest) != 1)
+        || sscanf(argv[3], "%lf", &secondsPerTest) != 1
+        || sscanf(argv[4], "%u", &microsecondsInCriticalSection) != 1)
         usage();
     
     runEverything<Benchmark>(argv[1]);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to