aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
-rw-r--r--tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp16
-rw-r--r--tests/auto/qml/ecmascripttests/TestExpectations35
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp7
26 files changed, 418 insertions, 152 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)
diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
index 17a99d6a43..ccb3a2dcdc 100644
--- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
+++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
@@ -220,7 +220,21 @@ public:
{
for (int i = 0, ei = m_stackTrace.size(); i != ei; ++i) {
m_capturedScope.append(NamedRefs());
- ScopeJob job(&collector, i, 0);
+ FrameJob frameJob(&collector, i);
+ debugger->runInEngine(&frameJob);
+ QJsonObject frameObj = frameJob.returnValue();
+ QJsonArray scopes = frameObj.value(QLatin1String("scopes")).toArray();
+ int nscopes = scopes.size();
+ int s = 0;
+ for (s = 0; s < nscopes; ++s) {
+ QJsonObject o = scopes.at(s).toObject();
+ if (o.value(QLatin1String("type")).toInt(-2) == 1) // CallContext
+ break;
+ }
+ if (s == nscopes)
+ return;
+
+ ScopeJob job(&collector, i, s);
debugger->runInEngine(&job);
NamedRefs &refs = m_capturedScope.last();
QJsonObject object = job.returnValue();
diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations
index 55007189b8..9576dca707 100644
--- a/tests/auto/qml/ecmascripttests/TestExpectations
+++ b/tests/auto/qml/ecmascripttests/TestExpectations
@@ -3938,25 +3938,8 @@ language/arguments-object/mapped/nonconfigurable-nonwritable-descriptors-set-by-
language/arguments-object/mapped/nonwritable-nonconfigurable-descriptors-set-by-arguments
language/arguments-object/mapped/Symbol.iterator
language/arguments-object/unmapped/Symbol.iterator
-language/block-scope/leave/finally-block-let-declaration-only-shadows-outer-parameter-value-1
-language/block-scope/leave/finally-block-let-declaration-only-shadows-outer-parameter-value-2
-language/block-scope/leave/for-loop-block-let-declaration-only-shadows-outer-parameter-value-1
-language/block-scope/leave/for-loop-block-let-declaration-only-shadows-outer-parameter-value-2
-language/block-scope/leave/nested-block-let-declaration-only-shadows-outer-parameter-value-1
-language/block-scope/leave/nested-block-let-declaration-only-shadows-outer-parameter-value-2
-language/block-scope/leave/outermost-binding-updated-in-catch-block-nested-block-let-declaration-unseen-outside-of-block
-language/block-scope/leave/try-block-let-declaration-only-shadows-outer-parameter-value-1
-language/block-scope/leave/try-block-let-declaration-only-shadows-outer-parameter-value-2
-language/block-scope/leave/verify-context-in-finally-block
-language/block-scope/leave/verify-context-in-for-loop-block
-language/block-scope/leave/verify-context-in-labelled-block
-language/block-scope/leave/verify-context-in-try-block
-language/block-scope/leave/x-after-break-to-label
-language/block-scope/leave/x-before-continue
-language/block-scope/shadowing/const-declaration-shadowing-catch-parameter
-language/block-scope/shadowing/const-declarations-shadowing-parameter-name-let-const-and-var-variables
-language/block-scope/shadowing/let-declaration-shadowing-catch-parameter
-language/block-scope/shadowing/let-declarations-shadowing-parameter-name-let-const-and-var
+language/block-scope/syntax/for-in/acquire-properties-from-array
+language/block-scope/syntax/for-in/acquire-properties-from-object
language/block-scope/syntax/for-in/mixed-values-in-iteration
language/computed-property-names/basics/string
language/computed-property-names/basics/symbol
@@ -4004,7 +3987,6 @@ language/destructuring/binding/syntax/recursive-array-and-object-patterns
language/eval-code/direct/lex-env-distinct-cls
language/eval-code/direct/lex-env-distinct-const
language/eval-code/direct/lex-env-distinct-let
-language/eval-code/direct/lex-env-heritage
language/eval-code/direct/lex-env-no-init-cls
language/eval-code/direct/lex-env-no-init-const
language/eval-code/direct/lex-env-no-init-let
@@ -4026,7 +4008,6 @@ language/eval-code/indirect/always-non-strict
language/eval-code/indirect/lex-env-distinct-cls
language/eval-code/indirect/lex-env-distinct-const
language/eval-code/indirect/lex-env-distinct-let
-language/eval-code/indirect/lex-env-heritage
language/eval-code/indirect/lex-env-no-init-cls
language/eval-code/indirect/lex-env-no-init-const
language/eval-code/indirect/lex-env-no-init-let
@@ -6186,14 +6167,12 @@ language/expressions/yield/star-rhs-unresolvable
language/expressions/yield/star-string
language/expressions/yield/then-return
language/expressions/yield/within-for
-language/function-code/block-decl-strict
language/function-code/each-param-has-own-non-shared-eval-scope
language/function-code/each-param-has-own-scope
language/function-code/eval-param-env-with-computed-key
language/function-code/eval-param-env-with-prop-initializer
language/function-code/switch-case-decl-strict
language/function-code/switch-dflt-decl-strict
-language/global-code/block-decl-strict
language/global-code/decl-lex
language/global-code/decl-lex-configurable-global
language/global-code/decl-lex-deletion
@@ -6442,8 +6421,6 @@ language/statements/async-function/try-return-finally-throw
language/statements/async-function/try-throw-finally-reject
language/statements/async-function/try-throw-finally-return
language/statements/async-function/try-throw-finally-throw
-language/statements/block/scope-lex-close
-language/statements/block/scope-lex-open
language/statements/block/tco-stmt
language/statements/block/tco-stmt-list
language/statements/class/accessor-name-inst-computed
@@ -7445,12 +7422,10 @@ language/statements/const/function-local-closure-get-before-initialization
language/statements/const/function-local-use-before-initialization-in-declaration-statement
language/statements/const/function-local-use-before-initialization-in-prior-statement
language/statements/const/global-closure-get-before-initialization
-language/statements/const/syntax/const
language/statements/const/syntax/const-invalid-assignment-next-expression-for
language/statements/const/syntax/const-invalid-assignment-statement-body-for-in
language/statements/const/syntax/const-invalid-assignment-statement-body-for-of
language/statements/const/syntax/const-outer-inner-let-bindings
-language/statements/continue/shadowing-loop-variable-in-same-scope-as-continue
language/statements/do-while/tco-body
language/statements/empty/cptn-value
language/statements/for/dstr-const-ary-init-iter-close
@@ -8697,7 +8672,6 @@ language/statements/try/dstr-obj-ptrn-list-err
language/statements/try/dstr-obj-ptrn-prop-eval-err
language/statements/try/dstr-obj-ptrn-prop-id-get-value-err
language/statements/try/dstr-obj-ptrn-prop-id-init-throws
-language/statements/try/scope-catch-block-lex-close
language/statements/try/scope-catch-block-lex-open
language/statements/try/scope-catch-param-lex-open
language/statements/try/scope-catch-param-var-none
@@ -8776,14 +8750,11 @@ built-ins/NativeErrors/SyntaxError/is-error-object
built-ins/NativeErrors/TypeError/is-error-object
built-ins/NativeErrors/URIError/is-error-object
-built-ins/Object/prototype/toLocaleString/primitive_this_value
language/expressions/object/yield-non-strict-access
language/expressions/object/yield-non-strict-syntax
language/expressions/object/not-defined
language/expressions/object/prop-def-id-get-error
language/expressions/object/prop-def-id-valid
-language/function-code/10.4.3-1-104
-language/function-code/10.4.3-1-106
# Expected to fail but passed ---
language/eval-code/direct/var-env-global-lex-non-strict
@@ -8791,4 +8762,4 @@ language/global-code/decl-lex-restricted-global
language/statements/const/global-use-before-initialization-in-declaration-statement
language/statements/const/global-use-before-initialization-in-prior-statement
language/statements/let/global-use-before-initialization-in-declaration-statement
-language/statements/let/global-use-before-initialization-in-prior-statement \ No newline at end of file
+language/statements/let/global-use-before-initialization-in-prior-statement
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index a57e0b2c79..4945a23302 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -3209,9 +3209,7 @@ void tst_QJSEngine::threadedEngine()
void tst_QJSEngine::functionDeclarationsInConditionals()
{
- // Even though this is bad practice (and test262 covers it with best practices test cases),
- // we do allow for function declarations in if and while statements, as unfortunately that's
- // real world JavaScript. (QTBUG-33064 for example)
+ // Visibility of function declarations inside blocks is limited to the block
QJSEngine eng;
QJSValue result = eng.evaluate("if (true) {\n"
" function blah() { return false; }\n"
@@ -3219,8 +3217,7 @@ void tst_QJSEngine::functionDeclarationsInConditionals()
" function blah() { return true; }\n"
"}\n"
"blah();");
- QVERIFY(result.isBool());
- QCOMPARE(result.toBool(), true);
+ QVERIFY(result.isError());
}
void tst_QJSEngine::arrayPop_QTBUG_35979()