aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/compiler/compiler.pri3
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp14
-rw-r--r--src/qml/compiler/qqmlirbuilder_p.h6
-rw-r--r--src/qml/compiler/qv4codegen.cpp403
-rw-r--r--src/qml/compiler/qv4codegen_p.h239
-rw-r--r--src/qml/compiler/qv4compiler.cpp10
-rw-r--r--src/qml/compiler/qv4compiler_p.h6
-rw-r--r--src/qml/compiler/qv4compilercontext.cpp82
-rw-r--r--src/qml/compiler/qv4compilercontext_p.h286
-rw-r--r--src/qml/compiler/qv4compilercontrolflow_p.h421
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp7
-rw-r--r--src/qml/jsruntime/qv4global_p.h16
-rw-r--r--src/qml/jsruntime/qv4script.cpp28
-rw-r--r--src/qml/jsruntime/qv4script_p.h6
-rw-r--r--src/qml/qml/qqmltypeloader.cpp4
-rw-r--r--tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp2
-rw-r--r--tools/qmlcachegen/qmlcachegen.cpp2
-rw-r--r--tools/qmljs/qmljs.cpp2
18 files changed, 860 insertions, 677 deletions
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri
index a8eeb2a1fa..826eafe966 100644
--- a/src/qml/compiler/compiler.pri
+++ b/src/qml/compiler/compiler.pri
@@ -6,6 +6,8 @@ HEADERS += \
$$PWD/qv4compileddata_p.h \
$$PWD/qv4compiler_p.h \
$$PWD/qv4compilationunit_moth_p.h \
+ $$PWD/qv4compilercontext_p.h \
+ $$PWD/qv4compilercontrolflow_p.h \
$$PWD/qv4codegen_p.h \
$$PWD/qqmlirbuilder_p.h \
$$PWD/qqmltypecompiler_p.h
@@ -15,6 +17,7 @@ SOURCES += \
$$PWD/qv4compileddata.cpp \
$$PWD/qv4compiler.cpp \
$$PWD/qv4compilationunit_moth.cpp \
+ $$PWD/qv4compilercontext.cpp \
$$PWD/qv4codegen.cpp \
$$PWD/qqmlirbuilder.cpp
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index bb55cfe6ec..dacfe272ed 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -1552,9 +1552,9 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, Binding
}
JSCodeGen::JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator,
- QQmlJS::Module *jsModule, QQmlJS::Engine *jsEngine,
+ QV4::Compiler::Module *jsModule, QQmlJS::Engine *jsEngine,
QQmlJS::AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports, const QV4::Compiler::StringTableGenerator *stringPool)
- : QQmlJS::Codegen(jsUnitGenerator, /*strict mode*/false)
+ : QV4::Compiler::Codegen(jsUnitGenerator, /*strict mode*/false)
, sourceCode(sourceCode)
, jsEngine(jsEngine)
, qmlRoot(qmlRoot)
@@ -1586,8 +1586,8 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
{
QVector<int> runtimeFunctionIndices(functions.size());
- ScanFunctions scan(this, sourceCode, QQmlJS::GlobalCode);
- scan.enterEnvironment(0, QQmlJS::QmlBinding);
+ ScanFunctions scan(this, sourceCode, QV4::Compiler::GlobalCode);
+ scan.enterEnvironment(0, QV4::Compiler::QmlBinding);
scan.enterQmlScope(qmlRoot, QStringLiteral("context scope"));
for (const CompiledFunctionOrExpression &f : functions) {
Q_ASSERT(f.node != qmlRoot);
@@ -1596,7 +1596,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
if (function)
scan.enterQmlFunction(function);
else
- scan.enterEnvironment(f.node, QQmlJS::QmlBinding);
+ scan.enterEnvironment(f.node, QV4::Compiler::QmlBinding);
scan(function ? function->body : f.node);
scan.leaveEnvironment();
@@ -1936,7 +1936,7 @@ void JSCodeGen::beginFunctionBodyHook()
#endif
}
-QQmlJS::Codegen::Reference JSCodeGen::fallbackNameLookup(const QString &name)
+QV4::Compiler::Codegen::Reference JSCodeGen::fallbackNameLookup(const QString &name)
{
#ifndef V4_BOOTSTRAP
if (_disableAcceleratedLookups)
@@ -1956,7 +1956,7 @@ QQmlJS::Codegen::Reference JSCodeGen::fallbackNameLookup(const QString &name)
// Look for IDs first.
for (const IdMapping &mapping : qAsConst(_idObjects)) {
if (name == mapping.name) {
- if (_context->compilationMode == QQmlJS::QmlBinding)
+ if (_context->compilationMode == QV4::Compiler::QmlBinding)
_context->idObjectDependencies.insert(mapping.idIndex);
Reference result = Reference::fromTemp(this);
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h
index 80d0fdc306..cba26bf50a 100644
--- a/src/qml/compiler/qqmlirbuilder_p.h
+++ b/src/qml/compiler/qqmlirbuilder_p.h
@@ -428,7 +428,7 @@ struct Q_QML_PRIVATE_EXPORT Document
Document(bool debugMode);
QString code;
QQmlJS::Engine jsParserEngine;
- QQmlJS::Module jsModule;
+ QV4::Compiler::Module jsModule;
QList<const QV4::CompiledData::Import *> imports;
QList<Pragma*> pragmas;
QQmlJS::AST::UiProgram *program;
@@ -581,9 +581,9 @@ struct Q_QML_EXPORT PropertyResolver
};
#endif
-struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QQmlJS::Codegen
+struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen
{
- JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator, QQmlJS::Module *jsModule,
+ JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator, QV4::Compiler::Module *jsModule,
QQmlJS::Engine *jsEngine, QQmlJS::AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports,
const QV4::Compiler::StringTableGenerator *stringPool);
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index 8cfdb6919d..d2ec9be105 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -51,6 +51,8 @@
#include <private/qqmljsast_p.h>
#include <private/qv4string_p.h>
#include <private/qv4value_p.h>
+#include <private/qv4compilercontext_p.h>
+#include <private/qv4compilercontrolflow_p.h>
#include <private/qv4bytecodegenerator_p.h>
#include <private/qv4compilationunit_moth_p.h>
@@ -67,368 +69,8 @@
QT_USE_NAMESPACE
using namespace QV4;
-using namespace QQmlJS;
-using namespace AST;
-
-QT_BEGIN_NAMESPACE
-namespace QV4 {
-
-struct ControlFlow {
- using Reference = Codegen::Reference;
- using BytecodeGenerator = Moth::BytecodeGenerator;
- using Instruction = Moth::Instruction;
-
- enum Type {
- Loop,
- With,
- Finally,
- Catch
- };
-
- enum HandlerType {
- Invalid,
- Break,
- Continue,
- Return,
- Throw
- };
-
- struct Handler {
- HandlerType type;
- QString label;
- BytecodeGenerator::Label linkLabel;
- int tempIndex;
- int value;
- };
-
- Codegen *cg;
- ControlFlow *parent;
- Type type;
- bool needsLookupByName = false;
-
- ControlFlow(Codegen *cg, Type type)
- : cg(cg), parent(cg->_context->controlFlow), type(type)
- {
- cg->_context->controlFlow = this;
- }
-
- virtual ~ControlFlow() {
- cg->_context->controlFlow = parent;
- }
-
- void jumpToHandler(const Handler &h) {
- if (h.tempIndex >= 0) {
- Reference val = Reference::fromConst(cg, QV4::Encode(h.value));
- Reference temp = Reference::fromTemp(cg, h.tempIndex);
- temp.store(val);
- }
- cg->bytecodeGenerator->jump().link(h.linkLabel);
- }
-
- virtual QString label() const { return QString(); }
-
- bool isSimple() const {
- return type == Loop;
- }
-
- Handler getParentHandler(HandlerType type, const QString &label = QString()) {
- if (parent)
- return parent->getHandler(type, label);
- switch (type) {
- case Break:
- case Continue:
- return { Invalid, QString(), {}, -1, 0 };
- case Return:
- case Throw:
- return { type, QString(), cg->_exitBlock, -1, 0 };
- case Invalid:
- break;
- }
- Q_ASSERT(false);
- Q_UNREACHABLE();
- }
-
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- return getParentHandler(type, label);
- }
-
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return parent ? parent->exceptionHandler() : 0;
- }
- BytecodeGenerator::ExceptionHandler *parentExceptionHandler() {
- return parent ? parent->exceptionHandler() : 0;
- }
-
- virtual void handleThrow(const Reference &expr) {
- Handler h = getHandler(ControlFlow::Throw);
- if (h.tempIndex >= 0) {
- Reference val = Reference::fromConst(cg, QV4::Encode(h.value));
- Reference temp = Reference::fromTemp(cg, h.tempIndex);
- temp.store(val);
- }
- Instruction::CallBuiltinThrow instr;
- instr.arg = expr.asRValue();
- generator()->addInstruction(instr);
- }
-
-protected:
- QString loopLabel() const {
- QString label;
- if (cg->_labelledStatement) {
- label = cg->_labelledStatement->label.toString();
- cg->_labelledStatement = 0;
- }
- return label;
- }
- BytecodeGenerator *generator() const {
- return cg->bytecodeGenerator;
- }
-};
-
-struct ControlFlowLoop : public ControlFlow
-{
- QString loopLabel;
- BytecodeGenerator::Label *breakLabel = 0;
- BytecodeGenerator::Label *continueLabel = 0;
-
- ControlFlowLoop(Codegen *cg, BytecodeGenerator::Label *breakLabel, BytecodeGenerator::Label *continueLabel = 0)
- : ControlFlow(cg, Loop), loopLabel(ControlFlow::loopLabel()), breakLabel(breakLabel), continueLabel(continueLabel)
- {
- }
-
- virtual QString label() const { return loopLabel; }
-
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- switch (type) {
- case Break:
- if (breakLabel && (label.isEmpty() || label == loopLabel))
- return { type, loopLabel, *breakLabel, -1, 0 };
- break;
- case Continue:
- if (continueLabel && (label.isEmpty() || label == loopLabel))
- return { type, loopLabel, *continueLabel, -1, 0 };
- break;
- case Return:
- case Throw:
- break;
- case Invalid:
- Q_ASSERT(false);
- Q_UNREACHABLE();
- }
- return ControlFlow::getHandler(type, label);
- }
-
-};
-
-struct ControlFlowUnwind : public ControlFlow
-{
- BytecodeGenerator::ExceptionHandler unwindLabel;
- int controlFlowTemp;
- QVector<Handler> handlers;
-
- ControlFlowUnwind(Codegen *cg, Type type)
- : ControlFlow(cg, type), unwindLabel(generator()->newExceptionHandler())
- {
- Q_ASSERT(type != Loop);
- controlFlowTemp = static_cast<int>(generator()->newTemp());
- Reference::fromTemp(cg, controlFlowTemp).store(Reference::fromConst(cg, QV4::Encode::undefined()));
- // we'll need at least a handler for throw
- getHandler(Throw);
- }
-
- void emitUnwindHandler()
- {
- Q_ASSERT(!isSimple());
-
- Reference temp = Reference::fromTemp(cg, controlFlowTemp);
- for (const auto &h : qAsConst(handlers)) {
- Codegen::TempScope tempScope(cg);
- Handler parentHandler = getParentHandler(h.type, h.label);
-
-
- if (h.type == Throw || parentHandler.tempIndex >= 0) {
- BytecodeGenerator::Label skip = generator()->newLabel();
- generator()->jumpStrictNotEqual(temp.asRValue(), Reference::fromConst(cg, QV4::Encode(h.value)).asRValue())
- .link(skip);
- if (h.type == Throw)
- emitForThrowHandling();
- Reference parentTemp = Reference::fromTemp(cg, parentHandler.tempIndex);
- parentTemp.store(Reference::fromConst(cg, QV4::Encode(parentHandler.value)));
- generator()->jump().link(parentHandler.linkLabel);
- skip.link();
- } else {
- generator()->jumpStrictEqual(temp.asRValue(), Reference::fromConst(cg, QV4::Encode(h.value)).asRValue())
- .link(parentHandler.linkLabel);
- }
- }
- }
-
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- for (const auto &h : qAsConst(handlers)) {
- if (h.type == type && h.label == label)
- return h;
- }
- Handler h = {
- type,
- label,
- unwindLabel,
- controlFlowTemp,
- handlers.size()
- };
- handlers.append(h);
- return h;
- }
-
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return &unwindLabel;
- }
-
- virtual void emitForThrowHandling() { }
-};
-
-struct ControlFlowWith : public ControlFlowUnwind
-{
- ControlFlowWith(Codegen *cg)
- : ControlFlowUnwind(cg, With)
- {
- needsLookupByName = true;
- generator()->setExceptionHandler(&unwindLabel);
- }
-
- virtual ~ControlFlowWith() {
- // emit code for unwinding
- unwindLabel.link();
-
- generator()->setExceptionHandler(parentExceptionHandler());
- Instruction::CallBuiltinPopScope pop;
- generator()->addInstruction(pop);
-
- emitUnwindHandler();
- }
-};
-
-struct ControlFlowCatch : public ControlFlowUnwind
-{
- AST::Catch *catchExpression;
- bool insideCatch = false;
- BytecodeGenerator::ExceptionHandler exceptionLabel;
- BytecodeGenerator::ExceptionHandler catchUnwindLabel;
-
- ControlFlowCatch(Codegen *cg, AST::Catch *catchExpression)
- : ControlFlowUnwind(cg, Catch), catchExpression(catchExpression),
- exceptionLabel(generator()->newExceptionHandler()),
- catchUnwindLabel(generator()->newExceptionHandler())
- {
- generator()->setExceptionHandler(&exceptionLabel);
- }
-
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- Handler h = ControlFlowUnwind::getHandler(type, label);
- if (insideCatch)
- // if we're inside the catch block, we need to jump to the pop scope
- // instruction at the end of the catch block, not the unwind handler
- h.linkLabel = catchUnwindLabel;
- else if (type == Throw)
- // if we're inside the try block, we need to jump to the catch block,
- // not the unwind handler
- h.linkLabel = exceptionLabel;
- return h;
- }
-
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return insideCatch ? &catchUnwindLabel : &exceptionLabel;
- }
-
- ~ControlFlowCatch() {
- // emit code for unwinding
-
- needsLookupByName = true;
- insideCatch = true;
-
- // exceptions inside the try block go here
- exceptionLabel.link();
- Reference name = Reference::fromName(cg, catchExpression->name.toString());
- Instruction::CallBuiltinPushCatchScope pushCatchScope;
- pushCatchScope.name = name.nameIndex;
- generator()->addInstruction(pushCatchScope);
- // clear the unwind temp for exceptions, we want to resume normal code flow afterwards
- Reference::fromTemp(cg, controlFlowTemp).store(Reference::fromConst(cg, QV4::Encode::undefined()));
- generator()->setExceptionHandler(&catchUnwindLabel);
-
- cg->statement(catchExpression->statement);
-
- insideCatch = false;
- needsLookupByName = false;
-
- // exceptions inside catch and break/return statements go here
- catchUnwindLabel.link();
- Instruction::CallBuiltinPopScope pop;
- generator()->addInstruction(pop);
-
- // break/continue/return statements in try go here
- unwindLabel.link();
- generator()->setExceptionHandler(parentExceptionHandler());
-
- emitUnwindHandler();
- }
-};
-
-struct ControlFlowFinally : public ControlFlowUnwind
-{
- AST::Finally *finally;
- bool insideFinally = false;
- int exceptionTemp = -1;
-
- ControlFlowFinally(Codegen *cg, AST::Finally *finally)
- : ControlFlowUnwind(cg, Finally), finally(finally)
- {
- Q_ASSERT(finally != 0);
- generator()->setExceptionHandler(&unwindLabel);
- }
-
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- // if we're inside the finally block, any exceptions etc. should
- // go directly to the parent handler
- if (insideFinally)
- return ControlFlow::getHandler(type, label);
- return ControlFlowUnwind::getHandler(type, label);
- }
-
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return insideFinally ? parentExceptionHandler() : ControlFlowUnwind::exceptionHandler();
- }
-
- ~ControlFlowFinally() {
- // emit code for unwinding
- unwindLabel.link();
-
- Codegen::TempScope scope(cg);
-
- insideFinally = true;
- exceptionTemp = generator()->newTemp();
- Reference exception = Reference::fromTemp(cg, exceptionTemp);
- Instruction::GetException instr;
- instr.result = exception.asLValue();
- generator()->addInstruction(instr);
-
- generator()->setExceptionHandler(parentExceptionHandler());
- cg->statement(finally->statement);
- insideFinally = false;
-
- emitUnwindHandler();
- }
-
- virtual void emitForThrowHandling() {
- // reset the exception flag, that got cleared before executing the statements in finally
- Instruction::SetException setException;
- Q_ASSERT(exceptionTemp != -1);
- setException.exception = Reference::fromTemp(cg, exceptionTemp).asRValue();
- generator()->addInstruction(setException);
- }
-};
-
-} // QV4 namespace
-QT_END_NAMESPACE
+using namespace QV4::Compiler;
+using namespace QQmlJS::AST;
static inline QV4::Runtime::RuntimeMethods aluOpFunction(QSOperator::Op op)
{
@@ -851,7 +493,7 @@ Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict)
void Codegen::generateFromProgram(const QString &fileName,
const QString &sourceCode,
Program *node,
- QQmlJS::Module *module,
+ Module *module,
CompilationMode mode)
{
Q_ASSERT(node);
@@ -870,7 +512,7 @@ void Codegen::generateFromProgram(const QString &fileName,
void Codegen::generateFromFunctionExpression(const QString &fileName,
const QString &sourceCode,
AST::FunctionExpression *ast,
- QQmlJS::Module *module)
+ Module *module)
{
_module = module;
_module->fileName = fileName;
@@ -3156,7 +2798,7 @@ QQmlRefPointer<CompiledData::CompilationUnit> Codegen::generateCompilationUnit(b
Moth::CompilationUnit *compilationUnit = new Moth::CompilationUnit;
compilationUnit->codeRefs.resize(_module->functions.size());
int i = 0;
- for (QQmlJS::Context *irFunction : qAsConst(_module->functions))
+ for (Context *irFunction : qAsConst(_module->functions))
compilationUnit->codeRefs[i++] = irFunction->code;
if (generateUnitData)
@@ -3514,34 +3156,3 @@ void Codegen::Reference::load(uint tmp) const
}
}
-Context *Module::newContext(Node *node, Context *parent, CompilationMode compilationMode)
-{
- Context *c = new Context(parent, compilationMode);
- if (node) {
- SourceLocation loc = node->firstSourceLocation();
- c->line = loc.startLine;
- c->column = loc.startColumn;
- }
-
- contextMap.insert(node, c);
-
- if (!parent)
- rootContext = c;
- else {
- parent->nestedContexts.append(c);
- c->isStrict = parent->isStrict;
- }
-
- return c;
-}
-
-bool Context::forceLookupByName()
-{
- QV4::ControlFlow *flow = controlFlow;
- while (flow) {
- if (flow->needsLookupByName)
- return true;
- flow = flow->parent;
- }
- return false;
-}
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index 03a2a089d1..e872dae464 100644
--- a/src/qml/compiler/qv4codegen_p.h
+++ b/src/qml/compiler/qv4codegen_p.h
@@ -56,6 +56,7 @@
#include <private/qqmljsengine_p.h>
#include <private/qv4instr_moth_p.h>
#include <private/qv4compiler_p.h>
+#include <private/qv4compilercontext_p.h>
#include <private/qqmlrefcount_p.h>
#include <QtCore/QStringList>
#include <QtCore/QDateTime>
@@ -68,10 +69,9 @@
QT_BEGIN_NAMESPACE
+using namespace QQmlJS;
+
namespace QV4 {
-struct ControlFlow;
-struct ControlFlowCatch;
-struct ControlFlowFinally;
namespace Moth {
struct Instruction;
@@ -80,225 +80,14 @@ struct Instruction;
namespace CompiledData {
struct CompilationUnit;
}
-}
-
-namespace QQmlJS {
-
-enum CompilationMode {
- GlobalCode,
- EvalCode,
- FunctionCode,
- QmlBinding // This is almost the same as EvalCode, except:
- // * function declarations are moved to the return address when encountered
- // * return statements are allowed everywhere (like in FunctionCode)
- // * variable declarations are treated as true locals (like in FunctionCode)
-};
-
-struct Context;
-
-struct Module {
- Module(bool debugMode)
- : debugMode(debugMode)
- {}
- ~Module() {
- qDeleteAll(contextMap);
- }
-
- Context *newContext(AST::Node *node, Context *parent, CompilationMode compilationMode);
-
- QHash<AST::Node *, Context *> contextMap;
- QList<Context *> functions;
- Context *rootContext;
- QString fileName;
- QDateTime sourceTimeStamp;
- uint unitFlags = 0; // flags merged into CompiledData::Unit::flags
- bool debugMode = false;
- QString targetABI; // ### seems unused currently
-};
-
-
-struct Context {
- Context *parent;
- QString name;
- int line = 0;
- int column = 0;
-
- enum MemberType {
- UndefinedMember,
- VariableDefinition,
- VariableDeclaration,
- FunctionDefinition
- };
-
- struct Member {
- MemberType type = UndefinedMember;
- int index = -1;
- AST::VariableDeclaration::VariableScope scope = AST::VariableDeclaration::FunctionScope;
- mutable bool canEscape = false;
- AST::FunctionExpression *function = 0;
-
- bool isLexicallyScoped() const { return this->scope != AST::VariableDeclaration::FunctionScope; }
- };
- typedef QMap<QString, Member> MemberMap;
-
- MemberMap members;
- AST::FormalParameterList *formals = 0;
- QStringList arguments;
- QStringList locals;
- QVector<Context *> nestedContexts;
-
- QV4::ControlFlow *controlFlow = 0;
- QByteArray code;
-
- int maxNumberOfArguments = 0;
- bool hasDirectEval = false;
- bool hasNestedFunctions = false;
- bool isStrict = false;
- bool isNamedFunctionExpression = false;
- bool usesThis = false;
- bool hasTry = false;
- bool hasWith = false;
- mutable bool argumentsCanEscape = false;
-
- enum UsesArgumentsObject {
- ArgumentsObjectUnknown,
- ArgumentsObjectNotUsed,
- ArgumentsObjectUsed
- };
-
- UsesArgumentsObject usesArgumentsObject = ArgumentsObjectUnknown;
-
- CompilationMode compilationMode;
-
- template <typename T>
- class SmallSet: public QVarLengthArray<T, 8>
- {
- public:
- void insert(int value)
- {
- for (auto it : *this) {
- if (it == value)
- return;
- }
- this->append(value);
- }
- };
-
- // Map from meta property index (existence implies dependency) to notify signal index
- struct KeyValuePair
- {
- quint32 _key;
- quint32 _value;
-
- KeyValuePair(): _key(0), _value(0) {}
- KeyValuePair(quint32 key, quint32 value): _key(key), _value(value) {}
-
- quint32 key() const { return _key; }
- quint32 value() const { return _value; }
- };
-
- class PropertyDependencyMap: public QVarLengthArray<KeyValuePair, 8>
- {
- public:
- void insert(quint32 key, quint32 value)
- {
- for (auto it = begin(), eit = end(); it != eit; ++it) {
- if (it->_key == key) {
- it->_value = value;
- return;
- }
- }
- append(KeyValuePair(key, value));
- }
- };
-
- // Qml extension:
- SmallSet<int> idObjectDependencies;
- PropertyDependencyMap contextObjectPropertyDependencies;
- PropertyDependencyMap scopeObjectPropertyDependencies;
-
- Context(Context *parent, CompilationMode mode)
- : parent(parent)
- , compilationMode(mode)
- {
- if (parent && parent->isStrict)
- isStrict = true;
- }
-
- bool forceLookupByName();
-
-
- bool canUseSimpleCall() const {
- return nestedContexts.isEmpty() &&
- locals.isEmpty() && arguments.size() <= QV4::Global::ReservedArgumentCount &&
- !hasTry && !hasWith && !isNamedFunctionExpression &&
- usesArgumentsObject == ArgumentsObjectNotUsed && !hasDirectEval;
- }
-
- int findArgument(const QString &name, bool canEscape)
- {
- // search backwards to handle duplicate argument names correctly
- for (int i = arguments.size() - 1; i >= 0; --i) {
- if (arguments.at(i) == name) {
- if (canEscape)
- argumentsCanEscape = true;
- return i;
- }
- }
- return -1;
- }
- int findMember(const QString &name, bool canEscape) const
- {
- MemberMap::const_iterator it = members.find(name);
- if (it == members.end())
- return -1;
- if (canEscape)
- (*it).canEscape = true;
- Q_ASSERT((*it).index != -1 || !parent);
- return (*it).index;
- }
-
- bool memberInfo(const QString &name, const Member **m) const
- {
- Q_ASSERT(m);
- MemberMap::const_iterator it = members.find(name);
- if (it == members.end()) {
- *m = 0;
- return false;
- }
- *m = &(*it);
- return true;
- }
-
- void addLocalVar(const QString &name, MemberType type, AST::VariableDeclaration::VariableScope scope, AST::FunctionExpression *function = 0)
- {
- if (! name.isEmpty()) {
- if (type != FunctionDefinition) {
- for (AST::FormalParameterList *it = formals; it; it = it->next)
- if (it->name == name)
- return;
- }
- MemberMap::iterator it = members.find(name);
- if (it == members.end()) {
- Member m;
- m.type = type;
- m.function = function;
- m.scope = scope;
- members.insert(name, m);
- } else {
- Q_ASSERT(scope == (*it).scope);
- if ((*it).type <= type) {
- (*it).type = type;
- (*it).function = function;
- }
- }
- }
- }
-};
+namespace Compiler {
+struct ControlFlow;
+struct ControlFlowCatch;
+struct ControlFlowFinally;
-class Q_QML_PRIVATE_EXPORT Codegen: protected AST::Visitor
+class Q_QML_PRIVATE_EXPORT Codegen: protected QQmlJS::AST::Visitor
{
protected:
using BytecodeGenerator = QV4::Moth::BytecodeGenerator;
@@ -310,12 +99,12 @@ public:
void generateFromProgram(const QString &fileName,
const QString &sourceCode,
AST::Program *ast,
- QQmlJS::Module *module,
+ Module *module,
CompilationMode mode = GlobalCode);
void generateFromFunctionExpression(const QString &fileName,
const QString &sourceCode,
AST::FunctionExpression *ast,
- QQmlJS::Module *module);
+ Module *module);
public:
struct Reference {
@@ -708,9 +497,9 @@ public:
static QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading();
protected:
- friend struct QV4::ControlFlow;
- friend struct QV4::ControlFlowCatch;
- friend struct QV4::ControlFlowFinally;
+ friend struct ControlFlow;
+ friend struct ControlFlowCatch;
+ friend struct ControlFlowFinally;
Result _expr;
Module *_module;
BytecodeGenerator::Label _exitBlock;
@@ -819,6 +608,8 @@ private:
}
+}
+
QT_END_NAMESPACE
#endif // QV4CODEGEN_P_H
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index 25902df9ec..f11905b319 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -100,7 +100,7 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit)
}
}
-QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QQmlJS::Module *module)
+QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QV4::Compiler::Module *module)
: module(module)
{
// Make sure the empty string always gets index 0
@@ -241,7 +241,7 @@ int QV4::Compiler::JSUnitGenerator::registerJSClass(int count, CompiledData::JSC
QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorOption option)
{
registerString(module->fileName);
- for (QQmlJS::Context *f : qAsConst(module->functions)) {
+ for (Context *f : qAsConst(module->functions)) {
registerString(f->name);
for (int i = 0; i < f->arguments.size(); ++i)
registerString(f->arguments.at(i));
@@ -265,7 +265,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO
memcpy(dataPtr + unit->offsetToFunctionTable, functionOffsets, unit->functionTableSize * sizeof(CompiledData::LEUInt32));
for (int i = 0; i < module->functions.size(); ++i) {
- QQmlJS::Context *function = module->functions.at(i);
+ Context *function = module->functions.at(i);
if (function == module->rootContext)
unit->indexOfRootFunction = i;
@@ -306,7 +306,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO
return unit;
}
-void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QQmlJS::Context *irFunction) const
+void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Context *irFunction) const
{
QV4::CompiledData::Function *function = (QV4::CompiledData::Function *)f;
@@ -438,7 +438,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
nextOffset += jsClassData.size();
for (int i = 0; i < module->functions.size(); ++i) {
- QQmlJS::Context *f = module->functions.at(i);
+ Context *f = module->functions.at(i);
functionOffsets[i] = nextOffset;
const int qmlIdDepsCount = f->idObjectDependencies.count();
diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h
index 50aa763452..3bab7a72cf 100644
--- a/src/qml/compiler/qv4compiler_p.h
+++ b/src/qml/compiler/qv4compiler_p.h
@@ -99,7 +99,7 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator {
bool isAccessor;
};
- JSUnitGenerator(QQmlJS::Module *module);
+ JSUnitGenerator(Module *module);
int registerString(const QString &str) { return stringTable.registerString(str); }
int getStringId(const QString &string) const { return stringTable.getStringId(string); }
@@ -129,14 +129,14 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator {
QV4::CompiledData::Unit *generateUnit(GeneratorOption option = GenerateWithStringTable);
// Returns bytes written
- void writeFunction(char *f, QQmlJS::Context *irFunction) const;
+ void writeFunction(char *f, Context *irFunction) const;
StringTableGenerator stringTable;
QString codeGeneratorName;
private:
CompiledData::Unit generateHeader(GeneratorOption option, QJsonPrivate::q_littleendian<quint32> *functionOffsets, uint *jsClassDataOffset);
- QQmlJS::Module *module;
+ Module *module;
QList<CompiledData::Lookup> lookups;
QVector<CompiledData::RegExp> regexps;
diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp
new file mode 100644
index 0000000000..3293dc53c8
--- /dev/null
+++ b/src/qml/compiler/qv4compilercontext.cpp
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4compilercontext_p.h"
+#include "qv4compilercontrolflow_p.h"
+
+QT_USE_NAMESPACE
+using namespace QV4;
+using namespace QV4::Compiler;
+using namespace QQmlJS::AST;
+
+QT_BEGIN_NAMESPACE
+
+Context *Module::newContext(Node *node, Context *parent, CompilationMode compilationMode)
+{
+ Context *c = new Context(parent, compilationMode);
+ if (node) {
+ SourceLocation loc = node->firstSourceLocation();
+ c->line = loc.startLine;
+ c->column = loc.startColumn;
+ }
+
+ contextMap.insert(node, c);
+
+ if (!parent)
+ rootContext = c;
+ else {
+ parent->nestedContexts.append(c);
+ c->isStrict = parent->isStrict;
+ }
+
+ return c;
+}
+
+bool Context::forceLookupByName()
+{
+ ControlFlow *flow = controlFlow;
+ while (flow) {
+ if (flow->needsLookupByName)
+ return true;
+ flow = flow->parent;
+ }
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h
new file mode 100644
index 0000000000..a924d087c0
--- /dev/null
+++ b/src/qml/compiler/qv4compilercontext_p.h
@@ -0,0 +1,286 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4COMPILERCONTEXT_P_H
+#define QV4COMPILERCONTEXT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qv4global_p.h"
+#include <private/qqmljsast_p.h>
+#include <QtCore/QStringList>
+#include <QtCore/QDateTime>
+#include <QtCore/QStack>
+#include <QtCore/QHash>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace Compiler {
+
+struct ControlFlow;
+
+enum CompilationMode {
+ GlobalCode,
+ EvalCode,
+ FunctionCode,
+ QmlBinding // This is almost the same as EvalCode, except:
+ // * function declarations are moved to the return address when encountered
+ // * return statements are allowed everywhere (like in FunctionCode)
+ // * variable declarations are treated as true locals (like in FunctionCode)
+};
+
+struct Context;
+
+struct Module {
+ Module(bool debugMode)
+ : debugMode(debugMode)
+ {}
+ ~Module() {
+ qDeleteAll(contextMap);
+ }
+
+ Context *newContext(QQmlJS::AST::Node *node, Context *parent, CompilationMode compilationMode);
+
+ QHash<QQmlJS::AST::Node *, Context *> contextMap;
+ QList<Context *> functions;
+ Context *rootContext;
+ QString fileName;
+ QDateTime sourceTimeStamp;
+ uint unitFlags = 0; // flags merged into CompiledData::Unit::flags
+ bool debugMode = false;
+ QString targetABI; // ### seems unused currently
+};
+
+
+struct Context {
+ Context *parent;
+ QString name;
+ int line = 0;
+ int column = 0;
+
+ enum MemberType {
+ UndefinedMember,
+ VariableDefinition,
+ VariableDeclaration,
+ FunctionDefinition
+ };
+
+ struct Member {
+ MemberType type = UndefinedMember;
+ int index = -1;
+ QQmlJS::AST::VariableDeclaration::VariableScope scope = QQmlJS::AST::VariableDeclaration::FunctionScope;
+ mutable bool canEscape = false;
+ QQmlJS::AST::FunctionExpression *function = 0;
+
+ bool isLexicallyScoped() const { return this->scope != QQmlJS::AST::VariableDeclaration::FunctionScope; }
+ };
+ typedef QMap<QString, Member> MemberMap;
+
+ MemberMap members;
+ QQmlJS::AST::FormalParameterList *formals = 0;
+ QStringList arguments;
+ QStringList locals;
+ QVector<Context *> nestedContexts;
+
+ ControlFlow *controlFlow = 0;
+ QByteArray code;
+
+ int maxNumberOfArguments = 0;
+ bool hasDirectEval = false;
+ bool hasNestedFunctions = false;
+ bool isStrict = false;
+ bool isNamedFunctionExpression = false;
+ bool usesThis = false;
+ bool hasTry = false;
+ bool hasWith = false;
+ mutable bool argumentsCanEscape = false;
+
+ enum UsesArgumentsObject {
+ ArgumentsObjectUnknown,
+ ArgumentsObjectNotUsed,
+ ArgumentsObjectUsed
+ };
+
+ UsesArgumentsObject usesArgumentsObject = ArgumentsObjectUnknown;
+
+ CompilationMode compilationMode;
+
+ template <typename T>
+ class SmallSet: public QVarLengthArray<T, 8>
+ {
+ public:
+ void insert(int value)
+ {
+ for (auto it : *this) {
+ if (it == value)
+ return;
+ }
+ this->append(value);
+ }
+ };
+
+ // Map from meta property index (existence implies dependency) to notify signal index
+ struct KeyValuePair
+ {
+ quint32 _key;
+ quint32 _value;
+
+ KeyValuePair(): _key(0), _value(0) {}
+ KeyValuePair(quint32 key, quint32 value): _key(key), _value(value) {}
+
+ quint32 key() const { return _key; }
+ quint32 value() const { return _value; }
+ };
+
+ class PropertyDependencyMap: public QVarLengthArray<KeyValuePair, 8>
+ {
+ public:
+ void insert(quint32 key, quint32 value)
+ {
+ for (auto it = begin(), eit = end(); it != eit; ++it) {
+ if (it->_key == key) {
+ it->_value = value;
+ return;
+ }
+ }
+ append(KeyValuePair(key, value));
+ }
+ };
+
+ // Qml extension:
+ SmallSet<int> idObjectDependencies;
+ PropertyDependencyMap contextObjectPropertyDependencies;
+ PropertyDependencyMap scopeObjectPropertyDependencies;
+
+ Context(Context *parent, CompilationMode mode)
+ : parent(parent)
+ , compilationMode(mode)
+ {
+ if (parent && parent->isStrict)
+ isStrict = true;
+ }
+
+ bool forceLookupByName();
+
+
+ bool canUseSimpleCall() const {
+ return nestedContexts.isEmpty() &&
+ locals.isEmpty() && arguments.size() <= QV4::Global::ReservedArgumentCount &&
+ !hasTry && !hasWith && !isNamedFunctionExpression &&
+ usesArgumentsObject == ArgumentsObjectNotUsed && !hasDirectEval;
+ }
+
+ int findArgument(const QString &name, bool canEscape)
+ {
+ // search backwards to handle duplicate argument names correctly
+ for (int i = arguments.size() - 1; i >= 0; --i) {
+ if (arguments.at(i) == name) {
+ if (canEscape)
+ argumentsCanEscape = true;
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ int findMember(const QString &name, bool canEscape) const
+ {
+ MemberMap::const_iterator it = members.find(name);
+ if (it == members.end())
+ return -1;
+ if (canEscape)
+ (*it).canEscape = true;
+ Q_ASSERT((*it).index != -1 || !parent);
+ return (*it).index;
+ }
+
+ bool memberInfo(const QString &name, const Member **m) const
+ {
+ Q_ASSERT(m);
+ MemberMap::const_iterator it = members.find(name);
+ if (it == members.end()) {
+ *m = 0;
+ return false;
+ }
+ *m = &(*it);
+ return true;
+ }
+
+ void addLocalVar(const QString &name, MemberType type, QQmlJS::AST::VariableDeclaration::VariableScope scope, QQmlJS::AST::FunctionExpression *function = 0)
+ {
+ if (! name.isEmpty()) {
+ if (type != FunctionDefinition) {
+ for (QQmlJS::AST::FormalParameterList *it = formals; it; it = it->next)
+ if (it->name == name)
+ return;
+ }
+ MemberMap::iterator it = members.find(name);
+ if (it == members.end()) {
+ Member m;
+ m.type = type;
+ m.function = function;
+ m.scope = scope;
+ members.insert(name, m);
+ } else {
+ Q_ASSERT(scope == (*it).scope);
+ if ((*it).type <= type) {
+ (*it).type = type;
+ (*it).function = function;
+ }
+ }
+ }
+ }
+};
+
+
+} } // namespace QV4::Compiler
+
+QT_END_NAMESPACE
+
+#endif // QV4CODEGEN_P_H
diff --git a/src/qml/compiler/qv4compilercontrolflow_p.h b/src/qml/compiler/qv4compilercontrolflow_p.h
new file mode 100644
index 0000000000..ac6c47c0b7
--- /dev/null
+++ b/src/qml/compiler/qv4compilercontrolflow_p.h
@@ -0,0 +1,421 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4COMPILERCONTROLFLOW_P_H
+#define QV4COMPILERCONTROLFLOW_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qv4global_p.h>
+#include <private/qv4codegen_p.h>
+#include <private/qqmljsast_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace Compiler {
+
+struct ControlFlow {
+ using Reference = Codegen::Reference;
+ using BytecodeGenerator = Moth::BytecodeGenerator;
+ using Instruction = Moth::Instruction;
+
+ enum Type {
+ Loop,
+ With,
+ Finally,
+ Catch
+ };
+
+ enum HandlerType {
+ Invalid,
+ Break,
+ Continue,
+ Return,
+ Throw
+ };
+
+ struct Handler {
+ HandlerType type;
+ QString label;
+ BytecodeGenerator::Label linkLabel;
+ int tempIndex;
+ int value;
+ };
+
+ Codegen *cg;
+ ControlFlow *parent;
+ Type type;
+ bool needsLookupByName = false;
+
+ ControlFlow(Codegen *cg, Type type)
+ : cg(cg), parent(cg->_context->controlFlow), type(type)
+ {
+ cg->_context->controlFlow = this;
+ }
+
+ virtual ~ControlFlow() {
+ cg->_context->controlFlow = parent;
+ }
+
+ void jumpToHandler(const Handler &h) {
+ if (h.tempIndex >= 0) {
+ Reference val = Reference::fromConst(cg, QV4::Encode(h.value));
+ Reference temp = Reference::fromTemp(cg, h.tempIndex);
+ temp.store(val);
+ }
+ cg->bytecodeGenerator->jump().link(h.linkLabel);
+ }
+
+ virtual QString label() const { return QString(); }
+
+ bool isSimple() const {
+ return type == Loop;
+ }
+
+ Handler getParentHandler(HandlerType type, const QString &label = QString()) {
+ if (parent)
+ return parent->getHandler(type, label);
+ switch (type) {
+ case Break:
+ case Continue:
+ return { Invalid, QString(), {}, -1, 0 };
+ case Return:
+ case Throw:
+ return { type, QString(), cg->_exitBlock, -1, 0 };
+ case Invalid:
+ break;
+ }
+ Q_ASSERT(false);
+ Q_UNREACHABLE();
+ }
+
+ virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
+ return getParentHandler(type, label);
+ }
+
+ virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
+ return parent ? parent->exceptionHandler() : 0;
+ }
+ BytecodeGenerator::ExceptionHandler *parentExceptionHandler() {
+ return parent ? parent->exceptionHandler() : 0;
+ }
+
+ virtual void handleThrow(const Reference &expr) {
+ Handler h = getHandler(ControlFlow::Throw);
+ if (h.tempIndex >= 0) {
+ Reference val = Reference::fromConst(cg, QV4::Encode(h.value));
+ Reference temp = Reference::fromTemp(cg, h.tempIndex);
+ temp.store(val);
+ }
+ Instruction::CallBuiltinThrow instr;
+ instr.arg = expr.asRValue();
+ generator()->addInstruction(instr);
+ }
+
+protected:
+ QString loopLabel() const {
+ QString label;
+ if (cg->_labelledStatement) {
+ label = cg->_labelledStatement->label.toString();
+ cg->_labelledStatement = 0;
+ }
+ return label;
+ }
+ BytecodeGenerator *generator() const {
+ return cg->bytecodeGenerator;
+ }
+};
+
+struct ControlFlowLoop : public ControlFlow
+{
+ QString loopLabel;
+ BytecodeGenerator::Label *breakLabel = 0;
+ BytecodeGenerator::Label *continueLabel = 0;
+
+ ControlFlowLoop(Codegen *cg, BytecodeGenerator::Label *breakLabel, BytecodeGenerator::Label *continueLabel = 0)
+ : ControlFlow(cg, Loop), loopLabel(ControlFlow::loopLabel()), breakLabel(breakLabel), continueLabel(continueLabel)
+ {
+ }
+
+ virtual QString label() const { return loopLabel; }
+
+ virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
+ switch (type) {
+ case Break:
+ if (breakLabel && (label.isEmpty() || label == loopLabel))
+ return { type, loopLabel, *breakLabel, -1, 0 };
+ break;
+ case Continue:
+ if (continueLabel && (label.isEmpty() || label == loopLabel))
+ return { type, loopLabel, *continueLabel, -1, 0 };
+ break;
+ case Return:
+ case Throw:
+ break;
+ case Invalid:
+ Q_ASSERT(false);
+ Q_UNREACHABLE();
+ }
+ return ControlFlow::getHandler(type, label);
+ }
+
+};
+
+struct ControlFlowUnwind : public ControlFlow
+{
+ BytecodeGenerator::ExceptionHandler unwindLabel;
+ int controlFlowTemp;
+ QVector<Handler> handlers;
+
+ ControlFlowUnwind(Codegen *cg, Type type)
+ : ControlFlow(cg, type), unwindLabel(generator()->newExceptionHandler())
+ {
+ Q_ASSERT(type != Loop);
+ controlFlowTemp = static_cast<int>(generator()->newTemp());
+ Reference::fromTemp(cg, controlFlowTemp).store(Reference::fromConst(cg, QV4::Encode::undefined()));
+ // we'll need at least a handler for throw
+ getHandler(Throw);
+ }
+
+ void emitUnwindHandler()
+ {
+ Q_ASSERT(!isSimple());
+
+ Reference temp = Reference::fromTemp(cg, controlFlowTemp);
+ for (const auto &h : qAsConst(handlers)) {
+ Codegen::TempScope tempScope(cg);
+ Handler parentHandler = getParentHandler(h.type, h.label);
+
+
+ if (h.type == Throw || parentHandler.tempIndex >= 0) {
+ BytecodeGenerator::Label skip = generator()->newLabel();
+ generator()->jumpStrictNotEqual(temp.asRValue(), Reference::fromConst(cg, QV4::Encode(h.value)).asRValue())
+ .link(skip);
+ if (h.type == Throw)
+ emitForThrowHandling();
+ Reference parentTemp = Reference::fromTemp(cg, parentHandler.tempIndex);
+ parentTemp.store(Reference::fromConst(cg, QV4::Encode(parentHandler.value)));
+ generator()->jump().link(parentHandler.linkLabel);
+ skip.link();
+ } else {
+ generator()->jumpStrictEqual(temp.asRValue(), Reference::fromConst(cg, QV4::Encode(h.value)).asRValue())
+ .link(parentHandler.linkLabel);
+ }
+ }
+ }
+
+ virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
+ for (const auto &h : qAsConst(handlers)) {
+ if (h.type == type && h.label == label)
+ return h;
+ }
+ Handler h = {
+ type,
+ label,
+ unwindLabel,
+ controlFlowTemp,
+ handlers.size()
+ };
+ handlers.append(h);
+ return h;
+ }
+
+ virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
+ return &unwindLabel;
+ }
+
+ virtual void emitForThrowHandling() { }
+};
+
+struct ControlFlowWith : public ControlFlowUnwind
+{
+ ControlFlowWith(Codegen *cg)
+ : ControlFlowUnwind(cg, With)
+ {
+ needsLookupByName = true;
+ generator()->setExceptionHandler(&unwindLabel);
+ }
+
+ virtual ~ControlFlowWith() {
+ // emit code for unwinding
+ unwindLabel.link();
+
+ generator()->setExceptionHandler(parentExceptionHandler());
+ Instruction::CallBuiltinPopScope pop;
+ generator()->addInstruction(pop);
+
+ emitUnwindHandler();
+ }
+};
+
+struct ControlFlowCatch : public ControlFlowUnwind
+{
+ AST::Catch *catchExpression;
+ bool insideCatch = false;
+ BytecodeGenerator::ExceptionHandler exceptionLabel;
+ BytecodeGenerator::ExceptionHandler catchUnwindLabel;
+
+ ControlFlowCatch(Codegen *cg, AST::Catch *catchExpression)
+ : ControlFlowUnwind(cg, Catch), catchExpression(catchExpression),
+ exceptionLabel(generator()->newExceptionHandler()),
+ catchUnwindLabel(generator()->newExceptionHandler())
+ {
+ generator()->setExceptionHandler(&exceptionLabel);
+ }
+
+ virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
+ Handler h = ControlFlowUnwind::getHandler(type, label);
+ if (insideCatch)
+ // if we're inside the catch block, we need to jump to the pop scope
+ // instruction at the end of the catch block, not the unwind handler
+ h.linkLabel = catchUnwindLabel;
+ else if (type == Throw)
+ // if we're inside the try block, we need to jump to the catch block,
+ // not the unwind handler
+ h.linkLabel = exceptionLabel;
+ return h;
+ }
+
+ virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
+ return insideCatch ? &catchUnwindLabel : &exceptionLabel;
+ }
+
+ ~ControlFlowCatch() {
+ // emit code for unwinding
+
+ needsLookupByName = true;
+ insideCatch = true;
+
+ // exceptions inside the try block go here
+ exceptionLabel.link();
+ Reference name = Reference::fromName(cg, catchExpression->name.toString());
+ Instruction::CallBuiltinPushCatchScope pushCatchScope;
+ pushCatchScope.name = name.nameIndex;
+ generator()->addInstruction(pushCatchScope);
+ // clear the unwind temp for exceptions, we want to resume normal code flow afterwards
+ Reference::fromTemp(cg, controlFlowTemp).store(Reference::fromConst(cg, QV4::Encode::undefined()));
+ generator()->setExceptionHandler(&catchUnwindLabel);
+
+ cg->statement(catchExpression->statement);
+
+ insideCatch = false;
+ needsLookupByName = false;
+
+ // exceptions inside catch and break/return statements go here
+ catchUnwindLabel.link();
+ Instruction::CallBuiltinPopScope pop;
+ generator()->addInstruction(pop);
+
+ // break/continue/return statements in try go here
+ unwindLabel.link();
+ generator()->setExceptionHandler(parentExceptionHandler());
+
+ emitUnwindHandler();
+ }
+};
+
+struct ControlFlowFinally : public ControlFlowUnwind
+{
+ AST::Finally *finally;
+ bool insideFinally = false;
+ int exceptionTemp = -1;
+
+ ControlFlowFinally(Codegen *cg, AST::Finally *finally)
+ : ControlFlowUnwind(cg, Finally), finally(finally)
+ {
+ Q_ASSERT(finally != 0);
+ generator()->setExceptionHandler(&unwindLabel);
+ }
+
+ virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
+ // if we're inside the finally block, any exceptions etc. should
+ // go directly to the parent handler
+ if (insideFinally)
+ return ControlFlow::getHandler(type, label);
+ return ControlFlowUnwind::getHandler(type, label);
+ }
+
+ virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
+ return insideFinally ? parentExceptionHandler() : ControlFlowUnwind::exceptionHandler();
+ }
+
+ ~ControlFlowFinally() {
+ // emit code for unwinding
+ unwindLabel.link();
+
+ Codegen::TempScope scope(cg);
+
+ insideFinally = true;
+ exceptionTemp = generator()->newTemp();
+ Reference exception = Reference::fromTemp(cg, exceptionTemp);
+ Instruction::GetException instr;
+ instr.result = exception.asLValue();
+ generator()->addInstruction(instr);
+
+ generator()->setExceptionHandler(parentExceptionHandler());
+ cg->statement(finally->statement);
+ insideFinally = false;
+
+ emitUnwindHandler();
+ }
+
+ virtual void emitForThrowHandling() {
+ // reset the exception flag, that got cleared before executing the statements in finally
+ Instruction::SetException setException;
+ Q_ASSERT(exceptionTemp != -1);
+ setException.exception = Reference::fromTemp(cg, exceptionTemp).asRValue();
+ generator()->addInstruction(setException);
+ }
+};
+
+} } // QV4::Compiler namespace
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index 992701d0fb..fbce1e0cda 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -210,17 +210,16 @@ void FunctionCtor::construct(const Managed *that, Scope &scope, CallData *callDa
return;
}
- using namespace QQmlJS::AST;
- FunctionExpression *fe = QQmlJS::AST::cast<FunctionExpression *>(parser.rootNode());
+ QQmlJS::AST::FunctionExpression *fe = QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression *>(parser.rootNode());
if (!fe) {
scope.result = scope.engine->throwSyntaxError(QLatin1String("Parse error"));
return;
}
- QQmlJS::Module module(scope.engine->debugger() != 0);
+ Compiler::Module module(scope.engine->debugger() != 0);
Compiler::JSUnitGenerator jsGenerator(&module);
- QQmlJS::RuntimeCodegen cg(scope.engine, &jsGenerator, f->strictMode());
+ Compiler::RuntimeCodegen cg(scope.engine, &jsGenerator, f->strictMode());
cg.generateFromFunctionExpression(QString(), function, fe, &module);
QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = cg.generateCompilationUnit();
diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h
index ab7ee17e68..549cf66e24 100644
--- a/src/qml/jsruntime/qv4global_p.h
+++ b/src/qml/jsruntime/qv4global_p.h
@@ -147,13 +147,15 @@ inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); }
#endif
QT_BEGIN_NAMESPACE
-namespace QQmlJS {
+
+namespace QV4 {
+
+namespace Compiler {
struct Module;
struct Context;
+ struct JSUnitGenerator;
}
-namespace QV4 {
-
namespace Heap {
struct Base;
struct MemberData;
@@ -188,14 +190,6 @@ namespace Heap {
template <typename T, size_t> struct Pointer;
}
-namespace IR {
-struct Function;
-struct Module;
-}
-namespace Compiler {
-struct JSUnitGenerator;
-}
-
class MemoryManager;
class ExecutableAllocator;
struct String;
diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp
index 68ed07b214..0370531579 100644
--- a/src/qml/jsruntime/qv4script.cpp
+++ b/src/qml/jsruntime/qv4script.cpp
@@ -80,16 +80,16 @@ void Script::parse()
if (parsed)
return;
- using namespace QQmlJS;
+ using namespace QV4::Compiler;
parsed = true;
ExecutionEngine *v4 = scope->engine();
Scope valueScope(v4);
- QQmlJS::Module module(v4->debugger() != 0);
+ Module module(v4->debugger() != 0);
- QQmlJS::Engine ee, *engine = &ee;
+ Engine ee, *engine = &ee;
Lexer lexer(engine);
lexer.setCode(sourceCode, line, parseAsBinding);
Parser parser(engine);
@@ -97,7 +97,7 @@ void Script::parse()
const bool parsed = parser.parseProgram();
const auto diagnosticMessages = parser.diagnosticMessages();
- for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
+ for (const DiagnosticMessage &m : diagnosticMessages) {
if (m.isError()) {
valueScope.engine->throwSyntaxError(m.message, sourceFile, m.loc.startLine, m.loc.startColumn);
return;
@@ -120,7 +120,7 @@ void Script::parse()
RuntimeCodegen cg(v4, &jsGenerator, strictMode);
if (inheritContext)
cg.setUseFastLookups(false);
- cg.generateFromProgram(sourceFile, sourceCode, program, &module, QQmlJS::EvalCode);
+ cg.generateFromProgram(sourceFile, sourceCode, program, &module, EvalCode);
if (v4->hasException)
return;
@@ -175,26 +175,26 @@ Function *Script::function()
return vmFunction;
}
-QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QQmlJS::Module *module, Compiler::JSUnitGenerator *unitGenerator,
+QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compiler::Module *module, Compiler::JSUnitGenerator *unitGenerator,
const QUrl &url, const QString &source, QList<QQmlError> *reportedErrors,
- QQmlJS::Directives *directivesCollector)
+ Directives *directivesCollector)
{
- using namespace QQmlJS;
+ using namespace QV4::Compiler;
using namespace QQmlJS::AST;
- QQmlJS::Engine ee;
+ Engine ee;
if (directivesCollector)
ee.setDirectives(directivesCollector);
- QQmlJS::Lexer lexer(&ee);
+ Lexer lexer(&ee);
lexer.setCode(source, /*line*/1, /*qml mode*/false);
- QQmlJS::Parser parser(&ee);
+ Parser parser(&ee);
parser.parseProgram();
QList<QQmlError> errors;
const auto diagnosticMessages = parser.diagnosticMessages();
- for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
+ for (const DiagnosticMessage &m : diagnosticMessages) {
if (m.isWarning()) {
qWarning("%s:%d : %s", qPrintable(url.toString()), m.loc.startLine, qPrintable(m.message));
continue;
@@ -221,9 +221,9 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QQmlJS::Mo
return 0;
}
- QQmlJS::Codegen cg(unitGenerator, /*strict mode*/false);
+ Codegen cg(unitGenerator, /*strict mode*/false);
cg.setUseFastLookups(false);
- cg.generateFromProgram(url.toString(), source, program, module, QQmlJS::EvalCode);
+ cg.generateFromProgram(url.toString(), source, program, module, EvalCode);
errors = cg.qmlErrors();
if (!errors.isEmpty()) {
if (reportedErrors)
diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h
index 760dad0060..75045dda9c 100644
--- a/src/qml/jsruntime/qv4script_p.h
+++ b/src/qml/jsruntime/qv4script_p.h
@@ -61,10 +61,6 @@ QT_BEGIN_NAMESPACE
class QQmlContextData;
-namespace QQmlJS {
-class Directives;
-}
-
namespace QV4 {
struct ContextStateSaver {
@@ -139,7 +135,7 @@ struct Q_QML_EXPORT Script {
Function *function();
- static QQmlRefPointer<CompiledData::CompilationUnit> precompile(QQmlJS::Module *module, Compiler::JSUnitGenerator *unitGenerator, const QUrl &url, const QString &source,
+ static QQmlRefPointer<CompiledData::CompilationUnit> precompile(QV4::Compiler::Module *module, Compiler::JSUnitGenerator *unitGenerator, const QUrl &url, const QString &source,
QList<QQmlError> *reportedErrors = 0, QQmlJS::Directives *directivesCollector = 0);
static ReturnedValue evaluate(ExecutionEngine *engine, const QString &script, QmlContext *qmlContext);
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index e4fb8874e8..0feedc19cd 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -2075,7 +2075,7 @@ bool QQmlTypeData::tryLoadFromDiskCache()
if (!v4)
return false;
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QQmlJS::Codegen::createUnitForLoading();
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading();
{
QString error;
if (!unit->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) {
@@ -2919,7 +2919,7 @@ struct EmptyCompilationUnit : public QV4::CompiledData::CompilationUnit
void QQmlScriptBlob::dataReceived(const SourceCodeData &data)
{
if (!disableDiskCache() || forceDiskCache()) {
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QQmlJS::Codegen::createUnitForLoading();
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading();
QString error;
if (unit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) {
initializeFromCompilationUnit(unit);
diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
index 015d06ab81..10a00a39e6 100644
--- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
+++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
@@ -172,7 +172,7 @@ struct TestCompiler
bool verify()
{
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QQmlJS::Codegen::createUnitForLoading();
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading();
return unit->loadFromDisk(QUrl::fromLocalFile(testFilePath), QFileInfo(testFilePath).lastModified(), &lastErrorString);
}
diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp
index 26297d3d9a..588dd14157 100644
--- a/tools/qmlcachegen/qmlcachegen.cpp
+++ b/tools/qmlcachegen/qmlcachegen.cpp
@@ -249,7 +249,7 @@ static bool compileJSFile(const QString &inputFileName, const QString &outputFil
&irDocument.jsModule, &irDocument.jsParserEngine,
irDocument.program, /*import cache*/0,
&irDocument.jsGenerator.stringTable);
- v4CodeGen.generateFromProgram(inputFileName, sourceCode, program, &irDocument.jsModule, QQmlJS::GlobalCode);
+ v4CodeGen.generateFromProgram(inputFileName, sourceCode, program, &irDocument.jsModule, QV4::Compiler::GlobalCode);
QList<QQmlJS::DiagnosticMessage> jsErrors = v4CodeGen.errors();
if (!jsErrors.isEmpty()) {
for (const QQmlJS::DiagnosticMessage &e: qAsConst(jsErrors)) {
diff --git a/tools/qmljs/qmljs.cpp b/tools/qmljs/qmljs.cpp
index e73dcd4dc6..2250a501e7 100644
--- a/tools/qmljs/qmljs.cpp
+++ b/tools/qmljs/qmljs.cpp
@@ -108,7 +108,7 @@ int main(int argc, char *argv[])
if (file.open(QFile::ReadOnly)) {
QScopedPointer<QV4::Script> script;
if (cache && QFile::exists(fn + QLatin1Char('c'))) {
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QQmlJS::Codegen::createUnitForLoading();
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading();
QString error;
if (unit->loadFromDisk(QUrl::fromLocalFile(fn), QFileInfo(fn).lastModified(), &error)) {
script.reset(new QV4::Script(&vm, nullptr, unit));