aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@qt.io>2018-08-21 16:51:17 +0200
committerSimon Hausmann <simon.hausmann@qt.io>2018-08-28 17:50:41 +0000
commitbead103138c0d9dff3c9f927c9c4e2f44ee7db4c (patch)
tree32b23e94d07b5169758b426b1ad614e240dae9cd /src/qml/jsruntime
parentec6996bcbed583177952f81f5bfaf1d67eb573ad (diff)
Implement the dead temporal zone
With const and let it is possible to access the declared member before initialization. This is expected to throw a type reference error at run-time. We initialize such variables with the empty value when entering their scope and check upon access for that. For locals we place the lexically scoped variables at the end. For register allocated lexical variables we group them into one batch and remember the index/size. Change-Id: Icb493ee0de0525bb682e1bc58981a4dfd33f750e Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/qml/jsruntime')
-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
7 files changed, 45 insertions, 1 deletions
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();