diff options
Diffstat (limited to 'src/qml')
-rw-r--r-- | src/qml/compiler/qv4bytecodehandler.cpp | 6 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 27 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen_p.h | 3 | ||||
-rw-r--r-- | src/qml/compiler/qv4compileddata_p.h | 11 | ||||
-rw-r--r-- | src/qml/compiler/qv4compiler.cpp | 5 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilercontext.cpp | 56 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilercontext_p.h | 4 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth.cpp | 8 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth_p.h | 4 | ||||
-rw-r--r-- | src/qml/jit/qv4baselineassembler.cpp | 24 | ||||
-rw-r--r-- | src/qml/jit/qv4baselineassembler_p.h | 1 | ||||
-rw-r--r-- | src/qml/jit/qv4baselinejit.cpp | 11 | ||||
-rw-r--r-- | src/qml/jit/qv4baselinejit_p.h | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4context.cpp | 4 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4context_p.h | 6 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4module.cpp | 5 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtime.cpp | 6 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtimeapi_p.h | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4stackframe_p.h | 9 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4vme_moth.cpp | 15 |
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, ®istersInTDZ](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(); |