diff options
Diffstat (limited to 'src/qml/compiler')
-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 |
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, ®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) |