aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/compiler')
-rw-r--r--src/qml/compiler/qv4bytecodehandler.cpp6
-rw-r--r--src/qml/compiler/qv4codegen.cpp27
-rw-r--r--src/qml/compiler/qv4codegen_p.h3
-rw-r--r--src/qml/compiler/qv4compileddata_p.h11
-rw-r--r--src/qml/compiler/qv4compiler.cpp5
-rw-r--r--src/qml/compiler/qv4compilercontext.cpp56
-rw-r--r--src/qml/compiler/qv4compilercontext_p.h4
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp8
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h4
9 files changed, 106 insertions, 18 deletions
diff --git a/src/qml/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp
index e2533a0505..181594010d 100644
--- a/src/qml/compiler/qv4bytecodehandler.cpp
+++ b/src/qml/compiler/qv4bytecodehandler.cpp
@@ -290,6 +290,9 @@ std::vector<int> ByteCodeHandler::collectLabelsInBytecode(const char *code, uint
addLabel(code - start + offset);
COLLECTOR_END_INSTR(UnwindToLabel)
+ COLLECTOR_BEGIN_INSTR(DeadTemporalZoneCheck)
+ COLLECTOR_END_INSTR(DeadTemporalZoneCheck)
+
COLLECTOR_BEGIN_INSTR(ThrowException)
COLLECTOR_END_INSTR(ThrowException)
@@ -519,6 +522,9 @@ std::vector<int> ByteCodeHandler::collectLabelsInBytecode(const char *code, uint
COLLECTOR_END_INSTR(Debug)
#endif // QT_NO_QML_DEBUGGER
+ COLLECTOR_BEGIN_INSTR(InitializeBlockDeadTemporalZone)
+ COLLECTOR_END_INSTR(InitializeBlockDeadTemporalZone)
+
COLLECTOR_BEGIN_INSTR(LoadQmlContext)
COLLECTOR_END_INSTR(LoadQmlContext)
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index 734de853d1..5d16494c1b 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -47,6 +47,7 @@
#include <QtCore/QBitArray>
#include <QtCore/QLinkedList>
#include <QtCore/QStack>
+#include <QScopeGuard>
#include <private/qqmljsast_p.h>
#include <private/qv4string_p.h>
#include <private/qv4value_p.h>
@@ -501,8 +502,14 @@ void Codegen::variableDeclaration(PatternElement *ast)
{
RegisterScope scope(this);
- if (!ast->initializer)
+ if (!ast->initializer) {
+ if (ast->isLexicallyScoped()) {
+ Reference::fromConst(this, Encode::undefined()).loadInAccumulator();
+ Reference varToStore = targetForPatternElement(ast);
+ varToStore.storeConsumeAccumulator();
+ }
return;
+ }
initializeAndDestructureBindingElement(ast, Reference(), /*isDefinition*/ true);
}
@@ -2258,6 +2265,8 @@ Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs)
r.isVolatile = true;
r.isArgOrEval = resolved.isArgOrEval;
r.isReferenceToConst = resolved.isConst;
+ r.requiresTDZCheck = resolved.requiresTDZCheck;
+ r.name = name; // used to show correct name at run-time when TDZ check fails.
return r;
}
@@ -3818,7 +3827,7 @@ Codegen::Reference &Codegen::Reference::operator =(const Reference &other)
scope = other.scope;
break;
case Name:
- name = other.name;
+ // name is always copied
break;
case Member:
propertyBase = other.propertyBase;
@@ -3848,6 +3857,8 @@ Codegen::Reference &Codegen::Reference::operator =(const Reference &other)
codegen = other.codegen;
isReadonly = other.isReadonly;
isReferenceToConst = other.isReferenceToConst;
+ name = other.name;
+ requiresTDZCheck = other.requiresTDZCheck;
stackSlotIsLocalOrArgument = other.stackSlotIsLocalOrArgument;
isVolatile = other.isVolatile;
global = other.global;
@@ -3969,10 +3980,10 @@ void Codegen::Reference::storeOnStack(int slotIndex) const
Codegen::Reference Codegen::Reference::doStoreOnStack(int slotIndex) const
{
- if (isStackSlot() && slotIndex == -1 && !(stackSlotIsLocalOrArgument && isVolatile))
+ if (isStackSlot() && slotIndex == -1 && !(stackSlotIsLocalOrArgument && isVolatile) && !requiresTDZCheck)
return *this;
- if (isStackSlot()) { // temp-to-temp move
+ if (isStackSlot() && !requiresTDZCheck) { // temp-to-temp move
Reference dest = Reference::fromStackSlot(codegen, slotIndex);
Instruction::MoveReg move;
move.srcReg = stackSlot();
@@ -4130,6 +4141,14 @@ void Codegen::Reference::storeAccumulator() const
void Codegen::Reference::loadInAccumulator() const
{
+ auto tdzGuard = qScopeGuard([this](){
+ if (!requiresTDZCheck)
+ return;
+ Instruction::DeadTemporalZoneCheck check;
+ check.name = codegen->registerString(name);
+ codegen->bytecodeGenerator->addInstruction(check);
+ });
+
switch (type) {
case Accumulator:
return;
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index 516f506396..b7992bd8f2 100644
--- a/src/qml/compiler/qv4codegen_p.h
+++ b/src/qml/compiler/qv4codegen_p.h
@@ -219,7 +219,7 @@ public:
case Subscript:
return true;
default:
- return false;
+ return requiresTDZCheck;
}
}
bool isConstant() const { return type == Const; }
@@ -394,6 +394,7 @@ public:
mutable bool isArgOrEval = false;
bool isReadonly = false;
bool isReferenceToConst = false;
+ bool requiresTDZCheck = false;
bool stackSlotIsLocalOrArgument = false;
bool isVolatile = false;
bool global = false;
diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h
index dca38eae54..1f6c591ada 100644
--- a/src/qml/compiler/qv4compileddata_p.h
+++ b/src/qml/compiler/qv4compileddata_p.h
@@ -237,6 +237,8 @@ struct Block
{
quint32_le nLocals;
quint32_le localsOffset;
+ quint16_le sizeOfLocalTemporalDeadZone;
+ quint16_le padding;
const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); }
@@ -251,7 +253,7 @@ struct Block
return (a + 7) & ~size_t(7);
}
};
-static_assert(sizeof(Block) == 8, "Block structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+static_assert(sizeof(Block) == 12, "Block structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
// Function is aligned on an 8-byte boundary to make sure there are no bus errors or penalties
// for unaligned access. The ordering of the fields is also from largest to smallest.
@@ -276,6 +278,10 @@ struct Function
quint16_le nLineNumbers;
size_t lineNumberOffset() const { return localsOffset + nLocals * sizeof(quint32); }
quint32_le nestedFunctionIndex; // for functions that only return a single closure, used in signal handlers
+ quint16_le sizeOfLocalTemporalDeadZone;
+ quint16_le firstTemporalDeadZoneRegister;
+ quint16_le sizeOfRegisterTemporalDeadZone;
+ quint16_le nRegisters;
Location location;
// Qml Extensions Begin
@@ -293,7 +299,6 @@ struct Function
// Keep all unaligned data at the end
quint8 flags;
quint8 padding1;
- quint16_le nRegisters;
// quint32 formalsIndex[nFormals]
// quint32 localsIndex[nLocals]
@@ -326,7 +331,7 @@ struct Function
return (a + 7) & ~size_t(7);
}
};
-static_assert(sizeof(Function) == 48, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+static_assert(sizeof(Function) == 52, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Method {
enum Type {
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index 4a726c366d..b6e5504c24 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -398,6 +398,10 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
function->formalsOffset = currentOffset;
currentOffset += function->nFormals * sizeof(quint32);
+ function->sizeOfLocalTemporalDeadZone = irFunction->sizeOfLocalTemporalDeadZone;
+ function->sizeOfRegisterTemporalDeadZone = irFunction->sizeOfRegisterTemporalDeadZone;
+ function->firstTemporalDeadZoneRegister = irFunction->firstTemporalDeadZoneRegister;
+
function->nLocals = irFunction->locals.size();
function->localsOffset = currentOffset;
currentOffset += function->nLocals * sizeof(quint32);
@@ -532,6 +536,7 @@ void QV4::Compiler::JSUnitGenerator::writeBlock(char *b, QV4::Compiler::Context
quint32 currentOffset = sizeof(QV4::CompiledData::Block);
currentOffset = (currentOffset + 7) & ~quint32(0x7);
+ block->sizeOfLocalTemporalDeadZone = irBlock->sizeOfLocalTemporalDeadZone;
block->nLocals = irBlock->locals.size();
block->localsOffset = currentOffset;
currentOffset += block->nLocals * sizeof(quint32);
diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp
index 09be19e427..ac0c2e0ecc 100644
--- a/src/qml/compiler/qv4compilercontext.cpp
+++ b/src/qml/compiler/qv4compilercontext.cpp
@@ -126,6 +126,7 @@ Context::ResolvedName Context::resolveName(const QString &name)
result.scope = scope;
result.index = m.index;
result.isConst = (m.scope == VariableScope::Const);
+ result.requiresTDZCheck = m.isLexicallyScoped();
if (c->isStrict && (name == QLatin1String("arguments") || name == QLatin1String("eval")))
result.isArgOrEval = true;
return result;
@@ -162,6 +163,8 @@ Context::ResolvedName Context::resolveName(const QString &name)
result.index = i;
result.type = ResolvedName::Import;
result.isConst = true;
+ // We don't know at compile time whether the imported value is let/const or not.
+ result.requiresTDZCheck = true;
return result;
}
}
@@ -209,6 +212,13 @@ void Context::emitBlockHeader(Codegen *codegen)
}
}
+ if (contextType == ContextType::Block && sizeOfRegisterTemporalDeadZone > 0) {
+ Instruction::InitializeBlockDeadTemporalZone tdzInit;
+ tdzInit.firstReg = registerOffset + nRegisters - sizeOfRegisterTemporalDeadZone;
+ tdzInit.count = sizeOfRegisterTemporalDeadZone;
+ bytecodeGenerator->addInstruction(tdzInit);
+ }
+
if (usesThis) {
Q_ASSERT(!isStrict);
// make sure we convert this to an object
@@ -303,21 +313,37 @@ void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator)
}
}
+ QVector<Context::MemberMap::Iterator> localsInTDZ;
+ const auto registerLocal = [this, &localsInTDZ](Context::MemberMap::iterator member) {
+ if (member->isLexicallyScoped()) {
+ localsInTDZ << member;
+ } else {
+ member->index = locals.size();
+ locals.append(member.key());
+ }
+ };
+
+ QVector<Context::MemberMap::Iterator> registersInTDZ;
+ const auto allocateRegister = [bytecodeGenerator, &registersInTDZ](Context::MemberMap::iterator member) {
+ if (member->isLexicallyScoped())
+ registersInTDZ << member;
+ else
+ member->index = bytecodeGenerator->newRegister();
+ };
+
switch (contextType) {
case ContextType::ESModule:
case ContextType::Block:
case ContextType::Function:
case ContextType::Binding: {
for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) {
- const QString &local = it.key();
if (it->canEscape) {
- it->index = locals.size();
- locals.append(local);
+ registerLocal(it);
} else {
if (it->type == Context::ThisFunctionName)
it->index = CallData::Function;
else
- it->index = bytecodeGenerator->newRegister();
+ allocateRegister(it);
}
}
break;
@@ -327,15 +353,25 @@ void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator)
for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) {
if (!it->isLexicallyScoped() && (contextType == ContextType::Global || !isStrict))
continue;
- if (it->canEscape) {
- it->index = locals.size();
- locals.append(it.key());
- } else {
- it->index = bytecodeGenerator->newRegister();
- }
+ if (it->canEscape)
+ registerLocal(it);
+ else
+ allocateRegister(it);
}
break;
}
+
+ sizeOfLocalTemporalDeadZone = localsInTDZ.count();
+ for (auto &member: qAsConst(localsInTDZ)) {
+ member->index = locals.size();
+ locals.append(member.key());
+ }
+
+ sizeOfRegisterTemporalDeadZone = registersInTDZ.count();
+ firstTemporalDeadZoneRegister = bytecodeGenerator->currentRegister();
+ for (auto &member: qAsConst(registersInTDZ))
+ member->index = bytecodeGenerator->newRegister();
+
nRegisters = bytecodeGenerator->currentRegister() - registerOffset;
}
diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h
index bd0bd90a59..e7847e7072 100644
--- a/src/qml/compiler/qv4compilercontext_p.h
+++ b/src/qml/compiler/qv4compilercontext_p.h
@@ -189,6 +189,9 @@ struct Context {
int nRegisters = 0;
int registerOffset = -1;
+ int sizeOfLocalTemporalDeadZone = 0;
+ int firstTemporalDeadZoneRegister = 0;
+ int sizeOfRegisterTemporalDeadZone = 0;
bool hasDirectEval = false;
bool allVarsEscape = false;
bool hasNestedFunctions = false;
@@ -323,6 +326,7 @@ struct Context {
Type type = Unresolved;
bool isArgOrEval = false;
bool isConst = false;
+ bool requiresTDZCheck = false;
int scope = -1;
int index = -1;
bool isValid() const { return type != Unresolved; }
diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp
index 514ec9ac9b..4653b7217d 100644
--- a/src/qml/compiler/qv4instr_moth.cpp
+++ b/src/qml/compiler/qv4instr_moth.cpp
@@ -415,6 +415,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << "(" << level << ") " << ABSOLUTE_OFFSET();
MOTH_END_INSTR(UnwindToLabel)
+ MOTH_BEGIN_INSTR(DeadTemporalZoneCheck)
+ d << name;
+ MOTH_END_INSTR(DeadTemporalZoneCheck)
+
MOTH_BEGIN_INSTR(ThrowException)
MOTH_END_INSTR(ThrowException)
@@ -690,6 +694,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_BEGIN_INSTR(Debug)
MOTH_END_INSTR(Debug)
+ MOTH_BEGIN_INSTR(InitializeBlockDeadTemporalZone)
+ d << dumpRegister(firstReg, nFormals) << ", " << count;
+ MOTH_END_INSTR(InitializeBlockDeadTemporalZone)
+
MOTH_BEGIN_INSTR(LoadQmlContext)
d << dumpRegister(result, nFormals);
MOTH_END_INSTR(LoadQmlContext)
diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h
index 25a07208c2..7c2cbf717e 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -118,6 +118,7 @@ QT_BEGIN_NAMESPACE
#define INSTR_SetUnwindHandler(op) INSTRUCTION(op, SetUnwindHandler, 1, offset)
#define INSTR_UnwindDispatch(op) INSTRUCTION(op, UnwindDispatch, 0)
#define INSTR_UnwindToLabel(op) INSTRUCTION(op, UnwindToLabel, 2, level, offset)
+#define INSTR_DeadTemporalZoneCheck(op) INSTRUCTION(op, DeadTemporalZoneCheck, 1, name)
#define INSTR_ThrowException(op) INSTRUCTION(op, ThrowException, 0)
#define INSTR_GetException(op) INSTRUCTION(op, GetException, 0)
#define INSTR_SetException(op) INSTRUCTION(op, SetException, 0)
@@ -192,6 +193,7 @@ QT_BEGIN_NAMESPACE
#define INSTR_Sub(op) INSTRUCTION(op, Sub, 1, lhs)
#define INSTR_LoadQmlContext(op) INSTRUCTION(op, LoadQmlContext, 1, result)
#define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result)
+#define INSTR_InitializeBlockDeadTemporalZone(op) INSTRUCTION(op, InitializeBlockDeadTemporalZone, 2, firstReg, count)
#define FOR_EACH_MOTH_INSTR_ALL(F) \
F(Nop) \
@@ -295,6 +297,7 @@ QT_BEGIN_NAMESPACE
F(SetUnwindHandler) \
F(UnwindDispatch) \
F(UnwindToLabel) \
+ F(DeadTemporalZoneCheck) \
F(ThrowException) \
F(GetException) \
F(SetException) \
@@ -326,6 +329,7 @@ QT_BEGIN_NAMESPACE
F(LoadSuperConstructor) \
F(PushScriptContext) \
F(PopScriptContext) \
+ F(InitializeBlockDeadTemporalZone) \
F(Debug) \
#define MOTH_NUM_INSTRUCTIONS() (static_cast<int>(Moth::Instr::Type::Debug_Wide) + 1)