From bac9b54dfb38767a34410fa55f8f46e64b458efb Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 30 Jun 2017 15:34:12 +0200 Subject: Split up qv4codegen into several files And changed the namespace of those classes to QV4::Compiler. ScanFunctions should over time also move into its own file. Change-Id: If084acea4a9a20b9c79ad47dac19e02dc720e098 Reviewed-by: Simon Hausmann --- src/qml/compiler/compiler.pri | 3 + src/qml/compiler/qqmlirbuilder.cpp | 14 +- src/qml/compiler/qqmlirbuilder_p.h | 6 +- src/qml/compiler/qv4codegen.cpp | 403 +------------------------- src/qml/compiler/qv4codegen_p.h | 239 +--------------- src/qml/compiler/qv4compiler.cpp | 10 +- src/qml/compiler/qv4compiler_p.h | 6 +- src/qml/compiler/qv4compilercontext.cpp | 82 ++++++ src/qml/compiler/qv4compilercontext_p.h | 286 +++++++++++++++++++ src/qml/compiler/qv4compilercontrolflow_p.h | 421 ++++++++++++++++++++++++++++ src/qml/jsruntime/qv4functionobject.cpp | 7 +- src/qml/jsruntime/qv4global_p.h | 16 +- src/qml/jsruntime/qv4script.cpp | 28 +- src/qml/jsruntime/qv4script_p.h | 6 +- src/qml/qml/qqmltypeloader.cpp | 4 +- 15 files changed, 857 insertions(+), 674 deletions(-) create mode 100644 src/qml/compiler/qv4compilercontext.cpp create mode 100644 src/qml/compiler/qv4compilercontext_p.h create mode 100644 src/qml/compiler/qv4compilercontrolflow_p.h (limited to 'src/qml') 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 JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList 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 JSCodeGen::generateJSCodeForFunctionsAndBindings(const QListbody : 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 imports; QList 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 #include #include +#include +#include #include #include @@ -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 handlers; - - ControlFlowUnwind(Codegen *cg, Type type) - : ControlFlow(cg, type), unwindLabel(generator()->newExceptionHandler()) - { - Q_ASSERT(type != Loop); - controlFlowTemp = static_cast(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 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 #include #include +#include #include #include #include @@ -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 contextMap; - QList 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 MemberMap; - - MemberMap members; - AST::FormalParameterList *formals = 0; - QStringList arguments; - QStringList locals; - QVector 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 - class SmallSet: public QVarLengthArray - { - 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 - { - 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 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 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 *functionOffsets, uint *jsClassDataOffset); - QQmlJS::Module *module; + Module *module; QList lookups; QVector 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 +#include +#include +#include +#include + +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 contextMap; + QList 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 MemberMap; + + MemberMap members; + QQmlJS::AST::FormalParameterList *formals = 0; + QStringList arguments; + QStringList locals; + QVector 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 + class SmallSet: public QVarLengthArray + { + 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 + { + 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 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 +#include +#include + +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 handlers; + + ControlFlowUnwind(Codegen *cg, Type type) + : ControlFlow(cg, type), unwindLabel(generator()->newExceptionHandler()) + { + Q_ASSERT(type != Loop); + controlFlowTemp = static_cast(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(parser.rootNode()); + QQmlJS::AST::FunctionExpression *fe = QQmlJS::AST::cast(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 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 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 = ⅇ + Engine ee, *engine = ⅇ 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 Script::precompile(QQmlJS::Module *module, Compiler::JSUnitGenerator *unitGenerator, +QQmlRefPointer Script::precompile(QV4::Compiler::Module *module, Compiler::JSUnitGenerator *unitGenerator, const QUrl &url, const QString &source, QList *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 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 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 precompile(QQmlJS::Module *module, Compiler::JSUnitGenerator *unitGenerator, const QUrl &url, const QString &source, + static QQmlRefPointer precompile(QV4::Compiler::Module *module, Compiler::JSUnitGenerator *unitGenerator, const QUrl &url, const QString &source, QList *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 unit = QQmlJS::Codegen::createUnitForLoading(); + QQmlRefPointer 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 unit = QQmlJS::Codegen::createUnitForLoading(); + QQmlRefPointer unit = QV4::Compiler::Codegen::createUnitForLoading(); QString error; if (unit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) { initializeFromCompilationUnit(unit); -- cgit v1.2.3