aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml')
-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
-rw-r--r--src/qml/jit/qv4baselineassembler.cpp24
-rw-r--r--src/qml/jit/qv4baselineassembler_p.h1
-rw-r--r--src/qml/jit/qv4baselinejit.cpp11
-rw-r--r--src/qml/jit/qv4baselinejit_p.h2
-rw-r--r--src/qml/jsruntime/qv4context.cpp4
-rw-r--r--src/qml/jsruntime/qv4context_p.h6
-rw-r--r--src/qml/jsruntime/qv4module.cpp5
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp6
-rw-r--r--src/qml/jsruntime/qv4runtimeapi_p.h1
-rw-r--r--src/qml/jsruntime/qv4stackframe_p.h9
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp15
20 files changed, 189 insertions, 19 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)
diff --git a/src/qml/jit/qv4baselineassembler.cpp b/src/qml/jit/qv4baselineassembler.cpp
index c7d8f3c72c..5950901499 100644
--- a/src/qml/jit/qv4baselineassembler.cpp
+++ b/src/qml/jit/qv4baselineassembler.cpp
@@ -66,6 +66,7 @@ namespace JIT {
#define callHelper(x) PlatformAssemblerCommon::callRuntimeUnchecked(#x, reinterpret_cast<void *>(&x))
const QV4::Value::ValueTypeInternal IntegerTag = QV4::Value::ValueTypeInternal::Integer;
+const QV4::Value::ValueTypeInternal EmptyTag = QV4::Value::ValueTypeInternal::Empty;
static ReturnedValue toNumberHelper(ReturnedValue v)
{
@@ -172,6 +173,11 @@ public:
return branch64(Equal, AccumulatorRegister, TrustedImm64(Primitive::emptyValue().asReturnedValue()));
}
+ Jump jumpNotEmpty()
+ {
+ return branch64(NotEqual, AccumulatorRegister, TrustedImm64(Primitive::emptyValue().asReturnedValue()));
+ }
+
void toBoolean(std::function<void(RegisterID)> continuation)
{
urshift64(AccumulatorRegister, TrustedImm32(Value::IsIntegerConvertible_Shift), ScratchRegister);
@@ -634,6 +640,11 @@ public:
return branch32(Equal, AccumulatorRegisterTag, TrustedImm32(Primitive::emptyValue().asReturnedValue() >> 32));
}
+ Jump jumpNotEmpty()
+ {
+ return branch32(NotEqual, AccumulatorRegisterTag, TrustedImm32(Primitive::emptyValue().asReturnedValue() >> 32));
+ }
+
void toBoolean(std::function<void(RegisterID)> continuation)
{
urshift32(AccumulatorRegisterTag, TrustedImm32(Value::IsIntegerConvertible_Shift - 32),
@@ -1538,6 +1549,19 @@ void BaselineAssembler::popContext()
pasm()->storeHeapObject(PlatformAssembler::ScratchRegister, regAddr(CallData::Context));
}
+void BaselineAssembler::deadTemporalZoneCheck(int offsetForSavedIP, int variableName)
+{
+ auto valueIsAliveJump = pasm()->jumpNotEmpty();
+ storeInstructionPointer(offsetForSavedIP);
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(2);
+ passInt32AsArg(variableName, 1);
+ passEngineAsArg(0);
+ ASM_GENERATE_RUNTIME_CALL(Runtime::method_throwReferenceError, CallResultDestination::Ignore);
+ gotoCatchException();
+ valueIsAliveJump.link(pasm());
+}
+
void BaselineAssembler::ret()
{
pasm()->generateFunctionExit();
diff --git a/src/qml/jit/qv4baselineassembler_p.h b/src/qml/jit/qv4baselineassembler_p.h
index 4b64f91cd4..a2140ce47b 100644
--- a/src/qml/jit/qv4baselineassembler_p.h
+++ b/src/qml/jit/qv4baselineassembler_p.h
@@ -161,6 +161,7 @@ public:
void unwindToLabel(int level, int offset);
void pushCatchContext(int index, int name);
void popContext();
+ void deadTemporalZoneCheck(int offsetForSavedIP, int variableName);
// other stuff
void ret();
diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp
index b729f21059..e9f85a5c6b 100644
--- a/src/qml/jit/qv4baselinejit.cpp
+++ b/src/qml/jit/qv4baselinejit.cpp
@@ -579,6 +579,10 @@ void BaselineJIT::generate_UnwindToLabel(int level, int offset)
as->unwindToLabel(level, absoluteOffsetForJump(offset));
}
+void BaselineJIT::generate_DeadTemporalZoneCheck(int name)
+{
+ as->deadTemporalZoneCheck(nextInstructionOffset(), name);
+}
void BaselineJIT::generate_ThrowException()
{
@@ -931,6 +935,13 @@ void BaselineJIT::generate_LoadQmlImportedScripts(int result)
as->storeReg(result);
}
+void BaselineJIT::generate_InitializeBlockDeadTemporalZone(int firstReg, int count)
+{
+ as->loadValue(Primitive::emptyValue().rawValue());
+ for (int i = firstReg, end = firstReg + count; i < end; ++i)
+ as->storeReg(i);
+}
+
void BaselineJIT::startInstruction(Instr::Type /*instr*/)
{
if (hasLabel())
diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h
index 07a1853816..a3cb850fc1 100644
--- a/src/qml/jit/qv4baselinejit_p.h
+++ b/src/qml/jit/qv4baselinejit_p.h
@@ -134,6 +134,7 @@ public:
void generate_SetUnwindHandler(int offset) override;
void generate_UnwindDispatch() override;
void generate_UnwindToLabel(int level, int offset) override;
+ void generate_DeadTemporalZoneCheck(int name) override;
void generate_ThrowException() override;
void generate_GetException() override;
void generate_SetException() override;
@@ -208,6 +209,7 @@ public:
void generate_Sub(int lhs) override;
void generate_LoadQmlContext(int result) override;
void generate_LoadQmlImportedScripts(int result) override;
+ void generate_InitializeBlockDeadTemporalZone(int firstReg, int count) override;
void startInstruction(Moth::Instr::Type instr) override;
void endInstruction(Moth::Instr::Type instr) override;
diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp
index 327e7b78c4..d22179173c 100644
--- a/src/qml/jsruntime/qv4context.cpp
+++ b/src/qml/jsruntime/qv4context.cpp
@@ -76,6 +76,8 @@ Heap::CallContext *ExecutionContext::newBlockContext(CppStackFrame *frame, int b
c->locals.size = nLocals;
c->locals.alloc = nLocals;
+ c->setupLocalTemporalDeadZone(function->compilationUnit->unitData()->blockAt(blockIndex));
+
return c;
}
@@ -115,6 +117,8 @@ Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame)
// memory allocated from the JS heap is 0 initialized, so check if empty is 0
Q_ASSERT(Primitive::undefinedValue().asReturnedValue() == 0);
+ c->setupLocalTemporalDeadZone(compiledFunction);
+
Value *args = c->locals.values + nLocals;
::memcpy(args, frame->originalArguments, frame->originalArgumentsCount * sizeof(Value));
c->nArgs = frame->originalArgumentsCount;
diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h
index fbb4168e9b..5de11d80cb 100644
--- a/src/qml/jsruntime/qv4context_p.h
+++ b/src/qml/jsruntime/qv4context_p.h
@@ -118,6 +118,12 @@ DECLARE_HEAP_OBJECT(CallContext, ExecutionContext) {
return locals.data() + locals.size;
}
void setArg(uint index, Value v);
+
+ template <typename BlockOrFunction>
+ void setupLocalTemporalDeadZone(BlockOrFunction *bof) {
+ for (uint i = bof->nLocals - bof->sizeOfLocalTemporalDeadZone; i < bof->nLocals; ++i)
+ locals.values[i] = Primitive::emptyValue();
+ }
};
Q_STATIC_ASSERT(std::is_trivial< CallContext >::value);
Q_STATIC_ASSERT(std::is_standard_layout<CallContextData>::value);
diff --git a/src/qml/jsruntime/qv4module.cpp b/src/qml/jsruntime/qv4module.cpp
index 583d0c3756..c892749a11 100644
--- a/src/qml/jsruntime/qv4module.cpp
+++ b/src/qml/jsruntime/qv4module.cpp
@@ -70,6 +70,9 @@ void Heap::Module::init(ExecutionEngine *engine, CompiledData::CompilationUnit *
scope->locals.alloc = locals;
scope->nArgs = 0;
+ // Prepare the temporal dead zone
+ scope->setupLocalTemporalDeadZone(moduleFunction->compiledFunction);
+
Scope valueScope(engine);
// It's possible for example to re-export an import, for example:
@@ -106,7 +109,7 @@ ReturnedValue Module::virtualGet(const Managed *m, PropertyKey id, const Value *
const Value *v = module->d()->unit->resolveExport(expectedName);
if (hasProperty)
*hasProperty = v != nullptr;
- if (!v)
+ if (!v || v->isEmpty())
return Encode::undefined();
return v->asReturnedValue();
}
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index 9180108a0c..54898ca6a1 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -1470,6 +1470,12 @@ ReturnedValue Runtime::method_popScriptContext(ExecutionEngine *engine)
return root;
}
+void Runtime::method_throwReferenceError(ExecutionEngine *engine, int nameIndex)
+{
+ Scope scope(engine);
+ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ engine->throwReferenceError(name);
+}
void Runtime::method_declareVar(ExecutionEngine *engine, bool deletable, int nameIndex)
{
diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h
index 3f65e6c6d6..182cc393ea 100644
--- a/src/qml/jsruntime/qv4runtimeapi_p.h
+++ b/src/qml/jsruntime/qv4runtimeapi_p.h
@@ -132,6 +132,7 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> {
F(ReturnedValue, createScriptContext, (ExecutionEngine *engine, int index)) \
F(ReturnedValue, cloneBlockContext, (ExecutionContext *previous)) \
F(ReturnedValue, popScriptContext, (ExecutionEngine *engine)) \
+ F(void, throwReferenceError, (ExecutionEngine *engine, int nameIndex)) \
\
/* closures */ \
F(ReturnedValue, closure, (ExecutionEngine *engine, int functionId)) \
diff --git a/src/qml/jsruntime/qv4stackframe_p.h b/src/qml/jsruntime/qv4stackframe_p.h
index 878f9283e7..ca39a8f7bb 100644
--- a/src/qml/jsruntime/qv4stackframe_p.h
+++ b/src/qml/jsruntime/qv4stackframe_p.h
@@ -181,6 +181,15 @@ struct Q_QML_EXPORT CppStackFrame {
const Value *end = jsFrame->args + nRegisters;
for (Value *v = jsFrame->args + argc; v < end; ++v)
*v = Encode::undefined();
+
+ if (v4Function && v4Function->compiledFunction) {
+ const int firstDeadZoneRegister = v4Function->compiledFunction->firstTemporalDeadZoneRegister;
+ const int registerDeadZoneSize = v4Function->compiledFunction->sizeOfRegisterTemporalDeadZone;
+
+ const Value * tdzEnd = stackSpace + firstDeadZoneRegister + registerDeadZoneSize;
+ for (Value *v = stackSpace + firstDeadZoneRegister; v < tdzEnd; ++v)
+ *v = Primitive::emptyValue().asReturnedValue();
+ }
}
#endif
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index aed3fce6b1..3570453525 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -784,6 +784,15 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
goto handleUnwind;
MOTH_END_INSTR(UnwindToLabel)
+ MOTH_BEGIN_INSTR(DeadTemporalZoneCheck)
+ if (ACC.isEmpty()) {
+ STORE_IP();
+ STORE_ACC();
+ Runtime::method_throwReferenceError(engine, name);
+ goto handleUnwind;
+ }
+ MOTH_END_INSTR(DeadTemporalZoneCheck)
+
MOTH_BEGIN_INSTR(ThrowException)
STORE_IP();
STORE_ACC();
@@ -1322,6 +1331,12 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
return acc;
MOTH_END_INSTR(Ret)
+ MOTH_BEGIN_INSTR(InitializeBlockDeadTemporalZone)
+ acc = Encode(Primitive::emptyValue());
+ for (int i = firstReg, end = firstReg + count; i < end; ++i)
+ STACK_VALUE(i) = acc;
+ MOTH_END_INSTR(InitializeBlockDeadTemporalZone)
+
MOTH_BEGIN_INSTR(Debug)
#if QT_CONFIG(qml_debug)
STORE_IP();