+2016-06-30 Filip Pizlo <fpizlo@apple.com>
+
+ 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 <bpoulain@apple.com>
[JSC] RegExp.compile is not returning the regexp when it succeed
#include "StrongInlines.h"
#include "UnlinkedCodeBlock.h"
#include "UnlinkedInstructionStream.h"
+#include <wtf/CommaPrinter.h>
#include <wtf/StdLibExtras.h>
#include <wtf/text/WTFString.h>
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());
// 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
m_codeBlock->setNumParameters(1);
- pushTDZVariables(*parentScopeTDZVariables, TDZCheckOptimization::DoNotOptimize);
+ pushTDZVariables(*parentScopeTDZVariables, TDZCheckOptimization::DoNotOptimize, TDZRequirement::UnderTDZ);
emitEnter();
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);
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);
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.
stackEntry.m_scope->deref();
}
- if (tdzRequirement == TDZRequirement::UnderTDZ)
- m_TDZStack.removeLast();
+ m_TDZStack.removeLast();
}
void BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration(VariableEnvironmentNode* node, RegisterID* loopSymbolTable)
result.setIsReadOnly();
return result;
}
-
+
return Variable(property);
}
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;
{
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());
+ }
}
}
void BytecodeGenerator::emitPopCatchScope(VariableEnvironment& environment)
{
- popLexicalScopeInternal(environment, TDZRequirement::NotUnderTDZ);
+ popLexicalScopeInternal(environment);
}
void BytecodeGenerator::beginSwitch(RegisterID* scrutineeRegister, SwitchInfo::SwitchType type)
}
} // 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
+
bool isConst() const { return isReadOnly() && m_isLexicallyScoped; }
void setIsReadOnly() { m_attributes |= ReadOnly; }
+ void dump(PrintStream&) const;
+
private:
Identifier m_ident;
VarOffset m_offset;
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*);
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;
}
+namespace WTF {
+
+void printInternal(PrintStream&, JSC::Variable::VariableKind);
+
+} // namespace WTF
+
#endif // BytecodeGenerator_h