aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2018-03-26 22:59:28 +0200
committerLars Knoll <lars.knoll@qt.io>2018-05-02 14:17:46 +0000
commit20d30b6b3a253eebedc927dbb91685bbec52cfee (patch)
tree0e91a6c519829bd279258f3fe807519e6c8a1e3d /src
parentd499a995292629d3522f5e77b7c958bacdf5d0ae (diff)
Add support for proper lexical scoping
This is still to some extend work in progress as lexically scoped for loops won't yet do the right thing. let and const variables are still accessible before they are declared, and the global scope doesn't yet have a proper context for lexically declared variables. Change-Id: Ie39f74a8fccdaead437fbf07f9fc228a444c26ed Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp10
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp2
-rw-r--r--src/qml/compiler/qv4bytecodegenerator_p.h6
-rw-r--r--src/qml/compiler/qv4codegen.cpp89
-rw-r--r--src/qml/compiler/qv4codegen_p.h8
-rw-r--r--src/qml/compiler/qv4compileddata.cpp18
-rw-r--r--src/qml/compiler/qv4compileddata_p.h39
-rw-r--r--src/qml/compiler/qv4compiler.cpp52
-rw-r--r--src/qml/compiler/qv4compiler_p.h2
-rw-r--r--src/qml/compiler/qv4compilercontext.cpp95
-rw-r--r--src/qml/compiler/qv4compilercontext_p.h21
-rw-r--r--src/qml/compiler/qv4compilercontrolflow_p.h65
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions.cpp30
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions_p.h1
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp4
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h2
-rw-r--r--src/qml/jit/qv4jit.cpp21
-rw-r--r--src/qml/jit/qv4jit_p.h1
-rw-r--r--src/qml/jsruntime/qv4context.cpp28
-rw-r--r--src/qml/jsruntime/qv4context_p.h4
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp7
-rw-r--r--src/qml/jsruntime/qv4runtimeapi_p.h1
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp6
23 files changed, 398 insertions, 114 deletions
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
index c33d0026d5..99d106d5f6 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
@@ -81,8 +81,9 @@ QV4::Heap::CallContext *QV4DataCollector::findScope(QV4::Heap::ExecutionContext
for (; scope > 0 && ctx; --scope)
ctx = ctx->outer;
- return (ctx && ctx->type == QV4::Heap::ExecutionContext::Type_CallContext) ?
- static_cast<QV4::Heap::CallContext *>(ctx) : nullptr;
+ if (!ctx || (ctx->type != QV4::Heap::ExecutionContext::Type_CallContext && ctx->type != QV4::Heap::ExecutionContext::Type_BlockContext))
+ return nullptr;
+ return static_cast<QV4::Heap::CallContext *>(ctx);
}
QVector<QV4::Heap::ExecutionContext::ContextType> QV4DataCollector::getScopeTypes(int frame)
@@ -108,8 +109,9 @@ int QV4DataCollector::encodeScopeType(QV4::Heap::ExecutionContext::ContextType s
return 2;
case QV4::Heap::ExecutionContext::Type_CallContext:
return 1;
- case QV4::Heap::ExecutionContext::Type_QmlContext:
- default:
+ case QV4::Heap::ExecutionContext::Type_BlockContext:
+ return 3;
+ default: // QV4::Heap::ExecutionContext::Type_QmlContext:
return -1;
}
}
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index badf89fe44..c4b5859b58 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -2199,7 +2199,7 @@ QV4::Compiler::Codegen::Reference JSCodeGen::fallbackNameLookup(const QString &n
// Look for IDs first.
for (const IdMapping &mapping : qAsConst(_idObjects)) {
if (name == mapping.name) {
- if (_context->type == QV4::Compiler::ContextType::Binding)
+ if (_context->contextType == QV4::Compiler::ContextType::Binding)
_context->idObjectDependencies.insert(mapping.idIndex);
Instruction::LoadIdObject load;
diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h
index 9bd9d82e0d..286ceed57d 100644
--- a/src/qml/compiler/qv4bytecodegenerator_p.h
+++ b/src/qml/compiler/qv4bytecodegenerator_p.h
@@ -133,14 +133,16 @@ public:
};
struct ExceptionHandler : public Label {
+ ExceptionHandler() = default;
ExceptionHandler(BytecodeGenerator *generator)
: Label(generator, LinkLater)
{
}
~ExceptionHandler()
{
- Q_ASSERT(generator->currentExceptionHandler != this);
+ Q_ASSERT(!generator || generator->currentExceptionHandler != this);
}
+ bool isValid() const { return generator != nullptr; }
};
Label label() {
@@ -281,7 +283,7 @@ private:
QVector<I> instructions;
QVector<int> labels;
- ExceptionHandler *currentExceptionHandler;
+ ExceptionHandler *currentExceptionHandler = nullptr;
int regCount = 0;
public:
int currentReg = 0;
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index d71cd0cdb3..463a1e8d27 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -137,7 +137,6 @@ void Codegen::enterContext(Node *node)
int Codegen::leaveContext()
{
Q_ASSERT(_context);
- Q_ASSERT(!_context->controlFlow);
int functionIndex = _context->functionIndex;
_context = _context->parent;
return functionIndex;
@@ -2240,7 +2239,7 @@ bool Codegen::visit(FunctionDeclaration * ast)
RegisterScope scope(this);
- if (_context->type == ContextType::Binding)
+ if (_functionContext->contextType == ContextType::Binding)
referenceForName(ast->name.toString(), true).loadInAccumulator();
_expr.accept(nx);
return false;
@@ -2252,7 +2251,7 @@ bool Codegen::visit(YieldExpression *ast)
return false;
}
-static bool endsWithReturn(Node *node)
+static bool endsWithReturn(Module *module, Node *node)
{
if (!node)
return false;
@@ -2261,16 +2260,22 @@ static bool endsWithReturn(Node *node)
if (AST::cast<ThrowStatement *>(node))
return true;
if (Program *p = AST::cast<Program *>(node))
- return endsWithReturn(p->statements);
+ return endsWithReturn(module, p->statements);
if (StatementList *sl = AST::cast<StatementList *>(node)) {
while (sl->next)
sl = sl->next;
- return endsWithReturn(sl->statement);
+ return endsWithReturn(module, sl->statement);
+ }
+ if (Block *b = AST::cast<Block *>(node)) {
+ Context *blockContext = module->contextMap.value(node);
+ if (blockContext->requiresExecutionContext)
+ // we need to emit a return statement here, because of the
+ // unwind handler
+ return false;
+ return endsWithReturn(module, b->statements);
}
- if (Block *b = AST::cast<Block *>(node))
- return endsWithReturn(b->statements);
if (IfStatement *is = AST::cast<IfStatement *>(node))
- return is->ko && endsWithReturn(is->ok) && endsWithReturn(is->ko);
+ return is->ko && endsWithReturn(module, is->ok) && endsWithReturn(module, is->ko);
return false;
}
@@ -2292,7 +2297,12 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
_module->functions.append(_context);
_context->functionIndex = _module->functions.count() - 1;
- _context->hasDirectEval |= (_context->type == ContextType::Eval || _context->type == ContextType::Global || _module->debugMode); // Conditional breakpoints are like eval in the function
+ Context *savedFunctionContext = _functionContext;
+ _functionContext = _context;
+ ControlFlow *savedControlFlow = controlFlow;
+ controlFlow = nullptr;
+
+ _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:
// onSignal: function() { doSomethingUsefull }
@@ -2319,24 +2329,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
qSwap(_returnAddress, returnAddress);
RegisterScope registerScope(this);
- _context->emitHeaderBytecode(this);
-
- for (const Context::Member &member : qAsConst(_context->members)) {
- if (member.function) {
- const int function = defineFunction(member.function->name.toString(), member.function, member.function->formals,
- member.function->body);
- loadClosure(function);
- if (! _context->parent) {
- Reference::fromName(this, member.function->name.toString()).storeConsumeAccumulator();
- } else {
- int idx = member.index;
- Q_ASSERT(idx >= 0);
- Reference local = member.canEscape ? Reference::fromScopedLocal(this, idx, 0)
- : Reference::fromStackSlot(this, idx, true);
- local.storeConsumeAccumulator();
- }
- }
- }
+ _context->emitBlockHeader(this);
int argc = 0;
while (formals) {
@@ -2367,7 +2360,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
statementList(body);
- if (hasError || !endsWithReturn(body)) {
+ if (hasError || !endsWithReturn(_module, body)) {
bytecodeGenerator->setLocation(ast->lastSourceLocation());
if (requiresReturnValue) {
if (_returnAddress >= 0) {
@@ -2381,6 +2374,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
bytecodeGenerator->addInstruction(Instruction::Ret());
}
+ Q_ASSERT(_context == _functionContext);
bytecodeGenerator->finalize(_context);
_context->registerCountInFunction = bytecodeGenerator->registerCount();
static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
@@ -2395,6 +2389,8 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
qSwap(_returnAddress, returnAddress);
qSwap(requiresReturnValue, _requiresReturnValue);
bytecodeGenerator = savedBytecodeGenerator;
+ controlFlow = savedControlFlow;
+ _functionContext = savedFunctionContext;
return leaveContext();
}
@@ -2406,7 +2402,14 @@ bool Codegen::visit(Block *ast)
RegisterScope scope(this);
- statementList(ast->statements);
+ enterContext(ast);
+ _module->blocks.append(_context);
+ _context->blockIndex = _module->blocks.count() - 1;
+ {
+ ControlFlowBlock controlFlow(this, _context);
+ statementList(ast->statements);
+ }
+ leaveContext();
return false;
}
@@ -2415,12 +2418,12 @@ bool Codegen::visit(BreakStatement *ast)
if (hasError)
return false;
- if (!_context->controlFlow) {
+ if (!controlFlow) {
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop"));
return false;
}
- ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Break, ast->label.toString());
+ ControlFlow::Handler h = controlFlow->getHandler(ControlFlow::Break, ast->label.toString());
if (h.type == ControlFlow::Invalid) {
if (ast->label.isEmpty())
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop"));
@@ -2429,7 +2432,7 @@ bool Codegen::visit(BreakStatement *ast)
return false;
}
- _context->controlFlow->jumpToHandler(h);
+ controlFlow->jumpToHandler(h);
return false;
}
@@ -2441,12 +2444,12 @@ bool Codegen::visit(ContinueStatement *ast)
RegisterScope scope(this);
- if (!_context->controlFlow) {
+ if (!controlFlow) {
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Continue outside of loop"));
return false;
}
- ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Continue, ast->label.toString());
+ ControlFlow::Handler h = controlFlow->getHandler(ControlFlow::Continue, ast->label.toString());
if (h.type == ControlFlow::Invalid) {
if (ast->label.isEmpty())
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Undefined label '%1'").arg(ast->label.toString()));
@@ -2455,7 +2458,7 @@ bool Codegen::visit(ContinueStatement *ast)
return false;
}
- _context->controlFlow->jumpToHandler(h);
+ controlFlow->jumpToHandler(h);
return false;
}
@@ -2617,7 +2620,7 @@ bool Codegen::visit(IfStatement *ast)
trueLabel.link();
statement(ast->ok);
if (ast->ko) {
- if (endsWithReturn(ast)) {
+ if (endsWithReturn(_module, ast)) {
falseLabel.link();
statement(ast->ko);
} else {
@@ -2641,7 +2644,7 @@ bool Codegen::visit(LabelledStatement *ast)
RegisterScope scope(this);
// check that no outer loop contains the label
- ControlFlow *l = _context->controlFlow;
+ ControlFlow *l = controlFlow;
while (l) {
if (l->label() == ast->label) {
QString error = QString(QStringLiteral("Label '%1' has already been declared")).arg(ast->label.toString());
@@ -2756,7 +2759,7 @@ bool Codegen::visit(ReturnStatement *ast)
if (hasError)
return true;
- if (_context->type != ContextType::Function && _context->type != ContextType::Binding) {
+ if (_functionContext->contextType != ContextType::Function && _functionContext->contextType != ContextType::Binding) {
throwSyntaxError(ast->returnToken, QStringLiteral("Return statement outside of function"));
return false;
}
@@ -2769,13 +2772,13 @@ bool Codegen::visit(ReturnStatement *ast)
expr = Reference::fromConst(this, Encode::undefined());
}
- if (_context->controlFlow && _context->controlFlow->returnRequiresUnwind()) {
+ if (controlFlow && controlFlow->returnRequiresUnwind()) {
if (_returnAddress >= 0)
(void) expr.storeOnStack(_returnAddress);
else
expr.loadInAccumulator();
- ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Return);
- _context->controlFlow->jumpToHandler(h);
+ ControlFlow::Handler h = controlFlow->getHandler(ControlFlow::Return);
+ controlFlow->jumpToHandler(h);
} else {
expr.loadInAccumulator();
bytecodeGenerator->addInstruction(Instruction::Ret());
@@ -2872,8 +2875,8 @@ bool Codegen::visit(ThrowStatement *ast)
if (hasError)
return false;
- if (_context->controlFlow) {
- _context->controlFlow->handleThrow(expr);
+ if (controlFlow) {
+ controlFlow->handleThrow(expr);
} else {
expr.loadInAccumulator();
Instruction::ThrowException instr;
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index 10926dd30b..3c64e0f00e 100644
--- a/src/qml/compiler/qv4codegen_p.h
+++ b/src/qml/compiler/qv4codegen_p.h
@@ -493,12 +493,12 @@ public:
int registerSetterLookup(int nameIndex) { return jsUnitGenerator->registerSetterLookup(nameIndex); }
int registerGlobalGetterLookup(int nameIndex) { return jsUnitGenerator->registerGlobalGetterLookup(nameIndex); }
-protected:
// Returns index in _module->functions
virtual int defineFunction(const QString &name, AST::Node *ast,
AST::FormalParameterList *formals,
AST::StatementList *body);
+protected:
void statement(AST::Statement *ast);
void statement(AST::ExpressionNode *ast);
void condition(AST::ExpressionNode *ast, const BytecodeGenerator::Label *iftrue,
@@ -517,8 +517,6 @@ protected:
void destructurePropertyList(const Reference &object, AST::PatternPropertyList *bindingList);
void destructureElementList(const Reference &array, AST::PatternElementList *bindingList);
- void loadClosure(int index);
-
// Hook provided to implement QML lookup semantics
virtual Reference fallbackNameLookup(const QString &name);
virtual void beginFunctionBodyHook() {}
@@ -653,6 +651,8 @@ public:
Context *currentContext() const { return _context; }
BytecodeGenerator *generator() const { return bytecodeGenerator; }
+ void loadClosure(int index);
+
protected:
friend class ScanFunctions;
friend struct ControlFlow;
@@ -663,12 +663,14 @@ protected:
Module *_module;
int _returnAddress;
Context *_context;
+ Context *_functionContext = nullptr;
AST::LabelledStatement *_labelledStatement;
QV4::Compiler::JSUnitGenerator *jsUnitGenerator;
BytecodeGenerator *bytecodeGenerator = nullptr;
bool _strictMode;
bool useFastLookups = true;
bool requiresReturnValue = false;
+ ControlFlow *controlFlow = nullptr;
bool _fileNameIsUrl;
bool hasError;
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp
index 59c37fb6c7..636b74e0fc 100644
--- a/src/qml/compiler/qv4compileddata.cpp
+++ b/src/qml/compiler/qv4compileddata.cpp
@@ -260,6 +260,9 @@ void CompilationUnit::markObjects(QV4::MarkStack *markStack)
for (QV4::Function *f : qAsConst(runtimeFunctions))
if (f && f->internalClass)
f->internalClass->mark(markStack);
+ for (QV4::Heap::InternalClass *c : qAsConst(runtimeBlocks))
+ if (c)
+ c->mark(markStack);
if (runtimeLookups) {
for (uint i = 0; i < data->lookupTableSize; ++i)
@@ -383,6 +386,21 @@ void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine)
const QV4::CompiledData::Function *compiledFunction = data->functionAt(i);
runtimeFunctions[i] = new QV4::Function(engine, this, compiledFunction, &Moth::VME::exec);
}
+
+ Scope scope(engine);
+ Scoped<InternalClass> ic(scope);
+
+ runtimeBlocks.resize(data->blockTableSize);
+ for (int i = 0 ;i < runtimeBlocks.size(); ++i) {
+ const QV4::CompiledData::Block *compiledBlock = data->blockAt(i);
+ ic = engine->internalClasses(EngineBase::Class_CallContext);
+
+ // first locals
+ const quint32_le *localsIndices = compiledBlock->localsTable();
+ for (quint32 i = 0; i < compiledBlock->nLocals; ++i)
+ ic = ic->addMember(engine->identifierTable->identifier(runtimeStrings[localsIndices[i]]), Attr_NotConfigurable);
+ runtimeBlocks[i] = ic->d();
+ }
}
#endif // V4_BOOTSTRAP
diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h
index 8f49fbd728..156fbc0149 100644
--- a/src/qml/compiler/qv4compileddata_p.h
+++ b/src/qml/compiler/qv4compileddata_p.h
@@ -202,6 +202,25 @@ struct CodeOffsetToLine {
quint32_le line;
};
+struct Block
+{
+ quint32_le nLocals;
+ quint32_le localsOffset;
+
+ const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); }
+
+ static int calculateSize(int nLocals) {
+ int trailingData = nLocals*sizeof (quint32);
+ size_t size = align(align(sizeof(Block)) + size_t(trailingData));
+ Q_ASSERT(size < INT_MAX);
+ return int(size);
+ }
+
+ static size_t align(size_t a) {
+ return (a + 7) & ~size_t(7);
+ }
+};
+
// 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.
struct Function
@@ -240,16 +259,14 @@ struct Function
quint32_le dependingScopePropertiesOffset; // Array of int pairs (property index and notify index)
// Qml Extensions End
-// quint32 formalsIndex[nFormals]
-// quint32 localsIndex[nLocals]
-// quint32 offsetForInnerFunctions[nInnerFunctions]
-// Function[nInnerFunctions]
-
// Keep all unaligned data at the end
quint8 flags;
quint8 padding1;
quint16_le padding2;
+ // quint32 formalsIndex[nFormals]
+ // quint32 localsIndex[nLocals]
+
const quint32_le *formalsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + formalsOffset); }
const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); }
const CodeOffsetToLine *lineNumberTable() const { return reinterpret_cast<const CodeOffsetToLine *>(reinterpret_cast<const char *>(this) + lineNumberOffset); }
@@ -710,6 +727,8 @@ struct Unit
quint32_le offsetToStringTable;
quint32_le functionTableSize;
quint32_le offsetToFunctionTable;
+ quint32_le blockTableSize;
+ quint32_le offsetToBlockTable;
quint32_le lookupTableSize;
quint32_le offsetToLookupTable;
quint32_le regexpTableSize;
@@ -771,6 +790,7 @@ struct Unit
}
const quint32_le *functionOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToFunctionTable); }
+ const quint32_le *blockOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToBlockTable); }
const Function *functionAt(int idx) const {
const quint32_le *offsetTable = functionOffsetTable();
@@ -778,6 +798,12 @@ struct Unit
return reinterpret_cast<const Function*>(reinterpret_cast<const char *>(this) + offset);
}
+ const Block *blockAt(int idx) const {
+ const quint32_le *offsetTable = blockOffsetTable();
+ const quint32_le offset = offsetTable[idx];
+ return reinterpret_cast<const Block *>(reinterpret_cast<const char *>(this) + offset);
+ }
+
const Lookup *lookupTable() const { return reinterpret_cast<const Lookup*>(reinterpret_cast<const char *>(this) + offsetToLookupTable); }
const RegExp *regexpAt(int index) const {
return reinterpret_cast<const RegExp*>(reinterpret_cast<const char *>(this) + offsetToRegexpTable + index * sizeof(RegExp));
@@ -796,7 +822,7 @@ struct Unit
}
};
-static_assert(sizeof(Unit) == 192, "Unit 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(Unit) == 200, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct TypeReference
{
@@ -945,6 +971,7 @@ public:
QV4::Lookup *runtimeLookups = nullptr;
QVector<QV4::Function *> runtimeFunctions;
+ QVector<QV4::Heap::InternalClass *> runtimeBlocks;
mutable QQmlNullableValue<QUrl> m_url;
mutable QQmlNullableValue<QUrl> m_finalUrl;
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index d9f37b542f..d5b77f61cb 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -233,28 +233,39 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO
for (int i = 0; i < f->locals.size(); ++i)
registerString(f->locals.at(i));
}
+ for (Context *c : qAsConst(module->blocks)) {
+ for (int i = 0; i < c->locals.size(); ++i)
+ registerString(c->locals.at(i));
+ }
- Q_ALLOCA_VAR(quint32_le, functionOffsets, module->functions.size() * sizeof(quint32_le));
+ Q_ALLOCA_VAR(quint32_le, blockAndFunctionOffsets, (module->functions.size() + module->blocks.size()) * sizeof(quint32_le));
uint jsClassDataOffset = 0;
char *dataPtr;
CompiledData::Unit *unit;
{
- QV4::CompiledData::Unit tempHeader = generateHeader(option, functionOffsets, &jsClassDataOffset);
+ QV4::CompiledData::Unit tempHeader = generateHeader(option, blockAndFunctionOffsets, &jsClassDataOffset);
dataPtr = reinterpret_cast<char *>(malloc(tempHeader.unitSize));
memset(dataPtr, 0, tempHeader.unitSize);
memcpy(&unit, &dataPtr, sizeof(CompiledData::Unit*));
memcpy(unit, &tempHeader, sizeof(tempHeader));
}
- memcpy(dataPtr + unit->offsetToFunctionTable, functionOffsets, unit->functionTableSize * sizeof(quint32_le));
+ memcpy(dataPtr + unit->offsetToFunctionTable, blockAndFunctionOffsets, unit->functionTableSize * sizeof(quint32_le));
+ memcpy(dataPtr + unit->offsetToBlockTable, blockAndFunctionOffsets + unit->functionTableSize, unit->blockTableSize * sizeof(quint32_le));
for (int i = 0; i < module->functions.size(); ++i) {
Context *function = module->functions.at(i);
if (function == module->rootContext)
unit->indexOfRootFunction = i;
- writeFunction(dataPtr + functionOffsets[i], function);
+ writeFunction(dataPtr + blockAndFunctionOffsets[i], function);
+ }
+
+ for (int i = 0; i < module->blocks.size(); ++i) {
+ Context *block = module->blocks.at(i);
+
+ writeBlock(dataPtr + blockAndFunctionOffsets[i + module->functions.size()], block);
}
CompiledData::Lookup *lookupsToWrite = reinterpret_cast<CompiledData::Lookup*>(dataPtr + unit->offsetToLookupTable);
@@ -395,7 +406,24 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
memcpy(f + function->codeOffset, irFunction->code.constData(), irFunction->code.size());
}
-QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, quint32_le *functionOffsets, uint *jsClassDataOffset)
+void QV4::Compiler::JSUnitGenerator::writeBlock(char *b, QV4::Compiler::Context *irBlock) const
+{
+ QV4::CompiledData::Block *block = reinterpret_cast<QV4::CompiledData::Block *>(b);
+
+ quint32 currentOffset = sizeof(QV4::CompiledData::Block);
+ currentOffset = (currentOffset + 7) & ~quint32(0x7);
+
+ block->nLocals = irBlock->locals.size();
+ block->localsOffset = currentOffset;
+ currentOffset += block->nLocals * sizeof(quint32);
+
+ // write locals
+ quint32_le *locals = (quint32_le *)(b + block->localsOffset);
+ for (int i = 0; i < irBlock->locals.size(); ++i)
+ locals[i] = getStringId(irBlock->locals.at(i));
+}
+
+QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, quint32_le *blockAndFunctionOffsets, uint *jsClassDataOffset)
{
CompiledData::Unit unit;
memset(&unit, 0, sizeof(unit));
@@ -414,6 +442,10 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
unit.offsetToFunctionTable = nextOffset;
nextOffset += unit.functionTableSize * sizeof(uint);
+ unit.blockTableSize = module->blocks.size();
+ unit.offsetToBlockTable = nextOffset;
+ nextOffset += unit.blockTableSize * sizeof(uint);
+
unit.lookupTableSize = lookups.count();
unit.offsetToLookupTable = nextOffset;
nextOffset += unit.lookupTableSize * sizeof(CompiledData::Lookup);
@@ -440,13 +472,21 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
for (int i = 0; i < module->functions.size(); ++i) {
Context *f = module->functions.at(i);
- functionOffsets[i] = nextOffset;
+ blockAndFunctionOffsets[i] = nextOffset;
const int qmlIdDepsCount = f->idObjectDependencies.count();
const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count();
nextOffset += QV4::CompiledData::Function::calculateSize(f->arguments.size(), f->locals.size(), f->lineNumberMapping.size(), f->nestedContexts.size(),
qmlIdDepsCount, qmlPropertyDepsCount, f->code.size());
}
+ blockAndFunctionOffsets += module->functions.size();
+
+ for (int i = 0; i < module->blocks.size(); ++i) {
+ Context *c = module->blocks.at(i);
+ blockAndFunctionOffsets[i] = nextOffset;
+
+ nextOffset += QV4::CompiledData::Block::calculateSize(c->locals.size());
+ }
if (option == GenerateWithStringTable) {
unit.stringTableSize = stringTable.stringCount();
diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h
index 360af6540f..bd330d3353 100644
--- a/src/qml/compiler/qv4compiler_p.h
+++ b/src/qml/compiler/qv4compiler_p.h
@@ -125,8 +125,8 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator {
};
QV4::CompiledData::Unit *generateUnit(GeneratorOption option = GenerateWithStringTable);
- // Returns bytes written
void writeFunction(char *f, Context *irFunction) const;
+ void writeBlock(char *f, Context *irBlock) const;
StringTableGenerator stringTable;
QString codeGeneratorName;
diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp
index e2e978be7c..4893a06af8 100644
--- a/src/qml/compiler/qv4compilercontext.cpp
+++ b/src/qml/compiler/qv4compilercontext.cpp
@@ -71,19 +71,12 @@ Context *Module::newContext(Node *node, Context *parent, ContextType contextType
return c;
}
-bool Context::forceLookupByName()
-{
- ControlFlow *flow = controlFlow;
- while (flow) {
- if (flow->needsLookupByName)
- return true;
- flow = flow->parent;
- }
- return false;
-}
-
bool Context::addLocalVar(const QString &name, Context::MemberType type, VariableScope scope, FunctionExpression *function)
{
+ // hoist var declarations to the function level
+ if (contextType == ContextType::Block && (scope == VariableScope::Var && type != MemberType::FunctionDefinition))
+ return parent->addLocalVar(name, type, scope, function);
+
if (name.isEmpty())
return true;
@@ -117,7 +110,7 @@ Context::ResolvedName Context::resolveName(const QString &name)
ResolvedName result;
while (c->parent) {
- if (c->forceLookupByName())
+ if (c->forceLookupByName)
return result;
Context::Member m = c->findMember(name);
@@ -128,7 +121,6 @@ Context::ResolvedName Context::resolveName(const QString &name)
if (c->isStrict && (name == QLatin1String("arguments") || name == QLatin1String("eval")))
result.isArgOrEval = true;
return result;
- Q_ASSERT(result.type != ResolvedName::Stack || result.scope == 0);
}
const int argIdx = c->findArgument(name);
if (argIdx != -1) {
@@ -145,7 +137,7 @@ Context::ResolvedName Context::resolveName(const QString &name)
return result;
}
}
- if (!c->isStrict && c->hasDirectEval)
+ if (!c->isStrict && c->hasDirectEval && c->contextType != ContextType::Block)
return result;
if (c->requiresExecutionContext)
@@ -154,25 +146,32 @@ Context::ResolvedName Context::resolveName(const QString &name)
}
// ### can we relax the restrictions here?
- if (c->forceLookupByName() || type == ContextType::Eval || c->type == ContextType::Binding)
+ if (c->forceLookupByName || contextType == ContextType::Eval || c->contextType == ContextType::Binding)
return result;
result.type = ResolvedName::Global;
return result;
}
-void Context::emitHeaderBytecode(Codegen *codegen)
+int Context::emitBlockHeader(Codegen *codegen)
{
-
using Instruction = Moth::Instruction;
Moth::BytecodeGenerator *bytecodeGenerator = codegen->generator();
setupFunctionIndices(bytecodeGenerator);
+ int contextReg = -1;
if (requiresExecutionContext ||
- type == 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.
- Instruction::CreateCallContext createContext;
- bytecodeGenerator->addInstruction(createContext);
+ 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) {
+ Instruction::PushBlockContext blockContext;
+ blockContext.index = blockIndex;
+ blockContext.reg = contextReg = bytecodeGenerator->newRegister();
+ bytecodeGenerator->addInstruction(blockContext);
+ } else {
+ Instruction::CreateCallContext createContext;
+ bytecodeGenerator->addInstruction(createContext);
+ }
}
if (usesThis && !isStrict) {
// make sure we convert this to an object
@@ -180,8 +179,10 @@ void Context::emitHeaderBytecode(Codegen *codegen)
bytecodeGenerator->addInstruction(convert);
}
- // variables in global code are properties of the global context object, not locals as with other functions.
- if (type == ContextType::Function || type == ContextType::Binding) {
+ switch (contextType) {
+ case ContextType::Block:
+ case ContextType::Function:
+ case 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
@@ -193,7 +194,11 @@ void Context::emitHeaderBytecode(Codegen *codegen)
bytecodeGenerator->addInstruction(store);
}
}
- } else {
+ 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();
@@ -203,8 +208,10 @@ void Context::emitHeaderBytecode(Codegen *codegen)
bytecodeGenerator->addInstruction(declareVar);
}
}
+ }
if (usesArgumentsObject == Context::ArgumentsObjectUsed) {
+ Q_ASSERT(contextType != ContextType::Block);
if (isStrict || (formals && !formals->isSimpleParameterList())) {
Instruction::CreateUnmappedArgumentsObject setup;
bytecodeGenerator->addInstruction(setup);
@@ -214,6 +221,38 @@ void Context::emitHeaderBytecode(Codegen *codegen)
}
codegen->referenceForName(QStringLiteral("arguments"), false).storeConsumeAccumulator();
}
+
+ for (const Context::Member &member : qAsConst(members)) {
+ 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();
+ }
+ }
+ }
+
+
+
+ return contextReg;
+}
+
+void Context::emitBlockFooter(Codegen *codegen, int oldContextReg)
+{
+ using Instruction = Moth::Instruction;
+ Moth::BytecodeGenerator *bytecodeGenerator = codegen->generator();
+
+ if (oldContextReg != -1) {
+ Instruction::PopContext popContext;
+ popContext.reg = oldContextReg;
+ bytecodeGenerator->addInstruction(popContext);
+ }
}
void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator)
@@ -222,7 +261,10 @@ void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator)
Q_ASSERT(nRegisters == 0);
registerOffset = bytecodeGenerator->registerCount();
- if (type == ContextType::Function || type == ContextType::Binding) {
+ switch (contextType) {
+ 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) {
@@ -235,6 +277,11 @@ void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator)
it->index = bytecodeGenerator->newRegister();
}
}
+ break;
+ }
+ case ContextType::Global:
+ case ContextType::Eval:
+ break;
}
nRegisters = bytecodeGenerator->registerCount() - registerOffset;
}
diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h
index 289488cbc8..189a623703 100644
--- a/src/qml/compiler/qv4compilercontext_p.h
+++ b/src/qml/compiler/qv4compilercontext_p.h
@@ -91,6 +91,7 @@ struct Module {
QHash<QQmlJS::AST::Node *, Context *> contextMap;
QList<Context *> functions;
+ QList<Context *> blocks;
Context *rootContext;
QString fileName;
QString finalUrl;
@@ -107,6 +108,7 @@ struct Context {
int column = 0;
int registerCountInFunction = 0;
int functionIndex = -1;
+ int blockIndex = -1;
enum MemberType {
UndefinedMember,
@@ -152,6 +154,7 @@ struct Context {
bool returnsClosure = false;
mutable bool argumentsCanEscape = false;
bool requiresExecutionContext = false;
+ bool forceLookupByName = false;
enum UsesArgumentsObject {
ArgumentsObjectUnknown,
@@ -161,7 +164,7 @@ struct Context {
UsesArgumentsObject usesArgumentsObject = ArgumentsObjectUnknown;
- ContextType type;
+ ContextType contextType;
template <typename T>
class SmallSet: public QVarLengthArray<T, 8>
@@ -212,14 +215,12 @@ struct Context {
Context(Context *parent, ContextType type)
: parent(parent)
- , type(type)
+ , contextType(type)
{
if (parent && parent->isStrict)
isStrict = true;
}
- bool forceLookupByName();
-
int findArgument(const QString &name)
{
// search backwards to handle duplicate argument names correctly
@@ -252,16 +253,16 @@ struct Context {
}
bool requiresImplicitReturnValue() const {
- return type == ContextType::Binding ||
- type == ContextType::Eval ||
- type == ContextType::Global;
+ return contextType == ContextType::Binding ||
+ contextType == ContextType::Eval ||
+ contextType == ContextType::Global;
}
void addUsedVariable(const QString &name) {
usedVariables.insert(name);
}
- bool addLocalVar(const QString &name, MemberType type, QQmlJS::AST::VariableScope scope, QQmlJS::AST::FunctionExpression *function = nullptr);
+ bool addLocalVar(const QString &name, MemberType contextType, QQmlJS::AST::VariableScope scope, QQmlJS::AST::FunctionExpression *function = nullptr);
struct ResolvedName {
enum Type {
@@ -277,7 +278,9 @@ struct Context {
bool isValid() const { return type != Unresolved; }
};
ResolvedName resolveName(const QString &name);
- void emitHeaderBytecode(Compiler::Codegen *codegen);
+ int emitBlockHeader(Compiler::Codegen *codegen);
+ void emitBlockFooter(Compiler::Codegen *codegen, int oldContextReg);
+
void setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator);
};
diff --git a/src/qml/compiler/qv4compilercontrolflow_p.h b/src/qml/compiler/qv4compilercontrolflow_p.h
index 9bda20905a..c02168c1b6 100644
--- a/src/qml/compiler/qv4compilercontrolflow_p.h
+++ b/src/qml/compiler/qv4compilercontrolflow_p.h
@@ -68,6 +68,7 @@ struct ControlFlow {
enum Type {
Loop,
With,
+ Block,
Finally,
Catch
};
@@ -91,16 +92,18 @@ struct ControlFlow {
Codegen *cg;
ControlFlow *parent;
Type type;
- bool needsLookupByName = false;
+ bool contextUsedLookupByName;
ControlFlow(Codegen *cg, Type type)
- : cg(cg), parent(cg->_context->controlFlow), type(type)
+ : cg(cg), parent(cg->controlFlow), type(type)
{
- cg->_context->controlFlow = this;
+ contextUsedLookupByName = cg->_context->forceLookupByName;
+ cg->controlFlow = this;
}
virtual ~ControlFlow() {
- cg->_context->controlFlow = parent;
+ cg->controlFlow = parent;
+ cg->_context->forceLookupByName = contextUsedLookupByName;
}
void emitReturnStatement() const {
@@ -235,9 +238,14 @@ struct ControlFlowUnwind : public ControlFlow
QVector<Handler> handlers;
ControlFlowUnwind(Codegen *cg, Type type)
- : ControlFlow(cg, type), unwindLabel(generator()->newExceptionHandler())
+ : ControlFlow(cg, type)
{
Q_ASSERT(type != Loop);
+ }
+
+ void setupExceptionHandler()
+ {
+ unwindLabel = generator()->newExceptionHandler();
controlFlowTemp = static_cast<int>(generator()->newRegister());
Reference::storeConstOnStack(cg, QV4::Encode::undefined(), controlFlowTemp);
// we'll need at least a handler for throw
@@ -289,7 +297,7 @@ struct ControlFlowUnwind : public ControlFlow
}
virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return &unwindLabel;
+ return unwindLabel.isValid() ? &unwindLabel : parentExceptionHandler();
}
virtual void emitForThrowHandling() { }
@@ -300,8 +308,9 @@ struct ControlFlowWith : public ControlFlowUnwind
ControlFlowWith(Codegen *cg)
: ControlFlowUnwind(cg, With)
{
- needsLookupByName = true;
+ cg->currentContext()->forceLookupByName = true;
+ setupExceptionHandler();
savedContextRegister = Moth::StackSlot::createRegister(generator()->newRegister());
// assumes the with object is in the accumulator
@@ -325,18 +334,56 @@ struct ControlFlowWith : public ControlFlowUnwind
Moth::StackSlot savedContextRegister;
};
+struct ControlFlowBlock : public ControlFlowUnwind
+{
+ ControlFlowBlock(Codegen *cg, Context *block)
+ : ControlFlowUnwind(cg, Block),
+ block(block)
+ {
+ savedContextRegister = block->emitBlockHeader(cg);
+
+ if (savedContextRegister != -1) {
+ setupExceptionHandler();
+ generator()->setExceptionHandler(&unwindLabel);
+ }
+ }
+
+ virtual ~ControlFlowBlock() {
+ // emit code for unwinding
+ if (savedContextRegister != -1) {
+ unwindLabel.link();
+ generator()->setExceptionHandler(parentExceptionHandler());
+ }
+
+ block->emitBlockFooter(cg, savedContextRegister);
+
+ if (savedContextRegister != -1)
+ emitUnwindHandler();
+ }
+ virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
+ if (savedContextRegister == -1)
+ return getParentHandler(type, label);
+ return ControlFlowUnwind::getHandler(type, label);
+ }
+
+ int savedContextRegister = -1;
+ Context *block;
+};
+
struct ControlFlowCatch : public ControlFlowUnwind
{
AST::Catch *catchExpression;
bool insideCatch = false;
BytecodeGenerator::ExceptionHandler exceptionLabel;
BytecodeGenerator::ExceptionHandler catchUnwindLabel;
+ bool oldLookupByName;
ControlFlowCatch(Codegen *cg, AST::Catch *catchExpression)
: ControlFlowUnwind(cg, Catch), catchExpression(catchExpression),
exceptionLabel(generator()->newExceptionHandler()),
catchUnwindLabel(generator()->newExceptionHandler())
{
+ setupExceptionHandler();
generator()->setExceptionHandler(&exceptionLabel);
}
@@ -363,7 +410,7 @@ struct ControlFlowCatch : public ControlFlowUnwind
~ControlFlowCatch() {
// emit code for unwinding
- needsLookupByName = true;
+ cg->_context->forceLookupByName = true;
insideCatch = true;
Codegen::RegisterScope scope(cg);
@@ -382,7 +429,6 @@ struct ControlFlowCatch : public ControlFlowUnwind
cg->statement(catchExpression->statement);
insideCatch = false;
- needsLookupByName = false;
// exceptions inside catch and break/return statements go here
catchUnwindLabel.link();
@@ -408,6 +454,7 @@ struct ControlFlowFinally : public ControlFlowUnwind
: ControlFlowUnwind(cg, Finally), finally(finally)
{
Q_ASSERT(finally != nullptr);
+ setupExceptionHandler();
generator()->setExceptionHandler(&unwindLabel);
}
diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp
index 4fa35f3adb..312f07e254 100644
--- a/src/qml/compiler/qv4compilerscanfunctions.cpp
+++ b/src/qml/compiler/qv4compilerscanfunctions.cpp
@@ -395,10 +395,17 @@ bool ScanFunctions::visit(ThisExpression *)
bool ScanFunctions::visit(Block *ast)
{
TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls);
+ enterEnvironment(ast, ContextType::Block);
+ _context->name = QLatin1String("Block");
Node::accept(ast->statements, this);
return false;
}
+void ScanFunctions::endVisit(Block *)
+{
+ leaveEnvironment();
+}
+
bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, StatementList *body, bool enterName)
{
Context *outerContext = _context;
@@ -421,6 +428,7 @@ bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParamete
outerContext->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
}
+ _context->name = name;
if (formals && formals->containsName(QStringLiteral("arguments")))
_context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
if (expr) {
@@ -496,18 +504,29 @@ void ScanFunctions::calcEscapingVariables()
c->hasWith |= inner->hasWith;
c = c->parent;
}
+ if (inner->contextType == ContextType::Block && inner->usesArgumentsObject == Context::ArgumentsObjectUsed) {
+ Context *f = inner->parent;
+ while (f->contextType == ContextType::Block)
+ f = f->parent;
+ f->usesArgumentsObject = Context::ArgumentsObjectUsed;
+ inner->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
+ }
}
for (Context *c : qAsConst(m->contextMap)) {
- bool allVarsEscape = c->hasWith || c->hasTry || c->hasDirectEval || m->debugMode;
+ bool allVarsEscape = c->hasWith || c->hasTry || c->hasDirectEval;
+ if (allVarsEscape && c->contextType == ContextType::Block && c->members.isEmpty())
+ allVarsEscape = false;
+ if (m->debugMode)
+ allVarsEscape = true;
if (allVarsEscape) {
c->requiresExecutionContext = true;
c->argumentsCanEscape = true;
}
// ### for now until we have lexically scoped vars that'll require it
- if (c->type == ContextType::Global)
+ 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->type == ContextType::Eval && c->isStrict)
+ if (c->contextType == ContextType::Eval && c->isStrict)
c->requiresExecutionContext = true;
if (!c->parent || c->usesArgumentsObject == Context::ArgumentsObjectUnknown)
c->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
@@ -529,11 +548,12 @@ void ScanFunctions::calcEscapingVariables()
if (showEscapingVars) {
qDebug() << "==== escaping variables ====";
for (Context *c : qAsConst(m->contextMap)) {
- qDebug() << "Context" << c << c->name << ":";
+ qDebug() << "Context" << c << c->name << "requiresExecutionContext" << c->requiresExecutionContext;
+ qDebug() << " parent:" << c->parent;
if (c->argumentsCanEscape)
qDebug() << " Arguments escape";
for (auto it = c->members.constBegin(); it != c->members.constEnd(); ++it) {
- qDebug() << " " << it.key() << it.value().canEscape;
+ qDebug() << " " << it.key() << it.value().canEscape << "isLexicallyScoped:" << it.value().isLexicallyScoped();
}
}
}
diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h
index fc24481ad4..d6969c56e8 100644
--- a/src/qml/compiler/qv4compilerscanfunctions_p.h
+++ b/src/qml/compiler/qv4compilerscanfunctions_p.h
@@ -134,6 +134,7 @@ protected:
bool visit(AST::ThisExpression *ast) override;
bool visit(AST::Block *ast) override;
+ void endVisit(AST::Block *ast) override;
protected:
bool enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::StatementList *body, bool enterName);
diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp
index c50e9c8379..094611e974 100644
--- a/src/qml/compiler/qv4instr_moth.cpp
+++ b/src/qml/compiler/qv4instr_moth.cpp
@@ -404,6 +404,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << dumpRegister(reg, nFormals);
MOTH_END_INSTR(PushWithContext)
+ MOTH_BEGIN_INSTR(PushBlockContext)
+ d << dumpRegister(reg, nFormals) << ", " << index;
+ MOTH_END_INSTR(PushBlockContext)
+
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 763e0adce1..2e1763d3be 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -114,6 +114,7 @@ QT_BEGIN_NAMESPACE
#define INSTR_CreateCallContext(op) INSTRUCTION(op, CreateCallContext, 0)
#define INSTR_PushCatchContext(op) INSTRUCTION(op, PushCatchContext, 2, name, reg)
#define INSTR_PushWithContext(op) INSTRUCTION(op, PushWithContext, 1, reg)
+#define INSTR_PushBlockContext(op) INSTRUCTION(op, PushBlockContext, 2, reg, index)
#define INSTR_PopContext(op) INSTRUCTION(op, PopContext, 1, reg)
#define INSTR_ForeachIteratorObject(op) INSTRUCTION(op, ForeachIteratorObject, 0)
#define INSTR_ForeachNextPropertyName(op) INSTRUCTION(op, ForeachNextPropertyName, 0)
@@ -235,6 +236,7 @@ QT_BEGIN_NAMESPACE
F(CreateCallContext) \
F(PushCatchContext) \
F(PushWithContext) \
+ F(PushBlockContext) \
F(PopContext) \
F(ForeachIteratorObject) \
F(ForeachNextPropertyName) \
diff --git a/src/qml/jit/qv4jit.cpp b/src/qml/jit/qv4jit.cpp
index aa9c244a60..4a2e2b959c 100644
--- a/src/qml/jit/qv4jit.cpp
+++ b/src/qml/jit/qv4jit.cpp
@@ -629,6 +629,24 @@ void BaselineJIT::generate_PushWithContext(int reg)
as->checkException();
}
+static void pushBlockContextHelper(QV4::Value *stack, int reg, int index)
+{
+ stack[reg] = stack[CallData::Context];
+ ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
+ stack[CallData::Context] = Runtime::method_createBlockContext(c, index);
+}
+
+void BaselineJIT::generate_PushBlockContext(int reg, int index)
+{
+ as->saveAccumulatorInFrame();
+ as->prepareCallWithArgCount(3);
+ as->passInt32AsArg(index, 2);
+ as->passInt32AsArg(reg, 1);
+ as->passRegAsArg(0, 0);
+ JIT_GENERATE_RUNTIME_CALL(pushBlockContextHelper, Assembler::IgnoreResult);
+}
+
+
void BaselineJIT::generate_PopContext(int reg) { as->popContext(reg); }
void BaselineJIT::generate_ForeachIteratorObject()
@@ -1177,6 +1195,9 @@ void BaselineJIT::collectLabelsInBytecode()
MOTH_BEGIN_INSTR(PushWithContext)
MOTH_END_INSTR(PushWithContext)
+ MOTH_BEGIN_INSTR(PushBlockContext)
+ MOTH_END_INSTR(PushBlockContext)
+
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 7861cdec38..3e81807754 100644
--- a/src/qml/jit/qv4jit_p.h
+++ b/src/qml/jit/qv4jit_p.h
@@ -177,6 +177,7 @@ public:
void generate_CreateCallContext() override;
void generate_PushCatchContext(int name, int reg) override;
void generate_PushWithContext(int reg) override;
+ void generate_PushBlockContext(int reg, int index) override;
void generate_PopContext(int reg) override;
void generate_ForeachIteratorObject() override;
void generate_ForeachNextPropertyName() override;
diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp
index 020e519e74..39fdcf0227 100644
--- a/src/qml/jsruntime/qv4context.cpp
+++ b/src/qml/jsruntime/qv4context.cpp
@@ -55,6 +55,29 @@ DEFINE_MANAGED_VTABLE(ExecutionContext);
DEFINE_MANAGED_VTABLE(CallContext);
DEFINE_MANAGED_VTABLE(CatchContext);
+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;
+ Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory, ic);
+ c->init();
+ c->type = Heap::ExecutionContext::Type_BlockContext;
+
+ c->outer.set(v4, outer);
+ c->function.set(v4, static_cast<Heap::FunctionObject *>(frame->jsFrame->function.m()));
+
+ c->locals.size = nLocals;
+ c->locals.alloc = nLocals;
+
+ return c;
+}
+
Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame)
{
Function *function = frame->v4Function;
@@ -112,6 +135,7 @@ 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());
@@ -170,6 +194,7 @@ bool ExecutionContext::deleteProperty(String *name)
return false;
break;
}
+ case Heap::ExecutionContext::Type_BlockContext:
case Heap::ExecutionContext::Type_CallContext: {
Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
uint index = c->internalClass->find(id);
@@ -225,6 +250,7 @@ ExecutionContext::Error ExecutionContext::setProperty(String *name, const Value
}
break;
}
+ case Heap::ExecutionContext::Type_BlockContext:
case Heap::ExecutionContext::Type_CallContext: {
Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
uint index = c->internalClass->find(id);
@@ -273,6 +299,7 @@ ReturnedValue ExecutionContext::getProperty(String *name)
return c->exceptionValue.asReturnedValue();
break;
}
+ case Heap::ExecutionContext::Type_BlockContext:
case Heap::ExecutionContext::Type_CallContext: {
Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
Identifier *id = name->identifier();
@@ -314,6 +341,7 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base)
return c->exceptionValue.asReturnedValue();
break;
}
+ case Heap::ExecutionContext::Type_BlockContext:
case Heap::ExecutionContext::Type_CallContext: {
Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
name->makeIdentifier();
diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h
index ccc1653c36..5a9c85c941 100644
--- a/src/qml/jsruntime/qv4context_p.h
+++ b/src/qml/jsruntime/qv4context_p.h
@@ -110,7 +110,8 @@ DECLARE_HEAP_OBJECT(ExecutionContext, Base) {
Type_CatchContext = 0x2,
Type_WithContext = 0x3,
Type_QmlContext = 0x4,
- Type_CallContext = 0x5
+ Type_BlockContext = 0x5,
+ Type_CallContext = 0x6
};
void init(ContextType t)
@@ -191,6 +192,7 @@ struct Q_QML_EXPORT ExecutionContext : public Managed
Q_MANAGED_TYPE(ExecutionContext)
V4_INTERNALCLASS(ExecutionContext)
+ static Heap::CallContext *newBlockContext(QV4::CppStackFrame *frame, int blockIndex);
static Heap::CallContext *newCallContext(QV4::CppStackFrame *frame);
Heap::ExecutionContext *newWithContext(Heap::Object *with);
Heap::CatchContext *newCatchContext(Heap::String *exceptionVarName, ReturnedValue exceptionValue);
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index b14395b507..95e4017344 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -1202,6 +1202,13 @@ ReturnedValue Runtime::method_createCatchContext(ExecutionContext *parent, int e
e->catchException(nullptr))->asReturnedValue();
}
+ReturnedValue Runtime::method_createBlockContext(ExecutionContext *parent, int index)
+{
+ ExecutionEngine *e = parent->engine();
+ return parent->newBlockContext(e->currentStackFrame, index)->asReturnedValue();
+}
+
+
void Runtime::method_declareVar(ExecutionEngine *engine, bool deletable, int nameIndex)
{
Scope scope(engine);
diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h
index 9bdb41b19e..24bab70327 100644
--- a/src/qml/jsruntime/qv4runtimeapi_p.h
+++ b/src/qml/jsruntime/qv4runtimeapi_p.h
@@ -126,6 +126,7 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> {
F(void, throwException, (ExecutionEngine *engine, const Value &value)) \
F(ReturnedValue, createWithContext, (ExecutionContext *parent, const Value &o)) \
F(ReturnedValue, createCatchContext, (ExecutionContext *parent, int exceptionVarNameIndex)) \
+ F(ReturnedValue, createBlockContext, (ExecutionContext *parent, int index)) \
\
/* 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 8e5b14d0ea..a515abd9de 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -894,6 +894,12 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
STACK_VALUE(CallData::Context) = Runtime::method_createWithContext(c, accumulator);
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);
+ MOTH_END_INSTR(PushBlockContext)
+
MOTH_BEGIN_INSTR(PopContext)
STACK_VALUE(CallData::Context) = STACK_VALUE(reg);
MOTH_END_INSTR(PopContext)