aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2018-05-04 15:16:08 +0200
committerLars Knoll <lars.knoll@qt.io>2018-05-11 07:17:05 +0000
commit4cf7e80c5740912804383e4d866ba12b2520d0e6 (patch)
tree143d960492aa166a7f3d7111b64151c42234a81f /src/qml
parent2fc50421c86134b5b42a4ba68aa7f6b87cfd7d74 (diff)
Ensure we have a lexical scope for global code
This requires a bit more work than simply pushing a new BlockContext for the lexically declared variables, as eval() and the Function constructor operate on the global scope (including the lexically declared names). To fix this introduce Push/PopScriptContext instructions, that create a BlockContext for the lexically declared vars and pushes that one as a global script context that eval and friends use. Change-Id: I0fd0b0f682f82e250545e874fe93978449fe5e46 Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src/qml')
-rw-r--r--src/qml/compiler/qv4codegen.cpp13
-rw-r--r--src/qml/compiler/qv4compilercontext.cpp76
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions.cpp30
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp7
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h4
-rw-r--r--src/qml/jit/qv4jit.cpp34
-rw-r--r--src/qml/jit/qv4jit_p.h2
-rw-r--r--src/qml/jsruntime/qv4context.cpp10
-rw-r--r--src/qml/jsruntime/qv4engine.cpp1
-rw-r--r--src/qml/jsruntime/qv4engine_p.h3
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp2
-rw-r--r--src/qml/jsruntime/qv4generatorobject.cpp2
-rw-r--r--src/qml/jsruntime/qv4globalobject.cpp2
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp16
-rw-r--r--src/qml/jsruntime/qv4runtimeapi_p.h2
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp14
16 files changed, 161 insertions, 57 deletions
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index 837649a7dc..f55e6205dc 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -2343,6 +2343,11 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
ControlFlow *savedControlFlow = controlFlow;
controlFlow = nullptr;
+ if (_context->contextType == ContextType::Global) {
+ _module->blocks.append(_context);
+ _context->blockIndex = _module->blocks.count() - 1;
+ }
+
_context->hasDirectEval |= (_context->contextType == ContextType::Eval || _context->contextType == ContextType::Global || _module->debugMode); // Conditional breakpoints are like eval in the function
// When a user writes the following QML signal binding:
@@ -2372,6 +2377,12 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
returnAddress = bytecodeGenerator->newRegister();
qSwap(_returnAddress, returnAddress);
+ // register the lexical scope for global code
+ if (!_context->parent && _context->requiresExecutionContext) {
+ _module->blocks.append(_context);
+ _context->blockIndex = _module->blocks.count() - 1;
+ }
+
RegisterScope registerScope(this);
_context->emitBlockHeader(this);
@@ -2414,6 +2425,8 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
statementList(body);
+ _context->emitBlockFooter(this, -1);
+
if (hasError || !endsWithReturn(_module, body)) {
bytecodeGenerator->setLocation(ast->lastSourceLocation());
if (requiresReturnValue) {
diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp
index 7dc8b68d78..67afea28ab 100644
--- a/src/qml/compiler/qv4compilercontext.cpp
+++ b/src/qml/compiler/qv4compilercontext.cpp
@@ -113,11 +113,14 @@ Context::ResolvedName Context::resolveName(const QString &name)
ResolvedName result;
- while (c->parent) {
+ while (c) {
if (c->isWithBlock)
return result;
Context::Member m = c->findMember(name);
+ if (!c->parent && m.index < 0)
+ break;
+
if (m.type != Context::UndefinedMember) {
result.type = m.canEscape ? ResolvedName::Local : ResolvedName::Stack;
result.scope = scope;
@@ -164,9 +167,13 @@ int Context::emitBlockHeader(Codegen *codegen)
setupFunctionIndices(bytecodeGenerator);
int contextReg = -1;
- if (requiresExecutionContext ||
+ if (requiresExecutionContext && contextType == ContextType::Global) {
+ Instruction::PushScriptContext scriptContext;
+ scriptContext.index = blockIndex;
+ bytecodeGenerator->addInstruction(scriptContext);
+ } else if (requiresExecutionContext ||
contextType == ContextType::Binding) { // we don't really need this for bindings, but we do for signal handlers, and we don't know if the code is a signal handler or not.
- if (contextType == ContextType::Block) {
+ if (contextType == ContextType::Block || (contextType == ContextType::Eval && !isStrict)) {
if (isCatchBlock) {
Instruction::PushCatchContext catchContext;
catchContext.index = blockIndex;
@@ -190,10 +197,21 @@ int Context::emitBlockHeader(Codegen *codegen)
bytecodeGenerator->addInstruction(convert);
}
- switch (contextType) {
- case ContextType::Block:
- case ContextType::Function:
- case ContextType::Binding: {
+ if (contextType == ContextType::Global || (contextType == ContextType::Eval && !isStrict)) {
+ // variables in global code are properties of the global context object, not locals as with other functions.
+ for (Context::MemberMap::const_iterator it = members.constBegin(), cend = members.constEnd(); it != cend; ++it) {
+ if (it->isLexicallyScoped())
+ continue;
+ const QString &local = it.key();
+
+ Instruction::DeclareVar declareVar;
+ declareVar.isDeletable = (contextType == ContextType::Eval);
+ declareVar.varName = codegen->registerString(local);
+ bytecodeGenerator->addInstruction(declareVar);
+ }
+ }
+
+ if (contextType == ContextType::Function || contextType == ContextType::Binding) {
for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) {
if (it->canEscape && it->type == Context::ThisFunctionName) {
// move the function from the stack to the call context
@@ -205,20 +223,6 @@ int Context::emitBlockHeader(Codegen *codegen)
bytecodeGenerator->addInstruction(store);
}
}
- break;
- }
- case ContextType::Global:
- case ContextType::Eval: {
- // variables in global code are properties of the global context object, not locals as with other functions.
- for (Context::MemberMap::const_iterator it = members.constBegin(), cend = members.constEnd(); it != cend; ++it) {
- const QString &local = it.key();
-
- Instruction::DeclareVar declareVar;
- declareVar.isDeletable = false;
- declareVar.varName = codegen->registerString(local);
- bytecodeGenerator->addInstruction(declareVar);
- }
- }
}
if (usesArgumentsObject == Context::ArgumentsObjectUsed) {
@@ -237,20 +241,11 @@ int Context::emitBlockHeader(Codegen *codegen)
if (member.function) {
const int function = codegen->defineFunction(member.function->name.toString(), member.function, member.function->formals, member.function->body);
codegen->loadClosure(function);
- if (!parent) {
- Codegen::Reference::fromName(codegen, member.function->name.toString()).storeConsumeAccumulator();
- } else {
- int idx = member.index;
- Q_ASSERT(idx >= 0);
- Codegen::Reference local = member.canEscape ? Codegen::Reference::fromScopedLocal(codegen, idx, 0)
- : Codegen::Reference::fromStackSlot(codegen, idx, true);
- local.storeConsumeAccumulator();
- }
+ Codegen::Reference r = codegen->referenceForName(member.function->name.toString(), true);
+ r.storeConsumeAccumulator();
}
}
-
-
return contextReg;
}
@@ -259,7 +254,12 @@ void Context::emitBlockFooter(Codegen *codegen, int oldContextReg)
using Instruction = Moth::Instruction;
Moth::BytecodeGenerator *bytecodeGenerator = codegen->generator();
- if (oldContextReg != -1) {
+ if (requiresExecutionContext && contextType == ContextType::Global) {
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty structs.
+ bytecodeGenerator->addInstruction(Instruction::PopScriptContext());
+QT_WARNING_POP
+ } else if (oldContextReg != -1) {
Instruction::PopContext popContext;
popContext.reg = oldContextReg;
bytecodeGenerator->addInstruction(popContext);
@@ -292,6 +292,16 @@ void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator)
}
case ContextType::Global:
case ContextType::Eval:
+ 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();
+ }
+ }
break;
}
nRegisters = bytecodeGenerator->registerCount() - registerOffset;
diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp
index 47329e978b..f7100a1d1a 100644
--- a/src/qml/compiler/qv4compilerscanfunctions.cpp
+++ b/src/qml/compiler/qv4compilerscanfunctions.cpp
@@ -522,20 +522,19 @@ void ScanFunctions::calcEscapingVariables()
if (current->isWithBlock || current->contextType != ContextType::Block)
break;
}
+ Q_ASSERT(c != inner);
while (c) {
Context::MemberMap::const_iterator it = c->members.find(var);
if (it != c->members.end()) {
- if (c != inner) {
+ if (c->parent || it->isLexicallyScoped()) {
it->canEscape = true;
c->requiresExecutionContext = true;
}
break;
}
if (c->findArgument(var) != -1) {
- if (c != inner) {
- c->argumentsCanEscape = true;
- c->requiresExecutionContext = true;
- }
+ c->argumentsCanEscape = true;
+ c->requiresExecutionContext = true;
break;
}
c = c->parent;
@@ -551,18 +550,21 @@ void ScanFunctions::calcEscapingVariables()
bool allVarsEscape = c->hasDirectEval;
if (allVarsEscape && c->contextType == ContextType::Block && c->members.isEmpty())
allVarsEscape = false;
- if (m->debugMode)
+ if (!c->parent || m->debugMode)
allVarsEscape = true;
if (allVarsEscape) {
- c->requiresExecutionContext = true;
- c->argumentsCanEscape = true;
+ if (c->parent) {
+ c->requiresExecutionContext = true;
+ c->argumentsCanEscape = true;
+ } else {
+ for (const auto &m : qAsConst(c->members)) {
+ if (m.isLexicallyScoped()) {
+ c->requiresExecutionContext = true;
+ break;
+ }
+ }
+ }
}
- // ### for now until we have lexically scoped vars that'll require it
- if (c->contextType == ContextType::Global)
- c->requiresExecutionContext = false;
- // ### Shouldn't be required, we could probably rather change the ContextType to FunctionCode for strict eval
- if (c->contextType == ContextType::Eval && c->isStrict)
- c->requiresExecutionContext = true;
if (c->contextType == ContextType::Block && c->isCatchBlock) {
c->requiresExecutionContext = true;
auto m = c->members.find(c->catchedVariable);
diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp
index 6ef4e79344..d4abc59a8b 100644
--- a/src/qml/compiler/qv4instr_moth.cpp
+++ b/src/qml/compiler/qv4instr_moth.cpp
@@ -415,6 +415,13 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << dumpRegister(reg, nFormals) << ", " << index;
MOTH_END_INSTR(PushBlockContext)
+ MOTH_BEGIN_INSTR(PushScriptContext)
+ d << index;
+ MOTH_END_INSTR(PushScriptContext)
+
+ MOTH_BEGIN_INSTR(PopScriptContext)
+ MOTH_END_INSTR(PopScriptContext)
+
MOTH_BEGIN_INSTR(PopContext)
d << dumpRegister(reg, nFormals);
MOTH_END_INSTR(PopContext)
diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h
index 546e8fd2de..d12d1a53cc 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -117,6 +117,8 @@ QT_BEGIN_NAMESPACE
#define INSTR_PushCatchContext(op) INSTRUCTION(op, PushCatchContext, 3, reg, index, name)
#define INSTR_PushWithContext(op) INSTRUCTION(op, PushWithContext, 1, reg)
#define INSTR_PushBlockContext(op) INSTRUCTION(op, PushBlockContext, 2, reg, index)
+#define INSTR_PushScriptContext(op) INSTRUCTION(op, PushScriptContext, 1, index)
+#define INSTR_PopScriptContext(op) INSTRUCTION(op, PopScriptContext, 0)
#define INSTR_PopContext(op) INSTRUCTION(op, PopContext, 1, reg)
#define INSTR_GetIterator(op) INSTRUCTION(op, GetIterator, 1, iterator)
#define INSTR_DeleteMember(op) INSTRUCTION(op, DeleteMember, 2, member, base)
@@ -240,6 +242,8 @@ QT_BEGIN_NAMESPACE
F(PushCatchContext) \
F(PushWithContext) \
F(PushBlockContext) \
+ F(PushScriptContext) \
+ F(PopScriptContext) \
F(PopContext) \
F(GetIterator) \
F(DeleteMember) \
diff --git a/src/qml/jit/qv4jit.cpp b/src/qml/jit/qv4jit.cpp
index 16b6b5fb94..f659ddfb00 100644
--- a/src/qml/jit/qv4jit.cpp
+++ b/src/qml/jit/qv4jit.cpp
@@ -659,6 +659,34 @@ void BaselineJIT::generate_PushBlockContext(int reg, int index)
JIT_GENERATE_RUNTIME_CALL(pushBlockContextHelper, Assembler::IgnoreResult);
}
+static void pushScriptContextHelper(QV4::Value *stack, ExecutionEngine *engine, int index)
+{
+ stack[CallData::Context] = Runtime::method_createScriptContext(engine, index);
+}
+
+void BaselineJIT::generate_PushScriptContext(int index)
+{
+ as->saveAccumulatorInFrame();
+ as->prepareCallWithArgCount(3);
+ as->passInt32AsArg(index, 2);
+ as->passEngineAsArg(1);
+ as->passRegAsArg(0, 0);
+ JIT_GENERATE_RUNTIME_CALL(pushScriptContextHelper, Assembler::IgnoreResult);
+}
+
+static void popScriptContextHelper(QV4::Value *stack, ExecutionEngine *engine)
+{
+ stack[CallData::Context] = Runtime::method_popScriptContext(engine);
+}
+
+void BaselineJIT::generate_PopScriptContext()
+{
+ as->saveAccumulatorInFrame();
+ as->prepareCallWithArgCount(2);
+ as->passEngineAsArg(1);
+ as->passRegAsArg(0, 0);
+ JIT_GENERATE_RUNTIME_CALL(popScriptContextHelper, Assembler::IgnoreResult);
+}
void BaselineJIT::generate_PopContext(int reg) { as->popContext(reg); }
@@ -1208,6 +1236,12 @@ void BaselineJIT::collectLabelsInBytecode()
MOTH_BEGIN_INSTR(PushBlockContext)
MOTH_END_INSTR(PushBlockContext)
+ MOTH_BEGIN_INSTR(PushScriptContext)
+ MOTH_END_INSTR(PushScriptContext)
+
+ MOTH_BEGIN_INSTR(PopScriptContext)
+ MOTH_END_INSTR(PopScriptContext)
+
MOTH_BEGIN_INSTR(PopContext)
MOTH_END_INSTR(PopContext)
diff --git a/src/qml/jit/qv4jit_p.h b/src/qml/jit/qv4jit_p.h
index 157fbffeb1..97dd2ef2a5 100644
--- a/src/qml/jit/qv4jit_p.h
+++ b/src/qml/jit/qv4jit_p.h
@@ -181,6 +181,8 @@ public:
void generate_PushCatchContext(int reg, int index, int name) override;
void generate_PushWithContext(int reg) override;
void generate_PushBlockContext(int reg, int index) override;
+ void generate_PushScriptContext(int index) override;
+ void generate_PopScriptContext() override;
void generate_PopContext(int reg) override;
void generate_GetIterator(int iterator) override;
void generate_DeleteMember(int member, int base) override;
diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp
index c81af767fa..8a6f1044b9 100644
--- a/src/qml/jsruntime/qv4context.cpp
+++ b/src/qml/jsruntime/qv4context.cpp
@@ -57,17 +57,17 @@ DEFINE_MANAGED_VTABLE(CallContext);
Heap::CallContext *ExecutionContext::newBlockContext(CppStackFrame *frame, int blockIndex)
{
Function *function = frame->v4Function;
- Heap::ExecutionContext *outer = static_cast<Heap::ExecutionContext *>(frame->context()->m());
Heap::InternalClass *ic = function->compilationUnit->runtimeBlocks.at(blockIndex);
int nLocals = ic->size;
size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * nLocals;
- ExecutionEngine *v4 = outer->internalClass->engine;
+ ExecutionEngine *v4 = function->internalClass->engine;
Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory, ic);
c->init();
c->type = Heap::ExecutionContext::Type_BlockContext;
+ Heap::ExecutionContext *outer = static_cast<Heap::ExecutionContext *>(frame->context()->m());
c->outer.set(v4, outer);
c->function.set(v4, static_cast<Heap::FunctionObject *>(frame->jsFrame->function.m()));
@@ -137,7 +137,6 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable)
ScopedContext ctx(scope, this);
while (ctx) {
switch (ctx->d()->type) {
- case Heap::ExecutionContext::Type_BlockContext:
case Heap::ExecutionContext::Type_CallContext:
if (!activation) {
Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d());
@@ -158,6 +157,8 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable)
activation = ctx->d()->activation;
break;
}
+ case Heap::ExecutionContext::Type_BlockContext:
+ // never create activation records on block contexts
default:
break;
}
@@ -169,7 +170,8 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable)
ScopedProperty desc(scope);
PropertyAttributes attrs(Attr_Data);
attrs.setConfigurable(deletable);
- activation->__defineOwnProperty__(scope.engine, name, desc, attrs);
+ if (!activation->__defineOwnProperty__(scope.engine, name, desc, attrs))
+ scope.engine->throwTypeError();
}
bool ExecutionContext::deleteProperty(String *name)
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 2a37f3ee47..b703f6e4f3 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -585,6 +585,7 @@ void ExecutionEngine::initRootContext()
r->d_unchecked()->init(Heap::ExecutionContext::Type_GlobalContext);
r->d()->activation.set(this, globalObject->d());
jsObjects[RootContext] = r;
+ jsObjects[ScriptContext] = r;
jsObjects[IntegerNull] = Encode((int)0);
}
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index e1b1ba38ca..e8218d0d1c 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -161,6 +161,7 @@ public:
enum JSObjects {
RootContext,
+ ScriptContext,
IntegerNull, // Has to come after the RootContext to make the context stack safe
ObjectProto,
SymbolProto,
@@ -223,6 +224,8 @@ public:
enum { NTypedArrayTypes = 9 }; // == TypedArray::NValues, avoid header dependency
ExecutionContext *rootContext() const { return reinterpret_cast<ExecutionContext *>(jsObjects + RootContext); }
+ ExecutionContext *scriptContext() const { return reinterpret_cast<ExecutionContext *>(jsObjects + ScriptContext); }
+ void setScriptContext(ReturnedValue c) { jsObjects[ScriptContext] = c; }
FunctionObject *objectCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Object_Ctor); }
FunctionObject *stringCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + String_Ctor); }
FunctionObject *symbolCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Symbol_Ctor); }
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index c556bdb008..8a70715728 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -270,7 +270,7 @@ ReturnedValue FunctionCtor::callAsConstructor(const FunctionObject *f, const Val
return Encode::undefined();
Function *vmf = compilationUnit->linkToEngine(engine);
- ExecutionContext *global = engine->rootContext();
+ ExecutionContext *global = engine->scriptContext();
return Encode(FunctionObject::createScriptFunction(global, vmf));
}
diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp
index 4339a95f45..c91e182b54 100644
--- a/src/qml/jsruntime/qv4generatorobject.cpp
+++ b/src/qml/jsruntime/qv4generatorobject.cpp
@@ -63,7 +63,7 @@ ReturnedValue GeneratorFunctionCtor::callAsConstructor(const FunctionObject *f,
return Encode::undefined();
Function *vmf = compilationUnit->linkToEngine(engine);
- ExecutionContext *global = engine->rootContext();
+ ExecutionContext *global = engine->scriptContext();
return Encode(GeneratorFunction::create(global, vmf));
}
diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp
index b68f3db3cf..47a6734eda 100644
--- a/src/qml/jsruntime/qv4globalobject.cpp
+++ b/src/qml/jsruntime/qv4globalobject.cpp
@@ -351,7 +351,7 @@ ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc,
if (!directCall) {
// the context for eval should be the global scope
- ctx = v4->rootContext();
+ ctx = v4->scriptContext();
}
String *scode = argv[0].stringValue();
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index 05b0ddf753..8bf6d6b73f 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -1230,6 +1230,22 @@ ReturnedValue Runtime::method_createBlockContext(ExecutionContext *parent, int i
return parent->newBlockContext(e->currentStackFrame, index)->asReturnedValue();
}
+ReturnedValue Runtime::method_createScriptContext(ExecutionEngine *engine, int index)
+{
+ Q_ASSERT(engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_GlobalContext ||
+ engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_QmlContext);
+ ReturnedValue c = ExecutionContext::newBlockContext(engine->currentStackFrame, index)->asReturnedValue();
+ engine->setScriptContext(c);
+ return c;
+}
+
+ReturnedValue Runtime::method_popScriptContext(ExecutionEngine *engine)
+{
+ ReturnedValue root = engine->rootContext()->asReturnedValue();
+ engine->setScriptContext(root);
+ return root;
+}
+
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 4d4112bf0c..f707de00f8 100644
--- a/src/qml/jsruntime/qv4runtimeapi_p.h
+++ b/src/qml/jsruntime/qv4runtimeapi_p.h
@@ -127,6 +127,8 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> {
F(ReturnedValue, createWithContext, (ExecutionContext *parent, const Value &o)) \
F(ReturnedValue, createCatchContext, (ExecutionContext *parent, int blockIndex, int exceptionVarNameIndex)) \
F(ReturnedValue, createBlockContext, (ExecutionContext *parent, int index)) \
+ F(ReturnedValue, createScriptContext, (ExecutionEngine *engine, int index)) \
+ F(ReturnedValue, popScriptContext, (ExecutionEngine *engine)) \
\
/* closures */ \
F(ReturnedValue, closure, (ExecutionEngine *engine, int functionId)) \
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index 7f27619d08..3fadb6e670 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -929,11 +929,19 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const uchar *code)
MOTH_END_INSTR(PushWithContext)
MOTH_BEGIN_INSTR(PushBlockContext)
- STACK_VALUE(reg) = STACK_VALUE(CallData::Context);
- ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
- STACK_VALUE(CallData::Context) = Runtime::method_createBlockContext(c, index);
+ STACK_VALUE(reg) = STACK_VALUE(CallData::Context);
+ ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
+ STACK_VALUE(CallData::Context) = Runtime::method_createBlockContext(c, index);
MOTH_END_INSTR(PushBlockContext)
+ MOTH_BEGIN_INSTR(PushScriptContext)
+ STACK_VALUE(CallData::Context) = Runtime::method_createScriptContext(engine, index);
+ MOTH_END_INSTR(PushScriptContext)
+
+ MOTH_BEGIN_INSTR(PopScriptContext)
+ STACK_VALUE(CallData::Context) = Runtime::method_popScriptContext(engine);
+ MOTH_END_INSTR(PopScriptContext)
+
MOTH_BEGIN_INSTR(PopContext)
STACK_VALUE(CallData::Context) = STACK_VALUE(reg);
MOTH_END_INSTR(PopContext)