From 3a9f4d3ae701c7119016a0bf8b4e65ceb17864b0 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 30 Jun 2017 08:44:09 +0200 Subject: Get rid of IR::Module and IR::Function Fold the stuff from IR::Function into QQmlJS::Context, and add a QQmlJS::Module class to replace the last pieces of the old IR. Change-Id: Ic02a6738a4f1db67a0ddf97b6c93ca32be81789d Reviewed-by: Erik Verbruggen --- src/qml/compiler/qqmlirbuilder.cpp | 8 +- src/qml/compiler/qqmlirbuilder_p.h | 4 +- src/qml/compiler/qqmltypecompiler.cpp | 7 +- src/qml/compiler/qqmltypecompiler_p.h | 2 - src/qml/compiler/qv4codegen.cpp | 178 ++++++++++++++++---------------- src/qml/compiler/qv4codegen_p.h | 99 ++++++++++++++++-- src/qml/compiler/qv4compiler.cpp | 57 +++++----- src/qml/compiler/qv4compiler_p.h | 6 +- src/qml/jsruntime/qv4functionobject.cpp | 2 +- src/qml/jsruntime/qv4global_p.h | 4 + src/qml/jsruntime/qv4script.cpp | 4 +- src/qml/jsruntime/qv4script_p.h | 2 +- src/qml/qml/qqmltypeloader.cpp | 2 +- 13 files changed, 230 insertions(+), 145 deletions(-) diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 6631452ca1..c6c6634a8c 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1552,7 +1552,7 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, Binding } JSCodeGen::JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator, - QV4::IR::Module *jsModule, QQmlJS::Engine *jsEngine, + QQmlJS::Module *jsModule, QQmlJS::Engine *jsEngine, QQmlJS::AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports, const QV4::Compiler::StringTableGenerator *stringPool) : QQmlJS::Codegen(jsUnitGenerator, /*strict mode*/false) , sourceCode(sourceCode) @@ -1605,7 +1605,6 @@ QVector JSCodeGen::generateJSCodeForFunctionsAndBindings(const QListfunctions.at(defineFunction(QStringLiteral("context scope"), qmlRoot, 0, 0)); for (int i = 0; i < functions.count(); ++i) { const CompiledFunctionOrExpression &qmlFunction = functions.at(i); @@ -1647,7 +1646,6 @@ QVector JSCodeGen::generateJSCodeForFunctionsAndBindings(const QListisQmlBinding) - _function->idObjectDependencies.insert(mapping.idIndex); + if (_context->compilationMode == QQmlJS::QmlBinding) + _context->idObjectDependencies.insert(mapping.idIndex); Reference result = Reference::fromTemp(this); Instruction::LoadIdObject load; diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index b42ee39eca..80d0fdc306 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; - QV4::IR::Module jsModule; + QQmlJS::Module jsModule; QList imports; QList pragmas; QQmlJS::AST::UiProgram *program; @@ -583,7 +583,7 @@ struct Q_QML_EXPORT PropertyResolver struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QQmlJS::Codegen { - JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator, QV4::IR::Module *jsModule, + JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator, QQmlJS::Module *jsModule, QQmlJS::Engine *jsEngine, QQmlJS::AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports, const QV4::Compiler::StringTableGenerator *stringPool); diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 1f459f1d13..9a96dbcbfc 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -139,7 +139,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() sss.scan(); } - document->jsModule.setFileName(typeData->finalUrlString()); + document->jsModule.fileName = typeData->finalUrlString(); QmlIR::JSCodeGen v4CodeGenerator(document->code, &document->jsGenerator, &document->jsModule, &document->jsParserEngine, document->program, typeNameCache, &document->jsGenerator.stringTable); v4CodeGenerator.setUseFastLookups(false); // ### v4CodeGenerator.setUseTypeInference(true); @@ -209,11 +209,6 @@ int QQmlTypeCompiler::registerString(const QString &str) return document->jsGenerator.registerString(str); } -QV4::IR::Module *QQmlTypeCompiler::jsIRModule() const -{ - return &document->jsModule; -} - const QV4::CompiledData::Unit *QQmlTypeCompiler::qmlUnit() const { return document->javaScriptCompilationUnit->data; diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 76aa422fc5..a70ad77bfb 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -111,8 +111,6 @@ public: int registerString(const QString &str); - QV4::IR::Module *jsIRModule() const; - const QV4::CompiledData::Unit *qmlUnit() const; QUrl url() const { return typeData->finalUrl(); } diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index bc3880b385..524c83705c 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -104,15 +104,16 @@ struct ControlFlow { Codegen *cg; ControlFlow *parent; Type type; + bool needsLookupByName = false; ControlFlow(Codegen *cg, Type type) - : cg(cg), parent(cg->_controlFlow), type(type) + : cg(cg), parent(cg->_context->controlFlow), type(type) { - cg->_controlFlow = this; + cg->_context->controlFlow = this; } virtual ~ControlFlow() { - cg->_controlFlow = parent; + cg->_context->controlFlow = parent; } void jumpToHandler(const Handler &h) { @@ -290,6 +291,7 @@ struct ControlFlowWith : public ControlFlowUnwind ControlFlowWith(Codegen *cg) : ControlFlowUnwind(cg, With) { + needsLookupByName = true; generator()->setExceptionHandler(&unwindLabel); } @@ -340,7 +342,7 @@ struct ControlFlowCatch : public ControlFlowUnwind ~ControlFlowCatch() { // emit code for unwinding - ++cg->_function->insideWithOrCatch; + needsLookupByName = true; insideCatch = true; // exceptions inside the try block go here @@ -355,8 +357,8 @@ struct ControlFlowCatch : public ControlFlowUnwind cg->statement(catchExpression->statement); - --cg->_function->insideWithOrCatch; insideCatch = false; + needsLookupByName = false; // exceptions inside catch and break/return statements go here catchUnwindLabel.link(); @@ -495,7 +497,7 @@ void Codegen::ScanFunctions::operator()(Node *node) void Codegen::ScanFunctions::enterEnvironment(Node *node, CompilationMode compilationMode) { - Context *e = _cg->cgModule.newContext(node, _variableEnvironment, compilationMode); + Context *e = _cg->_module->newContext(node, _variableEnvironment, compilationMode); if (!e->isStrict) e->isStrict = _cg->_strictMode; _envStack.append(e); @@ -799,7 +801,6 @@ bool Codegen::ScanFunctions::visit(Block *ast) { void Codegen::ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr, bool isExpression) { - bool wasStrict = false; if (_variableEnvironment) { _variableEnvironment->hasNestedFunctions = true; // The identifier of a function expression cannot be referenced from the enclosing environment. @@ -807,7 +808,6 @@ void Codegen::ScanFunctions::enterFunction(Node *ast, const QString &name, Forma _variableEnvironment->enter(name, Context::FunctionDefinition, AST::VariableDeclaration::FunctionScope, expr); if (name == QLatin1String("arguments")) _variableEnvironment->usesArgumentsObject = Context::ArgumentsObjectNotUsed; - wasStrict = _variableEnvironment->isStrict; } enterEnvironment(ast, FunctionCode); @@ -816,14 +816,13 @@ void Codegen::ScanFunctions::enterFunction(Node *ast, const QString &name, Forma _variableEnvironment->isNamedFunctionExpression = isExpression && !name.isEmpty(); _variableEnvironment->formals = formals; - if (body) + if (body && !_variableEnvironment->isStrict) checkDirectivePrologue(body->elements); - if (wasStrict || _variableEnvironment->isStrict) { - QStringList args; - for (FormalParameterList *it = formals; it; it = it->next) { - QString arg = it->name.toString(); - if (args.contains(arg)) { + for (FormalParameterList *it = formals; it; it = it->next) { + QString arg = it->name.toString(); + if (_variableEnvironment->isStrict) { + if (_variableEnvironment->arguments.contains(arg)) { _cg->throwSyntaxError(it->identifierToken, QStringLiteral("Duplicate parameter name '%1' is not allowed in strict mode").arg(arg)); return; } @@ -831,18 +830,16 @@ void Codegen::ScanFunctions::enterFunction(Node *ast, const QString &name, Forma _cg->throwSyntaxError(it->identifierToken, QStringLiteral("'%1' cannot be used as parameter name in strict mode").arg(arg)); return; } - args += arg; } + _variableEnvironment->arguments += arg; } } Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict) : _module(0) - , _function(0) , _returnAddress(0) , _context(0) - , _controlFlow(0) , _labelledStatement(0) , jsUnitGenerator(jsUnitGenerator) , _strictMode(strict) @@ -854,7 +851,7 @@ Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict) void Codegen::generateFromProgram(const QString &fileName, const QString &sourceCode, Program *node, - QV4::IR::Module *module, + QQmlJS::Module *module, CompilationMode mode) { Q_ASSERT(node); @@ -862,22 +859,21 @@ void Codegen::generateFromProgram(const QString &fileName, _module = module; _context = 0; - _module->setFileName(fileName); + _module->fileName = fileName; ScanFunctions scan(this, sourceCode, mode); scan(node); defineFunction(QStringLiteral("%entry"), node, 0, node->elements); - cgModule = QQmlJS::Module(); } void Codegen::generateFromFunctionExpression(const QString &fileName, const QString &sourceCode, AST::FunctionExpression *ast, - QV4::IR::Module *module) + QQmlJS::Module *module) { _module = module; - _module->setFileName(fileName); + _module->fileName = fileName; _context = 0; ScanFunctions scan(this, sourceCode, GlobalCode); @@ -886,21 +882,21 @@ void Codegen::generateFromFunctionExpression(const QString &fileName, scan(ast); scan.leaveEnvironment(); - defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); - - cgModule = QQmlJS::Module(); + int index = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); + _module->rootContext = _module->functions.at(index); } void Codegen::enterContext(Node *node) { - _context = cgModule.contextMap.value(node); + _context = _module->contextMap.value(node); Q_ASSERT(_context); } void Codegen::leaveContext() { Q_ASSERT(_context); + Q_ASSERT(!_context->controlFlow); _context = _context->parent; } @@ -1902,35 +1898,33 @@ bool Codegen::visit(FunctionExpression *ast) Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs) { uint scope = 0; - Context *e = _context; - IR::Function *f = _function; + Context *c = _context; - while (f && e->parent) { - if (f->insideWithOrCatch || (f->isNamedExpression && QStringRef(f->name) == name)) + while (c->parent) { + if (c->forceLookupByName() || (c->isNamedFunctionExpression && c->name == name)) goto loadByName; - int index = e->findMember(name); - Q_ASSERT (index < e->members.size()); + int index = c->findMember(name); + Q_ASSERT (index < c->members.size()); if (index != -1) { Reference r = Reference::fromLocal(this, index, scope); if (name == QLatin1String("arguments") || name == QLatin1String("eval")) { r.isArgOrEval = true; - if (isLhs && f->isStrict) + if (isLhs && c->isStrict) // ### add correct source location throwSyntaxError(SourceLocation(), QStringLiteral("Variable name may not be eval or arguments in strict mode")); } return r; } - const int argIdx = f->indexOfArgument(QStringRef(&name)); + const int argIdx = c->findArgument(name); if (argIdx != -1) return Reference::fromArgument(this, argIdx, scope); - if (!e->isStrict && e->hasDirectEval) + if (!c->isStrict && c->hasDirectEval) goto loadByName; ++scope; - e = e->parent; - f = f->outer; + c = c->parent; } { @@ -1940,8 +1934,7 @@ Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs) return fallback; } - if (!e->parent && (!f || !f->insideWithOrCatch) && - _context->compilationMode != EvalCode && e->compilationMode != QmlBinding) { + if (!c->parent && !c->forceLookupByName() && _context->compilationMode != EvalCode && c->compilationMode != QmlBinding) { Reference r = Reference::fromName(this, name); r.global = true; return r; @@ -2397,25 +2390,17 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, AST::SourceElements *body) { - ControlFlow *loop = 0; - qSwap(_controlFlow, loop); + Q_UNUSED(formals); enterContext(ast); - IR::Function *function = _module->newFunction(name, _function); - int functionIndex = _module->functions.count() - 1; - function->hasDirectEval = _context->hasDirectEval || _context->compilationMode == EvalCode - || _module->debugMode; // Conditional breakpoints are like eval in the function - function->usesArgumentsObject = _context->parent && (_context->usesArgumentsObject == Context::ArgumentsObjectUsed); - function->usesThis = _context->usesThis; - function->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, (int)QV4::Global::ReservedArgumentCount); - function->isStrict = _context->isStrict; - function->isNamedExpression = _context->isNamedFunctionExpression; - function->isQmlBinding = _context->compilationMode == QmlBinding; + _context->name = name; + _module->functions.append(_context); + int functionIndex = _module->functions.count() - 1; - AST::SourceLocation loc = ast->firstSourceLocation(); - function->line = loc.startLine; - function->column = loc.startColumn; + _context->hasDirectEval |= _context->compilationMode == EvalCode || _module->debugMode; // Conditional breakpoints are like eval in the function + // ### still needed? + _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, (int)QV4::Global::ReservedArgumentCount); BytecodeGenerator bytecode; BytecodeGenerator *savedBytecodeGenerator; @@ -2423,7 +2408,10 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, bytecodeGenerator = &bytecode; unsigned returnAddress = bytecodeGenerator->newTemp(); - if (function->usesArgumentsObject) + + if (!_context->parent || _context->usesArgumentsObject == Context::ArgumentsObjectUnknown) + _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; + if (_context->usesArgumentsObject == Context::ArgumentsObjectUsed) _context->enter(QStringLiteral("arguments"), Context::VariableDeclaration, AST::VariableDeclaration::FunctionScope); // variables in global code are properties of the global context object, not locals as with other functions. @@ -2431,7 +2419,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, unsigned t = 0; for (Context::MemberMap::iterator it = _context->members.begin(), end = _context->members.end(); it != end; ++it) { const QString &local = it.key(); - function->LOCAL(local); + _context->locals.append(local); (*it).index = t; ++t; } @@ -2448,14 +2436,9 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, auto exitBlock = bytecodeGenerator->newLabel(); - qSwap(_function, function); qSwap(_exitBlock, exitBlock); qSwap(_returnAddress, returnAddress); - for (FormalParameterList *it = formals; it; it = it->next) { - _function->RECEIVE(it->name.toString()); - } - for (const Context::Member &member : qAsConst(_context->members)) { if (member.function) { const int function = defineFunction(member.function->name.toString(), member.function, member.function->formals, @@ -2471,12 +2454,12 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, } } } - if (_function->usesArgumentsObject) { + if (_context->usesArgumentsObject == Context::ArgumentsObjectUsed) { Instruction::CallBuiltinSetupArgumentsObject setup; setup.result = referenceForName(QStringLiteral("arguments"), false).asLValue(); bytecodeGenerator->addInstruction(setup); } - if (_function->usesThis && !_context->isStrict) { + if (_context->usesThis && !_context->isStrict) { // make sure we convert this to an object Instruction::CallBuiltinConvertThisToObject convert; bytecodeGenerator->addInstruction(convert); @@ -2495,18 +2478,16 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, bytecodeGenerator->addInstruction(ret); } - _function->code = bytecodeGenerator->finalize(); + _context->code = bytecodeGenerator->finalize(); static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE"); if (showCode) { - qDebug() << "=== Bytecode for" << *_function->name; - QV4::Moth::dumpBytecode(_function->code); + qDebug() << "=== Bytecode for" << _context->name; + QV4::Moth::dumpBytecode(_context->code); qDebug(); } - qSwap(_function, function); qSwap(_exitBlock, exitBlock); qSwap(_returnAddress, returnAddress); - qSwap(_controlFlow, loop); leaveContext(); @@ -2551,12 +2532,12 @@ bool Codegen::visit(BreakStatement *ast) if (hasError) return false; - if (!_controlFlow) { + if (!_context->controlFlow) { throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop")); return false; } - ControlFlow::Handler h = _controlFlow->getHandler(ControlFlow::Break, ast->label.toString()); + ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Break, ast->label.toString()); if (h.type == ControlFlow::Invalid) { if (ast->label.isEmpty()) throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop")); @@ -2565,7 +2546,7 @@ bool Codegen::visit(BreakStatement *ast) return false; } - _controlFlow->jumpToHandler(h); + _context->controlFlow->jumpToHandler(h); return false; } @@ -2577,12 +2558,12 @@ bool Codegen::visit(ContinueStatement *ast) TempScope scope(this); - if (!_controlFlow) { + if (!_context->controlFlow) { throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Continue outside of loop")); return false; } - ControlFlow::Handler h = _controlFlow->getHandler(ControlFlow::Continue, ast->label.toString()); + ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Continue, ast->label.toString()); if (h.type == ControlFlow::Invalid) { if (ast->label.isEmpty()) throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Undefined label '%1'").arg(ast->label.toString())); @@ -2591,7 +2572,7 @@ bool Codegen::visit(ContinueStatement *ast) return false; } - _controlFlow->jumpToHandler(h); + _context->controlFlow->jumpToHandler(h); return false; } @@ -2762,7 +2743,7 @@ bool Codegen::visit(LabelledStatement *ast) TempScope scope(this); // check that no outer loop contains the label - ControlFlow *l = _controlFlow; + ControlFlow *l = _context->controlFlow; while (l) { if (l->label() == ast->label) { QString error = QString(QStringLiteral("Label '%1' has already been declared")).arg(ast->label.toString()); @@ -2881,9 +2862,9 @@ bool Codegen::visit(ReturnStatement *ast) Reference::fromTemp(this, _returnAddress).storeConsume(expr); } - if (_controlFlow) { - ControlFlow::Handler h = _controlFlow->getHandler(ControlFlow::Return); - _controlFlow->jumpToHandler(h); + if (_context->controlFlow) { + ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Return); + _context->controlFlow->jumpToHandler(h); } else { bytecodeGenerator->jump().link(_exitBlock); } @@ -2971,8 +2952,8 @@ bool Codegen::visit(ThrowStatement *ast) Reference expr = expression(ast->expression); - if (_controlFlow) { - _controlFlow->handleThrow(expr); + if (_context->controlFlow) { + _context->controlFlow->handleThrow(expr); } else { Instruction::CallBuiltinThrow instr; instr.arg = expr.asRValue(); @@ -3021,7 +3002,7 @@ bool Codegen::visit(TryStatement *ast) TempScope scope(this); - _function->hasTry = true; + _context->hasTry = true; if (ast->finallyExpression && ast->finallyExpression->statement) { handleTryFinally(ast); @@ -3069,7 +3050,7 @@ bool Codegen::visit(WithStatement *ast) TempScope scope(this); - _function->hasWith = true; + _context->hasWith = true; Reference src = expression(ast->expression); if (hasError) @@ -3077,14 +3058,12 @@ bool Codegen::visit(WithStatement *ast) src.asRValue(); // trigger load before we setup the exception handler, so exceptions here go to the right place ControlFlowWith flow(this); - ++_function->insideWithOrCatch; Instruction::CallBuiltinPushScope pushScope; pushScope.arg = src.asRValue(); bytecodeGenerator->addInstruction(pushScope); statement(ast->statement); - --_function->insideWithOrCatch; return false; } @@ -3177,7 +3156,7 @@ QQmlRefPointer Codegen::generateCompilationUnit(b Moth::CompilationUnit *compilationUnit = new Moth::CompilationUnit; compilationUnit->codeRefs.resize(_module->functions.size()); int i = 0; - for (IR::Function *irFunction : qAsConst(_module->functions)) + for (QQmlJS::Context *irFunction : qAsConst(_module->functions)) compilationUnit->codeRefs[i++] = irFunction->code; if (generateUnitData) @@ -3517,14 +3496,14 @@ void Codegen::Reference::load(uint tmp) const load.propertyIndex = qmlCoreIndex; load.result = temp; codegen->bytecodeGenerator->addInstruction(load); - codegen->_function->scopeObjectPropertyDependencies.insert(qmlCoreIndex, qmlNotifyIndex); + codegen->_context->scopeObjectPropertyDependencies.insert(qmlCoreIndex, qmlNotifyIndex); } else if (type == QmlContextObject) { Instruction::LoadContextObjectProperty load; load.base = base; load.propertyIndex = qmlCoreIndex; load.result = temp; codegen->bytecodeGenerator->addInstruction(load); - codegen->_function->contextObjectPropertyDependencies.insert(qmlCoreIndex, qmlNotifyIndex); + codegen->_context->contextObjectPropertyDependencies.insert(qmlCoreIndex, qmlNotifyIndex); } else if (type == This) { Instruction::LoadThis load; load.result = temp; @@ -3538,6 +3517,31 @@ 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 bac74149d4..c863159189 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -97,7 +97,9 @@ enum CompilationMode { struct Context; struct Module { - Module() {} + Module(bool debugMode) + : debugMode(debugMode) + {} ~Module() { qDeleteAll(contextMap); } @@ -105,11 +107,21 @@ struct Module { 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, @@ -130,12 +142,21 @@ struct Context { MemberMap members; AST::FormalParameterList *formals; + QStringList arguments; + QStringList locals; + QVector nestedContexts; + + QV4::ControlFlow *controlFlow = 0; + QByteArray code; + int maxNumberOfArguments; bool hasDirectEval; bool hasNestedFunctions; bool isStrict; bool isNamedFunctionExpression; bool usesThis; + bool hasTry = false; + bool hasWith = false; enum UsesArgumentsObject { ArgumentsObjectUnknown, ArgumentsObjectNotUsed, @@ -146,6 +167,53 @@ struct Context { 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) , formals(0) @@ -162,6 +230,26 @@ struct Context { 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) const + { + // search backwards to handle duplicate argument names correctly + for (int i = arguments.size() - 1; i >= 0; --i) { + if (arguments.at(i) == name) + return i; + } + return -1; + } + int findMember(const QString &name) const { MemberMap::const_iterator it = members.find(name); @@ -238,12 +326,12 @@ public: void generateFromProgram(const QString &fileName, const QString &sourceCode, AST::Program *ast, - QV4::IR::Module *module, + QQmlJS::Module *module, CompilationMode mode = GlobalCode); void generateFromFunctionExpression(const QString &fileName, const QString &sourceCode, AST::FunctionExpression *ast, - QV4::IR::Module *module); + QQmlJS::Module *module); public: struct Reference { @@ -647,13 +735,10 @@ protected: friend struct QV4::ControlFlowCatch; friend struct QV4::ControlFlowFinally; Result _expr; - QV4::IR::Module *_module; - QV4::IR::Function *_function; + Module *_module; BytecodeGenerator::Label _exitBlock; unsigned _returnAddress; Context *_context; - QV4::ControlFlow *_controlFlow; - Module cgModule; AST::LabelledStatement *_labelledStatement; QV4::Compiler::JSUnitGenerator *jsUnitGenerator; BytecodeGenerator *bytecodeGenerator = 0; diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 619993b874..2985e39765 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -39,6 +39,7 @@ #include #include +#include #include #include #include @@ -99,8 +100,8 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit) } } -QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QV4::IR::Module *module) - : irModule(module) +QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QQmlJS::Module *module) + : module(module) { // Make sure the empty string always gets index 0 registerString(QString()); @@ -256,16 +257,16 @@ int QV4::Compiler::JSUnitGenerator::registerJSClass(int count, CompiledData::JSC QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorOption option) { - registerString(irModule->fileName); - for (QV4::IR::Function *f : qAsConst(irModule->functions)) { - registerString(*f->name); - for (int i = 0; i < f->formals.size(); ++i) - registerString(*f->formals.at(i)); + registerString(module->fileName); + for (QQmlJS::Context *f : qAsConst(module->functions)) { + registerString(f->name); + for (int i = 0; i < f->arguments.size(); ++i) + registerString(f->arguments.at(i)); for (int i = 0; i < f->locals.size(); ++i) - registerString(*f->locals.at(i)); + registerString(f->locals.at(i)); } - Q_ALLOCA_VAR(CompiledData::LEUInt32, functionOffsets, irModule->functions.size() * sizeof(CompiledData::LEUInt32)); + Q_ALLOCA_VAR(CompiledData::LEUInt32, functionOffsets, module->functions.size() * sizeof(CompiledData::LEUInt32)); uint jsClassDataOffset = 0; char *dataPtr; @@ -280,9 +281,9 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO memcpy(dataPtr + unit->offsetToFunctionTable, functionOffsets, unit->functionTableSize * sizeof(CompiledData::LEUInt32)); - for (int i = 0; i < irModule->functions.size(); ++i) { - QV4::IR::Function *function = irModule->functions.at(i); - if (function == irModule->rootFunction) + for (int i = 0; i < module->functions.size(); ++i) { + QQmlJS::Context *function = module->functions.at(i); + if (function == module->rootContext) unit->indexOfRootFunction = i; writeFunction(dataPtr + functionOffsets[i], function); @@ -322,14 +323,14 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO return unit; } -void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *irFunction) const +void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QQmlJS::Context *irFunction) const { QV4::CompiledData::Function *function = (QV4::CompiledData::Function *)f; quint32 currentOffset = sizeof(QV4::CompiledData::Function); currentOffset = (currentOffset + 7) & ~quint32(0x7); - function->nameIndex = getStringId(*irFunction->name); + function->nameIndex = getStringId(irFunction->name); function->flags = 0; if (irFunction->hasDirectEval) function->flags |= CompiledData::Function::HasDirectEval; @@ -337,13 +338,13 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *i function->flags |= CompiledData::Function::UsesArgumentsObject; if (irFunction->isStrict) function->flags |= CompiledData::Function::IsStrict; - if (irFunction->isNamedExpression) + if (irFunction->isNamedFunctionExpression) function->flags |= CompiledData::Function::IsNamedExpression; if (irFunction->hasTry || irFunction->hasWith) function->flags |= CompiledData::Function::HasCatchOrWith; if (irFunction->canUseSimpleCall()) function->flags |= CompiledData::Function::CanUseSimpleCall; - function->nFormals = irFunction->formals.size(); + function->nFormals = irFunction->arguments.size(); function->formalsOffset = currentOffset; currentOffset += function->nFormals * sizeof(quint32); @@ -351,7 +352,7 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *i function->localsOffset = currentOffset; currentOffset += function->nLocals * sizeof(quint32); - function->nInnerFunctions = irFunction->nestedFunctions.size(); + function->nInnerFunctions = irFunction->nestedContexts.size(); function->nDependingIdObjects = 0; function->nDependingContextProperties = 0; @@ -383,13 +384,13 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *i // write formals quint32 *formals = (quint32 *)(f + function->formalsOffset); - for (int i = 0; i < irFunction->formals.size(); ++i) - formals[i] = getStringId(*irFunction->formals.at(i)); + for (int i = 0; i < irFunction->arguments.size(); ++i) + formals[i] = getStringId(irFunction->arguments.at(i)); // write locals quint32 *locals = (quint32 *)(f + function->localsOffset); for (int i = 0; i < irFunction->locals.size(); ++i) - locals[i] = getStringId(*irFunction->locals.at(i)); + locals[i] = getStringId(irFunction->locals.at(i)); // write QML dependencies quint32 *writtenDeps = (quint32 *)(f + function->dependingIdObjectsOffset); @@ -417,17 +418,17 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp memset(&unit, 0, sizeof(unit)); memcpy(unit.magic, CompiledData::magic_str, sizeof(unit.magic)); unit.flags = QV4::CompiledData::Unit::IsJavascript; - unit.flags |= irModule->unitFlags; + unit.flags |= module->unitFlags; unit.version = QV4_DATA_STRUCTURE_VERSION; unit.qtVersion = QT_VERSION; memset(unit.md5Checksum, 0, sizeof(unit.md5Checksum)); - unit.architectureIndex = registerString(irModule->targetABI.isEmpty() ? QSysInfo::buildAbi() : irModule->targetABI); + unit.architectureIndex = registerString(module->targetABI.isEmpty() ? QSysInfo::buildAbi() : module->targetABI); unit.codeGeneratorIndex = registerString(codeGeneratorName); memset(unit.dependencyMD5Checksum, 0, sizeof(unit.dependencyMD5Checksum)); quint32 nextOffset = sizeof(CompiledData::Unit); - unit.functionTableSize = irModule->functions.size(); + unit.functionTableSize = module->functions.size(); unit.offsetToFunctionTable = nextOffset; nextOffset += unit.functionTableSize * sizeof(uint); @@ -453,13 +454,13 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp *jsClassDataOffset = nextOffset; nextOffset += jsClassData.size(); - for (int i = 0; i < irModule->functions.size(); ++i) { - QV4::IR::Function *f = irModule->functions.at(i); + for (int i = 0; i < module->functions.size(); ++i) { + QQmlJS::Context *f = module->functions.at(i); functionOffsets[i] = nextOffset; const int qmlIdDepsCount = f->idObjectDependencies.count(); const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count(); - nextOffset += QV4::CompiledData::Function::calculateSize(f->formals.size(), f->locals.size(), f->nestedFunctions.size(), qmlIdDepsCount, qmlPropertyDepsCount); + nextOffset += QV4::CompiledData::Function::calculateSize(f->arguments.size(), f->locals.size(), f->nestedContexts.size(), qmlIdDepsCount, qmlPropertyDepsCount); } if (option == GenerateWithStringTable) { @@ -471,8 +472,8 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.offsetToStringTable = 0; } unit.indexOfRootFunction = -1; - unit.sourceFileIndex = getStringId(irModule->fileName); - unit.sourceTimeStamp = irModule->sourceTimeStamp.isValid() ? irModule->sourceTimeStamp.toMSecsSinceEpoch() : 0; + unit.sourceFileIndex = getStringId(module->fileName); + unit.sourceTimeStamp = module->sourceTimeStamp.isValid() ? module->sourceTimeStamp.toMSecsSinceEpoch() : 0; unit.nImports = 0; unit.offsetToImports = 0; unit.nObjects = 0; diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index 222b97ceb6..0ed03a4c2c 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -95,7 +95,7 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { bool isAccessor; }; - JSUnitGenerator(IR::Module *module); + JSUnitGenerator(QQmlJS::Module *module); int registerString(const QString &str) { return stringTable.registerString(str); } int getStringId(const QString &string) const { return stringTable.getStringId(string); } @@ -126,14 +126,14 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { QV4::CompiledData::Unit *generateUnit(GeneratorOption option = GenerateWithStringTable); // Returns bytes written - void writeFunction(char *f, IR::Function *irFunction) const; + void writeFunction(char *f, QQmlJS::Context *irFunction) const; StringTableGenerator stringTable; QString codeGeneratorName; private: CompiledData::Unit generateHeader(GeneratorOption option, QJsonPrivate::q_littleendian *functionOffsets, uint *jsClassDataOffset); - IR::Module *irModule; + QQmlJS::Module *module; QList lookups; QVector regexps; diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 17f92f5f7c..992701d0fb 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -217,7 +217,7 @@ void FunctionCtor::construct(const Managed *that, Scope &scope, CallData *callDa return; } - IR::Module module(scope.engine->debugger() != 0); + QQmlJS::Module module(scope.engine->debugger() != 0); Compiler::JSUnitGenerator jsGenerator(&module); QQmlJS::RuntimeCodegen cg(scope.engine, &jsGenerator, f->strictMode()); diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index b5b077124d..ab7ee17e68 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -147,6 +147,10 @@ inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); } #endif QT_BEGIN_NAMESPACE +namespace QQmlJS { + struct Module; + struct Context; +} namespace QV4 { diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 7ea16020a5..13f84ccd0e 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -88,7 +88,7 @@ void Script::parse() ExecutionEngine *v4 = scope->engine(); Scope valueScope(v4); - IR::Module module(v4->debugger() != 0); + QQmlJS::Module module(v4->debugger() != 0); QQmlJS::Engine ee, *engine = ⅇ Lexer lexer(engine); @@ -176,7 +176,7 @@ Function *Script::function() return vmFunction; } -QQmlRefPointer Script::precompile(IR::Module *module, Compiler::JSUnitGenerator *unitGenerator, +QQmlRefPointer Script::precompile(QQmlJS::Module *module, Compiler::JSUnitGenerator *unitGenerator, const QUrl &url, const QString &source, QList *reportedErrors, QQmlJS::Directives *directivesCollector) { diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index 25a9dbe3a1..760dad0060 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -139,7 +139,7 @@ struct Q_QML_EXPORT Script { Function *function(); - static QQmlRefPointer precompile(IR::Module *module, Compiler::JSUnitGenerator *unitGenerator, const QUrl &url, const QString &source, + static QQmlRefPointer precompile(QQmlJS::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 5917d96293..e4fb8874e8 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2442,7 +2442,7 @@ void QQmlTypeData::restoreIR(QQmlRefPointer m_document.reset(new QmlIR::Document(isDebugging())); QmlIR::IRLoader loader(unit->data, m_document.data()); loader.load(); - m_document->jsModule.setFileName(finalUrlString()); + m_document->jsModule.fileName = finalUrlString(); m_document->javaScriptCompilationUnit = unit; continueLoadFromIR(); } -- cgit v1.2.3