diff options
author | Simon Hausmann <simon.hausmann@qt.io> | 2017-11-21 12:29:40 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@qt.io> | 2017-11-21 12:29:40 +0100 |
commit | d373d5e7d70e968cfba8957596ed6fe4f46990c8 (patch) | |
tree | c52bf2b0fbbfdb13d644b4050aa7a931ef4b7109 /src/qml | |
parent | 9880acb424fd814501ba5fc4ae1caa989e23fafa (diff) | |
parent | 9af8a47746b69b6040fc149c1d24602a1e25b08f (diff) |
Merge remote-tracking branch 'origin/wip/new-backend' into dev
Conflicts:
src/qml/compiler/qv4isel_moth.cpp
src/qml/compiler/qv4jsir_p.h
src/qml/jsruntime/qv4engine_p.h
src/qml/jsruntime/qv4vme_moth.cpp
tests/auto/qml/qml.pro
Change-Id: Ia7b6ec24c7fcbcbb1786d9e798d2df294020ae37
Diffstat (limited to 'src/qml')
162 files changed, 15585 insertions, 29694 deletions
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index d9b985e33b..0d63d3b76f 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -2,26 +2,24 @@ INCLUDEPATH += $$PWD INCLUDEPATH += $$OUT_PWD HEADERS += \ + $$PWD/qv4bytecodegenerator_p.h \ $$PWD/qv4compileddata_p.h \ $$PWD/qv4compiler_p.h \ + $$PWD/qv4compilercontext_p.h \ + $$PWD/qv4compilercontrolflow_p.h \ + $$PWD/qv4compilerscanfunctions_p.h \ $$PWD/qv4codegen_p.h \ - $$PWD/qv4isel_p.h \ - $$PWD/qv4jsir_p.h \ - $$PWD/qv4isel_util_p.h \ - $$PWD/qv4ssa_p.h \ $$PWD/qqmlirbuilder_p.h \ - $$PWD/qqmltypecompiler_p.h \ - $$PWD/qv4jssimplifier_p.h + $$PWD/qqmltypecompiler_p.h SOURCES += \ + $$PWD/qv4bytecodegenerator.cpp \ $$PWD/qv4compileddata.cpp \ $$PWD/qv4compiler.cpp \ + $$PWD/qv4compilercontext.cpp \ + $$PWD/qv4compilerscanfunctions.cpp \ $$PWD/qv4codegen.cpp \ - $$PWD/qv4isel_p.cpp \ - $$PWD/qv4jsir.cpp \ - $$PWD/qv4ssa.cpp \ - $$PWD/qqmlirbuilder.cpp \ - $$PWD/qv4jssimplifier.cpp + $$PWD/qqmlirbuilder.cpp !qmldevtools_build { @@ -46,11 +44,9 @@ qtConfig(private_tests):qtConfig(dlopen): QMAKE_USE_PRIVATE += libdl qmldevtools_build|qtConfig(qml-interpreter) { HEADERS += \ - $$PWD/qv4instr_moth_p.h \ - $$PWD/qv4isel_moth_p.h + $$PWD/qv4instr_moth_p.h SOURCES += \ - $$PWD/qv4instr_moth.cpp \ - $$PWD/qv4isel_moth.cpp + $$PWD/qv4instr_moth.cpp } gcc { diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 02ff8947c4..128eb2c720 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -43,6 +43,7 @@ #include <private/qv4compileddata_p.h> #include <private/qqmljsparser_p.h> #include <private/qqmljslexer_p.h> +#include <private/qv4compilerscanfunctions_p.h> #include <QCoreApplication> #include <QCryptographicHash> #include <cmath> @@ -61,7 +62,7 @@ QT_USE_NAMESPACE static const quint32 emptyStringIndex = 0; -#ifndef V4_BOOTSTRAP +#if 0 //ndef V4_BOOTSTRAP DEFINE_BOOL_CONFIG_OPTION(lookupHints, QML_LOOKUP_HINTS); #endif // V4_BOOTSTRAP @@ -1067,15 +1068,18 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST } else if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(expr)) { binding->type = QV4::CompiledData::Binding::Type_Number; binding->setNumberValueInternal(lit->value); + } else if (QQmlJS::AST::CallExpression *call = QQmlJS::AST::cast<QQmlJS::AST::CallExpression *>(expr)) { + if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base)) { + tryGeneratingTranslationBinding(base->name, call->arguments, binding); + // If it wasn't a translation binding, a normal script binding will be generated + // below. + } } else if (QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression *>(expr)) { binding->flags |= QV4::CompiledData::Binding::IsFunctionExpression; - } else { - - if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) { - if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression)) { - binding->type = QV4::CompiledData::Binding::Type_Number; - binding->setNumberValueInternal(-lit->value); - } + } else if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) { + if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression)) { + binding->type = QV4::CompiledData::Binding::Type_Number; + binding->setNumberValueInternal(-lit->value); } } } @@ -1097,6 +1101,121 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST } } +void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::ArgumentList *args, QV4::CompiledData::Binding *binding) +{ + if (base == QLatin1String("qsTr")) { + QV4::CompiledData::TranslationData translationData; + translationData.number = -1; + translationData.commentIndex = 0; // empty string + + if (!args || !args->expression) + return; // no arguments, stop + + QStringRef translation; + if (QQmlJS::AST::StringLiteral *arg1 = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression)) { + translation = arg1->value; + } else { + return; // first argument is not a string, stop + } + + args = args->next; + + if (args) { + QQmlJS::AST::StringLiteral *arg2 = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression); + if (!arg2) + return; // second argument is not a string, stop + translationData.commentIndex = jsGenerator->registerString(arg2->value.toString()); + + args = args->next; + if (args) { + if (QQmlJS::AST::NumericLiteral *arg3 = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(args->expression)) { + translationData.number = int(arg3->value); + args = args->next; + } else { + return; // third argument is not a translation number, stop + } + } + } + + if (args) + return; // too many arguments, stop + + binding->type = QV4::CompiledData::Binding::Type_Translation; + binding->stringIndex = jsGenerator->registerString(translation.toString()); + binding->value.translationData = translationData; + } else if (base == QLatin1String("qsTrId")) { + QV4::CompiledData::TranslationData translationData; + translationData.number = -1; + translationData.commentIndex = 0; // empty string, but unused + + if (!args || !args->expression) + return; // no arguments, stop + + QStringRef id; + if (QQmlJS::AST::StringLiteral *arg1 = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression)) { + id = arg1->value; + } else { + return; // first argument is not a string, stop + } + + args = args->next; + + if (args) { + if (QQmlJS::AST::NumericLiteral *arg3 = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(args->expression)) { + translationData.number = int(arg3->value); + args = args->next; + } else { + return; // third argument is not a translation number, stop + } + } + + if (args) + return; // too many arguments, stop + + binding->type = QV4::CompiledData::Binding::Type_TranslationById; + binding->stringIndex = jsGenerator->registerString(id.toString()); + binding->value.translationData = translationData; + } else if (base == QLatin1String("QT_TR_NOOP") || base == QLatin1String("QT_TRID_NOOP")) { + if (!args || !args->expression) + return; // no arguments, stop + + QStringRef str; + if (QQmlJS::AST::StringLiteral *arg1 = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression)) { + str = arg1->value; + } else { + return; // first argument is not a string, stop + } + + args = args->next; + if (args) + return; // too many arguments, stop + + binding->type = QV4::CompiledData::Binding::Type_String; + binding->stringIndex = jsGenerator->registerString(str.toString()); + } else if (base == QLatin1String("QT_TRANSLATE_NOOP")) { + if (!args || !args->expression) + return; // no arguments, stop + + args = args->next; + if (!args || !args->expression) + return; // no second arguments, stop + + QStringRef str; + if (QQmlJS::AST::StringLiteral *arg2 = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression)) { + str = arg2->value; + } else { + return; // first argument is not a string, stop + } + + args = args->next; + if (args) + return; // too many arguments, stop + + binding->type = QV4::CompiledData::Binding::Type_String; + binding->stringIndex = jsGenerator->registerString(str.toString()); + } +} + void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value) { const QQmlJS::AST::SourceLocation qualifiedNameLocation = name->identifierToken; @@ -1649,9 +1768,10 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, Binding return bindingPtr; } -JSCodeGen::JSCodeGen(const QString &fileName, const QString &sourceCode, QV4::IR::Module *jsModule, QQmlJS::Engine *jsEngine, +JSCodeGen::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) - : QQmlJS::Codegen(/*strict mode*/false) + : QV4::Compiler::Codegen(jsUnitGenerator, /*strict mode*/false) , sourceCode(sourceCode) , jsEngine(jsEngine) , qmlRoot(qmlRoot) @@ -1660,11 +1780,10 @@ JSCodeGen::JSCodeGen(const QString &fileName, const QString &sourceCode, QV4::IR , _disableAcceleratedLookups(false) , _contextObject(0) , _scopeObject(0) - , _qmlContextTemp(-1) - , _importedScriptsTemp(-1) + , _qmlContextSlot(-1) + , _importedScriptsSlot(-1) { _module = jsModule; - _module->setFileName(fileName); _fileNameIsUrl = true; } @@ -1684,8 +1803,8 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil { QVector<int> runtimeFunctionIndices(functions.size()); - ScanFunctions scan(this, sourceCode, GlobalCode); - scan.enterEnvironment(0, QmlBinding); + QV4::Compiler::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); @@ -1694,7 +1813,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil if (function) scan.enterQmlFunction(function); else - scan.enterEnvironment(f.node, QmlBinding); + scan.enterEnvironment(f.node, QV4::Compiler::QmlBinding); scan(function ? function->body : f.node); scan.leaveEnvironment(); @@ -1702,8 +1821,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil scan.leaveEnvironment(); scan.leaveEnvironment(); - _variableEnvironment = 0; - _function = _module->functions.at(defineFunction(QStringLiteral("context scope"), qmlRoot, 0, 0)); + _context = 0; for (int i = 0; i < functions.count(); ++i) { const CompiledFunctionOrExpression &qmlFunction = functions.at(i); @@ -1745,35 +1863,36 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil runtimeFunctionIndices[i] = idx; } - qDeleteAll(_envMap); - _envMap.clear(); return runtimeFunctionIndices; } +int JSCodeGen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, AST::SourceElements *body) +{ + int qmlContextTemp = -1; + int importedScriptsTemp = -1; + qSwap(_qmlContextSlot, qmlContextTemp); + qSwap(_importedScriptsSlot, importedScriptsTemp); + + int result = Codegen::defineFunction(name, ast, formals, body); + + qSwap(_importedScriptsSlot, importedScriptsTemp); + qSwap(_qmlContextSlot, qmlContextTemp); + + return result; +} + #ifndef V4_BOOTSTRAP -QQmlPropertyData *JSCodeGen::lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name, bool *propertyExistsButForceNameLookup) +QQmlPropertyData *JSCodeGen::lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name) { - if (propertyExistsButForceNameLookup) - *propertyExistsButForceNameLookup = false; QQmlPropertyData *pd = cache->property(name, /*object*/0, /*context*/0); // Q_INVOKABLEs can't be FINAL, so we have to look them up at run-time - if (pd && pd->isFunction()) { - if (propertyExistsButForceNameLookup) - *propertyExistsButForceNameLookup = true; - pd = 0; - } + if (!pd || pd->isFunction()) + return 0; - if (pd && !cache->isAllowedInRevision(pd)) - pd = 0; + if (!cache->isAllowedInRevision(pd)) + return 0; - // Return a copy allocated from our memory pool. Property data pointers can change - // otherwise when the QQmlPropertyCache changes later in the QML type compilation process. - if (pd) { - QQmlPropertyData *copy = pd; - pd = _function->New<QQmlPropertyData>(); - *pd = *copy; - } return pd; } @@ -1784,7 +1903,9 @@ enum MetaObjectResolverFlags { ResolveTypeInformationOnly = 0x8 }; +#if 0 static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject); + static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, const QQmlType &qmlType, int index); static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine, @@ -2018,34 +2139,40 @@ static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, resolver->qmlType = qmlType; resolver->flags = index; } +#endif #endif // V4_BOOTSTRAP void JSCodeGen::beginFunctionBodyHook() { - _qmlContextTemp = _block->newTemp(); - _importedScriptsTemp = _block->newTemp(); + _qmlContextSlot = bytecodeGenerator->newRegister(); + _importedScriptsSlot = bytecodeGenerator->newRegister(); #ifndef V4_BOOTSTRAP - QV4::IR::Temp *temp = _block->TEMP(_qmlContextTemp); + Instruction::LoadQmlContext load; + load.result = Reference::fromStackSlot(this, _qmlContextSlot).stackSlot(); + bytecodeGenerator->addInstruction(load); + +#if 0 temp->type = QV4::IR::QObjectType; temp->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>(); initMetaObjectResolver(temp->memberResolver, _scopeObject); auto name = _block->NAME(QV4::IR::Name::builtin_qml_context, 0, 0); name->type = temp->type; - move(temp, name); +#endif - move(_block->TEMP(_importedScriptsTemp), _block->NAME(QV4::IR::Name::builtin_qml_imported_scripts_object, 0, 0)); + Instruction::LoadQmlImportedScripts loadScripts; + loadScripts.result = Reference::fromStackSlot(this, _importedScriptsSlot).stackSlot(); + bytecodeGenerator->addInstruction(loadScripts); #endif } -QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int col) +QV4::Compiler::Codegen::Reference JSCodeGen::fallbackNameLookup(const QString &name) { - Q_UNUSED(line) - Q_UNUSED(col) #ifndef V4_BOOTSTRAP if (_disableAcceleratedLookups) - return 0; + return Reference(); + // Implement QML lookup semantics in the current file context. // // Note: We do not check if properties of the qml scope object or context object @@ -2060,20 +2187,16 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int // Look for IDs first. for (const IdMapping &mapping : qAsConst(_idObjects)) { if (name == mapping.name) { - if (_function->isQmlBinding) - _function->idObjectDependencies.insert(mapping.idIndex); - - QV4::IR::Expr *s = _block->MEMBER(_block->TEMP(_qmlContextTemp), _function->newString(name), 0, QV4::IR::Member::MemberOfIdObjectsArray, mapping.idIndex); - QV4::IR::Temp *result = _block->TEMP(_block->newTemp()); - _block->MOVE(result, s); - result = _block->TEMP(result->index); - if (mapping.type) { - result->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>(); - result->memberResolver->owner = _function; - initMetaObjectResolver(result->memberResolver, mapping.type); - result->memberResolver->flags |= AllPropertiesAreFinal; - } - result->isReadOnly = true; // don't allow use as lvalue + if (_context->compilationMode == QV4::Compiler::QmlBinding) + _context->idObjectDependencies.insert(mapping.idIndex); + + Instruction::LoadIdObject load; + load.base = Reference::fromStackSlot(this, _qmlContextSlot).stackSlot(); + load.index = mapping.idIndex; + + Reference result = Reference::fromAccumulator(this); + bytecodeGenerator->addInstruction(load); + result.isReadonly = true; return result; } } @@ -2082,69 +2205,47 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int QQmlTypeNameCache::Result r = imports->query(name); if (r.isValid()) { if (r.scriptIndex != -1) { - return _block->SUBSCRIPT(_block->TEMP(_importedScriptsTemp), - _block->CONST(QV4::IR::SInt32Type, r.scriptIndex)); + Reference imports = Reference::fromStackSlot(this, _importedScriptsSlot); + return Reference::fromSubscript(imports, Reference::fromConst(this, QV4::Encode(r.scriptIndex))); } else if (r.type.isValid()) { - QV4::IR::Name *typeName = _block->NAME(name, line, col); - // Make sure the run-time loads this through the more efficient singleton getter. - typeName->qmlSingleton = r.type.isCompositeSingleton(); - typeName->freeOfSideEffects = true; - QV4::IR::Temp *result = _block->TEMP(_block->newTemp()); - _block->MOVE(result, typeName); - - result = _block->TEMP(result->index); - result->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>(); - result->memberResolver->owner = _function; - initQmlTypeResolver(result->memberResolver, r.type); - return result; + if (r.type.isCompositeSingleton()) { + Instruction::LoadQmlSingleton load; + load.name = registerString(name); + bytecodeGenerator->addInstruction(load); + return Reference::fromAccumulator(this); + } + return Reference::fromName(this, name); } else { Q_ASSERT(r.importNamespace); - QV4::IR::Name *namespaceName = _block->NAME(name, line, col); - namespaceName->freeOfSideEffects = true; - QV4::IR::Temp *result = _block->TEMP(_block->newTemp()); - result->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>(); - result->memberResolver->owner = _function; - initImportNamespaceResolver(result->memberResolver, imports, r.importNamespace); - - _block->MOVE(result, namespaceName); - return _block->TEMP(result->index); + return Reference::fromName(this, name); } } } if (_scopeObject) { - bool propertyExistsButForceNameLookup = false; - QQmlPropertyData *pd = lookupQmlCompliantProperty(_scopeObject, name, &propertyExistsButForceNameLookup); - if (propertyExistsButForceNameLookup) - return 0; - if (pd) { - QV4::IR::Temp *base = _block->TEMP(_qmlContextTemp); - base->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>(); - base->memberResolver->owner = _function; - initMetaObjectResolver(base->memberResolver, _scopeObject); - return _block->MEMBER(base, _function->newString(name), pd, QV4::IR::Member::MemberOfQmlScopeObject); - } + QQmlPropertyData *data = lookupQmlCompliantProperty(_scopeObject, name); + if (!data) + return Reference::fromName(this, name); + Reference base = Reference::fromStackSlot(this, _qmlContextSlot); + bool captureRequired = !data->isConstant() && !data->isQmlBinding(); + return Reference::fromQmlScopeObject(base, data->coreIndex(), data->notifyIndex(), + captureRequired); } if (_contextObject) { - bool propertyExistsButForceNameLookup = false; - QQmlPropertyData *pd = lookupQmlCompliantProperty(_contextObject, name, &propertyExistsButForceNameLookup); - if (propertyExistsButForceNameLookup) - return 0; - if (pd) { - QV4::IR::Temp *base = _block->TEMP(_qmlContextTemp); - base->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>(); - base->memberResolver->owner = _function; - initMetaObjectResolver(base->memberResolver, _contextObject); - return _block->MEMBER(base, _function->newString(name), pd, QV4::IR::Member::MemberOfQmlContextObject); - } + QQmlPropertyData *data = lookupQmlCompliantProperty(_contextObject, name); + if (!data) + return Reference::fromName(this, name); + Reference base = Reference::fromStackSlot(this, _qmlContextSlot); + bool captureRequired = !data->isConstant() && !data->isQmlBinding(); + return Reference::fromQmlContextObject(base, data->coreIndex(), data->notifyIndex(), + captureRequired); } - #else Q_UNUSED(name) #endif // V4_BOOTSTRAP // fall back to name lookup at run-time. - return 0; + return Reference(); } #ifndef V4_BOOTSTRAP diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index a5b4815745..406c939998 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -453,7 +453,7 @@ struct Q_QML_PRIVATE_EXPORT Document Document(bool debugMode); QString code; QQmlJS::Engine jsParserEngine; - QV4::IR::Module jsModule; + QV4::Compiler::Module jsModule; QList<const QV4::CompiledData::Import *> imports; QList<Pragma*> pragmas; QQmlJS::AST::UiProgram *program; @@ -527,6 +527,7 @@ public: const QQmlJS::AST::SourceLocation &last) const; void setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement); + void tryGeneratingTranslationBinding(const QStringRef &base, QQmlJS::AST::ArgumentList *args, QV4::CompiledData::Binding *binding); void appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value); void appendBinding(QQmlJS::AST::UiQualifiedId *name, int objectIndex, bool isOnAssignment = false); @@ -606,9 +607,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 &fileName, const QString &sourceCode, QV4::IR::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); @@ -626,12 +627,17 @@ struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QQmlJS::Codegen // Returns mapping from input functions to index in IR::Module::functions / compiledData->runtimeFunctions QVector<int> generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions); + int defineFunction(const QString &name, AST::Node *ast, + AST::FormalParameterList *formals, + AST::SourceElements *body) override; + protected: void beginFunctionBodyHook() override; - QV4::IR::Expr *fallbackNameLookup(const QString &name, int line, int col) override; + Reference fallbackNameLookup(const QString &name) override; private: - QQmlPropertyData *lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name, bool *propertyExistsButForceNameLookup = 0); + // returns nullptr if lookup needs to happen by name + QQmlPropertyData *lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name); QString sourceCode; QQmlJS::Engine *jsEngine; // needed for memory pool @@ -643,8 +649,8 @@ private: ObjectIdMapping _idObjects; QQmlPropertyCache *_contextObject; QQmlPropertyCache *_scopeObject; - int _qmlContextTemp; - int _importedScriptsTemp; + int _qmlContextSlot; + int _importedScriptsSlot; }; struct Q_QML_PRIVATE_EXPORT IRLoader { diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 46f0c46b99..97ca597953 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -44,10 +44,9 @@ #include <private/qqmlcustomparser_p.h> #include <private/qqmlvmemetaobject_p.h> #include <private/qqmlcomponent_p.h> -#include <private/qv4ssa_p.h> #include "qqmlpropertycachecreator_p.h" -#include "qv4jssimplifier_p.h" +//#include "qv4jssimplifier_p.h" #define COMPILE_EXCEPTION(token, desc) \ { \ @@ -140,19 +139,15 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() sss.scan(); } - QmlIR::JSCodeGen v4CodeGenerator(typeData->finalUrlString(), document->code, &document->jsModule, &document->jsParserEngine, document->program, typeNameCache, &document->jsGenerator.stringTable); + 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); QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator); if (!jsCodeGen.generateCodeForComponents()) return nullptr; - QQmlJavaScriptBindingExpressionSimplificationPass pass(document->objects, &document->jsModule, &document->jsGenerator); - pass.reduceTranslationBindings(); - - QV4::ExecutionEngine *v4 = engine->v4engine(); - QScopedPointer<QV4::EvalInstructionSelection> isel(v4->iselFactory->create(engine, v4->executableAllocator, &document->jsModule, &document->jsGenerator)); - isel->setUseFastLookups(false); - isel->setUseTypeInference(true); - document->javaScriptCompilationUnit = isel->compile(/*generated unit data*/false); + document->javaScriptCompilationUnit = v4CodeGenerator.generateCompilationUnit(/*generated unit data*/false); } // Generate QML compiled type data structures @@ -208,11 +203,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; @@ -501,7 +491,9 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio QQmlJS::AST::FunctionBody *body = new (pool) QQmlJS::AST::FunctionBody(elements); functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(compiler->newStringRef(stringAt(binding->propertyNameIndex)), paramList, body); - functionDeclaration->functionToken = foe->node->firstSourceLocation(); + functionDeclaration->lbraceToken = functionDeclaration->functionToken + = foe->node->firstSourceLocation(); + functionDeclaration->rbraceToken = foe->node->lastSourceLocation(); } foe->node = functionDeclaration; binding->propertyNameIndex = compiler->registerString(propertyName); diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 4878a90641..d905b956c7 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/qv4bytecodegenerator.cpp b/src/qml/compiler/qv4bytecodegenerator.cpp new file mode 100644 index 0000000000..05bbf25292 --- /dev/null +++ b/src/qml/compiler/qv4bytecodegenerator.cpp @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** 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 <private/qv4bytecodegenerator_p.h> +#include <private/qv4compilercontext_p.h> +#include <private/qqmljsastfwd_p.h> +#include <private/qv4compileddata_p.h> + +QT_USE_NAMESPACE +using namespace QV4; +using namespace Moth; + +void BytecodeGenerator::setLocation(const QQmlJS::AST::SourceLocation &loc) +{ + currentLine = static_cast<int>(loc.startLine); +} + +int BytecodeGenerator::newRegister() +{ + int t = currentReg++; + if (regCount < currentReg) + regCount = currentReg; + return t; +} + +int BytecodeGenerator::newRegisterArray(int n) +{ + int t = currentReg; + currentReg += n; + if (regCount < currentReg) + regCount = currentReg; + return t; +} + +void BytecodeGenerator::packInstruction(I &i) +{ + uchar type = *reinterpret_cast<uchar *>(i.packed); + Q_ASSERT(type >= MOTH_NUM_INSTRUCTIONS()); + if (type >= MOTH_NUM_INSTRUCTIONS()) + type -= MOTH_NUM_INSTRUCTIONS(); + int instructionsAsInts[sizeof(Instr)/sizeof(int)]; + int nMembers = Moth::InstrInfo::argumentCount[static_cast<int>(i.type)]; + memcpy(instructionsAsInts, i.packed + 1, nMembers*sizeof(int)); + enum { + Normal, + Wide + } width = Normal; + for (int n = 0; n < nMembers; ++n) { + if (width == Normal && (static_cast<char>(instructionsAsInts[n]) != instructionsAsInts[n])) + width = Wide; + } + char *code = i.packed; + switch (width) { + case Normal: + *reinterpret_cast<uchar *>(code) = type; + ++code; + for (int n = 0; n < nMembers; ++n) { + char v = static_cast<char>(instructionsAsInts[n]); + memcpy(code, &v, 1); + code += 1; + } + i.size = code - i.packed; + if (i.offsetForJump != -1) + i.offsetForJump = i.size - 1; + break; + case Wide: + // nothing to do + break; + } +} + +void BytecodeGenerator::adjustJumpOffsets() +{ + for (int index = 0; index < instructions.size(); ++index) { + auto &i = instructions[index]; + if (i.offsetForJump == -1) // no jump + continue; + Q_ASSERT(i.linkedLabel != -1 && labels.at(i.linkedLabel) != -1); + const auto &linkedInstruction = instructions.at(labels.at(i.linkedLabel)); + char *c = i.packed + i.offsetForJump; + int jumpOffset = linkedInstruction.position - (i.position + i.size); +// qDebug() << "adjusting jump offset for instruction" << index << i.position << i.size << "offsetForJump" << i.offsetForJump << "target" +// << labels.at(i.linkedLabel) << linkedInstruction.position << "jumpOffset" << jumpOffset; + uchar type = *reinterpret_cast<const uchar *>(i.packed); + if (type >= MOTH_NUM_INSTRUCTIONS()) { + Q_ASSERT(i.offsetForJump == i.size - 4); + memcpy(c, &jumpOffset, sizeof(int)); + } else { + Q_ASSERT(i.offsetForJump == i.size - 1); + char o = jumpOffset; + Q_ASSERT(o == jumpOffset); + *c = o; + } + } +} + +void BytecodeGenerator::compressInstructions() +{ + // first round: compress all non jump instructions + int position = 0; + for (auto &i : instructions) { + i.position = position; + if (i.offsetForJump == -1) + packInstruction(i); + position += i.size; + } + + adjustJumpOffsets(); + + // compress all jumps + position = 0; + for (auto &i : instructions) { + i.position = position; + if (i.offsetForJump != -1) + packInstruction(i); + position += i.size; + } + + // adjust once again, as the packing above could have changed offsets + adjustJumpOffsets(); +} + +void BytecodeGenerator::finalize(Compiler::Context *context) +{ + compressInstructions(); + + // collect content and line numbers + QByteArray code; + QVector<CompiledData::CodeOffsetToLine> lineNumbers; + currentLine = -1; + Q_UNUSED(startLine); + for (const auto &i : qAsConst(instructions)) { + if (i.line != currentLine) { + currentLine = i.line; + CompiledData::CodeOffsetToLine entry; + entry.codeOffset = code.size(); + entry.line = currentLine; + lineNumbers.append(entry); + } + code.append(i.packed, i.size); + } + + context->code = code; + context->lineNumberMapping = lineNumbers; +} + +int BytecodeGenerator::addInstructionHelper(Instr::Type type, const Instr &i, int offsetOfOffset) { + if (debugMode && type != Instr::Type::Debug) { +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // broken gcc warns about Instruction::Debug() + if (instructions.isEmpty() || currentLine != instructions.constLast().line) { + addInstruction(Instruction::Debug()); + } else if (type == Instr::Type::Ret) { + currentLine = -currentLine; + addInstruction(Instruction::Debug()); + currentLine = -currentLine; + } +QT_WARNING_POP + } + + const int pos = instructions.size(); + + int s = Moth::InstrInfo::argumentCount[static_cast<int>(type)]*sizeof(int); + if (offsetOfOffset != -1) + offsetOfOffset += 1; + I instr{type, static_cast<short>(s + 1), 0, currentLine, offsetOfOffset, -1, "\0\0" }; + char *code = instr.packed; + *reinterpret_cast<uchar *>(code) = static_cast<uchar>(MOTH_NUM_INSTRUCTIONS() + static_cast<int>(type)); + ++code; + Q_ASSERT(MOTH_NUM_INSTRUCTIONS() + static_cast<int>(type) < 256); + memcpy(code, &i, s); + instructions.append(instr); + + return pos; +} diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h new file mode 100644 index 0000000000..3d516da922 --- /dev/null +++ b/src/qml/compiler/qv4bytecodegenerator_p.h @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** 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 QV4BYTECODEGENERATOR_P_H +#define QV4BYTECODEGENERATOR_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/qv4instr_moth_p.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace AST { +class SourceLocation; +} +} + +namespace QV4 { +namespace Moth { + +class BytecodeGenerator { +public: + BytecodeGenerator(int line, bool debug) + : startLine(line), debugMode(debug) {} + + struct Label { + enum LinkMode { + LinkNow, + LinkLater + }; + Label() = default; + Label(BytecodeGenerator *generator, LinkMode mode = LinkNow) + : generator(generator), + index(generator->labels.size()) { + generator->labels.append(mode == LinkNow ? generator->instructions.size() : -1); + } + static Label returnLabel() { + Label l; + l.index = INT_MAX; + return l; + } + bool isReturn() const { + return index == INT_MAX; + } + + void link() { + Q_ASSERT(index >= 0); + Q_ASSERT(generator->labels[index] == -1); + generator->labels[index] = generator->instructions.size(); + } + + BytecodeGenerator *generator = 0; + int index = -1; + }; + + struct Jump { + Jump(BytecodeGenerator *generator, int instruction) + : generator(generator), + index(instruction) + {} + ~Jump() { + Q_ASSERT(generator->instructions[index].linkedLabel != -1); + } + + + BytecodeGenerator *generator; + int index; + + void link() { + link(generator->label()); + } + void link(Label l) { + Q_ASSERT(l.index >= 0); + Q_ASSERT(generator->instructions[index].linkedLabel == -1); + generator->instructions[index].linkedLabel = l.index; + } + }; + + struct ExceptionHandler : public Label { + ExceptionHandler(BytecodeGenerator *generator) + : Label(generator, LinkLater) + { + } + ~ExceptionHandler() + { + Q_ASSERT(generator->currentExceptionHandler != this); + } + }; + + Label label() { + return Label(this, Label::LinkNow); + } + + Label newLabel() { + return Label(this, Label::LinkLater); + } + + ExceptionHandler newExceptionHandler() { + return ExceptionHandler(this); + } + + template<int InstrT> + void addInstruction(const InstrData<InstrT> &data) + { + Instr genericInstr; + InstrMeta<InstrT>::setData(genericInstr, data); + addInstructionHelper(Moth::Instr::Type(InstrT), genericInstr); + } + + Q_REQUIRED_RESULT Jump jump() + { + Instruction::Jump data; + return addJumpInstruction(data); + } + + Q_REQUIRED_RESULT Jump jumpTrue() + { + Instruction::JumpTrue data; + return addJumpInstruction(data); + } + + Q_REQUIRED_RESULT Jump jumpFalse() + { + Instruction::JumpFalse data; + return addJumpInstruction(data); + } + + void jumpStrictEqual(const StackSlot &lhs, const Label &target) + { + Instruction::CmpStrictEqual cmp; + cmp.lhs = lhs; + addInstruction(cmp); + addJumpInstruction(Instruction::JumpTrue()).link(target); + } + + void jumpStrictNotEqual(const StackSlot &lhs, const Label &target) + { + Instruction::CmpStrictNotEqual cmp; + cmp.lhs = lhs; + addInstruction(cmp); + addJumpInstruction(Instruction::JumpTrue()).link(target); + } + + Q_REQUIRED_RESULT Jump jumpStrictEqualStackSlotInt(const StackSlot &lhs, int rhs) + { + Instruction::JumpStrictEqualStackSlotInt data; + data.lhs = lhs; + data.rhs = rhs; + return addJumpInstruction(data); + } + + Q_REQUIRED_RESULT Jump jumpStrictNotEqualStackSlotInt(const StackSlot &lhs, int rhs) + { + Instruction::JumpStrictNotEqualStackSlotInt data; + data.lhs = lhs; + data.rhs = rhs; + return addJumpInstruction(data); + } + + void setExceptionHandler(ExceptionHandler *handler) + { + currentExceptionHandler = handler; + Instruction::SetExceptionHandler data; + data.offset = 0; + if (!handler) + addInstruction(data); + else + addJumpInstruction(data).link(*handler); + } + + void setLocation(const QQmlJS::AST::SourceLocation &loc); + + ExceptionHandler *exceptionHandler() const { + return currentExceptionHandler; + } + + int newRegister(); + int newRegisterArray(int n); + int registerCount() const { return regCount; } + + void finalize(Compiler::Context *context); + + template<int InstrT> + Jump addJumpInstruction(const InstrData<InstrT> &data) + { + Instr genericInstr; + InstrMeta<InstrT>::setData(genericInstr, data); + return Jump(this, addInstructionHelper(Moth::Instr::Type(InstrT), genericInstr, offsetof(InstrData<InstrT>, offset))); + } + + void addCJumpInstruction(bool jumpOnFalse, const Label *trueLabel, const Label *falseLabel) + { + if (jumpOnFalse) + addJumpInstruction(Instruction::JumpFalse()).link(*falseLabel); + else + addJumpInstruction(Instruction::JumpTrue()).link(*trueLabel); + } + +private: + friend struct Jump; + friend struct Label; + friend struct ExceptionHandler; + + int addInstructionHelper(Moth::Instr::Type type, const Instr &i, int offsetOfOffset = -1); + + struct I { + Moth::Instr::Type type; + short size; + uint position; + int line; + int offsetForJump; + int linkedLabel; + char packed[sizeof(Instr) + 2]; // 2 for instruction and prefix + }; + + void compressInstructions(); + void packInstruction(I &i); + void adjustJumpOffsets(); + + QVector<I> instructions; + QVector<int> labels; + ExceptionHandler *currentExceptionHandler; + int regCount = 0; +public: + int currentReg = 0; +private: + int startLine = 0; + int currentLine = 0; + bool debugMode = false; +}; + +} +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 3fff29bce0..b9d223bab0 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** 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. @@ -39,7 +39,6 @@ #include "qv4codegen_p.h" #include "qv4util_p.h" -#include "qv4engine_p.h" #include <QtCore/QCoreApplication> #include <QtCore/QStringList> @@ -51,10 +50,10 @@ #include <private/qqmljsast_p.h> #include <private/qv4string_p.h> #include <private/qv4value_p.h> - -#ifndef V4_BOOTSTRAP -#include <qv4context_p.h> -#endif +#include <private/qv4compilercontext_p.h> +#include <private/qv4compilercontrolflow_p.h> +#include <private/qv4bytecodegenerator_p.h> +#include <private/qv4compilerscanfunctions_p.h> #include <cmath> #include <iostream> @@ -63,37 +62,13 @@ #undef CONST #endif +QT_USE_NAMESPACE using namespace QV4; -using namespace QQmlJS; -using namespace AST; - -static inline void setLocation(IR::Stmt *s, const SourceLocation &loc) -{ - if (s && loc.isValid()) - s->location = loc; -} - -static bool cjumpCanHandle(IR::AluOp op) -{ - switch (op) { - case IR::OpIn: - case IR::OpInstanceof: - case IR::OpEqual: - case IR::OpNotEqual: - case IR::OpGe: - case IR::OpGt: - case IR::OpLe: - case IR::OpLt: - case IR::OpStrictEqual: - case IR::OpStrictNotEqual: - return true; - default: - return false; - } -} +using namespace QV4::Compiler; +using namespace QQmlJS::AST; -static inline void setJumpOutLocation(IR::Stmt *s, const Statement *body, - const SourceLocation &fallback) +static inline void setJumpOutLocation(QV4::Moth::BytecodeGenerator *bytecodeGenerator, + const Statement *body, const SourceLocation &fallback) { switch (body->kind) { // Statements where we might never execute the last line. @@ -105,672 +80,170 @@ static inline void setJumpOutLocation(IR::Stmt *s, const Statement *body, case Statement::Kind_LocalForEachStatement: case Statement::Kind_LocalForStatement: case Statement::Kind_WhileStatement: - setLocation(s, fallback); + bytecodeGenerator->setLocation(fallback); break; default: - setLocation(s, body->lastSourceLocation()); + bytecodeGenerator->setLocation(body->lastSourceLocation()); break; } } -static inline bool isSimpleExpr(IR::Expr *e) -{ - switch (e->exprKind) { - case IR::Expr::TempExpr: - case IR::Expr::ArgLocalExpr: - case IR::Expr::ConstExpr: - return true; - default: - return false; - } -} - -Codegen::ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode) - : _cg(cg) - , _sourceCode(sourceCode) - , _variableEnvironment(0) - , _allowFuncDecls(true) - , defaultProgramMode(defaultProgramMode) -{ -} - -void Codegen::ScanFunctions::operator()(Node *node) -{ - if (node) - node->accept(this); -} - -void Codegen::ScanFunctions::enterEnvironment(Node *node, CompilationMode compilationMode) -{ - Environment *e = _cg->newEnvironment(node, _variableEnvironment, compilationMode); - if (!e->isStrict) - e->isStrict = _cg->_strictMode; - _envStack.append(e); - _variableEnvironment = e; -} - -void Codegen::ScanFunctions::leaveEnvironment() -{ - _envStack.pop(); - _variableEnvironment = _envStack.isEmpty() ? 0 : _envStack.top(); -} - -void Codegen::ScanFunctions::checkDirectivePrologue(SourceElements *ast) -{ - for (SourceElements *it = ast; it; it = it->next) { - if (StatementSourceElement *stmt = cast<StatementSourceElement *>(it->element)) { - if (ExpressionStatement *expr = cast<ExpressionStatement *>(stmt->statement)) { - if (StringLiteral *strLit = cast<StringLiteral *>(expr->expression)) { - // Use the source code, because the StringLiteral's - // value might have escape sequences in it, which is not - // allowed. - if (strLit->literalToken.length < 2) - continue; - QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2); - if (str == QLatin1String("use strict")) { - _variableEnvironment->isStrict = true; - } else { - // TODO: give a warning. - } - continue; - } - } - } - - break; - } -} - -void Codegen::ScanFunctions::checkName(const QStringRef &name, const SourceLocation &loc) -{ - if (_variableEnvironment->isStrict) { - if (name == QLatin1String("implements") - || name == QLatin1String("interface") - || name == QLatin1String("let") - || name == QLatin1String("package") - || name == QLatin1String("private") - || name == QLatin1String("protected") - || name == QLatin1String("public") - || name == QLatin1String("static") - || name == QLatin1String("yield")) { - _cg->throwSyntaxError(loc, QStringLiteral("Unexpected strict mode reserved word")); - } - } -} -void Codegen::ScanFunctions::checkForArguments(AST::FormalParameterList *parameters) -{ - while (parameters) { - if (parameters->name == QLatin1String("arguments")) - _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; - parameters = parameters->next; - } -} - -bool Codegen::ScanFunctions::visit(Program *ast) -{ - enterEnvironment(ast, defaultProgramMode); - checkDirectivePrologue(ast->elements); - return true; -} - -void Codegen::ScanFunctions::endVisit(Program *) -{ - leaveEnvironment(); -} - -bool Codegen::ScanFunctions::visit(CallExpression *ast) -{ - if (! _variableEnvironment->hasDirectEval) { - if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) { - if (id->name == QLatin1String("eval")) { - if (_variableEnvironment->usesArgumentsObject == Environment::ArgumentsObjectUnknown) - _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectUsed; - _variableEnvironment->hasDirectEval = true; - } - } - } - int argc = 0; - for (ArgumentList *it = ast->arguments; it; it = it->next) - ++argc; - _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, argc); - return true; -} - -bool Codegen::ScanFunctions::visit(NewMemberExpression *ast) -{ - int argc = 0; - for (ArgumentList *it = ast->arguments; it; it = it->next) - ++argc; - _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, argc); - return true; -} - -bool Codegen::ScanFunctions::visit(ArrayLiteral *ast) -{ - int index = 0; - for (ElementList *it = ast->elements; it; it = it->next) { - for (Elision *elision = it->elision; elision; elision = elision->next) - ++index; - ++index; - } - if (ast->elision) { - for (Elision *elision = ast->elision->next; elision; elision = elision->next) - ++index; - } - _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, index); - return true; -} - -bool Codegen::ScanFunctions::visit(VariableDeclaration *ast) -{ - if (_variableEnvironment->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) - _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode")); - checkName(ast->name, ast->identifierToken); - if (ast->name == QLatin1String("arguments")) - _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; - if (ast->scope == AST::VariableDeclaration::VariableScope::ReadOnlyBlockScope && !ast->expression) { - _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration")); - return false; - } - QString name = ast->name.toString(); - const Environment::Member *m = 0; - if (_variableEnvironment->memberInfo(name, &m)) { - if (m->isLexicallyScoped() || ast->isLexicallyScoped()) { - _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name)); - return false; - } - } - _variableEnvironment->enter(ast->name.toString(), ast->expression ? Environment::VariableDefinition : Environment::VariableDeclaration, ast->scope); - return true; -} - -bool Codegen::ScanFunctions::visit(IdentifierExpression *ast) -{ - checkName(ast->name, ast->identifierToken); - if (_variableEnvironment->usesArgumentsObject == Environment::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments")) - _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectUsed; - return true; -} - -bool Codegen::ScanFunctions::visit(ExpressionStatement *ast) -{ - if (FunctionExpression* expr = AST::cast<AST::FunctionExpression*>(ast->expression)) { - if (!_allowFuncDecls) - _cg->throwSyntaxError(expr->functionToken, QStringLiteral("conditional function or closure declaration")); - - enterFunction(expr, /*enterName*/ true); - Node::accept(expr->formals, this); - Node::accept(expr->body, this); - leaveEnvironment(); - return false; - } else { - SourceLocation firstToken = ast->firstSourceLocation(); - if (_sourceCode.midRef(firstToken.offset, firstToken.length) == QLatin1String("function")) { - _cg->throwSyntaxError(firstToken, QStringLiteral("unexpected token")); - } - } - return true; -} - -bool Codegen::ScanFunctions::visit(FunctionExpression *ast) -{ - enterFunction(ast, /*enterName*/ false); - return true; -} - -void Codegen::ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName, bool isExpression) -{ - if (_variableEnvironment->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) - _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode")); - enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : 0, isExpression); -} - -void Codegen::ScanFunctions::endVisit(FunctionExpression *) -{ - leaveEnvironment(); -} - -bool Codegen::ScanFunctions::visit(ObjectLiteral *ast) -{ - int argc = 0; - for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { - QString key = it->assignment->name->asString(); - if (QV4::String::toArrayIndex(key) != UINT_MAX) - ++argc; - ++argc; - if (AST::cast<AST::PropertyGetterSetter *>(it->assignment)) - ++argc; - } - _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, argc); - - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); - Node::accept(ast->properties, this); - return false; -} - -bool Codegen::ScanFunctions::visit(PropertyGetterSetter *ast) -{ - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); - enterFunction(ast, QString(), ast->formals, ast->functionBody, /*FunctionExpression*/0, /*isExpression*/false); - return true; -} - -void Codegen::ScanFunctions::endVisit(PropertyGetterSetter *) -{ - leaveEnvironment(); -} - -bool Codegen::ScanFunctions::visit(FunctionDeclaration *ast) -{ - enterFunction(ast, /*enterName*/ true, /*isExpression */false); - return true; -} - -void Codegen::ScanFunctions::endVisit(FunctionDeclaration *) -{ - leaveEnvironment(); -} - -bool Codegen::ScanFunctions::visit(WithStatement *ast) -{ - if (_variableEnvironment->isStrict) { - _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode")); - return false; - } - - return true; -} - -bool Codegen::ScanFunctions::visit(DoWhileStatement *ast) { - { - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); - Node::accept(ast->statement, this); - } - Node::accept(ast->expression, this); - return false; -} - -bool Codegen::ScanFunctions::visit(ForStatement *ast) { - Node::accept(ast->initialiser, this); - Node::accept(ast->condition, this); - Node::accept(ast->expression, this); - - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); - Node::accept(ast->statement, this); - - return false; -} - -bool Codegen::ScanFunctions::visit(LocalForStatement *ast) { - Node::accept(ast->declarations, this); - Node::accept(ast->condition, this); - Node::accept(ast->expression, this); - - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); - Node::accept(ast->statement, this); - - return false; -} - -bool Codegen::ScanFunctions::visit(ForEachStatement *ast) { - Node::accept(ast->initialiser, this); - Node::accept(ast->expression, this); - - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); - Node::accept(ast->statement, this); - - return false; -} - -bool Codegen::ScanFunctions::visit(LocalForEachStatement *ast) { - Node::accept(ast->declaration, this); - Node::accept(ast->expression, this); - - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); - Node::accept(ast->statement, this); - - return false; -} - -bool Codegen::ScanFunctions::visit(ThisExpression *) -{ - _variableEnvironment->usesThis = true; - return false; -} - -bool Codegen::ScanFunctions::visit(Block *ast) { - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _variableEnvironment->isStrict ? false : _allowFuncDecls); - Node::accept(ast->statements, this); - return false; -} - -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. - if (expr) - _variableEnvironment->enter(name, Environment::FunctionDefinition, AST::VariableDeclaration::FunctionScope, expr); - if (name == QLatin1String("arguments")) - _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; - wasStrict = _variableEnvironment->isStrict; - } - - enterEnvironment(ast, FunctionCode); - checkForArguments(formals); - - _variableEnvironment->isNamedFunctionExpression = isExpression && !name.isEmpty(); - _variableEnvironment->formals = formals; - - if (body) - 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)) { - _cg->throwSyntaxError(it->identifierToken, QStringLiteral("Duplicate parameter name '%1' is not allowed in strict mode").arg(arg)); - return; - } - if (arg == QLatin1String("eval") || arg == QLatin1String("arguments")) { - _cg->throwSyntaxError(it->identifierToken, QStringLiteral("'%1' cannot be used as parameter name in strict mode").arg(arg)); - return; - } - args += arg; - } - } -} - - -Codegen::Codegen(bool strict) +Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict) : _module(0) - , _function(0) - , _block(0) - , _exitBlock(0) , _returnAddress(0) - , _variableEnvironment(0) - , _loop(0) + , _context(0) , _labelledStatement(0) - , _scopeAndFinally(0) + , jsUnitGenerator(jsUnitGenerator) , _strictMode(strict) , _fileNameIsUrl(false) , hasError(false) { + jsUnitGenerator->codeGeneratorName = QStringLiteral("moth"); } void Codegen::generateFromProgram(const QString &fileName, const QString &sourceCode, Program *node, - QV4::IR::Module *module, - CompilationMode mode, - const QStringList &inheritedLocals) + Module *module, + CompilationMode mode) { Q_ASSERT(node); _module = module; - _variableEnvironment = 0; + _context = 0; - _module->setFileName(fileName); + _module->fileName = fileName; ScanFunctions scan(this, sourceCode, mode); scan(node); - defineFunction(QStringLiteral("%entry"), node, 0, node->elements, inheritedLocals); - qDeleteAll(_envMap); - _envMap.clear(); -} - -void Codegen::generateFromFunctionExpression(const QString &fileName, - const QString &sourceCode, - AST::FunctionExpression *ast, - QV4::IR::Module *module) -{ - _module = module; - _module->setFileName(fileName); - _variableEnvironment = 0; - - ScanFunctions scan(this, sourceCode, GlobalCode); - // fake a global environment - scan.enterEnvironment(0, FunctionCode); - scan(ast); - scan.leaveEnvironment(); - - defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); - - qDeleteAll(_envMap); - _envMap.clear(); -} - - -void Codegen::enterEnvironment(Node *node) -{ - _variableEnvironment = _envMap.value(node); - Q_ASSERT(_variableEnvironment); -} - -void Codegen::leaveEnvironment() -{ - Q_ASSERT(_variableEnvironment); - _variableEnvironment = _variableEnvironment->parent; -} - -void Codegen::enterLoop(Statement *node, IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock) -{ - _loop = new Loop(node, breakBlock, continueBlock, _loop); - _loop->labelledStatement = _labelledStatement; // consume the enclosing labelled statement - _loop->scopeAndFinally = _scopeAndFinally; - _labelledStatement = 0; -} - -void Codegen::leaveLoop() -{ - Loop *current = _loop; - _loop = _loop->parent; - delete current; -} - -IR::Expr *Codegen::member(IR::Expr *base, const QString *name) -{ - if (hasError) - return 0; - - if (base->asTemp() || base->asArgLocal()) - return _block->MEMBER(base, name); - else { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), base); - return _block->MEMBER(_block->TEMP(t), name); - } + defineFunction(QStringLiteral("%entry"), node, 0, node->elements); } -IR::Expr *Codegen::argument(IR::Expr *expr) +void Codegen::enterContext(Node *node) { - if (expr && !expr->asTemp()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), expr); - expr = _block->TEMP(t); - } - return expr; + _context = _module->contextMap.value(node); + Q_ASSERT(_context); } -// keeps references alive, converts other expressions to temps -IR::Expr *Codegen::reference(IR::Expr *expr) +int Codegen::leaveContext() { - if (hasError) - return 0; - - if (expr && !expr->asTemp() && !expr->asArgLocal() && !expr->asName() && !expr->asMember() && !expr->asSubscript()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), expr); - expr = _block->TEMP(t); - } - return expr; + Q_ASSERT(_context); + Q_ASSERT(!_context->controlFlow); + int functionIndex = _context->functionIndex; + _context = _context->parent; + return functionIndex; } -IR::Expr *Codegen::unop(IR::AluOp op, IR::Expr *expr, const SourceLocation &loc) +Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) { if (hasError) - return 0; - - Q_ASSERT(op != IR::OpIncrement); - Q_ASSERT(op != IR::OpDecrement); + return _expr.result(); - if (IR::Const *c = expr->asConst()) { - if (c->type == IR::NumberType) { +#ifndef V4_BOOTSTRAP + if (expr.isConst()) { + auto v = Value::fromReturnedValue(expr.constant); + if (v.isNumber()) { switch (op) { - case IR::OpNot: - return _block->CONST(IR::BoolType, !c->value); - case IR::OpUMinus: - return _block->CONST(IR::NumberType, -c->value); - case IR::OpUPlus: + case Not: + return Reference::fromConst(this, Encode(!v.toBoolean())); + case UMinus: + return Reference::fromConst(this, Runtime::method_uMinus(v)); + case UPlus: return expr; - case IR::OpCompl: - return _block->CONST(IR::NumberType, ~QV4::Primitive::toInt32(c->value)); - case IR::OpIncrement: - return _block->CONST(IR::NumberType, c->value + 1); - case IR::OpDecrement: - return _block->CONST(IR::NumberType, c->value - 1); + case Compl: + return Reference::fromConst(this, Encode((int)~v.toInt32())); default: break; } } } +#endif // V4_BOOTSTRAP - TempScope scope(_function); - if (isSimpleExpr(expr)) - return _block->UNOP(op, expr); - - const unsigned t = _block->newTemp(); - setLocation(move(_block->TEMP(t), expr), loc); - expr = _block->TEMP(t); - return _block->UNOP(op, expr); -} - -IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right, const AST::SourceLocation &loc) -{ - if (hasError) - return 0; - - TempScope scope(_function); - - if (IR::Const *c1 = left->asConst()) { - if (IR::Const *c2 = right->asConst()) { - if ((c1->type & IR::NumberType) && (c2->type & IR::NumberType)) { - switch (op) { - case IR::OpAdd: return _block->CONST(IR::NumberType, c1->value + c2->value); - case IR::OpAnd: return _block->CONST(IR::BoolType, c1->value ? c2->value : 0); - case IR::OpBitAnd: return _block->CONST(IR::NumberType, int(c1->value) & int(c2->value)); - case IR::OpBitOr: return _block->CONST(IR::NumberType, int(c1->value) | int(c2->value)); - case IR::OpBitXor: return _block->CONST(IR::NumberType, int(c1->value) ^ int(c2->value)); - case IR::OpDiv: return _block->CONST(IR::NumberType, c1->value / c2->value); - case IR::OpEqual: return _block->CONST(IR::BoolType, c1->value == c2->value); - case IR::OpNotEqual: return _block->CONST(IR::BoolType, c1->value != c2->value); - case IR::OpStrictEqual: return _block->CONST(IR::BoolType, c1->value == c2->value); - case IR::OpStrictNotEqual: return _block->CONST(IR::BoolType, c1->value != c2->value); - case IR::OpGe: return _block->CONST(IR::BoolType, c1->value >= c2->value); - case IR::OpGt: return _block->CONST(IR::BoolType, c1->value > c2->value); - case IR::OpLe: return _block->CONST(IR::BoolType, c1->value <= c2->value); - case IR::OpLt: return _block->CONST(IR::BoolType, c1->value < c2->value); - case IR::OpLShift: return _block->CONST(IR::NumberType, QV4::Primitive::toInt32(c1->value) << (QV4::Primitive::toUInt32(c2->value) & 0x1f)); - case IR::OpMod: return _block->CONST(IR::NumberType, std::fmod(c1->value, c2->value)); - case IR::OpMul: return _block->CONST(IR::NumberType, c1->value * c2->value); - case IR::OpOr: return _block->CONST(IR::NumberType, c1->value ? c1->value : c2->value); - case IR::OpRShift: return _block->CONST(IR::NumberType, QV4::Primitive::toInt32(c1->value) >> (QV4::Primitive::toUInt32(c2->value) & 0x1f)); - case IR::OpSub: return _block->CONST(IR::NumberType, c1->value - c2->value); - case IR::OpURShift: return _block->CONST(IR::NumberType,QV4::Primitive::toUInt32(c1->value) >> (QV4::Primitive::toUInt32(c2->value) & 0x1f)); - - case IR::OpInstanceof: - case IR::OpIn: - break; - - case IR::OpIfTrue: // unary ops - case IR::OpNot: - case IR::OpUMinus: - case IR::OpUPlus: - case IR::OpCompl: - case IR::OpIncrement: - case IR::OpDecrement: - case IR::OpInvalid: - break; - } - } + switch (op) { + case UMinus: { + expr.loadInAccumulator(); + Instruction::UMinus uminus; + bytecodeGenerator->addInstruction(uminus); + return Reference::fromAccumulator(this); + } + case UPlus: { + expr.loadInAccumulator(); + Instruction::UPlus uplus; + bytecodeGenerator->addInstruction(uplus); + return Reference::fromAccumulator(this); + } + case Not: { + expr.loadInAccumulator(); + Instruction::UNot unot; + bytecodeGenerator->addInstruction(unot); + return Reference::fromAccumulator(this); + } + case Compl: { + expr.loadInAccumulator(); + Instruction::UCompl ucompl; + bytecodeGenerator->addInstruction(ucompl); + return Reference::fromAccumulator(this); + } + case PostIncrement: + if (!_expr.accept(nx) || requiresReturnValue) { + Reference e = expr.asLValue(); + e.loadInAccumulator(); + Instruction::UPlus uplus; + bytecodeGenerator->addInstruction(uplus); + Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator(); + Instruction::Increment inc; + bytecodeGenerator->addInstruction(inc); + e.storeConsumeAccumulator(); + return originalValue; + } else { + // intentionally fall-through: the result is never used, so it's equivalent to + // "expr += 1", which is what a pre-increment does as well. } - } else if (op == IR::OpAdd) { - if (IR::String *s1 = left->asString()) { - if (IR::String *s2 = right->asString()) { - return _block->STRING(_function->newString(*s1->value + *s2->value)); - } + case PreIncrement: { + Reference e = expr.asLValue(); + e.loadInAccumulator(); + Instruction::Increment inc; + bytecodeGenerator->addInstruction(inc); + if (_expr.accept(nx)) + return e.storeConsumeAccumulator(); + else + return e.storeRetainAccumulator(); + } + case PostDecrement: + if (!_expr.accept(nx) || requiresReturnValue) { + Reference e = expr.asLValue(); + e.loadInAccumulator(); + Instruction::UPlus uplus; + bytecodeGenerator->addInstruction(uplus); + Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator(); + Instruction::Decrement dec; + bytecodeGenerator->addInstruction(dec); + e.storeConsumeAccumulator(); + return originalValue; + } else { + // intentionally fall-through: the result is never used, so it's equivalent to + // "expr -= 1", which is what a pre-decrement does as well. } + case PreDecrement: { + Reference e = expr.asLValue(); + e.loadInAccumulator(); + Instruction::Decrement dec; + bytecodeGenerator->addInstruction(dec); + if (_expr.accept(nx)) + return e.storeConsumeAccumulator(); + else + return e.storeRetainAccumulator(); } - - if (!left->asTemp() && !left->asArgLocal() && !left->asConst()) { - const unsigned t = _block->newTemp(); - setLocation(move(_block->TEMP(t), left), loc); - left = _block->TEMP(t); - } - - if (!right->asTemp() && !right->asArgLocal() && !right->asConst()) { - const unsigned t = _block->newTemp(); - setLocation(move(_block->TEMP(t), right), loc); - right = _block->TEMP(t); - } - - Q_ASSERT(left->asTemp() || left->asArgLocal() || left->asConst()); - Q_ASSERT(right->asTemp() || right->asArgLocal() || right->asConst()); - - return _block->BINOP(op, left, right); -} - -IR::Expr *Codegen::call(IR::Expr *base, IR::ExprList *args) -{ - if (hasError) - return 0; - base = reference(base); - return _block->CALL(base, args); -} - -IR::Stmt *Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op) -{ - if (hasError) - return 0; - - Q_ASSERT(target->isLValue()); - - if (op != IR::OpInvalid) { - return move(target, binop(op, target, source)); - } - - TempScope scope(_function); - - if (!source->asTemp() && !source->asConst() && !target->asTemp() && !source->asArgLocal() && !target->asArgLocal()) { - unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), source); - source = _block->TEMP(t); - } - if (source->asConst() && !target->asTemp() && !target->asArgLocal()) { - unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), source); - source = _block->TEMP(t); } - return _block->MOVE(target, source); + Q_UNREACHABLE(); } -IR::Stmt *Codegen::cjump(IR::Expr *cond, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) +void Codegen::addCJump() { - if (hasError) - return 0; - - TempScope scope(_function); - - if (! (cond->asTemp() || (cond->asBinop() && cjumpCanHandle(cond->asBinop()->op)) )) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), cond); - cond = _block->TEMP(t); - } - return _block->CJUMP(cond, iftrue, iffalse); + bytecodeGenerator->addCJumpInstruction(_expr.trueBlockFollowsCondition(), + _expr.iftrue(), _expr.iffalse()); } void Codegen::accept(Node *node) @@ -784,52 +257,71 @@ void Codegen::accept(Node *node) void Codegen::statement(Statement *ast) { - TempScope scope(_function); + RegisterScope scope(this); + + bytecodeGenerator->setLocation(ast->firstSourceLocation()); - _block->nextLocation = ast->firstSourceLocation(); + VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast); + qSwap(_volataleMemoryLocations, vLocs); accept(ast); + qSwap(_volataleMemoryLocations, vLocs); } void Codegen::statement(ExpressionNode *ast) { - TempScope scope(_function); + RegisterScope scope(this); if (! ast) { return; } else { Result r(nx); qSwap(_expr, r); + VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast); + qSwap(_volataleMemoryLocations, vLocs); + accept(ast); + + qSwap(_volataleMemoryLocations, vLocs); + qSwap(_expr, r); + if (hasError) return; - qSwap(_expr, r); - if (r.format == ex) { - if (r->asCall()) { - _block->EXP(*r); // the nest nx representation for calls is EXP(CALL(c..)) - } else if (r->asTemp() || r->asArgLocal()) { - // there is nothing to do - } else { - unsigned t = _block->newTemp(); - move(_block->TEMP(t), *r); - } - } + if (r.result().loadTriggersSideEffect()) + r.result().loadInAccumulator(); // triggers side effects } } -void Codegen::condition(ExpressionNode *ast, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) +void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *iftrue, + const BytecodeGenerator::Label *iffalse, bool trueBlockFollowsCondition) { - if (ast) { - Result r(iftrue, iffalse); - qSwap(_expr, r); - accept(ast); - qSwap(_expr, r); - if (r.format == ex) { - setLocation(cjump(*r, r.iftrue, r.iffalse), ast->firstSourceLocation()); - } + if (hasError) + return; + + if (!ast) + return; + + Result r(iftrue, iffalse, trueBlockFollowsCondition); + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + + if (hasError) + return; + + if (r.format() == ex) { + Q_ASSERT(iftrue == r.iftrue()); + Q_ASSERT(iffalse == r.iffalse()); + Q_ASSERT(r.result().isValid()); + bytecodeGenerator->setLocation(ast->firstSourceLocation()); + r.result().loadInAccumulator(); + if (r.trueBlockFollowsCondition()) + bytecodeGenerator->jumpFalse().link(*r.iffalse()); + else + bytecodeGenerator->jumpTrue().link(*r.iftrue()); } } -Codegen::Result Codegen::expression(ExpressionNode *ast) +Codegen::Reference Codegen::expression(ExpressionNode *ast) { Result r; if (ast) { @@ -837,7 +329,7 @@ Codegen::Result Codegen::expression(ExpressionNode *ast) accept(ast); qSwap(_expr, r); } - return r; + return r.result(); } Codegen::Result Codegen::sourceElement(SourceElement *ast) @@ -851,17 +343,6 @@ Codegen::Result Codegen::sourceElement(SourceElement *ast) return r; } -Codegen::UiMember Codegen::uiObjectMember(UiObjectMember *ast) -{ - UiMember m; - if (ast) { - qSwap(_uiMember, m); - accept(ast); - qSwap(_uiMember, m); - } - return m; -} - void Codegen::functionBody(FunctionBody *ast) { if (ast) @@ -877,36 +358,58 @@ void Codegen::program(Program *ast) void Codegen::sourceElements(SourceElements *ast) { + bool _requiresReturnValue = false; + qSwap(_requiresReturnValue, requiresReturnValue); for (SourceElements *it = ast; it; it = it->next) { + if (!it->next) + qSwap(_requiresReturnValue, requiresReturnValue); sourceElement(it->element); if (hasError) return; + if (StatementSourceElement *sse = AST::cast<StatementSourceElement *>(it->element)) { + if (AST::cast<ThrowStatement *>(sse->statement) || + AST::cast<ReturnStatement *>(sse->statement)) + return; + } + } +} + +void Codegen::statementList(StatementList *ast) +{ + bool _requiresReturnValue = requiresReturnValue; + requiresReturnValue = false; + for (StatementList *it = ast; it; it = it->next) { + if (!it->next || + it->next->statement->kind == Statement::Kind_BreakStatement || + it->next->statement->kind == Statement::Kind_ContinueStatement || + it->next->statement->kind == Statement::Kind_ReturnStatement) + requiresReturnValue = _requiresReturnValue; + statement(it->statement); + requiresReturnValue = false; + if (it->statement->kind == Statement::Kind_ThrowStatement || + it->statement->kind == Statement::Kind_BreakStatement || + it->statement->kind == Statement::Kind_ContinueStatement || + it->statement->kind == Statement::Kind_ReturnStatement) + // any code after those statements is unreachable + break; } + requiresReturnValue = _requiresReturnValue; } void Codegen::variableDeclaration(VariableDeclaration *ast) { - IR::Expr *initializer = 0; + RegisterScope scope(this); + if (!ast->expression) return; - Result expr = expression(ast->expression); + Reference rhs = expression(ast->expression); if (hasError) return; - Q_ASSERT(expr.code); - initializer = *expr; - - IR::Expr *lhs = identifier(ast->name.toString(), ast->identifierToken.startLine, - ast->identifierToken.startColumn); - - if (lhs->asArgLocal()) { - move(lhs, initializer); - } else { - TempScope scope(_function); - int initialized = _block->newTemp(); - move(_block->TEMP(initialized), initializer); - move(lhs, _block->TEMP(initialized)); - } + Reference lhs = referenceForName(ast->name.toString(), true); + //### if lhs is a temp, this won't generate a temp-to-temp move. Same for when rhs is a const + rhs.loadInAccumulator(); + lhs.storeConsumeAccumulator(); } void Codegen::variableDeclarationList(VariableDeclarationList *ast) @@ -919,175 +422,175 @@ void Codegen::variableDeclarationList(VariableDeclarationList *ast) bool Codegen::visit(ArgumentList *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(CaseBlock *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(CaseClause *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(CaseClauses *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(Catch *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(DefaultClause *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(ElementList *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(Elision *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(Finally *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(FormalParameterList *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(FunctionBody *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(Program *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(PropertyAssignmentList *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(PropertyNameAndValue *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(PropertyGetterSetter *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(SourceElements *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(StatementList *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(UiArrayMemberList *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(UiImport *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(UiHeaderItemList *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(UiPragma *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(UiObjectInitializer *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(UiObjectMemberList *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(UiParameterList *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(UiProgram *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(UiQualifiedId *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(UiQualifiedPragmaId *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(VariableDeclaration *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(VariableDeclarationList *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } @@ -1106,57 +609,47 @@ bool Codegen::visit(ArrayLiteral *ast) if (hasError) return false; - const unsigned t = _block->newTemp(); + RegisterScope scope(this); - TempScope scope(_function); + int argc = 0; + int args = -1; + auto push = [this, &argc, &args](AST::ExpressionNode *arg) { + int temp = bytecodeGenerator->newRegister(); + if (args == -1) + args = temp; + if (!arg) { + auto c = Reference::fromConst(this, Primitive::emptyValue().asReturnedValue()); + (void) c.storeOnStack(temp); + } else { + RegisterScope scope(this); + (void) expression(arg).storeOnStack(temp); + } + ++argc; + }; - IR::ExprList *args = 0; - IR::ExprList *current = 0; for (ElementList *it = ast->elements; it; it = it->next) { - for (Elision *elision = it->elision; elision; elision = elision->next) { - IR::ExprList *arg = _function->New<IR::ExprList>(); - if (!current) { - args = arg; - } else { - current->next = arg; - } - current = arg; - current->expr = _block->CONST(IR::MissingType, 0); - } - Result expr = expression(it->expression); - if (hasError) - return false; - IR::ExprList *arg = _function->New<IR::ExprList>(); - if (!current) { - args = arg; - } else { - current->next = arg; - } - current = arg; + for (Elision *elision = it->elision; elision; elision = elision->next) + push(0); - IR::Expr *exp = *expr; - if (exp->asTemp() || expr->asArgLocal() || exp->asConst()) { - current->expr = exp; - } else { - unsigned value = _block->newTemp(); - move(_block->TEMP(value), exp); - current->expr = _block->TEMP(value); - } + push(it->expression); + if (hasError) + return false; } - for (Elision *elision = ast->elision; elision; elision = elision->next) { - IR::ExprList *arg = _function->New<IR::ExprList>(); - if (!current) { - args = arg; - } else { - current->next = arg; - } - current = arg; - current->expr = _block->CONST(IR::MissingType, 0); + for (Elision *elision = ast->elision; elision; elision = elision->next) + push(0); + + if (args == -1) { + Q_ASSERT(argc == 0); + args = 0; } - move(_block->TEMP(t), _block->CALL(_block->NAME(IR::Name::builtin_define_array, 0, 0), args)); - _expr.code = _block->TEMP(t); + Instruction::DefineArray call; + call.argc = argc; + call.args = Moth::StackSlot::createRegister(args); + bytecodeGenerator->addInstruction(call); + _expr.setResult(Reference::fromAccumulator(this)); + return false; } @@ -1165,43 +658,41 @@ bool Codegen::visit(ArrayMemberExpression *ast) if (hasError) return false; - IR::Expr *base = *expression(ast->base); + Reference base = expression(ast->base); if (hasError) return false; - if (!isSimpleExpr(base)) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), base); - base = _block->TEMP(t); - } - - IR::Expr *index = *expression(ast->expression); - if (hasError) + base = base.storeOnStack(); + if (AST::StringLiteral *str = AST::cast<AST::StringLiteral *>(ast->expression)) { + QString s = str->value.toString(); + uint arrayIndex = QV4::String::toArrayIndex(s); + if (arrayIndex == UINT_MAX) { + _expr.setResult(Reference::fromMember(base, str->value.toString())); + return false; + } + Reference index = Reference::fromConst(this, QV4::Encode(arrayIndex)); + _expr.setResult(Reference::fromSubscript(base, index)); return false; - if (!isSimpleExpr(index)) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), index); - index = _block->TEMP(t); } - - _expr.code = _block->SUBSCRIPT(base, index); + Reference index = expression(ast->expression); + _expr.setResult(Reference::fromSubscript(base, index)); return false; } -static IR::AluOp baseOp(int op) +static QSOperator::Op baseOp(int op) { switch ((QSOperator::Op) op) { - case QSOperator::InplaceAnd: return IR::OpBitAnd; - case QSOperator::InplaceSub: return IR::OpSub; - case QSOperator::InplaceDiv: return IR::OpDiv; - case QSOperator::InplaceAdd: return IR::OpAdd; - case QSOperator::InplaceLeftShift: return IR::OpLShift; - case QSOperator::InplaceMod: return IR::OpMod; - case QSOperator::InplaceMul: return IR::OpMul; - case QSOperator::InplaceOr: return IR::OpBitOr; - case QSOperator::InplaceRightShift: return IR::OpRShift; - case QSOperator::InplaceURightShift: return IR::OpURShift; - case QSOperator::InplaceXor: return IR::OpBitXor; - default: return IR::OpInvalid; + case QSOperator::InplaceAnd: return QSOperator::BitAnd; + case QSOperator::InplaceSub: return QSOperator::Sub; + case QSOperator::InplaceDiv: return QSOperator::Div; + case QSOperator::InplaceAdd: return QSOperator::Add; + case QSOperator::InplaceLeftShift: return QSOperator::LShift; + case QSOperator::InplaceMod: return QSOperator::Mod; + case QSOperator::InplaceMul: return QSOperator::Mul; + case QSOperator::InplaceOr: return QSOperator::BitOr; + case QSOperator::InplaceRightShift: return QSOperator::RShift; + case QSOperator::InplaceURightShift: return QSOperator::URShift; + case QSOperator::InplaceXor: return QSOperator::BitXor; + default: return QSOperator::Invalid; } } @@ -1212,89 +703,89 @@ bool Codegen::visit(BinaryExpression *ast) if (ast->op == QSOperator::And) { if (_expr.accept(cx)) { - IR::BasicBlock *iftrue = _function->newBasicBlock(exceptionHandler()); - condition(ast->left, iftrue, _expr.iffalse); - _block = iftrue; - condition(ast->right, _expr.iftrue, _expr.iffalse); + auto iftrue = bytecodeGenerator->newLabel(); + condition(ast->left, &iftrue, _expr.iffalse(), true); + iftrue.link(); + condition(ast->right, _expr.iftrue(), _expr.iffalse(), _expr.trueBlockFollowsCondition()); } else { - IR::BasicBlock *iftrue = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler()); - - const unsigned r = _block->newTemp(); + auto iftrue = bytecodeGenerator->newLabel(); + auto endif = bytecodeGenerator->newLabel(); - Result lhs = expression(ast->left); + Reference left = expression(ast->left); if (hasError) return false; - move(_block->TEMP(r), *lhs); - setLocation(cjump(_block->TEMP(r), iftrue, endif), ast->operatorToken); - _block = iftrue; - Result rhs = expression(ast->right); + left.loadInAccumulator(); + + bytecodeGenerator->setLocation(ast->operatorToken); + bytecodeGenerator->jumpFalse().link(endif); + iftrue.link(); + + Reference right = expression(ast->right); if (hasError) return false; - move(_block->TEMP(r), *rhs); - _block->JUMP(endif); + right.loadInAccumulator(); - _expr.code = _block->TEMP(r); - _block = endif; + endif.link(); + + _expr.setResult(Reference::fromAccumulator(this)); } return false; } else if (ast->op == QSOperator::Or) { if (_expr.accept(cx)) { - IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler()); - condition(ast->left, _expr.iftrue, iffalse); - _block = iffalse; - condition(ast->right, _expr.iftrue, _expr.iffalse); + auto iffalse = bytecodeGenerator->newLabel(); + condition(ast->left, _expr.iftrue(), &iffalse, false); + iffalse.link(); + condition(ast->right, _expr.iftrue(), _expr.iffalse(), _expr.trueBlockFollowsCondition()); } else { - IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler()); + auto iffalse = bytecodeGenerator->newLabel(); + auto endif = bytecodeGenerator->newLabel(); - const unsigned r = _block->newTemp(); - Result lhs = expression(ast->left); + Reference left = expression(ast->left); if (hasError) return false; - move(_block->TEMP(r), *lhs); - setLocation(cjump(_block->TEMP(r), endif, iffalse), ast->operatorToken); - _block = iffalse; - Result rhs = expression(ast->right); + left.loadInAccumulator(); + + bytecodeGenerator->setLocation(ast->operatorToken); + bytecodeGenerator->jumpTrue().link(endif); + iffalse.link(); + + Reference right = expression(ast->right); if (hasError) return false; - move(_block->TEMP(r), *rhs); - _block->JUMP(endif); + right.loadInAccumulator(); + + endif.link(); - _block = endif; - _expr.code = _block->TEMP(r); + _expr.setResult(Reference::fromAccumulator(this)); } return false; } - IR::Expr* left = *expression(ast->left); + Reference left = expression(ast->left); if (hasError) return false; switch (ast->op) { case QSOperator::Or: case QSOperator::And: + Q_UNREACHABLE(); // handled separately above break; case QSOperator::Assign: { + if (!left.isLValue()) { + throwReferenceError(ast->operatorToken, QStringLiteral("left-hand side of assignment operator is not an lvalue")); + return false; + } + left = left.asLValue(); if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation())) return false; - Result right = expression(ast->right); + expression(ast->right).loadInAccumulator(); if (hasError) return false; - if (!left->isLValue()) { - throwReferenceError(ast->operatorToken, QStringLiteral("left-hand side of assignment operator is not an lvalue")); - return false; - } - - if (_expr.accept(nx)) { - move(left, *right); - } else { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), *right); - move(left, _block->TEMP(t)); - _expr.code = _block->TEMP(t); - } + if (_expr.accept(nx)) + _expr.setResult(left.storeConsumeAccumulator()); + else + _expr.setResult(left.storeRetainAccumulator()); break; } @@ -1311,23 +802,36 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::InplaceXor: { if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation())) return false; - Result right = expression(ast->right); - if (hasError) - return false; - if (!left->isLValue()) { + + if (!left.isLValue()) { throwSyntaxError(ast->operatorToken, QStringLiteral("left-hand side of inplace operator is not an lvalue")); return false; } + left = left.asLValue(); + + Reference tempLeft = left.storeOnStack(); + Reference right = expression(ast->right); + + if (hasError) + return false; + + binopHelper(baseOp(ast->op), tempLeft, right).loadInAccumulator(); + _expr.setResult(left.storeRetainAccumulator()); - TempScope scope(_function); - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), *right); - move(left, _block->TEMP(t), baseOp(ast->op)); - if (!_expr.accept(nx)) - _expr.code = left; break; } + case QSOperator::BitAnd: + case QSOperator::BitOr: + case QSOperator::BitXor: + if (left.isConst()) { + Reference right = expression(ast->right); + if (hasError) + return false; + _expr.setResult(binopHelper(static_cast<QSOperator::Op>(ast->op), right, left)); + break; + } + // intentional fall-through! case QSOperator::In: case QSOperator::InstanceOf: case QSOperator::Equal: @@ -1337,55 +841,408 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::Le: case QSOperator::Lt: case QSOperator::StrictEqual: - case QSOperator::StrictNotEqual: { - TempScope scope(_function); - if (!left->asTemp() && !left->asArgLocal() && !left->asConst()) { - const unsigned t = _block->newTemp(); - setLocation(move(_block->TEMP(t), left), ast->operatorToken); - left = _block->TEMP(t); + case QSOperator::StrictNotEqual: + case QSOperator::Add: + case QSOperator::Div: + case QSOperator::Mod: + case QSOperator::Mul: + case QSOperator::Sub: + case QSOperator::LShift: + case QSOperator::RShift: + case QSOperator::URShift: { + Reference right; + if (AST::NumericLiteral *rhs = AST::cast<AST::NumericLiteral *>(ast->right)) { + visit(rhs); + right = _expr.result(); + } else { + left = left.storeOnStack(); // force any loads of the lhs, so the rhs won't clobber it + right = expression(ast->right); } - - Result right = expression(ast->right); if (hasError) return false; - if (_expr.accept(cx)) { - setLocation(cjump(binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken), _expr.iftrue, _expr.iffalse), ast->operatorToken); + _expr.setResult(binopHelper(static_cast<QSOperator::Op>(ast->op), left, right)); + + break; + } + + } // switch + + return false; +} + +Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Reference &right) +{ + switch (oper) { + case QSOperator::Add: { + //### Todo: when we add type hints, we can generate an Increment when both the lhs is a number and the rhs == 1 + left = left.storeOnStack(); + right.loadInAccumulator(); + Instruction::Add add; + add.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(add); + break; + } + case QSOperator::Sub: { + if (right.isConst() && right.constant == Encode(int(1))) { + left.loadInAccumulator(); + bytecodeGenerator->addInstruction(Instruction::Decrement()); } else { - _expr.code = binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken); + left = left.storeOnStack(); + right.loadInAccumulator(); + Instruction::Sub sub; + sub.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(sub); } break; } - - case QSOperator::Add: + case QSOperator::Mul: { + left = left.storeOnStack(); + right.loadInAccumulator(); + Instruction::Mul mul; + mul.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(mul); + break; + } + case QSOperator::Div: { + left = left.storeOnStack(); + right.loadInAccumulator(); + Instruction::Div div; + div.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(div); + break; + } + case QSOperator::Mod: { + left = left.storeOnStack(); + right.loadInAccumulator(); + Instruction::Mod mod; + mod.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(mod); + break; + } case QSOperator::BitAnd: + if (right.isConst()) { + int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32(); + if (left.isConst()) { + int result = Primitive::fromReturnedValue(left.constant).toInt32() & rightAsInt; + return Reference::fromConst(this, Encode(result)); + } + left.loadInAccumulator(); + Instruction::BitAndConst bitAnd; + bitAnd.rhs = rightAsInt; + bytecodeGenerator->addInstruction(bitAnd); + } else { + right.loadInAccumulator(); + Instruction::BitAnd bitAnd; + bitAnd.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(bitAnd); + } + break; case QSOperator::BitOr: + if (right.isConst()) { + int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32(); + if (left.isConst()) { + int result = Primitive::fromReturnedValue(left.constant).toInt32() | rightAsInt; + return Reference::fromConst(this, Encode(result)); + } + left.loadInAccumulator(); + Instruction::BitOrConst bitOr; + bitOr.rhs = rightAsInt; + bytecodeGenerator->addInstruction(bitOr); + } else { + right.loadInAccumulator(); + Instruction::BitOr bitOr; + bitOr.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(bitOr); + } + break; case QSOperator::BitXor: - case QSOperator::Div: - case QSOperator::LShift: - case QSOperator::Mod: - case QSOperator::Mul: + if (right.isConst()) { + int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32(); + if (left.isConst()) { + int result = Primitive::fromReturnedValue(left.constant).toInt32() ^ rightAsInt; + return Reference::fromConst(this, Encode(result)); + } + left.loadInAccumulator(); + Instruction::BitXorConst bitXor; + bitXor.rhs = rightAsInt; + bytecodeGenerator->addInstruction(bitXor); + } else { + right.loadInAccumulator(); + Instruction::BitXor bitXor; + bitXor.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(bitXor); + } + break; + case QSOperator::URShift: + if (right.isConst()) { + left.loadInAccumulator(); + Instruction::UShrConst ushr; + ushr.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f; + bytecodeGenerator->addInstruction(ushr); + } else { + right.loadInAccumulator(); + Instruction::UShr ushr; + ushr.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(ushr); + } + break; case QSOperator::RShift: - case QSOperator::Sub: - case QSOperator::URShift: { - TempScope scope(_function); - if (!left->asTemp() && !left->asArgLocal() && !left->asConst()) { - const unsigned t = _block->newTemp(); - setLocation(move(_block->TEMP(t), left), ast->operatorToken); - left = _block->TEMP(t); + if (right.isConst()) { + left.loadInAccumulator(); + Instruction::ShrConst shr; + shr.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f; + bytecodeGenerator->addInstruction(shr); + } else { + right.loadInAccumulator(); + Instruction::Shr shr; + shr.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(shr); + } + break; + case QSOperator::LShift: + if (right.isConst()) { + left.loadInAccumulator(); + Instruction::ShlConst shl; + shl.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f; + bytecodeGenerator->addInstruction(shl); + } else { + right.loadInAccumulator(); + Instruction::Shl shl; + shl.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(shl); } + break; + case QSOperator::InstanceOf: { + Instruction::CmpInstanceOf binop; + left = left.storeOnStack(); + right.loadInAccumulator(); + binop.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(binop); + break; + } + case QSOperator::In: { + Instruction::CmpIn binop; + left = left.storeOnStack(); + right.loadInAccumulator(); + binop.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(binop); + break; + } + case QSOperator::StrictEqual: { + if (_expr.accept(cx)) + return jumpBinop(oper, left, right); - Result right = expression(ast->right); - if (hasError) - return false; + Instruction::CmpStrictEqual cmp; + left = left.storeOnStack(); + right.loadInAccumulator(); + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + break; + } + case QSOperator::StrictNotEqual: { + if (_expr.accept(cx)) + return jumpBinop(oper, left, right); + + Instruction::CmpStrictNotEqual cmp; + left = left.storeOnStack(); + right.loadInAccumulator(); + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + break; + } + case QSOperator::Equal: { + if (_expr.accept(cx)) + return jumpBinop(oper, left, right); - _expr.code = binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken); + Instruction::CmpEq cmp; + left = left.storeOnStack(); + right.loadInAccumulator(); + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); break; } + case QSOperator::NotEqual: { + if (_expr.accept(cx)) + return jumpBinop(oper, left, right); - } // switch + Instruction::CmpNe cmp; + left = left.storeOnStack(); + right.loadInAccumulator(); + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + break; + } + case QSOperator::Gt: { + if (_expr.accept(cx)) + return jumpBinop(oper, left, right); - return false; + Instruction::CmpGt cmp; + left = left.storeOnStack(); + right.loadInAccumulator(); + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + break; + } + case QSOperator::Ge: { + if (_expr.accept(cx)) + return jumpBinop(oper, left, right); + + Instruction::CmpGe cmp; + left = left.storeOnStack(); + right.loadInAccumulator(); + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + break; + } + case QSOperator::Lt: { + if (_expr.accept(cx)) + return jumpBinop(oper, left, right); + + Instruction::CmpLt cmp; + left = left.storeOnStack(); + right.loadInAccumulator(); + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + break; + } + case QSOperator::Le: + if (_expr.accept(cx)) + return jumpBinop(oper, left, right); + + Instruction::CmpLe cmp; + left = left.storeOnStack(); + right.loadInAccumulator(); + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + break; + default: + Q_UNREACHABLE(); + } + + return Reference::fromAccumulator(this); +} + +static QSOperator::Op operatorForSwappedOperands(QSOperator::Op oper) +{ + switch (oper) { + case QSOperator::StrictEqual: return QSOperator::StrictEqual; + case QSOperator::StrictNotEqual: return QSOperator::StrictNotEqual; + case QSOperator::Equal: return QSOperator::Equal; + case QSOperator::NotEqual: return QSOperator::NotEqual; + case QSOperator::Gt: return QSOperator::Le; + case QSOperator::Ge: return QSOperator::Lt; + case QSOperator::Lt: return QSOperator::Ge; + case QSOperator::Le: return QSOperator::Gt; + default: Q_UNIMPLEMENTED(); return QSOperator::Invalid; + } +} + +Codegen::Reference Codegen::jumpBinop(QSOperator::Op oper, Reference &left, Reference &right) +{ + if (left.isConst()) { + oper = operatorForSwappedOperands(oper); + qSwap(left, right); + } + + if (right.isConst() && (oper == QSOperator::Equal || oper == QSOperator::NotEqual)) { + Value c = Primitive::fromReturnedValue(right.constant); + if (c.isNull() || c.isUndefined()) { + left.loadInAccumulator(); + if (oper == QSOperator::Equal) { + Instruction::CmpEqNull cmp; + bytecodeGenerator->addInstruction(cmp); + addCJump(); + return Reference(); + } else if (oper == QSOperator::NotEqual) { + Instruction::CmpNeNull cmp; + bytecodeGenerator->addInstruction(cmp); + addCJump(); + return Reference(); + } + } else if (c.isInt32()) { + left.loadInAccumulator(); + if (oper == QSOperator::Equal) { + Instruction::CmpEqInt cmp; + cmp.lhs = c.int_32(); + bytecodeGenerator->addInstruction(cmp); + addCJump(); + return Reference(); + } else if (oper == QSOperator::NotEqual) { + Instruction::CmpNeInt cmp; + cmp.lhs = c.int_32(); + bytecodeGenerator->addInstruction(cmp); + addCJump(); + return Reference(); + } + + } + } + + left = left.storeOnStack(); + right.loadInAccumulator(); + + switch (oper) { + case QSOperator::StrictEqual: { + Instruction::CmpStrictEqual cmp; + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + addCJump(); + break; + } + case QSOperator::StrictNotEqual: { + Instruction::CmpStrictNotEqual cmp; + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + addCJump(); + break; + } + case QSOperator::Equal: { + Instruction::CmpEq cmp; + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + addCJump(); + break; + } + case QSOperator::NotEqual: { + Instruction::CmpNe cmp; + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + addCJump(); + break; + } + case QSOperator::Gt: { + Instruction::CmpGt cmp; + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + addCJump(); + break; + } + case QSOperator::Ge: { + Instruction::CmpGe cmp; + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + addCJump(); + break; + } + case QSOperator::Lt: { + Instruction::CmpLt cmp; + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + addCJump(); + break; + } + case QSOperator::Le: { + Instruction::CmpLe cmp; + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + addCJump(); + break; + } + default: + Q_UNREACHABLE(); + } + return Reference(); } bool Codegen::visit(CallExpression *ast) @@ -1393,54 +1250,138 @@ bool Codegen::visit(CallExpression *ast) if (hasError) return false; - Result base = expression(ast->base); - IR::ExprList *args = 0, **args_it = &args; - for (ArgumentList *it = ast->arguments; it; it = it->next) { - Result arg = expression(it->expression); - if (hasError) - return false; - IR::Expr *actual = argument(*arg); - *args_it = _function->New<IR::ExprList>(); - (*args_it)->init(actual); - args_it = &(*args_it)->next; + RegisterScope scope(this); + + Reference base = expression(ast->base); + if (hasError) + return false; + switch (base.type) { + case Reference::Member: + case Reference::Subscript: + base = base.asLValue(); + break; + case Reference::Name: + break; + default: + base = base.storeOnStack(); + break; } + + auto calldata = pushArgs(ast->arguments); if (hasError) return false; - _expr.code = call(*base, args); + + //### Do we really need all these call instructions? can's we load the callee in a temp? + if (base.type == Reference::Member) { + if (useFastLookups) { + Instruction::CallPropertyLookup call; + call.base = base.propertyBase.stackSlot(); + call.lookupIndex = registerGetterLookup(base.propertyNameIndex); + call.argc = calldata.argc; + call.argv = calldata.argv; + bytecodeGenerator->addInstruction(call); + } else { + Instruction::CallProperty call; + call.base = base.propertyBase.stackSlot(); + call.name = base.propertyNameIndex; + call.argc = calldata.argc; + call.argv = calldata.argv; + bytecodeGenerator->addInstruction(call); + } + } else if (base.type == Reference::Subscript) { + Instruction::CallElement call; + call.base = base.elementBase; + call.index = base.elementSubscript.stackSlot(); + call.argc = calldata.argc; + call.argv = calldata.argv; + bytecodeGenerator->addInstruction(call); + } else if (base.type == Reference::Name) { + if (base.name == QStringLiteral("eval")) { + Instruction::CallPossiblyDirectEval call; + call.argc = calldata.argc; + call.argv = calldata.argv; + bytecodeGenerator->addInstruction(call); + } else if (useFastLookups && base.global) { + Instruction::CallGlobalLookup call; + call.index = registerGlobalGetterLookup(base.nameAsIndex()); + call.argc = calldata.argc; + call.argv = calldata.argv; + bytecodeGenerator->addInstruction(call); + } else { + Instruction::CallName call; + call.name = base.nameAsIndex(); + call.argc = calldata.argc; + call.argv = calldata.argv; + bytecodeGenerator->addInstruction(call); + } + } else { + base.loadInAccumulator(); + + Instruction::CallValue call; + call.argc = calldata.argc; + call.argv = calldata.argv; + bytecodeGenerator->addInstruction(call); + } + + _expr.setResult(Reference::fromAccumulator(this)); return false; } +Codegen::Arguments Codegen::pushArgs(ArgumentList *args) +{ + int argc = 0; + for (ArgumentList *it = args; it; it = it->next) + ++argc; + + if (!argc) + return { 0, 0 }; + + int calldata = bytecodeGenerator->newRegisterArray(argc); + + argc = 0; + for (ArgumentList *it = args; it; it = it->next) { + RegisterScope scope(this); + Reference e = expression(it->expression); + if (hasError) + break; + if (!argc && !it->next) { + // avoid copy for functions taking a single argument + if (e.isStackSlot()) + return { 1, e.stackSlot() }; + } + (void) e.storeOnStack(calldata + argc); + ++argc; + } + + return { argc, calldata }; +} + bool Codegen::visit(ConditionalExpression *ast) { if (hasError) return true; - IR::BasicBlock *iftrue = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler()); + RegisterScope scope(this); - const unsigned t = _block->newTemp(); - TempScope scope(_function); + BytecodeGenerator::Label iftrue = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label iffalse = bytecodeGenerator->newLabel(); + condition(ast->expression, &iftrue, &iffalse, true); - condition(ast->expression, iftrue, iffalse); - - _block = iftrue; - Result ok = expression(ast->ok); + iftrue.link(); + Reference ok = expression(ast->ok); if (hasError) return false; - move(_block->TEMP(t), *ok); - _block->JUMP(endif); + ok.loadInAccumulator(); + BytecodeGenerator::Jump jump_endif = bytecodeGenerator->jump(); - _block = iffalse; - Result ko = expression(ast->ko); + iffalse.link(); + Reference ko = expression(ast->ko); if (hasError) return false; - move(_block->TEMP(t), *ko); - _block->JUMP(endif); - - _block = endif; + ko.loadInAccumulator(); - _expr.code = _block->TEMP(t); + jump_endif.link(); + _expr.setResult(Reference::fromAccumulator(this)); return false; } @@ -1450,48 +1391,59 @@ bool Codegen::visit(DeleteExpression *ast) if (hasError) return false; - IR::Expr* expr = *expression(ast->expression); + Reference expr = expression(ast->expression); if (hasError) return false; - // Temporaries cannot be deleted - IR::ArgLocal *al = expr->asArgLocal(); - if (al && al->index < static_cast<unsigned>(_variableEnvironment->members.size())) { + + switch (expr.type) { + case Reference::StackSlot: + if (!expr.stackSlotIsLocalOrArgument) + break; + // fall through + case Reference::ScopedLocal: // Trying to delete a function argument might throw. - if (_function->isStrict) { + if (_context->isStrict) { throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode.")); return false; } - _expr.code = _block->CONST(IR::BoolType, 0); + _expr.setResult(Reference::fromConst(this, QV4::Encode(false))); return false; - } - if (_function->isStrict && expr->asName()) { - throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode.")); + case Reference::Name: { + if (_context->isStrict) { + throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode.")); + return false; + } + Instruction::DeleteName del; + del.name = expr.nameAsIndex(); + bytecodeGenerator->addInstruction(del); + _expr.setResult(Reference::fromAccumulator(this)); return false; } - - // [[11.4.1]] Return true if it's not a reference - if (expr->asConst() || expr->asString()) { - _expr.code = _block->CONST(IR::BoolType, 1); + case Reference::Member: { + //### maybe add a variant where the base can be in the accumulator? + expr = expr.asLValue(); + Instruction::DeleteMember del; + del.base = expr.propertyBase.stackSlot(); + del.member = expr.propertyNameIndex; + bytecodeGenerator->addInstruction(del); + _expr.setResult(Reference::fromAccumulator(this)); return false; } - - // Return values from calls are also not a reference, but we have to - // perform the call to allow for side effects. - if (expr->asCall()) { - _block->EXP(expr); - _expr.code = _block->CONST(IR::BoolType, 1); + case Reference::Subscript: { + //### maybe add a variant where the index can be in the accumulator? + expr = expr.asLValue(); + Instruction::DeleteSubscript del; + del.base = expr.elementBase; + del.index = expr.elementSubscript.stackSlot(); + bytecodeGenerator->addInstruction(del); + _expr.setResult(Reference::fromAccumulator(this)); return false; } - if (expr->asTemp() || - (expr->asArgLocal() && - expr->asArgLocal()->index >= static_cast<unsigned>(_variableEnvironment->members.size()))) { - _expr.code = _block->CONST(IR::BoolType, 1); - return false; + default: + break; } - - IR::ExprList *args = _function->New<IR::ExprList>(); - args->init(reference(expr)); - _expr.code = call(_block->NAME(IR::Name::builtin_delete, ast->deleteToken.startLine, ast->deleteToken.startColumn), args); + // [[11.4.1]] Return true if it's not a reference + _expr.setResult(Reference::fromConst(this, QV4::Encode(true))); return false; } @@ -1500,11 +1452,7 @@ bool Codegen::visit(FalseLiteral *) if (hasError) return false; - if (_expr.accept(cx)) { - _block->JUMP(_expr.iffalse); - } else { - _expr.code = _block->CONST(IR::BoolType, 0); - } + _expr.setResult(Reference::fromConst(this, QV4::Encode(false))); return false; } @@ -1513,9 +1461,10 @@ bool Codegen::visit(FieldMemberExpression *ast) if (hasError) return false; - Result base = expression(ast->base); - if (!hasError) - _expr.code = member(*base, _function->newString(ast->name.toString())); + Reference base = expression(ast->base); + if (hasError) + return false; + _expr.setResult(Reference::fromMember(base, ast->name.toString())); return false; } @@ -1524,64 +1473,109 @@ bool Codegen::visit(FunctionExpression *ast) if (hasError) return false; - TempScope scope(_function); + RegisterScope scope(this); int function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); - _expr.code = _block->CLOSURE(function); + loadClosure(function); + _expr.setResult(Reference::fromAccumulator(this)); return false; } -IR::Expr *Codegen::identifier(const QString &name, int line, int col) +Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs) { - if (hasError) - return 0; - - uint scope = 0; - Environment *e = _variableEnvironment; - IR::Function *f = _function; + int scope = 0; + Context *c = _context; - while (f && e->parent) { - if (f->insideWithOrCatch || (f->isNamedExpression && QStringRef(f->name) == name)) - return _block->NAME(name, line, col); - - int index = e->findMember(name); - Q_ASSERT (index < e->members.size()); - if (index != -1) { - IR::ArgLocal *al = _block->LOCAL(index, scope); - if (name == QLatin1String("arguments") || name == QLatin1String("eval")) - al->isArgumentsOrEval = true; - return al; + // skip the innermost context if it's simple (as the runtime won't + // create a context for it + if (c->canUseSimpleCall()) { + Context::Member m = c->findMember(name); + if (m.type != Context::UndefinedMember) { + Q_ASSERT((!m.canEscape)); + Reference r = Reference::fromStackSlot(this, m.index, true /*isLocal*/); + if (name == QLatin1String("arguments") || name == QLatin1String("eval")) { + r.isArgOrEval = true; + 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 = c->findArgument(name); + if (argIdx != -1) { + Q_ASSERT(!c->argumentsCanEscape && (c->usesArgumentsObject != Context::ArgumentsObjectUsed || c->isStrict)); + return Reference::fromArgument(this, argIdx, _volataleMemoryLocations.isVolatile(name)); + } + c = c->parent; + } + + while (c->parent) { + if (c->forceLookupByName()) + goto loadByName; + + Context::Member m = c->findMember(name); + if (m.type != Context::UndefinedMember) { + Reference r = m.canEscape ? Reference::fromScopedLocal(this, m.index, scope) + : Reference::fromStackSlot(this, m.index, true /*isLocal*/); + if (name == QLatin1String("arguments") || name == QLatin1String("eval")) { + r.isArgOrEval = true; + 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 = c->findArgument(name); + if (argIdx != -1) { + if (c->argumentsCanEscape || c->usesArgumentsObject == Context::ArgumentsObjectUsed) { + int idx = argIdx + c->locals.size(); + return Reference::fromScopedLocal(this, idx, scope); + } else { + Q_ASSERT(scope == 0); + return Reference::fromArgument(this, argIdx, _volataleMemoryLocations.isVolatile(name)); + } } - const int argIdx = f->indexOfArgument(QStringRef(&name)); - if (argIdx != -1) - return _block->ARG(argIdx, scope); - if (!f->isStrict && f->hasDirectEval) - return _block->NAME(name, line, col); + if (!c->isStrict && c->hasDirectEval) + goto loadByName; ++scope; - e = e->parent; - f = f->outer; + c = c->parent; } - // This hook allows implementing QML lookup semantics - if (IR::Expr *fallback = fallbackNameLookup(name, line, col)) - return fallback; + { + // This hook allows implementing QML lookup semantics + Reference fallback = fallbackNameLookup(name); + if (fallback.type != Reference::Invalid) + return fallback; + } - if (!e->parent && (!f || !f->insideWithOrCatch) && _variableEnvironment->compilationMode != EvalCode && e->compilationMode != QmlBinding) - return _block->GLOBALNAME(name, line, col); + if (!c->parent && !c->forceLookupByName() && _context->compilationMode != EvalCode && c->compilationMode != QmlBinding) { + Reference r = Reference::fromName(this, name); + r.global = true; + return r; + } // global context or with. Lookup by name - return _block->NAME(name, line, col); + loadByName: + return Reference::fromName(this, name); +} +void Codegen::loadClosure(int closureId) +{ + if (closureId >= 0) { + Instruction::LoadClosure load; + load.value = closureId; + bytecodeGenerator->addInstruction(load); + } else { + Reference::fromConst(this, Encode::undefined()).loadInAccumulator(); + } } -IR::Expr *Codegen::fallbackNameLookup(const QString &name, int line, int col) +Codegen::Reference Codegen::fallbackNameLookup(const QString &name) { Q_UNUSED(name) - Q_UNUSED(line) - Q_UNUSED(col) - return 0; + return Reference(); } bool Codegen::visit(IdentifierExpression *ast) @@ -1589,7 +1583,7 @@ bool Codegen::visit(IdentifierExpression *ast) if (hasError) return false; - _expr.code = identifier(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn); + _expr.setResult(referenceForName(ast->name.toString(), false)); return false; } @@ -1606,18 +1600,21 @@ bool Codegen::visit(NewExpression *ast) { if (hasError) return false; - TempScope scope(_function); - Result base = expression(ast->expression); + RegisterScope scope(this); + + Reference base = expression(ast->expression); if (hasError) return false; - IR::Expr *expr = *base; - if (expr && !expr->asTemp() && !expr->asArgLocal() && !expr->asName() && !expr->asMember()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), expr); - expr = _block->TEMP(t); - } - _expr.code = _block->NEW(expr, 0); + //### Maybe create a ConstructA that takes an accumulator? + base = base.storeOnStack(); + + Instruction::Construct create; + create.func = base.stackSlot(); + create.argc = 0; + create.argv = 0; + bytecodeGenerator->addInstruction(create); + _expr.setResult(Reference::fromAccumulator(this)); return false; } @@ -1626,32 +1623,23 @@ bool Codegen::visit(NewMemberExpression *ast) if (hasError) return false; - const unsigned t = _block->newTemp(); + RegisterScope scope(this); - TempScope scope(_function); + Reference base = expression(ast->base); + if (hasError) + return false; + base = base.storeOnStack(); - Result base = expression(ast->base); + auto calldata = pushArgs(ast->arguments); if (hasError) return false; - IR::Expr *expr = *base; - if (expr && !expr->asTemp() && !expr->asArgLocal() && !expr->asName() && !expr->asMember()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), expr); - expr = _block->TEMP(t); - } - IR::ExprList *args = 0, **args_it = &args; - for (ArgumentList *it = ast->arguments; it; it = it->next) { - Result arg = expression(it->expression); - if (hasError) - return false; - IR::Expr *actual = argument(*arg); - *args_it = _function->New<IR::ExprList>(); - (*args_it)->init(actual); - args_it = &(*args_it)->next; - } - move(_block->TEMP(t), _block->NEW(expr, args)); - _expr.code = _block->TEMP(t); + Instruction::Construct create; + create.func = base.stackSlot(); + create.argc = calldata.argc; + create.argv = calldata.argv; + bytecodeGenerator->addInstruction(create); + _expr.setResult(Reference::fromAccumulator(this)); return false; } @@ -1660,14 +1648,7 @@ bool Codegen::visit(NotExpression *ast) if (hasError) return false; - const unsigned r = _block->newTemp(); - TempScope scope(_function); - - Result expr = expression(ast->expression); - if (hasError) - return false; - setLocation(move(_block->TEMP(r), unop(IR::OpNot, *expr, ast->notToken)), ast->notToken); - _expr.code = _block->TEMP(r); + _expr.setResult(unop(Not, expression(ast->expression))); return false; } @@ -1676,8 +1657,10 @@ bool Codegen::visit(NullExpression *) if (hasError) return false; - if (_expr.accept(cx)) _block->JUMP(_expr.iffalse); - else _expr.code = _block->CONST(IR::NullType, 0); + if (_expr.accept(cx)) + bytecodeGenerator->jump().link(*_expr.iffalse()); + else + _expr.setResult(Reference::fromConst(this, Encode::null())); return false; } @@ -1687,30 +1670,10 @@ bool Codegen::visit(NumericLiteral *ast) if (hasError) return false; - if (_expr.accept(cx)) { - if (ast->value) _block->JUMP(_expr.iftrue); - else _block->JUMP(_expr.iffalse); - } else { - _expr.code = _block->CONST(IR::NumberType, ast->value); - } + _expr.setResult(Reference::fromConst(this, QV4::Encode::smallestNumber(ast->value))); return false; } -struct ObjectPropertyValue { - ObjectPropertyValue() - : value(0) - , getter(-1) - , setter(-1) - {} - - IR::Expr *value; - int getter; // index in _module->functions or -1 if not set - int setter; - - bool hasGetter() const { return getter >= 0; } - bool hasSetter() const { return setter >= 0; } -}; - bool Codegen::visit(ObjectLiteral *ast) { if (hasError) @@ -1718,33 +1681,27 @@ bool Codegen::visit(ObjectLiteral *ast) QMap<QString, ObjectPropertyValue> valueMap; - const unsigned t = _block->newTemp(); - TempScope scope(_function); + RegisterScope scope(this); for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { QString name = it->assignment->name->asString(); if (PropertyNameAndValue *nv = AST::cast<AST::PropertyNameAndValue *>(it->assignment)) { - Result value = expression(nv->value); + Reference value = expression(nv->value); if (hasError) return false; + ObjectPropertyValue &v = valueMap[name]; - if (v.hasGetter() || v.hasSetter() || (_function->isStrict && v.value)) { + if (v.hasGetter() || v.hasSetter() || (_context->isStrict && v.rvalue.isValid())) { throwSyntaxError(nv->lastSourceLocation(), QStringLiteral("Illegal duplicate key '%1' in object literal").arg(name)); return false; } - if (IR::Const *c = (*value)->asConst()) { - valueMap[name].value = c; - } else { - unsigned t = _block->newTemp(); - move(_block->TEMP(t), *value); - valueMap[name].value = _block->TEMP(t); - } + v.rvalue = value.storeOnStack(); } else if (PropertyGetterSetter *gs = AST::cast<AST::PropertyGetterSetter *>(it->assignment)) { const int function = defineFunction(name, gs, gs->formals, gs->functionBody ? gs->functionBody->elements : 0); ObjectPropertyValue &v = valueMap[name]; - if (v.value || + if (v.rvalue.isValid() || (gs->type == PropertyGetterSetter::Getter && v.hasGetter()) || (gs->type == PropertyGetterSetter::Setter && v.hasSetter())) { throwSyntaxError(gs->lastSourceLocation(), @@ -1760,96 +1717,90 @@ bool Codegen::visit(ObjectLiteral *ast) } } - // The linked-list arguments to builtin_define_object_literal - // begin with a CONST counting the number of key/value pairs, followed by the - // key value pairs, followed by the array entries. - IR::ExprList *args = _function->New<IR::ExprList>(); - - IR::Const *entryCountParam = _function->New<IR::Const>(); - entryCountParam->init(IR::SInt32Type, 0); - args->expr = entryCountParam; - args->next = 0; - - IR::ExprList *keyValueEntries = 0; - IR::ExprList *currentKeyValueEntry = 0; - int keyValueEntryCount = 0; - IR::ExprList *arrayEntries = 0; + QVector<QString> nonArrayKey, arrayKeyWithValue, arrayKeyWithGetterSetter; + bool needSparseArray = false; // set to true if any array index is bigger than 16 - IR::ExprList *currentArrayEntry = 0; - - for (QMap<QString, ObjectPropertyValue>::iterator it = valueMap.begin(); it != valueMap.end(); ) { - IR::ExprList **currentPtr = 0; - uint keyAsIndex = QV4::String::toArrayIndex(it.key()); - if (keyAsIndex != UINT_MAX) { - if (!arrayEntries) { - arrayEntries = _function->New<IR::ExprList>(); - currentArrayEntry = arrayEntries; - } else { - currentArrayEntry->next = _function->New<IR::ExprList>(); - currentArrayEntry = currentArrayEntry->next; - } - currentPtr = ¤tArrayEntry; - IR::Const *idx = _function->New<IR::Const>(); - idx->init(IR::UInt32Type, keyAsIndex); - (*currentPtr)->expr = idx; + for (QMap<QString, ObjectPropertyValue>::iterator it = valueMap.begin(), eit = valueMap.end(); + it != eit; ++it) { + QString name = it.key(); + uint keyAsIndex = QV4::String::toArrayIndex(name); + if (keyAsIndex != std::numeric_limits<uint>::max()) { + it->keyAsIndex = keyAsIndex; + if (keyAsIndex > 16) + needSparseArray = true; + if (it->hasSetter() || it->hasGetter()) + arrayKeyWithGetterSetter.append(name); + else + arrayKeyWithValue.append(name); } else { - if (!keyValueEntries) { - keyValueEntries = _function->New<IR::ExprList>(); - currentKeyValueEntry = keyValueEntries; - } else { - currentKeyValueEntry->next = _function->New<IR::ExprList>(); - currentKeyValueEntry = currentKeyValueEntry->next; - } - currentPtr = ¤tKeyValueEntry; - (*currentPtr)->expr = _block->NAME(it.key(), 0, 0); - keyValueEntryCount++; + nonArrayKey.append(name); } + } - IR::ExprList *¤t = *currentPtr; - if (it->value) { - current->next = _function->New<IR::ExprList>(); - current = current->next; - current->expr = _block->CONST(IR::BoolType, true); - - current->next = _function->New<IR::ExprList>(); - current = current->next; - current->expr = it->value; + int args = -1; + auto push = [this, &args](const Reference &arg) { + int temp = bytecodeGenerator->newRegister(); + if (args == -1) + args = temp; + (void) arg.storeOnStack(temp); + }; + + QVector<QV4::Compiler::JSUnitGenerator::MemberInfo> members; + + Reference acc = Reference::fromAccumulator(this); + // generate the key/value pairs + for (const QString &key : qAsConst(nonArrayKey)) { + const ObjectPropertyValue &prop = valueMap[key]; + + if (prop.hasGetter() || prop.hasSetter()) { + Q_ASSERT(!prop.rvalue.isValid()); + loadClosure(prop.getter); + push(acc); + loadClosure(prop.setter); + push(acc); + members.append({ key, true }); } else { - current->next = _function->New<IR::ExprList>(); - current = current->next; - current->expr = _block->CONST(IR::BoolType, false); - - unsigned getter = _block->newTemp(); - unsigned setter = _block->newTemp(); - move(_block->TEMP(getter), it->hasGetter() ? _block->CLOSURE(it->getter) : _block->CONST(IR::UndefinedType, 0)); - move(_block->TEMP(setter), it->hasSetter() ? _block->CLOSURE(it->setter) : _block->CONST(IR::UndefinedType, 0)); - - current->next = _function->New<IR::ExprList>(); - current = current->next; - current->expr = _block->TEMP(getter); - current->next = _function->New<IR::ExprList>(); - current = current->next; - current->expr = _block->TEMP(setter); + Q_ASSERT(prop.rvalue.isValid()); + push(prop.rvalue); + members.append({ key, false }); } - - it = valueMap.erase(it); } - entryCountParam->value = keyValueEntryCount; + // generate array entries with values + for (const QString &key : qAsConst(arrayKeyWithValue)) { + const ObjectPropertyValue &prop = valueMap[key]; + Q_ASSERT(!prop.hasGetter() && !prop.hasSetter()); + push(Reference::fromConst(this, Encode(prop.keyAsIndex))); + push(prop.rvalue); + } - if (keyValueEntries) - args->next = keyValueEntries; - if (arrayEntries) { - if (currentKeyValueEntry) - currentKeyValueEntry->next = arrayEntries; - else - args->next = arrayEntries; + // generate array entries with both a value and a setter + for (const QString &key : qAsConst(arrayKeyWithGetterSetter)) { + const ObjectPropertyValue &prop = valueMap[key]; + Q_ASSERT(!prop.rvalue.isValid()); + push(Reference::fromConst(this, Encode(prop.keyAsIndex))); + loadClosure(prop.getter); + push(acc); + loadClosure(prop.setter); + push(acc); } - move(_block->TEMP(t), _block->CALL(_block->NAME(IR::Name::builtin_define_object_literal, - ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), args)); + int classId = jsUnitGenerator->registerJSClass(members); + + uint arrayGetterSetterCountAndFlags = arrayKeyWithGetterSetter.size(); + arrayGetterSetterCountAndFlags |= needSparseArray << 30; - _expr.code = _block->TEMP(t); + if (args == -1) + args = 0; + + Instruction::DefineObjectLiteral call; + call.internalClassId = classId; + call.arrayValueCount = arrayKeyWithValue.size(); + call.arrayGetterSetterCountAndFlags = arrayGetterSetterCountAndFlags; + call.args = Moth::StackSlot::createRegister(args); + bytecodeGenerator->addInstruction(call); + + _expr.setResult(Reference::fromAccumulator(this)); return false; } @@ -1858,26 +1809,17 @@ bool Codegen::visit(PostDecrementExpression *ast) if (hasError) return false; - Result expr = expression(ast->base); + Reference expr = expression(ast->base); if (hasError) return false; - if (!expr->isLValue()) { + if (!expr.isLValue()) { throwReferenceError(ast->base->lastSourceLocation(), QStringLiteral("Invalid left-hand side expression in postfix operation")); return false; } - if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken)) + if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->decrementToken)) return false; - const unsigned oldValue = _block->newTemp(); - setLocation(move(_block->TEMP(oldValue), unop(IR::OpUPlus, *expr, ast->decrementToken)), ast->decrementToken); - - TempScope scope(_function); - const unsigned newValue = _block->newTemp(); - setLocation(move(_block->TEMP(newValue), binop(IR::OpSub, _block->TEMP(oldValue), _block->CONST(IR::NumberType, 1), ast->decrementToken)), ast->decrementToken); - setLocation(move(*expr, _block->TEMP(newValue)), ast->decrementToken); - - if (!_expr.accept(nx)) - _expr.code = _block->TEMP(oldValue); + _expr.setResult(unop(PostDecrement, expr)); return false; } @@ -1887,54 +1829,35 @@ bool Codegen::visit(PostIncrementExpression *ast) if (hasError) return false; - Result expr = expression(ast->base); + Reference expr = expression(ast->base); if (hasError) return false; - if (!expr->isLValue()) { + if (!expr.isLValue()) { throwReferenceError(ast->base->lastSourceLocation(), QStringLiteral("Invalid left-hand side expression in postfix operation")); return false; } - if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken)) + if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->incrementToken)) return false; - const unsigned oldValue = _block->newTemp(); - setLocation(move(_block->TEMP(oldValue), unop(IR::OpUPlus, *expr, ast->incrementToken)), ast->incrementToken); - - TempScope scope(_function); - const unsigned newValue = _block->newTemp(); - setLocation(move(_block->TEMP(newValue), binop(IR::OpAdd, _block->TEMP(oldValue), _block->CONST(IR::NumberType, 1), ast->incrementToken)), ast->incrementToken); - setLocation(move(*expr, _block->TEMP(newValue)), ast->incrementToken); - - if (!_expr.accept(nx)) - _expr.code = _block->TEMP(oldValue); - + _expr.setResult(unop(PostIncrement, expr)); return false; } bool Codegen::visit(PreDecrementExpression *ast) -{ - if (hasError) +{ if (hasError) return false; - Result expr = expression(ast->expression); + Reference expr = expression(ast->expression); if (hasError) return false; - if (!expr->isLValue()) { + if (!expr.isLValue()) { throwReferenceError(ast->expression->lastSourceLocation(), QStringLiteral("Prefix ++ operator applied to value that is not a reference.")); return false; } - if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken)) + if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->decrementToken)) return false; - IR::Expr *op = binop(IR::OpSub, *expr, _block->CONST(IR::NumberType, 1), ast->decrementToken); - if (_expr.accept(nx)) { - setLocation(move(*expr, op), ast->decrementToken); - } else { - const unsigned t = _block->newTemp(); - setLocation(move(_block->TEMP(t), op), ast->decrementToken); - setLocation(move(*expr, _block->TEMP(t)), ast->decrementToken); - _expr.code = _block->TEMP(t); - } + _expr.setResult(unop(PreDecrement, expr)); return false; } @@ -1943,25 +1866,17 @@ bool Codegen::visit(PreIncrementExpression *ast) if (hasError) return false; - Result expr = expression(ast->expression); + Reference expr = expression(ast->expression); if (hasError) return false; - if (!expr->isLValue()) { + if (!expr.isLValue()) { throwReferenceError(ast->expression->lastSourceLocation(), QStringLiteral("Prefix ++ operator applied to value that is not a reference.")); return false; } - if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken)) + if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->incrementToken)) return false; - IR::Expr *op = binop(IR::OpAdd, unop(IR::OpUPlus, *expr, ast->incrementToken), _block->CONST(IR::NumberType, 1), ast->incrementToken); - if (_expr.accept(nx)) { - setLocation(move(*expr, op), ast->incrementToken); - } else { - const unsigned t = _block->newTemp(); - setLocation(move(_block->TEMP(t), op), ast->incrementToken); - setLocation(move(*expr, _block->TEMP(t)), ast->incrementToken); - _expr.code = _block->TEMP(t); - } + _expr.setResult(unop(PreIncrement, expr)); return false; } @@ -1970,7 +1885,13 @@ bool Codegen::visit(RegExpLiteral *ast) if (hasError) return false; - _expr.code = _block->REGEXP(_function->newString(ast->pattern.toString()), ast->flags); + auto r = Reference::fromAccumulator(this); + r.isReadonly = true; + _expr.setResult(r); + + Instruction::LoadRegExp instr; + instr.regExpId = jsUnitGenerator->registerRegExp(ast); + bytecodeGenerator->addInstruction(instr); return false; } @@ -1979,16 +1900,22 @@ bool Codegen::visit(StringLiteral *ast) if (hasError) return false; - _expr.code = _block->STRING(_function->newString(ast->value.toString())); + auto r = Reference::fromAccumulator(this); + r.isReadonly = true; + _expr.setResult(r); + + Instruction::LoadRuntimeString instr; + instr.stringId = registerString(ast->value.toString()); + bytecodeGenerator->addInstruction(instr); return false; } -bool Codegen::visit(ThisExpression *ast) +bool Codegen::visit(ThisExpression *) { if (hasError) return false; - _expr.code = _block->NAME(QStringLiteral("this"), ast->thisToken.startLine, ast->thisToken.startColumn); + _expr.setResult(Reference::fromThis(this)); return false; } @@ -1997,14 +1924,7 @@ bool Codegen::visit(TildeExpression *ast) if (hasError) return false; - const unsigned t = _block->newTemp(); - TempScope scope(_function); - - Result expr = expression(ast->expression); - if (hasError) - return false; - setLocation(move(_block->TEMP(t), unop(IR::OpCompl, *expr, ast->tildeToken)), ast->tildeToken); - _expr.code = _block->TEMP(t); + _expr.setResult(unop(Compl, expression(ast->expression))); return false; } @@ -2013,11 +1933,7 @@ bool Codegen::visit(TrueLiteral *) if (hasError) return false; - if (_expr.accept(cx)) { - _block->JUMP(_expr.iftrue); - } else { - _expr.code = _block->CONST(IR::BoolType, 1); - } + _expr.setResult(Reference::fromConst(this, QV4::Encode(true))); return false; } @@ -2026,14 +1942,24 @@ bool Codegen::visit(TypeOfExpression *ast) if (hasError) return false; - TempScope scope(_function); + RegisterScope scope(this); - Result expr = expression(ast->expression); + Reference expr = expression(ast->expression); if (hasError) return false; - IR::ExprList *args = _function->New<IR::ExprList>(); - args->init(reference(*expr)); - _expr.code = call(_block->NAME(IR::Name::builtin_typeof, ast->typeofToken.startLine, ast->typeofToken.startColumn), args); + + if (expr.type == Reference::Name) { + // special handling as typeof doesn't throw here + Instruction::TypeofName instr; + instr.name = expr.nameAsIndex(); + bytecodeGenerator->addInstruction(instr); + } else { + expr.loadInAccumulator(); + Instruction::TypeofValue instr; + bytecodeGenerator->addInstruction(instr); + } + _expr.setResult(Reference::fromAccumulator(this)); + return false; } @@ -2042,12 +1968,7 @@ bool Codegen::visit(UnaryMinusExpression *ast) if (hasError) return false; - Result expr = expression(ast->expression); - if (hasError) - return false; - const unsigned t = _block->newTemp(); - setLocation(move(_block->TEMP(t), unop(IR::OpUMinus, *expr, ast->minusToken)), ast->minusToken); - _expr.code = _block->TEMP(t); + _expr.setResult(unop(UMinus, expression(ast->expression))); return false; } @@ -2056,12 +1977,7 @@ bool Codegen::visit(UnaryPlusExpression *ast) if (hasError) return false; - Result expr = expression(ast->expression); - if (hasError) - return false; - const unsigned t = _block->newTemp(); - setLocation(move(_block->TEMP(t), unop(IR::OpUPlus, *expr, ast->plusToken)), ast->plusToken); - _expr.code = _block->TEMP(t); + _expr.setResult(unop(UPlus, expression(ast->expression))); return false; } @@ -2070,10 +1986,10 @@ bool Codegen::visit(VoidExpression *ast) if (hasError) return false; - TempScope scope(_function); + RegisterScope scope(this); statement(ast->expression); - _expr.code = _block->CONST(IR::UndefinedType, 0); + _expr.setResult(Reference::fromConst(this, Encode::undefined())); return false; } @@ -2082,146 +1998,190 @@ bool Codegen::visit(FunctionDeclaration * ast) if (hasError) return false; - TempScope scope(_function); + RegisterScope scope(this); - if (_variableEnvironment->compilationMode == QmlBinding) - move(_block->TEMP(_returnAddress), _block->NAME(ast->name.toString(), 0, 0)); + if (_context->compilationMode == QmlBinding) + Reference::fromName(this, ast->name.toString()).loadInAccumulator(); _expr.accept(nx); return false; } +static bool endsWithReturn(Node *node) +{ + if (!node) + return false; + if (AST::cast<ReturnStatement *>(node)) + return true; + if (AST::cast<ThrowStatement *>(node)) + return true; + if (Program *p = AST::cast<Program *>(node)) + return endsWithReturn(p->elements); + if (SourceElements *se = AST::cast<SourceElements *>(node)) { + while (se->next) + se = se->next; + return endsWithReturn(se->element); + } + if (StatementSourceElement *sse = AST::cast<StatementSourceElement *>(node)) + return endsWithReturn(sse->statement); + if (StatementList *sl = AST::cast<StatementList *>(node)) { + while (sl->next) + sl = sl->next; + return endsWithReturn(sl->statement); + } + if (Block *b = AST::cast<Block *>(node)) + return endsWithReturn(b->statements); + if (IfStatement *is = AST::cast<IfStatement *>(node)) + return is->ko && endsWithReturn(is->ok) && endsWithReturn(is->ko); + return false; +} + int Codegen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, - AST::SourceElements *body, - const QStringList &inheritedLocals) -{ - Loop *loop = 0; - qSwap(_loop, loop); - QStack<IR::BasicBlock *> exceptionHandlers; - qSwap(_exceptionHandlers, exceptionHandlers); - - ScopeAndFinally *scopeAndFinally = 0; - - enterEnvironment(ast); - IR::Function *function = _module->newFunction(name, _function); - int functionIndex = _module->functions.count() - 1; - - IR::BasicBlock *entryBlock = function->newBasicBlock(0); - IR::BasicBlock *exitBlock = function->newBasicBlock(0, IR::Function::DontInsertBlock); - function->hasDirectEval = _variableEnvironment->hasDirectEval || _variableEnvironment->compilationMode == EvalCode - || _module->debugMode; // Conditional breakpoints are like eval in the function - function->usesArgumentsObject = _variableEnvironment->parent && (_variableEnvironment->usesArgumentsObject == Environment::ArgumentsObjectUsed); - function->usesThis = _variableEnvironment->usesThis; - function->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, (int)QV4::Global::ReservedArgumentCount); - function->isStrict = _variableEnvironment->isStrict; - function->isNamedExpression = _variableEnvironment->isNamedFunctionExpression; - function->isQmlBinding = _variableEnvironment->compilationMode == QmlBinding; - - AST::SourceLocation loc = ast->firstSourceLocation(); - function->line = loc.startLine; - function->column = loc.startColumn; - - if (function->usesArgumentsObject) - _variableEnvironment->enter(QStringLiteral("arguments"), Environment::VariableDeclaration, AST::VariableDeclaration::FunctionScope); + AST::SourceElements *body) +{ + Q_UNUSED(formals); + + enterContext(ast); + + if (_context->functionIndex >= 0) + // already defined + return leaveContext(); + + _context->name = name; + _module->functions.append(_context); + _context->functionIndex = _module->functions.count() - 1; + + _context->hasDirectEval |= (_context->compilationMode == EvalCode || _context->compilationMode == GlobalCode || _module->debugMode); // Conditional breakpoints are like eval in the function + + BytecodeGenerator bytecode(_context->line, _module->debugMode); + BytecodeGenerator *savedBytecodeGenerator; + savedBytecodeGenerator = bytecodeGenerator; + bytecodeGenerator = &bytecode; + bytecodeGenerator->setLocation(ast->firstSourceLocation()); + + // reserve the js stack frame (Context & js Function & accumulator) + bytecodeGenerator->newRegisterArray(sizeof(CallData)/sizeof(Value) - 1 + _context->arguments.size()); + + int returnAddress = -1; + bool _requiresReturnValue = (_context->compilationMode == QmlBinding || _context->compilationMode == EvalCode || _context->compilationMode == GlobalCode); + qSwap(requiresReturnValue, _requiresReturnValue); + if (requiresReturnValue) + returnAddress = bytecodeGenerator->newRegister(); + if (!_context->parent || _context->usesArgumentsObject == Context::ArgumentsObjectUnknown) + _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; + if (_context->usesArgumentsObject == Context::ArgumentsObjectUsed) + _context->addLocalVar(QStringLiteral("arguments"), Context::VariableDeclaration, AST::VariableDeclaration::FunctionScope); + + bool allVarsEscape = _context->hasWith || _context->hasTry || _context->hasDirectEval; + if (_context->compilationMode == QmlBinding // we don't really need this for bindings, but we do for signal handlers, and we don't know if the code is a signal handler or not. + || (!_context->canUseSimpleCall() && _context->compilationMode != GlobalCode && + (_context->compilationMode != EvalCode || _context->isStrict))) { + Instruction::CreateCallContext createContext; + bytecodeGenerator->addInstruction(createContext); + } // variables in global code are properties of the global context object, not locals as with other functions. - if (_variableEnvironment->compilationMode == FunctionCode || _variableEnvironment->compilationMode == QmlBinding) { - unsigned t = 0; - for (Environment::MemberMap::iterator it = _variableEnvironment->members.begin(), end = _variableEnvironment->members.end(); it != end; ++it) { + if (_context->compilationMode == FunctionCode || _context->compilationMode == QmlBinding) { + for (Context::MemberMap::iterator it = _context->members.begin(), end = _context->members.end(); it != end; ++it) { const QString &local = it.key(); - function->LOCAL(local); - (*it).index = t; - entryBlock->MOVE(entryBlock->LOCAL(t, 0), entryBlock->CONST(IR::UndefinedType, 0)); - ++t; - } - } else { - if (!_variableEnvironment->isStrict) { - for (const QString &inheritedLocal : qAsConst(inheritedLocals)) { - function->LOCAL(inheritedLocal); - unsigned tempIndex = entryBlock->newTemp(); - Environment::Member member = { Environment::UndefinedMember, - static_cast<int>(tempIndex), 0, - AST::VariableDeclaration::VariableScope::FunctionScope }; - _variableEnvironment->members.insert(inheritedLocal, member); + if (allVarsEscape) + it->canEscape = true; + if (it->canEscape) { + it->index = _context->locals.size(); + _context->locals.append(local); + if (it->type == Context::ThisFunctionName) { + // move the name from the stack to the call context + Instruction::LoadReg load; + load.reg = CallData::Function; + bytecodeGenerator->addInstruction(load); + Instruction::StoreLocal store; + store.index = it->index; + bytecodeGenerator->addInstruction(store); + } + } else { + if (it->type == Context::ThisFunctionName) + it->index = CallData::Function; + else + it->index = bytecodeGenerator->newRegister(); } } - - IR::ExprList *args = 0; - for (Environment::MemberMap::const_iterator it = _variableEnvironment->members.constBegin(), cend = _variableEnvironment->members.constEnd(); it != cend; ++it) { + } else { + for (Context::MemberMap::const_iterator it = _context->members.constBegin(), cend = _context->members.constEnd(); it != cend; ++it) { const QString &local = it.key(); - IR::ExprList *next = function->New<IR::ExprList>(); - next->expr = entryBlock->NAME(local, 0, 0); - next->next = args; - args = next; - } - if (args) { - IR::ExprList *next = function->New<IR::ExprList>(); - next->expr = entryBlock->CONST(IR::BoolType, false); // ### Investigate removal of bool deletable - next->next = args; - args = next; - entryBlock->EXP(entryBlock->CALL(entryBlock->NAME(IR::Name::builtin_declare_vars, 0, 0), args)); + Instruction::DeclareVar declareVar; + declareVar.isDeletable = false; + declareVar.varName = registerString(local); + bytecodeGenerator->addInstruction(declareVar); } } - unsigned returnAddress = entryBlock->newTemp(); - - entryBlock->MOVE(entryBlock->TEMP(returnAddress), entryBlock->CONST(IR::UndefinedType, 0)); - setLocation(exitBlock->RET(exitBlock->TEMP(returnAddress)), ast->lastSourceLocation()); - - qSwap(_function, function); - qSwap(_block, entryBlock); - qSwap(_exitBlock, exitBlock); qSwap(_returnAddress, returnAddress); - qSwap(_scopeAndFinally, scopeAndFinally); - - for (FormalParameterList *it = formals; it; it = it->next) { - _function->RECEIVE(it->name.toString()); - } - - for (const Environment::Member &member : qAsConst(_variableEnvironment->members)) { + for (const Context::Member &member : qAsConst(_context->members)) { if (member.function) { const int function = defineFunction(member.function->name.toString(), member.function, member.function->formals, member.function->body ? member.function->body->elements : 0); - if (! _variableEnvironment->parent) { - move(_block->NAME(member.function->name.toString(), member.function->identifierToken.startLine, member.function->identifierToken.startColumn), - _block->CLOSURE(function)); + loadClosure(function); + if (! _context->parent) { + Reference::fromName(this, member.function->name.toString()).storeConsumeAccumulator(); } else { Q_ASSERT(member.index >= 0); - move(_block->LOCAL(member.index, 0), _block->CLOSURE(function)); + Reference local = member.canEscape ? Reference::fromScopedLocal(this, member.index, 0) + : Reference::fromStackSlot(this, member.index, true); + local.storeConsumeAccumulator(); } } } - if (_function->usesArgumentsObject) { - move(identifier(QStringLiteral("arguments"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), - _block->CALL(_block->NAME(IR::Name::builtin_setup_argument_object, - ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), 0)); + if (_context->usesArgumentsObject == Context::ArgumentsObjectUsed) { + if (_context->isStrict) { + Instruction::CreateUnmappedArgumentsObject setup; + bytecodeGenerator->addInstruction(setup); + } else { + Instruction::CreateMappedArgumentsObject setup; + bytecodeGenerator->addInstruction(setup); + } + referenceForName(QStringLiteral("arguments"), false).storeConsumeAccumulator(); } - if (_function->usesThis && !_function->isStrict) { + if (_context->usesThis && !_context->isStrict) { // make sure we convert this to an object - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_convert_this_to_object, - ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), 0)); + Instruction::ConvertThisToObject convert; + bytecodeGenerator->addInstruction(convert); } beginFunctionBodyHook(); sourceElements(body); - _function->addBasicBlock(_exitBlock); + if (hasError || !endsWithReturn(body)) { + bytecodeGenerator->setLocation(ast->lastSourceLocation()); + if (requiresReturnValue) { + if (_returnAddress >= 0) { + Instruction::LoadReg load; + load.reg = Moth::StackSlot::createRegister(_returnAddress); + bytecodeGenerator->addInstruction(load); + } + } else { + Reference::fromConst(this, Encode::undefined()).loadInAccumulator(); + } + bytecodeGenerator->addInstruction(Instruction::Ret()); + } - _block->JUMP(_exitBlock); + bytecodeGenerator->finalize(_context); + _context->registerCount = bytecodeGenerator->registerCount(); + static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE"); + if (showCode) { + qDebug() << "=== Bytecode for" << _context->name << "strict mode" << _context->isStrict + << "register count" << _context->registerCount; + QV4::Moth::dumpBytecode(_context->code, _context->locals.size(), _context->arguments.size(), + _context->line, _context->lineNumberMapping); + qDebug(); + } - qSwap(_function, function); - qSwap(_block, entryBlock); - qSwap(_exitBlock, exitBlock); qSwap(_returnAddress, returnAddress); - qSwap(_scopeAndFinally, scopeAndFinally); - qSwap(_exceptionHandlers, exceptionHandlers); - qSwap(_loop, loop); - - leaveEnvironment(); + qSwap(requiresReturnValue, _requiresReturnValue); + bytecodeGenerator = savedBytecodeGenerator; - return functionIndex; + return leaveContext(); } bool Codegen::visit(FunctionSourceElement *ast) @@ -2247,11 +2207,9 @@ bool Codegen::visit(Block *ast) if (hasError) return false; - TempScope scope(_function); + RegisterScope scope(this); - for (StatementList *it = ast->statements; it; it = it->next) { - statement(it->statement); - } + statementList(ast->statements); return false; } @@ -2260,27 +2218,22 @@ bool Codegen::visit(BreakStatement *ast) if (hasError) return false; - TempScope scope(_function); - - if (!_loop) { + if (!_context->controlFlow) { throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop")); return false; } - Loop *loop = 0; - if (ast->label.isEmpty()) - loop = _loop; - else { - for (loop = _loop; loop; loop = loop->parent) { - if (loop->labelledStatement && loop->labelledStatement->label == ast->label) - break; - } - if (!loop) { + + 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")); + else throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Undefined label '%1'").arg(ast->label.toString())); - return false; - } + return false; } - unwindException(loop->scopeAndFinally); - _block->JUMP(loop->breakBlock); + + _context->controlFlow->jumpToHandler(h); + return false; } @@ -2289,33 +2242,24 @@ bool Codegen::visit(ContinueStatement *ast) if (hasError) return false; - TempScope scope(_function); + RegisterScope scope(this); - Loop *loop = 0; - if (ast->label.isEmpty()) { - for (loop = _loop; loop; loop = loop->parent) { - if (loop->continueBlock) - break; - } - } else { - for (loop = _loop; loop; loop = loop->parent) { - if (loop->labelledStatement && loop->labelledStatement->label == ast->label) { - if (!loop->continueBlock) - loop = 0; - break; - } - } - if (!loop) { - throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Undefined label '%1'").arg(ast->label.toString())); - return false; - } + if (!_context->controlFlow) { + throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Continue outside of loop")); + return false; } - if (!loop) { - throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("continue outside of loop")); + + 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())); + else + throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("continue outside of loop")); return false; } - unwindException(loop->scopeAndFinally); - _block->JUMP(loop->continueBlock); + + _context->controlFlow->jumpToHandler(h); + return false; } @@ -2330,26 +2274,27 @@ bool Codegen::visit(DoWhileStatement *ast) if (hasError) return true; - TempScope scope(_function); + RegisterScope scope(this); - IR::BasicBlock *loopbody = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *loopcond = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *loopend = _function->newBasicBlock(exceptionHandler()); + BytecodeGenerator::Label body = bytecodeGenerator->label(); + BytecodeGenerator::Label cond = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); - enterLoop(ast, loopend, loopcond); + ControlFlowLoop flow(this, &end, &cond); - _block->JUMP(loopbody); - - _block = loopbody; statement(ast->statement); - setJumpOutLocation(_block->JUMP(loopcond), ast->statement, ast->semicolonToken); + setJumpOutLocation(bytecodeGenerator, ast->statement, ast->semicolonToken); - _block = loopcond; - condition(ast->expression, loopbody, loopend); + cond.link(); - _block = loopend; + if (!AST::cast<FalseLiteral *>(ast->expression)) { + if (AST::cast<TrueLiteral *>(ast->expression)) + bytecodeGenerator->jump().link(body); + else + condition(ast->expression, &body, &end, false); + } - leaveLoop(); + end.link(); return false; } @@ -2367,12 +2312,13 @@ bool Codegen::visit(ExpressionStatement *ast) if (hasError) return true; - TempScope scope(_function); + RegisterScope scope(this); - if (_variableEnvironment->compilationMode == EvalCode || _variableEnvironment->compilationMode == QmlBinding) { - Result e = expression(ast->expression); - if (*e) - move(_block->TEMP(_returnAddress), *e); + if (requiresReturnValue) { + Reference e = expression(ast->expression); + if (hasError) + return false; + (void) e.storeOnStack(_returnAddress); } else { statement(ast->expression); } @@ -2384,46 +2330,44 @@ bool Codegen::visit(ForEachStatement *ast) if (hasError) return true; - TempScope scope(_function); - - IR::BasicBlock *foreachin = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *foreachbody = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *foreachend = _function->newBasicBlock(exceptionHandler()); + RegisterScope scope(this); - int objectToIterateOn = _block->newTemp(); - Result expr = expression(ast->expression); + Reference obj = Reference::fromStackSlot(this); + Reference expr = expression(ast->expression); if (hasError) - return false; - move(_block->TEMP(objectToIterateOn), *expr); - IR::ExprList *args = _function->New<IR::ExprList>(); - args->init(_block->TEMP(objectToIterateOn)); + return true; - int iterator = _block->newTemp(); - move(_block->TEMP(iterator), _block->CALL(_block->NAME(IR::Name::builtin_foreach_iterator_object, 0, 0), args)); + expr.loadInAccumulator(); + Instruction::ForeachIteratorObject iteratorObjInstr; + bytecodeGenerator->addInstruction(iteratorObjInstr); + obj.storeConsumeAccumulator(); - enterLoop(ast, foreachend, foreachin); - _block->JUMP(foreachin); + BytecodeGenerator::Label in = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); + + bytecodeGenerator->jump().link(in); + + ControlFlowLoop flow(this, &end, &in); + + BytecodeGenerator::Label body = bytecodeGenerator->label(); - _block = foreachbody; - int temp = _block->newTemp(); - Result init = expression(ast->initialiser); - if (hasError) - return false; - move(*init, _block->TEMP(temp)); statement(ast->statement); - setJumpOutLocation(_block->JUMP(foreachin), ast->statement, ast->forToken); + setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken); + + in.link(); + + Reference lhs = expression(ast->initialiser).asLValue(); - _block = foreachin; + obj.loadInAccumulator(); + Instruction::ForeachNextPropertyName nextPropInstr; + bytecodeGenerator->addInstruction(nextPropInstr); + lhs = lhs.storeRetainAccumulator().storeOnStack(); - args = _function->New<IR::ExprList>(); - args->init(_block->TEMP(iterator)); - move(_block->TEMP(temp), _block->CALL(_block->NAME(IR::Name::builtin_foreach_next_property_name, 0, 0), args)); - int null = _block->newTemp(); - move(_block->TEMP(null), _block->CONST(IR::NullType, 0)); - setLocation(cjump(_block->BINOP(IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend), ast->forToken); - _block = foreachend; + Reference::fromConst(this, QV4::Encode::null()).loadInAccumulator(); + bytecodeGenerator->jumpStrictNotEqual(lhs.stackSlot(), body); + + end.link(); - leaveLoop(); return false; } @@ -2432,35 +2376,28 @@ bool Codegen::visit(ForStatement *ast) if (hasError) return true; - TempScope scope(_function); - - IR::BasicBlock *forcond = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *forbody = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *forstep = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *forend = _function->newBasicBlock(exceptionHandler()); + RegisterScope scope(this); statement(ast->initialiser); - _block->JUMP(forcond); - enterLoop(ast, forend, forstep); + BytecodeGenerator::Label cond = bytecodeGenerator->label(); + BytecodeGenerator::Label body = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label step = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); - _block = forcond; - if (ast->condition) - condition(ast->condition, forbody, forend); - else - _block->JUMP(forbody); + ControlFlowLoop flow(this, &end, &step); + + condition(ast->condition, &body, &end, true); - _block = forbody; + body.link(); statement(ast->statement); - setJumpOutLocation(_block->JUMP(forstep), ast->statement, ast->forToken); + setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken); - _block = forstep; + step.link(); statement(ast->expression); - _block->JUMP(forcond); + bytecodeGenerator->jump().link(cond); - _block = forend; - - leaveLoop(); + end.link(); return false; } @@ -2470,26 +2407,28 @@ bool Codegen::visit(IfStatement *ast) if (hasError) return true; - TempScope scope(_function); - - IR::BasicBlock *iftrue = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *iffalse = ast->ko ? _function->newBasicBlock(exceptionHandler()) : 0; - IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler()); + RegisterScope scope(this); - condition(ast->expression, iftrue, ast->ko ? iffalse : endif); + BytecodeGenerator::Label trueLabel = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label falseLabel = bytecodeGenerator->newLabel(); + condition(ast->expression, &trueLabel, &falseLabel, true); - _block = iftrue; + trueLabel.link(); statement(ast->ok); - setJumpOutLocation(_block->JUMP(endif), ast->ok, ast->ifToken); - if (ast->ko) { - _block = iffalse; - statement(ast->ko); - setJumpOutLocation(_block->JUMP(endif), ast->ko, ast->elseToken); + if (endsWithReturn(ast)) { + falseLabel.link(); + statement(ast->ko); + } else { + BytecodeGenerator::Jump jump_endif = bytecodeGenerator->jump(); + falseLabel.link(); + statement(ast->ko); + jump_endif.link(); + } + } else { + falseLabel.link(); } - _block = endif; - return false; } @@ -2498,12 +2437,12 @@ bool Codegen::visit(LabelledStatement *ast) if (hasError) return true; - TempScope scope(_function); + RegisterScope scope(this); // check that no outer loop contains the label - Loop *l = _loop; + ControlFlow *l = _context->controlFlow; while (l) { - if (l->labelledStatement && l->labelledStatement->label == ast->label) { + if (l->label() == ast->label) { QString error = QString(QStringLiteral("Label '%1' has already been declared")).arg(ast->label.toString()); throwSyntaxError(ast->firstSourceLocation(), error); return false; @@ -2521,12 +2460,10 @@ bool Codegen::visit(LabelledStatement *ast) AST::cast<AST::LocalForEachStatement *>(ast->statement)) { statement(ast->statement); // labelledStatement will be associated with the ast->statement's loop. } else { - IR::BasicBlock *breakBlock = _function->newBasicBlock(exceptionHandler()); - enterLoop(ast->statement, breakBlock, /*continueBlock*/ 0); + BytecodeGenerator::Label breakLabel = bytecodeGenerator->newLabel(); + ControlFlowLoop flow(this, &breakLabel); statement(ast->statement); - _block->JUMP(breakBlock); - _block = breakBlock; - leaveLoop(); + breakLabel.link(); } return false; @@ -2537,40 +2474,44 @@ bool Codegen::visit(LocalForEachStatement *ast) if (hasError) return true; - TempScope scope(_function); + RegisterScope scope(this); - IR::BasicBlock *foreachin = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *foreachbody = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *foreachend = _function->newBasicBlock(exceptionHandler()); + Reference obj = Reference::fromStackSlot(this); + Reference expr = expression(ast->expression); + if (hasError) + return true; variableDeclaration(ast->declaration); - int iterator = _block->newTemp(); - move(_block->TEMP(iterator), *expression(ast->expression)); - IR::ExprList *args = _function->New<IR::ExprList>(); - args->init(_block->TEMP(iterator)); - move(_block->TEMP(iterator), _block->CALL(_block->NAME(IR::Name::builtin_foreach_iterator_object, 0, 0), args)); + expr.loadInAccumulator(); + Instruction::ForeachIteratorObject iteratorObjInstr; + bytecodeGenerator->addInstruction(iteratorObjInstr); + obj.storeConsumeAccumulator(); + + BytecodeGenerator::Label in = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); - _block->JUMP(foreachin); - enterLoop(ast, foreachend, foreachin); + bytecodeGenerator->jump().link(in); + ControlFlowLoop flow(this, &end, &in); - _block = foreachbody; - int temp = _block->newTemp(); - move(identifier(ast->declaration->name.toString()), _block->TEMP(temp)); + BytecodeGenerator::Label body = bytecodeGenerator->label(); + + Reference it = referenceForName(ast->declaration->name.toString(), true).asLValue(); statement(ast->statement); - setJumpOutLocation(_block->JUMP(foreachin), ast->statement, ast->forToken); + setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken); + + in.link(); - _block = foreachin; + obj.loadInAccumulator(); + Instruction::ForeachNextPropertyName nextPropInstr; + bytecodeGenerator->addInstruction(nextPropInstr); + auto lhs = it.storeRetainAccumulator().storeOnStack(); - args = _function->New<IR::ExprList>(); - args->init(_block->TEMP(iterator)); - move(_block->TEMP(temp), _block->CALL(_block->NAME(IR::Name::builtin_foreach_next_property_name, 0, 0), args)); - int null = _block->newTemp(); - move(_block->TEMP(null), _block->CONST(IR::NullType, 0)); - setLocation(cjump(_block->BINOP(IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend), ast->forToken); - _block = foreachend; + Reference::fromConst(this, QV4::Encode::null()).loadInAccumulator(); + bytecodeGenerator->jumpStrictNotEqual(lhs.stackSlot(), body); + + end.link(); - leaveLoop(); return false; } @@ -2579,35 +2520,27 @@ bool Codegen::visit(LocalForStatement *ast) if (hasError) return true; - TempScope scope(_function); - - IR::BasicBlock *forcond = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *forbody = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *forstep = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *forend = _function->newBasicBlock(exceptionHandler()); + RegisterScope scope(this); variableDeclarationList(ast->declarations); - _block->JUMP(forcond); - enterLoop(ast, forend, forstep); + BytecodeGenerator::Label cond = bytecodeGenerator->label(); + BytecodeGenerator::Label body = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label step = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); - _block = forcond; - if (ast->condition) - condition(ast->condition, forbody, forend); - else - _block->JUMP(forbody); + ControlFlowLoop flow(this, &end, &step); + + condition(ast->condition, &body, &end, true); - _block = forbody; + body.link(); statement(ast->statement); - setJumpOutLocation(_block->JUMP(forstep), ast->statement, ast->forToken); + setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken); - _block = forstep; + step.link(); statement(ast->expression); - _block->JUMP(forcond); - - _block = forend; - - leaveLoop(); + bytecodeGenerator->jump().link(cond); + end.link(); return false; } @@ -2617,24 +2550,30 @@ bool Codegen::visit(ReturnStatement *ast) if (hasError) return true; - if (_variableEnvironment->compilationMode != FunctionCode && _variableEnvironment->compilationMode != QmlBinding) { + if (_context->compilationMode != FunctionCode && _context->compilationMode != QmlBinding) { throwSyntaxError(ast->returnToken, QStringLiteral("Return statement outside of function")); return false; } + Reference expr; if (ast->expression) { - Result expr = expression(ast->expression); - move(_block->TEMP(_returnAddress), *expr); + expr = expression(ast->expression); + if (hasError) + return false; + } else { + expr = Reference::fromConst(this, Encode::undefined()); } - // Since we're leaving, don't let any finally statements we emit as part of the unwinding - // jump to exception handlers at run-time if they throw. - IR::BasicBlock *unwindBlock = _function->newBasicBlock(/*no exception handler*/nullptr); - _block->JUMP(unwindBlock); - _block = unwindBlock; - - unwindException(0); - - _block->JUMP(_exitBlock); + if (_context->controlFlow && _context->controlFlow->returnRequiresUnwind()) { + if (_returnAddress >= 0) + (void) expr.storeOnStack(_returnAddress); + else + expr.loadInAccumulator(); + ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Return); + _context->controlFlow->jumpToHandler(h); + } else { + expr.loadInAccumulator(); + bytecodeGenerator->addInstruction(Instruction::Ret()); + } return false; } @@ -2643,245 +2582,151 @@ bool Codegen::visit(SwitchStatement *ast) if (hasError) return true; - TempScope scope(_function); - - IR::BasicBlock *switchend = _function->newBasicBlock(exceptionHandler()); + RegisterScope scope(this); if (ast->block) { - int lhs = _block->newTemp(); - move(_block->TEMP(lhs), *expression(ast->expression)); - IR::BasicBlock *switchcond = _function->newBasicBlock(exceptionHandler()); - _block->JUMP(switchcond); - IR::BasicBlock *previousBlock = 0; - - QHash<Node *, IR::BasicBlock *> blockMap; - - enterLoop(ast, switchend, 0); + BytecodeGenerator::Label switchEnd = bytecodeGenerator->newLabel(); + Reference lhs = expression(ast->expression); + if (hasError) + return false; + lhs = lhs.storeOnStack(); + + // set up labels for all clauses + QHash<Node *, BytecodeGenerator::Label> blockMap; + for (CaseClauses *it = ast->block->clauses; it; it = it->next) + blockMap[it->clause] = bytecodeGenerator->newLabel(); + if (ast->block->defaultClause) + blockMap[ast->block->defaultClause] = bytecodeGenerator->newLabel(); + for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) + blockMap[it->clause] = bytecodeGenerator->newLabel(); + + // do the switch conditions for (CaseClauses *it = ast->block->clauses; it; it = it->next) { CaseClause *clause = it->clause; - - _block = _function->newBasicBlock(exceptionHandler()); - blockMap[clause] = _block; - - if (previousBlock && !previousBlock->isTerminated()) - previousBlock->JUMP(_block); - - for (StatementList *it2 = clause->statements; it2; it2 = it2->next) - statement(it2->statement); - - previousBlock = _block; - } - - if (ast->block->defaultClause) { - _block = _function->newBasicBlock(exceptionHandler()); - blockMap[ast->block->defaultClause] = _block; - - if (previousBlock && !previousBlock->isTerminated()) - previousBlock->JUMP(_block); - - for (StatementList *it2 = ast->block->defaultClause->statements; it2; it2 = it2->next) - statement(it2->statement); - - previousBlock = _block; + Reference rhs = expression(clause->expression); + if (hasError) + return false; + rhs.loadInAccumulator(); + bytecodeGenerator->jumpStrictEqual(lhs.stackSlot(), blockMap.value(clause)); } for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { CaseClause *clause = it->clause; + Reference rhs = expression(clause->expression); + if (hasError) + return false; + rhs.loadInAccumulator(); + bytecodeGenerator->jumpStrictEqual(lhs.stackSlot(), blockMap.value(clause)); + } - _block = _function->newBasicBlock(exceptionHandler()); - blockMap[clause] = _block; + if (DefaultClause *defaultClause = ast->block->defaultClause) + bytecodeGenerator->jump().link(blockMap.value(defaultClause)); + else + bytecodeGenerator->jump().link(switchEnd); - if (previousBlock && !previousBlock->isTerminated()) - previousBlock->JUMP(_block); + ControlFlowLoop flow(this, &switchEnd); - for (StatementList *it2 = clause->statements; it2; it2 = it2->next) - statement(it2->statement); + for (CaseClauses *it = ast->block->clauses; it; it = it->next) { + CaseClause *clause = it->clause; + blockMap[clause].link(); - previousBlock = _block; + statementList(clause->statements); } - leaveLoop(); - - _block->JUMP(switchend); + if (ast->block->defaultClause) { + DefaultClause *clause = ast->block->defaultClause; + blockMap[clause].link(); - _block = switchcond; - for (CaseClauses *it = ast->block->clauses; it; it = it->next) { - CaseClause *clause = it->clause; - Result rhs = expression(clause->expression); - IR::BasicBlock *iftrue = blockMap[clause]; - IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler()); - setLocation(cjump(binop(IR::OpStrictEqual, _block->TEMP(lhs), *rhs), iftrue, iffalse), clause->caseToken); - _block = iffalse; + statementList(clause->statements); } for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { CaseClause *clause = it->clause; - Result rhs = expression(clause->expression); - IR::BasicBlock *iftrue = blockMap[clause]; - IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler()); - setLocation(cjump(binop(IR::OpStrictEqual, _block->TEMP(lhs), *rhs), iftrue, iffalse), clause->caseToken); - _block = iffalse; - } + blockMap[clause].link(); - if (DefaultClause *defaultClause = ast->block->defaultClause) { - setLocation(_block->JUMP(blockMap[ast->block->defaultClause]), defaultClause->defaultToken); + statementList(clause->statements); } - } - _block->JUMP(switchend); + switchEnd.link(); + + } - _block = switchend; return false; } bool Codegen::visit(ThrowStatement *ast) { if (hasError) - return true; + return false; + + RegisterScope scope(this); - TempScope scope(_function); + Reference expr = expression(ast->expression); + if (hasError) + return false; - Result expr = expression(ast->expression); - move(_block->TEMP(_returnAddress), *expr); - IR::ExprList *throwArgs = _function->New<IR::ExprList>(); - throwArgs->expr = _block->TEMP(_returnAddress); - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs)); + if (_context->controlFlow) { + _context->controlFlow->handleThrow(expr); + } else { + expr.loadInAccumulator(); + Instruction::ThrowException instr; + bytecodeGenerator->addInstruction(instr); + } return false; } -bool Codegen::visit(TryStatement *ast) +void Codegen::handleTryCatch(TryStatement *ast) { - if (hasError) - return true; - - TempScope scope(_function); - - _function->hasTry = true; - - if (_function->isStrict && ast->catchExpression && - (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments"))) { + Q_ASSERT(ast); + if (_context->isStrict && + (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments"))) { throwSyntaxError(ast->catchExpression->identifierToken, QStringLiteral("Catch variable name may not be eval or arguments in strict mode")); - return false; + return; } - IR::BasicBlock *surroundingExceptionHandler = exceptionHandler(); - - // We always need a finally body to clean up the exception handler - // exceptions thrown in finally get caught by the surrounding catch block - IR::BasicBlock *finallyBody = 0; - IR::BasicBlock *catchBody = 0; - IR::BasicBlock *catchExceptionHandler = 0; - IR::BasicBlock *end = _function->newBasicBlock(surroundingExceptionHandler, IR::Function::DontInsertBlock); - - if (ast->finallyExpression) - finallyBody = _function->newBasicBlock(surroundingExceptionHandler, IR::Function::DontInsertBlock); - - if (ast->catchExpression) { - // exception handler for the catch body - catchExceptionHandler = _function->newBasicBlock(0, IR::Function::DontInsertBlock); - pushExceptionHandler(catchExceptionHandler); - catchBody = _function->newBasicBlock(catchExceptionHandler, IR::Function::DontInsertBlock); - popExceptionHandler(); - pushExceptionHandler(catchBody); - } else { - Q_ASSERT(finallyBody); - pushExceptionHandler(finallyBody); + RegisterScope scope(this); + BytecodeGenerator::Label noException = bytecodeGenerator->newLabel(); + { + ControlFlowCatch catchFlow(this, ast->catchExpression); + RegisterScope scope(this); + statement(ast->statement); + bytecodeGenerator->jump().link(noException); } + noException.link(); +} - IR::BasicBlock *tryBody = _function->newBasicBlock(exceptionHandler()); - _block->JUMP(tryBody); - - ScopeAndFinally tcf(_scopeAndFinally, ast->finallyExpression); - _scopeAndFinally = &tcf; - - _block = tryBody; - statement(ast->statement); - _block->JUMP(finallyBody ? finallyBody : end); - - popExceptionHandler(); +void Codegen::handleTryFinally(TryStatement *ast) +{ + RegisterScope scope(this); + ControlFlowFinally finally(this, ast->finallyExpression); if (ast->catchExpression) { - pushExceptionHandler(catchExceptionHandler); - _function->addBasicBlock(catchBody); - _block = catchBody; - - ++_function->insideWithOrCatch; - IR::ExprList *catchArgs = _function->New<IR::ExprList>(); - catchArgs->init(_block->STRING(_function->newString(ast->catchExpression->name.toString()))); - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_push_catch_scope, 0, 0), catchArgs)); - { - ScopeAndFinally scope(_scopeAndFinally, ScopeAndFinally::CatchScope); - _scopeAndFinally = &scope; - statement(ast->catchExpression->statement); - _scopeAndFinally = scope.parent; - } - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0), 0)); - --_function->insideWithOrCatch; - _block->JUMP(finallyBody ? finallyBody : end); - popExceptionHandler(); - - _function->addBasicBlock(catchExceptionHandler); - catchExceptionHandler->EXP(catchExceptionHandler->CALL(catchExceptionHandler->NAME(IR::Name::builtin_pop_scope, 0, 0), 0)); - if (finallyBody || surroundingExceptionHandler) - catchExceptionHandler->JUMP(finallyBody ? finallyBody : surroundingExceptionHandler); - else - catchExceptionHandler->EXP(catchExceptionHandler->CALL(catchExceptionHandler->NAME(IR::Name::builtin_rethrow, 0, 0), 0)); + handleTryCatch(ast); + } else { + RegisterScope scope(this); + statement(ast->statement); } +} - _scopeAndFinally = tcf.parent; - - if (finallyBody) { - _function->addBasicBlock(finallyBody); - _block = finallyBody; - - TempScope scope(_function); +bool Codegen::visit(TryStatement *ast) +{ + if (hasError) + return true; - int hasException = _block->newTemp(); - move(_block->TEMP(hasException), _block->CALL(_block->NAME(IR::Name::builtin_unwind_exception, /*line*/0, /*column*/0), 0)); + Q_ASSERT(_context->hasTry); - if (ast->finallyExpression && ast->finallyExpression->statement) - statement(ast->finallyExpression->statement); + RegisterScope scope(this); - IR::ExprList *arg = _function->New<IR::ExprList>(); - arg->expr = _block->TEMP(hasException); - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_throw, /*line*/0, /*column*/0), arg)); - _block->JUMP(end); + if (ast->finallyExpression && ast->finallyExpression->statement) { + handleTryFinally(ast); + } else { + handleTryCatch(ast); } - _function->addBasicBlock(end); - _block = end; - return false; } -void Codegen::unwindException(Codegen::ScopeAndFinally *outest) -{ - int savedDepthForWidthOrCatch = _function->insideWithOrCatch; - ScopeAndFinally *scopeAndFinally = _scopeAndFinally; - qSwap(_scopeAndFinally, scopeAndFinally); - while (_scopeAndFinally != outest) { - switch (_scopeAndFinally->type) { - case ScopeAndFinally::WithScope: - // fall through - case ScopeAndFinally::CatchScope: - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0))); - _scopeAndFinally = _scopeAndFinally->parent; - --_function->insideWithOrCatch; - break; - case ScopeAndFinally::TryScope: { - ScopeAndFinally *tc = _scopeAndFinally; - _scopeAndFinally = tc->parent; - if (tc->finally && tc->finally->statement) - statement(tc->finally->statement); - break; - } - } - } - qSwap(_scopeAndFinally, scopeAndFinally); - _function->insideWithOrCatch = savedDepthForWidthOrCatch; -} - bool Codegen::visit(VariableStatement *ast) { if (hasError) @@ -2896,23 +2741,25 @@ bool Codegen::visit(WhileStatement *ast) if (hasError) return true; - IR::BasicBlock *whilecond = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *whilebody = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *whileend = _function->newBasicBlock(exceptionHandler()); + if (AST::cast<FalseLiteral *>(ast->expression)) + return false; - enterLoop(ast, whileend, whilecond); + RegisterScope scope(this); - _block->JUMP(whilecond); - _block = whilecond; - condition(ast->expression, whilebody, whileend); + BytecodeGenerator::Label start = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label cond = bytecodeGenerator->label(); + ControlFlowLoop flow(this, &end, &cond); - _block = whilebody; - statement(ast->statement); - setJumpOutLocation(_block->JUMP(whilecond), ast->statement, ast->whileToken); + if (!AST::cast<TrueLiteral *>(ast->expression)) + condition(ast->expression, &start, &end, true); - _block = whileend; - leaveLoop(); + start.link(); + statement(ast->statement); + setJumpOutLocation(bytecodeGenerator, ast->statement, ast->whileToken); + bytecodeGenerator->jump().link(cond); + end.link(); return false; } @@ -2921,103 +2768,75 @@ bool Codegen::visit(WithStatement *ast) if (hasError) return true; - TempScope scope(_function); + RegisterScope scope(this); - _function->hasWith = true; + _context->hasWith = true; - const int withObject = _block->newTemp(); - Result src = expression(ast->expression); + Reference src = expression(ast->expression); if (hasError) return false; - _block->MOVE(_block->TEMP(withObject), *src); - - // need an exception handler for with to cleanup the with scope - IR::BasicBlock *withExceptionHandler = _function->newBasicBlock(exceptionHandler()); - withExceptionHandler->EXP(withExceptionHandler->CALL(withExceptionHandler->NAME(IR::Name::builtin_pop_scope, 0, 0), 0)); - if (!exceptionHandler()) - withExceptionHandler->EXP(withExceptionHandler->CALL(withExceptionHandler->NAME(IR::Name::builtin_rethrow, 0, 0), 0)); - else - withExceptionHandler->JUMP(exceptionHandler()); - - pushExceptionHandler(withExceptionHandler); - - IR::BasicBlock *withBlock = _function->newBasicBlock(exceptionHandler()); + src = src.storeOnStack(); // trigger load before we setup the exception handler, so exceptions here go to the right place + src.loadInAccumulator(); - _block->JUMP(withBlock); - _block = withBlock; - IR::ExprList *args = _function->New<IR::ExprList>(); - args->init(_block->TEMP(withObject)); - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_push_with_scope, 0, 0), args)); + ControlFlowWith flow(this); - ++_function->insideWithOrCatch; - { - ScopeAndFinally scope(_scopeAndFinally); - _scopeAndFinally = &scope; - statement(ast->statement); - _scopeAndFinally = scope.parent; - } - --_function->insideWithOrCatch; - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0), 0)); - popExceptionHandler(); - - IR::BasicBlock *next = _function->newBasicBlock(exceptionHandler()); - _block->JUMP(next); - _block = next; + statement(ast->statement); return false; } bool Codegen::visit(UiArrayBinding *) { - Q_ASSERT(!"not implemented"); + Q_UNIMPLEMENTED(); return false; } bool Codegen::visit(UiObjectBinding *) { - Q_ASSERT(!"not implemented"); + Q_UNIMPLEMENTED(); return false; } bool Codegen::visit(UiObjectDefinition *) { - Q_ASSERT(!"not implemented"); + Q_UNIMPLEMENTED(); return false; } bool Codegen::visit(UiPublicMember *) { - Q_ASSERT(!"not implemented"); + Q_UNIMPLEMENTED(); return false; } bool Codegen::visit(UiScriptBinding *) { - Q_ASSERT(!"not implemented"); + Q_UNIMPLEMENTED(); return false; } bool Codegen::visit(UiSourceElement *) { - Q_ASSERT(!"not implemented"); + Q_UNIMPLEMENTED(); return false; } -bool Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(IR::Expr *expr, const SourceLocation& loc) +bool Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r, const SourceLocation& loc) { - if (!_variableEnvironment->isStrict) - return false; - if (IR::Name *n = expr->asName()) { - if (*n->id != QLatin1String("eval") && *n->id != QLatin1String("arguments")) - return false; - } else if (IR::ArgLocal *al = expr->asArgLocal()) { - if (!al->isArgumentsOrEval) - return false; - } else { + if (!_context->isStrict) return false; + bool isArgOrEval = false; + if (r.type == Reference::Name) { + QString str = jsUnitGenerator->stringForIndex(r.nameAsIndex()); + if (str == QLatin1String("eval") || str == QLatin1String("arguments")) { + isArgOrEval = true; + } + } else if (r.type == Reference::ScopedLocal || r.isRegister()) { + isArgOrEval = r.isArgOrEval; } - throwSyntaxError(loc, QStringLiteral("Variable name may not be eval or arguments in strict mode")); - return true; + if (isArgOrEval) + throwSyntaxError(loc, QStringLiteral("Variable name may not be eval or arguments in strict mode")); + return isArgOrEval; } void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail) @@ -3049,6 +2868,116 @@ QList<QQmlJS::DiagnosticMessage> Codegen::errors() const return _errors; } +QQmlRefPointer<CompiledData::CompilationUnit> Codegen::generateCompilationUnit(bool generateUnitData) +{ + CompiledData::CompilationUnit *compilationUnit = new CompiledData::CompilationUnit; + if (generateUnitData) + compilationUnit->data = jsUnitGenerator->generateUnit(); + + QQmlRefPointer<CompiledData::CompilationUnit> unit; + unit.adopt(compilationUnit); + return unit; +} + +QQmlRefPointer<CompiledData::CompilationUnit> Codegen::createUnitForLoading() +{ + QQmlRefPointer<CompiledData::CompilationUnit> result; + result.adopt(new CompiledData::CompilationUnit); + return result; +} + +class Codegen::VolatileMemoryLocationScanner: protected QQmlJS::AST::Visitor +{ + VolatileMemoryLocations locs; + +public: + Codegen::VolatileMemoryLocations scan(AST::Node *s) + { + s->accept(this); + return locs; + } + + bool visit(ArrayMemberExpression *) Q_DECL_OVERRIDE + { + locs.setAllVolatile(); + return false; + } + + bool visit(FieldMemberExpression *) Q_DECL_OVERRIDE + { + locs.setAllVolatile(); + return false; + } + + bool visit(PostIncrementExpression *e) Q_DECL_OVERRIDE + { + collectIdentifiers(locs.specificLocations, e->base); + return false; + } + + bool visit(PostDecrementExpression *e) Q_DECL_OVERRIDE + { + collectIdentifiers(locs.specificLocations, e->base); + return false; + } + + bool visit(PreIncrementExpression *e) Q_DECL_OVERRIDE + { + collectIdentifiers(locs.specificLocations, e->expression); + return false; + } + + bool visit(PreDecrementExpression *e) Q_DECL_OVERRIDE + { + collectIdentifiers(locs.specificLocations, e->expression); + return false; + } + + bool visit(BinaryExpression *e) Q_DECL_OVERRIDE + { + switch (e->op) { + case QSOperator::InplaceAnd: + case QSOperator::InplaceSub: + case QSOperator::InplaceDiv: + case QSOperator::InplaceAdd: + case QSOperator::InplaceLeftShift: + case QSOperator::InplaceMod: + case QSOperator::InplaceMul: + case QSOperator::InplaceOr: + case QSOperator::InplaceRightShift: + case QSOperator::InplaceURightShift: + case QSOperator::InplaceXor: + collectIdentifiers(locs.specificLocations, e); + return false; + + default: + return true; + } + } + +private: + void collectIdentifiers(QVector<QStringView> &ids, AST::Node *node) const { + class Collector: public QQmlJS::AST::Visitor { + QVector<QStringView> &ids; + public: + Collector(QVector<QStringView> &ids): ids(ids) {} + virtual bool visit(IdentifierExpression *ie) { + ids.append(ie->name); + return false; + } + }; + Collector collector(ids); + node->accept(&collector); + } +}; + +Codegen::VolatileMemoryLocations Codegen::scanVolatileMemoryLocations(AST::Node *ast) const +{ + VolatileMemoryLocationScanner scanner; + return scanner.scan(ast); +} + + #ifndef V4_BOOTSTRAP QList<QQmlError> Codegen::qmlErrors() const @@ -3074,20 +3003,458 @@ QList<QQmlError> Codegen::qmlErrors() const return qmlErrors; } -void RuntimeCodegen::throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) +#endif // V4_BOOTSTRAP + +bool Codegen::RValue::operator==(const RValue &other) const { - if (hasError) - return; - hasError = true; - engine->throwSyntaxError(detail, _module->fileName, loc.startLine, loc.startColumn); + switch (type) { + case Accumulator: + return other.isAccumulator(); + case StackSlot: + return other.isStackSlot() && theStackSlot == other.theStackSlot; + case Const: + return other.isConst() && constant == other.constant; + default: + return false; + } } -void RuntimeCodegen::throwReferenceError(const AST::SourceLocation &loc, const QString &detail) +Codegen::RValue Codegen::RValue::storeOnStack() const { - if (hasError) + switch (type) { + case Accumulator: + return RValue::fromStackSlot(codegen, Reference::fromAccumulator(codegen).storeOnStack().stackSlot()); + case StackSlot: + return *this; + case Const: + return RValue::fromStackSlot(codegen, Reference::storeConstOnStack(codegen, constant).stackSlot()); + default: + Q_UNREACHABLE(); + } +} + +Codegen::Reference::Reference(const Codegen::Reference &other) +{ + *this = other; +} + +Codegen::Reference &Codegen::Reference::operator =(const Reference &other) +{ + type = other.type; + + switch (type) { + case Invalid: + case Accumulator: + break; + case StackSlot: + theStackSlot = other.theStackSlot; + break; + case ScopedLocal: + index = other.index; + scope = other.scope; + break; + case Name: + name = other.name; + break; + case Member: + propertyBase = other.propertyBase; + propertyNameIndex = other.propertyNameIndex; + break; + case Subscript: + elementBase = other.elementBase; + elementSubscript = other.elementSubscript; + break; + case Const: + constant = other.constant; + break; + case QmlScopeObject: + case QmlContextObject: + qmlBase = other.qmlBase; + qmlCoreIndex = other.qmlCoreIndex; + qmlNotifyIndex = other.qmlNotifyIndex; + captureRequired = other.captureRequired; + break; + } + + // keep loaded reference + isArgOrEval = other.isArgOrEval; + codegen = other.codegen; + isReadonly = other.isReadonly; + stackSlotIsLocalOrArgument = other.stackSlotIsLocalOrArgument; + isVolatile = other.isVolatile; + global = other.global; + return *this; +} + +bool Codegen::Reference::operator==(const Codegen::Reference &other) const +{ + if (type != other.type) + return false; + switch (type) { + case Invalid: + case Accumulator: + break; + case StackSlot: + return theStackSlot == other.theStackSlot; + case ScopedLocal: + return index == other.index && scope == other.scope; + case Name: + return nameAsIndex() == other.nameAsIndex(); + case Member: + return propertyBase == other.propertyBase && propertyNameIndex == other.propertyNameIndex; + case Subscript: + return elementBase == other.elementBase && elementSubscript == other.elementSubscript; + case Const: + return constant == other.constant; + case QmlScopeObject: + case QmlContextObject: + return qmlCoreIndex == other.qmlCoreIndex && qmlNotifyIndex == other.qmlNotifyIndex + && captureRequired == other.captureRequired; + } + return true; +} + +Codegen::RValue Codegen::Reference::asRValue() const +{ + switch (type) { + case Invalid: + Q_UNREACHABLE(); + case Accumulator: + return RValue::fromAccumulator(codegen); + case StackSlot: + return RValue::fromStackSlot(codegen, stackSlot()); + case Const: + return RValue::fromConst(codegen, constant); + default: + loadInAccumulator(); + return RValue::fromAccumulator(codegen); + } +} + +Codegen::Reference Codegen::Reference::asLValue() const +{ + switch (type) { + case Invalid: + case Accumulator: + Q_UNREACHABLE(); + case Member: + if (!propertyBase.isStackSlot()) { + Reference r = *this; + r.propertyBase = propertyBase.storeOnStack(); + return r; + } + return *this; + case Subscript: + if (!elementSubscript.isStackSlot()) { + Reference r = *this; + r.elementSubscript = elementSubscript.storeOnStack(); + return r; + } + return *this; + default: + return *this; + } +} + +Codegen::Reference Codegen::Reference::storeConsumeAccumulator() const +{ + storeAccumulator(); // it doesn't matter what happens here, just do it. + return Reference(); +} + +Codegen::Reference Codegen::Reference::storeOnStack() const +{ return doStoreOnStack(-1); } + +void Codegen::Reference::storeOnStack(int slotIndex) const +{ doStoreOnStack(slotIndex); } + +Codegen::Reference Codegen::Reference::doStoreOnStack(int slotIndex) const +{ + if (isStackSlot() && slotIndex == -1 && !(stackSlotIsLocalOrArgument && isVolatile)) + return *this; + + if (isStackSlot()) { // temp-to-temp move + Reference dest = Reference::fromStackSlot(codegen, slotIndex); + Instruction::MoveReg move; + move.srcReg = stackSlot(); + move.destReg = dest.stackSlot(); + codegen->bytecodeGenerator->addInstruction(move); + return dest; + } + + Reference slot = Reference::fromStackSlot(codegen, slotIndex); + if (isConst()) { + Instruction::MoveConst move; + move.constIndex = codegen->registerConstant(constant); + move.destTemp = slot.stackSlot(); + codegen->bytecodeGenerator->addInstruction(move); + } else { + loadInAccumulator(); + slot.storeConsumeAccumulator(); + } + return slot; +} + +Codegen::Reference Codegen::Reference::storeRetainAccumulator() const +{ + if (storeWipesAccumulator()) { + // a store will + auto tmp = Reference::fromStackSlot(codegen); + tmp.storeAccumulator(); // this is safe, and won't destory the accumulator + storeAccumulator(); + return tmp; + } else { + // ok, this is safe, just do the store. + storeAccumulator(); + return *this; + } +} + +bool Codegen::Reference::storeWipesAccumulator() const +{ + switch (type) { + default: + case Invalid: + case Const: + case Accumulator: + Q_UNREACHABLE(); + return false; + case StackSlot: + case ScopedLocal: + return false; + case Name: + case Member: + case Subscript: + case QmlScopeObject: + case QmlContextObject: + return true; + } +} + +void Codegen::Reference::storeAccumulator() const +{ + switch (type) { + case StackSlot: { + Instruction::StoreReg store; + store.reg = theStackSlot; + codegen->bytecodeGenerator->addInstruction(store); return; - hasError = true; - engine->throwReferenceError(detail, _module->fileName, loc.startLine, loc.startColumn); + } + case ScopedLocal: { + if (scope == 0) { + Instruction::StoreLocal store; + store.index = index; + codegen->bytecodeGenerator->addInstruction(store); + } else { + Instruction::StoreScopedLocal store; + store.index = index; + store.scope = scope; + codegen->bytecodeGenerator->addInstruction(store); + } + return; + } + case Name: { + Context *c = codegen->currentContext(); + if (c->isStrict) { + Instruction::StoreNameStrict store; + store.name = nameAsIndex(); + codegen->bytecodeGenerator->addInstruction(store); + } else { + Instruction::StoreNameSloppy store; + store.name = nameAsIndex(); + codegen->bytecodeGenerator->addInstruction(store); + } + } return; + case Member: + if (codegen->useFastLookups) { + Instruction::SetLookup store; + store.base = propertyBase.stackSlot(); + store.index = codegen->registerSetterLookup(propertyNameIndex); + codegen->bytecodeGenerator->addInstruction(store); + } else { + Instruction::StoreProperty store; + store.base = propertyBase.stackSlot(); + store.name = propertyNameIndex; + codegen->bytecodeGenerator->addInstruction(store); + } + return; + case Subscript: { + Instruction::StoreElement store; + store.base = elementBase; + store.index = elementSubscript.stackSlot(); + codegen->bytecodeGenerator->addInstruction(store); + } return; + case QmlScopeObject: { + Instruction::StoreScopeObjectProperty store; + store.base = qmlBase; + store.propertyIndex = qmlCoreIndex; + codegen->bytecodeGenerator->addInstruction(store); + } return; + case QmlContextObject: { + Instruction::StoreContextObjectProperty store; + store.base = qmlBase; + store.propertyIndex = qmlCoreIndex; + codegen->bytecodeGenerator->addInstruction(store); + } return; + case Invalid: + case Accumulator: + case Const: + break; + } + + Q_ASSERT(false); + Q_UNREACHABLE(); } -#endif // V4_BOOTSTRAP +void Codegen::Reference::loadInAccumulator() const +{ + switch (type) { + case Accumulator: + return; + case Const: { +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty structs. + if (constant == Encode::null()) { + Instruction::LoadNull load; + codegen->bytecodeGenerator->addInstruction(load); + } else if (constant == Encode(true)) { + Instruction::LoadTrue load; + codegen->bytecodeGenerator->addInstruction(load); + } else if (constant == Encode(false)) { + Instruction::LoadFalse load; + codegen->bytecodeGenerator->addInstruction(load); + } else if (constant == Encode::undefined()) { + Instruction::LoadUndefined load; + codegen->bytecodeGenerator->addInstruction(load); + } else { + Value p = Primitive::fromReturnedValue(constant); + if (p.isNumber()) { + double d = p.asDouble(); + int i = static_cast<int>(d); + if (d == i && (d != 0 || !std::signbit(d))) { + if (!i) { + Instruction::LoadZero load; + codegen->bytecodeGenerator->addInstruction(load); + return; + } + Instruction::LoadInt load; + load.value = Primitive::fromReturnedValue(constant).toInt32(); + codegen->bytecodeGenerator->addInstruction(load); + return; + } + } + Instruction::LoadConst load; + load.index = codegen->registerConstant(constant); + codegen->bytecodeGenerator->addInstruction(load); + } +QT_WARNING_POP + } return; + case StackSlot: { + Instruction::LoadReg load; + load.reg = stackSlot(); + codegen->bytecodeGenerator->addInstruction(load); + } return; + case ScopedLocal: { + if (!scope) { + Instruction::LoadLocal load; + load.index = index; + codegen->bytecodeGenerator->addInstruction(load); + } else { + Instruction::LoadScopedLocal load; + load.index = index; + load.scope = scope; + codegen->bytecodeGenerator->addInstruction(load); + } + return; + } + case Name: + if (global) { + // these value properties of the global object are immutable, we we can directly convert them + // to their numeric value here + if (name == QStringLiteral("undefined")) { + Reference::fromConst(codegen, Encode::undefined()).loadInAccumulator(); + return; + } else if (name == QStringLiteral("Infinity")) { + Reference::fromConst(codegen, Encode(qInf())).loadInAccumulator(); + return; + } else if (name == QStringLiteral("Nan")) { + Reference::fromConst(codegen, Encode(qQNaN())).loadInAccumulator(); + return; + } + } + if (codegen->useFastLookups && global) { + Instruction::LoadGlobalLookup load; + load.index = codegen->registerGlobalGetterLookup(nameAsIndex()); + codegen->bytecodeGenerator->addInstruction(load); + } else { + Instruction::LoadName load; + load.name = nameAsIndex(); + codegen->bytecodeGenerator->addInstruction(load); + } + return; + case Member: + if (codegen->useFastLookups) { + if (propertyBase.isAccumulator()) { + Instruction::GetLookupA load; + load.index = codegen->registerGetterLookup(propertyNameIndex); + codegen->bytecodeGenerator->addInstruction(load); + } else { + Instruction::GetLookup load; + load.base = propertyBase.storeOnStack().stackSlot(); + load.index = codegen->registerGetterLookup(propertyNameIndex); + codegen->bytecodeGenerator->addInstruction(load); + } + } else { + if (propertyBase.isAccumulator()) { + Instruction::LoadPropertyA load; + load.name = propertyNameIndex; + codegen->bytecodeGenerator->addInstruction(load); + } else { + Instruction::LoadProperty load; + load.base = propertyBase.storeOnStack().stackSlot(); + load.name = propertyNameIndex; + codegen->bytecodeGenerator->addInstruction(load); + } + } + return; + case Subscript: { + if (elementSubscript.isAccumulator()) { + Instruction::LoadElementA load; + load.base = elementBase; + codegen->bytecodeGenerator->addInstruction(load); + } else if (elementSubscript.isConst()) { + Reference::fromConst(codegen, elementSubscript.constantValue()).loadInAccumulator(); + Instruction::LoadElementA load; + load.base = elementBase; + codegen->bytecodeGenerator->addInstruction(load); + } else { + Instruction::LoadElement load; + load.base = elementBase; + load.index = elementSubscript.storeOnStack().stackSlot(); + codegen->bytecodeGenerator->addInstruction(load); + } + } return; + case QmlScopeObject: { + Instruction::LoadScopeObjectProperty load; + load.base = qmlBase; + load.propertyIndex = qmlCoreIndex; + load.captureRequired = captureRequired; + codegen->bytecodeGenerator->addInstruction(load); + if (!captureRequired) + codegen->_context->scopeObjectPropertyDependencies.insert(qmlCoreIndex, qmlNotifyIndex); + } return; + case QmlContextObject: { + Instruction::LoadContextObjectProperty load; + load.base = qmlBase; + load.propertyIndex = qmlCoreIndex; + load.captureRequired = captureRequired; + codegen->bytecodeGenerator->addInstruction(load); + if (!captureRequired) + codegen->_context->contextObjectPropertyDependencies.insert(qmlCoreIndex, qmlNotifyIndex); + } return; + case Invalid: + break; + } + Q_ASSERT(false); + Q_UNREACHABLE(); +} diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 31e9cc0f46..dba9388292 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** 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. @@ -51,311 +51,465 @@ // #include "private/qv4global_p.h" -#include "qv4jsir_p.h" #include <private/qqmljsastvisitor_p.h> #include <private/qqmljsast_p.h> #include <private/qqmljsengine_p.h> +#include <private/qv4instr_moth_p.h> +#include <private/qv4compiler_p.h> +#include <private/qv4compilercontext_p.h> +#include <private/qqmlrefcount_p.h> #include <QtCore/QStringList> +#include <QtCore/QDateTime> #include <QStack> #ifndef V4_BOOTSTRAP #include <qqmlerror.h> #endif #include <private/qv4util_p.h> +#include <private/qv4bytecodegenerator_p.h> QT_BEGIN_NAMESPACE -namespace QQmlJS { -namespace AST { -class UiParameterList; +using namespace QQmlJS; + +namespace QV4 { + +namespace Moth { +struct Instruction; } +namespace CompiledData { +struct CompilationUnit; +} + +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; + using Instruction = QV4::Moth::Instruction; public: - Codegen(bool strict); - - 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) - }; + Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict); + void generateFromProgram(const QString &fileName, const QString &sourceCode, AST::Program *ast, - QV4::IR::Module *module, - CompilationMode mode = GlobalCode, - const QStringList &inheritedLocals = QStringList()); - void generateFromFunctionExpression(const QString &fileName, - const QString &sourceCode, - AST::FunctionExpression *ast, - QV4::IR::Module *module); + Module *module, + CompilationMode mode = GlobalCode); -protected: - enum Format { ex, cx, nx }; - struct Result { - QV4::IR::Expr *code; - QV4::IR::BasicBlock *iftrue; - QV4::IR::BasicBlock *iffalse; - Format format; - Format requested; +public: + class VolatileMemoryLocationScanner; + class VolatileMemoryLocations { + friend VolatileMemoryLocationScanner; + bool allVolatile = false; + QVector<QStringView> specificLocations; + public: + bool isVolatile(const QStringView &name) { + if (allVolatile) + return true; + return specificLocations.contains(name); + } - explicit Result(Format requested = ex) - : code(0) - , iftrue(0) - , iffalse(0) - , format(ex) - , requested(requested) {} - - explicit Result(QV4::IR::BasicBlock *iftrue, QV4::IR::BasicBlock *iffalse) - : code(0) - , iftrue(iftrue) - , iffalse(iffalse) - , format(ex) - , requested(cx) {} - - inline QV4::IR::Expr *operator*() const { Q_ASSERT(format == ex); return code; } - inline QV4::IR::Expr *operator->() const { Q_ASSERT(format == ex); return code; } + void add(const QStringRef &name) { if (!allVolatile) specificLocations.append(name); } + void setAllVolatile() { allVolatile = true; } + }; + class RValue { + Codegen *codegen; + enum Type { + Invalid, + Accumulator, + StackSlot, + Const + } type; + union { + Moth::StackSlot theStackSlot; + QV4::ReturnedValue constant; + }; - bool accept(Format f) - { - if (requested == f) { - format = f; + public: + static RValue fromStackSlot(Codegen *codegen, Moth::StackSlot stackSlot) { + RValue r; + r.codegen = codegen; + r.type = StackSlot; + r.theStackSlot = stackSlot; + return r; + } + static RValue fromAccumulator(Codegen *codegen) { + RValue r; + r.codegen = codegen; + r.type = Accumulator; + return r; + } + static RValue fromConst(Codegen *codegen, QV4::ReturnedValue value) { + RValue r; + r.codegen = codegen; + r.type = Const; + r.constant = value; + return r; + } + + bool operator==(const RValue &other) const; + + bool isValid() const { return type != Invalid; } + bool isAccumulator() const { return type == Accumulator; } + bool isStackSlot() const { return type == StackSlot; } + bool isConst() const { return type == Const; } + + Moth::StackSlot stackSlot() const { + Q_ASSERT(isStackSlot()); + return theStackSlot; + } + + QV4::ReturnedValue constantValue() const { + Q_ASSERT(isConst()); + return constant; + } + + Q_REQUIRED_RESULT RValue storeOnStack() const; + }; + struct Reference { + enum Type { + Invalid, + Accumulator, + StackSlot, + ScopedLocal, + Name, + Member, + Subscript, + QmlScopeObject, + QmlContextObject, + LastLValue = QmlContextObject, + Const + } type = Invalid; + + bool isLValue() const { return !isReadonly; } + + Reference(Codegen *cg, Type type = Invalid) : type(type), codegen(cg) {} + Reference() + : type(Invalid) + , codegen(nullptr) + {} + Reference(const Reference &other); + + Reference &operator =(const Reference &other); + + bool operator==(const Reference &other) const; + + bool isValid() const { return type != Invalid; } + bool loadTriggersSideEffect() const { + switch (type) { + case Name: + case Member: + case Subscript: return true; + default: + return false; } - return false; } - }; + bool isConst() const { return type == Const; } + bool isAccumulator() const { return type == Accumulator; } + bool isStackSlot() const { return type == StackSlot; } + bool isRegister() const { + return isStackSlot(); + } - struct Environment { - Environment *parent; + static Reference fromAccumulator(Codegen *cg) { + return Reference(cg, Accumulator); + } + static Reference fromStackSlot(Codegen *cg, int tempIndex = -1, bool isLocal = false) { + Reference r(cg, StackSlot); + if (tempIndex == -1) + tempIndex = cg->bytecodeGenerator->newRegister(); + r.theStackSlot = Moth::StackSlot::createRegister(tempIndex); + r.stackSlotIsLocalOrArgument = isLocal; + return r; + } + static Reference fromArgument(Codegen *cg, int index, bool isVolatile) { + Reference r(cg, StackSlot); + r.theStackSlot = Moth::StackSlot::createRegister(index + sizeof(CallData)/sizeof(Value) - 1); + r.stackSlotIsLocalOrArgument = true; + r.isVolatile = isVolatile; + return r; + } + static Reference fromScopedLocal(Codegen *cg, int index, int scope) { + Reference r(cg, ScopedLocal); + r.index = index; + r.scope = scope; + return r; + } + static Reference fromName(Codegen *cg, const QString &name) { + Reference r(cg, Name); + r.name = name; + return r; + } + static Reference fromMember(const Reference &baseRef, const QString &name) { + Reference r(baseRef.codegen, Member); + r.propertyBase = baseRef.asRValue(); + r.propertyNameIndex = r.codegen->registerString(name); + return r; + } + static Reference fromSubscript(const Reference &baseRef, const Reference &subscript) { + Q_ASSERT(baseRef.isStackSlot()); + Reference r(baseRef.codegen, Subscript); + r.elementBase = baseRef.stackSlot(); + r.elementSubscript = subscript.asRValue(); + return r; + } + static Reference fromConst(Codegen *cg, QV4::ReturnedValue constant) { + Reference r(cg, Const); + r.constant = constant; + r.isReadonly = true; + return r; + } + static Reference fromQmlScopeObject(const Reference &base, qint16 coreIndex, qint16 notifyIndex, bool captureRequired) { + Reference r(base.codegen, QmlScopeObject); + r.qmlBase = base.storeOnStack().stackSlot(); + r.qmlCoreIndex = coreIndex; + r.qmlNotifyIndex = notifyIndex; + r.captureRequired = captureRequired; + return r; + } + static Reference fromQmlContextObject(const Reference &base, qint16 coreIndex, qint16 notifyIndex, bool captureRequired) { + Reference r(base.codegen, QmlContextObject); + r.qmlBase = base.storeOnStack().stackSlot(); + r.qmlCoreIndex = coreIndex; + r.qmlNotifyIndex = notifyIndex; + r.captureRequired = captureRequired; + return r; + } + static Reference fromThis(Codegen *cg) { + Reference r = fromStackSlot(cg, CallData::This); + r.isReadonly = true; + return r; + } - enum MemberType { - UndefinedMember, - VariableDefinition, - VariableDeclaration, - FunctionDefinition - }; + RValue asRValue() const; + Reference asLValue() const; - struct Member { - MemberType type; - int index; - AST::FunctionExpression *function; - AST::VariableDeclaration::VariableScope scope; + Q_REQUIRED_RESULT static Reference storeConstOnStack(Codegen *cg, QV4::ReturnedValue constant) + { return Reference::fromConst(cg, constant).storeOnStack(); } - bool isLexicallyScoped() const { return this->scope != AST::VariableDeclaration::FunctionScope; } - }; - typedef QMap<QString, Member> MemberMap; - - MemberMap members; - AST::FormalParameterList *formals; - int maxNumberOfArguments; - bool hasDirectEval; - bool hasNestedFunctions; - bool isStrict; - bool isNamedFunctionExpression; - bool usesThis; - enum UsesArgumentsObject { - ArgumentsObjectUnknown, - ArgumentsObjectNotUsed, - ArgumentsObjectUsed + static void storeConstOnStack(Codegen *cg, QV4::ReturnedValue constant, int stackSlot) + { Reference::fromConst(cg, constant).storeOnStack(stackSlot); } + + Q_REQUIRED_RESULT Reference storeOnStack() const; + void storeOnStack(int tempIndex) const; + Q_REQUIRED_RESULT Reference storeRetainAccumulator() const; + Reference storeConsumeAccumulator() const; + + bool storeWipesAccumulator() const; + void loadInAccumulator() const; + + int nameAsIndex() const { + Q_ASSERT(type == Name); + return codegen->registerString(name); + } + + Moth::StackSlot stackSlot() const { + if (Q_UNLIKELY(!isStackSlot())) + Q_UNREACHABLE(); + return theStackSlot; + } + + union { + Moth::StackSlot theStackSlot; + QV4::ReturnedValue constant; + struct { // Scoped arguments/Local + int index; + int scope; + }; + struct { + RValue propertyBase; + int propertyNameIndex; + }; + struct { + Moth::StackSlot elementBase; + RValue elementSubscript; + }; + struct { // QML scope/context object case + Moth::StackSlot qmlBase; + qint16 qmlCoreIndex; + qint16 qmlNotifyIndex; + bool captureRequired; + }; }; + QString name; + mutable bool isArgOrEval = false; + bool isReadonly = false; + bool stackSlotIsLocalOrArgument = false; + bool isVolatile = false; + bool global = false; + Codegen *codegen; + + private: + void storeAccumulator() const; + Reference doStoreOnStack(int tempIndex) const; + }; - UsesArgumentsObject usesArgumentsObject; - - CompilationMode compilationMode; - - Environment(Environment *parent, CompilationMode mode) - : parent(parent) - , formals(0) - , maxNumberOfArguments(0) - , hasDirectEval(false) - , hasNestedFunctions(false) - , isStrict(false) - , isNamedFunctionExpression(false) - , usesThis(false) - , usesArgumentsObject(ArgumentsObjectUnknown) - , compilationMode(mode) - { - if (parent && parent->isStrict) - isStrict = true; + struct RegisterScope { + RegisterScope(Codegen *cg) + : generator(cg->bytecodeGenerator), + regCountForScope(generator->currentReg) {} + ~RegisterScope() { + generator->currentReg = regCountForScope; } + BytecodeGenerator *generator; + int regCountForScope; + }; + + struct ObjectPropertyValue { + ObjectPropertyValue() + : getter(-1) + , setter(-1) + , keyAsIndex(UINT_MAX) + {} + + Reference rvalue; + int getter; // index in _module->functions or -1 if not set + int setter; + uint keyAsIndex; + + bool hasGetter() const { return getter >= 0; } + bool hasSetter() const { return setter >= 0; } + }; +protected: - int findMember(const QString &name) const + enum Format { ex, cx, nx }; + class Result { + Reference _result; + + const BytecodeGenerator::Label *_iftrue; + const BytecodeGenerator::Label *_iffalse; + Format _format; + Format _requested; + bool _trueBlockFollowsCondition = false; + + public: + explicit Result(const Reference &lrvalue) + : _result(lrvalue) + , _iftrue(nullptr) + , _iffalse(nullptr) + , _format(ex) + , _requested(ex) { - MemberMap::const_iterator it = members.find(name); - if (it == members.end()) - return -1; - Q_ASSERT((*it).index != -1 || !parent); - return (*it).index; } - bool memberInfo(const QString &name, const Member **m) const + explicit Result(Format requested = ex) + : _iftrue(0) + , _iffalse(0) + , _format(ex) + , _requested(requested) {} + + explicit Result(const BytecodeGenerator::Label *iftrue, + const BytecodeGenerator::Label *iffalse, + bool trueBlockFollowsCondition) + : _iftrue(iftrue) + , _iffalse(iffalse) + , _format(ex) + , _requested(cx) + , _trueBlockFollowsCondition(trueBlockFollowsCondition) { - Q_ASSERT(m); - MemberMap::const_iterator it = members.find(name); - if (it == members.end()) { - *m = 0; - return false; - } - *m = &(*it); - return true; + Q_ASSERT(iftrue); + Q_ASSERT(iffalse); } - bool lookupMember(const QString &name, Environment **scope, int *index, int *distance) - { - Environment *it = this; - *distance = 0; - for (; it; it = it->parent, ++(*distance)) { - int idx = it->findMember(name); - if (idx != -1) { - *scope = it; - *index = idx; - return true; - } - } - return false; + const BytecodeGenerator::Label *iftrue() const { + Q_ASSERT(_requested == cx); + return _iftrue; + } + + const BytecodeGenerator::Label *iffalse() const { + Q_ASSERT(_requested == cx); + return _iffalse; + } + + Format format() const { + return _format; } - void enter(const QString &name, MemberType type, AST::VariableDeclaration::VariableScope scope, AST::FunctionExpression *function = 0) + bool accept(Format f) { - 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.index = -1; - 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; - } - } + if (_requested == f) { + _format = f; + return true; } + return false; } - }; - struct TempScope { - TempScope(QV4::IR::Function *f) - : function(f), - tempCountForScope(f->currentTemp) {} - ~TempScope() { - function->currentTemp = tempCountForScope; + bool trueBlockFollowsCondition() const { + return _trueBlockFollowsCondition; } - QV4::IR::Function *function; - int tempCountForScope; - }; - Environment *newEnvironment(AST::Node *node, Environment *parent, CompilationMode compilationMode) - { - Environment *env = new Environment(parent, compilationMode); - _envMap.insert(node, env); - return env; - } + const Reference &result() const { + return _result; + } - struct UiMember { + void setResult(const Reference &result) { + _result = result; + } }; - struct ScopeAndFinally { - enum ScopeType { - WithScope, - TryScope, - CatchScope - }; + void enterContext(AST::Node *node); + int leaveContext(); - ScopeAndFinally *parent; - AST::Finally *finally; - ScopeType type; + void leaveLoop(); - ScopeAndFinally(ScopeAndFinally *parent, ScopeType t = WithScope) : parent(parent), finally(0), type(t) {} - ScopeAndFinally(ScopeAndFinally *parent, AST::Finally *finally) - : parent(parent), finally(finally), type(TryScope) - {} + enum UnaryOperation { + UPlus, + UMinus, + PreIncrement, + PreDecrement, + PostIncrement, + PostDecrement, + Not, + Compl }; - struct Loop { - AST::LabelledStatement *labelledStatement; - AST::Statement *node; - QV4::IR::BasicBlock *breakBlock; - QV4::IR::BasicBlock *continueBlock; - Loop *parent; - ScopeAndFinally *scopeAndFinally; - - Loop(AST::Statement *node, QV4::IR::BasicBlock *breakBlock, QV4::IR::BasicBlock *continueBlock, Loop *parent) - : labelledStatement(0), node(node), breakBlock(breakBlock), continueBlock(continueBlock), parent(parent) {} - }; + Reference unop(UnaryOperation op, const Reference &expr); - void enterEnvironment(AST::Node *node); - void leaveEnvironment(); + void addCJump(); - void enterLoop(AST::Statement *node, QV4::IR::BasicBlock *breakBlock, QV4::IR::BasicBlock *continueBlock); - void leaveLoop(); - QV4::IR::BasicBlock *exceptionHandler() const - { - if (_exceptionHandlers.isEmpty()) - return 0; - return _exceptionHandlers.top(); - } - void pushExceptionHandler(QV4::IR::BasicBlock *handler) - { - handler->setExceptionHandler(true); - _exceptionHandlers.push(handler); + int registerString(const QString &name) { + return jsUnitGenerator->registerString(name); } - void popExceptionHandler() - { - Q_ASSERT(!_exceptionHandlers.isEmpty()); - _exceptionHandlers.pop(); - } - - QV4::IR::Expr *member(QV4::IR::Expr *base, const QString *name); - QV4::IR::Expr *argument(QV4::IR::Expr *expr); - QV4::IR::Expr *reference(QV4::IR::Expr *expr); - QV4::IR::Expr *unop(QV4::IR::AluOp op, QV4::IR::Expr *expr, const AST::SourceLocation &loc = AST::SourceLocation()); - QV4::IR::Expr *binop(QV4::IR::AluOp op, QV4::IR::Expr *left, QV4::IR::Expr *right, const AST::SourceLocation &loc = AST::SourceLocation()); - QV4::IR::Expr *call(QV4::IR::Expr *base, QV4::IR::ExprList *args); - QV4::IR::Stmt *move(QV4::IR::Expr *target, QV4::IR::Expr *source, QV4::IR::AluOp op = QV4::IR::OpInvalid); - QV4::IR::Stmt *cjump(QV4::IR::Expr *cond, QV4::IR::BasicBlock *iftrue, QV4::IR::BasicBlock *iffalse); + int registerConstant(QV4::ReturnedValue v) { return jsUnitGenerator->registerConstant(v); } + int registerGetterLookup(int nameIndex) { return jsUnitGenerator->registerGetterLookup(nameIndex); } + int registerSetterLookup(int nameIndex) { return jsUnitGenerator->registerSetterLookup(nameIndex); } + int registerGlobalGetterLookup(int nameIndex) { return jsUnitGenerator->registerGlobalGetterLookup(nameIndex); } // Returns index in _module->functions - int defineFunction(const QString &name, AST::Node *ast, - AST::FormalParameterList *formals, - AST::SourceElements *body, - const QStringList &inheritedLocals = QStringList()); - - void unwindException(ScopeAndFinally *outest); + virtual int defineFunction(const QString &name, AST::Node *ast, + AST::FormalParameterList *formals, + AST::SourceElements *body); void statement(AST::Statement *ast); void statement(AST::ExpressionNode *ast); - void condition(AST::ExpressionNode *ast, QV4::IR::BasicBlock *iftrue, QV4::IR::BasicBlock *iffalse); - Result expression(AST::ExpressionNode *ast); + void condition(AST::ExpressionNode *ast, const BytecodeGenerator::Label *iftrue, + const BytecodeGenerator::Label *iffalse, + bool trueBlockFollowsCondition); + Reference expression(AST::ExpressionNode *ast); Result sourceElement(AST::SourceElement *ast); - UiMember uiObjectMember(AST::UiObjectMember *ast); void accept(AST::Node *node); void functionBody(AST::FunctionBody *ast); void program(AST::Program *ast); void sourceElements(AST::SourceElements *ast); + void statementList(AST::StatementList *ast); void variableDeclaration(AST::VariableDeclaration *ast); void variableDeclarationList(AST::VariableDeclarationList *ast); - QV4::IR::Expr *identifier(const QString &name, int line = 0, int col = 0); + Reference referenceForName(const QString &name, bool lhs); + + void loadClosure(int index); + // Hook provided to implement QML lookup semantics - virtual QV4::IR::Expr *fallbackNameLookup(const QString &name, int line, int col); + virtual Reference fallbackNameLookup(const QString &name); virtual void beginFunctionBodyHook() {} // nodes @@ -457,7 +611,7 @@ protected: bool visit(AST::UiScriptBinding *ast) override; bool visit(AST::UiSourceElement *ast) override; - bool throwSyntaxErrorOnEvalOrArgumentsInStrictMode(QV4::IR::Expr* expr, const AST::SourceLocation &loc); + bool throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r, const AST::SourceLocation &loc); virtual void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail); virtual void throwReferenceError(const AST::SourceLocation &loc, const QString &detail); @@ -467,117 +621,47 @@ public: QList<QQmlError> qmlErrors() const; #endif + Reference binopHelper(QSOperator::Op oper, Reference &left, Reference &right); + Reference jumpBinop(QSOperator::Op oper, Reference &left, Reference &right); + struct Arguments { int argc; int argv; }; + Arguments pushArgs(AST::ArgumentList *args); + + void setUseFastLookups(bool b) { useFastLookups = b; } + + void handleTryCatch(AST::TryStatement *ast); + void handleTryFinally(AST::TryStatement *ast); + + QQmlRefPointer<QV4::CompiledData::CompilationUnit> generateCompilationUnit(bool generateUnitData = true); + static QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading(); + + Context *currentContext() const { return _context; } + protected: + friend class ScanFunctions; + friend struct ControlFlow; + friend struct ControlFlowCatch; + friend struct ControlFlowFinally; Result _expr; - QString _property; - UiMember _uiMember; - QV4::IR::Module *_module; - QV4::IR::Function *_function; - QV4::IR::BasicBlock *_block; - QV4::IR::BasicBlock *_exitBlock; - unsigned _returnAddress; - Environment *_variableEnvironment; - Loop *_loop; + VolatileMemoryLocations _volataleMemoryLocations; + Module *_module; + int _returnAddress; + Context *_context; AST::LabelledStatement *_labelledStatement; - ScopeAndFinally *_scopeAndFinally; - QHash<AST::Node *, Environment *> _envMap; - QHash<AST::FunctionExpression *, int> _functionMap; - QStack<QV4::IR::BasicBlock *> _exceptionHandlers; + QV4::Compiler::JSUnitGenerator *jsUnitGenerator; + BytecodeGenerator *bytecodeGenerator = 0; bool _strictMode; + bool useFastLookups = true; + bool requiresReturnValue = false; bool _fileNameIsUrl; bool hasError; QList<QQmlJS::DiagnosticMessage> _errors; - class ScanFunctions: protected Visitor - { - typedef QV4::TemporaryAssignment<bool> TemporaryBoolAssignment; - public: - ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode); - void operator()(AST::Node *node); - - void enterEnvironment(AST::Node *node, CompilationMode compilationMode); - void leaveEnvironment(); - - void enterQmlScope(AST::Node *ast, const QString &name) - { enterFunction(ast, name, /*formals*/0, /*body*/0, /*expr*/0, /*isExpression*/false); } - - void enterQmlFunction(AST::FunctionDeclaration *ast) - { enterFunction(ast, false, false); } - - protected: - using Visitor::visit; - using Visitor::endVisit; - - void checkDirectivePrologue(AST::SourceElements *ast); - - void checkName(const QStringRef &name, const AST::SourceLocation &loc); - void checkForArguments(AST::FormalParameterList *parameters); - - bool visit(AST::Program *ast) override; - void endVisit(AST::Program *) override; - - bool visit(AST::CallExpression *ast) override; - bool visit(AST::NewMemberExpression *ast) override; - bool visit(AST::ArrayLiteral *ast) override; - bool visit(AST::VariableDeclaration *ast) override; - bool visit(AST::IdentifierExpression *ast) override; - bool visit(AST::ExpressionStatement *ast) override; - bool visit(AST::FunctionExpression *ast) override; - - void enterFunction(AST::FunctionExpression *ast, bool enterName, bool isExpression = true); - - void endVisit(AST::FunctionExpression *) override; - - bool visit(AST::ObjectLiteral *ast) override; - - bool visit(AST::PropertyGetterSetter *ast) override; - void endVisit(AST::PropertyGetterSetter *) override; - - bool visit(AST::FunctionDeclaration *ast) override; - void endVisit(AST::FunctionDeclaration *) override; - - bool visit(AST::WithStatement *ast) override; - - bool visit(AST::DoWhileStatement *ast) override; - bool visit(AST::ForStatement *ast) override; - bool visit(AST::LocalForStatement *ast) override; - bool visit(AST::ForEachStatement *ast) override; - bool visit(AST::LocalForEachStatement *ast) override; - bool visit(AST::ThisExpression *ast) override; - - bool visit(AST::Block *ast) override; - - protected: - void enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::FunctionBody *body, AST::FunctionExpression *expr, bool isExpression); - - // fields: - Codegen *_cg; - const QString _sourceCode; - Environment *_variableEnvironment; - QStack<Environment *> _envStack; - - bool _allowFuncDecls; - CompilationMode defaultProgramMode; - }; - -}; - -#ifndef V4_BOOTSTRAP -class RuntimeCodegen : public Codegen -{ -public: - RuntimeCodegen(QV4::ExecutionEngine *engine, bool strict) - : Codegen(strict) - , engine(engine) - {} - - void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) override; - void throwReferenceError(const AST::SourceLocation &loc, const QString &detail) override; private: - QV4::ExecutionEngine *engine; + VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast) const; }; -#endif // V4_BOOTSTRAP + +} } diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 292f5cde45..3a1fd7fce5 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -38,7 +38,6 @@ ****************************************************************************/ #include "qv4compileddata_p.h" -#include "qv4jsir_p.h" #include <private/qv4value_p.h> #ifndef V4_BOOTSTRAP #include <private/qv4engine_p.h> @@ -50,6 +49,7 @@ #include <private/qqmlpropertycache_p.h> #include <private/qqmltypeloader_p.h> #include <private/qqmlengine_p.h> +#include <private/qv4vme_moth_p.h> #include "qv4compilationunitmapper_p.h" #include <QQmlPropertyMap> #include <QDateTime> @@ -94,23 +94,10 @@ static QString cacheFilePath(const QUrl &url) } #endif -#ifndef V4_BOOTSTRAP CompilationUnit::CompilationUnit() - : data(0) - , engine(0) - , qmlEngine(0) - , runtimeLookups(0) - , runtimeRegularExpressions(0) - , runtimeClasses(0) - , constants(nullptr) - , totalBindingsCount(0) - , totalParserStatusCount(0) - , totalObjectCount(0) - , metaTypeId(-1) - , listMetaTypeId(-1) - , isRegisteredWithEngine(false) {} +#ifndef V4_BOOTSTRAP CompilationUnit::~CompilationUnit() { unlink(); @@ -163,10 +150,6 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) l->setter = QV4::Lookup::setterGeneric; else if (type == CompiledData::Lookup::Type_GlobalGetter) l->globalGetter = QV4::Lookup::globalGetterGeneric; - else if (type == CompiledData::Lookup::Type_IndexedGetter) - l->indexedGetter = QV4::Lookup::indexedGetterGeneric; - else if (type == CompiledData::Lookup::Type_IndexedSetter) - l->indexedSetter = QV4::Lookup::indexedSetterGeneric; for (int j = 0; j < QV4::Lookup::Size; ++j) l->classList[j] = 0; @@ -201,6 +184,19 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) linkBackendToEngine(engine); + static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE"); + if (showCode) { + qDebug() << "=== Constant table"; + Moth::dumpConstantTable(constants, data->constantTableSize); + qDebug() << "=== String table"; + for (uint i = 0; i < data->stringTableSize; ++i) + qDebug() << " " << i << ":" << runtimeStrings[i]->toQString(); + qDebug() << "=== Closure table"; + for (uint i = 0; i < data->functionTableSize; ++i) + qDebug() << " " << i << ":" << runtimeFunctions[i]->name()->toQString(); + qDebug() << "root function at index " << (data->indexOfRootFunction != -1 ? data->indexOfRootFunction : 0); + } + if (data->indexOfRootFunction != -1) return runtimeFunctions[data->indexOfRootFunction]; else @@ -260,14 +256,6 @@ void CompilationUnit::markObjects(QV4::MarkStack *markStack) } } -void CompilationUnit::destroy() -{ - if (qmlEngine) - QQmlEnginePrivate::deleteInEngineThread(qmlEngine, this); - else - delete this; -} - IdentifierHash<int> CompilationUnit::namedObjectsPerComponent(int componentObjectIndex) { auto it = namedObjectsPerComponentCache.find(componentObjectIndex); @@ -349,7 +337,7 @@ bool CompilationUnit::verifyChecksum(const DependentTypesHasher &dependencyHashe sizeof(data->dependencyMD5Checksum)) == 0; } -bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, EvalISelFactory *iselFactory, QString *errorString) +bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString) { if (!QQmlFile::isLocalFile(url)) { *errorString = QStringLiteral("File has to be a local file."); @@ -382,26 +370,26 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeS { const QString foundCodeGenerator = stringAt(data->codeGeneratorIndex); - const QString expectedCodeGenerator = iselFactory->codeGeneratorName; + const QString expectedCodeGenerator = QStringLiteral("moth"); // ### if (foundCodeGenerator != expectedCodeGenerator) { *errorString = QString::fromUtf8("Code generator mismatch. Found code generated by %1 but expected %2").arg(foundCodeGenerator).arg(expectedCodeGenerator); return false; } } - if (!memoryMapCode(errorString)) - return false; - dataPtrChange.commit(); free(const_cast<Unit*>(oldDataPtr)); backingFile.reset(cacheFile.take()); return true; } -bool CompilationUnit::memoryMapCode(QString *errorString) +void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine) { - *errorString = QStringLiteral("Missing code mapping backend"); - return false; + runtimeFunctions.resize(data->functionTableSize); + for (int i = 0 ;i < runtimeFunctions.size(); ++i) { + const QV4::CompiledData::Function *compiledFunction = data->functionAt(i); + runtimeFunctions[i] = new QV4::Function(engine, this, compiledFunction, &Moth::VME::exec); + } } #endif // V4_BOOTSTRAP @@ -443,17 +431,12 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) memcpy(&unitPtr, &dataPtr, sizeof(unitPtr)); unitPtr->flags |= Unit::StaticData; - prepareCodeOffsetsForDiskStorage(unitPtr); - qint64 headerWritten = cacheFile.write(modifiedUnit); if (headerWritten != modifiedUnit.size()) { *errorString = cacheFile.errorString(); return false; } - if (!saveCodeToDisk(&cacheFile, unitPtr, errorString)) - return false; - if (!cacheFile.commit()) { *errorString = cacheFile.errorString(); return false; @@ -467,19 +450,6 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) #endif // QT_CONFIG(temporaryfile) } -void CompilationUnit::prepareCodeOffsetsForDiskStorage(Unit *unit) -{ - Q_UNUSED(unit); -} - -bool CompilationUnit::saveCodeToDisk(QIODevice *device, const Unit *unit, QString *errorString) -{ - Q_UNUSED(device); - Q_UNUSED(unit); - *errorString = QStringLiteral("Saving code to disk is not supported in this configuration"); - return false; -} - Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument) { if (!irDocument->javaScriptCompilationUnit->data) @@ -774,6 +744,17 @@ bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *e #endif +void CompilationUnit::destroy() +{ +#if !defined(V4_BOOTSTRAP) + if (qmlEngine) + QQmlEnginePrivate::deleteInEngineThread(qmlEngine, this); + else +#endif + delete this; +} + + void Unit::generateChecksum() { #ifndef V4_BOOTSTRAP diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index a83dea1a0a..55c5f1f29f 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -72,7 +72,7 @@ QT_BEGIN_NAMESPACE // Bump this whenever the compiler data structures change in an incompatible way. -#define QV4_DATA_STRUCTURE_VERSION 0x13 +#define QV4_DATA_STRUCTURE_VERSION 0x15 class QIODevice; class QQmlPropertyCache; @@ -87,9 +87,6 @@ struct Document; } namespace QV4 { -namespace IR { -struct Function; -} struct Function; class EvalISelFactory; @@ -155,9 +152,7 @@ struct Lookup enum Type : unsigned int { Type_Getter = 0x0, Type_Setter = 0x1, - Type_GlobalGetter = 2, - Type_IndexedGetter = 3, - Type_IndexedSetter = 4 + Type_GlobalGetter = 2 }; union { @@ -202,6 +197,11 @@ struct String }; static_assert(sizeof(String) == 4, "String structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +struct CodeOffsetToLine { + quint32_le codeOffset; + quint32_le line; +}; + // Function is aligned on an 8-byte boundary to make sure there are no bus errors or penalties // for unaligned access. The ordering of the fields is also from largest to smallest. struct Function @@ -210,22 +210,23 @@ struct Function IsStrict = 0x1, HasDirectEval = 0x2, UsesArgumentsObject = 0x4, - IsNamedExpression = 0x8, - HasCatchOrWith = 0x10, - CanUseSimpleCall = 0x20 +// Unused = 0x8, + HasCatchOrWith = 0x10 }; - // Absolute offset into file where the code for this function is located. Only used when the function - // is serialized. - quint64_le codeOffset; - quint64_le codeSize; + // Absolute offset into file where the code for this function is located. + quint32_le codeOffset; + quint32_le codeSize; quint32_le nameIndex; quint32_le nFormals; quint32_le formalsOffset; quint32_le nLocals; quint32_le localsOffset; + quint32_le nLineNumbers; + quint32_le lineNumberOffset; quint32_le nInnerFunctions; + quint32_le nRegisters; Location location; // Qml Extensions Begin @@ -249,6 +250,7 @@ struct Function const quint32_le *formalsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + formalsOffset); } const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); } + const CodeOffsetToLine *lineNumberTable() const { return reinterpret_cast<const CodeOffsetToLine *>(reinterpret_cast<const char *>(this) + lineNumberOffset); } const quint32_le *qmlIdObjectDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingIdObjectsOffset); } const quint32_le *qmlContextPropertiesDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingContextPropertiesOffset); } const quint32_le *qmlScopePropertiesDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingScopePropertiesOffset); } @@ -258,13 +260,23 @@ struct Function const quint32_le *formalsEnd() const { return formalsTable() + nFormals; } // --- + const uchar *code() const { return reinterpret_cast<const uchar *>(this) + codeOffset; } + inline bool hasQmlDependencies() const { return nDependingIdObjects > 0 || nDependingContextProperties > 0 || nDependingScopeProperties > 0; } - static int calculateSize(int nFormals, int nLocals, int nInnerfunctions, int nIdObjectDependencies, int nPropertyDependencies) { - return (sizeof(Function) + (nFormals + nLocals + nInnerfunctions + nIdObjectDependencies + 2 * nPropertyDependencies) * sizeof(quint32) + 7) & ~0x7; + static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int nIdObjectDependencies, int nPropertyDependencies, int codeSize) { + int trailingData = (nFormals + nLocals + nInnerfunctions + nIdObjectDependencies + + 2 * nPropertyDependencies)*sizeof (quint32) + nLines*sizeof(CodeOffsetToLine); + size_t size = align(align(sizeof(Function)) + size_t(trailingData)) + align(codeSize); + Q_ASSERT(size < INT_MAX); + return int(size); + } + + static size_t align(size_t a) { + return (a + 7) & ~size_t(7); } }; -static_assert(sizeof(Function) == 72, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +static_assert(sizeof(Function) == 76, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); // Qml data structures @@ -860,40 +872,59 @@ typedef QVector<QQmlPropertyData*> BindingPropertyData; struct Q_QML_PRIVATE_EXPORT CompilationUnitBase { - QV4::Heap::String **runtimeStrings = 0; // Array + // pointers either to data->constants() or little-endian memory copy. + QV4::Heap::String **runtimeStrings = nullptr; // Array + const Value* constants = nullptr; + QV4::Value *runtimeRegularExpressions = nullptr; }; Q_STATIC_ASSERT(std::is_standard_layout<CompilationUnitBase>::value); Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeStrings) == 0); +Q_STATIC_ASSERT(offsetof(CompilationUnitBase, constants) == sizeof(QV4::Heap::String **)); +Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeRegularExpressions) == offsetof(CompilationUnitBase, constants) + sizeof(const Value *)); -struct Q_QML_PRIVATE_EXPORT CompilationUnit : public CompilationUnitBase, public QQmlRefCount +struct Q_QML_PRIVATE_EXPORT CompilationUnit final : public CompilationUnitBase { +public: + CompilationUnit(); #ifdef V4_BOOTSTRAP - CompilationUnit() - : data(0) - {} - virtual ~CompilationUnit() {} + ~CompilationUnit() {} #else - CompilationUnit(); - virtual ~CompilationUnit(); + ~CompilationUnit(); #endif - const Unit *data; + void addref() + { + Q_ASSERT(refCount.load() > 0); + refCount.ref(); + } + + void release() + { + Q_ASSERT(refCount.load() > 0); + if (!refCount.deref()) + destroy(); + } + int count() const + { + return refCount.load(); + } + + const Unit *data = nullptr; // Called only when building QML, when we build the header for JS first and append QML data - virtual QV4::CompiledData::Unit *createUnitData(QmlIR::Document *irDocument); + QV4::CompiledData::Unit *createUnitData(QmlIR::Document *irDocument); #ifndef V4_BOOTSTRAP QIntrusiveListNode nextCompilationUnit; - ExecutionEngine *engine; - QQmlEnginePrivate *qmlEngine; // only used in QML environment for composite types, not in plain QJSEngine case. + ExecutionEngine *engine = nullptr; + QQmlEnginePrivate *qmlEngine = nullptr; // only used in QML environment for composite types, not in plain QJSEngine case. QString fileName() const { return data->stringAt(data->sourceFileIndex); } QUrl url() const { if (m_url.isNull) m_url = QUrl(fileName()); return m_url; } - QV4::Lookup *runtimeLookups; - QV4::Value *runtimeRegularExpressions; - QV4::InternalClass **runtimeClasses; + QV4::Lookup *runtimeLookups = nullptr; + QV4::InternalClass **runtimeClasses = nullptr; QVector<QV4::Function *> runtimeFunctions; mutable QQmlNullableValue<QUrl> m_url; @@ -913,23 +944,20 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public CompilationUnitBase, public QHash<int, IdentifierHash<int>> namedObjectsPerComponentCache; IdentifierHash<int> namedObjectsPerComponent(int componentObjectIndex); - // pointers either to data->constants() or little-endian memory copy. - const Value* constants; - void finalizeCompositeType(QQmlEnginePrivate *qmlEngine); - int totalBindingsCount; // Number of bindings used in this type - int totalParserStatusCount; // Number of instantiated types that are QQmlParserStatus subclasses - int totalObjectCount; // Number of objects explicitly instantiated + int totalBindingsCount = 0; // Number of bindings used in this type + int totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses + int totalObjectCount = 0; // Number of objects explicitly instantiated QVector<QQmlScriptData *> dependentScripts; ResolvedTypeReferenceMap resolvedTypes; bool verifyChecksum(const DependentTypesHasher &dependencyHasher) const; - int metaTypeId; - int listMetaTypeId; - bool isRegisteredWithEngine; + int metaTypeId = -1; + int listMetaTypeId = -1; + bool isRegisteredWithEngine = false; QScopedPointer<CompilationUnitMapper> backingFile; @@ -960,25 +988,23 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public CompilationUnitBase, public void markObjects(MarkStack *markStack); - void destroy() override; - - bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, EvalISelFactory *iselFactory, QString *errorString); + bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString); protected: - virtual void linkBackendToEngine(QV4::ExecutionEngine *engine) = 0; - virtual bool memoryMapCode(QString *errorString); + void linkBackendToEngine(QV4::ExecutionEngine *engine); #endif // V4_BOOTSTRAP +private: + void destroy(); + + QAtomicInt refCount = 1; + public: #if defined(V4_BOOTSTRAP) bool saveToDisk(const QString &outputFileName, QString *errorString); #else bool saveToDisk(const QUrl &unitUrl, QString *errorString); #endif - -protected: - virtual void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit); - virtual bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString); }; #ifndef V4_BOOTSTRAP diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index f02ee728c9..96c9307513 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -39,10 +39,12 @@ #include <qv4compiler_p.h> #include <qv4compileddata_p.h> -#include <qv4isel_p.h> +#include <qv4codegen_p.h> #include <private/qv4string_p.h> #include <private/qv4value_p.h> #include <private/qv4alloca_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsast_p.h> #include <wtf/MathExtras.h> #include <QCryptographicHash> @@ -98,70 +100,66 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit) } } -QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QV4::IR::Module *module) - : irModule(module) +QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QV4::Compiler::Module *module) + : module(module) { // Make sure the empty string always gets index 0 registerString(QString()); } -uint QV4::Compiler::JSUnitGenerator::registerIndexedGetterLookup() +int QV4::Compiler::JSUnitGenerator::registerGetterLookup(const QString &name) { - CompiledData::Lookup l; - l.type_and_flags = CompiledData::Lookup::Type_IndexedGetter; - l.nameIndex = 0; - lookups << l; - return lookups.size() - 1; + return registerGetterLookup(registerString(name)); } -uint QV4::Compiler::JSUnitGenerator::registerIndexedSetterLookup() +int QV4::Compiler::JSUnitGenerator::registerGetterLookup(int nameIndex) { CompiledData::Lookup l; - l.type_and_flags = CompiledData::Lookup::Type_IndexedSetter; - l.nameIndex = 0; + l.type_and_flags = CompiledData::Lookup::Type_Getter; + l.nameIndex = nameIndex; lookups << l; return lookups.size() - 1; } -uint QV4::Compiler::JSUnitGenerator::registerGetterLookup(const QString &name) +int QV4::Compiler::JSUnitGenerator::registerSetterLookup(const QString &name) { - CompiledData::Lookup l; - l.type_and_flags = CompiledData::Lookup::Type_Getter; - l.nameIndex = registerString(name); - lookups << l; - return lookups.size() - 1; + return registerSetterLookup(registerString(name)); } - -uint QV4::Compiler::JSUnitGenerator::registerSetterLookup(const QString &name) +int QV4::Compiler::JSUnitGenerator::registerSetterLookup(int nameIndex) { CompiledData::Lookup l; l.type_and_flags = CompiledData::Lookup::Type_Setter; - l.nameIndex = registerString(name); + l.nameIndex = nameIndex; lookups << l; return lookups.size() - 1; } -uint QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(const QString &name) +int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(const QString &name) +{ + return registerGlobalGetterLookup(registerString(name)); +} + +int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(int nameIndex) { CompiledData::Lookup l; l.type_and_flags = CompiledData::Lookup::Type_GlobalGetter; - l.nameIndex = registerString(name); + l.nameIndex = nameIndex; lookups << l; return lookups.size() - 1; } -int QV4::Compiler::JSUnitGenerator::registerRegExp(QV4::IR::RegExp *regexp) +int QV4::Compiler::JSUnitGenerator::registerRegExp(QQmlJS::AST::RegExpLiteral *regexp) { CompiledData::RegExp re; - re.stringIndex = registerString(*regexp->value); + re.stringIndex = registerString(regexp->pattern.toString()); re.flags = 0; - if (regexp->flags & QV4::IR::RegExp::RegExp_Global) + if (regexp->flags & QQmlJS::Lexer::RegExp_Global) re.flags |= CompiledData::RegExp::RegExp_Global; - if (regexp->flags & QV4::IR::RegExp::RegExp_IgnoreCase) + if (regexp->flags & QQmlJS::Lexer::RegExp_IgnoreCase) re.flags |= CompiledData::RegExp::RegExp_IgnoreCase; - if (regexp->flags & QV4::IR::RegExp::RegExp_Multiline) + if (regexp->flags & QQmlJS::Lexer::RegExp_Multiline) re.flags |= CompiledData::RegExp::RegExp_Multiline; regexps.append(re); @@ -177,50 +175,62 @@ int QV4::Compiler::JSUnitGenerator::registerConstant(QV4::ReturnedValue v) return constants.size() - 1; } -int QV4::Compiler::JSUnitGenerator::registerJSClass(int count, IR::ExprList *args) +QV4::ReturnedValue QV4::Compiler::JSUnitGenerator::constant(int idx) +{ + return constants.at(idx); +} + +int QV4::Compiler::JSUnitGenerator::registerJSClass(const QVector<MemberInfo> &members) { // ### re-use existing class definitions. - const int size = CompiledData::JSClass::calculateSize(count); + const int size = CompiledData::JSClass::calculateSize(members.size()); jsClassOffsets.append(jsClassData.size()); const int oldSize = jsClassData.size(); jsClassData.resize(jsClassData.size() + size); memset(jsClassData.data() + oldSize, 0, size); CompiledData::JSClass *jsClass = reinterpret_cast<CompiledData::JSClass*>(jsClassData.data() + oldSize); - jsClass->nMembers = count; + jsClass->nMembers = members.size(); CompiledData::JSClassMember *member = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + 1); - IR::ExprList *it = args; - for (int i = 0; i < count; ++i, it = it->next, ++member) { - QV4::IR::Name *name = it->expr->asName(); - it = it->next; + for (const MemberInfo &memberInfo : members) { + member->nameOffset = registerString(memberInfo.name); + member->isAccessor = memberInfo.isAccessor; + ++member; + } - const bool isData = it->expr->asConst()->value; - it = it->next; + return jsClassOffsets.size() - 1; +} - member->nameOffset = registerString(*name->id); - member->isAccessor = !isData; +int QV4::Compiler::JSUnitGenerator::registerJSClass(int count, CompiledData::JSClassMember *members) +{ + const int size = CompiledData::JSClass::calculateSize(count); + jsClassOffsets.append(jsClassData.size()); + const int oldSize = jsClassData.size(); + jsClassData.resize(jsClassData.size() + size); + memset(jsClassData.data() + oldSize, 0, size); - if (!isData) - it = it->next; - } + CompiledData::JSClass *jsClass = reinterpret_cast<CompiledData::JSClass*>(jsClassData.data() + oldSize); + jsClass->nMembers = count; + CompiledData::JSClassMember *jsClassMembers = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + 1); + memcpy(jsClassMembers, members, sizeof(CompiledData::JSClassMember)*count); return jsClassOffsets.size() - 1; } 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 (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(quint32_le, functionOffsets, irModule->functions.size() * sizeof(quint32_le)); + Q_ALLOCA_VAR(quint32_le, functionOffsets, module->functions.size() * sizeof(quint32_le)); uint jsClassDataOffset = 0; char *dataPtr; @@ -235,9 +245,9 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO memcpy(dataPtr + unit->offsetToFunctionTable, functionOffsets, unit->functionTableSize * sizeof(quint32_le)); - 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) { + Context *function = module->functions.at(i); + if (function == module->rootContext) unit->indexOfRootFunction = i; writeFunction(dataPtr + functionOffsets[i], function); @@ -277,14 +287,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, QV4::Compiler::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; @@ -292,13 +302,9 @@ 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) - 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); @@ -306,7 +312,13 @@ 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->nLineNumbers = irFunction->lineNumberMapping.size(); + function->lineNumberOffset = currentOffset; + currentOffset += function->nLineNumbers * sizeof(CompiledData::CodeOffsetToLine); + + function->nInnerFunctions = irFunction->nestedContexts.size(); + + function->nRegisters = irFunction->registerCount; function->nDependingIdObjects = 0; function->nDependingContextProperties = 0; @@ -333,18 +345,21 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *i function->location.line = irFunction->line; function->location.column = irFunction->column; - function->codeOffset = 0; - function->codeSize = 0; + function->codeOffset = currentOffset; + function->codeSize = irFunction->code.size(); // write formals quint32_le *formals = (quint32_le *)(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_le *locals = (quint32_le *)(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 line numbers + memcpy(f + function->lineNumberOffset, irFunction->lineNumberMapping.constData(), irFunction->lineNumberMapping.size()*sizeof(CompiledData::CodeOffsetToLine)); // write QML dependencies quint32_le *writtenDeps = (quint32_le *)(f + function->dependingIdObjectsOffset); @@ -364,6 +379,9 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *i *writtenDeps++ = property.key(); // property index *writtenDeps++ = property.value(); // notify index } + + // write byte code + memcpy(f + function->codeOffset, irFunction->code.constData(), irFunction->code.size()); } QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, quint32_le *functionOffsets, uint *jsClassDataOffset) @@ -372,17 +390,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); @@ -410,13 +428,14 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp nextOffset = (nextOffset + 7) & ~quint32(0x7); - 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) { + 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->lineNumberMapping.size(), f->nestedContexts.size(), + qmlIdDepsCount, qmlPropertyDepsCount, f->code.size()); } if (option == GenerateWithStringTable) { @@ -428,8 +447,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 9c51a44bad..360af6540f 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -51,8 +51,11 @@ // #include <QtCore/qstring.h> -#include "qv4jsir_p.h" -#include <private/qendian_p.h> +#include <QtCore/qhash.h> +#include <QtCore/qstringlist.h> +#include <private/qv4global_p.h> +#include <private/qqmljsastfwd_p.h> +#include <private/qv4compileddata_p.h> QT_BEGIN_NAMESPACE @@ -90,23 +93,31 @@ private: }; struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { - JSUnitGenerator(IR::Module *module); + struct MemberInfo { + QString name; + bool isAccessor; + }; + + JSUnitGenerator(Module *module); int registerString(const QString &str) { return stringTable.registerString(str); } int getStringId(const QString &string) const { return stringTable.getStringId(string); } QString stringForIndex(int index) const { return stringTable.stringForIndex(index); } - uint registerGetterLookup(const QString &name); - uint registerSetterLookup(const QString &name); - uint registerGlobalGetterLookup(const QString &name); - uint registerIndexedGetterLookup(); - uint registerIndexedSetterLookup(); + int registerGetterLookup(const QString &name); + int registerGetterLookup(int nameIndex); + int registerSetterLookup(const QString &name); + int registerSetterLookup(int nameIndex); + int registerGlobalGetterLookup(const QString &name); + int registerGlobalGetterLookup(int nameIndex); - int registerRegExp(IR::RegExp *regexp); + int registerRegExp(QQmlJS::AST::RegExpLiteral *regexp); int registerConstant(ReturnedValue v); + ReturnedValue constant(int idx); - int registerJSClass(int count, IR::ExprList *args); + int registerJSClass(const QVector<MemberInfo> &members); + int registerJSClass(int count, CompiledData::JSClassMember *members); enum GeneratorOption { GenerateWithStringTable, @@ -115,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, Context *irFunction) const; StringTableGenerator stringTable; QString codeGeneratorName; private: CompiledData::Unit generateHeader(GeneratorOption option, quint32_le *functionOffsets, uint *jsClassDataOffset); - IR::Module *irModule; + Module *module; QList<CompiledData::Lookup> lookups; QVector<CompiledData::RegExp> regexps; diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp new file mode 100644 index 0000000000..3293dc53c8 --- /dev/null +++ b/src/qml/compiler/qv4compilercontext.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4compilercontext_p.h" +#include "qv4compilercontrolflow_p.h" + +QT_USE_NAMESPACE +using namespace QV4; +using namespace QV4::Compiler; +using namespace QQmlJS::AST; + +QT_BEGIN_NAMESPACE + +Context *Module::newContext(Node *node, Context *parent, CompilationMode compilationMode) +{ + Context *c = new Context(parent, compilationMode); + if (node) { + SourceLocation loc = node->firstSourceLocation(); + c->line = loc.startLine; + c->column = loc.startColumn; + } + + contextMap.insert(node, c); + + if (!parent) + rootContext = c; + else { + parent->nestedContexts.append(c); + c->isStrict = parent->isStrict; + } + + return c; +} + +bool Context::forceLookupByName() +{ + ControlFlow *flow = controlFlow; + while (flow) { + if (flow->needsLookupByName) + return true; + flow = flow->parent; + } + return false; +} + +QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h new file mode 100644 index 0000000000..3db30ea23d --- /dev/null +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -0,0 +1,290 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4COMPILERCONTEXT_P_H +#define QV4COMPILERCONTEXT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qv4global_p.h" +#include <private/qqmljsast_p.h> +#include <private/qv4compileddata_p.h> +#include <QtCore/QStringList> +#include <QtCore/QDateTime> +#include <QtCore/QStack> +#include <QtCore/QHash> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace Compiler { + +struct ControlFlow; + +enum CompilationMode { + GlobalCode, + EvalCode, + FunctionCode, + QmlBinding // This is almost the same as EvalCode, except: + // * function declarations are moved to the return address when encountered + // * return statements are allowed everywhere (like in FunctionCode) + // * variable declarations are treated as true locals (like in FunctionCode) +}; + +struct Context; + +struct Module { + Module(bool debugMode) + : debugMode(debugMode) + {} + ~Module() { + qDeleteAll(contextMap); + } + + Context *newContext(QQmlJS::AST::Node *node, Context *parent, CompilationMode compilationMode); + + QHash<QQmlJS::AST::Node *, Context *> contextMap; + QList<Context *> functions; + Context *rootContext; + QString fileName; + QDateTime sourceTimeStamp; + uint unitFlags = 0; // flags merged into CompiledData::Unit::flags + bool debugMode = false; + QString targetABI; // ### seems unused currently +}; + + +struct Context { + Context *parent; + QString name; + int line = 0; + int column = 0; + int registerCount = 0; + int functionIndex = -1; + + enum MemberType { + UndefinedMember, + ThisFunctionName, + VariableDefinition, + VariableDeclaration, + FunctionDefinition + }; + + struct Member { + MemberType type = UndefinedMember; + int index = -1; + QQmlJS::AST::VariableDeclaration::VariableScope scope = QQmlJS::AST::VariableDeclaration::FunctionScope; + mutable bool canEscape = false; + QQmlJS::AST::FunctionExpression *function = 0; + + bool isLexicallyScoped() const { return this->scope != QQmlJS::AST::VariableDeclaration::FunctionScope; } + }; + typedef QMap<QString, Member> MemberMap; + + MemberMap members; + QSet<QString> usedVariables; + QQmlJS::AST::FormalParameterList *formals = 0; + QStringList arguments; + QStringList locals; + QVector<Context *> nestedContexts; + + ControlFlow *controlFlow = 0; + QByteArray code; + QVector<CompiledData::CodeOffsetToLine> lineNumberMapping; + + int maxNumberOfArguments = 0; + bool hasDirectEval = false; + bool hasNestedFunctions = false; + bool isStrict = false; + bool usesThis = false; + bool hasTry = false; + bool hasWith = false; + mutable bool argumentsCanEscape = false; + + enum UsesArgumentsObject { + ArgumentsObjectUnknown, + ArgumentsObjectNotUsed, + ArgumentsObjectUsed + }; + + UsesArgumentsObject usesArgumentsObject = ArgumentsObjectUnknown; + + CompilationMode compilationMode; + + template <typename T> + class SmallSet: public QVarLengthArray<T, 8> + { + public: + void insert(int value) + { + for (auto it : *this) { + if (it == value) + return; + } + this->append(value); + } + }; + + // Map from meta property index (existence implies dependency) to notify signal index + struct KeyValuePair + { + quint32 _key; + quint32 _value; + + KeyValuePair(): _key(0), _value(0) {} + KeyValuePair(quint32 key, quint32 value): _key(key), _value(value) {} + + quint32 key() const { return _key; } + quint32 value() const { return _value; } + }; + + class PropertyDependencyMap: public QVarLengthArray<KeyValuePair, 8> + { + public: + void insert(quint32 key, quint32 value) + { + for (auto it = begin(), eit = end(); it != eit; ++it) { + if (it->_key == key) { + it->_value = value; + return; + } + } + append(KeyValuePair(key, value)); + } + }; + + // Qml extension: + SmallSet<int> idObjectDependencies; + PropertyDependencyMap contextObjectPropertyDependencies; + PropertyDependencyMap scopeObjectPropertyDependencies; + + Context(Context *parent, CompilationMode mode) + : parent(parent) + , compilationMode(mode) + { + if (parent && parent->isStrict) + isStrict = true; + } + + bool forceLookupByName(); + + + bool canUseSimpleCall() const { + return nestedContexts.isEmpty() && + locals.isEmpty() && + !hasTry && !hasWith && + (usesArgumentsObject == ArgumentsObjectNotUsed || isStrict) && !hasDirectEval; + } + + int findArgument(const QString &name) + { + // 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; + } + + Member findMember(const QString &name) const + { + MemberMap::const_iterator it = members.find(name); + if (it == members.end()) + return Member(); + Q_ASSERT(it->index != -1 || !parent); + return (*it); + } + + 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 addUsedVariable(const QString &name) { + usedVariables.insert(name); + } + + 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..13716fe29f --- /dev/null +++ b/src/qml/compiler/qv4compilercontrolflow_p.h @@ -0,0 +1,467 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4COMPILERCONTROLFLOW_P_H +#define QV4COMPILERCONTROLFLOW_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qv4global_p.h> +#include <private/qv4codegen_p.h> +#include <private/qqmljsast_p.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace Compiler { + +struct ControlFlow { + using Reference = Codegen::Reference; + using BytecodeGenerator = Moth::BytecodeGenerator; + using Instruction = Moth::Instruction; + + enum Type { + Loop, + With, + Finally, + Catch + }; + + enum HandlerType { + Invalid, + Break, + Continue, + Return, + Throw + }; + + struct Handler { + HandlerType type; + QString label; + BytecodeGenerator::Label linkLabel; + int tempIndex; + int value; + }; + + Codegen *cg; + ControlFlow *parent; + Type type; + bool needsLookupByName = false; + + ControlFlow(Codegen *cg, Type type) + : cg(cg), parent(cg->_context->controlFlow), type(type) + { + cg->_context->controlFlow = this; + } + + virtual ~ControlFlow() { + cg->_context->controlFlow = parent; + } + + void emitReturnStatement() const { + if (cg->_returnAddress >= 0) { + Instruction::LoadReg load; + load.reg = Moth::StackSlot::createRegister(cg->_returnAddress); + generator()->addInstruction(load); + } + Instruction::Ret ret; + cg->bytecodeGenerator->addInstruction(ret); + } + + void jumpToHandler(const Handler &h) { + if (h.linkLabel.isReturn()) { + emitReturnStatement(); + } else { + if (h.tempIndex >= 0) + Reference::storeConstOnStack(cg, QV4::Encode(h.value), h.tempIndex); + cg->bytecodeGenerator->jump().link(h.linkLabel); + } + } + + bool returnRequiresUnwind() const { + const ControlFlow *f = this; + while (f) { + if (f->type == Finally) + return true; + f = f->parent; + } + return false; + } + + 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(), BytecodeGenerator::Label::returnLabel(), -1, 0 }; + case Invalid: + break; + } + Q_ASSERT(false); + Q_UNREACHABLE(); + } + + virtual Handler getHandler(HandlerType type, const QString &label = QString()) = 0; + + BytecodeGenerator::ExceptionHandler *parentExceptionHandler() { + return parent ? parent->exceptionHandler() : 0; + } + + virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() { + return parentExceptionHandler(); + } + + + virtual void handleThrow(const Reference &expr) { + Reference e = expr; + Handler h = getHandler(ControlFlow::Throw); + if (h.tempIndex >= 0) { + e = e.storeOnStack(); + Reference::storeConstOnStack(cg, QV4::Encode(h.value), h.tempIndex); + } + e.loadInAccumulator(); + Instruction::ThrowException instr; + 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 getParentHandler(type, label); + } + +}; + +struct ControlFlowUnwind : public ControlFlow +{ + BytecodeGenerator::ExceptionHandler unwindLabel; + int controlFlowTemp; + QVector<Handler> handlers; + + ControlFlowUnwind(Codegen *cg, Type type) + : ControlFlow(cg, type), unwindLabel(generator()->newExceptionHandler()) + { + Q_ASSERT(type != Loop); + controlFlowTemp = static_cast<int>(generator()->newRegister()); + Reference::storeConstOnStack(cg, QV4::Encode::undefined(), controlFlowTemp); + // we'll need at least a handler for throw + getHandler(Throw); + } + + void emitUnwindHandler() + { + Q_ASSERT(!isSimple()); + + Reference temp = Reference::fromStackSlot(cg, controlFlowTemp); + for (const auto &h : qAsConst(handlers)) { + Handler parentHandler = getParentHandler(h.type, h.label); + + if (h.type == Throw || parentHandler.tempIndex >= 0) { + BytecodeGenerator::Label skip = generator()->newLabel(); + generator()->jumpStrictNotEqualStackSlotInt(temp.stackSlot(), h.value).link(skip); + if (h.type == Throw) + emitForThrowHandling(); + jumpToHandler(parentHandler); + skip.link(); + } else { + if (parentHandler.linkLabel.isReturn()) { + BytecodeGenerator::Label skip = generator()->newLabel(); + generator()->jumpStrictNotEqualStackSlotInt(temp.stackSlot(), h.value).link(skip); + emitReturnStatement(); + skip.link(); + } else { + generator()->jumpStrictEqualStackSlotInt(temp.stackSlot(), h.value).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; + + savedContextRegister = Moth::StackSlot::createRegister(generator()->newRegister()); + + // assumes the with object is in the accumulator + Instruction::PushWithContext pushScope; + pushScope.reg = savedContextRegister; + generator()->addInstruction(pushScope); + generator()->setExceptionHandler(&unwindLabel); + } + + virtual ~ControlFlowWith() { + // emit code for unwinding + unwindLabel.link(); + + generator()->setExceptionHandler(parentExceptionHandler()); + Instruction::PopContext pop; + pop.reg = savedContextRegister; + generator()->addInstruction(pop); + + emitUnwindHandler(); + } + Moth::StackSlot savedContextRegister; +}; + +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 = getParentHandler(type, label); + if (h.type == Invalid) + return h; + 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; + + Codegen::RegisterScope scope(cg); + + // exceptions inside the try block go here + exceptionLabel.link(); + Moth::StackSlot savedContextReg = Moth::StackSlot::createRegister(generator()->newRegister()); + Instruction::PushCatchContext pushCatch; + pushCatch.name = cg->registerString(catchExpression->name.toString()); + pushCatch.reg = savedContextReg; + generator()->addInstruction(pushCatch); + // clear the unwind temp for exceptions, we want to resume normal code flow afterwards + Reference::storeConstOnStack(cg, QV4::Encode::undefined(), controlFlowTemp); + generator()->setExceptionHandler(&catchUnwindLabel); + + cg->statement(catchExpression->statement); + + insideCatch = false; + needsLookupByName = false; + + // exceptions inside catch and break/return statements go here + catchUnwindLabel.link(); + Instruction::PopContext pop; + pop.reg = savedContextReg; + 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 getParentHandler(type, label); + return ControlFlowUnwind::getHandler(type, label); + } + + virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() { + return insideFinally ? parentExceptionHandler() : ControlFlowUnwind::exceptionHandler(); + } + + ~ControlFlowFinally() { + // emit code for unwinding + unwindLabel.link(); + + Codegen::RegisterScope scope(cg); + + Moth::StackSlot retVal = Moth::StackSlot::createRegister(generator()->newRegister()); + Instruction::StoreReg storeRetVal; + storeRetVal.reg = retVal; + generator()->addInstruction(storeRetVal); + + insideFinally = true; + exceptionTemp = generator()->newRegister(); + Instruction::GetException instr; + generator()->addInstruction(instr); + Reference::fromStackSlot(cg, exceptionTemp).storeConsumeAccumulator(); + + generator()->setExceptionHandler(parentExceptionHandler()); + cg->statement(finally->statement); + insideFinally = false; + + Instruction::LoadReg loadRetVal; + loadRetVal.reg = retVal; + generator()->addInstruction(loadRetVal); + + emitUnwindHandler(); + } + + virtual void emitForThrowHandling() { + // reset the exception flag, that got cleared before executing the statements in finally + Reference::fromStackSlot(cg, exceptionTemp).loadInAccumulator(); + Instruction::SetException setException; + Q_ASSERT(exceptionTemp != -1); + generator()->addInstruction(setException); + } +}; + +} } // QV4::Compiler namespace + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp new file mode 100644 index 0000000000..6a9b064bd8 --- /dev/null +++ b/src/qml/compiler/qv4compilerscanfunctions.cpp @@ -0,0 +1,475 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include "qv4compilerscanfunctions_p.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QStringList> +#include <QtCore/QSet> +#include <QtCore/QBuffer> +#include <QtCore/QBitArray> +#include <QtCore/QLinkedList> +#include <QtCore/QStack> +#include <private/qqmljsast_p.h> +#include <private/qv4compilercontext_p.h> +#include <private/qv4codegen_p.h> +#include <private/qv4string_p.h> + +QT_USE_NAMESPACE +using namespace QV4; +using namespace QV4::Compiler; +using namespace QQmlJS::AST; + +ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode) + : _cg(cg) + , _sourceCode(sourceCode) + , _context(0) + , _allowFuncDecls(true) + , defaultProgramMode(defaultProgramMode) +{ +} + +void ScanFunctions::operator()(Node *node) +{ + if (node) + node->accept(this); + + calcEscapingVariables(); +} + +void ScanFunctions::enterEnvironment(Node *node, CompilationMode compilationMode) +{ + Context *e = _cg->_module->newContext(node, _context, compilationMode); + if (!e->isStrict) + e->isStrict = _cg->_strictMode; + _contextStack.append(e); + _context = e; +} + +void ScanFunctions::leaveEnvironment() +{ + _contextStack.pop(); + _context = _contextStack.isEmpty() ? 0 : _contextStack.top(); +} + +void ScanFunctions::checkDirectivePrologue(SourceElements *ast) +{ + for (SourceElements *it = ast; it; it = it->next) { + if (StatementSourceElement *stmt = cast<StatementSourceElement *>(it->element)) { + if (ExpressionStatement *expr = cast<ExpressionStatement *>(stmt->statement)) { + if (StringLiteral *strLit = cast<StringLiteral *>(expr->expression)) { + // Use the source code, because the StringLiteral's + // value might have escape sequences in it, which is not + // allowed. + if (strLit->literalToken.length < 2) + continue; + QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2); + if (str == QLatin1String("use strict")) { + _context->isStrict = true; + } else { + // TODO: give a warning. + } + continue; + } + } + } + + break; + } +} + +void ScanFunctions::checkName(const QStringRef &name, const SourceLocation &loc) +{ + if (_context->isStrict) { + if (name == QLatin1String("implements") + || name == QLatin1String("interface") + || name == QLatin1String("let") + || name == QLatin1String("package") + || name == QLatin1String("private") + || name == QLatin1String("protected") + || name == QLatin1String("public") + || name == QLatin1String("static") + || name == QLatin1String("yield")) { + _cg->throwSyntaxError(loc, QStringLiteral("Unexpected strict mode reserved word")); + } + } +} +void ScanFunctions::checkForArguments(AST::FormalParameterList *parameters) +{ + while (parameters) { + if (parameters->name == QLatin1String("arguments")) + _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; + parameters = parameters->next; + } +} + +bool ScanFunctions::visit(Program *ast) +{ + enterEnvironment(ast, defaultProgramMode); + checkDirectivePrologue(ast->elements); + return true; +} + +void ScanFunctions::endVisit(Program *) +{ + leaveEnvironment(); +} + +bool ScanFunctions::visit(CallExpression *ast) +{ + if (! _context->hasDirectEval) { + if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) { + if (id->name == QLatin1String("eval")) { + if (_context->usesArgumentsObject == Context::ArgumentsObjectUnknown) + _context->usesArgumentsObject = Context::ArgumentsObjectUsed; + _context->hasDirectEval = true; + } + } + } + int argc = 0; + for (ArgumentList *it = ast->arguments; it; it = it->next) + ++argc; + _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc); + return true; +} + +bool ScanFunctions::visit(NewMemberExpression *ast) +{ + int argc = 0; + for (ArgumentList *it = ast->arguments; it; it = it->next) + ++argc; + _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc); + return true; +} + +bool ScanFunctions::visit(ArrayLiteral *ast) +{ + int index = 0; + for (ElementList *it = ast->elements; it; it = it->next) { + for (Elision *elision = it->elision; elision; elision = elision->next) + ++index; + ++index; + } + if (ast->elision) { + for (Elision *elision = ast->elision->next; elision; elision = elision->next) + ++index; + } + _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, index); + return true; +} + +bool ScanFunctions::visit(VariableDeclaration *ast) +{ + if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode")); + checkName(ast->name, ast->identifierToken); + if (ast->name == QLatin1String("arguments")) + _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; + if (ast->scope == AST::VariableDeclaration::VariableScope::ReadOnlyBlockScope && !ast->expression) { + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration")); + return false; + } + QString name = ast->name.toString(); + const Context::Member *m = 0; + if (_context->memberInfo(name, &m)) { + if (m->isLexicallyScoped() || ast->isLexicallyScoped()) { + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name)); + return false; + } + } + _context->addLocalVar(ast->name.toString(), ast->expression ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope); + return true; +} + +bool ScanFunctions::visit(IdentifierExpression *ast) +{ + checkName(ast->name, ast->identifierToken); + if (_context->usesArgumentsObject == Context::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments")) + _context->usesArgumentsObject = Context::ArgumentsObjectUsed; + _context->addUsedVariable(ast->name.toString()); + return true; +} + +bool ScanFunctions::visit(ExpressionStatement *ast) +{ + if (FunctionExpression* expr = AST::cast<AST::FunctionExpression*>(ast->expression)) { + if (!_allowFuncDecls) + _cg->throwSyntaxError(expr->functionToken, QStringLiteral("conditional function or closure declaration")); + + enterFunction(expr, /*enterName*/ true); + Node::accept(expr->formals, this); + Node::accept(expr->body, this); + leaveEnvironment(); + return false; + } else { + SourceLocation firstToken = ast->firstSourceLocation(); + if (_sourceCode.midRef(firstToken.offset, firstToken.length) == QLatin1String("function")) { + _cg->throwSyntaxError(firstToken, QStringLiteral("unexpected token")); + } + } + return true; +} + +bool ScanFunctions::visit(FunctionExpression *ast) +{ + enterFunction(ast, /*enterName*/ false); + return true; +} + +void ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName) +{ + if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode")); + enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : 0); +} + +void ScanFunctions::endVisit(FunctionExpression *) +{ + leaveEnvironment(); +} + +bool ScanFunctions::visit(ObjectLiteral *ast) +{ + int argc = 0; + for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { + QString key = it->assignment->name->asString(); + if (QV4::String::toArrayIndex(key) != UINT_MAX) + ++argc; + ++argc; + if (AST::cast<AST::PropertyGetterSetter *>(it->assignment)) + ++argc; + } + _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); + Node::accept(ast->properties, this); + return false; +} + +bool ScanFunctions::visit(PropertyGetterSetter *ast) +{ + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); + enterFunction(ast, QString(), ast->formals, ast->functionBody, /*FunctionExpression*/0); + return true; +} + +void ScanFunctions::endVisit(PropertyGetterSetter *) +{ + leaveEnvironment(); +} + +bool ScanFunctions::visit(FunctionDeclaration *ast) +{ + enterFunction(ast, /*enterName*/ true); + return true; +} + +void ScanFunctions::endVisit(FunctionDeclaration *) +{ + leaveEnvironment(); +} + +bool ScanFunctions::visit(TryStatement *) +{ + // ### should limit to catch(), as try{} finally{} should be ok without + _context->hasTry = true; + return true; +} + +bool ScanFunctions::visit(WithStatement *ast) +{ + if (_context->isStrict) { + _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode")); + return false; + } + + _context->hasWith = true; + return true; +} + +bool ScanFunctions::visit(DoWhileStatement *ast) { + { + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); + Node::accept(ast->statement, this); + } + Node::accept(ast->expression, this); + return false; +} + +bool ScanFunctions::visit(ForStatement *ast) { + Node::accept(ast->initialiser, this); + Node::accept(ast->condition, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); + Node::accept(ast->statement, this); + + return false; +} + +bool ScanFunctions::visit(LocalForStatement *ast) { + Node::accept(ast->declarations, this); + Node::accept(ast->condition, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); + Node::accept(ast->statement, this); + + return false; +} + +bool ScanFunctions::visit(ForEachStatement *ast) { + Node::accept(ast->initialiser, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); + Node::accept(ast->statement, this); + + return false; +} + +bool ScanFunctions::visit(LocalForEachStatement *ast) { + Node::accept(ast->declaration, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); + Node::accept(ast->statement, this); + + return false; +} + +bool ScanFunctions::visit(ThisExpression *) +{ + _context->usesThis = true; + return false; +} + +bool ScanFunctions::visit(Block *ast) { + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls); + Node::accept(ast->statements, this); + return false; +} + +void ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr) +{ + if (_context) { + _context->hasNestedFunctions = true; + // The identifier of a function expression cannot be referenced from the enclosing environment. + if (expr) + _context->addLocalVar(name, Context::FunctionDefinition, AST::VariableDeclaration::FunctionScope, expr); + if (name == QLatin1String("arguments")) + _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; + } + + enterEnvironment(ast, FunctionCode); + checkForArguments(formals); + + if (!name.isEmpty()) + _context->addLocalVar(name, Context::ThisFunctionName, QQmlJS::AST::VariableDeclaration::FunctionScope); + _context->formals = formals; + + if (body && !_context->isStrict) + checkDirectivePrologue(body->elements); + + for (FormalParameterList *it = formals; it; it = it->next) { + QString arg = it->name.toString(); + int duplicateIndex = _context->arguments.indexOf(arg); + if (duplicateIndex != -1) { + if (_context->isStrict) { + _cg->throwSyntaxError(it->identifierToken, QStringLiteral("Duplicate parameter name '%1' is not allowed in strict mode").arg(arg)); + return; + } else { + // change the name of the earlier argument to enforce the specified lookup semantics + QString modified = arg; + while (_context->arguments.contains(modified)) + modified += QString(0xfffe); + _context->arguments[duplicateIndex] = modified; + } + } + if (_context->isStrict) { + if (arg == QLatin1String("eval") || arg == QLatin1String("arguments")) { + _cg->throwSyntaxError(it->identifierToken, QStringLiteral("'%1' cannot be used as parameter name in strict mode").arg(arg)); + return; + } + } + _context->arguments += arg; + } +} + +void ScanFunctions::calcEscapingVariables() +{ + Module *m = _cg->_module; + + for (Context *inner : m->contextMap) { + for (const QString &var : qAsConst(inner->usedVariables)) { + Context *c = inner; + while (c) { + Context::MemberMap::const_iterator it = c->members.find(var); + if (it != c->members.end()) { + if (c != inner) + it->canEscape = true; + break; + } + if (c->findArgument(var) != -1) { + if (c != inner) + c->argumentsCanEscape = true; + break; + } + c = c->parent; + } + } + Context *c = inner->parent; + while (c) { + c->hasDirectEval |= inner->hasDirectEval; + c = c->parent; + } + } + + static const bool showEscapingVars = qEnvironmentVariableIsSet("QV4_SHOW_ESCAPING_VARS"); + if (showEscapingVars) { + qDebug() << "==== escaping variables ===="; + for (Context *c : m->contextMap) { + qDebug() << "Context" << c->name << ":"; + qDebug() << " Arguments escape" << c->argumentsCanEscape; + for (auto it = c->members.constBegin(); it != c->members.constEnd(); ++it) { + qDebug() << " " << it.key() << it.value().canEscape; + } + } + } +} diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h new file mode 100644 index 0000000000..0b898e587d --- /dev/null +++ b/src/qml/compiler/qv4compilerscanfunctions_p.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** 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 QV4COMPILERSCANFUNCTIONS_P_H +#define QV4COMPILERSCANFUNCTIONS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qv4global_p.h" +#include <private/qqmljsastvisitor_p.h> +#include <private/qqmljsast_p.h> +#include <private/qqmljsengine_p.h> +#include <private/qv4compilercontext_p.h> +#include <private/qv4util_p.h> +#include <QtCore/QStringList> +#include <QStack> + +QT_BEGIN_NAMESPACE + +using namespace QQmlJS; + +namespace QV4 { + +namespace Moth { +struct Instruction; +} + +namespace CompiledData { +struct CompilationUnit; +} + +namespace Compiler { + +class Codegen; + +class ScanFunctions: protected QQmlJS::AST::Visitor +{ + typedef QV4::TemporaryAssignment<bool> TemporaryBoolAssignment; +public: + ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode); + void operator()(AST::Node *node); + + void enterEnvironment(AST::Node *node, CompilationMode compilationMode); + void leaveEnvironment(); + + void enterQmlScope(AST::Node *ast, const QString &name) + { enterFunction(ast, name, /*formals*/0, /*body*/0, /*expr*/0); } + + void enterQmlFunction(AST::FunctionDeclaration *ast) + { enterFunction(ast, false); } + +protected: + using Visitor::visit; + using Visitor::endVisit; + + void checkDirectivePrologue(AST::SourceElements *ast); + + void checkName(const QStringRef &name, const AST::SourceLocation &loc); + void checkForArguments(AST::FormalParameterList *parameters); + + bool visit(AST::Program *ast) override; + void endVisit(AST::Program *) override; + + bool visit(AST::CallExpression *ast) override; + bool visit(AST::NewMemberExpression *ast) override; + bool visit(AST::ArrayLiteral *ast) override; + bool visit(AST::VariableDeclaration *ast) override; + bool visit(AST::IdentifierExpression *ast) override; + bool visit(AST::ExpressionStatement *ast) override; + bool visit(AST::FunctionExpression *ast) override; + + void enterFunction(AST::FunctionExpression *ast, bool enterName); + + void endVisit(AST::FunctionExpression *) override; + + bool visit(AST::ObjectLiteral *ast) override; + + bool visit(AST::PropertyGetterSetter *ast) override; + void endVisit(AST::PropertyGetterSetter *) override; + + bool visit(AST::FunctionDeclaration *ast) override; + void endVisit(AST::FunctionDeclaration *) override; + + bool visit(AST::TryStatement *ast) override; + bool visit(AST::WithStatement *ast) override; + + bool visit(AST::DoWhileStatement *ast) override; + bool visit(AST::ForStatement *ast) override; + bool visit(AST::LocalForStatement *ast) override; + bool visit(AST::ForEachStatement *ast) override; + bool visit(AST::LocalForEachStatement *ast) override; + bool visit(AST::ThisExpression *ast) override; + + bool visit(AST::Block *ast) override; + +protected: + void enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::FunctionBody *body, AST::FunctionExpression *expr); + + void calcEscapingVariables(); +// fields: + Codegen *_cg; + const QString _sourceCode; + Context *_context; + QStack<Context *> _contextStack; + + bool _allowFuncDecls; + CompilationMode defaultProgramMode; +}; + +} + +} + +QT_END_NAMESPACE + +#endif // QV4CODEGEN_P_H diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp index cf8cf623bc..9cfde99e6b 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -38,17 +38,603 @@ ****************************************************************************/ #include "qv4instr_moth_p.h" +#include <private/qv4compileddata_p.h> using namespace QV4; using namespace QV4::Moth; -int Instr::size(Type type) +int InstrInfo::size(Instr::Type type) { -#define MOTH_RETURN_INSTR_SIZE(I, FMT) case I: return InstrMeta<(int)I>::Size; +#define MOTH_RETURN_INSTR_SIZE(I) case Instr::Type::I: return InstrMeta<int(Instr::Type::I)>::Size; switch (type) { FOR_EACH_MOTH_INSTR(MOTH_RETURN_INSTR_SIZE) - default: return 0; } #undef MOTH_RETURN_INSTR_SIZE + Q_UNREACHABLE(); } +static QByteArray alignedNumber(int n) { + QByteArray number = QByteArray::number(n); + while (number.size() < 8) + number.prepend(' '); + return number; +} + +static QByteArray alignedLineNumber(int line) { + if (line > 0) + return alignedNumber(static_cast<int>(line)); + return QByteArray(" "); +} + +static QByteArray rawBytes(const char *data, int n) +{ + QByteArray ba; + while (n) { + uint num = *reinterpret_cast<const uchar *>(data); + if (num < 16) + ba += '0'; + ba += QByteArray::number(num, 16) + " "; + ++data; + --n; + } + while (ba.size() < 25) + ba += ' '; + return ba; +} + +static QString toString(QV4::ReturnedValue v) +{ +#ifdef V4_BOOTSTRAP + return QStringLiteral("string-const(%1)").arg(v); +#else // !V4_BOOTSTRAP + Value val = Value::fromReturnedValue(v); + QString result; + if (val.isInt32()) + result = QLatin1String("int "); + else if (val.isDouble()) + result = QLatin1String("double "); + if (val.isEmpty()) + result += QLatin1String("empty"); + else + result += val.toQStringNoThrow(); + return result; +#endif // V4_BOOTSTRAP +} + +#define ABSOLUTE_OFFSET() \ + (code - start + offset) + +#define MOTH_BEGIN_INSTR(instr) \ + { \ + INSTR_##instr(MOTH_DECODE_WITH_BASE) \ + QDebug d = qDebug(); \ + d.noquote(); \ + d.nospace(); \ + d << alignedLineNumber(line) << alignedNumber(codeOffset).constData() << ": " \ + << rawBytes(base_ptr, int(code - base_ptr)) << #instr << " "; + +#define MOTH_END_INSTR(instr) \ + continue; \ + } + +QT_BEGIN_NAMESPACE +namespace QV4 { +namespace Moth { + +const int InstrInfo::argumentCount[] = { + FOR_EACH_MOTH_INSTR(MOTH_COLLECT_NARGS) +}; + + +void dumpConstantTable(const Value *constants, uint count) +{ + QDebug d = qDebug(); + d.nospace(); + for (uint i = 0; i < count; ++i) + d << alignedNumber(int(i)).constData() << ": " + << toString(constants[i].asReturnedValue()).toUtf8().constData() << "\n"; +} + +QString dumpRegister(int reg, int nFormals) +{ + Q_STATIC_ASSERT(offsetof(CallData, function) == 0); + Q_STATIC_ASSERT(offsetof(CallData, context) == sizeof(Value)); + Q_STATIC_ASSERT(offsetof(CallData, accumulator) == 2*sizeof(Value)); + Q_STATIC_ASSERT(offsetof(CallData, thisObject) == 3*sizeof(Value)); + if (reg == CallData::Function) + return QStringLiteral("(function)"); + else if (reg == CallData::Context) + return QStringLiteral("(context)"); + else if (reg == CallData::Accumulator) + return QStringLiteral("(accumulator)"); + else if (reg == CallData::This) + return QStringLiteral("(this)"); + else if (reg == CallData::Argc) + return QStringLiteral("(argc)"); + reg -= 4; + if (reg <= nFormals) + return QStringLiteral("a%1").arg(reg); + reg -= nFormals; + return QStringLiteral("r%1").arg(reg); + +} + +QString dumpArguments(int argc, int argv, int nFormals) +{ + if (!argc) + return QStringLiteral("()"); + return QStringLiteral("(") + dumpRegister(argv, nFormals) + QStringLiteral(", ") + QString::number(argc) + QStringLiteral(")"); +} + + +void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*startLine*/, const QVector<CompiledData::CodeOffsetToLine> &lineNumberMapping) +{ + MOTH_JUMP_TABLE; + + auto findLine = [](const CompiledData::CodeOffsetToLine &entry, uint offset) { + return entry.codeOffset < offset; + }; + + int lastLine = -1; + const char *start = code; + const char *end = code + len; + while (code < end) { + const CompiledData::CodeOffsetToLine *codeToLine = std::lower_bound(lineNumberMapping.constBegin(), lineNumberMapping.constEnd(), static_cast<uint>(code - start) + 1, findLine) - 1; + int line = int(codeToLine->line); + if (line != lastLine) + lastLine = line; + else + line = -1; + + int codeOffset = int(code - start); + + MOTH_DISPATCH() + + MOTH_BEGIN_INSTR(LoadReg) + d << dumpRegister(reg, nFormals); + MOTH_END_INSTR(LoadReg) + + MOTH_BEGIN_INSTR(StoreReg) + d << dumpRegister(reg, nFormals); + MOTH_END_INSTR(StoreReg) + + MOTH_BEGIN_INSTR(MoveReg) + d << dumpRegister(destReg, nFormals) << ", " << dumpRegister(srcReg, nFormals); + MOTH_END_INSTR(MoveReg) + + MOTH_BEGIN_INSTR(LoadConst) + d << "C" << index; + MOTH_END_INSTR(LoadConst) + + MOTH_BEGIN_INSTR(LoadNull) + MOTH_END_INSTR(LoadNull) + + MOTH_BEGIN_INSTR(LoadZero) + MOTH_END_INSTR(LoadZero) + + MOTH_BEGIN_INSTR(LoadTrue) + MOTH_END_INSTR(LoadTrue) + + MOTH_BEGIN_INSTR(LoadFalse) + MOTH_END_INSTR(LoadFalse) + + MOTH_BEGIN_INSTR(LoadUndefined) + MOTH_END_INSTR(LoadUndefined) + + MOTH_BEGIN_INSTR(LoadInt) + d << value; + MOTH_END_INSTR(LoadInt) + + MOTH_BEGIN_INSTR(MoveConst) + d << dumpRegister(destTemp, nFormals) << ", C" << constIndex; + MOTH_END_INSTR(MoveConst) + + MOTH_BEGIN_INSTR(LoadLocal) + if (index < nLocals) + d << "l" << index; + else + d << "a" << (index - nLocals); + MOTH_END_INSTR(LoadLocal) + + MOTH_BEGIN_INSTR(StoreLocal) + if (index < nLocals) + d << "l" << index; + else + d << "a" << (index - nLocals); + MOTH_END_INSTR(StoreLocal) + + MOTH_BEGIN_INSTR(LoadScopedLocal) + if (index < nLocals) + d << "l" << index << "@" << scope; + else + d << "a" << (index - nLocals) << "@" << scope; + MOTH_END_INSTR(LoadScopedLocal) + + MOTH_BEGIN_INSTR(StoreScopedLocal) + if (index < nLocals) + d << ", " << "l" << index << "@" << scope; + else + d << ", " << "a" << (index - nLocals) << "@" << scope; + MOTH_END_INSTR(StoreScopedLocal) + + MOTH_BEGIN_INSTR(LoadRuntimeString) + d << stringId; + MOTH_END_INSTR(LoadRuntimeString) + + MOTH_BEGIN_INSTR(LoadRegExp) + d << regExpId; + MOTH_END_INSTR(LoadRegExp) + + MOTH_BEGIN_INSTR(LoadClosure) + d << value; + MOTH_END_INSTR(LoadClosure) + + MOTH_BEGIN_INSTR(LoadName) + d << name; + MOTH_END_INSTR(LoadName) + + MOTH_BEGIN_INSTR(LoadGlobalLookup) + d << index; + MOTH_END_INSTR(LoadGlobalLookup) + + MOTH_BEGIN_INSTR(StoreNameSloppy) + d << name; + MOTH_END_INSTR(StoreNameSloppy) + + MOTH_BEGIN_INSTR(StoreNameStrict) + d << name; + MOTH_END_INSTR(StoreNameStrict) + + MOTH_BEGIN_INSTR(LoadElement) + d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]"; + MOTH_END_INSTR(LoadElement) + + MOTH_BEGIN_INSTR(LoadElementA) + d << dumpRegister(base, nFormals) << "[acc]"; + MOTH_END_INSTR(LoadElement) + + MOTH_BEGIN_INSTR(StoreElement) + d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]"; + MOTH_END_INSTR(StoreElement) + + MOTH_BEGIN_INSTR(LoadProperty) + d << dumpRegister(base, nFormals) << "[" << name << "]"; + MOTH_END_INSTR(LoadProperty) + + MOTH_BEGIN_INSTR(LoadPropertyA) + d << "acc[" << name << "]"; + MOTH_END_INSTR(LoadElementA) + + MOTH_BEGIN_INSTR(GetLookup) + d << dumpRegister(base, nFormals) << "(" << index << ")"; + MOTH_END_INSTR(GetLookup) + + MOTH_BEGIN_INSTR(GetLookupA) + d << "acc(" << index << ")"; + MOTH_END_INSTR(GetLookupA) + + MOTH_BEGIN_INSTR(StoreProperty) + d << dumpRegister(base, nFormals) << "[" << name<< "]"; + MOTH_END_INSTR(StoreProperty) + + MOTH_BEGIN_INSTR(SetLookup) + d << dumpRegister(base, nFormals) << "(" << index << ")"; + MOTH_END_INSTR(SetLookup) + + MOTH_BEGIN_INSTR(StoreScopeObjectProperty) + d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]"; + MOTH_END_INSTR(StoreScopeObjectProperty) + + MOTH_BEGIN_INSTR(LoadScopeObjectProperty) + d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]" << (captureRequired ? " (capture)" : " (no capture)"); + MOTH_END_INSTR(LoadScopeObjectProperty) + + MOTH_BEGIN_INSTR(StoreContextObjectProperty) + d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]"; + MOTH_END_INSTR(StoreContextObjectProperty) + + MOTH_BEGIN_INSTR(LoadContextObjectProperty) + d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]" << (captureRequired ? " (capture)" : " (no capture)"); + MOTH_END_INSTR(LoadContextObjectProperty) + + MOTH_BEGIN_INSTR(LoadIdObject) + d << dumpRegister(base, nFormals) << "[" << index << "]"; + MOTH_END_INSTR(LoadIdObject) + + MOTH_BEGIN_INSTR(CallValue) + d << dumpArguments(argc, argv, nFormals); + MOTH_END_INSTR(CallValue) + + MOTH_BEGIN_INSTR(CallProperty) + d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals); + MOTH_END_INSTR(CallProperty) + + MOTH_BEGIN_INSTR(CallPropertyLookup) + d << dumpRegister(base, nFormals) << "." << lookupIndex << dumpArguments(argc, argv, nFormals); + MOTH_END_INSTR(CallPropertyLookup) + + MOTH_BEGIN_INSTR(CallElement) + d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]" << dumpArguments(argc, argv, nFormals); + MOTH_END_INSTR(CallElement) + + MOTH_BEGIN_INSTR(CallName) + d << name << dumpArguments(argc, argv, nFormals); + MOTH_END_INSTR(CallName) + + MOTH_BEGIN_INSTR(CallPossiblyDirectEval) + d << dumpArguments(argc, argv, nFormals); + MOTH_END_INSTR(CallPossiblyDirectEval) + + MOTH_BEGIN_INSTR(CallGlobalLookup) + d << index << dumpArguments(argc, argv, nFormals); + MOTH_END_INSTR(CallGlobalLookup) + + MOTH_BEGIN_INSTR(SetExceptionHandler) + if (offset) + d << ABSOLUTE_OFFSET(); + else + d << "<null>"; + MOTH_END_INSTR(SetExceptionHandler) + + MOTH_BEGIN_INSTR(ThrowException) + MOTH_END_INSTR(ThrowException) + + MOTH_BEGIN_INSTR(GetException) + MOTH_END_INSTR(HasException) + + MOTH_BEGIN_INSTR(SetException) + MOTH_END_INSTR(SetExceptionFlag) + + MOTH_BEGIN_INSTR(CreateCallContext) + MOTH_END_INSTR(CreateCallContext) + + MOTH_BEGIN_INSTR(PushCatchContext) + d << dumpRegister(reg, nFormals) << ", " << name; + MOTH_END_INSTR(PushCatchContext) + + MOTH_BEGIN_INSTR(PushWithContext) + d << dumpRegister(reg, nFormals); + MOTH_END_INSTR(PushWithContext) + + MOTH_BEGIN_INSTR(PopContext) + d << dumpRegister(reg, nFormals); + MOTH_END_INSTR(PopContext) + + MOTH_BEGIN_INSTR(ForeachIteratorObject) + MOTH_END_INSTR(ForeachIteratorObject) + + MOTH_BEGIN_INSTR(ForeachNextPropertyName) + MOTH_END_INSTR(ForeachNextPropertyName) + + MOTH_BEGIN_INSTR(DeleteMember) + d << dumpRegister(base, nFormals) << "[" << member << "]"; + MOTH_END_INSTR(DeleteMember) + + MOTH_BEGIN_INSTR(DeleteSubscript) + d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]"; + MOTH_END_INSTR(DeleteSubscript) + + MOTH_BEGIN_INSTR(DeleteName) + d << name; + MOTH_END_INSTR(DeleteName) + + MOTH_BEGIN_INSTR(TypeofName) + d << name; + MOTH_END_INSTR(TypeofName) + + MOTH_BEGIN_INSTR(TypeofValue) + MOTH_END_INSTR(TypeofValue) + + MOTH_BEGIN_INSTR(DeclareVar) + d << isDeletable << ", " << varName; + MOTH_END_INSTR(DeclareVar) + + MOTH_BEGIN_INSTR(DefineArray) + d << dumpRegister(args, nFormals) << ", " << argc; + MOTH_END_INSTR(DefineArray) + + MOTH_BEGIN_INSTR(DefineObjectLiteral) + d << dumpRegister(args, nFormals) + << ", " << internalClassId + << ", " << arrayValueCount + << ", " << arrayGetterSetterCountAndFlags; + MOTH_END_INSTR(DefineObjectLiteral) + + MOTH_BEGIN_INSTR(CreateMappedArgumentsObject) + MOTH_END_INSTR(CreateMappedArgumentsObject) + + MOTH_BEGIN_INSTR(CreateUnmappedArgumentsObject) + MOTH_END_INSTR(CreateUnmappedArgumentsObject) + + MOTH_BEGIN_INSTR(ConvertThisToObject) + MOTH_END_INSTR(ConvertThisToObject) + + MOTH_BEGIN_INSTR(Construct) + d << "new" << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals); + MOTH_END_INSTR(Construct) + + MOTH_BEGIN_INSTR(Jump) + d << ABSOLUTE_OFFSET(); + MOTH_END_INSTR(Jump) + + MOTH_BEGIN_INSTR(JumpTrue) + d << ABSOLUTE_OFFSET(); + MOTH_END_INSTR(JumpTrue) + + MOTH_BEGIN_INSTR(JumpFalse) + d << ABSOLUTE_OFFSET(); + MOTH_END_INSTR(JumpFalse) + + MOTH_BEGIN_INSTR(CmpEqNull) + MOTH_END_INSTR(CmpEqNull) + + MOTH_BEGIN_INSTR(CmpNeNull) + MOTH_END_INSTR(CmpNeNull) + + MOTH_BEGIN_INSTR(CmpEqInt) + d << lhs; + MOTH_END_INSTR(CmpEq) + + MOTH_BEGIN_INSTR(CmpNeInt) + d << lhs; + MOTH_END_INSTR(CmpNeInt) + + MOTH_BEGIN_INSTR(CmpEq) + d << dumpRegister(lhs, nFormals); + MOTH_END_INSTR(CmpEq) + + MOTH_BEGIN_INSTR(CmpNe) + d << dumpRegister(lhs, nFormals); + MOTH_END_INSTR(CmpNe) + + MOTH_BEGIN_INSTR(CmpGt) + d << dumpRegister(lhs, nFormals); + MOTH_END_INSTR(CmpGt) + + MOTH_BEGIN_INSTR(CmpGe) + d << dumpRegister(lhs, nFormals); + MOTH_END_INSTR(CmpGe) + + MOTH_BEGIN_INSTR(CmpLt) + d << dumpRegister(lhs, nFormals); + MOTH_END_INSTR(CmpLt) + + MOTH_BEGIN_INSTR(CmpLe) + d << dumpRegister(lhs, nFormals); + MOTH_END_INSTR(CmpLe) + + MOTH_BEGIN_INSTR(CmpStrictEqual) + d << dumpRegister(lhs, nFormals); + MOTH_END_INSTR(CmpStrictEqual) + + MOTH_BEGIN_INSTR(CmpStrictNotEqual) + d << dumpRegister(lhs, nFormals); + MOTH_END_INSTR(CmpStrictNotEqual) + + MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt) + d << dumpRegister(lhs, nFormals) << ", " << rhs << " " << ABSOLUTE_OFFSET(); + MOTH_END_INSTR(JumpStrictEqualStackSlotInt) + + MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt) + d << dumpRegister(lhs, nFormals) << ", " << rhs << " " << ABSOLUTE_OFFSET(); + MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt) + + MOTH_BEGIN_INSTR(UNot) + MOTH_END_INSTR(UNot) + + MOTH_BEGIN_INSTR(UPlus) + MOTH_END_INSTR(UPlus) + + MOTH_BEGIN_INSTR(UMinus) + MOTH_END_INSTR(UMinus) + + MOTH_BEGIN_INSTR(UCompl) + MOTH_END_INSTR(UCompl) + + MOTH_BEGIN_INSTR(Increment) + MOTH_END_INSTR(PreIncrement) + + MOTH_BEGIN_INSTR(Decrement) + MOTH_END_INSTR(PreDecrement) + + MOTH_BEGIN_INSTR(Add) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(Add) + + MOTH_BEGIN_INSTR(BitAnd) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(BitAnd) + + MOTH_BEGIN_INSTR(BitOr) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(BitOr) + + MOTH_BEGIN_INSTR(BitXor) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(BitXor) + + MOTH_BEGIN_INSTR(UShr) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(UShr) + + MOTH_BEGIN_INSTR(Shr) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(Shr) + + MOTH_BEGIN_INSTR(Shl) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(Shl) + + MOTH_BEGIN_INSTR(BitAndConst) + d << "acc, " << rhs; + MOTH_END_INSTR(BitAndConst) + + MOTH_BEGIN_INSTR(BitOrConst) + d << "acc, " << rhs; + MOTH_END_INSTR(BitOr) + + MOTH_BEGIN_INSTR(BitXorConst) + d << "acc, " << rhs; + MOTH_END_INSTR(BitXor) + + MOTH_BEGIN_INSTR(UShrConst) + d << "acc, " << rhs; + MOTH_END_INSTR(UShrConst) + + MOTH_BEGIN_INSTR(ShrConst) + d << "acc, " << rhs; + MOTH_END_INSTR(ShrConst) + + MOTH_BEGIN_INSTR(ShlConst) + d << "acc, " << rhs; + MOTH_END_INSTR(ShlConst) + + MOTH_BEGIN_INSTR(Mul) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(Mul) + + MOTH_BEGIN_INSTR(Div) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(Div) + + MOTH_BEGIN_INSTR(Mod) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(Mod) + + MOTH_BEGIN_INSTR(Sub) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(Sub) + + MOTH_BEGIN_INSTR(CmpIn) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(CmpIn) + + MOTH_BEGIN_INSTR(CmpInstanceOf) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(CmpInstanceOf) + + MOTH_BEGIN_INSTR(Ret) + MOTH_END_INSTR(Ret) + +#ifndef QT_NO_QML_DEBUGGER + MOTH_BEGIN_INSTR(Debug) + MOTH_END_INSTR(Debug) +#endif // QT_NO_QML_DEBUGGER + + MOTH_BEGIN_INSTR(LoadQmlContext) + d << dumpRegister(result, nFormals); + MOTH_END_INSTR(LoadQmlContext) + + MOTH_BEGIN_INSTR(LoadQmlImportedScripts) + d << dumpRegister(result, nFormals); + MOTH_END_INSTR(LoadQmlImportedScripts) + + MOTH_BEGIN_INSTR(LoadQmlSingleton) + d << name; + MOTH_END_INSTR(LoadQmlSingleton) + } +} + +} +} +QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 1bdb8df414..e0b013eb95 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -64,851 +64,451 @@ QT_BEGIN_NAMESPACE #define MOTH_DEBUG_INSTR(F) #else #define MOTH_DEBUG_INSTR(F) \ - F(Line, line) \ - F(Debug, debug) + F(Debug) #endif +#define INSTRUCTION(op, name, nargs, ...) \ + op##_INSTRUCTION(name, nargs, __VA_ARGS__) + +/* for all jump instructions, the offset has to come last, to simplify the job of the bytecode generator */ +#define INSTR_Ret(op) INSTRUCTION(op, Ret, 0) +#define INSTR_Debug(op) INSTRUCTION(op, Debug, 0) +#define INSTR_LoadConst(op) INSTRUCTION(op, LoadConst, 1, index) +#define INSTR_LoadZero(op) INSTRUCTION(op, LoadZero, 0) +#define INSTR_LoadTrue(op) INSTRUCTION(op, LoadTrue, 0) +#define INSTR_LoadFalse(op) INSTRUCTION(op, LoadFalse, 0) +#define INSTR_LoadNull(op) INSTRUCTION(op, LoadNull, 0) +#define INSTR_LoadUndefined(op) INSTRUCTION(op, LoadUndefined, 0) +#define INSTR_LoadInt(op) INSTRUCTION(op, LoadInt, 1, value) +#define INSTR_MoveConst(op) INSTRUCTION(op, MoveConst, 2, constIndex, destTemp) +#define INSTR_LoadReg(op) INSTRUCTION(op, LoadReg, 1, reg) +#define INSTR_StoreReg(op) INSTRUCTION(op, StoreReg, 1, reg) +#define INSTR_MoveReg(op) INSTRUCTION(op, MoveReg, 2, srcReg, destReg) +#define INSTR_LoadLocal(op) INSTRUCTION(op, LoadLocal, 1, index) +#define INSTR_StoreLocal(op) INSTRUCTION(op, StoreLocal, 1, index) +#define INSTR_LoadScopedLocal(op) INSTRUCTION(op, LoadScopedLocal, 2, scope, index) +#define INSTR_StoreScopedLocal(op) INSTRUCTION(op, StoreScopedLocal, 2, scope, index) +#define INSTR_LoadRuntimeString(op) INSTRUCTION(op, LoadRuntimeString, 1, stringId) +#define INSTR_LoadRegExp(op) INSTRUCTION(op, LoadRegExp, 1, regExpId) +#define INSTR_LoadClosure(op) INSTRUCTION(op, LoadClosure, 1, value) +#define INSTR_LoadName(op) INSTRUCTION(op, LoadName, 1, name) +#define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 1, index) +#define INSTR_StoreNameSloppy(op) INSTRUCTION(op, StoreNameSloppy, 1, name) +#define INSTR_StoreNameStrict(op) INSTRUCTION(op, StoreNameStrict, 1, name) +#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 2, name, base) +#define INSTR_LoadPropertyA(op) INSTRUCTION(op, LoadPropertyA, 1, name) +#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 2, index, base) +#define INSTR_GetLookupA(op) INSTRUCTION(op, GetLookupA, 1, index) +#define INSTR_LoadScopeObjectProperty(op) INSTRUCTION(op, LoadScopeObjectProperty, 3, propertyIndex, base, captureRequired) +#define INSTR_LoadContextObjectProperty(op) INSTRUCTION(op, LoadContextObjectProperty, 3, propertyIndex, base, captureRequired) +#define INSTR_LoadIdObject(op) INSTRUCTION(op, LoadIdObject, 2, index, base) +#define INSTR_StoreProperty(op) INSTRUCTION(op, StoreProperty, 2, name, base) +#define INSTR_SetLookup(op) INSTRUCTION(op, SetLookup, 2, index, base) +#define INSTR_StoreScopeObjectProperty(op) INSTRUCTION(op, StoreScopeObjectProperty, 2, base, propertyIndex) +#define INSTR_StoreContextObjectProperty(op) INSTRUCTION(op, StoreContextObjectProperty, 2, base, propertyIndex) +#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 2, base, index) +#define INSTR_LoadElementA(op) INSTRUCTION(op, LoadElementA, 1, base) +#define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 2, base, index) +#define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 2, argc, argv) +#define INSTR_CallProperty(op) INSTRUCTION(op, CallProperty, 4, name, base, argc, argv) +#define INSTR_CallPropertyLookup(op) INSTRUCTION(op, CallPropertyLookup, 4, lookupIndex, base, argc, argv) +#define INSTR_CallElement(op) INSTRUCTION(op, CallElement, 4, base, index, argc, argv) +#define INSTR_CallName(op) INSTRUCTION(op, CallName, 3, name, argc, argv) +#define INSTR_CallPossiblyDirectEval(op) INSTRUCTION(op, CallPossiblyDirectEval, 2, argc, argv) +#define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 3, index, argc, argv) +#define INSTR_SetExceptionHandler(op) INSTRUCTION(op, SetExceptionHandler, 1, offset) +#define INSTR_ThrowException(op) INSTRUCTION(op, ThrowException, 0) +#define INSTR_GetException(op) INSTRUCTION(op, GetException, 0) +#define INSTR_SetException(op) INSTRUCTION(op, SetException, 0) +#define INSTR_CreateCallContext(op) INSTRUCTION(op, CreateCallContext, 0) +#define INSTR_PushCatchContext(op) INSTRUCTION(op, PushCatchContext, 2, name, reg) +#define INSTR_PushWithContext(op) INSTRUCTION(op, PushWithContext, 1, reg) +#define INSTR_PopContext(op) INSTRUCTION(op, PopContext, 1, reg) +#define INSTR_ForeachIteratorObject(op) INSTRUCTION(op, ForeachIteratorObject, 0) +#define INSTR_ForeachNextPropertyName(op) INSTRUCTION(op, ForeachNextPropertyName, 0) +#define INSTR_DeleteMember(op) INSTRUCTION(op, DeleteMember, 2, member, base) +#define INSTR_DeleteSubscript(op) INSTRUCTION(op, DeleteSubscript, 2, base, index) +#define INSTR_DeleteName(op) INSTRUCTION(op, DeleteName, 1, name) +#define INSTR_TypeofName(op) INSTRUCTION(op, TypeofName, 1, name) +#define INSTR_TypeofValue(op) INSTRUCTION(op, TypeofValue, 0) +#define INSTR_DeclareVar(op) INSTRUCTION(op, DeclareVar, 2, varName, isDeletable) +#define INSTR_DefineArray(op) INSTRUCTION(op, DefineArray, 2, argc, args) +// arrayGetterSetterCountAndFlags contains 30 bits for count, 1 bit for needsSparseArray boolean +#define INSTR_DefineObjectLiteral(op) INSTRUCTION(op, DefineObjectLiteral, 4, internalClassId, arrayValueCount, arrayGetterSetterCountAndFlags, args) +#define INSTR_CreateMappedArgumentsObject(op) INSTRUCTION(op, CreateMappedArgumentsObject, 0) +#define INSTR_CreateUnmappedArgumentsObject(op) INSTRUCTION(op, CreateUnmappedArgumentsObject, 0) +#define INSTR_ConvertThisToObject(op) INSTRUCTION(op, ConvertThisToObject, 0) +#define INSTR_Construct(op) INSTRUCTION(op, Construct, 3, func, argc, argv) +#define INSTR_Jump(op) INSTRUCTION(op, Jump, 1, offset) +#define INSTR_JumpTrue(op) INSTRUCTION(op, JumpTrue, 1, offset) +#define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 1, offset) +#define INSTR_CmpEqNull(op) INSTRUCTION(op, CmpEqNull, 0) +#define INSTR_CmpNeNull(op) INSTRUCTION(op, CmpNeNull, 0) +#define INSTR_CmpEqInt(op) INSTRUCTION(op, CmpEqInt, 1, lhs) +#define INSTR_CmpNeInt(op) INSTRUCTION(op, CmpNeInt, 1, lhs) +#define INSTR_CmpEq(op) INSTRUCTION(op, CmpEq, 1, lhs) +#define INSTR_CmpNe(op) INSTRUCTION(op, CmpNe, 1, lhs) +#define INSTR_CmpGt(op) INSTRUCTION(op, CmpGt, 1, lhs) +#define INSTR_CmpGe(op) INSTRUCTION(op, CmpGe, 1, lhs) +#define INSTR_CmpLt(op) INSTRUCTION(op, CmpLt, 1, lhs) +#define INSTR_CmpLe(op) INSTRUCTION(op, CmpLe, 1, lhs) +#define INSTR_CmpStrictEqual(op) INSTRUCTION(op, CmpStrictEqual, 1, lhs) +#define INSTR_CmpStrictNotEqual(op) INSTRUCTION(op, CmpStrictNotEqual, 1, lhs) +#define INSTR_CmpIn(op) INSTRUCTION(op, CmpIn, 1, lhs) +#define INSTR_CmpInstanceOf(op) INSTRUCTION(op, CmpInstanceOf, 1, lhs) +#define INSTR_JumpStrictEqualStackSlotInt(op) INSTRUCTION(op, JumpStrictEqualStackSlotInt, 3, lhs, rhs, offset) +#define INSTR_JumpStrictNotEqualStackSlotInt(op) INSTRUCTION(op, JumpStrictNotEqualStackSlotInt, 3, lhs, rhs, offset) +#define INSTR_UNot(op) INSTRUCTION(op, UNot, 0) +#define INSTR_UPlus(op) INSTRUCTION(op, UPlus, 0) +#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 0) +#define INSTR_UCompl(op) INSTRUCTION(op, UCompl, 0) +#define INSTR_Increment(op) INSTRUCTION(op, Increment, 0) +#define INSTR_Decrement(op) INSTRUCTION(op, Decrement, 0) +#define INSTR_Add(op) INSTRUCTION(op, Add, 1, lhs) +#define INSTR_BitAnd(op) INSTRUCTION(op, BitAnd, 1, lhs) +#define INSTR_BitOr(op) INSTRUCTION(op, BitOr, 1, lhs) +#define INSTR_BitXor(op) INSTRUCTION(op, BitXor, 1, lhs) +#define INSTR_UShr(op) INSTRUCTION(op, UShr, 1, lhs) +#define INSTR_Shr(op) INSTRUCTION(op, Shr, 1, lhs) +#define INSTR_Shl(op) INSTRUCTION(op, Shl, 1, lhs) +#define INSTR_BitAndConst(op) INSTRUCTION(op, BitAndConst, 1, rhs) +#define INSTR_BitOrConst(op) INSTRUCTION(op, BitOrConst, 1, rhs) +#define INSTR_BitXorConst(op) INSTRUCTION(op, BitXorConst, 1, rhs) +#define INSTR_UShrConst(op) INSTRUCTION(op, UShrConst, 1, rhs) +#define INSTR_ShrConst(op) INSTRUCTION(op, ShrConst, 1, rhs) +#define INSTR_ShlConst(op) INSTRUCTION(op, ShlConst, 1, rhs) +#define INSTR_Mul(op) INSTRUCTION(op, Mul, 1, lhs) +#define INSTR_Div(op) INSTRUCTION(op, Div, 1, lhs) +#define INSTR_Mod(op) INSTRUCTION(op, Mod, 1, lhs) +#define INSTR_Sub(op) INSTRUCTION(op, Sub, 1, lhs) +#define INSTR_LoadQmlContext(op) INSTRUCTION(op, LoadQmlContext, 1, result) +#define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result) +#define INSTR_LoadQmlSingleton(op) INSTRUCTION(op, LoadQmlSingleton, 1, name) + + #define FOR_EACH_MOTH_INSTR(F) \ - F(Ret, ret) \ - MOTH_DEBUG_INSTR(F) \ - F(LoadRuntimeString, loadRuntimeString) \ - F(LoadRegExp, loadRegExp) \ - F(LoadClosure, loadClosure) \ - F(Move, move) \ - F(MoveConst, moveConst) \ - F(SwapTemps, swapTemps) \ - F(LoadName, loadName) \ - F(GetGlobalLookup, getGlobalLookup) \ - F(StoreName, storeName) \ - F(LoadElement, loadElement) \ - F(LoadElementLookup, loadElementLookup) \ - F(StoreElement, storeElement) \ - F(StoreElementLookup, storeElementLookup) \ - F(LoadProperty, loadProperty) \ - F(GetLookup, getLookup) \ - F(StoreProperty, storeProperty) \ - F(SetLookup, setLookup) \ - F(StoreQObjectProperty, storeQObjectProperty) \ - F(LoadQObjectProperty, loadQObjectProperty) \ - F(StoreScopeObjectProperty, storeScopeObjectProperty) \ - F(StoreContextObjectProperty, storeContextObjectProperty) \ - F(LoadScopeObjectProperty, loadScopeObjectProperty) \ - F(LoadContextObjectProperty, loadContextObjectProperty) \ - F(LoadIdObject, loadIdObject) \ - F(LoadAttachedQObjectProperty, loadAttachedQObjectProperty) \ - F(LoadSingletonQObjectProperty, loadQObjectProperty) \ - F(Push, push) \ - F(CallValue, callValue) \ - F(CallProperty, callProperty) \ - F(CallPropertyLookup, callPropertyLookup) \ - F(CallScopeObjectProperty, callScopeObjectProperty) \ - F(CallContextObjectProperty, callContextObjectProperty) \ - F(CallElement, callElement) \ - F(CallActivationProperty, callActivationProperty) \ - F(CallGlobalLookup, callGlobalLookup) \ - F(SetExceptionHandler, setExceptionHandler) \ - F(CallBuiltinThrow, callBuiltinThrow) \ - F(CallBuiltinUnwindException, callBuiltinUnwindException) \ - F(CallBuiltinPushCatchScope, callBuiltinPushCatchScope) \ - F(CallBuiltinPushScope, callBuiltinPushScope) \ - F(CallBuiltinPopScope, callBuiltinPopScope) \ - F(CallBuiltinForeachIteratorObject, callBuiltinForeachIteratorObject) \ - F(CallBuiltinForeachNextPropertyName, callBuiltinForeachNextPropertyName) \ - F(CallBuiltinDeleteMember, callBuiltinDeleteMember) \ - F(CallBuiltinDeleteSubscript, callBuiltinDeleteSubscript) \ - F(CallBuiltinDeleteName, callBuiltinDeleteName) \ - F(CallBuiltinTypeofScopeObjectProperty, callBuiltinTypeofScopeObjectProperty) \ - F(CallBuiltinTypeofContextObjectProperty, callBuiltinTypeofContextObjectProperty) \ - F(CallBuiltinTypeofMember, callBuiltinTypeofMember) \ - F(CallBuiltinTypeofSubscript, callBuiltinTypeofSubscript) \ - F(CallBuiltinTypeofName, callBuiltinTypeofName) \ - F(CallBuiltinTypeofValue, callBuiltinTypeofValue) \ - F(CallBuiltinDeclareVar, callBuiltinDeclareVar) \ - F(CallBuiltinDefineArray, callBuiltinDefineArray) \ - F(CallBuiltinDefineObjectLiteral, callBuiltinDefineObjectLiteral) \ - F(CallBuiltinSetupArgumentsObject, callBuiltinSetupArgumentsObject) \ - F(CallBuiltinConvertThisToObject, callBuiltinConvertThisToObject) \ - F(CreateValue, createValue) \ - F(CreateProperty, createProperty) \ - F(ConstructPropertyLookup, constructPropertyLookup) \ - F(CreateActivationProperty, createActivationProperty) \ - F(ConstructGlobalLookup, constructGlobalLookup) \ - F(Jump, jump) \ - F(JumpEq, jumpEq) \ - F(JumpNe, jumpNe) \ - F(UNot, unot) \ - F(UNotBool, unotBool) \ - F(UPlus, uplus) \ - F(UMinus, uminus) \ - F(UCompl, ucompl) \ - F(UComplInt, ucomplInt) \ - F(Increment, increment) \ - F(Decrement, decrement) \ - F(Binop, binop) \ - F(Add, add) \ - F(BitAnd, bitAnd) \ - F(BitOr, bitOr) \ - F(BitXor, bitXor) \ - F(Shr, shr) \ - F(Shl, shl) \ - F(BitAndConst, bitAndConst) \ - F(BitOrConst, bitOrConst) \ - F(BitXorConst, bitXorConst) \ - F(ShrConst, shrConst) \ - F(ShlConst, shlConst) \ - F(Mul, mul) \ - F(Sub, sub) \ - F(BinopContext, binopContext) \ - F(LoadThis, loadThis) \ - F(LoadQmlContext, loadQmlContext) \ - F(LoadQmlImportedScripts, loadQmlImportedScripts) \ - F(LoadQmlSingleton, loadQmlSingleton) + F(Ret) \ + F(Debug) \ + F(LoadConst) \ + F(LoadZero) \ + F(LoadTrue) \ + F(LoadFalse) \ + F(LoadNull) \ + F(LoadUndefined) \ + F(LoadInt) \ + F(MoveConst) \ + F(LoadReg) \ + F(StoreReg) \ + F(MoveReg) \ + F(LoadLocal) \ + F(StoreLocal) \ + F(LoadScopedLocal) \ + F(StoreScopedLocal) \ + F(LoadRuntimeString) \ + F(LoadRegExp) \ + F(LoadClosure) \ + F(LoadName) \ + F(LoadGlobalLookup) \ + F(StoreNameSloppy) \ + F(StoreNameStrict) \ + F(LoadElement) \ + F(LoadElementA) \ + F(StoreElement) \ + F(LoadProperty) \ + F(LoadPropertyA) \ + F(GetLookup) \ + F(GetLookupA) \ + F(StoreProperty) \ + F(SetLookup) \ + F(StoreScopeObjectProperty) \ + F(StoreContextObjectProperty) \ + F(LoadScopeObjectProperty) \ + F(LoadContextObjectProperty) \ + F(LoadIdObject) \ + F(CallValue) \ + F(CallProperty) \ + F(CallPropertyLookup) \ + F(CallElement) \ + F(CallName) \ + F(CallPossiblyDirectEval) \ + F(CallGlobalLookup) \ + F(SetExceptionHandler) \ + F(ThrowException) \ + F(GetException) \ + F(SetException) \ + F(CreateCallContext) \ + F(PushCatchContext) \ + F(PushWithContext) \ + F(PopContext) \ + F(ForeachIteratorObject) \ + F(ForeachNextPropertyName) \ + F(DeleteMember) \ + F(DeleteSubscript) \ + F(DeleteName) \ + F(TypeofName) \ + F(TypeofValue) \ + F(DeclareVar) \ + F(DefineArray) \ + F(DefineObjectLiteral) \ + F(CreateMappedArgumentsObject) \ + F(CreateUnmappedArgumentsObject) \ + F(ConvertThisToObject) \ + F(Construct) \ + F(Jump) \ + F(JumpTrue) \ + F(JumpFalse) \ + F(CmpEqNull) \ + F(CmpNeNull) \ + F(CmpEqInt) \ + F(CmpNeInt) \ + F(CmpEq) \ + F(CmpNe) \ + F(CmpGt) \ + F(CmpGe) \ + F(CmpLt) \ + F(CmpLe) \ + F(CmpStrictEqual) \ + F(CmpStrictNotEqual) \ + F(CmpIn) \ + F(CmpInstanceOf) \ + F(JumpStrictEqualStackSlotInt) \ + F(JumpStrictNotEqualStackSlotInt) \ + F(UNot) \ + F(UPlus) \ + F(UMinus) \ + F(UCompl) \ + F(Increment) \ + F(Decrement) \ + F(Add) \ + F(BitAnd) \ + F(BitOr) \ + F(BitXor) \ + F(UShr) \ + F(Shr) \ + F(Shl) \ + F(BitAndConst) \ + F(BitOrConst) \ + F(BitXorConst) \ + F(UShrConst) \ + F(ShrConst) \ + F(ShlConst) \ + F(Mul) \ + F(Div) \ + F(Mod) \ + F(Sub) \ + F(LoadQmlContext) \ + F(LoadQmlImportedScripts) \ + F(LoadQmlSingleton) +#define MOTH_NUM_INSTRUCTIONS() (static_cast<int>(Moth::Instr::Type::LoadQmlSingleton) + 1) #if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) -# define MOTH_THREADED_INTERPRETER +# define MOTH_COMPUTED_GOTO #endif #define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QV4::Moth::Instr) - 1) -#define MOTH_INSTR_HEADER quint32 instructionType; +#define MOTH_INSTR_ENUM(I) I, +#define MOTH_INSTR_SIZE(I) (sizeof(QV4::Moth::Instr::instr_##I)) + +#define MOTH_EXPAND_FOR_MSVC(x) x +#define MOTH_DEFINE_ARGS(nargs, ...) \ + MOTH_EXPAND_FOR_MSVC(MOTH_DEFINE_ARGS##nargs(__VA_ARGS__)) + +#define MOTH_DEFINE_ARGS0() +#define MOTH_DEFINE_ARGS1(arg) \ + int arg; +#define MOTH_DEFINE_ARGS2(arg1, arg2) \ + int arg1; \ + int arg2; +#define MOTH_DEFINE_ARGS3(arg1, arg2, arg3) \ + int arg1; \ + int arg2; \ + int arg3; +#define MOTH_DEFINE_ARGS4(arg1, arg2, arg3, arg4) \ + int arg1; \ + int arg2; \ + int arg3; \ + int arg4; + +#define MOTH_COLLECT_ENUMS(instr) \ + INSTR_##instr(MOTH_GET_ENUM) +#define MOTH_GET_ENUM_INSTRUCTION(name, ...) \ + name, + +#define MOTH_EMIT_STRUCTS(instr) \ + INSTR_##instr(MOTH_EMIT_STRUCT) +#define MOTH_EMIT_STRUCT_INSTRUCTION(name, nargs, ...) \ + struct instr_##name { \ + MOTH_DEFINE_ARGS(nargs, __VA_ARGS__) \ + }; + +#define MOTH_EMIT_INSTR_MEMBERS(instr) \ + INSTR_##instr(MOTH_EMIT_INSTR_MEMBER) +#define MOTH_EMIT_INSTR_MEMBER_INSTRUCTION(name, nargs, ...) \ + instr_##name name; + +#define MOTH_COLLECT_NARGS(instr) \ + INSTR_##instr(MOTH_COLLECT_ARG_COUNT) +#define MOTH_COLLECT_ARG_COUNT_INSTRUCTION(name, nargs, ...) \ + nargs, + +#define MOTH_DECODE_ARG(arg, type, nargs, offset) \ + arg = reinterpret_cast<const type *>(code)[-nargs + offset]; +#define MOTH_ADJUST_CODE(type, nargs) \ + code += static_cast<quintptr>(nargs*sizeof(type) + 1) + +#define MOTH_DECODE_INSTRUCTION(name, nargs, ...) \ + MOTH_DEFINE_ARGS(nargs, __VA_ARGS__) \ + op_int_##name: \ + MOTH_ADJUST_CODE(int, nargs); \ + MOTH_DECODE_ARGS(name, int, nargs, __VA_ARGS__) \ + goto op_main_##name; \ + op_char_##name: \ + MOTH_ADJUST_CODE(char, nargs); \ + MOTH_DECODE_ARGS(name, char, nargs, __VA_ARGS__) \ + op_main_##name: \ + ; \ + +#define MOTH_DECODE_WITH_BASE_INSTRUCTION(name, nargs, ...) \ + MOTH_DEFINE_ARGS(nargs, __VA_ARGS__) \ + const char *base_ptr; \ + op_int_##name: \ + base_ptr = code; \ + MOTH_ADJUST_CODE(int, nargs); \ + MOTH_DECODE_ARGS(name, int, nargs, __VA_ARGS__) \ + goto op_main_##name; \ + op_char_##name: \ + base_ptr = code; \ + MOTH_ADJUST_CODE(char, nargs); \ + MOTH_DECODE_ARGS(name, char, nargs, __VA_ARGS__) \ + op_main_##name: \ + ; \ + +#define MOTH_DECODE_ARGS(name, type, nargs, ...) \ + MOTH_EXPAND_FOR_MSVC(MOTH_DECODE_ARGS##nargs(name, type, nargs, __VA_ARGS__)) + +#define MOTH_DECODE_ARGS0(name, type, nargs, dummy) +#define MOTH_DECODE_ARGS1(name, type, nargs, arg) \ + MOTH_DECODE_ARG(arg, type, nargs, 0); +#define MOTH_DECODE_ARGS2(name, type, nargs, arg1, arg2) \ + MOTH_DECODE_ARGS1(name, type, nargs, arg1); \ + MOTH_DECODE_ARG(arg2, type, nargs, 1); +#define MOTH_DECODE_ARGS3(name, type, nargs, arg1, arg2, arg3) \ + MOTH_DECODE_ARGS2(name, type, nargs, arg1, arg2); \ + MOTH_DECODE_ARG(arg3, type, nargs, 2); +#define MOTH_DECODE_ARGS4(name, type, nargs, arg1, arg2, arg3, arg4) \ + MOTH_DECODE_ARGS3(name, type, nargs, arg1, arg2, arg3); \ + MOTH_DECODE_ARG(arg4, type, nargs, 3); + +#ifdef MOTH_COMPUTED_GOTO +/* collect jump labels */ +#define COLLECT_LABELS(instr) \ + INSTR_##instr(GET_LABEL) +#define GET_LABEL_INSTRUCTION(name, ...) \ + &&op_char_##name, +#define COLLECT_LABELS_WIDE(instr) \ + INSTR_##instr(GET_LABEL_WIDE) +#define GET_LABEL_WIDE_INSTRUCTION(name, ...) \ + &&op_int_##name, + +#define MOTH_JUMP_TABLE \ + static const void *jumpTable[] = { \ + FOR_EACH_MOTH_INSTR(COLLECT_LABELS) \ + FOR_EACH_MOTH_INSTR(COLLECT_LABELS_WIDE) \ + }; + +#define MOTH_DISPATCH() \ + goto *jumpTable[*reinterpret_cast<const uchar *>(code)]; +#else +#define MOTH_JUMP_TABLE + +#define MOTH_INSTR_CASE_AND_JUMP(instr) \ + INSTR_##instr(GET_CASE_AND_JUMP) +#define GET_CASE_AND_JUMP_INSTRUCTION(name, ...) \ + case static_cast<uchar>(Instr::Type::name): goto op_char_##name; +#define MOTH_INSTR_CASE_AND_JUMP_WIDE(instr) \ + INSTR_##instr(GET_CASE_AND_JUMP_WIDE) +#define GET_CASE_AND_JUMP_WIDE_INSTRUCTION(name, ...) \ + case (static_cast<uchar>(Instr::Type::name) + MOTH_NUM_INSTRUCTIONS()): goto op_int_##name; + +#define MOTH_DISPATCH() \ + switch (static_cast<uchar>(*code)) { \ + FOR_EACH_MOTH_INSTR(MOTH_INSTR_CASE_AND_JUMP) \ + FOR_EACH_MOTH_INSTR(MOTH_INSTR_CASE_AND_JUMP_WIDE) \ + } +#endif -#define MOTH_INSTR_ENUM(I, FMT) I, -#define MOTH_INSTR_SIZE(I, FMT) ((sizeof(QV4::Moth::Instr::instr_##FMT) + MOTH_INSTR_ALIGN_MASK) & ~MOTH_INSTR_ALIGN_MASK) +namespace QV4 { +namespace CompiledData { +struct CodeOffsetToLine; +} -namespace QV4 { namespace Moth { - // When making changes to the instructions, make sure to bump QV4_DATA_STRUCTURE_VERSION in qv4compileddata_p.h - -struct Param { - // Params are looked up as follows: - // Constant: 0 - // Temp: 1 - // Argument: 2 - // Local: 3 - // Arg(outer): 4 - // Local(outer): 5 - // ... - unsigned scope : 12; - unsigned index : 20; - - bool isConstant() const { return !scope; } - bool isArgument() const { return scope >= 2 && !(scope &1); } - bool isLocal() const { return scope == 3; } - bool isTemp() const { return scope == 1; } - bool isScopedLocal() const { return scope >= 3 && (scope & 1); } - - static Param createConstant(int index) - { - Param p; - p.scope = 0; - p.index = index; - return p; - } +class StackSlot { + int index; - static Param createArgument(unsigned idx, uint scope) - { - Param p; - p.scope = 2 + 2*scope; - p.index = idx; - return p; +public: + static StackSlot createRegister(int index) { + Q_ASSERT(index >= 0); + StackSlot t; + t.index = index; + return t; } - static Param createLocal(unsigned idx) - { - Param p; - p.scope = 3; - p.index = idx; - return p; - } + int stackSlot() const { return index; } + operator int() const { return index; } +}; - static Param createTemp(unsigned idx) - { - Param p; - p.scope = 1; - p.index = idx; - return p; - } +inline bool operator==(const StackSlot &l, const StackSlot &r) { return l.stackSlot() == r.stackSlot(); } +inline bool operator!=(const StackSlot &l, const StackSlot &r) { return l.stackSlot() != r.stackSlot(); } - static Param createScopedLocal(unsigned idx, uint scope) - { - Param p; - p.scope = 3 + 2*scope; - p.index = idx; - return p; - } +// When making changes to the instructions, make sure to bump QV4_DATA_STRUCTURE_VERSION in qv4compileddata_p.h - inline bool operator==(const Param &other) const - { return scope == other.scope && index == other.index; } - - inline bool operator!=(const Param &other) const - { return !(*this == other); } -}; +void dumpConstantTable(const Value *constants, uint count); +void dumpBytecode(const char *bytecode, int len, int nLocals, int nFormals, int startLine = 1, + const QVector<CompiledData::CodeOffsetToLine> &lineNumberMapping = QVector<CompiledData::CodeOffsetToLine>()); +inline void dumpBytecode(const QByteArray &bytecode, int nLocals, int nFormals, int startLine = 1, + const QVector<CompiledData::CodeOffsetToLine> &lineNumberMapping = QVector<CompiledData::CodeOffsetToLine>()) { + dumpBytecode(bytecode.constData(), bytecode.length(), nLocals, nFormals, startLine, lineNumberMapping); +} union Instr { - enum Type { + enum class Type { FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM) - LastInstruction }; - struct instr_common { - MOTH_INSTR_HEADER - }; - struct instr_ret { - MOTH_INSTR_HEADER - Param result; - }; - -#ifndef QT_NO_QML_DEBUGGING - struct instr_line { - MOTH_INSTR_HEADER - qint32 lineNumber; - }; - struct instr_debug { - MOTH_INSTR_HEADER - qint32 lineNumber; - }; -#endif // QT_NO_QML_DEBUGGING + FOR_EACH_MOTH_INSTR(MOTH_EMIT_STRUCTS) - struct instr_loadRuntimeString { - MOTH_INSTR_HEADER - int stringId; - Param result; - }; - struct instr_loadRegExp { - MOTH_INSTR_HEADER - int regExpId; - Param result; - }; - struct instr_move { - MOTH_INSTR_HEADER - Param source; - Param result; - }; - struct instr_moveConst { - MOTH_INSTR_HEADER - QV4::ReturnedValue source; - Param result; - }; - struct instr_swapTemps { - MOTH_INSTR_HEADER - Param left; - Param right; - }; - struct instr_loadClosure { - MOTH_INSTR_HEADER - int value; - Param result; - }; - struct instr_loadName { - MOTH_INSTR_HEADER - int name; - Param result; - }; - struct instr_getGlobalLookup { - MOTH_INSTR_HEADER - int index; - Param result; - }; - struct instr_storeName { - MOTH_INSTR_HEADER - int name; - Param source; - }; - struct instr_loadProperty { - MOTH_INSTR_HEADER - int name; - Param base; - Param result; - }; - struct instr_getLookup { - MOTH_INSTR_HEADER - int index; - Param base; - Param result; - }; - struct instr_loadScopeObjectProperty { - MOTH_INSTR_HEADER - int propertyIndex; - Param base; - Param result; - bool captureRequired; - }; - struct instr_loadContextObjectProperty { - MOTH_INSTR_HEADER - int propertyIndex; - Param base; - Param result; - bool captureRequired; - }; - struct instr_loadIdObject { - MOTH_INSTR_HEADER - int index; - Param base; - Param result; - }; - struct instr_loadQObjectProperty { - MOTH_INSTR_HEADER - int propertyIndex; - Param base; - Param result; - bool captureRequired; - }; - struct instr_loadAttachedQObjectProperty { - MOTH_INSTR_HEADER - int propertyIndex; - Param result; - int attachedPropertiesId; - }; - struct instr_storeProperty { - MOTH_INSTR_HEADER - int name; - Param base; - Param source; - }; - struct instr_setLookup { - MOTH_INSTR_HEADER - int index; - Param base; - Param source; - }; - struct instr_storeScopeObjectProperty { - MOTH_INSTR_HEADER - Param base; - int propertyIndex; - Param source; - }; - struct instr_storeContextObjectProperty { - MOTH_INSTR_HEADER - Param base; - int propertyIndex; - Param source; - }; - struct instr_storeQObjectProperty { - MOTH_INSTR_HEADER - Param base; - int propertyIndex; - Param source; - }; - struct instr_loadElement { - MOTH_INSTR_HEADER - Param base; - Param index; - Param result; - }; - struct instr_loadElementLookup { - MOTH_INSTR_HEADER - uint lookup; - Param base; - Param index; - Param result; - }; - struct instr_storeElement { - MOTH_INSTR_HEADER - Param base; - Param index; - Param source; - }; - struct instr_storeElementLookup { - MOTH_INSTR_HEADER - uint lookup; - Param base; - Param index; - Param source; - }; - struct instr_push { - MOTH_INSTR_HEADER - quint32 value; - }; - struct instr_callValue { - MOTH_INSTR_HEADER - quint32 argc; - quint32 callData; - Param dest; - Param result; - }; - struct instr_callProperty { - MOTH_INSTR_HEADER - int name; - quint32 argc; - quint32 callData; - Param base; - Param result; - }; - struct instr_callPropertyLookup { - MOTH_INSTR_HEADER - int lookupIndex; - quint32 argc; - quint32 callData; - Param base; - Param result; - }; - struct instr_callScopeObjectProperty { - MOTH_INSTR_HEADER - int index; - quint32 argc; - quint32 callData; - Param base; - Param result; - }; - struct instr_callContextObjectProperty { - MOTH_INSTR_HEADER - int index; - quint32 argc; - quint32 callData; - Param base; - Param result; - }; - struct instr_callElement { - MOTH_INSTR_HEADER - Param base; - Param index; - quint32 argc; - quint32 callData; - Param result; - }; - struct instr_callActivationProperty { - MOTH_INSTR_HEADER - int name; - quint32 argc; - quint32 callData; - Param result; - }; - struct instr_callGlobalLookup { - MOTH_INSTR_HEADER - int index; - quint32 argc; - quint32 callData; - Param result; - }; - struct instr_setExceptionHandler { - MOTH_INSTR_HEADER - qptrdiff offset; - }; - struct instr_callBuiltinThrow { - MOTH_INSTR_HEADER - Param arg; - }; - struct instr_callBuiltinUnwindException { - MOTH_INSTR_HEADER - Param result; - }; - struct instr_callBuiltinPushCatchScope { - MOTH_INSTR_HEADER - int name; - }; - struct instr_callBuiltinPushScope { - MOTH_INSTR_HEADER - Param arg; - }; - struct instr_callBuiltinPopScope { - MOTH_INSTR_HEADER - }; - struct instr_callBuiltinForeachIteratorObject { - MOTH_INSTR_HEADER - Param arg; - Param result; - }; - struct instr_callBuiltinForeachNextPropertyName { - MOTH_INSTR_HEADER - Param arg; - Param result; - }; - struct instr_callBuiltinDeleteMember { - MOTH_INSTR_HEADER - int member; - Param base; - Param result; - }; - struct instr_callBuiltinDeleteSubscript { - MOTH_INSTR_HEADER - Param base; - Param index; - Param result; - }; - struct instr_callBuiltinDeleteName { - MOTH_INSTR_HEADER - int name; - Param result; - }; - struct instr_callBuiltinTypeofScopeObjectProperty { - MOTH_INSTR_HEADER - int index; - Param base; - Param result; - }; - struct instr_callBuiltinTypeofContextObjectProperty { - MOTH_INSTR_HEADER - int index; - Param base; - Param result; - }; - struct instr_callBuiltinTypeofMember { - MOTH_INSTR_HEADER - int member; - Param base; - Param result; - }; - struct instr_callBuiltinTypeofSubscript { - MOTH_INSTR_HEADER - Param base; - Param index; - Param result; - }; - struct instr_callBuiltinTypeofName { - MOTH_INSTR_HEADER - int name; - Param result; - }; - struct instr_callBuiltinTypeofValue { - MOTH_INSTR_HEADER - Param value; - Param result; - }; - struct instr_callBuiltinDeclareVar { - MOTH_INSTR_HEADER - int varName; - bool isDeletable; - }; - struct instr_callBuiltinDefineArray { - MOTH_INSTR_HEADER - quint32 argc; - quint32 args; - Param result; - }; - struct instr_callBuiltinDefineObjectLiteral { - MOTH_INSTR_HEADER - int internalClassId; - uint arrayValueCount; - uint arrayGetterSetterCountAndFlags; // 30 bits for count, 1 bit for needsSparseArray boolean - quint32 args; - Param result; - }; - struct instr_callBuiltinSetupArgumentsObject { - MOTH_INSTR_HEADER - Param result; - }; - struct instr_callBuiltinConvertThisToObject { - MOTH_INSTR_HEADER - }; - struct instr_createValue { - MOTH_INSTR_HEADER - quint32 argc; - quint32 callData; - Param func; - Param result; - }; - struct instr_createProperty { - MOTH_INSTR_HEADER - int name; - quint32 argc; - quint32 callData; - Param base; - Param result; - }; - struct instr_constructPropertyLookup { - MOTH_INSTR_HEADER - int index; - quint32 argc; - quint32 callData; - Param base; - Param result; - }; - struct instr_createActivationProperty { - MOTH_INSTR_HEADER - int name; - quint32 argc; - quint32 callData; - Param result; - }; - struct instr_constructGlobalLookup { - MOTH_INSTR_HEADER - int index; - quint32 argc; - quint32 callData; - Param result; - }; - struct instr_jump { - MOTH_INSTR_HEADER - ptrdiff_t offset; - }; - struct instr_jumpEq { - MOTH_INSTR_HEADER - ptrdiff_t offset; - Param condition; - }; - struct instr_jumpNe { - MOTH_INSTR_HEADER - ptrdiff_t offset; - Param condition; - }; - struct instr_unot { - MOTH_INSTR_HEADER - Param source; - Param result; - }; - struct instr_unotBool { - MOTH_INSTR_HEADER - Param source; - Param result; - }; - struct instr_uplus { - MOTH_INSTR_HEADER - Param source; - Param result; - }; - struct instr_uminus { - MOTH_INSTR_HEADER - Param source; - Param result; - }; - struct instr_ucompl { - MOTH_INSTR_HEADER - Param source; - Param result; - }; - struct instr_ucomplInt { - MOTH_INSTR_HEADER - Param source; - Param result; - }; - struct instr_increment { - MOTH_INSTR_HEADER - Param source; - Param result; - }; - struct instr_decrement { - MOTH_INSTR_HEADER - Param source; - Param result; - }; - struct instr_binop { - MOTH_INSTR_HEADER - int alu; // QV4::Runtime::RuntimeMethods enum value - Param lhs; - Param rhs; - Param result; - }; - struct instr_add { - MOTH_INSTR_HEADER - Param lhs; - Param rhs; - Param result; - }; - struct instr_bitAnd { - MOTH_INSTR_HEADER - Param lhs; - Param rhs; - Param result; - }; - struct instr_bitOr { - MOTH_INSTR_HEADER - Param lhs; - Param rhs; - Param result; - }; - struct instr_bitXor { - MOTH_INSTR_HEADER - Param lhs; - Param rhs; - Param result; - }; - struct instr_shr { - MOTH_INSTR_HEADER - Param lhs; - Param rhs; - Param result; - }; - struct instr_shl { - MOTH_INSTR_HEADER - Param lhs; - Param rhs; - Param result; - }; - struct instr_bitAndConst { - MOTH_INSTR_HEADER - Param lhs; - int rhs; - Param result; - }; - struct instr_bitOrConst { - MOTH_INSTR_HEADER - Param lhs; - int rhs; - Param result; - }; - struct instr_bitXorConst { - MOTH_INSTR_HEADER - Param lhs; - int rhs; - Param result; - }; - struct instr_shrConst { - MOTH_INSTR_HEADER - Param lhs; - int rhs; - Param result; - }; - struct instr_shlConst { - MOTH_INSTR_HEADER - Param lhs; - int rhs; - Param result; - }; - struct instr_mul { - MOTH_INSTR_HEADER - Param lhs; - Param rhs; - Param result; - }; - struct instr_sub { - MOTH_INSTR_HEADER - Param lhs; - Param rhs; - Param result; - }; - struct instr_binopContext { - MOTH_INSTR_HEADER - uint alu; // offset inside the runtime methods - Param lhs; - Param rhs; - Param result; - }; - struct instr_loadThis { - MOTH_INSTR_HEADER - Param result; - }; - struct instr_loadQmlContext { - MOTH_INSTR_HEADER - Param result; - }; - struct instr_loadQmlImportedScripts { - MOTH_INSTR_HEADER - Param result; - }; - struct instr_loadQmlSingleton { - MOTH_INSTR_HEADER - Param result; - int name; - }; + FOR_EACH_MOTH_INSTR(MOTH_EMIT_INSTR_MEMBERS) +}; - instr_common common; - instr_ret ret; - instr_line line; - instr_debug debug; - instr_loadRuntimeString loadRuntimeString; - instr_loadRegExp loadRegExp; - instr_move move; - instr_moveConst moveConst; - instr_swapTemps swapTemps; - instr_loadClosure loadClosure; - instr_loadName loadName; - instr_getGlobalLookup getGlobalLookup; - instr_storeName storeName; - instr_loadElement loadElement; - instr_loadElementLookup loadElementLookup; - instr_storeElement storeElement; - instr_storeElementLookup storeElementLookup; - instr_loadProperty loadProperty; - instr_getLookup getLookup; - instr_loadScopeObjectProperty loadScopeObjectProperty; - instr_loadContextObjectProperty loadContextObjectProperty; - instr_loadIdObject loadIdObject; - instr_loadQObjectProperty loadQObjectProperty; - instr_loadAttachedQObjectProperty loadAttachedQObjectProperty; - instr_storeProperty storeProperty; - instr_setLookup setLookup; - instr_storeScopeObjectProperty storeScopeObjectProperty; - instr_storeContextObjectProperty storeContextObjectProperty; - instr_storeQObjectProperty storeQObjectProperty; - instr_push push; - instr_callValue callValue; - instr_callProperty callProperty; - instr_callPropertyLookup callPropertyLookup; - instr_callScopeObjectProperty callScopeObjectProperty; - instr_callContextObjectProperty callContextObjectProperty; - instr_callElement callElement; - instr_callActivationProperty callActivationProperty; - instr_callGlobalLookup callGlobalLookup; - instr_callBuiltinThrow callBuiltinThrow; - instr_setExceptionHandler setExceptionHandler; - instr_callBuiltinUnwindException callBuiltinUnwindException; - instr_callBuiltinPushCatchScope callBuiltinPushCatchScope; - instr_callBuiltinPushScope callBuiltinPushScope; - instr_callBuiltinPopScope callBuiltinPopScope; - instr_callBuiltinForeachIteratorObject callBuiltinForeachIteratorObject; - instr_callBuiltinForeachNextPropertyName callBuiltinForeachNextPropertyName; - instr_callBuiltinDeleteMember callBuiltinDeleteMember; - instr_callBuiltinDeleteSubscript callBuiltinDeleteSubscript; - instr_callBuiltinDeleteName callBuiltinDeleteName; - instr_callBuiltinTypeofScopeObjectProperty callBuiltinTypeofScopeObjectProperty; - instr_callBuiltinTypeofContextObjectProperty callBuiltinTypeofContextObjectProperty; - instr_callBuiltinTypeofMember callBuiltinTypeofMember; - instr_callBuiltinTypeofSubscript callBuiltinTypeofSubscript; - instr_callBuiltinTypeofName callBuiltinTypeofName; - instr_callBuiltinTypeofValue callBuiltinTypeofValue; - instr_callBuiltinDeclareVar callBuiltinDeclareVar; - instr_callBuiltinDefineArray callBuiltinDefineArray; - instr_callBuiltinDefineObjectLiteral callBuiltinDefineObjectLiteral; - instr_callBuiltinSetupArgumentsObject callBuiltinSetupArgumentsObject; - instr_callBuiltinConvertThisToObject callBuiltinConvertThisToObject; - instr_createValue createValue; - instr_createProperty createProperty; - instr_constructPropertyLookup constructPropertyLookup; - instr_createActivationProperty createActivationProperty; - instr_constructGlobalLookup constructGlobalLookup; - instr_jump jump; - instr_jumpEq jumpEq; - instr_jumpNe jumpNe; - instr_unot unot; - instr_unotBool unotBool; - instr_uplus uplus; - instr_uminus uminus; - instr_ucompl ucompl; - instr_ucomplInt ucomplInt; - instr_increment increment; - instr_decrement decrement; - instr_binop binop; - instr_add add; - instr_bitAnd bitAnd; - instr_bitOr bitOr; - instr_bitXor bitXor; - instr_shr shr; - instr_shl shl; - instr_bitAndConst bitAndConst; - instr_bitOrConst bitOrConst; - instr_bitXorConst bitXorConst; - instr_shrConst shrConst; - instr_shlConst shlConst; - instr_mul mul; - instr_sub sub; - instr_binopContext binopContext; - instr_loadThis loadThis; - instr_loadQmlContext loadQmlContext; - instr_loadQmlImportedScripts loadQmlImportedScripts; - instr_loadQmlSingleton loadQmlSingleton; - - static int size(Type type); +struct InstrInfo +{ + static const int argumentCount[]; + static int size(Instr::Type type); }; +Q_STATIC_ASSERT(MOTH_NUM_INSTRUCTIONS() < 128); + template<int N> struct InstrMeta { }; QT_WARNING_PUSH QT_WARNING_DISABLE_GCC("-Wuninitialized") -#define MOTH_INSTR_META_TEMPLATE(I, FMT) \ - template<> struct InstrMeta<(int)Instr::I> { \ - enum { Size = MOTH_INSTR_SIZE(I, FMT) }; \ - typedef Instr::instr_##FMT DataType; \ - static const DataType &data(const Instr &instr) { return instr.FMT; } \ - static void setData(Instr &instr, const DataType &v) { instr.FMT = v; } \ - static void setDataNoCommon(Instr &instr, const DataType &v) \ - { memcpy(reinterpret_cast<char *>(&instr.FMT) + sizeof(Instr::instr_common), \ - reinterpret_cast<const char *>(&v) + sizeof(Instr::instr_common), \ - Size - sizeof(Instr::instr_common)); } \ +QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") +#define MOTH_INSTR_META_TEMPLATE(I) \ + template<> struct InstrMeta<int(Instr::Type::I)> { \ + enum { Size = MOTH_INSTR_SIZE(I) }; \ + typedef Instr::instr_##I DataType; \ + static const DataType &data(const Instr &instr) { return instr.I; } \ + static void setData(Instr &instr, const DataType &v) \ + { memcpy(reinterpret_cast<char *>(&instr.I), \ + reinterpret_cast<const char *>(&v), \ + Size); } \ }; FOR_EACH_MOTH_INSTR(MOTH_INSTR_META_TEMPLATE); #undef MOTH_INSTR_META_TEMPLATE @@ -919,6 +519,14 @@ class InstrData : public InstrMeta<InstrType>::DataType { }; +struct Instruction { +#define MOTH_INSTR_DATA_TYPEDEF(I) typedef InstrData<int(Instr::Type::I)> I; +FOR_EACH_MOTH_INSTR(MOTH_INSTR_DATA_TYPEDEF) +#undef MOTH_INSTR_DATA_TYPEDEF +private: + Instruction(); +}; + } // namespace Moth } // namespace QV4 diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp deleted file mode 100644 index d25d1733c8..0000000000 --- a/src/qml/compiler/qv4isel_moth.cpp +++ /dev/null @@ -1,1522 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#include "qv4isel_util_p.h" -#include "qv4isel_moth_p.h" -#include "qv4ssa_p.h" -#include <private/qv4compileddata_p.h> -#include <wtf/MathExtras.h> - -#if !defined(V4_BOOTSTRAP) -#include "qv4vme_moth_p.h" -#include <private/qv4function_p.h> -#endif - -#undef USE_TYPE_INFO - -using namespace QV4; -using namespace QV4::Moth; - -namespace { - -inline QV4::Runtime::RuntimeMethods aluOpFunction(IR::AluOp op) -{ - switch (op) { - case IR::OpInvalid: - return QV4::Runtime::InvalidRuntimeMethod; - case IR::OpIfTrue: - return QV4::Runtime::InvalidRuntimeMethod; - case IR::OpNot: - return QV4::Runtime::InvalidRuntimeMethod; - case IR::OpUMinus: - return QV4::Runtime::InvalidRuntimeMethod; - case IR::OpUPlus: - return QV4::Runtime::InvalidRuntimeMethod; - case IR::OpCompl: - return QV4::Runtime::InvalidRuntimeMethod; - case IR::OpBitAnd: - return QV4::Runtime::bitAnd; - case IR::OpBitOr: - return QV4::Runtime::bitOr; - case IR::OpBitXor: - return QV4::Runtime::bitXor; - case IR::OpAdd: - return QV4::Runtime::InvalidRuntimeMethod; - case IR::OpSub: - return QV4::Runtime::sub; - case IR::OpMul: - return QV4::Runtime::mul; - case IR::OpDiv: - return QV4::Runtime::div; - case IR::OpMod: - return QV4::Runtime::mod; - case IR::OpLShift: - return QV4::Runtime::shl; - case IR::OpRShift: - return QV4::Runtime::shr; - case IR::OpURShift: - return QV4::Runtime::ushr; - case IR::OpGt: - return QV4::Runtime::greaterThan; - case IR::OpLt: - return QV4::Runtime::lessThan; - case IR::OpGe: - return QV4::Runtime::greaterEqual; - case IR::OpLe: - return QV4::Runtime::lessEqual; - case IR::OpEqual: - return QV4::Runtime::equal; - case IR::OpNotEqual: - return QV4::Runtime::notEqual; - case IR::OpStrictEqual: - return QV4::Runtime::strictEqual; - case IR::OpStrictNotEqual: - return QV4::Runtime::strictNotEqual; - case IR::OpInstanceof: - return QV4::Runtime::InvalidRuntimeMethod; - case IR::OpIn: - return QV4::Runtime::InvalidRuntimeMethod; - case IR::OpAnd: - return QV4::Runtime::InvalidRuntimeMethod; - case IR::OpOr: - return QV4::Runtime::InvalidRuntimeMethod; - default: - Q_ASSERT(!"Unknown AluOp"); - return QV4::Runtime::InvalidRuntimeMethod; - } -}; - -inline bool isNumberType(IR::Expr *e) -{ - switch (e->type) { - case IR::SInt32Type: - case IR::UInt32Type: - case IR::DoubleType: - return true; - default: - return false; - } -} - -inline bool isIntegerType(IR::Expr *e) -{ - switch (e->type) { - case IR::SInt32Type: - case IR::UInt32Type: - return true; - default: - return false; - } -} - -inline bool isBoolType(IR::Expr *e) -{ - return (e->type == IR::BoolType); -} - -} // anonymous namespace - -InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory) - : EvalInstructionSelection(execAllocator, module, jsGenerator, iselFactory) - , qmlEngine(qmlEngine) - , _block(0) - , _codeStart(0) - , _codeNext(0) - , _codeEnd(0) - , _currentStatement(0) - , compilationUnit(new CompilationUnit) -{ - setUseTypeInference(false); -} - -InstructionSelection::~InstructionSelection() -{ -} - -void InstructionSelection::run(int functionIndex) -{ - IR::Function *function = irModule->functions[functionIndex]; - IR::BasicBlock *block = 0, *nextBlock = 0; - - QHash<IR::BasicBlock *, QVector<ptrdiff_t> > patches; - QHash<IR::BasicBlock *, ptrdiff_t> addrs; - - int codeSize = 4096; - uchar *codeStart = new uchar[codeSize]; - memset(codeStart, 0, codeSize); - uchar *codeNext = codeStart; - uchar *codeEnd = codeStart + codeSize; - - qSwap(_function, function); - qSwap(block, _block); - qSwap(nextBlock, _nextBlock); - qSwap(patches, _patches); - qSwap(addrs, _addrs); - qSwap(codeStart, _codeStart); - qSwap(codeNext, _codeNext); - qSwap(codeEnd, _codeEnd); - - IR::Optimizer opt(_function); - opt.run(qmlEngine, useTypeInference, /*peelLoops =*/ false); - if (opt.isInSSA()) { - static const bool doStackSlotAllocation = - qEnvironmentVariableIsEmpty("QV4_NO_INTERPRETER_STACK_SLOT_ALLOCATION"); - - if (doStackSlotAllocation) { - IR::AllocateStackSlots(opt.lifeTimeIntervals()).forFunction(_function); - } else { - opt.convertOutOfSSA(); - ConvertTemps().toStackSlots(_function); - } - opt.showMeTheCode(_function, "After stack slot allocation"); - } else { - ConvertTemps().toStackSlots(_function); - } - - BitVector removableJumps = opt.calculateOptionalJumps(); - qSwap(_removableJumps, removableJumps); - - IR::Stmt *cs = 0; - qSwap(_currentStatement, cs); - - int locals = frameSize(); - Q_ASSERT(locals >= 0); - - IR::BasicBlock *exceptionHandler = 0; - - Instruction::Push push; - push.value = quint32(locals); - addInstruction(push); - - currentLine = 0; - const QVector<IR::BasicBlock *> &basicBlocks = _function->basicBlocks(); - for (int i = 0, ei = basicBlocks.size(); i != ei; ++i) { - blockNeedsDebugInstruction = irModule->debugMode; - _block = basicBlocks[i]; - _nextBlock = (i < ei - 1) ? basicBlocks[i + 1] : 0; - _addrs.insert(_block, _codeNext - _codeStart); - - if (_block->catchBlock != exceptionHandler) { - Instruction::SetExceptionHandler set; - set.offset = 0; - if (_block->catchBlock) { - ptrdiff_t loc = addInstruction(set) + (((const char *)&set.offset) - ((const char *)&set)); - _patches[_block->catchBlock].append(loc); - } else { - addInstruction(set); - } - exceptionHandler = _block->catchBlock; - } else if (_block->catchBlock == nullptr && _block->index() != 0 && _block->in.isEmpty()) { - exceptionHandler = nullptr; - Instruction::SetExceptionHandler set; - set.offset = 0; - addInstruction(set); - } - - for (IR::Stmt *s : _block->statements()) { - _currentStatement = s; - - if (s->location.isValid()) { - if (s->location.startLine != currentLine) { - blockNeedsDebugInstruction = false; - currentLine = s->location.startLine; -#if QT_CONFIG(qml_debug) - if (irModule->debugMode) { - Instruction::Debug debug; - debug.lineNumber = currentLine; - addInstruction(debug); - } else { - Instruction::Line line; - line.lineNumber = currentLine; - addInstruction(line); - } -#endif - } - } - - visit(s); - } - } - - // TODO: patch stack size (the push instruction) - patchJumpAddresses(); - - codeRefs.insert(_function, squeezeCode()); - - qSwap(_currentStatement, cs); - qSwap(_removableJumps, removableJumps); - qSwap(_function, function); - qSwap(block, _block); - qSwap(nextBlock, _nextBlock); - qSwap(patches, _patches); - qSwap(addrs, _addrs); - qSwap(codeStart, _codeStart); - qSwap(codeNext, _codeNext); - qSwap(codeEnd, _codeEnd); - - delete[] codeStart; -} - -QQmlRefPointer<QV4::CompiledData::CompilationUnit> InstructionSelection::backendCompileStep() -{ - compilationUnit->codeRefs.resize(irModule->functions.size()); - int i = 0; - for (IR::Function *irFunction : qAsConst(irModule->functions)) - compilationUnit->codeRefs[i++] = codeRefs[irFunction]; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> result; - result.adopt(compilationUnit.take()); - return result; -} - -void InstructionSelection::callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) -{ - Instruction::CallValue call; - prepareCallArgs(args, call.argc); - call.callData = callDataStart(); - call.dest = getParam(value); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) -{ - if (kind == IR::Member::MemberOfQmlScopeObject) { - Instruction::CallScopeObjectProperty call; - call.base = getParam(base); - call.index = propertyIndex; - prepareCallArgs(args, call.argc); - call.callData = callDataStart(); - call.result = getResultParam(result); - addInstruction(call); - } else if (kind == IR::Member::MemberOfQmlContextObject) { - Instruction::CallContextObjectProperty call; - call.base = getParam(base); - call.index = propertyIndex; - prepareCallArgs(args, call.argc); - call.callData = callDataStart(); - call.result = getResultParam(result); - addInstruction(call); - } else { - Q_ASSERT(false); - } -} - -void InstructionSelection::callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, - IR::Expr *result) -{ - if (useFastLookups) { - Instruction::CallPropertyLookup call; - call.base = getParam(base); - call.lookupIndex = registerGetterLookup(name); - prepareCallArgs(args, call.argc); - call.callData = callDataStart(); - call.result = getResultParam(result); - addInstruction(call); - } else { - // call the property on the loaded base - Instruction::CallProperty call; - call.base = getParam(base); - call.name = registerString(name); - prepareCallArgs(args, call.argc); - call.callData = callDataStart(); - call.result = getResultParam(result); - addInstruction(call); - } -} - -void InstructionSelection::callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, - IR::Expr *result) -{ - // call the property on the loaded base - Instruction::CallElement call; - call.base = getParam(base); - call.index = getParam(index); - prepareCallArgs(args, call.argc); - call.callData = callDataStart(); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::convertType(IR::Expr *source, IR::Expr *target) -{ - // FIXME: do something more useful with this info - if (target->type & IR::NumberType && !(source->type & IR::NumberType)) - unop(IR::OpUPlus, source, target); - else - copyValue(source, target); -} - -void InstructionSelection::constructActivationProperty(IR::Name *func, - IR::ExprList *args, - IR::Expr *target) -{ - if (useFastLookups && func->global) { - Instruction::ConstructGlobalLookup call; - call.index = registerGlobalGetterLookup(*func->id); - prepareCallArgs(args, call.argc); - call.callData = callDataStart(); - call.result = getResultParam(target); - addInstruction(call); - return; - } - Instruction::CreateActivationProperty create; - create.name = registerString(*func->id); - prepareCallArgs(args, create.argc); - create.callData = callDataStart(); - create.result = getResultParam(target); - addInstruction(create); -} - -void InstructionSelection::constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *target) -{ - if (useFastLookups) { - Instruction::ConstructPropertyLookup call; - call.base = getParam(base); - call.index = registerGetterLookup(name); - prepareCallArgs(args, call.argc); - call.callData = callDataStart(); - call.result = getResultParam(target); - addInstruction(call); - return; - } - Instruction::CreateProperty create; - create.base = getParam(base); - create.name = registerString(name); - prepareCallArgs(args, create.argc); - create.callData = callDataStart(); - create.result = getResultParam(target); - addInstruction(create); -} - -void InstructionSelection::constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *target) -{ - Instruction::CreateValue create; - create.func = getParam(value); - prepareCallArgs(args, create.argc); - create.callData = callDataStart(); - create.result = getResultParam(target); - addInstruction(create); -} - -void InstructionSelection::loadThisObject(IR::Expr *e) -{ - Instruction::LoadThis load; - load.result = getResultParam(e); - addInstruction(load); -} - -void InstructionSelection::loadQmlContext(IR::Expr *e) -{ - Instruction::LoadQmlContext load; - load.result = getResultParam(e); - addInstruction(load); -} - -void InstructionSelection::loadQmlImportedScripts(IR::Expr *e) -{ - Instruction::LoadQmlImportedScripts load; - load.result = getResultParam(e); - addInstruction(load); -} - -void InstructionSelection::loadQmlSingleton(const QString &name, IR::Expr *e) -{ - Instruction::LoadQmlSingleton load; - load.result = getResultParam(e); - load.name = registerString(name); - addInstruction(load); -} - -void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Expr *e) -{ - Q_ASSERT(sourceConst); - - Instruction::MoveConst move; - move.source = convertToValue(sourceConst).asReturnedValue(); - move.result = getResultParam(e); - addInstruction(move); -} - -void InstructionSelection::loadString(const QString &str, IR::Expr *target) -{ - Instruction::LoadRuntimeString load; - load.stringId = registerString(str); - load.result = getResultParam(target); - addInstruction(load); -} - -void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target) -{ - Instruction::LoadRegExp load; - load.regExpId = registerRegExp(sourceRegexp); - load.result = getResultParam(target); - addInstruction(load); -} - -void InstructionSelection::getActivationProperty(const IR::Name *name, IR::Expr *target) -{ - if (useFastLookups && name->global) { - Instruction::GetGlobalLookup load; - load.index = registerGlobalGetterLookup(*name->id); - load.result = getResultParam(target); - addInstruction(load); - return; - } - Instruction::LoadName load; - load.name = registerString(*name->id); - load.result = getResultParam(target); - addInstruction(load); -} - -void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName) -{ - Instruction::StoreName store; - store.source = getParam(source); - store.name = registerString(targetName); - addInstruction(store); -} - -void InstructionSelection::initClosure(IR::Closure *closure, IR::Expr *target) -{ - int id = closure->value; - Instruction::LoadClosure load; - load.value = id; - load.result = getResultParam(target); - addInstruction(load); -} - -void InstructionSelection::getProperty(IR::Expr *base, const QString &name, IR::Expr *target) -{ - if (useFastLookups) { - Instruction::GetLookup load; - load.base = getParam(base); - load.index = registerGetterLookup(name); - load.result = getResultParam(target); - addInstruction(load); - return; - } - Instruction::LoadProperty load; - load.base = getParam(base); - load.name = registerString(name); - load.result = getResultParam(target); - addInstruction(load); -} - -void InstructionSelection::setProperty(IR::Expr *source, IR::Expr *targetBase, - const QString &targetName) -{ - if (useFastLookups) { - Instruction::SetLookup store; - store.base = getParam(targetBase); - store.index = registerSetterLookup(targetName); - store.source = getParam(source); - addInstruction(store); - return; - } - Instruction::StoreProperty store; - store.base = getParam(targetBase); - store.name = registerString(targetName); - store.source = getParam(source); - addInstruction(store); -} - -void InstructionSelection::setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) -{ - if (kind == IR::Member::MemberOfQmlScopeObject) { - Instruction::StoreScopeObjectProperty store; - store.base = getParam(targetBase); - store.propertyIndex = propertyIndex; - store.source = getParam(source); - addInstruction(store); - } else if (kind == IR::Member::MemberOfQmlContextObject) { - Instruction::StoreContextObjectProperty store; - store.base = getParam(targetBase); - store.propertyIndex = propertyIndex; - store.source = getParam(source); - addInstruction(store); - } else { - Q_ASSERT(false); - } -} - -void InstructionSelection::setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) -{ - Instruction::StoreQObjectProperty store; - store.base = getParam(targetBase); - store.propertyIndex = propertyIndex; - store.source = getParam(source); - addInstruction(store); -} - -void InstructionSelection::getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target) -{ - if (kind == IR::Member::MemberOfQmlScopeObject) { - Instruction::LoadScopeObjectProperty load; - load.base = getParam(source); - load.propertyIndex = index; - load.captureRequired = captureRequired; - load.result = getResultParam(target); - addInstruction(load); - } else if (kind == IR::Member::MemberOfQmlContextObject) { - Instruction::LoadContextObjectProperty load; - load.base = getParam(source); - load.propertyIndex = index; - load.captureRequired = captureRequired; - load.result = getResultParam(target); - addInstruction(load); - } else if (kind == IR::Member::MemberOfIdObjectsArray) { - Instruction::LoadIdObject load; - load.base = getParam(source); - load.index = index; - load.result = getResultParam(target); - addInstruction(load); - } else { - Q_ASSERT(false); - } -} - -void InstructionSelection::getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingletonProperty, int attachedPropertiesId, IR::Expr *target) -{ - if (attachedPropertiesId != 0) { - Instruction::LoadAttachedQObjectProperty load; - load.propertyIndex = propertyIndex; - load.result = getResultParam(target); - load.attachedPropertiesId = attachedPropertiesId; - addInstruction(load); - } else if (isSingletonProperty) { - Instruction::LoadSingletonQObjectProperty load; - load.base = getParam(base); - load.propertyIndex = propertyIndex; - load.result = getResultParam(target); - load.captureRequired = captureRequired; - addInstruction(load); - } else { - Instruction::LoadQObjectProperty load; - load.base = getParam(base); - load.propertyIndex = propertyIndex; - load.result = getResultParam(target); - load.captureRequired = captureRequired; - addInstruction(load); - } -} - -void InstructionSelection::getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) -{ - if (0 && useFastLookups) { - Instruction::LoadElementLookup load; - load.lookup = registerIndexedGetterLookup(); - load.base = getParam(base); - load.index = getParam(index); - load.result = getResultParam(target); - addInstruction(load); - return; - } - Instruction::LoadElement load; - load.base = getParam(base); - load.index = getParam(index); - load.result = getResultParam(target); - addInstruction(load); -} - -void InstructionSelection::setElement(IR::Expr *source, IR::Expr *targetBase, - IR::Expr *targetIndex) -{ - if (0 && useFastLookups) { - Instruction::StoreElementLookup store; - store.lookup = registerIndexedSetterLookup(); - store.base = getParam(targetBase); - store.index = getParam(targetIndex); - store.source = getParam(source); - addInstruction(store); - return; - } - Instruction::StoreElement store; - store.base = getParam(targetBase); - store.index = getParam(targetIndex); - store.source = getParam(source); - addInstruction(store); -} - -void InstructionSelection::copyValue(IR::Expr *source, IR::Expr *target) -{ - Instruction::Move move; - move.source = getParam(source); - move.result = getResultParam(target); - if (move.source != move.result) - addInstruction(move); -} - -void InstructionSelection::swapValues(IR::Expr *source, IR::Expr *target) -{ - Instruction::SwapTemps swap; - swap.left = getParam(source); - swap.right = getParam(target); - addInstruction(swap); -} - -void InstructionSelection::unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target) -{ - switch (oper) { - case IR::OpIfTrue: - Q_ASSERT(!"unreachable"); break; - case IR::OpNot: { - // ### enabling this fails in some cases, where apparently the value is not a bool at runtime - if (0 && isBoolType(source)) { - Instruction::UNotBool unot; - unot.source = getParam(source); - unot.result = getResultParam(target); - addInstruction(unot); - return; - } - Instruction::UNot unot; - unot.source = getParam(source); - unot.result = getResultParam(target); - addInstruction(unot); - return; - } - case IR::OpUMinus: { - Instruction::UMinus uminus; - uminus.source = getParam(source); - uminus.result = getResultParam(target); - addInstruction(uminus); - return; - } - case IR::OpUPlus: { - if (isNumberType(source)) { - // use a move - Instruction::Move move; - move.source = getParam(source); - move.result = getResultParam(target); - if (move.source != move.result) - addInstruction(move); - return; - } - Instruction::UPlus uplus; - uplus.source = getParam(source); - uplus.result = getResultParam(target); - addInstruction(uplus); - return; - } - case IR::OpCompl: { - // ### enabling this fails in some cases, where apparently the value is not a int at runtime - if (0 && isIntegerType(source)) { - Instruction::UComplInt unot; - unot.source = getParam(source); - unot.result = getResultParam(target); - addInstruction(unot); - return; - } - Instruction::UCompl ucompl; - ucompl.source = getParam(source); - ucompl.result = getResultParam(target); - addInstruction(ucompl); - return; - } - case IR::OpIncrement: { - Instruction::Increment inc; - inc.source = getParam(source); - inc.result = getResultParam(target); - addInstruction(inc); - return; - } - case IR::OpDecrement: { - Instruction::Decrement dec; - dec.source = getParam(source); - dec.result = getResultParam(target); - addInstruction(dec); - return; - } - default: break; - } // switch - - Q_ASSERT(!"unreachable"); -} - -void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) -{ - binopHelper(oper, leftSource, rightSource, target); -} - -Param InstructionSelection::binopHelper(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) -{ - if (oper == IR::OpAdd) { - Instruction::Add add; - add.lhs = getParam(leftSource); - add.rhs = getParam(rightSource); - add.result = getResultParam(target); - addInstruction(add); - return add.result; - } - if (oper == IR::OpSub) { - Instruction::Sub sub; - sub.lhs = getParam(leftSource); - sub.rhs = getParam(rightSource); - sub.result = getResultParam(target); - addInstruction(sub); - return sub.result; - } - if (oper == IR::OpMul) { - Instruction::Mul mul; - mul.lhs = getParam(leftSource); - mul.rhs = getParam(rightSource); - mul.result = getResultParam(target); - addInstruction(mul); - return mul.result; - } - if (oper == IR::OpBitAnd) { - if (leftSource->asConst()) - qSwap(leftSource, rightSource); - if (IR::Const *c = rightSource->asConst()) { - Instruction::BitAndConst bitAnd; - bitAnd.lhs = getParam(leftSource); - bitAnd.rhs = convertToValue(c).Value::toInt32(); - bitAnd.result = getResultParam(target); - addInstruction(bitAnd); - return bitAnd.result; - } - Instruction::BitAnd bitAnd; - bitAnd.lhs = getParam(leftSource); - bitAnd.rhs = getParam(rightSource); - bitAnd.result = getResultParam(target); - addInstruction(bitAnd); - return bitAnd.result; - } - if (oper == IR::OpBitOr) { - if (leftSource->asConst()) - qSwap(leftSource, rightSource); - if (IR::Const *c = rightSource->asConst()) { - Instruction::BitOrConst bitOr; - bitOr.lhs = getParam(leftSource); - bitOr.rhs = convertToValue(c).Value::toInt32(); - bitOr.result = getResultParam(target); - addInstruction(bitOr); - return bitOr.result; - } - Instruction::BitOr bitOr; - bitOr.lhs = getParam(leftSource); - bitOr.rhs = getParam(rightSource); - bitOr.result = getResultParam(target); - addInstruction(bitOr); - return bitOr.result; - } - if (oper == IR::OpBitXor) { - if (leftSource->asConst()) - qSwap(leftSource, rightSource); - if (IR::Const *c = rightSource->asConst()) { - Instruction::BitXorConst bitXor; - bitXor.lhs = getParam(leftSource); - bitXor.rhs = convertToValue(c).Value::toInt32(); - bitXor.result = getResultParam(target); - addInstruction(bitXor); - return bitXor.result; - } - Instruction::BitXor bitXor; - bitXor.lhs = getParam(leftSource); - bitXor.rhs = getParam(rightSource); - bitXor.result = getResultParam(target); - addInstruction(bitXor); - return bitXor.result; - } - if (oper == IR::OpRShift) { - if (IR::Const *c = rightSource->asConst()) { - Instruction::ShrConst shr; - shr.lhs = getParam(leftSource); - shr.rhs = convertToValue(c).Value::toInt32() & 0x1f; - shr.result = getResultParam(target); - addInstruction(shr); - return shr.result; - } - Instruction::Shr shr; - shr.lhs = getParam(leftSource); - shr.rhs = getParam(rightSource); - shr.result = getResultParam(target); - addInstruction(shr); - return shr.result; - } - if (oper == IR::OpLShift) { - if (IR::Const *c = rightSource->asConst()) { - Instruction::ShlConst shl; - shl.lhs = getParam(leftSource); - shl.rhs = convertToValue(c).Value::toInt32() & 0x1f; - shl.result = getResultParam(target); - addInstruction(shl); - return shl.result; - } - Instruction::Shl shl; - shl.lhs = getParam(leftSource); - shl.rhs = getParam(rightSource); - shl.result = getResultParam(target); - addInstruction(shl); - return shl.result; - } - - if (oper == IR::OpInstanceof || oper == IR::OpIn || oper == IR::OpAdd) { - Instruction::BinopContext binop; - if (oper == IR::OpInstanceof) - binop.alu = QV4::Runtime::instanceof; - else if (oper == IR::OpIn) - binop.alu = QV4::Runtime::in; - else - binop.alu = QV4::Runtime::add; - binop.lhs = getParam(leftSource); - binop.rhs = getParam(rightSource); - binop.result = getResultParam(target); - Q_ASSERT(binop.alu != QV4::Runtime::InvalidRuntimeMethod); - addInstruction(binop); - return binop.result; - } else { - auto binopFunc = aluOpFunction(oper); - Q_ASSERT(binopFunc != QV4::Runtime::InvalidRuntimeMethod); - Instruction::Binop binop; - binop.alu = binopFunc; - binop.lhs = getParam(leftSource); - binop.rhs = getParam(rightSource); - binop.result = getResultParam(target); - addInstruction(binop); - return binop.result; - } -} - -void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint32 *args) -{ - int argLocation = outgoingArgumentTempStart(); - argc = 0; - if (args) - *args = argLocation; - if (e) { - // We need to move all the temps into the function arg array - Q_ASSERT(argLocation >= 0); - while (e) { - if (IR::Const *c = e->expr->asConst()) { - Instruction::MoveConst move; - move.source = convertToValue(c).asReturnedValue(); - move.result = Param::createTemp(argLocation); - addInstruction(move); - } else { - Instruction::Move move; - move.source = getParam(e->expr); - move.result = Param::createTemp(argLocation); - addInstruction(move); - } - ++argLocation; - ++argc; - e = e->next; - } - } -} - -void InstructionSelection::addDebugInstruction() -{ -#if QT_CONFIG(qml_debug) - if (blockNeedsDebugInstruction) { - Instruction::Debug debug; - debug.lineNumber = -int(currentLine); - addInstruction(debug); - } -#endif -} - -void InstructionSelection::visitJump(IR::Jump *s) -{ - if (s->target == _nextBlock) - return; - if (_removableJumps.at(_block->index())) - return; - - addDebugInstruction(); - - Instruction::Jump jump; - jump.offset = 0; - ptrdiff_t loc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); - - _patches[s->target].append(loc); -} - -void InstructionSelection::visitCJump(IR::CJump *s) -{ - addDebugInstruction(); - - Param condition; - if (IR::Temp *t = s->cond->asTemp()) { - condition = getResultParam(t); - } else if (IR::Binop *b = s->cond->asBinop()) { - condition = binopHelper(b->op, b->left, b->right, /*target*/0); - } else { - Q_UNIMPLEMENTED(); - } - - if (s->iftrue == _nextBlock) { - Instruction::JumpNe jump; - jump.offset = 0; - jump.condition = condition; - ptrdiff_t falseLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); - _patches[s->iffalse].append(falseLoc); - } else { - Instruction::JumpEq jump; - jump.offset = 0; - jump.condition = condition; - ptrdiff_t trueLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); - _patches[s->iftrue].append(trueLoc); - - if (s->iffalse != _nextBlock) { - Instruction::Jump jump; - jump.offset = 0; - ptrdiff_t falseLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); - _patches[s->iffalse].append(falseLoc); - } - } -} - -void InstructionSelection::visitRet(IR::Ret *s) -{ - // this is required so stepOut will always be guaranteed to stop in every stack frame - addDebugInstruction(); - - Instruction::Ret ret; - ret.result = getParam(s->expr); - addInstruction(ret); -} - -void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result) -{ - if (useFastLookups && func->global) { - Instruction::CallGlobalLookup call; - call.index = registerGlobalGetterLookup(*func->id); - prepareCallArgs(args, call.argc); - call.callData = callDataStart(); - call.result = getResultParam(result); - addInstruction(call); - return; - } - Instruction::CallActivationProperty call; - call.name = registerString(*func->id); - prepareCallArgs(args, call.argc); - call.callData = callDataStart(); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinTypeofQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::Expr *result) -{ - if (kind == IR::Member::MemberOfQmlScopeObject) { - Instruction::CallBuiltinTypeofScopeObjectProperty call; - call.base = getParam(base); - call.index = propertyIndex; - call.result = getResultParam(result); - addInstruction(call); - } else if (kind == IR::Member::MemberOfQmlContextObject) { - Instruction::CallBuiltinTypeofContextObjectProperty call; - call.base = getParam(base); - call.index = propertyIndex; - call.result = getResultParam(result); - addInstruction(call); - } else { - Q_UNREACHABLE(); - } -} - -void InstructionSelection::callBuiltinTypeofMember(IR::Expr *base, const QString &name, - IR::Expr *result) -{ - Instruction::CallBuiltinTypeofMember call; - call.base = getParam(base); - call.member = registerString(name); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, - IR::Expr *result) -{ - Instruction::CallBuiltinTypeofSubscript call; - call.base = getParam(base); - call.index = getParam(index); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Expr *result) -{ - Instruction::CallBuiltinTypeofName call; - call.name = registerString(name); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result) -{ - Instruction::CallBuiltinTypeofValue call; - call.value = getParam(value); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result) -{ - Instruction::CallBuiltinDeleteMember call; - call.base = getParam(base); - call.member = registerString(name); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index, - IR::Expr *result) -{ - Instruction::CallBuiltinDeleteSubscript call; - call.base = getParam(base); - call.index = getParam(index); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Expr *result) -{ - Instruction::CallBuiltinDeleteName call; - call.name = registerString(name); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinDeleteValue(IR::Expr *result) -{ - Instruction::MoveConst move; - move.source = QV4::Encode(false); - move.result = getResultParam(result); - addInstruction(move); -} - -void InstructionSelection::callBuiltinThrow(IR::Expr *arg) -{ - Instruction::CallBuiltinThrow call; - call.arg = getParam(arg); - addInstruction(call); -} - -void InstructionSelection::callBuiltinReThrow() -{ - if (_block->catchBlock) { - // jump to exception handler - Instruction::Jump jump; - jump.offset = 0; - ptrdiff_t loc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); - - _patches[_block->catchBlock].append(loc); - } else { - Instruction::Ret ret; - int idx = jsUnitGenerator()->registerConstant(QV4::Encode::undefined()); - ret.result = Param::createConstant(idx); - addInstruction(ret); - } -} - -void InstructionSelection::callBuiltinUnwindException(IR::Expr *result) -{ - Instruction::CallBuiltinUnwindException call; - call.result = getResultParam(result); - addInstruction(call); -} - - -void InstructionSelection::callBuiltinPushCatchScope(const QString &exceptionName) -{ - Instruction::CallBuiltinPushCatchScope call; - call.name = registerString(exceptionName); - addInstruction(call); -} - -void InstructionSelection::callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result) -{ - Instruction::CallBuiltinForeachIteratorObject call; - call.arg = getParam(arg); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result) -{ - Instruction::CallBuiltinForeachNextPropertyName call; - call.arg = getParam(arg); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinPushWithScope(IR::Expr *arg) -{ - Instruction::CallBuiltinPushScope call; - call.arg = getParam(arg); - addInstruction(call); -} - -void InstructionSelection::callBuiltinPopScope() -{ - QT_WARNING_PUSH - QT_WARNING_DISABLE_GCC("-Wuninitialized") - Instruction::CallBuiltinPopScope call; - addInstruction(call); - QT_WARNING_POP -} - -void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) -{ - Instruction::CallBuiltinDeclareVar call; - call.isDeletable = deletable; - call.varName = registerString(name); - addInstruction(call); -} - -void InstructionSelection::callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args) -{ - Instruction::CallBuiltinDefineArray call; - prepareCallArgs(args, call.argc, &call.args); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray) -{ - int argLocation = outgoingArgumentTempStart(); - - const int classId = registerJSClass(keyValuePairCount, keyValuePairs); - - // Process key/value pairs first - IR::ExprList *it = keyValuePairs; - for (int i = 0; i < keyValuePairCount; ++i, it = it->next) { - // Skip name - it = it->next; - - bool isData = it->expr->asConst()->value; - it = it->next; - - if (IR::Const *c = it->expr->asConst()) { - Instruction::MoveConst move; - move.source = convertToValue(c).asReturnedValue(); - move.result = Param::createTemp(argLocation); - addInstruction(move); - } else { - Instruction::Move move; - move.source = getParam(it->expr); - move.result = Param::createTemp(argLocation); - addInstruction(move); - } - ++argLocation; - - if (!isData) { - it = it->next; - - Instruction::Move move; - move.source = getParam(it->expr); - move.result = Param::createTemp(argLocation); - addInstruction(move); - ++argLocation; - } - } - - // Process array values - uint arrayValueCount = 0; - it = arrayEntries; - while (it) { - IR::Const *index = it->expr->asConst(); - it = it->next; - - bool isData = it->expr->asConst()->value; - it = it->next; - - if (!isData) { - it = it->next; // getter - it = it->next; // setter - continue; - } - - ++arrayValueCount; - - Instruction::MoveConst indexMove; - indexMove.source = convertToValue(index).asReturnedValue(); - indexMove.result = Param::createTemp(argLocation); - addInstruction(indexMove); - ++argLocation; - - Instruction::Move move; - move.source = getParam(it->expr); - move.result = Param::createTemp(argLocation); - addInstruction(move); - ++argLocation; - it = it->next; - } - - // Process array getter/setter pairs - uint arrayGetterSetterCount = 0; - it = arrayEntries; - while (it) { - IR::Const *index = it->expr->asConst(); - it = it->next; - - bool isData = it->expr->asConst()->value; - it = it->next; - - if (isData) { - it = it->next; // value - continue; - } - - ++arrayGetterSetterCount; - - Instruction::MoveConst indexMove; - indexMove.source = convertToValue(index).asReturnedValue(); - indexMove.result = Param::createTemp(argLocation); - addInstruction(indexMove); - ++argLocation; - - // getter - Instruction::Move moveGetter; - moveGetter.source = getParam(it->expr); - moveGetter.result = Param::createTemp(argLocation); - addInstruction(moveGetter); - ++argLocation; - it = it->next; - - // setter - Instruction::Move moveSetter; - moveSetter.source = getParam(it->expr); - moveSetter.result = Param::createTemp(argLocation); - addInstruction(moveSetter); - ++argLocation; - it = it->next; - } - - Instruction::CallBuiltinDefineObjectLiteral call; - call.internalClassId = classId; - call.arrayValueCount = arrayValueCount; - call.arrayGetterSetterCountAndFlags = arrayGetterSetterCount | (needSparseArray << 30); - call.args = outgoingArgumentTempStart(); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinSetupArgumentObject(IR::Expr *result) -{ - Instruction::CallBuiltinSetupArgumentsObject call; - call.result = getResultParam(result); - addInstruction(call); -} - - -void QV4::Moth::InstructionSelection::callBuiltinConvertThisToObject() -{ - QT_WARNING_PUSH - QT_WARNING_DISABLE_GCC("-Wuninitialized") - Instruction::CallBuiltinConvertThisToObject call; - addInstruction(call); - QT_WARNING_POP -} - -ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr) -{ - instr.common.instructionType = type; - - int instructionSize = Instr::size(type); - if (_codeEnd - _codeNext < instructionSize) { - int currSize = _codeEnd - _codeStart; - uchar *newCode = new uchar[currSize * 2]; - ::memset(newCode + currSize, 0, currSize); - ::memcpy(newCode, _codeStart, currSize); - _codeNext = _codeNext - _codeStart + newCode; - delete[] _codeStart; - _codeStart = newCode; - _codeEnd = _codeStart + currSize * 2; - } - - ::memcpy(_codeNext, reinterpret_cast<const char *>(&instr), instructionSize); - ptrdiff_t ptrOffset = _codeNext - _codeStart; - _codeNext += instructionSize; - - return ptrOffset; -} - -void InstructionSelection::patchJumpAddresses() -{ - typedef QHash<IR::BasicBlock *, QVector<ptrdiff_t> >::ConstIterator PatchIt; - for (PatchIt i = _patches.cbegin(), ei = _patches.cend(); i != ei; ++i) { - Q_ASSERT(_addrs.contains(i.key())); - ptrdiff_t target = _addrs.value(i.key()); - - const QVector<ptrdiff_t> &patchList = i.value(); - for (int ii = 0, eii = patchList.count(); ii < eii; ++ii) { - ptrdiff_t patch = patchList.at(ii); - - *((ptrdiff_t *)(_codeStart + patch)) = target - patch; - } - } - - _patches.clear(); - _addrs.clear(); -} - -QByteArray InstructionSelection::squeezeCode() const -{ - int codeSize = _codeNext - _codeStart; - QByteArray squeezed; - squeezed.resize(codeSize); - ::memcpy(squeezed.data(), _codeStart, codeSize); - return squeezed; -} - -Param InstructionSelection::getParam(IR::Expr *e) { - Q_ASSERT(e); - - if (IR::Const *c = e->asConst()) { - int idx = jsUnitGenerator()->registerConstant(convertToValue(c).asReturnedValue()); - return Param::createConstant(idx); - } else if (IR::Temp *t = e->asTemp()) { - switch (t->kind) { - case IR::Temp::StackSlot: - return Param::createTemp(t->index); - default: - Q_UNREACHABLE(); - return Param(); - } - } else if (IR::ArgLocal *al = e->asArgLocal()) { - switch (al->kind) { - case IR::ArgLocal::Formal: - case IR::ArgLocal::ScopedFormal: return Param::createArgument(al->index, al->scope); - case IR::ArgLocal::Local: return Param::createLocal(al->index); - case IR::ArgLocal::ScopedLocal: return Param::createScopedLocal(al->index, al->scope); - default: - Q_UNREACHABLE(); - return Param(); - } - } else { - Q_UNIMPLEMENTED(); - return Param(); - } -} - - -CompilationUnit::~CompilationUnit() -{ -} - -#if !defined(V4_BOOTSTRAP) - -void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine) -{ - runtimeFunctions.resize(data->functionTableSize); - runtimeFunctions.fill(0); - for (int i = 0 ;i < runtimeFunctions.size(); ++i) { - const QV4::CompiledData::Function *compiledFunction = data->functionAt(i); - - QV4::Function *runtimeFunction = new QV4::Function(engine, this, compiledFunction, &VME::exec); - runtimeFunction->codeData = reinterpret_cast<const uchar *>(codeRefs.at(i).constData()); - runtimeFunctions[i] = runtimeFunction; - } -} - -bool CompilationUnit::memoryMapCode(QString *errorString) -{ - Q_UNUSED(errorString); - codeRefs.resize(data->functionTableSize); - - const char *basePtr = reinterpret_cast<const char *>(data); - - for (uint i = 0; i < data->functionTableSize; ++i) { - const CompiledData::Function *compiledFunction = data->functionAt(i); - const char *codePtr = const_cast<const char *>(reinterpret_cast<const char *>(basePtr + compiledFunction->codeOffset)); - QByteArray code = QByteArray::fromRawData(codePtr, compiledFunction->codeSize); - codeRefs[i] = code; - } - - return true; -} - -#endif // V4_BOOTSTRAP - -void CompilationUnit::prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) -{ - const int codeAlignment = 16; - quint64 offset = WTF::roundUpToMultipleOf(codeAlignment, unit->unitSize); - Q_ASSERT(int(unit->functionTableSize) == codeRefs.size()); - for (int i = 0; i < codeRefs.size(); ++i) { - CompiledData::Function *compiledFunction = const_cast<CompiledData::Function *>(unit->functionAt(i)); - compiledFunction->codeOffset = offset; - compiledFunction->codeSize = codeRefs.at(i).size(); - offset = WTF::roundUpToMultipleOf(codeAlignment, offset + compiledFunction->codeSize); - } -} - -bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) -{ - Q_ASSERT(device->pos() == unit->unitSize); - Q_ASSERT(device->atEnd()); - Q_ASSERT(int(unit->functionTableSize) == codeRefs.size()); - - QByteArray padding; - - for (int i = 0; i < codeRefs.size(); ++i) { - const CompiledData::Function *compiledFunction = unit->functionAt(i); - - if (device->pos() > qint64(compiledFunction->codeOffset)) { - *errorString = QStringLiteral("Invalid state of cache file to write."); - return false; - } - - const quint64 paddingSize = compiledFunction->codeOffset - device->pos(); - padding.fill(0, paddingSize); - qint64 written = device->write(padding); - if (written != padding.size()) { - *errorString = device->errorString(); - return false; - } - - QByteArray code = codeRefs.at(i); - - written = device->write(code.constData(), compiledFunction->codeSize); - if (written != qint64(compiledFunction->codeSize)) { - *errorString = device->errorString(); - return false; - } - } - return true; -} - -QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory::createUnitForLoading() -{ - QQmlRefPointer<CompiledData::CompilationUnit> result; - result.adopt(new Moth::CompilationUnit); - return result; -} diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h deleted file mode 100644 index db49177783..0000000000 --- a/src/qml/compiler/qv4isel_moth_p.h +++ /dev/null @@ -1,240 +0,0 @@ -/**************************************************************************** -** -** 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 QV4ISEL_MOTH_P_H -#define QV4ISEL_MOTH_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qv4global_p.h> -#include <private/qv4isel_p.h> -#include <private/qv4isel_util_p.h> -#include <private/qv4util_p.h> -#include <private/qv4jsir_p.h> -#include <private/qv4value_p.h> -#include "qv4instr_moth_p.h" - -#if !defined(V4_BOOTSTRAP) -QT_REQUIRE_CONFIG(qml_interpreter); -#endif - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace Moth { - -struct CompilationUnit : public QV4::CompiledData::CompilationUnit -{ - virtual ~CompilationUnit(); -#if !defined(V4_BOOTSTRAP) - void linkBackendToEngine(QV4::ExecutionEngine *engine) override; - bool memoryMapCode(QString *errorString) override; -#endif - void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) override; - bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) override; - - QVector<QByteArray> codeRefs; - -}; - -class Q_QML_EXPORT InstructionSelection: - public IR::IRDecoder, - public EvalInstructionSelection -{ -public: - InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory); - ~InstructionSelection(); - - void run(int functionIndex) override; - -protected: - QQmlRefPointer<CompiledData::CompilationUnit> backendCompileStep() override; - - void visitJump(IR::Jump *) override; - void visitCJump(IR::CJump *) override; - void visitRet(IR::Ret *) override; - - void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result) override; - void callBuiltinTypeofQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::Expr *result) override; - void callBuiltinTypeofMember(IR::Expr *base, const QString &name, IR::Expr *result) override; - void callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) override; - void callBuiltinTypeofName(const QString &name, IR::Expr *result) override; - void callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result) override; - void callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result) override; - void callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) override; - void callBuiltinDeleteName(const QString &name, IR::Expr *result) override; - void callBuiltinDeleteValue(IR::Expr *result) override; - void callBuiltinThrow(IR::Expr *arg) override; - void callBuiltinReThrow() override; - void callBuiltinUnwindException(IR::Expr *) override; - void callBuiltinPushCatchScope(const QString &exceptionName) override; - void callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result) override; - void callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result) override; - void callBuiltinPushWithScope(IR::Expr *arg) override; - void callBuiltinPopScope() override; - void callBuiltinDeclareVar(bool deletable, const QString &name) override; - void callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args) override; - void callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray) override; - void callBuiltinSetupArgumentObject(IR::Expr *result) override; - void callBuiltinConvertThisToObject() override; - void callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) override; - void callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) override; - void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) override; - void callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, IR::Expr *result) override; - void convertType(IR::Expr *source, IR::Expr *target) override; - void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Expr *result) override; - void constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) override; - void constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) override; - void loadThisObject(IR::Expr *e) override; - void loadQmlContext(IR::Expr *e) override; - void loadQmlImportedScripts(IR::Expr *e) override; - void loadQmlSingleton(const QString &name, IR::Expr *e) override; - void loadConst(IR::Const *sourceConst, IR::Expr *e) override; - void loadString(const QString &str, IR::Expr *target) override; - void loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target) override; - void getActivationProperty(const IR::Name *name, IR::Expr *target) override; - void setActivationProperty(IR::Expr *source, const QString &targetName) override; - void initClosure(IR::Closure *closure, IR::Expr *target) override; - void getProperty(IR::Expr *base, const QString &name, IR::Expr *target) override; - void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName) override; - void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) override; - void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) override; - void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target) override; - void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target) override; - void getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) override; - void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) override; - void copyValue(IR::Expr *source, IR::Expr *target) override; - void swapValues(IR::Expr *source, IR::Expr *target) override; - void unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target) override; - void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) override; - -private: - Param binopHelper(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target); - - struct Instruction { -#define MOTH_INSTR_DATA_TYPEDEF(I, FMT) typedef InstrData<Instr::I> I; - FOR_EACH_MOTH_INSTR(MOTH_INSTR_DATA_TYPEDEF) -#undef MOTH_INSTR_DATA_TYPEDEF - private: - Instruction(); - }; - - Param getParam(IR::Expr *e); - - Param getResultParam(IR::Expr *result) - { - if (result) - return getParam(result); - else - return Param::createTemp(scratchTempIndex()); - } - - void simpleMove(IR::Move *); - void prepareCallArgs(IR::ExprList *, quint32 &, quint32 * = 0); - - int scratchTempIndex() const { return _function->tempCount; } - int callDataStart() const { return scratchTempIndex() + 1; } - int outgoingArgumentTempStart() const { return callDataStart() + offsetof(QV4::CallData, args)/sizeof(QV4::Value); } - int frameSize() const { return outgoingArgumentTempStart() + _function->maxNumberOfArguments; } - - template <int Instr> - inline ptrdiff_t addInstruction(const InstrData<Instr> &data); - inline void addDebugInstruction(); - - ptrdiff_t addInstructionHelper(Instr::Type type, Instr &instr); - void patchJumpAddresses(); - QByteArray squeezeCode() const; - - QQmlEnginePrivate *qmlEngine; - - bool blockNeedsDebugInstruction; - uint currentLine; - IR::BasicBlock *_block; - IR::BasicBlock *_nextBlock; - - QHash<IR::BasicBlock *, QVector<ptrdiff_t> > _patches; - QHash<IR::BasicBlock *, ptrdiff_t> _addrs; - - uchar *_codeStart; - uchar *_codeNext; - uchar *_codeEnd; - - BitVector _removableJumps; - IR::Stmt *_currentStatement; - - QScopedPointer<CompilationUnit> compilationUnit; - QHash<IR::Function *, QByteArray> codeRefs; -}; - -class Q_QML_EXPORT ISelFactory: public EvalISelFactory -{ -public: - ISelFactory() : EvalISelFactory(QStringLiteral("moth")) {} - virtual ~ISelFactory() {} - EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) override final - { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator, this); } - bool jitCompileRegexps() const override final - { return false; } - QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading() override; - -}; - -template<int InstrT> -ptrdiff_t InstructionSelection::addInstruction(const InstrData<InstrT> &data) -{ - Instr genericInstr; - InstrMeta<InstrT>::setDataNoCommon(genericInstr, data); - return addInstructionHelper(static_cast<Instr::Type>(InstrT), genericInstr); -} - -} // namespace Moth -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4ISEL_MOTH_P_H diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp deleted file mode 100644 index efcfb9bd77..0000000000 --- a/src/qml/compiler/qv4isel_p.cpp +++ /dev/null @@ -1,446 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#include <QtCore/QDebug> -#include <QtCore/QBuffer> -#include "qv4jsir_p.h" -#include "qv4isel_p.h" -#include "qv4isel_util_p.h" -#include <private/qv4value_p.h> -#ifndef V4_BOOTSTRAP -#include <private/qqmlpropertycache_p.h> -#endif - -#include <QString> - -using namespace QV4; -using namespace QV4::IR; - -EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory) - : useFastLookups(true) - , useTypeInference(true) - , executableAllocator(execAllocator) - , irModule(module) -{ - if (!jsGenerator) { - jsGenerator = new QV4::Compiler::JSUnitGenerator(module); - ownJSGenerator.reset(jsGenerator); - } - this->jsGenerator = jsGenerator; -#ifndef V4_BOOTSTRAP - Q_ASSERT(execAllocator); -#endif - Q_ASSERT(module); - jsGenerator->codeGeneratorName = iselFactory->codeGeneratorName; -} - -EvalInstructionSelection::~EvalInstructionSelection() -{} - -EvalISelFactory::~EvalISelFactory() -{} - -QQmlRefPointer<CompiledData::CompilationUnit> EvalInstructionSelection::compile(bool generateUnitData) -{ - for (int i = 0; i < irModule->functions.size(); ++i) - run(i); - - QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = backendCompileStep(); - if (generateUnitData) - unit->data = jsGenerator->generateUnit(); - return unit; -} - -void IRDecoder::visitMove(IR::Move *s) -{ - if (IR::Name *n = s->target->asName()) { - if (s->source->asTemp() || s->source->asConst() || s->source->asArgLocal()) { - setActivationProperty(s->source, *n->id); - return; - } - } else if (s->target->asTemp() || s->target->asArgLocal()) { - if (IR::Name *n = s->source->asName()) { - if (n->id && *n->id == QLatin1String("this")) // TODO: `this' should be a builtin. - loadThisObject(s->target); - else if (n->builtin == IR::Name::builtin_qml_context) - loadQmlContext(s->target); - else if (n->builtin == IR::Name::builtin_qml_imported_scripts_object) - loadQmlImportedScripts(s->target); - else if (n->qmlSingleton) - loadQmlSingleton(*n->id, s->target); - else - getActivationProperty(n, s->target); - return; - } else if (IR::Const *c = s->source->asConst()) { - loadConst(c, s->target); - return; - } else if (s->source->asTemp() || s->source->asArgLocal()) { - if (s->swap) - swapValues(s->source, s->target); - else - copyValue(s->source, s->target); - return; - } else if (IR::String *str = s->source->asString()) { - loadString(*str->value, s->target); - return; - } else if (IR::RegExp *re = s->source->asRegExp()) { - loadRegexp(re, s->target); - return; - } else if (IR::Closure *clos = s->source->asClosure()) { - initClosure(clos, s->target); - return; - } else if (IR::New *ctor = s->source->asNew()) { - if (Name *func = ctor->base->asName()) { - constructActivationProperty(func, ctor->args, s->target); - return; - } else if (IR::Member *member = ctor->base->asMember()) { - constructProperty(member->base, *member->name, ctor->args, s->target); - return; - } else if (ctor->base->asTemp() || ctor->base->asArgLocal()) { - constructValue(ctor->base, ctor->args, s->target); - return; - } - } else if (IR::Member *m = s->source->asMember()) { - if (m->property) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else - bool captureRequired = true; - - Q_ASSERT(m->kind != IR::Member::MemberOfEnum && m->kind != IR::Member::MemberOfIdObjectsArray); - const int attachedPropertiesId = m->attachedPropertiesId; - const bool isSingletonProperty = m->kind == IR::Member::MemberOfSingletonObject; - - if (_function && attachedPropertiesId == 0 && !m->property->isConstant() && _function->isQmlBinding) { - if (m->kind == IR::Member::MemberOfQmlContextObject) { - _function->contextObjectPropertyDependencies.insert(m->property->coreIndex(), m->property->notifyIndex()); - captureRequired = false; - } else if (m->kind == IR::Member::MemberOfQmlScopeObject) { - _function->scopeObjectPropertyDependencies.insert(m->property->coreIndex(), m->property->notifyIndex()); - captureRequired = false; - } - } - if (m->kind == IR::Member::MemberOfQmlScopeObject || m->kind == IR::Member::MemberOfQmlContextObject) { - getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex(), captureRequired, s->target); - return; - } - getQObjectProperty(m->base, m->property->coreIndex(), captureRequired, isSingletonProperty, attachedPropertiesId, s->target); -#endif // V4_BOOTSTRAP - return; - } else if (m->kind == IR::Member::MemberOfIdObjectsArray) { - getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, m->idIndex, /*captureRequired*/false, s->target); - return; - } else if (m->base->asTemp() || m->base->asConst() || m->base->asArgLocal()) { - getProperty(m->base, *m->name, s->target); - return; - } - } else if (IR::Subscript *ss = s->source->asSubscript()) { - getElement(ss->base, ss->index, s->target); - return; - } else if (IR::Unop *u = s->source->asUnop()) { - unop(u->op, u->expr, s->target); - return; - } else if (IR::Binop *b = s->source->asBinop()) { - binop(b->op, b->left, b->right, s->target); - return; - } else if (IR::Call *c = s->source->asCall()) { - if (c->base->asName()) { - callBuiltin(c, s->target); - return; - } else if (Member *member = c->base->asMember()) { -#ifndef V4_BOOTSTRAP - Q_ASSERT(member->kind != IR::Member::MemberOfIdObjectsArray); - if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) { - callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex(), c->args, s->target); - return; - } -#endif - callProperty(member->base, *member->name, c->args, s->target); - return; - } else if (Subscript *ss = c->base->asSubscript()) { - callSubscript(ss->base, ss->index, c->args, s->target); - return; - } else if (c->base->asTemp() || c->base->asArgLocal() || c->base->asConst()) { - callValue(c->base, c->args, s->target); - return; - } - } else if (IR::Convert *c = s->source->asConvert()) { - Q_ASSERT(c->expr->asTemp() || c->expr->asArgLocal()); - convertType(c->expr, s->target); - return; - } - } else if (IR::Member *m = s->target->asMember()) { - if (m->base->asTemp() || m->base->asConst() || m->base->asArgLocal()) { - if (s->source->asTemp() || s->source->asConst() || s->source->asArgLocal()) { - Q_ASSERT(m->kind != IR::Member::MemberOfEnum); - Q_ASSERT(m->kind != IR::Member::MemberOfIdObjectsArray); - const int attachedPropertiesId = m->attachedPropertiesId; - if (m->property && attachedPropertiesId == 0) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else - if (m->kind == IR::Member::MemberOfQmlScopeObject || m->kind == IR::Member::MemberOfQmlContextObject) { - setQmlContextProperty(s->source, m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex()); - return; - } - setQObjectProperty(s->source, m->base, m->property->coreIndex()); -#endif - return; - } else { - setProperty(s->source, m->base, *m->name); - return; - } - } - } - } else if (IR::Subscript *ss = s->target->asSubscript()) { - if (s->source->asTemp() || s->source->asConst() || s->source->asArgLocal()) { - setElement(s->source, ss->base, ss->index); - return; - } - } - - // For anything else...: - Q_UNIMPLEMENTED(); - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - IRPrinter(&qout).print(s); - qout << endl; - qDebug("%s", buf.data().constData()); - Q_ASSERT(!"TODO"); -} - -IRDecoder::~IRDecoder() -{ -} - -void IRDecoder::visitExp(IR::Exp *s) -{ - if (IR::Call *c = s->expr->asCall()) { - // These are calls where the result is ignored. - if (c->base->asName()) { - callBuiltin(c, 0); - } else if (c->base->asTemp() || c->base->asArgLocal() || c->base->asConst()) { - callValue(c->base, c->args, 0); - } else if (Member *member = c->base->asMember()) { - Q_ASSERT(member->base->asTemp() || member->base->asArgLocal()); -#ifndef V4_BOOTSTRAP - Q_ASSERT(member->kind != IR::Member::MemberOfIdObjectsArray); - if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) { - callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex(), c->args, 0); - return; - } -#endif - callProperty(member->base, *member->name, c->args, 0); - } else if (Subscript *s = c->base->asSubscript()) { - callSubscript(s->base, s->index, c->args, 0); - } else { - Q_UNREACHABLE(); - } - } else { - Q_UNREACHABLE(); - } -} - -void IRDecoder::callBuiltin(IR::Call *call, Expr *result) -{ - IR::Name *baseName = call->base->asName(); - Q_ASSERT(baseName != 0); - - switch (baseName->builtin) { - case IR::Name::builtin_invalid: - callBuiltinInvalid(baseName, call->args, result); - return; - - case IR::Name::builtin_typeof: { - if (IR::Member *member = call->args->expr->asMember()) { -#ifndef V4_BOOTSTRAP - Q_ASSERT(member->kind != IR::Member::MemberOfIdObjectsArray); - if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) { - callBuiltinTypeofQmlContextProperty(member->base, - IR::Member::MemberKind(member->kind), - member->property->coreIndex(), result); - return; - } -#endif - callBuiltinTypeofMember(member->base, *member->name, result); - return; - } else if (IR::Subscript *ss = call->args->expr->asSubscript()) { - callBuiltinTypeofSubscript(ss->base, ss->index, result); - return; - } else if (IR::Name *n = call->args->expr->asName()) { - callBuiltinTypeofName(*n->id, result); - return; - } else if (call->args->expr->asTemp() || - call->args->expr->asConst() || - call->args->expr->asArgLocal()) { - callBuiltinTypeofValue(call->args->expr, result); - return; - } - } break; - - case IR::Name::builtin_delete: { - if (IR::Member *m = call->args->expr->asMember()) { - callBuiltinDeleteMember(m->base, *m->name, result); - return; - } else if (IR::Subscript *ss = call->args->expr->asSubscript()) { - callBuiltinDeleteSubscript(ss->base, ss->index, result); - return; - } else if (IR::Name *n = call->args->expr->asName()) { - callBuiltinDeleteName(*n->id, result); - return; - } else if (call->args->expr->asTemp() || - call->args->expr->asArgLocal()) { - // TODO: should throw in strict mode - callBuiltinDeleteValue(result); - return; - } - } break; - - case IR::Name::builtin_throw: { - IR::Expr *arg = call->args->expr; - Q_ASSERT(arg->asTemp() || arg->asConst() || arg->asArgLocal()); - callBuiltinThrow(arg); - } return; - - case IR::Name::builtin_rethrow: { - callBuiltinReThrow(); - } return; - - case IR::Name::builtin_unwind_exception: { - callBuiltinUnwindException(result); - } return; - - case IR::Name::builtin_push_catch_scope: { - IR::String *s = call->args->expr->asString(); - Q_ASSERT(s); - callBuiltinPushCatchScope(*s->value); - } return; - - case IR::Name::builtin_foreach_iterator_object: { - IR::Expr *arg = call->args->expr; - Q_ASSERT(arg != 0); - callBuiltinForeachIteratorObject(arg, result); - } return; - - case IR::Name::builtin_foreach_next_property_name: { - IR::Expr *arg = call->args->expr; - Q_ASSERT(arg != 0); - callBuiltinForeachNextPropertyname(arg, result); - } return; - case IR::Name::builtin_push_with_scope: { - if (call->args->expr->asTemp() || call->args->expr->asArgLocal()) - callBuiltinPushWithScope(call->args->expr); - else - Q_UNIMPLEMENTED(); - } return; - - case IR::Name::builtin_pop_scope: - callBuiltinPopScope(); - return; - - case IR::Name::builtin_declare_vars: { - if (!call->args) - return; - IR::Const *deletable = call->args->expr->asConst(); - Q_ASSERT(deletable->type == IR::BoolType); - for (IR::ExprList *it = call->args->next; it; it = it->next) { - IR::Name *arg = it->expr->asName(); - Q_ASSERT(arg != 0); - callBuiltinDeclareVar(deletable->value != 0, *arg->id); - } - } return; - - case IR::Name::builtin_define_array: - callBuiltinDefineArray(result, call->args); - return; - - case IR::Name::builtin_define_object_literal: { - IR::ExprList *args = call->args; - const int keyValuePairsCount = args->expr->asConst()->value; - args = args->next; - - IR::ExprList *keyValuePairs = args; - for (int i = 0; i < keyValuePairsCount; ++i) { - args = args->next; // name - bool isData = args->expr->asConst()->value; - args = args->next; // isData flag - args = args->next; // value or getter - if (!isData) - args = args->next; // setter - } - - IR::ExprList *arrayEntries = args; - bool needSparseArray = false; - for (IR::ExprList *it = arrayEntries; it; it = it->next) { - uint index = it->expr->asConst()->value; - if (index > 16) { - needSparseArray = true; - break; - } - it = it->next; - bool isData = it->expr->asConst()->value; - it = it->next; - if (!isData) - it = it->next; - } - - callBuiltinDefineObjectLiteral(result, keyValuePairsCount, keyValuePairs, arrayEntries, needSparseArray); - } return; - - case IR::Name::builtin_setup_argument_object: - callBuiltinSetupArgumentObject(result); - return; - - case IR::Name::builtin_convert_this_to_object: - callBuiltinConvertThisToObject(); - return; - - default: - break; - } - - Q_UNIMPLEMENTED(); - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - IRPrinter(&qout).print(call); qout << endl; - qDebug("%s", buf.data().constData()); - Q_UNREACHABLE(); -} diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h deleted file mode 100644 index 037c02e5ea..0000000000 --- a/src/qml/compiler/qv4isel_p.h +++ /dev/null @@ -1,218 +0,0 @@ -/**************************************************************************** -** -** 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 QV4ISEL_P_H -#define QV4ISEL_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 "qv4jsir_p.h" -#include <private/qv4compileddata_p.h> -#include <private/qv4compiler_p.h> - -#include <qglobal.h> -#include <QHash> - -QT_BEGIN_NAMESPACE - -class QQmlEnginePrivate; - -namespace QV4 { - -class EvalISelFactory; -class ExecutableAllocator; -struct Function; - -class Q_QML_PRIVATE_EXPORT EvalInstructionSelection -{ -public: - EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory); - virtual ~EvalInstructionSelection() = 0; - - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compile(bool generateUnitData = true); - - void setUseFastLookups(bool b) { useFastLookups = b; } - void setUseTypeInference(bool onoff) { useTypeInference = onoff; } - - int registerString(const QString &str) { return jsGenerator->registerString(str); } - uint registerIndexedGetterLookup() { return jsGenerator->registerIndexedGetterLookup(); } - uint registerIndexedSetterLookup() { return jsGenerator->registerIndexedSetterLookup(); } - uint registerGetterLookup(const QString &name) { return jsGenerator->registerGetterLookup(name); } - uint registerSetterLookup(const QString &name) { return jsGenerator->registerSetterLookup(name); } - uint registerGlobalGetterLookup(const QString &name) { return jsGenerator->registerGlobalGetterLookup(name); } - int registerRegExp(IR::RegExp *regexp) { return jsGenerator->registerRegExp(regexp); } - int registerJSClass(int count, IR::ExprList *args) { return jsGenerator->registerJSClass(count, args); } - QV4::Compiler::JSUnitGenerator *jsUnitGenerator() const { return jsGenerator; } - -protected: - virtual void run(int functionIndex) = 0; - virtual QQmlRefPointer<QV4::CompiledData::CompilationUnit> backendCompileStep() = 0; - - bool useFastLookups; - bool useTypeInference; - QV4::ExecutableAllocator *executableAllocator; - QV4::Compiler::JSUnitGenerator *jsGenerator; - QScopedPointer<QV4::Compiler::JSUnitGenerator> ownJSGenerator; - IR::Module *irModule; -}; - -class Q_QML_PRIVATE_EXPORT EvalISelFactory -{ -public: - EvalISelFactory(const QString &codeGeneratorName) : codeGeneratorName(codeGeneratorName) {} - virtual ~EvalISelFactory() = 0; - virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) = 0; - virtual bool jitCompileRegexps() const = 0; - virtual QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading() = 0; - - const QString codeGeneratorName; -}; - -namespace IR { -class Q_QML_PRIVATE_EXPORT IRDecoder -{ -public: - IRDecoder() : _function(0) {} - virtual ~IRDecoder() = 0; - - void visit(Stmt *s) - { - if (auto e = s->asExp()) { - visitExp(e); - } else if (auto m = s->asMove()) { - visitMove(m); - } else if (auto j = s->asJump()) { - visitJump(j); - } else if (auto c = s->asCJump()) { - visitCJump(c); - } else if (auto r = s->asRet()) { - visitRet(r); - } else if (auto p = s->asPhi()) { - visitPhi(p); - } else { - Q_UNREACHABLE(); - } - } - -private: // visitor methods for StmtVisitor: - void visitMove(IR::Move *s); - void visitExp(IR::Exp *s); - -public: // to implement by subclasses: - virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result) = 0; - virtual void callBuiltinTypeofQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::Expr *result) = 0; - virtual void callBuiltinTypeofMember(IR::Expr *base, const QString &name, IR::Expr *result) = 0; - virtual void callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) = 0; - virtual void callBuiltinTypeofName(const QString &name, IR::Expr *result) = 0; - virtual void callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result) = 0; - virtual void callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result) = 0; - virtual void callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) = 0; - virtual void callBuiltinDeleteName(const QString &name, IR::Expr *result) = 0; - virtual void callBuiltinDeleteValue(IR::Expr *result) = 0; - virtual void callBuiltinThrow(IR::Expr *arg) = 0; - virtual void callBuiltinReThrow() = 0; - virtual void callBuiltinUnwindException(IR::Expr *) = 0; - virtual void callBuiltinPushCatchScope(const QString &exceptionName) = 0; - virtual void callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result) = 0; - virtual void callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result) = 0; - virtual void callBuiltinPushWithScope(IR::Expr *arg) = 0; - virtual void callBuiltinPopScope() = 0; - virtual void callBuiltinDeclareVar(bool deletable, const QString &name) = 0; - virtual void callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args) = 0; - virtual void callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray) = 0; - virtual void callBuiltinSetupArgumentObject(IR::Expr *result) = 0; - virtual void callBuiltinConvertThisToObject() = 0; - virtual void callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) = 0; - virtual void callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) = 0; - virtual void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) = 0; - virtual void callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, IR::Expr *result) = 0; - virtual void convertType(IR::Expr *source, IR::Expr *target) = 0; - virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Expr *result) = 0; - virtual void constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) = 0; - virtual void constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) = 0; - virtual void loadThisObject(IR::Expr *target) = 0; - virtual void loadQmlContext(IR::Expr *target) = 0; - virtual void loadQmlImportedScripts(IR::Expr *target) = 0; - virtual void loadQmlSingleton(const QString &name, IR::Expr *target) = 0; - virtual void loadConst(IR::Const *sourceConst, IR::Expr *target) = 0; - virtual void loadString(const QString &str, IR::Expr *target) = 0; - virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target) = 0; - virtual void getActivationProperty(const IR::Name *name, IR::Expr *target) = 0; - virtual void setActivationProperty(IR::Expr *source, const QString &targetName) = 0; - virtual void initClosure(IR::Closure *closure, IR::Expr *target) = 0; - virtual void getProperty(IR::Expr *base, const QString &name, IR::Expr *target) = 0; - virtual void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingletonProperty, int attachedPropertiesId, IR::Expr *target) = 0; - virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target) = 0; - virtual void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName) = 0; - virtual void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) = 0; - virtual void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) = 0; - virtual void getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) = 0; - virtual void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) = 0; - virtual void copyValue(IR::Expr *source, IR::Expr *target) = 0; - virtual void swapValues(IR::Expr *source, IR::Expr *target) = 0; - virtual void unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target) = 0; - virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) = 0; - -protected: - virtual void visitJump(IR::Jump *) = 0; - virtual void visitCJump(IR::CJump *) = 0; - virtual void visitRet(IR::Ret *) = 0; - virtual void visitPhi(IR::Phi *) {} - - virtual void callBuiltin(IR::Call *c, IR::Expr *result); - - IR::Function *_function; // subclass needs to set -}; -} // namespace IR - -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4ISEL_P_H diff --git a/src/qml/compiler/qv4isel_util_p.h b/src/qml/compiler/qv4isel_util_p.h deleted file mode 100644 index e949e6f0ad..0000000000 --- a/src/qml/compiler/qv4isel_util_p.h +++ /dev/null @@ -1,241 +0,0 @@ -/**************************************************************************** -** -** 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 QV4ISEL_UTIL_P_H -#define QV4ISEL_UTIL_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/qv4value_p.h" -#include "qv4jsir_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct TargetPrimitive32 { - static TargetPrimitive32 emptyValue() { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Empty) << 32; return p; } - static TargetPrimitive32 nullValue() { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Null) << 32; return p; } - static TargetPrimitive32 undefinedValue() { TargetPrimitive32 p; p._val = quint64(Value::Managed_Type_Internal_32) << 32; return p; } - static TargetPrimitive32 fromBoolean(bool b) { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Boolean) << 32 | quint64(b); return p; } - static TargetPrimitive32 fromInt32(int v) { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Integer) << 32 | quint32(v); return p; } - static TargetPrimitive32 fromDouble(double v) { - TargetPrimitive32 p; - memcpy(&p._val, &v, 8); - return p; - } - static TargetPrimitive32 fromUInt32(uint v) { - if (v < INT_MAX) - return fromInt32(qint32(v)); - return fromDouble(double(v)); - } - - quint32 value() const { return _val & quint64(~quint32(0)); } - quint32 tag() const { return _val >> 32; } - - quint64 rawValue() const { return _val; } - -private: - quint64 _val; -}; - -struct TargetPrimitive64 { - static TargetPrimitive64 emptyValue() { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Empty) << 32; return p; } - static TargetPrimitive64 nullValue() { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Null) << 32; return p; } - static TargetPrimitive64 undefinedValue() { TargetPrimitive64 p; p._val = 0; return p; } - static TargetPrimitive64 fromBoolean(bool b) { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Boolean) << 32 | quint64(b); return p; } - static TargetPrimitive64 fromInt32(int v) { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Integer) << 32 | quint32(v); return p; } - static TargetPrimitive64 fromDouble(double v) { - TargetPrimitive64 p; - memcpy(&p._val, &v, 8); - p._val ^= Value::NaNEncodeMask; - return p; - } - static TargetPrimitive64 fromUInt32(uint v) { - if (v < INT_MAX) - return fromInt32(qint32(v)); - return fromDouble(double(v)); - } - - quint32 value() const { return _val & quint64(~quint32(0)); } - quint32 tag() const { return _val >> 32; } - - quint64 rawValue() const { return _val; } - -private: - quint64 _val; -}; - -inline bool canConvertToSignedInteger(double value) -{ - int ival = (int) value; - // +0 != -0, so we need to convert to double when negating 0 - return ival == value && !(value == 0 && isNegative(value)); -} - -inline bool canConvertToUnsignedInteger(double value) -{ - unsigned uval = (unsigned) value; - // +0 != -0, so we need to convert to double when negating 0 - return uval == value && !(value == 0 && isNegative(value)); -} - -template <typename PrimitiveType = Primitive> -inline PrimitiveType convertToValue(IR::Const *c) -{ - switch (c->type) { - case IR::MissingType: - return PrimitiveType::emptyValue(); - case IR::NullType: - return PrimitiveType::nullValue(); - case IR::UndefinedType: - return PrimitiveType::undefinedValue(); - case IR::BoolType: - return PrimitiveType::fromBoolean(c->value != 0); - case IR::SInt32Type: - return PrimitiveType::fromInt32(int(c->value)); - case IR::UInt32Type: - return PrimitiveType::fromUInt32(unsigned(c->value)); - case IR::DoubleType: - return PrimitiveType::fromDouble(c->value); - case IR::NumberType: { - int ival = (int)c->value; - if (canConvertToSignedInteger(c->value)) { - return PrimitiveType::fromInt32(ival); - } else { - return PrimitiveType::fromDouble(c->value); - } - } - default: - Q_UNREACHABLE(); - } - // unreachable, but the function must return something - return PrimitiveType::undefinedValue(); -} - -class ConvertTemps -{ - void renumber(IR::Temp *t) - { - if (t->kind != IR::Temp::VirtualRegister) - return; - - int stackSlot = _stackSlotForTemp.value(t->index, -1); - if (stackSlot == -1) { - stackSlot = allocateFreeSlot(); - _stackSlotForTemp[t->index] = stackSlot; - } - - t->kind = IR::Temp::StackSlot; - t->index = stackSlot; - } - -protected: - int _nextUnusedStackSlot; - QHash<int, int> _stackSlotForTemp; - IR::BasicBlock *_currentBasicBlock; - virtual int allocateFreeSlot() - { - return _nextUnusedStackSlot++; - } - - virtual void process(IR::Stmt *s) - { - visit(s); - } - -public: - ConvertTemps() - : _nextUnusedStackSlot(0) - , _currentBasicBlock(0) - {} - - void toStackSlots(IR::Function *function) - { - _stackSlotForTemp.reserve(function->tempCount); - - for (IR::BasicBlock *bb : function->basicBlocks()) { - if (bb->isRemoved()) - continue; - _currentBasicBlock = bb; - for (IR::Stmt *s : bb->statements()) - process(s); - } - - function->tempCount = _nextUnusedStackSlot; - } - -protected: - void visit(IR::Stmt *s) { - switch (s->stmtKind) { - case IR::Stmt::PhiStmt: - visitPhi(s->asPhi()); - break; - default: - STMT_VISIT_ALL_KINDS(s); - break; - } - } - - virtual void visitPhi(IR::Phi *) - { Q_UNREACHABLE(); } - -private: - void visit(IR::Expr *e) { - if (auto temp = e->asTemp()) { - renumber(temp); - } else { - EXPR_VISIT_ALL_KINDS(e); - } - } -}; -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4ISEL_UTIL_P_H diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp deleted file mode 100644 index 0b0ed391fb..0000000000 --- a/src/qml/compiler/qv4jsir.cpp +++ /dev/null @@ -1,993 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#include "qv4jsir_p.h" -#include <private/qqmljsast_p.h> - -#ifndef V4_BOOTSTRAP -#include <private/qqmlpropertycache_p.h> -#endif - -#include <QtCore/QBuffer> -#include <QtCore/qtextstream.h> -#include <QtCore/qdebug.h> -#include <QtCore/qset.h> -#include <cmath> - -#include <vector> - -#ifdef CONST -#undef CONST -#endif - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -QString typeName(Type t) -{ - switch (t) { - case UnknownType: return QStringLiteral(""); - case MissingType: return QStringLiteral("missing"); - case UndefinedType: return QStringLiteral("undefined"); - case NullType: return QStringLiteral("null"); - case BoolType: return QStringLiteral("bool"); - case UInt32Type: return QStringLiteral("uint32"); - case SInt32Type: return QStringLiteral("int32"); - case DoubleType: return QStringLiteral("double"); - case NumberType: return QStringLiteral("number"); - case StringType: return QStringLiteral("string"); - case VarType: return QStringLiteral("var"); - case QObjectType: return QStringLiteral("qobject"); - default: return QStringLiteral("multiple"); - } -} - -const char *opname(AluOp op) -{ - switch (op) { - case OpInvalid: return "?"; - - case OpIfTrue: return "(bool)"; - case OpNot: return "not"; - case OpUMinus: return "neg"; - case OpUPlus: return "plus"; - case OpCompl: return "invert"; - case OpIncrement: return "incr"; - case OpDecrement: return "decr"; - - case OpBitAnd: return "bitand"; - case OpBitOr: return "bitor"; - case OpBitXor: return "bitxor"; - - case OpAdd: return "add"; - case OpSub: return "sub"; - case OpMul: return "mul"; - case OpDiv: return "div"; - case OpMod: return "mod"; - - case OpLShift: return "shl"; - case OpRShift: return "shr"; - case OpURShift: return "asr"; - - case OpGt: return "gt"; - case OpLt: return "lt"; - case OpGe: return "ge"; - case OpLe: return "le"; - case OpEqual: return "eq"; - case OpNotEqual: return "ne"; - case OpStrictEqual: return "se"; - case OpStrictNotEqual: return "sne"; - - case OpInstanceof: return "instanceof"; - case OpIn: return "in"; - - case OpAnd: return "and"; - case OpOr: return "or"; - - default: return "?"; - - } // switch -} - -AluOp binaryOperator(int op) -{ - switch (static_cast<QSOperator::Op>(op)) { - case QSOperator::Add: return OpAdd; - case QSOperator::And: return OpAnd; - case QSOperator::BitAnd: return OpBitAnd; - case QSOperator::BitOr: return OpBitOr; - case QSOperator::BitXor: return OpBitXor; - case QSOperator::Div: return OpDiv; - case QSOperator::Equal: return OpEqual; - case QSOperator::Ge: return OpGe; - case QSOperator::Gt: return OpGt; - case QSOperator::Le: return OpLe; - case QSOperator::LShift: return OpLShift; - case QSOperator::Lt: return OpLt; - case QSOperator::Mod: return OpMod; - case QSOperator::Mul: return OpMul; - case QSOperator::NotEqual: return OpNotEqual; - case QSOperator::Or: return OpOr; - case QSOperator::RShift: return OpRShift; - case QSOperator::StrictEqual: return OpStrictEqual; - case QSOperator::StrictNotEqual: return OpStrictNotEqual; - case QSOperator::Sub: return OpSub; - case QSOperator::URShift: return OpURShift; - case QSOperator::InstanceOf: return OpInstanceof; - case QSOperator::In: return OpIn; - default: return OpInvalid; - } -} - -class RemoveSharedExpressions -{ - CloneExpr clone; - std::vector<Expr *> subexpressions; // contains all the non-cloned subexpressions in the given function. sorted using std::lower_bound. - Expr *uniqueExpr; - -public: - RemoveSharedExpressions(): uniqueExpr(0) {} - - void operator()(IR::Function *function) - { - subexpressions.clear(); - subexpressions.reserve(function->basicBlockCount() * 8); - - for (BasicBlock *block : function->basicBlocks()) { - if (block->isRemoved()) - continue; - clone.setBasicBlock(block); - - for (Stmt *s : block->statements()) { - visit(s); - } - } - } - -private: - template <typename Expr_> - Expr_ *cleanup(Expr_ *expr) - { - std::vector<Expr *>::iterator it = std::lower_bound(subexpressions.begin(), subexpressions.end(), expr); - if (it == subexpressions.end() || *it != expr) { - subexpressions.insert(it, expr); - IR::Expr *e = expr; - qSwap(uniqueExpr, e); - visit(expr); - qSwap(uniqueExpr, e); - return static_cast<Expr_ *>(e); - } - - // the cloned expression is unique by definition - // so we don't need to add it to `subexpressions'. - return clone(expr); - } - - void visit(Stmt *s) - { - if (auto e = s->asExp()) { - e->expr = cleanup(e->expr); - } else if (auto m = s->asMove()) { - m->target = cleanup(m->target); - m->source = cleanup(m->source); - } else if (auto c = s->asCJump()) { - c->cond = cleanup(c->cond); - } else if (auto r = s->asRet()) { - r->expr = cleanup(r->expr); - } - } - - void visit(Expr *e) - { - if (auto c = e->asConvert()) { - c->expr = cleanup(c->expr); - } else if (auto u = e->asUnop()) { - u->expr = cleanup(u->expr); - } else if (auto b = e->asBinop()) { - b->left = cleanup(b->left); - b->right = cleanup(b->right); - } else if (auto c = e->asCall()) { - c->base = cleanup(c->base); - for (IR::ExprList *it = c->args; it; it = it->next) { - it->expr = cleanup(it->expr); - } - } else if (auto n = e->asNew()) { - n->base = cleanup(n->base); - for (IR::ExprList *it = n->args; it; it = it->next) { - it->expr = cleanup(it->expr); - } - } else if (auto s = e->asSubscript()) { - s->base = cleanup(s->base); - s->index = cleanup(s->index); - } else if (auto m = e->asMember()) { - m->base = cleanup(m->base); - } - } -}; - -void Name::initGlobal(const QString *id, quint32 line, quint32 column) -{ - this->id = id; - this->builtin = builtin_invalid; - this->global = true; - this->qmlSingleton = false; - this->freeOfSideEffects = false; - this->line = line; - this->column = column; -} - -void Name::init(const QString *id, quint32 line, quint32 column) -{ - this->id = id; - this->builtin = builtin_invalid; - this->global = false; - this->qmlSingleton = false; - this->freeOfSideEffects = false; - this->line = line; - this->column = column; -} - -void Name::init(Builtin builtin, quint32 line, quint32 column) -{ - this->id = 0; - this->builtin = builtin; - this->global = false; - this->qmlSingleton = false; - this->freeOfSideEffects = false; - this->line = line; - this->column = column; -} - -const char *builtin_to_string(Name::Builtin b) -{ - switch (b) { - case Name::builtin_invalid: - return "builtin_invalid"; - case Name::builtin_typeof: - return "builtin_typeof"; - case Name::builtin_delete: - return "builtin_delete"; - case Name::builtin_throw: - return "builtin_throw"; - case Name::builtin_rethrow: - return "builtin_rethrow"; - case Name::builtin_unwind_exception: - return "builtin_unwind_exception"; - case Name::builtin_push_catch_scope: - return "builtin_push_catch_scope"; - case IR::Name::builtin_foreach_iterator_object: - return "builtin_foreach_iterator_object"; - case IR::Name::builtin_foreach_next_property_name: - return "builtin_foreach_next_property_name"; - case IR::Name::builtin_push_with_scope: - return "builtin_push_with_scope"; - case IR::Name::builtin_pop_scope: - return "builtin_pop_scope"; - case IR::Name::builtin_declare_vars: - return "builtin_declare_vars"; - case IR::Name::builtin_define_array: - return "builtin_define_array"; - case IR::Name::builtin_define_object_literal: - return "builtin_define_object_literal"; - case IR::Name::builtin_setup_argument_object: - return "builtin_setup_argument_object"; - case IR::Name::builtin_convert_this_to_object: - return "builtin_convert_this_to_object"; - case IR::Name::builtin_qml_context: - return "builtin_qml_context"; - case IR::Name::builtin_qml_imported_scripts_object: - return "builtin_qml_imported_scripts_object"; - } - return "builtin_(###FIXME)"; -}; - -bool operator<(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW -{ - if (t1.kind < t2.kind) return true; - if (t1.kind > t2.kind) return false; - return t1.index < t2.index; -} - -Function *Module::newFunction(const QString &name, Function *outer) -{ - Function *f = new Function(this, outer, name); - functions.append(f); - if (!outer) { - if (!isQmlModule) { - Q_ASSERT(!rootFunction); - rootFunction = f; - } - } else { - outer->nestedFunctions.append(f); - } - return f; -} - -Module::~Module() -{ - qDeleteAll(functions); -} - -void Module::setFileName(const QString &name) -{ - fileName = name; -} - -Function::Function(Module *module, Function *outer, const QString &name) - : module(module) - , pool(&module->pool) - , tempCount(0) - , maxNumberOfArguments(0) - , outer(outer) - , insideWithOrCatch(0) - , hasDirectEval(false) - , usesArgumentsObject(false) - , usesThis(false) - , isStrict(false) - , isNamedExpression(false) - , hasTry(false) - , hasWith(false) - , isQmlBinding(false) - , unused(0) - , line(0) - , column(0) - , _allBasicBlocks(0) - , _statementCount(0) -{ - this->name = newString(name); - _basicBlocks.reserve(8); -} - -Function::~Function() -{ - if (_allBasicBlocks) { - qDeleteAll(*_allBasicBlocks); - delete _allBasicBlocks; - } else { - qDeleteAll(_basicBlocks); - } - - pool = 0; - module = 0; -} - - -const QString *Function::newString(const QString &text) -{ - return &*strings.insert(text); -} - -BasicBlock *Function::newBasicBlock(BasicBlock *catchBlock, BasicBlockInsertMode mode) -{ - BasicBlock *block = new BasicBlock(this, catchBlock); - return mode == InsertBlock ? addBasicBlock(block) : block; -} - -BasicBlock *Function::addBasicBlock(BasicBlock *block) -{ - Q_ASSERT(block->index() < 0); - block->setIndex(_basicBlocks.size()); - _basicBlocks.append(block); - return block; -} - -void Function::removeBasicBlock(BasicBlock *block) -{ - block->markAsRemoved(); - block->in.clear(); - block->out.clear(); -} - -int Function::liveBasicBlocksCount() const -{ - int count = 0; - for (BasicBlock *bb : basicBlocks()) - if (!bb->isRemoved()) - ++count; - return count; -} - -void Function::removeSharedExpressions() -{ - RemoveSharedExpressions removeSharedExpressions; - removeSharedExpressions(this); -} - -int Function::indexOfArgument(const QStringRef &string) const -{ - for (int i = formals.size() - 1; i >= 0; --i) { - if (*formals.at(i) == string) - return i; - } - return -1; -} - -void Function::setScheduledBlocks(const QVector<BasicBlock *> &scheduled) -{ - Q_ASSERT(!_allBasicBlocks); - _allBasicBlocks = new QVector<BasicBlock *>(basicBlocks()); - _basicBlocks = scheduled; - for (int i = 0, ei = basicBlockCount(); i != ei; ++i) - basicBlock(i)->changeIndex(i); -} - -BasicBlock *Function::getOrCreateBasicBlock(int index) -{ - if (_basicBlocks.size() <= index) { - const int oldSize = _basicBlocks.size(); - _basicBlocks.resize(index + 1); - for (int i = oldSize; i <= index; ++i) { - BasicBlock *block = new BasicBlock(this, 0); - block->setIndex(i); - _basicBlocks[i] = block; - } - } - - return _basicBlocks.at(index); -} - -void Function::setStatementCount(int cnt) -{ - _statementCount = cnt; -} - -void BasicBlock::setStatements(const QVector<Stmt *> &newStatements) -{ - Q_ASSERT(!isRemoved()); - Q_ASSERT(newStatements.size() >= _statements.size()); - for (Stmt *s : qAsConst(_statements)) { - if (Phi *p = s->asPhi()) { - if (!newStatements.contains(p)) { - // phi-node was not copied over, so: - p->destroyData(); - } - } else { - break; - } - } - _statements = newStatements; -} - -CloneExpr::CloneExpr(BasicBlock *block) - : block(block), cloned(0) -{ -} - -void CloneExpr::setBasicBlock(BasicBlock *block) -{ - this->block = block; -} - -ExprList *CloneExpr::clone(ExprList *list) -{ - if (! list) - return 0; - - ExprList *clonedList = block->function->New<IR::ExprList>(); - clonedList->init(clone(list->expr), clone(list->next)); - return clonedList; -} - -void CloneExpr::visit(Expr *e) -{ - if (auto c = e->asConst()) { - cloned = cloneConst(c, block->function); - } else if (auto s = e->asString()) { - cloned = block->STRING(s->value); - } else if (auto r = e->asRegExp()) { - cloned = block->REGEXP(r->value, r->flags); - } else if (auto n = e->asName()) { - cloned = cloneName(n, block->function); - } else if (auto t = e->asTemp()) { - cloned = cloneTemp(t, block->function); - } else if (auto a = e->asArgLocal()) { - cloned = cloneArgLocal(a, block->function); - } else if (auto c = e->asClosure()) { - cloned = block->CLOSURE(c->value); - } else if (auto c = e->asConvert()) { - cloned = block->CONVERT(clone(c->expr), c->type); - } else if (auto u = e->asUnop()) { - cloned = block->UNOP(u->op, clone(u->expr)); - } else if (auto b = e->asBinop()) { - cloned = block->BINOP(b->op, clone(b->left), clone(b->right)); - } else if (auto c = e->asCall()) { - cloned = block->CALL(clone(c->base), clone(c->args)); - } else if (auto n = e->asNew()) { - cloned = block->NEW(clone(n->base), clone(n->args)); - } else if (auto s = e->asSubscript()) { - cloned = block->SUBSCRIPT(clone(s->base), clone(s->index)); - } else if (auto m = e->asMember()) { - cloned = block->MEMBER(clone(m->base), m->name, m->property, m->kind, m->idIndex); - } else { - Q_UNREACHABLE(); - } -} - -IRPrinter::IRPrinter(QTextStream *out) - : out(out) - , positionSize(Stmt::InvalidId) - , currentBB(0) -{ -} - -IRPrinter::~IRPrinter() -{ -} - -void IRPrinter::print(Stmt *s) -{ - visit(s); -} - -void IRPrinter::print(const Expr &e) -{ - visit(const_cast<Expr *>(&e)); -} - -void IRPrinter::print(Expr *e) -{ - visit(e); -} - -void IRPrinter::print(Function *f) -{ - if (positionSize == Stmt::InvalidId) - positionSize = QString::number(f->statementCount()).size(); - - QString n = f->name ? *f->name : QString(); - if (n.isEmpty()) - n.sprintf("%p", f); - *out << "function " << n << '('; - - for (int i = 0; i < f->formals.size(); ++i) { - if (i != 0) - *out << ", "; - *out << *f->formals.at(i); - } - *out << ')' << endl - << '{' << endl; - - for (const QString *local : qAsConst(f->locals)) - *out << " local var " << *local << endl; - - bool needsSeperator = !f->locals.isEmpty(); - for (BasicBlock *bb : f->basicBlocks()) { - if (bb->isRemoved()) - continue; - - if (needsSeperator) - *out << endl; - else - needsSeperator = true; - print(bb); - } - *out << '}' << endl; -} - -void IRPrinter::print(BasicBlock *bb) -{ - std::swap(currentBB, bb); - printBlockStart(); - - for (Stmt *s : currentBB->statements()) { - if (!s) - continue; - - QByteArray str; - QBuffer buf(&str); - buf.open(QIODevice::WriteOnly); - QTextStream os(&buf); - QTextStream *prevOut = &os; - std::swap(out, prevOut); - addStmtNr(s); - visit(s); - if (s->location.startLine) { - out->flush(); - for (int i = 58 - str.length(); i > 0; --i) - *out << ' '; - *out << " ; line: " << s->location.startLine << ", column: " << s->location.startColumn; - } - - out->flush(); - std::swap(out, prevOut); - - *out << " " << str << endl; - } - - std::swap(currentBB, bb); -} - -void IRPrinter::visit(Stmt *s) -{ - if (auto e = s->asExp()) { - visitExp(e); - } else if (auto m = s->asMove()) { - visitMove(m); - } else if (auto j = s->asJump()) { - visitJump(j); - } else if (auto c = s->asCJump()) { - visitCJump(c); - } else if (auto r = s->asRet()) { - visitRet(r); - } else if (auto p = s->asPhi()) { - visitPhi(p); - } else { - Q_UNREACHABLE(); - } -} - -void IRPrinter::visitExp(Exp *s) -{ - *out << "void "; - visit(s->expr); -} - -void IRPrinter::visitMove(Move *s) -{ - if (Temp *targetTemp = s->target->asTemp()) - if (!s->swap && targetTemp->type != UnknownType) - *out << typeName(targetTemp->type) << ' '; - - visit(s->target); - *out << ' '; - if (s->swap) - *out << "<=> "; - else - *out << "= "; - visit(s->source); -} - -void IRPrinter::visitJump(Jump *s) -{ - *out << "goto L" << s->target->index(); -} - -void IRPrinter::visitCJump(CJump *s) -{ - *out << "if "; - visit(s->cond); - *out << " goto L" << s->iftrue->index() - << " else goto L" << s->iffalse->index(); -} - -void IRPrinter::visitRet(Ret *s) -{ - *out << "return"; - if (s->expr) { - *out << ' '; - visit(s->expr); - } -} - -void IRPrinter::visitPhi(Phi *s) -{ - if (s->targetTemp->type != UnknownType) - *out << typeName(s->targetTemp->type) << ' '; - - visit(s->targetTemp); - *out << " = phi "; - for (int i = 0, ei = s->incoming.size(); i < ei; ++i) { - if (i > 0) - *out << ", "; - if (currentBB) - *out << 'L' << currentBB->in.at(i)->index() << ": "; - if (s->incoming[i]) - visit(s->incoming[i]); - } -} - -void IRPrinter::visit(Expr *e) -{ - if (auto c = e->asConst()) { - visitConst(c); - } else if (auto s = e->asString()) { - visitString(s); - } else if (auto r = e->asRegExp()) { - visitRegExp(r); - } else if (auto n = e->asName()) { - visitName(n); - } else if (auto t = e->asTemp()) { - visitTemp(t); - } else if (auto a = e->asArgLocal()) { - visitArgLocal(a); - } else if (auto c = e->asClosure()) { - visitClosure(c); - } else if (auto c = e->asConvert()) { - visitConvert(c); - } else if (auto u = e->asUnop()) { - visitUnop(u); - } else if (auto b = e->asBinop()) { - visitBinop(b); - } else if (auto c = e->asCall()) { - visitCall(c); - } else if (auto n = e->asNew()) { - visitNew(n); - } else if (auto s = e->asSubscript()) { - visitSubscript(s); - } else if (auto m = e->asMember()) { - visitMember(m); - } else { - Q_UNREACHABLE(); - } -} - -void IRPrinter::visitConst(Const *e) -{ - switch (e->type) { - case QV4::IR::UndefinedType: - *out << "undefined"; - break; - case QV4::IR::NullType: - *out << "null"; - break; - case QV4::IR::BoolType: - *out << (e->value ? "true" : "false"); - break; - case QV4::IR::MissingType: - *out << "missing"; - break; - default: - if (int(e->value) == 0 && int(e->value) == e->value) { - if (isNegative(e->value)) - *out << "-0"; - else - *out << "0"; - } else { - *out << QString::number(e->value, 'g', 16); - } - break; - } -} - -void IRPrinter::visitString(String *e) -{ - *out << '"' << escape(*e->value) << '"'; -} - -void IRPrinter::visitRegExp(RegExp *e) -{ - char f[3]; - int i = 0; - if (e->flags & RegExp::RegExp_Global) - f[i++] = 'g'; - if (e->flags & RegExp::RegExp_IgnoreCase) - f[i++] = 'i'; - if (e->flags & RegExp::RegExp_Multiline) - f[i++] = 'm'; - f[i] = 0; - - *out << '/' << *e->value << '/' << f; -} - -void IRPrinter::visitName(Name *e) -{ - if (e->id) { - if (*e->id != QLatin1String("this")) - *out << '.'; - *out << *e->id; - } else { - *out << builtin_to_string(e->builtin); - } -} - -void IRPrinter::visitTemp(Temp *e) -{ - switch (e->kind) { - case Temp::VirtualRegister: *out << '%' << e->index; break; - case Temp::PhysicalRegister: *out << (e->type == DoubleType ? "fp" : "r") - << e->index; break; - case Temp::StackSlot: *out << '&' << e->index; break; - default: *out << "INVALID"; - } -} - -void IRPrinter::visitArgLocal(ArgLocal *e) -{ - switch (e->kind) { - case ArgLocal::Formal: *out << '#' << e->index; break; - case ArgLocal::ScopedFormal: *out << '#' << e->index - << '@' << e->scope; break; - case ArgLocal::Local: *out << '$' << e->index; break; - case ArgLocal::ScopedLocal: *out << '$' << e->index - << '@' << e->scope; break; - default: *out << "INVALID"; - } -} - -void IRPrinter::visitClosure(Closure *e) -{ - QString name = e->functionName ? *e->functionName : QString(); - if (name.isEmpty()) - name.sprintf("%x", e->value); - *out << "closure " << name; -} - -void IRPrinter::visitConvert(Convert *e) -{ - *out << "convert " << typeName(e->expr->type) << " to " << typeName(e->type) << ' '; - visit(e->expr); -} - -void IRPrinter::visitUnop(Unop *e) -{ - *out << opname(e->op) << ' '; - visit(e->expr); -} - -void IRPrinter::visitBinop(Binop *e) -{ - *out << opname(e->op) << ' '; - visit(e->left); - *out << ", "; - visit(e->right); -} - -void IRPrinter::visitCall(Call *e) -{ - *out << "call "; - visit(e->base); - *out << '('; - for (ExprList *it = e->args; it; it = it->next) { - if (it != e->args) - *out << ", "; - visit(it->expr); - } - *out << ')'; -} - -void IRPrinter::visitNew(New *e) -{ - *out << "new "; - visit(e->base); - *out << '('; - for (ExprList *it = e->args; it; it = it->next) { - if (it != e->args) - *out << ", "; - visit(it->expr); - } - *out << ')'; -} - -void IRPrinter::visitSubscript(Subscript *e) -{ - visit(e->base); - *out << '['; - visit(e->index); - *out << ']'; -} - -void IRPrinter::visitMember(Member *e) -{ - if (e->kind != Member::MemberOfEnum && e->kind != Member::MemberOfIdObjectsArray - && e->attachedPropertiesId != 0 && !e->base->asTemp()) - *out << "[[attached property from " << e->attachedPropertiesId << "]]"; - else - visit(e->base); - *out << '.' << *e->name; -#ifndef V4_BOOTSTRAP - if (e->property) - *out << " (meta-property " << e->property->coreIndex() - << " <" << QMetaType::typeName(e->property->propType()) - << ">)"; - else if (e->kind == Member::MemberOfIdObjectsArray) - *out << "(id object " << e->idIndex << ")"; -#endif -} - -QString IRPrinter::escape(const QString &s) -{ - QString r; - for (int i = 0; i < s.length(); ++i) { - const QChar ch = s.at(i); - if (ch == QLatin1Char('\n')) - r += QLatin1String("\\n"); - else if (ch == QLatin1Char('\r')) - r += QLatin1String("\\r"); - else if (ch == QLatin1Char('\\')) - r += QLatin1String("\\\\"); - else if (ch == QLatin1Char('"')) - r += QLatin1String("\\\""); - else if (ch == QLatin1Char('\'')) - r += QLatin1String("\\'"); - else - r += ch; - } - return r; -} - -void IRPrinter::addStmtNr(Stmt *s) -{ - if (s->id() >= 0) - addJustifiedNr(s->id()); -} - -void IRPrinter::addJustifiedNr(int pos) -{ - if (positionSize == Stmt::InvalidId) { - *out << pos << ": "; - } else { - QString posStr; - if (pos != Stmt::InvalidId) - posStr = QString::number(pos); - *out << posStr.rightJustified(positionSize); - if (pos == Stmt::InvalidId) - *out << " "; - else - *out << ": "; - } -} - -void IRPrinter::printBlockStart() -{ - if (currentBB->isRemoved()) { - *out << "(block has been removed)"; - return; - } - - QByteArray str; - str.append('L'); - str.append(QByteArray::number(currentBB->index())); - str.append(':'); - if (currentBB->catchBlock) { - str.append(" (exception handler L"); - str.append(QByteArray::number(currentBB->catchBlock->index())); - str.append(')'); - } - for (int i = 66 - str.length(); i; --i) - str.append(' '); - *out << str; - - *out << "; predecessors:"; - for (BasicBlock *in : qAsConst(currentBB->in)) - *out << " L" << in->index(); - if (currentBB->in.isEmpty()) - *out << " none"; - if (BasicBlock *container = currentBB->containingGroup()) - *out << ", container: L" << container->index(); - if (currentBB->isGroupStart()) - *out << ", loop_header: yes"; - *out << endl; -} - -} // end of namespace IR -} // end of namespace QV4 - -QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h deleted file mode 100644 index 7bdad31260..0000000000 --- a/src/qml/compiler/qv4jsir_p.h +++ /dev/null @@ -1,1813 +0,0 @@ -/**************************************************************************** -** -** 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 QV4JSIR_P_H -#define QV4JSIR_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "private/qv4global_p.h" -#include <private/qqmljsmemorypool_p.h> -#include <private/qqmljsastfwd_p.h> -#include <private/qflagpointer_p.h> -#ifndef V4_BOOTSTRAP -#include <private/qqmlmetatype_p.h> -#endif - -#include <QtCore/private/qnumeric_p.h> -#include <QtCore/QVector> -#include <QtCore/QString> -#include <QtCore/QBitArray> -#include <QtCore/qurl.h> -#include <QtCore/QVarLengthArray> -#include <QtCore/QDateTime> -#include <qglobal.h> - -#if defined(CONST) && defined(Q_OS_WIN) -# define QT_POP_CONST -# pragma push_macro("CONST") -# undef CONST // CONST conflicts with our own identifier -#endif - -QT_BEGIN_NAMESPACE - -class QTextStream; -class QQmlType; -class QQmlPropertyData; -class QQmlPropertyCache; -class QQmlEnginePrivate; -struct QQmlImportRef; -class QQmlTypeNameCache; - -namespace QV4 { - -inline bool isNegative(double d) -{ - uchar *dch = (uchar *)&d; - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) - return (dch[0] & 0x80); - else - return (dch[7] & 0x80); - -} - -namespace IR { - -struct BasicBlock; -struct Function; -struct Module; - -struct Stmt; -struct Expr; - -// expressions -struct Const; -struct String; -struct RegExp; -struct Name; -struct Temp; -struct ArgLocal; -struct Closure; -struct Convert; -struct Unop; -struct Binop; -struct Call; -struct New; -struct Subscript; -struct Member; - -// statements -struct Exp; -struct Move; -struct Jump; -struct CJump; -struct Ret; -struct Phi; - -template<class T, int Prealloc> -class VarLengthArray: public QVarLengthArray<T, Prealloc> -{ -public: - bool removeOne(const T &element) - { - for (int i = 0; i < this->size(); ++i) { - if (this->at(i) == element) { - this->remove(i); - return true; - } - } - - return false; - } -}; - -// Flag pointer: -// * The first flag indicates whether the meta object is final. -// If final, then none of its properties themselves need to -// be final when considering for lookups in QML. -// * The second flag indicates whether enums should be included -// in the lookup of properties or not. The default is false. -typedef QFlagPointer<QQmlPropertyCache> IRMetaObject; - -enum AluOp { - OpInvalid = 0, - - OpIfTrue, - OpNot, - OpUMinus, - OpUPlus, - OpCompl, - OpIncrement, - OpDecrement, - - OpBitAnd, - OpBitOr, - OpBitXor, - - OpAdd, - OpSub, - OpMul, - OpDiv, - OpMod, - - OpLShift, - OpRShift, - OpURShift, - - OpGt, - OpLt, - OpGe, - OpLe, - OpEqual, - OpNotEqual, - OpStrictEqual, - OpStrictNotEqual, - - OpInstanceof, - OpIn, - - OpAnd, - OpOr, - - LastAluOp = OpOr -}; -AluOp binaryOperator(int op); -const char *opname(IR::AluOp op); - -enum Type : quint16 { - UnknownType = 0, - - MissingType = 1 << 0, - UndefinedType = 1 << 1, - NullType = 1 << 2, - BoolType = 1 << 3, - - SInt32Type = 1 << 4, - UInt32Type = 1 << 5, - DoubleType = 1 << 6, - NumberType = SInt32Type | UInt32Type | DoubleType, - - StringType = 1 << 7, - QObjectType = 1 << 8, - VarType = 1 << 9 -}; - -inline bool strictlyEqualTypes(Type t1, Type t2) -{ - return t1 == t2 || ((t1 & NumberType) && (t2 & NumberType)); -} - -QString typeName(Type t); - -struct MemberExpressionResolver; - -struct DiscoveredType { - int type; - MemberExpressionResolver *memberResolver; - - DiscoveredType() : type(UnknownType), memberResolver(0) {} - DiscoveredType(Type t) : type(t), memberResolver(0) { Q_ASSERT(type != QObjectType); } - explicit DiscoveredType(int t) : type(t), memberResolver(0) { Q_ASSERT(type != QObjectType); } - explicit DiscoveredType(MemberExpressionResolver *memberResolver) - : type(QObjectType) - , memberResolver(memberResolver) - { Q_ASSERT(memberResolver); } - - bool test(Type t) const { return type & t; } - bool isNumber() const { return (type & NumberType) && !(type & ~NumberType); } - - bool operator!=(Type other) const { return type != other; } - bool operator==(Type other) const { return type == other; } - bool operator==(const DiscoveredType &other) const { return type == other.type; } - bool operator!=(const DiscoveredType &other) const { return type != other.type; } -}; - -struct MemberExpressionResolver -{ - typedef DiscoveredType (*ResolveFunction)(QQmlEnginePrivate *engine, - const MemberExpressionResolver *resolver, - Member *member); - - MemberExpressionResolver() - : resolveMember(0), import(nullptr), propertyCache(nullptr), typenameCache(nullptr), owner(nullptr), flags(0) {} - - bool isValid() const { return !!resolveMember; } - void clear() { *this = MemberExpressionResolver(); } - - ResolveFunction resolveMember; -#ifndef V4_BOOTSTRAP - QQmlType qmlType; -#endif - const QQmlImportRef *import; - QQmlPropertyCache *propertyCache; - QQmlTypeNameCache *typenameCache; - Function *owner; - unsigned int flags; -}; - -struct Q_AUTOTEST_EXPORT Expr { - enum ExprKind : quint8 { - NameExpr, - TempExpr, - ArgLocalExpr, - SubscriptExpr, - MemberExpr, - - LastLValue = MemberExpr, - - ConstExpr, - StringExpr, - RegExpExpr, - ClosureExpr, - ConvertExpr, - UnopExpr, - BinopExpr, - CallExpr, - NewExpr - }; - - Type type; - const ExprKind exprKind; - - Expr &operator=(const Expr &other) { - Q_ASSERT(exprKind == other.exprKind); - type = other.type; - return *this; - } - - template <typename To> - inline bool isa() const { - return To::classof(this); - } - - template <typename To> - inline To *as() { - if (isa<To>()) { - return static_cast<To *>(this); - } else { - return nullptr; - } - } - - template <typename To> - inline const To *as() const { - if (isa<To>()) { - return static_cast<const To *>(this); - } else { - return nullptr; - } - } - - Expr(ExprKind exprKind): type(UnknownType), exprKind(exprKind) {} - bool isLValue() const; - - Const *asConst(); - String *asString(); - RegExp *asRegExp(); - Name *asName(); - Temp *asTemp(); - ArgLocal *asArgLocal(); - Closure *asClosure(); - Convert *asConvert(); - Unop *asUnop(); - Binop *asBinop(); - Call *asCall(); - New *asNew(); - Subscript *asSubscript(); - Member *asMember(); -}; - -#define EXPR_VISIT_ALL_KINDS(e) \ - switch (e->exprKind) { \ - case QV4::IR::Expr::ConstExpr: \ - break; \ - case QV4::IR::Expr::StringExpr: \ - break; \ - case QV4::IR::Expr::RegExpExpr: \ - break; \ - case QV4::IR::Expr::NameExpr: \ - break; \ - case QV4::IR::Expr::TempExpr: \ - break; \ - case QV4::IR::Expr::ArgLocalExpr: \ - break; \ - case QV4::IR::Expr::ClosureExpr: \ - break; \ - case QV4::IR::Expr::ConvertExpr: { \ - auto casted = e->asConvert(); \ - visit(casted->expr); \ - } break; \ - case QV4::IR::Expr::UnopExpr: { \ - auto casted = e->asUnop(); \ - visit(casted->expr); \ - } break; \ - case QV4::IR::Expr::BinopExpr: { \ - auto casted = e->asBinop(); \ - visit(casted->left); \ - visit(casted->right); \ - } break; \ - case QV4::IR::Expr::CallExpr: { \ - auto casted = e->asCall(); \ - visit(casted->base); \ - for (QV4::IR::ExprList *it = casted->args; it; it = it->next) \ - visit(it->expr); \ - } break; \ - case QV4::IR::Expr::NewExpr: { \ - auto casted = e->asNew(); \ - visit(casted->base); \ - for (QV4::IR::ExprList *it = casted->args; it; it = it->next) \ - visit(it->expr); \ - } break; \ - case QV4::IR::Expr::SubscriptExpr: { \ - auto casted = e->asSubscript(); \ - visit(casted->base); \ - visit(casted->index); \ - } break; \ - case QV4::IR::Expr::MemberExpr: { \ - auto casted = e->asMember(); \ - visit(casted->base); \ - } break; \ - } - -struct ExprList { - Expr *expr; - ExprList *next; - - ExprList(): expr(0), next(0) {} - - void init(Expr *expr, ExprList *next = 0) - { - this->expr = expr; - this->next = next; - } -}; - -struct Const: Expr { - double value; - - Const(): Expr(ConstExpr) {} - - void init(Type type, double value) - { - this->type = type; - this->value = value; - } - - static bool classof(const Expr *c) { return c->exprKind == ConstExpr; } -}; - -struct String: Expr { - const QString *value; - - String(): Expr(StringExpr) {} - - void init(const QString *value) - { - this->value = value; - } - - static bool classof(const Expr *c) { return c->exprKind == StringExpr; } -}; - -struct RegExp: Expr { - // needs to be compatible with the flags in the lexer, and in RegExpObject - enum Flags { - RegExp_Global = 0x01, - RegExp_IgnoreCase = 0x02, - RegExp_Multiline = 0x04 - }; - - const QString *value; - int flags; - - RegExp(): Expr(RegExpExpr) {} - - void init(const QString *value, int flags) - { - this->value = value; - this->flags = flags; - } - - static bool classof(const Expr *c) { return c->exprKind == RegExpExpr; } -}; - -struct Name: Expr { - enum Builtin { - builtin_invalid, - builtin_typeof, - builtin_delete, - builtin_throw, - builtin_rethrow, - builtin_unwind_exception, - builtin_push_catch_scope, - builtin_foreach_iterator_object, - builtin_foreach_next_property_name, - builtin_push_with_scope, - builtin_pop_scope, - builtin_declare_vars, - builtin_define_array, - builtin_define_object_literal, - builtin_setup_argument_object, - builtin_convert_this_to_object, - builtin_qml_context, - builtin_qml_imported_scripts_object - }; - - const QString *id; - Builtin builtin; - bool global : 1; - bool qmlSingleton : 1; - bool freeOfSideEffects : 1; - quint32 line; - quint32 column; - - Name(): Expr(NameExpr) {} - - void initGlobal(const QString *id, quint32 line, quint32 column); - void init(const QString *id, quint32 line, quint32 column); - void init(Builtin builtin, quint32 line, quint32 column); - - static bool classof(const Expr *c) { return c->exprKind == NameExpr; } -}; - -struct Q_AUTOTEST_EXPORT Temp: Expr { - enum Kind { - Invalid = 0, - VirtualRegister, - PhysicalRegister, - StackSlot - }; - - unsigned index : 28; - unsigned isReadOnly : 1; - unsigned kind : 3; - - // Used when temp is used as base in member expression - MemberExpressionResolver *memberResolver; - - Temp() - : Expr(TempExpr) - , index((1 << 28) - 1) - , isReadOnly(0) - , kind(Invalid) - , memberResolver(0) - {} - - Temp(Type type, Kind kind, unsigned index) - : Expr(TempExpr) - , index(index) - , isReadOnly(0) - , kind(kind) - , memberResolver(0) - { - this->type = type; - } - - void init(unsigned kind, unsigned index) - { - this->index = index; - this->isReadOnly = false; - this->kind = kind; - } - - bool isInvalid() const { return kind == Invalid; } - - static bool classof(const Expr *c) { return c->exprKind == TempExpr; } -}; - -inline bool operator==(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW -{ return t1.index == t2.index && t1.kind == t2.kind && t1.type == t2.type; } - -inline bool operator!=(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW -{ return !(t1 == t2); } - -inline uint qHash(const Temp &t, uint seed = 0) Q_DECL_NOTHROW -{ return t.index ^ t.kind ^ seed; } - -bool operator<(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW; - -struct Q_AUTOTEST_EXPORT ArgLocal: Expr { - enum Kind { - Formal = 0, - ScopedFormal, - Local, - ScopedLocal - }; - - unsigned index; - unsigned scope : 29; // how many scopes outside the current one? - unsigned kind : 2; - unsigned isArgumentsOrEval : 1; - - void init(unsigned kind, unsigned index, unsigned scope) - { - Q_ASSERT((kind == ScopedLocal && scope != 0) || - (kind == ScopedFormal && scope != 0) || - (scope == 0)); - - this->kind = kind; - this->index = index; - this->scope = scope; - this->isArgumentsOrEval = false; - } - - ArgLocal(): Expr(ArgLocalExpr) {} - - bool operator==(const ArgLocal &other) const - { return index == other.index && scope == other.scope && kind == other.kind; } - - static bool classof(const Expr *c) { return c->exprKind == ArgLocalExpr; } -}; - -struct Closure: Expr { - int value; // index in _module->functions - const QString *functionName; - - Closure(): Expr(ClosureExpr) {} - - void init(int functionInModule, const QString *functionName) - { - this->value = functionInModule; - this->functionName = functionName; - } - - static bool classof(const Expr *c) { return c->exprKind == ClosureExpr; } -}; - -struct Convert: Expr { - Expr *expr; - - Convert(): Expr(ConvertExpr) {} - - void init(Expr *expr, Type type) - { - this->expr = expr; - this->type = type; - } - - static bool classof(const Expr *c) { return c->exprKind == ConvertExpr; } -}; - -struct Unop: Expr { - Expr *expr; - AluOp op; - - Unop(): Expr(UnopExpr) {} - - void init(AluOp op, Expr *expr) - { - this->op = op; - this->expr = expr; - } - - static bool classof(const Expr *c) { return c->exprKind == UnopExpr; } -}; - -struct Binop: Expr { - Expr *left; // Temp or Const - Expr *right; // Temp or Const - AluOp op; - - Binop(): Expr(BinopExpr) {} - - void init(AluOp op, Expr *left, Expr *right) - { - this->op = op; - this->left = left; - this->right = right; - } - - static bool classof(const Expr *c) { return c->exprKind == BinopExpr; } -}; - -struct Call: Expr { - Expr *base; // Name, Member, Temp - ExprList *args; // List of Temps - - Call(): Expr(CallExpr) {} - - void init(Expr *base, ExprList *args) - { - this->base = base; - this->args = args; - } - - Expr *onlyArgument() const { - if (args && ! args->next) - return args->expr; - return 0; - } - - static bool classof(const Expr *c) { return c->exprKind == CallExpr; } -}; - -struct New: Expr { - Expr *base; // Name, Member, Temp - ExprList *args; // List of Temps - - New(): Expr(NewExpr) {} - - void init(Expr *base, ExprList *args) - { - this->base = base; - this->args = args; - } - - Expr *onlyArgument() const { - if (args && ! args->next) - return args->expr; - return 0; - } - - static bool classof(const Expr *c) { return c->exprKind == NewExpr; } -}; - -struct Subscript: Expr { - Expr *base; - Expr *index; - - Subscript(): Expr(SubscriptExpr) {} - - void init(Expr *base, Expr *index) - { - this->base = base; - this->index = index; - } - - static bool classof(const Expr *c) { return c->exprKind == SubscriptExpr; } -}; - -struct Member: Expr { - // Used for property dependency tracking - enum MemberKind { - UnspecifiedMember, - MemberOfEnum, - MemberOfQmlScopeObject, - MemberOfQmlContextObject, - MemberOfIdObjectsArray, - MemberOfSingletonObject, - }; - - Expr *base; - const QString *name; - QQmlPropertyData *property; - union { // depending on kind - int attachedPropertiesId; - int enumValue; - int idIndex; - }; - uchar freeOfSideEffects : 1; - - // This is set for example for for QObject properties. All sorts of extra behavior - // is defined when writing to them, for example resettable properties are reset - // when writing undefined to them, and an exception is thrown when they're missing - // a reset function. And then there's also Qt.binding(). - uchar inhibitTypeConversionOnWrite: 1; - - uchar kind: 3; // MemberKind - - Member(): Expr(MemberExpr) {} - - void setEnumValue(int value) { - kind = MemberOfEnum; - enumValue = value; - } - - void setAttachedPropertiesId(int id) { - Q_ASSERT(kind != MemberOfEnum && kind != MemberOfIdObjectsArray); - attachedPropertiesId = id; - } - - void init(Expr *base, const QString *name, QQmlPropertyData *property = 0, uchar kind = UnspecifiedMember, int index = 0) - { - this->base = base; - this->name = name; - this->property = property; - this->idIndex = index; - this->freeOfSideEffects = false; - this->inhibitTypeConversionOnWrite = property != 0; - this->kind = kind; - } - - static bool classof(const Expr *c) { return c->exprKind == MemberExpr; } -}; - -inline bool Expr::isLValue() const { - if (auto t = as<Temp>()) - return !t->isReadOnly; - return exprKind <= LastLValue; -} - -struct Stmt { - enum StmtKind: quint8 { - MoveStmt, - ExpStmt, - JumpStmt, - CJumpStmt, - RetStmt, - PhiStmt - }; - - template <typename To> - inline bool isa() const { - return To::classof(this); - } - - template <typename To> - inline To *as() { - if (isa<To>()) - return static_cast<To *>(this); - else - return nullptr; - } - - enum { InvalidId = -1 }; - - QQmlJS::AST::SourceLocation location; - - explicit Stmt(int id, StmtKind stmtKind): _id(id), stmtKind(stmtKind) {} - - Stmt *asTerminator(); - - Exp *asExp(); - Move *asMove(); - Jump *asJump(); - CJump *asCJump(); - Ret *asRet(); - Phi *asPhi(); - - int id() const { return _id; } - -private: // For memory management in BasicBlock - friend struct BasicBlock; - -private: - friend struct Function; - int _id; - -public: - const StmtKind stmtKind; -}; - -#define STMT_VISIT_ALL_KINDS(s) \ - switch (s->stmtKind) { \ - case QV4::IR::Stmt::MoveStmt: { \ - auto casted = s->asMove(); \ - visit(casted->target); \ - visit(casted->source); \ - } break; \ - case QV4::IR::Stmt::ExpStmt: { \ - auto casted = s->asExp(); \ - visit(casted->expr); \ - } break; \ - case QV4::IR::Stmt::JumpStmt: \ - break; \ - case QV4::IR::Stmt::CJumpStmt: { \ - auto casted = s->asCJump(); \ - visit(casted->cond); \ - } break; \ - case QV4::IR::Stmt::RetStmt: { \ - auto casted = s->asRet(); \ - visit(casted->expr); \ - } break; \ - case QV4::IR::Stmt::PhiStmt: { \ - auto casted = s->asPhi(); \ - visit(casted->targetTemp); \ - for (auto *e : casted->incoming) { \ - visit(e); \ - } \ - } break; \ - } - -struct Exp: Stmt { - Expr *expr; - - Exp(int id): Stmt(id, ExpStmt) {} - - void init(Expr *expr) - { - this->expr = expr; - } - - static bool classof(const Stmt *c) { return c->stmtKind == ExpStmt; } -}; - -struct Move: Stmt { - Expr *target; // LHS - Temp, Name, Member or Subscript - Expr *source; - bool swap; - - Move(int id): Stmt(id, MoveStmt) {} - - void init(Expr *target, Expr *source) - { - this->target = target; - this->source = source; - this->swap = false; - } - - static bool classof(const Stmt *c) { return c->stmtKind == MoveStmt; } -}; - -struct Jump: Stmt { - BasicBlock *target; - - Jump(int id): Stmt(id, JumpStmt) {} - - void init(BasicBlock *target) - { - this->target = target; - } - - static bool classof(const Stmt *c) { return c->stmtKind == JumpStmt; } -}; - -struct CJump: Stmt { - Expr *cond; // Temp, Binop - BasicBlock *iftrue; - BasicBlock *iffalse; - BasicBlock *parent; - - CJump(int id): Stmt(id, CJumpStmt) {} - - void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse, BasicBlock *parent) - { - this->cond = cond; - this->iftrue = iftrue; - this->iffalse = iffalse; - this->parent = parent; - } - - static bool classof(const Stmt *c) { return c->stmtKind == CJumpStmt; } -}; - -struct Ret: Stmt { - Expr *expr; - - Ret(int id): Stmt(id, RetStmt) {} - - void init(Expr *expr) - { - this->expr = expr; - } - - static bool classof(const Stmt *c) { return c->stmtKind == RetStmt; } -}; - -// Phi nodes can only occur at the start of a basic block. If there are any, they need to be -// subsequent to eachother, and the first phi node should be the first statement in the basic-block. -// A number of loops rely on this behavior, so they don't need to walk through the whole list -// of instructions in a basic-block (e.g. the calls to destroyData in BasicBlock::~BasicBlock). -struct Phi: Stmt { - Temp *targetTemp; - VarLengthArray<Expr *, 4> incoming; - - Phi(int id): Stmt(id, PhiStmt) {} - - static bool classof(const Stmt *c) { return c->stmtKind == PhiStmt; } - - void destroyData() - { incoming.~VarLengthArray(); } -}; - -inline Stmt *Stmt::asTerminator() -{ - if (auto s = asJump()) { - return s; - } else if (auto s = asCJump()) { - return s; - } else if (auto s = asRet()) { - return s; - } else { - return nullptr; - } -} - -struct Q_QML_PRIVATE_EXPORT Module { - QQmlJS::MemoryPool pool; - QVector<Function *> functions; - Function *rootFunction; - QString fileName; - QDateTime sourceTimeStamp; - bool isQmlModule; // implies rootFunction is always 0 - uint unitFlags; // flags merged into CompiledData::Unit::flags - QString targetABI; // fallback to QSysInfo::buildAbi() if empty -#if !QT_CONFIG(qml_debug) - static const bool debugMode = false; -#else - bool debugMode; -#endif - - Function *newFunction(const QString &name, Function *outer); - - Module(bool debugMode) - : rootFunction(0) - , isQmlModule(false) - , unitFlags(0) -#if QT_CONFIG(qml_debug) - , debugMode(debugMode) - {} -#else - { Q_UNUSED(debugMode); } -#endif - ~Module(); - - void setFileName(const QString &name); -}; - -struct BasicBlock { -private: - Q_DISABLE_COPY(BasicBlock) - -public: - typedef VarLengthArray<BasicBlock *, 4> IncomingEdges; - typedef VarLengthArray<BasicBlock *, 2> OutgoingEdges; - - Function *function; - BasicBlock *catchBlock; - IncomingEdges in; - OutgoingEdges out; - QQmlJS::AST::SourceLocation nextLocation; - - BasicBlock(Function *function, BasicBlock *catcher) - : function(function) - , catchBlock(catcher) - , _containingGroup(0) - , _index(-1) - , _isExceptionHandler(false) - , _groupStart(false) - , _isRemoved(false) - {} - - ~BasicBlock() - { - for (Stmt *s : qAsConst(_statements)) { - if (Phi *p = s->asPhi()) { - p->destroyData(); - } else { - break; - } - } - } - - const QVector<Stmt *> &statements() const - { - Q_ASSERT(!isRemoved()); - return _statements; - } - - int statementCount() const - { - Q_ASSERT(!isRemoved()); - return _statements.size(); - } - - void setStatements(const QVector<Stmt *> &newStatements); - - template <typename Instr> inline Instr i(Instr i) - { - Q_ASSERT(!isRemoved()); - appendStatement(i); - return i; - } - - void appendStatement(Stmt *statement) - { - Q_ASSERT(!isRemoved()); - if (nextLocation.startLine) - statement->location = nextLocation; - _statements.append(statement); - } - - void prependStatement(Stmt *stmt) - { - Q_ASSERT(!isRemoved()); - _statements.prepend(stmt); - } - - void prependStatements(const QVector<Stmt *> &stmts) - { - Q_ASSERT(!isRemoved()); - QVector<Stmt *> newStmts = stmts; - newStmts += _statements; - _statements = newStmts; - } - - void insertStatementBefore(Stmt *before, Stmt *newStmt) - { - int idx = _statements.indexOf(before); - Q_ASSERT(idx >= 0); - _statements.insert(idx, newStmt); - } - - void insertStatementBefore(int index, Stmt *newStmt) - { - Q_ASSERT(index >= 0); - _statements.insert(index, newStmt); - } - - void insertStatementBeforeTerminator(Stmt *stmt) - { - Q_ASSERT(!isRemoved()); - _statements.insert(_statements.size() - 1, stmt); - } - - void replaceStatement(int index, Stmt *newStmt) - { - Q_ASSERT(!isRemoved()); - if (Phi *p = _statements[index]->asPhi()) { - p->destroyData(); - } - _statements[index] = newStmt; - } - - void removeStatement(Stmt *stmt) - { - Q_ASSERT(!isRemoved()); - if (Phi *p = stmt->asPhi()) { - p->destroyData(); - } - _statements.remove(_statements.indexOf(stmt)); - } - - void removeStatement(int idx) - { - Q_ASSERT(!isRemoved()); - if (Phi *p = _statements[idx]->asPhi()) { - p->destroyData(); - } - _statements.remove(idx); - } - - inline bool isEmpty() const { - Q_ASSERT(!isRemoved()); - return _statements.isEmpty(); - } - - inline Stmt *terminator() const { - Q_ASSERT(!isRemoved()); - if (! _statements.isEmpty() && _statements.last()->asTerminator() != 0) - return _statements.last(); - return 0; - } - - inline bool isTerminated() const { - Q_ASSERT(!isRemoved()); - if (terminator() != 0) - return true; - return false; - } - - enum TempForWhom { - NewTempForCodegen, - NewTempForOptimizer - }; - unsigned newTemp(TempForWhom tfw = NewTempForCodegen); - - Temp *TEMP(unsigned kind); - ArgLocal *ARG(unsigned index, unsigned scope); - ArgLocal *LOCAL(unsigned index, unsigned scope); - - Expr *CONST(Type type, double value); - Expr *STRING(const QString *value); - Expr *REGEXP(const QString *value, int flags); - - Name *NAME(const QString &id, quint32 line, quint32 column); - Name *NAME(Name::Builtin builtin, quint32 line, quint32 column); - - Name *GLOBALNAME(const QString &id, quint32 line, quint32 column); - - Closure *CLOSURE(int functionInModule); - - Expr *CONVERT(Expr *expr, Type type); - Expr *UNOP(AluOp op, Expr *expr); - Expr *BINOP(AluOp op, Expr *left, Expr *right); - Expr *CALL(Expr *base, ExprList *args = 0); - Expr *NEW(Expr *base, ExprList *args = 0); - Expr *SUBSCRIPT(Expr *base, Expr *index); - Expr *MEMBER(Expr *base, const QString *name, QQmlPropertyData *property = 0, uchar kind = Member::UnspecifiedMember, int attachedPropertiesIdOrEnumValue = 0); - - Stmt *EXP(Expr *expr); - - Stmt *MOVE(Expr *target, Expr *source); - - Stmt *JUMP(BasicBlock *target); - Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse); - Stmt *RET(Expr *expr); - - BasicBlock *containingGroup() const - { - Q_ASSERT(!isRemoved()); - return _containingGroup; - } - - void setContainingGroup(BasicBlock *loopHeader) - { - Q_ASSERT(!isRemoved()); - _containingGroup = loopHeader; - } - - bool isGroupStart() const - { - Q_ASSERT(!isRemoved()); - return _groupStart; - } - - void markAsGroupStart(bool mark = true) - { - Q_ASSERT(!isRemoved()); - _groupStart = mark; - } - - // Returns the index of the basic-block. - // See Function for the full description. - int index() const - { - Q_ASSERT(!isRemoved()); - return _index; - } - - bool isExceptionHandler() const - { return _isExceptionHandler; } - - void setExceptionHandler(bool onoff) - { _isExceptionHandler = onoff; } - - bool isRemoved() const - { return _isRemoved; } - -private: // For Function's eyes only. - friend struct Function; - void setIndex(int index) - { - Q_ASSERT(_index < 0); - changeIndex(index); - } - - void changeIndex(int index) - { - Q_ASSERT(index >= 0); - _index = index; - } - - void markAsRemoved() - { - _isRemoved = true; - _index = -1; - } - -private: - QVector<Stmt *> _statements; - BasicBlock *_containingGroup; - int _index; - unsigned _isExceptionHandler : 1; - unsigned _groupStart : 1; - unsigned _isRemoved : 1; -}; - -template <typename T> -class SmallSet: public QVarLengthArray<T, 8> -{ -public: - void insert(int value) - { - for (auto it : *this) { - if (it == value) - return; - } - this->append(value); - } -}; - -// Map from meta property index (existence implies dependency) to notify signal index -struct KeyValuePair -{ - quint32 _key; - quint32 _value; - - KeyValuePair(): _key(0), _value(0) {} - KeyValuePair(quint32 key, quint32 value): _key(key), _value(value) {} - - quint32 key() const { return _key; } - quint32 value() const { return _value; } -}; - -class PropertyDependencyMap: public QVarLengthArray<KeyValuePair, 8> -{ -public: - void insert(quint32 key, quint32 value) - { - for (auto it = begin(), eit = end(); it != eit; ++it) { - if (it->_key == key) { - it->_value = value; - return; - } - } - append(KeyValuePair(key, value)); - } -}; - -// The Function owns (manages), among things, a list of basic-blocks. All the blocks have an index, -// which corresponds to the index in the entry/index in the vector in which they are stored. This -// means that algorithms/classes can also store any information about a basic block in an array, -// where the index corresponds to the index of the basic block, which can then be used to query -// the function for a pointer to a basic block. This also means that basic-blocks cannot be removed -// or renumbered. -// -// Note that currently there is one exception: after optimization and block scheduling, the -// method setScheduledBlocks can be called once, to register a newly ordered list. For debugging -// purposes, these blocks are not immediately renumbered, so renumberBasicBlocks should be called -// immediately after changing the order. That will restore the property of having a corresponding -// block-index and block-position-in-basicBlocks-vector. -// -// In order for optimization/transformation passes to skip uninteresting basic blocks that will be -// removed, the block can be marked as such. After doing so, any access will result in a failing -// assertion. -struct Function { - Module *module; - QQmlJS::MemoryPool *pool; - const QString *name; - int currentTemp = 0; - int tempCount; - int maxNumberOfArguments; - QSet<QString> strings; - QList<const QString *> formals; - QList<const QString *> locals; - QVector<Function *> nestedFunctions; - Function *outer; - - int insideWithOrCatch; - - uint hasDirectEval: 1; - uint usesArgumentsObject : 1; - uint usesThis : 1; - uint isStrict: 1; - uint isNamedExpression : 1; - uint hasTry: 1; - uint hasWith: 1; - uint isQmlBinding: 1; - uint unused : 24; - - // Location of declaration in source code (0 if not specified) - uint line; - uint column; - - // Qml extension: - SmallSet<int> idObjectDependencies; - PropertyDependencyMap contextObjectPropertyDependencies; - PropertyDependencyMap scopeObjectPropertyDependencies; - - template <typename T> T *New() { return new (pool->allocate(sizeof(T))) T(); } - template <typename T> T *NewStmt() { - return new (pool->allocate(sizeof(T))) T(getNewStatementId()); - } - - Function(Module *module, Function *outer, const QString &name); - ~Function(); - - enum BasicBlockInsertMode { - InsertBlock, - DontInsertBlock - }; - - BasicBlock *newBasicBlock(BasicBlock *catchBlock, BasicBlockInsertMode mode = InsertBlock); - const QString *newString(const QString &text); - - void RECEIVE(const QString &name) { formals.append(newString(name)); } - void LOCAL(const QString &name) { locals.append(newString(name)); } - - BasicBlock *addBasicBlock(BasicBlock *block); - void removeBasicBlock(BasicBlock *block); - - const QVector<BasicBlock *> &basicBlocks() const - { return _basicBlocks; } - - BasicBlock *basicBlock(int idx) const - { return _basicBlocks.at(idx); } - - int basicBlockCount() const - { return _basicBlocks.size(); } - - int liveBasicBlocksCount() const; - - void removeSharedExpressions(); - - int indexOfArgument(const QStringRef &string) const; - - bool variablesCanEscape() const - { return hasDirectEval || !nestedFunctions.isEmpty() || module->debugMode; } - - void setScheduledBlocks(const QVector<BasicBlock *> &scheduled); - - int getNewStatementId() { return _statementCount++; } - int statementCount() const { return _statementCount; } - - bool canUseSimpleCall() const { - return nestedFunctions.isEmpty() && - locals.isEmpty() && formals.size() <= QV4::Global::ReservedArgumentCount && - !hasTry && !hasWith && !isNamedExpression && !usesArgumentsObject && !hasDirectEval; - } - - bool argLocalRequiresWriteBarrier(ArgLocal *al) const { - uint scope = al->scope; - const IR::Function *f = this; - while (scope) { - f = f->outer; - --scope; - } - return !f->canUseSimpleCall(); - } - int localsCountForScope(ArgLocal *al) const { - uint scope = al->scope; - const IR::Function *f = this; - while (scope) { - f = f->outer; - --scope; - } - return f->locals.size(); - } - -private: - BasicBlock *getOrCreateBasicBlock(int index); - void setStatementCount(int cnt); - -private: - QVector<BasicBlock *> _basicBlocks; - QVector<BasicBlock *> *_allBasicBlocks; - int _statementCount; -}; - -class CloneExpr -{ -public: - explicit CloneExpr(IR::BasicBlock *block = 0); - - void setBasicBlock(IR::BasicBlock *block); - - template <typename ExprSubclass> - ExprSubclass *operator()(ExprSubclass *expr) - { - return clone(expr); - } - - template <typename ExprSubclass> - ExprSubclass *clone(ExprSubclass *expr) - { - Expr *c = expr; - qSwap(cloned, c); - visit(expr); - qSwap(cloned, c); - return static_cast<ExprSubclass *>(c); - } - - static Const *cloneConst(Const *c, Function *f) - { - Const *newConst = f->New<Const>(); - newConst->init(c->type, c->value); - return newConst; - } - - static Name *cloneName(Name *n, Function *f) - { - Name *newName = f->New<Name>(); - newName->type = n->type; - newName->id = n->id; - newName->builtin = n->builtin; - newName->global = n->global; - newName->qmlSingleton = n->qmlSingleton; - newName->freeOfSideEffects = n->freeOfSideEffects; - newName->line = n->line; - newName->column = n->column; - return newName; - } - - static Temp *cloneTemp(Temp *t, Function *f) - { - Temp *newTemp = f->New<Temp>(); - newTemp->init(t->kind, t->index); - newTemp->type = t->type; - newTemp->memberResolver = t->memberResolver; - return newTemp; - } - - static ArgLocal *cloneArgLocal(ArgLocal *argLocal, Function *f) - { - ArgLocal *newArgLocal = f->New<ArgLocal>(); - newArgLocal->init(argLocal->kind, argLocal->index, argLocal->scope); - newArgLocal->type = argLocal->type; - newArgLocal->isArgumentsOrEval = argLocal->isArgumentsOrEval; - return newArgLocal; - } - -private: - IR::ExprList *clone(IR::ExprList *list); - - void visit(Expr *e); - -protected: - IR::BasicBlock *block; - -private: - IR::Expr *cloned; -}; - -class Q_AUTOTEST_EXPORT IRPrinter -{ -public: - IRPrinter(QTextStream *out); - virtual ~IRPrinter(); - - void print(Stmt *s); - void print(Expr *e); - void print(const Expr &e); - - virtual void print(Function *f); - virtual void print(BasicBlock *bb); - - void visit(Stmt *s); - virtual void visitExp(Exp *s); - virtual void visitMove(Move *s); - virtual void visitJump(Jump *s); - virtual void visitCJump(CJump *s); - virtual void visitRet(Ret *s); - virtual void visitPhi(Phi *s); - - void visit(Expr *e); - virtual void visitConst(Const *e); - virtual void visitString(String *e); - virtual void visitRegExp(RegExp *e); - virtual void visitName(Name *e); - virtual void visitTemp(Temp *e); - virtual void visitArgLocal(ArgLocal *e); - virtual void visitClosure(Closure *e); - virtual void visitConvert(Convert *e); - virtual void visitUnop(Unop *e); - virtual void visitBinop(Binop *e); - virtual void visitCall(Call *e); - virtual void visitNew(New *e); - virtual void visitSubscript(Subscript *e); - virtual void visitMember(Member *e); - - static QString escape(const QString &s); - -protected: - virtual void addStmtNr(Stmt *s); - void addJustifiedNr(int pos); - void printBlockStart(); - -protected: - QTextStream *out; - int positionSize; - BasicBlock *currentBB; -}; - -inline unsigned BasicBlock::newTemp(TempForWhom tfw) -{ - Q_ASSERT(!isRemoved()); - - if (tfw == NewTempForOptimizer) - return function->tempCount++; - - int t = function->currentTemp++; - if (function->tempCount < function->currentTemp) - function->tempCount = function->currentTemp; - return t; -} - -inline Temp *BasicBlock::TEMP(unsigned index) -{ - Q_ASSERT(!isRemoved()); - Temp *e = function->New<Temp>(); - e->init(Temp::VirtualRegister, index); - return e; -} - -inline ArgLocal *BasicBlock::ARG(unsigned index, unsigned scope) -{ - Q_ASSERT(!isRemoved()); - ArgLocal *e = function->New<ArgLocal>(); - e->init(scope ? ArgLocal::ScopedFormal : ArgLocal::Formal, index, scope); - return e; -} - -inline ArgLocal *BasicBlock::LOCAL(unsigned index, unsigned scope) -{ - Q_ASSERT(!isRemoved()); - ArgLocal *e = function->New<ArgLocal>(); - e->init(scope ? ArgLocal::ScopedLocal : ArgLocal::Local, index, scope); - return e; -} - -inline Expr *BasicBlock::CONST(Type type, double value) -{ - Q_ASSERT(!isRemoved()); - Const *e = function->New<Const>(); - if (type == NumberType) { - int ival = (int)value; - // +0 != -0, so we need to convert to double when negating 0 - if (ival == value && !(value == 0 && isNegative(value))) - type = SInt32Type; - else - type = DoubleType; - } else if (type == NullType) { - value = 0; - } else if (type == UndefinedType) { - value = qt_qnan(); - } - - e->init(type, value); - return e; -} - -inline Expr *BasicBlock::STRING(const QString *value) -{ - Q_ASSERT(!isRemoved()); - String *e = function->New<String>(); - e->init(value); - return e; -} - -inline Expr *BasicBlock::REGEXP(const QString *value, int flags) -{ - Q_ASSERT(!isRemoved()); - RegExp *e = function->New<RegExp>(); - e->init(value, flags); - return e; -} - -inline Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column) -{ - Q_ASSERT(!isRemoved()); - Name *e = function->New<Name>(); - e->init(function->newString(id), line, column); - return e; -} - -inline Name *BasicBlock::GLOBALNAME(const QString &id, quint32 line, quint32 column) -{ - Q_ASSERT(!isRemoved()); - Name *e = function->New<Name>(); - e->initGlobal(function->newString(id), line, column); - return e; -} - - -inline Name *BasicBlock::NAME(Name::Builtin builtin, quint32 line, quint32 column) -{ - Q_ASSERT(!isRemoved()); - Name *e = function->New<Name>(); - e->init(builtin, line, column); - return e; -} - -inline Closure *BasicBlock::CLOSURE(int functionInModule) -{ - Q_ASSERT(!isRemoved()); - Closure *clos = function->New<Closure>(); - clos->init(functionInModule, function->module->functions.at(functionInModule)->name); - return clos; -} - -inline Expr *BasicBlock::CONVERT(Expr *expr, Type type) -{ - Q_ASSERT(!isRemoved()); - Convert *e = function->New<Convert>(); - e->init(expr, type); - return e; -} - -inline Expr *BasicBlock::UNOP(AluOp op, Expr *expr) -{ - Q_ASSERT(!isRemoved()); - Unop *e = function->New<Unop>(); - e->init(op, expr); - return e; -} - -inline Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right) -{ - Q_ASSERT(!isRemoved()); - Binop *e = function->New<Binop>(); - e->init(op, left, right); - return e; -} - -inline Expr *BasicBlock::CALL(Expr *base, ExprList *args) -{ - Q_ASSERT(!isRemoved()); - Call *e = function->New<Call>(); - e->init(base, args); - int argc = 0; - for (ExprList *it = args; it; it = it->next) - ++argc; - function->maxNumberOfArguments = qMax(function->maxNumberOfArguments, argc); - return e; -} - -inline Expr *BasicBlock::NEW(Expr *base, ExprList *args) -{ - Q_ASSERT(!isRemoved()); - New *e = function->New<New>(); - e->init(base, args); - return e; -} - -inline Expr *BasicBlock::SUBSCRIPT(Expr *base, Expr *index) -{ - Q_ASSERT(!isRemoved()); - Subscript *e = function->New<Subscript>(); - e->init(base, index); - return e; -} - -inline Expr *BasicBlock::MEMBER(Expr *base, const QString *name, QQmlPropertyData *property, uchar kind, int attachedPropertiesIdOrEnumValue) -{ - Q_ASSERT(!isRemoved()); - Member*e = function->New<Member>(); - e->init(base, name, property, kind, attachedPropertiesIdOrEnumValue); - return e; -} - -inline Stmt *BasicBlock::EXP(Expr *expr) -{ - Q_ASSERT(!isRemoved()); - if (isTerminated()) - return 0; - - Exp *s = function->NewStmt<Exp>(); - s->init(expr); - appendStatement(s); - return s; -} - -inline Stmt *BasicBlock::MOVE(Expr *target, Expr *source) -{ - Q_ASSERT(!isRemoved()); - if (isTerminated()) - return 0; - - Move *s = function->NewStmt<Move>(); - s->init(target, source); - appendStatement(s); - return s; -} - -inline Stmt *BasicBlock::JUMP(BasicBlock *target) -{ - Q_ASSERT(!isRemoved()); - if (isTerminated()) - return 0; - - Jump *s = function->NewStmt<Jump>(); - s->init(target); - appendStatement(s); - - Q_ASSERT(! out.contains(target)); - out.append(target); - - Q_ASSERT(! target->in.contains(this)); - target->in.append(this); - - return s; -} - -inline Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) -{ - Q_ASSERT(!isRemoved()); - if (isTerminated()) - return 0; - - if (iftrue == iffalse) { - MOVE(TEMP(newTemp()), cond); - return JUMP(iftrue); - } - - CJump *s = function->NewStmt<CJump>(); - s->init(cond, iftrue, iffalse, this); - appendStatement(s); - - Q_ASSERT(! out.contains(iftrue)); - out.append(iftrue); - - Q_ASSERT(! iftrue->in.contains(this)); - iftrue->in.append(this); - - Q_ASSERT(! out.contains(iffalse)); - out.append(iffalse); - - Q_ASSERT(! iffalse->in.contains(this)); - iffalse->in.append(this); - - return s; -} - -inline Stmt *BasicBlock::RET(Expr *expr) -{ - Q_ASSERT(!isRemoved()); - if (isTerminated()) - return 0; - - Ret *s = function->NewStmt<Ret>(); - s->init(expr); - appendStatement(s); - return s; -} - -inline Const *Expr::asConst() { return as<Const>(); } -inline String *Expr::asString() { return as<String>(); } -inline RegExp *Expr::asRegExp() { return as<RegExp>(); } -inline Name *Expr::asName() { return as<Name>(); } -inline Temp *Expr::asTemp() { return as<Temp>(); } -inline ArgLocal *Expr::asArgLocal() { return as<ArgLocal>(); } -inline Closure *Expr::asClosure() { return as<Closure>(); } -inline Convert *Expr::asConvert() { return as<Convert>(); } -inline Unop *Expr::asUnop() { return as<Unop>(); } -inline Binop *Expr::asBinop() { return as<Binop>(); } -inline Call *Expr::asCall() { return as<Call>(); } -inline New *Expr::asNew() { return as<New>(); } -inline Subscript *Expr::asSubscript() { return as<Subscript>(); } -inline Member *Expr::asMember() { return as<Member>(); } - -inline Exp *Stmt::asExp() { return as<Exp>(); } -inline Move *Stmt::asMove() { return as<Move>(); } -inline Jump *Stmt::asJump() { return as<Jump>(); } -inline CJump *Stmt::asCJump() { return as<CJump>(); } -inline Ret *Stmt::asRet() { return as<Ret>(); } -inline Phi *Stmt::asPhi() { return as<Phi>(); } - -} // end of namespace IR - -} // end of namespace QV4 - -QT_END_NAMESPACE - -#if defined(QT_POP_CONST) -# pragma pop_macro("CONST") // Restore peace -# undef QT_POP_CONST -#endif - -#endif // QV4IR_P_H diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp deleted file mode 100644 index cf79168c6c..0000000000 --- a/src/qml/compiler/qv4ssa.cpp +++ /dev/null @@ -1,5848 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -// When building with debug code, the macro below will enable debug helpers when using libc++. -// For example, the std::vector<T>::operator[] will use _LIBCPP_ASSERT to check if the index is -// within the array bounds. Note that this only works reliably with OSX 10.9 or later. -//#define _LIBCPP_DEBUG2 2 - -#include "qv4ssa_p.h" -#include "qv4isel_util_p.h" -#include "qv4util_p.h" - -#include <QtCore/QBuffer> -#include <QtCore/QCoreApplication> -#include <QtCore/QStringList> -#include <QtCore/QSet> -#include <QtCore/QLinkedList> -#include <QtCore/QStack> -#include <qv4runtime_p.h> -#include <cmath> -#include <iostream> -#include <cassert> - -QT_USE_NAMESPACE - -using namespace QV4; -using namespace IR; - -namespace { - -enum { DebugMoveMapping = 0 }; - -#ifdef QT_NO_DEBUG -enum { DoVerification = 0 }; -#else -enum { DoVerification = 1 }; -#endif - -static void showMeTheCode(IR::Function *function, const char *marker) -{ - static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_IR"); - if (showCode) { - qDebug() << marker; - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream stream(&buf); - IRPrinter(&stream).print(function); - stream << endl; - qDebug("%s", buf.data().constData()); - } -} - -class ProcessedBlocks -{ - BitVector processed; - -public: - ProcessedBlocks(IR::Function *function) - : processed(function->basicBlockCount(), false) - {} - - bool alreadyProcessed(BasicBlock *bb) const - { - Q_ASSERT(bb); - - return processed.at(bb->index()); - } - - void markAsProcessed(BasicBlock *bb) - { - processed.setBit(bb->index()); - } -}; - -class BasicBlockSet -{ - typedef BitVector Flags; - - QVarLengthArray<int, 8> blockNumbers; - Flags *blockFlags; - IR::Function *function; - enum { MaxVectorCapacity = 8 }; - -public: - class const_iterator - { - const BasicBlockSet &set; - // ### These two members could go into a union, but clang won't compile (https://codereview.qt-project.org/#change,74259) - QVarLengthArray<int, 8>::const_iterator numberIt; - int flagIt; - - friend class BasicBlockSet; - const_iterator(const BasicBlockSet &set, bool end) - : set(set) - { - if (end || !set.function) { - if (!set.blockFlags) - numberIt = set.blockNumbers.end(); - else - flagIt = set.blockFlags->size(); - } else { - if (!set.blockFlags) - numberIt = set.blockNumbers.begin(); - else - findNextWithFlags(0); - } - } - - void findNextWithFlags(int start) - { - flagIt = set.blockFlags->findNext(start, true, /*wrapAround = */false); - Q_ASSERT(flagIt <= set.blockFlags->size()); - } - - public: - BasicBlock *operator*() const - { - if (!set.blockFlags) { - return set.function->basicBlock(*numberIt); - } else { - Q_ASSERT(flagIt <= set.function->basicBlockCount()); - return set.function->basicBlock(flagIt); - } - } - - bool operator==(const const_iterator &other) const - { - if (&set != &other.set) - return false; - if (!set.blockFlags) - return numberIt == other.numberIt; - else - return flagIt == other.flagIt; - } - - bool operator!=(const const_iterator &other) const - { return !(*this == other); } - - const_iterator &operator++() - { - if (!set.blockFlags) - ++numberIt; - else - findNextWithFlags(flagIt + 1); - - return *this; - } - }; - - friend class const_iterator; - -public: - BasicBlockSet(IR::Function *f = 0): blockFlags(0), function(0) - { - if (f) - init(f); - } - -#ifdef Q_COMPILER_RVALUE_REFS - BasicBlockSet(BasicBlockSet &&other): blockFlags(0) - { - std::swap(blockNumbers, other.blockNumbers); - std::swap(blockFlags, other.blockFlags); - std::swap(function, other.function); - } -#endif // Q_COMPILER_RVALUE_REFS - - BasicBlockSet(const BasicBlockSet &other) - : blockFlags(0) - , function(other.function) - { - if (other.blockFlags) - blockFlags = new Flags(*other.blockFlags); - blockNumbers = other.blockNumbers; - } - - BasicBlockSet &operator=(const BasicBlockSet &other) - { - if (blockFlags) { - delete blockFlags; - blockFlags = 0; - } - function = other.function; - if (other.blockFlags) - blockFlags = new Flags(*other.blockFlags); - blockNumbers = other.blockNumbers; - return *this; - } - - ~BasicBlockSet() - { - delete blockFlags; - } - - void init(IR::Function *f) - { - Q_ASSERT(!function); - Q_ASSERT(f); - function = f; - } - - bool empty() const - { - return begin() == end(); - } - - void insert(BasicBlock *bb) - { - Q_ASSERT(function); - - if (blockFlags) { - blockFlags->setBit(bb->index()); - return; - } - - for (int i = 0; i < blockNumbers.size(); ++i) { - if (blockNumbers[i] == bb->index()) - return; - } - - if (blockNumbers.size() == MaxVectorCapacity) { - blockFlags = new Flags(function->basicBlockCount(), false); - for (int i = 0; i < blockNumbers.size(); ++i) { - blockFlags->setBit(blockNumbers[i]); - } - blockNumbers.clear(); - blockFlags->setBit(bb->index()); - } else { - blockNumbers.append(bb->index()); - } - } - - void remove(BasicBlock *bb) - { - Q_ASSERT(function); - - if (blockFlags) { - blockFlags->clearBit(bb->index()); - return; - } - - for (int i = 0; i < blockNumbers.size(); ++i) { - if (blockNumbers[i] == bb->index()) { - blockNumbers.remove(i); - return; - } - } - } - - const_iterator begin() const { return const_iterator(*this, false); } - const_iterator end() const { return const_iterator(*this, true); } - - void collectValues(std::vector<BasicBlock *> &bbs) const - { - Q_ASSERT(function); - - for (const_iterator it = begin(), eit = end(); it != eit; ++it) - bbs.push_back(*it); - } - - bool contains(BasicBlock *bb) const - { - Q_ASSERT(function); - - if (blockFlags) - return blockFlags->at(bb->index()); - - for (int i = 0; i < blockNumbers.size(); ++i) { - if (blockNumbers[i] == bb->index()) - return true; - } - - return false; - } -}; - -class DominatorTree -{ - enum { - DebugDominatorFrontiers = 0, - DebugImmediateDominators = 0, - - DebugCodeCanUseLotsOfCpu = 0 - }; - - typedef int BasicBlockIndex; - enum { InvalidBasicBlockIndex = -1 }; - - struct Data - { - int N; - std::vector<int> dfnum; // BasicBlock index -> dfnum - std::vector<int> vertex; - std::vector<BasicBlockIndex> parent; // BasicBlock index -> parent BasicBlock index - std::vector<BasicBlockIndex> ancestor; // BasicBlock index -> ancestor BasicBlock index - std::vector<BasicBlockIndex> best; // BasicBlock index -> best BasicBlock index - std::vector<BasicBlockIndex> semi; // BasicBlock index -> semi dominator BasicBlock index - std::vector<BasicBlockIndex> samedom; // BasicBlock index -> same dominator BasicBlock index - - Data(): N(0) {} - }; - - IR::Function *function; - QScopedPointer<Data> d; - std::vector<BasicBlockIndex> idom; // BasicBlock index -> immediate dominator BasicBlock index - std::vector<BasicBlockSet> DF; // BasicBlock index -> dominator frontier - - struct DFSTodo { - BasicBlockIndex node, parent; - - DFSTodo() - : node(InvalidBasicBlockIndex) - , parent(InvalidBasicBlockIndex) - {} - - DFSTodo(BasicBlockIndex node, BasicBlockIndex parent) - : node(node) - , parent(parent) - {} - }; - - void DFS(BasicBlockIndex node) { - std::vector<DFSTodo> worklist; - worklist.reserve(d->vertex.capacity() / 2); - DFSTodo todo(node, InvalidBasicBlockIndex); - - while (true) { - BasicBlockIndex n = todo.node; - - if (d->dfnum[n] == 0) { - d->dfnum[n] = d->N; - d->vertex[d->N] = n; - d->parent[n] = todo.parent; - ++d->N; - const BasicBlock::OutgoingEdges &out = function->basicBlock(n)->out; - for (int i = out.size() - 1; i > 0; --i) - worklist.push_back(DFSTodo(out[i]->index(), n)); - - if (out.size() > 0) { - todo.node = out.first()->index(); - todo.parent = n; - continue; - } - } - - if (worklist.empty()) - break; - - todo = worklist.back(); - worklist.pop_back(); - } - } - - BasicBlockIndex ancestorWithLowestSemi(BasicBlockIndex v, std::vector<BasicBlockIndex> &worklist) { - worklist.clear(); - for (BasicBlockIndex it = v; it != InvalidBasicBlockIndex; it = d->ancestor[it]) - worklist.push_back(it); - - if (worklist.size() < 2) - return d->best[v]; - - BasicBlockIndex b = InvalidBasicBlockIndex; - BasicBlockIndex last = worklist.back(); - Q_ASSERT(worklist.size() <= INT_MAX); - for (int it = static_cast<int>(worklist.size()) - 2; it >= 0; --it) { - BasicBlockIndex bbIt = worklist[it]; - d->ancestor[bbIt] = last; - BasicBlockIndex &best_it = d->best[bbIt]; - if (b != InvalidBasicBlockIndex && d->dfnum[d->semi[b]] < d->dfnum[d->semi[best_it]]) - best_it = b; - else - b = best_it; - } - return b; - } - - void link(BasicBlockIndex p, BasicBlockIndex n) { - d->ancestor[n] = p; - d->best[n] = n; - } - - void calculateIDoms() { - Q_ASSERT(function->basicBlock(0)->in.isEmpty()); - - const int bbCount = function->basicBlockCount(); - d->vertex = std::vector<int>(bbCount, InvalidBasicBlockIndex); - d->parent = std::vector<int>(bbCount, InvalidBasicBlockIndex); - d->dfnum = std::vector<int>(size_t(bbCount), 0); - d->semi = std::vector<BasicBlockIndex>(bbCount, InvalidBasicBlockIndex); - d->ancestor = std::vector<BasicBlockIndex>(bbCount, InvalidBasicBlockIndex); - idom = std::vector<BasicBlockIndex>(bbCount, InvalidBasicBlockIndex); - d->samedom = std::vector<BasicBlockIndex>(bbCount, InvalidBasicBlockIndex); - d->best = std::vector<BasicBlockIndex>(bbCount, InvalidBasicBlockIndex); - - QHash<BasicBlockIndex, std::vector<BasicBlockIndex> > bucket; - bucket.reserve(bbCount); - - DFS(function->basicBlock(0)->index()); - Q_ASSERT(d->N == function->liveBasicBlocksCount()); - - std::vector<BasicBlockIndex> worklist; - worklist.reserve(d->vertex.capacity() / 2); - - for (int i = d->N - 1; i > 0; --i) { - BasicBlockIndex n = d->vertex[i]; - BasicBlockIndex p = d->parent[n]; - BasicBlockIndex s = p; - - for (BasicBlock *v : function->basicBlock(n)->in) { - BasicBlockIndex ss = InvalidBasicBlockIndex; - if (d->dfnum[v->index()] <= d->dfnum[n]) - ss = v->index(); - else - ss = d->semi[ancestorWithLowestSemi(v->index(), worklist)]; - if (d->dfnum[ss] < d->dfnum[s]) - s = ss; - } - d->semi[n] = s; - bucket[s].push_back(n); - link(p, n); - if (bucket.contains(p)) { - for (BasicBlockIndex v : bucket[p]) { - BasicBlockIndex y = ancestorWithLowestSemi(v, worklist); - BasicBlockIndex semi_v = d->semi[v]; - if (d->semi[y] == semi_v) - idom[v] = semi_v; - else - d->samedom[v] = y; - } - bucket.remove(p); - } - } - - for (int i = 1; i < d->N; ++i) { - BasicBlockIndex n = d->vertex[i]; - Q_ASSERT(n != InvalidBasicBlockIndex); - Q_ASSERT(!bucket.contains(n)); - Q_ASSERT(d->ancestor[n] != InvalidBasicBlockIndex - && ((d->semi[n] != InvalidBasicBlockIndex - && d->dfnum[d->ancestor[n]] <= d->dfnum[d->semi[n]]) || d->semi[n] == n)); - BasicBlockIndex sdn = d->samedom[n]; - if (sdn != InvalidBasicBlockIndex) - idom[n] = idom[sdn]; - } - - dumpImmediateDominators(); - } - - struct NodeProgress { - std::vector<BasicBlockIndex> children; - std::vector<BasicBlockIndex> todo; - }; - -public: - DominatorTree(IR::Function *function) - : function(function) - , d(new Data) - { - calculateIDoms(); - d.reset(); - } - - void computeDF() { - DF.resize(function->basicBlockCount()); - - // compute children of each node in the dominator tree - std::vector<std::vector<BasicBlockIndex> > children; // BasicBlock index -> children - children.resize(function->basicBlockCount()); - for (BasicBlock *n : function->basicBlocks()) { - if (n->isRemoved()) - continue; - const BasicBlockIndex nodeIndex = n->index(); - Q_ASSERT(function->basicBlock(nodeIndex) == n); - const BasicBlockIndex nodeDominator = idom[nodeIndex]; - if (nodeDominator == InvalidBasicBlockIndex) - continue; // there is no dominator to add this node to as a child (e.g. the start node) - children[nodeDominator].push_back(nodeIndex); - } - - // Fill the worklist and initialize the node status for each basic-block - std::vector<NodeProgress> nodeStatus; - nodeStatus.resize(function->basicBlockCount()); - std::vector<BasicBlockIndex> worklist; - worklist.reserve(function->basicBlockCount()); - for (BasicBlock *bb : function->basicBlocks()) { - if (bb->isRemoved()) - continue; - BasicBlockIndex nodeIndex = bb->index(); - worklist.push_back(nodeIndex); - NodeProgress &np = nodeStatus[nodeIndex]; - np.children = children[nodeIndex]; - np.todo = children[nodeIndex]; - } - - BitVector DF_done(function->basicBlockCount(), false); - - while (!worklist.empty()) { - BasicBlockIndex node = worklist.back(); - - if (DF_done.at(node)) { - worklist.pop_back(); - continue; - } - - NodeProgress &np = nodeStatus[node]; - std::vector<BasicBlockIndex>::iterator it = np.todo.begin(); - while (it != np.todo.end()) { - if (DF_done.at(*it)) { - it = np.todo.erase(it); - } else { - worklist.push_back(*it); - break; - } - } - - if (np.todo.empty()) { - BasicBlockSet &S = DF[node]; - S.init(function); - for (BasicBlock *y : function->basicBlock(node)->out) - if (idom[y->index()] != node) - S.insert(y); - for (BasicBlockIndex child : np.children) { - const BasicBlockSet &ws = DF[child]; - for (BasicBlockSet::const_iterator it = ws.begin(), eit = ws.end(); it != eit; ++it) { - BasicBlock *w = *it; - const BasicBlockIndex wIndex = w->index(); - if (node == wIndex || !dominates(node, w->index())) - S.insert(w); - } - } - DF_done.setBit(node); - worklist.pop_back(); - } - } - - if (DebugDominatorFrontiers) { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - qout << "Dominator Frontiers:" << endl; - for (BasicBlock *n : function->basicBlocks()) { - if (n->isRemoved()) - continue; - - qout << "\tDF[" << n->index() << "]: {"; - const BasicBlockSet &SList = DF[n->index()]; - for (BasicBlockSet::const_iterator i = SList.begin(), ei = SList.end(); i != ei; ++i) { - if (i != SList.begin()) - qout << ", "; - qout << (*i)->index(); - } - qout << "}" << endl; - } - qDebug("%s", buf.data().constData()); - } - - if (DebugDominatorFrontiers && DebugCodeCanUseLotsOfCpu) { - for (BasicBlock *n : function->basicBlocks()) { - if (n->isRemoved()) - continue; - const BasicBlockSet &fBlocks = DF[n->index()]; - for (BasicBlockSet::const_iterator it = fBlocks.begin(), eit = fBlocks.end(); it != eit; ++it) { - BasicBlock *fBlock = *it; - Q_ASSERT(!dominates(n, fBlock) || fBlock == n); - bool hasDominatedSucc = false; - for (BasicBlock *succ : fBlock->in) { - if (dominates(n, succ)) { - hasDominatedSucc = true; - break; - } - } - if (!hasDominatedSucc) { - qDebug("%d in DF[%d] has no dominated predecessors", fBlock->index(), n->index()); - } - Q_ASSERT(hasDominatedSucc); - } - } - } - } - - const BasicBlockSet &dominatorFrontier(BasicBlock *n) const { - return DF[n->index()]; - } - - BasicBlock *immediateDominator(BasicBlock *bb) const { - const BasicBlockIndex idx = idom[bb->index()]; - if (idx == -1) - return 0; - return function->basicBlock(idx); - } - - void dumpImmediateDominators() const - { - if (DebugImmediateDominators) { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - qout << "Immediate dominators:" << endl; - for (BasicBlock *to : function->basicBlocks()) { - if (to->isRemoved()) - continue; - - qout << '\t'; - BasicBlockIndex from = idom.at(to->index()); - if (from != InvalidBasicBlockIndex) - qout << from; - else - qout << "(none)"; - qout << " dominates " << to->index() << endl; - } - qDebug("%s", buf.data().constData()); - } - } - - void setImmediateDominator(BasicBlock *bb, BasicBlock *newDominator) - { - Q_ASSERT(bb->index() >= 0); - Q_ASSERT(!newDominator || newDominator->index() >= 0); - - if (static_cast<std::vector<BasicBlockIndex>::size_type>(bb->index()) >= idom.size()) { - // This is a new block, probably introduced by edge splitting. So, we'll have to grow - // the array before inserting the immediate dominator. - idom.resize(function->basicBlockCount(), InvalidBasicBlockIndex); - } - - const BasicBlockIndex newIdx = newDominator ? newDominator->index() : InvalidBasicBlockIndex; - if (DebugImmediateDominators) - qDebug() << "Setting idom of" << bb->index() << "from" << idom[bb->index()] << "to" << newIdx; - idom[bb->index()] = newIdx; - } - - void collectSiblings(BasicBlock *node, BasicBlockSet &siblings) - { - siblings.insert(node); - const BasicBlockIndex dominator = idom[node->index()]; - if (dominator == InvalidBasicBlockIndex) - return; - for (size_t i = 0, ei = idom.size(); i != ei; ++i) { - if (idom[i] == dominator) { - BasicBlock *bb = function->basicBlock(int(i)); - if (!bb->isRemoved()) - siblings.insert(bb); - } - } - } - - void recalculateIDoms(const BasicBlockSet &nodes, BasicBlock *limit = 0) - { - const BasicBlockIndex limitIndex = limit ? limit->index() : InvalidBasicBlockIndex; - BasicBlockSet todo(nodes), postponed(function); - while (!todo.empty()) - recalculateIDom(*todo.begin(), todo, postponed, limitIndex); - } - - bool dominates(BasicBlock *dominator, BasicBlock *dominated) const { - return dominates(dominator->index(), dominated->index()); - } - - struct Cmp { - std::vector<int> *nodeDepths; - Cmp(std::vector<int> *nodeDepths) - : nodeDepths(nodeDepths) - { Q_ASSERT(nodeDepths); } - bool operator()(BasicBlock *one, BasicBlock *two) const - { - if (one->isRemoved()) - return false; - if (two->isRemoved()) - return true; - return nodeDepths->at(one->index()) > nodeDepths->at(two->index()); - } - }; - - // Calculate a depth-first iteration order on the nodes of the dominator tree. - // - // The order of the nodes in the vector is not the same as one where a recursive depth-first - // iteration is done on a tree. Rather, the nodes are (reverse) sorted on tree depth. - // So for the: - // 1 dominates 2 - // 2 dominates 3 - // 3 dominates 4 - // 2 dominates 5 - // the order will be: - // 4, 3, 5, 2, 1 - // or: - // 4, 5, 3, 2, 1 - // So the order of nodes on the same depth is undefined, but it will be after the nodes - // they dominate, and before the nodes that dominate them. - // - // The reason for this order is that a proper DFS pre-/post-order would require inverting - // the idom vector by either building a real tree datastructure or by searching the idoms - // for siblings and children. Both have a higher time complexity than sorting by depth. - QVector<BasicBlock *> calculateDFNodeIterOrder() const - { - std::vector<int> depths = calculateNodeDepths(); - QVector<BasicBlock *> order = function->basicBlocks(); - std::sort(order.begin(), order.end(), Cmp(&depths)); - for (int i = 0; i < order.size(); ) { - if (order[i]->isRemoved()) - order.remove(i); - else - ++i; - } - return order; - } - - void mergeIntoPredecessor(BasicBlock *successor) - { - int succIdx = successor->index(); - if (succIdx == InvalidBasicBlockIndex) { - return; - } - - int succDom = idom[unsigned(succIdx)]; - for (BasicBlockIndex &idx : idom) { - if (idx == succIdx) { - idx = succDom; - } - } - } - -private: - bool dominates(BasicBlockIndex dominator, BasicBlockIndex dominated) const { - // dominator can be Invalid when the dominated block has no dominator (i.e. the start node) - Q_ASSERT(dominated != InvalidBasicBlockIndex); - - if (dominator == dominated) - return false; - - for (BasicBlockIndex it = idom[dominated]; it != InvalidBasicBlockIndex; it = idom[it]) { - if (it == dominator) - return true; - } - - return false; - } - - // Algorithm: - // - for each node: - // - get the depth of a node. If it's unknown (-1): - // - get the depth of the immediate dominator. - // - if that's unknown too, calculate it by calling calculateNodeDepth - // - set the current node's depth to that of immediate dominator + 1 - std::vector<int> calculateNodeDepths() const - { - std::vector<int> nodeDepths(size_t(function->basicBlockCount()), -1); - nodeDepths[0] = 0; - for (BasicBlock *bb : function->basicBlocks()) { - if (bb->isRemoved()) - continue; - - int &bbDepth = nodeDepths[bb->index()]; - if (bbDepth == -1) { - const int immDom = idom[bb->index()]; - int immDomDepth = nodeDepths[immDom]; - if (immDomDepth == -1) - immDomDepth = calculateNodeDepth(immDom, nodeDepths); - bbDepth = immDomDepth + 1; - } - } - return nodeDepths; - } - - // Algorithm: - // - search for the first dominator of a node that has a known depth. As all nodes are - // reachable from the start node, and that node's depth is 0, this is finite. - // - while doing that search, put all unknown nodes in the worklist - // - pop all nodes from the worklist, and set their depth to the previous' (== dominating) - // node's depth + 1 - // This way every node's depth is calculated once, and the complexity is O(n). - int calculateNodeDepth(int nodeIdx, std::vector<int> &nodeDepths) const - { - std::vector<int> worklist; - worklist.reserve(8); - int depth = -1; - - do { - worklist.push_back(nodeIdx); - nodeIdx = idom[nodeIdx]; - depth = nodeDepths[nodeIdx]; - } while (depth == -1); - - for (std::vector<int>::const_reverse_iterator it = worklist.rbegin(), eit = worklist.rend(); it != eit; ++it) - nodeDepths[*it] = ++depth; - - return depth; - } - - // The immediate-dominator recalculation is used when edges are removed from the CFG. See - // [Ramalingam] for a description. Note that instead of calculating the priority, a recursive - // algorithm is used: when recalculating the immediate dominator of a node by looking for the - // least-common ancestor, and a node is hit that also needs recalculation, a recursive call - // is done to calculate that nodes immediate dominator first. - // - // Note that this simplified algorithm cannot cope with back-edges. It only works for - // non-looping edges (which is our use-case). - void recalculateIDom(BasicBlock *node, BasicBlockSet &todo, BasicBlockSet &postponed, BasicBlockIndex limit) { - Q_ASSERT(!postponed.contains(node)); - Q_ASSERT(todo.contains(node)); - todo.remove(node); - - if (node->in.size() == 1) { - // Special case: if the node has only one incoming edge, then that is the immediate - // dominator. - setImmediateDominator(node, node->in.first()); - return; - } - - std::vector<BasicBlockIndex> prefix; - prefix.reserve(32); - - for (BasicBlock *in : node->in) { - if (node == in) // back-edge to self - continue; - if (dominates(node->index(), in->index())) // a known back-edge - continue; - - if (prefix.empty()) { - calculatePrefix(node, in, prefix, todo, postponed, limit); - - if (!prefix.empty()) { - std::reverse(prefix.begin(), prefix.end()); - Q_ASSERT(!prefix.empty()); - Q_ASSERT(prefix.front() == limit || limit == InvalidBasicBlockIndex); - } - } else { - std::vector<BasicBlockIndex> anotherPrefix; - anotherPrefix.reserve(prefix.size()); - calculatePrefix(node, in, anotherPrefix, todo, postponed, limit); - - if (!anotherPrefix.empty()) - commonPrefix(prefix, anotherPrefix); - } - } - - Q_ASSERT(!prefix.empty()); - idom[node->index()] = prefix.back(); - } - - void calculatePrefix(BasicBlock *node, BasicBlock *in, std::vector<BasicBlockIndex> &prefix, BasicBlockSet &todo, BasicBlockSet &postponed, BasicBlockIndex limit) - { - for (BasicBlockIndex it = in->index(); it != InvalidBasicBlockIndex; it = idom[it]) { - prefix.push_back(it); - if (it == limit) - return; - BasicBlock *n = function->basicBlock(it); - if (postponed.contains(n)) { // possible back-edge, bail out. - prefix.clear(); - return; - } - if (todo.contains(n)) { - postponed.insert(node); - recalculateIDom(n, todo, postponed, limit); - postponed.remove(node); - } - } - } - - // Calculate the LCA (Least Common Ancestor) by finding the longest common prefix between two - // dominator chains. Note that "anotherPrefix" has the node's immediate dominator first, while - // "bestPrefix" has it last (meaning: is in reverse order). The reason for this is that removing - // nodes from "bestPrefix" is cheaper because it's done at the end of the vector, while - // reversing all "anotherPrefix" nodes would take unnecessary time. - static void commonPrefix(std::vector<BasicBlockIndex> &bestPrefix, const std::vector<BasicBlockIndex> &anotherPrefix) - { - const size_t anotherSize = anotherPrefix.size(); - size_t minLen = qMin(bestPrefix.size(), anotherPrefix.size()); - while (minLen != 0) { - --minLen; - if (bestPrefix[minLen] == anotherPrefix[anotherSize - minLen - 1]) { - ++minLen; - break; - } - } - if (minLen != bestPrefix.size()) - bestPrefix.erase(bestPrefix.begin() + minLen, bestPrefix.end()); - } -}; - -class VariableCollector { - std::vector<Temp> _allTemps; - std::vector<BasicBlockSet> _defsites; - std::vector<std::vector<int> > A_orig; - BitVector nonLocals; - BitVector killed; - - BasicBlock *currentBB; - bool isCollectable(Temp *t) const - { - Q_UNUSED(t); - Q_ASSERT(t->kind != Temp::PhysicalRegister && t->kind != Temp::StackSlot); - return true; - } - - void addDefInCurrentBlock(Temp *t) - { - std::vector<int> &temps = A_orig[currentBB->index()]; - if (std::find(temps.begin(), temps.end(), t->index) == temps.end()) - temps.push_back(t->index); - } - - void addTemp(Temp *t) - { - if (_allTemps[t->index].kind == Temp::Invalid) - _allTemps[t->index] = *t; - } - -public: - VariableCollector(IR::Function *function) - { - _allTemps.resize(function->tempCount); - _defsites.resize(function->tempCount); - for (int i = 0; i < function->tempCount; ++i) - _defsites[i].init(function); - nonLocals.resize(function->tempCount); - const size_t ei = function->basicBlockCount(); - A_orig.resize(ei); - for (size_t i = 0; i != ei; ++i) - A_orig[i].reserve(8); - - for (BasicBlock *bb : function->basicBlocks()) { - if (bb->isRemoved()) - continue; - - currentBB = bb; - killed.assign(function->tempCount, false); - for (Stmt *s : bb->statements()) - visit(s); - } - } - - const std::vector<Temp> &allTemps() const - { return _allTemps; } - - void collectDefSites(const Temp &n, std::vector<BasicBlock *> &bbs) const { - Q_ASSERT(!n.isInvalid()); - Q_ASSERT(n.index < _defsites.size()); - _defsites[n.index].collectValues(bbs); - } - - const std::vector<int> &inBlock(BasicBlock *n) const - { - return A_orig.at(n->index()); - } - - bool isNonLocal(const Temp &var) const - { - Q_ASSERT(!var.isInvalid()); - Q_ASSERT(static_cast<int>(var.index) < nonLocals.size()); - return nonLocals.at(var.index); - } - -private: - void visit(Stmt *s) - { - if (s->asPhi()) { - // nothing to do - } else if (auto move = s->asMove()) { - visit(move->source); - - if (Temp *t = move->target->asTemp()) { - addTemp(t); - - if (isCollectable(t)) { - _defsites[t->index].insert(currentBB); - addDefInCurrentBlock(t); - - // For semi-pruned SSA: - killed.setBit(t->index); - } - } else { - visit(move->target); - } - } else { - STMT_VISIT_ALL_KINDS(s) - } - } - - void visit(Expr *e) - { - if (auto t = e->asTemp()) { - addTemp(t); - - if (isCollectable(t)) { - if (!killed.at(t->index)) { - nonLocals.setBit(t->index); - } - } - } else { - EXPR_VISIT_ALL_KINDS(e); - } - } -}; - -struct UntypedTemp { - Temp temp; - UntypedTemp() {} - UntypedTemp(const Temp &t): temp(t) {} -}; -inline bool operator==(const UntypedTemp &t1, const UntypedTemp &t2) Q_DECL_NOTHROW -{ return t1.temp.index == t2.temp.index && t1.temp.kind == t2.temp.kind; } -inline bool operator!=(const UntypedTemp &t1, const UntypedTemp &t2) Q_DECL_NOTHROW -{ return !(t1 == t2); } - -class DefUses -{ -public: - struct DefUse { - DefUse() - : defStmt(0) - , blockOfStatement(0) - { uses.reserve(8); } - Temp temp; - Stmt *defStmt; - BasicBlock *blockOfStatement; - QVector<Stmt *> uses; - - bool isValid() const - { return temp.kind != Temp::Invalid; } - - void clear() - { defStmt = 0; blockOfStatement = 0; uses.clear(); } - }; - -private: - std::vector<DefUse> _defUses; - typedef QVarLengthArray<Temp, 4> Temps; - std::vector<Temps> _usesPerStatement; - - void ensure(Temp *newTemp) - { - if (_defUses.size() <= newTemp->index) { - _defUses.resize(newTemp->index + 1); - } - } - - void ensure(Stmt *s) - { - Q_ASSERT(s->id() >= 0); - if (static_cast<unsigned>(s->id()) >= _usesPerStatement.size()) { - _usesPerStatement.resize(s->id() + 1); - } - } - - void addUseForStatement(Stmt *s, const Temp &var) - { - ensure(s); - _usesPerStatement[s->id()].push_back(var); - } - -public: - DefUses(IR::Function *function) - { - _usesPerStatement.resize(function->statementCount()); - _defUses.resize(function->tempCount); - } - - void cleanup() - { - for (size_t i = 0, ei = _defUses.size(); i != ei; ++i) { - DefUse &defUse = _defUses[i]; - if (defUse.isValid() && !defUse.defStmt) - defUse.clear(); - } - } - - unsigned statementCount() const - { return unsigned(_usesPerStatement.size()); } - - unsigned tempCount() const - { return unsigned(_defUses.size()); } - - const Temp &temp(int idx) const - { return _defUses[idx].temp; } - - void addDef(Temp *newTemp, Stmt *defStmt, BasicBlock *defBlock) - { - ensure(newTemp); - DefUse &defUse = _defUses[newTemp->index]; - Q_ASSERT(!defUse.isValid()); - defUse.temp = *newTemp; - defUse.defStmt = defStmt; - defUse.blockOfStatement = defBlock; - } - - QVector<UntypedTemp> defsUntyped() const - { - QVector<UntypedTemp> res; - res.reserve(tempCount()); - for (const DefUse &du : _defUses) - if (du.isValid()) - res.append(UntypedTemp(du.temp)); - return res; - } - - std::vector<const Temp *> defs() const { - std::vector<const Temp *> res; - const size_t ei = _defUses.size(); - res.reserve(ei); - for (size_t i = 0; i != ei; ++i) { - const DefUse &du = _defUses.at(i); - if (du.isValid()) - res.push_back(&du.temp); - } - return res; - } - - void removeDef(const Temp &variable) { - Q_ASSERT(static_cast<unsigned>(variable.index) < _defUses.size()); - _defUses[variable.index].clear(); - } - - void addUses(const Temp &variable, const QVector<Stmt *> &newUses) - { - Q_ASSERT(static_cast<unsigned>(variable.index) < _defUses.size()); - QVector<Stmt *> &uses = _defUses[variable.index].uses; - for (Stmt *stmt : newUses) - if (std::find(uses.begin(), uses.end(), stmt) == uses.end()) - uses.push_back(stmt); - } - - void addUse(const Temp &variable, Stmt *newUse) - { - if (_defUses.size() <= variable.index) { - _defUses.resize(variable.index + 1); - DefUse &du = _defUses[variable.index]; - du.temp = variable; - du.uses.push_back(newUse); - addUseForStatement(newUse, variable); - return; - } - - QVector<Stmt *> &uses = _defUses[variable.index].uses; - if (std::find(uses.begin(), uses.end(), newUse) == uses.end()) - uses.push_back(newUse); - addUseForStatement(newUse, variable); - } - - int useCount(const Temp &variable) const - { - Q_ASSERT(static_cast<unsigned>(variable.index) < _defUses.size()); - return _defUses[variable.index].uses.size(); - } - - Stmt *defStmt(const Temp &variable) const - { - Q_ASSERT(static_cast<unsigned>(variable.index) < _defUses.size()); - return _defUses[variable.index].defStmt; - } - - BasicBlock *defStmtBlock(const Temp &variable) const - { - Q_ASSERT(static_cast<unsigned>(variable.index) < _defUses.size()); - return _defUses[variable.index].blockOfStatement; - } - - void replaceBasicBlock(BasicBlock *from, BasicBlock *to) - { - for (auto &du : _defUses) { - if (du.blockOfStatement == from) { - du.blockOfStatement = to; - } - } - } - - void removeUse(Stmt *usingStmt, const Temp &var) - { - Q_ASSERT(static_cast<unsigned>(var.index) < _defUses.size()); - QVector<Stmt *> &uses = _defUses[var.index].uses; - uses.erase(std::remove(uses.begin(), uses.end(), usingStmt), uses.end()); - } - - void registerNewStatement(Stmt *s) - { - ensure(s); - } - - const Temps &usedVars(Stmt *s) const - { - Q_ASSERT(s->id() >= 0); - Q_ASSERT(static_cast<unsigned>(s->id()) < _usesPerStatement.size()); - return _usesPerStatement[s->id()]; - } - - const QVector<Stmt *> &uses(const Temp &var) const - { - return _defUses[var.index].uses; - } - - QVector<Stmt*> removeDefUses(Stmt *s) - { - QVector<Stmt*> defStmts; - for (const Temp &usedVar : usedVars(s)) { - if (Stmt *ds = defStmt(usedVar)) - defStmts += ds; - removeUse(s, usedVar); - } - if (Move *m = s->asMove()) { - if (Temp *t = m->target->asTemp()) - removeDef(*t); - } else if (Phi *p = s->asPhi()) { - removeDef(*p->targetTemp); - } - - return defStmts; - } - - void dump() const - { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - qout << "Defines and uses:" << endl; - for (const DefUse &du : _defUses) { - if (!du.isValid()) - continue; - qout << '%' << du.temp.index; - qout << " -> defined in block " << du.blockOfStatement->index() - << ", statement: " << du.defStmt->id() - << endl; - qout << " uses:"; - for (Stmt *s : du.uses) - qout << ' ' << s->id(); - qout << endl; - } - qout << "Uses per statement:" << endl; - for (size_t i = 0, ei = _usesPerStatement.size(); i != ei; ++i) { - qout << " " << i << ":"; - for (const Temp &t : _usesPerStatement[i]) - qout << ' ' << t.index; - qout << endl; - } - qDebug("%s", buf.data().constData()); - } -}; - -void insertPhiNode(const Temp &a, BasicBlock *y, IR::Function *f) { - Phi *phiNode = f->NewStmt<Phi>(); - phiNode->targetTemp = f->New<Temp>(); - phiNode->targetTemp->init(a.kind, a.index); - y->prependStatement(phiNode); - - phiNode->incoming.resize(y->in.size()); - for (int i = 0, ei = y->in.size(); i < ei; ++i) { - Temp *t = f->New<Temp>(); - t->init(a.kind, a.index); - phiNode->incoming[i] = t; - } -} - -// High-level (recursive) algorithm: -// Mapping: old temp number -> new temp number -// -// Start: -// Rename(start-node) -// -// Rename(node, mapping): -// for each statement S in block n -// if S not in a phi-function -// for each use of some variable x in S -// y = mapping[x] -// replace the use of x with y in S -// for each definition of some variable a in S [1] -// a_new = generate new/unique temp -// mapping[a] = a_new -// replace definition of a with definition of a_new in S -// for each successor Y of block n -// Suppose n is the j-th predecessor of Y -// for each phi function in Y -// suppose the j-th operand of the phi-function is a -// i = mapping[a] -// replace the j-th operand with a_i -// for each child X of n [2] -// Rename(X) -// for each newly generated temp from step [1] restore the old value [3] -// -// This algorithm can run out of CPU stack space when there are lots of basic-blocks, like in a -// switch statement with 8000 cases that all fall-through. The iterativer version below uses a -// work-item stack, where step [1] from the algorithm above also pushes an "undo mapping change", -// and step [2] pushes a "rename(X)" action. This eliminates step [3]. -// -// Iterative version: -// Mapping: old temp number -> new temp number -// -// The stack can hold two kinds of actions: -// "Rename basic block n" -// "Restore count for temp" -// -// Start: -// counter = 0 -// push "Rename start node" onto the stack -// while the stack is not empty: -// take the last item, and process it -// -// Rename(n) = -// for each statement S in block n -// if S not in a phi-function -// for each use of some variable x in S -// y = mapping[x] -// replace the use of x with y in S -// for each definition of some variable a in S -// old = mapping[a] -// push Undo(a, old) -// counter = counter + 1 -// new = counter; -// mapping[a] = new -// replace definition of a with definition of a_new in S -// for each successor Y of block n -// Suppose n is the j-th predecessor of Y -// for each phi function in Y -// suppose the j-th operand of the phi-function is a -// i = mapping[a] -// replace the j-th operand with a_i -// for each child X of n -// push Rename(X) -// -// Undo(t, c) = -// mapping[t] = c -class VariableRenamer -{ - Q_DISABLE_COPY(VariableRenamer) - - IR::Function *function; - DefUses &defUses; - unsigned tempCount; - - typedef std::vector<int> Mapping; // maps from existing/old temp number to the new and unique temp number. - enum { Absent = -1 }; - Mapping vregMapping; - ProcessedBlocks processed; - - BasicBlock *currentBB; - Stmt *currentStmt; - - struct TodoAction { - enum { RestoreVReg, Rename } action; - union { - struct { - unsigned temp; - int previous; - } restoreData; - struct { - BasicBlock *basicBlock; - } renameData; - }; - - bool isValid() const { return action != Rename || renameData.basicBlock != 0; } - - TodoAction() - { - action = Rename; - renameData.basicBlock = 0; - } - - TodoAction(const Temp &t, int prev) - { - Q_ASSERT(t.kind == Temp::VirtualRegister); - - action = RestoreVReg; - restoreData.temp = t.index; - restoreData.previous = prev; - } - - TodoAction(BasicBlock *bb) - { - Q_ASSERT(bb); - - action = Rename; - renameData.basicBlock = bb; - } - }; - - QVector<TodoAction> todo; - -public: - VariableRenamer(IR::Function *f, DefUses &defUses) - : function(f) - , defUses(defUses) - , tempCount(0) - , processed(f) - { - vregMapping.assign(f->tempCount, Absent); - todo.reserve(f->basicBlockCount()); - } - - void run() { - todo.append(TodoAction(function->basicBlock(0))); - - while (!todo.isEmpty()) { - TodoAction todoAction = todo.back(); - Q_ASSERT(todoAction.isValid()); - todo.pop_back(); - - switch (todoAction.action) { - case TodoAction::Rename: - rename(todoAction.renameData.basicBlock); - break; - case TodoAction::RestoreVReg: - restore(vregMapping, todoAction.restoreData.temp, todoAction.restoreData.previous); - break; - default: - Q_UNREACHABLE(); - } - } - - function->tempCount = tempCount; - } - -private: - static inline void restore(Mapping &mapping, int temp, int previous) - { - mapping[temp] = previous; - } - - void rename(BasicBlock *bb) - { - while (bb && !processed.alreadyProcessed(bb)) { - renameStatementsAndPhis(bb); - processed.markAsProcessed(bb); - - BasicBlock *next = 0; - for (BasicBlock *out : bb->out) { - if (processed.alreadyProcessed(out)) - continue; - if (!next) - next = out; - else - todo.append(TodoAction(out)); - } - bb = next; - } - } - - void renameStatementsAndPhis(BasicBlock *bb) - { - currentBB = bb; - - for (Stmt *s : bb->statements()) { - currentStmt = s; - visit(s); - } - - for (BasicBlock *Y : bb->out) { - const int j = Y->in.indexOf(bb); - Q_ASSERT(j >= 0 && j < Y->in.size()); - for (Stmt *s : Y->statements()) { - if (Phi *phi = s->asPhi()) { - Temp *t = phi->incoming[j]->asTemp(); - unsigned newTmp = currentNumber(*t); -// qDebug()<<"I: replacing phi use"<<a<<"with"<<newTmp<<"in L"<<Y->index; - t->index = newTmp; - t->kind = Temp::VirtualRegister; - defUses.addUse(*t, phi); - } else { - break; - } - } - } - } - - unsigned currentNumber(const Temp &t) - { - int nr = Absent; - switch (t.kind) { - case Temp::VirtualRegister: - nr = vregMapping[t.index]; - break; - default: - Q_UNREACHABLE(); - nr = Absent; - break; - } - if (nr == Absent) { - // Special case: we didn't prune the Phi nodes yet, so for proper temps (virtual - // registers) the SSA algorithm might insert superfluous Phis that have uses without - // definition. E.g.: if a temporary got introduced in the "then" clause, it "could" - // reach the "end-if" block, so there will be a phi node for that temp. A later pass - // will clean this up by looking for uses-without-defines in phi nodes. So, what we do - // is to generate a new unique number, and leave it dangling. - nr = nextFreeTemp(t); - } - - return nr; - } - - unsigned nextFreeTemp(const Temp &t) - { - unsigned newIndex = tempCount++; - Q_ASSERT(newIndex <= INT_MAX); - int oldIndex = Absent; - - switch (t.kind) { - case Temp::VirtualRegister: - oldIndex = vregMapping[t.index]; - vregMapping[t.index] = newIndex; - break; - default: - Q_UNREACHABLE(); - } - - todo.append(TodoAction(t, oldIndex)); - - return newIndex; - } - -private: - void visit(Stmt *s) - { - if (auto move = s->asMove()) { - // uses: - visit(move->source); - - // defs: - if (Temp *t = move->target->asTemp()) { - renameTemp(t); - } else { - visit(move->target); - } - } else if (auto phi = s->asPhi()) { - renameTemp(phi->targetTemp); - } else { - STMT_VISIT_ALL_KINDS(s); - } - } - - void visit(Expr *e) - { - if (auto temp = e->asTemp()) { - temp->index = currentNumber(*temp); - temp->kind = Temp::VirtualRegister; - defUses.addUse(*temp, currentStmt); - } else { - EXPR_VISIT_ALL_KINDS(e); - } - } - - void renameTemp(Temp *t) { // only called for defs, not uses - const int newIdx = nextFreeTemp(*t); -// qDebug()<<"I: replacing def of"<<a<<"with"<<newIdx; - t->kind = Temp::VirtualRegister; - t->index = newIdx; - defUses.addDef(t, currentStmt, currentBB); - } -}; - -// This function converts the IR to semi-pruned SSA form. For details about SSA and the algorightm, -// see [Appel]. For the changes needed for semi-pruned SSA form, and for its advantages, see [Briggs]. -void convertToSSA(IR::Function *function, const DominatorTree &df, DefUses &defUses) -{ - // Collect all applicable variables: - VariableCollector variables(function); - - // Prepare for phi node insertion: - std::vector<BitVector > A_phi; - const size_t ei = function->basicBlockCount(); - A_phi.resize(ei); - for (size_t i = 0; i != ei; ++i) - A_phi[i].assign(function->tempCount, false); - - std::vector<BasicBlock *> W; - W.reserve(8); - - // Place phi functions: - for (const Temp &a : variables.allTemps()) { - if (a.isInvalid()) - continue; - if (!variables.isNonLocal(a)) - continue; // for semi-pruned SSA - - W.clear(); - variables.collectDefSites(a, W); - while (!W.empty()) { - BasicBlock *n = W.back(); - W.pop_back(); - const BasicBlockSet &dominatorFrontierForN = df.dominatorFrontier(n); - for (BasicBlockSet::const_iterator it = dominatorFrontierForN.begin(), eit = dominatorFrontierForN.end(); - it != eit; ++it) { - BasicBlock *y = *it; - if (!A_phi.at(y->index()).at(a.index)) { - insertPhiNode(a, y, function); - A_phi[y->index()].setBit(a.index); - const std::vector<int> &varsInBlockY = variables.inBlock(y); - if (std::find(varsInBlockY.begin(), varsInBlockY.end(), a.index) == varsInBlockY.end()) - W.push_back(y); - } - } - } - } - - // Rename variables: - VariableRenamer(function, defUses).run(); -} - -/// Calculate if a phi node result is used only by other phi nodes, and if those uses are -/// in turn also used by other phi nodes. -bool hasPhiOnlyUses(Phi *phi, const DefUses &defUses, QBitArray &collectedPhis) -{ - collectedPhis.setBit(phi->id()); - - for (Stmt *use : defUses.uses(*phi->targetTemp)) { - Phi *dependentPhi = use->asPhi(); - if (!dependentPhi) - return false; // there is a use by a non-phi node - - if (collectedPhis.at(dependentPhi->id())) - continue; // we already found this node - - if (!hasPhiOnlyUses(dependentPhi, defUses, collectedPhis)) - return false; - } - - return true; -} - -void cleanupPhis(DefUses &defUses) -{ - QBitArray toRemove(defUses.statementCount()); - QBitArray collectedPhis(defUses.statementCount()); - std::vector<Phi *> allPhis; - allPhis.reserve(32); - - for (const Temp *def : defUses.defs()) { - Stmt *defStmt = defUses.defStmt(*def); - if (!defStmt) - continue; - - Phi *phi = defStmt->asPhi(); - if (!phi) - continue; - allPhis.push_back(phi); - if (toRemove.at(phi->id())) - continue; - - collectedPhis.fill(false); - if (hasPhiOnlyUses(phi, defUses, collectedPhis)) - toRemove |= collectedPhis; - } - - for (Phi *phi : allPhis) { - if (!toRemove.at(phi->id())) - continue; - - const Temp &targetVar = *phi->targetTemp; - defUses.defStmtBlock(targetVar)->removeStatement(phi); - - for (const Temp &usedVar : defUses.usedVars(phi)) - defUses.removeUse(phi, usedVar); - defUses.removeDef(targetVar); - } - - defUses.cleanup(); -} - -class StatementWorklist -{ - IR::Function *theFunction; - std::vector<Stmt *> stmts; - BitVector worklist; - unsigned worklistSize; - std::vector<int> replaced; - BitVector removed; - - Q_DISABLE_COPY(StatementWorklist) - -public: - StatementWorklist(IR::Function *function) - : theFunction(function) - , stmts(function->statementCount(), static_cast<Stmt *>(0)) - , worklist(function->statementCount(), false) - , worklistSize(0) - , replaced(function->statementCount(), Stmt::InvalidId) - , removed(function->statementCount()) - { - grow(); - - for (BasicBlock *bb : function->basicBlocks()) { - if (bb->isRemoved()) - continue; - - for (Stmt *s : bb->statements()) { - if (!s) - continue; - - stmts[s->id()] = s; - worklist.setBit(s->id()); - ++worklistSize; - } - } - } - - void reset() - { - worklist.assign(worklist.size(), false); - worklistSize = 0; - - for (Stmt *s : stmts) { - if (!s) - continue; - - worklist.setBit(s->id()); - ++worklistSize; - } - - replaced.assign(replaced.size(), Stmt::InvalidId); - removed.assign(removed.size(), false); - } - - void remove(Stmt *stmt) - { - replaced[stmt->id()] = Stmt::InvalidId; - removed.setBit(stmt->id()); - if (worklist.at(stmt->id())) { - worklist.clearBit(stmt->id()); - Q_ASSERT(worklistSize > 0); - --worklistSize; - } - } - - void replace(Stmt *oldStmt, Stmt *newStmt) - { - Q_ASSERT(oldStmt); - Q_ASSERT(replaced[oldStmt->id()] == Stmt::InvalidId); - Q_ASSERT(removed.at(oldStmt->id()) == false); - - Q_ASSERT(newStmt); - registerNewStatement(newStmt); - Q_ASSERT(replaced[newStmt->id()] == Stmt::InvalidId); - Q_ASSERT(removed.at(newStmt->id()) == false); - - replaced[oldStmt->id()] = newStmt->id(); - worklist.clearBit(oldStmt->id()); - } - - void applyToFunction() - { - for (BasicBlock *bb : theFunction->basicBlocks()) { - if (bb->isRemoved()) - continue; - - for (int i = 0; i < bb->statementCount();) { - Stmt *stmt = bb->statements().at(i); - - int id = stmt->id(); - Q_ASSERT(id != Stmt::InvalidId); - Q_ASSERT(static_cast<unsigned>(stmt->id()) < stmts.size()); - - for (int replacementId = replaced[id]; replacementId != Stmt::InvalidId; replacementId = replaced[replacementId]) - id = replacementId; - Q_ASSERT(id != Stmt::InvalidId); - Q_ASSERT(static_cast<unsigned>(stmt->id()) < stmts.size()); - - if (removed.at(id)) { - bb->removeStatement(i); - } else { - if (id != stmt->id()) - bb->replaceStatement(i, stmts[id]); - - ++i; - } - } - } - - replaced.assign(replaced.size(), Stmt::InvalidId); - removed.assign(removed.size(), false); - } - - StatementWorklist &operator+=(const QVector<Stmt *> &stmts) - { - for (Stmt *s : stmts) - this->operator+=(s); - - return *this; - } - - StatementWorklist &operator+=(Stmt *s) - { - if (!s) - return *this; - - Q_ASSERT(s->id() >= 0); - Q_ASSERT(s->id() < worklist.size()); - - if (!worklist.at(s->id())) { - worklist.setBit(s->id()); - ++worklistSize; - } - - return *this; - } - - StatementWorklist &operator-=(Stmt *s) - { - Q_ASSERT(s->id() >= 0); - Q_ASSERT(s->id() < worklist.size()); - - if (worklist.at(s->id())) { - worklist.clearBit(s->id()); - Q_ASSERT(worklistSize > 0); - --worklistSize; - } - - return *this; - } - - unsigned size() const - { - return worklistSize; - } - - Stmt *takeNext(Stmt *last) - { - if (worklistSize == 0) - return 0; - - const int startAt = last ? last->id() + 1 : 0; - Q_ASSERT(startAt >= 0); - Q_ASSERT(startAt <= worklist.size()); - - Q_ASSERT(static_cast<size_t>(worklist.size()) == stmts.size()); - - int pos = worklist.findNext(startAt, true, /*wrapAround = */true); - - worklist.clearBit(pos); - Q_ASSERT(worklistSize > 0); - --worklistSize; - Stmt *s = stmts.at(pos); - Q_ASSERT(s); - - if (removed.at(s->id())) - return takeNext(s); - - return s; - } - - IR::Function *function() const - { - return theFunction; - } - - void registerNewStatement(Stmt *s) - { - Q_ASSERT(s->id() >= 0); - if (static_cast<unsigned>(s->id()) >= stmts.size()) { - if (static_cast<unsigned>(s->id()) >= stmts.capacity()) - grow(); - - int newSize = s->id() + 1; - stmts.resize(newSize, 0); - worklist.resize(newSize); - replaced.resize(newSize, Stmt::InvalidId); - removed.resize(newSize); - } - - stmts[s->id()] = s; - } - -private: - void grow() - { - Q_ASSERT(stmts.capacity() < INT_MAX / 2); - int newCapacity = ((static_cast<int>(stmts.capacity()) + 1) * 3) / 2; - stmts.reserve(newCapacity); - worklist.reserve(newCapacity); - replaced.reserve(newCapacity); - removed.reserve(newCapacity); - } -}; - -class SideEffectsChecker -{ - bool _sideEffect; - -public: - SideEffectsChecker() - : _sideEffect(false) - {} - - ~SideEffectsChecker() - {} - - bool hasSideEffects(Expr *expr) - { - bool sideEffect = false; - qSwap(_sideEffect, sideEffect); - visit(expr); - qSwap(_sideEffect, sideEffect); - return sideEffect; - } - -protected: - void markAsSideEffect() - { - _sideEffect = true; - } - - bool seenSideEffects() const { return _sideEffect; } - - void visit(Expr *e) - { - if (auto n = e->asName()) { - visitName(n); - } else if (auto t = e->asTemp()) { - visitTemp(t); - } else if (auto c = e->asClosure()) { - visitClosure(c); - } else if (auto c = e->asConvert()) { - visitConvert(c); - } else if (auto u = e->asUnop()) { - visitUnop(u); - } else if (auto b = e->asBinop()) { - visitBinop(b); - } else if (auto c = e->asCall()) { - visitCall(c); - } else if (auto n = e->asNew()) { - visitNew(n); - } else if (auto s = e->asSubscript()) { - visitSubscript(s); - } else if (auto m = e->asMember()) { - visitMember(m); - } - } - - virtual void visitTemp(Temp *) {} - -private: - void visitName(Name *e) { - if (e->freeOfSideEffects) - return; - // TODO: maybe we can distinguish between built-ins of which we know that they do not have - // a side-effect. - if (e->builtin == Name::builtin_invalid || (e->id && *e->id != QLatin1String("this"))) - markAsSideEffect(); - } - - void visitClosure(Closure *) { - markAsSideEffect(); - } - - void visitConvert(Convert *e) { - visit(e->expr); - - switch (e->expr->type) { - case QObjectType: - case StringType: - case VarType: - markAsSideEffect(); - break; - default: - break; - } - } - - void visitUnop(Unop *e) { - visit(e->expr); - - switch (e->op) { - case OpUPlus: - case OpUMinus: - case OpNot: - case OpIncrement: - case OpDecrement: - if (e->expr->type == VarType || e->expr->type == StringType || e->expr->type == QObjectType) - markAsSideEffect(); - break; - - default: - break; - } - } - - void visitBinop(Binop *e) { - // TODO: prune parts that don't have a side-effect. For example, in: - // function f(x) { +x+1; return 0; } - // we can prune the binop and leave the unop/conversion. - _sideEffect = hasSideEffects(e->left); - _sideEffect |= hasSideEffects(e->right); - - if (e->left->type == VarType || e->left->type == StringType || e->left->type == QObjectType - || e->right->type == VarType || e->right->type == StringType || e->right->type == QObjectType) - markAsSideEffect(); - } - - void visitSubscript(Subscript *e) { - visit(e->base); - visit(e->index); - markAsSideEffect(); - } - - void visitMember(Member *e) { - visit(e->base); - if (e->freeOfSideEffects) - return; - markAsSideEffect(); - } - - void visitCall(Call *e) { - visit(e->base); - for (ExprList *args = e->args; args; args = args->next) - visit(args->expr); - markAsSideEffect(); // TODO: there are built-in functions that have no side effect. - } - - void visitNew(New *e) { - visit(e->base); - for (ExprList *args = e->args; args; args = args->next) - visit(args->expr); - markAsSideEffect(); // TODO: there are built-in types that have no side effect. - } -}; - -class EliminateDeadCode: public SideEffectsChecker -{ - DefUses &_defUses; - StatementWorklist &_worklist; - QVarLengthArray<Temp *, 8> _collectedTemps; - -public: - EliminateDeadCode(DefUses &defUses, StatementWorklist &worklist) - : _defUses(defUses) - , _worklist(worklist) - {} - - void run(Expr *&expr, Stmt *stmt) { - _collectedTemps.clear(); - if (!hasSideEffects(expr)) { - expr = 0; - for (Temp *t : _collectedTemps) { - _defUses.removeUse(stmt, *t); - _worklist += _defUses.defStmt(*t); - } - } - } - -protected: - void visitTemp(Temp *e) override final - { - _collectedTemps.append(e); - } -}; - -class PropagateTempTypes -{ - const DefUses &defUses; - UntypedTemp theTemp; - DiscoveredType newType; - -public: - PropagateTempTypes(const DefUses &defUses) - : defUses(defUses) - {} - - void run(const UntypedTemp &temp, const DiscoveredType &type) - { - newType = type; - theTemp = temp; - if (Stmt *defStmt = defUses.defStmt(temp.temp)) - visit(defStmt); - for (Stmt *use : defUses.uses(temp.temp)) - visit(use); - } - -private: - void visit(Stmt *s) - { - STMT_VISIT_ALL_KINDS(s); - } - - void visit(Expr *e) - { - if (auto temp = e->asTemp()) { - if (theTemp == UntypedTemp(*temp)) { - temp->type = static_cast<Type>(newType.type); - temp->memberResolver = newType.memberResolver; - } - } else { - EXPR_VISIT_ALL_KINDS(e); - } - } -}; - -class TypeInference -{ - enum { DebugTypeInference = 0 }; - - QQmlEnginePrivate *qmlEngine; - const DefUses &_defUses; - typedef std::vector<DiscoveredType> TempTypes; - TempTypes _tempTypes; - StatementWorklist *_worklist; - struct TypingResult { - DiscoveredType type; - bool fullyTyped; - - TypingResult(const DiscoveredType &type = DiscoveredType()) { -#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 6 - // avoid optimization bug in gcc 4.6.3 armhf - ((int volatile &) this->type.type) = type.type; -#endif - this->type = type; - fullyTyped = type.type != UnknownType; - } - explicit TypingResult(MemberExpressionResolver *memberResolver) - : type(memberResolver) - , fullyTyped(true) - {} - }; - TypingResult _ty; - Stmt *_currentStmt; - -public: - TypeInference(QQmlEnginePrivate *qmlEngine, const DefUses &defUses) - : qmlEngine(qmlEngine) - , _defUses(defUses) - , _tempTypes(_defUses.tempCount()) - , _worklist(0) - , _ty(UnknownType) - , _currentStmt(nullptr) - {} - - void run(StatementWorklist &w) { - _worklist = &w; - - Stmt *s = 0; - while ((s = _worklist->takeNext(s))) { - if (s->asJump()) - continue; - - if (DebugTypeInference) { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - qout<<"Typing stmt "; - IRPrinter(&qout).print(s); - qout.flush(); - qDebug("%s", buf.data().constData()); - - qDebug("%u left in the worklist", _worklist->size()); - } - - if (!run(s)) { - *_worklist += s; - if (DebugTypeInference) { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - qout<<"Pushing back stmt: "; - IRPrinter(&qout).print(s); - qout.flush(); - qDebug("%s", buf.data().constData()); - } - } else { - if (DebugTypeInference) { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - qout<<"Finished: "; - IRPrinter(&qout).print(s); - qout.flush(); - qDebug("%s", buf.data().constData()); - } - } - } - - PropagateTempTypes propagator(_defUses); - for (size_t i = 0, ei = _tempTypes.size(); i != ei; ++i) { - const Temp &temp = _defUses.temp(int(i)); - if (temp.kind == Temp::Invalid) - continue; - const DiscoveredType &tempType = _tempTypes[i]; - if (tempType.type == UnknownType) - continue; - propagator.run(temp, tempType); - } - - _worklist = 0; - } - -private: - bool run(Stmt *s) { - TypingResult ty; - std::swap(_ty, ty); - std::swap(_currentStmt, s); - visit(_currentStmt); - std::swap(_currentStmt, s); - std::swap(_ty, ty); - return ty.fullyTyped; - } - - TypingResult run(Expr *e) { - TypingResult ty; - std::swap(_ty, ty); - visit(e); - std::swap(_ty, ty); - - if (ty.type != UnknownType) - setType(e, ty.type); - return ty; - } - - void setType(Expr *e, DiscoveredType ty) { - if (Temp *t = e->asTemp()) { - if (DebugTypeInference) - qDebug() << "Setting type for temp" << t->index - << " to " << typeName(Type(ty.type)) << "(" << ty.type << ")" - << endl; - - DiscoveredType &it = _tempTypes[t->index]; - if (it != ty) { - it = ty; - - if (DebugTypeInference) { - for (Stmt *s : _defUses.uses(*t)) { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - qout << "Pushing back dependent stmt: "; - IRPrinter(&qout).print(s); - qout.flush(); - qDebug("%s", buf.data().constData()); - } - } - - for (Stmt *s : qAsConst(_defUses.uses(*t))) { - if (s != _currentStmt) { - *_worklist += s; - } - } - } - } else { - e->type = (Type) ty.type; - } - } - -private: - void visit(Expr *e) - { - if (auto c = e->asConst()) { - visitConst(c); - } else if (auto s = e->asString()) { - visitString(s); - } else if (auto r = e->asRegExp()) { - visitRegExp(r); - } else if (auto n = e->asName()) { - visitName(n); - } else if (auto t = e->asTemp()) { - visitTemp(t); - } else if (auto a = e->asArgLocal()) { - visitArgLocal(a); - } else if (auto c = e->asClosure()) { - visitClosure(c); - } else if (auto c = e->asConvert()) { - visitConvert(c); - } else if (auto u = e->asUnop()) { - visitUnop(u); - } else if (auto b = e->asBinop()) { - visitBinop(b); - } else if (auto c = e->asCall()) { - visitCall(c); - } else if (auto n = e->asNew()) { - visitNew(n); - } else if (auto s = e->asSubscript()) { - visitSubscript(s); - } else if (auto m = e->asMember()) { - visitMember(m); - } else { - Q_UNREACHABLE(); - } - } - - void visitConst(Const *c) { - if (c->type & NumberType) { - if (canConvertToSignedInteger(c->value)) - _ty = TypingResult(SInt32Type); - else if (canConvertToUnsignedInteger(c->value)) - _ty = TypingResult(UInt32Type); - else - _ty = TypingResult(c->type); - } else - _ty = TypingResult(c->type); - } - void visitString(IR::String *) { _ty = TypingResult(StringType); } - void visitRegExp(IR::RegExp *) { _ty = TypingResult(VarType); } - void visitName(Name *) { _ty = TypingResult(VarType); } - void visitTemp(Temp *e) { - if (e->memberResolver && e->memberResolver->isValid()) - _ty = TypingResult(e->memberResolver); - else - _ty = TypingResult(_tempTypes[e->index]); - setType(e, _ty.type); - } - void visitArgLocal(ArgLocal *e) { - _ty = TypingResult(VarType); - setType(e, _ty.type); - } - - void visitClosure(Closure *) { _ty = TypingResult(VarType); } - void visitConvert(Convert *e) { - _ty = TypingResult(e->type); - } - - void visitUnop(Unop *e) { - _ty = run(e->expr); - switch (e->op) { - case OpUPlus: _ty.type = DoubleType; return; - case OpUMinus: _ty.type = DoubleType; return; - case OpCompl: _ty.type = SInt32Type; return; - case OpNot: _ty.type = BoolType; return; - - case OpIncrement: - case OpDecrement: - Q_ASSERT(!"Inplace operators should have been removed!"); - Q_UNREACHABLE(); - default: - Q_UNIMPLEMENTED(); - Q_UNREACHABLE(); - } - } - - void visitBinop(Binop *e) { - TypingResult leftTy = run(e->left); - TypingResult rightTy = run(e->right); - _ty.fullyTyped = leftTy.fullyTyped && rightTy.fullyTyped; - - switch (e->op) { - case OpAdd: - if (leftTy.type.test(VarType) || leftTy.type.test(QObjectType) || rightTy.type.test(VarType) || rightTy.type.test(QObjectType)) - _ty.type = VarType; - else if (leftTy.type.test(StringType) || rightTy.type.test(StringType)) - _ty.type = StringType; - else if (leftTy.type != UnknownType && rightTy.type != UnknownType) - _ty.type = DoubleType; - else - _ty.type = UnknownType; - break; - case OpSub: - _ty.type = DoubleType; - break; - - case OpMul: - case OpDiv: - case OpMod: - _ty.type = DoubleType; - break; - - case OpBitAnd: - case OpBitOr: - case OpBitXor: - case OpLShift: - case OpRShift: - _ty.type = SInt32Type; - break; - case OpURShift: - _ty.type = UInt32Type; - break; - - case OpGt: - case OpLt: - case OpGe: - case OpLe: - case OpEqual: - case OpNotEqual: - case OpStrictEqual: - case OpStrictNotEqual: - case OpAnd: - case OpOr: - case OpInstanceof: - case OpIn: - _ty.type = BoolType; - break; - - default: - Q_UNIMPLEMENTED(); - Q_UNREACHABLE(); - } - } - - void visitCall(Call *e) { - _ty = run(e->base); - for (ExprList *it = e->args; it; it = it->next) - _ty.fullyTyped &= run(it->expr).fullyTyped; - _ty.type = VarType; - } - void visitNew(New *e) { - _ty = run(e->base); - for (ExprList *it = e->args; it; it = it->next) - _ty.fullyTyped &= run(it->expr).fullyTyped; - _ty.type = VarType; - } - void visitSubscript(Subscript *e) { - _ty.fullyTyped = run(e->base).fullyTyped && run(e->index).fullyTyped; - _ty.type = VarType; - } - - void visitMember(Member *e) { - _ty = run(e->base); - - if (_ty.fullyTyped && _ty.type.memberResolver && _ty.type.memberResolver->isValid()) { - MemberExpressionResolver *resolver = _ty.type.memberResolver; - _ty.type = resolver->resolveMember(qmlEngine, resolver, e); - } else - _ty.type = VarType; - } - - void visit(Stmt *s) - { - if (auto e = s->asExp()) { - visitExp(e); - } else if (auto m = s->asMove()) { - visitMove(m); - } else if (auto j = s->asJump()) { - visitJump(j); - } else if (auto c = s->asCJump()) { - visitCJump(c); - } else if (auto r = s->asRet()) { - visitRet(r); - } else if (auto p = s->asPhi()) { - visitPhi(p); - } else { - Q_UNREACHABLE(); - } - } - - void visitExp(Exp *s) { _ty = run(s->expr); } - void visitMove(Move *s) { - if (Temp *t = s->target->asTemp()) { - if (Name *n = s->source->asName()) { - if (n->builtin == Name::builtin_qml_context) { - _ty = TypingResult(t->memberResolver); - setType(n, _ty.type); - setType(t, _ty.type); - return; - } - } - TypingResult sourceTy = run(s->source); - setType(t, sourceTy.type); - _ty = sourceTy; - return; - } - - TypingResult sourceTy = run(s->source); - _ty = run(s->target); - _ty.fullyTyped &= sourceTy.fullyTyped; - } - - void visitJump(Jump *) { _ty = TypingResult(MissingType); } - void visitCJump(CJump *s) { _ty = run(s->cond); } - void visitRet(Ret *s) { _ty = run(s->expr); } - void visitPhi(Phi *s) { - _ty = run(s->incoming[0]); - for (int i = 1, ei = s->incoming.size(); i != ei; ++i) { - TypingResult ty = run(s->incoming[i]); - if (!ty.fullyTyped && _ty.fullyTyped) { - // When one of the temps not fully typed, we already know that we cannot completely type this node. - // So, pick the type we calculated upto this point, and wait until the unknown one will be typed. - // At that point, this statement will be re-scheduled, and then we can fully type this node. - _ty.fullyTyped = false; - break; - } - _ty.type.type |= ty.type.type; - _ty.fullyTyped &= ty.fullyTyped; - if (_ty.type.test(QObjectType) && _ty.type.memberResolver) - _ty.type.memberResolver->clear(); // ### TODO: find common ancestor meta-object - } - - switch (_ty.type.type) { - case UnknownType: - case UndefinedType: - case NullType: - case BoolType: - case SInt32Type: - case UInt32Type: - case DoubleType: - case StringType: - case QObjectType: - case VarType: - // The type is not a combination of two or more types, so we're done. - break; - - default: - // There are multiple types involved, so: - if (_ty.type.isNumber()) - // The type is any combination of double/int32/uint32, but nothing else. So we can - // type it as double. - _ty.type = DoubleType; - else - // There just is no single type that can hold this combination, so: - _ty.type = VarType; - } - - setType(s->targetTemp, _ty.type); - } -}; - -class ReverseInference -{ - const DefUses &_defUses; - -public: - ReverseInference(const DefUses &defUses) - : _defUses(defUses) - {} - - void run(IR::Function *f) - { - Q_UNUSED(f); - - QVector<UntypedTemp> knownOk; - QVector<UntypedTemp> candidates = _defUses.defsUntyped(); - while (!candidates.isEmpty()) { - UntypedTemp temp = candidates.last(); - candidates.removeLast(); - - if (knownOk.contains(temp)) - continue; - - if (!isUsedAsInt32(temp, knownOk)) - continue; - - Stmt *s = _defUses.defStmt(temp.temp); - Move *m = s->asMove(); - if (!m) - continue; - Temp *target = m->target->asTemp(); - if (!target || temp != UntypedTemp(*target) || target->type == SInt32Type) - continue; - if (Temp *t = m->source->asTemp()) { - candidates.append(*t); - } else if (m->source->asConvert()) { - break; - } else if (Binop *b = m->source->asBinop()) { - bool iterateOnOperands = true; - - switch (b->op) { - case OpSub: - case OpMul: - case OpAdd: - if (b->left->type == SInt32Type && b->right->type == SInt32Type) { - iterateOnOperands = false; - break; - } else { - continue; - } - case OpBitAnd: - case OpBitOr: - case OpBitXor: - case OpLShift: - case OpRShift: - case OpURShift: - break; - default: - continue; - } - - if (iterateOnOperands) { - if (Temp *lt = b->left->asTemp()) - candidates.append(*lt); - if (Temp *rt = b->right->asTemp()) - candidates.append(*rt); - } - } else if (Unop *u = m->source->asUnop()) { - if (u->op == OpCompl || u->op == OpUPlus) { - if (Temp *t = u->expr->asTemp()) - candidates.append(*t); - } - } else { - continue; - } - - knownOk.append(temp); - } - - PropagateTempTypes propagator(_defUses); - for (const UntypedTemp &t : qAsConst(knownOk)) { - propagator.run(t, SInt32Type); - if (Stmt *defStmt = _defUses.defStmt(t.temp)) { - if (Move *m = defStmt->asMove()) { - if (Convert *c = m->source->asConvert()) { - c->type = SInt32Type; - } else if (Unop *u = m->source->asUnop()) { - if (u->op != OpUMinus) - u->type = SInt32Type; - } else if (Binop *b = m->source->asBinop()) { - b->type = SInt32Type; - } - } - } - } - } - -private: - bool isUsedAsInt32(const UntypedTemp &t, const QVector<UntypedTemp> &knownOk) const - { - const QVector<Stmt *> &uses = _defUses.uses(t.temp); - if (uses.isEmpty()) - return false; - - for (Stmt *use : uses) { - if (Move *m = use->asMove()) { - Temp *targetTemp = m->target->asTemp(); - - if (m->source->asTemp()) { - if (!targetTemp || !knownOk.contains(*targetTemp)) - return false; - } else if (m->source->asConvert()) { - continue; - } else if (Binop *b = m->source->asBinop()) { - switch (b->op) { - case OpAdd: - case OpSub: - case OpMul: - if (!targetTemp || !knownOk.contains(*targetTemp)) - return false; - Q_FALLTHROUGH(); - case OpBitAnd: - case OpBitOr: - case OpBitXor: - case OpRShift: - case OpLShift: - case OpURShift: - continue; - default: - return false; - } - } else if (Unop *u = m->source->asUnop()) { - if (u->op == OpUPlus) { - if (!targetTemp || !knownOk.contains(*targetTemp)) - return false; - } else if (u->op != OpCompl) { - return false; - } - } else { - return false; - } - } else - return false; - } - - return true; - } -}; - -void convertConst(Const *c, Type targetType) -{ - switch (targetType) { - case DoubleType: - break; - case SInt32Type: - c->value = QV4::Primitive::toInt32(c->value); - break; - case UInt32Type: - c->value = QV4::Primitive::toUInt32(c->value); - break; - case BoolType: - c->value = !(c->value == 0 || std::isnan(c->value)); - break; - case NullType: - case UndefinedType: - c->value = qt_qnan(); - c->type = targetType; - break; - default: - Q_UNIMPLEMENTED(); - Q_ASSERT(!"Unimplemented!"); - break; - } - c->type = targetType; -} - -class TypePropagation -{ - DefUses &_defUses; - Type _ty; - IR::Function *_f; - - bool run(Expr *&e, Type requestedType = UnknownType, bool insertConversion = true) { - qSwap(_ty, requestedType); - visit(e); - qSwap(_ty, requestedType); - - if (requestedType != UnknownType) { - if (e->type != requestedType) { - if (requestedType & NumberType || requestedType == BoolType) { - if (insertConversion) - addConversion(e, requestedType); - return true; - } - } - } - - return false; - } - - struct Conversion { - Expr **expr; - Type targetType; - Stmt *stmt; - - Conversion(Expr **expr = 0, Type targetType = UnknownType, Stmt *stmt = 0) - : expr(expr) - , targetType(targetType) - , stmt(stmt) - {} - }; - - Stmt *_currStmt; - QVector<Conversion> _conversions; - - void addConversion(Expr *&expr, Type targetType) { - _conversions.append(Conversion(&expr, targetType, _currStmt)); - } - -public: - TypePropagation(DefUses &defUses) : _defUses(defUses), _ty(UnknownType) {} - - void run(IR::Function *f, StatementWorklist &worklist) { - _f = f; - for (BasicBlock *bb : f->basicBlocks()) { - if (bb->isRemoved()) - continue; - _conversions.clear(); - - for (Stmt *s : bb->statements()) { - _currStmt = s; - visit(s); - } - - for (const Conversion &conversion : qAsConst(_conversions)) { - IR::Move *move = conversion.stmt->asMove(); - - // Note: isel only supports move into member when source is a temp, so convert - // is not a supported source. - if (move && move->source->asTemp() && !move->target->asMember()) { - *conversion.expr = bb->CONVERT(*conversion.expr, conversion.targetType); - } else if (Const *c = (*conversion.expr)->asConst()) { - convertConst(c, conversion.targetType); - } else if (ArgLocal *al = (*conversion.expr)->asArgLocal()) { - Temp *target = bb->TEMP(bb->newTemp(BasicBlock::NewTempForOptimizer)); - target->type = conversion.targetType; - Expr *convert = bb->CONVERT(al, conversion.targetType); - Move *convCall = f->NewStmt<Move>(); - worklist.registerNewStatement(convCall); - convCall->init(target, convert); - _defUses.addDef(target, convCall, bb); - - Temp *source = bb->TEMP(target->index); - source->type = conversion.targetType; - _defUses.addUse(*source, conversion.stmt); - - if (conversion.stmt->asPhi()) { - // Only temps can be used as arguments to phi nodes, so this is a sanity check...: - Q_UNREACHABLE(); - } else { - bb->insertStatementBefore(conversion.stmt, convCall); - } - - *conversion.expr = source; - } else if (Temp *t = (*conversion.expr)->asTemp()) { - Temp *target = bb->TEMP(bb->newTemp(BasicBlock::NewTempForOptimizer)); - target->type = conversion.targetType; - Expr *convert = bb->CONVERT(t, conversion.targetType); - Move *convCall = f->NewStmt<Move>(); - worklist.registerNewStatement(convCall); - convCall->init(target, convert); - _defUses.addDef(target, convCall, bb); - _defUses.addUse(*t, convCall); - - Temp *source = bb->TEMP(target->index); - source->type = conversion.targetType; - _defUses.removeUse(conversion.stmt, *t); - _defUses.addUse(*source, conversion.stmt); - - if (Phi *phi = conversion.stmt->asPhi()) { - int idx = phi->incoming.indexOf(t); - Q_ASSERT(idx != -1); - bb->in[idx]->insertStatementBeforeTerminator(convCall); - } else { - bb->insertStatementBefore(conversion.stmt, convCall); - } - - *conversion.expr = source; - } else if (Unop *u = (*conversion.expr)->asUnop()) { - // convert: - // int32{%2} = double{-double{%1}}; - // to: - // double{%3} = double{-double{%1}}; - // int32{%2} = int32{convert(double{%3})}; - Temp *tmp = bb->TEMP(bb->newTemp(BasicBlock::NewTempForOptimizer)); - tmp->type = u->type; - Move *extraMove = f->NewStmt<Move>(); - worklist.registerNewStatement(extraMove); - extraMove->init(tmp, u); - _defUses.addDef(tmp, extraMove, bb); - - if (Temp *unopOperand = u->expr->asTemp()) { - _defUses.addUse(*unopOperand, extraMove); - _defUses.removeUse(move, *unopOperand); - } - - bb->insertStatementBefore(conversion.stmt, extraMove); - - *conversion.expr = bb->CONVERT(CloneExpr::cloneTemp(tmp, f), conversion.targetType); - _defUses.addUse(*tmp, move); - } else { - Q_UNREACHABLE(); - } - } - } - } - -private: - void visit(Expr *e) - { - if (auto c = e->asConst()) { - visitConst(c); - } else if (auto c = e->asConvert()) { - run(c->expr, c->type); - } else if (auto u = e->asUnop()) { - run(u->expr, u->type); - } else if (auto b = e->asBinop()) { - visitBinop(b); - } else if (auto c = e->asCall()) { - visitCall(c); - } else if (auto n = e->asNew()) { - visitNew(n); - } else if (auto s = e->asSubscript()) { - visitSubscript(s); - } else if (auto m = e->asMember()) { - visitMember(m); - } - } - - void visitConst(Const *c) { - if (_ty & NumberType && c->type & NumberType) { - if (_ty == SInt32Type) - c->value = QV4::Primitive::toInt32(c->value); - else if (_ty == UInt32Type) - c->value = QV4::Primitive::toUInt32(c->value); - c->type = _ty; - } - } - - void visitBinop(Binop *e) { - // FIXME: This routine needs more tuning! - switch (e->op) { - case OpAdd: - case OpSub: - case OpMul: - case OpDiv: - case OpMod: - case OpBitAnd: - case OpBitOr: - case OpBitXor: - run(e->left, e->type); - run(e->right, e->type); - break; - - case OpLShift: - case OpRShift: - case OpURShift: - run(e->left, SInt32Type); - run(e->right, SInt32Type); - break; - - case OpGt: - case OpLt: - case OpGe: - case OpLe: - case OpEqual: - case OpNotEqual: - if (e->left->type == DoubleType) { - run(e->right, DoubleType); - } else if (e->right->type == DoubleType) { - run(e->left, DoubleType); - } else { - run(e->left, e->left->type); - run(e->right, e->right->type); - } - break; - - case OpStrictEqual: - case OpStrictNotEqual: - case OpInstanceof: - case OpIn: - run(e->left, e->left->type); - run(e->right, e->right->type); - break; - - default: - Q_UNIMPLEMENTED(); - Q_UNREACHABLE(); - } - } - void visitCall(Call *e) { - run(e->base); - for (ExprList *it = e->args; it; it = it->next) - run(it->expr); - } - void visitNew(New *e) { - run(e->base); - for (ExprList *it = e->args; it; it = it->next) - run(it->expr); - } - void visitSubscript(Subscript *e) { run(e->base); run(e->index); } - void visitMember(Member *e) { run(e->base); } - - void visit(Stmt *s) - { - if (auto e = s->asExp()) { - visitExp(e); - } else if (auto m = s->asMove()) { - visitMove(m); - } else if (auto c = s->asCJump()) { - visitCJump(c); - } else if (auto r = s->asRet()) { - visitRet(r); - } else if (auto p = s->asPhi()) { - visitPhi(p); - } - } - - void visitExp(Exp *s) { run(s->expr); } - void visitMove(Move *s) { - if (s->source->asConvert()) - return; // this statement got inserted for a phi-node type conversion - - run(s->target); - - if (Unop *u = s->source->asUnop()) { - if (u->op == OpUPlus) { - if (run(u->expr, s->target->type, false)) { - Convert *convert = _f->New<Convert>(); - convert->init(u->expr, s->target->type); - s->source = convert; - } else { - s->source = u->expr; - } - - return; - } - } - - const Member *targetMember = s->target->asMember(); - const bool inhibitConversion = targetMember && targetMember->inhibitTypeConversionOnWrite; - - run(s->source, s->target->type, !inhibitConversion); - } - void visitCJump(CJump *s) { - run(s->cond, BoolType); - } - void visitRet(Ret *s) { run(s->expr); } - void visitPhi(Phi *s) { - Type ty = s->targetTemp->type; - for (int i = 0, ei = s->incoming.size(); i != ei; ++i) - run(s->incoming[i], ty); - } -}; - -void splitCriticalEdges(IR::Function *f, DominatorTree &df, StatementWorklist &worklist, DefUses &defUses) -{ - const QVector<BasicBlock *> copy = f->basicBlocks(); - for (BasicBlock *toBB : copy) { - if (toBB->isRemoved()) - continue; - if (toBB->in.size() < 2) - continue; - - for (int inIdx = 0, eInIdx = toBB->in.size(); inIdx != eInIdx; ++inIdx) { - BasicBlock *fromBB = toBB->in[inIdx]; - if (fromBB->out.size() < 2) - continue; - - // We found a critical edge. - // create the basic block: - BasicBlock *newBB = f->newBasicBlock(toBB->catchBlock); - Jump *s = f->NewStmt<Jump>(); - worklist.registerNewStatement(s); - defUses.registerNewStatement(s); - s->init(toBB); - newBB->appendStatement(s); - - // rewire the old outgoing edge - int outIdx = fromBB->out.indexOf(toBB); - fromBB->out[outIdx] = newBB; - newBB->in.append(fromBB); - - // rewire the old incoming edge - toBB->in[inIdx] = newBB; - newBB->out.append(toBB); - - // add newBB to the correct loop group - if (toBB->isGroupStart()) { - if (fromBB == toBB) { - // special case: the loop header points back to itself (so it's a small loop). - newBB->setContainingGroup(toBB); - } else { - BasicBlock *container; - for (container = fromBB->containingGroup(); container; container = container->containingGroup()) - if (container == toBB) - break; - if (container == toBB) // if we were already inside the toBB loop - newBB->setContainingGroup(toBB); - else - newBB->setContainingGroup(toBB->containingGroup()); - } - } else { - newBB->setContainingGroup(toBB->containingGroup()); - } - - // patch the terminator - Stmt *terminator = fromBB->terminator(); - if (Jump *j = terminator->asJump()) { - Q_ASSERT(outIdx == 0); - j->target = newBB; - } else if (CJump *j = terminator->asCJump()) { - if (outIdx == 0) - j->iftrue = newBB; - else if (outIdx == 1) - j->iffalse = newBB; - else - Q_ASSERT(!"Invalid out edge index for CJUMP!"); - } else if (terminator->asRet()) { - Q_ASSERT(!"A block with a RET at the end cannot have outgoing edges."); - } else { - Q_ASSERT(!"Unknown terminator!"); - } - -// qDebug() << "splitting edge" << fromBB->index() << "->" << toBB->index() -// << "by inserting block" << newBB->index(); - - // Set the immediate dominator of the new block to inBB - df.setImmediateDominator(newBB, fromBB); - - bool toNeedsNewIdom = true; - for (BasicBlock *bb : toBB->in) { - if (bb != newBB && bb != toBB && !df.dominates(toBB, bb)) { - toNeedsNewIdom = false; - break; - } - } - if (toNeedsNewIdom) - df.setImmediateDominator(toBB, newBB); - } - } -} - -// Detect all (sub-)loops in a function. -// -// Doing loop detection on the CFG is better than relying on the statement information in -// order to mark loops. Although JavaScript only has natural loops, it can still be the case -// that something is not a loop even though a loop-like-statement is in the source. For -// example: -// while (true) { -// if (i > 0) -// break; -// else -// break; -// } -// -// Algorithm: -// - do a DFS on the dominator tree, where for each node: -// - collect all back-edges -// - if there are back-edges, the node is a loop-header for a new loop, so: -// - walk the CFG is reverse-direction, and for every node: -// - if the node already belongs to a loop, we've found a nested loop: -// - get the loop-header for the (outermost) nested loop -// - add that loop-header to the current loop -// - continue by walking all incoming edges that do not yet belong to the current loop -// - if the node does not belong to a loop yet, add it to the current loop, and -// go on with all incoming edges -// -// Loop-header detection by checking for back-edges is very straight forward: a back-edge is -// an incoming edge where the other node is dominated by the current node. Meaning: all -// execution paths that reach that other node have to go through the current node, that other -// node ends with a (conditional) jump back to the loop header. -// -// The exact order of the DFS on the dominator tree is not important. The only property has to -// be that a node is only visited when all the nodes it dominates have been visited before. -// The reason for the DFS is that for nested loops, the inner loop's loop-header is dominated -// by the outer loop's header. So, by visiting depth-first, sub-loops are identified before -// their containing loops, which makes nested-loop identification free. An added benefit is -// that the nodes for those sub-loops are only processed once. -// -// Note: independent loops that share the same header are merged together. For example, in -// the code snippet below, there are 2 back-edges into the loop-header, but only one single -// loop will be detected. -// while (a) { -// if (b) -// continue; -// else -// continue; -// } -class LoopDetection -{ - enum { DebugLoopDetection = 0 }; - - Q_DISABLE_COPY(LoopDetection) - -public: - struct LoopInfo - { - BasicBlock *loopHeader; - QVector<BasicBlock *> loopBody; - QVector<LoopInfo *> nestedLoops; - LoopInfo *parentLoop; - - LoopInfo(BasicBlock *loopHeader = 0) - : loopHeader(loopHeader) - , parentLoop(0) - {} - - bool isValid() const - { return loopHeader != 0; } - - void addNestedLoop(LoopInfo *nested) - { - Q_ASSERT(nested); - Q_ASSERT(!nestedLoops.contains(nested)); - Q_ASSERT(nested->parentLoop == 0); - nested->parentLoop = this; - nestedLoops.append(nested); - } - }; - -public: - LoopDetection(const DominatorTree &dt) - : dt(dt) - {} - - ~LoopDetection() - { - qDeleteAll(loopInfos); - } - - void run(IR::Function *function) - { - std::vector<BasicBlock *> backedges; - backedges.reserve(4); - - const auto order = dt.calculateDFNodeIterOrder(); - for (BasicBlock *bb : order) { - Q_ASSERT(!bb->isRemoved()); - - backedges.clear(); - - for (BasicBlock *in : bb->in) - if (bb == in || dt.dominates(bb, in)) - backedges.push_back(in); - - if (!backedges.empty()) { - subLoop(bb, backedges); - } - } - - createLoopInfos(function); - dumpLoopInfo(); - } - - void dumpLoopInfo() const - { - if (!DebugLoopDetection) - return; - - qDebug() << "Found" << loopInfos.size() << "loops"; - for (const LoopInfo *info : loopInfos) { - qDebug() << "Loop header:" << info->loopHeader->index() - << "for loop" << quint64(info); - for (BasicBlock *bb : info->loopBody) - qDebug() << " " << bb->index(); - for (LoopInfo *nested : info->nestedLoops) - qDebug() << " sub loop:" << quint64(nested); - qDebug() << " parent loop:" << quint64(info->parentLoop); - } - } - - QVector<LoopInfo *> allLoops() const - { return loopInfos; } - - // returns all loop headers for loops that have no nested loops. - QVector<LoopInfo *> innermostLoops() const - { - QVector<LoopInfo *> inner(loopInfos); - - for (int i = 0; i < inner.size(); ) { - if (inner.at(i)->nestedLoops.isEmpty()) - ++i; - else - inner.remove(i); - } - - return inner; - } - -private: - void subLoop(BasicBlock *loopHead, const std::vector<BasicBlock *> &backedges) - { - loopHead->markAsGroupStart(); - LoopInfo *info = new LoopInfo; - info->loopHeader = loopHead; - loopInfos.append(info); - - std::vector<BasicBlock *> worklist; - worklist.reserve(backedges.size() + 8); - worklist.insert(worklist.end(), backedges.begin(), backedges.end()); - while (!worklist.empty()) { - BasicBlock *predIt = worklist.back(); - worklist.pop_back(); - - BasicBlock *subloop = predIt->containingGroup(); - if (subloop) { - // This is a discovered block. Find its outermost discovered loop. - while (BasicBlock *parentLoop = subloop->containingGroup()) - subloop = parentLoop; - - // If it is already discovered to be a subloop of this loop, continue. - if (subloop == loopHead) - continue; - - // Yay, it's a subloop of this loop. - subloop->setContainingGroup(loopHead); - predIt = subloop; - - // Add all predecessors of the subloop header to the worklist, as long as - // those predecessors are not in the current subloop. It might be the case - // that they are in other loops, which we will then add as a subloop to the - // current loop. - for (BasicBlock *predIn : predIt->in) - if (predIn->containingGroup() != subloop) - worklist.push_back(predIn); - } else { - if (predIt == loopHead) - continue; - - // This is an undiscovered block. Map it to the current loop. - predIt->setContainingGroup(loopHead); - - // Add all incoming edges to the worklist. - for (BasicBlock *bb : predIt->in) - worklist.push_back(bb); - } - } - } - -private: - const DominatorTree &dt; - QVector<LoopInfo *> loopInfos; - - void createLoopInfos(IR::Function *function) - { - for (BasicBlock *bb : function->basicBlocks()) { - if (bb->isRemoved()) - continue; - if (BasicBlock *loopHeader = bb->containingGroup()) - findLoop(loopHeader)->loopBody.append(bb); - } - - for (int i = 0, size = loopInfos.size(); i < size; ++i) { - if (BasicBlock *containingLoopHeader = loopInfos.at(i)->loopHeader->containingGroup()) - findLoop(containingLoopHeader)->addNestedLoop(loopInfos.at(i)); - } - } - - LoopInfo *findLoop(BasicBlock *loopHeader) - { - for (LoopInfo *info : qAsConst(loopInfos)) { - if (info->loopHeader == loopHeader) - return info; - } - - Q_UNREACHABLE(); - return nullptr; - } -}; - -// High-level algorithm: -// 0. start with the first node (the start node) of a function -// 1. emit the node -// 2. add all outgoing edges that are not yet emitted to the postponed stack -// 3. When the postponed stack is empty, pop a stack from the loop stack. If that is empty too, -// we're done. -// 4. pop a node from the postponed stack, and check if it can be scheduled: -// a. if all incoming edges are scheduled, go to 4. -// b. if an incoming edge is unscheduled, but it's a back-edge (an edge in a loop that jumps -// back to the start of the loop), ignore it -// c. if there is any unscheduled edge that is not a back-edge, ignore this node, and go to 4. -// 5. if this node is the start of a loop, push the postponed stack on the loop stack. -// 6. go back to 1. -// -// The postponing action in step 2 will put the node into its containing group. The case where this -// is important is when a (labeled) continue or a (labeled) break statement occur in a loop: the -// outgoing edge points to a node that is not part of the current loop (and possibly not of the -// parent loop). -// -// Linear scan register allocation benefits greatly from short life-time intervals with few holes -// (see for example section 4 (Lifetime Analysis) of [Wimmer1]). This algorithm makes sure that the -// blocks of a group are scheduled together, with no non-loop blocks in between. This applies -// recursively for nested loops. It also schedules groups of if-then-else-endif blocks together for -// the same reason. -class BlockScheduler -{ - enum { DebugBlockScheduler = 0 }; - - IR::Function *function; - const DominatorTree &dominatorTree; - - struct WorkForGroup - { - BasicBlock *group; - QStack<BasicBlock *> postponed; - - WorkForGroup(BasicBlock *group = 0): group(group) {} - }; - WorkForGroup currentGroup; - QStack<WorkForGroup> postponedGroups; - QVector<BasicBlock *> sequence; - ProcessedBlocks emitted; - QHash<BasicBlock *, BasicBlock *> loopsStartEnd; - - bool checkCandidate(BasicBlock *candidate) - { - Q_ASSERT(candidate->containingGroup() == currentGroup.group); - - for (BasicBlock *in : candidate->in) { - if (emitted.alreadyProcessed(in)) - continue; - - if (dominatorTree.dominates(candidate, in)) - // this is a loop, where there in -> candidate edge is the jump back to the top of the loop. - continue; - - if (in == candidate) - // this is a very tight loop, e.g.: - // L1: ... - // goto L1 - // This can happen when, for example, the basic-block merging gets rid of the empty - // body block. In this case, we can safely schedule this block (if all other - // incoming edges are either loop-back edges, or have been scheduled already). - continue; - - return false; // an incoming edge that is not yet emitted, and is not a back-edge - } - - if (candidate->isGroupStart()) { - // postpone everything, and schedule the loop first. - postponedGroups.push(currentGroup); - currentGroup = WorkForGroup(candidate); - } - - return true; - } - - BasicBlock *pickNext() - { - while (true) { - while (currentGroup.postponed.isEmpty()) { - if (postponedGroups.isEmpty()) - return 0; - if (currentGroup.group) // record the first and the last node of a group - loopsStartEnd.insert(currentGroup.group, sequence.last()); - currentGroup = postponedGroups.pop(); - } - - BasicBlock *next = currentGroup.postponed.pop(); - if (checkCandidate(next)) - return next; - } - - Q_UNREACHABLE(); - return 0; - } - - void emitBlock(BasicBlock *bb) - { - Q_ASSERT(!bb->isRemoved()); - if (emitted.alreadyProcessed(bb)) - return; - - sequence.append(bb); - emitted.markAsProcessed(bb); - } - - void schedule(BasicBlock *functionEntryPoint) - { - BasicBlock *next = functionEntryPoint; - - while (next) { - emitBlock(next); - for (int i = next->out.size(); i != 0; ) { - // postpone all outgoing edges, if they were not already processed - --i; - BasicBlock *out = next->out[i]; - if (!emitted.alreadyProcessed(out)) - postpone(out); - } - next = pickNext(); - } - } - - void postpone(BasicBlock *bb) - { - if (currentGroup.group == bb->containingGroup()) { - currentGroup.postponed.append(bb); - return; - } - - for (int i = postponedGroups.size(); i != 0; ) { - --i; - WorkForGroup &g = postponedGroups[i]; - if (g.group == bb->containingGroup()) { - g.postponed.append(bb); - return; - } - } - - Q_UNREACHABLE(); - } - - void dumpLoopStartsEnds() const - { - qDebug() << "Found" << loopsStartEnd.size() << "loops:"; - for (auto key : loopsStartEnd.keys()) - qDebug("Loop starting at L%d ends at L%d.", key->index(), - loopsStartEnd.value(key)->index()); - } - -public: - BlockScheduler(IR::Function *function, const DominatorTree &dominatorTree) - : function(function) - , dominatorTree(dominatorTree) - , sequence(0) - , emitted(function) - {} - - QHash<BasicBlock *, BasicBlock *> go() - { - showMeTheCode(function, "Before block scheduling"); - if (DebugBlockScheduler) - dominatorTree.dumpImmediateDominators(); - - schedule(function->basicBlock(0)); - - Q_ASSERT(function->liveBasicBlocksCount() == sequence.size()); - function->setScheduledBlocks(sequence); - if (DebugBlockScheduler) - dumpLoopStartsEnds(); - return loopsStartEnd; - } -}; - -#ifndef QT_NO_DEBUG -void checkCriticalEdges(const QVector<BasicBlock *> &basicBlocks) { - for (BasicBlock *bb : basicBlocks) { - if (bb && bb->out.size() > 1) { - for (BasicBlock *bb2 : bb->out) { - if (bb2 && bb2->in.size() > 1) { - qDebug() << "found critical edge between block" - << bb->index() << "and block" << bb2->index(); - Q_ASSERT(false); - } - } - } - } -} -#endif - -static void cleanupBasicBlocks(IR::Function *function) -{ - showMeTheCode(function, "Before basic block cleanup"); - - // Algorithm: this is the iterative version of a depth-first search for all blocks that are - // reachable through outgoing edges, starting with the start block and all exception handler - // blocks. - QBitArray reachableBlocks(function->basicBlockCount()); - QVarLengthArray<BasicBlock *, 16> postponed; - for (int i = 0, ei = function->basicBlockCount(); i != ei; ++i) { - BasicBlock *bb = function->basicBlock(i); - if (i == 0 || bb->isExceptionHandler()) - postponed.append(bb); - } - - while (!postponed.isEmpty()) { - BasicBlock *bb = postponed.back(); - postponed.pop_back(); - if (bb->isRemoved()) // this block was removed before, we don't need to clean it up. - continue; - - reachableBlocks.setBit(bb->index()); - - for (BasicBlock *outBB : bb->out) { - if (!reachableBlocks.at(outBB->index())) - postponed.append(outBB); - } - } - - for (BasicBlock *bb : function->basicBlocks()) { - if (bb->isRemoved()) // the block has already been removed, so ignore it - continue; - if (reachableBlocks.at(bb->index())) // the block is reachable, so ignore it - continue; - - for (BasicBlock *outBB : bb->out) { - if (outBB->isRemoved() || !reachableBlocks.at(outBB->index())) - continue; // We do not need to unlink from blocks that are scheduled to be removed. - - int idx = outBB->in.indexOf(bb); - if (idx != -1) { - outBB->in.remove(idx); - for (Stmt *s : outBB->statements()) { - if (Phi *phi = s->asPhi()) - phi->incoming.remove(idx); - else - break; - } - } - } - - function->removeBasicBlock(bb); - } - - showMeTheCode(function, "After basic block cleanup"); -} - -inline Const *isConstPhi(Phi *phi) -{ - if (Const *c = phi->incoming[0]->asConst()) { - for (int i = 1, ei = phi->incoming.size(); i != ei; ++i) { - if (Const *cc = phi->incoming[i]->asConst()) { - if (c->value != cc->value) - return 0; - if (!(c->type == cc->type || (c->type & NumberType && cc->type & NumberType))) - return 0; - if (int(c->value) == 0 && int(cc->value) == 0) - if (isNegative(c->value) != isNegative(cc->value)) - return 0; - } else { - return 0; - } - } - return c; - } - return 0; -} - -static Expr *clone(Expr *e, IR::Function *function) { - if (Temp *t = e->asTemp()) { - return CloneExpr::cloneTemp(t, function); - } else if (Const *c = e->asConst()) { - return CloneExpr::cloneConst(c, function); - } else if (Name *n = e->asName()) { - return CloneExpr::cloneName(n, function); - } else { - Q_UNREACHABLE(); - return e; - } -} - -class ExprReplacer -{ - DefUses &_defUses; - IR::Function* _function; - Temp *_toReplace; - Expr *_replacement; - -public: - ExprReplacer(DefUses &defUses, IR::Function *function) - : _defUses(defUses) - , _function(function) - , _toReplace(0) - , _replacement(0) - {} - - bool operator()(Temp *toReplace, Expr *replacement, StatementWorklist &W, QVector<Stmt *> *newUses = 0) - { - Q_ASSERT(replacement->asTemp() || replacement->asConst() || replacement->asName()); - - qSwap(_toReplace, toReplace); - qSwap(_replacement, replacement); - - const QVector<Stmt *> &uses = _defUses.uses(*_toReplace); - - // Prevent the following: - // L3: - // %1 = phi L1: %2, L2: %3 - // %4 = phi L1: %5, L2: %6 - // %6 = %1 - // From turning into: - // L3: - // %1 = phi L1: %2, L2: %3 - // %4 = phi L1: %5, L2: %1 - // - // Because both phi nodes are "executed in parallel", we cannot replace %6 by %1 in the - // second phi node. So, if the defining statement for a temp is a phi node, and one of the - // uses of the to-be-replaced statement is a phi node in the same block as the defining - // statement, bail out. - if (Temp *r = _replacement->asTemp()) { - if (_defUses.defStmt(*r)->asPhi()) { - BasicBlock *replacementDefBlock = _defUses.defStmtBlock(*r); - for (Stmt *use : uses) { - if (Phi *usePhi = use->asPhi()) { - if (_defUses.defStmtBlock(*usePhi->targetTemp) == replacementDefBlock) - return false; - } - } - } - } - -// qout << "Replacing ";toReplace->dump(qout);qout<<" by ";replacement->dump(qout);qout<<endl; - - if (newUses) - newUses->reserve(uses.size()); - -// qout << " " << uses.size() << " uses:"<<endl; - for (Stmt *use : uses) { -// qout<<" ";use->dump(qout);qout<<"\n"; - visit(use); -// qout<<" -> ";use->dump(qout);qout<<"\n"; - W += use; - if (newUses) - newUses->push_back(use); - } - - qSwap(_replacement, replacement); - qSwap(_toReplace, toReplace); - return true; - } - -private: - void visit(Expr *e) - { - if (auto c = e->asConst()) { - visitConst(c); - } else if (auto s = e->asString()) { - visitString(s); - } else if (auto r = e->asRegExp()) { - visitRegExp(r); - } else if (auto n = e->asName()) { - visitName(n); - } else if (auto t = e->asTemp()) { - visitTemp(t); - } else if (auto a = e->asArgLocal()) { - visitArgLocal(a); - } else if (auto c = e->asClosure()) { - visitClosure(c); - } else if (auto c = e->asConvert()) { - visitConvert(c); - } else if (auto u = e->asUnop()) { - visitUnop(u); - } else if (auto b = e->asBinop()) { - visitBinop(b); - } else if (auto c = e->asCall()) { - visitCall(c); - } else if (auto n = e->asNew()) { - visitNew(n); - } else if (auto s = e->asSubscript()) { - visitSubscript(s); - } else if (auto m = e->asMember()) { - visitMember(m); - } else { - Q_UNREACHABLE(); - } - } - - void visitConst(Const *) {} - void visitString(IR::String *) {} - void visitRegExp(IR::RegExp *) {} - void visitName(Name *) {} - void visitTemp(Temp *) {} - void visitArgLocal(ArgLocal *) {} - void visitClosure(Closure *) {} - void visitConvert(Convert *e) { check(e->expr); } - void visitUnop(Unop *e) { check(e->expr); } - void visitBinop(Binop *e) { check(e->left); check(e->right); } - void visitCall(Call *e) { - check(e->base); - for (ExprList *it = e->args; it; it = it->next) - check(it->expr); - } - void visitNew(New *e) { - check(e->base); - for (ExprList *it = e->args; it; it = it->next) - check(it->expr); - } - void visitSubscript(Subscript *e) { check(e->base); check(e->index); } - void visitMember(Member *e) { check(e->base); } - - void visit(Stmt *s) - { - if (auto e = s->asExp()) { - visitExp(e); - } else if (auto m = s->asMove()) { - visitMove(m); - } else if (auto j = s->asJump()) { - visitJump(j); - } else if (auto c = s->asCJump()) { - visitCJump(c); - } else if (auto r = s->asRet()) { - visitRet(r); - } else if (auto p = s->asPhi()) { - visitPhi(p); - } else { - Q_UNREACHABLE(); - } - } - - void visitExp(Exp *s) { check(s->expr); } - void visitMove(Move *s) { check(s->target); check(s->source); } - void visitJump(Jump *) {} - void visitCJump(CJump *s) { check(s->cond); } - void visitRet(Ret *s) { check(s->expr); } - void visitPhi(Phi *s) { - for (int i = 0, ei = s->incoming.size(); i != ei; ++i) - check(s->incoming[i]); - } - -private: - void check(Expr *&e) { - if (equals(e, _toReplace)) { - e = clone(_replacement, _function); - } else { - visit(e); - } - } - - // This only calculates equality for everything needed by constant propagation - bool equals(Expr *e1, Expr *e2) const { - if (e1 == e2) - return true; - - if (Const *c1 = e1->asConst()) { - if (Const *c2 = e2->asConst()) - return c1->value == c2->value && (c1->type == c2->type || (c1->type & NumberType && c2->type & NumberType)); - } else if (Temp *t1 = e1->asTemp()) { - if (Temp *t2 = e2->asTemp()) - return *t1 == *t2; - } else if (Name *n1 = e1->asName()) { - if (Name *n2 = e2->asName()) { - if (n1->id) { - if (n2->id) - return *n1->id == *n2->id; - } else { - return n1->builtin == n2->builtin; - } - } - } - - if (e1->type == IR::NullType && e2->type == IR::NullType) - return true; - if (e1->type == IR::UndefinedType && e2->type == IR::UndefinedType) - return true; - - return false; - } -}; - -namespace { -/// This function removes the basic-block from the function's list, unlinks any uses and/or defs, -/// and removes unreachable staements from the worklist, so that optimiseSSA won't consider them -/// anymore. -void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUses, - StatementWorklist &W, DominatorTree &dt) -{ - enum { DebugUnlinking = 0 }; - - struct Util { - static void removeIncomingEdge(BasicBlock *from, BasicBlock *to, DefUses &defUses, StatementWorklist &W) - { - int idx = to->in.indexOf(from); - if (idx == -1) - return; - - to->in.remove(idx); - for (Stmt *outStmt : to->statements()) { - if (!outStmt) - continue; - if (Phi *phi = outStmt->asPhi()) { - if (Temp *t = phi->incoming[idx]->asTemp()) { - defUses.removeUse(phi, *t); - W += defUses.defStmt(*t); - } - phi->incoming.remove(idx); - W += phi; - } else { - break; - } - } - } - - static bool isReachable(BasicBlock *bb, const DominatorTree &dt) - { - for (BasicBlock *in : bb->in) { - if (in->isRemoved()) - continue; - if (dt.dominates(bb, in)) // a back-edge, not interesting - continue; - return true; - } - - return false; - } - }; - - Q_ASSERT(!from->isRemoved()); - Q_ASSERT(!to->isRemoved()); - - // don't purge blocks that are entry points for catch statements. They might not be directly - // connected, but are required anyway - if (to->isExceptionHandler()) - return; - - if (DebugUnlinking) - qDebug("Unlinking L%d -> L%d...", from->index(), to->index()); - - // First, unlink the edge - from->out.removeOne(to); - Util::removeIncomingEdge(from, to, defUses, W); - - BasicBlockSet siblings; - siblings.init(func); - - // Check if the target is still reachable... - if (Util::isReachable(to, dt)) { // yes, recalculate the immediate dominator, and we're done. - if (DebugUnlinking) - qDebug(".. L%d is still reachable, recalulate idom.", to->index()); - dt.collectSiblings(to, siblings); - } else { - if (DebugUnlinking) - qDebug(".. L%d is unreachable, purging it:", to->index()); - // The target is unreachable, so purge it: - QVector<BasicBlock *> toPurge; - toPurge.reserve(8); - toPurge.append(to); - while (!toPurge.isEmpty()) { - BasicBlock *bb = toPurge.first(); - toPurge.removeFirst(); - if (DebugUnlinking) - qDebug("... purging L%d", bb->index()); - - if (bb->isRemoved()) - continue; - - // unlink all incoming edges - for (BasicBlock *in : bb->in) { - int idx = in->out.indexOf(bb); - if (idx != -1) - in->out.remove(idx); - } - - // unlink all outgoing edges, including "arguments" to phi statements - for (BasicBlock *out : bb->out) { - if (out->isRemoved()) - continue; - - Util::removeIncomingEdge(bb, out, defUses, W); - - if (Util::isReachable(out, dt)) { - dt.collectSiblings(out, siblings); - } else { - // if a successor has no incoming edges after unlinking the current basic block, - // then it is unreachable, and can be purged too - toPurge.append(out); - } - } - - // unlink all defs/uses from the statements in the basic block - for (Stmt *s : bb->statements()) { - if (!s) - continue; - - W += defUses.removeDefUses(s); - W -= s; - } - - siblings.remove(bb); - dt.setImmediateDominator(bb, 0); - func->removeBasicBlock(bb); - } - } - - dt.recalculateIDoms(siblings); - if (DebugUnlinking) - qDebug("Unlinking done."); -} - -bool tryOptimizingComparison(Expr *&expr) -{ - Binop *b = expr->asBinop(); - if (!b) - return false; - Const *leftConst = b->left->asConst(); - if (!leftConst || leftConst->type == StringType || leftConst->type == VarType || leftConst->type == QObjectType) - return false; - Const *rightConst = b->right->asConst(); - if (!rightConst || rightConst->type == StringType || rightConst->type == VarType || rightConst->type == QObjectType) - return false; - - QV4::Primitive l = convertToValue(leftConst); - QV4::Primitive r = convertToValue(rightConst); - - switch (b->op) { - case OpGt: - leftConst->value = Runtime::method_compareGreaterThan(l, r); - leftConst->type = BoolType; - expr = leftConst; - return true; - case OpLt: - leftConst->value = Runtime::method_compareLessThan(l, r); - leftConst->type = BoolType; - expr = leftConst; - return true; - case OpGe: - leftConst->value = Runtime::method_compareGreaterEqual(l, r); - leftConst->type = BoolType; - expr = leftConst; - return true; - case OpLe: - leftConst->value = Runtime::method_compareLessEqual(l, r); - leftConst->type = BoolType; - expr = leftConst; - return true; - case OpStrictEqual: - leftConst->value = Runtime::method_compareStrictEqual(l, r); - leftConst->type = BoolType; - expr = leftConst; - return true; - case OpEqual: - leftConst->value = Runtime::method_compareEqual(l, r); - leftConst->type = BoolType; - expr = leftConst; - return true; - case OpStrictNotEqual: - leftConst->value = Runtime::method_compareStrictNotEqual(l, r); - leftConst->type = BoolType; - expr = leftConst; - return true; - case OpNotEqual: - leftConst->value = Runtime::method_compareNotEqual(l, r); - leftConst->type = BoolType; - expr = leftConst; - return true; - default: - break; - } - - return false; -} - -void cfg2dot(IR::Function *f, const QVector<LoopDetection::LoopInfo *> &loops = QVector<LoopDetection::LoopInfo *>()) -{ - static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_IR"); - if (!showCode) - return; - - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - - struct Util { - QTextStream &qout; - Util(QTextStream &qout): qout(qout) {} - void genLoop(const LoopDetection::LoopInfo *loop) - { - qout << " subgraph \"cluster" << quint64(loop) << "\" {\n"; - qout << " L" << loop->loopHeader->index() << ";\n"; - for (BasicBlock *bb : loop->loopBody) - qout << " L" << bb->index() << ";\n"; - for (LoopDetection::LoopInfo *nested : loop->nestedLoops) - genLoop(nested); - qout << " }\n"; - } - }; - - QString name; - if (f->name) name = *f->name; - else name = QStringLiteral("%1").arg((unsigned long long)f); - qout << "digraph \"" << name << "\" { ordering=out;\n"; - - for (LoopDetection::LoopInfo *l : loops) { - if (l->parentLoop == 0) - Util(qout).genLoop(l); - } - - for (BasicBlock *bb : f->basicBlocks()) { - if (bb->isRemoved()) - continue; - - int idx = bb->index(); - qout << " L" << idx << " [label=\"L" << idx << "\""; - if (idx == 0 || bb->terminator()->asRet()) - qout << ", shape=doublecircle"; - else - qout << ", shape=circle"; - qout << "];\n"; - for (BasicBlock *out : bb->out) - qout << " L" << idx << " -> L" << out->index() << "\n"; - } - - qout << "}\n"; - buf.close(); - qDebug("%s", buf.data().constData()); -} - -} // anonymous namespace - -void optimizeSSA(StatementWorklist &W, DefUses &defUses, DominatorTree &df) -{ - IR::Function *function = W.function(); - ExprReplacer replaceUses(defUses, function); - - Stmt *s = 0; - while ((s = W.takeNext(s))) { - - if (Phi *phi = s->asPhi()) { - // dead code elimination: - if (defUses.useCount(*phi->targetTemp) == 0) { - W += defUses.removeDefUses(phi); - W.remove(s); - continue; - } - - // constant propagation: - if (Const *c = isConstPhi(phi)) { - replaceUses(phi->targetTemp, c, W); - defUses.removeDef(*phi->targetTemp); - W.remove(s); - continue; - } - - // copy propagation: - if (phi->incoming.size() == 1) { - Temp *t = phi->targetTemp; - Expr *e = phi->incoming.first(); - - QVector<Stmt *> newT2Uses; - replaceUses(t, e, W, &newT2Uses); - if (Temp *t2 = e->asTemp()) { - defUses.removeUse(s, *t2); - defUses.addUses(*t2, newT2Uses); - W += defUses.defStmt(*t2); - } - defUses.removeDef(*t); - W.remove(s); - continue; - } - } else if (Move *m = s->asMove()) { - if (Convert *convert = m->source->asConvert()) { - if (Const *sourceConst = convert->expr->asConst()) { - convertConst(sourceConst, convert->type); - m->source = sourceConst; - W += m; - continue; - } else if (Temp *sourceTemp = convert->expr->asTemp()) { - if (sourceTemp->type == convert->type) { - m->source = sourceTemp; - W += m; - continue; - } - } - } - - if (Temp *targetTemp = m->target->asTemp()) { - // dead code elimination: - if (defUses.useCount(*targetTemp) == 0) { - EliminateDeadCode(defUses, W).run(m->source, s); - if (!m->source) - W.remove(s); - continue; - } - - // constant propagation: - if (Const *sourceConst = m->source->asConst()) { - Q_ASSERT(sourceConst->type != UnknownType); - replaceUses(targetTemp, sourceConst, W); - defUses.removeDef(*targetTemp); - W.remove(s); - continue; - } - if (Member *member = m->source->asMember()) { - if (member->kind == Member::MemberOfEnum) { - Const *c = function->New<Const>(); - const int enumValue = member->enumValue; - c->init(SInt32Type, enumValue); - replaceUses(targetTemp, c, W); - defUses.removeDef(*targetTemp); - W.remove(s); - defUses.removeUse(s, *member->base->asTemp()); - continue; - } else if (member->kind != IR::Member::MemberOfIdObjectsArray && member->attachedPropertiesId != 0 && member->property && member->base->asTemp()) { - // Attached properties have no dependency on their base. Isel doesn't - // need it and we can eliminate the temp used to initialize it. - defUses.removeUse(s, *member->base->asTemp()); - Const *c = function->New<Const>(); - c->init(SInt32Type, 0); - member->base = c; - continue; - } - } - - // copy propagation: - if (Temp *sourceTemp = m->source->asTemp()) { - QVector<Stmt *> newT2Uses; - if (replaceUses(targetTemp, sourceTemp, W, &newT2Uses)) { - defUses.removeUse(s, *sourceTemp); - defUses.addUses(*sourceTemp, newT2Uses); - defUses.removeDef(*targetTemp); - W.remove(s); - } - continue; - } - - if (Unop *unop = m->source->asUnop()) { - // Constant unary expression evaluation: - if (Const *constOperand = unop->expr->asConst()) { - if (constOperand->type & NumberType || constOperand->type == BoolType) { - // TODO: implement unop propagation for other constant types - bool doneSomething = false; - switch (unop->op) { - case OpNot: - constOperand->value = !constOperand->value; - constOperand->type = BoolType; - doneSomething = true; - break; - case OpUMinus: - if (int(constOperand->value) == 0 && int(constOperand->value) == constOperand->value) { - if (isNegative(constOperand->value)) - constOperand->value = 0; - else - constOperand->value = -1 / Q_INFINITY; - constOperand->type = DoubleType; - doneSomething = true; - break; - } - - constOperand->value = -constOperand->value; - doneSomething = true; - break; - case OpUPlus: - if (unop->type != UnknownType) - constOperand->type = unop->type; - doneSomething = true; - break; - case OpCompl: - constOperand->value = ~QV4::Primitive::toInt32(constOperand->value); - constOperand->type = SInt32Type; - doneSomething = true; - break; - case OpIncrement: - constOperand->value = constOperand->value + 1; - doneSomething = true; - break; - case OpDecrement: - constOperand->value = constOperand->value - 1; - doneSomething = true; - break; - default: - break; - }; - - if (doneSomething) { - m->source = constOperand; - W += m; - } - } - } - // TODO: if the result of a unary not operation is only used in a cjump, - // then inline it. - - continue; - } - - if (Binop *binop = m->source->asBinop()) { - Const *leftConst = binop->left->asConst(); - Const *rightConst = binop->right->asConst(); - - { // Typical casts to int32: - Expr *casted = 0; - switch (binop->op) { - case OpBitAnd: - if (leftConst && !rightConst && QV4::Primitive::toUInt32(leftConst->value) == 0xffffffff) - casted = binop->right; - else if (!leftConst && rightConst && QV4::Primitive::toUInt32(rightConst->value) == 0xffffffff) - casted = binop->left; - break; - case OpBitOr: - if (leftConst && !rightConst && QV4::Primitive::toInt32(leftConst->value) == 0) - casted = binop->right; - else if (!leftConst && rightConst && QV4::Primitive::toUInt32(rightConst->value) == 0) - casted = binop->left; - break; - default: - break; - } - if (casted && casted->type == SInt32Type) { - m->source = casted; - W += m; - continue; - } - } - if (rightConst) { - switch (binop->op) { - case OpLShift: - case OpRShift: - if (double v = QV4::Primitive::toInt32(rightConst->value) & 0x1f) { - // mask right hand side of shift operations - rightConst->value = v; - rightConst->type = SInt32Type; - } else { - // shifting a value over 0 bits is a move: - if (rightConst->value == 0) { - m->source = binop->left; - W += m; - } - } - - break; - default: - break; - } - } - - // TODO: More constant binary expression evaluation - // TODO: If the result of the move is only used in one single cjump, then - // inline the binop into the cjump. - if (!leftConst || leftConst->type == StringType || leftConst->type == VarType || leftConst->type == QObjectType) - continue; - if (!rightConst || rightConst->type == StringType || rightConst->type == VarType || rightConst->type == QObjectType) - continue; - - QV4::Primitive lc = convertToValue(leftConst); - QV4::Primitive rc = convertToValue(rightConst); - double l = lc.toNumber(); - double r = rc.toNumber(); - - switch (binop->op) { - case OpMul: - leftConst->value = l * r; - leftConst->type = DoubleType; - m->source = leftConst; - W += m; - break; - case OpAdd: - leftConst->value = l + r; - leftConst->type = DoubleType; - m->source = leftConst; - W += m; - break; - case OpSub: - leftConst->value = l - r; - leftConst->type = DoubleType; - m->source = leftConst; - W += m; - break; - case OpDiv: - leftConst->value = l / r; - leftConst->type = DoubleType; - m->source = leftConst; - W += m; - break; - case OpMod: - leftConst->value = std::fmod(l, r); - leftConst->type = DoubleType; - m->source = leftConst; - W += m; - break; - default: - if (tryOptimizingComparison(m->source)) - W += m; - break; - } - - continue; - } - } // TODO: var{#0} = double{%10} where %10 is defined once and used once. E.g.: function(t){t = t % 2; return t; } - - } else if (CJump *cjump = s->asCJump()) { - if (Const *constantCondition = cjump->cond->asConst()) { - // Note: this assumes that there are no critical edges! Meaning, we can safely purge - // any basic blocks that are found to be unreachable. - Jump *jump = function->NewStmt<Jump>(); - W.registerNewStatement(jump); - if (convertToValue(constantCondition).toBoolean()) { - jump->target = cjump->iftrue; - unlink(cjump->parent, cjump->iffalse, function, defUses, W, df); - } else { - jump->target = cjump->iffalse; - unlink(cjump->parent, cjump->iftrue, function, defUses, W, df); - } - W.replace(s, jump); - - continue; - } else if (cjump->cond->asBinop()) { - if (tryOptimizingComparison(cjump->cond)) - W += cjump; - continue; - } - // TODO: Constant unary expression evaluation - // TODO: if the expression is an unary not operation, lift the expression, and switch - // the then/else blocks. - } - } - - W.applyToFunction(); -} - -//### TODO: use DefUses from the optimizer, because it already has all this information -class InputOutputCollector -{ - void setOutput(Temp *out) - { - Q_ASSERT(!output); - output = out; - } - -public: - std::vector<Temp *> inputs; - Temp *output; - - InputOutputCollector() - { inputs.reserve(4); } - - void collect(Stmt *s) { - inputs.resize(0); - output = 0; - visit(s); - } - -private: - void visit(Expr *e) - { - if (auto t = e->asTemp()) { - inputs.push_back(t); - } else { - EXPR_VISIT_ALL_KINDS(e); - } - } - - void visit(Stmt *s) - { - if (auto m = s->asMove()) { - visit(m->source); - if (Temp *t = m->target->asTemp()) { - setOutput(t); - } else { - visit(m->target); - } - } else if (s->asPhi()) { - // Handled separately - } else { - STMT_VISIT_ALL_KINDS(s); - } - } -}; - -/* - * The algorithm is described in: - * - * Linear Scan Register Allocation on SSA Form - * Christian Wimmer & Michael Franz, CGO'10, April 24-28, 2010 - * - * See LifeTimeIntervals::renumber for details on the numbering. - */ -class LifeRanges { - class LiveRegs - { - typedef std::vector<int> Storage; - Storage regs; - - public: - void insert(int r) - { - if (find(r) == end()) - regs.push_back(r); - } - - void unite(const LiveRegs &other) - { - if (other.empty()) - return; - if (empty()) { - regs = other.regs; - return; - } - for (int r : other.regs) - insert(r); - } - - typedef Storage::iterator iterator; - iterator find(int r) - { return std::find(regs.begin(), regs.end(), r); } - - iterator begin() - { return regs.begin(); } - - iterator end() - { return regs.end(); } - - void erase(iterator it) - { regs.erase(it); } - - void remove(int r) - { - iterator it = find(r); - if (it != end()) - erase(it); - } - - bool empty() const - { return regs.empty(); } - - int size() const - { return int(regs.size()); } - - int at(int idx) const - { return regs.at(idx); } - }; - - std::vector<LiveRegs> _liveIn; - std::vector<LifeTimeInterval *> _intervals; - LifeTimeIntervals::Ptr _sortedIntervals; - - LifeTimeInterval &interval(const Temp *temp) - { - LifeTimeInterval *lti = _intervals[temp->index]; - Q_ASSERT(lti); - return *lti; - } - - void ensureInterval(const IR::Temp &temp) - { - Q_ASSERT(!temp.isInvalid()); - LifeTimeInterval *<i = _intervals[temp.index]; - if (lti) - return; - lti = new LifeTimeInterval; - lti->setTemp(temp); - } - - int defPosition(IR::Stmt *s) const - { - return usePosition(s) + 1; - } - - int usePosition(IR::Stmt *s) const - { - return _sortedIntervals->positionForStatement(s); - } - - int start(IR::BasicBlock *bb) const - { - return _sortedIntervals->startPosition(bb); - } - - int end(IR::BasicBlock *bb) const - { - return _sortedIntervals->endPosition(bb); - } - -public: - LifeRanges(IR::Function *function, const QHash<BasicBlock *, BasicBlock *> &startEndLoops) - : _intervals(function->tempCount) - { - _sortedIntervals = LifeTimeIntervals::create(function); - _liveIn.resize(function->basicBlockCount()); - - for (int i = function->basicBlockCount() - 1; i >= 0; --i) { - BasicBlock *bb = function->basicBlock(i); - buildIntervals(bb, startEndLoops.value(bb, 0)); - } - - _intervals.clear(); - } - - LifeTimeIntervals::Ptr intervals() const { return _sortedIntervals; } - - void dump() const - { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - - qout << "Life ranges:" << endl; - qout << "Intervals:" << endl; - const auto intervals = _sortedIntervals->intervals(); - for (const LifeTimeInterval *range : intervals) { - range->dump(qout); - qout << endl; - } - - IRPrinter printer(&qout); - for (size_t i = 0, ei = _liveIn.size(); i != ei; ++i) { - qout << "L" << i <<" live-in: "; - auto live = _liveIn.at(i); - if (live.empty()) - qout << "(none)"; - std::sort(live.begin(), live.end()); - for (int i = 0; i < live.size(); ++i) { - if (i > 0) qout << ", "; - qout << '%' << live.at(i); - } - qout << endl; - } - buf.close(); - qDebug("%s", buf.data().constData()); - } - -private: - void buildIntervals(BasicBlock *bb, BasicBlock *loopEnd) - { - LiveRegs live; - for (BasicBlock *successor : bb->out) { - live.unite(_liveIn[successor->index()]); - const int bbIndex = successor->in.indexOf(bb); - Q_ASSERT(bbIndex >= 0); - - for (Stmt *s : successor->statements()) { - if (Phi *phi = s->asPhi()) { - if (Temp *t = phi->incoming.at(bbIndex)->asTemp()) { - ensureInterval(*t); - live.insert(t->index); - } - } else { - break; - } - } - } - - const QVector<Stmt *> &statements = bb->statements(); - - for (int reg : live) - _intervals[reg]->addRange(start(bb), end(bb)); - - InputOutputCollector collector; - for (int i = statements.size() - 1; i >= 0; --i) { - Stmt *s = statements.at(i); - if (Phi *phi = s->asPhi()) { - ensureInterval(*phi->targetTemp); - LiveRegs::iterator it = live.find(phi->targetTemp->index); - if (it == live.end()) { - // a phi node target that is only defined, but never used - interval(phi->targetTemp).setFrom(start(bb)); - } else { - live.erase(it); - } - _sortedIntervals->add(&interval(phi->targetTemp)); - continue; - } - collector.collect(s); - //### TODO: use DefUses from the optimizer, because it already has all this information - if (Temp *opd = collector.output) { - ensureInterval(*opd); - LifeTimeInterval <i = interval(opd); - lti.setFrom(defPosition(s)); - live.remove(lti.temp().index); - _sortedIntervals->add(<i); - } - //### TODO: use DefUses from the optimizer, because it already has all this information - for (size_t i = 0, ei = collector.inputs.size(); i != ei; ++i) { - Temp *opd = collector.inputs[i]; - ensureInterval(*opd); - interval(opd).addRange(start(bb), usePosition(s)); - live.insert(opd->index); - } - } - - if (loopEnd) { // Meaning: bb is a loop header, because loopEnd is set to non-null. - for (int reg : live) - _intervals[reg]->addRange(start(bb), usePosition(loopEnd->terminator())); - } - - _liveIn[bb->index()] = std::move(live); - } -}; - -void removeUnreachleBlocks(IR::Function *function) -{ - QVector<BasicBlock *> newSchedule; - newSchedule.reserve(function->basicBlockCount()); - for (BasicBlock *bb : function->basicBlocks()) - if (!bb->isRemoved()) - newSchedule.append(bb); - function->setScheduledBlocks(newSchedule); -} - -class ConvertArgLocals -{ -public: - ConvertArgLocals(IR::Function *function) - : function(function) - , convertArgs(!function->usesArgumentsObject) - { - tempForFormal.resize(function->formals.size(), -1); - tempForLocal.resize(function->locals.size(), -1); - } - - void toTemps() - { - if (function->variablesCanEscape()) - return; - - QVector<Stmt *> extraMoves; - if (convertArgs) { - const int formalCount = function->formals.size(); - extraMoves.reserve(formalCount + function->basicBlock(0)->statementCount()); - extraMoves.resize(formalCount); - - for (int i = 0; i != formalCount; ++i) { - const int newTemp = function->tempCount++; - tempForFormal[i] = newTemp; - - ArgLocal *source = function->New<ArgLocal>(); - source->init(ArgLocal::Formal, i, 0); - - Temp *target = function->New<Temp>(); - target->init(Temp::VirtualRegister, newTemp); - - Move *m = function->NewStmt<Move>(); - m->init(target, source); - extraMoves[i] = m; - } - } - - for (BasicBlock *bb : function->basicBlocks()) { - if (!bb->isRemoved()) { - for (Stmt *s : bb->statements()) { - visit(s); - } - } - } - - if (convertArgs && function->formals.size() > 0) - function->basicBlock(0)->prependStatements(extraMoves); - - function->locals.clear(); - } - -private: - void visit(Stmt *s) - { - if (auto e = s->asExp()) { - check(e->expr); - } else if (auto m = s->asMove()) { - check(m->target); check(m->source); - } else if (auto c = s->asCJump()) { - check(c->cond); - } else if (auto r = s->asRet()) { - check(r->expr); - } - } - - void visit(Expr *e) - { - if (auto c = e->asConvert()) { - check(c->expr); - } else if (auto u = e->asUnop()) { - check(u->expr); - } else if (auto b = e->asBinop()) { - check(b->left); check(b->right); - } else if (auto c = e->asCall()) { - check(c->base); - for (ExprList *it = c->args; it; it = it->next) { - check(it->expr); - } - } else if (auto n = e->asNew()) { - check(n->base); - for (ExprList *it = n->args; it; it = it->next) { - check(it->expr); - } - } else if (auto s = e->asSubscript()) { - check(s->base); check(s->index); - } else if (auto m = e->asMember()) { - check(m->base); - } - } - - void check(Expr *&e) { - if (ArgLocal *al = e->asArgLocal()) { - if (al->kind == ArgLocal::Local) { - Temp *t = function->New<Temp>(); - t->init(Temp::VirtualRegister, fetchTempForLocal(al->index)); - e = t; - } else if (convertArgs && al->kind == ArgLocal::Formal) { - Temp *t = function->New<Temp>(); - t->init(Temp::VirtualRegister, fetchTempForFormal(al->index)); - e = t; - } - } else { - visit(e); - } - } - - int fetchTempForLocal(int local) - { - int &ref = tempForLocal[local]; - if (ref == -1) - ref = function->tempCount++; - return ref; - } - - int fetchTempForFormal(int formal) - { - return tempForFormal[formal]; - } - - IR::Function *function; - bool convertArgs; - std::vector<int> tempForFormal; - std::vector<int> tempForLocal; -}; - -class CloneBasicBlock: protected CloneExpr -{ -public: - BasicBlock *operator()(IR::BasicBlock *originalBlock) - { - block = new BasicBlock(originalBlock->function, 0); - - for (Stmt *s : originalBlock->statements()) { - visit(s); - clonedStmt->location = s->location; - } - - return block; - } - -private: - void visit(Stmt *s) - { - if (auto e = s->asExp()) { - clonedStmt = block->EXP(clone(e->expr)); - } else if (auto m = s->asMove()) { - clonedStmt = block->MOVE(clone(m->target), clone(m->source)); - } else if (auto j = s->asJump()) { - clonedStmt = block->JUMP(j->target); - } else if (auto c = s->asCJump()) { - clonedStmt = block->CJUMP(clone(c->cond), c->iftrue, c->iffalse); - } else if (auto r = s->asRet()) { - clonedStmt = block->RET(clone(r->expr)); - } else if (auto p = s->asPhi()) { - Phi *phi = block->function->NewStmt<Phi>(); - clonedStmt = phi; - - phi->targetTemp = clone(p->targetTemp); - for (Expr *in : p->incoming) - phi->incoming.append(clone(in)); - block->appendStatement(phi); - } else { - Q_UNREACHABLE(); - } - } - -private: - IR::Stmt *clonedStmt; -}; - -static void verifyCFG(IR::Function *function) -{ - if (!DoVerification) - return; - - for (BasicBlock *bb : function->basicBlocks()) { - if (bb->isRemoved()) { - Q_ASSERT(bb->in.isEmpty()); - Q_ASSERT(bb->out.isEmpty()); - continue; - } - - Q_ASSERT(function->basicBlock(bb->index()) == bb); - - // Check the terminators: - Stmt *terminator = bb->terminator(); - if (terminator == nullptr) { - Stmt *last = bb->statements().last(); - Call *call = last->asExp()->expr->asCall(); - Name *baseName = call->base->asName(); - Q_ASSERT(baseName->builtin == Name::builtin_rethrow); - Q_UNUSED(baseName); - } else if (Jump *jump = terminator->asJump()) { - Q_UNUSED(jump); - Q_ASSERT(jump->target); - Q_ASSERT(!jump->target->isRemoved()); - Q_ASSERT(bb->out.size() == 1); - Q_ASSERT(bb->out.first() == jump->target); - } else if (CJump *cjump = terminator->asCJump()) { - Q_UNUSED(cjump); - Q_ASSERT(bb->out.size() == 2); - Q_ASSERT(cjump->iftrue); - Q_ASSERT(!cjump->iftrue->isRemoved()); - Q_ASSERT(cjump->iftrue == bb->out[0]); - Q_ASSERT(cjump->iffalse); - Q_ASSERT(!cjump->iffalse->isRemoved()); - Q_ASSERT(cjump->iffalse == bb->out[1]); - } else if (terminator->asRet()) { - Q_ASSERT(bb->out.size() == 0); - } else { - Q_UNREACHABLE(); - } - - // Check the outgoing edges: - for (BasicBlock *out : bb->out) { - Q_UNUSED(out); - Q_ASSERT(!out->isRemoved()); - Q_ASSERT(out->in.contains(bb)); - } - - // Check the incoming edges: - for (BasicBlock *in : bb->in) { - Q_UNUSED(in); - Q_ASSERT(!in->isRemoved()); - Q_ASSERT(in->out.contains(bb)); - } - } -} - -static void verifyImmediateDominators(const DominatorTree &dt, IR::Function *function) -{ - if (!DoVerification) - return; - - cfg2dot(function); - dt.dumpImmediateDominators(); - DominatorTree referenceTree(function); - - for (BasicBlock *bb : function->basicBlocks()) { - if (bb->isRemoved()) - continue; - - BasicBlock *idom = dt.immediateDominator(bb); - BasicBlock *referenceIdom = referenceTree.immediateDominator(bb); - Q_UNUSED(idom); - Q_UNUSED(referenceIdom); - Q_ASSERT(idom == referenceIdom); - } -} - -static void verifyNoPointerSharing(IR::Function *function) -{ - if (!DoVerification) - return; - - class { - public: - void operator()(IR::Function *f) - { - for (BasicBlock *bb : f->basicBlocks()) { - if (bb->isRemoved()) - continue; - - for (Stmt *s : bb->statements()) { - visit(s); - } - } - } - - private: - void visit(Stmt *s) - { - check(s); - STMT_VISIT_ALL_KINDS(s); - } - - void visit(Expr *e) - { - check(e); - EXPR_VISIT_ALL_KINDS(e); - } - - private: - void check(Stmt *s) - { - Q_ASSERT(!stmts.contains(s)); - stmts.insert(s); - } - - void check(Expr *e) - { - Q_ASSERT(!exprs.contains(e)); - exprs.insert(e); - } - - QSet<Stmt *> stmts; - QSet<Expr *> exprs; - } V; - V(function); -} - -// Loop-peeling is done by unfolding the loop once. The "original" loop basic blocks stay where they -// are, and a copy of the loop is placed after it. Special care is taken while copying the loop body: -// by having the copies of the basic-blocks point to the same nodes as the "original" basic blocks, -// updating the immediate dominators is easy: if the edge of a copied basic-block B points to a -// block C that has also been copied, set the immediate dominator of B to the corresponding -// immediate dominator of C. Finally, for any node outside the loop that gets a new edge attached, -// the immediate dominator has to be re-calculated. -class LoopPeeling -{ - DominatorTree &dt; - -public: - LoopPeeling(DominatorTree &dt) - : dt(dt) - {} - - void run(const QVector<LoopDetection::LoopInfo *> &loops) - { - for (LoopDetection::LoopInfo *loopInfo : loops) - peelLoop(loopInfo); - } - -private: - // All copies have their outgoing edges pointing to the same successor block as the originals. - // For each copied block, check where the outgoing edges point to. If it's a block inside the - // (original) loop, rewire it to the corresponding copy. Otherwise, which is when it points - // out of the loop, leave it alone. - // As an extra, collect all edges that point out of the copied loop, because the targets need - // to have their immediate dominator rechecked. - void rewire(BasicBlock *newLoopBlock, const QVector<BasicBlock *> &from, const QVector<BasicBlock *> &to, QVector<BasicBlock *> &loopExits) - { - for (int i = 0, ei = newLoopBlock->out.size(); i != ei; ++i) { - BasicBlock *&out = newLoopBlock->out[i]; - const int idx = from.indexOf(out); - if (idx == -1) { - if (!loopExits.contains(out)) - loopExits.append(out); - } else { - out->in.removeOne(newLoopBlock); - BasicBlock *newTo = to.at(idx); - newTo->in.append(newLoopBlock); - out = newTo; - - Stmt *terminator = newLoopBlock->terminator(); - if (Jump *jump = terminator->asJump()) { - Q_ASSERT(i == 0); - jump->target = newTo; - } else if (CJump *cjump = terminator->asCJump()) { - Q_ASSERT(i == 0 || i == 1); - if (i == 0) - cjump->iftrue = newTo; - else - cjump->iffalse = newTo; - } - } - } - } - - void peelLoop(LoopDetection::LoopInfo *loop) - { - IR::Function *f = loop->loopHeader->function; - CloneBasicBlock clone; - - LoopDetection::LoopInfo unpeeled(*loop); - unpeeled.loopHeader = clone(unpeeled.loopHeader); - unpeeled.loopHeader->setContainingGroup(loop->loopHeader->containingGroup()); - unpeeled.loopHeader->markAsGroupStart(true); - f->addBasicBlock(unpeeled.loopHeader); - for (int i = 0, ei = unpeeled.loopBody.size(); i != ei; ++i) { - BasicBlock *&bodyBlock = unpeeled.loopBody[i]; - bodyBlock = clone(bodyBlock); - bodyBlock->setContainingGroup(unpeeled.loopHeader); - Q_ASSERT(bodyBlock->statementCount() == loop->loopBody[i]->statementCount()); - } - - // The cloned blocks will have no incoming edges, but they do have outgoing ones (copying - // the terminators will automatically insert that edge). The blocks where the originals - // pointed to will have an extra incoming edge from the copied blocks. - - BasicBlock::IncomingEdges inCopy = loop->loopHeader->in; - for (BasicBlock *in : inCopy) { - if (loop->loopHeader != in // this can happen for really tight loops (where there are no body blocks). This is a back-edge in that case. - && unpeeled.loopHeader != in && !unpeeled.loopBody.contains(in) // if the edge is not coming from within the copied set, leave it alone - && !dt.dominates(loop->loopHeader, in)) // an edge coming from within the loop (so a back-edge): this is handled when rewiring all outgoing edges - continue; - - unpeeled.loopHeader->in.append(in); - loop->loopHeader->in.removeOne(in); - - Stmt *terminator = in->terminator(); - if (Jump *jump = terminator->asJump()) { - jump->target = unpeeled.loopHeader; - in->out[0] = unpeeled.loopHeader; - } else if (CJump *cjump = terminator->asCJump()) { - if (cjump->iftrue == loop->loopHeader) { - cjump->iftrue = unpeeled.loopHeader; - Q_ASSERT(in->out[0] == loop->loopHeader); - in->out[0] = unpeeled.loopHeader; - } else if (cjump->iffalse == loop->loopHeader) { - cjump->iffalse = unpeeled.loopHeader; - Q_ASSERT(in->out[1] == loop->loopHeader); - in->out[1] = unpeeled.loopHeader; - } else { - Q_UNREACHABLE(); - } - } - } - - QVector<BasicBlock *> loopExits; - loopExits.reserve(8); - loopExits.append(unpeeled.loopHeader); - - rewire(unpeeled.loopHeader, loop->loopBody, unpeeled.loopBody, loopExits); - for (int i = 0, ei = unpeeled.loopBody.size(); i != ei; ++i) { - BasicBlock *bodyBlock = unpeeled.loopBody.at(i); - rewire(bodyBlock, loop->loopBody, unpeeled.loopBody, loopExits); - f->addBasicBlock(bodyBlock); - } - - // The original loop is now peeled off, and won't jump back to the loop header. Meaning, it - // is not a loop anymore, so unmark it. - loop->loopHeader->markAsGroupStart(false); - for (BasicBlock *bb : qAsConst(loop->loopBody)) - bb->setContainingGroup(loop->loopHeader->containingGroup()); - - // Set the immediate dominator of the new loop header to the old one. The real immediate - // dominator will be calculated later. - dt.setImmediateDominator(unpeeled.loopHeader, loop->loopHeader); - // calculate the idoms in a separate loop, because addBasicBlock in the previous loop will - // set the block index, which in turn is used by the dominator tree. - for (int i = 0, ei = unpeeled.loopBody.size(); i != ei; ++i) { - BasicBlock *bodyBlock = unpeeled.loopBody.at(i); - BasicBlock *idom = dt.immediateDominator(loop->loopBody.at(i)); - const int idx = loop->loopBody.indexOf(idom); - if (idom == loop->loopHeader) - idom = unpeeled.loopHeader; - else if (idx != -1) - idom = unpeeled.loopBody.at(idx); - Q_ASSERT(idom); - dt.setImmediateDominator(bodyBlock, idom); - } - - BasicBlockSet siblings(f); - for (BasicBlock *bb : qAsConst(loopExits)) - dt.collectSiblings(bb, siblings); - - siblings.insert(unpeeled.loopHeader); - dt.recalculateIDoms(siblings, loop->loopHeader); - dt.dumpImmediateDominators(); - verifyImmediateDominators(dt, f); - } -}; - -class RemoveLineNumbers: private SideEffectsChecker -{ -public: - static void run(IR::Function *function) - { - for (BasicBlock *bb : function->basicBlocks()) { - if (bb->isRemoved()) - continue; - - for (Stmt *s : bb->statements()) { - if (!hasSideEffects(s)) { - s->location = QQmlJS::AST::SourceLocation(); - } - } - } - } - -private: - ~RemoveLineNumbers() {} - - static bool hasSideEffects(Stmt *stmt) - { - RemoveLineNumbers checker; - if (auto e = stmt->asExp()) { - checker.visit(e->expr); - } else if (auto m = stmt->asMove()) { - checker.visit(m->source); - if (!checker.seenSideEffects()) { - checker.visit(m->target); - } - } else if (auto c = stmt->asCJump()) { - checker.visit(c->cond); - } else if (auto r = stmt->asRet()) { - checker.visit(r->expr); - } - return checker.seenSideEffects(); - } - - void visitTemp(Temp *) override final {} -}; - -void mergeBasicBlocks(IR::Function *function, DefUses *du, DominatorTree *dt) -{ - enum { DebugBlockMerging = 0 }; - - if (function->hasTry) - return; - - showMeTheCode(function, "Before basic block merging"); - - // Now merge a basic block with its successor when there is one outgoing edge, and the - // successor has one incoming edge. - for (int i = 0, ei = function->basicBlockCount(); i != ei; ++i) { - BasicBlock *bb = function->basicBlock(i); - - bb->nextLocation = QQmlJS::AST::SourceLocation(); // make sure appendStatement doesn't mess with the line info - - if (bb->isRemoved()) continue; // the block has been removed, so ignore it - if (bb->out.size() != 1) continue; // more than one outgoing edge - BasicBlock *successor = bb->out.first(); - if (successor->in.size() != 1) continue; // more than one incoming edge - - // Loop header? No efficient way to update the other blocks that refer to this as containing group, - // so don't do merging yet. - if (successor->isGroupStart()) continue; - - // Ok, we can merge the two basic blocks. - if (DebugBlockMerging) { - qDebug("Merging L%d into L%d", successor->index(), bb->index()); - } - Q_ASSERT(bb->terminator()->asJump()); - bb->removeStatement(bb->statementCount() - 1); // remove the terminator, and replace it with: - for (Stmt *s : successor->statements()) { - bb->appendStatement(s); // add all statements from the successor to the current basic block - if (auto cjump = s->asCJump()) - cjump->parent = bb; - } - bb->out = successor->out; // set the outgoing edges to the successor's so they're now in sync with our new terminator - for (auto newSuccessor : bb->out) { - for (auto &backlink : newSuccessor->in) { - if (backlink == successor) { - backlink = bb; // for all successors of our successor: set the incoming edges to come from bb, because we'll now jump there. - } - } - } - if (du) { - // all statements in successor have moved to bb, so make sure that the containing blocks - // stored in DefUses get updated (meaning: point to bb) - du->replaceBasicBlock(successor, bb); - } - if (dt) { - // update the immediate dominators to: any block that was dominated by the successor - // will now need to point to bb's immediate dominator. The reason is that bb itself - // won't be anyones immediate dominator, because it had just one outgoing edge. - dt->mergeIntoPredecessor(successor); - } - function->removeBasicBlock(successor); - --i; // re-run on the current basic-block, so any chain gets collapsed. - } - - showMeTheCode(function, "After basic block merging"); - verifyCFG(function); -} - -} // anonymous namespace - -void LifeTimeInterval::setFrom(int from) { - Q_ASSERT(from > 0); - - if (_ranges.isEmpty()) { // this is the case where there is no use, only a define - _ranges.prepend(LifeTimeIntervalRange(from, from)); - if (_end == InvalidPosition) - _end = from; - } else { - _ranges.first().start = from; - } -} - -void LifeTimeInterval::addRange(int from, int to) { - Q_ASSERT(from > 0); - Q_ASSERT(to > 0); - Q_ASSERT(to >= from); - - if (_ranges.isEmpty()) { - _ranges.prepend(LifeTimeIntervalRange(from, to)); - _end = to; - return; - } - - LifeTimeIntervalRange *p = &_ranges.first(); - if (to + 1 >= p->start && p->end + 1 >= from) { - p->start = qMin(p->start, from); - p->end = qMax(p->end, to); - while (_ranges.count() > 1) { - LifeTimeIntervalRange *p1 = p + 1; - if (p->end + 1 < p1->start || p1->end + 1 < p->start) - break; - p1->start = qMin(p->start, p1->start); - p1->end = qMax(p->end, p1->end); - _ranges.remove(0); - p = &_ranges.first(); - } - } else { - if (to < p->start) { - _ranges.prepend(LifeTimeIntervalRange(from, to)); - } else { - Q_ASSERT(from > _ranges.last().end); - _ranges.push_back(LifeTimeIntervalRange(from, to)); - } - } - - _end = _ranges.last().end; -} - -LifeTimeInterval LifeTimeInterval::split(int atPosition, int newStart) -{ - Q_ASSERT(atPosition < newStart || newStart == InvalidPosition); - Q_ASSERT(atPosition <= _end); - Q_ASSERT(newStart <= _end || newStart == InvalidPosition); - - if (_ranges.isEmpty() || atPosition < _ranges.first().start) - return LifeTimeInterval(); - - LifeTimeInterval newInterval = *this; - newInterval.setSplitFromInterval(true); - - // search where to split the interval - for (int i = 0, ei = _ranges.size(); i < ei; ++i) { - if (_ranges.at(i).start <= atPosition) { - if (_ranges.at(i).end >= atPosition) { - // split happens in the middle of a range. Keep this range in both the old and the - // new interval, and correct the end/start later - _ranges.resize(i + 1); - newInterval._ranges.remove(0, i); - break; - } - } else { - // split happens between two ranges. - _ranges.resize(i); - newInterval._ranges.remove(0, i); - break; - } - } - - if (newInterval._ranges.first().end == atPosition) - newInterval._ranges.remove(0); - - if (newStart == InvalidPosition) { - // the temp stays inactive for the rest of its lifetime - newInterval = LifeTimeInterval(); - } else { - // find the first range where the temp will get active again: - while (!newInterval._ranges.isEmpty()) { - const LifeTimeIntervalRange &range = newInterval._ranges.first(); - if (range.start > newStart) { - // The split position is before the start of the range. Either we managed to skip - // over the correct range, or we got an invalid split request. Either way, this - // Should Never Happen <TM>. - Q_ASSERT(range.start > newStart); - return LifeTimeInterval(); - } else if (range.start <= newStart && range.end >= newStart) { - // yay, we found the range that should be the new first range in the new interval! - break; - } else { - // the temp stays inactive for this interval, so remove it. - newInterval._ranges.remove(0); - } - } - Q_ASSERT(!newInterval._ranges.isEmpty()); - newInterval._ranges.first().start = newStart; - _end = newStart; - } - - // if we're in the middle of a range, set the end to the split position - if (_ranges.last().end > atPosition) - _ranges.last().end = atPosition; - - validate(); - newInterval.validate(); - - return newInterval; -} - -void LifeTimeInterval::dump(QTextStream &out) const { - IRPrinter(&out).print(const_cast<Temp *>(&_temp)); - out << ": ends at " << _end << " with ranges "; - if (_ranges.isEmpty()) - out << "(none)"; - for (int i = 0; i < _ranges.size(); ++i) { - if (i > 0) out << ", "; - out << _ranges[i].start << " - " << _ranges[i].end; - } - if (_reg != InvalidRegister) - out << " (register " << _reg << ")"; -} - - -bool LifeTimeInterval::lessThanForTemp(const LifeTimeInterval *r1, const LifeTimeInterval *r2) -{ - return r1->temp() < r2->temp(); -} - -LifeTimeIntervals::LifeTimeIntervals(IR::Function *function) - : _basicBlockPosition(function->basicBlockCount()) - , _positionForStatement(function->statementCount(), IR::Stmt::InvalidId) - , _lastPosition(0) -{ - _intervals.reserve(function->tempCount + 32); // we reserve a bit more space for intervals, because the register allocator will add intervals with fixed ranges for each register. - renumber(function); -} - -// Renumbering works as follows: -// - phi statements are not numbered -// - statement numbers start at 0 (zero) and increment get an even number (lastPosition + 2) -// - basic blocks start at firstStatementNumber - 1, or rephrased: lastPosition + 1 -// - basic blocks end at the number of the last statement -// And during life-time calculation the next rule is used: -// - any temporary starts its life-time at definingStatementPosition + 1 -// -// This numbering simulates half-open intervals. For example: -// 0: %1 = 1 -// 2: %2 = 2 -// 4: %3 = %1 + %2 -// 6: print(%3) -// Here the half-open life-time intervals would be: -// %1: (0-4] -// %2: (2-4] -// %3: (4-6] -// Instead, we use the even statement positions for uses of temporaries, and the odd positions for -// their definitions: -// %1: [1-4] -// %2: [3-4] -// %3: [5-6] -// This has the nice advantage that placing %3 (for example) is really easy: the start will -// never overlap with the end of the uses of the operands used in the defining statement. -// -// The reason to start a basic-block at firstStatementPosition - 1 is to have correct start -// positions for target temporaries of phi-nodes. Those temporaries will now start before the -// first statement. This also means that any moves that get generated when transforming out of SSA -// form, will not interfere with (read: overlap) any defining statements in the preceding -// basic-block. -void LifeTimeIntervals::renumber(IR::Function *function) -{ - for (BasicBlock *bb : function->basicBlocks()) { - if (bb->isRemoved()) - continue; - - _basicBlockPosition[bb->index()].start = _lastPosition + 1; - - for (Stmt *s : bb->statements()) { - if (s->asPhi()) - continue; - - _lastPosition += 2; - _positionForStatement[s->id()] = _lastPosition; - } - - _basicBlockPosition[bb->index()].end = _lastPosition; - } -} - -LifeTimeIntervals::~LifeTimeIntervals() -{ - qDeleteAll(_intervals); -} - -Optimizer::Optimizer(IR::Function *function) - : function(function) - , inSSA(false) -{} - -void Optimizer::run(QQmlEnginePrivate *qmlEngine, bool doTypeInference, bool peelLoops) -{ - showMeTheCode(function, "Before running the optimizer"); - - cleanupBasicBlocks(function); - - function->removeSharedExpressions(); - int statementCount = 0; - for (BasicBlock *bb : function->basicBlocks()) - if (!bb->isRemoved()) - statementCount += bb->statementCount(); -// showMeTheCode(function); - - static bool doSSA = qEnvironmentVariableIsEmpty("QV4_NO_SSA"); - - if (!function->hasTry && !function->hasWith && !function->module->debugMode && doSSA && statementCount <= 300) { -// qout << "SSA for " << (function->name ? qPrintable(*function->name) : "<anonymous>") << endl; - - mergeBasicBlocks(function, nullptr, nullptr); - - ConvertArgLocals(function).toTemps(); - showMeTheCode(function, "After converting arguments to locals"); - - // Calculate the dominator tree: - DominatorTree df(function); - - { - // This is in a separate scope, because loop-peeling doesn't (yet) update the LoopInfo - // calculated by the LoopDetection. So by putting it in a separate scope, it is not - // available after peeling. - - LoopDetection loopDetection(df); - loopDetection.run(function); - showMeTheCode(function, "After loop detection"); -// cfg2dot(function, loopDetection.allLoops()); - - // ### disable loop peeling for now. It doesn't give any measurable performance - // improvements at this time, but significantly increases the size of the - // JIT generated code - Q_UNUSED(peelLoops); - if (0 && peelLoops) { - QVector<LoopDetection::LoopInfo *> innerLoops = loopDetection.innermostLoops(); - LoopPeeling(df).run(innerLoops); - -// cfg2dot(function, loopDetection.allLoops()); - showMeTheCode(function, "After loop peeling"); - if (!innerLoops.isEmpty()) - verifyImmediateDominators(df, function); - } - } - - verifyCFG(function); - verifyNoPointerSharing(function); - - df.computeDF(); - - verifyCFG(function); - verifyImmediateDominators(df, function); - - DefUses defUses(function); - -// qout << "Converting to SSA..." << endl; - convertToSSA(function, df, defUses); -// showMeTheCode(function); -// defUses.dump(); - -// qout << "Cleaning up phi nodes..." << endl; - cleanupPhis(defUses); - showMeTheCode(function, "After cleaning up phi-nodes"); - - StatementWorklist worklist(function); - - if (doTypeInference) { -// qout << "Running type inference..." << endl; - TypeInference(qmlEngine, defUses).run(worklist); - showMeTheCode(function, "After type inference"); - -// qout << "Doing reverse inference..." << endl; - ReverseInference(defUses).run(function); -// showMeTheCode(function); - -// qout << "Doing type propagation..." << endl; - TypePropagation(defUses).run(function, worklist); -// showMeTheCode(function); - verifyNoPointerSharing(function); - } - - static const bool doOpt = qEnvironmentVariableIsEmpty("QV4_NO_OPT"); - if (doOpt) { -// qout << "Running SSA optimization..." << endl; - worklist.reset(); - optimizeSSA(worklist, defUses, df); - showMeTheCode(function, "After optimization"); - - verifyImmediateDominators(df, function); - verifyCFG(function); - } - - verifyNoPointerSharing(function); - mergeBasicBlocks(function, &defUses, &df); - - verifyImmediateDominators(df, function); - verifyCFG(function); - - // Basic-block cycles that are unreachable (i.e. for loops in a then-part where the - // condition is calculated to be always false) are not yet removed. This will choke the - // block scheduling, so remove those now. -// qout << "Cleaning up unreachable basic blocks..." << endl; - cleanupBasicBlocks(function); -// showMeTheCode(function); - - verifyImmediateDominators(df, function); - verifyCFG(function); - - // Transform the CFG into edge-split SSA. - showMeTheCode(function, "Before edge splitting"); - splitCriticalEdges(function, df, worklist, defUses); - showMeTheCode(function, "After edge splitting"); - - verifyImmediateDominators(df, function); - verifyCFG(function); - -// qout << "Doing block scheduling..." << endl; -// df.dumpImmediateDominators(); - startEndLoops = BlockScheduler(function, df).go(); - showMeTheCode(function, "After basic block scheduling"); -// cfg2dot(function); - -#ifndef QT_NO_DEBUG - checkCriticalEdges(function->basicBlocks()); -#endif - - if (!function->module->debugMode) { - RemoveLineNumbers::run(function); - showMeTheCode(function, "After line number removal"); - } - -// qout << "Finished SSA." << endl; - inSSA = true; - } else { - removeUnreachleBlocks(function); - inSSA = false; - } -} - -void Optimizer::convertOutOfSSA() { - if (!inSSA) - return; - - // There should be no critical edges at this point. - - for (BasicBlock *bb : function->basicBlocks()) { - MoveMapping moves; - - for (BasicBlock *successor : bb->out) { - const int inIdx = successor->in.indexOf(bb); - Q_ASSERT(inIdx >= 0); - for (Stmt *s : successor->statements()) { - if (Phi *phi = s->asPhi()) { - moves.add(clone(phi->incoming[inIdx], function), - clone(phi->targetTemp, function)->asTemp()); - } else { - break; - } - } - } - - if (DebugMoveMapping) { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream os(&buf); - os << "Move mapping for function "; - if (function->name) - os << *function->name; - else - os << (void *) function; - os << " on basic-block L" << bb->index() << ":" << endl; - moves.dump(); - buf.close(); - qDebug("%s", buf.data().constData()); - } - - moves.order(); - - moves.insertMoves(bb, function, true); - } - - for (BasicBlock *bb : function->basicBlocks()) { - while (!bb->isEmpty()) { - if (bb->statements().first()->asPhi()) { - bb->removeStatement(0); - } else { - break; - } - } - } -} - -LifeTimeIntervals::Ptr Optimizer::lifeTimeIntervals() const -{ - Q_ASSERT(isInSSA()); - - LifeRanges lifeRanges(function, startEndLoops); -// lifeRanges.dump(); -// showMeTheCode(function); - return lifeRanges.intervals(); -} - -static int countPhis(BasicBlock *bb) -{ - int count = 0; - for (Stmt *s : bb->statements()) { - if (s->isa<Phi>()) - ++count; - else - break; - } - - return count; -} - -// Basic blocks can have only 1 terminator. This function returns a bit vector, where a 1 on a -// certain index indicates that the terminator (jump) at the end of the basic block with that index -// can be omitted. -BitVector Optimizer::calculateOptionalJumps() -{ - const int maxSize = function->basicBlockCount(); - BitVector optional(maxSize, false); - if (maxSize < 2) - return optional; - - BitVector reachableWithoutJump(maxSize, false); - - for (int i = maxSize - 1; i >= 0; --i) { - BasicBlock *bb = function->basicBlock(i); - if (bb->isRemoved()) - continue; - - if (Jump *jump = bb->statements().last()->asJump()) { - if (reachableWithoutJump.at(jump->target->index())) { - if (bb->statements().size() - countPhis(bb)> 1) - reachableWithoutJump.clear(); - optional.setBit(bb->index()); - reachableWithoutJump.setBit(bb->index()); - continue; - } - } - - reachableWithoutJump.clear(); - reachableWithoutJump.setBit(bb->index()); - } - - return optional; -} - -void Optimizer::showMeTheCode(IR::Function *function, const char *marker) -{ - ::showMeTheCode(function, marker); -} - -static inline bool overlappingStorage(const Temp &t1, const Temp &t2) -{ - // This is the same as the operator==, but for one detail: memory locations are not sensitive - // to types, and neither are general-purpose registers. - - if (t1.index != t2.index) - return false; // different position, where-ever that may (physically) be. - if (t1.kind != t2.kind) - return false; // formal/local/(physical-)register/stack do never overlap - if (t1.kind != Temp::PhysicalRegister) // Other than registers, ... - return t1.kind == t2.kind; // ... everything else overlaps: any memory location can hold everything. - - // So now the index is the same, and we know that both stored in a register. If both are - // floating-point registers, they are the same. Or, if both are non-floating-point registers, - // generally called general-purpose registers, they are also the same. - return (t1.type == DoubleType && t2.type == DoubleType) - || (t1.type != DoubleType && t2.type != DoubleType); -} - -MoveMapping::Moves MoveMapping::sourceUsages(Expr *e, const Moves &moves) -{ - Moves usages; - - if (Temp *t = e->asTemp()) { - for (int i = 0, ei = moves.size(); i != ei; ++i) { - const Move &move = moves[i]; - if (Temp *from = move.from->asTemp()) - if (overlappingStorage(*from, *t)) - usages.append(move); - } - } - - return usages; -} - -void MoveMapping::add(Expr *from, Temp *to) { - if (Temp *t = from->asTemp()) { - if (overlappingStorage(*t, *to)) { - // assignments like fp1 = fp1 or var{&1} = double{&1} can safely be skipped. - if (DebugMoveMapping) { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream os(&buf); - IRPrinter printer(&os); - os << "Skipping "; - printer.print(to); - os << " <- "; - printer.print(from); - buf.close(); - qDebug("%s", buf.data().constData()); - } - return; - } - } - - Move m(from, to); - if (_moves.contains(m)) - return; - _moves.append(m); -} - -// Order the moves that are generated when resolving edges during register allocation (see [Wimmer1] -// section 6 for details). Now these moves form one or more graphs, so we have to output them in -// such an order that values don't get overwritten: -// r1 <- r0 -// r2 <- r1 -// That input has to be ordered as follows in order to prevent the value in r1 from being lost: -// r2 <- r1 -// r1 <- r0 -// -// So, the algorithm is to output the leaves first, and take them out of the input. This will result -// in some moves to become leaves (in the above example: when leaf r2 <- r1 is generated and taken -// away, the r1 <- r0 is now a leaf), so we can output those and take those out, and repeat until -// there are no more leafs. -// -// The tricky part is that there might be cycles: -// r4 <- r5 -// r5 <- r4 -// These have to be turned into a "register swap": -// r4 <=> r5 -// -// So after running the above algorithm where we progressively remove the leaves, we are left with -// zero or more cycles. To resolve those, we break one of the edges of the cycle, and for all other -// edges we generate swaps. Note that the swaps will always occur as the last couple of moves, -// because otherwise they might clobber sources for moves: -// r4 <=> r5 -// r6 <- r5 -// Here, the value of r5 is already overwritten with the one in r4, so the correct order is: -// r6 <- r5 -// r4 <=> r5 -void MoveMapping::order() -{ - QList<Move> output; - output.reserve(_moves.size()); - - while (!_moves.isEmpty()) { - // Take out all leaf edges, because we can output them without any problems. - int nextLeaf = findLeaf(); - if (nextLeaf == -1) - break; // No more leafs left, we're done here. - output.append(_moves.takeAt(nextLeaf)); - // Now there might be new leaf edges: any move that had the input of the previously found - // leaf as an output, so loop around. - } - - while (!_moves.isEmpty()) { - // We're now left with one or more cycles. - // Step one: break the/a cycle. - _moves.removeFirst(); - // Step two: find the other edges of the cycle, starting with the one of that is now a leaf. - while (!_moves.isEmpty()) { - int nextLeaf = findLeaf(); - if (nextLeaf == -1) - break; // We're done with this cycle. - Move m = _moves.takeAt(nextLeaf); - // Step three: get the edges from the cycle and turn it into a swap - m.needsSwap = true; - output.append(m); - // Because we took out a leaf, find the next one. - } - // We're done with the cycle, let's see if there are more. - } - - _moves = output; -} - -int MoveMapping::findLeaf() const -{ - for (int i = 0, e = _moves.size(); i != e; ++i) { - // Take an edge from the list... - const Temp *target = _moves.at(i).to; - // ... and see if its target is used as a source... - bool targetUsedAsSource = false; - for (int j = 0; j != e; ++j) { - if (i == j) - continue; - - Expr *source = _moves.at(j).from; - if (const Temp *sourceTemp = source->asTemp()) { - if (overlappingStorage(*target, *sourceTemp)) { - targetUsedAsSource = true; - break; - } - } - } - // ... if not, we have a leaf edge ... - if (!targetUsedAsSource) - return i; - // .. otherwise we try the next one. - } - - return -1; // No leaf found -} - -QList<IR::Move *> MoveMapping::insertMoves(BasicBlock *bb, IR::Function *function, bool atEnd) const -{ - QList<IR::Move *> newMoves; - newMoves.reserve(_moves.size()); - - int insertionPoint = atEnd ? bb->statements().size() - 1 : 0; - for (const Move &m : _moves) { - IR::Move *move = function->NewStmt<IR::Move>(); - move->init(clone(m.to, function), clone(m.from, function)); - move->swap = m.needsSwap; - bb->insertStatementBefore(insertionPoint++, move); - newMoves.append(move); - } - - return newMoves; -} - -void MoveMapping::dump() const -{ - if (DebugMoveMapping) { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream os(&buf); - IRPrinter printer(&os); - os << "Move mapping has " << _moves.size() << " moves..." << endl; - for (const Move &m : _moves) { - os << "\t"; - printer.print(m.to); - if (m.needsSwap) - os << " <-> "; - else - os << " <-- "; - printer.print(m.from); - os << endl; - } - qDebug("%s", buf.data().constData()); - } -} - -// References: -// [Wimmer1] C. Wimmer and M. Franz. Linear Scan Register Allocation on SSA Form. In Proceedings of -// CGO'10, ACM Press, 2010 -// [Wimmer2] C. Wimmer and H. Mossenbock. Optimized Interval Splitting in a Linear Scan Register -// Allocator. In Proceedings of the ACM/USENIX International Conference on Virtual -// Execution Environments, pages 132-141. ACM Press, 2005. -// [Briggs] P. Briggs, K.D. Cooper, T.J. Harvey, and L.T. Simpson. Practical Improvements to the -// Construction and Destruction of Static Single Assignment Form. -// [Appel] A.W. Appel. Modern Compiler Implementation in Java. Second edition, Cambridge -// University Press. -// [Ramalingam] G. Ramalingam and T. Reps. An Incremental Algorithm for Maintaining the Dominator -// Tree of a Reducible Flowgraph. diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h deleted file mode 100644 index b9d8ae0a6c..0000000000 --- a/src/qml/compiler/qv4ssa_p.h +++ /dev/null @@ -1,472 +0,0 @@ -/**************************************************************************** -** -** 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 QV4SSA_P_H -#define QV4SSA_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 "qv4jsir_p.h" -#include "qv4isel_util_p.h" -#include <private/qv4util_p.h> -#include <QtCore/QSharedPointer> - -QT_BEGIN_NAMESPACE -class QTextStream; -class QQmlEnginePrivate; - -namespace QV4 { -namespace IR { - -struct LifeTimeIntervalRange { - int start; - int end; - - LifeTimeIntervalRange(int start = -1, int end = -1) - : start(start) - , end(end) - {} - - bool covers(int position) const { return start <= position && position <= end; } -}; -} // IR namespace -} // QV4 namespace - -Q_DECLARE_TYPEINFO(QV4::IR::LifeTimeIntervalRange, Q_PRIMITIVE_TYPE); - -namespace QV4 { -namespace IR { - -class Q_AUTOTEST_EXPORT LifeTimeInterval { -public: - typedef QVarLengthArray<LifeTimeIntervalRange, 4> Ranges; - -private: - Temp _temp; - Ranges _ranges; - int _end; - int _reg; - unsigned _isFixedInterval : 1; - unsigned _isSplitFromInterval : 1; - -public: - enum { InvalidPosition = -1 }; - enum { InvalidRegister = -1 }; - - explicit LifeTimeInterval(int rangeCapacity = 4) - : _end(InvalidPosition) - , _reg(InvalidRegister) - , _isFixedInterval(0) - , _isSplitFromInterval(0) - { _ranges.reserve(rangeCapacity); } - - bool isValid() const { return _end != InvalidRegister; } - - void setTemp(const Temp &temp) { this->_temp = temp; } - Temp temp() const { return _temp; } - bool isFP() const { return _temp.type == IR::DoubleType; } - - void setFrom(int from); - void addRange(int from, int to); - const Ranges &ranges() const { return _ranges; } - - int start() const { return _ranges.first().start; } - int end() const { return _end; } - bool covers(int position) const - { - for (int i = 0, ei = _ranges.size(); i != ei; ++i) { - if (_ranges.at(i).covers(position)) - return true; - } - return false; - } - - int reg() const { return _reg; } - void setReg(int reg) { Q_ASSERT(!_isFixedInterval); _reg = reg; } - - bool isFixedInterval() const { return _isFixedInterval; } - void setFixedInterval(bool isFixedInterval) { _isFixedInterval = isFixedInterval; } - - LifeTimeInterval split(int atPosition, int newStart); - bool isSplitFromInterval() const { return _isSplitFromInterval; } - void setSplitFromInterval(bool isSplitFromInterval) { _isSplitFromInterval = isSplitFromInterval; } - - void dump(QTextStream &out) const; - static bool lessThan(const LifeTimeInterval *r1, const LifeTimeInterval *r2); - static bool lessThanForTemp(const LifeTimeInterval *r1, const LifeTimeInterval *r2); - - void validate() const { -#if !defined(QT_NO_DEBUG) - // Validate the new range - if (_end != InvalidPosition) { - Q_ASSERT(!_ranges.isEmpty()); - for (const LifeTimeIntervalRange &range : qAsConst(_ranges)) { - Q_ASSERT(range.start >= 0); - Q_ASSERT(range.end >= 0); - Q_ASSERT(range.start <= range.end); - } - } -#endif - } -}; - -inline bool LifeTimeInterval::lessThan(const LifeTimeInterval *r1, const LifeTimeInterval *r2) -{ - if (r1->_ranges.first().start == r2->_ranges.first().start) { - if (r1->isSplitFromInterval() == r2->isSplitFromInterval()) - return r1->_ranges.last().end < r2->_ranges.last().end; - else - return r1->isSplitFromInterval(); - } else - return r1->_ranges.first().start < r2->_ranges.first().start; -} - -class LifeTimeIntervals -{ - Q_DISABLE_COPY(LifeTimeIntervals) - - LifeTimeIntervals(IR::Function *function); - void renumber(IR::Function *function); - -public: - typedef QSharedPointer<LifeTimeIntervals> Ptr; - static Ptr create(IR::Function *function) - { return Ptr(new LifeTimeIntervals(function)); } - - ~LifeTimeIntervals(); - - // takes ownership of the pointer - void add(LifeTimeInterval *interval) - { _intervals.append(interval); } - - // After calling Optimizer::lifeTimeIntervals() the result will have all intervals in descending order of start position. - QVector<LifeTimeInterval *> intervals() const - { return _intervals; } - - int size() const - { return _intervals.size(); } - - int positionForStatement(Stmt *stmt) const - { - Q_ASSERT(stmt->id() >= 0); - if (static_cast<unsigned>(stmt->id()) < _positionForStatement.size()) - return _positionForStatement[stmt->id()]; - - return Stmt::InvalidId; - } - - int startPosition(BasicBlock *bb) const - { - Q_ASSERT(bb->index() >= 0); - Q_ASSERT(static_cast<unsigned>(bb->index()) < _basicBlockPosition.size()); - - return _basicBlockPosition.at(bb->index()).start; - } - - int endPosition(BasicBlock *bb) const - { - Q_ASSERT(bb->index() >= 0); - Q_ASSERT(static_cast<unsigned>(bb->index()) < _basicBlockPosition.size()); - - return _basicBlockPosition.at(bb->index()).end; - } - - int lastPosition() const - { - return _lastPosition; - } - -private: - struct BasicBlockPositions { - int start; - int end; - - BasicBlockPositions() - : start(IR::Stmt::InvalidId) - , end(IR::Stmt::InvalidId) - {} - }; - - std::vector<BasicBlockPositions> _basicBlockPosition; - std::vector<int> _positionForStatement; - QVector<LifeTimeInterval *> _intervals; - int _lastPosition; -}; - -class Q_QML_PRIVATE_EXPORT Optimizer -{ - Q_DISABLE_COPY(Optimizer) - -public: - Optimizer(Function *function); - - void run(QQmlEnginePrivate *qmlEngine, bool doTypeInference = true, bool peelLoops = true); - void convertOutOfSSA(); - - bool isInSSA() const - { return inSSA; } - - QHash<BasicBlock *, BasicBlock *> loopStartEndBlocks() const { return startEndLoops; } - - LifeTimeIntervals::Ptr lifeTimeIntervals() const; - - BitVector calculateOptionalJumps(); - - static void showMeTheCode(Function *function, const char *marker); - -private: - Function *function; - bool inSSA; - QHash<BasicBlock *, BasicBlock *> startEndLoops; -}; - -class Q_QML_AUTOTEST_EXPORT MoveMapping -{ -#ifdef V4_AUTOTEST -public: -#endif - struct Move { - Expr *from; - Temp *to; - bool needsSwap; - - Move(Expr *from, Temp *to, bool needsSwap = false) - : from(from), to(to), needsSwap(needsSwap) - {} - - bool operator==(const Move &other) const - { return from == other.from && to == other.to; } - }; - typedef QList<Move> Moves; - - Moves _moves; - - static Moves sourceUsages(Expr *e, const Moves &moves); - -public: - void add(Expr *from, Temp *to); - void order(); - QList<IR::Move *> insertMoves(BasicBlock *bb, Function *function, bool atEnd) const; - - void dump() const; - -private: - int findLeaf() const; -}; - -/* - * stack slot allocation: - * - * foreach bb do - * foreach stmt do - * if the current statement is not a phi-node: - * purge ranges that end before the current statement - * check for life ranges to activate, and if they don't have a stackslot associated then allocate one - * renumber temps to stack - * for phi nodes: check if all temps (src+dst) are assigned stack slots and marked as allocated - * if it's a jump: - * foreach phi node in the successor: - * allocate slots for each temp (both sources and targets) if they don't have one allocated already - * insert moves before the jump - */ -class AllocateStackSlots: protected ConvertTemps -{ - IR::LifeTimeIntervals::Ptr _intervals; - QVector<IR::LifeTimeInterval *> _unhandled; - QVector<IR::LifeTimeInterval *> _live; - QBitArray _slotIsInUse; - IR::Function *_function; - - int defPosition(IR::Stmt *s) const - { - return usePosition(s) + 1; - } - - int usePosition(IR::Stmt *s) const - { - return _intervals->positionForStatement(s); - } - -public: - AllocateStackSlots(const IR::LifeTimeIntervals::Ptr &intervals) - : _intervals(intervals) - , _slotIsInUse(intervals->size(), false) - , _function(0) - { - _live.reserve(8); - _unhandled = _intervals->intervals(); - } - - void forFunction(IR::Function *function) - { - IR::Optimizer::showMeTheCode(function, "Before stack slot allocation"); - _function = function; - toStackSlots(function); - } - -protected: - int allocateFreeSlot() override - { - for (int i = 0, ei = _slotIsInUse.size(); i != ei; ++i) { - if (!_slotIsInUse[i]) { - if (_nextUnusedStackSlot <= i) { - Q_ASSERT(_nextUnusedStackSlot == i); - _nextUnusedStackSlot = i + 1; - } - _slotIsInUse[i] = true; - return i; - } - } - - Q_UNREACHABLE(); - return -1; - } - - void process(IR::Stmt *s) override - { -// qDebug("L%d statement %d:", _currentBasicBlock->index, s->id); - - if (IR::Phi *phi = s->asPhi()) { - visitPhi(phi); - } else { - // purge ranges no longer alive: - for (int i = 0; i < _live.size(); ) { - const IR::LifeTimeInterval *lti = _live.at(i); - if (lti->end() < usePosition(s)) { -// qDebug() << "\t - moving temp" << lti->temp().index << "to handled, freeing slot" << _stackSlotForTemp[lti->temp().index]; - _live.remove(i); - Q_ASSERT(_slotIsInUse[_stackSlotForTemp[lti->temp().index]]); - _slotIsInUse[_stackSlotForTemp[lti->temp().index]] = false; - continue; - } else { - ++i; - } - } - - // active new ranges: - while (!_unhandled.isEmpty()) { - IR::LifeTimeInterval *lti = _unhandled.last(); - if (lti->start() > defPosition(s)) - break; // we're done - Q_ASSERT(!_stackSlotForTemp.contains(lti->temp().index)); - _stackSlotForTemp[lti->temp().index] = allocateFreeSlot(); -// qDebug() << "\t - activating temp" << lti->temp().index << "on slot" << _stackSlotForTemp[lti->temp().index]; - _live.append(lti); - _unhandled.removeLast(); - } - - visit(s); - } - - if (IR::Jump *jump = s->asJump()) { - IR::MoveMapping moves; - for (IR::Stmt *succStmt : jump->target->statements()) { - if (IR::Phi *phi = succStmt->asPhi()) { - forceActivation(*phi->targetTemp); - for (int i = 0, ei = phi->incoming.size(); i != ei; ++i) { - IR::Expr *e = phi->incoming[i]; - if (IR::Temp *t = e->asTemp()) { - forceActivation(*t); - } - if (jump->target->in[i] == _currentBasicBlock) - moves.add(phi->incoming[i], phi->targetTemp); - } - } else { - break; - } - } - moves.order(); - const QList<IR::Move *> newMoves = moves.insertMoves(_currentBasicBlock, _function, true); - for (IR::Move *move : newMoves) - visit(move); - } - } - - void forceActivation(const IR::Temp &t) - { - if (_stackSlotForTemp.contains(t.index)) - return; - - int i = _unhandled.size() - 1; - for (; i >= 0; --i) { - IR::LifeTimeInterval *lti = _unhandled[i]; - if (lti->temp() == t) { - _live.append(lti); - _unhandled.remove(i); - break; - } - } - Q_ASSERT(i >= 0); // check that we always found the entry - - _stackSlotForTemp[t.index] = allocateFreeSlot(); -// qDebug() << "\t - force activating temp" << t.index << "on slot" << _stackSlotForTemp[t.index]; - } - - void visitPhi(IR::Phi *phi) override - { - Q_UNUSED(phi); -#if !defined(QT_NO_DEBUG) - Q_ASSERT(_stackSlotForTemp.contains(phi->targetTemp->index)); - Q_ASSERT(_slotIsInUse[_stackSlotForTemp[phi->targetTemp->index]]); - for (IR::Expr *e : phi->incoming) { - if (IR::Temp *t = e->asTemp()) - Q_ASSERT(_stackSlotForTemp.contains(t->index)); - } -#endif // defined(QT_NO_DEBUG) - } -}; - -} // IR namespace -} // QV4 namespace - - -Q_DECLARE_TYPEINFO(QV4::IR::LifeTimeInterval, Q_MOVABLE_TYPE); - -QT_END_NAMESPACE - -#endif // QV4SSA_P_H diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h index 9ffe6ff247..f17b1a7528 100644 --- a/src/qml/debugger/qqmlprofiler_p.h +++ b/src/qml/debugger/qqmlprofiler_p.h @@ -151,74 +151,120 @@ class Q_QML_PRIVATE_EXPORT QQmlProfiler : public QObject, public QQmlProfilerDef Q_OBJECT public: - class FunctionRefCount : public QQmlRefCount { - public: - FunctionRefCount(QV4::Function *function): - m_function(function) + struct Location { + Location(const QQmlSourceLocation &location = QQmlSourceLocation(), + const QUrl &url = QUrl()) : + location(location), url(url) {} + QQmlSourceLocation location; + QUrl url; + }; + + // Unfortunately we have to resolve the locations right away because the QML context might not + // be available anymore when we send the data. + struct RefLocation : public Location { + RefLocation() + : Location(), locationType(MaximumRangeType), sent(false) { - m_function->compilationUnit->addref(); + function = nullptr; } - FunctionRefCount(const FunctionRefCount &other) : - QQmlRefCount(other), m_function(other.m_function) + RefLocation(QV4::Function *ref) + : Location(ref->sourceLocation()), locationType(Binding), sent(false) { - m_function->compilationUnit->addref(); + function = ref; + function->compilationUnit->addref(); } - FunctionRefCount &operator=(const FunctionRefCount &other) + RefLocation(QV4::CompiledData::CompilationUnit *ref, const QUrl &url, const QV4::CompiledData::Object *obj, const QString &type) + : Location(QQmlSourceLocation(type, obj->location.line, obj->location.column), url), + locationType(Creating), sent(false) + { + unit = ref; + unit->addref(); + } + + RefLocation(QQmlBoundSignalExpression *ref) + : Location(ref->sourceLocation()), locationType(HandlingSignal), sent(false) + { + boundSignal = ref; + boundSignal->addref(); + } + + RefLocation(QQmlDataBlob *ref) + : Location(QQmlSourceLocation()), locationType(Compiling), sent(false) + { + blob = ref; + blob->addref(); + } + + RefLocation(const RefLocation &other) + : Location(other), + locationType(other.locationType), + function(other.function), + sent(other.sent) + { + addref(); + } + + RefLocation &operator=(const RefLocation &other) { if (this != &other) { - QQmlRefCount::operator=(other); - other.m_function->compilationUnit->addref(); - m_function->compilationUnit->release(); - m_function = other.m_function; + release(); + Location::operator=(other); + locationType = other.locationType; + function = other.function; + sent = other.sent; + addref(); } return *this; } - ~FunctionRefCount() + ~RefLocation() { - m_function->compilationUnit->release(); + release(); } - private: - QV4::Function *m_function; - }; - - struct Location { - Location(const QQmlSourceLocation &location = QQmlSourceLocation(), - const QUrl &url = QUrl()) : - location(location), url(url) {} - QQmlSourceLocation location; - QUrl url; - }; + void addref() + { + switch (locationType) { + case Binding: + function->compilationUnit->addref(); + break; + case Creating: + unit->addref(); + break; + case HandlingSignal: + boundSignal->addref(); + break; + case Compiling: + blob->addref(); + break; + default: + Q_ASSERT(locationType == MaximumRangeType); + break; + } + } - // Unfortunately we have to resolve the locations right away because the QML context might not - // be available anymore when we send the data. - struct RefLocation : public Location { - RefLocation() : Location(), locationType(MaximumRangeType), ref(nullptr), sent(false) - {} - - RefLocation(QV4::Function *function) : - Location(function->sourceLocation()), locationType(Binding), - ref(new FunctionRefCount(function), - QQmlRefPointer<QQmlRefCount>::Adopt), sent(false) - {} - - RefLocation(QV4::CompiledData::CompilationUnit *ref, const QUrl &url, const QV4::CompiledData::Object *obj, - const QString &type) : - Location(QQmlSourceLocation(type, obj->location.line, obj->location.column), url), - locationType(Creating), ref(ref), sent(false) - {} - - RefLocation(QQmlBoundSignalExpression *ref) : - Location(ref->sourceLocation()), locationType(HandlingSignal), ref(ref), sent(false) - {} - - RefLocation(QQmlDataBlob *ref) : - Location(QQmlSourceLocation(), ref->url()), locationType(Compiling), ref(ref), - sent(false) - {} + void release() + { + switch (locationType) { + case Binding: + function->compilationUnit->release(); + break; + case Creating: + unit->release(); + break; + case HandlingSignal: + boundSignal->release(); + break; + case Compiling: + blob->release(); + break; + default: + Q_ASSERT(locationType == MaximumRangeType); + break; + } + } bool isValid() const { @@ -226,7 +272,12 @@ public: } RangeType locationType; - QQmlRefPointer<QQmlRefCount> ref; + union { + QV4::Function *function; + QV4::CompiledData::CompilationUnit *unit; + QQmlBoundSignalExpression *boundSignal; + QQmlDataBlob *blob; + }; bool sent; }; diff --git a/src/qml/jit/jit.pri b/src/qml/jit/jit.pri index 7ea4e951d5..2080844d93 100644 --- a/src/qml/jit/jit.pri +++ b/src/qml/jit/jit.pri @@ -1,22 +1,10 @@ -include(../../3rdparty/masm/masm-defs.pri) - INCLUDEPATH += $$PWD INCLUDEPATH += $$OUT_PWD -HEADERS += \ - $$PWD/qv4assembler_p.h \ - $$PWD/qv4regalloc_p.h \ - $$PWD/qv4targetplatform_p.h \ - $$PWD/qv4isel_masm_p.h \ - $$PWD/qv4binop_p.h \ - $$PWD/qv4unop_p.h \ - $$PWD/qv4registerinfo_p.h - SOURCES += \ - $$PWD/qv4assembler.cpp \ - $$PWD/qv4regalloc.cpp \ - $$PWD/qv4isel_masm.cpp \ - $$PWD/qv4binop.cpp \ - $$PWD/qv4unop.cpp \ + $$PWD/qv4jit.cpp \ + $$PWD/qv4assembler.cpp -include(../../3rdparty/masm/masm.pri) +HEADERS += \ + $$PWD/qv4jit_p.h \ + $$PWD/qv4assembler_p.h diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp index d062f3bbb2..e357211074 100644 --- a/src/qml/jit/qv4assembler.cpp +++ b/src/qml/jit/qv4assembler.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** 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. @@ -37,510 +37,1105 @@ ** ****************************************************************************/ -#include "qv4isel_masm_p.h" -#include "qv4runtime_p.h" -#include "qv4ssa_p.h" -#include "qv4regalloc_p.h" +#include <QBuffer> + +#include "qv4engine_p.h" #include "qv4assembler_p.h" +#include <private/qv4function_p.h> +#include <private/qv4runtime_p.h> +#include <wtf/Vector.h> +#include <assembler/MacroAssembler.h> +#include <assembler/MacroAssemblerCodeRef.h> #include <assembler/LinkBuffer.h> #include <WTFStubs.h> -#if !defined(V4_BOOTSTRAP) -#include "qv4function_p.h" +#undef ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES + +#ifdef Q_STATIC_ASSERT_FOR_SANE_COMPILERS +# undef Q_STATIC_ASSERT_FOR_SANE_COMPILERS +#endif +#if defined(Q_CC_MSVC) && _MSC_VER < 1900 +# define Q_STATIC_ASSERT_FOR_SANE_COMPILERS(x) // insane +#else +# define Q_STATIC_ASSERT_FOR_SANE_COMPILERS(x) Q_STATIC_ASSERT(x) #endif -#include <iostream> -#include <QBuffer> -#include <QCoreApplication> +#ifdef V4_ENABLE_JIT -#if ENABLE(ASSEMBLER) +QT_BEGIN_NAMESPACE +namespace QV4 { +namespace JIT { -#if USE(UDIS86) -# include <udis86.h> -#endif +#define callHelper(x) PlatformAssemblerCommon::callRuntime(#x, reinterpret_cast<void *>(&x)) -using namespace QV4; -using namespace QV4::JIT; +const QV4::Value::ValueTypeInternal IntegerTag = QV4::Value::ValueTypeInternal::Integer; -CompilationUnit::~CompilationUnit() +static ReturnedValue toNumberHelper(ReturnedValue v) { + return Encode(Value::fromReturnedValue(v).toNumber()); } -#if !defined(V4_BOOTSTRAP) +static ReturnedValue toInt32Helper(ReturnedValue v) +{ + return Encode(Value::fromReturnedValue(v).toInt32()); +} -void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine) +#if defined(Q_PROCESSOR_X86_64) || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES) +#if defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_FREEBSD) || defined(Q_OS_DARWIN) + +struct PlatformAssembler_X86_64_SysV : JSC::MacroAssembler<JSC::MacroAssemblerX86_64> { - runtimeFunctions.resize(data->functionTableSize); - runtimeFunctions.fill(0); - for (int i = 0 ;i < runtimeFunctions.size(); ++i) { - const CompiledData::Function *compiledFunction = data->functionAt(i); + static const RegisterID NoRegister = RegisterID(-1); + + static const RegisterID ReturnValueRegister = RegisterID::eax; + static const RegisterID AccumulatorRegister = RegisterID::eax; + static const RegisterID AccumulatorRegisterValue = AccumulatorRegister; + static const RegisterID ScratchRegister = RegisterID::r10; + static const RegisterID ScratchRegister2 = RegisterID::r9; // Note: overlaps with Arg5Reg, so do not use while setting up a call! + static const RegisterID JSStackFrameRegister = RegisterID::r12; + static const RegisterID CppStackFrameRegister = RegisterID::r13; + static const RegisterID EngineRegister = RegisterID::r14; + static const RegisterID StackPointerRegister = RegisterID::esp; + static const RegisterID FramePointerRegister = RegisterID::ebp; + static const FPRegisterID FPScratchRegister = FPRegisterID::xmm1; + + static const RegisterID Arg0Reg = RegisterID::edi; + static const RegisterID Arg1Reg = RegisterID::esi; + static const RegisterID Arg2Reg = RegisterID::edx; + static const RegisterID Arg3Reg = RegisterID::ecx; + static const RegisterID Arg4Reg = RegisterID::r8; + static const RegisterID Arg5Reg = RegisterID::r9; + static const RegisterID Arg6Reg = NoRegister; + static const RegisterID Arg7Reg = NoRegister; + static const int ArgInRegCount = 6; + + void popValue() + { + addPtr(TrustedImmPtr(sizeof(ReturnedValue)), StackPointerRegister); + } - QV4::Function *runtimeFunction = new QV4::Function(engine, this, compiledFunction, - (ReturnedValue (*)(QV4::ExecutionEngine *, const uchar *)) codeRefs[i].code().executableAddress()); - runtimeFunctions[i] = runtimeFunction; + void generatePlatformFunctionEntry() + { + push(RegisterID::ebp); + move(RegisterID::esp, RegisterID::ebp); + move(TrustedImmPtr(nullptr), AccumulatorRegister); push(AccumulatorRegister); // exceptionHandler + push(JSStackFrameRegister); + push(CppStackFrameRegister); + push(EngineRegister); + move(Arg0Reg, CppStackFrameRegister); + move(Arg1Reg, EngineRegister); + loadPtr(Address(CppStackFrameRegister, offsetof(CppStackFrame, jsFrame)), JSStackFrameRegister); } -} -bool CompilationUnit::memoryMapCode(QString *errorString) -{ - Q_UNUSED(errorString); - codeRefs.resize(data->functionTableSize); + void generatePlatformFunctionExit() + { + pop(EngineRegister); + pop(CppStackFrameRegister); + pop(JSStackFrameRegister); + pop(); // exceptionHandler + pop(RegisterID::ebp); + ret(); + } - const char *basePtr = reinterpret_cast<const char *>(data); + void callAbsolute(const void *funcPtr) + { + move(TrustedImmPtr(funcPtr), ScratchRegister); + call(ScratchRegister); + } - for (uint i = 0; i < data->functionTableSize; ++i) { - const CompiledData::Function *compiledFunction = data->functionAt(i); - void *codePtr = const_cast<void *>(reinterpret_cast<const void *>(basePtr + compiledFunction->codeOffset)); - JSC::MacroAssemblerCodeRef codeRef = JSC::MacroAssemblerCodeRef::createSelfManagedCodeRef(JSC::MacroAssemblerCodePtr(codePtr)); - JSC::ExecutableAllocator::makeExecutable(codePtr, compiledFunction->codeSize); - codeRefs[i] = codeRef; + void pushAligned(RegisterID reg) + { + subPtr(TrustedImm32(PointerSize), StackPointerRegister); + push(reg); + } - static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_ASM"); - if (showCode) { - WTF::dataLogF("Mapped JIT code for %s\n", qPrintable(stringAt(compiledFunction->nameIndex))); - disassemble(codeRef.code(), compiledFunction->codeSize, " ", WTF::dataFile()); - } + void popAligned(RegisterID reg) + { + pop(reg); + addPtr(TrustedImm32(PointerSize), StackPointerRegister); } +}; - return true; -} +typedef PlatformAssembler_X86_64_SysV PlatformAssemblerBase; -#endif // !defined(V4_BOOTSTRAP) +#endif +#if defined(Q_OS_WIN) -void CompilationUnit::prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) +struct PlatformAssembler_Win64 : JSC::MacroAssembler<JSC::MacroAssemblerX86_64> { - const int codeAlignment = 16; - quint64 offset = WTF::roundUpToMultipleOf(codeAlignment, unit->unitSize); - Q_ASSERT(int(unit->functionTableSize) == codeRefs.size()); - for (int i = 0; i < codeRefs.size(); ++i) { - CompiledData::Function *compiledFunction = const_cast<CompiledData::Function *>(unit->functionAt(i)); - compiledFunction->codeOffset = offset; - compiledFunction->codeSize = codeRefs.at(i).size(); - offset = WTF::roundUpToMultipleOf(codeAlignment, offset + compiledFunction->codeSize); + static const RegisterID NoRegister = RegisterID(-1); + + static const RegisterID ReturnValueRegister = RegisterID::eax; + static const RegisterID AccumulatorRegister = RegisterID::eax; + static const RegisterID AccumulatorRegisterValue = AccumulatorRegister; + static const RegisterID ScratchRegister = RegisterID::r10; + static const RegisterID ScratchRegister2 = RegisterID::r9; // Note: overlaps with Arg3Reg, so do not use while setting up a call! + static const RegisterID JSStackFrameRegister = RegisterID::r12; + static const RegisterID CppStackFrameRegister = RegisterID::r13; + static const RegisterID EngineRegister = RegisterID::r14; + static const RegisterID StackPointerRegister = RegisterID::esp; + static const RegisterID FramePointerRegister = RegisterID::ebp; + static const FPRegisterID FPScratchRegister = FPRegisterID::xmm1; + + static const RegisterID Arg0Reg = RegisterID::ecx; + static const RegisterID Arg1Reg = RegisterID::edx; + static const RegisterID Arg2Reg = RegisterID::r8; + static const RegisterID Arg3Reg = RegisterID::r9; + static const RegisterID Arg4Reg = NoRegister; + static const RegisterID Arg5Reg = NoRegister; + static const RegisterID Arg6Reg = NoRegister; + static const RegisterID Arg7Reg = NoRegister; + static const int ArgInRegCount = 4; + + void popValue() + { + addPtr(TrustedImmPtr(sizeof(ReturnedValue)), StackPointerRegister); } -} -bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) + void generatePlatformFunctionEntry() + { + push(RegisterID::ebp); + move(RegisterID::esp, RegisterID::ebp); + move(TrustedImmPtr(nullptr), AccumulatorRegister); push(AccumulatorRegister); // exceptionHandler + push(JSStackFrameRegister); + push(CppStackFrameRegister); + push(EngineRegister); + move(Arg0Reg, CppStackFrameRegister); + move(Arg1Reg, EngineRegister); + loadPtr(Address(CppStackFrameRegister, offsetof(CppStackFrame, jsFrame)), JSStackFrameRegister); + } + + void generatePlatformFunctionExit() + { + pop(EngineRegister); + pop(CppStackFrameRegister); + pop(JSStackFrameRegister); + pop(); // exceptionHandler + pop(RegisterID::ebp); + ret(); + } + + void callAbsolute(const void *funcPtr) + { + move(TrustedImmPtr(funcPtr), ScratchRegister); + subPtr(TrustedImm32(4 * PointerSize), StackPointerRegister); + call(ScratchRegister); + addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister); + } + + void pushAligned(RegisterID reg) + { + subPtr(TrustedImm32(PointerSize), StackPointerRegister); + push(reg); + } + + void popAligned(RegisterID reg) + { + pop(reg); + addPtr(TrustedImm32(PointerSize), StackPointerRegister); + } +}; + +typedef PlatformAssembler_Win64 PlatformAssemblerBase; + +#endif +#endif + +#if (defined(Q_PROCESSOR_X86) && !defined(Q_PROCESSOR_X86_64)) || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES) + +struct PlatformAssembler_X86_All : JSC::MacroAssembler<JSC::MacroAssemblerX86> { - Q_ASSERT(device->pos() == unit->unitSize); - Q_ASSERT(device->atEnd()); - Q_ASSERT(int(unit->functionTableSize) == codeRefs.size()); + static const RegisterID NoRegister = RegisterID(-1); + + static const RegisterID ReturnValueRegisterValue = RegisterID::eax; + static const RegisterID ReturnValueRegisterTag = RegisterID::edx; + static const RegisterID ScratchRegister = RegisterID::ecx; + static const RegisterID AccumulatorRegisterValue = ReturnValueRegisterValue; + static const RegisterID AccumulatorRegisterTag = ReturnValueRegisterTag; + static const RegisterID JSStackFrameRegister = RegisterID::ebx; + static const RegisterID CppStackFrameRegister = RegisterID::esi; + static const RegisterID EngineRegister = RegisterID::edi; + static const RegisterID StackPointerRegister = RegisterID::esp; + static const RegisterID FramePointerRegister = RegisterID::ebp; + static const FPRegisterID FPScratchRegister = FPRegisterID::xmm1; + + static const RegisterID Arg0Reg = NoRegister; + static const RegisterID Arg1Reg = NoRegister; + static const RegisterID Arg2Reg = NoRegister; + static const RegisterID Arg3Reg = NoRegister; + static const RegisterID Arg4Reg = NoRegister; + static const RegisterID Arg5Reg = NoRegister; + static const RegisterID Arg6Reg = NoRegister; + static const RegisterID Arg7Reg = NoRegister; + static const int ArgInRegCount = 0; + + void popValue() + { + addPtr(TrustedImmPtr(sizeof(ReturnedValue)), StackPointerRegister); + } - QByteArray padding; + void generatePlatformFunctionEntry() + { + push(RegisterID::ebp); + move(RegisterID::esp, RegisterID::ebp); + move(TrustedImmPtr(nullptr), AccumulatorRegisterValue); push(AccumulatorRegisterValue); // exceptionHandler + push(JSStackFrameRegister); + push(CppStackFrameRegister); + push(EngineRegister); + loadPtr(Address(FramePointerRegister, 2 * PointerSize), CppStackFrameRegister); + loadPtr(Address(FramePointerRegister, 3 * PointerSize), EngineRegister); + loadPtr(Address(CppStackFrameRegister, offsetof(CppStackFrame, jsFrame)), JSStackFrameRegister); + } - for (int i = 0; i < codeRefs.size(); ++i) { - const CompiledData::Function *compiledFunction = unit->functionAt(i); + void generatePlatformFunctionExit() + { + pop(EngineRegister); + pop(CppStackFrameRegister); + pop(JSStackFrameRegister); + pop(); // exceptionHandler + pop(RegisterID::ebp); + ret(); + } - if (device->pos() > qint64(compiledFunction->codeOffset)) { - *errorString = QStringLiteral("Invalid state of cache file to write."); - return false; - } + void callAbsolute(const void *funcPtr) + { + move(TrustedImmPtr(funcPtr), ScratchRegister); + call(ScratchRegister); + } - const quint64 paddingSize = compiledFunction->codeOffset - device->pos(); - padding.fill(0, paddingSize); - qint64 written = device->write(padding); - if (written != padding.size()) { - *errorString = device->errorString(); - return false; - } + void pushAligned(RegisterID reg) + { + subPtr(TrustedImm32(PointerSize), StackPointerRegister); + push(reg); + } - const void *undecoratedCodePtr = codeRefs.at(i).code().dataLocation(); - written = device->write(reinterpret_cast<const char *>(undecoratedCodePtr), compiledFunction->codeSize); - if (written != qint64(compiledFunction->codeSize)) { - *errorString = device->errorString(); - return false; - } + void popAligned(RegisterID reg) + { + pop(reg); + addPtr(TrustedImm32(PointerSize), StackPointerRegister); } - return true; -} +}; -template <typename TargetConfiguration> -Assembler<TargetConfiguration>::Assembler(QV4::Compiler::JSUnitGenerator *jsGenerator, IR::Function* function, QV4::ExecutableAllocator *executableAllocator) - : _function(function) - , _nextBlock(0) - , _executableAllocator(executableAllocator) - , _jsGenerator(jsGenerator) -{ - _addrs.resize(_function->basicBlockCount()); - _patches.resize(_function->basicBlockCount()); - _labelPatches.resize(_function->basicBlockCount()); -} +typedef PlatformAssembler_X86_All PlatformAssemblerBase; -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::registerBlock(IR::BasicBlock* block, IR::BasicBlock *nextBlock) -{ - _addrs[block->index()] = label(); - catchBlock = block->catchBlock; - _nextBlock = nextBlock; -} +#endif + +#if defined(Q_PROCESSOR_ARM_64) || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES) -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target) +struct PlatformAssembler_ARM64 : JSC::MacroAssembler<JSC::MacroAssemblerARM64> { - Q_UNUSED(current); + static const RegisterID NoRegister = RegisterID(-1); + + static const RegisterID ReturnValueRegister = JSC::ARM64Registers::x0; + static const RegisterID AccumulatorRegister = JSC::ARM64Registers::x9; + static const RegisterID AccumulatorRegisterValue = AccumulatorRegister; + static const RegisterID ScratchRegister = JSC::ARM64Registers::x10; + static const RegisterID ScratchRegister2 = JSC::ARM64Registers::x7; // Note: overlaps with Arg7Reg, so do not use while setting up a call! + static const RegisterID JSStackFrameRegister = JSC::ARM64Registers::x19; + static const RegisterID CppStackFrameRegister = JSC::ARM64Registers::x20; + static const RegisterID EngineRegister = JSC::ARM64Registers::x21; + static const RegisterID StackPointerRegister = JSC::ARM64Registers::sp; + static const RegisterID FramePointerRegister = JSC::ARM64Registers::fp; + static const FPRegisterID FPScratchRegister = JSC::ARM64Registers::q1; + + static const RegisterID Arg0Reg = JSC::ARM64Registers::x0; + static const RegisterID Arg1Reg = JSC::ARM64Registers::x1; + static const RegisterID Arg2Reg = JSC::ARM64Registers::x2; + static const RegisterID Arg3Reg = JSC::ARM64Registers::x3; + static const RegisterID Arg4Reg = JSC::ARM64Registers::x4; + static const RegisterID Arg5Reg = JSC::ARM64Registers::x5; + static const RegisterID Arg6Reg = JSC::ARM64Registers::x6; + static const RegisterID Arg7Reg = JSC::ARM64Registers::x7; + static const int ArgInRegCount = 8; + + void push(RegisterID src) + { + pushToSave(src); + } - if (target != _nextBlock) - _patches[target->index()].push_back(jump()); -} + void pop(RegisterID dest) + { + popToRestore(dest); + } -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::addPatch(IR::BasicBlock* targetBlock, Jump targetJump) -{ - _patches[targetBlock->index()].push_back(targetJump); -} + void pop() + { + add64(TrustedImm32(16), stackPointerRegister); + } -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::addPatch(DataLabelPtr patch, Label target) -{ - DataLabelPatch p; - p.dataLabel = patch; - p.target = target; - _dataLabelPatches.push_back(p); -} + void popValue() + { + pop(); + } -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::addPatch(DataLabelPtr patch, IR::BasicBlock *target) -{ - _labelPatches[target->index()].push_back(patch); -} + void generatePlatformFunctionEntry() + { + pushPair(JSC::ARM64Registers::fp, JSC::ARM64Registers::lr); + move(RegisterID::sp, RegisterID::fp); + move(TrustedImmPtr(nullptr), AccumulatorRegister); // exceptionHandler + pushPair(JSStackFrameRegister, AccumulatorRegister); + pushPair(EngineRegister, CppStackFrameRegister); + move(Arg0Reg, CppStackFrameRegister); + move(Arg1Reg, EngineRegister); + } -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBlock, - IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) -{ - generateCJumpOnCompare(RelationalCondition::NotEqual, reg, TrustedImm32(0), currentBlock, trueBlock, falseBlock); -} + void generatePlatformFunctionExit() + { + move(AccumulatorRegister, ReturnValueRegister); + popPair(EngineRegister, CppStackFrameRegister); + popPair(JSStackFrameRegister, AccumulatorRegister); + popPair(JSC::ARM64Registers::fp, JSC::ARM64Registers::lr); + ret(); + } -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::generateCJumpOnCompare(RelationalCondition cond, - RegisterID left, - TrustedImm32 right, - IR::BasicBlock *currentBlock, - IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) -{ - if (trueBlock == _nextBlock) { - Jump target = branch32(invert(cond), left, right); - addPatch(falseBlock, target); - } else { - Jump target = branch32(cond, left, right); - addPatch(trueBlock, target); - jumpToBlock(currentBlock, falseBlock); + void callAbsolute(const void *funcPtr) + { + move(TrustedImmPtr(funcPtr), ScratchRegister); + call(ScratchRegister); } -} -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::generateCJumpOnCompare(RelationalCondition cond, - RegisterID left, - RegisterID right, - IR::BasicBlock *currentBlock, - IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) -{ - if (trueBlock == _nextBlock) { - Jump target = branch32(invert(cond), left, right); - addPatch(falseBlock, target); - } else { - Jump target = branch32(cond, left, right); - addPatch(trueBlock, target); - jumpToBlock(currentBlock, falseBlock); + void pushAligned(RegisterID reg) + { + pushToSave(reg); } -} -template <typename TargetConfiguration> -typename Assembler<TargetConfiguration>::Pointer -Assembler<TargetConfiguration>::loadAddressForWriting(RegisterID tmp, IR::Expr *e, WriteBarrier::Type *barrier) -{ - if (barrier) - *barrier = WriteBarrier::NoBarrier; - IR::Temp *t = e->asTemp(); - if (t) - return loadTempAddress(t); - else - return loadArgLocalAddressForWriting(tmp, e->asArgLocal(), barrier); -} + void popAligned(RegisterID reg) + { + popToRestore(reg); + } +}; -template <typename TargetConfiguration> -typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>::loadTempAddress(IR::Temp *t) +typedef PlatformAssembler_ARM64 PlatformAssemblerBase; + +#endif + +#if defined(Q_PROCESSOR_ARM_32) || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES) + +struct PlatformAssembler_ARM32 : JSC::MacroAssembler<JSC::MacroAssemblerARMv7> { - if (t->kind == IR::Temp::StackSlot) - return stackSlotPointer(t); - else - Q_UNREACHABLE(); -} + static const RegisterID NoRegister = RegisterID(-1); + + static const RegisterID ReturnValueRegisterValue = JSC::ARMRegisters::r0; + static const RegisterID ReturnValueRegisterTag = JSC::ARMRegisters::r1; + static const RegisterID ScratchRegister = JSC::ARMRegisters::r2; + static const RegisterID AccumulatorRegisterValue = JSC::ARMRegisters::r4; + static const RegisterID AccumulatorRegisterTag = JSC::ARMRegisters::r5; + // r6 is used by MacroAssemblerARMv7 + static const RegisterID JSStackFrameRegister = JSC::ARMRegisters::r8; + static const RegisterID CppStackFrameRegister = JSC::ARMRegisters::r10; +#if CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP) + static const RegisterID FramePointerRegister = JSC::ARMRegisters::r7; + static const RegisterID EngineRegister = JSC::ARMRegisters::r11; +#else // Thumbs down + static const RegisterID FramePointerRegister = JSC::ARMRegisters::r11; + static const RegisterID EngineRegister = JSC::ARMRegisters::r7; +#endif + static const RegisterID StackPointerRegister = JSC::ARMRegisters::r13; + static const FPRegisterID FPScratchRegister = JSC::ARMRegisters::d1; + + static const RegisterID Arg0Reg = JSC::ARMRegisters::r0; + static const RegisterID Arg1Reg = JSC::ARMRegisters::r1; + static const RegisterID Arg2Reg = JSC::ARMRegisters::r2; + static const RegisterID Arg3Reg = JSC::ARMRegisters::r3; + static const RegisterID Arg4Reg = NoRegister; + static const RegisterID Arg5Reg = NoRegister; + static const RegisterID Arg6Reg = NoRegister; + static const RegisterID Arg7Reg = NoRegister; + static const int ArgInRegCount = 4; + + void popValue() + { + addPtr(TrustedImm32(sizeof(ReturnedValue)), StackPointerRegister); + } -template <typename TargetConfiguration> -typename Assembler<TargetConfiguration>::Pointer -Assembler<TargetConfiguration>::loadArgLocalAddressForWriting(RegisterID baseReg, IR::ArgLocal *al, WriteBarrier::Type *barrier) + void generatePlatformFunctionEntry() + { + push(JSC::ARMRegisters::lr); + push(FramePointerRegister); + move(StackPointerRegister, FramePointerRegister); + push(TrustedImm32(0)); // exceptionHandler + push(AccumulatorRegisterValue); + push(AccumulatorRegisterTag); + push(addressTempRegister); + push(JSStackFrameRegister); + push(CppStackFrameRegister); + push(EngineRegister); + subPtr(TrustedImm32(4), StackPointerRegister); // stack alignment + move(Arg0Reg, CppStackFrameRegister); + move(Arg1Reg, EngineRegister); + } + + void generatePlatformFunctionExit() + { + move(AccumulatorRegisterValue, ReturnValueRegisterValue); + move(AccumulatorRegisterTag, ReturnValueRegisterTag); + addPtr(TrustedImm32(4), StackPointerRegister); // stack alignment + pop(EngineRegister); + pop(CppStackFrameRegister); + pop(JSStackFrameRegister); + pop(addressTempRegister); + pop(AccumulatorRegisterTag); + pop(AccumulatorRegisterValue); + pop(); // exceptionHandler + pop(FramePointerRegister); + pop(JSC::ARMRegisters::lr); + ret(); + } + + void callAbsolute(const void *funcPtr) + { + move(TrustedImmPtr(funcPtr), dataTempRegister); + call(dataTempRegister); + } + + void pushAligned(RegisterID reg) + { + subPtr(TrustedImm32(PointerSize), StackPointerRegister); + push(reg); + } + + void popAligned(RegisterID reg) + { + pop(reg); + addPtr(TrustedImm32(PointerSize), StackPointerRegister); + } +}; + +typedef PlatformAssembler_ARM32 PlatformAssemblerBase; + +#endif + +struct PlatformAssemblerCommon : PlatformAssemblerBase { - if (barrier) - *barrier = _function->argLocalRequiresWriteBarrier(al) ? WriteBarrier::Barrier : WriteBarrier::NoBarrier; + const Value* constantTable; + struct JumpTarget { JSC::MacroAssemblerBase::Jump jump; int offset; }; + std::vector<JumpTarget> patches; + struct ExceptionHanlderTarget { JSC::MacroAssemblerBase::DataLabelPtr label; int offset; }; + std::vector<ExceptionHanlderTarget> ehTargets; + QHash<int, JSC::MacroAssemblerBase::Label> labelsByOffset; + QHash<const void *, const char *> functions; + std::vector<Jump> catchyJumps; + Label functionExit; + + Address exceptionHandlerAddress() const + { + return Address(FramePointerRegister, -1 * PointerSize); + } - int32_t offset = 0; - int scope = al->scope; - loadPtr(Address(EngineRegister, targetStructureOffset(offsetof(EngineBase, current))), baseReg); + Address contextAddress() const + { + return Address(JSStackFrameRegister, offsetof(CallData, context)); + } - const qint32 outerOffset = targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, outer)); - const qint32 localsOffset = targetStructureOffset(Heap::CallContextData::baseOffset + offsetof(Heap::CallContextData, function)) - + 8 // locals is always 8 bytes away from function, regardless of pointer size. - + offsetof(ValueArray<0>, values); + RegisterID registerForArg(int arg) const + { + Q_ASSERT(arg >= 0); + Q_ASSERT(arg < ArgInRegCount); + switch (arg) { + case 0: return Arg0Reg; + case 1: return Arg1Reg; + case 2: return Arg2Reg; + case 3: return Arg3Reg; + case 4: return Arg4Reg; + case 5: return Arg5Reg; + case 6: return Arg6Reg; + case 7: return Arg7Reg; + default: + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); + } + } - while (scope) { - loadPtr(Address(baseReg, outerOffset), baseReg); - --scope; + void callRuntime(const char *functionName, const void *funcPtr) + { + functions.insert(funcPtr, functionName); + callAbsolute(funcPtr); } - switch (al->kind) { - case IR::ArgLocal::Formal: - case IR::ArgLocal::ScopedFormal: { - if (barrier && *barrier == WriteBarrier::Barrier) { - // if we need a barrier, the baseReg has to point to the ExecutionContext - // callData comes directly after locals, calculate the offset using that - offset = localsOffset + _function->localsCountForScope(al) * sizeof(Value); - offset += sizeof(CallData) + (al->index - 1) * sizeof(Value); - } else { - const qint32 callDataOffset = targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, callData)); - loadPtr(Address(baseReg, callDataOffset), baseReg); - offset = sizeof(CallData) + (al->index - 1) * sizeof(Value); + + Address loadFunctionPtr(RegisterID target) + { + Address addr(CppStackFrameRegister, offsetof(CppStackFrame, v4Function)); + loadPtr(addr, target); + return Address(target); + } + + Address loadCompilationUnitPtr(RegisterID target) + { + Address addr = loadFunctionPtr(target); + addr.offset = offsetof(QV4::Function, compilationUnit); + loadPtr(addr, target); + return Address(target); + } + + Address loadConstAddress(int constIndex, RegisterID baseReg = ScratchRegister) + { + Address addr = loadCompilationUnitPtr(baseReg); + addr.offset = offsetof(QV4::CompiledData::CompilationUnitBase, constants); + loadPtr(addr, baseReg); + addr.offset = constIndex * int(sizeof(QV4::Value)); + return addr; + } + + Address loadStringAddress(int stringId) + { + Address addr = loadCompilationUnitPtr(ScratchRegister); + addr.offset = offsetof(QV4::CompiledData::CompilationUnitBase, runtimeStrings); + loadPtr(addr, ScratchRegister); + return Address(ScratchRegister, stringId * PointerSize); + } + + void passAsArg(RegisterID src, int arg) + { + move(src, registerForArg(arg)); + } + + void generateCatchTrampoline(std::function<void()> loadUndefined) + { + for (Jump j : catchyJumps) + j.link(this); + + loadPtr(exceptionHandlerAddress(), ScratchRegister); + Jump exitFunction = branchPtr(Equal, ScratchRegister, TrustedImmPtr(0)); + jump(ScratchRegister); + exitFunction.link(this); + loadUndefined(); + + if (functionExit.isSet()) + jump(functionExit); + else + generateFunctionExit(); + } + + void addCatchyJump(Jump j) + { + Q_ASSERT(j.isSet()); + catchyJumps.push_back(j); + } + + void generateFunctionEntry() + { + generatePlatformFunctionEntry(); + loadPtr(Address(CppStackFrameRegister, offsetof(CppStackFrame, jsFrame)), JSStackFrameRegister); + } + + void generateFunctionExit() + { + if (functionExit.isSet()) { + jump(functionExit); + return; } - } break; - case IR::ArgLocal::Local: - case IR::ArgLocal::ScopedLocal: { - offset = localsOffset + al->index * sizeof(Value); - } break; - default: - Q_UNREACHABLE(); + + functionExit = label(); + generatePlatformFunctionExit(); } - return Pointer(baseReg, offset); -} +}; -template <typename TargetConfiguration> -typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>::loadStringAddress(RegisterID reg, const QString &string) +#if QT_POINTER_SIZE == 8 || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES) +struct PlatformAssembler64 : PlatformAssemblerCommon { - loadPtr(Address(Assembler::EngineRegister, targetStructureOffset(offsetof(QV4::EngineBase, current))), Assembler::ScratchRegister); - loadPtr(Address(Assembler::ScratchRegister, targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, compilationUnit))), Assembler::ScratchRegister); - loadPtr(Address(Assembler::ScratchRegister, offsetof(CompiledData::CompilationUnitBase, runtimeStrings)), reg); - const int id = _jsGenerator->registerString(string); - return Pointer(reg, id * RegisterSize); -} + void callRuntime(const char *functionName, const void *funcPtr, + Assembler::CallResultDestination dest) + { + PlatformAssemblerCommon::callRuntime(functionName, funcPtr); + if (dest == Assembler::ResultInAccumulator) + move(ReturnValueRegister, AccumulatorRegister); + } -template <typename TargetConfiguration> -typename Assembler<TargetConfiguration>::Address Assembler<TargetConfiguration>::loadConstant(IR::Const *c, RegisterID baseReg) -{ - return loadConstant(convertToValue<TargetPrimitive>(c), baseReg); -} + void loadUndefined(RegisterID dest = AccumulatorRegister) + { + move(TrustedImm64(0), dest); + } -template <typename TargetConfiguration> -typename Assembler<TargetConfiguration>::Address Assembler<TargetConfiguration>::loadConstant(const TargetPrimitive &v, RegisterID baseReg) -{ - loadPtr(Address(Assembler::EngineRegister, targetStructureOffset(offsetof(QV4::EngineBase, current))), baseReg); - loadPtr(Address(baseReg, targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, constantTable))), baseReg); - const int index = _jsGenerator->registerConstant(v.rawValue()); - return Address(baseReg, index * sizeof(QV4::Value)); -} + void copyConst(int constIndex, Address dest) + { + //### + if (constantTable[constIndex].isUndefined()) { + loadUndefined(ScratchRegister); + } else { + load64(loadConstAddress(constIndex, ScratchRegister), ScratchRegister); + } + store64(ScratchRegister, dest); + } -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::loadStringRef(RegisterID reg, const QString &string) -{ - const int id = _jsGenerator->registerString(string); - move(TrustedImm32(id), reg); -} + void copyReg(Address src, Address dst) + { + loadReg(src, ScratchRegister); + store64(ScratchRegister, dst); + } -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::storeValue(TargetPrimitive value, IR::Expr *destination) -{ - WriteBarrier::Type barrier; - Address addr = loadAddressForWriting(ScratchRegister, destination, &barrier); - storeValue(value, addr, barrier); -} + void loadReg(Address addr, RegisterID dest = AccumulatorRegister) + { + load64(addr, dest); + } -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::enterStandardStackFrame(const RegisterInformation ®ularRegistersToSave, - const RegisterInformation &fpRegistersToSave) -{ - platformEnterStandardStackFrame(this); + void loadAccumulator(Address addr) + { + load64(addr, AccumulatorRegister); + } - move(StackPointerRegister, JITTargetPlatform::FramePointerRegister); + void storeAccumulator(Address addr) + { + store64(AccumulatorRegister, addr); + } - const int frameSize = _stackLayout->calculateStackFrameSize(); - subPtr(TrustedImm32(frameSize), StackPointerRegister); + void loadString(int stringId) + { + loadAccumulator(loadStringAddress(stringId)); + } - Address slotAddr(JITTargetPlatform::FramePointerRegister, 0); - for (int i = 0, ei = fpRegistersToSave.size(); i < ei; ++i) { - Q_ASSERT(fpRegistersToSave.at(i).isFloatingPoint()); - slotAddr.offset -= sizeof(double); - TargetConfiguration::MacroAssembler::storeDouble(fpRegistersToSave.at(i).reg<FPRegisterID>(), slotAddr); + void loadValue(ReturnedValue value) + { + move(TrustedImm64(value), AccumulatorRegister); } - for (int i = 0, ei = regularRegistersToSave.size(); i < ei; ++i) { - Q_ASSERT(regularRegistersToSave.at(i).isRegularRegister()); - slotAddr.offset -= RegisterSize; - storePtr(regularRegistersToSave.at(i).reg<RegisterID>(), slotAddr); + + void generateCatchTrampoline() + { + PlatformAssemblerCommon::generateCatchTrampoline([this](){loadUndefined();}); } - platformFinishEnteringStandardStackFrame(this); -} + void toBoolean(std::function<void(RegisterID)> continuation) + { + urshift64(AccumulatorRegister, TrustedImm32(Value::IsIntegerConvertible_Shift), ScratchRegister); + auto needsConversion = branch32(NotEqual, TrustedImm32(1), ScratchRegister); + continuation(AccumulatorRegister); + Jump done = jump(); + + // slow path: + needsConversion.link(this); + push(AccumulatorRegister); + move(AccumulatorRegister, registerForArg(0)); + callHelper(Value::toBooleanImpl); + and32(TrustedImm32(1), ReturnValueRegister, ScratchRegister); + pop(AccumulatorRegister); + continuation(ScratchRegister); + + done.link(this); + } -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::leaveStandardStackFrame(const RegisterInformation ®ularRegistersToSave, - const RegisterInformation &fpRegistersToSave) -{ - Address slotAddr(JITTargetPlatform::FramePointerRegister, -regularRegistersToSave.size() * RegisterSize - fpRegistersToSave.size() * sizeof(double)); + void toNumber() + { + move(AccumulatorRegister, registerForArg(0)); + callHelper(toNumberHelper); + move(ReturnValueRegister, AccumulatorRegister); + } - // restore the callee saved registers - for (int i = regularRegistersToSave.size() - 1; i >= 0; --i) { - Q_ASSERT(regularRegistersToSave.at(i).isRegularRegister()); - loadPtr(slotAddr, regularRegistersToSave.at(i).reg<RegisterID>()); - slotAddr.offset += RegisterSize; + void toInt32() + { + move(AccumulatorRegister, registerForArg(0)); + callRuntime("toInt32Helper", reinterpret_cast<void *>(&toInt32Helper), + Assembler::ResultInAccumulator); } - for (int i = fpRegistersToSave.size() - 1; i >= 0; --i) { - Q_ASSERT(fpRegistersToSave.at(i).isFloatingPoint()); - TargetConfiguration::MacroAssembler::loadDouble(slotAddr, fpRegistersToSave.at(i).reg<FPRegisterID>()); - slotAddr.offset += sizeof(double); + + void regToInt32(Address srcReg, RegisterID targetReg) + { + pushAligned(AccumulatorRegister); + load64(srcReg, registerForArg(0)); + callHelper(toInt32Helper); + move(ReturnValueRegister, targetReg); + popAligned(AccumulatorRegister); } - Q_ASSERT(slotAddr.offset == 0); + void isNullOrUndefined() + { + move(AccumulatorRegister, ScratchRegister); + compare64(Equal, ScratchRegister, TrustedImm32(0), AccumulatorRegister); + Jump isUndef = branch32(NotEqual, TrustedImm32(0), AccumulatorRegister); - const int frameSize = _stackLayout->calculateStackFrameSize(); - platformLeaveStandardStackFrame(this, frameSize); -} + // not undefined + rshift64(TrustedImm32(32), ScratchRegister); + compare32(Equal, ScratchRegister, TrustedImm32(int(QV4::Value::ValueTypeInternal::Null)), + AccumulatorRegister); + isUndef.link(this); + } + void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset) + { + Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value))); + load64(lhsAddr, ScratchRegister); + Jump isUndef = branch64(Equal, ScratchRegister, TrustedImm64(0)); + Jump equal = branch32(Equal, TrustedImm32(rhs), ScratchRegister); + patches.push_back({ equal, offset }); + isUndef.link(this); + } + void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset) + { + Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value))); + load64(lhsAddr, ScratchRegister); + Jump isUndef = branch64(Equal, ScratchRegister, TrustedImm64(0)); + patches.push_back({ isUndef, offset }); + Jump notEqual = branch32(NotEqual, TrustedImm32(rhs), ScratchRegister); + patches.push_back({ notEqual, offset }); + } -// Try to load the source expression into the destination FP register. This assumes that two -// general purpose (integer) registers are available: the ScratchRegister and the -// ReturnValueRegister. It returns a Jump if no conversion can be performed. -template <typename TargetConfiguration> -typename Assembler<TargetConfiguration>::Jump Assembler<TargetConfiguration>::genTryDoubleConversion(IR::Expr *src, FPRegisterID dest) -{ - switch (src->type) { - case IR::DoubleType: - moveDouble(toDoubleRegister(src, dest), dest); - return Assembler::Jump(); - case IR::SInt32Type: - convertInt32ToDouble(toInt32Register(src, Assembler::ScratchRegister), - dest); - return Assembler::Jump(); - case IR::UInt32Type: - convertUInt32ToDouble(toUInt32Register(src, Assembler::ScratchRegister), - dest, Assembler::ReturnValueRegister); - return Assembler::Jump(); - case IR::NullType: - case IR::UndefinedType: - case IR::BoolType: - // TODO? - case IR::StringType: - return jump(); - default: - break; + void setAccumulatorTag(QV4::Value::ValueTypeInternal tag, RegisterID sourceReg = NoRegister) + { + if (sourceReg == NoRegister) + or64(TrustedImm64(int64_t(tag) << 32), AccumulatorRegister); + else + or64(TrustedImm64(int64_t(tag) << 32), sourceReg, AccumulatorRegister); } - Q_ASSERT(src->asTemp() || src->asArgLocal()); + void encodeDoubleIntoAccumulator(FPRegisterID src) + { + moveDoubleTo64(src, AccumulatorRegister); + move(TrustedImm64(Value::NaNEncodeMask), ScratchRegister); + xor64(ScratchRegister, AccumulatorRegister); + } - // It's not a number type, so it cannot be in a register. - Q_ASSERT(src->asArgLocal() || src->asTemp()->kind != IR::Temp::PhysicalRegister || src->type == IR::BoolType); + void pushValue(ReturnedValue v) + { + loadValue(v); + push(AccumulatorRegister); + } - Assembler::Pointer tagAddr = loadAddressForReading(Assembler::ScratchRegister, src); - tagAddr.offset += 4; - load32(tagAddr, Assembler::ScratchRegister); + void pushValueAligned(ReturnedValue v) + { + subPtr(TrustedImm32(PointerSize), StackPointerRegister); + pushValue(v); + } - // check if it's an int32: - Assembler::Jump isNoInt = branch32(Assembler::NotEqual, Assembler::ScratchRegister, - Assembler::TrustedImm32(quint32(ValueTypeInternal::Integer))); - convertInt32ToDouble(toInt32Register(src, Assembler::ScratchRegister), dest); - Assembler::Jump intDone = jump(); + void popValueAligned() + { + addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); + } - // not an int, check if it's a double: - isNoInt.link(this); - Assembler::Jump isNoDbl = RegisterSizeDependentOps::checkIfTagRegisterIsDouble(this, ScratchRegister); - toDoubleRegister(src, dest); - intDone.link(this); + Jump binopBothIntPath(Address lhsAddr, std::function<Jump(void)> fastPath) + { + urshift64(AccumulatorRegister, TrustedImm32(32), ScratchRegister); + Jump accNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), ScratchRegister); + load64(lhsAddr, ScratchRegister); + urshift64(ScratchRegister, TrustedImm32(32), ScratchRegister2); + Jump lhsNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), ScratchRegister2); + + // both integer + Jump failure = fastPath(); + Jump done = jump(); + + // all other cases + if (failure.isSet()) + failure.link(this); + accNotInt.link(this); + lhsNotInt.link(this); + + return done; + } +}; - return isNoDbl; -} +typedef PlatformAssembler64 PlatformAssembler; +#endif -template <typename TargetConfiguration> -typename Assembler<TargetConfiguration>::Jump Assembler<TargetConfiguration>::branchDouble(bool invertCondition, IR::AluOp op, - IR::Expr *left, IR::Expr *right) +#if QT_POINTER_SIZE == 4 || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES) +struct PlatformAssembler32 : PlatformAssemblerCommon { - DoubleCondition cond; - switch (op) { - case IR::OpGt: cond = Assembler::DoubleGreaterThan; break; - case IR::OpLt: cond = Assembler::DoubleLessThan; break; - case IR::OpGe: cond = Assembler::DoubleGreaterThanOrEqual; break; - case IR::OpLe: cond = Assembler::DoubleLessThanOrEqual; break; - case IR::OpEqual: - case IR::OpStrictEqual: cond = Assembler::DoubleEqual; break; - case IR::OpNotEqual: - case IR::OpStrictNotEqual: cond = Assembler::DoubleNotEqualOrUnordered; break; // No, the inversion of DoubleEqual is NOT DoubleNotEqual. - default: - Q_UNREACHABLE(); + void callRuntime(const char *functionName, const void *funcPtr, + Assembler::CallResultDestination dest) + { + PlatformAssemblerCommon::callRuntime(functionName, funcPtr); + if (dest == Assembler::ResultInAccumulator) { + move(ReturnValueRegisterValue, AccumulatorRegisterValue); + move(ReturnValueRegisterTag, AccumulatorRegisterTag); + } } - if (invertCondition) - cond = TargetConfiguration::MacroAssembler::invert(cond); - return TargetConfiguration::MacroAssembler::branchDouble(cond, toDoubleRegister(left, FPGpr0), toDoubleRegister(right, JITTargetPlatform::FPGpr1)); -} + void loadUndefined() + { + move(TrustedImm32(0), AccumulatorRegisterValue); + move(TrustedImm32(0), AccumulatorRegisterTag); + } -template <typename TargetConfiguration> -typename Assembler<TargetConfiguration>::Jump Assembler<TargetConfiguration>::branchInt32(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right) -{ - Assembler::RelationalCondition cond; - switch (op) { - case IR::OpGt: cond = Assembler::GreaterThan; break; - case IR::OpLt: cond = Assembler::LessThan; break; - case IR::OpGe: cond = Assembler::GreaterThanOrEqual; break; - case IR::OpLe: cond = Assembler::LessThanOrEqual; break; - case IR::OpEqual: - case IR::OpStrictEqual: cond = Assembler::Equal; break; - case IR::OpNotEqual: - case IR::OpStrictNotEqual: cond = Assembler::NotEqual; break; - default: - Q_UNREACHABLE(); + void copyConst(int constIndex, Address destRegAddr) + { + //### + if (constantTable[constIndex].isUndefined()) { + move(TrustedImm32(0), ScratchRegister); + store32(ScratchRegister, destRegAddr); + destRegAddr.offset += 4; + store32(ScratchRegister, destRegAddr); + } else { + Address src = loadConstAddress(constIndex); + loadDouble(src, FPScratchRegister); + storeDouble(FPScratchRegister, destRegAddr); + } } - if (invertCondition) - cond = TargetConfiguration::MacroAssembler::invert(cond); - - return TargetConfiguration::MacroAssembler::branch32(cond, - toInt32Register(left, Assembler::ScratchRegister), - toInt32Register(right, Assembler::ReturnValueRegister)); -} - -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::setStackLayout(int maxArgCountForBuiltins, int regularRegistersToSave, int fpRegistersToSave) -{ - _stackLayout.reset(new StackLayout(_function, maxArgCountForBuiltins, regularRegistersToSave, fpRegistersToSave)); -} - -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::returnFromFunction(IR::Ret *s, RegisterInformation regularRegistersToSave, RegisterInformation fpRegistersToSave) -{ - if (!s) { - // this only happens if the method doesn't have a return statement and can - // only exit through an exception - } else if (IR::Temp *t = s->expr->asTemp()) { - RegisterSizeDependentOps::setFunctionReturnValueFromTemp(this, t); - } else if (IR::Const *c = s->expr->asConst()) { - auto retVal = convertToValue<TargetPrimitive>(c); - RegisterSizeDependentOps::setFunctionReturnValueFromConst(this, retVal); - } else { - Q_UNREACHABLE(); - Q_UNUSED(s); + + void copyReg(Address src, Address dest) + { + loadDouble(src, FPScratchRegister); + storeDouble(FPScratchRegister, dest); + } + + void loadReg(Address addr) + { + load32(addr, AccumulatorRegisterValue); + addr.offset += 4; + load32(addr, AccumulatorRegisterTag); + } + + void loadAccumulator(Address src) + { + load32(src, AccumulatorRegisterValue); + src.offset += 4; + load32(src, AccumulatorRegisterTag); + } + + void storeAccumulator(Address addr) + { + store32(AccumulatorRegisterValue, addr); + addr.offset += 4; + store32(AccumulatorRegisterTag, addr); + } + + void loadString(int stringId) + { + load32(loadStringAddress(stringId), AccumulatorRegisterValue); + move(TrustedImm32(0), AccumulatorRegisterTag); + } + + void loadValue(ReturnedValue value) + { + move(TrustedImm32(Value::fromReturnedValue(value).value()), AccumulatorRegisterValue); + move(TrustedImm32(Value::fromReturnedValue(value).tag()), AccumulatorRegisterTag); + } + + void generateCatchTrampoline() + { + PlatformAssemblerCommon::generateCatchTrampoline([this](){loadUndefined();}); + } + + void toNumber() + { + if (ArgInRegCount < 2) { + push(AccumulatorRegisterTag); + push(AccumulatorRegisterValue); + } else { + move(AccumulatorRegisterValue, registerForArg(0)); + move(AccumulatorRegisterTag, registerForArg(1)); + } + callRuntime("toNumberHelper", reinterpret_cast<void *>(&toNumberHelper), + Assembler::ResultInAccumulator); + move(ReturnValueRegisterValue, AccumulatorRegisterValue); + move(ReturnValueRegisterTag, AccumulatorRegisterTag); + if (ArgInRegCount < 2) + addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); + } + + void toInt32() + { + if (ArgInRegCount < 2) { + push(AccumulatorRegisterTag); + push(AccumulatorRegisterValue); + } else { + move(AccumulatorRegisterValue, registerForArg(0)); + move(AccumulatorRegisterTag, registerForArg(1)); + } + callRuntime("toInt32Helper", reinterpret_cast<void *>(&toInt32Helper), + Assembler::ResultInAccumulator); + if (ArgInRegCount < 2) + addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); + } + + void regToInt32(Address srcReg, RegisterID targetReg) + { + bool accumulatorNeedsSaving = AccumulatorRegisterValue == ReturnValueRegisterValue + || AccumulatorRegisterTag == ReturnValueRegisterTag; + if (accumulatorNeedsSaving) { + push(AccumulatorRegisterTag); + push(AccumulatorRegisterValue); + } + if (ArgInRegCount < 2) { + srcReg.offset += 4; + load32(srcReg, targetReg); + push(targetReg); + srcReg.offset -= 4; + load32(srcReg, targetReg); + push(targetReg); + } else { + load32(srcReg, registerForArg(0)); + srcReg.offset += 4; + load32(srcReg, registerForArg(1)); + } + callHelper(toInt32Helper); + move(ReturnValueRegisterValue, targetReg); + if (ArgInRegCount < 2) + addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); + if (accumulatorNeedsSaving) { + pop(AccumulatorRegisterValue); + pop(AccumulatorRegisterTag); + } + } + + void isNullOrUndefined() + { + Jump notUndefOrPtr = branch32(NotEqual, TrustedImm32(0), AccumulatorRegisterTag); + compare32(Equal, AccumulatorRegisterValue, TrustedImm32(0), AccumulatorRegisterValue); + auto done = jump(); + + // not undefined or managed + notUndefOrPtr.link(this); + compare32(Equal, AccumulatorRegisterTag, TrustedImm32(int(QV4::Value::ValueTypeInternal::Null)), + AccumulatorRegisterValue); + + done.link(this); + } + + void pushValue(ReturnedValue v) + { + push(TrustedImm32(v >> 32)); + push(TrustedImm32(v)); + } + + void toBoolean(std::function<void(RegisterID)> continuation) + { + urshift32(AccumulatorRegisterTag, TrustedImm32(Value::IsIntegerConvertible_Shift - 32), + ScratchRegister); + auto needsConversion = branch32(NotEqual, TrustedImm32(1), ScratchRegister); + continuation(AccumulatorRegisterValue); + Jump done = jump(); + + // slow path: + needsConversion.link(this); + + bool accumulatorNeedsSaving = AccumulatorRegisterValue == ReturnValueRegisterValue + || AccumulatorRegisterTag == ReturnValueRegisterTag; + if (accumulatorNeedsSaving) { + push(AccumulatorRegisterTag); + push(AccumulatorRegisterValue); + } + + if (ArgInRegCount < 2) { + push(AccumulatorRegisterTag); + push(AccumulatorRegisterValue); + } else { + move(AccumulatorRegisterValue, registerForArg(0)); + move(AccumulatorRegisterTag, registerForArg(1)); + } + callHelper(Value::toBooleanImpl); + if (ArgInRegCount < 2) + addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); + + and32(TrustedImm32(1), ReturnValueRegisterValue, ScratchRegister); + if (accumulatorNeedsSaving) { + pop(AccumulatorRegisterValue); + pop(AccumulatorRegisterTag); + } + continuation(ScratchRegister); + + done.link(this); + } + + void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset) + { + Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value))); + load32(lhsAddr, ScratchRegister); + Jump notEqInt = branch32(NotEqual, ScratchRegister, TrustedImm32(rhs)); + Jump notEqUndefVal = branch32(NotEqual, ScratchRegister, TrustedImm32(0)); + patches.push_back({ notEqUndefVal, offset }); + lhsAddr.offset += 4; + load32(lhsAddr, ScratchRegister); + Jump notEqUndefTag = branch32(NotEqual, ScratchRegister, TrustedImm32(0)); + patches.push_back({ notEqUndefTag, offset }); + notEqInt.link(this); + } + + void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset) + { + Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value))); + load32(lhsAddr, ScratchRegister); + Jump notEqual = branch32(NotEqual, TrustedImm32(rhs), ScratchRegister); + patches.push_back({ notEqual, offset }); + Jump notUndefValue = branch32(NotEqual, TrustedImm32(0), ScratchRegister); + lhsAddr.offset += 4; + load32(lhsAddr, ScratchRegister); + Jump equalUndef = branch32(Equal, TrustedImm32(0), ScratchRegister); + patches.push_back({ equalUndef, offset }); + notUndefValue.link(this); + } + + void setAccumulatorTag(QV4::Value::ValueTypeInternal tag, RegisterID sourceReg = NoRegister) + { + if (sourceReg != NoRegister) + move(sourceReg, AccumulatorRegisterValue); + move(TrustedImm32(int(tag)), AccumulatorRegisterTag); + } + + void encodeDoubleIntoAccumulator(FPRegisterID src) + { + moveDoubleToInts(src, AccumulatorRegisterValue, AccumulatorRegisterTag); + xor32(TrustedImm32(Value::NaNEncodeMask >> 32), AccumulatorRegisterTag); + } + + void pushValueAligned(ReturnedValue v) + { + pushValue(v); } - Label leaveStackFrame = label(); + void popValueAligned() + { + popValue(); + } + + Jump binopBothIntPath(Address lhsAddr, std::function<Jump(void)> fastPath) + { + Jump accNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), AccumulatorRegisterTag); + Address lhsAddrTag = lhsAddr; lhsAddrTag.offset += Value::tagOffset(); + load32(lhsAddrTag, ScratchRegister); + Jump lhsNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), ScratchRegister); + + // both integer + Address lhsAddrValue = lhsAddr; lhsAddrValue.offset += Value::valueOffset(); + load32(lhsAddrValue, ScratchRegister); + Jump failure = fastPath(); + Jump done = jump(); + + // all other cases + if (failure.isSet()) + failure.link(this); + accNotInt.link(this); + lhsNotInt.link(this); + + return done; + } +}; + +typedef PlatformAssembler32 PlatformAssembler; +#endif + +typedef PlatformAssembler::TrustedImmPtr TrustedImmPtr; +typedef PlatformAssembler::TrustedImm32 TrustedImm32; +typedef PlatformAssembler::TrustedImm64 TrustedImm64; +typedef PlatformAssembler::Address Address; +typedef PlatformAssembler::RegisterID RegisterID; +typedef PlatformAssembler::FPRegisterID FPRegisterID; - const int locals = stackLayout().calculateJSStackFrameSize(); - subPtr(TrustedImm32(sizeof(QV4::Value)*locals), JITTargetPlatform::LocalsRegister); - storePtr(JITTargetPlatform::LocalsRegister, Address(JITTargetPlatform::EngineRegister, targetStructureOffset(offsetof(EngineBase, jsStackTop)))); +#define pasm() reinterpret_cast<PlatformAssembler *>(this->d) - leaveStandardStackFrame(regularRegistersToSave, fpRegistersToSave); - ret(); +static Address regAddr(int reg) +{ + return Address(PlatformAssembler::JSStackFrameRegister, reg * int(sizeof(QV4::Value))); +} + +Assembler::Assembler(const Value *constantTable) + : d(new PlatformAssembler) +{ + pasm()->constantTable = constantTable; +} - exceptionReturnLabel = label(); - auto retVal = TargetPrimitive::undefinedValue(); - RegisterSizeDependentOps::setFunctionReturnValueFromConst(this, retVal); - jump(leaveStackFrame); +Assembler::~Assembler() +{ + delete pasm(); +} + +void Assembler::generatePrologue() +{ + pasm()->generateFunctionEntry(); +} + +void Assembler::generateEpilogue() +{ + pasm()->generateCatchTrampoline(); } namespace { @@ -560,7 +1155,7 @@ public: ~QIODevicePrintStream() {} - void vprintf(const char* format, va_list argList) override WTF_ATTRIBUTE_PRINTF(2, 0) + void vprintf(const char* format, va_list argList) WTF_ATTRIBUTE_PRINTF(2, 0) { const int written = qvsnprintf(buf.data(), buf.size(), format, argList); if (written > 0) @@ -568,7 +1163,7 @@ public: memset(buf.data(), 0, qMin(written, buf.size())); } - void flush() override + void flush() {} private: @@ -577,150 +1172,792 @@ private: }; } // anonymous namespace -static void printDisassembledOutputWithCalls(QByteArray processedOutput, const QHash<void*, const char*>& functions) +static void printDisassembledOutputWithCalls(QByteArray processedOutput, + const QHash<const void*, const char*>& functions) { - for (QHash<void*, const char*>::ConstIterator it = functions.begin(), end = functions.end(); + for (QHash<const void*, const char*>::ConstIterator it = functions.begin(), end = functions.end(); it != end; ++it) { const QByteArray ptrString = "0x" + QByteArray::number(quintptr(it.key()), 16); - int idx = processedOutput.indexOf(ptrString); - if (idx < 0) - continue; - idx = processedOutput.lastIndexOf('\n', idx); - if (idx < 0) - continue; - processedOutput = processedOutput.insert(idx, QByteArrayLiteral(" ; call ") + it.value()); + int idx = 0; + while (idx >= 0) { + idx = processedOutput.indexOf(ptrString, idx); + if (idx < 0) + break; + idx = processedOutput.indexOf('\n', idx); + if (idx < 0) + break; + processedOutput = processedOutput.insert(idx, QByteArrayLiteral(" ; ") + it.value()); + } } qDebug("%s", processedOutput.constData()); } -#if defined(Q_OS_LINUX) -static FILE *pmap; +void Assembler::link(Function *function) +{ + for (const auto &jumpTarget : pasm()->patches) + jumpTarget.jump.linkTo(pasm()->labelsByOffset[jumpTarget.offset], pasm()); + + JSC::JSGlobalData dummy(function->internalClass->engine->executableAllocator); + JSC::LinkBuffer<PlatformAssembler::MacroAssembler> linkBuffer(dummy, pasm(), 0); + + for (const auto &ehTarget : pasm()->ehTargets) { + auto targetLabel = pasm()->labelsByOffset.value(ehTarget.offset); + linkBuffer.patch(ehTarget.label, linkBuffer.locationOf(targetLabel)); + } + + JSC::MacroAssemblerCodeRef codeRef; + + static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_ASM"); + if (showCode) { + QBuffer buf; + buf.open(QIODevice::WriteOnly); + WTF::setDataFile(new QIODevicePrintStream(&buf)); + + QByteArray name = function->name()->toQString().toUtf8(); + if (name.isEmpty()) { + name = QByteArray::number(quintptr(function), 16); + name.prepend("QV4::Function(0x"); + name.append(')'); + } + codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data()); + + WTF::setDataFile(stderr); + printDisassembledOutputWithCalls(buf.data(), pasm()->functions); + } else { + codeRef = linkBuffer.finalizeCodeWithoutDisassembly(); + } + + function->codeRef = new JSC::MacroAssemblerCodeRef(codeRef); + function->jittedCode = reinterpret_cast<Function::JittedCode>(function->codeRef->code().executableAddress()); +} + +void Assembler::addLabel(int offset) +{ + pasm()->labelsByOffset[offset] = pasm()->label(); +} -static void qt_closePmap() +void Assembler::loadConst(int constIndex) { - if (pmap) { - fclose(pmap); - pmap = 0; + //### + if (pasm()->constantTable[constIndex].isUndefined()) { + pasm()->loadUndefined(); + } else { + pasm()->loadAccumulator(pasm()->loadConstAddress(constIndex)); } } -#endif +void Assembler::copyConst(int constIndex, int destReg) +{ + pasm()->copyConst(constIndex, regAddr(destReg)); +} -template <typename TargetConfiguration> -JSC::MacroAssemblerCodeRef Assembler<TargetConfiguration>::link(int *codeSize) +void Assembler::loadReg(int reg) { - Label endOfCode = label(); + pasm()->loadReg(regAddr(reg)); +} - { - for (size_t i = 0, ei = _patches.size(); i != ei; ++i) { - Label target = _addrs.at(i); - Q_ASSERT(target.isSet()); - for (Jump jump : qAsConst(_patches.at(i))) - jump.linkTo(target, this); - } - } +void Assembler::storeReg(int reg) +{ + pasm()->storeAccumulator(regAddr(reg)); +} - JSC::JSGlobalData dummy(_executableAllocator); - JSC::LinkBuffer<typename TargetConfiguration::MacroAssembler> linkBuffer(dummy, this, 0); +void Assembler::loadString(int stringId) +{ + pasm()->loadString(stringId); +} - for (const DataLabelPatch &p : qAsConst(_dataLabelPatches)) - linkBuffer.patch(p.dataLabel, linkBuffer.locationOf(p.target)); +void Assembler::loadValue(ReturnedValue value) +{ + pasm()->loadValue(value); +} - // link exception handlers - for (Jump jump : qAsConst(exceptionPropagationJumps)) - linkBuffer.link(jump, linkBuffer.locationOf(exceptionReturnLabel)); +void Assembler::toNumber() +{ + pasm()->toNumber(); +} - { - for (size_t i = 0, ei = _labelPatches.size(); i != ei; ++i) { - Label target = _addrs.at(i); - Q_ASSERT(target.isSet()); - for (DataLabelPtr label : _labelPatches.at(i)) - linkBuffer.patch(label, linkBuffer.locationOf(target)); - } - } +void Assembler::uminus() +{ + saveAccumulatorInFrame(); + prepareCallWithArgCount(1); + passAccumulatorAsArg(0); + IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_uMinus, ResultInAccumulator); + checkException(); +} - *codeSize = linkBuffer.offsetOf(endOfCode); +void Assembler::ucompl() +{ + pasm()->toInt32(); + pasm()->xor32(TrustedImm32(-1), PlatformAssembler::AccumulatorRegisterValue); + pasm()->setAccumulatorTag(IntegerTag); +} - QByteArray name; +static ReturnedValue incHelper(const Value &v) +{ + return Encode(v.toNumber() + 1.); +} - JSC::MacroAssemblerCodeRef codeRef; +void Assembler::inc() +{ +// auto done = pasm()->incFastPath(); - static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_ASM"); - if (showCode) { - QHash<void*, const char*> functions; + // slow path: + saveAccumulatorInFrame(); + prepareCallWithArgCount(1); + passAccumulatorAsArg(0); + IN_JIT_GENERATE_RUNTIME_CALL(incHelper, ResultInAccumulator); + checkException(); + + // done. +// done.link(pasm()); +} + +static ReturnedValue decHelper(const Value &v) +{ + return Encode(v.toNumber() - 1.); +} + +void Assembler::dec() +{ +// auto done = pasm()->decFastPath(); + + // slow path: + saveAccumulatorInFrame(); + prepareCallWithArgCount(1); + passAccumulatorAsArg(0); + IN_JIT_GENERATE_RUNTIME_CALL(decHelper, ResultInAccumulator); + checkException(); + + // done. +// done.link(pasm()); +} + +void Assembler::unot() +{ + pasm()->toBoolean([this](PlatformAssembler::RegisterID resultReg){ + pasm()->compare32(PlatformAssembler::Equal, resultReg, + TrustedImm32(0), PlatformAssembler::AccumulatorRegisterValue); + pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean); + }); +} + +void Assembler::add(int lhs) +{ + auto done = pasm()->binopBothIntPath(regAddr(lhs), [this](){ + auto overflowed = pasm()->branchAdd32(PlatformAssembler::Overflow, + PlatformAssembler::AccumulatorRegisterValue, + PlatformAssembler::ScratchRegister); + pasm()->setAccumulatorTag(IntegerTag, + PlatformAssembler::ScratchRegister); + return overflowed; + }); + + // slow path: + saveAccumulatorInFrame(); + prepareCallWithArgCount(3); + passAccumulatorAsArg(2); + passRegAsArg(lhs, 1); + passEngineAsArg(0); + IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_add, ResultInAccumulator); + checkException(); + + // done. + done.link(pasm()); +} + +void Assembler::bitAnd(int lhs) +{ + PlatformAssembler::Address lhsAddr = regAddr(lhs); + pasm()->regToInt32(lhsAddr, PlatformAssembler::ScratchRegister); + pasm()->pushAligned(PlatformAssembler::ScratchRegister); + pasm()->toInt32(); + pasm()->popAligned(PlatformAssembler::ScratchRegister); + pasm()->and32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue); + pasm()->setAccumulatorTag(IntegerTag); +} + +void Assembler::bitOr(int lhs) +{ + PlatformAssembler::Address lhsAddr = regAddr(lhs); + pasm()->regToInt32(lhsAddr, PlatformAssembler::ScratchRegister); + pasm()->pushAligned(PlatformAssembler::ScratchRegister); + pasm()->toInt32(); + pasm()->popAligned(PlatformAssembler::ScratchRegister); + pasm()->or32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue); + pasm()->setAccumulatorTag(IntegerTag); +} + +void Assembler::bitXor(int lhs) +{ + PlatformAssembler::Address lhsAddr = regAddr(lhs); + pasm()->regToInt32(lhsAddr, PlatformAssembler::ScratchRegister); + pasm()->pushAligned(PlatformAssembler::ScratchRegister); + pasm()->toInt32(); + pasm()->popAligned(PlatformAssembler::ScratchRegister); + pasm()->xor32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue); + pasm()->setAccumulatorTag(IntegerTag); +} + +void Assembler::ushr(int lhs) +{ + PlatformAssembler::Address lhsAddr = regAddr(lhs); + pasm()->regToInt32(lhsAddr, PlatformAssembler::ScratchRegister); + pasm()->pushAligned(PlatformAssembler::ScratchRegister); + pasm()->toInt32(); + pasm()->and32(TrustedImm32(0x1f), PlatformAssembler::AccumulatorRegisterValue, + PlatformAssembler::ScratchRegister); + pasm()->popAligned(PlatformAssembler::AccumulatorRegisterValue); + pasm()->urshift32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue); + auto doubleEncode = pasm()->branch32(PlatformAssembler::LessThan, + PlatformAssembler::AccumulatorRegisterValue, + TrustedImm32(0)); + pasm()->setAccumulatorTag(IntegerTag); + auto done = pasm()->jump(); + + doubleEncode.link(pasm()); + pasm()->convertUInt32ToDouble(PlatformAssembler::AccumulatorRegisterValue, + PlatformAssembler::FPScratchRegister, + PlatformAssembler::ScratchRegister); + pasm()->encodeDoubleIntoAccumulator(PlatformAssembler::FPScratchRegister); + done.link(pasm()); +} + +void Assembler::shr(int lhs) +{ + PlatformAssembler::Address lhsAddr = regAddr(lhs); + pasm()->regToInt32(lhsAddr, PlatformAssembler::ScratchRegister); + pasm()->pushAligned(PlatformAssembler::ScratchRegister); + pasm()->toInt32(); + pasm()->and32(TrustedImm32(0x1f), PlatformAssembler::AccumulatorRegisterValue, + PlatformAssembler::ScratchRegister); + pasm()->popAligned(PlatformAssembler::AccumulatorRegisterValue); + pasm()->rshift32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue); + pasm()->setAccumulatorTag(IntegerTag); +} + +void Assembler::shl(int lhs) +{ + PlatformAssembler::Address lhsAddr = regAddr(lhs); + pasm()->regToInt32(lhsAddr, PlatformAssembler::ScratchRegister); + pasm()->pushAligned(PlatformAssembler::ScratchRegister); + pasm()->toInt32(); + pasm()->and32(TrustedImm32(0x1f), PlatformAssembler::AccumulatorRegisterValue, + PlatformAssembler::ScratchRegister); + pasm()->popAligned(PlatformAssembler::AccumulatorRegisterValue); + pasm()->lshift32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue); + pasm()->setAccumulatorTag(IntegerTag); +} + +void Assembler::bitAndConst(int rhs) +{ + pasm()->toInt32(); + pasm()->and32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue); + pasm()->setAccumulatorTag(IntegerTag); +} + +void Assembler::bitOrConst(int rhs) +{ + pasm()->toInt32(); + pasm()->or32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue); + pasm()->setAccumulatorTag(IntegerTag); +} + +void Assembler::bitXorConst(int rhs) +{ + pasm()->toInt32(); + pasm()->xor32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue); + pasm()->setAccumulatorTag(IntegerTag); +} + +void Assembler::ushrConst(int rhs) +{ + rhs &= 0x1f; + pasm()->toInt32(); + if (rhs) // shift with 0 can act weird + pasm()->urshift32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue); + auto doubleEncode = pasm()->branch32(PlatformAssembler::LessThan, + PlatformAssembler::AccumulatorRegisterValue, + TrustedImm32(0)); + pasm()->setAccumulatorTag(IntegerTag); + auto done = pasm()->jump(); + + doubleEncode.link(pasm()); + pasm()->convertUInt32ToDouble(PlatformAssembler::AccumulatorRegisterValue, + PlatformAssembler::FPScratchRegister, + PlatformAssembler::ScratchRegister); + pasm()->encodeDoubleIntoAccumulator(PlatformAssembler::FPScratchRegister); + done.link(pasm()); +} + +void Assembler::shrConst(int rhs) +{ + rhs &= 0x1f; + pasm()->toInt32(); + if (rhs) // shift with 0 can act weird + pasm()->rshift32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue); + pasm()->setAccumulatorTag(IntegerTag); +} + +void Assembler::shlConst(int rhs) +{ + rhs &= 0x1f; + pasm()->toInt32(); + if (rhs) // shift with 0 can act weird + pasm()->lshift32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue); + pasm()->setAccumulatorTag(IntegerTag); +} + +void Assembler::mul(int lhs) +{ + auto done = pasm()->binopBothIntPath(regAddr(lhs), [this](){ + auto overflowed = pasm()->branchMul32(PlatformAssembler::Overflow, + PlatformAssembler::AccumulatorRegisterValue, + PlatformAssembler::ScratchRegister); + pasm()->setAccumulatorTag(IntegerTag, + PlatformAssembler::ScratchRegister); + return overflowed; + }); + + // slow path: + saveAccumulatorInFrame(); + prepareCallWithArgCount(2); + passAccumulatorAsArg(1); + passRegAsArg(lhs, 0); + IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_mul, ResultInAccumulator); + checkException(); + + // done. + done.link(pasm()); +} + +void Assembler::div(int lhs) +{ + saveAccumulatorInFrame(); + prepareCallWithArgCount(2); + passAccumulatorAsArg(1); + passRegAsArg(lhs, 0); + IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_div, ResultInAccumulator); + checkException(); +} + +void Assembler::mod(int lhs) +{ + saveAccumulatorInFrame(); + prepareCallWithArgCount(2); + passAccumulatorAsArg(1); + passRegAsArg(lhs, 0); + IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_mod, ResultInAccumulator); + checkException(); +} + +void Assembler::sub(int lhs) +{ + auto done = pasm()->binopBothIntPath(regAddr(lhs), [this](){ + auto overflowed = pasm()->branchSub32(PlatformAssembler::Overflow, + PlatformAssembler::AccumulatorRegisterValue, + PlatformAssembler::ScratchRegister); + pasm()->setAccumulatorTag(IntegerTag, + PlatformAssembler::ScratchRegister); + return overflowed; + }); + + // slow path: + saveAccumulatorInFrame(); + prepareCallWithArgCount(2); + passAccumulatorAsArg(1); + passRegAsArg(lhs, 0); + IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_sub, ResultInAccumulator); + checkException(); + + // done. + done.link(pasm()); +} + +void Assembler::cmpeqNull() +{ + pasm()->isNullOrUndefined(); + pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean); +} + +void Assembler::cmpneNull() +{ + pasm()->isNullOrUndefined(); + pasm()->xor32(TrustedImm32(1), PlatformAssembler::AccumulatorRegisterValue); + pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean); +} + +void Assembler::cmpeqInt(int lhs) +{ + saveAccumulatorInFrame(); + pasm()->pushValueAligned(Encode(lhs)); + if (PlatformAssembler::ArgInRegCount < 2) + pasm()->push(PlatformAssembler::StackPointerRegister); + else + pasm()->move(PlatformAssembler::StackPointerRegister, pasm()->registerForArg(1)); + passAccumulatorAsArg_internal(0, true); + pasm()->callRuntime("Runtime::method_equal", (void*)Runtime::method_equal, ResultInAccumulator); + if (PlatformAssembler::ArgInRegCount < 2) + pasm()->addPtr(TrustedImm32(2 * PlatformAssembler::PointerSize), PlatformAssembler::StackPointerRegister); + pasm()->popValueAligned(); +} + +void Assembler::cmpneInt(int lhs) +{ + saveAccumulatorInFrame(); + pasm()->pushValueAligned(Encode(lhs)); + if (PlatformAssembler::ArgInRegCount < 2) + pasm()->push(PlatformAssembler::StackPointerRegister); + else + pasm()->move(PlatformAssembler::StackPointerRegister, pasm()->registerForArg(1)); + passAccumulatorAsArg_internal(0, true); + pasm()->callRuntime("Runtime::method_notEqual", (void*)Runtime::method_notEqual, ResultInAccumulator); + if (PlatformAssembler::ArgInRegCount < 2) + pasm()->addPtr(TrustedImm32(2 * PlatformAssembler::PointerSize), PlatformAssembler::StackPointerRegister); + pasm()->popValueAligned(); +} + +void Assembler::cmp(int /*cond*/, CmpFunc function, const char *functionName, int lhs) +{ +// PlatformAssembler::Address lhsAddr(PlatformAssembler::JSStackFrameRegister, lhs * int(sizeof(QV4::Value))); +// auto done = pasm()->cmpFastPath(static_cast<PlatformAssembler::RelationalCondition>(cond), lhsAddr); + + // slow path: + saveAccumulatorInFrame(); + prepareCallWithArgCount(2); + passAccumulatorAsArg(1); + passRegAsArg(lhs, 0); + + callRuntime(functionName, reinterpret_cast<void*>(function), ResultInAccumulator); + checkException(); + pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean); + + // done. +// done.link(pasm()); +} + +void Assembler::cmpeq(int lhs) +{ + cmp(PlatformAssembler::Equal, &Runtime::method_compareEqual, + "Runtime::method_compareEqual", lhs); +} + +void Assembler::cmpne(int lhs) +{ + cmp(PlatformAssembler::NotEqual, &Runtime::method_compareNotEqual, + "Runtime::method_compareNotEqual", lhs); +} + +void Assembler::cmpgt(int lhs) +{ + cmp(PlatformAssembler::GreaterThan, &Runtime::method_compareGreaterThan, + "Runtime::method_compareGreaterThan", lhs); +} + +void Assembler::cmpge(int lhs) +{ + cmp(PlatformAssembler::GreaterThanOrEqual, &Runtime::method_compareGreaterEqual, + "Runtime::method_compareGreaterEqual", lhs); +} + +void Assembler::cmplt(int lhs) +{ + cmp(PlatformAssembler::LessThan, &Runtime::method_compareLessThan, + "Runtime::method_compareLessThan", lhs); +} + +void Assembler::cmple(int lhs) +{ + cmp(PlatformAssembler::LessThanOrEqual, &Runtime::method_compareLessEqual, + "Runtime::method_compareLessEqual", lhs); +} + +void Assembler::cmpStrictEqual(int lhs) +{ + saveAccumulatorInFrame(); + prepareCallWithArgCount(2); + passAccumulatorAsArg(1); + passRegAsArg(lhs, 0); + IN_JIT_GENERATE_RUNTIME_CALL(RuntimeHelpers::strictEqual, ResultInAccumulator); + pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean); +} + +void Assembler::cmpStrictNotEqual(int lhs) +{ + saveAccumulatorInFrame(); + prepareCallWithArgCount(2); + passAccumulatorAsArg(1); + passRegAsArg(lhs, 0); + IN_JIT_GENERATE_RUNTIME_CALL(RuntimeHelpers::strictEqual, ResultInAccumulator); + pasm()->xor32(TrustedImm32(1), PlatformAssembler::AccumulatorRegisterValue); + pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean); +} + +void Assembler::jump(int offset) +{ + pasm()->patches.push_back({ pasm()->jump(), offset }); +} + +void Assembler::jumpTrue(int offset) +{ + pasm()->toBoolean([this, offset](PlatformAssembler::RegisterID resultReg) { + auto jump = pasm()->branch32(PlatformAssembler::NotEqual, TrustedImm32(0), resultReg); + pasm()->patches.push_back({ jump, offset }); + }); +} + +void Assembler::jumpFalse(int offset) +{ + pasm()->toBoolean([this, offset](PlatformAssembler::RegisterID resultReg) { + auto jump = pasm()->branch32(PlatformAssembler::Equal, TrustedImm32(0), resultReg); + pasm()->patches.push_back({ jump, offset }); + }); +} + +void Assembler::jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset) +{ + pasm()->jumpStrictEqualStackSlotInt(lhs, rhs, offset); +} + +void Assembler::jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset) +{ + pasm()->jumpStrictNotEqualStackSlotInt(lhs, rhs, offset); +} + +void Assembler::prepareCallWithArgCount(int argc) +{ #ifndef QT_NO_DEBUG - for (CallInfo call : qAsConst(_callInfos)) - functions[linkBuffer.locationOf(call.label).dataLocation()] = call.functionName; + Q_ASSERT(remainingArgcForCall == NoCall); + remainingArgcForCall = argc; #endif - QBuffer buf; - buf.open(QIODevice::WriteOnly); - WTF::setDataFile(new QIODevicePrintStream(&buf)); + if (argc > PlatformAssembler::ArgInRegCount) { + argcOnStackForCall = int(WTF::roundUpToMultipleOf(16, size_t(argc - PlatformAssembler::ArgInRegCount) * PlatformAssembler::PointerSize)); + pasm()->subPtr(TrustedImm32(argcOnStackForCall), PlatformAssembler::StackPointerRegister); + } +} - name = _function->name->toUtf8(); - if (name.isEmpty()) - name = "IR::Function(0x" + QByteArray::number(quintptr(_function), 16) + ')'; - codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data()); +void Assembler::storeInstructionPointer(int instructionOffset) +{ + PlatformAssembler::Address addr(PlatformAssembler::CppStackFrameRegister, + offsetof(QV4::CppStackFrame, instructionPointer)); + pasm()->store32(TrustedImm32(instructionOffset), addr); +} - WTF::setDataFile(stderr); - printDisassembledOutputWithCalls(buf.data(), functions); +Address argStackAddress(int arg) +{ + int offset = arg - PlatformAssembler::ArgInRegCount; + Q_ASSERT(offset >= 0); + return Address(PlatformAssembler::StackPointerRegister, offset * PlatformAssembler::PointerSize); +} + +void Assembler::passAccumulatorAsArg(int arg) +{ +#ifndef QT_NO_DEBUG + Q_ASSERT(arg < remainingArgcForCall); + --remainingArgcForCall; +#endif + + passAccumulatorAsArg_internal(arg, false); +} + +void Assembler::passAccumulatorAsArg_internal(int arg, bool push) +{ + if (arg < PlatformAssembler::ArgInRegCount) { + pasm()->addPtr(TrustedImm32(offsetof(CallData, accumulator)), + PlatformAssembler::JSStackFrameRegister, + pasm()->registerForArg(arg)); } else { - codeRef = linkBuffer.finalizeCodeWithoutDisassembly(); + pasm()->addPtr(TrustedImm32(offsetof(CallData, accumulator)), + PlatformAssembler::JSStackFrameRegister, + PlatformAssembler::ScratchRegister); + if (push) + pasm()->push(PlatformAssembler::ScratchRegister); + else + pasm()->storePtr(PlatformAssembler::ScratchRegister, + argStackAddress(arg)); } +} -#if defined(Q_OS_LINUX) - // This implements writing of JIT'd addresses so that perf can find the - // symbol names. - // - // Perf expects the mapping to be in a certain place and have certain - // content, for more information, see: - // https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/jit-interface.txt - static bool doProfile = !qEnvironmentVariableIsEmpty("QV4_PROFILE_WRITE_PERF_MAP"); - static bool profileInitialized = false; - if (doProfile && !profileInitialized) { - profileInitialized = true; +void Assembler::passFunctionAsArg(int arg) +{ +#ifndef QT_NO_DEBUG + Q_ASSERT(arg < remainingArgcForCall); + --remainingArgcForCall; +#endif - char pname[PATH_MAX]; - snprintf(pname, PATH_MAX - 1, "/tmp/perf-%lu.map", - (unsigned long)QCoreApplication::applicationPid()); + if (arg < PlatformAssembler::ArgInRegCount) { + pasm()->loadFunctionPtr(pasm()->registerForArg(arg)); + } else { + pasm()->loadFunctionPtr(PlatformAssembler::ScratchRegister); + pasm()->storePtr(PlatformAssembler::ScratchRegister, + argStackAddress(arg)); + } +} - pmap = fopen(pname, "w"); - if (!pmap) - qWarning("QV4: Can't write %s, call stacks will not contain JavaScript function names", pname); +void Assembler::passEngineAsArg(int arg) +{ +#ifndef QT_NO_DEBUG + Q_ASSERT(arg < remainingArgcForCall); + --remainingArgcForCall; +#endif - // make sure we clean up nicely - std::atexit(qt_closePmap); + if (arg < PlatformAssembler::ArgInRegCount) { + pasm()->move(PlatformAssembler::EngineRegister, pasm()->registerForArg(arg)); + } else { + pasm()->storePtr(PlatformAssembler::EngineRegister, argStackAddress(arg)); } +} - if (pmap) { - // this may have been pre-populated, if QV4_SHOW_ASM was on - if (name.isEmpty()) { - name = _function->name->toUtf8(); - if (name.isEmpty()) - name = "IR::Function(0x" + QByteArray::number(quintptr(_function), 16) + ')'; - } +void Assembler::passRegAsArg(int reg, int arg) +{ +#ifndef QT_NO_DEBUG + Q_ASSERT(arg < remainingArgcForCall); + --remainingArgcForCall; +#endif - fprintf(pmap, "%llx %x %.*s\n", - (long long unsigned int)codeRef.code().executableAddress(), - *codeSize, - name.length(), - name.constData()); - fflush(pmap); + if (arg < PlatformAssembler::ArgInRegCount) { + pasm()->addPtr(TrustedImm32(reg * int(sizeof(QV4::Value))), + PlatformAssembler::JSStackFrameRegister, + pasm()->registerForArg(arg)); + } else { + pasm()->addPtr(TrustedImm32(reg * int(sizeof(QV4::Value))), + PlatformAssembler::JSStackFrameRegister, + PlatformAssembler::ScratchRegister); + pasm()->storePtr(PlatformAssembler::ScratchRegister, + argStackAddress(arg)); } +} + +void JIT::Assembler::passCppFrameAsArg(int arg) +{ +#ifndef QT_NO_DEBUG + Q_ASSERT(arg < remainingArgcForCall); + --remainingArgcForCall; #endif - return codeRef; + if (arg < PlatformAssembler::ArgInRegCount) { + pasm()->move(PlatformAssembler::CppStackFrameRegister, pasm()->registerForArg(arg)); + } else { + pasm()->store32(PlatformAssembler::CppStackFrameRegister, argStackAddress(arg)); + } } -template class QV4::JIT::Assembler<DefaultAssemblerTargetConfiguration>; -#if defined(V4_BOOTSTRAP) -#if !CPU(ARM_THUMB2) -template class QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>; -#endif -#if !CPU(ARM64) -template class QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>; -#endif +void Assembler::passInt32AsArg(int value, int arg) +{ +#ifndef QT_NO_DEBUG + Q_ASSERT(arg < remainingArgcForCall); + --remainingArgcForCall; #endif + if (arg < PlatformAssembler::ArgInRegCount) { + pasm()->move(TrustedImm32(value), pasm()->registerForArg(arg)); + } else { + pasm()->store32(TrustedImm32(value), argStackAddress(arg)); + } +} + +void Assembler::callRuntime(const char *functionName, const void *funcPtr, + Assembler::CallResultDestination dest) +{ +#ifndef QT_NO_DEBUG + Q_ASSERT(remainingArgcForCall == 0); + remainingArgcForCall = NoCall; #endif + pasm()->callRuntime(functionName, funcPtr, dest); + if (argcOnStackForCall > 0) { + pasm()->addPtr(TrustedImm32(argcOnStackForCall), PlatformAssembler::StackPointerRegister); + argcOnStackForCall = 0; + } +} + +void Assembler::saveAccumulatorInFrame() +{ + pasm()->storeAccumulator(PlatformAssembler::Address(PlatformAssembler::JSStackFrameRegister, + offsetof(CallData, accumulator))); +} + +void Assembler::checkException() +{ + pasm()->addCatchyJump( + pasm()->branch32( + PlatformAssembler::NotEqual, + PlatformAssembler::Address(PlatformAssembler::EngineRegister, + offsetof(EngineBase, hasException)), + TrustedImm32(0))); +} + +void Assembler::gotoCatchException() +{ + pasm()->addCatchyJump(pasm()->jump()); +} + +void Assembler::getException() +{ + Q_STATIC_ASSERT_FOR_SANE_COMPILERS(sizeof(QV4::EngineBase::hasException) == 1); + + Address hasExceptionAddr(PlatformAssembler::EngineRegister, + offsetof(EngineBase, hasException)); + PlatformAssembler::Jump nope = pasm()->branch8(PlatformAssembler::Equal, + hasExceptionAddr, + TrustedImm32(0)); + pasm()->loadPtr(Address(PlatformAssembler::EngineRegister, + offsetof(EngineBase, exceptionValue)), + PlatformAssembler::ScratchRegister); + pasm()->loadAccumulator(Address(PlatformAssembler::ScratchRegister)); + pasm()->store8(TrustedImm32(0), hasExceptionAddr); + auto done = pasm()->jump(); + nope.link(pasm()); + pasm()->loadValue(Primitive::emptyValue().asReturnedValue()); + + done.link(pasm()); +} + +void Assembler::setException() +{ + Address addr(PlatformAssembler::EngineRegister, offsetof(EngineBase, exceptionValue)); + pasm()->loadPtr(addr, PlatformAssembler::ScratchRegister); + pasm()->storeAccumulator(Address(PlatformAssembler::ScratchRegister)); + addr.offset = offsetof(EngineBase, hasException); + Q_STATIC_ASSERT_FOR_SANE_COMPILERS(sizeof(QV4::EngineBase::hasException) == 1); + pasm()->store8(TrustedImm32(1), addr); +} + +void Assembler::setExceptionHandler(int offset) +{ + auto l = pasm()->storePtrWithPatch(TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress()); + pasm()->ehTargets.push_back({ l, offset }); +} + + +void Assembler::clearExceptionHandler() +{ + pasm()->storePtr(TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress()); +} + +void Assembler::pushCatchContext(int name, int reg) +{ + pasm()->copyReg(pasm()->contextAddress(), regAddr(reg)); + prepareCallWithArgCount(2); + passInt32AsArg(name, 1); + passRegAsArg(CallData::Context, 0); + IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_createCatchContext, ResultInAccumulator); + pasm()->storeAccumulator(pasm()->contextAddress()); +} + +void Assembler::popContext(int reg) +{ + pasm()->copyReg(regAddr(reg), pasm()->contextAddress()); +} + +void Assembler::ret() +{ + pasm()->generateFunctionExit(); +} + +} // JIT namespace +} // QV4 namepsace + +QT_END_NAMESPACE + +#endif // V4_ENABLE_JIT diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index 2ae4818814..eafe27c8bb 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** 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. @@ -36,6 +36,7 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ + #ifndef QV4ASSEMBLER_P_H #define QV4ASSEMBLER_P_H @@ -50,1802 +51,138 @@ // We mean it. // -#include "private/qv4global_p.h" -#include "private/qv4jsir_p.h" -#include "private/qv4isel_p.h" -#include "private/qv4isel_util_p.h" -#include "private/qv4value_p.h" -#include "private/qv4context_p.h" -#include "private/qv4engine_p.h" -#include "private/qv4writebarrier_p.h" -#include "qv4targetplatform_p.h" - -#include <config.h> -#include <wtf/Vector.h> - -#include <climits> - -#if ENABLE(ASSEMBLER) - -#include <assembler/MacroAssembler.h> -#include <assembler/MacroAssemblerCodeRef.h> +#include <private/qv4global_p.h> +#include <private/qv4function_p.h> +#include <QHash> QT_BEGIN_NAMESPACE namespace QV4 { namespace JIT { -struct CompilationUnit : public QV4::CompiledData::CompilationUnit -{ - virtual ~CompilationUnit(); - -#if !defined(V4_BOOTSTRAP) - void linkBackendToEngine(QV4::ExecutionEngine *engine) override; - bool memoryMapCode(QString *errorString) override; -#endif - void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) override; - bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) override; - - // Coderef + execution engine - - QVector<JSC::MacroAssemblerCodeRef> codeRefs; -}; - -template <typename PlatformAssembler, TargetOperatingSystemSpecialization Specialization> -struct AssemblerTargetConfiguration -{ - typedef JSC::MacroAssembler<PlatformAssembler> MacroAssembler; - typedef TargetPlatform<PlatformAssembler, Specialization> Platform; - // More things coming here in the future, such as Target OS -}; - -#if CPU(ARM_THUMB2) -typedef JSC::MacroAssemblerARMv7 DefaultPlatformMacroAssembler; -typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; -#elif CPU(ARM64) -typedef JSC::MacroAssemblerARM64 DefaultPlatformMacroAssembler; -typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; -#elif CPU(ARM_TRADITIONAL) -typedef JSC::MacroAssemblerARM DefaultPlatformMacroAssembler; -typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; -#elif CPU(MIPS) -typedef JSC::MacroAssemblerMIPS DefaultPlatformMacroAssembler; -typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; -#elif CPU(X86) -typedef JSC::MacroAssemblerX86 DefaultPlatformMacroAssembler; -typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; -#elif CPU(X86_64) -typedef JSC::MacroAssemblerX86_64 DefaultPlatformMacroAssembler; - -#if OS(WINDOWS) -typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, WindowsSpecialization> DefaultAssemblerTargetConfiguration; -#else -typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; -#endif - -#elif CPU(SH4) -typedef JSC::MacroAssemblerSH4 DefaultPlatformMacroAssembler; -typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; -#endif - -#define isel_stringIfyx(s) #s -#define isel_stringIfy(s) isel_stringIfyx(s) - -#define generateRuntimeCall(as, t, function, ...) \ - as->generateFunctionCallImp(Runtime::Method_##function##_NeedsExceptionCheck, t, "Runtime::" isel_stringIfy(function), typename JITAssembler::RuntimeCall(QV4::Runtime::function), __VA_ARGS__) - - -template <typename JITAssembler, typename MacroAssembler, typename TargetPlatform, int RegisterSize> -struct RegisterSizeDependentAssembler -{ -}; - -template <typename JITAssembler, typename MacroAssembler, typename TargetPlatform> -struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatform, 4> -{ - using RegisterID = typename JITAssembler::RegisterID; - using FPRegisterID = typename JITAssembler::FPRegisterID; - using RelationalCondition = typename JITAssembler::RelationalCondition; - using ResultCondition = typename JITAssembler::ResultCondition; - using Address = typename JITAssembler::Address; - using Pointer = typename JITAssembler::Pointer; - using TrustedImm32 = typename JITAssembler::TrustedImm32; - using TrustedImm64 = typename JITAssembler::TrustedImm64; - using Jump = typename JITAssembler::Jump; - using Label = typename JITAssembler::Label; - using ValueTypeInternal = Value::ValueTypeInternal_32; - using TargetPrimitive = TargetPrimitive32; - - static void emitSetGrayBit(JITAssembler *as, RegisterID base) - { - bool returnValueUsed = (base == TargetPlatform::ReturnValueRegister); - - as->push(TargetPlatform::EngineRegister); // free up one register for work - - RegisterID grayBitmap = returnValueUsed ? TargetPlatform::ScratchRegister : TargetPlatform::ReturnValueRegister; - as->move(base, grayBitmap); - Q_ASSERT(base != grayBitmap); - as->urshift32(TrustedImm32(Chunk::ChunkShift), grayBitmap); - as->lshift32(TrustedImm32(Chunk::ChunkShift), grayBitmap); - Q_STATIC_ASSERT(offsetof(Chunk, grayBitmap) == 0); - - RegisterID index = base; - as->move(base, index); - as->sub32(grayBitmap, index); - as->urshift32(TrustedImm32(Chunk::SlotSizeShift), index); - RegisterID grayIndex = TargetPlatform::EngineRegister; - as->move(index, grayIndex); - as->urshift32(TrustedImm32(Chunk::BitShift), grayIndex); - as->lshift32(TrustedImm32(2), grayIndex); // 4 bytes per quintptr - as->add32(grayIndex, grayBitmap); - as->and32(TrustedImm32(Chunk::Bits - 1), index); - - RegisterID bit = TargetPlatform::EngineRegister; - as->move(TrustedImm32(1), bit); - as->lshift32(index, bit); - - as->load32(Pointer(grayBitmap, 0), index); - as->or32(bit, index); - as->store32(index, Pointer(grayBitmap, 0)); - - as->pop(TargetPlatform::EngineRegister); - } - -#if WRITEBARRIER(none) - static Q_ALWAYS_INLINE void emitWriteBarrier(JITAssembler *, Address) {} -#endif - - static void loadDouble(JITAssembler *as, Address addr, FPRegisterID dest) - { - as->MacroAssembler::loadDouble(addr, dest); - } - - static void storeDouble(JITAssembler *as, FPRegisterID source, Address addr, WriteBarrier::Type barrier) - { - as->MacroAssembler::storeDouble(source, addr); - if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, addr); - } - - static void storeDouble(JITAssembler *as, FPRegisterID source, IR::Expr* target) - { - WriteBarrier::Type barrier; - Pointer ptr = as->loadAddressForWriting(TargetPlatform::ScratchRegister, target, &barrier); - as->storeDouble(source, ptr, barrier); - } - - static void storeValue(JITAssembler *as, TargetPrimitive value, Address destination, WriteBarrier::Type barrier) - { - as->store32(TrustedImm32(value.value()), destination); - destination.offset += 4; - as->store32(TrustedImm32(value.tag()), destination); - if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, destination); - } - - template <typename Source, typename Destination> - static void copyValueViaRegisters(JITAssembler *as, Source source, Destination destination, WriteBarrier::Type barrier) - { - as->loadDouble(source, TargetPlatform::FPGpr0); - // We need to pass NoBarrier to storeDouble and call emitWriteBarrier ourselves, as the - // code in storeDouble assumes the type we're storing is actually a double, something - // that isn't always the case here. - as->storeDouble(TargetPlatform::FPGpr0, destination, WriteBarrier::NoBarrier); - if (WriteBarrier::isRequired<WriteBarrier::Unknown>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, destination); - } - - static void loadDoubleConstant(JITAssembler *as, IR::Const *c, FPRegisterID target) - { - as->MacroAssembler::loadDouble(as->loadConstant(c, TargetPlatform::ScratchRegister), target); - } - - static void storeReturnValue(JITAssembler *as, FPRegisterID dest) - { - as->moveIntsToDouble(TargetPlatform::LowReturnValueRegister, TargetPlatform::HighReturnValueRegister, dest, TargetPlatform::FPGpr0); - } - - static void storeReturnValue(JITAssembler *as, const Pointer &dest, WriteBarrier::Type barrier) - { - Address destination = dest; - as->store32(TargetPlatform::LowReturnValueRegister, destination); - destination.offset += 4; - as->store32(TargetPlatform::HighReturnValueRegister, destination); - if (WriteBarrier::isRequired<WriteBarrier::Unknown>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, dest); - } - - static void setFunctionReturnValueFromTemp(JITAssembler *as, IR::Temp *t) - { - const auto lowReg = TargetPlatform::LowReturnValueRegister; - const auto highReg = TargetPlatform::HighReturnValueRegister; - - if (t->kind == IR::Temp::PhysicalRegister) { - switch (t->type) { - case IR::DoubleType: - as->moveDoubleToInts((FPRegisterID) t->index, lowReg, highReg); - break; - case IR::UInt32Type: { - RegisterID srcReg = (RegisterID) t->index; - Jump intRange = as->branch32(JITAssembler::GreaterThanOrEqual, srcReg, TrustedImm32(0)); - as->convertUInt32ToDouble(srcReg, TargetPlatform::FPGpr0, TargetPlatform::ReturnValueRegister); - as->moveDoubleToInts(TargetPlatform::FPGpr0, lowReg, highReg); - Jump done = as->jump(); - intRange.link(as); - as->move(srcReg, lowReg); - as->move(TrustedImm32(quint32(QV4::Value::ValueTypeInternal_32::Integer)), highReg); - done.link(as); - } break; - case IR::SInt32Type: - as->move((RegisterID) t->index, lowReg); - as->move(TrustedImm32(quint32(QV4::Value::ValueTypeInternal_32::Integer)), highReg); - break; - case IR::BoolType: - as->move((RegisterID) t->index, lowReg); - as->move(TrustedImm32(quint32(QV4::Value::ValueTypeInternal_32::Boolean)), highReg); - break; - default: - Q_UNREACHABLE(); - } - } else { - Pointer addr = as->loadAddressForReading(TargetPlatform::ScratchRegister, t); - as->load32(addr, lowReg); - addr.offset += 4; - as->load32(addr, highReg); - } - } - - static void setFunctionReturnValueFromConst(JITAssembler *as, TargetPrimitive retVal) - { - as->move(TrustedImm32(retVal.value()), TargetPlatform::LowReturnValueRegister); - as->move(TrustedImm32(retVal.tag()), TargetPlatform::HighReturnValueRegister); - } - - static void loadArgumentInRegister(JITAssembler *as, IR::Temp* temp, RegisterID dest, int argumentNumber) - { - Q_UNUSED(as); - Q_UNUSED(temp); - Q_UNUSED(dest); - Q_UNUSED(argumentNumber); - } - - static void loadArgumentInRegister(JITAssembler *as, IR::ArgLocal* al, RegisterID dest, int argumentNumber) - { - Q_UNUSED(as); - Q_UNUSED(al); - Q_UNUSED(dest); - Q_UNUSED(argumentNumber); - } - - static void loadArgumentInRegister(JITAssembler *as, IR::Const* c, RegisterID dest, int argumentNumber) - { - Q_UNUSED(as); - Q_UNUSED(c); - Q_UNUSED(dest); - Q_UNUSED(argumentNumber); - } - - static void loadArgumentInRegister(JITAssembler *as, IR::Expr* expr, RegisterID dest, int argumentNumber) - { - Q_UNUSED(as); - Q_UNUSED(expr); - Q_UNUSED(dest); - Q_UNUSED(argumentNumber); - } - - static void zeroRegister(JITAssembler *as, RegisterID reg) - { - as->move(TrustedImm32(0), reg); - } - - static void zeroStackSlot(JITAssembler *as, int slot) - { - as->poke(TrustedImm32(0), slot); - } - - static void generateCJumpOnUndefined(JITAssembler *as, - RelationalCondition cond, IR::Expr *right, - RegisterID scratchRegister, RegisterID tagRegister, - IR::BasicBlock *nextBlock, IR::BasicBlock *currentBlock, - IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) - { - Pointer tagAddr = as->loadAddressForReading(scratchRegister, right); - as->load32(tagAddr, tagRegister); - Jump j = as->branch32(JITAssembler::invert(cond), tagRegister, TrustedImm32(0)); - as->addPatch(falseBlock, j); - - tagAddr.offset += 4; - as->load32(tagAddr, tagRegister); - const TrustedImm32 tag(QV4::Value::Managed_Type_Internal); - Q_ASSERT(nextBlock == as->nextBlock()); - Q_UNUSED(nextBlock); - as->generateCJumpOnCompare(cond, tagRegister, tag, currentBlock, trueBlock, falseBlock); - } - - static void convertVarToSInt32(JITAssembler *as, IR::Expr *source, IR::Expr *target) - { - Q_ASSERT(source->type == IR::VarType); - // load the tag: - Pointer addr = as->loadAddressForReading(TargetPlatform::ScratchRegister, source); - Pointer tagAddr = addr; - tagAddr.offset += 4; - as->load32(tagAddr, TargetPlatform::ReturnValueRegister); - - // check if it's an int32: - Jump fallback = as->branch32(RelationalCondition::NotEqual, TargetPlatform::ReturnValueRegister, - TrustedImm32(quint32(Value::ValueTypeInternal_32::Integer))); - IR::Temp *targetTemp = target->asTemp(); - if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { - as->load32(addr, TargetPlatform::ReturnValueRegister); - WriteBarrier::Type barrier; - Pointer targetAddr = as->loadAddressForWriting(TargetPlatform::ScratchRegister, target, &barrier); - as->store32(TargetPlatform::ReturnValueRegister, targetAddr); - targetAddr.offset += 4; - as->store32(TrustedImm32(quint32(Value::ValueTypeInternal_32::Integer)), targetAddr); - if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, targetAddr); - } else { - as->load32(addr, (RegisterID) targetTemp->index); - } - Jump intDone = as->jump(); - - // not an int: - fallback.link(as); - generateRuntimeCall(as, TargetPlatform::ReturnValueRegister, toInt, - as->loadAddressForReading(TargetPlatform::ScratchRegister, source)); - as->storeInt32(TargetPlatform::ReturnValueRegister, target); - - intDone.link(as); - } - - static void loadManagedPointer(JITAssembler *as, RegisterID registerWithPtr, Pointer destAddr, WriteBarrier::Type barrier) - { - as->store32(registerWithPtr, destAddr); - destAddr.offset += 4; - as->store32(TrustedImm32(QV4::Value::Managed_Type_Internal_32), destAddr); - if (WriteBarrier::isRequired<WriteBarrier::Object>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, destAddr); - } - - static Jump generateIsDoubleCheck(JITAssembler *as, RegisterID tagOrValueRegister) - { - as->and32(TrustedImm32(Value::NotDouble_Mask), tagOrValueRegister); - return as->branch32(RelationalCondition::NotEqual, tagOrValueRegister, - TrustedImm32(Value::NotDouble_Mask)); - } - - static void initializeLocalVariables(JITAssembler *as, int localsCount) - { - as->move(TrustedImm32(0), TargetPlatform::ReturnValueRegister); - as->move(TrustedImm32(localsCount), TargetPlatform::ScratchRegister); - Label loop = as->label(); - as->store32(TargetPlatform::ReturnValueRegister, Address(TargetPlatform::LocalsRegister)); - as->add32(TrustedImm32(4), TargetPlatform::LocalsRegister); - as->store32(TargetPlatform::ReturnValueRegister, Address(TargetPlatform::LocalsRegister)); - as->add32(TrustedImm32(4), TargetPlatform::LocalsRegister); - Jump jump = as->branchSub32(ResultCondition::NonZero, TrustedImm32(1), TargetPlatform::ScratchRegister); - jump.linkTo(loop, as); - } - - static Jump checkIfTagRegisterIsDouble(JITAssembler *as, RegisterID tagRegister) - { - as->and32(TrustedImm32(Value::NotDouble_Mask), tagRegister); - Jump isNoDbl = as->branch32(RelationalCondition::Equal, tagRegister, TrustedImm32(Value::NotDouble_Mask)); - return isNoDbl; - } -}; - -template <typename JITAssembler, typename MacroAssembler, typename TargetPlatform> -struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatform, 8> -{ - using RegisterID = typename JITAssembler::RegisterID; - using FPRegisterID = typename JITAssembler::FPRegisterID; - using Address = typename JITAssembler::Address; - using TrustedImm32 = typename JITAssembler::TrustedImm32; - using TrustedImm64 = typename JITAssembler::TrustedImm64; - using Pointer = typename JITAssembler::Pointer; - using RelationalCondition = typename JITAssembler::RelationalCondition; - using ResultCondition = typename JITAssembler::ResultCondition; - using BranchTruncateType = typename JITAssembler::BranchTruncateType; - using Jump = typename JITAssembler::Jump; - using Label = typename JITAssembler::Label; - using ValueTypeInternal = Value::ValueTypeInternal_64; - using TargetPrimitive = TargetPrimitive64; - - static void emitSetGrayBit(JITAssembler *as, RegisterID base) - { - bool returnValueUsed = (base == TargetPlatform::ReturnValueRegister); - - as->push(TargetPlatform::EngineRegister); // free up one register for work - - RegisterID grayBitmap = returnValueUsed ? TargetPlatform::ScratchRegister : TargetPlatform::ReturnValueRegister; - as->move(base, grayBitmap); - Q_ASSERT(base != grayBitmap); - as->urshift64(TrustedImm32(Chunk::ChunkShift), grayBitmap); - as->lshift64(TrustedImm32(Chunk::ChunkShift), grayBitmap); - Q_STATIC_ASSERT(offsetof(Chunk, grayBitmap) == 0); - - RegisterID index = base; - as->move(base, index); - as->sub64(grayBitmap, index); - as->urshift64(TrustedImm32(Chunk::SlotSizeShift), index); - RegisterID grayIndex = TargetPlatform::EngineRegister; - as->move(index, grayIndex); - as->urshift64(TrustedImm32(Chunk::BitShift), grayIndex); - as->lshift64(TrustedImm32(3), grayIndex); // 8 bytes per quintptr - as->add64(grayIndex, grayBitmap); - as->and64(TrustedImm32(Chunk::Bits - 1), index); - - RegisterID bit = TargetPlatform::EngineRegister; - as->move(TrustedImm32(1), bit); - as->lshift64(index, bit); - - as->load64(Pointer(grayBitmap, 0), index); - as->or64(bit, index); - as->store64(index, Pointer(grayBitmap, 0)); - - as->pop(TargetPlatform::EngineRegister); - } +#define JIT_STRINGIFYx(s) #s +#define JIT_STRINGIFY(s) JIT_STRINGIFYx(s) -#if WRITEBARRIER(none) - static Q_ALWAYS_INLINE void emitWriteBarrier(JITAssembler *, Address) {} -#endif - - static void loadDouble(JITAssembler *as, Address addr, FPRegisterID dest) - { - as->load64(addr, TargetPlatform::ReturnValueRegister); - as->xor64(TargetPlatform::DoubleMaskRegister, TargetPlatform::ReturnValueRegister); - as->move64ToDouble(TargetPlatform::ReturnValueRegister, dest); - } - - static void storeDouble(JITAssembler *as, FPRegisterID source, Address addr, WriteBarrier::Type barrier) - { - as->moveDoubleTo64(source, TargetPlatform::ReturnValueRegister); - as->xor64(TargetPlatform::DoubleMaskRegister, TargetPlatform::ReturnValueRegister); - as->store64(TargetPlatform::ReturnValueRegister, addr); - if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, addr); - } - - static void storeDouble(JITAssembler *as, FPRegisterID source, IR::Expr* target) - { - as->moveDoubleTo64(source, TargetPlatform::ReturnValueRegister); - as->xor64(TargetPlatform::DoubleMaskRegister, TargetPlatform::ReturnValueRegister); - WriteBarrier::Type barrier; - Pointer ptr = as->loadAddressForWriting(TargetPlatform::ScratchRegister, target, &barrier); - as->store64(TargetPlatform::ReturnValueRegister, ptr); - if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, ptr); - } - - static void storeReturnValue(JITAssembler *as, FPRegisterID dest) - { - as->xor64(TargetPlatform::DoubleMaskRegister, TargetPlatform::ReturnValueRegister); - as->move64ToDouble(TargetPlatform::ReturnValueRegister, dest); - } - - static void storeReturnValue(JITAssembler *as, const Pointer &dest, WriteBarrier::Type barrier) - { - as->store64(TargetPlatform::ReturnValueRegister, dest); - if (WriteBarrier::isRequired<WriteBarrier::Unknown>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, dest); - } - - static void setFunctionReturnValueFromTemp(JITAssembler *as, IR::Temp *t) - { - if (t->kind == IR::Temp::PhysicalRegister) { - if (t->type == IR::DoubleType) { - as->moveDoubleTo64((FPRegisterID) t->index, - TargetPlatform::ReturnValueRegister); - as->xor64(TargetPlatform::DoubleMaskRegister, TargetPlatform::ReturnValueRegister); - } else if (t->type == IR::UInt32Type) { - RegisterID srcReg = (RegisterID) t->index; - Jump intRange = as->branch32(RelationalCondition::GreaterThanOrEqual, srcReg, TrustedImm32(0)); - as->convertUInt32ToDouble(srcReg, TargetPlatform::FPGpr0, TargetPlatform::ReturnValueRegister); - as->moveDoubleTo64(TargetPlatform::FPGpr0, TargetPlatform::ReturnValueRegister); - as->xor64(TargetPlatform::DoubleMaskRegister, TargetPlatform::ReturnValueRegister); - Jump done = as->jump(); - intRange.link(as); - as->zeroExtend32ToPtr(srcReg, TargetPlatform::ReturnValueRegister); - quint64 tag = quint64(QV4::Value::ValueTypeInternal_64::Integer); - as->or64(TrustedImm64(tag << 32), - TargetPlatform::ReturnValueRegister); - done.link(as); - } else { - as->zeroExtend32ToPtr((RegisterID) t->index, TargetPlatform::ReturnValueRegister); - quint64 tag; - switch (t->type) { - case IR::SInt32Type: - tag = quint64(QV4::Value::ValueTypeInternal_64::Integer); - break; - case IR::BoolType: - tag = quint64(QV4::Value::ValueTypeInternal_64::Boolean); - break; - default: - tag = 31337; // bogus value - Q_UNREACHABLE(); - } - as->or64(TrustedImm64(tag << 32), - TargetPlatform::ReturnValueRegister); - } - } else { - as->copyValue(TargetPlatform::ReturnValueRegister, t, WriteBarrier::NoBarrier); - } - } - - static void setFunctionReturnValueFromConst(JITAssembler *as, TargetPrimitive retVal) - { - as->move(TrustedImm64(retVal.rawValue()), TargetPlatform::ReturnValueRegister); - } - - static void storeValue(JITAssembler *as, TargetPrimitive value, Address destination, WriteBarrier::Type barrier) - { - as->store64(TrustedImm64(value.rawValue()), destination); - if (WriteBarrier::isRequired<WriteBarrier::Unknown>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, destination); - } - - template <typename Source, typename Destination> - static void copyValueViaRegisters(JITAssembler *as, Source source, Destination destination, WriteBarrier::Type barrier) - { - // Use ReturnValueRegister as "scratch" register because loadArgument - // and storeArgument are functions that may need a scratch register themselves. - loadArgumentInRegister(as, source, TargetPlatform::ReturnValueRegister, 0); - as->storeReturnValue(destination, barrier); - } - - static void loadDoubleConstant(JITAssembler *as, IR::Const *c, FPRegisterID target) - { - Q_STATIC_ASSERT(sizeof(int64_t) == sizeof(double)); - int64_t i; - memcpy(&i, &c->value, sizeof(double)); - as->move(TrustedImm64(i), TargetPlatform::ReturnValueRegister); - as->move64ToDouble(TargetPlatform::ReturnValueRegister, target); - } - - static void loadArgumentInRegister(JITAssembler *as, Address addressOfValue, RegisterID dest, int argumentNumber) - { - Q_UNUSED(argumentNumber); - as->load64(addressOfValue, dest); - } - - static void loadArgumentInRegister(JITAssembler *as, IR::Temp* temp, RegisterID dest, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - if (temp) { - Pointer addr = as->loadTempAddress(temp); - as->load64(addr, dest); - } else { - auto undefined = TargetPrimitive::undefinedValue(); - as->move(TrustedImm64(undefined.rawValue()), dest); - } - } - - static void loadArgumentInRegister(JITAssembler *as, IR::ArgLocal* al, RegisterID dest, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - if (al) { - Pointer addr = as->loadArgLocalAddressForReading(dest, al); - as->load64(addr, dest); - } else { - auto undefined = TargetPrimitive::undefinedValue(); - as->move(TrustedImm64(undefined.rawValue()), dest); - } - } - - static void loadArgumentInRegister(JITAssembler *as, IR::Const* c, RegisterID dest, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - auto v = convertToValue<TargetPrimitive64>(c); - as->move(TrustedImm64(v.rawValue()), dest); - } - - static void loadArgumentInRegister(JITAssembler *as, IR::Expr* expr, RegisterID dest, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - if (!expr) { - auto undefined = TargetPrimitive::undefinedValue(); - as->move(TrustedImm64(undefined.rawValue()), dest); - } else if (IR::Temp *t = expr->asTemp()){ - loadArgumentInRegister(as, t, dest, argumentNumber); - } else if (IR::ArgLocal *al = expr->asArgLocal()) { - loadArgumentInRegister(as, al, dest, argumentNumber); - } else if (IR::Const *c = expr->asConst()) { - loadArgumentInRegister(as, c, dest, argumentNumber); - } else { - Q_ASSERT(!"unimplemented expression type in loadArgument"); - } - } - - static void zeroRegister(JITAssembler *as, RegisterID reg) - { - as->move(TrustedImm64(0), reg); - } - - static void zeroStackSlot(JITAssembler *as, int slot) - { - as->store64(TrustedImm64(0), as->addressForPoke(slot)); - } - - static void generateCJumpOnCompare(JITAssembler *as, - RelationalCondition cond, - RegisterID left, - TrustedImm64 right, - IR::BasicBlock *nextBlock, - IR::BasicBlock *currentBlock, - IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) - { - if (trueBlock == nextBlock) { - Jump target = as->branch64(as->invert(cond), left, right); - as->addPatch(falseBlock, target); - } else { - Jump target = as->branch64(cond, left, right); - as->addPatch(trueBlock, target); - as->jumpToBlock(currentBlock, falseBlock); - } - } - - static void generateCJumpOnUndefined(JITAssembler *as, - RelationalCondition cond, IR::Expr *right, - RegisterID scratchRegister, RegisterID tagRegister, - IR::BasicBlock *nextBlock, IR::BasicBlock *currentBlock, - IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) - { - Pointer addr = as->loadAddressForReading(scratchRegister, right); - as->load64(addr, tagRegister); - const TrustedImm64 tag(0); - generateCJumpOnCompare(as, cond, tagRegister, tag, nextBlock, currentBlock, trueBlock, falseBlock); - } - - static void convertVarToSInt32(JITAssembler *as, IR::Expr *source, IR::Expr *target) - { - Q_ASSERT(source->type == IR::VarType); - Pointer addr = as->loadAddressForReading(TargetPlatform::ScratchRegister, source); - as->load64(addr, TargetPlatform::ScratchRegister); - as->move(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); - - // check if it's integer convertible - as->urshift64(TrustedImm32(QV4::Value::IsIntegerConvertible_Shift), TargetPlatform::ScratchRegister); - Jump isIntConvertible = as->branch32(RelationalCondition::Equal, TargetPlatform::ScratchRegister, TrustedImm32(3)); - - // nope, not integer convertible, so check for a double: - as->urshift64(TrustedImm32( - QV4::Value::IsDoubleTag_Shift - QV4::Value::IsIntegerConvertible_Shift), - TargetPlatform::ScratchRegister); - Jump fallback = as->branch32(RelationalCondition::GreaterThan, TargetPlatform::ScratchRegister, TrustedImm32(0)); - - // it's a double - as->xor64(TargetPlatform::DoubleMaskRegister, TargetPlatform::ReturnValueRegister); - as->move64ToDouble(TargetPlatform::ReturnValueRegister, TargetPlatform::FPGpr0); - Jump success = - as->branchTruncateDoubleToInt32(TargetPlatform::FPGpr0, TargetPlatform::ReturnValueRegister, - BranchTruncateType::BranchIfTruncateSuccessful); - - // not an int: - fallback.link(as); - generateRuntimeCall(as, TargetPlatform::ReturnValueRegister, toInt, - as->loadAddressForReading(TargetPlatform::ScratchRegister, source)); - - - isIntConvertible.link(as); - success.link(as); - IR::Temp *targetTemp = target->asTemp(); - if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { - WriteBarrier::Type barrier; - Pointer targetAddr = as->loadAddressForWriting(TargetPlatform::ScratchRegister, target, &barrier); - as->store32(TargetPlatform::ReturnValueRegister, targetAddr); - targetAddr.offset += 4; - as->store32(TrustedImm32(quint32(Value::ValueTypeInternal_64::Integer)), targetAddr); - if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, targetAddr); - } else { - as->storeInt32(TargetPlatform::ReturnValueRegister, target); - } - } - - static void loadManagedPointer(JITAssembler *as, RegisterID registerWithPtr, Pointer destAddr, WriteBarrier::Type barrier) - { - as->store64(registerWithPtr, destAddr); - if (WriteBarrier::isRequired<WriteBarrier::Object>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, destAddr); - } - - static Jump generateIsDoubleCheck(JITAssembler *as, RegisterID tagOrValueRegister) - { - as->rshift32(TrustedImm32(Value::IsDoubleTag_Shift), tagOrValueRegister); - return as->branch32(RelationalCondition::NotEqual, tagOrValueRegister, - TrustedImm32(0)); - } - - static void initializeLocalVariables(JITAssembler *as, int localsCount) - { - as->move(TrustedImm64(0), TargetPlatform::ReturnValueRegister); - as->move(TrustedImm32(localsCount), TargetPlatform::ScratchRegister); - Label loop = as->label(); - as->store64(TargetPlatform::ReturnValueRegister, Address(TargetPlatform::LocalsRegister)); - as->add64(TrustedImm32(8), TargetPlatform::LocalsRegister); - Jump jump = as->branchSub32(ResultCondition::NonZero, TrustedImm32(1), TargetPlatform::ScratchRegister); - jump.linkTo(loop, as); - } - - static Jump checkIfTagRegisterIsDouble(JITAssembler *as, RegisterID tagRegister) - { - as->rshift32(TrustedImm32(Value::IsDoubleTag_Shift), tagRegister); - Jump isNoDbl = as->branch32(RelationalCondition::Equal, tagRegister, TrustedImm32(0)); - return isNoDbl; - } -}; - -template <typename TargetConfiguration> -class Assembler : public TargetConfiguration::MacroAssembler, public TargetConfiguration::Platform -{ - Q_DISABLE_COPY(Assembler) +#define IN_JIT_GENERATE_RUNTIME_CALL(function, destination) \ + callRuntime(JIT_STRINGIFY(function), \ + reinterpret_cast<void *>(&function), \ + destination) +#define JIT_GENERATE_RUNTIME_CALL(function, destination) \ + as->IN_JIT_GENERATE_RUNTIME_CALL(function, destination) +class Assembler { public: - Assembler(QV4::Compiler::JSUnitGenerator *jsGenerator, IR::Function* function, QV4::ExecutableAllocator *executableAllocator); - - using MacroAssembler = typename TargetConfiguration::MacroAssembler; - using RegisterID = typename MacroAssembler::RegisterID; - using FPRegisterID = typename MacroAssembler::FPRegisterID; - using Address = typename MacroAssembler::Address; - using Label = typename MacroAssembler::Label; - using Jump = typename MacroAssembler::Jump; - using DataLabelPtr = typename MacroAssembler::DataLabelPtr; - using TrustedImm32 = typename MacroAssembler::TrustedImm32; - using TrustedImm64 = typename MacroAssembler::TrustedImm64; - using TrustedImmPtr = typename MacroAssembler::TrustedImmPtr; - using RelationalCondition = typename MacroAssembler::RelationalCondition; - using typename MacroAssembler::DoubleCondition; - using MacroAssembler::label; - using MacroAssembler::move; - using MacroAssembler::jump; - using MacroAssembler::add32; - using MacroAssembler::and32; - using MacroAssembler::store32; - using MacroAssembler::loadPtr; - using MacroAssembler::load32; - using MacroAssembler::branch32; - using MacroAssembler::subDouble; - using MacroAssembler::subPtr; - using MacroAssembler::addPtr; - using MacroAssembler::call; - using MacroAssembler::poke; - using MacroAssembler::branchTruncateDoubleToUint32; - using MacroAssembler::or32; - using MacroAssembler::moveDouble; - using MacroAssembler::convertUInt32ToDouble; - using MacroAssembler::invert; - using MacroAssembler::convertInt32ToDouble; - using MacroAssembler::rshift32; - using MacroAssembler::storePtr; - using MacroAssembler::ret; - - using JITTargetPlatform = typename TargetConfiguration::Platform; - using JITTargetPlatform::RegisterArgumentCount; - using JITTargetPlatform::StackSpaceAllocatedUponFunctionEntry; - using JITTargetPlatform::RegisterSize; - using JITTargetPlatform::StackAlignment; - using JITTargetPlatform::ReturnValueRegister; - using JITTargetPlatform::StackPointerRegister; - using JITTargetPlatform::ScratchRegister; - using JITTargetPlatform::EngineRegister; - using JITTargetPlatform::StackShadowSpace; - using JITTargetPlatform::registerForArgument; - using JITTargetPlatform::FPGpr0; - using JITTargetPlatform::platformEnterStandardStackFrame; - using JITTargetPlatform::platformFinishEnteringStandardStackFrame; - using JITTargetPlatform::platformLeaveStandardStackFrame; - - static qint32 targetStructureOffset(qint32 hostOffset) - { - Q_ASSERT(hostOffset % QT_POINTER_SIZE == 0); - return (hostOffset * RegisterSize) / QT_POINTER_SIZE; - } - - struct LookupCall { - Address addr; - uint getterSetterOffset; - - LookupCall(const Address &addr, uint getterSetterOffset) - : addr(addr) - , getterSetterOffset(getterSetterOffset) - {} - }; - - struct RuntimeCall { - Address addr; - - inline RuntimeCall(Runtime::RuntimeMethods method = Runtime::InvalidRuntimeMethod); - bool isValid() const { return addr.offset >= 0; } - }; - - // Explicit type to allow distinguishing between - // pushing an address itself or the value it points - // to onto the stack when calling functions. - struct Pointer : public Address - { - explicit Pointer(const Address& addr) - : Address(addr) - {} - explicit Pointer(RegisterID reg, int32_t offset) - : Address(reg, offset) - {} - }; - - using RegisterSizeDependentOps = RegisterSizeDependentAssembler<Assembler<TargetConfiguration>, MacroAssembler, JITTargetPlatform, RegisterSize>; - using ValueTypeInternal = typename RegisterSizeDependentOps::ValueTypeInternal; - using TargetPrimitive = typename RegisterSizeDependentOps::TargetPrimitive; - - // V4 uses two stacks: one stack with QV4::Value items, which is checked by the garbage - // collector, and one stack used by the native C/C++/ABI code. This C++ stack is not scanned - // by the garbage collector, so if any JS object needs to be retained, it should be put on the - // JS stack. - // - // The "saved reg arg X" are on the C++ stack is used to store values in registers that need to - // be passed by reference to native functions. It is fine to use the C++ stack, because only - // non-object values can be stored in registers. - // - // Stack layout for the C++ stack: - // return address - // old FP <- FP - // callee saved reg n - // ... - // callee saved reg 0 - // saved reg arg n - // ... - // saved reg arg 0 <- SP - // - // Stack layout for the JS stack: - // function call argument n <- LocalsRegister - // ... - // function call argument 0 - // local 0 - // ... - // local n - class StackLayout - { - public: - StackLayout(IR::Function *function, int maxArgCountForBuiltins, int normalRegistersToSave, int fpRegistersToSave) - : normalRegistersToSave(normalRegistersToSave) - , fpRegistersToSave(fpRegistersToSave) - , maxOutgoingArgumentCount(function->maxNumberOfArguments) - , localCount(function->tempCount) - , savedRegCount(maxArgCountForBuiltins) - { -#if 0 // debug code - qDebug("calleeSavedRegCount.....: %d",calleeSavedRegCount); - qDebug("maxOutgoingArgumentCount: %d",maxOutgoingArgumentCount); - qDebug("localCount..............: %d",localCount); - qDebug("savedConstCount.........: %d",savedRegCount); - for (int i = 0; i < maxOutgoingArgumentCount; ++i) - qDebug("argumentAddressForCall(%d) = 0x%x / -0x%x", i, - argumentAddressForCall(i).offset, -argumentAddressForCall(i).offset); - for (int i = 0; i < localCount; ++i) - qDebug("local(%d) = 0x%x / -0x%x", i, stackSlotPointer(i).offset, - -stackSlotPointer(i).offset); - qDebug("savedReg(0) = 0x%x / -0x%x", savedRegPointer(0).offset, -savedRegPointer(0).offset); - qDebug("savedReg(1) = 0x%x / -0x%x", savedRegPointer(1).offset, -savedRegPointer(1).offset); - qDebug("savedReg(2) = 0x%x / -0x%x", savedRegPointer(2).offset, -savedRegPointer(2).offset); - qDebug("savedReg(3) = 0x%x / -0x%x", savedRegPointer(3).offset, -savedRegPointer(3).offset); - qDebug("savedReg(4) = 0x%x / -0x%x", savedRegPointer(4).offset, -savedRegPointer(4).offset); - qDebug("savedReg(5) = 0x%x / -0x%x", savedRegPointer(5).offset, -savedRegPointer(5).offset); - - qDebug("callDataAddress(0) = 0x%x", callDataAddress(0).offset); -#endif - } - - int calculateStackFrameSize() const - { - // sp was aligned before executing the call instruction. So, calculate all contents - // that were saved after that aligned stack...: - const int stackSpaceAllocatedOtherwise = StackSpaceAllocatedUponFunctionEntry - + RegisterSize; // saved FramePointerRegister - - // ... then calculate the stuff we want to store ...: - int frameSize = RegisterSize * normalRegistersToSave + sizeof(double) * fpRegistersToSave; - frameSize += savedRegCount * sizeof(QV4::Value); // (these get written out as Values, not as native registers) - - Q_ASSERT(frameSize + stackSpaceAllocatedOtherwise < INT_MAX); - // .. then align that chunk ..: - frameSize = static_cast<int>(WTF::roundUpToMultipleOf(StackAlignment, frameSize + stackSpaceAllocatedOtherwise)); - // ... which now holds our frame size + the extra stuff that was pushed due to the call. - // So subtract that extra stuff, and we have our frame size: - frameSize -= stackSpaceAllocatedOtherwise; - - return frameSize; - } - - /// \return the stack frame size in number of Value items. - int calculateJSStackFrameSize() const - { - return (localCount + sizeof(QV4::CallData)/sizeof(QV4::Value) - 1 + maxOutgoingArgumentCount) + 1; - } - - Address stackSlotPointer(int idx) const - { - Q_ASSERT(idx >= 0); - Q_ASSERT(idx < localCount); - - Pointer addr = callDataAddress(0); - addr.offset -= sizeof(QV4::Value) * (idx + 1); - return addr; - } - - // Some run-time functions take (Value* args, int argc). This function is for populating - // the args. - Pointer argumentAddressForCall(int argument) const - { - Q_ASSERT(argument >= 0); - Q_ASSERT(argument < maxOutgoingArgumentCount); - - const int index = maxOutgoingArgumentCount - argument; - return Pointer(Assembler::LocalsRegister, sizeof(QV4::Value) * (-index)); - } - - Pointer callDataAddress(int offset = 0) const { - return Pointer(Assembler::LocalsRegister, offset - (sizeof(QV4::CallData) + sizeof(QV4::Value) * (maxOutgoingArgumentCount - 1))); - } - - Address savedRegPointer(int offset) const - { - Q_ASSERT(offset >= 0); - Q_ASSERT(offset < savedRegCount); - - // Get the address of the bottom-most element of our frame: - Address ptr(Assembler::FramePointerRegister, -calculateStackFrameSize()); - // This now is the element with offset 0. So: - ptr.offset += offset * sizeof(QV4::Value); - // and we're done! - return ptr; - } - - private: - int normalRegistersToSave; - int fpRegistersToSave; - - /// arg count for calls to JS functions - int maxOutgoingArgumentCount; - - /// the number of spill slots needed by this function - int localCount; - - /// used by built-ins to save arguments (e.g. constants) to the stack when they need to be - /// passed by reference. - int savedRegCount; + enum CallResultDestination { + IgnoreResult, + ResultInAccumulator, }; - struct VoidType { VoidType() {} }; - static const VoidType Void; + Assembler(const Value* constantTable); + ~Assembler(); + + // codegen infrastructure + void generatePrologue(); + void generateEpilogue(); + void link(Function *function); + void addLabel(int offset); + + // loads/stores/moves + void loadConst(int constIndex); + void copyConst(int constIndex, int destReg); + void loadReg(int reg); + void storeReg(int reg); + void loadString(int stringId); + void loadValue(ReturnedValue value); + + // numeric ops + void unot(); + void toNumber(); + void uminus(); + void ucompl(); + void inc(); + void dec(); + void add(int lhs); + void bitAnd(int lhs); + void bitOr(int lhs); + void bitXor(int lhs); + void ushr(int lhs); + void shr(int lhs); + void shl(int lhs); + void bitAndConst(int rhs); + void bitOrConst(int rhs); + void bitXorConst(int rhs); + void ushrConst(int rhs); + void shrConst(int rhs); + void shlConst(int rhs); + void mul(int lhs); + void div(int lhs); + void mod(int lhs); + void sub(int lhs); + + // comparissons + void cmpeqNull(); + void cmpneNull(); + void cmpeqInt(int lhs); + void cmpneInt(int lhs); + void cmpeq(int lhs); + void cmpne(int lhs); + void cmpgt(int lhs); + void cmpge(int lhs); + void cmplt(int lhs); + void cmple(int lhs); + void cmpStrictEqual(int lhs); + void cmpStrictNotEqual(int lhs); + + // jumps + void jump(int offset); + void jumpTrue(int offset); + void jumpFalse(int offset); + void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset); + void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset); + + // stuff for runtime calls + void prepareCallWithArgCount(int argc); + void storeInstructionPointer(int instructionOffset); + void passAccumulatorAsArg(int arg); + void passFunctionAsArg(int arg); + void passEngineAsArg(int arg); + void passRegAsArg(int reg, int arg); + void passCppFrameAsArg(int arg); + void passInt32AsArg(int value, int arg); + void callRuntime(const char *functionName, const void *funcPtr, Assembler::CallResultDestination dest); + void saveAccumulatorInFrame(); + + // exception/context stuff + void checkException(); + void gotoCatchException(); + void getException(); + void setException(); + void setExceptionHandler(int offset); + void clearExceptionHandler(); + void pushCatchContext(int name, int reg); + void popContext(int reg); + + // other stuff + void ret(); + +protected: + void *d; - typedef JSC::FunctionPtr FunctionPtr; - -#ifndef QT_NO_DEBUG - struct CallInfo { - Label label; - const char* functionName; - }; -#endif - struct PointerToValue { - PointerToValue(IR::Expr *value) - : value(value) - {} - IR::Expr *value; - }; - struct StringToIndex { - explicit StringToIndex(const QString &string) : string(string) {} - QString string; - }; - struct Reference { - Reference(IR::Expr *value) : value(value) { - Q_ASSERT(value->asTemp() || value->asArgLocal()); - } - IR::Expr *value; - }; - - void callAbsolute(const char* /*functionName*/, const LookupCall &lookupCall) - { - call(lookupCall.addr); - } - - void callAbsolute(const char *functionName, const RuntimeCall &runtimeCall) - { - call(runtimeCall.addr); #ifndef QT_NO_DEBUG - // the code below is to get proper function names in the disassembly - CallInfo info; - info.functionName = functionName; - info.label = label(); - _callInfos.append(info); -#else - Q_UNUSED(functionName) + enum { NoCall = -1 }; + int remainingArgcForCall = NoCall; #endif - } - - void registerBlock(IR::BasicBlock*, IR::BasicBlock *nextBlock); - IR::BasicBlock *nextBlock() const { return _nextBlock; } - void jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target); - void addPatch(IR::BasicBlock* targetBlock, Jump targetJump); - void addPatch(DataLabelPtr patch, Label target); - void addPatch(DataLabelPtr patch, IR::BasicBlock *target); - void generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBlock, - IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); - void generateCJumpOnCompare(RelationalCondition cond, RegisterID left, TrustedImm32 right, - IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock); - void generateCJumpOnCompare(RelationalCondition cond, RegisterID left, RegisterID right, - IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock); - void generateCJumpOnUndefined(RelationalCondition cond, IR::Expr *right, - RegisterID scratchRegister, RegisterID tagRegister, - IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) - { - RegisterSizeDependentOps::generateCJumpOnUndefined(this, cond, right, scratchRegister, tagRegister, - _nextBlock, currentBlock, trueBlock, falseBlock); - } - - Jump generateIsDoubleCheck(RegisterID tagOrValueRegister) - { - return RegisterSizeDependentOps::generateIsDoubleCheck(this, tagOrValueRegister); - } - - Jump genTryDoubleConversion(IR::Expr *src, FPRegisterID dest); - Jump branchDouble(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right); - Jump branchInt32(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right); - - Pointer loadAddressForWriting(RegisterID tmp, IR::Expr *t, WriteBarrier::Type *barrier); - Pointer loadAddressForReading(RegisterID tmp, IR::Expr *t) { - return loadAddressForWriting(tmp, t, 0); - } - - Pointer loadTempAddress(IR::Temp *t); - Pointer loadArgLocalAddressForWriting(RegisterID baseReg, IR::ArgLocal *al, WriteBarrier::Type *barrier); - Pointer loadArgLocalAddressForReading(RegisterID baseReg, IR::ArgLocal *al) { - return loadArgLocalAddressForWriting(baseReg, al, 0); - } - Pointer loadStringAddress(RegisterID reg, const QString &string); - Address loadConstant(IR::Const *c, RegisterID baseReg); - Address loadConstant(const TargetPrimitive &v, RegisterID baseReg); - void loadStringRef(RegisterID reg, const QString &string); - Pointer stackSlotPointer(IR::Temp *t) const - { - Q_ASSERT(t->kind == IR::Temp::StackSlot); - - return Pointer(_stackLayout->stackSlotPointer(t->index)); - } - - template <int argumentNumber> - void saveOutRegister(PointerToValue arg) - { - if (!arg.value) - return; - if (IR::Temp *t = arg.value->asTemp()) { - if (t->kind == IR::Temp::PhysicalRegister) { - Pointer addr(_stackLayout->savedRegPointer(argumentNumber)); - switch (t->type) { - case IR::BoolType: - storeBool((RegisterID) t->index, addr, WriteBarrier::NoBarrier); - break; - case IR::SInt32Type: - storeInt32((RegisterID) t->index, addr, WriteBarrier::NoBarrier); - break; - case IR::UInt32Type: - storeUInt32((RegisterID) t->index, addr, WriteBarrier::NoBarrier); - break; - case IR::DoubleType: - storeDouble((FPRegisterID) t->index, addr, WriteBarrier::NoBarrier); - break; - default: - Q_UNIMPLEMENTED(); - } - } - } - } - - template <int, typename ArgType> - void saveOutRegister(ArgType) - {} - - void loadArgumentInRegister(RegisterID source, RegisterID dest, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - move(source, dest); - } - - void loadArgumentInRegister(const Pointer& ptr, RegisterID dest, int argumentNumber) - { - Q_UNUSED(argumentNumber); - addPtr(TrustedImm32(ptr.offset), ptr.base, dest); - } - - void loadArgumentInRegister(PointerToValue temp, RegisterID dest, int argumentNumber) - { - if (!temp.value) { - RegisterSizeDependentOps::zeroRegister(this, dest); - } else { - Pointer addr = toAddress(dest, temp.value, argumentNumber, 0); - loadArgumentInRegister(addr, dest, argumentNumber); - } - } - void loadArgumentInRegister(StringToIndex temp, RegisterID dest, int argumentNumber) - { - Q_UNUSED(argumentNumber); - loadStringRef(dest, temp.string); - } - - void loadArgumentInRegister(Reference temp, RegisterID dest, int argumentNumber) - { - Q_ASSERT(temp.value); - Pointer addr = loadAddressForReading(dest, temp.value); - loadArgumentInRegister(addr, dest, argumentNumber); - } - - void loadArgumentInRegister(IR::Temp* temp, RegisterID dest, int argumentNumber) - { - RegisterSizeDependentOps::loadArgumentInRegister(this, temp, dest, argumentNumber); - } - - void loadArgumentInRegister(IR::ArgLocal* al, RegisterID dest, int argumentNumber) - { - RegisterSizeDependentOps::loadArgumentInRegister(this, al, dest, argumentNumber); - } - - void loadArgumentInRegister(IR::Const* c, RegisterID dest, int argumentNumber) - { - RegisterSizeDependentOps::loadArgumentInRegister(this, c, dest, argumentNumber); - } - - void loadArgumentInRegister(IR::Expr* expr, RegisterID dest, int argumentNumber) - { - RegisterSizeDependentOps::loadArgumentInRegister(this, expr, dest, argumentNumber); - } - - void loadArgumentInRegister(TrustedImm32 imm32, RegisterID dest, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - RegisterSizeDependentOps::zeroRegister(this, dest); - if (imm32.m_value) - move(imm32, dest); - } - - void storeReturnValue(RegisterID dest, WriteBarrier::Type barrier = WriteBarrier::NoBarrier) - { - Q_UNUSED(barrier); - Q_ASSERT(barrier == WriteBarrier::NoBarrier); - move(ReturnValueRegister, dest); - } - - void storeUInt32ReturnValue(RegisterID dest) - { - subPtr(TrustedImm32(sizeof(QV4::Value)), StackPointerRegister); - Pointer tmp(StackPointerRegister, 0); - storeReturnValue(tmp, WriteBarrier::NoBarrier); - toUInt32Register(tmp, dest); - addPtr(TrustedImm32(sizeof(QV4::Value)), StackPointerRegister); - } - - void storeReturnValue(FPRegisterID dest) - { - RegisterSizeDependentOps::storeReturnValue(this, dest); - } - - void storeReturnValue(const Pointer &dest, WriteBarrier::Type barrier) - { - RegisterSizeDependentOps::storeReturnValue(this, dest, barrier); - } - - void storeReturnValue(IR::Expr *target) - { - if (!target) - return; - - IR::Temp *temp = target->asTemp(); - if (temp && temp->kind == IR::Temp::PhysicalRegister) { - if (temp->type == IR::DoubleType) - storeReturnValue((FPRegisterID) temp->index); - else if (temp->type == IR::UInt32Type) - storeUInt32ReturnValue((RegisterID) temp->index); - else - storeReturnValue((RegisterID) temp->index); - return; - } else { - WriteBarrier::Type barrier; - Pointer addr = loadAddressForWriting(ScratchRegister, target, &barrier); - storeReturnValue(addr, barrier); - } - } + int argcOnStackForCall = 0; - void storeReturnValue(VoidType) - { - } - - template <int StackSlot> - void loadArgumentOnStack(RegisterID reg, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - poke(reg, StackSlot); - } - - template <int StackSlot> - void loadArgumentOnStack(TrustedImm32 value, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - poke(value, StackSlot); - } - - template <int StackSlot> - void loadArgumentOnStack(const Pointer& ptr, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - addPtr(TrustedImm32(ptr.offset), ptr.base, ScratchRegister); - poke(ScratchRegister, StackSlot); - } - - template <int StackSlot> - void loadArgumentOnStack(PointerToValue temp, int argumentNumber) - { - if (temp.value) { - Pointer ptr = toAddress(ScratchRegister, temp.value, argumentNumber, 0); - loadArgumentOnStack<StackSlot>(ptr, argumentNumber); - } else { - RegisterSizeDependentOps::zeroStackSlot(this, StackSlot); - } - } - - template <int StackSlot> - void loadArgumentOnStack(StringToIndex temp, int argumentNumber) - { - Q_UNUSED(argumentNumber); - loadStringRef(ScratchRegister, temp.string); - poke(ScratchRegister, StackSlot); - } - - template <int StackSlot> - void loadArgumentOnStack(Reference temp, int argumentNumber) - { - Q_ASSERT (temp.value); - - Pointer ptr = loadAddressForReading(ScratchRegister, temp.value); - loadArgumentOnStack<StackSlot>(ptr, argumentNumber); - } - - void loadDouble(IR::Expr *source, FPRegisterID dest) - { - IR::Temp *sourceTemp = source->asTemp(); - if (sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister) { - moveDouble((FPRegisterID) sourceTemp->index, dest); - return; - } - Pointer ptr = loadAddressForReading(ScratchRegister, source); - loadDouble(ptr, dest); - } - - void storeDouble(FPRegisterID source, IR::Expr* target) - { - IR::Temp *targetTemp = target->asTemp(); - if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { - moveDouble(source, (FPRegisterID) targetTemp->index); - return; - } - RegisterSizeDependentOps::storeDouble(this, source, target); - } - - void loadDouble(Address addr, FPRegisterID dest) - { - RegisterSizeDependentOps::loadDouble(this, addr, dest); - } - - void storeDouble(FPRegisterID source, Address addr, WriteBarrier::Type barrier) - { - RegisterSizeDependentOps::storeDouble(this, source, addr, barrier); - } - - template <typename Result, typename Source> - void copyValue(Result result, Source source, WriteBarrier::Type barrier); - template <typename Result> - void copyValue(Result result, IR::Expr* source, WriteBarrier::Type barrier); - - // The scratch register is used to calculate the temp address for the source. - void memcopyValue(Pointer target, IR::Expr *source, RegisterID scratchRegister, WriteBarrier::Type barrier) - { - Q_ASSERT(!source->asTemp() || source->asTemp()->kind != IR::Temp::PhysicalRegister); - Q_ASSERT(target.base != scratchRegister); - loadRawValue(loadAddressForReading(scratchRegister, source), FPGpr0); - storeRawValue(FPGpr0, target, barrier); - } - - // The scratch register is used to calculate the temp address for the source. - void memcopyValue(IR::Expr *target, Pointer source, FPRegisterID fpScratchRegister, RegisterID scratchRegister) - { - loadRawValue(source, fpScratchRegister); - WriteBarrier::Type barrier; - Pointer dest = loadAddressForWriting(scratchRegister, target, &barrier); - storeRawValue(fpScratchRegister, dest, barrier); - } - - void loadRawValue(Pointer source, FPRegisterID dest) - { - TargetConfiguration::MacroAssembler::loadDouble(source, dest); - } - - void storeRawValue(FPRegisterID source, Pointer dest, WriteBarrier::Type barrier) - { - TargetConfiguration::MacroAssembler::storeDouble(source, dest); - if (WriteBarrier::isRequired<WriteBarrier::Unknown>() && barrier == WriteBarrier::Barrier) - RegisterSizeDependentOps::emitWriteBarrier(this, dest); - } - - void storeValue(TargetPrimitive value, Address destination, WriteBarrier::Type barrier) - { - RegisterSizeDependentOps::storeValue(this, value, destination, barrier); - } - - void storeValue(TargetPrimitive value, IR::Expr* temp); - - void emitWriteBarrier(Address addr, WriteBarrier::Type barrier) { - if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) - RegisterSizeDependentOps::emitWriteBarrier(this, addr); - } - - void enterStandardStackFrame(const RegisterInformation ®ularRegistersToSave, - const RegisterInformation &fpRegistersToSave); - void leaveStandardStackFrame(const RegisterInformation ®ularRegistersToSave, - const RegisterInformation &fpRegistersToSave); - - void checkException() { - this->load8(Address(EngineRegister, targetStructureOffset(offsetof(QV4::EngineBase, hasException))), ScratchRegister); - Jump exceptionThrown = branch32(RelationalCondition::NotEqual, ScratchRegister, TrustedImm32(0)); - if (catchBlock) - addPatch(catchBlock, exceptionThrown); - else - exceptionPropagationJumps.append(exceptionThrown); - } - void jumpToExceptionHandler() { - Jump exceptionThrown = jump(); - if (catchBlock) - addPatch(catchBlock, exceptionThrown); - else - exceptionPropagationJumps.append(exceptionThrown); - } - - template <int argumentNumber, typename T> - void loadArgumentOnStackOrRegister(const T &value) - { - if (argumentNumber < RegisterArgumentCount) - loadArgumentInRegister(value, registerForArgument(argumentNumber), argumentNumber); - else - loadArgumentOnStack<argumentNumber - RegisterArgumentCount + (StackShadowSpace / RegisterSize)>(value, argumentNumber); - } - - template <int argumentNumber> - void loadArgumentOnStackOrRegister(const VoidType &value) - { - Q_UNUSED(value); - } - - template <bool selectFirst, int First, int Second> - struct Select - { - enum { Chosen = First }; - }; - - template <int First, int Second> - struct Select<false, First, Second> - { - enum { Chosen = Second }; - }; - - template <int ArgumentIndex, typename Parameter> - struct SizeOnStack - { - enum { Size = Select<ArgumentIndex >= RegisterArgumentCount, RegisterSize, 0>::Chosen }; - }; - - template <int ArgumentIndex> - struct SizeOnStack<ArgumentIndex, VoidType> - { - enum { Size = 0 }; - }; - - template <typename T> bool prepareCall(T &) - { return true; } - - bool prepareCall(LookupCall &lookupCall) - { - // IMPORTANT! See generateLookupCall in qv4isel_masm_p.h for details! - - // load the table from the context - loadPtr(Address(EngineRegister, targetStructureOffset(offsetof(QV4::EngineBase, current))), ScratchRegister); - loadPtr(Address(ScratchRegister, targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, lookups))), - lookupCall.addr.base); - // pre-calculate the indirect address for the lookupCall table: - if (lookupCall.addr.offset) - addPtr(TrustedImm32(lookupCall.addr.offset), lookupCall.addr.base); - // store it as the first argument - loadArgumentOnStackOrRegister<0>(lookupCall.addr.base); - // set the destination addresses offset to the getterSetterOffset. The base is the lookupCall table's address - lookupCall.addr.offset = lookupCall.getterSetterOffset; - return false; - } - - template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6> - void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) - { - int stackSpaceNeeded = SizeOnStack<0, Arg1>::Size - + SizeOnStack<1, Arg2>::Size - + SizeOnStack<2, Arg3>::Size - + SizeOnStack<3, Arg4>::Size - + SizeOnStack<4, Arg5>::Size - + SizeOnStack<5, Arg6>::Size - + StackShadowSpace; - - if (stackSpaceNeeded) { - Q_ASSERT(stackSpaceNeeded < (INT_MAX - StackAlignment)); - stackSpaceNeeded = static_cast<int>(WTF::roundUpToMultipleOf(StackAlignment, stackSpaceNeeded)); - subPtr(TrustedImm32(stackSpaceNeeded), StackPointerRegister); - } - - // First save any arguments that reside in registers, because they could be overwritten - // if that register is also used to pass arguments. - saveOutRegister<5>(arg6); - saveOutRegister<4>(arg5); - saveOutRegister<3>(arg4); - saveOutRegister<2>(arg3); - saveOutRegister<1>(arg2); - saveOutRegister<0>(arg1); - - loadArgumentOnStackOrRegister<5>(arg6); - loadArgumentOnStackOrRegister<4>(arg5); - loadArgumentOnStackOrRegister<3>(arg4); - loadArgumentOnStackOrRegister<2>(arg3); - loadArgumentOnStackOrRegister<1>(arg2); - - if (prepareCall(function)) - loadArgumentOnStackOrRegister<0>(arg1); - - if (JITTargetPlatform::gotRegister != -1) - load32(Address(JITTargetPlatform::FramePointerRegister, JITTargetPlatform::savedGOTRegisterSlotOnStack()), static_cast<RegisterID>(JITTargetPlatform::gotRegister)); // restore the GOT ptr - - callAbsolute(functionName, function); - - if (stackSpaceNeeded) - addPtr(TrustedImm32(stackSpaceNeeded), StackPointerRegister); - - if (needsExceptionCheck) { - checkException(); - } - - storeReturnValue(r); - - } - - template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> - void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) - { - generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, arg3, arg4, arg5, VoidType()); - } - - template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4> - void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) - { - generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, arg3, arg4, VoidType(), VoidType()); - } - - template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3> - void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3) - { - generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, arg3, VoidType(), VoidType(), VoidType()); - } - - template <typename ArgRet, typename Callable, typename Arg1, typename Arg2> - void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2) - { - generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, VoidType(), VoidType(), VoidType(), VoidType()); - } - - template <typename ArgRet, typename Callable, typename Arg1> - void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1) - { - generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, VoidType(), VoidType(), VoidType(), VoidType(), VoidType()); - } - - Pointer toAddress(RegisterID tmpReg, IR::Expr *e, int offset, WriteBarrier::Type *barrier) - { - if (barrier) - *barrier = WriteBarrier::NoBarrier; - if (IR::Const *c = e->asConst()) { - Address addr = _stackLayout->savedRegPointer(offset); - Address tagAddr = addr; - tagAddr.offset += 4; - - auto v = convertToValue<TargetPrimitive>(c); - store32(TrustedImm32(v.value()), addr); - store32(TrustedImm32(v.tag()), tagAddr); - return Pointer(addr); - } - - if (IR::Temp *t = e->asTemp()) - if (t->kind == IR::Temp::PhysicalRegister) - return Pointer(_stackLayout->savedRegPointer(offset)); - - return loadAddressForWriting(tmpReg, e, barrier); - } - - void storeBool(RegisterID reg, Pointer addr, WriteBarrier::Type barrier) - { - store32(reg, addr); - addr.offset += 4; - store32(TrustedImm32(TargetPrimitive::fromBoolean(0).tag()), addr); - if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) - RegisterSizeDependentOps::emitWriteBarrier(this, addr); - } - - void storeBool(RegisterID src, RegisterID dest) - { - move(src, dest); - } - - void storeBool(RegisterID reg, IR::Expr *target) - { - if (IR::Temp *targetTemp = target->asTemp()) { - if (targetTemp->kind == IR::Temp::PhysicalRegister) { - move(reg, (RegisterID) targetTemp->index); - return; - } - } - - WriteBarrier::Type barrier; - Pointer addr = loadAddressForWriting(ScratchRegister, target, &barrier); - storeBool(reg, addr, barrier); - } - - void storeBool(bool value, IR::Expr *target) { - TrustedImm32 trustedValue(value ? 1 : 0); - - if (IR::Temp *targetTemp = target->asTemp()) { - if (targetTemp->kind == IR::Temp::PhysicalRegister) { - move(trustedValue, (RegisterID) targetTemp->index); - return; - } - } - - move(trustedValue, ScratchRegister); - storeBool(ScratchRegister, target); - } - - void storeInt32(RegisterID src, RegisterID dest) - { - move(src, dest); - } - - void storeInt32(RegisterID reg, Pointer addr, WriteBarrier::Type barrier) - { - store32(reg, addr); - addr.offset += 4; - store32(TrustedImm32(TargetPrimitive::fromInt32(0).tag()), addr); - if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) - RegisterSizeDependentOps::emitWriteBarrier(this, addr); - } - - void storeInt32(RegisterID reg, IR::Expr *target) - { - IR::Temp *targetTemp = target->asTemp(); - if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { - move(reg, (RegisterID) targetTemp->index); - } else { - WriteBarrier::Type barrier; - Pointer addr = loadAddressForWriting(ScratchRegister, target, &barrier); - storeInt32(reg, addr, barrier); - } - } - - void storeUInt32(RegisterID src, RegisterID dest) - { - move(src, dest); - } - - void storeUInt32(RegisterID reg, Pointer addr, WriteBarrier::Type barrier) - { - // The UInt32 representation in QV4::Value is really convoluted. See also toUInt32Register. - Jump intRange = branch32(RelationalCondition::GreaterThanOrEqual, reg, TrustedImm32(0)); - convertUInt32ToDouble(reg, FPGpr0, ReturnValueRegister); - storeDouble(FPGpr0, addr, barrier); - Jump done = jump(); - intRange.link(this); - storeInt32(reg, addr, barrier); - done.link(this); - } - - void storeUInt32(RegisterID reg, IR::Expr *target) - { - IR::Temp *targetTemp = target->asTemp(); - if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { - move(reg, (RegisterID) targetTemp->index); - } else { - WriteBarrier::Type barrier; - Pointer addr = loadAddressForWriting(ScratchRegister, target, &barrier); - storeUInt32(reg, addr, barrier); - } - } - - FPRegisterID toDoubleRegister(IR::Expr *e, FPRegisterID target = FPGpr0) - { - if (IR::Const *c = e->asConst()) { - RegisterSizeDependentOps::loadDoubleConstant(this, c, target); - return target; - } - - if (IR::Temp *t = e->asTemp()) - if (t->kind == IR::Temp::PhysicalRegister) - return (FPRegisterID) t->index; - - loadDouble(e, target); - return target; - } - - RegisterID toBoolRegister(IR::Expr *e, RegisterID scratchReg) - { - return toInt32Register(e, scratchReg); - } - - RegisterID toInt32Register(IR::Expr *e, RegisterID scratchReg) - { - if (IR::Const *c = e->asConst()) { - move(TrustedImm32(convertToValue<Primitive>(c).int_32()), scratchReg); - return scratchReg; - } - - if (IR::Temp *t = e->asTemp()) - if (t->kind == IR::Temp::PhysicalRegister) - return (RegisterID) t->index; - - return toInt32Register(loadAddressForReading(scratchReg, e), scratchReg); - } - - RegisterID toInt32Register(Pointer addr, RegisterID scratchReg) - { - load32(addr, scratchReg); - return scratchReg; - } - - RegisterID toUInt32Register(IR::Expr *e, RegisterID scratchReg) - { - if (IR::Const *c = e->asConst()) { - move(TrustedImm32(unsigned(c->value)), scratchReg); - return scratchReg; - } - - if (IR::Temp *t = e->asTemp()) - if (t->kind == IR::Temp::PhysicalRegister) - return (RegisterID) t->index; - - return toUInt32Register(loadAddressForReading(scratchReg, e), scratchReg); - } - - RegisterID toUInt32Register(Pointer addr, RegisterID scratchReg) - { - Q_ASSERT(addr.base != scratchReg); - - // The UInt32 representation in QV4::Value is really convoluted. See also storeUInt32. - Pointer tagAddr = addr; - tagAddr.offset += 4; - load32(tagAddr, scratchReg); - Jump inIntRange = branch32(RelationalCondition::Equal, scratchReg, TrustedImm32(quint32(ValueTypeInternal::Integer))); - - // it's not in signed int range, so load it as a double, and truncate it down - loadDouble(addr, FPGpr0); - Address inversionAddress = loadConstant(TargetPrimitive::fromDouble(double(INT_MAX) + 1), scratchReg); - subDouble(inversionAddress, FPGpr0); - Jump canNeverHappen = branchTruncateDoubleToUint32(FPGpr0, scratchReg); - canNeverHappen.link(this); - or32(TrustedImm32(1 << 31), scratchReg); - Jump done = jump(); - - inIntRange.link(this); - load32(addr, scratchReg); - - done.link(this); - return scratchReg; - } - - void returnFromFunction(IR::Ret *s, RegisterInformation regularRegistersToSave, RegisterInformation fpRegistersToSave); - - JSC::MacroAssemblerCodeRef link(int *codeSize); - - void setStackLayout(int maxArgCountForBuiltins, int regularRegistersToSave, int fpRegistersToSave); - const StackLayout &stackLayout() const { return *_stackLayout.data(); } - void initializeLocalVariables() - { - const int locals = _stackLayout->calculateJSStackFrameSize(); - if (locals <= 0) - return; - loadPtr(Address(JITTargetPlatform::EngineRegister, targetStructureOffset(offsetof(EngineBase, jsStackTop))), JITTargetPlatform::LocalsRegister); - RegisterSizeDependentOps::initializeLocalVariables(this, locals); - storePtr(JITTargetPlatform::LocalsRegister, Address(JITTargetPlatform::EngineRegister, targetStructureOffset(offsetof(EngineBase, jsStackTop)))); - } - - Label exceptionReturnLabel; - IR::BasicBlock * catchBlock; - QVector<Jump> exceptionPropagationJumps; private: - QScopedPointer<const StackLayout> _stackLayout; - IR::Function *_function; - std::vector<Label> _addrs; - std::vector<std::vector<Jump>> _patches; -#ifndef QT_NO_DEBUG - QVector<CallInfo> _callInfos; -#endif - - struct DataLabelPatch { - DataLabelPtr dataLabel; - Label target; - }; - std::vector<DataLabelPatch> _dataLabelPatches; - - std::vector<std::vector<DataLabelPtr>> _labelPatches; - IR::BasicBlock *_nextBlock; - - QV4::ExecutableAllocator *_executableAllocator; - QV4::Compiler::JSUnitGenerator *_jsGenerator; + typedef unsigned(*CmpFunc)(const Value&,const Value&); + void cmp(int cond, CmpFunc function, const char *functionName, int lhs); + void passAccumulatorAsArg_internal(int arg, bool push); }; -template <typename TargetConfiguration> -const typename Assembler<TargetConfiguration>::VoidType Assembler<TargetConfiguration>::Void; - -template <typename TargetConfiguration> -template <typename Result, typename Source> -void Assembler<TargetConfiguration>::copyValue(Result result, Source source, WriteBarrier::Type barrier) -{ - RegisterSizeDependentOps::copyValueViaRegisters(this, source, result, barrier); -} - -template <typename TargetConfiguration> -template <typename Result> -void Assembler<TargetConfiguration>::copyValue(Result result, IR::Expr* source, WriteBarrier::Type barrier) -{ - if (source->type == IR::BoolType) { - RegisterID reg = toInt32Register(source, ScratchRegister); - storeBool(reg, result, barrier); - } else if (source->type == IR::SInt32Type) { - RegisterID reg = toInt32Register(source, ScratchRegister); - storeInt32(reg, result, barrier); - } else if (source->type == IR::UInt32Type) { - RegisterID reg = toUInt32Register(source, ScratchRegister); - storeUInt32(reg, result, barrier); - } else if (source->type == IR::DoubleType) { - storeDouble(toDoubleRegister(source), result, barrier); - } else if (source->asTemp() || source->asArgLocal()) { - RegisterSizeDependentOps::copyValueViaRegisters(this, source, result, barrier); - } else if (IR::Const *c = source->asConst()) { - auto v = convertToValue<TargetPrimitive>(c); - storeValue(v, result, barrier); - } else { - Q_UNREACHABLE(); - } -} - -template <typename TargetConfiguration> -inline Assembler<TargetConfiguration>::RuntimeCall::RuntimeCall(Runtime::RuntimeMethods method) - : addr(Assembler::EngineRegister, - method == Runtime::InvalidRuntimeMethod ? -1 : (Assembler<TargetConfiguration>::targetStructureOffset(offsetof(EngineBase, runtime) + Runtime::runtimeMethodOffset(method)))) -{ -} - -} // end of namespace JIT -} // end of namespace QV4 +} // namespace JIT +} // namespace QV4 QT_END_NAMESPACE -#endif // ENABLE(ASSEMBLER) - -#endif // QV4ISEL_MASM_P_H +#endif // QV4ASSEMBLER_P_H diff --git a/src/qml/jit/qv4binop.cpp b/src/qml/jit/qv4binop.cpp deleted file mode 100644 index a1c65f644c..0000000000 --- a/src/qml/jit/qv4binop.cpp +++ /dev/null @@ -1,665 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ -#include <qv4binop_p.h> -#include <qv4assembler_p.h> - -#if ENABLE(ASSEMBLER) - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace JIT { - -template <typename JITAssembler> -struct ArchitectureSpecificBinaryOperation -{ - using FPRegisterID = typename JITAssembler::FPRegisterID; - - static bool doubleAdd(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) - { - Q_UNUSED(as); - Q_UNUSED(lhs); - Q_UNUSED(rhs); - Q_UNUSED(targetReg); - return false; - } - static bool doubleMul(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) - { - Q_UNUSED(as); - Q_UNUSED(lhs); - Q_UNUSED(rhs); - Q_UNUSED(targetReg); - return false; - } - static bool doubleSub(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) - { - Q_UNUSED(as); - Q_UNUSED(lhs); - Q_UNUSED(rhs); - Q_UNUSED(targetReg); - return false; - } - static bool doubleDiv(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) - { - Q_UNUSED(as); - Q_UNUSED(lhs); - Q_UNUSED(rhs); - Q_UNUSED(targetReg); - return false; - } -}; - -#if CPU(X86) -template <> -struct ArchitectureSpecificBinaryOperation<Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerX86, NoOperatingSystemSpecialization>>> -{ - using JITAssembler = Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerX86, NoOperatingSystemSpecialization>>; - using FPRegisterID = JITAssembler::FPRegisterID; - using Address = JITAssembler::Address; - - static bool doubleAdd(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) - { - if (IR::Const *c = rhs->asConst()) { // Y = X + constant -> Y = X; Y += [constant-address] - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Address addr = as->loadConstant(c, JITAssembler::ScratchRegister); - as->addDouble(addr, targetReg); - return true; - } - if (IR::Temp *t = rhs->asTemp()) { // Y = X + [temp-memory-address] -> Y = X; Y += [temp-memory-address] - if (t->kind != IR::Temp::PhysicalRegister) { - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - as->addDouble(as->loadTempAddress(t), targetReg); - return true; - } - } - return false; - } - static bool doubleMul(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) - { - if (IR::Const *c = rhs->asConst()) { // Y = X * constant -> Y = X; Y *= [constant-address] - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Address addr = as->loadConstant(c, JITAssembler::ScratchRegister); - as->mulDouble(addr, targetReg); - return true; - } - if (IR::Temp *t = rhs->asTemp()) { // Y = X * [temp-memory-address] -> Y = X; Y *= [temp-memory-address] - if (t->kind != IR::Temp::PhysicalRegister) { - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - as->mulDouble(as->loadTempAddress(t), targetReg); - return true; - } - } - return false; - } - static bool doubleSub(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) - { - if (IR::Const *c = rhs->asConst()) { // Y = X - constant -> Y = X; Y -= [constant-address] - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Address addr = as->loadConstant(c, JITAssembler::ScratchRegister); - as->subDouble(addr, targetReg); - return true; - } - if (IR::Temp *t = rhs->asTemp()) { // Y = X - [temp-memory-address] -> Y = X; Y -= [temp-memory-address] - if (t->kind != IR::Temp::PhysicalRegister) { - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - as->subDouble(as->loadTempAddress(t), targetReg); - return true; - } - } - return false; - } - static bool doubleDiv(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) - { - if (IR::Const *c = rhs->asConst()) { // Y = X / constant -> Y = X; Y /= [constant-address] - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Address addr = as->loadConstant(c, JITAssembler::ScratchRegister); - as->divDouble(addr, targetReg); - return true; - } - if (IR::Temp *t = rhs->asTemp()) { // Y = X / [temp-memory-address] -> Y = X; Y /= [temp-memory-address] - if (t->kind != IR::Temp::PhysicalRegister) { - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - as->divDouble(as->loadTempAddress(t), targetReg); - return true; - } - } - return false; - } -}; -#endif - -#define OP(op) \ - { "Runtime::" isel_stringIfy(op), QV4::Runtime::op, QV4::Runtime::InvalidRuntimeMethod, 0, 0, QV4::Runtime::Method_##op##_NeedsExceptionCheck } -#define OPCONTEXT(op) \ - { "Runtime::" isel_stringIfy(op), QV4::Runtime::InvalidRuntimeMethod, QV4::Runtime::op, 0, 0, QV4::Runtime::Method_##op##_NeedsExceptionCheck } - -#define INLINE_OP(op, memOp, immOp) \ - { "Runtime::" isel_stringIfy(op), QV4::Runtime::op, QV4::Runtime::InvalidRuntimeMethod, memOp, immOp, QV4::Runtime::Method_##op##_NeedsExceptionCheck } -#define INLINE_OPCONTEXT(op, memOp, immOp) \ - { "Runtime::" isel_stringIfy(op), QV4::Runtime::InvalidRuntimeMethod, QV4::Runtime::op, memOp, immOp, QV4::Runtime::Method_##op##_NeedsExceptionCheck } - -#define NULL_OP \ - { 0, QV4::Runtime::InvalidRuntimeMethod, QV4::Runtime::InvalidRuntimeMethod, 0, 0, false } - -template <typename JITAssembler> -const typename Binop<JITAssembler>::OpInfo Binop<JITAssembler>::operations[IR::LastAluOp + 1] = { - NULL_OP, // OpInvalid - NULL_OP, // OpIfTrue - NULL_OP, // OpNot - NULL_OP, // OpUMinus - NULL_OP, // OpUPlus - NULL_OP, // OpCompl - NULL_OP, // OpIncrement - NULL_OP, // OpDecrement - - INLINE_OP(bitAnd, &Binop<JITAssembler>::inline_and32, &Binop<JITAssembler>::inline_and32), // OpBitAnd - INLINE_OP(bitOr, &Binop<JITAssembler>::inline_or32, &Binop<JITAssembler>::inline_or32), // OpBitOr - INLINE_OP(bitXor, &Binop<JITAssembler>::inline_xor32, &Binop<JITAssembler>::inline_xor32), // OpBitXor - - INLINE_OPCONTEXT(add, &Binop<JITAssembler>::inline_add32, &Binop<JITAssembler>::inline_add32), // OpAdd - INLINE_OP(sub, &Binop<JITAssembler>::inline_sub32, &Binop<JITAssembler>::inline_sub32), // OpSub - INLINE_OP(mul, &Binop<JITAssembler>::inline_mul32, &Binop<JITAssembler>::inline_mul32), // OpMul - - OP(div), // OpDiv - OP(mod), // OpMod - - INLINE_OP(shl, &Binop<JITAssembler>::inline_shl32, &Binop<JITAssembler>::inline_shl32), // OpLShift - INLINE_OP(shr, &Binop<JITAssembler>::inline_shr32, &Binop<JITAssembler>::inline_shr32), // OpRShift - INLINE_OP(ushr, &Binop<JITAssembler>::inline_ushr32, &Binop<JITAssembler>::inline_ushr32), // OpURShift - - OP(greaterThan), // OpGt - OP(lessThan), // OpLt - OP(greaterEqual), // OpGe - OP(lessEqual), // OpLe - OP(equal), // OpEqual - OP(notEqual), // OpNotEqual - OP(strictEqual), // OpStrictEqual - OP(strictNotEqual), // OpStrictNotEqual - - OPCONTEXT(instanceof), // OpInstanceof - OPCONTEXT(in), // OpIn - - NULL_OP, // OpAnd - NULL_OP // OpOr -}; - - - -template <typename JITAssembler> -void Binop<JITAssembler>::generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) -{ - if (op != IR::OpMod - && lhs->type == IR::DoubleType && rhs->type == IR::DoubleType) { - doubleBinop(lhs, rhs, target); - return; - } - if (lhs->type == IR::SInt32Type && rhs->type == IR::SInt32Type) { - if (int32Binop(lhs, rhs, target)) - return; - } - - Jump done; - if (lhs->type != IR::StringType && rhs->type != IR::StringType) - done = genInlineBinop(lhs, rhs, target); - - // TODO: inline var===null and var!==null - Binop::OpInfo info = Binop::operation(op); - - if (op == IR::OpAdd && - (lhs->type == IR::StringType || rhs->type == IR::StringType)) { - const Binop::OpInfo stringAdd = OPCONTEXT(addString); - info = stringAdd; - } - - typename JITAssembler::RuntimeCall fallBack(info.fallbackImplementation); - typename JITAssembler::RuntimeCall context(info.contextImplementation); - if (fallBack.isValid()) { - as->generateFunctionCallImp(info.needsExceptionCheck, target, info.name, fallBack, - PointerToValue(lhs), - PointerToValue(rhs)); - } else if (context.isValid()) { - as->generateFunctionCallImp(info.needsExceptionCheck, target, info.name, context, - JITAssembler::EngineRegister, - PointerToValue(lhs), - PointerToValue(rhs)); - } else { - Q_ASSERT(!"unreachable"); - } - - if (done.isSet()) - done.link(as); - -} - -template <typename JITAssembler> -void Binop<JITAssembler>::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) -{ - IR::Temp *targetTemp = target->asTemp(); - FPRegisterID targetReg; - if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) - targetReg = (FPRegisterID) targetTemp->index; - else - targetReg = JITAssembler::FPGpr0; - - switch (op) { - case IR::OpAdd: - if (lhs->asConst()) - std::swap(lhs, rhs); // Y = constant + X -> Y = X + constant - - if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleAdd(as, lhs, rhs, targetReg)) - break; - - as->addDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg); - break; - - case IR::OpMul: - if (lhs->asConst()) - std::swap(lhs, rhs); // Y = constant * X -> Y = X * constant - - if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleMul(as, lhs, rhs, targetReg)) - break; - - as->mulDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg); - break; - - case IR::OpSub: - if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleSub(as, lhs, rhs, targetReg)) - break; - - if (rhs->asTemp() && rhs->asTemp()->kind == IR::Temp::PhysicalRegister - && targetTemp - && targetTemp->kind == IR::Temp::PhysicalRegister - && targetTemp->index == rhs->asTemp()->index) { // Y = X - Y -> Tmp = Y; Y = X - Tmp - as->moveDouble(as->toDoubleRegister(rhs, JITAssembler::FPGpr1), JITAssembler::FPGpr1); - as->subDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), JITAssembler::FPGpr1, targetReg); - break; - } - - as->subDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg); - break; - - case IR::OpDiv: - if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleDiv(as, lhs, rhs, targetReg)) - break; - - if (rhs->asTemp() && rhs->asTemp()->kind == IR::Temp::PhysicalRegister - && targetTemp - && targetTemp->kind == IR::Temp::PhysicalRegister - && targetTemp->index == rhs->asTemp()->index) { // Y = X / Y -> Tmp = Y; Y = X / Tmp - as->moveDouble(as->toDoubleRegister(rhs, JITAssembler::FPGpr1), JITAssembler::FPGpr1); - as->divDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), JITAssembler::FPGpr1, targetReg); - break; - } - - as->divDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg); - break; - - default: { - Jump trueCase = as->branchDouble(false, op, lhs, rhs); - as->storeBool(false, target); - Jump done = as->jump(); - trueCase.link(as); - as->storeBool(true, target); - done.link(as); - } return; - } - - if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) - as->storeDouble(targetReg, target); -} - -template <typename JITAssembler> -bool Binop<JITAssembler>::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) -{ - Q_ASSERT(leftSource->type == IR::SInt32Type); - Q_ASSERT(rightSource->type == IR::SInt32Type); - - switch (op) { - case IR::OpBitAnd: - case IR::OpBitOr: - case IR::OpBitXor: - case IR::OpAdd: - case IR::OpMul: - if (leftSource->asConst()) // X = Const op Y -> X = Y op Const - std::swap(leftSource, rightSource); - else if (IR::Temp *t = leftSource->asTemp()) { - if (t->kind != IR::Temp::PhysicalRegister) // X = [address] op Y -> X = Y op [address] - std::swap(leftSource, rightSource); - } - break; - - case IR::OpLShift: - case IR::OpRShift: - case IR::OpURShift: - case IR::OpSub: - // handled by this method, but we can't flip operands. - break; - - default: - return false; // not handled by this method, stop here. - } - - bool inplaceOpWithAddress = false; - - IR::Temp *targetTemp = target->asTemp(); - RegisterID targetReg = JITAssembler::ReturnValueRegister; - if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { - IR::Temp *rhs = rightSource->asTemp(); - if (!rhs || rhs->kind != IR::Temp::PhysicalRegister || rhs->index != targetTemp->index) { - // We try to load leftSource into the target's register, but we can't do that if - // the target register is the same as rightSource. - targetReg = (RegisterID) targetTemp->index; - } else if (rhs && rhs->kind == IR::Temp::PhysicalRegister && targetTemp->index == rhs->index) { - // However, if the target register is the same as the rightSource register, we can flip - // the operands for certain operations. - switch (op) { - case IR::OpBitAnd: - case IR::OpBitOr: - case IR::OpBitXor: - case IR::OpAdd: - case IR::OpMul: - // X = Y op X -> X = X op Y (or rephrased: X op= Y (so an in-place operation)) - std::swap(leftSource, rightSource); - targetReg = (RegisterID) targetTemp->index; - break; - - case IR::OpLShift: - case IR::OpRShift: - case IR::OpURShift: - case IR::OpSub: - break; - - default: - Q_UNREACHABLE(); - return false; - } - } - - // determine if we have X op= [address] - if (IR::Temp *lhs = leftSource->asTemp()) { - if (lhs->kind == IR::Temp::PhysicalRegister && lhs->index == targetTemp->index) { - if (IR::Temp *rhs = rightSource->asTemp()) { - if (rhs->kind != IR::Temp::PhysicalRegister) { - switch (op) { - case IR::OpBitAnd: - case IR::OpBitOr: - case IR::OpBitXor: - case IR::OpAdd: - case IR::OpMul: - inplaceOpWithAddress = true; - break; - default: - break; - } - } - } - } - } - } - - // Special cases: - switch (op) { - case IR::OpSub: - if (rightSource->asTemp() && rightSource->asTemp()->kind == IR::Temp::PhysicalRegister - && targetTemp - && targetTemp->kind == IR::Temp::PhysicalRegister - && targetTemp->index == rightSource->asTemp()->index) { - // X = Y - X -> Tmp = X; X = Y; X -= Tmp - targetReg = (RegisterID) targetTemp->index; - as->move(targetReg, JITAssembler::ScratchRegister); - as->move(as->toInt32Register(leftSource, targetReg), targetReg); - as->sub32(JITAssembler::ScratchRegister, targetReg); - } else { - as->move(as->toInt32Register(leftSource, targetReg), targetReg); - as->sub32(as->toInt32Register(rightSource, JITAssembler::ScratchRegister), targetReg); - } - as->storeInt32(targetReg, target); - return true; - - case IR::OpLShift: - case IR::OpRShift: - case IR::OpURShift: - if (IR::Const *c = rightSource->asConst()) { - if ((QV4::Primitive::toUInt32(c->value) & 0x1f) == 0) { - RegisterID r = as->toInt32Register(leftSource, targetReg); - as->storeInt32(r, target); - return true; - } - } - break; - - default: - break; - } - - RegisterID l = as->toInt32Register(leftSource, targetReg); - if (IR::Const *c = rightSource->asConst()) { // All cases of Y = X op Const - TrustedImm32 r(int(c->value)); - TrustedImm32 ur(QV4::Primitive::toUInt32(c->value) & 0x1f); - - switch (op) { - case IR::OpBitAnd: as->and32(r, l, targetReg); break; - case IR::OpBitOr: as->or32 (r, l, targetReg); break; - case IR::OpBitXor: as->xor32(r, l, targetReg); break; - case IR::OpAdd: as->add32(r, l, targetReg); break; - case IR::OpMul: as->mul32(r, l, targetReg); break; - - case IR::OpLShift: as->lshift32(l, ur, targetReg); break; - case IR::OpRShift: as->rshift32(l, ur, targetReg); break; - case IR::OpURShift: as->urshift32(l, ur, targetReg); - as->storeUInt32(targetReg, target); // IMPORTANT: do NOT do a break here! The stored type of an urshift is different from the other binary operations! - return true; - - case IR::OpSub: // already handled before - default: // not handled by this method: - Q_UNREACHABLE(); - return false; - } - } else if (inplaceOpWithAddress) { // All cases of X = X op [address-of-Y] - Pointer rhsAddr = as->loadAddressForReading(JITAssembler::ScratchRegister, rightSource); - switch (op) { - case IR::OpBitAnd: as->and32(rhsAddr, targetReg); break; - case IR::OpBitOr: as->or32 (rhsAddr, targetReg); break; - case IR::OpBitXor: as->xor32(rhsAddr, targetReg); break; - case IR::OpAdd: as->add32(rhsAddr, targetReg); break; - case IR::OpMul: as->mul32(rhsAddr, targetReg); break; - break; - - default: // not handled by this method: - Q_UNREACHABLE(); - return false; - } - } else { // All cases of Z = X op Y - RegisterID r = as->toInt32Register(rightSource, JITAssembler::ScratchRegister); - switch (op) { - case IR::OpBitAnd: as->and32(l, r, targetReg); break; - case IR::OpBitOr: as->or32 (l, r, targetReg); break; - case IR::OpBitXor: as->xor32(l, r, targetReg); break; - case IR::OpAdd: as->add32(l, r, targetReg); break; - case IR::OpMul: as->mul32(l, r, targetReg); break; - -#if CPU(X86) || CPU(X86_64) - // Intel does the & 0x1f on the CPU, so: - case IR::OpLShift: as->lshift32(l, r, targetReg); break; - case IR::OpRShift: as->rshift32(l, r, targetReg); break; - case IR::OpURShift: as->urshift32(l, r, targetReg); - as->storeUInt32(targetReg, target); // IMPORTANT: do NOT do a break here! The stored type of an urshift is different from the other binary operations! - return true; -#else - // Not all CPUs accept shifts over more than 31 bits, and some CPUs (like ARM) will do - // surprising stuff when shifting over 0 bits. -#define CHECK_RHS(op) { \ - as->and32(TrustedImm32(0x1f), r, JITAssembler::ScratchRegister); \ - Jump notZero = as->branch32(RelationalCondition::NotEqual, JITAssembler::ScratchRegister, TrustedImm32(0)); \ - as->move(l, targetReg); \ - Jump done = as->jump(); \ - notZero.link(as); \ - op; \ - done.link(as); \ -} - case IR::OpLShift: CHECK_RHS(as->lshift32(l, JITAssembler::ScratchRegister, targetReg)); break; - case IR::OpRShift: CHECK_RHS(as->rshift32(l, JITAssembler::ScratchRegister, targetReg)); break; - case IR::OpURShift: - CHECK_RHS(as->urshift32(l, JITAssembler::ScratchRegister, targetReg)); - as->storeUInt32(targetReg, target); - // IMPORTANT: do NOT do a break here! The stored type of an urshift is different from the other binary operations! - return true; -#undef CHECK_RHS -#endif - - case IR::OpSub: // already handled before - default: // not handled by this method: - Q_UNREACHABLE(); - return false; - } - } - - as->storeInt32(targetReg, target); - return true; -} - -template <typename JITAssembler> -inline typename JITAssembler::FPRegisterID getFreeFPReg(IR::Expr *shouldNotOverlap, unsigned hint) -{ - if (IR::Temp *t = shouldNotOverlap->asTemp()) - if (t->type == IR::DoubleType) - if (t->kind == IR::Temp::PhysicalRegister) - if (t->index == hint) - return typename JITAssembler::FPRegisterID(hint + 1); - return typename JITAssembler::FPRegisterID(hint); -} - -template <typename JITAssembler> -typename JITAssembler::Jump Binop<JITAssembler>::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) -{ - Jump done; - - // Try preventing a call for a few common binary operations. This is used in two cases: - // - no register allocation was performed (not available for the platform, or the IR was - // not transformed into SSA) - // - type inference found that either or both operands can be of non-number type, and the - // register allocator will have prepared for a call (meaning: all registers that do not - // hold operands are spilled to the stack, which makes them available here) - // Note: FPGPr0 can still not be used, because uint32->double conversion uses it as a scratch - // register. - switch (op) { - case IR::OpAdd: { - FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2); - FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4); - Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); - Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); - - as->addDouble(rReg, lReg); - as->storeDouble(lReg, target); - done = as->jump(); - - if (leftIsNoDbl.isSet()) - leftIsNoDbl.link(as); - if (rightIsNoDbl.isSet()) - rightIsNoDbl.link(as); - } break; - case IR::OpMul: { - FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2); - FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4); - Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); - Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); - - as->mulDouble(rReg, lReg); - as->storeDouble(lReg, target); - done = as->jump(); - - if (leftIsNoDbl.isSet()) - leftIsNoDbl.link(as); - if (rightIsNoDbl.isSet()) - rightIsNoDbl.link(as); - } break; - case IR::OpSub: { - FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2); - FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4); - Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); - Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); - - as->subDouble(rReg, lReg); - as->storeDouble(lReg, target); - done = as->jump(); - - if (leftIsNoDbl.isSet()) - leftIsNoDbl.link(as); - if (rightIsNoDbl.isSet()) - rightIsNoDbl.link(as); - } break; - case IR::OpDiv: { - FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2); - FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4); - Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); - Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); - - as->divDouble(rReg, lReg); - as->storeDouble(lReg, target); - done = as->jump(); - - if (leftIsNoDbl.isSet()) - leftIsNoDbl.link(as); - if (rightIsNoDbl.isSet()) - rightIsNoDbl.link(as); - } break; - default: - break; - } - - return done; -} - -template struct QV4::JIT::Binop<QV4::JIT::Assembler<DefaultAssemblerTargetConfiguration>>; -#if defined(V4_BOOTSTRAP) -#if !CPU(ARM_THUMB2) -template struct QV4::JIT::Binop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>>; -#endif -#if !CPU(ARM64) -template struct QV4::JIT::Binop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>>; -#endif -#endif - -} // end of namespace JIT -} // end of namespace QV4 - -QT_END_NAMESPACE - - -#endif diff --git a/src/qml/jit/qv4binop_p.h b/src/qml/jit/qv4binop_p.h deleted file mode 100644 index 1b1ab7f24d..0000000000 --- a/src/qml/jit/qv4binop_p.h +++ /dev/null @@ -1,256 +0,0 @@ -/**************************************************************************** -** -** 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 QV4BINOP_P_H -#define QV4BINOP_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 <qv4jsir_p.h> -#include <qv4isel_masm_p.h> -#include <qv4assembler_p.h> - -QT_BEGIN_NAMESPACE - -#if ENABLE(ASSEMBLER) - -namespace QV4 { -namespace JIT { - -template <typename JITAssembler> -struct Binop { - Binop(JITAssembler *assembler, IR::AluOp operation) - : as(assembler) - , op(operation) - {} - - using Jump = typename JITAssembler::Jump; - using Address = typename JITAssembler::Address; - using RegisterID = typename JITAssembler::RegisterID; - using FPRegisterID = typename JITAssembler::FPRegisterID; - using TrustedImm32 = typename JITAssembler::TrustedImm32; - using ResultCondition = typename JITAssembler::ResultCondition; - using RelationalCondition = typename JITAssembler::RelationalCondition; - using Pointer = typename JITAssembler::Pointer; - using PointerToValue = typename JITAssembler::PointerToValue; - - void generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target); - void doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target); - bool int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target); - Jump genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target); - - typedef Jump (Binop::*MemRegOp)(Address, RegisterID); - typedef Jump (Binop::*ImmRegOp)(TrustedImm32, RegisterID); - - struct OpInfo { - const char *name; - Runtime::RuntimeMethods fallbackImplementation; - Runtime::RuntimeMethods contextImplementation; - MemRegOp inlineMemRegOp; - ImmRegOp inlineImmRegOp; - bool needsExceptionCheck; - }; - - static const OpInfo operations[IR::LastAluOp + 1]; - static const OpInfo &operation(IR::AluOp operation) - { return operations[operation]; } - - Jump inline_add32(Address addr, RegisterID reg) - { -#if HAVE(ALU_OPS_WITH_MEM_OPERAND) - return as->branchAdd32(ResultCondition::Overflow, addr, reg); -#else - as->load32(addr, JITAssembler::ScratchRegister); - return as->branchAdd32(ResultCondition::Overflow, JITAssembler::ScratchRegister, reg); -#endif - } - - Jump inline_add32(TrustedImm32 imm, RegisterID reg) - { - return as->branchAdd32(ResultCondition::Overflow, imm, reg); - } - - Jump inline_sub32(Address addr, RegisterID reg) - { -#if HAVE(ALU_OPS_WITH_MEM_OPERAND) - return as->branchSub32(ResultCondition::Overflow, addr, reg); -#else - as->load32(addr, JITAssembler::ScratchRegister); - return as->branchSub32(ResultCondition::Overflow, JITAssembler::ScratchRegister, reg); -#endif - } - - Jump inline_sub32(TrustedImm32 imm, RegisterID reg) - { - return as->branchSub32(ResultCondition::Overflow, imm, reg); - } - - Jump inline_mul32(Address addr, RegisterID reg) - { -#if HAVE(ALU_OPS_WITH_MEM_OPERAND) - return as->branchMul32(JITAssembler::Overflow, addr, reg); -#else - as->load32(addr, JITAssembler::ScratchRegister); - return as->branchMul32(ResultCondition::Overflow, JITAssembler::ScratchRegister, reg); -#endif - } - - Jump inline_mul32(TrustedImm32 imm, RegisterID reg) - { - return as->branchMul32(ResultCondition::Overflow, imm, reg, reg); - } - - Jump inline_shl32(Address addr, RegisterID reg) - { - as->load32(addr, JITAssembler::ScratchRegister); - as->and32(TrustedImm32(0x1f), JITAssembler::ScratchRegister); - as->lshift32(JITAssembler::ScratchRegister, reg); - return Jump(); - } - - Jump inline_shl32(TrustedImm32 imm, RegisterID reg) - { - imm.m_value &= 0x1f; - as->lshift32(imm, reg); - return Jump(); - } - - Jump inline_shr32(Address addr, RegisterID reg) - { - as->load32(addr, JITAssembler::ScratchRegister); - as->and32(TrustedImm32(0x1f), JITAssembler::ScratchRegister); - as->rshift32(JITAssembler::ScratchRegister, reg); - return Jump(); - } - - Jump inline_shr32(TrustedImm32 imm, RegisterID reg) - { - imm.m_value &= 0x1f; - as->rshift32(imm, reg); - return Jump(); - } - - Jump inline_ushr32(Address addr, RegisterID reg) - { - as->load32(addr, JITAssembler::ScratchRegister); - as->and32(TrustedImm32(0x1f), JITAssembler::ScratchRegister); - as->urshift32(JITAssembler::ScratchRegister, reg); - return as->branchTest32(ResultCondition::Signed, reg, reg); - } - - Jump inline_ushr32(TrustedImm32 imm, RegisterID reg) - { - imm.m_value &= 0x1f; - as->urshift32(imm, reg); - return as->branchTest32(ResultCondition::Signed, reg, reg); - } - - Jump inline_and32(Address addr, RegisterID reg) - { -#if HAVE(ALU_OPS_WITH_MEM_OPERAND) - as->and32(addr, reg); -#else - as->load32(addr, JITAssembler::ScratchRegister); - as->and32(JITAssembler::ScratchRegister, reg); -#endif - return Jump(); - } - - Jump inline_and32(TrustedImm32 imm, RegisterID reg) - { - as->and32(imm, reg); - return Jump(); - } - - Jump inline_or32(Address addr, RegisterID reg) - { -#if HAVE(ALU_OPS_WITH_MEM_OPERAND) - as->or32(addr, reg); -#else - as->load32(addr, JITAssembler::ScratchRegister); - as->or32(JITAssembler::ScratchRegister, reg); -#endif - return Jump(); - } - - Jump inline_or32(TrustedImm32 imm, RegisterID reg) - { - as->or32(imm, reg); - return Jump(); - } - - Jump inline_xor32(Address addr, RegisterID reg) - { -#if HAVE(ALU_OPS_WITH_MEM_OPERAND) - as->xor32(addr, reg); -#else - as->load32(addr, JITAssembler::ScratchRegister); - as->xor32(JITAssembler::ScratchRegister, reg); -#endif - return Jump(); - } - - Jump inline_xor32(TrustedImm32 imm, RegisterID reg) - { - as->xor32(imm, reg); - return Jump(); - } - - - - JITAssembler *as; - IR::AluOp op; -}; - -} -} - -#endif - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp deleted file mode 100644 index 7784eb364e..0000000000 --- a/src/qml/jit/qv4isel_masm.cpp +++ /dev/null @@ -1,1684 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#include "qv4isel_masm_p.h" -#include "qv4runtime_p.h" -#include "qv4lookup_p.h" -#include "qv4ssa_p.h" -#include "qv4regalloc_p.h" -#include "qv4assembler_p.h" -#include "qv4unop_p.h" -#include "qv4binop_p.h" - -#include <QtCore/QBuffer> -#include <QtCore/QCoreApplication> - -#include <assembler/LinkBuffer.h> -#include <WTFStubs.h> - -#include <iostream> - -#if ENABLE(ASSEMBLER) - -#if USE(UDIS86) -# include <udis86.h> -#endif - -using namespace QV4; -using namespace QV4::JIT; - - -template <typename JITAssembler> -InstructionSelection<JITAssembler>::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory) - : EvalInstructionSelection(execAllocator, module, jsGenerator, iselFactory) - , _block(0) - , _as(0) - , compilationUnit(new CompilationUnit) - , qmlEngine(qmlEngine) -{ - compilationUnit->codeRefs.resize(module->functions.size()); - module->unitFlags |= QV4::CompiledData::Unit::ContainsMachineCode; -} - -template <typename JITAssembler> -InstructionSelection<JITAssembler>::~InstructionSelection() -{ - delete _as; -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::run(int functionIndex) -{ - IR::Function *function = irModule->functions[functionIndex]; - qSwap(_function, function); - - IR::Optimizer opt(_function); - opt.run(qmlEngine); - - static const bool withRegisterAllocator = qEnvironmentVariableIsEmpty("QV4_NO_REGALLOC"); - if (JITTargetPlatform::RegAllocIsSupported && opt.isInSSA() && withRegisterAllocator) { - RegisterAllocator regalloc(JITTargetPlatform::getRegisterInfo()); - regalloc.run(_function, opt); - calculateRegistersToSave(regalloc.usedRegisters()); - } else { - if (opt.isInSSA()) - // No register allocator available for this platform, or env. var was set, so: - opt.convertOutOfSSA(); - ConvertTemps().toStackSlots(_function); - IR::Optimizer::showMeTheCode(_function, "After stack slot allocation"); - calculateRegistersToSave(JITTargetPlatform::getRegisterInfo()); // FIXME: this saves all registers. We can probably do with a subset: those that are not used by the register allocator. - } - BitVector removableJumps = opt.calculateOptionalJumps(); - qSwap(_removableJumps, removableJumps); - - JITAssembler* oldAssembler = _as; - _as = new JITAssembler(jsGenerator, _function, executableAllocator); - _as->setStackLayout(6, // 6 == max argc for calls to built-ins with an argument array - regularRegistersToSave.size(), - fpRegistersToSave.size()); - _as->enterStandardStackFrame(regularRegistersToSave, fpRegistersToSave); - - if (JITTargetPlatform::RegisterArgumentCount > 0) - _as->move(_as->registerForArgument(0), JITTargetPlatform::EngineRegister); - else - _as->loadPtr(addressForArgument(0), JITTargetPlatform::EngineRegister); - - _as->initializeLocalVariables(); - - int lastLine = 0; - for (int i = 0, ei = _function->basicBlockCount(); i != ei; ++i) { - IR::BasicBlock *nextBlock = (i < ei - 1) ? _function->basicBlock(i + 1) : 0; - _block = _function->basicBlock(i); - if (_block->isRemoved()) - continue; - _as->registerBlock(_block, nextBlock); - - for (IR::Stmt *s : _block->statements()) { - if (s->location.isValid()) { - if (int(s->location.startLine) != lastLine) { - _as->loadPtr(Address(JITTargetPlatform::EngineRegister, JITAssembler::targetStructureOffset(offsetof(QV4::EngineBase, current))), JITTargetPlatform::ScratchRegister); - Address lineAddr(JITTargetPlatform::ScratchRegister, JITAssembler::targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, lineNumber))); - _as->store32(TrustedImm32(s->location.startLine), lineAddr); - lastLine = s->location.startLine; - } - } - visit(s); - } - } - - if (!_as->exceptionReturnLabel.isSet()) - visitRet(0); - - int dummySize; - JSC::MacroAssemblerCodeRef codeRef =_as->link(&dummySize); - compilationUnit->codeRefs[functionIndex] = codeRef; - - qSwap(_function, function); - delete _as; - _as = oldAssembler; - qSwap(_removableJumps, removableJumps); -} - -template <typename JITAssembler> -QQmlRefPointer<QV4::CompiledData::CompilationUnit> InstructionSelection<JITAssembler>::backendCompileStep() -{ - QQmlRefPointer<QV4::CompiledData::CompilationUnit> result; - result.adopt(compilationUnit.take()); - return result; -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result) -{ - prepareCallData(args, 0); - - if (useFastLookups && func->global) { - uint index = registerGlobalGetterLookup(*func->id); - generateRuntimeCall(_as, result, callGlobalLookup, - JITTargetPlatform::EngineRegister, - TrustedImm32(index), - baseAddressForCallData()); - } else { - generateRuntimeCall(_as, result, callActivationProperty, - JITTargetPlatform::EngineRegister, - StringToIndex(*func->id), - baseAddressForCallData()); - } -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinTypeofQmlContextProperty(IR::Expr *base, - IR::Member::MemberKind kind, - int propertyIndex, IR::Expr *result) -{ - if (kind == IR::Member::MemberOfQmlScopeObject) { - generateRuntimeCall(_as, result, typeofScopeObjectProperty, JITTargetPlatform::EngineRegister, - PointerToValue(base), - TrustedImm32(propertyIndex)); - } else if (kind == IR::Member::MemberOfQmlContextObject) { - generateRuntimeCall(_as, result, typeofContextObjectProperty, - JITTargetPlatform::EngineRegister, PointerToValue(base), - TrustedImm32(propertyIndex)); - } else { - Q_UNREACHABLE(); - } -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinTypeofMember(IR::Expr *base, const QString &name, - IR::Expr *result) -{ - generateRuntimeCall(_as, result, typeofMember, JITTargetPlatform::EngineRegister, - PointerToValue(base), StringToIndex(name)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, - IR::Expr *result) -{ - generateRuntimeCall(_as, result, typeofElement, - JITTargetPlatform::EngineRegister, - PointerToValue(base), PointerToValue(index)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinTypeofName(const QString &name, IR::Expr *result) -{ - generateRuntimeCall(_as, result, typeofName, JITTargetPlatform::EngineRegister, - StringToIndex(name)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result) -{ - generateRuntimeCall(_as, result, typeofValue, JITTargetPlatform::EngineRegister, - PointerToValue(value)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result) -{ - generateRuntimeCall(_as, result, deleteMember, JITTargetPlatform::EngineRegister, - Reference(base), StringToIndex(name)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index, - IR::Expr *result) -{ - generateRuntimeCall(_as, result, deleteElement, JITTargetPlatform::EngineRegister, - Reference(base), PointerToValue(index)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinDeleteName(const QString &name, IR::Expr *result) -{ - generateRuntimeCall(_as, result, deleteName, JITTargetPlatform::EngineRegister, - StringToIndex(name)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinDeleteValue(IR::Expr *result) -{ - _as->storeValue(JITAssembler::TargetPrimitive::fromBoolean(false), result); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinThrow(IR::Expr *arg) -{ - generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, throwException, JITTargetPlatform::EngineRegister, - PointerToValue(arg)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinReThrow() -{ - _as->jumpToExceptionHandler(); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinUnwindException(IR::Expr *result) -{ - generateRuntimeCall(_as, result, unwindException, JITTargetPlatform::EngineRegister); - -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinPushCatchScope(const QString &exceptionName) -{ - generateRuntimeCall(_as, JITAssembler::Void, pushCatchScope, JITTargetPlatform::EngineRegister, StringToIndex(exceptionName)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result) -{ - Q_ASSERT(arg); - Q_ASSERT(result); - - generateRuntimeCall(_as, result, foreachIterator, JITTargetPlatform::EngineRegister, PointerToValue(arg)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result) -{ - Q_ASSERT(arg); - Q_ASSERT(result); - - generateRuntimeCall(_as, result, foreachNextPropertyName, Reference(arg)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinPushWithScope(IR::Expr *arg) -{ - Q_ASSERT(arg); - - generateRuntimeCall(_as, JITAssembler::Void, pushWithScope, Reference(arg), JITTargetPlatform::EngineRegister); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinPopScope() -{ - generateRuntimeCall(_as, JITAssembler::Void, popScope, JITTargetPlatform::EngineRegister); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinDeclareVar(bool deletable, const QString &name) -{ - generateRuntimeCall(_as, JITAssembler::Void, declareVar, JITTargetPlatform::EngineRegister, - TrustedImm32(deletable), StringToIndex(name)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args) -{ - Q_ASSERT(result); - - int length = prepareVariableArguments(args); - generateRuntimeCall(_as, result, arrayLiteral, JITTargetPlatform::EngineRegister, - baseAddressForCallArguments(), TrustedImm32(length)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray) -{ - Q_ASSERT(result); - - int argc = 0; - - const int classId = registerJSClass(keyValuePairCount, keyValuePairs); - - IR::ExprList *it = keyValuePairs; - for (int i = 0; i < keyValuePairCount; ++i, it = it->next) { - it = it->next; - - bool isData = it->expr->asConst()->value; - it = it->next; - - _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr, WriteBarrier::NoBarrier); - - if (!isData) { - it = it->next; - _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr, WriteBarrier::NoBarrier); - } - } - - it = arrayEntries; - uint arrayValueCount = 0; - while (it) { - uint index = it->expr->asConst()->value; - it = it->next; - - bool isData = it->expr->asConst()->value; - it = it->next; - - if (!isData) { - it = it->next; // getter - it = it->next; // setter - continue; - } - - ++arrayValueCount; - - // Index - _as->storeValue(JITAssembler::TargetPrimitive::fromUInt32(index), _as->stackLayout().argumentAddressForCall(argc++), WriteBarrier::NoBarrier); - - // Value - _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr, WriteBarrier::NoBarrier); - it = it->next; - } - - it = arrayEntries; - uint arrayGetterSetterCount = 0; - while (it) { - uint index = it->expr->asConst()->value; - it = it->next; - - bool isData = it->expr->asConst()->value; - it = it->next; - - if (isData) { - it = it->next; // value - continue; - } - - ++arrayGetterSetterCount; - - // Index - _as->storeValue(JITAssembler::TargetPrimitive::fromUInt32(index), _as->stackLayout().argumentAddressForCall(argc++), WriteBarrier::NoBarrier); - - // Getter - _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr, WriteBarrier::NoBarrier); - it = it->next; - - // Setter - _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr, WriteBarrier::NoBarrier); - it = it->next; - } - - generateRuntimeCall(_as, result, objectLiteral, JITTargetPlatform::EngineRegister, - baseAddressForCallArguments(), TrustedImm32(classId), - TrustedImm32(arrayValueCount), TrustedImm32(arrayGetterSetterCount | (needSparseArray << 30))); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinSetupArgumentObject(IR::Expr *result) -{ - generateRuntimeCall(_as, result, setupArgumentsObject, JITTargetPlatform::EngineRegister); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinConvertThisToObject() -{ - generateRuntimeCall(_as, JITAssembler::Void, convertThisToObject, JITTargetPlatform::EngineRegister); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) -{ - Q_ASSERT(value); - - prepareCallData(args, 0); - if (value->asConst()) - generateRuntimeCall(_as, result, callValue, JITTargetPlatform::EngineRegister, - PointerToValue(value), - baseAddressForCallData()); - else - generateRuntimeCall(_as, result, callValue, JITTargetPlatform::EngineRegister, - Reference(value), - baseAddressForCallData()); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::loadThisObject(IR::Expr *temp) -{ - WriteBarrier::Type barrier; - Pointer addr = _as->loadAddressForWriting(JITTargetPlatform::ScratchRegister, temp, &barrier); - _as->loadPtr(Address(JITTargetPlatform::EngineRegister, JITAssembler::targetStructureOffset(offsetof(QV4::EngineBase, current))), JITTargetPlatform::ReturnValueRegister); - _as->loadPtr(Address(JITTargetPlatform::ReturnValueRegister,JITAssembler::targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, callData))), JITTargetPlatform::ReturnValueRegister); - _as->copyValue(addr, Address(JITTargetPlatform::ReturnValueRegister, offsetof(CallData, thisObject)), barrier); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::loadQmlContext(IR::Expr *temp) -{ - generateRuntimeCall(_as, temp, getQmlContext, JITTargetPlatform::EngineRegister); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::loadQmlImportedScripts(IR::Expr *temp) -{ - generateRuntimeCall(_as, temp, getQmlImportedScripts, JITTargetPlatform::EngineRegister); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::loadQmlSingleton(const QString &name, IR::Expr *temp) -{ - generateRuntimeCall(_as, temp, getQmlSingleton, JITTargetPlatform::EngineRegister, StringToIndex(name)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::loadConst(IR::Const *sourceConst, IR::Expr *target) -{ - if (IR::Temp *targetTemp = target->asTemp()) { - if (targetTemp->kind == IR::Temp::PhysicalRegister) { - if (targetTemp->type == IR::DoubleType) { - Q_ASSERT(sourceConst->type == IR::DoubleType); - _as->toDoubleRegister(sourceConst, (FPRegisterID) targetTemp->index); - } else if (targetTemp->type == IR::SInt32Type) { - Q_ASSERT(sourceConst->type == IR::SInt32Type); - _as->toInt32Register(sourceConst, (RegisterID) targetTemp->index); - } else if (targetTemp->type == IR::UInt32Type) { - Q_ASSERT(sourceConst->type == IR::UInt32Type); - _as->toUInt32Register(sourceConst, (RegisterID) targetTemp->index); - } else if (targetTemp->type == IR::BoolType) { - Q_ASSERT(sourceConst->type == IR::BoolType); - _as->move(TrustedImm32(convertToValue<Primitive>(sourceConst).int_32()), - (RegisterID) targetTemp->index); - } else { - Q_UNREACHABLE(); - } - return; - } - } - - _as->storeValue(convertToValue<typename JITAssembler::TargetPrimitive>(sourceConst), target); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::loadString(const QString &str, IR::Expr *target) -{ - Pointer srcAddr = _as->loadStringAddress(JITTargetPlatform::ReturnValueRegister, str); - _as->loadPtr(srcAddr, JITTargetPlatform::ReturnValueRegister); - WriteBarrier::Type barrier; - Pointer destAddr = _as->loadAddressForWriting(JITTargetPlatform::ScratchRegister, target, &barrier); - JITAssembler::RegisterSizeDependentOps::loadManagedPointer(_as, JITTargetPlatform::ReturnValueRegister, destAddr, barrier); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target) -{ - int id = registerRegExp(sourceRegexp); - generateRuntimeCall(_as, target, regexpLiteral, JITTargetPlatform::EngineRegister, TrustedImm32(id)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::getActivationProperty(const IR::Name *name, IR::Expr *target) -{ - if (useFastLookups && name->global) { - uint index = registerGlobalGetterLookup(*name->id); - generateLookupCall(target, index, offsetof(QV4::Lookup, globalGetter), JITTargetPlatform::EngineRegister, JITAssembler::Void); - return; - } - generateRuntimeCall(_as, target, getActivationProperty, JITTargetPlatform::EngineRegister, StringToIndex(*name->id)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::setActivationProperty(IR::Expr *source, const QString &targetName) -{ - // ### should use a lookup call here - generateRuntimeCall(_as, JITAssembler::Void, setActivationProperty, - JITTargetPlatform::EngineRegister, StringToIndex(targetName), PointerToValue(source)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::initClosure(IR::Closure *closure, IR::Expr *target) -{ - int id = closure->value; - generateRuntimeCall(_as, target, closure, JITTargetPlatform::EngineRegister, TrustedImm32(id)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::getProperty(IR::Expr *base, const QString &name, IR::Expr *target) -{ - if (useFastLookups) { - uint index = registerGetterLookup(name); - generateLookupCall(target, index, offsetof(QV4::Lookup, getter), JITTargetPlatform::EngineRegister, PointerToValue(base), JITAssembler::Void); - } else { - generateRuntimeCall(_as, target, getProperty, JITTargetPlatform::EngineRegister, - PointerToValue(base), StringToIndex(name)); - } -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target) -{ - if (kind == IR::Member::MemberOfQmlScopeObject) - generateRuntimeCall(_as, target, getQmlScopeObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(index), TrustedImm32(captureRequired)); - else if (kind == IR::Member::MemberOfQmlContextObject) - generateRuntimeCall(_as, target, getQmlContextObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(index), TrustedImm32(captureRequired)); - else if (kind == IR::Member::MemberOfIdObjectsArray) - generateRuntimeCall(_as, target, getQmlIdObject, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(index)); - else - Q_ASSERT(false); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target) -{ - if (attachedPropertiesId != 0) - generateRuntimeCall(_as, target, getQmlAttachedProperty, JITTargetPlatform::EngineRegister, TrustedImm32(attachedPropertiesId), TrustedImm32(propertyIndex)); - else if (isSingleton) - generateRuntimeCall(_as, target, getQmlSingletonQObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(propertyIndex), - TrustedImm32(captureRequired)); - else - generateRuntimeCall(_as, target, getQmlQObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(propertyIndex), - TrustedImm32(captureRequired)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::setProperty(IR::Expr *source, IR::Expr *targetBase, - const QString &targetName) -{ - if (useFastLookups) { - uint index = registerSetterLookup(targetName); - generateLookupCall(JITAssembler::Void, index, offsetof(QV4::Lookup, setter), - JITTargetPlatform::EngineRegister, - PointerToValue(targetBase), - PointerToValue(source)); - } else { - generateRuntimeCall(_as, JITAssembler::Void, setProperty, JITTargetPlatform::EngineRegister, - PointerToValue(targetBase), StringToIndex(targetName), - PointerToValue(source)); - } -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) -{ - if (kind == IR::Member::MemberOfQmlScopeObject) - generateRuntimeCall(_as, JITAssembler::Void, setQmlScopeObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(targetBase), - TrustedImm32(propertyIndex), PointerToValue(source)); - else if (kind == IR::Member::MemberOfQmlContextObject) - generateRuntimeCall(_as, JITAssembler::Void, setQmlContextObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(targetBase), - TrustedImm32(propertyIndex), PointerToValue(source)); - else - Q_ASSERT(false); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) -{ - generateRuntimeCall(_as, JITAssembler::Void, setQmlQObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(targetBase), - TrustedImm32(propertyIndex), PointerToValue(source)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) -{ - if (0 && useFastLookups) { - uint lookup = registerIndexedGetterLookup(); - generateLookupCall(target, lookup, offsetof(QV4::Lookup, indexedGetter), - JITTargetPlatform::EngineRegister, - PointerToValue(base), - PointerToValue(index)); - return; - } - - generateRuntimeCall(_as, target, getElement, JITTargetPlatform::EngineRegister, - PointerToValue(base), PointerToValue(index)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) -{ - if (0 && useFastLookups) { - uint lookup = registerIndexedSetterLookup(); - generateLookupCall(JITAssembler::Void, lookup, offsetof(QV4::Lookup, indexedSetter), - JITTargetPlatform::EngineRegister, - PointerToValue(targetBase), PointerToValue(targetIndex), - PointerToValue(source)); - return; - } - generateRuntimeCall(_as, JITAssembler::Void, setElement, JITTargetPlatform::EngineRegister, - PointerToValue(targetBase), PointerToValue(targetIndex), - PointerToValue(source)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::copyValue(IR::Expr *source, IR::Expr *target) -{ - IR::Temp *sourceTemp = source->asTemp(); - IR::Temp *targetTemp = target->asTemp(); - - if (sourceTemp && targetTemp && *sourceTemp == *targetTemp) - return; - if (IR::ArgLocal *sal = source->asArgLocal()) - if (IR::ArgLocal *tal = target->asArgLocal()) - if (*sal == *tal) - return; - - if (sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister) { - if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { - if (sourceTemp->type == IR::DoubleType) - _as->moveDouble((FPRegisterID) sourceTemp->index, - (FPRegisterID) targetTemp->index); - else - _as->move((RegisterID) sourceTemp->index, - (RegisterID) targetTemp->index); - return; - } else { - switch (sourceTemp->type) { - case IR::DoubleType: - _as->storeDouble((FPRegisterID) sourceTemp->index, target); - break; - case IR::SInt32Type: - _as->storeInt32((RegisterID) sourceTemp->index, target); - break; - case IR::UInt32Type: - _as->storeUInt32((RegisterID) sourceTemp->index, target); - break; - case IR::BoolType: - _as->storeBool((RegisterID) sourceTemp->index, target); - break; - default: - Q_ASSERT(!"Unreachable"); - break; - } - return; - } - } else if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { - switch (targetTemp->type) { - case IR::DoubleType: - Q_ASSERT(source->type == IR::DoubleType); - _as->toDoubleRegister(source, (FPRegisterID) targetTemp->index); - return; - case IR::BoolType: - Q_ASSERT(source->type == IR::BoolType); - _as->toInt32Register(source, (RegisterID) targetTemp->index); - return; - case IR::SInt32Type: - Q_ASSERT(source->type == IR::SInt32Type); - _as->toInt32Register(source, (RegisterID) targetTemp->index); - return; - case IR::UInt32Type: - Q_ASSERT(source->type == IR::UInt32Type); - _as->toUInt32Register(source, (RegisterID) targetTemp->index); - return; - default: - Q_ASSERT(!"Unreachable"); - break; - } - } - - WriteBarrier::Type barrier; - Pointer addr = _as->loadAddressForWriting(JITTargetPlatform::ReturnValueRegister, target, &barrier); - // The target is not a physical register, nor is the source. So we can do a memory-to-memory copy: - _as->memcopyValue(addr, source, JITTargetPlatform::ScratchRegister, barrier); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::swapValues(IR::Expr *source, IR::Expr *target) -{ - IR::Temp *sourceTemp = source->asTemp(); - IR::Temp *targetTemp = target->asTemp(); - - if (sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister) { - if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { - Q_ASSERT(sourceTemp->type == targetTemp->type); - - if (sourceTemp->type == IR::DoubleType) { - _as->moveDouble((FPRegisterID) targetTemp->index, JITTargetPlatform::FPGpr0); - _as->moveDouble((FPRegisterID) sourceTemp->index, - (FPRegisterID) targetTemp->index); - _as->moveDouble(JITTargetPlatform::FPGpr0, (FPRegisterID) sourceTemp->index); - } else { - _as->swap((RegisterID) sourceTemp->index, - (RegisterID) targetTemp->index); - } - return; - } - } else if (!sourceTemp || sourceTemp->kind == IR::Temp::StackSlot) { - if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { - // Note: a swap for two stack-slots can involve different types. - WriteBarrier::Type barrierForSource, barrierForTarget; - Pointer sAddr = _as->loadAddressForWriting(JITTargetPlatform::ScratchRegister, source, &barrierForSource); - Pointer tAddr = _as->loadAddressForWriting(JITTargetPlatform::ReturnValueRegister, target, &barrierForTarget); - _as->loadRawValue(sAddr, JITTargetPlatform::FPGpr0); - _as->loadRawValue(tAddr, JITTargetPlatform::FPGpr1); - _as->storeRawValue(JITTargetPlatform::FPGpr1, sAddr, barrierForSource); - _as->storeRawValue(JITTargetPlatform::FPGpr0, tAddr, barrierForTarget); - return; - } - } - - IR::Expr *memExpr = !sourceTemp || sourceTemp->kind == IR::Temp::StackSlot ? source : target; - IR::Temp *regTemp = sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister ? sourceTemp - : targetTemp; - Q_ASSERT(memExpr); - Q_ASSERT(regTemp); - - WriteBarrier::Type barrier; - Pointer addr = _as->loadAddressForWriting(JITTargetPlatform::ReturnValueRegister, memExpr, &barrier); - if (regTemp->type == IR::DoubleType) { - _as->loadDouble(addr, JITTargetPlatform::FPGpr0); - _as->storeDouble((FPRegisterID) regTemp->index, addr, barrier); - _as->moveDouble(JITTargetPlatform::FPGpr0, (FPRegisterID) regTemp->index); - } else if (regTemp->type == IR::UInt32Type) { - _as->toUInt32Register(addr, JITTargetPlatform::ScratchRegister); - _as->storeUInt32((RegisterID) regTemp->index, addr, barrier); - _as->move(JITTargetPlatform::ScratchRegister, (RegisterID) regTemp->index); - } else { - _as->load32(addr, JITTargetPlatform::ScratchRegister); - _as->store32((RegisterID) regTemp->index, addr); - if (regTemp->type != memExpr->type) { - addr.offset += 4; - quint32 tag; - switch (regTemp->type) { - case IR::BoolType: - tag = quint32(JITAssembler::ValueTypeInternal::Boolean); - break; - case IR::SInt32Type: - tag = quint32(JITAssembler::ValueTypeInternal::Integer); - break; - default: - tag = 31337; // bogus value - Q_UNREACHABLE(); - } - _as->store32(TrustedImm32(tag), addr); - _as->emitWriteBarrier(addr, barrier); - } - _as->move(JITTargetPlatform::ScratchRegister, (RegisterID) regTemp->index); - } -} - -#define setOp(op, opName, operation) \ - do { \ - op = typename JITAssembler::RuntimeCall(QV4::Runtime::operation); opName = "Runtime::" isel_stringIfy(operation); \ - needsExceptionCheck = QV4::Runtime::Method_##operation##_NeedsExceptionCheck; \ - } while (0) -#define setOpContext(op, opName, operation) \ - do { \ - opContext = typename JITAssembler::RuntimeCall(QV4::Runtime::operation); opName = "Runtime::" isel_stringIfy(operation); \ - needsExceptionCheck = QV4::Runtime::Method_##operation##_NeedsExceptionCheck; \ - } while (0) - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target) -{ - QV4::JIT::Unop<JITAssembler> unop(_as, oper); - unop.generate(source, target); -} - - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) -{ - QV4::JIT::Binop<JITAssembler> binop(_as, oper); - binop.generate(leftSource, rightSource, target); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) -{ - prepareCallData(args, base); - - if (kind == IR::Member::MemberOfQmlScopeObject) - generateRuntimeCall(_as, result, callQmlScopeObjectProperty, - JITTargetPlatform::EngineRegister, - TrustedImm32(propertyIndex), - baseAddressForCallData()); - else if (kind == IR::Member::MemberOfQmlContextObject) - generateRuntimeCall(_as, result, callQmlContextObjectProperty, - JITTargetPlatform::EngineRegister, - TrustedImm32(propertyIndex), - baseAddressForCallData()); - else - Q_ASSERT(false); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, - IR::Expr *result) -{ - Q_ASSERT(base != 0); - - prepareCallData(args, base); - - if (useFastLookups) { - uint index = registerGetterLookup(name); - generateRuntimeCall(_as, result, callPropertyLookup, - JITTargetPlatform::EngineRegister, - TrustedImm32(index), - baseAddressForCallData()); - } else { - generateRuntimeCall(_as, result, callProperty, JITTargetPlatform::EngineRegister, - StringToIndex(name), - baseAddressForCallData()); - } -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, - IR::Expr *result) -{ - Q_ASSERT(base != 0); - - prepareCallData(args, base); - generateRuntimeCall(_as, result, callElement, JITTargetPlatform::EngineRegister, - PointerToValue(index), - baseAddressForCallData()); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::convertType(IR::Expr *source, IR::Expr *target) -{ - switch (target->type) { - case IR::DoubleType: - convertTypeToDouble(source, target); - break; - case IR::BoolType: - convertTypeToBool(source, target); - break; - case IR::SInt32Type: - convertTypeToSInt32(source, target); - break; - case IR::UInt32Type: - convertTypeToUInt32(source, target); - break; - default: - convertTypeSlowPath(source, target); - break; - } -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::convertTypeSlowPath(IR::Expr *source, IR::Expr *target) -{ - Q_ASSERT(target->type != IR::BoolType); - - if (target->type & IR::NumberType) - unop(IR::OpUPlus, source, target); - else - copyValue(source, target); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::convertTypeToDouble(IR::Expr *source, IR::Expr *target) -{ - switch (source->type) { - case IR::SInt32Type: - case IR::BoolType: - case IR::NullType: - convertIntToDouble(source, target); - break; - case IR::UInt32Type: - convertUIntToDouble(source, target); - break; - case IR::UndefinedType: - _as->loadDouble(_as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source), JITTargetPlatform::FPGpr0); - _as->storeDouble(JITTargetPlatform::FPGpr0, target); - break; - case IR::StringType: - case IR::VarType: { - // load the tag: - Pointer tagAddr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source); - tagAddr.offset += 4; - _as->load32(tagAddr, JITTargetPlatform::ScratchRegister); - - // check if it's an int32: - Jump isNoInt = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister, - TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Integer))); - convertIntToDouble(source, target); - Jump intDone = _as->jump(); - - // not an int, check if it's NOT a double: - isNoInt.link(_as); - Jump isDbl = _as->generateIsDoubleCheck(JITTargetPlatform::ScratchRegister); - - generateRuntimeCall(_as, target, toDouble, PointerToValue(source)); - Jump noDoubleDone = _as->jump(); - - // it is a double: - isDbl.link(_as); - Pointer addr2 = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source); - IR::Temp *targetTemp = target->asTemp(); - if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { - _as->memcopyValue(target, addr2, JITTargetPlatform::FPGpr0, JITTargetPlatform::ReturnValueRegister); - } else { - _as->loadDouble(addr2, (FPRegisterID) targetTemp->index); - } - - noDoubleDone.link(_as); - intDone.link(_as); - } break; - default: - convertTypeSlowPath(source, target); - break; - } -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::convertTypeToBool(IR::Expr *source, IR::Expr *target) -{ - IR::Temp *sourceTemp = source->asTemp(); - switch (source->type) { - case IR::SInt32Type: - case IR::UInt32Type: - convertIntToBool(source, target); - break; - case IR::DoubleType: { - // The source is in a register if the register allocator is used. If the register - // allocator was not used, then that means that we can use any register for to - // load the double into. - FPRegisterID reg; - if (sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister) - reg = (FPRegisterID) sourceTemp->index; - else - reg = _as->toDoubleRegister(source, (FPRegisterID) 1); - Jump nonZero = _as->branchDoubleNonZero(reg, JITTargetPlatform::FPGpr0); - - // it's 0, so false: - _as->storeBool(false, target); - Jump done = _as->jump(); - - // it's non-zero, so true: - nonZero.link(_as); - _as->storeBool(true, target); - - // done: - done.link(_as); - } break; - case IR::UndefinedType: - case IR::NullType: - _as->storeBool(false, target); - break; - case IR::StringType: - generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toBoolean, - PointerToValue(source)); - _as->storeBool(JITTargetPlatform::ReturnValueRegister, target); - Q_FALLTHROUGH(); - case IR::VarType: - default: - Pointer addr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source); - Pointer tagAddr = addr; - tagAddr.offset += 4; - _as->load32(tagAddr, JITTargetPlatform::ReturnValueRegister); - - // checkif it's a bool: - Jump notBool = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ReturnValueRegister, - TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Boolean))); - _as->load32(addr, JITTargetPlatform::ReturnValueRegister); - Jump boolDone = _as->jump(); - // check if it's an int32: - notBool.link(_as); - Jump fallback = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ReturnValueRegister, - TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Integer))); - _as->load32(addr, JITTargetPlatform::ReturnValueRegister); - Jump isZero = _as->branch32(RelationalCondition::Equal, JITTargetPlatform::ReturnValueRegister, - TrustedImm32(0)); - _as->move(TrustedImm32(1), JITTargetPlatform::ReturnValueRegister); - Jump intDone = _as->jump(); - - // not an int: - fallback.link(_as); - generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toBoolean, - PointerToValue(source)); - - isZero.link(_as); - intDone.link(_as); - boolDone.link(_as); - _as->storeBool(JITTargetPlatform::ReturnValueRegister, target); - - break; - } -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::convertTypeToSInt32(IR::Expr *source, IR::Expr *target) -{ - switch (source->type) { - case IR::VarType: { - JITAssembler::RegisterSizeDependentOps::convertVarToSInt32(_as, source, target); - } break; - case IR::DoubleType: { - Jump success = - _as->branchTruncateDoubleToInt32(_as->toDoubleRegister(source), - JITTargetPlatform::ReturnValueRegister, - BranchTruncateType::BranchIfTruncateSuccessful); - generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, doubleToInt, - PointerToValue(source)); - success.link(_as); - _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target); - } break; - case IR::UInt32Type: - _as->storeInt32(_as->toUInt32Register(source, JITTargetPlatform::ReturnValueRegister), target); - break; - case IR::NullType: - case IR::UndefinedType: - _as->move(TrustedImm32(0), JITTargetPlatform::ReturnValueRegister); - _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target); - break; - case IR::BoolType: - _as->storeInt32(_as->toInt32Register(source, JITTargetPlatform::ReturnValueRegister), target); - break; - case IR::StringType: - default: - generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toInt, - _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source)); - _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target); - break; - } // switch (source->type) -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::convertTypeToUInt32(IR::Expr *source, IR::Expr *target) -{ - switch (source->type) { - case IR::VarType: { - // load the tag: - Pointer tagAddr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source); - tagAddr.offset += 4; - _as->load32(tagAddr, JITTargetPlatform::ScratchRegister); - - // check if it's an int32: - Jump isNoInt = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister, - TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Integer))); - Pointer addr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source); - _as->storeUInt32(_as->toInt32Register(addr, JITTargetPlatform::ScratchRegister), target); - Jump intDone = _as->jump(); - - // not an int: - isNoInt.link(_as); - generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toUInt, - _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source)); - _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target); - - intDone.link(_as); - } break; - case IR::DoubleType: { - FPRegisterID reg = _as->toDoubleRegister(source); - Jump success = - _as->branchTruncateDoubleToUint32(reg, JITTargetPlatform::ReturnValueRegister, - BranchTruncateType::BranchIfTruncateSuccessful); - generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, doubleToUInt, - PointerToValue(source)); - success.link(_as); - _as->storeUInt32(JITTargetPlatform::ReturnValueRegister, target); - } break; - case IR::NullType: - case IR::UndefinedType: - _as->move(TrustedImm32(0), JITTargetPlatform::ReturnValueRegister); - _as->storeUInt32(JITTargetPlatform::ReturnValueRegister, target); - break; - case IR::StringType: - generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toUInt, - PointerToValue(source)); - _as->storeUInt32(JITTargetPlatform::ReturnValueRegister, target); - break; - case IR::SInt32Type: - case IR::BoolType: - _as->storeUInt32(_as->toInt32Register(source, JITTargetPlatform::ReturnValueRegister), target); - break; - default: - break; - } // switch (source->type) -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Expr *result) -{ - Q_ASSERT(func != 0); - prepareCallData(args, 0); - - if (useFastLookups && func->global) { - uint index = registerGlobalGetterLookup(*func->id); - generateRuntimeCall(_as, result, constructGlobalLookup, - JITTargetPlatform::EngineRegister, - TrustedImm32(index), baseAddressForCallData()); - return; - } - - generateRuntimeCall(_as, result, constructActivationProperty, - JITTargetPlatform::EngineRegister, - StringToIndex(*func->id), - baseAddressForCallData()); -} - - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) -{ - prepareCallData(args, base); - if (useFastLookups) { - uint index = registerGetterLookup(name); - generateRuntimeCall(_as, result, constructPropertyLookup, - JITTargetPlatform::EngineRegister, - TrustedImm32(index), - baseAddressForCallData()); - return; - } - - generateRuntimeCall(_as, result, constructProperty, JITTargetPlatform::EngineRegister, - StringToIndex(name), - baseAddressForCallData()); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) -{ - Q_ASSERT(value != 0); - - prepareCallData(args, 0); - generateRuntimeCall(_as, result, constructValue, - JITTargetPlatform::EngineRegister, - Reference(value), - baseAddressForCallData()); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::visitJump(IR::Jump *s) -{ - if (!_removableJumps.at(_block->index())) - _as->jumpToBlock(_block, s->target); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::visitCJump(IR::CJump *s) -{ - IR::Temp *t = s->cond->asTemp(); - if (t || s->cond->asArgLocal()) { - RegisterID reg; - if (t && t->kind == IR::Temp::PhysicalRegister) { - Q_ASSERT(t->type == IR::BoolType); - reg = (RegisterID) t->index; - } else if (t && t->kind == IR::Temp::StackSlot && t->type == IR::BoolType) { - reg = JITTargetPlatform::ReturnValueRegister; - _as->toInt32Register(t, reg); - } else { - Address temp = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, s->cond); - Address tag = temp; - tag.offset += QV4::Value::tagOffset(); - Jump booleanConversion = _as->branch32(RelationalCondition::NotEqual, tag, - TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Boolean))); - - Address data = temp; - data.offset += QV4::Value::valueOffset(); - _as->load32(data, JITTargetPlatform::ReturnValueRegister); - Jump testBoolean = _as->jump(); - - booleanConversion.link(_as); - reg = JITTargetPlatform::ReturnValueRegister; - generateRuntimeCall(_as, reg, toBoolean, Reference(s->cond)); - - testBoolean.link(_as); - } - - _as->generateCJumpOnNonZero(reg, _block, s->iftrue, s->iffalse); - return; - } else if (IR::Const *c = s->cond->asConst()) { - // TODO: SSA optimization for constant condition evaluation should remove this. - // See also visitCJump() in RegAllocInfo. - generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toBoolean, - PointerToValue(c)); - _as->generateCJumpOnNonZero(JITTargetPlatform::ReturnValueRegister, _block, s->iftrue, s->iffalse); - return; - } else if (IR::Binop *b = s->cond->asBinop()) { - if (b->left->type == IR::DoubleType && b->right->type == IR::DoubleType - && visitCJumpDouble(b->op, b->left, b->right, s->iftrue, s->iffalse)) - return; - - if (b->left->type == IR::SInt32Type && b->right->type == IR::SInt32Type - && visitCJumpSInt32(b->op, b->left, b->right, s->iftrue, s->iffalse)) - return; - - if (b->op == IR::OpStrictEqual || b->op == IR::OpStrictNotEqual) { - visitCJumpStrict(b, s->iftrue, s->iffalse); - return; - } - if (b->op == IR::OpEqual || b->op == IR::OpNotEqual) { - visitCJumpEqual(b, s->iftrue, s->iffalse); - return; - } - - typename JITAssembler::RuntimeCall op; - typename JITAssembler::RuntimeCall opContext; - const char *opName = 0; - bool needsExceptionCheck; - switch (b->op) { - default: Q_UNREACHABLE(); Q_ASSERT(!"todo"); break; - case IR::OpGt: setOp(op, opName, compareGreaterThan); break; - case IR::OpLt: setOp(op, opName, compareLessThan); break; - case IR::OpGe: setOp(op, opName, compareGreaterEqual); break; - case IR::OpLe: setOp(op, opName, compareLessEqual); break; - case IR::OpEqual: setOp(op, opName, compareEqual); break; - case IR::OpNotEqual: setOp(op, opName, compareNotEqual); break; - case IR::OpStrictEqual: setOp(op, opName, compareStrictEqual); break; - case IR::OpStrictNotEqual: setOp(op, opName, compareStrictNotEqual); break; - case IR::OpInstanceof: setOpContext(op, opName, compareInstanceof); break; - case IR::OpIn: setOpContext(op, opName, compareIn); break; - } // switch - - // TODO: in SSA optimization, do constant expression evaluation. - // The case here is, for example: - // if (true === true) ..... - // Of course, after folding the CJUMP to a JUMP, dead-code (dead-basic-block) - // elimination (which isn't there either) would remove the whole else block. - if (opContext.isValid()) - _as->generateFunctionCallImp(needsExceptionCheck, - JITTargetPlatform::ReturnValueRegister, opName, opContext, - JITTargetPlatform::EngineRegister, - PointerToValue(b->left), - PointerToValue(b->right)); - else - _as->generateFunctionCallImp(needsExceptionCheck, - JITTargetPlatform::ReturnValueRegister, opName, op, - PointerToValue(b->left), - PointerToValue(b->right)); - - _as->generateCJumpOnNonZero(JITTargetPlatform::ReturnValueRegister, _block, s->iftrue, s->iffalse); - return; - } - Q_UNREACHABLE(); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::visitRet(IR::Ret *s) -{ - _as->returnFromFunction(s, regularRegistersToSave, fpRegistersToSave); -} - -template <typename JITAssembler> -int InstructionSelection<JITAssembler>::prepareVariableArguments(IR::ExprList* args) -{ - int argc = 0; - for (IR::ExprList *it = args; it; it = it->next) { - ++argc; - } - - int i = 0; - for (IR::ExprList *it = args; it; it = it->next, ++i) { - IR::Expr *arg = it->expr; - Q_ASSERT(arg != 0); - Pointer dst(_as->stackLayout().argumentAddressForCall(i)); - if (arg->asTemp() && arg->asTemp()->kind != IR::Temp::PhysicalRegister) - _as->memcopyValue(dst, arg->asTemp(), JITTargetPlatform::ScratchRegister, WriteBarrier::NoBarrier); - else - _as->copyValue(dst, arg, WriteBarrier::NoBarrier); - } - - return argc; -} - -template <typename JITAssembler> -int InstructionSelection<JITAssembler>::prepareCallData(IR::ExprList* args, IR::Expr *thisObject) -{ - int argc = 0; - for (IR::ExprList *it = args; it; it = it->next) { - ++argc; - } - - Pointer p = _as->stackLayout().callDataAddress(offsetof(CallData, tag)); - _as->store32(TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Integer)), p); - p = _as->stackLayout().callDataAddress(offsetof(CallData, argc)); - _as->store32(TrustedImm32(argc), p); - p = _as->stackLayout().callDataAddress(offsetof(CallData, thisObject)); - if (!thisObject) - _as->storeValue(JITAssembler::TargetPrimitive::undefinedValue(), p, WriteBarrier::NoBarrier); - else - _as->copyValue(p, thisObject, WriteBarrier::NoBarrier); - - int i = 0; - for (IR::ExprList *it = args; it; it = it->next, ++i) { - IR::Expr *arg = it->expr; - Q_ASSERT(arg != 0); - Pointer dst(_as->stackLayout().argumentAddressForCall(i)); - if (arg->asTemp() && arg->asTemp()->kind != IR::Temp::PhysicalRegister) - _as->memcopyValue(dst, arg->asTemp(), JITTargetPlatform::ScratchRegister, WriteBarrier::NoBarrier); - else - _as->copyValue(dst, arg, WriteBarrier::NoBarrier); - } - return argc; -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::calculateRegistersToSave(const RegisterInformation &used) -{ - regularRegistersToSave.clear(); - fpRegistersToSave.clear(); - - for (const RegisterInfo &ri : JITTargetPlatform::getRegisterInfo()) { - if (JITTargetPlatform::gotRegister != -1 && ri.isRegularRegister() && ri.reg<RegisterID>() == JITTargetPlatform::gotRegister) { - regularRegistersToSave.append(ri); - continue; - } - if (ri.isCallerSaved()) - continue; - if (ri.isRegularRegister()) { - if (ri.isPredefined() || used.contains(ri)) - regularRegistersToSave.append(ri); - } else { - Q_ASSERT(ri.isFloatingPoint()); - if (ri.isPredefined() || used.contains(ri)) - fpRegistersToSave.append(ri); - } - } -} - -QT_BEGIN_NAMESPACE -namespace QV4 { -bool operator==(const Primitive &v1, const Primitive &v2) -{ - return v1.rawValue() == v2.rawValue(); -} -} // QV4 namespace -QT_END_NAMESPACE - -template <typename JITAssembler> -bool InstructionSelection<JITAssembler>::visitCJumpDouble(IR::AluOp op, IR::Expr *left, IR::Expr *right, - IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) -{ - if (_as->nextBlock() == iftrue) { - Jump target = _as->branchDouble(true, op, left, right); - _as->addPatch(iffalse, target); - } else { - Jump target = _as->branchDouble(false, op, left, right); - _as->addPatch(iftrue, target); - _as->jumpToBlock(_block, iffalse); - } - return true; -} - -template <typename JITAssembler> -bool InstructionSelection<JITAssembler>::visitCJumpSInt32(IR::AluOp op, IR::Expr *left, IR::Expr *right, - IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) -{ - if (_as->nextBlock() == iftrue) { - Jump target = _as->branchInt32(true, op, left, right); - _as->addPatch(iffalse, target); - } else { - Jump target = _as->branchInt32(false, op, left, right); - _as->addPatch(iftrue, target); - _as->jumpToBlock(_block, iffalse); - } - return true; -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) -{ - Q_ASSERT(binop->op == IR::OpStrictEqual || binop->op == IR::OpStrictNotEqual); - - if (visitCJumpStrictNull(binop, trueBlock, falseBlock)) - return; - if (visitCJumpStrictUndefined(binop, trueBlock, falseBlock)) - return; - if (visitCJumpStrictBool(binop, trueBlock, falseBlock)) - return; - - IR::Expr *left = binop->left; - IR::Expr *right = binop->right; - - generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, compareStrictEqual, - PointerToValue(left), PointerToValue(right)); - _as->generateCJumpOnCompare(binop->op == IR::OpStrictEqual ? RelationalCondition::NotEqual : RelationalCondition::Equal, - JITTargetPlatform::ReturnValueRegister, TrustedImm32(0), - _block, trueBlock, falseBlock); -} - -// Only load the non-null temp. -template <typename JITAssembler> -bool InstructionSelection<JITAssembler>::visitCJumpStrictNull(IR::Binop *binop, - IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) -{ - IR::Expr *varSrc = 0; - if (binop->left->type == IR::VarType && binop->right->type == IR::NullType) - varSrc = binop->left; - else if (binop->left->type == IR::NullType && binop->right->type == IR::VarType) - varSrc = binop->right; - if (!varSrc) - return false; - - if (varSrc->asTemp() && varSrc->asTemp()->kind == IR::Temp::PhysicalRegister) { - _as->jumpToBlock(_block, falseBlock); - return true; - } - - if (IR::Const *c = varSrc->asConst()) { - if (c->type == IR::NullType) - _as->jumpToBlock(_block, trueBlock); - else - _as->jumpToBlock(_block, falseBlock); - return true; - } - - Pointer tagAddr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, varSrc); - tagAddr.offset += 4; - const RegisterID tagReg = JITTargetPlatform::ScratchRegister; - _as->load32(tagAddr, tagReg); - - RelationalCondition cond = binop->op == IR::OpStrictEqual ? RelationalCondition::Equal - : RelationalCondition::NotEqual; - const TrustedImm32 tag{quint32(JITAssembler::ValueTypeInternal::Null)}; - _as->generateCJumpOnCompare(cond, tagReg, tag, _block, trueBlock, falseBlock); - return true; -} - -template <typename JITAssembler> -bool InstructionSelection<JITAssembler>::visitCJumpStrictUndefined(IR::Binop *binop, - IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) -{ - IR::Expr *varSrc = 0; - if (binop->left->type == IR::VarType && binop->right->type == IR::UndefinedType) - varSrc = binop->left; - else if (binop->left->type == IR::UndefinedType && binop->right->type == IR::VarType) - varSrc = binop->right; - if (!varSrc) - return false; - - if (varSrc->asTemp() && varSrc->asTemp()->kind == IR::Temp::PhysicalRegister) { - _as->jumpToBlock(_block, falseBlock); - return true; - } - - if (IR::Const *c = varSrc->asConst()) { - if (c->type == IR::UndefinedType) - _as->jumpToBlock(_block, trueBlock); - else - _as->jumpToBlock(_block, falseBlock); - return true; - } - - RelationalCondition cond = binop->op == IR::OpStrictEqual ? RelationalCondition::Equal - : RelationalCondition::NotEqual; - const RegisterID tagReg = JITTargetPlatform::ReturnValueRegister; - _as->generateCJumpOnUndefined(cond, varSrc, JITTargetPlatform::ScratchRegister, tagReg, _block, trueBlock, falseBlock); - return true; -} - -template <typename JITAssembler> -bool InstructionSelection<JITAssembler>::visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) -{ - IR::Expr *boolSrc = 0, *otherSrc = 0; - if (binop->left->type == IR::BoolType) { - boolSrc = binop->left; - otherSrc = binop->right; - } else if (binop->right->type == IR::BoolType) { - boolSrc = binop->right; - otherSrc = binop->left; - } else { - // neither operands are statically typed as bool, so bail out. - return false; - } - if (otherSrc->type == IR::UnknownType) { - // Ok, we really need to call into the runtime. - // (This case doesn't happen when the optimizer ran, because everything will be typed (yes, - // possibly as "var" meaning anything), but it does happen for $0===true, which is generated - // for things where the optimizer didn't run (like functions with a try block).) - return false; - } - - RelationalCondition cond = binop->op == IR::OpStrictEqual ? RelationalCondition::Equal - : RelationalCondition::NotEqual; - - if (otherSrc->type == IR::BoolType) { // both are boolean - RegisterID one = _as->toBoolRegister(boolSrc, JITTargetPlatform::ReturnValueRegister); - RegisterID two = _as->toBoolRegister(otherSrc, JITTargetPlatform::ScratchRegister); - _as->generateCJumpOnCompare(cond, one, two, _block, trueBlock, falseBlock); - return true; - } - - if (otherSrc->type != IR::VarType) { - _as->jumpToBlock(_block, falseBlock); - return true; - } - - Pointer otherAddr = _as->loadAddressForReading(JITTargetPlatform::ReturnValueRegister, otherSrc); - otherAddr.offset += 4; // tag address - - // check if the tag of the var operand is indicates 'boolean' - _as->load32(otherAddr, JITTargetPlatform::ScratchRegister); - Jump noBool = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister, - TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Boolean))); - if (binop->op == IR::OpStrictEqual) - _as->addPatch(falseBlock, noBool); - else - _as->addPatch(trueBlock, noBool); - - // ok, both are boolean, so let's load them and compare them. - otherAddr.offset -= 4; // int_32 address - _as->load32(otherAddr, JITTargetPlatform::ReturnValueRegister); - RegisterID boolReg = _as->toBoolRegister(boolSrc, JITTargetPlatform::ScratchRegister); - _as->generateCJumpOnCompare(cond, boolReg, JITTargetPlatform::ReturnValueRegister, _block, trueBlock, - falseBlock); - return true; -} - -template <typename JITAssembler> -bool InstructionSelection<JITAssembler>::visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Binop *binop, - IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) -{ - Q_ASSERT(nullOrUndef == IR::NullType || nullOrUndef == IR::UndefinedType); - - IR::Expr *varSrc = 0; - if (binop->left->type == IR::VarType && binop->right->type == nullOrUndef) - varSrc = binop->left; - else if (binop->left->type == nullOrUndef && binop->right->type == IR::VarType) - varSrc = binop->right; - if (!varSrc) - return false; - - if (varSrc->asTemp() && varSrc->asTemp()->kind == IR::Temp::PhysicalRegister) { - _as->jumpToBlock(_block, falseBlock); - return true; - } - - if (IR::Const *c = varSrc->asConst()) { - if (c->type == nullOrUndef) - _as->jumpToBlock(_block, trueBlock); - else - _as->jumpToBlock(_block, falseBlock); - return true; - } - - Pointer tagAddr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, varSrc); - tagAddr.offset += 4; - const RegisterID tagReg = JITTargetPlatform::ReturnValueRegister; - _as->load32(tagAddr, tagReg); - - if (binop->op == IR::OpNotEqual) - qSwap(trueBlock, falseBlock); - Jump isNull = _as->branch32(RelationalCondition::Equal, tagReg, TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Null))); - Jump isNotUndefinedTag = _as->branch32(RelationalCondition::NotEqual, tagReg, TrustedImm32(int(QV4::Value::Managed_Type_Internal))); - tagAddr.offset -= 4; - _as->load32(tagAddr, tagReg); - Jump isNotUndefinedValue = _as->branch32(RelationalCondition::NotEqual, tagReg, TrustedImm32(0)); - _as->addPatch(trueBlock, isNull); - _as->addPatch(falseBlock, isNotUndefinedTag); - _as->addPatch(falseBlock, isNotUndefinedValue); - _as->jumpToBlock(_block, trueBlock); - - return true; -} - - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::visitCJumpEqual(IR::Binop *binop, IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) -{ - Q_ASSERT(binop->op == IR::OpEqual || binop->op == IR::OpNotEqual); - - if (visitCJumpNullUndefined(IR::NullType, binop, trueBlock, falseBlock)) - return; - - IR::Expr *left = binop->left; - IR::Expr *right = binop->right; - - generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, compareEqual, - PointerToValue(left), PointerToValue(right)); - _as->generateCJumpOnCompare(binop->op == IR::OpEqual ? RelationalCondition::NotEqual : RelationalCondition::Equal, - JITTargetPlatform::ReturnValueRegister, TrustedImm32(0), - _block, trueBlock, falseBlock); -} - -template <typename JITAssembler> -QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory<JITAssembler>::createUnitForLoading() -{ - QQmlRefPointer<CompiledData::CompilationUnit> result; - result.adopt(new JIT::CompilationUnit); - return result; -} - -#endif // ENABLE(ASSEMBLER) - -QT_BEGIN_NAMESPACE -namespace QV4 { namespace JIT { -#if ENABLE(ASSEMBLER) -template class Q_QML_EXPORT InstructionSelection<>; -template class Q_QML_EXPORT ISelFactory<>; -#endif - -#if defined(V4_BOOTSTRAP) - -Q_QML_EXPORT QV4::EvalISelFactory *createISelForArchitecture(const QString &architecture) -{ -#if ENABLE(ASSEMBLER) - using ARMv7CrossAssembler = QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>; - using ARM64CrossAssembler = QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>; - - if (architecture == QLatin1String("arm")) - return new ISelFactory<ARMv7CrossAssembler>; - else if (architecture == QLatin1String("arm64")) - return new ISelFactory<ARM64CrossAssembler>; - - QString hostArch; -#if CPU(ARM_THUMB2) - hostArch = QStringLiteral("arm"); -#elif CPU(MIPS) - hostArch = QStringLiteral("mips"); -#elif CPU(X86) - hostArch = QStringLiteral("i386"); -#elif CPU(X86_64) - hostArch = QStringLiteral("x86_64"); -#endif - if (!hostArch.isEmpty() && architecture == hostArch) - return new ISelFactory<>; -#endif // ENABLE(ASSEMBLER) - - return nullptr; -} - -#endif -} } -QT_END_NAMESPACE - diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h deleted file mode 100644 index 869f857c41..0000000000 --- a/src/qml/jit/qv4isel_masm_p.h +++ /dev/null @@ -1,319 +0,0 @@ -/**************************************************************************** -** -** 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 QV4ISEL_MASM_P_H -#define QV4ISEL_MASM_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "private/qv4global_p.h" -#include "private/qv4jsir_p.h" -#include "private/qv4isel_p.h" -#include "private/qv4isel_util_p.h" -#include "private/qv4util_p.h" -#include "private/qv4value_p.h" -#include "private/qv4lookup_p.h" - -#include <QtCore/QHash> -#include <QtCore/QStack> -#include <config.h> -#include <wtf/Vector.h> - -#include "qv4assembler_p.h" - -#if ENABLE(ASSEMBLER) - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace JIT { - -template <typename JITAssembler = Assembler<DefaultAssemblerTargetConfiguration>> -class Q_QML_EXPORT InstructionSelection: - protected IR::IRDecoder, - public EvalInstructionSelection -{ -public: - InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory); - ~InstructionSelection(); - - void run(int functionIndex) override; - -protected: - QQmlRefPointer<QV4::CompiledData::CompilationUnit> backendCompileStep() override; - - void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result) override; - void callBuiltinTypeofQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::Expr *result) override; - void callBuiltinTypeofMember(IR::Expr *base, const QString &name, IR::Expr *result) override; - void callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) override; - void callBuiltinTypeofName(const QString &name, IR::Expr *result) override; - void callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result) override; - void callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result) override; - void callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) override; - void callBuiltinDeleteName(const QString &name, IR::Expr *result) override; - void callBuiltinDeleteValue(IR::Expr *result) override; - void callBuiltinThrow(IR::Expr *arg) override; - void callBuiltinReThrow() override; - void callBuiltinUnwindException(IR::Expr *) override; - void callBuiltinPushCatchScope(const QString &exceptionName) override; - void callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result) override; - void callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result) override; - void callBuiltinPushWithScope(IR::Expr *arg) override; - void callBuiltinPopScope() override; - void callBuiltinDeclareVar(bool deletable, const QString &name) override; - void callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args) override; - void callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray) override; - void callBuiltinSetupArgumentObject(IR::Expr *result) override; - void callBuiltinConvertThisToObject() override; - void callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) override; - void callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) override; - void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) override; - void callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, IR::Expr *result) override; - void convertType(IR::Expr *source, IR::Expr *target) override; - void loadThisObject(IR::Expr *temp) override; - void loadQmlContext(IR::Expr *target) override; - void loadQmlImportedScripts(IR::Expr *target) override; - void loadQmlSingleton(const QString &name, IR::Expr *target) override; - void loadConst(IR::Const *sourceConst, IR::Expr *target) override; - void loadString(const QString &str, IR::Expr *target) override; - void loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target) override; - void getActivationProperty(const IR::Name *name, IR::Expr *target) override; - void setActivationProperty(IR::Expr *source, const QString &targetName) override; - void initClosure(IR::Closure *closure, IR::Expr *target) override; - void getProperty(IR::Expr *base, const QString &name, IR::Expr *target) override; - void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target) override; - void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target) override; - void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName) override; - void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) override; - void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) override; - void getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) override; - void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) override; - void copyValue(IR::Expr *source, IR::Expr *target) override; - void swapValues(IR::Expr *source, IR::Expr *target) override; - void unop(IR::AluOp oper, IR::Expr *sourceTemp, IR::Expr *target) override; - void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) override; - - using Address = typename JITAssembler::Address; - using Pointer = typename JITAssembler::Pointer; - using PointerToValue = typename JITAssembler::PointerToValue; - using RegisterID = typename JITAssembler::RegisterID; - using FPRegisterID = typename JITAssembler::FPRegisterID; - using ResultCondition = typename JITAssembler::ResultCondition; - using TrustedImm32 = typename JITAssembler::TrustedImm32; - using TrustedImm64 = typename JITAssembler::TrustedImm64; - using Label = typename JITAssembler::Label; - using Jump = typename JITAssembler::Jump; - using StringToIndex = typename JITAssembler::StringToIndex; - using Reference = typename JITAssembler::Reference; - using RelationalCondition = typename JITAssembler::RelationalCondition; - using BranchTruncateType = typename JITAssembler::BranchTruncateType; - using RuntimeCall = typename JITAssembler::RuntimeCall; - - using JITTargetPlatform = typename JITAssembler::JITTargetPlatform; - - Address addressForArgument(int index) const - { - // FramePointerRegister points to its old value on the stack, and above - // it we have the return address, hence the need to step over two - // values before reaching the first argument. - return Address(JITTargetPlatform::FramePointerRegister, (index + 2) * JITTargetPlatform::RegisterSize); - } - - Pointer baseAddressForCallArguments() - { - return _as->stackLayout().argumentAddressForCall(0); - } - - Pointer baseAddressForCallData() - { - return _as->stackLayout().callDataAddress(); - } - - void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Expr *result) override; - void constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr*result) override; - void constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) override; - - void visitJump(IR::Jump *) override; - void visitCJump(IR::CJump *) override; - void visitRet(IR::Ret *) override; - - bool visitCJumpDouble(IR::AluOp op, IR::Expr *left, IR::Expr *right, - IR::BasicBlock *iftrue, IR::BasicBlock *iffalse); - bool visitCJumpSInt32(IR::AluOp op, IR::Expr *left, IR::Expr *right, - IR::BasicBlock *iftrue, IR::BasicBlock *iffalse); - void visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); - bool visitCJumpStrictNull(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); - bool visitCJumpStrictUndefined(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); - bool visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); - bool visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Binop *binop, - IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); - void visitCJumpEqual(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); - -private: - void convertTypeSlowPath(IR::Expr *source, IR::Expr *target); - void convertTypeToDouble(IR::Expr *source, IR::Expr *target); - void convertTypeToBool(IR::Expr *source, IR::Expr *target); - void convertTypeToSInt32(IR::Expr *source, IR::Expr *target); - void convertTypeToUInt32(IR::Expr *source, IR::Expr *target); - - void convertIntToDouble(IR::Expr *source, IR::Expr *target) - { - if (IR::Temp *targetTemp = target->asTemp()) { - if (targetTemp->kind == IR::Temp::PhysicalRegister) { - if (IR::Temp *sourceTemp = source->asTemp()) { - if (sourceTemp->kind == IR::Temp::PhysicalRegister) { - _as->convertInt32ToDouble((RegisterID) sourceTemp->index, - (FPRegisterID) targetTemp->index); - } else { - _as->convertInt32ToDouble(_as->loadAddressForReading(JITTargetPlatform::ReturnValueRegister, sourceTemp), - (FPRegisterID) targetTemp->index); - } - } else { - _as->convertInt32ToDouble(_as->toInt32Register(source, JITTargetPlatform::ScratchRegister), - (FPRegisterID) targetTemp->index); - } - - return; - } - } - - _as->convertInt32ToDouble(_as->toInt32Register(source, JITTargetPlatform::ScratchRegister), - JITTargetPlatform::FPGpr0); - _as->storeDouble(JITTargetPlatform::FPGpr0, target); - } - - void convertUIntToDouble(IR::Expr *source, IR::Expr *target) - { - RegisterID tmpReg = JITTargetPlatform::ScratchRegister; - RegisterID reg = _as->toInt32Register(source, tmpReg); - - if (IR::Temp *targetTemp = target->asTemp()) { - if (targetTemp->kind == IR::Temp::PhysicalRegister) { - _as->convertUInt32ToDouble(reg, (FPRegisterID) targetTemp->index, tmpReg); - return; - } - } - - _as->convertUInt32ToDouble(_as->toUInt32Register(source, tmpReg), - JITTargetPlatform::FPGpr0, tmpReg); - _as->storeDouble(JITTargetPlatform::FPGpr0, target); - } - - void convertIntToBool(IR::Expr *source, IR::Expr *target) - { - RegisterID reg = JITTargetPlatform::ScratchRegister; - - if (IR::Temp *targetTemp = target->asTemp()) - if (targetTemp->kind == IR::Temp::PhysicalRegister) - reg = (RegisterID) targetTemp->index; - _as->move(_as->toInt32Register(source, reg), reg); - _as->compare32(RelationalCondition::NotEqual, reg, TrustedImm32(0), reg); - _as->storeBool(reg, target); - } - - int prepareVariableArguments(IR::ExprList* args); - int prepareCallData(IR::ExprList* args, IR::Expr *thisObject); - - void calculateRegistersToSave(const RegisterInformation &used); - - template <typename Retval, typename Arg1, typename Arg2, typename Arg3, typename Arg4> - void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) - { - // Note: using the return value register is intentional: for ABIs where the first parameter - // goes into the same register as the return value (currently only ARM), the prepareCall - // will combine loading the looupAddr into the register and calculating the indirect call - // address. - Pointer lookupAddr(JITTargetPlatform::ReturnValueRegister, index * sizeof(QV4::Lookup)); - - _as->generateFunctionCallImp(true, retval, "lookup getter/setter", - typename JITAssembler::LookupCall(lookupAddr, getterSetterOffset), lookupAddr, - arg1, arg2, arg3, arg4); - } - - template <typename Retval, typename Arg1, typename Arg2> - void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2) - { - generateLookupCall(retval, index, getterSetterOffset, arg1, arg2, typename JITAssembler::VoidType()); - } - - template <typename Retval, typename Arg1, typename Arg2, typename Arg3> - void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2, Arg3 arg3) - { - generateLookupCall(retval, index, getterSetterOffset, arg1, arg2, arg3, typename JITAssembler::VoidType()); - } - - IR::BasicBlock *_block; - BitVector _removableJumps; - JITAssembler* _as; - - QScopedPointer<CompilationUnit> compilationUnit; - QQmlEnginePrivate *qmlEngine; - RegisterInformation regularRegistersToSave; - RegisterInformation fpRegistersToSave; -}; - -template <typename JITAssembler = Assembler<DefaultAssemblerTargetConfiguration>> -class Q_QML_EXPORT ISelFactory: public EvalISelFactory -{ -public: - ISelFactory() : EvalISelFactory(QStringLiteral("jit")) {} - virtual ~ISelFactory() {} - EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) override final - { return new InstructionSelection<JITAssembler>(qmlEngine, execAllocator, module, jsGenerator, this); } - bool jitCompileRegexps() const override final - { return true; } - QQmlRefPointer<CompiledData::CompilationUnit> createUnitForLoading() override final; -}; - -} // end of namespace JIT -} // end of namespace QV4 - -QT_END_NAMESPACE - -#endif // ENABLE(ASSEMBLER) - -#endif // QV4ISEL_MASM_P_H diff --git a/src/qml/jit/qv4jit.cpp b/src/qml/jit/qv4jit.cpp new file mode 100644 index 0000000000..a8bdd20fbd --- /dev/null +++ b/src/qml/jit/qv4jit.cpp @@ -0,0 +1,1365 @@ +/**************************************************************************** +** +** 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 "qv4jit_p.h" +#include "qv4assembler_p.h" +#include <private/qv4lookup_p.h> + +#ifdef V4_ENABLE_JIT + +QT_USE_NAMESPACE +using namespace QV4; +using namespace QV4::JIT; +using namespace QV4::Moth; + +ByteCodeHandler::~ByteCodeHandler() +{ +} + +#define DISPATCH_INSTRUCTION(name, nargs, ...) \ + generate_##name( \ + __VA_ARGS__ \ + ); + +#define DECODE_AND_DISPATCH(instr) \ + { \ + INSTR_##instr(MOTH_DECODE_WITH_BASE) \ + Q_UNUSED(base_ptr); \ + startInstruction(Instr::Type::instr); \ + _offset = code - start; \ + INSTR_##instr(DISPATCH) \ + endInstruction(Instr::Type::instr); \ + continue; \ + } + +void ByteCodeHandler::decode(const char *code, uint len) +{ + MOTH_JUMP_TABLE; + + const char *start = code; + const char *end = code + len; + while (code < end) { + MOTH_DISPATCH() + + FOR_EACH_MOTH_INSTR(DECODE_AND_DISPATCH) + } +} + +#undef DECODE_AND_DISPATCH +#undef DISPATCH_INSTRUCTION + +BaselineJIT::BaselineJIT(Function *function) + : function(function) + , as(new Assembler(function->compilationUnit->constants)) +{} + +BaselineJIT::~BaselineJIT() +{} + +void BaselineJIT::generate() +{ +// qDebug()<<"jitting" << function->name()->toQString(); + collectLabelsInBytecode(); + + as->generatePrologue(); + decode(reinterpret_cast<const char *>(function->codeData), function->compiledFunction->codeSize); + as->generateEpilogue(); + + as->link(function); +// qDebug()<<"done"; +} + +#define STORE_IP() as->storeInstructionPointer(instructionOffset()) +#define STORE_ACC() as->saveAccumulatorInFrame() + +void BaselineJIT::generate_Ret() +{ + as->ret(); +} + +void BaselineJIT::generate_Debug() { Q_UNREACHABLE(); } + +void BaselineJIT::generate_LoadConst(int index) +{ + as->loadConst(index); +} + +void BaselineJIT::generate_LoadZero() +{ + as->loadValue(Encode(int(0))); +} + +void BaselineJIT::generate_LoadTrue() +{ + as->loadValue(Encode(true)); +} + +void BaselineJIT::generate_LoadFalse() +{ + as->loadValue(Encode(false)); +} + +void BaselineJIT::generate_LoadNull() +{ + as->loadValue(Encode::null()); +} + +void BaselineJIT::generate_LoadUndefined() +{ + as->loadValue(Encode::undefined()); +} + +void BaselineJIT::generate_LoadInt(int value) +{ + //### + as->loadValue(Encode(value)); +} + +void BaselineJIT::generate_MoveConst(int constIndex, int destTemp) +{ + as->copyConst(constIndex, destTemp); +} + +void BaselineJIT::generate_LoadReg(int reg) +{ + as->loadReg(reg); +} + +void BaselineJIT::generate_StoreReg(int reg) +{ + as->storeReg(reg); +} + +void BaselineJIT::generate_MoveReg(int srcReg, int destReg) +{ + as->loadReg(srcReg); + as->storeReg(destReg); +} + +static ReturnedValue loadLocalHelper(const Value &context, int index) +{ + auto cc = static_cast<Heap::CallContext *>(context.m()); + return cc->locals[uint(index)].asReturnedValue(); +} + +void BaselineJIT::generate_LoadLocal(int index) +{ + as->prepareCallWithArgCount(2); + as->passInt32AsArg(index, 1); + as->passRegAsArg(CallData::Context, 0); + JIT_GENERATE_RUNTIME_CALL(loadLocalHelper, Assembler::ResultInAccumulator); +} + +static void storeLocalHelper(ExecutionEngine *engine, const Value &context, int index, const Value &acc) +{ + auto cc = static_cast<Heap::CallContext *>(context.m()); + QV4::WriteBarrier::write(engine, cc, cc->locals.values + index, acc); +} + +void BaselineJIT::generate_StoreLocal(int index) +{ + as->checkException(); + as->prepareCallWithArgCount(4); + STORE_ACC(); + as->passAccumulatorAsArg(3); + as->passInt32AsArg(index, 2); + as->passRegAsArg(CallData::Context, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(storeLocalHelper, Assembler::IgnoreResult); +} + +static inline Heap::CallContext *getScope(Value *stack, int level) +{ + Heap::ExecutionContext *scope = static_cast<ExecutionContext &>(stack[CallData::Context]).d(); + while (level > 0) { + --level; + scope = scope->outer; + } + Q_ASSERT(scope); + return static_cast<Heap::CallContext *>(scope); +} + +static ReturnedValue loadScopedLocalHelper(Value *stack, int scope, int index) +{ + auto cc = getScope(stack, scope); + return cc->locals[uint(index)].asReturnedValue(); +} + +void BaselineJIT::generate_LoadScopedLocal(int scope, int index) +{ + as->prepareCallWithArgCount(3); + as->passInt32AsArg(index, 2); + as->passInt32AsArg(scope, 1); + as->passRegAsArg(0, 0); + JIT_GENERATE_RUNTIME_CALL(loadScopedLocalHelper, Assembler::ResultInAccumulator); +} + +static void storeScopedLocalHelper(ExecutionEngine *engine, Value *stack, int scope, int index, + const Value &acc) +{ + auto cc = getScope(stack, scope); + QV4::WriteBarrier::write(engine, cc, cc->locals.values + index, acc); +} + +void BaselineJIT::generate_StoreScopedLocal(int scope, int index) +{ + as->checkException(); + as->prepareCallWithArgCount(5); + STORE_ACC(); + as->passAccumulatorAsArg(4); + as->passInt32AsArg(index, 3); + as->passInt32AsArg(scope, 2); + as->passRegAsArg(0, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(storeScopedLocalHelper, Assembler::IgnoreResult); +} + +void BaselineJIT::generate_LoadRuntimeString(int stringId) +{ + as->loadString(stringId); +} + +void BaselineJIT::generate_LoadRegExp(int regExpId) +{ + as->prepareCallWithArgCount(2); + as->passInt32AsArg(regExpId, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_regexpLiteral, Assembler::ResultInAccumulator); +} + +void BaselineJIT::generate_LoadClosure(int value) +{ + as->prepareCallWithArgCount(2); + as->passInt32AsArg(value, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_closure, Assembler::ResultInAccumulator); +} + +void BaselineJIT::generate_LoadName(int name) +{ + STORE_IP(); + as->prepareCallWithArgCount(2); + as->passInt32AsArg(name, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadName, Assembler::ResultInAccumulator); + as->checkException(); +} + +static ReturnedValue loadGlobalLookupHelper(ExecutionEngine *engine, QV4::Function *f, int index) +{ + QV4::Lookup *l = f->compilationUnit->runtimeLookups + index; + return l->globalGetter(l, engine); +} + +void BaselineJIT::generate_LoadGlobalLookup(int index) +{ + as->prepareCallWithArgCount(3); + as->passInt32AsArg(index, 2); + as->passFunctionAsArg(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(loadGlobalLookupHelper, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_StoreNameSloppy(int name) +{ + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(3); + as->passAccumulatorAsArg(2); + as->passInt32AsArg(name, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_storeNameSloppy, Assembler::IgnoreResult); + as->checkException(); +} + +void BaselineJIT::generate_StoreNameStrict(int name) +{ + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(3); + as->passAccumulatorAsArg(2); + as->passInt32AsArg(name, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_storeNameStrict, Assembler::IgnoreResult); + as->checkException(); +} + +void BaselineJIT::generate_LoadElement(int base, int index) +{ + STORE_IP(); + as->prepareCallWithArgCount(3); + as->passRegAsArg(index, 2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadElement, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_LoadElementA(int base) +{ + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(3); + as->passAccumulatorAsArg(2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadElement, Assembler::ResultInAccumulator); + as->checkException(); +} + +static void storeElementHelper(QV4::Function *f, const Value &base, const Value &index, const Value &value) +{ + auto engine = f->internalClass->engine; + if (!Runtime::method_storeElement(engine, base, index, value) && f->isStrict()) + engine->throwTypeError(); +} + +void BaselineJIT::generate_StoreElement(int base, int index) +{ + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(4); + as->passAccumulatorAsArg(3); + as->passRegAsArg(index, 2); + as->passRegAsArg(base, 1); + as->passFunctionAsArg(0); + JIT_GENERATE_RUNTIME_CALL(storeElementHelper, Assembler::IgnoreResult); + as->checkException(); +} + +void BaselineJIT::generate_LoadProperty(int name, int base) +{ + STORE_IP(); + as->prepareCallWithArgCount(3); + as->passInt32AsArg(name, 2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadProperty, Assembler::ResultInAccumulator); + as->checkException(); +} +void BaselineJIT::generate_LoadPropertyA(int name) +{ + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(3); + as->passInt32AsArg(name, 2); + as->passAccumulatorAsArg(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadProperty, Assembler::ResultInAccumulator); + as->checkException(); +} + +static ReturnedValue getLookupHelper(ExecutionEngine *engine, QV4::Function *f, int index, const QV4::Value &base) +{ + QV4::Lookup *l = f->compilationUnit->runtimeLookups + index; + return l->getter(l, engine, base); +} + +void BaselineJIT::generate_GetLookup(int index, int base) +{ + STORE_IP(); + as->prepareCallWithArgCount(4); + as->passRegAsArg(base, 3); + as->passInt32AsArg(index, 2); + as->passFunctionAsArg(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(getLookupHelper, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_GetLookupA(int index) +{ + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(4); + as->passAccumulatorAsArg(3); + as->passInt32AsArg(index, 2); + as->passFunctionAsArg(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(getLookupHelper, Assembler::ResultInAccumulator); + as->checkException(); +} + +static void storePropertyHelper(QV4::Function *f, const Value &base, int name, const Value &value) +{ + auto engine = f->internalClass->engine; + if (!Runtime::method_storeProperty(engine, base, name, value) && f->isStrict()) + engine->throwTypeError(); +} + +void BaselineJIT::generate_StoreProperty(int name, int base) +{ + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(4); + as->passAccumulatorAsArg(3); + as->passInt32AsArg(name, 2); + as->passRegAsArg(base, 1); + as->passFunctionAsArg(0); + JIT_GENERATE_RUNTIME_CALL(storePropertyHelper, Assembler::IgnoreResult); + as->checkException(); +} + +static void setLookupHelper(QV4::Function *f, int index, QV4::Value &base, const QV4::Value &value) +{ + ExecutionEngine *engine = f->internalClass->engine; + QV4::Lookup *l = f->compilationUnit->runtimeLookups + index; + if (!l->setter(l, engine, base, value) && f->isStrict()) + engine->throwTypeError(); +} + +void BaselineJIT::generate_SetLookup(int index, int base) +{ + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(4); + as->passAccumulatorAsArg(3); + as->passRegAsArg(base, 2); + as->passInt32AsArg(index, 1); + as->passFunctionAsArg(0); + JIT_GENERATE_RUNTIME_CALL(setLookupHelper, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_StoreScopeObjectProperty(int base, int propertyIndex) +{ + STORE_ACC(); + as->prepareCallWithArgCount(4); + as->passAccumulatorAsArg(3); + as->passInt32AsArg(propertyIndex, 2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_storeQmlScopeObjectProperty, Assembler::IgnoreResult); + as->checkException(); +} + +void BaselineJIT::generate_StoreContextObjectProperty(int base, int propertyIndex) +{ + STORE_ACC(); + as->prepareCallWithArgCount(4); + as->passAccumulatorAsArg(3); + as->passInt32AsArg(propertyIndex, 2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_storeQmlContextObjectProperty, Assembler::IgnoreResult); + as->checkException(); +} + +void BaselineJIT::generate_LoadScopeObjectProperty(int propertyIndex, int base, int captureRequired) +{ + STORE_IP(); + as->prepareCallWithArgCount(4); + as->passInt32AsArg(captureRequired, 3); + as->passInt32AsArg(propertyIndex, 2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlScopeObjectProperty, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_LoadContextObjectProperty(int propertyIndex, int base, int captureRequired) +{ + STORE_IP(); + as->prepareCallWithArgCount(4); + as->passInt32AsArg(captureRequired, 3); + as->passInt32AsArg(propertyIndex, 2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlContextObjectProperty, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_LoadIdObject(int index, int base) +{ + STORE_IP(); + as->prepareCallWithArgCount(3); + as->passInt32AsArg(index, 2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlIdObject, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_CallValue(int argc, int argv) +{ + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(4); + as->passInt32AsArg(argc, 3); + as->passRegAsArg(argv, 2); + as->passAccumulatorAsArg(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_callValue, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv) +{ + STORE_IP(); + as->prepareCallWithArgCount(5); + as->passInt32AsArg(argc, 4); + as->passRegAsArg(argv, 3); + as->passInt32AsArg(name, 2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_callProperty, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv) +{ + STORE_IP(); + as->prepareCallWithArgCount(5); + as->passInt32AsArg(argc, 4); + as->passRegAsArg(argv, 3); + as->passInt32AsArg(lookupIndex, 2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_callPropertyLookup, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv) +{ + STORE_IP(); + as->prepareCallWithArgCount(5); + as->passInt32AsArg(argc, 4); + as->passRegAsArg(argv, 3); + as->passRegAsArg(index, 2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_callElement, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_CallName(int name, int argc, int argv) +{ + STORE_IP(); + as->prepareCallWithArgCount(4); + as->passInt32AsArg(argc, 3); + as->passRegAsArg(argv, 2); + as->passInt32AsArg(name, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_callName, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_CallPossiblyDirectEval(int argc, int argv) +{ + STORE_IP(); + as->prepareCallWithArgCount(3); + as->passInt32AsArg(argc, 2); + as->passRegAsArg(argv, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_callPossiblyDirectEval, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv) +{ + STORE_IP(); + as->prepareCallWithArgCount(4); + as->passInt32AsArg(argc, 3); + as->passRegAsArg(argv, 2); + as->passInt32AsArg(index, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_callGlobalLookup, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_SetExceptionHandler(int offset) +{ + if (offset) + as->setExceptionHandler(instructionOffset() + offset); + else + as->clearExceptionHandler(); +} + +void BaselineJIT::generate_ThrowException() +{ + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(2); + as->passAccumulatorAsArg(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_throwException, Assembler::IgnoreResult); + as->gotoCatchException(); +} + +void BaselineJIT::generate_GetException() { as->getException(); } +void BaselineJIT::generate_SetException() { as->setException(); } + +static void createCallContextHelper(Value *stack, CppStackFrame *frame) +{ + stack[CallData::Context] = ExecutionContext::newCallContext(frame); +} + +void BaselineJIT::generate_CreateCallContext() +{ + as->prepareCallWithArgCount(2); + as->passCppFrameAsArg(1); + as->passRegAsArg(0, 0); + JIT_GENERATE_RUNTIME_CALL(createCallContextHelper, Assembler::IgnoreResult); +} + +void BaselineJIT::generate_PushCatchContext(int name, int reg) { as->pushCatchContext(name, reg); } + +static void pushWithContextHelper(ExecutionEngine *engine, QV4::Value *stack, int reg) +{ + QV4::Value &accumulator = stack[CallData::Accumulator]; + accumulator = accumulator.toObject(engine); + if (engine->hasException) + return; + stack[reg] = stack[CallData::Context]; + ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); + stack[CallData::Context] = Runtime::method_createWithContext(c, accumulator); +} + +void BaselineJIT::generate_PushWithContext(int reg) +{ + STORE_IP(); + as->saveAccumulatorInFrame(); + as->prepareCallWithArgCount(3); + as->passInt32AsArg(reg, 2); + as->passRegAsArg(0, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(pushWithContextHelper, Assembler::IgnoreResult); + as->checkException(); +} + +void BaselineJIT::generate_PopContext(int reg) { as->popContext(reg); } + +void BaselineJIT::generate_ForeachIteratorObject() +{ + as->saveAccumulatorInFrame(); + as->prepareCallWithArgCount(2); + as->passAccumulatorAsArg(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_foreachIterator, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_ForeachNextPropertyName() +{ + as->saveAccumulatorInFrame(); + as->prepareCallWithArgCount(1); + as->passAccumulatorAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_foreachNextPropertyName, + Assembler::ResultInAccumulator); + as->checkException(); +} + +static ReturnedValue deleteMemberHelper(QV4::Function *function, const QV4::Value &base, int member) +{ + auto engine = function->internalClass->engine; + if (!Runtime::method_deleteMember(engine, base, member)) { + if (function->isStrict()) + engine->throwTypeError(); + return Encode(false); + } else { + return Encode(true); + } +} + +void BaselineJIT::generate_DeleteMember(int member, int base) +{ + STORE_IP(); + as->prepareCallWithArgCount(3); + as->passInt32AsArg(member, 2); + as->passRegAsArg(base, 1); + as->passFunctionAsArg(0); + JIT_GENERATE_RUNTIME_CALL(deleteMemberHelper, Assembler::ResultInAccumulator); + as->checkException(); +} + +static ReturnedValue deleteSubscriptHelper(QV4::Function *function, const QV4::Value &base, const QV4::Value &index) +{ + auto engine = function->internalClass->engine; + if (!Runtime::method_deleteElement(engine, base, index)) { + if (function->isStrict()) + engine->throwTypeError(); + return Encode(false); + } else { + return Encode(true); + } +} + +void BaselineJIT::generate_DeleteSubscript(int base, int index) +{ + STORE_IP(); + as->prepareCallWithArgCount(3); + as->passRegAsArg(index, 2); + as->passRegAsArg(base, 1); + as->passFunctionAsArg(0); + JIT_GENERATE_RUNTIME_CALL(deleteSubscriptHelper, Assembler::ResultInAccumulator); + as->checkException(); +} + +static ReturnedValue deleteNameHelper(QV4::Function *function, int name) +{ + auto engine = function->internalClass->engine; + if (!Runtime::method_deleteName(engine, name)) { + if (function->isStrict()) + engine->throwTypeError(); + return Encode(false); + } else { + return Encode(true); + } +} + +void BaselineJIT::generate_DeleteName(int name) +{ + STORE_IP(); + as->prepareCallWithArgCount(2); + as->passInt32AsArg(name, 1); + as->passFunctionAsArg(0); + JIT_GENERATE_RUNTIME_CALL(deleteNameHelper, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_TypeofName(int name) +{ + as->prepareCallWithArgCount(2); + as->passInt32AsArg(name, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_typeofName, Assembler::ResultInAccumulator); +} + +void BaselineJIT::generate_TypeofValue() +{ + STORE_ACC(); + as->prepareCallWithArgCount(2); + as->passAccumulatorAsArg(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_typeofValue, Assembler::ResultInAccumulator); +} + +void BaselineJIT::generate_DeclareVar(int varName, int isDeletable) +{ + as->prepareCallWithArgCount(3); + as->passInt32AsArg(varName, 2); + as->passInt32AsArg(isDeletable, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_declareVar, Assembler::IgnoreResult); +} + +void BaselineJIT::generate_DefineArray(int argc, int args) +{ + as->prepareCallWithArgCount(3); + as->passInt32AsArg(argc, 2); + as->passRegAsArg(args, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_arrayLiteral, Assembler::ResultInAccumulator); +} + +void BaselineJIT::generate_DefineObjectLiteral(int internalClassId, int arrayValueCount, + int arrayGetterSetterCountAndFlags, int args) +{ + as->prepareCallWithArgCount(5); + as->passInt32AsArg(arrayGetterSetterCountAndFlags, 4); + as->passInt32AsArg(arrayValueCount, 3); + as->passInt32AsArg(internalClassId, 2); + as->passRegAsArg(args, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_objectLiteral, Assembler::ResultInAccumulator); +} +void BaselineJIT::generate_CreateMappedArgumentsObject() +{ + as->prepareCallWithArgCount(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_createMappedArgumentsObject, + Assembler::ResultInAccumulator); +} + +void BaselineJIT::generate_CreateUnmappedArgumentsObject() +{ + as->prepareCallWithArgCount(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_createUnmappedArgumentsObject, + Assembler::ResultInAccumulator); +} + +static void convertThisToObjectHelper(ExecutionEngine *engine, Value *t) +{ + if (!t->isObject()) { + if (t->isNullOrUndefined()) { + *t = engine->globalObject->asReturnedValue(); + } else { + *t = t->toObject(engine)->asReturnedValue(); + } + } +} + +void BaselineJIT::generate_ConvertThisToObject() +{ + as->prepareCallWithArgCount(2); + as->passRegAsArg(CallData::This, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(convertThisToObjectHelper, Assembler::IgnoreResult); + as->checkException(); +} + +void BaselineJIT::generate_Construct(int func, int argc, int argv) +{ + STORE_IP(); + as->prepareCallWithArgCount(4); + as->passInt32AsArg(argc, 3); + as->passRegAsArg(argv, 2); + as->passRegAsArg(func, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_construct, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_Jump(int offset) { as->jump(instructionOffset() + offset); } +void BaselineJIT::generate_JumpTrue(int offset) { as->jumpTrue(instructionOffset() + offset); } +void BaselineJIT::generate_JumpFalse(int offset) { as->jumpFalse(instructionOffset() + offset); } + +void BaselineJIT::generate_CmpEqNull() { as->cmpeqNull(); } +void BaselineJIT::generate_CmpNeNull() { as->cmpneNull(); } +void BaselineJIT::generate_CmpEqInt(int lhs) { as->cmpeqInt(lhs); } +void BaselineJIT::generate_CmpNeInt(int lhs) { as->cmpneInt(lhs); } +void BaselineJIT::generate_CmpEq(int lhs) { as->cmpeq(lhs); } +void BaselineJIT::generate_CmpNe(int lhs) { as->cmpne(lhs); } +void BaselineJIT::generate_CmpGt(int lhs) { as->cmpgt(lhs); } +void BaselineJIT::generate_CmpGe(int lhs) { as->cmpge(lhs); } +void BaselineJIT::generate_CmpLt(int lhs) { as->cmplt(lhs); } +void BaselineJIT::generate_CmpLe(int lhs) { as->cmple(lhs); } +void BaselineJIT::generate_CmpStrictEqual(int lhs) { as->cmpStrictEqual(lhs); } +void BaselineJIT::generate_CmpStrictNotEqual(int lhs) { as->cmpStrictNotEqual(lhs); } + +void BaselineJIT::generate_CmpIn(int lhs) +{ + STORE_ACC(); + as->prepareCallWithArgCount(3); + as->passAccumulatorAsArg(2); + as->passRegAsArg(lhs, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_in, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_CmpInstanceOf(int lhs) +{ + STORE_ACC(); + as->prepareCallWithArgCount(3); + as->passAccumulatorAsArg(2); + as->passRegAsArg(lhs, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_instanceof, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_JumpStrictEqualStackSlotInt(int lhs, int rhs, int offset) +{ + as->jumpStrictEqualStackSlotInt(lhs, rhs, instructionOffset() + offset); +} + +void BaselineJIT::generate_JumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset) +{ + as->jumpStrictNotEqualStackSlotInt(lhs, rhs, instructionOffset() + offset); +} + +void BaselineJIT::generate_UNot() { as->unot(); } +void BaselineJIT::generate_UPlus() { as->toNumber(); } +void BaselineJIT::generate_UMinus() { as->uminus(); } +void BaselineJIT::generate_UCompl() { as->ucompl(); } +void BaselineJIT::generate_Increment() { as->inc(); } +void BaselineJIT::generate_Decrement() { as->dec(); } +void BaselineJIT::generate_Add(int lhs) { as->add(lhs); } + +void BaselineJIT::generate_BitAnd(int lhs) { as->bitAnd(lhs); } +void BaselineJIT::generate_BitOr(int lhs) { as->bitOr(lhs); } +void BaselineJIT::generate_BitXor(int lhs) { as->bitXor(lhs); } +void BaselineJIT::generate_UShr(int lhs) { as->ushr(lhs); } +void BaselineJIT::generate_Shr(int lhs) { as->shr(lhs); } +void BaselineJIT::generate_Shl(int lhs) { as->shl(lhs); } + +void BaselineJIT::generate_BitAndConst(int rhs) { as->bitAndConst(rhs); } +void BaselineJIT::generate_BitOrConst(int rhs) { as->bitOrConst(rhs); } +void BaselineJIT::generate_BitXorConst(int rhs) { as->bitXorConst(rhs); } +void BaselineJIT::generate_UShrConst(int rhs) { as->ushrConst(rhs); } +void BaselineJIT::generate_ShrConst(int rhs) { as->shrConst(rhs); } +void BaselineJIT::generate_ShlConst(int rhs) { as->shlConst(rhs); } + +void BaselineJIT::generate_Mul(int lhs) { as->mul(lhs); } +void BaselineJIT::generate_Div(int lhs) { as->div(lhs); } +void BaselineJIT::generate_Mod(int lhs) { as->mod(lhs); } +void BaselineJIT::generate_Sub(int lhs) { as->sub(lhs); } + +//void BaselineJIT::generate_BinopContext(int alu, int lhs) +//{ +// auto engine = function->internalClass->engine; +// void *op = engine->runtime.runtimeMethods[alu]; +// STORE_ACC(); +// as->passAccumulatorAsArg(2); +// as->passRegAsArg(lhs, 1); +// as->passEngineAsArg(0); +// as->callRuntime("binopContext", op, Assembler::ResultInAccumulator); +// as->checkException(); +//} + +void BaselineJIT::generate_LoadQmlContext(int result) +{ + as->prepareCallWithArgCount(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlContext, Assembler::ResultInAccumulator); + as->storeReg(result); +} + +void BaselineJIT::generate_LoadQmlImportedScripts(int result) +{ + as->prepareCallWithArgCount(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlImportedScripts, Assembler::ResultInAccumulator); + as->storeReg(result); +} + +void BaselineJIT::generate_LoadQmlSingleton(int name) +{ + as->prepareCallWithArgCount(2); + as->passInt32AsArg(name, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlSingleton, Assembler::ResultInAccumulator); +} + +void BaselineJIT::startInstruction(Instr::Type /*instr*/) +{ + if (hasLabel()) + as->addLabel(instructionOffset()); +} + +void BaselineJIT::endInstruction(Instr::Type instr) +{ + Q_UNUSED(instr); +} + +#define MOTH_UNUSED_ARGS0() +#define MOTH_UNUSED_ARGS1(arg) \ + Q_UNUSED(arg); +#define MOTH_UNUSED_ARGS2(arg1, arg2) \ + Q_UNUSED(arg1); \ + Q_UNUSED(arg2); +#define MOTH_UNUSED_ARGS3(arg1, arg2, arg3) \ + Q_UNUSED(arg1); \ + Q_UNUSED(arg2); \ + Q_UNUSED(arg3); +#define MOTH_UNUSED_ARGS4(arg1, arg2, arg3, arg4) \ + Q_UNUSED(arg1); \ + Q_UNUSED(arg2); \ + Q_UNUSED(arg3); \ + Q_UNUSED(arg4); + +#define MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, ...) \ + MOTH_EXPAND_FOR_MSVC(MOTH_UNUSED_ARGS##nargs(__VA_ARGS__)) + +#define MOTH_MARK_ARGS_UNUSED_INSTRUCTION(name, nargs, ...) \ + MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, __VA_ARGS__) + +#define MOTH_BEGIN_INSTR(instr) \ + { \ + INSTR_##instr(MOTH_DECODE_WITH_BASE) \ + INSTR_##instr(MOTH_MARK_ARGS_UNUSED) \ + Q_UNUSED(base_ptr); + +#define MOTH_END_INSTR(instr) \ + continue; \ + } + +void BaselineJIT::collectLabelsInBytecode() +{ + MOTH_JUMP_TABLE; + + const char *code = reinterpret_cast<const char *>(function->codeData); + const char *start = code; + const char *end = code + function->compiledFunction->codeSize; + while (code < end) { + MOTH_DISPATCH() + Q_UNREACHABLE(); + + MOTH_BEGIN_INSTR(LoadReg) + MOTH_END_INSTR(LoadReg) + + MOTH_BEGIN_INSTR(StoreReg) + MOTH_END_INSTR(StoreReg) + + MOTH_BEGIN_INSTR(MoveReg) + MOTH_END_INSTR(MoveReg) + + MOTH_BEGIN_INSTR(LoadConst) + MOTH_END_INSTR(LoadConst) + + MOTH_BEGIN_INSTR(LoadNull) + MOTH_END_INSTR(LoadNull) + + MOTH_BEGIN_INSTR(LoadZero) + MOTH_END_INSTR(LoadZero) + + MOTH_BEGIN_INSTR(LoadTrue) + MOTH_END_INSTR(LoadTrue) + + MOTH_BEGIN_INSTR(LoadFalse) + MOTH_END_INSTR(LoadFalse) + + MOTH_BEGIN_INSTR(LoadUndefined) + MOTH_END_INSTR(LoadUndefined) + + MOTH_BEGIN_INSTR(LoadInt) + MOTH_END_INSTR(LoadInt) + + MOTH_BEGIN_INSTR(MoveConst) + MOTH_END_INSTR(MoveConst) + + MOTH_BEGIN_INSTR(LoadLocal) + MOTH_END_INSTR(LoadLocal) + + MOTH_BEGIN_INSTR(StoreLocal) + MOTH_END_INSTR(StoreLocal) + + MOTH_BEGIN_INSTR(LoadScopedLocal) + MOTH_END_INSTR(LoadScopedLocal) + + MOTH_BEGIN_INSTR(StoreScopedLocal) + MOTH_END_INSTR(StoreScopedLocal) + + MOTH_BEGIN_INSTR(LoadRuntimeString) + MOTH_END_INSTR(LoadRuntimeString) + + MOTH_BEGIN_INSTR(LoadRegExp) + MOTH_END_INSTR(LoadRegExp) + + MOTH_BEGIN_INSTR(LoadClosure) + MOTH_END_INSTR(LoadClosure) + + MOTH_BEGIN_INSTR(LoadName) + MOTH_END_INSTR(LoadName) + + MOTH_BEGIN_INSTR(LoadGlobalLookup) + MOTH_END_INSTR(LoadGlobalLookup) + + MOTH_BEGIN_INSTR(StoreNameSloppy) + MOTH_END_INSTR(StoreNameSloppy) + + MOTH_BEGIN_INSTR(StoreNameStrict) + MOTH_END_INSTR(StoreNameStrict) + + MOTH_BEGIN_INSTR(LoadElement) + MOTH_END_INSTR(LoadElement) + + MOTH_BEGIN_INSTR(LoadElementA) + MOTH_END_INSTR(LoadElement) + + MOTH_BEGIN_INSTR(StoreElement) + MOTH_END_INSTR(StoreElement) + + MOTH_BEGIN_INSTR(LoadProperty) + MOTH_END_INSTR(LoadProperty) + + MOTH_BEGIN_INSTR(LoadPropertyA) + MOTH_END_INSTR(LoadElementA) + + MOTH_BEGIN_INSTR(GetLookup) + MOTH_END_INSTR(GetLookup) + + MOTH_BEGIN_INSTR(GetLookupA) + MOTH_END_INSTR(GetLookupA) + + MOTH_BEGIN_INSTR(StoreProperty) + MOTH_END_INSTR(StoreProperty) + + MOTH_BEGIN_INSTR(SetLookup) + MOTH_END_INSTR(SetLookup) + + MOTH_BEGIN_INSTR(StoreScopeObjectProperty) + MOTH_END_INSTR(StoreScopeObjectProperty) + + MOTH_BEGIN_INSTR(LoadScopeObjectProperty) + MOTH_END_INSTR(LoadScopeObjectProperty) + + MOTH_BEGIN_INSTR(StoreContextObjectProperty) + MOTH_END_INSTR(StoreContextObjectProperty) + + MOTH_BEGIN_INSTR(LoadContextObjectProperty) + MOTH_END_INSTR(LoadContextObjectProperty) + + MOTH_BEGIN_INSTR(LoadIdObject) + MOTH_END_INSTR(LoadIdObject) + + MOTH_BEGIN_INSTR(CallValue) + MOTH_END_INSTR(CallValue) + + MOTH_BEGIN_INSTR(CallProperty) + MOTH_END_INSTR(CallProperty) + + MOTH_BEGIN_INSTR(CallPropertyLookup) + MOTH_END_INSTR(CallPropertyLookup) + + MOTH_BEGIN_INSTR(CallElement) + MOTH_END_INSTR(CallElement) + + MOTH_BEGIN_INSTR(CallName) + MOTH_END_INSTR(CallName) + + MOTH_BEGIN_INSTR(CallPossiblyDirectEval) + MOTH_END_INSTR(CallPossiblyDirectEval) + + MOTH_BEGIN_INSTR(CallGlobalLookup) + MOTH_END_INSTR(CallGlobalLookup) + + MOTH_BEGIN_INSTR(SetExceptionHandler) + labels.push_back(code - start + offset); + MOTH_END_INSTR(SetExceptionHandler) + + MOTH_BEGIN_INSTR(ThrowException) + MOTH_END_INSTR(ThrowException) + + MOTH_BEGIN_INSTR(GetException) + MOTH_END_INSTR(HasException) + + MOTH_BEGIN_INSTR(SetException) + MOTH_END_INSTR(SetExceptionFlag) + + MOTH_BEGIN_INSTR(CreateCallContext) + MOTH_END_INSTR(CreateCallContext) + + MOTH_BEGIN_INSTR(PushCatchContext) + MOTH_END_INSTR(PushCatchContext) + + MOTH_BEGIN_INSTR(PushWithContext) + MOTH_END_INSTR(PushWithContext) + + MOTH_BEGIN_INSTR(PopContext) + MOTH_END_INSTR(PopContext) + + MOTH_BEGIN_INSTR(ForeachIteratorObject) + MOTH_END_INSTR(ForeachIteratorObject) + + MOTH_BEGIN_INSTR(ForeachNextPropertyName) + MOTH_END_INSTR(ForeachNextPropertyName) + + MOTH_BEGIN_INSTR(DeleteMember) + MOTH_END_INSTR(DeleteMember) + + MOTH_BEGIN_INSTR(DeleteSubscript) + MOTH_END_INSTR(DeleteSubscript) + + MOTH_BEGIN_INSTR(DeleteName) + MOTH_END_INSTR(DeleteName) + + MOTH_BEGIN_INSTR(TypeofName) + MOTH_END_INSTR(TypeofName) + + MOTH_BEGIN_INSTR(TypeofValue) + MOTH_END_INSTR(TypeofValue) + + MOTH_BEGIN_INSTR(DeclareVar) + MOTH_END_INSTR(DeclareVar) + + MOTH_BEGIN_INSTR(DefineArray) + MOTH_END_INSTR(DefineArray) + + MOTH_BEGIN_INSTR(DefineObjectLiteral) + MOTH_END_INSTR(DefineObjectLiteral) + + MOTH_BEGIN_INSTR(CreateMappedArgumentsObject) + MOTH_END_INSTR(CreateMappedArgumentsObject) + + MOTH_BEGIN_INSTR(CreateUnmappedArgumentsObject) + MOTH_END_INSTR(CreateUnmappedArgumentsObject) + + MOTH_BEGIN_INSTR(ConvertThisToObject) + MOTH_END_INSTR(ConvertThisToObject) + + MOTH_BEGIN_INSTR(Construct) + MOTH_END_INSTR(Construct) + + MOTH_BEGIN_INSTR(Jump) + labels.push_back(code - start + offset); + MOTH_END_INSTR(Jump) + + MOTH_BEGIN_INSTR(JumpTrue) + labels.push_back(code - start + offset); + MOTH_END_INSTR(JumpTrue) + + MOTH_BEGIN_INSTR(JumpFalse) + labels.push_back(code - start + offset); + MOTH_END_INSTR(JumpFalse) + + MOTH_BEGIN_INSTR(CmpEqNull) + MOTH_END_INSTR(CmpEqNull) + + MOTH_BEGIN_INSTR(CmpNeNull) + MOTH_END_INSTR(CmpNeNull) + + MOTH_BEGIN_INSTR(CmpEqInt) + MOTH_END_INSTR(CmpEq) + + MOTH_BEGIN_INSTR(CmpNeInt) + MOTH_END_INSTR(CmpNeInt) + + MOTH_BEGIN_INSTR(CmpEq) + MOTH_END_INSTR(CmpEq) + + MOTH_BEGIN_INSTR(CmpNe) + MOTH_END_INSTR(CmpNe) + + MOTH_BEGIN_INSTR(CmpGt) + MOTH_END_INSTR(CmpGt) + + MOTH_BEGIN_INSTR(CmpGe) + MOTH_END_INSTR(CmpGe) + + MOTH_BEGIN_INSTR(CmpLt) + MOTH_END_INSTR(CmpLt) + + MOTH_BEGIN_INSTR(CmpLe) + MOTH_END_INSTR(CmpLe) + + MOTH_BEGIN_INSTR(CmpStrictEqual) + MOTH_END_INSTR(CmpStrictEqual) + + MOTH_BEGIN_INSTR(CmpStrictNotEqual) + MOTH_END_INSTR(CmpStrictNotEqual) + + MOTH_BEGIN_INSTR(CmpIn) + MOTH_END_INSTR(CmpIn) + + MOTH_BEGIN_INSTR(CmpInstanceOf) + MOTH_END_INSTR(CmpInstanceOf) + + MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt) + labels.push_back(code - start + offset); + MOTH_END_INSTR(JumpStrictEqualStackSlotInt) + + MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt) + labels.push_back(code - start + offset); + MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt) + + MOTH_BEGIN_INSTR(UNot) + MOTH_END_INSTR(UNot) + + MOTH_BEGIN_INSTR(UPlus) + MOTH_END_INSTR(UPlus) + + MOTH_BEGIN_INSTR(UMinus) + MOTH_END_INSTR(UMinus) + + MOTH_BEGIN_INSTR(UCompl) + MOTH_END_INSTR(UCompl) + + MOTH_BEGIN_INSTR(Increment) + MOTH_END_INSTR(PreIncrement) + + MOTH_BEGIN_INSTR(Decrement) + MOTH_END_INSTR(PreDecrement) + + MOTH_BEGIN_INSTR(Add) + MOTH_END_INSTR(Add) + + MOTH_BEGIN_INSTR(BitAnd) + MOTH_END_INSTR(BitAnd) + + MOTH_BEGIN_INSTR(BitOr) + MOTH_END_INSTR(BitOr) + + MOTH_BEGIN_INSTR(BitXor) + MOTH_END_INSTR(BitXor) + + MOTH_BEGIN_INSTR(UShr) + MOTH_END_INSTR(UShr) + + MOTH_BEGIN_INSTR(Shr) + MOTH_END_INSTR(Shr) + + MOTH_BEGIN_INSTR(Shl) + MOTH_END_INSTR(Shl) + + MOTH_BEGIN_INSTR(BitAndConst) + MOTH_END_INSTR(BitAndConst) + + MOTH_BEGIN_INSTR(BitOrConst) + MOTH_END_INSTR(BitOr) + + MOTH_BEGIN_INSTR(BitXorConst) + MOTH_END_INSTR(BitXor) + + MOTH_BEGIN_INSTR(UShrConst) + MOTH_END_INSTR(UShrConst) + + MOTH_BEGIN_INSTR(ShrConst) + MOTH_END_INSTR(ShrConst) + + MOTH_BEGIN_INSTR(ShlConst) + MOTH_END_INSTR(ShlConst) + + MOTH_BEGIN_INSTR(Mul) + MOTH_END_INSTR(Mul) + + MOTH_BEGIN_INSTR(Div) + MOTH_END_INSTR(Div) + + MOTH_BEGIN_INSTR(Mod) + MOTH_END_INSTR(Mod) + + MOTH_BEGIN_INSTR(Sub) + MOTH_END_INSTR(Sub) + + MOTH_BEGIN_INSTR(Ret) + MOTH_END_INSTR(Ret) + +#ifndef QT_NO_QML_DEBUGGER + MOTH_BEGIN_INSTR(Debug) + MOTH_END_INSTR(Debug) +#endif // QT_NO_QML_DEBUGGER + + MOTH_BEGIN_INSTR(LoadQmlContext) + MOTH_END_INSTR(LoadQmlContext) + + MOTH_BEGIN_INSTR(LoadQmlImportedScripts) + MOTH_END_INSTR(LoadQmlImportedScripts) + + MOTH_BEGIN_INSTR(LoadQmlSingleton) + MOTH_END_INSTR(LoadQmlSingleton) + } +} +#undef MOTH_BEGIN_INSTR +#undef MOTH_END_INSTR + +#endif // V4_ENABLE_JIT diff --git a/src/qml/jit/qv4jit_p.h b/src/qml/jit/qv4jit_p.h new file mode 100644 index 0000000000..dfa2a79c48 --- /dev/null +++ b/src/qml/jit/qv4jit_p.h @@ -0,0 +1,265 @@ +/**************************************************************************** +** +** 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 QV4JIT_P_H +#define QV4JIT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qv4global_p.h> +#include <private/qv4function_p.h> +#include <private/qv4instr_moth_p.h> + +//QT_REQUIRE_CONFIG(qml_jit); + +#define JIT_DEFINE_ARGS(nargs, ...) \ + MOTH_EXPAND_FOR_MSVC(JIT_DEFINE_ARGS##nargs(__VA_ARGS__)) + +#define JIT_DEFINE_ARGS0() +#define JIT_DEFINE_ARGS1(arg) \ + int arg +#define JIT_DEFINE_ARGS2(arg1, arg2) \ + int arg1, \ + int arg2 +#define JIT_DEFINE_ARGS3(arg1, arg2, arg3) \ + int arg1, \ + int arg2, \ + int arg3 +#define JIT_DEFINE_ARGS4(arg1, arg2, arg3, arg4) \ + int arg1, \ + int arg2, \ + int arg3, \ + int arg4 + +#define JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER_INSTRUCTION(name, nargs, ...) \ + virtual void generate_##name( \ + JIT_DEFINE_ARGS(nargs, __VA_ARGS__) \ + ) = 0; + +#define JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER(instr) \ + INSTR_##instr(JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER) + +QT_BEGIN_NAMESPACE + +namespace QV4 { +namespace JIT { + +class Assembler; + +class ByteCodeHandler +{ +public: + virtual ~ByteCodeHandler(); + + void decode(const char *code, uint len); + + int instructionOffset() const { return _offset; } + +protected: + FOR_EACH_MOTH_INSTR(JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER) + + virtual void startInstruction(Moth::Instr::Type instr) = 0; + virtual void endInstruction(Moth::Instr::Type instr) = 0; + +private: + int _offset = 0; +}; + +#ifdef V4_ENABLE_JIT +class BaselineJIT final: public ByteCodeHandler +{ +public: + BaselineJIT(QV4::Function *); + virtual ~BaselineJIT(); + + void generate(); + + void generate_Ret() Q_DECL_OVERRIDE; + void generate_Debug() Q_DECL_OVERRIDE; + void generate_LoadConst(int index) Q_DECL_OVERRIDE; + void generate_LoadZero() Q_DECL_OVERRIDE; + void generate_LoadTrue() Q_DECL_OVERRIDE; + void generate_LoadFalse() Q_DECL_OVERRIDE; + void generate_LoadNull() Q_DECL_OVERRIDE; + void generate_LoadUndefined() Q_DECL_OVERRIDE; + void generate_LoadInt(int value) Q_DECL_OVERRIDE; + void generate_MoveConst(int constIndex, int destTemp) Q_DECL_OVERRIDE; + void generate_LoadReg(int reg) Q_DECL_OVERRIDE; + void generate_StoreReg(int reg) Q_DECL_OVERRIDE; + void generate_MoveReg(int srcReg, int destReg) Q_DECL_OVERRIDE; + void generate_LoadLocal(int index) Q_DECL_OVERRIDE; + void generate_StoreLocal(int index) Q_DECL_OVERRIDE; + void generate_LoadScopedLocal(int scope, int index) Q_DECL_OVERRIDE; + void generate_StoreScopedLocal(int scope, int index) Q_DECL_OVERRIDE; + void generate_LoadRuntimeString(int stringId) Q_DECL_OVERRIDE; + void generate_LoadRegExp(int regExpId) Q_DECL_OVERRIDE; + void generate_LoadClosure(int value) Q_DECL_OVERRIDE; + void generate_LoadName(int name) Q_DECL_OVERRIDE; + void generate_LoadGlobalLookup(int index) Q_DECL_OVERRIDE; + void generate_StoreNameSloppy(int name) Q_DECL_OVERRIDE; + void generate_StoreNameStrict(int name) Q_DECL_OVERRIDE; + void generate_LoadElement(int base, int index) Q_DECL_OVERRIDE; + void generate_LoadElementA(int base) Q_DECL_OVERRIDE; + void generate_StoreElement(int base, int index) Q_DECL_OVERRIDE; + void generate_LoadProperty(int name, int base) Q_DECL_OVERRIDE; + void generate_LoadPropertyA(int name) Q_DECL_OVERRIDE; + void generate_GetLookup(int index, int base) Q_DECL_OVERRIDE; + void generate_GetLookupA(int index) Q_DECL_OVERRIDE; + void generate_StoreProperty(int name, int base) Q_DECL_OVERRIDE; + void generate_SetLookup(int index, int base) Q_DECL_OVERRIDE; + void generate_StoreScopeObjectProperty(int base, + int propertyIndex) Q_DECL_OVERRIDE; + void generate_StoreContextObjectProperty(int base, + int propertyIndex) Q_DECL_OVERRIDE; + void generate_LoadScopeObjectProperty(int propertyIndex, int base, + int captureRequired) Q_DECL_OVERRIDE; + void generate_LoadContextObjectProperty(int propertyIndex, int base, + int captureRequired) Q_DECL_OVERRIDE; + void generate_LoadIdObject(int index, int base) Q_DECL_OVERRIDE; + void generate_CallValue(int argc, int argv) Q_DECL_OVERRIDE; + void generate_CallProperty(int name, int base, int argc, int argv) Q_DECL_OVERRIDE; + void generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv) Q_DECL_OVERRIDE; + void generate_CallElement(int base, int index, int argc, int argv) Q_DECL_OVERRIDE; + void generate_CallName(int name, int argc, int argv) Q_DECL_OVERRIDE; + void generate_CallPossiblyDirectEval(int argc, int argv) Q_DECL_OVERRIDE; + void generate_CallGlobalLookup(int index, int argc, int argv) Q_DECL_OVERRIDE; + void generate_SetExceptionHandler(int offset) Q_DECL_OVERRIDE; + void generate_ThrowException() Q_DECL_OVERRIDE; + void generate_GetException() Q_DECL_OVERRIDE; + void generate_SetException() Q_DECL_OVERRIDE; + void generate_CreateCallContext() Q_DECL_OVERRIDE; + void generate_PushCatchContext(int name, int reg) Q_DECL_OVERRIDE; + void generate_PushWithContext(int reg) Q_DECL_OVERRIDE; + void generate_PopContext(int reg) Q_DECL_OVERRIDE; + void generate_ForeachIteratorObject() Q_DECL_OVERRIDE; + void generate_ForeachNextPropertyName() Q_DECL_OVERRIDE; + void generate_DeleteMember(int member, int base) Q_DECL_OVERRIDE; + void generate_DeleteSubscript(int base, int index) Q_DECL_OVERRIDE; + void generate_DeleteName(int name) Q_DECL_OVERRIDE; + void generate_TypeofName(int name) Q_DECL_OVERRIDE; + void generate_TypeofValue() Q_DECL_OVERRIDE; + void generate_DeclareVar(int varName, int isDeletable) Q_DECL_OVERRIDE; + void generate_DefineArray(int argc, int args) Q_DECL_OVERRIDE; + void generate_DefineObjectLiteral(int internalClassId, int arrayValueCount, + int arrayGetterSetterCountAndFlags, + int args) Q_DECL_OVERRIDE; + void generate_CreateMappedArgumentsObject() Q_DECL_OVERRIDE; + void generate_CreateUnmappedArgumentsObject() Q_DECL_OVERRIDE; + void generate_ConvertThisToObject() Q_DECL_OVERRIDE; + void generate_Construct(int func, int argc, int argv) Q_DECL_OVERRIDE; + void generate_Jump(int offset) Q_DECL_OVERRIDE; + void generate_JumpTrue(int offset) Q_DECL_OVERRIDE; + void generate_JumpFalse(int offset) Q_DECL_OVERRIDE; + void generate_CmpEqNull() Q_DECL_OVERRIDE; + void generate_CmpNeNull() Q_DECL_OVERRIDE; + void generate_CmpEqInt(int lhs) Q_DECL_OVERRIDE; + void generate_CmpNeInt(int lhs) Q_DECL_OVERRIDE; + void generate_CmpEq(int lhs) Q_DECL_OVERRIDE; + void generate_CmpNe(int lhs) Q_DECL_OVERRIDE; + void generate_CmpGt(int lhs) Q_DECL_OVERRIDE; + void generate_CmpGe(int lhs) Q_DECL_OVERRIDE; + void generate_CmpLt(int lhs) Q_DECL_OVERRIDE; + void generate_CmpLe(int lhs) Q_DECL_OVERRIDE; + void generate_CmpStrictEqual(int lhs) Q_DECL_OVERRIDE; + void generate_CmpStrictNotEqual(int lhs) Q_DECL_OVERRIDE; + void generate_CmpIn(int lhs) Q_DECL_OVERRIDE; + void generate_CmpInstanceOf(int lhs) Q_DECL_OVERRIDE; + void generate_JumpStrictEqualStackSlotInt(int lhs, int rhs, + int offset) Q_DECL_OVERRIDE; + void generate_JumpStrictNotEqualStackSlotInt(int lhs, int rhs, + int offset) Q_DECL_OVERRIDE; + void generate_UNot() Q_DECL_OVERRIDE; + void generate_UPlus() Q_DECL_OVERRIDE; + void generate_UMinus() Q_DECL_OVERRIDE; + void generate_UCompl() Q_DECL_OVERRIDE; + void generate_Increment() Q_DECL_OVERRIDE; + void generate_Decrement() Q_DECL_OVERRIDE; + void generate_Add(int lhs) Q_DECL_OVERRIDE; + void generate_BitAnd(int lhs) Q_DECL_OVERRIDE; + void generate_BitOr(int lhs) Q_DECL_OVERRIDE; + void generate_BitXor(int lhs) Q_DECL_OVERRIDE; + void generate_UShr(int lhs) Q_DECL_OVERRIDE; + void generate_Shr(int lhs) Q_DECL_OVERRIDE; + void generate_Shl(int lhs) Q_DECL_OVERRIDE; + void generate_BitAndConst(int rhs) Q_DECL_OVERRIDE; + void generate_BitOrConst(int rhs) Q_DECL_OVERRIDE; + void generate_BitXorConst(int rhs) Q_DECL_OVERRIDE; + void generate_UShrConst(int rhs) Q_DECL_OVERRIDE; + void generate_ShrConst(int rhs) Q_DECL_OVERRIDE; + void generate_ShlConst(int rhs) Q_DECL_OVERRIDE; + void generate_Mul(int lhs) Q_DECL_OVERRIDE; + void generate_Div(int lhs) Q_DECL_OVERRIDE; + void generate_Mod(int lhs) Q_DECL_OVERRIDE; + void generate_Sub(int lhs) Q_DECL_OVERRIDE; + void generate_LoadQmlContext(int result) Q_DECL_OVERRIDE; + void generate_LoadQmlImportedScripts(int result) Q_DECL_OVERRIDE; + void generate_LoadQmlSingleton(int name) Q_DECL_OVERRIDE; + + void startInstruction(Moth::Instr::Type instr) Q_DECL_OVERRIDE; + void endInstruction(Moth::Instr::Type instr) Q_DECL_OVERRIDE; + +protected: + bool hasLabel() const + { return std::find(labels.cbegin(), labels.cend(), instructionOffset()) != labels.cend(); } + +private: + void collectLabelsInBytecode(); + +private: + QV4::Function *function; + QScopedPointer<Assembler> as; + std::vector<int> labels; +}; +#endif // V4_ENABLE_JIT + +} // namespace JIT +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4JIT_P_H diff --git a/src/qml/jit/qv4regalloc.cpp b/src/qml/jit/qv4regalloc.cpp deleted file mode 100644 index cd2d53ab40..0000000000 --- a/src/qml/jit/qv4regalloc.cpp +++ /dev/null @@ -1,1971 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the V4VM 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 <QtCore/QBuffer> -#include <QtCore/QDebug> -#include "qv4regalloc_p.h" -#include "qv4alloca_p.h" -#include <private/qv4value_p.h> - -#include <algorithm> -#if defined(Q_CC_MINGW) -# include <malloc.h> -#endif - -namespace { -enum { DebugRegAlloc = 0 }; - -struct Use { - enum RegisterFlag { MustHaveRegister = 0, CouldHaveRegister = 1 }; - unsigned flag : 1; - unsigned pos : 31; - - Use(): flag(MustHaveRegister), pos(0) {} - Use(int position, RegisterFlag flag): flag(flag), pos(position) - { Q_ASSERT(position >= 0); } - - bool mustHaveRegister() const { return flag == MustHaveRegister; } -}; -} - -QT_BEGIN_NAMESPACE - -Q_DECLARE_TYPEINFO(Use, Q_MOVABLE_TYPE); - -using namespace QV4::IR; - -namespace QV4 { -namespace JIT { - -namespace { -class IRPrinterWithPositions: public IRPrinter -{ - LifeTimeIntervals::Ptr intervals; - const int positionSize; - -public: - IRPrinterWithPositions(QTextStream *out, const LifeTimeIntervals::Ptr &intervals) - : IRPrinter(out) - , intervals(intervals) - , positionSize(QString::number(intervals->lastPosition()).size()) - {} - -protected: - void addStmtNr(Stmt *s) override final - { - addJustifiedNr(intervals->positionForStatement(s)); - } -}; - -class IRPrinterWithRegisters: public IRPrinterWithPositions -{ - const RegisterInformation &_registerInformation; - QHash<int, const RegisterInfo *> _infoForRegularRegister; - QHash<int, const RegisterInfo *> _infoForFPRegister; - -public: - IRPrinterWithRegisters(QTextStream *out, const LifeTimeIntervals::Ptr &intervals, - const RegisterInformation ®isterInformation) - : IRPrinterWithPositions(out, intervals) - , _registerInformation(registerInformation) - { - for (int i = 0, ei = _registerInformation.size(); i != ei; ++i) - if (_registerInformation.at(i).isRegularRegister()) - _infoForRegularRegister.insert(_registerInformation.at(i).reg<int>(), - &_registerInformation.at(i)); - else - _infoForFPRegister.insert(_registerInformation.at(i).reg<int>(), - &_registerInformation.at(i)); - } - -protected: - void visitTemp(Temp *e) override final - { - switch (e->kind) { - case Temp::PhysicalRegister: { - const RegisterInfo *ri = e->type == DoubleType ? _infoForFPRegister.value(e->index, 0) - : _infoForRegularRegister.value(e->index, 0); - if (ri) { - *out << ri->prettyName(); - break; - } - Q_FALLTHROUGH(); - } - default: - IRPrinterWithPositions::visitTemp(e); - } - } -}; -} - -class RegAllocInfo: public IRDecoder -{ -public: - typedef QVarLengthArray<Temp, 4> Hints; - -private: - struct Def { - unsigned valid : 1; - unsigned canHaveReg : 1; - unsigned isPhiTarget : 1; - - Def(): valid(0), canHaveReg(0), isPhiTarget(0) {} - Def(bool canHaveReg, bool isPhiTarget) - : valid(1), canHaveReg(canHaveReg), isPhiTarget(isPhiTarget) - { - } - - bool isValid() const { return valid != 0; } - }; - - IR::LifeTimeIntervals::Ptr _lifeTimeIntervals; - BasicBlock *_currentBB; - Stmt *_currentStmt; - std::vector<Def> _defs; - std::vector<std::vector<Use> > _uses; - std::vector<int> _calls; - std::vector<Hints> _hints; - - int usePosition(Stmt *s) const - { - int usePos = _lifeTimeIntervals->positionForStatement(s); - if (usePos == Stmt::InvalidId) // phi-node operand, so: - usePos = _lifeTimeIntervals->startPosition(_currentBB); - return usePos; - } - -public: - RegAllocInfo(): _currentBB(0), _currentStmt(0) {} - - void collect(IR::Function *function, const IR::LifeTimeIntervals::Ptr &lifeTimeIntervals) - { - _lifeTimeIntervals = lifeTimeIntervals; - _defs.resize(function->tempCount); - _uses.resize(function->tempCount); - _calls.reserve(function->statementCount() / 3); - _hints.resize(function->tempCount); - - for (BasicBlock *bb : function->basicBlocks()) { - _currentBB = bb; - for (Stmt *s : bb->statements()) { - _currentStmt = s; - visit(s); - } - } - } - - const std::vector<Use> &uses(const Temp &t) const - { - return _uses.at(t.index); - } - - bool canHaveRegister(const Temp &t) const { - Q_ASSERT(_defs[t.index].isValid()); - return _defs[t.index].canHaveReg; - } - bool isPhiTarget(const Temp &t) const { - Q_ASSERT(_defs[t.index].isValid()); - return _defs[t.index].isPhiTarget; - } - - const std::vector<int> &calls() const { return _calls; } - const Hints &hints(const Temp &t) const { return _hints[t.index]; } - void addHint(const Temp &t, int physicalRegister) - { addHint(t, Temp::PhysicalRegister, physicalRegister); } - - void addHint(const Temp &t, Temp::Kind kind, int hintedIndex) - { - Hints &hints = _hints[t.index]; - for (Hints::iterator i = hints.begin(), ei = hints.end(); i != ei; ++i) - if (i->index == hintedIndex) - return; - - Temp hint; - hint.init(kind, hintedIndex); - hints.append(hint); - } - - void dump() const - { - if (!DebugRegAlloc) - return; - - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - IRPrinterWithPositions printer(&qout, _lifeTimeIntervals); - - qout << "RegAllocInfo:" << endl << "Defs/uses:" << endl; - for (unsigned t = 0; t < _defs.size(); ++t) { - const std::vector<Use> &uses = _uses[t]; - if (uses.empty()) - continue; - qout << "%" << t <<": " - << " (" - << (_defs[t].canHaveReg ? "can" : "can NOT") - << " have a register, and " - << (_defs[t].isPhiTarget ? "is" : "is NOT") - << " defined by a phi node), uses at: "; - for (unsigned i = 0; i < uses.size(); ++i) { - if (i > 0) qout << ", "; - qout << uses[i].pos; - if (uses[i].mustHaveRegister()) qout << "(R)"; else qout << "(S)"; - } - qout << endl; - } - - qout << "Calls at: "; - for (unsigned i = 0; i < _calls.size(); ++i) { - if (i > 0) qout << ", "; - qout << _calls[i]; - } - qout << endl; - - qout << "Hints:" << endl; - for (unsigned t = 0; t < _hints.size(); ++t) { - if (_uses[t].empty()) - continue; - qout << "\t%" << t << ": "; - const Hints &hints = _hints[t]; - for (int i = 0; i < hints.size(); ++i) { - if (i > 0) qout << ", "; - printer.print(hints[i]); - } - qout << endl; - } - qDebug("%s", buf.data().constData()); - } - -protected: // IRDecoder - void callBuiltinInvalid(IR::Name *, IR::ExprList *, IR::Expr *) override {} - void callBuiltinTypeofQmlContextProperty(IR::Expr *, IR::Member::MemberKind, int, IR::Expr *) override {} - void callBuiltinTypeofMember(IR::Expr *, const QString &, IR::Expr *) override {} - void callBuiltinTypeofSubscript(IR::Expr *, IR::Expr *, IR::Expr *) override {} - void callBuiltinTypeofName(const QString &, IR::Expr *) override {} - void callBuiltinTypeofValue(IR::Expr *, IR::Expr *) override {} - void callBuiltinDeleteMember(IR::Expr *, const QString &, IR::Expr *) override {} - void callBuiltinDeleteSubscript(IR::Expr *, IR::Expr *, IR::Expr *) override {} - void callBuiltinDeleteName(const QString &, IR::Expr *) override {} - void callBuiltinDeleteValue(IR::Expr *) override {} - void callBuiltinThrow(IR::Expr *) override {} - void callBuiltinReThrow() override {} - void callBuiltinUnwindException(IR::Expr *) override {} - void callBuiltinPushCatchScope(const QString &) override {}; - void callBuiltinForeachIteratorObject(IR::Expr *, IR::Expr *) override {} - virtual void callBuiltinForeachNextProperty(IR::Temp *, IR::Temp *) {} - void callBuiltinForeachNextPropertyname(IR::Expr *, IR::Expr *) override {} - void callBuiltinPushWithScope(IR::Expr *) override {} - void callBuiltinPopScope() override {} - void callBuiltinDeclareVar(bool , const QString &) override {} - void callBuiltinDefineArray(IR::Expr *, IR::ExprList *) override {} - void callBuiltinDefineObjectLiteral(IR::Expr *, int, IR::ExprList *, IR::ExprList *, bool) override {} - void callBuiltinSetupArgumentObject(IR::Expr *) override {} - void callBuiltinConvertThisToObject() override {} - - void callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) override - { - addDef(result); - if (IR::Temp *tempValue = value->asTemp()) - addUses(tempValue, Use::CouldHaveRegister); - addUses(args, Use::CouldHaveRegister); - addCall(); - } - - void callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind /*kind*/, int propertyIndex, - IR::ExprList *args, IR::Expr *result) override - { - Q_UNUSED(propertyIndex) - - addDef(result); - addUses(base->asTemp(), Use::CouldHaveRegister); - addUses(args, Use::CouldHaveRegister); - addCall(); - } - - void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, - IR::Expr *result) override - { - Q_UNUSED(name) - - addDef(result); - addUses(base->asTemp(), Use::CouldHaveRegister); - addUses(args, Use::CouldHaveRegister); - addCall(); - } - - void callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, - IR::Expr *result) override - { - addDef(result); - addUses(base->asTemp(), Use::CouldHaveRegister); - addUses(index->asTemp(), Use::CouldHaveRegister); - addUses(args, Use::CouldHaveRegister); - addCall(); - } - - void convertType(IR::Expr *source, IR::Expr *target) override - { - addDef(target); - - bool needsCall = true; - Use::RegisterFlag sourceReg = Use::CouldHaveRegister; - - switch (target->type) { - case DoubleType: - switch (source->type) { - case UInt32Type: - case SInt32Type: - case NullType: - case UndefinedType: - case BoolType: - needsCall = false; - break; - default: - break; - } - break; - case BoolType: - switch (source->type) { - case UInt32Type: - sourceReg = Use::MustHaveRegister; - needsCall = false; - break; - case DoubleType: - case UndefinedType: - case NullType: - case SInt32Type: - needsCall = false; - break; - default: - break; - } - break; - case SInt32Type: - switch (source->type) { - case UInt32Type: - case NullType: - case UndefinedType: - case BoolType: - needsCall = false; - default: - break; - } - break; - case UInt32Type: - switch (source->type) { - case SInt32Type: - case NullType: - case UndefinedType: - case BoolType: - needsCall = false; - default: - break; - } - break; - default: - break; - } - - Temp *sourceTemp = source->asTemp(); - if (sourceTemp) - addUses(sourceTemp, sourceReg); - - if (needsCall) - addCall(); - else if (target->asTemp()) - addHint(target->asTemp(), sourceTemp); - } - - void constructActivationProperty(IR::Name *, IR::ExprList *args, IR::Expr *result) override - { - addDef(result); - addUses(args, Use::CouldHaveRegister); - addCall(); - } - - void constructProperty(IR::Expr *base, const QString &, IR::ExprList *args, IR::Expr *result) override - { - addDef(result); - addUses(base, Use::CouldHaveRegister); - addUses(args, Use::CouldHaveRegister); - addCall(); - } - - void constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) override - { - addDef(result); - addUses(value, Use::CouldHaveRegister); - addUses(args, Use::CouldHaveRegister); - addCall(); - } - - void loadThisObject(IR::Expr *temp) override - { - addDef(temp); - } - - void loadQmlContext(IR::Expr *temp) override - { - addDef(temp); - addCall(); - } - - void loadQmlImportedScripts(IR::Expr *temp) override - { - addDef(temp); - addCall(); - } - - void loadQmlSingleton(const QString &/*name*/, Expr *temp) override - { - Q_UNUSED(temp); - - addDef(temp); - addCall(); - } - - void loadConst(IR::Const *sourceConst, Expr *targetTemp) override - { - Q_UNUSED(sourceConst); - - addDef(targetTemp); - } - - void loadString(const QString &str, Expr *targetTemp) override - { - Q_UNUSED(str); - - addDef(targetTemp); - } - - void loadRegexp(IR::RegExp *sourceRegexp, Expr *targetTemp) override - { - Q_UNUSED(sourceRegexp); - - addDef(targetTemp); - addCall(); - } - - void getActivationProperty(const IR::Name *, Expr *temp) override - { - addDef(temp); - addCall(); - } - - void setActivationProperty(IR::Expr *source, const QString &) override - { - addUses(source->asTemp(), Use::CouldHaveRegister); - addCall(); - } - - void initClosure(IR::Closure *closure, Expr *target) override - { - Q_UNUSED(closure); - - addDef(target); - addCall(); - } - - void getProperty(IR::Expr *base, const QString &, Expr *target) override - { - addDef(target); - addUses(base->asTemp(), Use::CouldHaveRegister); - addCall(); - } - - void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &) override - { - addUses(source->asTemp(), Use::CouldHaveRegister); - addUses(targetBase->asTemp(), Use::CouldHaveRegister); - addCall(); - } - - void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, - IR::Member::MemberKind /*kind*/, int /*propertyIndex*/) override - { - addUses(source->asTemp(), Use::CouldHaveRegister); - addUses(targetBase->asTemp(), Use::CouldHaveRegister); - addCall(); - } - - void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int /*propertyIndex*/) override - { - addUses(source->asTemp(), Use::CouldHaveRegister); - addUses(targetBase->asTemp(), Use::CouldHaveRegister); - addCall(); - } - - void getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind /*kind*/, int /*index*/, - bool /*captureRequired*/, IR::Expr *target) override - { - addDef(target); - addUses(base->asTemp(), Use::CouldHaveRegister); - addCall(); - } - - void getQObjectProperty(IR::Expr *base, int /*propertyIndex*/, bool /*captureRequired*/, - bool /*isSingleton*/, int /*attachedPropertiesId*/, IR::Expr *target) override - { - addDef(target); - addUses(base->asTemp(), Use::CouldHaveRegister); - addCall(); - } - - void getElement(IR::Expr *base, IR::Expr *index, Expr *target) override - { - addDef(target); - addUses(base->asTemp(), Use::CouldHaveRegister); - addUses(index->asTemp(), Use::CouldHaveRegister); - addCall(); - } - - void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) override - { - addUses(source->asTemp(), Use::CouldHaveRegister); - addUses(targetBase->asTemp(), Use::CouldHaveRegister); - addUses(targetIndex->asTemp(), Use::CouldHaveRegister); - addCall(); - } - - void copyValue(Expr *source, Expr *target) override - { - addDef(target); - Temp *sourceTemp = source->asTemp(); - if (!sourceTemp) - return; - addUses(sourceTemp, Use::CouldHaveRegister); - Temp *targetTemp = target->asTemp(); - if (targetTemp) - addHint(targetTemp, sourceTemp); - } - - void swapValues(Expr *, Expr *) override - { - // Inserted by the register allocator, so it cannot occur here. - Q_UNREACHABLE(); - } - - void unop(AluOp oper, Expr *source, Expr *target) override - { - addDef(target); - - bool needsCall = true; - if (oper == OpNot && source->type == IR::BoolType && target->type == IR::BoolType) - needsCall = false; - -#if 0 // TODO: change masm to generate code - switch (oper) { - case OpIfTrue: - case OpNot: - case OpUMinus: - case OpUPlus: - case OpCompl: - needsCall = sourceTemp->type & ~NumberType && sourceTemp->type != BoolType; - break; - - case OpIncrement: - case OpDecrement: - default: - Q_UNREACHABLE(); - } -#endif - - IR::Temp *sourceTemp = source->asTemp(); - if (needsCall) { - if (sourceTemp) - addUses(sourceTemp, Use::CouldHaveRegister); - addCall(); - } else { - if (sourceTemp) - addUses(sourceTemp, Use::MustHaveRegister); - } - } - - void binop(AluOp oper, Expr *leftSource, Expr *rightSource, Expr *target) override - { - bool needsCall = true; - - if (oper == OpStrictEqual || oper == OpStrictNotEqual) { - bool noCall = leftSource->type == NullType || rightSource->type == NullType - || leftSource->type == UndefinedType || rightSource->type == UndefinedType - || leftSource->type == BoolType || rightSource->type == BoolType; - needsCall = !noCall; - } else if (leftSource->type == DoubleType && rightSource->type == DoubleType) { - if (oper == OpMul || oper == OpAdd || oper == OpDiv || oper == OpSub - || (oper >= OpGt && oper <= OpStrictNotEqual)) { - needsCall = false; - } - } else if (oper == OpBitAnd || oper == OpBitOr || oper == OpBitXor || oper == OpLShift || oper == OpRShift || oper == OpURShift) { - needsCall = false; - } else if (oper == OpAdd || oper == OpMul || oper == OpSub - || (oper >= OpGt && oper <= OpStrictNotEqual)) { - if (leftSource->type == SInt32Type && rightSource->type == SInt32Type) - needsCall = false; - } - - addDef(target); - - if (needsCall) { - addUses(leftSource->asTemp(), Use::CouldHaveRegister); - addUses(rightSource->asTemp(), Use::CouldHaveRegister); - addCall(); - } else { - addUses(leftSource->asTemp(), Use::MustHaveRegister); - addHint(target, leftSource->asTemp()); - addHint(target, rightSource->asTemp()); - -#if CPU(X86) || CPU(X86_64) - switch (oper) { - // The rhs operand can be a memory address - case OpAdd: - case OpSub: - case OpMul: - case OpDiv: -#if CPU(X86_64) - if (leftSource->type == DoubleType || rightSource->type == DoubleType) { - // well, on 64bit the doubles are mangled, so they must first be loaded in a register and demangled, so...: - addUses(rightSource->asTemp(), Use::MustHaveRegister); - break; - } - Q_FALLTHROUGH(); -#endif - case OpBitAnd: - case OpBitOr: - case OpBitXor: - addUses(rightSource->asTemp(), Use::CouldHaveRegister); - break; - - default: - addUses(rightSource->asTemp(), Use::MustHaveRegister); - break; - } -#else - addUses(rightSource->asTemp(), Use::MustHaveRegister); -#endif - } - } - - void visitJump(IR::Jump *) override {} - void visitCJump(IR::CJump *s) override - { - if (Temp *t = s->cond->asTemp()) { -#if 0 // TODO: change masm to generate code - addUses(t, Use::MustHaveRegister); -#else - addUses(t, Use::CouldHaveRegister); - addCall(); -#endif - } else if (Binop *b = s->cond->asBinop()) { - binop(b->op, b->left, b->right, 0); - } else if (s->cond->asConst()) { - // TODO: SSA optimization for constant condition evaluation should remove this. - // See also visitCJump() in masm. - addCall(); - } else { - Q_UNREACHABLE(); - } - } - - void visitRet(IR::Ret *s) override - { addUses(s->expr->asTemp(), Use::CouldHaveRegister); } - - void visitPhi(IR::Phi *s) override - { - addDef(s->targetTemp, true); - for (int i = 0, ei = s->incoming.size(); i < ei; ++i) { - Expr *e = s->incoming.at(i); - if (Temp *t = e->asTemp()) { - // The actual use of an incoming value in a phi node is right before the terminator - // of the other side of the incoming edge. - const int usePos = _lifeTimeIntervals->positionForStatement(_currentBB->in.at(i)->terminator()) - 1; - addUses(t, Use::CouldHaveRegister, usePos); - addHint(s->targetTemp, t); - addHint(t, s->targetTemp); - } - } - } - -protected: - void callBuiltin(IR::Call *c, IR::Expr *result) override - { - addDef(result); - addUses(c->base, Use::CouldHaveRegister); - addUses(c->args, Use::CouldHaveRegister); - addCall(); - } - -private: - void addDef(Expr *e, bool isPhiTarget = false) - { - if (!e) - return; - Temp *t = e->asTemp(); - if (!t) - return; - if (!t || t->kind != Temp::VirtualRegister) - return; - Q_ASSERT(!_defs[t->index].isValid()); - bool canHaveReg = true; - switch (t->type) { - case QObjectType: - case VarType: - case StringType: - case UndefinedType: - case NullType: - canHaveReg = false; - break; - default: - break; - } - - _defs[t->index] = Def(canHaveReg, isPhiTarget); - } - - void addUses(Expr *e, Use::RegisterFlag flag) - { - const int usePos = usePosition(_currentStmt); - addUses(e, flag, usePos); - } - - void addUses(Expr *e, Use::RegisterFlag flag, int usePos) - { - Q_ASSERT(usePos > 0); - if (!e) - return; - Temp *t = e->asTemp(); - if (!t) - return; - if (t && t->kind == Temp::VirtualRegister) - _uses[t->index].push_back(Use(usePos, flag)); - } - - void addUses(ExprList *l, Use::RegisterFlag flag) - { - for (ExprList *it = l; it; it = it->next) - addUses(it->expr, flag); - } - - void addCall() - { - _calls.push_back(usePosition(_currentStmt)); - } - - void addHint(Expr *hinted, Temp *hint1, Temp *hint2 = 0) - { - if (hinted) - if (Temp *hintedTemp = hinted->asTemp()) - addHint(hintedTemp, hint1, hint2); - } - - void addHint(Temp *hinted, Temp *hint1, Temp *hint2 = 0) - { - if (!hinted || hinted->kind != Temp::VirtualRegister) - return; - if (hint1 && hint1->kind == Temp::VirtualRegister && hinted->type == hint1->type) - addHint(*hinted, Temp::VirtualRegister, hint1->index); - if (hint2 && hint2->kind == Temp::VirtualRegister && hinted->type == hint2->type) - addHint(*hinted, Temp::VirtualRegister, hint2->index); - } -}; - -} // JIT namespace -} // QV4 namespace -QT_END_NAMESPACE - -QT_USE_NAMESPACE - -using namespace QT_PREPEND_NAMESPACE(QV4::JIT); -using namespace QT_PREPEND_NAMESPACE(QV4::IR); -using namespace QT_PREPEND_NAMESPACE(QV4); - -namespace { -class ResolutionPhase -{ - Q_DISABLE_COPY(ResolutionPhase) - - LifeTimeIntervals::Ptr _intervals; - QVector<LifeTimeInterval *> _unprocessedReverseOrder; - IR::Function *_function; - const std::vector<int> &_assignedSpillSlots; - std::vector<const LifeTimeInterval *> _liveIntervals; - const QVector<const RegisterInfo *> &_intRegs; - const QVector<const RegisterInfo *> &_fpRegs; - - Stmt *_currentStmt; - std::vector<Move *> _loads; - std::vector<Move *> _stores; - - std::vector<std::vector<const LifeTimeInterval *> > _liveAtStart; - std::vector<std::vector<const LifeTimeInterval *> > _liveAtEnd; - -public: - ResolutionPhase(QVector<LifeTimeInterval *> &&unprocessedReversedOrder, - const LifeTimeIntervals::Ptr &intervals, - IR::Function *function, - const std::vector<int> &assignedSpillSlots, - const QVector<const RegisterInfo *> &intRegs, - const QVector<const RegisterInfo *> &fpRegs) - : _intervals(intervals) - , _unprocessedReverseOrder(unprocessedReversedOrder) - , _function(function) - , _assignedSpillSlots(assignedSpillSlots) - , _intRegs(intRegs) - , _fpRegs(fpRegs) - , _currentStmt(0) - { - _liveAtStart.resize(function->basicBlockCount()); - _liveAtEnd.resize(function->basicBlockCount()); - } - - void run() { - renumber(); - if (DebugRegAlloc) { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - IRPrinterWithPositions(&qout, _intervals).print(_function); - qDebug("%s", buf.data().constData()); - } - resolve(); - } - -private: - int defPosition(Stmt *s) const - { - return usePosition(s) + 1; - } - - int usePosition(Stmt *s) const - { - return _intervals->positionForStatement(s); - } - - void renumber() - { - QVector<Stmt *> newStatements; - - for (BasicBlock *bb : _function->basicBlocks()) { - _currentStmt = 0; - - QVector<Stmt *> statements = bb->statements(); - newStatements.reserve(bb->statements().size() + 7); - newStatements.erase(newStatements.begin(), newStatements.end()); - - cleanOldIntervals(_intervals->startPosition(bb)); - addNewIntervals(_intervals->startPosition(bb)); - _liveAtStart[bb->index()] = _liveIntervals; - - for (int i = 0, ei = statements.size(); i != ei; ++i) { - _currentStmt = statements.at(i); - _loads.clear(); - _stores.clear(); - if (_currentStmt->asTerminator()) - addNewIntervals(usePosition(_currentStmt)); - else - addNewIntervals(defPosition(_currentStmt)); - visit(_currentStmt); - for (Move *load : _loads) - newStatements.append(load); - if (_currentStmt->asPhi()) - newStatements.prepend(_currentStmt); - else - newStatements.append(_currentStmt); - for (Move *store : _stores) - newStatements.append(store); - } - - cleanOldIntervals(_intervals->endPosition(bb)); - _liveAtEnd[bb->index()] = _liveIntervals; - - if (DebugRegAlloc) { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream os(&buf); - os << "Intervals live at the start of L" << bb->index() << ":" << endl; - if (_liveAtStart[bb->index()].empty()) - os << "\t(none)" << endl; - for (const LifeTimeInterval *i : _liveAtStart.at(bb->index())) { - os << "\t"; - i->dump(os); - os << endl; - } - os << "Intervals live at the end of L" << bb->index() << ":" << endl; - if (_liveAtEnd[bb->index()].empty()) - os << "\t(none)" << endl; - for (const LifeTimeInterval *i : _liveAtEnd.at(bb->index())) { - os << "\t"; - i->dump(os); - os << endl; - } - qDebug("%s", buf.data().constData()); - } - - bb->setStatements(newStatements); - } - - } - - const LifeTimeInterval *findLiveInterval(Temp *t) const - { - for (const LifeTimeInterval *lti : _liveIntervals) { - if (lti->temp() == *t) - return lti; - } - - return nullptr; - } - - void maybeGenerateSpill(Temp *t) - { - const LifeTimeInterval *i = findLiveInterval(t); - if (i->reg() == LifeTimeInterval::InvalidRegister) - return; - - const RegisterInfo *pReg = platformRegister(*i); - Q_ASSERT(pReg); - int spillSlot = _assignedSpillSlots[i->temp().index]; - if (spillSlot != RegisterAllocator::InvalidSpillSlot) - _stores.push_back(generateSpill(spillSlot, i->temp().type, pReg->reg<int>())); - } - - void addNewIntervals(int position) - { - if (position == Stmt::InvalidId) - return; - - while (!_unprocessedReverseOrder.isEmpty()) { - const LifeTimeInterval *i = _unprocessedReverseOrder.constLast(); - if (i->start() > position) - break; - - Q_ASSERT(!i->isFixedInterval()); - auto it = _liveIntervals.begin(); - for (; it != _liveIntervals.end(); ++it) { - if ((*it)->temp() == i->temp()) { - *it = i; - break; - } - } - if (it == _liveIntervals.end()) - _liveIntervals.push_back(i); -// qDebug() << "-- Activating interval for temp" << i->temp().index; - - _unprocessedReverseOrder.removeLast(); - } - } - - void cleanOldIntervals(int position) - { - for (size_t it = 0; it != _liveIntervals.size(); ) { - const LifeTimeInterval *lti = _liveIntervals.at(it); - if (lti->end() < position || lti->isFixedInterval()) - _liveIntervals.erase(_liveIntervals.begin() + it); - else - ++it; - } - } - - void resolve() - { - for (BasicBlock *bb : _function->basicBlocks()) { - for (BasicBlock *bbOut : bb->out) - resolveEdge(bb, bbOut); - } - } - - Phi *findDefPhi(const Temp &t, BasicBlock *bb) const - { - for (Stmt *s : bb->statements()) { - Phi *phi = s->asPhi(); - if (!phi) - return 0; - - if (*phi->targetTemp == t) - return phi; - } - - Q_UNREACHABLE(); - } - - void resolveEdge(BasicBlock *predecessor, BasicBlock *successor) - { - if (DebugRegAlloc) { - qDebug() << "Resolving edge" << predecessor->index() << "->" << successor->index(); - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - IRPrinterWithPositions printer(&qout, _intervals); - printer.print(predecessor); - printer.print(successor); - qDebug("%s", buf.data().constData()); - } - - MoveMapping mapping; - - const int predecessorEnd = _intervals->endPosition(predecessor); - Q_ASSERT(predecessorEnd > 0); - - int successorStart = _intervals->startPosition(successor); - Q_ASSERT(successorStart > 0); - - for (const LifeTimeInterval *it : _liveAtStart.at(successor->index())) { - bool isPhiTarget = false; - Expr *moveFrom = 0; - - if (it->start() == successorStart) { - if (Phi *phi = findDefPhi(it->temp(), successor)) { - isPhiTarget = true; - Expr *opd = phi->incoming[successor->in.indexOf(predecessor)]; - if (opd->asConst()) { - moveFrom = opd; - } else { - Temp *t = opd->asTemp(); - Q_ASSERT(t); - - for (const LifeTimeInterval *it2 : _liveAtEnd.at(predecessor->index())) { - if (it2->temp() == *t - && it2->reg() != LifeTimeInterval::InvalidRegister - && it2->covers(predecessorEnd)) { - moveFrom = createPhysicalRegister(it2, t->type); - break; - } - } - if (!moveFrom) - moveFrom = createTemp(Temp::StackSlot, - _assignedSpillSlots[t->index], - t->type); - } - } - } else { - for (const LifeTimeInterval *predIt : _liveAtEnd.at(predecessor->index())) { - if (predIt->temp() == it->temp()) { - if (predIt->reg() != LifeTimeInterval::InvalidRegister - && predIt->covers(predecessorEnd)) { - moveFrom = createPhysicalRegister(predIt, predIt->temp().type); - } else { - int spillSlot = _assignedSpillSlots[predIt->temp().index]; - if (spillSlot != -1) - moveFrom = createTemp(Temp::StackSlot, spillSlot, predIt->temp().type); - } - break; - } - } - } - if (!moveFrom) { -#if !defined(QT_NO_DEBUG) && 0 - bool lifeTimeHole = false; - if (it->ranges().first().start <= successorStart && it->ranges().last().end >= successorStart) - lifeTimeHole = !it->covers(successorStart); - - Q_ASSERT(!_info->isPhiTarget(it->temp()) || it->isSplitFromInterval() || lifeTimeHole); - if (_info->def(it->temp()) != successorStart && !it->isSplitFromInterval()) { - const int successorEnd = successor->terminator()->id(); - const int idx = successor->in.indexOf(predecessor); - for (const Use &use : _info->uses(it->temp)) { - if (use.pos == static_cast<unsigned>(successorStart)) { - // only check the current edge, not all other possible ones. This is - // important for phi nodes: they have uses that are only valid when - // coming in over a specific edge. - for (Stmt *s : successor->statements()) { - if (Phi *phi = s->asPhi()) { - Q_ASSERT(it->temp().index != phi->targetTemp->index); - Q_ASSERT(phi->d->incoming[idx]->asTemp() == 0 - || it->temp().index != phi->d->incoming[idx]->asTemp()->index); - } else { - // TODO: check that the first non-phi statement does not use - // the temp. - break; - } - } - } else { - Q_ASSERT(use.pos < static_cast<unsigned>(successorStart) || - use.pos > static_cast<unsigned>(successorEnd)); - } - } - } -#endif - - continue; - } - - Temp *moveTo; - if (it->reg() == LifeTimeInterval::InvalidRegister || !it->covers(successorStart)) { - if (!isPhiTarget) // if it->temp() is a phi target, skip it. - continue; - const int spillSlot = _assignedSpillSlots[it->temp().index]; - if (spillSlot == RegisterAllocator::InvalidSpillSlot) - continue; // it has a life-time hole here. - moveTo = createTemp(Temp::StackSlot, spillSlot, it->temp().type); - } else { - moveTo = createPhysicalRegister(it, it->temp().type); - } - - // add move to mapping - mapping.add(moveFrom, moveTo); - } - - if (DebugRegAlloc) - mapping.dump(); - mapping.order(); - if (DebugRegAlloc) - mapping.dump(); - - bool insertIntoPredecessor = successor->in.size() > 1; - mapping.insertMoves(insertIntoPredecessor ? predecessor : successor, _function, - insertIntoPredecessor); - - if (DebugRegAlloc) { - qDebug() << ".. done, result:"; - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - IRPrinterWithPositions printer(&qout, _intervals); - printer.print(predecessor); - printer.print(successor); - qDebug("%s", buf.data().constData()); - } - } - - Temp *createTemp(Temp::Kind kind, int index, Type type) const - { - Q_ASSERT(index >= 0); - Temp *t = _function->New<Temp>(); - t->init(kind, index); - t->type = type; - return t; - } - - Temp *createPhysicalRegister(const LifeTimeInterval *i, Type type) const - { - const RegisterInfo *ri = platformRegister(*i); - Q_ASSERT(ri); - return createTemp(Temp::PhysicalRegister, ri->reg<int>(), type); - } - - const RegisterInfo *platformRegister(const LifeTimeInterval &i) const - { - if (i.isFP()) - return _fpRegs.value(i.reg(), 0); - else - return _intRegs.value(i.reg(), 0); - } - - Move *generateSpill(int spillSlot, Type type, int pReg) const - { - Q_ASSERT(spillSlot >= 0); - - Move *store = _function->NewStmt<Move>(); - store->init(createTemp(Temp::StackSlot, spillSlot, type), - createTemp(Temp::PhysicalRegister, pReg, type)); - return store; - } - - Move *generateUnspill(const Temp &t, int pReg) const - { - Q_ASSERT(pReg >= 0); - int spillSlot = _assignedSpillSlots[t.index]; - Q_ASSERT(spillSlot != -1); - Move *load = _function->NewStmt<Move>(); - load->init(createTemp(Temp::PhysicalRegister, pReg, t.type), - createTemp(Temp::StackSlot, spillSlot, t.type)); - return load; - } - -private: - void visit(Expr *e) - { - switch (e->exprKind) { - case Expr::TempExpr: - visitTemp(e->asTemp()); - break; - default: - EXPR_VISIT_ALL_KINDS(e); - break; - } - } - - void visitTemp(Temp *t) - { - if (t->kind != Temp::VirtualRegister) - return; - - const LifeTimeInterval *i = findLiveInterval(t); - Q_ASSERT(i->isValid()); - - if (_currentStmt != 0 && i->start() == usePosition(_currentStmt)) { - Q_ASSERT(i->isSplitFromInterval()); - const RegisterInfo *pReg = platformRegister(*i); - Q_ASSERT(pReg); - _loads.push_back(generateUnspill(i->temp(), pReg->reg<int>())); - } - - if (i->reg() != LifeTimeInterval::InvalidRegister && - (i->covers(defPosition(_currentStmt)) || - i->covers(usePosition(_currentStmt)))) { - const RegisterInfo *pReg = platformRegister(*i); - Q_ASSERT(pReg); - t->kind = Temp::PhysicalRegister; - t->index = pReg->reg<unsigned>(); - } else { - int stackSlot = _assignedSpillSlots[t->index]; - Q_ASSERT(stackSlot >= 0); - t->kind = Temp::StackSlot; - t->index = stackSlot; - } - } - - void visit(Stmt *s) - { - switch (s->stmtKind) { - case Stmt::MoveStmt: { - auto m = s->asMove(); - if (Temp *t = m->target->asTemp()) - maybeGenerateSpill(t); - - visit(m->source); - visit(m->target); - } break; - case Stmt::PhiStmt: { - auto p = s->asPhi(); - maybeGenerateSpill(p->targetTemp); - } break; - default: - STMT_VISIT_ALL_KINDS(s); - break; - } - } -}; -} // anonymous namespace - -RegisterAllocator::RegisterAllocator(const QV4::JIT::RegisterInformation ®isterInformation) - : _registerInformation(registerInformation) -{ - for (int i = 0, ei = registerInformation.size(); i != ei; ++i) { - const RegisterInfo ®Info = registerInformation.at(i); - if (regInfo.useForRegAlloc()) { - if (regInfo.isRegularRegister()) - _normalRegisters.append(®Info); - else - _fpRegisters.append(®Info); - } - } - Q_ASSERT(_normalRegisters.size() >= 2); - Q_ASSERT(_fpRegisters.size() >= 2); - _active.reserve((_normalRegisters.size() + _fpRegisters.size()) * 2); - _inactive.reserve(_active.size()); - - _regularRegsInUse.resize(_normalRegisters.size()); - _fpRegsInUse.resize(_fpRegisters.size()); -} - -RegisterAllocator::~RegisterAllocator() -{ -} - -void RegisterAllocator::run(IR::Function *function, const Optimizer &opt) -{ - _lastAssignedRegister.assign(function->tempCount, LifeTimeInterval::InvalidRegister); - _assignedSpillSlots.assign(function->tempCount, InvalidSpillSlot); - _activeSpillSlots.resize(function->tempCount); - - if (DebugRegAlloc) - qDebug() << "*** Running regalloc for function" << (function->name ? qPrintable(*function->name) : "NO NAME") << "***"; - - _lifeTimeIntervals = opt.lifeTimeIntervals(); - - _unhandled = _lifeTimeIntervals->intervals(); - _handled.reserve(_unhandled.size()); - - _info.reset(new RegAllocInfo); - _info->collect(function, _lifeTimeIntervals); - - if (DebugRegAlloc) { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - qout << "Ranges:" << endl; - QVector<LifeTimeInterval *> intervals = _unhandled; - std::reverse(intervals.begin(), intervals.end()); - for (const LifeTimeInterval *r : qAsConst(intervals)) { - r->dump(qout); - qout << endl; - } - qDebug("%s", buf.data().constData()); - _info->dump(); - - qDebug() << "*** Before register allocation:"; - buf.setData(QByteArray()); - IRPrinterWithPositions(&qout, _lifeTimeIntervals).print(function); - qDebug("%s", buf.data().constData()); - } - prepareRanges(); - - linearScan(); - - if (DebugRegAlloc) - dump(function); - - // sort the ranges in reverse order, so the ResolutionPhase can take from the end (and thereby - // prevent the copy overhead that taking from the beginning would give). - std::sort(_handled.begin(), _handled.end(), - [](const LifeTimeInterval *r1, const LifeTimeInterval *r2) -> bool { - return LifeTimeInterval::lessThan(r2, r1); - }); - ResolutionPhase(std::move(_handled), _lifeTimeIntervals, function, _assignedSpillSlots, _normalRegisters, _fpRegisters).run(); - - function->tempCount = *std::max_element(_assignedSpillSlots.begin(), _assignedSpillSlots.end()) + 1; - - if (DebugRegAlloc) - qDebug() << "*** Finished regalloc , result:"; - - static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_IR"); - if (showCode) { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - IRPrinterWithRegisters(&qout, _lifeTimeIntervals, _registerInformation).print(function); - qDebug("%s", buf.data().constData()); - } -} - -RegisterInformation RegisterAllocator::usedRegisters() const -{ - RegisterInformation regInfo; - - for (int i = 0, ei = _normalRegisters.size(); i != ei; ++i) { - if (_regularRegsInUse.testBit(i)) - regInfo.append(*_normalRegisters.at(i)); - } - - for (int i = 0, ei = _fpRegisters.size(); i != ei; ++i) { - if (_fpRegsInUse.testBit(i)) - regInfo.append(*_fpRegisters.at(i)); - } - - return regInfo; -} - -void RegisterAllocator::markInUse(int reg, bool isFPReg) -{ - if (isFPReg) - _fpRegsInUse.setBit(reg); - else - _regularRegsInUse.setBit(reg); -} - -static inline LifeTimeInterval createFixedInterval(int rangeCount) -{ - LifeTimeInterval i(rangeCount); - i.setReg(0); - - Temp t; - t.init(Temp::PhysicalRegister, 0); - t.type = IR::SInt32Type; - i.setTemp(t); - - return i; -} - -LifeTimeInterval *RegisterAllocator::cloneFixedInterval(int reg, bool isFP, const LifeTimeInterval &original) -{ - LifeTimeInterval *lti = new LifeTimeInterval(original); - _lifeTimeIntervals->add(lti); - lti->setReg(reg); - lti->setFixedInterval(true); - - Temp t; - t.init(Temp::PhysicalRegister, reg); - t.type = isFP ? IR::DoubleType : IR::SInt32Type; - lti->setTemp(t); - - return lti; -} - -// Creates the intervals with fixed ranges. See [Wimmer2]. Note that this only applies to callee- -// saved registers. -void RegisterAllocator::prepareRanges() -{ - LifeTimeInterval ltiWithCalls = createFixedInterval(int(_info->calls().size())); - for (int callPosition : _info->calls()) - ltiWithCalls.addRange(callPosition, callPosition); - - const int regCount = _normalRegisters.size(); - _fixedRegisterRanges.resize(regCount); - for (int reg = 0; reg < regCount; ++reg) { - if (_normalRegisters.at(reg)->isCallerSaved()) { - LifeTimeInterval *lti = cloneFixedInterval(reg, false, ltiWithCalls); - if (lti->isValid()) { - _fixedRegisterRanges[reg] = lti; - _active.append(lti); - } - } - } - - const int fpRegCount = _fpRegisters.size(); - _fixedFPRegisterRanges.resize(fpRegCount); - for (int fpReg = 0; fpReg < fpRegCount; ++fpReg) { - if (_fpRegisters.at(fpReg)->isCallerSaved()) { - LifeTimeInterval *lti = cloneFixedInterval(fpReg, true, ltiWithCalls); - if (lti->isValid()) { - _fixedFPRegisterRanges[fpReg] = lti; - _active.append(lti); - } - } - } -} - -void RegisterAllocator::linearScan() -{ - while (!_unhandled.isEmpty()) { - LifeTimeInterval *current = _unhandled.back(); - _unhandled.pop_back(); - const int position = current->start(); - - // check for intervals in active that are handled or inactive - for (int i = 0; i < _active.size(); ) { - LifeTimeInterval *it = _active.at(i); - if (it->end() < position) { - if (!it->isFixedInterval()) - _handled += it; - _active.remove(i); - } else if (!it->covers(position)) { - _inactive += it; - _active.remove(i); - } else { - ++i; - } - } - - // check for intervals in inactive that are handled or active - for (int i = 0; i < _inactive.size(); ) { - LifeTimeInterval *it = _inactive.at(i); - if (it->end() < position) { - if (!it->isFixedInterval()) - _handled += it; - _inactive.remove(i); - } else if (it->covers(position)) { - if (it->reg() != LifeTimeInterval::InvalidRegister) { - _active += it; - _inactive.remove(i); - } else { - // although this interval is now active, it has no register allocated (always - // spilled), so leave it in inactive. - ++i; - } - } else { - ++i; - } - } - - Q_ASSERT(!current->isFixedInterval()); - -#ifdef DEBUG_REGALLOC - qDebug() << "** Position" << position; -#endif // DEBUG_REGALLOC - - if (_info->canHaveRegister(current->temp())) { - tryAllocateFreeReg(*current); - if (current->reg() == LifeTimeInterval::InvalidRegister) - allocateBlockedReg(*current); - if (current->reg() != LifeTimeInterval::InvalidRegister) - _active += current; - } else { - assignSpillSlot(current->temp(), current->start(), current->end()); - _inactive += current; - if (DebugRegAlloc) - qDebug() << "*** allocating stack slot" << _assignedSpillSlots[current->temp().index] - << "for %" << current->temp().index << "as it cannot be loaded in a register"; - } - } - - for (LifeTimeInterval *r : qAsConst(_active)) - if (!r->isFixedInterval()) - _handled.append(r); - _active.clear(); - for (LifeTimeInterval *r : qAsConst(_inactive)) - if (!r->isFixedInterval()) - _handled.append(r); - _inactive.clear(); -} - -static inline int indexOfRangeCoveringPosition(const LifeTimeInterval::Ranges &ranges, int position) -{ - for (int i = 0, ei = ranges.size(); i != ei; ++i) { - if (position <= ranges[i].end) - return i; - } - return -1; -} - -static inline int intersectionPosition(const LifeTimeIntervalRange &one, const LifeTimeIntervalRange &two) -{ - if (one.covers(two.start)) - return two.start; - if (two.covers(one.start)) - return one.start; - return -1; -} - -static inline bool isFP(const Temp &t) -{ return t.type == DoubleType; } - -static inline bool candidateIsBetterFit(int bestSizeSoFar, int idealSize, int candidateSize) -{ - // If the candidateSize is larger than the current we take it only if the current size does not - // yet fit for the whole interval. - if (bestSizeSoFar < candidateSize && bestSizeSoFar < idealSize) - return true; - - // If the candidateSize is smaller we only take it if it still fits the whole interval. - if (bestSizeSoFar > candidateSize && candidateSize >= idealSize) - return true; - - // Other wise: no luck. - return false; -} - -// Out of all available registers (with their next-uses), choose the one that fits the requested -// duration best. This can return a register that is not free for the whole interval, but that's -// fine: we just have to split the current interval. -static void longestAvailableReg(int *nextUses, int nextUseCount, int ®, int &freeUntilPos_reg, int lastUse) -{ - reg = LifeTimeInterval::InvalidRegister; - freeUntilPos_reg = 0; - - for (int candidate = 0, candidateEnd = nextUseCount; candidate != candidateEnd; ++candidate) { - int fp = nextUses[candidate]; - if (candidateIsBetterFit(freeUntilPos_reg, lastUse, fp)) { - reg = candidate; - freeUntilPos_reg = fp; - } - } -} - -#define CALLOC_ON_STACK(ty, ptr, sz, val) \ - Q_ASSERT(sz > 0); \ - Q_ALLOCA_VAR(ty, ptr, sizeof(ty) * (sz)); \ - for (ty *it = ptr, *eit = ptr + (sz); it != eit; ++it) \ - *it = val; - -// Try to allocate a register that's currently free. -void RegisterAllocator::tryAllocateFreeReg(LifeTimeInterval ¤t) -{ - Q_ASSERT(!current.isFixedInterval()); - Q_ASSERT(current.reg() == LifeTimeInterval::InvalidRegister); - - const bool needsFPReg = isFP(current.temp()); - const int freeUntilPosCount = needsFPReg ? _fpRegisters.size() : _normalRegisters.size(); - CALLOC_ON_STACK(int, freeUntilPos, freeUntilPosCount, INT_MAX); - - for (Intervals::const_iterator i = _active.constBegin(), ei = _active.constEnd(); i != ei; ++i) { - const LifeTimeInterval *it = *i; - if (it->isFP() == needsFPReg) - freeUntilPos[it->reg()] = 0; // mark register as unavailable - } - - for (Intervals::const_iterator i = _inactive.constBegin(), ei = _inactive.constEnd(); i != ei; ++i) { - const LifeTimeInterval *it = *i; - if (it->isFP() != needsFPReg) - continue; // different register type, so not applicable. - if (it->reg() == LifeTimeInterval::InvalidRegister) - continue; // this range does not block a register from being used, as it has no register assigned - - if (current.isSplitFromInterval() || it->isFixedInterval()) { - const int intersectionPos = nextIntersection(current, *it); - if (intersectionPos != -1) - freeUntilPos[it->reg()] = qMin(freeUntilPos[it->reg()], intersectionPos); - } - } - - int reg = LifeTimeInterval::InvalidRegister; - int freeUntilPos_reg = 0; - - const RegAllocInfo::Hints &hints = _info->hints(current.temp()); - for (RegAllocInfo::Hints::const_iterator i = hints.begin(), ei = hints.end(); i != ei; ++i) { - const Temp &hint = *i; - int candidate; - if (hint.kind == Temp::PhysicalRegister) - candidate = hint.index; - else - candidate = _lastAssignedRegister[hint.index]; - - const int end = current.end(); - if (candidate == LifeTimeInterval::InvalidRegister) - continue; // the candidate has no register assigned, so it cannot be (re-)used - if (current.isFP() != isFP(hint)) - continue; // different register type, so not applicable. - - const int fp = freeUntilPos[candidate]; - if (candidateIsBetterFit(freeUntilPos_reg, end, fp)) { - reg = candidate; - freeUntilPos_reg = fp; - } - } - - // None of the hinted registers could fit the interval, so try all registers next. - if (reg == LifeTimeInterval::InvalidRegister) - longestAvailableReg(freeUntilPos, freeUntilPosCount, reg, freeUntilPos_reg, current.end()); - - if (freeUntilPos_reg == 0) { - // no register available without spilling - if (DebugRegAlloc) - qDebug("*** no register available for %u", current.temp().index); - return; - } else if (current.end() < freeUntilPos_reg) { - // register available for the whole interval - if (DebugRegAlloc) - qDebug() << "*** allocating register" << reg << "for the whole interval of %" << current.temp().index; - current.setReg(reg); - _lastAssignedRegister[current.temp().index] = reg; - markInUse(reg, needsFPReg); - } else { - // register available for the first part of the interval - - // TODO: this is slightly inefficient in the following case: - // %1 = something - // some_call(%1) - // %2 = %1 + 1 - // Now %1 will get a register assigned, and will be spilled to the stack immediately. It - // would be better to check if there are actually uses in the range before the split. - - current.setReg(reg); - _lastAssignedRegister[current.temp().index] = reg; - if (DebugRegAlloc) - qDebug() << "*** allocating register" << reg << "for the first part of interval of %" << current.temp().index; - split(current, freeUntilPos_reg, true); - markInUse(reg, needsFPReg); - } -} - -// This gets called when all registers are currently in use. -void RegisterAllocator::allocateBlockedReg(LifeTimeInterval ¤t) -{ - Q_ASSERT(!current.isFixedInterval()); - Q_ASSERT(current.reg() == LifeTimeInterval::InvalidRegister); - const int position = current.start(); - - const bool isPhiTarget = _info->isPhiTarget(current.temp()); - if (isPhiTarget && !current.isSplitFromInterval()) { - // Special case: storing to a phi-node's target will result in a single move. So, if we - // would spill another interval to the stack (that's 1 store), and then do the move for the - // phi target (at least 1 move or a load), that would result in 2 instructions. Instead, we - // force the phi-node's target to go to the stack immediately, which is always a single - // store. - split(current, position + 1, true); - _inactive.append(¤t); - return; - } - - const bool needsFPReg = isFP(current.temp()); - const int nextUsePosCount = needsFPReg ? _fpRegisters.size() : _normalRegisters.size(); - CALLOC_ON_STACK(int, nextUsePos, nextUsePosCount, INT_MAX); - QVector<LifeTimeInterval *> nextUseRangeForReg(nextUsePosCount, 0); - - for (Intervals::const_iterator i = _active.constBegin(), ei = _active.constEnd(); i != ei; ++i) { - LifeTimeInterval &it = **i; - if (it.isFP() != needsFPReg) - continue; // different register type, so not applicable. - - const int nu = it.isFixedInterval() ? 0 : nextUse(it.temp(), current.start()); - if (nu == position) { - nextUsePos[it.reg()] = 0; - } else if (nu != -1 && nu < nextUsePos[it.reg()]) { - nextUsePos[it.reg()] = nu; - nextUseRangeForReg[it.reg()] = ⁢ - } else if (nu == -1 && nextUsePos[it.reg()] == INT_MAX) { - // in a loop, the range can be active, but the result might only be used before the - // current position (e.g. the induction variable being used in the phi node in the loop - // header). So, we can use this register, but we need to remember to split the interval - // in order to have the edge-resolving generate a load at the edge going back to the - // loop header. - nextUseRangeForReg[it.reg()] = ⁢ - } - } - - for (Intervals::const_iterator i = _inactive.constBegin(), ei = _inactive.constEnd(); i != ei; ++i) { - LifeTimeInterval &it = **i; - if (it.isFP() != needsFPReg) - continue; // different register type, so not applicable. - if (it.reg() == LifeTimeInterval::InvalidRegister) - continue; // this range does not block a register from being used, as it has no register assigned - - if (current.isSplitFromInterval() || it.isFixedInterval()) { - if (nextIntersection(current, it) != -1) { - const int nu = nextUse(it.temp(), current.start()); - if (nu != -1 && nu < nextUsePos[it.reg()]) { - nextUsePos[it.reg()] = nu; - nextUseRangeForReg[it.reg()] = ⁢ - } - } - } - } - - int reg, nextUsePos_reg; - longestAvailableReg(nextUsePos, nextUsePosCount, reg, nextUsePos_reg, current.end()); - - Q_ASSERT(current.start() <= nextUsePos_reg); - - // spill interval that currently block reg - if (DebugRegAlloc) { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream out(&buf); - out << "*** spilling intervals that block reg " <<reg<< " for interval "; - current.dump(out); - qDebug("%s", buf.data().constData()); - } - current.setReg(reg); - _lastAssignedRegister[current.temp().index] = reg; - LifeTimeInterval *nextUse = nextUseRangeForReg[reg]; - Q_ASSERT(nextUse); - Q_ASSERT(!nextUse->isFixedInterval()); - - split(*nextUse, position, /*skipOptionalRegisterUses =*/ true); - - // We might have chosen a register that is used by a range that has a hole in its life time. - // If that's the case, check if the current interval completely fits in the hole. Or rephrased: - // check if the current interval will use the register after that hole ends (so that range, and - // if so, split that interval so that it gets a new register assigned when it needs one. - splitInactiveAtEndOfLifetimeHole(reg, needsFPReg, position); - - // make sure that current does not intersect with the fixed interval for reg - const LifeTimeInterval *fixedRegRange = needsFPReg ? _fixedFPRegisterRanges.at(reg) - : _fixedRegisterRanges.at(reg); - if (fixedRegRange) { - int ni = nextIntersection(current, *fixedRegRange); - if (ni != -1) { - if (DebugRegAlloc) { - qDebug("***-- current range intersects with a fixed reg use at %d, so splitting it.", ni); - } - // current does overlap with a fixed interval, so split current before that intersection. - split(current, ni, true); - } - } -} - -int RegisterAllocator::nextIntersection(const LifeTimeInterval ¤t, - const LifeTimeInterval &another) const -{ - const LifeTimeInterval::Ranges ¤tRanges = current.ranges(); - int currentIt = 0; - - const LifeTimeInterval::Ranges &anotherRanges = another.ranges(); - const int anotherItStart = indexOfRangeCoveringPosition(anotherRanges, current.start()); - if (anotherItStart == -1) - return -1; - - for (int currentEnd = currentRanges.size(); currentIt < currentEnd; ++currentIt) { - const LifeTimeIntervalRange currentRange = currentRanges.at(currentIt); - for (int anotherIt = anotherItStart, anotherEnd = anotherRanges.size(); anotherIt < anotherEnd; ++anotherIt) { - const LifeTimeIntervalRange anotherRange = anotherRanges.at(anotherIt); - if (anotherRange.start > currentRange.end) - break; - int intersectPos = intersectionPosition(currentRange, anotherRange); - if (intersectPos != -1) - return intersectPos; - } - } - - return -1; -} - -/// Find the first use after the start position for the given temp. -/// -/// This is only called when all registers are in use, and when one of them has to be spilled to the -/// stack. So, uses where a register is optional can be ignored. -int RegisterAllocator::nextUse(const Temp &t, int startPosition) const -{ - typedef std::vector<Use>::const_iterator ConstIt; - - const std::vector<Use> &usePositions = _info->uses(t); - const ConstIt cend = usePositions.end(); - for (ConstIt it = usePositions.begin(); it != cend; ++it) { - if (it->mustHaveRegister()) { - const int usePos = it->pos; - if (usePos >= startPosition) - return usePos; - } - } - - return -1; -} - -static inline void insertReverseSorted(QVector<LifeTimeInterval *> &intervals, LifeTimeInterval *newInterval) -{ - newInterval->validate(); - for (int i = intervals.size(); i > 0;) { - if (LifeTimeInterval::lessThan(newInterval, intervals.at(--i))) { - intervals.insert(i + 1, newInterval); - return; - } - } - intervals.insert(0, newInterval); -} - -void RegisterAllocator::split(LifeTimeInterval ¤t, int beforePosition, - bool skipOptionalRegisterUses) -{ // TODO: check if we can always skip the optional register uses - Q_ASSERT(!current.isFixedInterval()); - - if (DebugRegAlloc) { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream out(&buf); - out << "***** split request for range "; - current.dump(out); - out << " before position " << beforePosition - << " and skipOptionalRegisterUses = " << skipOptionalRegisterUses << endl; - qDebug("%s", buf.data().constData()); - } - - assignSpillSlot(current.temp(), current.start(), current.end()); - - const int firstPosition = current.start(); - Q_ASSERT(beforePosition > firstPosition && "split before start"); - - int lastUse = firstPosition; - int nextUse = -1; - const std::vector<Use> &usePositions = _info->uses(current.temp()); - for (size_t i = 0, ei = usePositions.size(); i != ei; ++i) { - const Use &usePosition = usePositions.at(i); - const int usePos = usePosition.pos; - if (lastUse < usePos && usePos < beforePosition) { - lastUse = usePos; - } else if (usePos >= beforePosition) { - if (!skipOptionalRegisterUses || usePosition.mustHaveRegister()) { - nextUse = usePos; - break; - } - } - } - Q_ASSERT(lastUse != -1); - Q_ASSERT(lastUse < beforePosition); - - LifeTimeInterval newInterval = current.split(lastUse, nextUse); - if (DebugRegAlloc) { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream out(&buf); - out << "***** last use = " << lastUse << ", nextUse = " << nextUse << endl; - out << "***** new interval: "; - newInterval.dump(out); - out << endl; - out << "***** preceding interval: "; - current.dump(out); - out << endl; - qDebug("%s", buf.data().constData()); - } - if (newInterval.isValid()) { - if (current.reg() != LifeTimeInterval::InvalidRegister) - _info->addHint(current.temp(), current.reg()); - newInterval.setReg(LifeTimeInterval::InvalidRegister); - LifeTimeInterval *newIntervalPtr = new LifeTimeInterval(newInterval); - _lifeTimeIntervals->add(newIntervalPtr); - insertReverseSorted(_unhandled, newIntervalPtr); - } -} - -void RegisterAllocator::splitInactiveAtEndOfLifetimeHole(int reg, bool isFPReg, int position) -{ - for (int i = 0, ei = _inactive.size(); i != ei; ++i) { - LifeTimeInterval &interval = *_inactive[i]; - if (interval.isFixedInterval()) - continue; - if (isFPReg == interval.isFP() && interval.reg() == reg) { - LifeTimeInterval::Ranges ranges = interval.ranges(); - int endOfLifetimeHole = -1; - for (int j = 0, ej = ranges.size(); j != ej; ++j) { - if (position < ranges[j].start) - endOfLifetimeHole = ranges[j].start; - } - if (endOfLifetimeHole != -1) - split(interval, endOfLifetimeHole); - } - } -} - -void RegisterAllocator::assignSpillSlot(const Temp &t, int startPos, int endPos) -{ - if (_assignedSpillSlots[t.index] != InvalidSpillSlot) - return; - - for (int i = 0, ei = _activeSpillSlots.size(); i != ei; ++i) { - if (_activeSpillSlots.at(i) < startPos) { - _activeSpillSlots[i] = endPos; - _assignedSpillSlots[t.index] = i; - return; - } - } - - Q_UNREACHABLE(); -} - -void RegisterAllocator::dump(IR::Function *function) const -{ - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - IRPrinterWithPositions printer(&qout, _lifeTimeIntervals); - - qout << "Ranges:" << endl; - QVector<LifeTimeInterval *> handled = _handled; - std::sort(handled.begin(), handled.end(), LifeTimeInterval::lessThanForTemp); - for (const LifeTimeInterval *r : qAsConst(handled)) { - r->dump(qout); - qout << endl; - } - - qout << "Spill slots:" << endl; - for (unsigned i = 0; i < _assignedSpillSlots.size(); ++i) - if (_assignedSpillSlots[i] != InvalidSpillSlot) - qout << "\t%" << i << " -> " << _assignedSpillSlots[i] << endl; - - printer.print(function); - qDebug("%s", buf.data().constData()); -} - -// References: -// [Wimmer1] C. Wimmer and M. Franz. Linear Scan Register Allocation on SSA Form. In Proceedings of -// CGO'10, ACM Press, 2010 -// [Wimmer2] C. Wimmer and H. Mossenbock. Optimized Interval Splitting in a Linear Scan Register -// Allocator. In Proceedings of the ACM/USENIX International Conference on Virtual -// Execution Environments, pages 132-141. ACM Press, 2005. -// [Traub] Omri Traub, Glenn Holloway, and Michael D. Smith. Quality and Speed in Linear-scan -// Register Allocation. In Proceedings of the ACM SIGPLAN 1998 Conference on Programming -// Language Design and Implementation, pages 142-151, June 1998. diff --git a/src/qml/jit/qv4regalloc_p.h b/src/qml/jit/qv4regalloc_p.h deleted file mode 100644 index 4ee440b73e..0000000000 --- a/src/qml/jit/qv4regalloc_p.h +++ /dev/null @@ -1,140 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the V4VM 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 QV4REGALLOC_P_H -#define QV4REGALLOC_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 "qv4global_p.h" -#include "qv4isel_p.h" -#include "qv4ssa_p.h" -#include "qv4registerinfo_p.h" - -#include <config.h> - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace JIT { - -class RegAllocInfo; - -// This class implements a linear-scan register allocator, with a couple of tweaks: -// - Second-chance allocation is used when an interval becomes active after a spill, which results -// in fewer differences between edges, and hence fewer moves before jumps. -// - Use positions are flagged with either "must have" register or "could have" register. This is -// used to decide whether a register is really needed when a temporary is used after a spill -// occurred. -// - Fixed intervals are used to denotate IR positions where certain registers are needed in order -// to implement the operation, and cannot be used by a temporary on that position. An example is -// caller saved registers, where the call will use/clobber those registers. -// - Hints are used to indicate which registers could be used to generate more compact code. An -// example is an addition, where one (or both) operands' life-time ends at that instruction. In -// this case, re-using an operand register for the result will result in an in-place add. -// - SSA form properties are used: -// - to simplify life-times (two temporaries will never interfere as long as their intervals -// are not split), resulting in a slightly faster algorithm; -// - when a temporary needs to be spilled, it is done directly after calculating it, so that -// 1 store is generated even if multiple spills/splits happen. -// - phi-node elimination (SSA form deconstruction) is done when resolving differences between -// CFG edges -class RegisterAllocator -{ - typedef IR::LifeTimeInterval LifeTimeInterval; - - const RegisterInformation &_registerInformation; - QVector<const RegisterInfo *> _normalRegisters; - QVector<const RegisterInfo *> _fpRegisters; - QScopedPointer<RegAllocInfo> _info; - - QVector<LifeTimeInterval *> _fixedRegisterRanges, _fixedFPRegisterRanges; - - IR::LifeTimeIntervals::Ptr _lifeTimeIntervals; - typedef QVector<LifeTimeInterval *> Intervals; - Intervals _unhandled, _active, _inactive, _handled; - - std::vector<int> _lastAssignedRegister; - std::vector<int> _assignedSpillSlots; - QVector<int> _activeSpillSlots; - - QBitArray _regularRegsInUse, _fpRegsInUse; - - Q_DISABLE_COPY(RegisterAllocator) - -public: - enum { InvalidSpillSlot = -1 }; - - RegisterAllocator(const RegisterInformation ®isterInformation); - ~RegisterAllocator(); - - void run(IR::Function *function, const IR::Optimizer &opt); - RegisterInformation usedRegisters() const; - -private: - void markInUse(int reg, bool isFPReg); - LifeTimeInterval *cloneFixedInterval(int reg, bool isFP, const LifeTimeInterval &original); - void prepareRanges(); - void linearScan(); - void tryAllocateFreeReg(LifeTimeInterval ¤t); - void allocateBlockedReg(LifeTimeInterval ¤t); - int nextIntersection(const LifeTimeInterval ¤t, const LifeTimeInterval &another) const; - int nextUse(const IR::Temp &t, int startPosition) const; - void split(LifeTimeInterval ¤t, int beforePosition, bool skipOptionalRegisterUses =false); - void splitInactiveAtEndOfLifetimeHole(int reg, bool isFPReg, int position); - void assignSpillSlot(const IR::Temp &t, int startPos, int endPos); - void resolve(IR::Function *function, const IR::Optimizer &opt); - - void dump(IR::Function *function) const; -}; - -} // end of namespace JIT -} // end of namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4REGALLOC_P_H diff --git a/src/qml/jit/qv4registerinfo_p.h b/src/qml/jit/qv4registerinfo_p.h deleted file mode 100644 index 214206db91..0000000000 --- a/src/qml/jit/qv4registerinfo_p.h +++ /dev/null @@ -1,112 +0,0 @@ -/**************************************************************************** -** -** 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 QV4REGISTERINFO_P_H -#define QV4REGISTERINFO_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 <QtCore/QString> - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace JIT { - -class RegisterInfo -{ -public: - enum { InvalidRegister = (1 << 29) - 1 }; - enum SavedBy { CallerSaved, CalleeSaved }; - enum RegisterType { RegularRegister, FloatingPointRegister }; - enum Usage { Predefined, RegAlloc }; - -public: - RegisterInfo() - : _savedBy(CallerSaved) - , _usage(Predefined) - , _type(RegularRegister) - , _reg(InvalidRegister) - {} - - RegisterInfo(int reg, const QString &prettyName, RegisterType type, SavedBy savedBy, Usage usage) - : _prettyName(prettyName) - , _savedBy(savedBy) - , _usage(usage) - , _type(type) - , _reg(reg) - {} - - bool operator==(const RegisterInfo &other) const - { return _type == other._type && _reg == other._reg; } - - bool isValid() const { return _reg != InvalidRegister; } - template <typename T> T reg() const { return static_cast<T>(_reg); } - QString prettyName() const { return _prettyName; } - bool isCallerSaved() const { return _savedBy == CallerSaved; } - bool isCalleeSaved() const { return _savedBy == CalleeSaved; } - bool isFloatingPoint() const { return _type == FloatingPointRegister; } - bool isRegularRegister() const { return _type == RegularRegister; } - bool useForRegAlloc() const { return _usage == RegAlloc; } - bool isPredefined() const { return _usage == Predefined; } - -private: - QString _prettyName; - unsigned _savedBy : 1; - unsigned _usage : 1; - unsigned _type : 1; - unsigned _reg : 29; -}; -typedef QVector<RegisterInfo> RegisterInformation; - -} // JIT namespace -} // QV4 namespace - -QT_END_NAMESPACE - -#endif // QV4REGISTERINFO_P_H diff --git a/src/qml/jit/qv4targetplatform_p.h b/src/qml/jit/qv4targetplatform_p.h deleted file mode 100644 index 6d788f4a93..0000000000 --- a/src/qml/jit/qv4targetplatform_p.h +++ /dev/null @@ -1,707 +0,0 @@ -/**************************************************************************** -** -** 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 QV4TARGETPLATFORM_P_H -#define QV4TARGETPLATFORM_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 <config.h> - -#if ENABLE(ASSEMBLER) - -#include <private/qv4value_p.h> -#include "qv4registerinfo_p.h" -#include <assembler/MacroAssembler.h> - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace JIT { - -enum TargetOperatingSystemSpecialization { - NoOperatingSystemSpecialization, - WindowsSpecialization -}; - -// The TargetPlatform class describes how the stack and the registers work on a CPU+ABI combination. -// -// All combinations have a separate definition, guarded by #ifdefs. The exceptions are: -// - Linux/x86 and win32, which are the same, except that linux non-PIC/PIE code does not need to -// restore ebx (which holds the GOT ptr) before a call -// - All (supported) ARM platforms, where the only variety is the platform specific usage of r9, -// and the frame-pointer in Thumb/Thumb2 v.s. ARM mode. -// -// Specific handling of ebx when it holds the GOT: -// In this case we can use it, but it needs to be restored when doing a call. So, the handling is as -// follows: it is marked as caller saved, meaning the value in it won't survive a call. When -// calculating the list of callee saved registers in getCalleeSavedRegisters (which is used to -// generate push/pop instructions in the prelude/postlude), we add ebx too. Then when synthesizing -// a call, we add a load it right before emitting the call instruction. -// -// NOTE: When adding new architecture, do not forget to whitelist it in qv4global_p.h! -template <typename PlatformAssembler, TargetOperatingSystemSpecialization specialization = NoOperatingSystemSpecialization> -class TargetPlatform -{ -}; - -#if CPU(X86) && (OS(LINUX) || OS(WINDOWS) || OS(QNX) || OS(FREEBSD) || defined(Q_OS_IOS)) -template <> -class TargetPlatform<JSC::MacroAssemblerX86, NoOperatingSystemSpecialization> -{ -public: - using PlatformAssembler = JSC::MacroAssemblerX86; - using RegisterID = PlatformAssembler::RegisterID; - using FPRegisterID = PlatformAssembler::FPRegisterID; - using TrustedImm32 = PlatformAssembler::TrustedImm32; - - enum { RegAllocIsSupported = 1 }; - - static const RegisterID FramePointerRegister = JSC::X86Registers::ebp; - static const RegisterID StackPointerRegister = JSC::X86Registers::esp; - static const RegisterID LocalsRegister = JSC::X86Registers::edi; - static const RegisterID EngineRegister = JSC::X86Registers::esi; - static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; - static const RegisterID ScratchRegister = JSC::X86Registers::ecx; - static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; - static const FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; - static const RegisterID LowReturnValueRegister = JSC::X86Registers::eax; - static const RegisterID HighReturnValueRegister = JSC::X86Registers::edx; - - static RegisterInformation getRegisterInfo() - { - typedef RegisterInfo RI; - static RegisterInformation info = RegisterInformation() - << RI(JSC::X86Registers::edx, QStringLiteral("edx"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::ebx, QStringLiteral("ebx"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::edi, QStringLiteral("edi"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::esi, QStringLiteral("esi"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::xmm2, QStringLiteral("xmm2"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm3, QStringLiteral("xmm3"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm4, QStringLiteral("xmm4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm5, QStringLiteral("xmm5"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm6, QStringLiteral("xmm6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm7, QStringLiteral("xmm7"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - ; - return info; - } - -# define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 - static const int RegisterSize = 4; - - static const int RegisterArgumentCount = 0; - static RegisterID registerForArgument(int) { Q_UNREACHABLE(); } - - static const int StackAlignment = 16; - static const int StackShadowSpace = 0; - static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; // Return address is pushed onto stack by the CPU. - static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(FramePointerRegister); } - static void platformFinishEnteringStandardStackFrame(PlatformAssembler *) {} - static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) - { - if (frameSize > 0) - as->add32(TrustedImm32(frameSize), StackPointerRegister); - as->pop(FramePointerRegister); - } - -#if OS(WINDOWS) || OS(QNX) || \ - ((OS(LINUX) || OS(FREEBSD)) && (defined(__PIC__) || defined(__PIE__))) - - static const int gotRegister = JSC::X86Registers::ebx; - static int savedGOTRegisterSlotOnStack() { - static int ebxIdx = -1; - if (ebxIdx == -1) { - int calleeSaves = 0; - const auto infos = getRegisterInfo(); - for (const RegisterInfo &info : infos) { - if (info.reg<JSC::X86Registers::RegisterID>() == JSC::X86Registers::ebx) { - ebxIdx = calleeSaves; - break; - } else if (info.isCalleeSaved()) { - ++calleeSaves; - } - } - Q_ASSERT(ebxIdx >= 0); - ebxIdx += 1; - } - return ebxIdx * -int(sizeof(void*)); - } -#else - static const int gotRegister = -1; - static int savedGOTRegisterSlotOnStack() { return -1; } -#endif -}; -#endif // x86 - -#if CPU(X86_64) && (OS(LINUX) || OS(MAC_OS_X) || OS(FREEBSD) || OS(QNX) || defined(Q_OS_IOS)) -template <> -class TargetPlatform<JSC::MacroAssemblerX86_64, NoOperatingSystemSpecialization> -{ -public: - using PlatformAssembler = JSC::MacroAssemblerX86_64; - using RegisterID = PlatformAssembler::RegisterID; - using FPRegisterID = PlatformAssembler::FPRegisterID; - using TrustedImm32 = PlatformAssembler::TrustedImm32; - - enum { RegAllocIsSupported = 1 }; - - static const RegisterID FramePointerRegister = JSC::X86Registers::ebp; - static const RegisterID StackPointerRegister = JSC::X86Registers::esp; - static const RegisterID LocalsRegister = JSC::X86Registers::r12; - static const RegisterID EngineRegister = JSC::X86Registers::r14; - static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; - static const RegisterID ScratchRegister = JSC::X86Registers::r10; - static const RegisterID DoubleMaskRegister = JSC::X86Registers::r13; - static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; - static const FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; - - static RegisterInformation getRegisterInfo() - { - typedef RegisterInfo RI; - static RegisterInformation info = RegisterInformation() - << RI(JSC::X86Registers::ebx, QStringLiteral("rbx"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::X86Registers::edi, QStringLiteral("rdi"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::esi, QStringLiteral("rsi"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::edx, QStringLiteral("rdx"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::r9, QStringLiteral("r9"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::r8, QStringLiteral("r8"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - // r11 is used as scratch register by the macro assembler - << RI(JSC::X86Registers::r12, QStringLiteral("r12"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::r13, QStringLiteral("r13"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::r14, QStringLiteral("r14"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::r15, QStringLiteral("r15"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm2, QStringLiteral("xmm2"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm3, QStringLiteral("xmm3"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm4, QStringLiteral("xmm4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm5, QStringLiteral("xmm5"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm6, QStringLiteral("xmm6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm7, QStringLiteral("xmm7"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - ; - return info; - } - -#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 - static const int RegisterSize = 8; - - static const int RegisterArgumentCount = 6; - static RegisterID registerForArgument(int index) - { - static RegisterID regs[RegisterArgumentCount] = { - JSC::X86Registers::edi, - JSC::X86Registers::esi, - JSC::X86Registers::edx, - JSC::X86Registers::ecx, - JSC::X86Registers::r8, - JSC::X86Registers::r9 - }; - Q_ASSERT(index >= 0 && index < RegisterArgumentCount); - return regs[index]; - }; - - static const int StackAlignment = 16; - static const int StackShadowSpace = 0; - static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; // Return address is pushed onto stack by the CPU. - static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(FramePointerRegister); } - static void platformFinishEnteringStandardStackFrame(PlatformAssembler *as) - { - as->move(PlatformAssembler::TrustedImm64(QV4::Value::NaNEncodeMask), DoubleMaskRegister); - } - static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) - { - if (frameSize > 0) - as->add64(TrustedImm32(frameSize), StackPointerRegister); - as->pop(FramePointerRegister); - } - - static const int gotRegister = -1; - static int savedGOTRegisterSlotOnStack() { return -1; } -}; -#endif // Linux/MacOS on x86_64 - -#if CPU(X86_64) && OS(WINDOWS) -template <> -class TargetPlatform<JSC::MacroAssemblerX86_64, WindowsSpecialization> -{ -public: - using PlatformAssembler = JSC::MacroAssemblerX86_64; - using RegisterID = PlatformAssembler::RegisterID; - using FPRegisterID = PlatformAssembler::FPRegisterID; - using TrustedImm32 = PlatformAssembler::TrustedImm32; - - enum { RegAllocIsSupported = 1 }; - - static const RegisterID FramePointerRegister = JSC::X86Registers::ebp; - static const RegisterID StackPointerRegister = JSC::X86Registers::esp; - static const RegisterID LocalsRegister = JSC::X86Registers::r12; - static const RegisterID EngineRegister = JSC::X86Registers::r14; - static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; - static const RegisterID ScratchRegister = JSC::X86Registers::r10; - static const RegisterID DoubleMaskRegister = JSC::X86Registers::r13; - static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; - static const FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; - - static RegisterInformation getRegisterInfo() - { - typedef RegisterInfo RI; - static RegisterInformation info = RegisterInformation() - << RI(JSC::X86Registers::ebx, QStringLiteral("rbx"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::X86Registers::edi, QStringLiteral("rdi"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::X86Registers::esi, QStringLiteral("rsi"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::X86Registers::edx, QStringLiteral("rdx"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::r8, QStringLiteral("r8"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::r9, QStringLiteral("r9"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - // r11 is used as scratch register by the macro assembler - << RI(JSC::X86Registers::r12, QStringLiteral("r12"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::r13, QStringLiteral("r13"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::r14, QStringLiteral("r14"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::r15, QStringLiteral("r15"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm2, QStringLiteral("xmm2"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm3, QStringLiteral("xmm3"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm4, QStringLiteral("xmm4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm5, QStringLiteral("xmm5"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm6, QStringLiteral("xmm6"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm7, QStringLiteral("xmm7"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - ; - return info; - } - -#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 - static const int RegisterSize = 8; - - static const int RegisterArgumentCount = 4; - static RegisterID registerForArgument(int index) - { - static RegisterID regs[RegisterArgumentCount] = { - JSC::X86Registers::ecx, - JSC::X86Registers::edx, - JSC::X86Registers::r8, - JSC::X86Registers::r9 - }; - Q_ASSERT(index >= 0 && index < RegisterArgumentCount); - return regs[index]; - } - - static const int StackAlignment = 16; - static const int StackShadowSpace = 32; - static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; // Return address is pushed onto stack by the CPU. - static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(FramePointerRegister); } - static void platformFinishEnteringStandardStackFrame(PlatformAssembler *as) - { - as->move(PlatformAssembler::TrustedImm64(QV4::Value::NaNEncodeMask), DoubleMaskRegister); - } - static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) - { - if (frameSize > 0) - as->add64(TrustedImm32(frameSize), StackPointerRegister); - as->pop(FramePointerRegister); - } - - static const int gotRegister = -1; - static int savedGOTRegisterSlotOnStack() { return -1; } -}; -#endif // Windows on x86_64 - -#if CPU(ARM) || defined(V4_BOOTSTRAP) -template <> -class TargetPlatform<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization> -{ -public: - using PlatformAssembler = JSC::MacroAssemblerARMv7; - using RegisterID = PlatformAssembler::RegisterID; - using FPRegisterID = PlatformAssembler::FPRegisterID; - using TrustedImm32 = PlatformAssembler::TrustedImm32; - - enum { RegAllocIsSupported = 1 }; - - // The AAPCS specifies that the platform ABI has to define the usage of r9. Known are: - // - The GNU/Linux ABI defines it as an additional callee-saved variable register (v6). - // - iOS (for which we cannot JIT, but still...) defines it as having a special use, so we do - // not touch it, nor use it. - // - Any other platform has not been verified, so we conservatively assume we cannot use it. -#if OS(LINUX) -#define CAN_USE_R9 -#endif - - // There are two designated frame-pointer registers on ARM, depending on which instruction set - // is used for the subroutine: r7 for Thumb or Thumb2, and r11 for ARM. We assign the constants - // accordingly, and assign the locals-register to the "other" register. -#if CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP) - static const RegisterID FramePointerRegister = JSC::ARMRegisters::r7; - static const RegisterID LocalsRegister = JSC::ARMRegisters::r11; -#else // Thumbs down - static const RegisterID FramePointerRegister = JSC::ARMRegisters::r11; - static const RegisterID LocalsRegister = JSC::ARMRegisters::r7; -#endif - static const RegisterID StackPointerRegister = JSC::ARMRegisters::r13; - static const RegisterID ScratchRegister = JSC::ARMRegisters::r5; - static const RegisterID EngineRegister = JSC::ARMRegisters::r10; - static const RegisterID ReturnValueRegister = JSC::ARMRegisters::r0; - static const FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; - static const FPRegisterID FPGpr1 = JSC::ARMRegisters::d1; - static const RegisterID LowReturnValueRegister = JSC::ARMRegisters::r0; - static const RegisterID HighReturnValueRegister = JSC::ARMRegisters::r1; - - static RegisterInformation getRegisterInfo() - { - typedef RegisterInfo RI; - static RegisterInformation info = RegisterInformation() - << RI(JSC::ARMRegisters::r0, QStringLiteral("r0"), RI::RegularRegister, RI::CallerSaved, RI::Predefined) - << RI(JSC::ARMRegisters::r1, QStringLiteral("r1"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::r2, QStringLiteral("r2"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::r3, QStringLiteral("r3"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::r4, QStringLiteral("r4"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::r5, QStringLiteral("r5"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::ARMRegisters::r6, QStringLiteral("r6"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) -#if !CPU(ARM_THUMB2) && !defined(V4_BOOTSTRAP) - << RI(JSC::ARMRegisters::r7, QStringLiteral("r7"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) -#endif - << RI(JSC::ARMRegisters::r8, QStringLiteral("r8"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) -#ifdef CAN_USE_R9 - << RI(JSC::ARMRegisters::r9, QStringLiteral("r9"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) -#endif - << RI(JSC::ARMRegisters::r10, QStringLiteral("r10"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) -#if CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP) - << RI(JSC::ARMRegisters::r11, QStringLiteral("r11"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) -#endif - << RI(JSC::ARMRegisters::d2, QStringLiteral("d2"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d3, QStringLiteral("d3"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d4, QStringLiteral("d4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d5, QStringLiteral("d5"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d6, QStringLiteral("d6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d8, QStringLiteral("d8"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d9, QStringLiteral("d9"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d10, QStringLiteral("d10"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d11, QStringLiteral("d11"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d12, QStringLiteral("d12"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d13, QStringLiteral("d13"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d14, QStringLiteral("d14"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d15, QStringLiteral("d15"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - ; - return info; - } - -#undef HAVE_ALU_OPS_WITH_MEM_OPERAND - static const int RegisterSize = 4; - - static const int RegisterArgumentCount = 4; - static RegisterID registerForArgument(int index) - { - static RegisterID regs[RegisterArgumentCount] = { - JSC::ARMRegisters::r0, - JSC::ARMRegisters::r1, - JSC::ARMRegisters::r2, - JSC::ARMRegisters::r3 - }; - - Q_ASSERT(index >= 0 && index < RegisterArgumentCount); - return regs[index]; - }; - - static const int StackAlignment = 8; // Per AAPCS - static const int StackShadowSpace = 0; - static const int StackSpaceAllocatedUponFunctionEntry = 1 * RegisterSize; // Registers saved in platformEnterStandardStackFrame below. - - static void platformEnterStandardStackFrame(PlatformAssembler *as) - { - as->push(JSC::ARMRegisters::lr); - as->push(FramePointerRegister); - } - - static void platformFinishEnteringStandardStackFrame(PlatformAssembler *) {} - - static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) - { - if (frameSize > 0) { - // Work around bug in ARMv7Assembler.h where add32(imm, sp, sp) doesn't - // work well for large immediates. - as->move(TrustedImm32(frameSize), JSC::ARMRegisters::r3); - as->add32(JSC::ARMRegisters::r3, StackPointerRegister); - } - as->pop(FramePointerRegister); - as->pop(JSC::ARMRegisters::lr); - } - - static const int gotRegister = -1; - static int savedGOTRegisterSlotOnStack() { return -1; } -}; -#endif // ARM (32 bit) - -#if CPU(ARM64) || defined(V4_BOOTSTRAP) -template <> -class TargetPlatform<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization> -{ -public: - using PlatformAssembler = JSC::MacroAssemblerARM64; - using RegisterID = PlatformAssembler::RegisterID; - using FPRegisterID = PlatformAssembler::FPRegisterID; - using TrustedImm32 = PlatformAssembler::TrustedImm32; - - enum { RegAllocIsSupported = 1 }; - - static const RegisterID FramePointerRegister = JSC::ARM64Registers::fp; - static const RegisterID LocalsRegister = JSC::ARM64Registers::x28; - static const RegisterID StackPointerRegister = JSC::ARM64Registers::sp; - static const RegisterID ScratchRegister = JSC::ARM64Registers::x9; - static const RegisterID EngineRegister = JSC::ARM64Registers::x27; - static const RegisterID ReturnValueRegister = JSC::ARM64Registers::x0; - static const RegisterID DoubleMaskRegister = JSC::ARM64Registers::x26; - static const FPRegisterID FPGpr0 = JSC::ARM64Registers::q0; - static const FPRegisterID FPGpr1 = JSC::ARM64Registers::q1; - - static RegisterInformation getRegisterInfo() - { - typedef RegisterInfo RI; - static RegisterInformation info = RegisterInformation() - << RI(JSC::ARM64Registers::x0, QStringLiteral("x0"), RI::RegularRegister, RI::CallerSaved, RI::Predefined) - << RI(JSC::ARM64Registers::x1, QStringLiteral("x1"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x2, QStringLiteral("x2"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x3, QStringLiteral("x3"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x4, QStringLiteral("x4"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x5, QStringLiteral("x5"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x6, QStringLiteral("x6"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x7, QStringLiteral("x7"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x8, QStringLiteral("x8"), RI::RegularRegister, RI::CallerSaved, RI::Predefined) - << RI(JSC::ARM64Registers::x9, QStringLiteral("x9"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::ARM64Registers::x10, QStringLiteral("x10"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x11, QStringLiteral("x11"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x12, QStringLiteral("x12"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x13, QStringLiteral("x13"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x14, QStringLiteral("x14"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x15, QStringLiteral("x15"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x19, QStringLiteral("x19"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x20, QStringLiteral("x20"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x21, QStringLiteral("x21"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x22, QStringLiteral("x22"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x23, QStringLiteral("x23"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x24, QStringLiteral("x24"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x25, QStringLiteral("x25"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x26, QStringLiteral("x26"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::ARM64Registers::x27, QStringLiteral("x27"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::ARM64Registers::x28, QStringLiteral("x28"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - - << RI(JSC::ARM64Registers::q2, QStringLiteral("q2"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q3, QStringLiteral("q3"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q4, QStringLiteral("q4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q5, QStringLiteral("q5"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q6, QStringLiteral("q6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q8, QStringLiteral("q8"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q9, QStringLiteral("q9"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q10, QStringLiteral("q10"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q11, QStringLiteral("q11"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q12, QStringLiteral("q12"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q13, QStringLiteral("q13"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q14, QStringLiteral("q14"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q15, QStringLiteral("q15"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q16, QStringLiteral("q16"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q17, QStringLiteral("q17"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q18, QStringLiteral("q18"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q19, QStringLiteral("q19"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q20, QStringLiteral("q20"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q21, QStringLiteral("q21"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q22, QStringLiteral("q22"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q23, QStringLiteral("q23"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q24, QStringLiteral("q24"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q25, QStringLiteral("q25"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q26, QStringLiteral("q26"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q27, QStringLiteral("q27"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q28, QStringLiteral("q28"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q29, QStringLiteral("q29"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q30, QStringLiteral("q30"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q31, QStringLiteral("q31"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - ; - return info; - } - -#undef HAVE_ALU_OPS_WITH_MEM_OPERAND - static const int RegisterSize = 8; - - static const int RegisterArgumentCount = 8; - static RegisterID registerForArgument(int index) - { - static RegisterID regs[RegisterArgumentCount] = { - JSC::ARM64Registers::x0, - JSC::ARM64Registers::x1, - JSC::ARM64Registers::x2, - JSC::ARM64Registers::x3, - JSC::ARM64Registers::x4, - JSC::ARM64Registers::x5, - JSC::ARM64Registers::x6, - JSC::ARM64Registers::x7 - }; - - Q_ASSERT(index >= 0 && index < RegisterArgumentCount); - return regs[index]; - }; - - static const int StackAlignment = 16; - static const int StackShadowSpace = 0; - static const int StackSpaceAllocatedUponFunctionEntry = 1 * RegisterSize; // Registers saved in platformEnterStandardStackFrame below. - - static void platformEnterStandardStackFrame(PlatformAssembler *as) - { - as->pushPair(FramePointerRegister, JSC::ARM64Registers::lr); - } - - static void platformFinishEnteringStandardStackFrame(PlatformAssembler *as) - { - as->move(PlatformAssembler::TrustedImm64(QV4::Value::NaNEncodeMask), DoubleMaskRegister); - } - - static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) - { - if (frameSize > 0) - as->add64(TrustedImm32(frameSize), StackPointerRegister); - as->popPair(FramePointerRegister, JSC::ARM64Registers::lr); - } - - static const int gotRegister = -1; - static int savedGOTRegisterSlotOnStack() { return -1; } -}; -#endif // ARM64 - -#if defined(Q_PROCESSOR_MIPS_32) && defined(Q_OS_LINUX) -template <> -class TargetPlatform<JSC::MacroAssemblerMIPS, NoOperatingSystemSpecialization> -{ -public: - using PlatformAssembler = JSC::MacroAssemblerMIPS; - using RegisterID = PlatformAssembler::RegisterID; - using FPRegisterID = PlatformAssembler::FPRegisterID; - using TrustedImm32 = PlatformAssembler::TrustedImm32; - enum { RegAllocIsSupported = 1 }; - - static const RegisterID FramePointerRegister = JSC::MIPSRegisters::fp; - static const RegisterID StackPointerRegister = JSC::MIPSRegisters::sp; - static const RegisterID LocalsRegister = JSC::MIPSRegisters::s0; - static const RegisterID EngineRegister = JSC::MIPSRegisters::s1; - static const RegisterID ReturnValueRegister = JSC::MIPSRegisters::v0; - static const RegisterID ScratchRegister = JSC::MIPSRegisters::s2; - static const FPRegisterID FPGpr0 = JSC::MIPSRegisters::f0; - static const FPRegisterID FPGpr1 = JSC::MIPSRegisters::f2; - static const RegisterID LowReturnValueRegister = JSC::MIPSRegisters::v0; - static const RegisterID HighReturnValueRegister = JSC::MIPSRegisters::v1; - - static RegisterInformation getRegisterInfo() - { - typedef RegisterInfo RI; - static RegisterInformation info = RegisterInformation() - // Note: t0, t1, t2, t3 and f16 are already used by MacroAssemblerMIPS. - << RI(JSC::MIPSRegisters::t4, QStringLiteral("t4"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::t5, QStringLiteral("t5"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::t6, QStringLiteral("t6"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::t7, QStringLiteral("t7"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::t8, QStringLiteral("t8"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::s0, QStringLiteral("s0"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::MIPSRegisters::s1, QStringLiteral("s1"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::MIPSRegisters::s2, QStringLiteral("s2"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::MIPSRegisters::s3, QStringLiteral("s3"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::f4, QStringLiteral("f4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::f6, QStringLiteral("f6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::f8, QStringLiteral("f8"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::f10, QStringLiteral("f10"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::f18, QStringLiteral("f18"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::f20, QStringLiteral("f20"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::f22, QStringLiteral("f22"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::f24, QStringLiteral("f24"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::f26, QStringLiteral("f26"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::f28, QStringLiteral("f28"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - ; - return info; - } - -#undef HAVE_ALU_OPS_WITH_MEM_OPERAND - static const int RegisterSize = 4; - - static const int RegisterArgumentCount = 4; - static RegisterID registerForArgument(int index) - { - static RegisterID regs[RegisterArgumentCount] = { - JSC::MIPSRegisters::a0, - JSC::MIPSRegisters::a1, - JSC::MIPSRegisters::a2, - JSC::MIPSRegisters::a3 - }; - - Q_ASSERT(index >= 0 && index < RegisterArgumentCount); - return regs[index]; - }; - - static const int StackAlignment = 8; - static const int StackShadowSpace = 4 * RegisterSize; // Stack space for 4 argument registers. - static const int StackSpaceAllocatedUponFunctionEntry = 1 * RegisterSize; // Registers saved in platformEnterStandardStackFrame below. - - static void platformEnterStandardStackFrame(PlatformAssembler *as) - { - as->push(JSC::MIPSRegisters::ra); - as->push(FramePointerRegister); - } - - static void platformFinishEnteringStandardStackFrame(PlatformAssembler *) {} - - static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) - { - if (frameSize > 0) - as->add32(TrustedImm32(frameSize), StackPointerRegister); - as->pop(FramePointerRegister); - as->pop(JSC::MIPSRegisters::ra); - } - - - static const int gotRegister = -1; - static int savedGOTRegisterSlotOnStack() { return -1; } -}; -#endif // Linux on MIPS (32 bit) - -} // JIT namespace -} // QV4 namespace - -QT_END_NAMESPACE - -#endif // ENABLE(ASSEMBLER) - -#endif // QV4TARGETPLATFORM_P_H diff --git a/src/qml/jit/qv4unop.cpp b/src/qml/jit/qv4unop.cpp deleted file mode 100644 index 78546e1509..0000000000 --- a/src/qml/jit/qv4unop.cpp +++ /dev/null @@ -1,166 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ -#include <qv4unop_p.h> -#include <qv4assembler_p.h> - -#if ENABLE(ASSEMBLER) - -using namespace QV4; -using namespace JIT; - -#define stringIfyx(s) #s -#define stringIfy(s) stringIfyx(s) -#define setOp(operation) \ - do { \ - call = typename JITAssembler::RuntimeCall(QV4::Runtime::operation); name = "Runtime::" stringIfy(operation); \ - needsExceptionCheck = Runtime::Method_##operation##_NeedsExceptionCheck; \ - } while (0) - -template <typename JITAssembler> -void Unop<JITAssembler>::generate(IR::Expr *source, IR::Expr *target) -{ - bool needsExceptionCheck; - typename JITAssembler::RuntimeCall call; - const char *name = 0; - switch (op) { - case IR::OpNot: - generateNot(source, target); - return; - case IR::OpUMinus: - generateUMinus(source, target); - return; - case IR::OpUPlus: setOp(uPlus); break; - case IR::OpCompl: - generateCompl(source, target); - return; - case IR::OpIncrement: setOp(increment); break; - case IR::OpDecrement: setOp(decrement); break; - default: - Q_UNREACHABLE(); - } // switch - - Q_ASSERT(call.isValid()); - _as->generateFunctionCallImp(needsExceptionCheck, target, name, call, PointerToValue(source)); -} - -template <typename JITAssembler> -void Unop<JITAssembler>::generateUMinus(IR::Expr *source, IR::Expr *target) -{ - IR::Temp *targetTemp = target->asTemp(); - - if (IR::Const *c = source->asConst()) { - if (c->value == 0 && source->type == IR::SInt32Type) { - // special case: minus integer 0 is 0, which is not what JS expects it to be, so always - // do a runtime call - generateRuntimeCall(_as, target, uMinus, PointerToValue(source)); - return; - } - } - if (source->type == IR::SInt32Type) { - typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister; - if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) - tReg = (typename JITAssembler::RegisterID) targetTemp->index; - typename JITAssembler::RegisterID sReg = _as->toInt32Register(source, tReg); - _as->move(sReg, tReg); - _as->neg32(tReg); - if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) - _as->storeInt32(tReg, target); - return; - } - - generateRuntimeCall(_as, target, uMinus, PointerToValue(source)); -} - -template <typename JITAssembler> -void Unop<JITAssembler>::generateNot(IR::Expr *source, IR::Expr *target) -{ - IR::Temp *targetTemp = target->asTemp(); - if (source->type == IR::BoolType) { - typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister; - if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) - tReg = (typename JITAssembler::RegisterID) targetTemp->index; - _as->xor32(TrustedImm32(0x1), _as->toInt32Register(source, tReg), tReg); - if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) - _as->storeBool(tReg, target); - return; - } else if (source->type == IR::SInt32Type) { - typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister; - if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) - tReg = (typename JITAssembler::RegisterID) targetTemp->index; - _as->compare32(RelationalCondition::Equal, - _as->toInt32Register(source, JITAssembler::ScratchRegister), TrustedImm32(0), - tReg); - if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) - _as->storeBool(tReg, target); - return; - } else if (source->type == IR::DoubleType) { - // ### - } - // ## generic implementation testing for int/bool - - generateRuntimeCall(_as, target, uNot, PointerToValue(source)); -} - -template <typename JITAssembler> -void Unop<JITAssembler>::generateCompl(IR::Expr *source, IR::Expr *target) -{ - IR::Temp *targetTemp = target->asTemp(); - if (source->type == IR::SInt32Type) { - typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister; - if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) - tReg = (typename JITAssembler::RegisterID) targetTemp->index; - _as->xor32(TrustedImm32(0xffffffff), _as->toInt32Register(source, tReg), tReg); - if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) - _as->storeInt32(tReg, target); - return; - } - generateRuntimeCall(_as, target, complement, PointerToValue(source)); -} - -template struct QV4::JIT::Unop<QV4::JIT::Assembler<DefaultAssemblerTargetConfiguration>>; -#if defined(V4_BOOTSTRAP) -#if !CPU(ARM_THUMB2) -template struct QV4::JIT::Unop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>>; -#endif -#if !CPU(ARM64) -template struct QV4::JIT::Unop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>>; -#endif -#endif - -#endif diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index d5b8b295a7..bca4057fbe 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -443,15 +443,14 @@ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, in { QV4::ExecutionEngine *v4 = d->m_v4Engine; QV4::Scope scope(v4); - QV4::ExecutionContextSaver saver(scope); - - QV4::ExecutionContext *ctx = v4->currentContext; - if (ctx->d() != v4->rootContext()->d()) - ctx = v4->pushGlobalContext(); QV4::ScopedValue result(scope); - QV4::Script script(ctx, program, fileName, lineNumber); - script.strictMode = ctx->d()->strictMode; + QV4::Script script(v4->rootContext(), QV4::Compiler::EvalCode, program, fileName, lineNumber); + script.strictMode = false; + if (v4->currentStackFrame) + script.strictMode = v4->currentStackFrame->v4Function->isStrict(); + else if (v4->globalCode) + script.strictMode = v4->globalCode->isStrict(); script.inheritContext = true; script.parse(); if (!scope.engine->hasException) @@ -459,7 +458,9 @@ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, in if (scope.engine->hasException) result = v4->catchException(); - return QJSValue(v4, result->asReturnedValue()); + QJSValue retval(v4, result->asReturnedValue()); + + return retval; } /*! diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index 953e5d3ef6..c1a135c835 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -53,7 +53,7 @@ #include "qv4errorobject_p.h" #include "private/qv8engine_p.h" #include <private/qv4mm_p.h> -#include <private/qv4scopedvalue_p.h> +#include <private/qv4jscall_p.h> #include <private/qv4qobjectwrapper_p.h> /*! @@ -664,21 +664,21 @@ QJSValue QJSValue::call(const QJSValueList &args) Q_ASSERT(engine); Scope scope(engine); - ScopedCallData callData(scope, args.length()); - callData->thisObject = engine->globalObject; + JSCallData jsCallData(scope, args.length()); + *jsCallData->thisObject = engine->globalObject; for (int i = 0; i < args.size(); ++i) { if (!QJSValuePrivate::checkEngine(engine, args.at(i))) { qWarning("QJSValue::call() failed: cannot call function with argument created in a different engine"); return QJSValue(); } - callData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i)); + jsCallData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i)); } - f->call(scope, callData); + ScopedValue result(scope, f->call(jsCallData)); if (engine->hasException) - scope.result = engine->catchException(); + result = engine->catchException(); - return QJSValue(engine, scope.result.asReturnedValue()); + return QJSValue(engine, result->asReturnedValue()); } /*! @@ -720,21 +720,21 @@ QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList return QJSValue(); } - ScopedCallData callData(scope, args.size()); - callData->thisObject = QJSValuePrivate::convertedToValue(engine, instance); + JSCallData jsCallData(scope, args.size()); + *jsCallData->thisObject = QJSValuePrivate::convertedToValue(engine, instance); for (int i = 0; i < args.size(); ++i) { if (!QJSValuePrivate::checkEngine(engine, args.at(i))) { qWarning("QJSValue::call() failed: cannot call function with argument created in a different engine"); return QJSValue(); } - callData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i)); + jsCallData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i)); } - f->call(scope, callData); + ScopedValue result(scope, f->call(jsCallData)); if (engine->hasException) - scope.result = engine->catchException(); + result = engine->catchException(); - return QJSValue(engine, scope.result.asReturnedValue()); + return QJSValue(engine, result->asReturnedValue()); } /*! @@ -769,20 +769,20 @@ QJSValue QJSValue::callAsConstructor(const QJSValueList &args) Q_ASSERT(engine); Scope scope(engine); - ScopedCallData callData(scope, args.size()); + JSCallData jsCallData(scope, args.size()); for (int i = 0; i < args.size(); ++i) { if (!QJSValuePrivate::checkEngine(engine, args.at(i))) { qWarning("QJSValue::callAsConstructor() failed: cannot construct function with argument created in a different engine"); return QJSValue(); } - callData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i)); + jsCallData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i)); } - f->construct(scope, callData); + ScopedValue result(scope, f->callAsConstructor(jsCallData)); if (engine->hasException) - scope.result = engine->catchException(); + result = engine->catchException(); - return QJSValue(engine, scope.result.asReturnedValue()); + return QJSValue(engine, result->asReturnedValue()); } #ifdef QT_DEPRECATED @@ -1173,10 +1173,7 @@ bool QJSValue::deleteProperty(const QString &name) return false; ScopedString s(scope, engine->newString(name)); - bool b = o->deleteProperty(s); - if (engine->hasException) - engine->catchException(); - return b; + return o->deleteProperty(s); } /*! diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index ebdb3d6d03..519c87d0c4 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -33,6 +33,7 @@ SOURCES += \ $$PWD/qv4variantobject.cpp \ $$PWD/qv4objectiterator.cpp \ $$PWD/qv4regexp.cpp \ + $$PWD/qv4runtimecodegen.cpp \ $$PWD/qv4serialize.cpp \ $$PWD/qv4script.cpp \ $$PWD/qv4sequenceobject.cpp \ @@ -58,6 +59,7 @@ HEADERS += \ $$PWD/qv4identifiertable_p.h \ $$PWD/qv4managed_p.h \ $$PWD/qv4internalclass_p.h \ + $$PWD/qv4jscall_p.h \ $$PWD/qv4sparsearray_p.h \ $$PWD/qv4arraydata_p.h \ $$PWD/qv4arrayobject_p.h \ @@ -76,6 +78,7 @@ HEADERS += \ $$PWD/qv4objectproto_p.h \ $$PWD/qv4qmlcontext_p.h \ $$PWD/qv4regexpobject_p.h \ + $$PWD/qv4runtimecodegen_p.h \ $$PWD/qv4stringobject_p.h \ $$PWD/qv4variantobject_p.h \ $$PWD/qv4property_p.h \ diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index 0905c2828a..075e7afd8a 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -41,40 +41,53 @@ #include <qv4scopedvalue_p.h> #include <qv4string_p.h> #include <qv4function_p.h> +#include <qv4jscall_p.h> using namespace QV4; DEFINE_OBJECT_VTABLE(ArgumentsObject); +DEFINE_OBJECT_VTABLE(StrictArgumentsObject); -void Heap::ArgumentsObject::init(QV4::CallContext *context) +void Heap::ArgumentsObject::init(QV4::CppStackFrame *frame) { ExecutionEngine *v4 = internalClass->engine; + int nFormals = frame->v4Function->nFormals; + QV4::CallContext *context = static_cast<QV4::CallContext *>(frame->context()); + Object::init(); fullyCreated = false; + this->nFormals = nFormals; this->context.set(v4, context->d()); Q_ASSERT(vtable() == QV4::ArgumentsObject::staticVTable()); + Q_ASSERT(CalleePropertyIndex == internalClass->find(v4->id_callee())); + setProperty(v4, CalleePropertyIndex, context->d()->function); + Q_ASSERT(LengthPropertyIndex == internalClass->find(v4->id_length())); + setProperty(v4, LengthPropertyIndex, Primitive::fromInt32(context->argc())); +} + +void Heap::StrictArgumentsObject::init(QV4::CppStackFrame *frame) +{ + Q_ASSERT(vtable() == QV4::StrictArgumentsObject::staticVTable()); + ExecutionEngine *v4 = internalClass->engine; + + Object::init(); + + Q_ASSERT(CalleePropertyIndex == internalClass->find(v4->id_callee())); + Q_ASSERT(CallerPropertyIndex == internalClass->find(v4->id_caller())); + setProperty(v4, CalleePropertyIndex + QV4::Object::GetterOffset, *v4->thrower()); + setProperty(v4, CalleePropertyIndex + QV4::Object::SetterOffset, *v4->thrower()); + setProperty(v4, CallerPropertyIndex + QV4::Object::GetterOffset, *v4->thrower()); + setProperty(v4, CallerPropertyIndex + QV4::Object::SetterOffset, *v4->thrower()); + Scope scope(v4); - Scoped<QV4::ArgumentsObject> args(scope, this); - - if (context->d()->strictMode) { - Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(v4->id_callee())); - Q_ASSERT(CallerPropertyIndex == args->internalClass()->find(v4->id_caller())); - args->setProperty(CalleePropertyIndex + QV4::Object::GetterOffset, *v4->thrower()); - args->setProperty(CalleePropertyIndex + QV4::Object::SetterOffset, *v4->thrower()); - args->setProperty(CallerPropertyIndex + QV4::Object::GetterOffset, *v4->thrower()); - args->setProperty(CallerPropertyIndex + QV4::Object::SetterOffset, *v4->thrower()); - - args->arrayReserve(context->argc()); - args->arrayPut(0, context->args(), context->argc()); - args->d()->fullyCreated = true; - } else { - Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(v4->id_callee())); - args->setProperty(CalleePropertyIndex, context->d()->function); - } + Scoped<QV4::StrictArgumentsObject> args(scope, this); + args->arrayReserve(frame->originalArgumentsCount); + args->arrayPut(0, frame->originalArguments, frame->originalArgumentsCount); + Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(v4->id_length())); - args->setProperty(LengthPropertyIndex, Primitive::fromInt32(context->d()->callData->argc)); + setProperty(v4, LengthPropertyIndex, Primitive::fromInt32(frame->originalArgumentsCount)); } void ArgumentsObject::fullyCreate() @@ -84,8 +97,8 @@ void ArgumentsObject::fullyCreate() Scope scope(engine()); - uint argCount = context()->callData->argc; - uint numAccessors = qMin(context()->formalParameterCount(), argCount); + int argCount = context()->argc(); + uint numAccessors = qMin(d()->nFormals, argCount); ArrayData::realloc(this, Heap::ArrayData::Sparse, argCount, true); scope.engine->requireArgumentsAccessors(numAccessors); @@ -93,12 +106,12 @@ void ArgumentsObject::fullyCreate() if (numAccessors) { d()->mappedArguments.set(scope.engine, md->allocate(scope.engine, numAccessors)); for (uint i = 0; i < numAccessors; ++i) { - d()->mappedArguments->values.set(scope.engine, i, context()->callData->args[i]); + d()->mappedArguments->values.set(scope.engine, i, context()->args()[i]); arraySet(i, scope.engine->argumentsAccessors + i, Attr_Accessor); } } - arrayPut(numAccessors, context()->callData->args + numAccessors, argCount - numAccessors); - for (uint i = numAccessors; i < argCount; ++i) + arrayPut(numAccessors, context()->args() + numAccessors, argCount - numAccessors); + for (int i = int(numAccessors); i < argCount; ++i) setArrayAttributes(i, Attr_Data); d()->fullyCreated = true; @@ -111,7 +124,7 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con Scope scope(engine); ScopedProperty map(scope); PropertyAttributes mapAttrs; - uint numAccessors = qMin(context()->formalParameterCount(), static_cast<uint>(context()->callData->argc)); + uint numAccessors = qMin(d()->nFormals, context()->argc()); bool isMapped = false; if (arrayData() && index < numAccessors && arrayData()->attributes(index).isAccessor() && @@ -127,18 +140,18 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con arrayIndex.set(scope.engine, d()->mappedArguments->values[index]); } - bool strict = engine->current->strictMode; - engine->current->strictMode = false; bool result = Object::defineOwnProperty2(scope.engine, index, desc, attrs); - engine->current->strictMode = strict; + if (!result) { + return false; + } if (isMapped && attrs.isData()) { Q_ASSERT(arrayData()); ScopedFunctionObject setter(scope, map->setter()); - ScopedCallData callData(scope, 1); - callData->thisObject = this->asReturnedValue(); - callData->args[0] = desc->value; - setter->call(scope, callData); + JSCallData jsCallData(scope, 1); + *jsCallData->thisObject = this->asReturnedValue(); + jsCallData->args[0] = desc->value; + setter->call(jsCallData); if (attrs.isWritable()) { setArrayAttributes(index, mapAttrs); @@ -146,8 +159,6 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con } } - if (engine->current->strictMode && !result) - return engine->throwTypeError(); return result; } @@ -157,10 +168,10 @@ ReturnedValue ArgumentsObject::getIndexed(const Managed *m, uint index, bool *ha if (args->fullyCreated()) return Object::getIndexed(m, index, hasProperty); - if (index < static_cast<uint>(args->context()->callData->argc)) { + if (index < static_cast<uint>(args->context()->argc())) { if (hasProperty) *hasProperty = true; - return args->context()->callData->args[index].asReturnedValue(); + return args->context()->args()[index].asReturnedValue(); } if (hasProperty) *hasProperty = false; @@ -170,13 +181,13 @@ ReturnedValue ArgumentsObject::getIndexed(const Managed *m, uint index, bool *ha bool ArgumentsObject::putIndexed(Managed *m, uint index, const Value &value) { ArgumentsObject *args = static_cast<ArgumentsObject *>(m); - if (!args->fullyCreated() && index >= static_cast<uint>(args->context()->callData->argc)) + if (!args->fullyCreated() && index >= static_cast<uint>(args->context()->argc())) args->fullyCreate(); if (args->fullyCreated()) return Object::putIndexed(m, index, value); - args->context()->callData->args[index] = value; + args->context()->setArg(index, value); return true; } @@ -194,8 +205,8 @@ PropertyAttributes ArgumentsObject::queryIndexed(const Managed *m, uint index) if (args->fullyCreated()) return Object::queryIndexed(m, index); - uint numAccessors = qMin((int)args->context()->formalParameterCount(), args->context()->callData->argc); - uint argCount = args->context()->callData->argc; + uint numAccessors = qMin(args->d()->nFormals, args->context()->argc()); + uint argCount = args->context()->argc(); if (index >= argCount) return PropertyAttributes(); if (index >= numAccessors) @@ -205,35 +216,33 @@ PropertyAttributes ArgumentsObject::queryIndexed(const Managed *m, uint index) DEFINE_OBJECT_VTABLE(ArgumentsGetterFunction); -void ArgumentsGetterFunction::call(const Managed *getter, Scope &scope, CallData *callData) +ReturnedValue ArgumentsGetterFunction::call(const FunctionObject *getter, const Value *thisObject, const Value *, int) { - ExecutionEngine *v4 = static_cast<const ArgumentsGetterFunction *>(getter)->engine(); - Scoped<ArgumentsGetterFunction> g(scope, static_cast<const ArgumentsGetterFunction *>(getter)); - Scoped<ArgumentsObject> o(scope, callData->thisObject.as<ArgumentsObject>()); - if (!o) { - scope.result = v4->throwTypeError(); - return; - } + ExecutionEngine *v4 = getter->engine(); + Scope scope(v4); + const ArgumentsGetterFunction *g = static_cast<const ArgumentsGetterFunction *>(getter); + Scoped<ArgumentsObject> o(scope, thisObject->as<ArgumentsObject>()); + if (!o) + return v4->throwTypeError(); - Q_ASSERT(g->index() < static_cast<unsigned>(o->context()->callData->argc)); - scope.result = o->context()->callData->args[g->index()]; + Q_ASSERT(g->index() < static_cast<unsigned>(o->context()->argc())); + return o->context()->args()[g->index()].asReturnedValue(); } DEFINE_OBJECT_VTABLE(ArgumentsSetterFunction); -void ArgumentsSetterFunction::call(const Managed *setter, Scope &scope, CallData *callData) +ReturnedValue ArgumentsSetterFunction::call(const FunctionObject *setter, const Value *thisObject, const Value *argv, int argc) { - ExecutionEngine *v4 = static_cast<const ArgumentsSetterFunction *>(setter)->engine(); - Scoped<ArgumentsSetterFunction> s(scope, static_cast<const ArgumentsSetterFunction *>(setter)); - Scoped<ArgumentsObject> o(scope, callData->thisObject.as<ArgumentsObject>()); - if (!o) { - scope.result = v4->throwTypeError(); - return; - } + ExecutionEngine *v4 = setter->engine(); + Scope scope(v4); + const ArgumentsSetterFunction *s = static_cast<const ArgumentsSetterFunction *>(setter); + Scoped<ArgumentsObject> o(scope, thisObject->as<ArgumentsObject>()); + if (!o) + return v4->throwTypeError(); - Q_ASSERT(s->index() < static_cast<unsigned>(o->context()->callData->argc)); - o->context()->callData->args[s->index()] = callData->argc ? callData->args[0].asReturnedValue() : Encode::undefined(); - scope.result = Encode::undefined(); + Q_ASSERT(s->index() < static_cast<unsigned>(o->context()->argc())); + o->context()->setArg(s->index(), argc ? argv[0] : Primitive::undefinedValue()); + return Encode::undefined(); } uint ArgumentsObject::getLength(const Managed *m) diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h index 46e1f884e8..ac281f555a 100644 --- a/src/qml/jsruntime/qv4argumentsobject_p.h +++ b/src/qml/jsruntime/qv4argumentsobject_p.h @@ -63,7 +63,7 @@ namespace Heap { Member(class, NoMark, uint, index) DECLARE_HEAP_OBJECT(ArgumentsGetterFunction, FunctionObject) { - DECLARE_MARK_TABLE(ArgumentsGetterFunction); + DECLARE_MARKOBJECTS(ArgumentsGetterFunction); inline void init(QV4::ExecutionContext *scope, uint index); }; @@ -71,23 +71,34 @@ DECLARE_HEAP_OBJECT(ArgumentsGetterFunction, FunctionObject) { Member(class, NoMark, uint, index) DECLARE_HEAP_OBJECT(ArgumentsSetterFunction, FunctionObject) { - DECLARE_MARK_TABLE(ArgumentsSetterFunction); + DECLARE_MARKOBJECTS(ArgumentsSetterFunction); inline void init(QV4::ExecutionContext *scope, uint index); }; #define ArgumentsObjectMembers(class, Member) \ Member(class, Pointer, CallContext *, context) \ Member(class, Pointer, MemberData *, mappedArguments) \ - Member(class, NoMark, bool, fullyCreated) + Member(class, NoMark, bool, fullyCreated) \ + Member(class, NoMark, int, nFormals) DECLARE_HEAP_OBJECT(ArgumentsObject, Object) { - DECLARE_MARK_TABLE(ArgumentsObject); + DECLARE_MARKOBJECTS(ArgumentsObject); + enum { + LengthPropertyIndex = 0, + CalleePropertyIndex = 1 + }; + void init(CppStackFrame *frame); +}; + +#define StrictArgumentsObjectMembers(class, Member) + +DECLARE_HEAP_OBJECT(StrictArgumentsObject, Object) { enum { LengthPropertyIndex = 0, CalleePropertyIndex = 1, CallerPropertyIndex = 3 }; - void init(QV4::CallContext *context); + void init(CppStackFrame *frame); }; } @@ -97,7 +108,7 @@ struct ArgumentsGetterFunction: FunctionObject V4_OBJECT2(ArgumentsGetterFunction, FunctionObject) uint index() const { return d()->index; } - static void call(const Managed *that, Scope &scope, CallData *d); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; inline void @@ -112,7 +123,7 @@ struct ArgumentsSetterFunction: FunctionObject V4_OBJECT2(ArgumentsSetterFunction, FunctionObject) uint index() const { return d()->index; } - static void call(const Managed *that, Scope &scope, CallData *callData); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; inline void @@ -131,8 +142,7 @@ struct ArgumentsObject: Object { bool fullyCreated() const { return d()->fullyCreated; } static bool isNonStrictArgumentsObject(Managed *m) { - return m->d()->vtable()->type == Type_ArgumentsObject && - !static_cast<ArgumentsObject *>(m)->context()->strictMode; + return m->d()->vtable() == staticVTable(); } bool defineOwnProperty(ExecutionEngine *engine, uint index, const Property *desc, PropertyAttributes attrs); @@ -146,6 +156,11 @@ struct ArgumentsObject: Object { }; +struct StrictArgumentsObject : Object { + V4_OBJECT2(StrictArgumentsObject, Object) + Q_MANAGED_TYPE(ArgumentsObject) +}; + } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4arraybuffer.cpp b/src/qml/jsruntime/qv4arraybuffer.cpp index ffe9aa846f..f7b9e8acef 100644 --- a/src/qml/jsruntime/qv4arraybuffer.cpp +++ b/src/qml/jsruntime/qv4arraybuffer.cpp @@ -40,6 +40,7 @@ #include "qv4typedarray_p.h" #include "qv4dataview_p.h" #include "qv4string_p.h" +#include "qv4jscall_p.h" using namespace QV4; @@ -51,49 +52,42 @@ void Heap::ArrayBufferCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("ArrayBuffer")); } -void ArrayBufferCtor::construct(const Managed *m, Scope &scope, CallData *callData) +ReturnedValue ArrayBufferCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - ExecutionEngine *v4 = static_cast<const Object *>(m)->engine(); + ExecutionEngine *v4 = f->engine(); + Scope scope(v4); - ScopedValue l(scope, callData->argument(0)); + ScopedValue l(scope, argc ? argv[0] : Primitive::undefinedValue()); double dl = l->toInteger(); - if (v4->hasException) { - scope.result = Encode::undefined(); - return; - } + if (v4->hasException) + return Encode::undefined(); uint len = (uint)qBound(0., dl, (double)UINT_MAX); - if (len != dl) { - scope.result = v4->throwRangeError(QLatin1String("ArrayBuffer constructor: invalid length")); - return; - } + if (len != dl) + return v4->throwRangeError(QLatin1String("ArrayBuffer constructor: invalid length")); Scoped<ArrayBuffer> a(scope, v4->newArrayBuffer(len)); - if (scope.engine->hasException) { - scope.result = Encode::undefined(); - } else { - scope.result = a->asReturnedValue(); - } + if (scope.engine->hasException) + return Encode::undefined(); + + return a->asReturnedValue(); } -void ArrayBufferCtor::call(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue ArrayBufferCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) { - construct(that, scope, callData); + return callAsConstructor(f, argv, argc); } -void ArrayBufferCtor::method_isView(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayBufferCtor::method_isView(const FunctionObject *, const Value *, const Value *argv, int argc) { - QV4::Scoped<TypedArray> a(scope, callData->argument(0)); - if (!!a) { - scope.result = Encode(true); - return; - } - QV4::Scoped<DataView> v(scope, callData->argument(0)); - if (!!v) { - scope.result = Encode(true); - return; - } - scope.result = Encode(false); + if (argc < 1) + return Encode(false); + + if (argv[0].as<TypedArray>() || + argv[0].as<DataView>()) + return Encode(true); + + return Encode(false); } @@ -163,48 +157,51 @@ void ArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("toString"), method_toString, 0); } -void ArrayBufferPrototype::method_get_byteLength(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayBufferPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int) { - Scoped<ArrayBuffer> v(scope, callData->thisObject); - if (!v) - THROW_TYPE_ERROR(); + const ArrayBuffer *a = thisObject->as<ArrayBuffer>(); + if (!a) + return b->engine()->throwTypeError(); - scope.result = Encode(v->d()->data->size); + return Encode(a->d()->data->size); } -void ArrayBufferPrototype::method_slice(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayBufferPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - Scoped<ArrayBuffer> a(scope, callData->thisObject); + ExecutionEngine *v4 = b->engine(); + const ArrayBuffer *a = thisObject->as<ArrayBuffer>(); if (!a) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); - double start = callData->argc > 0 ? callData->args[0].toInteger() : 0; - double end = (callData->argc < 2 || callData->args[1].isUndefined()) ? - a->d()->data->size : callData->args[1].toInteger(); - CHECK_EXCEPTION(); + double start = argc > 0 ? argv[0].toInteger() : 0; + double end = (argc < 2 || argv[1].isUndefined()) ? + a->d()->data->size : argv[1].toInteger(); + if (v4->hasException) + return QV4::Encode::undefined(); double first = (start < 0) ? qMax(a->d()->data->size + start, 0.) : qMin(start, (double)a->d()->data->size); double final = (end < 0) ? qMax(a->d()->data->size + end, 0.) : qMin(end, (double)a->d()->data->size); + Scope scope(v4); ScopedFunctionObject constructor(scope, a->get(scope.engine->id_constructor())); if (!constructor) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); - ScopedCallData cData(scope, 1); double newLen = qMax(final - first, 0.); - cData->args[0] = QV4::Encode(newLen); - constructor->construct(scope, cData); - QV4::Scoped<ArrayBuffer> newBuffer(scope, scope.result); + ScopedValue argument(scope, QV4::Encode(newLen)); + QV4::Scoped<ArrayBuffer> newBuffer(scope, constructor->callAsConstructor(argument, 1)); if (!newBuffer || newBuffer->d()->data->size < (int)newLen) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); memcpy(newBuffer->d()->data->data(), a->d()->data->data() + (uint)first, newLen); + return Encode::undefined(); } -void ArrayBufferPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayBufferPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) { - Scoped<ArrayBuffer> a(scope, callData->thisObject); + ExecutionEngine *v4 = b->engine(); + const ArrayBuffer *a = thisObject->as<ArrayBuffer>(); if (!a) RETURN_UNDEFINED(); - scope.result = scope.engine->newString(QString::fromUtf8(a->asByteArray())); + return Encode(v4->newString(QString::fromUtf8(a->asByteArray()))); } diff --git a/src/qml/jsruntime/qv4arraybuffer_p.h b/src/qml/jsruntime/qv4arraybuffer_p.h index 4f7926d3dc..e236a23d1f 100644 --- a/src/qml/jsruntime/qv4arraybuffer_p.h +++ b/src/qml/jsruntime/qv4arraybuffer_p.h @@ -78,10 +78,10 @@ struct ArrayBufferCtor: FunctionObject { V4_OBJECT2(ArrayBufferCtor, FunctionObject) - static void construct(const Managed *m, Scope &scope, CallData *callData); - static void call(const Managed *that, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); - static void method_isView(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_isView(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; @@ -104,9 +104,9 @@ struct ArrayBufferPrototype: Object { void init(ExecutionEngine *engine, Object *ctor); - static void method_get_byteLength(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_slice(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_get_byteLength(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_slice(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp index df9884d84a..390840fb1e 100644 --- a/src/qml/jsruntime/qv4arraydata.cpp +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -43,6 +43,7 @@ #include "qv4runtime_p.h" #include "qv4argumentsobject_p.h" #include "qv4string_p.h" +#include "qv4jscall_p.h" using namespace QV4; @@ -52,7 +53,6 @@ const QV4::VTable QV4::ArrayData::static_vtbl = { 0, 0, 0, - 0, QV4::ArrayData::IsExecutionContext, QV4::ArrayData::IsString, QV4::ArrayData::IsObject, @@ -63,7 +63,7 @@ const QV4::VTable QV4::ArrayData::static_vtbl = { QV4::ArrayData::MyType, "ArrayData", Q_VTABLE_FUNCTION(QV4::ArrayData, destroy), - 0, + ArrayData::Data::markObjects, isEqualTo }; @@ -111,6 +111,13 @@ static Q_ALWAYS_INLINE void storeValue(ReturnedValue *target, uint value) *target = v.asReturnedValue(); } +void Heap::ArrayData::markObjects(Heap::Base *base, MarkStack *stack) +{ + ArrayData *a = static_cast<ArrayData *>(base); + a->values.mark(stack); +} + + void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAttributes) { Scope scope(o->engine()); @@ -161,6 +168,8 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt } newData->setAlloc(alloc); newData->setType(newType); + if (d) + newData->d()->needsMark = d->d()->needsMark; newData->setAttrs(enforceAttributes ? reinterpret_cast<PropertyAttributes *>(newData->d()->values.values + alloc) : 0); o->setArrayData(newData); @@ -183,6 +192,8 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt memcpy(newData->d()->values.values, d->d()->values.values + offset, sizeof(Value)*toCopy); } + if (newType != Heap::ArrayData::Simple) + newData->d()->needsMark = true; if (newType != Heap::ArrayData::Sparse) return; @@ -672,15 +683,14 @@ bool ArrayElementLessThan::operator()(Value v1, Value v2) const return false; if (v2.isUndefined() || v2.isEmpty()) return true; - ScopedObject o(scope, m_comparefn); + ScopedFunctionObject o(scope, m_comparefn); if (o) { Scope scope(o->engine()); ScopedValue result(scope); - ScopedCallData callData(scope, 2); - callData->thisObject = Primitive::undefinedValue(); - callData->args[0] = v1; - callData->args[1] = v2; - result = QV4::Runtime::method_callValue(scope.engine, m_comparefn, callData); + JSCallData jsCallData(scope, 2); + jsCallData->args[0] = v1; + jsCallData->args[1] = v2; + result = o->call(jsCallData); return result->toNumber() < 0; } @@ -754,7 +764,7 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c if (!arrayData || !arrayData->length()) return; - if (!(comparefn.isUndefined() || comparefn.as<Object>())) { + if (!comparefn.isUndefined() && !comparefn.isFunctionObject()) { engine->throwTypeError(); return; } @@ -833,7 +843,7 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c } - ArrayElementLessThan lessThan(engine, thisObject, comparefn); + ArrayElementLessThan lessThan(engine, thisObject, static_cast<const FunctionObject &>(comparefn)); Value *begin = thisObject->arrayData()->values.values; sortHelper(begin, begin + len, *begin, lessThan); diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h index e1de2e82e6..db9db5a220 100644 --- a/src/qml/jsruntime/qv4arraydata_p.h +++ b/src/qml/jsruntime/qv4arraydata_p.h @@ -91,7 +91,8 @@ struct ArrayVTable namespace Heap { #define ArrayDataMembers(class, Member) \ - Member(class, NoMark, uint, type) \ + Member(class, NoMark, ushort, type) \ + Member(class, NoMark, ushort, needsMark) \ Member(class, NoMark, uint, offset) \ Member(class, NoMark, PropertyAttributes *, attrs) \ Member(class, NoMark, ReturnedValue, freeList) \ @@ -99,7 +100,7 @@ namespace Heap { Member(class, ValueArray, ValueArray, values) DECLARE_HEAP_OBJECT(ArrayData, Base) { - DECLARE_MARK_TABLE(ArrayData); + static void markObjects(Heap::Base *base, MarkStack *stack); enum Type { Simple = 0, Complex = 1, Sparse = 2, Custom = 3 }; @@ -144,9 +145,11 @@ DECLARE_HEAP_OBJECT(ArrayData, Base) { V4_ASSERT_IS_TRIVIAL(ArrayData) struct SimpleArrayData : public ArrayData { - uint mappedIndex(uint index) const { return (index + offset) % values.alloc; } + uint mappedIndex(uint index) const { index += offset; if (index >= values.alloc) index -= values.alloc; return index; } const Value &data(uint index) const { return values[mappedIndex(index)]; } void setData(EngineBase *e, uint index, Value newVal) { + if (newVal.isManaged()) + needsMark = true; values.set(e, mappedIndex(index), newVal); } diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index a2c19e1f2d..bd019d3bcb 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -40,7 +40,7 @@ #include "qv4arrayobject_p.h" #include "qv4sparsearray_p.h" #include "qv4objectproto_p.h" -#include "qv4scopedvalue_p.h" +#include "qv4jscall_p.h" #include "qv4argumentsobject_p.h" #include "qv4runtime_p.h" #include "qv4string_p.h" @@ -55,35 +55,34 @@ void Heap::ArrayCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Array")); } -void ArrayCtor::construct(const Managed *m, Scope &scope, CallData *callData) +ReturnedValue ArrayCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - ExecutionEngine *v4 = static_cast<const ArrayCtor *>(m)->engine(); + ExecutionEngine *v4 = static_cast<const ArrayCtor *>(f)->engine(); + Scope scope(v4); ScopedArrayObject a(scope, v4->newArrayObject()); uint len; - if (callData->argc == 1 && callData->args[0].isNumber()) { + if (argc == 1 && argv[0].isNumber()) { bool ok; - len = callData->args[0].asArrayLength(&ok); + len = argv[0].asArrayLength(&ok); - if (!ok) { - scope.result = v4->throwRangeError(callData->args[0]); - return; - } + if (!ok) + return v4->throwRangeError(argv[0]); if (len < 0x1000) a->arrayReserve(len); } else { - len = callData->argc; + len = argc; a->arrayReserve(len); - a->arrayPut(0, callData->args, len); + a->arrayPut(0, argv, len); } a->setArrayLengthUnchecked(len); - scope.result = a.asReturnedValue(); + return a.asReturnedValue(); } -void ArrayCtor::call(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue ArrayCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) { - construct(that, scope, callData); + return callAsConstructor(f, argv, argc); } void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor) @@ -119,35 +118,35 @@ void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("reduceRight"), method_reduceRight, 1); } -void ArrayPrototype::method_isArray(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_isArray(const FunctionObject *, const Value *, const Value *argv, int argc) { - bool isArray = callData->argc && callData->args[0].as<ArrayObject>(); - scope.result = Encode(isArray); + bool isArray = argc && argv[0].as<ArrayObject>(); + return Encode(isArray); } -void ArrayPrototype::method_toString(const BuiltinFunction *builtin, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_toString(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc) { - ScopedObject o(scope, callData->thisObject, ScopedObject::Convert); - CHECK_EXCEPTION(); - ScopedString s(scope, scope.engine->newString(QStringLiteral("join"))); - ScopedFunctionObject f(scope, o->get(s)); - if (!!f) { - ScopedCallData d(scope, 0); - d->thisObject = callData->thisObject; - f->call(scope, d); - return; - } - ObjectPrototype::method_toString(builtin, scope, callData); + Scope scope(builtin); + ScopedObject that(scope, thisObject->toObject(scope.engine)); + if (scope.hasException()) + return QV4::Encode::undefined(); + + ScopedString string(scope, scope.engine->newString(QStringLiteral("join"))); + ScopedFunctionObject f(scope, that->get(string)); + if (f) + return f->call(that, argv, argc); + return ObjectPrototype::method_toString(builtin, that, argv, argc); } -void ArrayPrototype::method_toLocaleString(const BuiltinFunction *builtin, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_toLocaleString(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc) { - return method_toString(builtin, scope, callData); + return method_toString(builtin, thisObject, argv, argc); } -void ArrayPrototype::method_concat(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_concat(const FunctionObject *b, const Value *that, const Value *argv, int argc) { - ScopedObject thisObject(scope, callData->thisObject.toObject(scope.engine)); + Scope scope(b); + ScopedObject thisObject(scope, that->toObject(scope.engine)); if (!thisObject) RETURN_UNDEFINED(); @@ -162,9 +161,9 @@ void ArrayPrototype::method_concat(const BuiltinFunction *, Scope &scope, CallDa ScopedArrayObject elt(scope); ScopedObject eltAsObj(scope); ScopedValue entry(scope); - for (int i = 0; i < callData->argc; ++i) { - eltAsObj = callData->args[i]; - elt = callData->args[i]; + for (int i = 0, ei = argc; i < ei; ++i) { + eltAsObj = argv[i]; + elt = argv[i]; if (elt) { uint n = elt->getLength(); uint newLen = ArrayData::append(result, elt, n); @@ -173,93 +172,94 @@ void ArrayPrototype::method_concat(const BuiltinFunction *, Scope &scope, CallDa const uint startIndex = result->getLength(); for (int i = 0, len = eltAsObj->getLength(); i < len; ++i) { entry = eltAsObj->getIndexed(i); + // spec says not to throw if this fails result->putIndexed(startIndex + i, entry); } } else { - result->arraySet(result->getLength(), callData->args[i]); + result->arraySet(result->getLength(), argv[i]); } } - scope.result = result; + return result.asReturnedValue(); } -void ArrayPrototype::method_find(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_find(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); uint len = instance->getLength(); - ScopedFunctionObject callback(scope, callData->argument(0)); - if (!callback) + if (!argc || !argv[0].isFunctionObject()) THROW_TYPE_ERROR(); + const FunctionObject *callback = static_cast<const FunctionObject *>(argv); - ScopedCallData cData(scope, 3); - cData->thisObject = callData->argument(1); - cData->args[2] = instance; + ScopedValue result(scope); + Value *arguments = scope.alloc(3); - ScopedValue v(scope); + ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); for (uint k = 0; k < len; ++k) { - v = instance->getIndexed(k); + arguments[0] = instance->getIndexed(k); CHECK_EXCEPTION(); - cData->args[0] = v; - cData->args[1] = Primitive::fromDouble(k); - callback->call(scope, cData); + arguments[1] = Primitive::fromDouble(k); + arguments[2] = instance; + result = callback->call(that, arguments, 3); CHECK_EXCEPTION(); - if (scope.result.toBoolean()) - RETURN_RESULT(v); + if (result->toBoolean()) + return arguments[0].asReturnedValue(); } RETURN_UNDEFINED(); } -void ArrayPrototype::method_findIndex(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_findIndex(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); uint len = instance->getLength(); - ScopedFunctionObject callback(scope, callData->argument(0)); - if (!callback) + if (!argc || !argv[0].isFunctionObject()) THROW_TYPE_ERROR(); + const FunctionObject *callback = static_cast<const FunctionObject *>(argv); - ScopedCallData cData(scope, 3); - cData->thisObject = callData->argument(1); - cData->args[2] = instance; + ScopedValue result(scope); + Value *arguments = scope.alloc(3); - ScopedValue v(scope); + ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); for (uint k = 0; k < len; ++k) { - v = instance->getIndexed(k); + arguments[0] = instance->getIndexed(k); CHECK_EXCEPTION(); - cData->args[0] = v; - cData->args[1] = Primitive::fromDouble(k); - callback->call(scope, cData); + arguments[1] = Primitive::fromDouble(k); + arguments[2] = instance; + result = callback->call(that, arguments, 3); CHECK_EXCEPTION(); - if (scope.result.toBoolean()) - RETURN_RESULT(Encode(k)); + if (result->toBoolean()) + return Encode(k); } - RETURN_RESULT(Encode(-1)); + return Encode(-1); } -void ArrayPrototype::method_join(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_join(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ScopedValue arg(scope, callData->argument(0)); - ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); - if (!instance) { - scope.result = scope.engine->newString(); - return; - } + if (!instance) + return Encode(scope.engine->newString()); + + ScopedValue arg(scope, argc ? argv[0] : Primitive::undefinedValue()); QString r4; if (arg->isUndefined()) @@ -270,10 +270,8 @@ void ArrayPrototype::method_join(const BuiltinFunction *, Scope &scope, CallData ScopedValue length(scope, instance->get(scope.engine->id_length())); const quint32 r2 = length->isUndefined() ? 0 : length->toUInt32(); - if (!r2) { - scope.result = scope.engine->newString(); - return; - } + if (!r2) + return Encode(scope.engine->newString()); QString R; @@ -311,12 +309,13 @@ void ArrayPrototype::method_join(const BuiltinFunction *, Scope &scope, CallData } } - scope.result = scope.engine->newString(R); + return Encode(scope.engine->newString(R)); } -void ArrayPrototype::method_pop(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_pop(const FunctionObject *b, const Value *thisObject, const Value *, int) { - ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); @@ -331,19 +330,22 @@ void ArrayPrototype::method_pop(const BuiltinFunction *, Scope &scope, CallData ScopedValue result(scope, instance->getIndexed(len - 1)); CHECK_EXCEPTION(); - instance->deleteIndexedProperty(len - 1); - CHECK_EXCEPTION(); + if (!instance->deleteIndexedProperty(len - 1)) + return scope.engine->throwTypeError(); if (instance->isArrayObject()) instance->setArrayLength(len - 1); - else - instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - 1))); - scope.result = result; + else { + if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - 1)))) + return scope.engine->throwTypeError(); + } + return result->asReturnedValue(); } -void ArrayPrototype::method_push(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_push(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); @@ -352,47 +354,52 @@ void ArrayPrototype::method_push(const BuiltinFunction *, Scope &scope, CallData uint len = instance->getLength(); - if (len + callData->argc < len) { - // ughh... + if (len + argc < len) { + // ughh... this goes beyond UINT_MAX double l = len; ScopedString s(scope); - for (int i = 0; i < callData->argc; ++i) { + for (int i = 0, ei = argc; i < ei; ++i) { s = Primitive::fromDouble(l + i).toString(scope.engine); - instance->put(s, callData->args[i]); + if (!instance->put(s, argv[i])) + return scope.engine->throwTypeError(); } - double newLen = l + callData->argc; - if (!instance->isArrayObject()) - instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(newLen))); - else { + double newLen = l + argc; + if (!instance->isArrayObject()) { + if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(newLen)))) + return scope.engine->throwTypeError(); + } else { ScopedString str(scope, scope.engine->newString(QStringLiteral("Array.prototype.push: Overflow"))); - scope.result = scope.engine->throwRangeError(str); - return; + return scope.engine->throwRangeError(str); } - scope.result = Encode(newLen); - return; + return Encode(newLen); } - if (!callData->argc) + if (!argc) ; else if (!instance->protoHasArray() && instance->arrayData()->length() <= len && instance->arrayData()->type == Heap::ArrayData::Simple) { - instance->arrayData()->vtable()->putArray(instance, len, callData->args, callData->argc); + instance->arrayData()->vtable()->putArray(instance, len, argv, argc); len = instance->arrayData()->length(); } else { - for (int i = 0; i < callData->argc; ++i) - instance->putIndexed(len + i, callData->args[i]); - len += callData->argc; + for (int i = 0, ei = argc; i < ei; ++i) { + if (!instance->putIndexed(len + i, argv[i])) + return scope.engine->throwTypeError(); + } + len += argc; } if (instance->isArrayObject()) instance->setArrayLengthUnchecked(len); - else - instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len))); + else { + if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len)))) + return scope.engine->throwTypeError(); + } - scope.result = Encode(len); + return Encode(len); } -void ArrayPrototype::method_reverse(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_reverse(const FunctionObject *b, const Value *thisObject, const Value *, int) { - ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); @@ -407,22 +414,27 @@ void ArrayPrototype::method_reverse(const BuiltinFunction *, Scope &scope, CallD lval = instance->getIndexed(lo, &loExists); hval = instance->getIndexed(hi, &hiExists); CHECK_EXCEPTION(); + bool ok; if (hiExists) - instance->putIndexed(lo, hval); - else - instance->deleteIndexedProperty(lo); - CHECK_EXCEPTION(); - if (loExists) - instance->putIndexed(hi, lval); + ok = instance->putIndexed(lo, hval); else - instance->deleteIndexedProperty(hi); + ok = instance->deleteIndexedProperty(lo); + if (ok) { + if (loExists) + ok = instance->putIndexed(hi, lval); + else + ok = instance->deleteIndexedProperty(hi); + } + if (!ok) + return scope.engine->throwTypeError(); } - scope.result = instance; + return instance->asReturnedValue(); } -void ArrayPrototype::method_shift(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_shift(const FunctionObject *b, const Value *thisObject, const Value *, int) { - ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); @@ -433,14 +445,16 @@ void ArrayPrototype::method_shift(const BuiltinFunction *, Scope &scope, CallDat if (!len) { if (!instance->isArrayObject()) - instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromInt32(0))); + if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromInt32(0)))) + return scope.engine->throwTypeError(); RETURN_UNDEFINED(); } + ScopedValue result(scope); if (!instance->protoHasArray() && !instance->arrayData()->attrs && instance->arrayData()->length() <= len && instance->arrayData()->type != Heap::ArrayData::Custom) { - scope.result = instance->arrayData()->vtable()->pop_front(instance); + result = instance->arrayData()->vtable()->pop_front(instance); } else { - scope.result = instance->getIndexed(0); + result = instance->getIndexed(0); CHECK_EXCEPTION(); ScopedValue v(scope); // do it the slow way @@ -448,31 +462,40 @@ void ArrayPrototype::method_shift(const BuiltinFunction *, Scope &scope, CallDat bool exists; v = instance->getIndexed(k, &exists); CHECK_EXCEPTION(); + bool ok; if (exists) - instance->putIndexed(k - 1, v); + ok = instance->putIndexed(k - 1, v); else - instance->deleteIndexedProperty(k - 1); - CHECK_EXCEPTION(); + ok = instance->deleteIndexedProperty(k - 1); + if (!ok) + return scope.engine->throwTypeError(); } - instance->deleteIndexedProperty(len - 1); - CHECK_EXCEPTION(); + bool ok = instance->deleteIndexedProperty(len - 1); + if (!ok) + return scope.engine->throwTypeError(); } if (instance->isArrayObject()) instance->setArrayLengthUnchecked(len - 1); - else - instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - 1))); + else { + bool ok = instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - 1))); + if (!ok) + return scope.engine->throwTypeError(); + } + + return result->asReturnedValue(); } -void ArrayPrototype::method_slice(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ScopedObject o(scope, callData->thisObject.toObject(scope.engine)); + Scope scope(b); + ScopedObject o(scope, thisObject->toObject(scope.engine)); if (!o) RETURN_UNDEFINED(); ScopedArrayObject result(scope, scope.engine->newArrayObject()); uint len = o->getLength(); - double s = ScopedValue(scope, callData->argument(0))->toInteger(); + double s = (argc ? argv[0] : Primitive::undefinedValue()).toInteger(); uint start; if (s < 0) start = (uint)qMax(len + s, 0.); @@ -481,8 +504,8 @@ void ArrayPrototype::method_slice(const BuiltinFunction *, Scope &scope, CallDat else start = (uint) s; uint end = len; - if (callData->argc > 1 && !callData->args[1].isUndefined()) { - double e = callData->args[1].toInteger(); + if (argc > 1 && !argv[1].isUndefined()) { + double e = argv[1].toInteger(); if (e < 0) end = (uint)qMax(len + e, 0.); else if (e > len) @@ -501,25 +524,27 @@ void ArrayPrototype::method_slice(const BuiltinFunction *, Scope &scope, CallDat result->arraySet(n, v); ++n; } - scope.result = result; + return result->asReturnedValue(); } -void ArrayPrototype::method_sort(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_sort(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); uint len = instance->getLength(); - ScopedValue comparefn(scope, callData->argument(0)); + ScopedValue comparefn(scope, argc ? argv[0] : Primitive::undefinedValue()); ArrayData::sort(scope.engine, instance, comparefn, len); - scope.result = callData->thisObject; + return thisObject->asReturnedValue(); } -void ArrayPrototype::method_splice(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_splice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); @@ -527,14 +552,14 @@ void ArrayPrototype::method_splice(const BuiltinFunction *, Scope &scope, CallDa ScopedArrayObject newArray(scope, scope.engine->newArrayObject()); - double rs = ScopedValue(scope, callData->argument(0))->toInteger(); + double rs = (argc ? argv[0] : Primitive::undefinedValue()).toInteger(); uint start; if (rs < 0) start = (uint) qMax(0., len + rs); else start = (uint) qMin(rs, (double)len); - uint deleteCount = (uint)qMin(qMax(ScopedValue(scope, callData->argument(1))->toInteger(), 0.), (double)(len - start)); + uint deleteCount = (uint)qMin(qMax((argc > 1 ? argv[1] : Primitive::undefinedValue()).toInteger(), 0.), (double)(len - start)); newArray->arrayReserve(deleteCount); ScopedValue v(scope); @@ -547,22 +572,24 @@ void ArrayPrototype::method_splice(const BuiltinFunction *, Scope &scope, CallDa } newArray->setArrayLengthUnchecked(deleteCount); - uint itemCount = callData->argc < 2 ? 0 : callData->argc - 2; + uint itemCount = argc < 2 ? 0 : argc - 2; if (itemCount < deleteCount) { for (uint k = start; k < len - deleteCount; ++k) { bool exists; v = instance->getIndexed(k + deleteCount, &exists); CHECK_EXCEPTION(); + bool ok; if (exists) - instance->putIndexed(k + itemCount, v); + ok = instance->putIndexed(k + itemCount, v); else - instance->deleteIndexedProperty(k + itemCount); - CHECK_EXCEPTION(); + ok = instance->deleteIndexedProperty(k + itemCount); + if (!ok) + return scope.engine->throwTypeError(); } for (uint k = len; k > len - deleteCount + itemCount; --k) { - instance->deleteIndexedProperty(k - 1); - CHECK_EXCEPTION(); + if (!instance->deleteIndexedProperty(k - 1)) + return scope.engine->throwTypeError(); } } else if (itemCount > deleteCount) { uint k = len - deleteCount; @@ -570,31 +597,30 @@ void ArrayPrototype::method_splice(const BuiltinFunction *, Scope &scope, CallDa bool exists; v = instance->getIndexed(k + deleteCount - 1, &exists); CHECK_EXCEPTION(); + bool ok; if (exists) - instance->putIndexed(k + itemCount - 1, v); + ok = instance->putIndexed(k + itemCount - 1, v); else - instance->deleteIndexedProperty(k + itemCount - 1); - CHECK_EXCEPTION(); + ok = instance->deleteIndexedProperty(k + itemCount - 1); + if (!ok) + return scope.engine->throwTypeError(); --k; } } - for (uint i = 0; i < itemCount; ++i) { - instance->putIndexed(start + i, callData->args[i + 2]); - CHECK_EXCEPTION(); - } + for (uint i = 0; i < itemCount; ++i) + instance->putIndexed(start + i, argv[i + 2]); - bool wasStrict = scope.engine->current->strictMode; - scope.engine->current->strictMode = true; - instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - deleteCount + itemCount))); + if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - deleteCount + itemCount)))) + return scope.engine->throwTypeError(); - scope.result = newArray; - scope.engine->current->strictMode = wasStrict; + return newArray->asReturnedValue(); } -void ArrayPrototype::method_unshift(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_unshift(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); @@ -605,52 +631,57 @@ void ArrayPrototype::method_unshift(const BuiltinFunction *, Scope &scope, CallD if (!instance->protoHasArray() && !instance->arrayData()->attrs && instance->arrayData()->length() <= len && instance->arrayData()->type != Heap::ArrayData::Custom) { - instance->arrayData()->vtable()->push_front(instance, callData->args, callData->argc); + instance->arrayData()->vtable()->push_front(instance, argv, argc); } else { ScopedValue v(scope); for (uint k = len; k > 0; --k) { bool exists; v = instance->getIndexed(k - 1, &exists); + bool ok; if (exists) - instance->putIndexed(k + callData->argc - 1, v); + ok = instance->putIndexed(k + argc - 1, v); else - instance->deleteIndexedProperty(k + callData->argc - 1); + ok = instance->deleteIndexedProperty(k + argc - 1); + if (!ok) + return scope.engine->throwTypeError(); + } + for (int i = 0, ei = argc; i < ei; ++i) { + bool ok = instance->putIndexed(i, argv[i]); + if (!ok) + return scope.engine->throwTypeError(); } - for (int i = 0; i < callData->argc; ++i) - instance->putIndexed(i, callData->args[i]); } - uint newLen = len + callData->argc; + uint newLen = len + argc; if (instance->isArrayObject()) instance->setArrayLengthUnchecked(newLen); - else - instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(newLen))); + else { + if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(newLen)))) + return scope.engine->throwTypeError(); + } - scope.result = Encode(newLen); + return Encode(newLen); } -void ArrayPrototype::method_indexOf(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); uint len = instance->getLength(); - if (!len) { - scope.result = Encode(-1); - return; - } + if (!len) + return Encode(-1); - ScopedValue searchValue(scope, callData->argument(0)); + ScopedValue searchValue(scope, argc ? argv[0] : Primitive::undefinedValue()); uint fromIndex = 0; - if (callData->argc >= 2) { - double f = callData->args[1].toInteger(); + if (argc >= 2) { + double f = argv[1].toInteger(); CHECK_EXCEPTION(); - if (f >= len) { - scope.result = Encode(-1); - return; - } + if (f >= len) + return Encode(-1); if (f < 0) f = qMax(len + f, 0.); fromIndex = (uint) f; @@ -661,13 +692,10 @@ void ArrayPrototype::method_indexOf(const BuiltinFunction *, Scope &scope, CallD for (uint k = fromIndex; k < len; ++k) { bool exists; v = instance->getIndexed(k, &exists); - if (exists && RuntimeHelpers::strictEqual(v, searchValue)) { - scope.result = Encode(k); - return; - } + if (exists && RuntimeHelpers::strictEqual(v, searchValue)) + return Encode(k); } - scope.result = Encode(-1); - return; + return Encode(-1); } ScopedValue value(scope); @@ -679,14 +707,11 @@ void ArrayPrototype::method_indexOf(const BuiltinFunction *, Scope &scope, CallD bool exists; value = instance->getIndexed(i, &exists); CHECK_EXCEPTION(); - if (exists && RuntimeHelpers::strictEqual(value, searchValue)) { - scope.result = Encode(i); - return; - } + if (exists && RuntimeHelpers::strictEqual(value, searchValue)) + return Encode(i); } } else if (!instance->arrayData()) { - scope.result = Encode(-1); - return; + return Encode(-1); } else { Q_ASSERT(instance->arrayType() == Heap::ArrayData::Simple || instance->arrayType() == Heap::ArrayData::Complex); Heap::SimpleArrayData *sa = instance->d()->arrayData.cast<Heap::SimpleArrayData>(); @@ -696,47 +721,42 @@ void ArrayPrototype::method_indexOf(const BuiltinFunction *, Scope &scope, CallD while (idx < len) { value = sa->data(idx); CHECK_EXCEPTION(); - if (RuntimeHelpers::strictEqual(value, searchValue)) { - scope.result = Encode(idx); - return; - } + if (RuntimeHelpers::strictEqual(value, searchValue)) + return Encode(idx); ++idx; } } - scope.result = Encode(-1); + return Encode(-1); } -void ArrayPrototype::method_lastIndexOf(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_lastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); uint len = instance->getLength(); - if (!len) { - scope.result = Encode(-1); - return; - } + if (!len) + return Encode(-1); ScopedValue searchValue(scope); uint fromIndex = len; - if (callData->argc >= 1) - searchValue = callData->argument(0); + if (argc >= 1) + searchValue = argv[0]; else searchValue = Primitive::undefinedValue(); - if (callData->argc >= 2) { - double f = callData->args[1].toInteger(); + if (argc >= 2) { + double f = argv[1].toInteger(); CHECK_EXCEPTION(); if (f > 0) f = qMin(f, (double)(len - 1)); else if (f < 0) { f = len + f; - if (f < 0) { - scope.result = Encode(-1); - return; - } + if (f < 0) + return Encode(-1); } fromIndex = (uint) f + 1; } @@ -747,281 +767,277 @@ void ArrayPrototype::method_lastIndexOf(const BuiltinFunction *, Scope &scope, C bool exists; v = instance->getIndexed(k, &exists); CHECK_EXCEPTION(); - if (exists && RuntimeHelpers::strictEqual(v, searchValue)) { - scope.result = Encode(k); - return; - } + if (exists && RuntimeHelpers::strictEqual(v, searchValue)) + return Encode(k); } - scope.result = Encode(-1); + return Encode(-1); } -void ArrayPrototype::method_every(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_every(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); uint len = instance->getLength(); - ScopedFunctionObject callback(scope, callData->argument(0)); - if (!callback) + if (!argc || !argv->isFunctionObject()) THROW_TYPE_ERROR(); + const FunctionObject *callback = static_cast<const FunctionObject *>(argv); - ScopedCallData cData(scope, 3); - cData->args[2] = instance; - cData->thisObject = callData->argument(1); - ScopedValue v(scope); + ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); + ScopedValue r(scope); + Value *arguments = scope.alloc(3); bool ok = true; for (uint k = 0; ok && k < len; ++k) { bool exists; - v = instance->getIndexed(k, &exists); + arguments[0] = instance->getIndexed(k, &exists); if (!exists) continue; - cData->args[0] = v; - cData->args[1] = Primitive::fromDouble(k); - callback->call(scope, cData); - ok = scope.result.toBoolean(); + arguments[1] = Primitive::fromDouble(k); + arguments[2] = instance; + r = callback->call(that, arguments, 3); + ok = r->toBoolean(); } - scope.result = Encode(ok); + return Encode(ok); } -void ArrayPrototype::method_some(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_some(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); uint len = instance->getLength(); - ScopedFunctionObject callback(scope, callData->argument(0)); - if (!callback) + if (!argc || !argv->isFunctionObject()) THROW_TYPE_ERROR(); + const FunctionObject *callback = static_cast<const FunctionObject *>(argv); - ScopedCallData cData(scope, 3); - cData->thisObject = callData->argument(1); - cData->args[2] = instance; - ScopedValue v(scope); + ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); + ScopedValue result(scope); + Value *arguments = scope.alloc(3); for (uint k = 0; k < len; ++k) { bool exists; - v = instance->getIndexed(k, &exists); + arguments[0] = instance->getIndexed(k, &exists); if (!exists) continue; - cData->args[0] = v; - cData->args[1] = Primitive::fromDouble(k); - callback->call(scope, cData); - if (scope.result.toBoolean()) { - scope.result = Encode(true); - return; - } + arguments[1] = Primitive::fromDouble(k); + arguments[2] = instance; + result = callback->call(that, arguments, 3); + if (result->toBoolean()) + return Encode(true); } - scope.result = Encode(false); + return Encode(false); } -void ArrayPrototype::method_forEach(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_forEach(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); uint len = instance->getLength(); - ScopedFunctionObject callback(scope, callData->argument(0)); - if (!callback) + if (!argc || !argv->isFunctionObject()) THROW_TYPE_ERROR(); + const FunctionObject *callback = static_cast<const FunctionObject *>(argv); - ScopedCallData cData(scope, 3); - cData->thisObject = callData->argument(1); - cData->args[2] = instance; + ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); + Value *arguments = scope.alloc(3); - ScopedValue v(scope); for (uint k = 0; k < len; ++k) { bool exists; - v = instance->getIndexed(k, &exists); + arguments[0] = instance->getIndexed(k, &exists); if (!exists) continue; - cData->args[0] = v; - cData->args[1] = Primitive::fromDouble(k); - callback->call(scope, cData); + arguments[1] = Primitive::fromDouble(k); + arguments[2] = instance; + callback->call(that, arguments, 3); } RETURN_UNDEFINED(); } -void ArrayPrototype::method_map(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_map(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); uint len = instance->getLength(); - ScopedFunctionObject callback(scope, callData->argument(0)); - if (!callback) + if (!argc || !argv->isFunctionObject()) THROW_TYPE_ERROR(); + const FunctionObject *callback = static_cast<const FunctionObject *>(argv); ScopedArrayObject a(scope, scope.engine->newArrayObject()); a->arrayReserve(len); a->setArrayLengthUnchecked(len); - ScopedCallData cData(scope, 3); - cData->thisObject = callData->argument(1); - cData->args[2] = instance; - ScopedValue v(scope); + ScopedValue mapped(scope); + ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); + Value *arguments = scope.alloc(3); + for (uint k = 0; k < len; ++k) { bool exists; - v = instance->getIndexed(k, &exists); + arguments[0] = instance->getIndexed(k, &exists); if (!exists) continue; - cData->args[0] = v; - cData->args[1] = Primitive::fromDouble(k); - callback->call(scope, cData); - a->arraySet(k, scope.result); + arguments[1] = Primitive::fromDouble(k); + arguments[2] = instance; + mapped = callback->call(that, arguments, 3); + a->arraySet(k, mapped); } - scope.result = a.asReturnedValue(); + return a.asReturnedValue(); } -void ArrayPrototype::method_filter(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_filter(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); uint len = instance->getLength(); - ScopedFunctionObject callback(scope, callData->argument(0)); - if (!callback) + if (!argc || !argv->isFunctionObject()) THROW_TYPE_ERROR(); + const FunctionObject *callback = static_cast<const FunctionObject *>(argv); ScopedArrayObject a(scope, scope.engine->newArrayObject()); a->arrayReserve(len); - ScopedCallData cData(scope, 3); - cData->thisObject = callData->argument(1); - cData->args[2] = instance; - - ScopedValue v(scope); + ScopedValue selected(scope); + ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); + Value *arguments = scope.alloc(3); uint to = 0; for (uint k = 0; k < len; ++k) { bool exists; - v = instance->getIndexed(k, &exists); + arguments[0] = instance->getIndexed(k, &exists); if (!exists) continue; - cData->args[0] = v; - cData->args[1] = Primitive::fromDouble(k); - callback->call(scope, cData); - if (scope.result.toBoolean()) { - a->arraySet(to, v); + arguments[1] = Primitive::fromDouble(k); + arguments[2] = instance; + selected = callback->call(that, arguments, 3); + if (selected->toBoolean()) { + a->arraySet(to, arguments[0]); ++to; } } - scope.result = a.asReturnedValue(); + return a.asReturnedValue(); } -void ArrayPrototype::method_reduce(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_reduce(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); uint len = instance->getLength(); - ScopedFunctionObject callback(scope, callData->argument(0)); - if (!callback) + if (!argc || !argv->isFunctionObject()) THROW_TYPE_ERROR(); + const FunctionObject *callback = static_cast<const FunctionObject *>(argv); uint k = 0; + ScopedValue acc(scope); ScopedValue v(scope); - if (callData->argc > 1) { - scope.result = callData->argument(1); + if (argc > 1) { + acc = argv[1]; } else { bool kPresent = false; while (k < len && !kPresent) { v = instance->getIndexed(k, &kPresent); if (kPresent) - scope.result = v; + acc = v; ++k; } if (!kPresent) THROW_TYPE_ERROR(); } - ScopedCallData cData(scope, 4); - cData->thisObject = Primitive::undefinedValue(); - cData->args[0] = scope.result; - cData->args[3] = instance; + Value *arguments = scope.alloc(4); while (k < len) { bool kPresent; v = instance->getIndexed(k, &kPresent); if (kPresent) { - cData->args[0] = scope.result; - cData->args[1] = v; - cData->args[2] = Primitive::fromDouble(k); - callback->call(scope, cData); + arguments[0] = acc; + arguments[1] = v; + arguments[2] = Primitive::fromDouble(k); + arguments[3] = instance; + acc = callback->call(nullptr, arguments, 4); } ++k; } + return acc->asReturnedValue(); } -void ArrayPrototype::method_reduceRight(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_reduceRight(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); uint len = instance->getLength(); - ScopedFunctionObject callback(scope, callData->argument(0)); - if (!callback) + if (!argc || !argv->isFunctionObject()) THROW_TYPE_ERROR(); + const FunctionObject *callback = static_cast<const FunctionObject *>(argv); if (len == 0) { - if (callData->argc == 1) + if (argc == 1) THROW_TYPE_ERROR(); - scope.result = callData->argument(1); - return; + return argv[1].asReturnedValue(); } uint k = len; + ScopedValue acc(scope); ScopedValue v(scope); - if (callData->argc > 1) { - scope.result = callData->argument(1); + if (argc > 1) { + acc = argv[1]; } else { bool kPresent = false; while (k > 0 && !kPresent) { v = instance->getIndexed(k - 1, &kPresent); if (kPresent) - scope.result = v; + acc = v; --k; } if (!kPresent) THROW_TYPE_ERROR(); } - ScopedCallData cData(scope, 4); - cData->thisObject = Primitive::undefinedValue(); - cData->args[3] = instance; + Value *arguments = scope.alloc(4); while (k > 0) { bool kPresent; v = instance->getIndexed(k - 1, &kPresent); if (kPresent) { - cData->args[0] = scope.result; - cData->args[1] = v; - cData->args[2] = Primitive::fromDouble(k - 1); - callback->call(scope, cData); + arguments[0] = acc; + arguments[1] = v; + arguments[2] = Primitive::fromDouble(k - 1); + arguments[3] = instance; + acc = callback->call(nullptr, arguments, 4); } --k; } - scope.result = scope.result.asReturnedValue(); + return acc->asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4arrayobject_p.h b/src/qml/jsruntime/qv4arrayobject_p.h index 689752433b..3825a600a2 100644 --- a/src/qml/jsruntime/qv4arrayobject_p.h +++ b/src/qml/jsruntime/qv4arrayobject_p.h @@ -70,38 +70,38 @@ struct ArrayCtor: FunctionObject { V4_OBJECT2(ArrayCtor, FunctionObject) - static void construct(const Managed *m, Scope &scope, CallData *callData); - static void call(const Managed *that, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct ArrayPrototype: ArrayObject { void init(ExecutionEngine *engine, Object *ctor); - static void method_isArray(const BuiltinFunction *, Scope &, CallData *callData); - static void method_toString(const BuiltinFunction *, Scope &, CallData *callData); - static void method_toLocaleString(const BuiltinFunction *builtin, Scope &, CallData *callData); - static void method_concat(const BuiltinFunction *, Scope &, CallData *callData); - static void method_find(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_findIndex(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_join(const BuiltinFunction *, Scope &, CallData *callData); - static void method_pop(const BuiltinFunction *, Scope &, CallData *callData); - static void method_push(const BuiltinFunction *, Scope &, CallData *callData); - static void method_reverse(const BuiltinFunction *, Scope &, CallData *callData); - static void method_shift(const BuiltinFunction *, Scope &, CallData *callData); - static void method_slice(const BuiltinFunction *, Scope &, CallData *callData); - static void method_sort(const BuiltinFunction *, Scope &, CallData *callData); - static void method_splice(const BuiltinFunction *, Scope &, CallData *callData); - static void method_unshift(const BuiltinFunction *, Scope &, CallData *callData); - static void method_indexOf(const BuiltinFunction *, Scope &, CallData *callData); - static void method_lastIndexOf(const BuiltinFunction *, Scope &, CallData *callData); - static void method_every(const BuiltinFunction *, Scope &, CallData *callData); - static void method_some(const BuiltinFunction *, Scope &, CallData *callData); - static void method_forEach(const BuiltinFunction *, Scope &, CallData *callData); - static void method_map(const BuiltinFunction *, Scope &, CallData *callData); - static void method_filter(const BuiltinFunction *, Scope &, CallData *callData); - static void method_reduce(const BuiltinFunction *, Scope &, CallData *callData); - static void method_reduceRight(const BuiltinFunction *, Scope &, CallData *callData); + static ReturnedValue method_isArray(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toLocaleString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_concat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_find(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_findIndex(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_join(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_pop(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_push(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_reverse(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_shift(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_slice(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_sort(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_splice(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_unshift(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_indexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_lastIndexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_every(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_some(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_forEach(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_map(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_filter(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_reduce(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_reduceRight(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp index c8e9ebb2dd..eb83f902db 100644 --- a/src/qml/jsruntime/qv4booleanobject.cpp +++ b/src/qml/jsruntime/qv4booleanobject.cpp @@ -50,16 +50,16 @@ void Heap::BooleanCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Boolean")); } -void BooleanCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue BooleanCtor::callAsConstructor(const FunctionObject *that, const Value *argv, int argc) { - bool n = callData->argc ? callData->args[0].toBoolean() : false; - scope.result = Encode(scope.engine->newBooleanObject(n)); + bool n = argc ? argv[0].toBoolean() : false; + return Encode(that->engine()->newBooleanObject(n)); } -void BooleanCtor::call(const Managed *, Scope &scope, CallData *callData) +ReturnedValue BooleanCtor::call(const FunctionObject *, const Value *, const Value *argv, int argc) { - bool value = callData->argc ? callData->args[0].toBoolean() : 0; - scope.result = Encode(value); + bool value = argc ? argv[0].toBoolean() : 0; + return Encode(value); } void BooleanPrototype::init(ExecutionEngine *engine, Object *ctor) @@ -73,31 +73,39 @@ void BooleanPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(engine->id_valueOf(), method_valueOf); } -void BooleanPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) +static bool value(const Value *thisObject, bool *exception) { - bool result; - if (callData->thisObject.isBoolean()) { - result = callData->thisObject.booleanValue(); + *exception = false; + if (thisObject->isBoolean()) { + return thisObject->booleanValue(); } else { - const BooleanObject *thisObject = callData->thisObject.as<BooleanObject>(); - if (!thisObject) - THROW_TYPE_ERROR(); - result = thisObject->value(); + const BooleanObject *that = thisObject->as<BooleanObject>(); + if (that) + return that->value(); } + *exception = true; + return false; +} + +ReturnedValue BooleanPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + bool exception; + bool result = ::value(thisObject, &exception); + ExecutionEngine *v4 = b->engine(); + if (exception) + return v4->throwTypeError(); - scope.result = result ? scope.engine->id_true() : scope.engine->id_false(); + return Encode(result ? v4->id_true() : v4->id_false()); } -void BooleanPrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue BooleanPrototype::method_valueOf(const FunctionObject *b, const Value *thisObject, const Value *, int) { - if (callData->thisObject.isBoolean()) { - scope.result = callData->thisObject.asReturnedValue(); - return; + bool exception; + bool result = ::value(thisObject, &exception); + if (exception) { + ExecutionEngine *v4 = b->engine(); + return v4->throwTypeError(); } - const BooleanObject *thisObject = callData->thisObject.as<BooleanObject>(); - if (!thisObject) - THROW_TYPE_ERROR(); - - scope.result = Encode(thisObject->value()); + return Encode(result); } diff --git a/src/qml/jsruntime/qv4booleanobject_p.h b/src/qml/jsruntime/qv4booleanobject_p.h index ca2cf7d17a..3cf09b2667 100644 --- a/src/qml/jsruntime/qv4booleanobject_p.h +++ b/src/qml/jsruntime/qv4booleanobject_p.h @@ -70,8 +70,8 @@ struct BooleanCtor: FunctionObject { V4_OBJECT2(BooleanCtor, FunctionObject) - static void construct(const Managed *, Scope &scope, CallData *callData); - static void call(const Managed *that, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct BooleanPrototype: BooleanObject @@ -79,8 +79,8 @@ struct BooleanPrototype: BooleanObject V4_PROTOTYPE(objectPrototype) void init(ExecutionEngine *engine, Object *ctor); - static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_valueOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 6807c835b0..00d816fe91 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -48,59 +48,52 @@ #include "qv4errorobject_p.h" #include "qv4string_p.h" #include "qv4qmlcontext_p.h" -#include "qv4profiling_p.h" -#include <private/qqmljavascriptexpression_p.h> using namespace QV4; DEFINE_MANAGED_VTABLE(ExecutionContext); -DEFINE_MANAGED_VTABLE(SimpleCallContext); DEFINE_MANAGED_VTABLE(CallContext); -DEFINE_MANAGED_VTABLE(WithContext); DEFINE_MANAGED_VTABLE(CatchContext); -DEFINE_MANAGED_VTABLE(GlobalContext); -Heap::CallContext *ExecutionContext::newCallContext(Function *function, CallData *callData) +Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame) { - uint localsAndFormals = function->compiledFunction->nLocals + sizeof(CallData)/sizeof(Value) - 1 + qMax(static_cast<uint>(callData->argc), function->nFormals); - size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * (localsAndFormals); - - ExecutionEngine *v4 = engine(); - Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory); - c->init(Heap::ExecutionContext::Type_CallContext); + Function *function = frame->v4Function; + Heap::ExecutionContext *outer = static_cast<Heap::ExecutionContext *>(frame->context()->m()); - c->v4Function = function; + uint nFormals = qMax(static_cast<uint>(frame->originalArgumentsCount), function->nFormals); + uint localsAndFormals = function->compiledFunction->nLocals + nFormals; + size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * (localsAndFormals); - c->strictMode = function->isStrict(); - c->outer.set(v4, this->d()); + ExecutionEngine *v4 = outer->internalClass->engine; + Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory, function->internalClass); + c->init(); - c->compilationUnit = function->compilationUnit; - c->lookups = function->compilationUnit->runtimeLookups; - c->constantTable = function->compilationUnit->constants; + c->outer.set(v4, outer); + c->function.set(v4, static_cast<Heap::FunctionObject *>(frame->jsFrame->function.m())); const CompiledData::Function *compiledFunction = function->compiledFunction; uint nLocals = compiledFunction->nLocals; c->locals.size = nLocals; c->locals.alloc = localsAndFormals; -#if QT_POINTER_SIZE == 8 - // memory allocated from the JS heap is 0 initialized, so skip the std::fill() below + // memory allocated from the JS heap is 0 initialized, so check if undefined is 0 Q_ASSERT(Primitive::undefinedValue().asReturnedValue() == 0); -#else - if (nLocals) - std::fill(c->locals.values, c->locals.values + nLocals, Primitive::undefinedValue()); -#endif - c->callData = reinterpret_cast<CallData *>(c->locals.values + nLocals); - ::memcpy(c->callData, callData, sizeof(CallData) - sizeof(Value) + static_cast<uint>(callData->argc) * sizeof(Value)); - if (callData->argc < static_cast<int>(compiledFunction->nFormals)) - std::fill(c->callData->args + c->callData->argc, c->callData->args + compiledFunction->nFormals, Primitive::undefinedValue()); + Value *args = c->locals.values + nLocals; + ::memcpy(args, frame->originalArguments, frame->originalArgumentsCount * sizeof(Value)); + c->nArgs = frame->originalArgumentsCount; + for (uint i = frame->originalArgumentsCount; i < function->nFormals; ++i) + args[i] = Encode::undefined(); return c; } -Heap::WithContext *ExecutionContext::newWithContext(Heap::Object *with) +Heap::ExecutionContext *ExecutionContext::newWithContext(Heap::Object *with) { - return engine()->memoryManager->alloc<WithContext>(d(), with); + Heap::ExecutionContext *c = engine()->memoryManager->alloc<ExecutionContext>(Heap::ExecutionContext::Type_WithContext); + c->outer.set(engine(), d()); + c->activation.set(engine(), with); + + return c; } Heap::CatchContext *ExecutionContext::newCatchContext(Heap::String *exceptionVarName, ReturnedValue exceptionValue) @@ -120,25 +113,23 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable) while (ctx) { switch (ctx->d()->type) { case Heap::ExecutionContext::Type_CallContext: - case Heap::ExecutionContext::Type_SimpleCallContext: { - Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d()); if (!activation) { + Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); if (!c->activation) c->activation.set(scope.engine, scope.engine->newObject()); activation = c->activation; } break; - } case Heap::ExecutionContext::Type_QmlContext: { // this is ugly, as it overrides the inner callcontext, but has to stay as long // as bindings still get their own callcontext - Heap::QmlContext *qml = static_cast<Heap::QmlContext *>(ctx->d()); - activation = qml->qml; + activation = ctx->d()->activation; break; } case Heap::ExecutionContext::Type_GlobalContext: { + Q_ASSERT(scope.engine->globalObject->d() == ctx->d()->activation); if (!activation) - activation = scope.engine->globalObject; + activation = ctx->d()->activation; break; } default: @@ -155,99 +146,46 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable) activation->__defineOwnProperty__(scope.engine, name, desc, attrs); } -void Heap::GlobalContext::init(ExecutionEngine *eng) -{ - Heap::ExecutionContext::init(Heap::ExecutionContext::Type_GlobalContext); - global.set(eng, eng->globalObject->d()); -} - void Heap::CatchContext::init(ExecutionContext *outerContext, String *exceptionVarName, const Value &exceptionValue) { Heap::ExecutionContext::init(Heap::ExecutionContext::Type_CatchContext); outer.set(internalClass->engine, outerContext); - strictMode = outer->strictMode; - callData = outer->callData; - lookups = outer->lookups; - constantTable = outer->constantTable; - compilationUnit = outer->compilationUnit; this->exceptionVarName.set(internalClass->engine, exceptionVarName); this->exceptionValue.set(internalClass->engine, exceptionValue); } -void Heap::WithContext::init(ExecutionContext *outerContext, Object *with) -{ - Heap::ExecutionContext::init(Heap::ExecutionContext::Type_WithContext); - outer.set(internalClass->engine, outerContext); - callData = outer->callData; - lookups = outer->lookups; - constantTable = outer->constantTable; - compilationUnit = outer->compilationUnit; - - withObject.set(internalClass->engine, with); -} - -Identifier * const *SimpleCallContext::formals() const -{ - return d()->v4Function ? d()->v4Function->internalClass->nameMap.constData() : 0; -} - -unsigned int SimpleCallContext::formalCount() const -{ - return d()->v4Function ? d()->v4Function->nFormals : 0; -} - -Identifier * const *SimpleCallContext::variables() const -{ - return d()->v4Function ? d()->v4Function->internalClass->nameMap.constData() + d()->v4Function->nFormals : 0; -} - -unsigned int SimpleCallContext::variableCount() const -{ - return d()->v4Function ? d()->v4Function->compiledFunction->nLocals : 0; -} - - - bool ExecutionContext::deleteProperty(String *name) { name->makeIdentifier(); Identifier *id = name->identifier(); - Scope scope(this); - ScopedContext ctx(scope, this); - for (; ctx; ctx = ctx->d()->outer) { - switch (ctx->d()->type) { + Heap::ExecutionContext *ctx = d(); + for (; ctx; ctx = ctx->outer) { + switch (ctx->type) { case Heap::ExecutionContext::Type_CatchContext: { - Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); + Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx); if (c->exceptionVarName->isEqualTo(name->d())) return false; break; } - case Heap::ExecutionContext::Type_WithContext: { - ScopedObject withObject(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject); - if (withObject->hasProperty(name)) - return withObject->deleteProperty(name); - break; - } - case Heap::ExecutionContext::Type_GlobalContext: { - ScopedObject global(scope, static_cast<Heap::GlobalContext *>(ctx->d())->global); - if (global->hasProperty(name)) - return global->deleteProperty(name); - break; - } - case Heap::ExecutionContext::Type_CallContext: - Q_FALLTHROUGH(); - case Heap::ExecutionContext::Type_SimpleCallContext: { - Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d()); - uint index = c->v4Function->internalClass->find(id); + case Heap::ExecutionContext::Type_CallContext: { + Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx); + uint index = c->internalClass->find(id); if (index < UINT_MAX) // ### throw in strict mode? return false; - ScopedObject qml(scope, c->activation); - if (qml && qml->hasProperty(name)) - return qml->deleteProperty(name); + Q_FALLTHROUGH(); + } + case Heap::ExecutionContext::Type_WithContext: + case Heap::ExecutionContext::Type_GlobalContext: { + if (ctx->activation) { + Scope scope(this); + ScopedObject object(scope, ctx->activation); + if (object && object->hasProperty(name)) + return object->deleteProperty(name); + } break; } case Heap::ExecutionContext::Type_QmlContext: @@ -256,305 +194,165 @@ bool ExecutionContext::deleteProperty(String *name) } } - if (d()->strictMode) - engine()->throwSyntaxError(QStringLiteral("Can't delete property %1").arg(name->toQString())); - return true; -} - -// Do a standard call with this execution context as the outer scope -void ExecutionContext::call(Scope &scope, CallData *callData, Function *function, const FunctionObject *f) -{ - ExecutionContextSaver ctxSaver(scope); - - Scoped<CallContext> ctx(scope, newCallContext(function, callData)); - if (f) - ctx->d()->function.set(scope.engine, f->d()); - scope.engine->pushContext(ctx); - - scope.result = Q_V4_PROFILE(scope.engine, function); - - if (function->hasQmlDependencies) - QQmlPropertyCapture::registerQmlDependencies(function->compiledFunction, scope); + return !engine()->currentStackFrame->v4Function->isStrict(); } -// Do a simple, fast call with this execution context as the outer scope -void QV4::ExecutionContext::simpleCall(Scope &scope, CallData *callData, Function *function) -{ - Q_ASSERT(function->canUseSimpleFunction()); - - ExecutionContextSaver ctxSaver(scope); - - SimpleCallContext::Data *ctx = scope.engine->memoryManager->allocSimpleCallContext(); - - ctx->strictMode = function->isStrict(); - ctx->callData = callData; - ctx->v4Function = function; - ctx->compilationUnit = function->compilationUnit; - ctx->lookups = function->compilationUnit->runtimeLookups; - ctx->constantTable = function->compilationUnit->constants; - ctx->outer.set(scope.engine, this->d()); - for (int i = callData->argc; i < (int)function->nFormals; ++i) - callData->args[i] = Encode::undefined(); - - scope.engine->pushContext(ctx); - Q_ASSERT(scope.engine->current == ctx); - - scope.result = Q_V4_PROFILE(scope.engine, function); - - if (function->hasQmlDependencies) - QQmlPropertyCapture::registerQmlDependencies(function->compiledFunction, scope); - scope.engine->memoryManager->freeSimpleCallContext(); -} - -void ExecutionContext::setProperty(String *name, const Value &value) +ExecutionContext::Error ExecutionContext::setProperty(String *name, const Value &value) { name->makeIdentifier(); Identifier *id = name->identifier(); - Scope scope(this); - ScopedContext ctx(scope, this); - ScopedObject activation(scope); + QV4::ExecutionEngine *v4 = engine(); + Heap::ExecutionContext *ctx = d(); - for (; ctx; ctx = ctx->d()->outer) { - activation = (Object *)0; - switch (ctx->d()->type) { + for (; ctx; ctx = ctx->outer) { + switch (ctx->type) { case Heap::ExecutionContext::Type_CatchContext: { - Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); + Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx); if (c->exceptionVarName->isEqualTo(name->d())) { - c->exceptionValue.set(scope.engine, value); - return; + c->exceptionValue.set(v4, value); + return NoError; } break; } case Heap::ExecutionContext::Type_WithContext: { - ScopedObject w(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject); + Scope scope(v4); + ScopedObject w(scope, ctx->activation); if (w->hasProperty(name)) { - w->put(name, value); - return; + if (!w->put(name, value)) + return TypeError; + return NoError; } break; } - case Heap::ExecutionContext::Type_GlobalContext: { - activation = static_cast<Heap::GlobalContext *>(ctx->d())->global; - break; + case Heap::ExecutionContext::Type_CallContext: { + Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx); + uint index = c->internalClass->find(id); + if (index < UINT_MAX) { + static_cast<Heap::CallContext *>(c)->locals.set(v4, index, value); + return NoError; + } } - case Heap::ExecutionContext::Type_CallContext: - case Heap::ExecutionContext::Type_SimpleCallContext: { - Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d()); - if (c->v4Function) { - uint index = c->v4Function->internalClass->find(id); - if (index < UINT_MAX) { - if (index < c->v4Function->nFormals) { - c->callData->args[c->v4Function->nFormals - index - 1] = value; - } else { - Q_ASSERT(c->type == Heap::ExecutionContext::Type_CallContext); - index -= c->v4Function->nFormals; - static_cast<Heap::CallContext *>(c)->locals.set(scope.engine, index, value); - } - return; + Q_FALLTHROUGH(); + case Heap::ExecutionContext::Type_GlobalContext: + if (ctx->activation) { + uint member = ctx->activation->internalClass->find(id); + if (member < UINT_MAX) { + Scope scope(v4); + ScopedObject a(scope, ctx->activation); + if (!a->putValue(member, value)) + return TypeError; + return NoError; } } - activation = c->activation; break; - } case Heap::ExecutionContext::Type_QmlContext: { - activation = static_cast<Heap::QmlContext *>(ctx->d())->qml; - activation->put(name, value); - return; + Scope scope(v4); + ScopedObject activation(scope, ctx->activation); + if (!activation->put(name, value)) + return TypeError; + return NoError; } } - if (activation) { - uint member = activation->internalClass()->find(id); - if (member < UINT_MAX) { - activation->putValue(member, value); - return; - } - } } - if (d()->strictMode || name->equals(engine()->id_this())) { - ScopedValue n(scope, name->asReturnedValue()); - engine()->throwReferenceError(n); - return; - } - engine()->globalObject->put(name, value); + return RangeError; } ReturnedValue ExecutionContext::getProperty(String *name) { - Scope scope(this); - ScopedValue v(scope); name->makeIdentifier(); - if (name->equals(engine()->id_this())) - return thisObject().asReturnedValue(); - - ScopedContext ctx(scope, this); - for (; ctx; ctx = ctx->d()->outer) { - switch (ctx->d()->type) { + Heap::ExecutionContext *ctx = d(); + for (; ctx; ctx = ctx->outer) { + switch (ctx->type) { case Heap::ExecutionContext::Type_CatchContext: { - Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); + Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx); if (c->exceptionVarName->isEqualTo(name->d())) return c->exceptionValue.asReturnedValue(); break; } - case Heap::ExecutionContext::Type_WithContext: { - ScopedObject w(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject); - bool hasProperty = false; - v = w->get(name, &hasProperty); - if (hasProperty) { - return v->asReturnedValue(); - } - break; - } - case Heap::ExecutionContext::Type_GlobalContext: { - ScopedObject global(scope, static_cast<Heap::GlobalContext *>(ctx->d())->global); - bool hasProperty = false; - v = global->get(name, &hasProperty); - if (hasProperty) - return v->asReturnedValue(); - break; - } - case Heap::ExecutionContext::Type_CallContext: - Q_FALLTHROUGH(); - case Heap::ExecutionContext::Type_SimpleCallContext: { - Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d()); - - name->makeIdentifier(); + case Heap::ExecutionContext::Type_CallContext: { + Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx); Identifier *id = name->identifier(); - uint index = c->v4Function->internalClass->find(id); - if (index < UINT_MAX) { - if (index < c->v4Function->nFormals) - return c->callData->args[c->v4Function->nFormals - index - 1].asReturnedValue(); - if (c->type == Heap::ExecutionContext::Type_CallContext) - return static_cast<Heap::CallContext *>(c)->locals[index - c->v4Function->nFormals].asReturnedValue(); - } - ScopedObject activation(scope, c->activation); - if (activation) { + uint index = c->internalClass->find(id); + if (index < UINT_MAX) + return c->locals[index].asReturnedValue(); + Q_FALLTHROUGH(); + } + case Heap::ExecutionContext::Type_WithContext: + case Heap::ExecutionContext::Type_GlobalContext: + case Heap::ExecutionContext::Type_QmlContext: { + if (ctx->activation) { + Scope scope(this); + ScopedObject activation(scope, ctx->activation); bool hasProperty = false; - v = activation->get(name, &hasProperty); + ReturnedValue v = activation->get(name, &hasProperty); if (hasProperty) - return v->asReturnedValue(); - } - - if (c->v4Function->isNamedExpression() && c->type == Heap::ExecutionContext::Type_CallContext) { - if (name->equals(ScopedString(scope, c->v4Function->name()))) - if (auto func = static_cast<Heap::CallContext *>(c)->function) - return func->asReturnedValue(); + return v; } break; } - case Heap::ExecutionContext::Type_QmlContext: { - ScopedObject qml(scope, static_cast<Heap::QmlContext *>(ctx->d())->qml); - bool hasProperty = false; - v = qml->get(name, &hasProperty); - if (hasProperty) - return v->asReturnedValue(); - break; - } } } - ScopedValue n(scope, name); - return engine()->throwReferenceError(n); + return engine()->throwReferenceError(*name); } ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) { - Scope scope(this); - ScopedValue v(scope); base->setM(0); name->makeIdentifier(); - if (name->equals(engine()->id_this())) - return thisObject().asReturnedValue(); - - ScopedContext ctx(scope, this); - for (; ctx; ctx = ctx->d()->outer) { - switch (ctx->d()->type) { + Heap::ExecutionContext *ctx = d(); + for (; ctx; ctx = ctx->outer) { + switch (ctx->type) { case Heap::ExecutionContext::Type_CatchContext: { - Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); + Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx); if (c->exceptionVarName->isEqualTo(name->d())) return c->exceptionValue.asReturnedValue(); break; } - case Heap::ExecutionContext::Type_WithContext: { - ScopedObject w(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject); - bool hasProperty = false; - v = w->get(name, &hasProperty); - if (hasProperty) { - base->setM(w->d()); - return v->asReturnedValue(); - } - break; - } - case Heap::ExecutionContext::Type_GlobalContext: { - ScopedObject global(scope, static_cast<Heap::GlobalContext *>(ctx->d())->global); - bool hasProperty = false; - v = global->get(name, &hasProperty); - if (hasProperty) - return v->asReturnedValue(); - break; - } - case Heap::ExecutionContext::Type_CallContext: - Q_FALLTHROUGH(); - case Heap::ExecutionContext::Type_SimpleCallContext: { - Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d()); - + case Heap::ExecutionContext::Type_CallContext: { + Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx); name->makeIdentifier(); Identifier *id = name->identifier(); - uint index = c->v4Function->internalClass->find(id); - if (index < UINT_MAX) { - if (index < c->v4Function->nFormals) - return c->callData->args[c->v4Function->nFormals - index - 1].asReturnedValue(); - if (c->type == Heap::ExecutionContext::Type_CallContext) - return static_cast<Heap::CallContext *>(c)->locals[index - c->v4Function->nFormals].asReturnedValue(); - } - ScopedObject activation(scope, c->activation); - if (activation) { + uint index = c->internalClass->find(id); + if (index < UINT_MAX) + return c->locals[index].asReturnedValue(); + Q_FALLTHROUGH(); + } + case Heap::ExecutionContext::Type_GlobalContext: { + if (ctx->activation) { + Scope scope(this); + ScopedObject activation(scope, ctx->activation); bool hasProperty = false; - v = activation->get(name, &hasProperty); + ReturnedValue v = activation->get(name, &hasProperty); if (hasProperty) - return v->asReturnedValue(); - } - - if (c->v4Function->isNamedExpression() && c->type == Heap::ExecutionContext::Type_CallContext) { - if (name->equals(ScopedString(scope, c->v4Function->name()))) - if (auto func = static_cast<Heap::CallContext *>(c)->function) - return func->asReturnedValue(); + return v; } break; } + case Heap::ExecutionContext::Type_WithContext: case Heap::ExecutionContext::Type_QmlContext: { - ScopedObject qml(scope, static_cast<Heap::QmlContext *>(ctx->d())->qml); + Scope scope(this); + ScopedObject o(scope, ctx->activation); bool hasProperty = false; - v = qml->get(name, &hasProperty); + ReturnedValue v = o->get(name, &hasProperty); if (hasProperty) { - base->setM(qml->d()); - return v->asReturnedValue(); + base->setM(o->d()); + return v; } break; } } } - ScopedValue n(scope, name); - return engine()->throwReferenceError(n); + return engine()->throwReferenceError(*name); } -Function *ExecutionContext::getFunction() const +void Heap::CallContext::setArg(uint index, Value v) { - Scope scope(engine()); - ScopedContext it(scope, this->d()); - for (; it; it = it->d()->outer) { - if (const SimpleCallContext *callCtx = it->asSimpleCallContext()) - return callCtx->d()->v4Function; - else if (it->asCatchContext() || it->asWithContext()) - continue; // look in the parent context for a FunctionObject - else - break; - } - - return 0; + locals.set(internalClass->engine, locals.size + index, v); } diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index a854c324d0..4efd0bc899 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -68,59 +68,66 @@ struct Function; struct Function; struct Identifier; struct CallContext; -struct SimpleCallContext; struct CatchContext; -struct WithContext; struct QmlContext; struct QQmlContextWrapper; -// Attention: Make sure that this structure is the same size on 32-bit and 64-bit -// architecture or you'll have to change the JIT code. struct CallData { - // below is to be compatible with Value. Initialize tag to 0 -#if Q_BYTE_ORDER != Q_LITTLE_ENDIAN - uint tag; -#endif - int argc; -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - uint tag; -#endif + enum Offsets { + Function = 0, + Context = 1, + Accumulator = 2, + This = 3, + Argc = 4 + }; + + Value function; + Value context; + Value accumulator; + Value thisObject; + Value _argc; + + int argc() const { + Q_ASSERT(_argc.isInteger()); + return _argc.int_32(); + } + + void setArgc(int argc) { + Q_ASSERT(argc >= 0); + _argc.setInt_32(argc); + } + inline ReturnedValue argument(int i) const { - return i < argc ? args[i].asReturnedValue() : Primitive::undefinedValue().asReturnedValue(); + return i < argc() ? args[i].asReturnedValue() : Primitive::undefinedValue().asReturnedValue(); } - Value thisObject; Value args[1]; + + static Q_DECL_CONSTEXPR int HeaderSize() { return offsetof(CallData, args) / sizeof(QV4::Value); } }; Q_STATIC_ASSERT(std::is_standard_layout<CallData>::value); -Q_STATIC_ASSERT(offsetof(CallData, thisObject) == 8); -Q_STATIC_ASSERT(offsetof(CallData, args) == 16); +Q_STATIC_ASSERT(offsetof(CallData, thisObject) == CallData::This*sizeof(Value)); +Q_STATIC_ASSERT(offsetof(CallData, args) == 5*sizeof(Value)); namespace Heap { struct QmlContext; #define ExecutionContextMembers(class, Member) \ - Member(class, NoMark, CallData *, callData) \ Member(class, Pointer, ExecutionContext *, outer) \ - Member(class, NoMark, Lookup *, lookups) \ - Member(class, NoMark, const QV4::Value *, constantTable) \ - Member(class, NoMark, CompiledData::CompilationUnitBase *, compilationUnit) \ - Member(class, NoMark, int, lineNumber) // as member of non-pointer size this has to come last to preserve the ability to - // translate offsetof of it between 64-bit and 32-bit. + Member(class, Pointer, Object *, activation) DECLARE_HEAP_OBJECT(ExecutionContext, Base) { - DECLARE_MARK_TABLE(ExecutionContext); + DECLARE_MARKOBJECTS(ExecutionContext); enum ContextType { Type_GlobalContext = 0x1, Type_CatchContext = 0x2, Type_WithContext = 0x3, Type_QmlContext = 0x4, - Type_SimpleCallContext = 0x5, - Type_CallContext = 0x6 + Type_CallContext = 0x5 }; void init(ContextType t) @@ -128,105 +135,63 @@ DECLARE_HEAP_OBJECT(ExecutionContext, Base) { Base::init(); type = t; - lineNumber = -1; } - quint8 type; - bool strictMode : 8; + quint32 type : 8; + quint32 nArgs : 24; #if QT_POINTER_SIZE == 8 - quint8 padding_[6]; -#else - quint8 padding_[2]; + quint8 padding_[4]; #endif }; V4_ASSERT_IS_TRIVIAL(ExecutionContext) Q_STATIC_ASSERT(sizeof(ExecutionContext) == sizeof(Base) + sizeof(ExecutionContextData) + QT_POINTER_SIZE); Q_STATIC_ASSERT(std::is_standard_layout<ExecutionContextData>::value); -Q_STATIC_ASSERT(offsetof(ExecutionContextData, callData) == 0); -Q_STATIC_ASSERT(offsetof(ExecutionContextData, outer) == offsetof(ExecutionContextData, callData) + QT_POINTER_SIZE); -Q_STATIC_ASSERT(offsetof(ExecutionContextData, lookups) == offsetof(ExecutionContextData, outer) + QT_POINTER_SIZE); -Q_STATIC_ASSERT(offsetof(ExecutionContextData, constantTable) == offsetof(ExecutionContextData, lookups) + QT_POINTER_SIZE); -Q_STATIC_ASSERT(offsetof(ExecutionContextData, compilationUnit) == offsetof(ExecutionContextData, constantTable) + QT_POINTER_SIZE); -Q_STATIC_ASSERT(offsetof(ExecutionContextData, lineNumber) == offsetof(ExecutionContextData, compilationUnit) + QT_POINTER_SIZE); - -#define SimpleCallContextMembers(class, Member) \ - Member(class, Pointer, Object *, activation) \ - Member(class, NoMark, QV4::Function *, v4Function) - -DECLARE_HEAP_OBJECT(SimpleCallContext, ExecutionContext) { - DECLARE_MARK_TABLE(SimpleCallContext); - - void init(ContextType t = Type_SimpleCallContext) - { - ExecutionContext::init(t); - } - - inline unsigned int formalParameterCount() const; +Q_STATIC_ASSERT(offsetof(ExecutionContextData, outer) == 0); +Q_STATIC_ASSERT(offsetof(ExecutionContextData, activation) == offsetof(ExecutionContextData, outer) + QT_POINTER_SIZE); -}; -V4_ASSERT_IS_TRIVIAL(SimpleCallContext) -Q_STATIC_ASSERT(std::is_standard_layout<SimpleCallContextData>::value); -Q_STATIC_ASSERT(offsetof(SimpleCallContextData, activation) == 0); -Q_STATIC_ASSERT(offsetof(SimpleCallContextData, v4Function) == offsetof(SimpleCallContextData, activation) + QT_POINTER_SIZE); -Q_STATIC_ASSERT(sizeof(SimpleCallContextData) == 2 * QT_POINTER_SIZE); -Q_STATIC_ASSERT(sizeof(SimpleCallContext) == sizeof(ExecutionContext) + sizeof(SimpleCallContextData)); - -#if QT_POINTER_SIZE == 8 #define CallContextMembers(class, Member) \ Member(class, Pointer, FunctionObject *, function) \ Member(class, ValueArray, ValueArray, locals) -#else -#define CallContextMembers(class, Member) \ - Member(class, Pointer, FunctionObject *, function) \ - Member(class, NoMark, void *, padding) \ - Member(class, ValueArray, ValueArray, locals) -#endif -DECLARE_HEAP_OBJECT(CallContext, SimpleCallContext) { - DECLARE_MARK_TABLE(CallContext); +DECLARE_HEAP_OBJECT(CallContext, ExecutionContext) { + DECLARE_MARKOBJECTS(CallContext); - using SimpleCallContext::formalParameterCount; -}; + void init() + { + ExecutionContext::init(Type_CallContext); + } + int argc() const { + return static_cast<int>(nArgs); + } + const Value *args() const { + return locals.data() + locals.size; + } + void setArg(uint index, Value v); +}; +V4_ASSERT_IS_TRIVIAL(CallContext) Q_STATIC_ASSERT(std::is_standard_layout<CallContextData>::value); Q_STATIC_ASSERT(offsetof(CallContextData, function) == 0); -// IMPORTANT: we cannot do offsetof(CallContextData, locals) in the JIT as the offset does not scale with -// the pointer size. On 32-bit ARM the offset of the ValueArray is aligned to 8 bytes, on 32-bit x86 for -// example it is not. Therefore we have a padding in place and always have a distance of 8 bytes. -Q_STATIC_ASSERT(offsetof(CallContextData, locals) == offsetof(CallContextData, function) + 8); - -#define GlobalContextMembers(class, Member) \ - Member(class, Pointer, Object *, global) - -DECLARE_HEAP_OBJECT(GlobalContext, ExecutionContext) { - DECLARE_MARK_TABLE(GlobalContext); - - void init(ExecutionEngine *engine); -}; -V4_ASSERT_IS_TRIVIAL(GlobalContext) +//### The following size check fails on Win8. With the ValueArray at the end of the +// CallContextMembers, it doesn't look very useful. +//#if defined(Q_PROCESSOR_ARM_32) && !defined(Q_OS_IOS) +//Q_STATIC_ASSERT(sizeof(CallContext) == sizeof(ExecutionContext) + sizeof(CallContextData) + QT_POINTER_SIZE); +//#else +//Q_STATIC_ASSERT(sizeof(CallContext) == sizeof(ExecutionContext) + sizeof(CallContextData)); +//#endif #define CatchContextMembers(class, Member) \ Member(class, Pointer, String *, exceptionVarName) \ Member(class, HeapValue, HeapValue, exceptionValue) DECLARE_HEAP_OBJECT(CatchContext, ExecutionContext) { - DECLARE_MARK_TABLE(CatchContext); + DECLARE_MARKOBJECTS(CatchContext); void init(ExecutionContext *outerContext, String *exceptionVarName, const Value &exceptionValue); }; V4_ASSERT_IS_TRIVIAL(CatchContext) -#define WithContextMembers(class, Member) \ - Member(class, Pointer, Object *, withObject) - -DECLARE_HEAP_OBJECT(WithContext, ExecutionContext) { - DECLARE_MARK_TABLE(WithContext); - - void init(ExecutionContext *outerContext, Object *with); -}; -V4_ASSERT_IS_TRIVIAL(WithContext) - } struct Q_QML_EXPORT ExecutionContext : public Managed @@ -239,68 +204,39 @@ struct Q_QML_EXPORT ExecutionContext : public Managed Q_MANAGED_TYPE(ExecutionContext) V4_INTERNALCLASS(ExecutionContext) - Heap::CallContext *newCallContext(Function *f, CallData *callData); - Heap::WithContext *newWithContext(Heap::Object *with); + static Heap::CallContext *newCallContext(QV4::CppStackFrame *frame); + Heap::ExecutionContext *newWithContext(Heap::Object *with); Heap::CatchContext *newCatchContext(Heap::String *exceptionVarName, ReturnedValue exceptionValue); void createMutableBinding(String *name, bool deletable); - void setProperty(String *name, const Value &value); + enum Error { + NoError, + TypeError, + RangeError + }; + + Error setProperty(String *name, const Value &value); + ReturnedValue getProperty(String *name); ReturnedValue getPropertyAndBase(String *name, Value *base); bool deleteProperty(String *name); - inline SimpleCallContext *asSimpleCallContext(); - inline const SimpleCallContext *asSimpleCallContext() const; - inline const CatchContext *asCatchContext() const; - inline const WithContext *asWithContext() const; + inline CallContext *asCallContext(); + inline const CallContext *asCallContext() const; +}; - Function *getFunction() const; +struct Q_QML_EXPORT CallContext : public ExecutionContext +{ + V4_MANAGED(CallContext, ExecutionContext) + V4_INTERNALCLASS(CallContext) - Value &thisObject() const { - return d()->callData->thisObject; - } int argc() const { - return d()->callData->argc; + return d()->argc(); } const Value *args() const { - return d()->callData->args; - } - ReturnedValue argument(int i) const { - return d()->callData->argument(i); + return d()->args(); } - - void call(Scope &scope, CallData *callData, QV4::Function *function, const QV4::FunctionObject *f = 0); - void simpleCall(Scope &scope, CallData *callData, QV4::Function *function); -}; - -struct Q_QML_EXPORT SimpleCallContext : public ExecutionContext -{ - V4_MANAGED(SimpleCallContext, ExecutionContext) - V4_INTERNALCLASS(SimpleCallContext) - - // formals are in reverse order - Identifier * const *formals() const; - unsigned int formalCount() const; - Identifier * const *variables() const; - unsigned int variableCount() const; - - inline ReturnedValue argument(int i) const; -}; - -inline ReturnedValue SimpleCallContext::argument(int i) const { - return i < argc() ? args()[i].asReturnedValue() : Primitive::undefinedValue().asReturnedValue(); -} - -struct Q_QML_EXPORT CallContext : public SimpleCallContext -{ - V4_MANAGED(CallContext, SimpleCallContext) -}; - -struct GlobalContext : public ExecutionContext -{ - V4_MANAGED(GlobalContext, ExecutionContext) - }; struct CatchContext : public ExecutionContext @@ -308,29 +244,14 @@ struct CatchContext : public ExecutionContext V4_MANAGED(CatchContext, ExecutionContext) }; -struct WithContext : public ExecutionContext -{ - V4_MANAGED(WithContext, ExecutionContext) -}; - -inline SimpleCallContext *ExecutionContext::asSimpleCallContext() -{ - return d()->type >= Heap::ExecutionContext::Type_SimpleCallContext ? static_cast<SimpleCallContext *>(this) : 0; -} - -inline const SimpleCallContext *ExecutionContext::asSimpleCallContext() const -{ - return d()->type >= Heap::ExecutionContext::Type_SimpleCallContext ? static_cast<const SimpleCallContext *>(this) : 0; -} - -inline const CatchContext *ExecutionContext::asCatchContext() const +inline CallContext *ExecutionContext::asCallContext() { - return d()->type == Heap::ExecutionContext::Type_CatchContext ? static_cast<const CatchContext *>(this) : 0; + return d()->type == Heap::ExecutionContext::Type_CallContext ? static_cast<CallContext *>(this) : 0; } -inline const WithContext *ExecutionContext::asWithContext() const +inline const CallContext *ExecutionContext::asCallContext() const { - return d()->type == Heap::ExecutionContext::Type_WithContext ? static_cast<const WithContext *>(this) : 0; + return d()->type == Heap::ExecutionContext::Type_CallContext ? static_cast<const CallContext *>(this) : 0; } } // namespace QV4 diff --git a/src/qml/jsruntime/qv4dataview.cpp b/src/qml/jsruntime/qv4dataview.cpp index f1405e08ee..397ef1cfec 100644 --- a/src/qml/jsruntime/qv4dataview.cpp +++ b/src/qml/jsruntime/qv4dataview.cpp @@ -54,34 +54,31 @@ void Heap::DataViewCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("DataView")); } -void DataViewCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue DataViewCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - Scoped<ArrayBuffer> buffer(scope, callData->argument(0)); - if (!buffer) { - scope.result = scope.engine->throwTypeError(); - return; - } + Scope scope(f->engine()); + Scoped<ArrayBuffer> buffer(scope, argc ? argv[0] : Primitive::undefinedValue()); + if (!buffer) + return scope.engine->throwTypeError(); - double bo = callData->argc > 1 ? callData->args[1].toNumber() : 0; + double bo = argc > 1 ? argv[1].toNumber() : 0; uint byteOffset = (uint)bo; uint bufferLength = buffer->d()->data->size; - double bl = callData->argc < 3 || callData->args[2].isUndefined() ? (bufferLength - bo) : callData->args[2].toNumber(); + double bl = argc < 3 || argv[2].isUndefined() ? (bufferLength - bo) : argv[2].toNumber(); uint byteLength = (uint)bl; - if (bo != byteOffset || bl != byteLength || byteOffset + byteLength > bufferLength) { - scope.result = scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range")); - return; - } + if (bo != byteOffset || bl != byteLength || byteOffset + byteLength > bufferLength) + return scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range")); Scoped<DataView> a(scope, scope.engine->memoryManager->allocObject<DataView>()); a->d()->buffer.set(scope.engine, buffer->d()); a->d()->byteLength = byteLength; a->d()->byteOffset = byteOffset; - scope.result = a.asReturnedValue(); + return a.asReturnedValue(); } -void DataViewCtor::call(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue DataViewCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) { - construct(that, scope, callData); + return callAsConstructor(f, argv, argc); } void DataViewPrototype::init(ExecutionEngine *engine, Object *ctor) @@ -122,84 +119,84 @@ void DataViewPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("setUInt32"), method_set<unsigned int>, 0); } -void DataViewPrototype::method_get_buffer(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DataViewPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int) { - Scoped<DataView> v(scope, callData->thisObject); + const DataView *v = thisObject->as<DataView>(); if (!v) - THROW_TYPE_ERROR(); + return b->engine()->throwTypeError(); - scope.result = v->d()->buffer; + return v->d()->buffer->asReturnedValue(); } -void DataViewPrototype::method_get_byteLength(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DataViewPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int) { - Scoped<DataView> v(scope, callData->thisObject); + const DataView *v = thisObject->as<DataView>(); if (!v) - THROW_TYPE_ERROR(); + return b->engine()->throwTypeError(); - scope.result = Encode(v->d()->byteLength); + return Encode(v->d()->byteLength); } -void DataViewPrototype::method_get_byteOffset(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DataViewPrototype::method_get_byteOffset(const FunctionObject *b, const Value *thisObject, const Value *, int) { - Scoped<DataView> v(scope, callData->thisObject); + const DataView *v = thisObject->as<DataView>(); if (!v) - THROW_TYPE_ERROR(); + return b->engine()->throwTypeError(); - scope.result = Encode(v->d()->byteOffset); + return Encode(v->d()->byteOffset); } template <typename T> -void DataViewPrototype::method_getChar(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DataViewPrototype::method_getChar(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - Scoped<DataView> v(scope, callData->thisObject); - if (!v || callData->argc < 1) - THROW_TYPE_ERROR(); - double l = callData->args[0].toNumber(); + const DataView *v = thisObject->as<DataView>(); + if (!v || argc < 1) + return b->engine()->throwTypeError(); + double l = argv[0].toNumber(); uint idx = (uint)l; if (l != idx || idx + sizeof(T) > v->d()->byteLength) - THROW_TYPE_ERROR(); + return b->engine()->throwTypeError(); idx += v->d()->byteOffset; T t = T(v->d()->buffer->data->data()[idx]); - scope.result = Encode((int)t); + return Encode((int)t); } template <typename T> -void DataViewPrototype::method_get(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DataViewPrototype::method_get(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - Scoped<DataView> v(scope, callData->thisObject); - if (!v || callData->argc < 1) - THROW_TYPE_ERROR(); - double l = callData->args[0].toNumber(); + const DataView *v = thisObject->as<DataView>(); + if (!v || argc < 1) + return b->engine()->throwTypeError(); + double l = argv[0].toNumber(); uint idx = (uint)l; if (l != idx || idx + sizeof(T) > v->d()->byteLength) - THROW_TYPE_ERROR(); + return b->engine()->throwTypeError(); idx += v->d()->byteOffset; - bool littleEndian = callData->argc < 2 ? false : callData->args[1].toBoolean(); + bool littleEndian = argc < 2 ? false : argv[1].toBoolean(); T t = littleEndian ? qFromLittleEndian<T>((uchar *)v->d()->buffer->data->data() + idx) : qFromBigEndian<T>((uchar *)v->d()->buffer->data->data() + idx); - scope.result = Encode(t); + return Encode(t); } template <typename T> -void DataViewPrototype::method_getFloat(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DataViewPrototype::method_getFloat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - Scoped<DataView> v(scope, callData->thisObject); - if (!v || callData->argc < 1) - THROW_TYPE_ERROR(); - double l = callData->args[0].toNumber(); + const DataView *v = thisObject->as<DataView>(); + if (!v || argc < 1) + return b->engine()->throwTypeError(); + double l = argv[0].toNumber(); uint idx = (uint)l; if (l != idx || idx + sizeof(T) > v->d()->byteLength) - THROW_TYPE_ERROR(); + return b->engine()->throwTypeError(); idx += v->d()->byteOffset; - bool littleEndian = callData->argc < 2 ? false : callData->args[1].toBoolean(); + bool littleEndian = argc < 2 ? false : argv[1].toBoolean(); if (sizeof(T) == 4) { // float @@ -210,7 +207,7 @@ void DataViewPrototype::method_getFloat(const BuiltinFunction *, Scope &scope, C u.i = littleEndian ? qFromLittleEndian<uint>((uchar *)v->d()->buffer->data->data() + idx) : qFromBigEndian<uint>((uchar *)v->d()->buffer->data->data() + idx); - scope.result = Encode(u.f); + return Encode(u.f); } else { Q_ASSERT(sizeof(T) == 8); union { @@ -220,43 +217,43 @@ void DataViewPrototype::method_getFloat(const BuiltinFunction *, Scope &scope, C u.i = littleEndian ? qFromLittleEndian<quint64>((uchar *)v->d()->buffer->data->data() + idx) : qFromBigEndian<quint64>((uchar *)v->d()->buffer->data->data() + idx); - scope.result = Encode(u.d); + return Encode(u.d); } } template <typename T> -void DataViewPrototype::method_setChar(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DataViewPrototype::method_setChar(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - Scoped<DataView> v(scope, callData->thisObject); - if (!v || callData->argc < 1) - THROW_TYPE_ERROR(); - double l = callData->args[0].toNumber(); + const DataView *v = thisObject->as<DataView>(); + if (!v || argc < 1) + return b->engine()->throwTypeError(); + double l = argv[0].toNumber(); uint idx = (uint)l; if (l != idx || idx + sizeof(T) > v->d()->byteLength) - THROW_TYPE_ERROR(); + return b->engine()->throwTypeError(); idx += v->d()->byteOffset; - int val = callData->argc >= 2 ? callData->args[1].toInt32() : 0; + int val = argc >= 2 ? argv[1].toInt32() : 0; v->d()->buffer->data->data()[idx] = (char)val; RETURN_UNDEFINED(); } template <typename T> -void DataViewPrototype::method_set(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DataViewPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - Scoped<DataView> v(scope, callData->thisObject); - if (!v || callData->argc < 1) - THROW_TYPE_ERROR(); - double l = callData->args[0].toNumber(); + const DataView *v = thisObject->as<DataView>(); + if (!v || argc < 1) + return b->engine()->throwTypeError(); + double l = argv[0].toNumber(); uint idx = (uint)l; if (l != idx || idx + sizeof(T) > v->d()->byteLength) - THROW_TYPE_ERROR(); + return b->engine()->throwTypeError(); idx += v->d()->byteOffset; - int val = callData->argc >= 2 ? callData->args[1].toInt32() : 0; + int val = argc >= 2 ? argv[1].toInt32() : 0; - bool littleEndian = callData->argc < 3 ? false : callData->args[2].toBoolean(); + bool littleEndian = argc < 3 ? false : argv[2].toBoolean(); if (littleEndian) qToLittleEndian<T>(val, (uchar *)v->d()->buffer->data->data() + idx); @@ -267,19 +264,19 @@ void DataViewPrototype::method_set(const BuiltinFunction *, Scope &scope, CallDa } template <typename T> -void DataViewPrototype::method_setFloat(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DataViewPrototype::method_setFloat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - Scoped<DataView> v(scope, callData->thisObject); - if (!v || callData->argc < 1) - THROW_TYPE_ERROR(); - double l = callData->args[0].toNumber(); + const DataView *v = thisObject->as<DataView>(); + if (!v || argc < 1) + return b->engine()->throwTypeError(); + double l = argv[0].toNumber(); uint idx = (uint)l; if (l != idx || idx + sizeof(T) > v->d()->byteLength) - THROW_TYPE_ERROR(); + return b->engine()->throwTypeError(); idx += v->d()->byteOffset; - double val = callData->argc >= 2 ? callData->args[1].toNumber() : qt_qnan(); - bool littleEndian = callData->argc < 3 ? false : callData->args[2].toBoolean(); + double val = argc >= 2 ? argv[1].toNumber() : qt_qnan(); + bool littleEndian = argc < 3 ? false : argv[2].toBoolean(); if (sizeof(T) == 4) { // float diff --git a/src/qml/jsruntime/qv4dataview_p.h b/src/qml/jsruntime/qv4dataview_p.h index 5c50df4655..1e07d85118 100644 --- a/src/qml/jsruntime/qv4dataview_p.h +++ b/src/qml/jsruntime/qv4dataview_p.h @@ -69,7 +69,7 @@ struct DataViewCtor : FunctionObject { Member(class, NoMark, uint, byteOffset) DECLARE_HEAP_OBJECT(DataView, Object) { - DECLARE_MARK_TABLE(DataView); + DECLARE_MARKOBJECTS(DataView); void init() { Object::init(); } }; @@ -79,8 +79,8 @@ struct DataViewCtor: FunctionObject { V4_OBJECT2(DataViewCtor, FunctionObject) - static void construct(const Managed *m, Scope &scope, CallData *callData); - static void call(const Managed *that, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct DataView : Object @@ -93,21 +93,21 @@ struct DataViewPrototype: Object { void init(ExecutionEngine *engine, Object *ctor); - static void method_get_buffer(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_get_byteLength(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_get_byteOffset(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_get_buffer(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get_byteLength(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get_byteOffset(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); template <typename T> - static void method_getChar(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_getChar(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); template <typename T> - static void method_get(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_get(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); template <typename T> - static void method_getFloat(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_getFloat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); template <typename T> - static void method_setChar(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_setChar(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); template <typename T> - static void method_set(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); template <typename T> - static void method_setFloat(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_setFloat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index 3aa20931ad..b8392d27e9 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -43,6 +43,7 @@ #include "qv4scopedvalue_p.h" #include "qv4runtime_p.h" #include "qv4string_p.h" +#include "qv4jscall_p.h" #include <QtCore/QDebug> #include <QtCore/QDateTime> @@ -744,15 +745,16 @@ void Heap::DateCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Date")); } -void DateCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue DateCtor::callAsConstructor(const FunctionObject *that, const Value *argv, int argc) { double t = 0; - if (callData->argc == 0) + if (argc == 0) t = currentTime(); - else if (callData->argc == 1) { - ScopedValue arg(scope, callData->args[0]); + else if (argc == 1) { + Scope scope(that->engine()); + ScopedValue arg(scope, argv[0]); if (DateObject *d = arg->as<DateObject>()) { t = d->date(); } else { @@ -766,26 +768,26 @@ void DateCtor::construct(const Managed *, Scope &scope, CallData *callData) } else { // d.argc > 1 - double year = callData->args[0].toNumber(); - double month = callData->args[1].toNumber(); - double day = callData->argc >= 3 ? callData->args[2].toNumber() : 1; - double hours = callData->argc >= 4 ? callData->args[3].toNumber() : 0; - double mins = callData->argc >= 5 ? callData->args[4].toNumber() : 0; - double secs = callData->argc >= 6 ? callData->args[5].toNumber() : 0; - double ms = callData->argc >= 7 ? callData->args[6].toNumber() : 0; + double year = argv[0].toNumber(); + double month = argv[1].toNumber(); + double day = argc >= 3 ? argv[2].toNumber() : 1; + double hours = argc >= 4 ? argv[3].toNumber() : 0; + double mins = argc >= 5 ? argv[4].toNumber() : 0; + double secs = argc >= 6 ? argv[5].toNumber() : 0; + double ms = argc >= 7 ? argv[6].toNumber() : 0; if (year >= 0 && year <= 99) year += 1900; t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); t = TimeClip(UTC(t)); } - scope.result = Encode(scope.engine->newDateObject(Primitive::fromDouble(t))); + return Encode(that->engine()->newDateObject(Primitive::fromDouble(t))); } -void DateCtor::call(const Managed *m, Scope &scope, CallData *) +ReturnedValue DateCtor::call(const FunctionObject *m, const Value *, const Value *, int) { double t = currentTime(); - scope.result = static_cast<const DateCtor *>(m)->engine()->newString(ToString(t)); + return m->engine()->newString(ToString(t))->asReturnedValue(); } void DatePrototype::init(ExecutionEngine *engine, Object *ctor) @@ -862,459 +864,518 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("toJSON"), method_toJSON, 1); } -double DatePrototype::getThisDate(Scope &scope, CallData *callData) +double DatePrototype::getThisDate(ExecutionEngine *v4, const Value *thisObject) { - if (DateObject *thisObject = callData->thisObject.as<DateObject>()) - return thisObject->date(); - else { - scope.engine->throwTypeError(); - return 0; - } + if (const DateObject *that = thisObject->as<DateObject>()) + return that->date(); + v4->throwTypeError(); + return 0; } -void DatePrototype::method_parse(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_parse(const FunctionObject *, const Value *, const Value *argv, int argc) { - if (!callData->argc) - scope.result = Encode(qt_qnan()); + if (!argc) + return Encode(qt_qnan()); else - scope.result = Encode(ParseString(callData->args[0].toQString())); + return Encode(ParseString(argv[0].toQString())); } -void DatePrototype::method_UTC(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_UTC(const FunctionObject *, const Value *, const Value *argv, int argc) { - const int numArgs = callData->argc; + const int numArgs = argc; if (numArgs >= 2) { - double year = callData->args[0].toNumber(); - double month = callData->args[1].toNumber(); - double day = numArgs >= 3 ? callData->args[2].toNumber() : 1; - double hours = numArgs >= 4 ? callData->args[3].toNumber() : 0; - double mins = numArgs >= 5 ? callData->args[4].toNumber() : 0; - double secs = numArgs >= 6 ? callData->args[5].toNumber() : 0; - double ms = numArgs >= 7 ? callData->args[6].toNumber() : 0; + double year = argv[0].toNumber(); + double month = argv[1].toNumber(); + double day = numArgs >= 3 ? argv[2].toNumber() : 1; + double hours = numArgs >= 4 ? argv[3].toNumber() : 0; + double mins = numArgs >= 5 ? argv[4].toNumber() : 0; + double secs = numArgs >= 6 ? argv[5].toNumber() : 0; + double ms = numArgs >= 7 ? argv[6].toNumber() : 0; if (year >= 0 && year <= 99) year += 1900; double t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); - scope.result = Encode(TimeClip(t)); - return; + return Encode(TimeClip(t)); } RETURN_UNDEFINED(); } -void DatePrototype::method_now(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_now(const FunctionObject *, const Value *, const Value *, int) { - Q_UNUSED(callData); - double t = currentTime(); - scope.result = Encode(t); + return Encode(currentTime()); } -void DatePrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); - scope.result = scope.engine->newString(ToString(t)); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); + return Encode(v4->newString(ToString(t))); } -void DatePrototype::method_toDateString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_toDateString(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); - scope.result = scope.engine->newString(ToDateString(t)); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); + return Encode(v4->newString(ToDateString(t))); } -void DatePrototype::method_toTimeString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_toTimeString(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); - scope.result = scope.engine->newString(ToTimeString(t)); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); + return Encode(v4->newString(ToTimeString(t))); } -void DatePrototype::method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); - scope.result = scope.engine->newString(ToLocaleString(t)); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); + return Encode(v4->newString(ToLocaleString(t))); } -void DatePrototype::method_toLocaleDateString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_toLocaleDateString(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); - scope.result = scope.engine->newString(ToLocaleDateString(t)); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); + return Encode(v4->newString(ToLocaleDateString(t))); } -void DatePrototype::method_toLocaleTimeString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_toLocaleTimeString(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); - scope.result = scope.engine->newString(ToLocaleTimeString(t)); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); + return Encode(v4->newString(ToLocaleTimeString(t))); } -void DatePrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_valueOf(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); - scope.result = Encode(t); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); + return Encode(t); } -void DatePrototype::method_getTime(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getTime(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); - scope.result = Encode(t); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); + return Encode(t); } -void DatePrototype::method_getYear(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getYear(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); if (!std::isnan(t)) t = YearFromTime(LocalTime(t)) - 1900; - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getFullYear(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getFullYear(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); if (!std::isnan(t)) t = YearFromTime(LocalTime(t)); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getUTCFullYear(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getUTCFullYear(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); if (!std::isnan(t)) t = YearFromTime(t); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getMonth(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getMonth(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); if (!std::isnan(t)) t = MonthFromTime(LocalTime(t)); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getUTCMonth(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getUTCMonth(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); if (!std::isnan(t)) t = MonthFromTime(t); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getDate(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getDate(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); if (!std::isnan(t)) t = DateFromTime(LocalTime(t)); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getUTCDate(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getUTCDate(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); if (!std::isnan(t)) t = DateFromTime(t); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getDay(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getDay(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); if (!std::isnan(t)) t = WeekDay(LocalTime(t)); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getUTCDay(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getUTCDay(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); if (!std::isnan(t)) t = WeekDay(t); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getHours(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getHours(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); if (!std::isnan(t)) t = HourFromTime(LocalTime(t)); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getUTCHours(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getUTCHours(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); if (!std::isnan(t)) t = HourFromTime(t); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getMinutes(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getMinutes(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); if (!std::isnan(t)) t = MinFromTime(LocalTime(t)); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getUTCMinutes(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getUTCMinutes(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); if (!std::isnan(t)) t = MinFromTime(t); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getSeconds(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getSeconds(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); if (!std::isnan(t)) t = SecFromTime(LocalTime(t)); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getUTCSeconds(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getUTCSeconds(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); if (!std::isnan(t)) t = SecFromTime(t); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getMilliseconds(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); if (!std::isnan(t)) t = msFromTime(LocalTime(t)); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getUTCMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getUTCMilliseconds(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); if (!std::isnan(t)) t = msFromTime(t); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getTimezoneOffset(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getTimezoneOffset(const FunctionObject *b, const Value *thisObject, const Value *, int) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, thisObject); if (!std::isnan(t)) t = (t - LocalTime(t)) / msPerMinute; - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_setTime(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setTime(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - Scoped<DateObject> self(scope, callData->thisObject); + ExecutionEngine *v4 = b->engine(); + DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>()); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); - double t = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - CHECK_EXCEPTION(); + double t = argc ? argv[0].toNumber() : qt_qnan(); + if (v4->hasException) + return QV4::Encode::undefined(); self->setDate(TimeClip(t)); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setMilliseconds(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - Scoped<DateObject> self(scope, callData->thisObject); + ExecutionEngine *v4 = b->engine(); + DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>()); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = LocalTime(self->date()); - CHECK_EXCEPTION(); - double ms = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - CHECK_EXCEPTION(); + if (v4->hasException) + return QV4::Encode::undefined(); + double ms = argc ? argv[0].toNumber() : qt_qnan(); + if (v4->hasException) + return QV4::Encode::undefined(); self->setDate(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setUTCMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setUTCMilliseconds(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - DateObject *self = callData->thisObject.as<DateObject>(); + ExecutionEngine *v4 = b->engine(); + DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>()); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = self->date(); - CHECK_EXCEPTION(); - double ms = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - CHECK_EXCEPTION(); + if (v4->hasException) + return QV4::Encode::undefined(); + double ms = argc ? argv[0].toNumber() : qt_qnan(); + if (v4->hasException) + return QV4::Encode::undefined(); self->setDate(TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setSeconds(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setSeconds(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - DateObject *self = callData->thisObject.as<DateObject>(); + ExecutionEngine *v4 = b->engine(); + DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>()); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = LocalTime(self->date()); - CHECK_EXCEPTION(); - double sec = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - CHECK_EXCEPTION(); - double ms = (callData->argc < 2) ? msFromTime(t) : callData->args[1].toNumber(); - CHECK_EXCEPTION(); + if (v4->hasException) + return QV4::Encode::undefined(); + double sec = argc ? argv[0].toNumber() : qt_qnan(); + if (v4->hasException) + return QV4::Encode::undefined(); + double ms = (argc < 2) ? msFromTime(t) : argv[1].toNumber(); + if (v4->hasException) + return QV4::Encode::undefined(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setUTCSeconds(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setUTCSeconds(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - DateObject *self = callData->thisObject.as<DateObject>(); + ExecutionEngine *v4 = b->engine(); + DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>()); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = self->date(); - double sec = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - double ms = (callData->argc < 2) ? msFromTime(t) : callData->args[1].toNumber(); + double sec = argc ? argv[0].toNumber() : qt_qnan(); + double ms = (argc < 2) ? msFromTime(t) : argv[1].toNumber(); t = TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setMinutes(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setMinutes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - DateObject *self = callData->thisObject.as<DateObject>(); + ExecutionEngine *v4 = b->engine(); + DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>()); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = LocalTime(self->date()); - CHECK_EXCEPTION(); - double min = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - CHECK_EXCEPTION(); - double sec = (callData->argc < 2) ? SecFromTime(t) : callData->args[1].toNumber(); - CHECK_EXCEPTION(); - double ms = (callData->argc < 3) ? msFromTime(t) : callData->args[2].toNumber(); - CHECK_EXCEPTION(); + if (v4->hasException) + return QV4::Encode::undefined(); + double min = argc ? argv[0].toNumber() : qt_qnan(); + if (v4->hasException) + return QV4::Encode::undefined(); + double sec = (argc < 2) ? SecFromTime(t) : argv[1].toNumber(); + if (v4->hasException) + return QV4::Encode::undefined(); + double ms = (argc < 3) ? msFromTime(t) : argv[2].toNumber(); + if (v4->hasException) + return QV4::Encode::undefined(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setUTCMinutes(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setUTCMinutes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - DateObject *self = callData->thisObject.as<DateObject>(); + ExecutionEngine *v4 = b->engine(); + DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>()); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = self->date(); - double min = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - double sec = (callData->argc < 2) ? SecFromTime(t) : callData->args[1].toNumber(); - double ms = (callData->argc < 3) ? msFromTime(t) : callData->args[2].toNumber(); + double min = argc ? argv[0].toNumber() : qt_qnan(); + double sec = (argc < 2) ? SecFromTime(t) : argv[1].toNumber(); + double ms = (argc < 3) ? msFromTime(t) : argv[2].toNumber(); t = TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setHours(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setHours(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - DateObject *self = callData->thisObject.as<DateObject>(); + ExecutionEngine *v4 = b->engine(); + DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>()); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = LocalTime(self->date()); - CHECK_EXCEPTION(); - double hour = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - CHECK_EXCEPTION(); - double min = (callData->argc < 2) ? MinFromTime(t) : callData->args[1].toNumber(); - CHECK_EXCEPTION(); - double sec = (callData->argc < 3) ? SecFromTime(t) : callData->args[2].toNumber(); - CHECK_EXCEPTION(); - double ms = (callData->argc < 4) ? msFromTime(t) : callData->args[3].toNumber(); - CHECK_EXCEPTION(); + if (v4->hasException) + return QV4::Encode::undefined(); + double hour = argc ? argv[0].toNumber() : qt_qnan(); + if (v4->hasException) + return QV4::Encode::undefined(); + double min = (argc < 2) ? MinFromTime(t) : argv[1].toNumber(); + if (v4->hasException) + return QV4::Encode::undefined(); + double sec = (argc < 3) ? SecFromTime(t) : argv[2].toNumber(); + if (v4->hasException) + return QV4::Encode::undefined(); + double ms = (argc < 4) ? msFromTime(t) : argv[3].toNumber(); + if (v4->hasException) + return QV4::Encode::undefined(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setUTCHours(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setUTCHours(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - DateObject *self = callData->thisObject.as<DateObject>(); + ExecutionEngine *v4 = b->engine(); + DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>()); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = self->date(); - double hour = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - double min = (callData->argc < 2) ? MinFromTime(t) : callData->args[1].toNumber(); - double sec = (callData->argc < 3) ? SecFromTime(t) : callData->args[2].toNumber(); - double ms = (callData->argc < 4) ? msFromTime(t) : callData->args[3].toNumber(); + double hour = argc ? argv[0].toNumber() : qt_qnan(); + double min = (argc < 2) ? MinFromTime(t) : argv[1].toNumber(); + double sec = (argc < 3) ? SecFromTime(t) : argv[2].toNumber(); + double ms = (argc < 4) ? msFromTime(t) : argv[3].toNumber(); t = TimeClip(MakeDate(Day(t), MakeTime(hour, min, sec, ms))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setDate(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setDate(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - DateObject *self = callData->thisObject.as<DateObject>(); + ExecutionEngine *v4 = b->engine(); + DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>()); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = LocalTime(self->date()); - CHECK_EXCEPTION(); - double date = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - CHECK_EXCEPTION(); + if (v4->hasException) + return QV4::Encode::undefined(); + double date = argc ? argv[0].toNumber() : qt_qnan(); + if (v4->hasException) + return QV4::Encode::undefined(); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setUTCDate(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setUTCDate(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - DateObject *self = callData->thisObject.as<DateObject>(); + ExecutionEngine *v4 = b->engine(); + DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>()); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = self->date(); - CHECK_EXCEPTION(); - double date = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - CHECK_EXCEPTION(); + if (v4->hasException) + return QV4::Encode::undefined(); + double date = argc ? argv[0].toNumber() : qt_qnan(); + if (v4->hasException) + return QV4::Encode::undefined(); t = TimeClip(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setMonth(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setMonth(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - DateObject *self = callData->thisObject.as<DateObject>(); + ExecutionEngine *v4 = b->engine(); + DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>()); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = LocalTime(self->date()); - CHECK_EXCEPTION(); - double month = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - CHECK_EXCEPTION(); - double date = (callData->argc < 2) ? DateFromTime(t) : callData->args[1].toNumber(); - CHECK_EXCEPTION(); + if (v4->hasException) + return QV4::Encode::undefined(); + double month = argc ? argv[0].toNumber() : qt_qnan(); + if (v4->hasException) + return QV4::Encode::undefined(); + double date = (argc < 2) ? DateFromTime(t) : argv[1].toNumber(); + if (v4->hasException) + return QV4::Encode::undefined(); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setUTCMonth(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setUTCMonth(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - DateObject *self = callData->thisObject.as<DateObject>(); + ExecutionEngine *v4 = b->engine(); + DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>()); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = self->date(); - double month = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - double date = (callData->argc < 2) ? DateFromTime(t) : callData->args[1].toNumber(); + double month = argc ? argv[0].toNumber() : qt_qnan(); + double date = (argc < 2) ? DateFromTime(t) : argv[1].toNumber(); t = TimeClip(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setYear(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setYear(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - DateObject *self = callData->thisObject.as<DateObject>(); + ExecutionEngine *v4 = b->engine(); + DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>()); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = self->date(); if (std::isnan(t)) t = 0; else t = LocalTime(t); - double year = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double year = argc ? argv[0].toNumber() : qt_qnan(); double r; if (std::isnan(year)) { r = qt_qnan(); @@ -1326,53 +1387,60 @@ void DatePrototype::method_setYear(const BuiltinFunction *, Scope &scope, CallDa r = TimeClip(r); } self->setDate(r); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setUTCFullYear(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setUTCFullYear(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - DateObject *self = callData->thisObject.as<DateObject>(); + ExecutionEngine *v4 = b->engine(); + DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>()); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = self->date(); - double year = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - double month = (callData->argc < 2) ? MonthFromTime(t) : callData->args[1].toNumber(); - double date = (callData->argc < 3) ? DateFromTime(t) : callData->args[2].toNumber(); + double year = argc ? argv[0].toNumber() : qt_qnan(); + double month = (argc < 2) ? MonthFromTime(t) : argv[1].toNumber(); + double date = (argc < 3) ? DateFromTime(t) : argv[2].toNumber(); t = TimeClip(MakeDate(MakeDay(year, month, date), TimeWithinDay(t))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setFullYear(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setFullYear(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - DateObject *self = callData->thisObject.as<DateObject>(); + ExecutionEngine *v4 = b->engine(); + DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>()); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = LocalTime(self->date()); - CHECK_EXCEPTION(); + if (v4->hasException) + return QV4::Encode::undefined(); if (std::isnan(t)) t = 0; - double year = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - CHECK_EXCEPTION(); - double month = (callData->argc < 2) ? MonthFromTime(t) : callData->args[1].toNumber(); - CHECK_EXCEPTION(); - double date = (callData->argc < 3) ? DateFromTime(t) : callData->args[2].toNumber(); - CHECK_EXCEPTION(); + double year = argc ? argv[0].toNumber() : qt_qnan(); + if (v4->hasException) + return QV4::Encode::undefined(); + double month = (argc < 2) ? MonthFromTime(t) : argv[1].toNumber(); + if (v4->hasException) + return QV4::Encode::undefined(); + double date = (argc < 3) ? DateFromTime(t) : argv[2].toNumber(); + if (v4->hasException) + return QV4::Encode::undefined(); t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_toUTCString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_toUTCString(const FunctionObject *b, const Value *thisObject, const Value *, int) { - DateObject *self = callData->thisObject.as<DateObject>(); + ExecutionEngine *v4 = b->engine(); + DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>()); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = self->date(); - scope.result = scope.engine->newString(ToUTCString(t)); + return Encode(v4->newString(ToUTCString(t))); } static void addZeroPrefixedInt(QString &str, int num, int nDigits) @@ -1388,21 +1456,22 @@ static void addZeroPrefixedInt(QString &str, int num, int nDigits) } } -void DatePrototype::method_toISOString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_toISOString(const FunctionObject *b, const Value *thisObject, const Value *, int) { - DateObject *self = callData->thisObject.as<DateObject>(); + ExecutionEngine *v4 = b->engine(); + DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>()); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = self->date(); if (!std::isfinite(t)) - RETURN_RESULT(scope.engine->throwRangeError(callData->thisObject)); + RETURN_RESULT(v4->throwRangeError(*thisObject)); QString result; int year = (int)YearFromTime(t); if (year < 0 || year > 9999) { if (qAbs(year) >= 1000000) - RETURN_RESULT(scope.engine->newString(QStringLiteral("Invalid Date"))); + RETURN_RESULT(v4->newString(QStringLiteral("Invalid Date"))); result += year < 0 ? QLatin1Char('-') : QLatin1Char('+'); year = qAbs(year); addZeroPrefixedInt(result, year, 6); @@ -1423,29 +1492,30 @@ void DatePrototype::method_toISOString(const BuiltinFunction *, Scope &scope, Ca addZeroPrefixedInt(result, msFromTime(t), 3); result += QLatin1Char('Z'); - scope.result = scope.engine->newString(result); + return Encode(v4->newString(result)); } -void DatePrototype::method_toJSON(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_toJSON(const FunctionObject *b, const Value *thisObject, const Value *, int) { - ScopedObject O(scope, callData->thisObject.toObject(scope.engine)); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + ScopedObject O(scope, thisObject->toObject(v4)); + if (v4->hasException) + return QV4::Encode::undefined(); ScopedValue tv(scope, RuntimeHelpers::toPrimitive(O, NUMBER_HINT)); if (tv->isNumber() && !std::isfinite(tv->toNumber())) - RETURN_RESULT(Encode::null()); + return Encode::null(); - ScopedString s(scope, scope.engine->newString(QStringLiteral("toISOString"))); + ScopedString s(scope, v4->newString(QStringLiteral("toISOString"))); ScopedValue v(scope, O->get(s)); FunctionObject *toIso = v->as<FunctionObject>(); if (!toIso) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); - ScopedCallData cData(scope); - cData->thisObject = callData->thisObject; - toIso->call(scope, cData); + return toIso->call(O, nullptr, 0); } void DatePrototype::timezoneUpdated() diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index b0373884dd..a4ab0a27ed 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -108,8 +108,8 @@ struct DateCtor: FunctionObject { V4_OBJECT2(DateCtor, FunctionObject) - static void construct(const Managed *, Scope &scope, CallData *callData); - static void call(const Managed *that, Scope &scope, CallData *); + static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int); }; struct DatePrototype: Object @@ -118,57 +118,57 @@ struct DatePrototype: Object void init(ExecutionEngine *engine, Object *ctor); - static double getThisDate(Scope &scope, CallData *callData); - - static void method_parse(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_UTC(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_now(const BuiltinFunction *, Scope &scope, CallData *callData); - - static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toDateString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toTimeString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toLocaleDateString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toLocaleTimeString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getTime(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getYear(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getFullYear(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getUTCFullYear(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getMonth(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getUTCMonth(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getDate(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getUTCDate(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getDay(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getUTCDay(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getHours(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getUTCHours(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getMinutes(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getUTCMinutes(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getSeconds(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getUTCSeconds(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getUTCMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getTimezoneOffset(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setTime(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setUTCMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setSeconds(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setUTCSeconds(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setMinutes(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setUTCMinutes(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setHours(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setUTCHours(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setDate(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setUTCDate(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setMonth(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setUTCMonth(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setYear(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setFullYear(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setUTCFullYear(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toUTCString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toISOString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toJSON(const BuiltinFunction *, Scope &scope, CallData *callData); + static double getThisDate(ExecutionEngine *v4, const Value *thisObject); + + static ReturnedValue method_parse(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_UTC(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_now(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + + static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toDateString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toTimeString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toLocaleString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toLocaleDateString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toLocaleTimeString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_valueOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getTime(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getYear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getFullYear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getUTCFullYear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getMonth(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getUTCMonth(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getDate(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getUTCDate(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getDay(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getUTCDay(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getHours(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getUTCHours(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getMinutes(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getUTCMinutes(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getSeconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getUTCSeconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getMilliseconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getUTCMilliseconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getTimezoneOffset(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_setTime(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_setMilliseconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_setUTCMilliseconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_setSeconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_setUTCSeconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_setMinutes(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_setUTCMinutes(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_setHours(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_setUTCHours(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_setDate(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_setUTCDate(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_setMonth(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_setUTCMonth(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_setYear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_setFullYear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_setUTCFullYear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toUTCString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toISOString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toJSON(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static void timezoneUpdated(); }; diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 78cc95b98d..c57f39f61c 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -81,14 +81,6 @@ #include <QtCore/QTextStream> #include <QDateTime> -#ifdef V4_ENABLE_JIT -#include "qv4isel_masm_p.h" -#endif // V4_ENABLE_JIT - -#if QT_CONFIG(qml_interpreter) -#include "qv4isel_moth_p.h" -#endif - #if USE(PTHREADS) # include <pthread.h> #if !defined(Q_OS_INTEGRITY) @@ -109,9 +101,9 @@ using namespace QV4; static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1); -void throwTypeError(const BuiltinFunction *, Scope &scope, CallData *) +ReturnedValue throwTypeError(const BuiltinFunction *b, CallData *) { - scope.result = scope.engine->throwTypeError(); + return b->engine()->throwTypeError(); } @@ -129,7 +121,7 @@ QQmlEngine *ExecutionEngine::qmlEngine() const qint32 ExecutionEngine::maxCallDepth = -1; -ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) +ExecutionEngine::ExecutionEngine() : executableAllocator(new QV4::ExecutableAllocator) , regExpAllocator(new QV4::ExecutableAllocator) , bumperPointerAllocator(new WTF::BumpPointerAllocator) @@ -149,38 +141,15 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) bool ok = false; maxCallDepth = qEnvironmentVariableIntValue("QV4_MAX_CALL_DEPTH", &ok); if (!ok || maxCallDepth <= 0) { +#ifdef QT_NO_DEBUG maxCallDepth = 1234; - } - } - Q_ASSERT(maxCallDepth > 0); - - if (!factory) { -#if QT_CONFIG(qml_interpreter) - bool jitDisabled = true; - -#ifdef V4_ENABLE_JIT - static const bool forceMoth = !qEnvironmentVariableIsEmpty("QV4_FORCE_INTERPRETER") || - !OSAllocator::canAllocateExecutableMemory(); - if (forceMoth) { - factory = new Moth::ISelFactory; - } else { - factory = new JIT::ISelFactory<>; - jitDisabled = false; - } -#else // !V4_ENABLE_JIT - factory = new Moth::ISelFactory; -#endif // V4_ENABLE_JIT - - if (jitDisabled) { - qWarning("JIT is disabled for QML. Property bindings and animations will be " - "very slow. Visit https://wiki.qt.io/V4 to learn about possible " - "solutions for your platform."); - } #else - factory = new JIT::ISelFactory<>; + // no (tail call) optimization is done, so there'll be a lot mare stack frames active + maxCallDepth = 200; #endif + } } - iselFactory.reset(factory); + Q_ASSERT(maxCallDepth > 0); // reserve space for the JS stack // we allow it to grow to a bit more than JSStackLimit, as we can overshoot due to ScopedValues @@ -219,7 +188,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) internalClasses[Class_SimpleArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SimpleArrayData::staticVTable()); internalClasses[Class_SparseArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SparseArrayData::staticVTable()); internalClasses[Class_ExecutionContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::ExecutionContext::staticVTable()); - internalClasses[Class_SimpleCallContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::CallContext::staticVTable()); + internalClasses[Class_CallContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::CallContext::staticVTable()); jsStrings[String_Empty] = newIdentifier(QString()); jsStrings[String_undefined] = newIdentifier(QStringLiteral("undefined")); @@ -273,6 +242,8 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) InternalClass *argsClass = newInternalClass(ArgumentsObject::staticVTable(), objectPrototype()); argsClass = argsClass->addMember(id_length(), Attr_NotEnumerable); internalClasses[EngineBase::Class_ArgumentsObject] = argsClass->addMember(id_callee(), Attr_Data|Attr_NotEnumerable); + argsClass = newInternalClass(StrictArgumentsObject::staticVTable(), objectPrototype()); + argsClass = argsClass->addMember(id_length(), Attr_NotEnumerable); argsClass = argsClass->addMember(id_callee(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); internalClasses[EngineBase::Class_StrictArgumentsObject] = argsClass->addMember(id_caller(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); @@ -425,8 +396,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) // // set up the global object // - rootContext()->d()->global.set(scope.engine, globalObject->d()); - rootContext()->d()->callData->thisObject = globalObject; + rootContext()->d()->activation.set(scope.engine, globalObject->d()); Q_ASSERT(globalObject->d()->vtable()); globalObject->defineDefaultProperty(QStringLiteral("Object"), *objectCtor()); @@ -535,19 +505,11 @@ void ExecutionEngine::setProfiler(Profiling::Profiler *profiler) void ExecutionEngine::initRootContext() { Scope scope(this); - Scoped<GlobalContext> r(scope, memoryManager->allocManaged<GlobalContext>( - sizeof(GlobalContext::Data) + sizeof(CallData))); - r->d_unchecked()->init(this); - r->d()->callData = reinterpret_cast<CallData *>(r->d() + 1); - r->d()->callData->tag = quint32(Value::ValueTypeInternal::Integer); - r->d()->callData->argc = 0; - r->d()->callData->thisObject = globalObject; - r->d()->callData->args[0] = Encode::undefined(); + Scoped<ExecutionContext> r(scope, memoryManager->allocManaged<ExecutionContext>(sizeof(ExecutionContext::Data))); + r->d_unchecked()->init(Heap::ExecutionContext::Type_GlobalContext); + r->d()->activation.set(this, globalObject->d()); jsObjects[RootContext] = r; jsObjects[IntegerNull] = Encode((int)0); - - currentContext = static_cast<ExecutionContext *>(jsObjects + RootContext); - current = currentContext->d(); } InternalClass *ExecutionEngine::newClass(const InternalClass &other) @@ -555,14 +517,6 @@ InternalClass *ExecutionEngine::newClass(const InternalClass &other) return new (classPool) InternalClass(other); } -ExecutionContext *ExecutionEngine::pushGlobalContext() -{ - pushContext(rootContext()->d()); - - Q_ASSERT(current == rootContext()->d()); - return currentContext; -} - InternalClass *ExecutionEngine::newInternalClass(const VTable *vtable, Object *prototype) { return internalClasses[EngineBase::Class_Empty]->changeVTable(vtable)->changePrototype(prototype ? prototype->d() : 0); @@ -633,6 +587,12 @@ Heap::ArrayObject *ExecutionEngine::newArrayObject(const Value *values, int leng // this doesn't require a write barrier, things will be ok, when the new array data gets inserted into // the parent object memcpy(&d->values.values, values, length*sizeof(Value)); + for (int i = 0; i < length; ++i) { + if (values[i].isManaged()) { + d->needsMark = true; + break; + } + } a->d()->arrayData.set(this, d); a->setArrayLengthUnchecked(length); } @@ -685,9 +645,9 @@ Heap::DateObject *ExecutionEngine::newDateObjectFromTime(const QTime &t) Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) { - bool global = (flags & IR::RegExp::RegExp_Global); - bool ignoreCase = (flags & IR::RegExp::RegExp_IgnoreCase); - bool multiline = (flags & IR::RegExp::RegExp_Multiline); + bool global = (flags & QV4::CompiledData::RegExp::RegExp_Global); + bool ignoreCase = (flags & QV4::CompiledData::RegExp::RegExp_IgnoreCase); + bool multiline = (flags & QV4::CompiledData::RegExp::RegExp_Multiline); Scope scope(this); Scoped<RegExp> re(scope, RegExp::create(this, pattern, ignoreCase, multiline, global)); @@ -760,11 +720,9 @@ Heap::Object *ExecutionEngine::newForEachIteratorObject(Object *o) Heap::QmlContext *ExecutionEngine::qmlContext() const { - Heap::ExecutionContext *ctx = current; - - // get the correct context when we're within a builtin function - if (ctx->type == Heap::ExecutionContext::Type_SimpleCallContext && !ctx->outer) - ctx = parentContext(currentContext)->d(); + if (!currentStackFrame) + return 0; + Heap::ExecutionContext *ctx = currentContext()->d(); if (ctx->type != Heap::ExecutionContext::Type_QmlContext && !ctx->outer) return 0; @@ -785,7 +743,7 @@ QObject *ExecutionEngine::qmlScopeObject() const if (!ctx) return 0; - return ctx->qml->scopeObject; + return ctx->qml()->scopeObject; } ReturnedValue ExecutionEngine::qmlSingletonWrapper(String *name) @@ -815,57 +773,56 @@ QQmlContextData *ExecutionEngine::callingQmlContext() const if (!ctx) return 0; - return ctx->qml->context->contextData(); + return ctx->qml()->context->contextData(); } -QVector<StackFrame> ExecutionEngine::stackTrace(int frameLimit) const +QString CppStackFrame::source() const { - Scope scope(const_cast<ExecutionEngine *>(this)); - ScopedString name(scope); - QVector<StackFrame> stack; - - ExecutionContext *c = currentContext; - while (c && frameLimit) { - QV4::Function *function = c->getFunction(); - if (function) { - StackFrame frame; - frame.source = function->sourceFile(); - name = function->name(); - frame.function = name->toQString(); - - // line numbers can be negative for places where you can't set a real breakpoint - frame.line = qAbs(c->d()->lineNumber); - frame.column = -1; - - stack.append(frame); - --frameLimit; - } - c = parentContext(c); - } - - if (frameLimit && globalCode) { - StackFrame frame; - frame.source = globalCode->sourceFile(); - frame.function = globalCode->name()->toQString(); - frame.line = rootContext()->d()->lineNumber; - frame.column = -1; + return v4Function->sourceFile(); +} - stack.append(frame); - } - return stack; +QString CppStackFrame::function() const +{ + return v4Function->name()->toQString(); } -StackFrame ExecutionEngine::currentStackFrame() const +int CppStackFrame::lineNumber() const { - StackFrame frame; - frame.line = -1; - frame.column = -1; + auto findLine = [](const CompiledData::CodeOffsetToLine &entry, uint offset) { + return entry.codeOffset < offset; + }; + + const QV4::CompiledData::Function *cf = v4Function->compiledFunction; + uint offset = instructionPointer; + const CompiledData::CodeOffsetToLine *lineNumbers = cf->lineNumberTable(); + uint nLineNumbers = cf->nLineNumbers; + const CompiledData::CodeOffsetToLine *line = std::lower_bound(lineNumbers, lineNumbers + nLineNumbers, offset, findLine) - 1; + return line->line; +} - QVector<StackFrame> trace = stackTrace(/*limit*/ 1); - if (!trace.isEmpty()) - frame = trace.first(); +ReturnedValue CppStackFrame::thisObject() const { + return jsFrame->thisObject.asReturnedValue(); +} - return frame; +StackTrace ExecutionEngine::stackTrace(int frameLimit) const +{ + Scope scope(const_cast<ExecutionEngine *>(this)); + ScopedString name(scope); + StackTrace stack; + + CppStackFrame *f = currentStackFrame; + while (f && frameLimit) { + QV4::StackFrame frame; + frame.source = f->source(); + frame.function = f->function(); + frame.line = qAbs(f->lineNumber()); + frame.column = -1; + stack.append(frame); + --frameLimit; + f = f->parent; + } + + return stack; } /* Helper and "C" linkage exported function to format a GDBMI stacktrace for @@ -908,14 +865,13 @@ QUrl ExecutionEngine::resolvedUrl(const QString &file) return src; QUrl base; - ExecutionContext *c = currentContext; - while (c) { - SimpleCallContext *callCtx = c->asSimpleCallContext(); - if (callCtx && callCtx->d()->v4Function) { - base.setUrl(callCtx->d()->v4Function->sourceFile()); + CppStackFrame *f = currentStackFrame; + while (f) { + if (f->v4Function) { + base.setUrl(f->v4Function->sourceFile()); break; } - c = parentContext(c); + f = f->parent; } if (base.isEmpty() && globalCode) @@ -1589,9 +1545,13 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data) return 0; } -void ExecutionEngine::failStackLimitCheck(Scope &scope) +bool ExecutionEngine::canJIT() { - scope.result = throwRangeError(QStringLiteral("Maximum call stack size exceeded.")); +#ifdef V4_ENABLE_JIT + return true; +#else + return false; +#endif } // Converts a JS value to a meta-type. diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index e4ac51efed..e143fe2de2 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -51,7 +51,6 @@ // #include "qv4global_p.h" -#include "private/qv4isel_p.h" #include "qv4managed_p.h" #include "qv4context_p.h" #include <private/qintrusivelist_p.h> @@ -89,6 +88,32 @@ struct CompilationUnit; struct InternalClass; struct InternalClassPool; +struct Q_QML_EXPORT CppStackFrame { + CppStackFrame *parent; + Function *v4Function; + CallData *jsFrame; + const Value *originalArguments; + int originalArgumentsCount; + int instructionPointer; + + QString source() const; + QString function() const; + inline QV4::ExecutionContext *context() const { + return static_cast<ExecutionContext *>(&jsFrame->context); + } + int lineNumber() const; + + inline QV4::Heap::CallContext *callContext() const { + Heap::ExecutionContext *ctx = static_cast<ExecutionContext &>(jsFrame->context).d();\ + while (ctx->type != Heap::ExecutionContext::Type_CallContext) + ctx = ctx->outer; + return static_cast<Heap::CallContext *>(ctx); + } + ReturnedValue thisObject() const; +}; + + + struct Q_QML_EXPORT ExecutionEngine : public EngineBase { private: @@ -100,7 +125,6 @@ private: public: ExecutableAllocator *executableAllocator; ExecutableAllocator *regExpAllocator; - QScopedPointer<EvalISelFactory> iselFactory; WTF::BumpPointerAllocator *bumperPointerAllocator; // Used by Yarr Regex engine. @@ -183,7 +207,7 @@ public: Value *jsObjects; enum { NTypedArrayTypes = 9 }; // == TypedArray::NValues, avoid header dependency - GlobalContext *rootContext() const { return reinterpret_cast<GlobalContext *>(jsObjects + RootContext); } + ExecutionContext *rootContext() const { return reinterpret_cast<ExecutionContext *>(jsObjects + RootContext); } FunctionObject *objectCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Object_Ctor); } FunctionObject *stringCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + String_Ctor); } FunctionObject *numberCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Number_Ctor); } @@ -342,7 +366,7 @@ public: // bookkeeping. MultiplyWrappedQObjectMap *m_multiplyWrappedQObjects; - ExecutionEngine(EvalISelFactory *iselFactory = 0); + ExecutionEngine(); ~ExecutionEngine(); #if !QT_CONFIG(qml_debug) @@ -359,11 +383,10 @@ public: void setProfiler(Profiling::Profiler *profiler); #endif // QT_CONFIG(qml_debug) - ExecutionContext *pushGlobalContext(); - void pushContext(Heap::ExecutionContext *context); - void pushContext(ExecutionContext *context); - void popContext(); - ExecutionContext *parentContext(ExecutionContext *context) const; + void setCurrentContext(Heap::ExecutionContext *context); + ExecutionContext *currentContext() const { + return static_cast<ExecutionContext *>(¤tStackFrame->jsFrame->context); + } InternalClass *newInternalClass(const VTable *vtable, Object *prototype); @@ -413,7 +436,6 @@ public: StackTrace stackTrace(int frameLimit = -1) const; - StackFrame currentStackFrame() const; QUrl resolvedUrl(const QString &file); void requireArgumentsAccessors(int n); @@ -424,8 +446,6 @@ public: InternalClass *newClass(const InternalClass &other); - // Exception handling - Value *exceptionValue; StackTrace exceptionStackTrace; ReturnedValue throwError(const Value &value); @@ -455,11 +475,11 @@ public: bool metaTypeFromJS(const Value *value, int type, void *data); QV4::ReturnedValue metaTypeToJS(int type, const void *data); - bool checkStackLimits(Scope &scope); + bool checkStackLimits(); -private: - void failStackLimitCheck(Scope &scope); + static bool canJIT(); +private: #if QT_CONFIG(qml_debug) QScopedPointer<QV4::Debugging::Debugger> m_debugger; QScopedPointer<QV4::Profiling::Profiler> m_profiler; @@ -477,71 +497,12 @@ struct NoThrowEngine; #endif -inline void ExecutionEngine::pushContext(Heap::ExecutionContext *context) -{ - Q_ASSERT(currentContext && context); - Value *v = jsAlloca(2); - v[0] = Encode(context); - v[1] = Encode((int)(v - static_cast<Value *>(currentContext))); - currentContext = static_cast<ExecutionContext *>(v); - current = currentContext->d(); -} - -inline void ExecutionEngine::pushContext(ExecutionContext *context) -{ - pushContext(context->d()); -} - - -inline void ExecutionEngine::popContext() -{ - Q_ASSERT(jsStackTop > currentContext); - QV4::Value *offset = (currentContext + 1); - Q_ASSERT(offset->isInteger()); - int o = offset->integerValue(); - Q_ASSERT(o); - currentContext -= o; - current = currentContext->d(); -} - -inline ExecutionContext *ExecutionEngine::parentContext(ExecutionContext *context) const -{ - Value *offset = static_cast<Value *>(context) + 1; - Q_ASSERT(offset->isInteger()); - int o = offset->integerValue(); - return o ? context - o : 0; -} - -inline -void Heap::Base::mark(QV4::MarkStack *markStack) -{ - Q_ASSERT(inUse()); - const HeapItem *h = reinterpret_cast<const HeapItem *>(this); - Chunk *c = h->chunk(); - size_t index = h - c->realBase(); - Q_ASSERT(!Chunk::testBit(c->extendsBitmap, index)); - quintptr *bitmap = c->blackBitmap + Chunk::bitmapIndex(index); - quintptr bit = Chunk::bitForIndex(index); - if (!(*bitmap & bit)) { - *bitmap |= bit; - markStack->push(this); - } -} - -inline void Value::mark(MarkStack *markStack) -{ - Heap::Base *o = heapObject(); - if (o) - o->mark(markStack); -} - -inline void Managed::mark(MarkStack *markStack) +inline void ExecutionEngine::setCurrentContext(Heap::ExecutionContext *context) { - Q_ASSERT(m()); - m()->mark(markStack); + currentStackFrame->jsFrame->context = context; } -#define CHECK_STACK_LIMITS(v4, scope) if ((v4)->checkStackLimits(scope)) return; \ +#define CHECK_STACK_LIMITS(v4) if ((v4)->checkStackLimits()) return Encode::undefined(); \ ExecutionEngineCallDepthRecorder _executionEngineCallDepthRecorder(v4); struct ExecutionEngineCallDepthRecorder @@ -552,10 +513,10 @@ struct ExecutionEngineCallDepthRecorder ~ExecutionEngineCallDepthRecorder() { --ee->callDepth; } }; -inline bool ExecutionEngine::checkStackLimits(Scope &scope) +inline bool ExecutionEngine::checkStackLimits() { if (Q_UNLIKELY((jsStackTop > jsStackLimit) || (callDepth >= maxCallDepth))) { - failStackLimitCheck(scope); + throwRangeError(QStringLiteral("Maximum call stack size exceeded.")); return true; } diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h index 88f9dfd85c..e0f5f3ffb1 100644 --- a/src/qml/jsruntime/qv4enginebase_p.h +++ b/src/qml/jsruntime/qv4enginebase_p.h @@ -57,31 +57,36 @@ QT_BEGIN_NAMESPACE namespace QV4 { +struct CppStackFrame; + // Base class for the execution engine #if defined(Q_CC_MSVC) || defined(Q_CC_GNU) #pragma pack(push, 1) #endif -struct EngineBase { - Heap::ExecutionContext *current = 0; +struct Q_QML_EXPORT EngineBase { + + CppStackFrame *currentStackFrame = nullptr; - Value *jsStackTop = 0; + Value *jsStackTop = nullptr; quint8 hasException = false; quint8 writeBarrierActive = false; quint16 unused = 0; #if QT_POINTER_SIZE == 8 quint8 padding[4]; #endif - MemoryManager *memoryManager = 0; + MemoryManager *memoryManager = nullptr; Runtime runtime; qint32 callDepth = 0; - Value *jsStackLimit = 0; - Value *jsStackBase = 0; + Value *jsStackLimit = nullptr; + Value *jsStackBase = nullptr; + + IdentifierTable *identifierTable = nullptr; + Object *globalObject = nullptr; - ExecutionContext *currentContext = 0; - IdentifierTable *identifierTable = 0; - Object *globalObject = 0; + // Exception handling + Value *exceptionValue = nullptr; enum { Class_Empty, @@ -90,7 +95,7 @@ struct EngineBase { Class_SimpleArrayData, Class_SparseArrayData, Class_ExecutionContext, - Class_SimpleCallContext, + Class_CallContext, Class_Object, Class_ArrayObject, Class_FunctionObject, @@ -115,8 +120,8 @@ struct EngineBase { #endif Q_STATIC_ASSERT(std::is_standard_layout<EngineBase>::value); -Q_STATIC_ASSERT(offsetof(EngineBase, current) == 0); -Q_STATIC_ASSERT(offsetof(EngineBase, jsStackTop) == offsetof(EngineBase, current) + QT_POINTER_SIZE); +Q_STATIC_ASSERT(offsetof(EngineBase, currentStackFrame) == 0); +Q_STATIC_ASSERT(offsetof(EngineBase, jsStackTop) == offsetof(EngineBase, currentStackFrame) + QT_POINTER_SIZE); Q_STATIC_ASSERT(offsetof(EngineBase, hasException) == offsetof(EngineBase, jsStackTop) + QT_POINTER_SIZE); Q_STATIC_ASSERT(offsetof(EngineBase, memoryManager) == offsetof(EngineBase, hasException) + QT_POINTER_SIZE); Q_STATIC_ASSERT(offsetof(EngineBase, runtime) == offsetof(EngineBase, memoryManager) + QT_POINTER_SIZE); diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp index b3bd28e18b..90e158ba37 100644 --- a/src/qml/jsruntime/qv4errorobject.cpp +++ b/src/qml/jsruntime/qv4errorobject.cpp @@ -51,7 +51,6 @@ #include <private/qqmljslexer_p.h> #include <private/qqmljsparser_p.h> #include <private/qqmljsast_p.h> -#include <qv4jsir_p.h> #include <qv4codegen_p.h> #ifndef Q_OS_WIN @@ -107,6 +106,7 @@ void Heap::ErrorObject::init(const Value &message, ErrorType t) void Heap::ErrorObject::init(const Value &message, const QString &fileName, int line, int column, ErrorObject::ErrorType t) { + Q_UNUSED(fileName); // #### Object::init(); errorType = t; @@ -123,10 +123,9 @@ void Heap::ErrorObject::init(const Value &message, const QString &fileName, int frame.column = column; e->d()->stackTrace->prepend(frame); - if (!e->d()->stackTrace->isEmpty()) { - setProperty(scope.engine, QV4::ErrorObject::Index_FileName, scope.engine->newString(e->d()->stackTrace->at(0).source)); - setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Primitive::fromInt32(e->d()->stackTrace->at(0).line)); - } + Q_ASSERT(!e->d()->stackTrace->isEmpty()); + setProperty(scope.engine, QV4::ErrorObject::Index_FileName, scope.engine->newString(e->d()->stackTrace->at(0).source)); + setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Primitive::fromInt32(e->d()->stackTrace->at(0).line)); if (!message.isUndefined()) setProperty(scope.engine, QV4::ErrorObject::Index_Message, message); @@ -153,11 +152,12 @@ const char *ErrorObject::className(Heap::ErrorObject::ErrorType t) Q_UNREACHABLE(); } -void ErrorObject::method_get_stack(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ErrorObject::method_get_stack(const FunctionObject *b, const Value *thisObject, const Value *, int) { - Scoped<ErrorObject> This(scope, callData->thisObject); + ExecutionEngine *v4 = b->engine(); + const ErrorObject *This = thisObject->as<ErrorObject>(); if (!This) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); if (!This->d()->stack) { QString trace; for (int i = 0; i < This->d()->stackTrace->count(); ++i) { @@ -168,9 +168,9 @@ void ErrorObject::method_get_stack(const BuiltinFunction *, Scope &scope, CallDa if (frame.line >= 0) trace += QLatin1Char(':') + QString::number(frame.line); } - This->d()->stack.set(scope.engine, scope.engine->newString(trace)); + This->d()->stack.set(v4, v4->newString(trace)); } - scope.result = This->d()->stack; + return This->d()->stack->asReturnedValue(); } DEFINE_OBJECT_VTABLE(ErrorObject); @@ -233,15 +233,15 @@ void Heap::ErrorCtor::init(QV4::ExecutionContext *scope, const QString &name) Heap::FunctionObject::init(scope, name); } -void ErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue ErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - ScopedValue v(scope, callData->argument(0)); - scope.result = ErrorObject::create<ErrorObject>(scope.engine, v); + Value v = argc ? *argv : Primitive::undefinedValue(); + return ErrorObject::create<ErrorObject>(f->engine(), v)->asReturnedValue(); } -void ErrorCtor::call(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue ErrorCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) { - static_cast<const Object *>(that)->construct(scope, callData); + return f->callAsConstructor(argv, argc); } void Heap::EvalErrorCtor::init(QV4::ExecutionContext *scope) @@ -249,10 +249,10 @@ void Heap::EvalErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("EvalError")); } -void EvalErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue EvalErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - ScopedValue v(scope, callData->argument(0)); - scope.result = ErrorObject::create<EvalErrorObject>(scope.engine, v); + Value v = argc ? *argv : Primitive::undefinedValue(); + return ErrorObject::create<EvalErrorObject>(f->engine(), v)->asReturnedValue(); } void Heap::RangeErrorCtor::init(QV4::ExecutionContext *scope) @@ -260,10 +260,10 @@ void Heap::RangeErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("RangeError")); } -void RangeErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue RangeErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - ScopedValue v(scope, callData->argument(0)); - scope.result = ErrorObject::create<RangeErrorObject>(scope.engine, v); + Value v = argc ? *argv : Primitive::undefinedValue(); + return ErrorObject::create<RangeErrorObject>(f->engine(), v)->asReturnedValue(); } void Heap::ReferenceErrorCtor::init(QV4::ExecutionContext *scope) @@ -271,10 +271,10 @@ void Heap::ReferenceErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("ReferenceError")); } -void ReferenceErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue ReferenceErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - ScopedValue v(scope, callData->argument(0)); - scope.result = ErrorObject::create<ReferenceErrorObject>(scope.engine, v); + Value v = argc ? *argv : Primitive::undefinedValue(); + return ErrorObject::create<ReferenceErrorObject>(f->engine(), v)->asReturnedValue(); } void Heap::SyntaxErrorCtor::init(QV4::ExecutionContext *scope) @@ -282,10 +282,10 @@ void Heap::SyntaxErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("SyntaxError")); } -void SyntaxErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue SyntaxErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - ScopedValue v(scope, callData->argument(0)); - scope.result = ErrorObject::create<SyntaxErrorObject>(scope.engine, v); + Value v = argc ? *argv : Primitive::undefinedValue(); + return ErrorObject::create<SyntaxErrorObject>(f->engine(), v)->asReturnedValue(); } void Heap::TypeErrorCtor::init(QV4::ExecutionContext *scope) @@ -293,10 +293,10 @@ void Heap::TypeErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("TypeError")); } -void TypeErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue TypeErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - ScopedValue v(scope, callData->argument(0)); - scope.result = ErrorObject::create<TypeErrorObject>(scope.engine, v); + Value v = argc ? *argv : Primitive::undefinedValue(); + return ErrorObject::create<TypeErrorObject>(f->engine(), v)->asReturnedValue(); } void Heap::URIErrorCtor::init(QV4::ExecutionContext *scope) @@ -304,10 +304,10 @@ void Heap::URIErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("URIError")); } -void URIErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue URIErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - ScopedValue v(scope, callData->argument(0)); - scope.result = ErrorObject::create<URIErrorObject>(scope.engine, v); + Value v = argc ? *argv : Primitive::undefinedValue(); + return ErrorObject::create<URIErrorObject>(f->engine(), v)->asReturnedValue(); } void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj, Heap::ErrorObject::ErrorType t) @@ -323,12 +323,14 @@ void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj, He obj->defineDefaultProperty(engine->id_toString(), method_toString, 0); } -void ErrorPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ErrorPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) { - Object *o = callData->thisObject.as<Object>(); + ExecutionEngine *v4 = b->engine(); + const Object *o = thisObject->as<Object>(); if (!o) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); + Scope scope(v4); ScopedValue name(scope, o->get(scope.engine->id_name())); QString qname; if (name->isUndefined()) @@ -351,5 +353,5 @@ void ErrorPrototype::method_toString(const BuiltinFunction *, Scope &scope, Call str = qname + QLatin1String(": ") + qmessage; } - scope.result = scope.engine->newString(str)->asReturnedValue(); + return scope.engine->newString(str)->asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h index d556617b48..a5ee0eb886 100644 --- a/src/qml/jsruntime/qv4errorobject_p.h +++ b/src/qml/jsruntime/qv4errorobject_p.h @@ -67,7 +67,7 @@ namespace Heap { Member(class, Pointer, String *, stack) DECLARE_HEAP_OBJECT(ErrorObject, Object) { - DECLARE_MARK_TABLE(ErrorObject); + DECLARE_MARKOBJECTS(ErrorObject); enum ErrorType { Error, EvalError, @@ -175,7 +175,7 @@ struct ErrorObject: Object { static const char *className(Heap::ErrorObject::ErrorType t); - static void method_get_stack(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_get_stack(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; template<> @@ -229,50 +229,50 @@ struct ErrorCtor: FunctionObject { V4_OBJECT2(ErrorCtor, FunctionObject) - static void construct(const Managed *, Scope &scope, CallData *callData); - static void call(const Managed *that, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct EvalErrorCtor: ErrorCtor { V4_OBJECT2(EvalErrorCtor, ErrorCtor) - static void construct(const Managed *m, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); }; struct RangeErrorCtor: ErrorCtor { V4_OBJECT2(RangeErrorCtor, ErrorCtor) - static void construct(const Managed *, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); }; struct ReferenceErrorCtor: ErrorCtor { V4_OBJECT2(ReferenceErrorCtor, ErrorCtor) - static void construct(const Managed *m, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); }; struct SyntaxErrorCtor: ErrorCtor { V4_OBJECT2(SyntaxErrorCtor, ErrorCtor) - static void construct(const Managed *m, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); }; struct TypeErrorCtor: ErrorCtor { V4_OBJECT2(TypeErrorCtor, ErrorCtor) - static void construct(const Managed *m, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); }; struct URIErrorCtor: ErrorCtor { V4_OBJECT2(URIErrorCtor, ErrorCtor) - static void construct(const Managed *m, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); }; @@ -286,7 +286,7 @@ struct ErrorPrototype : ErrorObject void init(ExecutionEngine *engine, Object *ctor) { init(engine, ctor, this, Heap::ErrorObject::Error); } static void init(ExecutionEngine *engine, Object *ctor, Object *obj, Heap::ErrorObject::ErrorType t); - static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; struct EvalErrorPrototype : ErrorObject diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 4b88d85e68..83e861138b 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qv4function_p.h" +#include "qv4functionobject_p.h" #include "qv4managed_p.h" #include "qv4string_p.h" #include "qv4value_p.h" @@ -45,76 +46,83 @@ #include "qv4lookup_p.h" #include <private/qv4mm_p.h> #include <private/qv4identifiertable_p.h> +#include <assembler/MacroAssemblerCodeRef.h> QT_BEGIN_NAMESPACE using namespace QV4; -Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function, - ReturnedValue (*codePtr)(ExecutionEngine *, const uchar *)) +Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function, Code codePtr) : compiledFunction(function) , compilationUnit(unit) , code(codePtr) - , codeData(0) + , codeData(function->code()) + , jittedCode(nullptr) + , codeRef(nullptr) , hasQmlDependencies(function->hasQmlDependencies()) { - internalClass = engine->internalClasses[EngineBase::Class_Empty]; - const quint32_le *formalsIndices = compiledFunction->formalsTable(); - // iterate backwards, so we get the right ordering for duplicate names - Scope scope(engine); - ScopedString arg(scope); - for (int i = static_cast<int>(compiledFunction->nFormals - 1); i >= 0; --i) { - arg = compilationUnit->runtimeStrings[formalsIndices[i]]; - while (1) { - InternalClass *newClass = internalClass->addMember(arg, Attr_NotConfigurable); - if (newClass != internalClass) { - internalClass = newClass; - break; - } - // duplicate arguments, need some trick to store them - MemoryManager *mm = engine->memoryManager; - arg = mm->alloc<String>(arg->d(), engine->newString(QString(0xfffe))); - } - } - nFormals = compiledFunction->nFormals; + Q_UNUSED(engine); + + internalClass = engine->internalClasses[EngineBase::Class_CallContext]; + // first locals const quint32_le *localsIndices = compiledFunction->localsTable(); for (quint32 i = 0; i < compiledFunction->nLocals; ++i) internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable); - canUseSimpleCall = compiledFunction->flags & CompiledData::Function::CanUseSimpleCall; + const quint32_le *formalsIndices = compiledFunction->formalsTable(); + for (quint32 i = 0; i < compiledFunction->nFormals; ++i) + internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[formalsIndices[i]]), Attr_NotConfigurable); + + nFormals = compiledFunction->nFormals; } Function::~Function() { + delete codeRef; } void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> ¶meters) { - internalClass = engine->internalClasses[EngineBase::Class_Empty]; + QStringList parameterNames; - // iterate backwards, so we get the right ordering for duplicate names - Scope scope(engine); - ScopedString arg(scope); - for (int i = parameters.size() - 1; i >= 0; --i) { - arg = engine->newString(QString::fromUtf8(parameters.at(i))); - while (1) { - InternalClass *newClass = internalClass->addMember(arg, Attr_NotConfigurable); - if (newClass != internalClass) { - internalClass = newClass; + // Resolve duplicate parameter names: + for (int i = 0, ei = parameters.count(); i != ei; ++i) { + const QByteArray ¶m = parameters.at(i); + int duplicate = -1; + + for (int j = i - 1; j >= 0; --j) { + const QByteArray &prevParam = parameters.at(j); + if (param == prevParam) { + duplicate = j; break; } - // duplicate arguments, need some trick to store them - arg = engine->memoryManager->alloc<String>(arg->d(), engine->newString(QString(0xfffe))); } + + if (duplicate == -1) { + parameterNames.append(QString::fromUtf8(param)); + } else { + const QString &dup = parameterNames[duplicate]; + parameterNames.append(dup); + parameterNames[duplicate] = + QString(0xfffe) + QString::number(duplicate) + dup; + } + + } + + internalClass = engine->internalClasses[EngineBase::Class_CallContext]; + + Scope scope(engine); + ScopedString arg(scope); + for (const QString ¶meterName : parameterNames) { + arg = engine->newString(parameterName); + internalClass = internalClass->addMember(arg, Attr_NotConfigurable); } nFormals = parameters.size(); const quint32_le *localsIndices = compiledFunction->localsTable(); for (quint32 i = 0; i < compiledFunction->nLocals; ++i) internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable); - - canUseSimpleCall = false; } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index b11c8af94a..0e61be5115 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -54,6 +54,11 @@ #include <private/qqmlglobal_p.h> #include <private/qv4compileddata_p.h> #include <private/qv4context_p.h> +#include <private/qv4vme_moth_p.h> + +namespace JSC { +class MacroAssemblerCodeRef; +} QT_BEGIN_NAMESPACE @@ -63,17 +68,24 @@ struct Q_QML_EXPORT Function { const CompiledData::Function *compiledFunction; CompiledData::CompilationUnit *compilationUnit; - ReturnedValue (*code)(ExecutionEngine *, const uchar *); + ReturnedValue call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context) { + return Moth::VME::exec(this, thisObject, argv, argc, context); + } + + typedef ReturnedValue (*Code)(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc); + Code code; const uchar *codeData; + typedef ReturnedValue (*JittedCode)(CppStackFrame *, ExecutionEngine *); + JittedCode jittedCode; + JSC::MacroAssemblerCodeRef *codeRef; + // first nArguments names in internalClass are the actual arguments InternalClass *internalClass; uint nFormals; bool hasQmlDependencies; - bool canUseSimpleCall; - Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function, - ReturnedValue (*codePtr)(ExecutionEngine *, const uchar *)); + Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function, Code codePtr); ~Function(); // used when dynamically assigning signal handlers (QQmlConnection) @@ -86,24 +98,13 @@ struct Q_QML_EXPORT Function { inline bool usesArgumentsObject() const { return compiledFunction->flags & CompiledData::Function::UsesArgumentsObject; } inline bool isStrict() const { return compiledFunction->flags & CompiledData::Function::IsStrict; } - inline bool isNamedExpression() const { return compiledFunction->flags & CompiledData::Function::IsNamedExpression; } - - inline bool canUseSimpleFunction() const { return canUseSimpleCall; } QQmlSourceLocation sourceLocation() const { return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column); } - }; - -inline unsigned int Heap::SimpleCallContext::formalParameterCount() const -{ - return v4Function ? v4Function->nFormals : 0; -} - - } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 45fdde98f7..4293d43791 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -38,8 +38,6 @@ ****************************************************************************/ #include "qv4object_p.h" -#include "qv4jsir_p.h" -#include "qv4isel_p.h" #include "qv4objectproto_p.h" #include "qv4stringobject_p.h" #include "qv4function_p.h" @@ -55,9 +53,10 @@ #include <private/qqmljsast_p.h> #include <private/qqmljavascriptexpression_p.h> #include <private/qqmlengine_p.h> -#include <qv4codegen_p.h> +#include <qv4runtimecodegen_p.h> #include "private/qlocale_tools_p.h" #include "private/qqmlbuiltinfunctions_p.h" +#include <private/qv4jscall_p.h> #include <QtCore/QDebug> #include <algorithm> @@ -69,10 +68,25 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(FunctionObject); -Q_STATIC_ASSERT((Heap::FunctionObject::markTable & Heap::Object::markTable) == Heap::Object::markTable); +void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, + ReturnedValue (*code)(const QV4::FunctionObject *, const Value *thisObject, const Value *argv, int argc)) +{ + jsCall = code; + jsConstruct = QV4::FunctionObject::callAsConstructor; + + Object::init(); + function = nullptr; + this->scope.set(scope->engine(), scope->d()); + Scope s(scope->engine()); + ScopedFunctionObject f(s, this); + f->init(name, false); +} void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, bool createProto) { + jsCall = reinterpret_cast<const ObjectVTable *>(vtable())->call; + jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor; + Object::init(); function = nullptr; this->scope.set(scope->engine(), scope->d()); @@ -83,6 +97,9 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, void Heap::FunctionObject::init(QV4::ExecutionContext *scope, Function *function, bool createProto) { + jsCall = reinterpret_cast<const ObjectVTable *>(vtable())->call; + jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor; + Object::init(); this->function = function; function->compilationUnit->addref(); @@ -102,6 +119,9 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, const QString &nam void Heap::FunctionObject::init() { + jsCall = reinterpret_cast<const ObjectVTable *>(vtable())->call; + jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor; + Object::init(); function = nullptr; this->scope.set(internalClass->engine, internalClass->engine->rootContext()->d()); @@ -141,14 +161,14 @@ ReturnedValue FunctionObject::name() const return get(scope()->internalClass->engine->id_name()); } -void FunctionObject::construct(const Managed *that, Scope &scope, CallData *) +ReturnedValue FunctionObject::callAsConstructor(const FunctionObject *f, const Value *, int) { - scope.result = static_cast<const FunctionObject *>(that)->engine()->throwTypeError(); + return f->engine()->throwTypeError(); } -void FunctionObject::call(const Managed *, Scope &scope, CallData *) +ReturnedValue FunctionObject::call(const FunctionObject *, const Value *, const Value *, int) { - scope.result = Encode::undefined(); + return Encode::undefined(); } Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *scope, Function *function) @@ -179,24 +199,22 @@ void Heap::FunctionCtor::init(QV4::ExecutionContext *scope) } // 15.3.2 -void FunctionCtor::construct(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue FunctionCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - Scoped<FunctionCtor> f(scope, static_cast<const FunctionCtor *>(that)); + Scope scope(f->engine()); QString arguments; QString body; - if (callData->argc > 0) { - for (int i = 0; i < callData->argc - 1; ++i) { + if (argc > 0) { + for (int i = 0, ei = argc - 1; i < ei; ++i) { if (i) arguments += QLatin1String(", "); - arguments += callData->args[i].toQString(); + arguments += argv[i].toQString(); } - body = callData->args[callData->argc - 1].toQString(); - } - if (scope.engine->hasException) { - scope.result = Encode::undefined(); - return; + body = argv[argc - 1].toQString(); } + if (scope.engine->hasException) + return Encode::undefined(); QString function = QLatin1String("function(") + arguments + QLatin1String("){") + body + QLatin1Char('}'); @@ -207,36 +225,30 @@ void FunctionCtor::construct(const Managed *that, Scope &scope, CallData *callDa const bool parsed = parser.parseExpression(); - if (!parsed) { - scope.result = scope.engine->throwSyntaxError(QLatin1String("Parse error")); - return; - } + if (!parsed) + return scope.engine->throwSyntaxError(QLatin1String("Parse error")); - using namespace QQmlJS::AST; - FunctionExpression *fe = QQmlJS::AST::cast<FunctionExpression *>(parser.rootNode()); - if (!fe) { - scope.result = scope.engine->throwSyntaxError(QLatin1String("Parse error")); - return; - } + QQmlJS::AST::FunctionExpression *fe = QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression *>(parser.rootNode()); + if (!fe) + return scope.engine->throwSyntaxError(QLatin1String("Parse error")); - IR::Module module(scope.engine->debugger() != 0); + Compiler::Module module(scope.engine->debugger() != 0); - QQmlJS::RuntimeCodegen cg(scope.engine, f->strictMode()); + Compiler::JSUnitGenerator jsGenerator(&module); + RuntimeCodegen cg(scope.engine, &jsGenerator, false); cg.generateFromFunctionExpression(QString(), function, fe, &module); - Compiler::JSUnitGenerator jsGenerator(&module); - QScopedPointer<EvalInstructionSelection> isel(scope.engine->iselFactory->create(QQmlEnginePrivate::get(scope.engine), scope.engine->executableAllocator, &module, &jsGenerator)); - QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = isel->compile(); + QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = cg.generateCompilationUnit(); Function *vmf = compilationUnit->linkToEngine(scope.engine); ExecutionContext *global = scope.engine->rootContext(); - scope.result = FunctionObject::createScriptFunction(global, vmf); + return Encode(FunctionObject::createScriptFunction(global, vmf)); } // 15.3.1: This is equivalent to new Function(...) -void FunctionCtor::call(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue FunctionCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) { - construct(that, scope, callData); + return callAsConstructor(f, argv, argc); } DEFINE_OBJECT_VTABLE(FunctionPrototype); @@ -263,152 +275,117 @@ void FunctionPrototype::init(ExecutionEngine *engine, Object *ctor) } -void FunctionPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue FunctionPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) { - FunctionObject *fun = callData->thisObject.as<FunctionObject>(); + ExecutionEngine *v4 = b->engine(); + const FunctionObject *fun = thisObject->as<FunctionObject>(); if (!fun) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); - scope.result = scope.engine->newString(QStringLiteral("function() { [code] }")); + return Encode(v4->newString(QStringLiteral("function() { [code] }"))); } -void FunctionPrototype::method_apply(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - FunctionObject *o = callData->thisObject.as<FunctionObject>(); - if (!o) - THROW_TYPE_ERROR(); + ExecutionEngine *v4 = b->engine(); + const FunctionObject *f = thisObject->as<FunctionObject>(); + if (!f) + return v4->throwTypeError(); + thisObject = argc ? argv : nullptr; + if (argc < 2 || argv[1].isNullOrUndefined()) + return f->call(thisObject, argv, 0); - ScopedValue arg(scope, callData->argument(1)); + Object *arr = argv[1].objectValue(); + if (!arr) + return v4->throwTypeError(); - ScopedObject arr(scope, arg); - - quint32 len; - if (!arr) { - len = 0; - if (!arg->isNullOrUndefined()) - THROW_TYPE_ERROR(); - } else { - len = arr->getLength(); - } - - ScopedCallData cData(scope, len); + uint len = arr->getLength(); + Scope scope(v4); + Value *arguments = v4->jsAlloca(len); if (len) { if (ArgumentsObject::isNonStrictArgumentsObject(arr) && !arr->cast<ArgumentsObject>()->fullyCreated()) { QV4::ArgumentsObject *a = arr->cast<ArgumentsObject>(); - int l = qMin(len, (uint)a->d()->context->callData->argc); - memcpy(cData->args, a->d()->context->callData->args, l*sizeof(Value)); + int l = qMin(len, (uint)a->d()->context->argc()); + memcpy(arguments, a->d()->context->args(), l*sizeof(Value)); for (quint32 i = l; i < len; ++i) - cData->args[i] = Primitive::undefinedValue(); + arguments[i] = Primitive::undefinedValue(); } else if (arr->arrayType() == Heap::ArrayData::Simple && !arr->protoHasArray()) { auto sad = static_cast<Heap::SimpleArrayData *>(arr->arrayData()); uint alen = sad ? sad->values.size : 0; if (alen > len) alen = len; for (uint i = 0; i < alen; ++i) - cData->args[i] = sad->data(i); + arguments[i] = sad->data(i); for (quint32 i = alen; i < len; ++i) - cData->args[i] = Primitive::undefinedValue(); + arguments[i] = Primitive::undefinedValue(); } else { for (quint32 i = 0; i < len; ++i) - cData->args[i] = arr->getIndexed(i); + arguments[i] = arr->getIndexed(i); } } - cData->thisObject = callData->argument(0); - o->call(scope, cData); + return f->call(thisObject, arguments, len); } -void FunctionPrototype::method_call(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue FunctionPrototype::method_call(const QV4::FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - FunctionObject *o = callData->thisObject.as<FunctionObject>(); - if (!o) - THROW_TYPE_ERROR(); + if (!thisObject->isFunctionObject()) + return b->engine()->throwTypeError(); - ScopedCallData cData(scope, callData->argc ? callData->argc - 1 : 0); - if (callData->argc) { - for (int i = 1; i < callData->argc; ++i) - cData->args[i - 1] = callData->args[i]; - } - cData->thisObject = callData->argument(0); + const FunctionObject *f = static_cast<const FunctionObject *>(thisObject); - o->call(scope, cData); + thisObject = argc ? argv : nullptr; + if (argc) { + ++argv; + --argc; + } + return f->call(thisObject, argv, argc); } -void FunctionPrototype::method_bind(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue FunctionPrototype::method_bind(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - FunctionObject *target = callData->thisObject.as<FunctionObject>(); + QV4::Scope scope(b); + ScopedFunctionObject target(scope, thisObject); if (!target) - THROW_TYPE_ERROR(); + return scope.engine->throwTypeError(); - ScopedValue boundThis(scope, callData->argument(0)); + ScopedValue boundThis(scope, argc ? argv[0] : Primitive::undefinedValue()); Scoped<MemberData> boundArgs(scope, (Heap::MemberData *)0); - if (callData->argc > 1) { - boundArgs = MemberData::allocate(scope.engine, callData->argc - 1); - boundArgs->d()->values.size = callData->argc - 1; - for (uint i = 0; i < static_cast<uint>(callData->argc - 1); ++i) - boundArgs->set(scope.engine, i, callData->args[i + 1]); + if (argc > 1) { + boundArgs = MemberData::allocate(scope.engine, argc - 1); + boundArgs->d()->values.size = argc - 1; + for (uint i = 0, ei = static_cast<uint>(argc - 1); i < ei; ++i) + boundArgs->set(scope.engine, i, argv[i + 1]); } ExecutionContext *global = scope.engine->rootContext(); - scope.result = BoundFunction::create(global, target, boundThis, boundArgs); + return BoundFunction::create(global, target, boundThis, boundArgs)->asReturnedValue(); } DEFINE_OBJECT_VTABLE(ScriptFunction); -void ScriptFunction::construct(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue ScriptFunction::callAsConstructor(const FunctionObject *fo, const Value *argv, int argc) { - ExecutionEngine *v4 = scope.engine; - if (Q_UNLIKELY(v4->hasException)) { - scope.result = Encode::undefined(); - return; - } - CHECK_STACK_LIMITS(v4, scope); + ExecutionEngine *v4 = fo->engine(); + const ScriptFunction *f = static_cast<const ScriptFunction *>(fo); - ExecutionContextSaver ctxSaver(scope); + Scope scope(v4); + InternalClass *ic = f->classForConstructor(); + ScopedValue thisObject(scope, v4->memoryManager->allocObject<Object>(ic)); - Scoped<ScriptFunction> f(scope, static_cast<const ScriptFunction *>(that)); + ReturnedValue result = Moth::VME::exec(fo, thisObject, argv, argc); - InternalClass *ic = f->classForConstructor(); - ScopedObject proto(scope, ic->prototype); - ScopedObject obj(scope, v4->newObject(ic, proto)); - callData->thisObject = obj.asReturnedValue(); - - QV4::Function *v4Function = f->function(); - Q_ASSERT(v4Function); - - ScopedContext c(scope, f->scope()); - if (v4Function->canUseSimpleCall) - c->simpleCall(scope, callData, v4Function); - else - c->call(scope, callData, v4Function, f); - - if (Q_UNLIKELY(v4->hasException)) { - scope.result = Encode::undefined(); - } else if (!scope.result.isObject()) { - scope.result = obj.asReturnedValue(); - } + if (Q_UNLIKELY(v4->hasException)) + return Encode::undefined(); + else if (!Value::fromReturnedValue(result).isObject()) + return thisObject->asReturnedValue(); + return result; } -void ScriptFunction::call(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue ScriptFunction::call(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc) { - ExecutionEngine *v4 = scope.engine; - if (Q_UNLIKELY(v4->hasException)) { - scope.result = Encode::undefined(); - return; - } - CHECK_STACK_LIMITS(v4, scope); - - Scoped<ScriptFunction> f(scope, static_cast<const ScriptFunction *>(that)); - - QV4::Function *v4Function = f->function(); - Q_ASSERT(v4Function); - - ScopedContext c(scope, f->scope()); - if (v4Function->canUseSimpleCall) - c->simpleCall(scope, callData, v4Function); - else - c->call(scope, callData, v4Function, f); + return Moth::VME::exec(fo, thisObject, argv, argc); } void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function) @@ -429,7 +406,7 @@ void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function Q_ASSERT(internalClass && internalClass->find(s.engine->id_length()) == Index_Length); setProperty(s.engine, Index_Length, Primitive::fromInt32(f->formalParameterCount())); - if (scope->d()->strictMode) { + if (function->isStrict()) { ScopedProperty pd(s); pd->value = s.engine->thrower(); pd->set = s.engine->thrower(); @@ -455,49 +432,23 @@ InternalClass *ScriptFunction::classForConstructor() const DEFINE_OBJECT_VTABLE(BuiltinFunction); -void Heap::BuiltinFunction::init(QV4::ExecutionContext *scope, QV4::String *name, void (*code)(const QV4::BuiltinFunction *, Scope &, CallData *)) +void Heap::BuiltinFunction::init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(const QV4::BuiltinFunction *, CallData *)) { Heap::FunctionObject::init(scope, name); this->code = code; } -void BuiltinFunction::construct(const Managed *f, Scope &scope, CallData *) -{ - scope.result = static_cast<const BuiltinFunction *>(f)->internalClass()->engine->throwTypeError(); -} - -void BuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue BuiltinFunction::callAsConstructor(const QV4::FunctionObject *f, const Value *, int) { - const BuiltinFunction *f = static_cast<const BuiltinFunction *>(that); - ExecutionEngine *v4 = scope.engine; - if (v4->hasException) { - scope.result = Encode::undefined(); - return; - } - f->d()->code(f, scope, callData); + return f->engine()->throwTypeError(); } - -void IndexedBuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue BuiltinFunction::call(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc) { - const IndexedBuiltinFunction *f = static_cast<const IndexedBuiltinFunction *>(that); - ExecutionEngine *v4 = scope.engine; - if (v4->hasException) { - scope.result = Encode::undefined(); - return; - } - CHECK_STACK_LIMITS(v4, scope); - - ExecutionContextSaver ctxSaver(scope); - - SimpleCallContext::Data *ctx = v4->memoryManager->allocSimpleCallContext(); - ctx->strictMode = f->scope()->strictMode; // ### needed? scope or parent context? - ctx->callData = callData; - v4->pushContext(ctx); - Q_ASSERT(v4->current == ctx); - - scope.result = f->d()->code(static_cast<QV4::CallContext *>(v4->currentContext), f->d()->index); - v4->memoryManager->freeSimpleCallContext(); + const BuiltinFunction *f = static_cast<const BuiltinFunction *>(fo); + Scope scope(f->engine()); + JSCallData callData(scope, argc, argv, thisObject); + return f->d()->code(f, callData.callData()); } DEFINE_OBJECT_VTABLE(IndexedBuiltinFunction); @@ -530,43 +481,43 @@ void Heap::BoundFunction::init(QV4::ExecutionContext *scope, QV4::FunctionObject f->insertMember(s.engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); } -void BoundFunction::call(const Managed *that, Scope &scope, CallData *dd) +ReturnedValue BoundFunction::call(const FunctionObject *fo, const Value *, const Value *argv, int argc) { - const BoundFunction *f = static_cast<const BoundFunction *>(that); - if (scope.hasException()) { - scope.result = Encode::undefined(); - return; - } + const BoundFunction *f = static_cast<const BoundFunction *>(fo); + Scope scope(f->engine()); + + if (scope.hasException()) + return Encode::undefined(); Scoped<MemberData> boundArgs(scope, f->boundArgs()); - ScopedCallData callData(scope, (boundArgs ? boundArgs->size() : 0) + dd->argc); - callData->thisObject = f->boundThis(); - Value *argp = callData->args; + ScopedFunctionObject target(scope, f->target()); + JSCallData jsCallData(scope, (boundArgs ? boundArgs->size() : 0) + argc); + *jsCallData->thisObject = f->boundThis(); + Value *argp = jsCallData->args; if (boundArgs) { memcpy(argp, boundArgs->data(), boundArgs->size()*sizeof(Value)); argp += boundArgs->size(); } - memcpy(argp, dd->args, dd->argc*sizeof(Value)); - ScopedFunctionObject t(scope, f->target()); - t->call(scope, callData); + memcpy(argp, argv, argc*sizeof(Value)); + return target->call(jsCallData); } -void BoundFunction::construct(const Managed *that, Scope &scope, CallData *dd) +ReturnedValue BoundFunction::callAsConstructor(const FunctionObject *fo, const Value *argv, int argc) { - const BoundFunction *f = static_cast<const BoundFunction *>(that); - if (scope.hasException()) { - scope.result = Encode::undefined(); - return; - } + const BoundFunction *f = static_cast<const BoundFunction *>(fo); + Scope scope(f->engine()); + + if (scope.hasException()) + return Encode::undefined(); Scoped<MemberData> boundArgs(scope, f->boundArgs()); - ScopedCallData callData(scope, (boundArgs ? boundArgs->size() : 0) + dd->argc); - Value *argp = callData->args; + ScopedFunctionObject target(scope, f->target()); + JSCallData jsCallData(scope, (boundArgs ? boundArgs->size() : 0) + argc); + Value *argp = jsCallData->args; if (boundArgs) { memcpy(argp, boundArgs->data(), boundArgs->size()*sizeof(Value)); argp += boundArgs->size(); } - memcpy(argp, dd->args, dd->argc*sizeof(Value)); - ScopedFunctionObject t(scope, f->target()); - t->construct(scope, callData); + memcpy(argp, argv, argc*sizeof(Value)); + return target->callAsConstructor(jsCallData); } diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index 6ce5734b6d..d61006a6b0 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -62,20 +62,29 @@ struct QQmlSourceLocation; namespace QV4 { struct BuiltinFunction; +struct IndexedBuiltinFunction; +struct JSCallData; + +typedef ReturnedValue (*jsCallFunction)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); +typedef ReturnedValue (*jsConstructFunction)(const FunctionObject *, const Value *argv, int argc); namespace Heap { + #define FunctionObjectMembers(class, Member) \ Member(class, Pointer, ExecutionContext *, scope) \ - Member(class, NoMark, Function *, function) + Member(class, NoMark, Function *, function) \ + Member(class, NoMark, jsCallFunction, jsCall) \ + Member(class, NoMark, jsConstructFunction, jsConstruct) DECLARE_HEAP_OBJECT(FunctionObject, Object) { - DECLARE_MARK_TABLE(FunctionObject); + DECLARE_MARKOBJECTS(FunctionObject); enum { Index_Prototype = 0, Index_ProtoConstructor = 0 }; + void init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(const QV4::FunctionObject *, const Value *thisObject, const Value *argv, int argc)); void init(QV4::ExecutionContext *scope, QV4::String *name = 0, bool createProto = false); void init(QV4::ExecutionContext *scope, QV4::Function *function, bool createProto = false); void init(QV4::ExecutionContext *scope, const QString &name, bool createProto = false); @@ -96,19 +105,13 @@ struct FunctionPrototype : FunctionObject { void init(); }; -struct Q_QML_EXPORT OldBuiltinFunction : FunctionObject { - void init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(QV4::CallContext *)); - ReturnedValue (*code)(QV4::CallContext *); -}; - struct Q_QML_EXPORT BuiltinFunction : FunctionObject { - void init(QV4::ExecutionContext *scope, QV4::String *name, void (*code)(const QV4::BuiltinFunction *, Scope &, CallData *)); - void (*code)(const QV4::BuiltinFunction *, Scope &, CallData *); + void init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(const QV4::BuiltinFunction *, CallData *)); + ReturnedValue (*code)(const QV4::BuiltinFunction *, CallData *); }; -struct IndexedBuiltinFunction : FunctionObject { - inline void init(QV4::ExecutionContext *scope, uint index, ReturnedValue (*code)(QV4::CallContext *ctx, uint index)); - ReturnedValue (*code)(QV4::CallContext *, uint index); +struct IndexedBuiltinFunction : BuiltinFunction { + inline void init(QV4::ExecutionContext *scope, uint index, ReturnedValue (*code)(const QV4::BuiltinFunction *, CallData *)); uint index; }; @@ -128,7 +131,7 @@ struct ScriptFunction : FunctionObject { Member(class, Pointer, MemberData *, boundArgs) DECLARE_HEAP_OBJECT(BoundFunction, FunctionObject) { - DECLARE_MARK_TABLE(BoundFunction); + DECLARE_MARKOBJECTS(BoundFunction); void init(QV4::ExecutionContext *scope, QV4::FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs); }; @@ -144,6 +147,7 @@ struct Q_QML_EXPORT FunctionObject: Object { V4_INTERNALCLASS(FunctionObject) V4_PROTOTYPE(functionPrototype) V4_NEEDS_DESTROY + enum { NInlineProperties = 1 }; Heap::ExecutionContext *scope() const { return d()->scope; } Function *function() const { return d()->function; } @@ -154,10 +158,16 @@ struct Q_QML_EXPORT FunctionObject: Object { void init(String *name, bool createProto); - using Object::construct; - using Object::call; - static void construct(const Managed *that, Scope &scope, CallData *); - static void call(const Managed *that, Scope &scope, CallData *d); + inline ReturnedValue callAsConstructor(const JSCallData &data) const; + ReturnedValue callAsConstructor(const Value *argv, int argc) const { + return d()->jsConstruct(this, argv, argc); + } + inline ReturnedValue call(const JSCallData &data) const; + ReturnedValue call(const Value *thisObject, const Value *argv, int argc) const { + return d()->jsCall(this, thisObject, argv, argc); + } + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function); @@ -178,8 +188,8 @@ struct FunctionCtor: FunctionObject { V4_OBJECT2(FunctionCtor, FunctionObject) - static void construct(const Managed *that, Scope &scope, CallData *callData); - static void call(const Managed *that, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct FunctionPrototype: FunctionObject @@ -188,52 +198,51 @@ struct FunctionPrototype: FunctionObject void init(ExecutionEngine *engine, Object *ctor); - static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_apply(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_call(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_bind(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_apply(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_bind(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; struct Q_QML_EXPORT BuiltinFunction : FunctionObject { V4_OBJECT2(BuiltinFunction, FunctionObject) V4_INTERNALCLASS(BuiltinFunction) - static Heap::BuiltinFunction *create(ExecutionContext *scope, String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *)) + static Heap::BuiltinFunction *create(ExecutionContext *scope, String *name, ReturnedValue (*code)(const BuiltinFunction *, CallData *)) { return scope->engine()->memoryManager->allocObject<BuiltinFunction>(scope, name, code); } - static void construct(const Managed *, Scope &scope, CallData *); - static void call(const Managed *that, Scope &scope, CallData *callData); -}; - -struct IndexedBuiltinFunction: FunctionObject -{ - V4_OBJECT2(IndexedBuiltinFunction, FunctionObject) - - static void construct(const Managed *m, Scope &scope, CallData *) + static Heap::FunctionObject *create(ExecutionContext *scope, String *name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)) { - scope.result = static_cast<const IndexedBuiltinFunction *>(m)->engine()->throwTypeError(); + return scope->engine()->memoryManager->allocObject<FunctionObject>(scope, name, code); } - static void call(const Managed *that, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; + +struct IndexedBuiltinFunction: BuiltinFunction +{ + V4_OBJECT2(IndexedBuiltinFunction, BuiltinFunction) }; void Heap::IndexedBuiltinFunction::init(QV4::ExecutionContext *scope, uint index, - ReturnedValue (*code)(QV4::CallContext *ctx, uint index)) + ReturnedValue (*code)(const QV4::BuiltinFunction *, CallData *)) { Heap::FunctionObject::init(scope); - this->index = index; this->code = code; + this->index = index; } struct ScriptFunction : FunctionObject { V4_OBJECT2(ScriptFunction, FunctionObject) V4_INTERNALCLASS(ScriptFunction) + enum { NInlineProperties = 3 }; - static void construct(const Managed *, Scope &scope, CallData *callData); - static void call(const Managed *that, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); InternalClass *classForConstructor() const; }; @@ -251,8 +260,8 @@ struct BoundFunction: FunctionObject { Value boundThis() const { return d()->boundThis; } Heap::MemberData *boundArgs() const { return d()->boundArgs; } - static void construct(const Managed *, Scope &scope, CallData *d); - static void call(const Managed *that, Scope &scope, CallData *dd); + static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; } diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index 8769519a59..090a164ef6 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -107,8 +107,8 @@ inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); } # if defined(Q_OS_LINUX) # define V4_ENABLE_JIT # endif -#elif defined(Q_PROCESSOR_MIPS_32) && defined(Q_OS_LINUX) -# define V4_ENABLE_JIT +//#elif defined(Q_PROCESSOR_MIPS_32) && defined(Q_OS_LINUX) +//# define V4_ENABLE_JIT #endif // Black list some platforms @@ -147,6 +147,12 @@ QT_BEGIN_NAMESPACE namespace QV4 { +namespace Compiler { + struct Module; + struct Context; + struct JSUnitGenerator; +} + namespace Heap { struct Base; struct MemberData; @@ -157,7 +163,6 @@ namespace Heap { struct ObjectPrototype; struct ExecutionContext; - struct GlobalContext; struct CallContext; struct ScriptFunction; @@ -182,12 +187,12 @@ namespace Heap { } class MemoryManager; +class ExecutableAllocator; struct String; struct Object; struct ObjectPrototype; struct ObjectIterator; struct ExecutionContext; -struct GlobalContext; struct CallContext; struct ScriptFunction; struct InternalClass; @@ -243,12 +248,6 @@ struct IdentifierTable; class RegExpCache; class MultiplyWrappedQObjectMap; -namespace Global { - enum { - ReservedArgumentCount = 6 - }; -} - enum PropertyFlag { Attr_Data = 0, Attr_Accessor = 0x1, @@ -349,11 +348,11 @@ struct PropertyAttributes } }; -struct StackFrame { +struct Q_QML_EXPORT StackFrame { QString source; QString function; - int line; - int column; + int line = -1; + int column = -1; }; typedef QVector<StackFrame> StackTrace; diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp index 0916e8e110..3214a716e8 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -47,12 +47,12 @@ #include "qv4script_p.h" #include "qv4scopedvalue_p.h" #include "qv4string_p.h" +#include "qv4jscall_p.h" #include <private/qqmljsengine_p.h> #include <private/qqmljslexer_p.h> #include <private/qqmljsparser_p.h> #include <private/qqmljsast_p.h> -#include <qv4jsir_p.h> #include <qv4codegen_p.h> #include "private/qlocale_tools_p.h" #include "private/qtools_p.h" @@ -338,72 +338,56 @@ void Heap::EvalFunction::init(QV4::ExecutionContext *scope) f->defineReadonlyProperty(s.engine->id_length(), Primitive::fromInt32(1)); } -void EvalFunction::evalCall(Scope &scope, CallData *callData, bool directCall) const +ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc, bool directCall) const { - if (callData->argc < 1) { - scope.result = Encode::undefined(); - return; - } + if (argc < 1) + return Encode::undefined(); ExecutionEngine *v4 = engine(); - ExecutionContextSaver ctxSaver(scope); + bool isStrict = v4->currentStackFrame->v4Function->isStrict(); - ExecutionContext *currentContext = v4->currentContext; - ExecutionContext *ctx = currentContext; + Scope scope(v4); + ScopedContext ctx(scope, v4->currentContext()); if (!directCall) { - // the context for eval should be the global scope, so we fake a root - // context - ctx = v4->pushGlobalContext(); + // the context for eval should be the global scope + ctx = v4->rootContext(); } - String *scode = callData->args[0].stringValue(); - if (!scode) { - scope.result = callData->args[0].asReturnedValue(); - return; - } + String *scode = argv[0].stringValue(); + if (!scode) + return argv[0].asReturnedValue(); const QString code = scode->toQString(); - bool inheritContext = !ctx->d()->strictMode; + bool inheritContext = !isStrict; - Script script(ctx, code, QStringLiteral("eval code")); - script.strictMode = (directCall && currentContext->d()->strictMode); + Script script(ctx, QV4::Compiler::EvalCode, code, QStringLiteral("eval code")); + script.strictMode = (directCall && isStrict); script.inheritContext = inheritContext; script.parse(); - if (v4->hasException) { - scope.result = Encode::undefined(); - return; - } + if (v4->hasException) + return Encode::undefined(); Function *function = script.function(); - if (!function) { - scope.result = Encode::undefined(); - return; - } + if (!function) + return Encode::undefined(); - if (function->isStrict() || (ctx->d()->strictMode)) { + if (function->isStrict() || isStrict) { ScopedFunctionObject e(scope, FunctionObject::createScriptFunction(ctx, function)); - ScopedCallData callData(scope, 0); - callData->thisObject = ctx->thisObject(); - e->call(scope, callData); - return; + ScopedValue thisObject(scope, directCall ? scope.engine->currentStackFrame->thisObject() : scope.engine->globalObject->asReturnedValue()); + return e->call(thisObject, 0, 0); } - ContextStateSaver stateSaver(scope, ctx); - - // set the correct strict mode flag on the context - ctx->d()->strictMode = false; - ctx->d()->compilationUnit = function->compilationUnit; - ctx->d()->constantTable = function->compilationUnit->constants; + ScopedValue thisObject(scope, scope.engine->currentStackFrame->thisObject()); - scope.result = Q_V4_PROFILE(ctx->engine(), function); + return function->call(thisObject, 0, 0, ctx); } -void EvalFunction::call(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue EvalFunction::call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) { // indirect call - static_cast<const EvalFunction *>(that)->evalCall(scope, callData, false); + return static_cast<const EvalFunction *>(f)->evalCall(thisObject, argv, argc, false); } @@ -424,10 +408,11 @@ static inline int toInt(const QChar &qc, int R) } // parseInt [15.1.2.2] -void GlobalFunctions::method_parseInt(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalFunctions::method_parseInt(const FunctionObject *b, const Value *, const Value *argv, int argc) { - ScopedValue inputString(scope, callData->argument(0)); - ScopedValue radix(scope, callData->argument(1)); + Scope scope(b); + ScopedValue inputString(scope, argc ? argv[0] : Primitive::undefinedValue()); + ScopedValue radix(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); int R = radix->isUndefined() ? 0 : radix->toInt32(); // [15.1.2.2] step by step: @@ -504,10 +489,11 @@ void GlobalFunctions::method_parseInt(const BuiltinFunction *, Scope &scope, Cal } // parseFloat [15.1.2.3] -void GlobalFunctions::method_parseFloat(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalFunctions::method_parseFloat(const FunctionObject *b, const Value *, const Value *argv, int argc) { + Scope scope(b); // [15.1.2.3] step by step: - ScopedString inputString(scope, callData->argument(0), ScopedString::Convert); + ScopedString inputString(scope, argc ? argv[0] : Primitive::undefinedValue(), ScopedString::Convert); CHECK_EXCEPTION(); QString trimmed = inputString->toQString().trimmed(); // 2 @@ -530,115 +516,125 @@ void GlobalFunctions::method_parseFloat(const BuiltinFunction *, Scope &scope, C } /// isNaN [15.1.2.4] -void GlobalFunctions::method_isNaN(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalFunctions::method_isNaN(const FunctionObject *, const Value *, const Value *argv, int argc) { - if (!callData->argc) + if (!argc) // undefined gets converted to NaN RETURN_RESULT(Encode(true)); - if (callData->args[0].integerCompatible()) + if (argv[0].integerCompatible()) RETURN_RESULT(Encode(false)); - double d = callData->args[0].toNumber(); + double d = argv[0].toNumber(); RETURN_RESULT(Encode((bool)std::isnan(d))); } /// isFinite [15.1.2.5] -void GlobalFunctions::method_isFinite(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalFunctions::method_isFinite(const FunctionObject *, const Value *, const Value *argv, int argc) { - if (!callData->argc) + if (!argc) // undefined gets converted to NaN RETURN_RESULT(Encode(false)); - if (callData->args[0].integerCompatible()) + if (argv[0].integerCompatible()) RETURN_RESULT(Encode(true)); - double d = callData->args[0].toNumber(); + double d = argv[0].toNumber(); RETURN_RESULT(Encode((bool)std::isfinite(d))); } /// decodeURI [15.1.3.1] -void GlobalFunctions::method_decodeURI(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalFunctions::method_decodeURI(const FunctionObject *b, const Value *, const Value *argv, int argc) { - if (callData->argc == 0) + if (argc == 0) RETURN_UNDEFINED(); - QString uriString = callData->args[0].toQString(); + ExecutionEngine *v4 = b->engine(); + QString uriString = argv[0].toQString(); bool ok; QString out = decode(uriString, DecodeNonReserved, &ok); if (!ok) { + Scope scope(v4); ScopedString s(scope, scope.engine->newString(QStringLiteral("malformed URI sequence"))); RETURN_RESULT(scope.engine->throwURIError(s)); } - RETURN_RESULT(scope.engine->newString(out)); + RETURN_RESULT(v4->newString(out)); } /// decodeURIComponent [15.1.3.2] -void GlobalFunctions::method_decodeURIComponent(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalFunctions::method_decodeURIComponent(const FunctionObject *b, const Value *, const Value *argv, int argc) { - if (callData->argc == 0) + if (argc == 0) RETURN_UNDEFINED(); - QString uriString = callData->args[0].toQString(); + ExecutionEngine *v4 = b->engine(); + QString uriString = argv[0].toQString(); bool ok; QString out = decode(uriString, DecodeAll, &ok); if (!ok) { + Scope scope(v4); ScopedString s(scope, scope.engine->newString(QStringLiteral("malformed URI sequence"))); RETURN_RESULT(scope.engine->throwURIError(s)); } - RETURN_RESULT(scope.engine->newString(out)); + RETURN_RESULT(v4->newString(out)); } /// encodeURI [15.1.3.3] -void GlobalFunctions::method_encodeURI(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalFunctions::method_encodeURI(const FunctionObject *b, const Value *, const Value *argv, int argc) { - if (callData->argc == 0) + if (argc == 0) RETURN_UNDEFINED(); - QString uriString = callData->args[0].toQString(); + ExecutionEngine *v4 = b->engine(); + QString uriString = argv[0].toQString(); bool ok; QString out = encode(uriString, uriUnescapedReserved, &ok); if (!ok) { + Scope scope(v4); ScopedString s(scope, scope.engine->newString(QStringLiteral("malformed URI sequence"))); RETURN_RESULT(scope.engine->throwURIError(s)); } - RETURN_RESULT(scope.engine->newString(out)); + RETURN_RESULT(v4->newString(out)); } /// encodeURIComponent [15.1.3.4] -void GlobalFunctions::method_encodeURIComponent(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalFunctions::method_encodeURIComponent(const FunctionObject *b, const Value *, const Value *argv, int argc) { - if (callData->argc == 0) + if (argc == 0) RETURN_UNDEFINED(); - QString uriString = callData->args[0].toQString(); + ExecutionEngine *v4 = b->engine(); + QString uriString = argv[0].toQString(); bool ok; QString out = encode(uriString, uriUnescaped, &ok); if (!ok) { + Scope scope(v4); ScopedString s(scope, scope.engine->newString(QStringLiteral("malformed URI sequence"))); RETURN_RESULT(scope.engine->throwURIError(s)); } - RETURN_RESULT(scope.engine->newString(out)); + RETURN_RESULT(v4->newString(out)); } -void GlobalFunctions::method_escape(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalFunctions::method_escape(const FunctionObject *b, const Value *, const Value *argv, int argc) { - if (!callData->argc) - RETURN_RESULT(scope.engine->newString(QStringLiteral("undefined"))); + ExecutionEngine *v4 = b->engine(); + if (!argc) + RETURN_RESULT(v4->newString(QStringLiteral("undefined"))); - QString str = callData->args[0].toQString(); - RETURN_RESULT(scope.engine->newString(escape(str))); + QString str = argv[0].toQString(); + RETURN_RESULT(v4->newString(escape(str))); } -void GlobalFunctions::method_unescape(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalFunctions::method_unescape(const FunctionObject *b, const Value *, const Value *argv, int argc) { - if (!callData->argc) - RETURN_RESULT(scope.engine->newString(QStringLiteral("undefined"))); + ExecutionEngine *v4 = b->engine(); + if (!argc) + RETURN_RESULT(v4->newString(QStringLiteral("undefined"))); - QString str = callData->args[0].toQString(); - RETURN_RESULT(scope.engine->newString(unescape(str))); + QString str = argv[0].toQString(); + RETURN_RESULT(v4->newString(unescape(str))); } diff --git a/src/qml/jsruntime/qv4globalobject_p.h b/src/qml/jsruntime/qv4globalobject_p.h index 273f1ba7ea..fd1820c23c 100644 --- a/src/qml/jsruntime/qv4globalobject_p.h +++ b/src/qml/jsruntime/qv4globalobject_p.h @@ -69,23 +69,23 @@ struct Q_QML_EXPORT EvalFunction : FunctionObject { V4_OBJECT2(EvalFunction, FunctionObject) - void evalCall(Scope &scope, CallData *callData, bool directCall) const; + ReturnedValue evalCall(const Value *thisObject, const Value *argv, int argc, bool directCall) const; - static void call(const Managed *that, Scope &scope, CallData *callData); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct GlobalFunctions { - static void method_parseInt(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_parseFloat(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_isNaN(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_isFinite(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_decodeURI(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_decodeURIComponent(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_encodeURI(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_encodeURIComponent(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_escape(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_unescape(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_parseInt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_parseFloat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_isNaN(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_isFinite(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_decodeURI(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_decodeURIComponent(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_encodeURI(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_encodeURIComponent(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_escape(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_unescape(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; } diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp index 1edb7d3914..3e04ed63df 100644 --- a/src/qml/jsruntime/qv4include.cpp +++ b/src/qml/jsruntime/qv4include.cpp @@ -39,6 +39,7 @@ #include "qv4include_p.h" #include "qv4scopedvalue_p.h" +#include "qv4jscall_p.h" #include <QtQml/qjsengine.h> #if QT_CONFIG(qml_network) @@ -118,10 +119,10 @@ void QV4Include::callback(const QV4::Value &callback, const QV4::Value &status) if (!f) return; - QV4::ScopedCallData callData(scope, 1); - callData->thisObject = v4->globalObject->asReturnedValue(); - callData->args[0] = status; - f->call(scope, callData); + QV4::JSCallData jsCallData(scope, 1); + *jsCallData->thisObject = v4->globalObject->asReturnedValue(); + jsCallData->args[0] = status; + f->call(jsCallData); if (scope.hasException()) scope.engine->catchException(); } @@ -195,9 +196,10 @@ void QV4Include::finished() /* Documented in qv8engine.cpp */ -void QV4Include::method_include(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +QV4::ReturnedValue QV4Include::method_include(const QV4::BuiltinFunction *b, QV4::CallData *callData) { - if (!callData->argc) + QV4::Scope scope(b); + if (!callData->argc()) RETURN_UNDEFINED(); QQmlContextData *context = scope.engine->callingQmlContext(); @@ -206,7 +208,7 @@ void QV4Include::method_include(const QV4::BuiltinFunction *, QV4::Scope &scope, RETURN_RESULT(scope.engine->throwError(QString::fromUtf8("Qt.include(): Can only be called from JavaScript files"))); QV4::ScopedValue callbackFunction(scope, QV4::Primitive::undefinedValue()); - if (callData->argc >= 2 && callData->args[1].as<QV4::FunctionObject>()) + if (callData->argc() >= 2 && callData->args[1].as<QV4::FunctionObject>()) callbackFunction = callData->args[1]; #if QT_CONFIG(qml_network) @@ -260,13 +262,13 @@ void QV4Include::method_include(const QV4::BuiltinFunction *, QV4::Scope &scope, callback(callbackFunction, result); } - scope.result = result; #else QV4::ScopedValue result(scope); result = resultValue(scope.engine, NetworkError); callback(callbackFunction, result); - scope.result = result; #endif + + return result->asReturnedValue(); } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4include_p.h b/src/qml/jsruntime/qv4include_p.h index 5908d6bfde..68537ba2e8 100644 --- a/src/qml/jsruntime/qv4include_p.h +++ b/src/qml/jsruntime/qv4include_p.h @@ -77,7 +77,7 @@ public: Exception = 3 }; - static void method_include(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static QV4::ReturnedValue method_include(const QV4::BuiltinFunction *, QV4::CallData *callData); private Q_SLOTS: void finished(); diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h new file mode 100644 index 0000000000..6d641bf9c5 --- /dev/null +++ b/src/qml/jsruntime/qv4jscall_p.h @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** 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 QV4JSCALL_H +#define QV4JSCALL_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 "qv4object_p.h" +#include "qv4function_p.h" +#include "qv4functionobject_p.h" +#include "qv4context_p.h" +#include "qv4scopedvalue_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct JSCallData { + JSCallData(const Scope &scope, int argc = 0, const Value *argv = 0, const Value *thisObject = 0) + : scope(scope), argc(argc) + { + if (thisObject) + this->thisObject = const_cast<Value *>(thisObject); + else + this->thisObject = scope.alloc(1); + if (argv) + this->args = const_cast<Value *>(argv); + else + this->args = scope.alloc(argc); + } + + JSCallData *operator->() { + return this; + } + + CallData *callData(const FunctionObject *f = nullptr) const { + int size = int(offsetof(QV4::CallData, args)/sizeof(QV4::Value)) + argc; + CallData *ptr = reinterpret_cast<CallData *>(scope.engine->jsStackTop); + scope.engine->jsStackTop += size; + ptr->function = Encode::undefined(); + ptr->context = Encode::undefined(); + ptr->accumulator = Encode::undefined(); + ptr->thisObject = thisObject->asReturnedValue(); + ptr->setArgc(argc); + if (argc) + memcpy(ptr->args, args, argc*sizeof(Value)); + if (f) + ptr->function = f->asReturnedValue(); + return ptr; + } + const Scope &scope; + int argc; + Value *args; + Value *thisObject; +}; + +inline +ReturnedValue FunctionObject::callAsConstructor(const JSCallData &data) const +{ + return d()->jsConstruct(this, data.args, data.argc); +} + +inline +ReturnedValue FunctionObject::call(const JSCallData &data) const +{ + return d()->jsCall(this, data.thisObject, data.args, data.argc); +} + + +struct ScopedStackFrame { + Scope &scope; + CppStackFrame frame; + + ScopedStackFrame(Scope &scope, Heap::ExecutionContext *context) + : scope(scope) + { + frame.parent = scope.engine->currentStackFrame; + if (!context) + return; + frame.jsFrame = reinterpret_cast<CallData *>(scope.alloc(sizeof(CallData)/sizeof(Value))); + frame.jsFrame->context = context; + frame.v4Function = frame.parent ? frame.parent->v4Function : 0; + scope.engine->currentStackFrame = &frame; + } + ~ScopedStackFrame() { + scope.engine->currentStackFrame = frame.parent; + } +}; + +} + +QT_END_NAMESPACE + +#endif // QV4JSCALL_H diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp index 0f021c8bd0..5e580b8b4d 100644 --- a/src/qml/jsruntime/qv4jsonobject.cpp +++ b/src/qml/jsruntime/qv4jsonobject.cpp @@ -46,6 +46,7 @@ #include <qv4runtime_p.h> #include <qv4variantobject_p.h> #include "qv4string_p.h" +#include "qv4jscall_p.h" #include <qstack.h> #include <qstringlist.h> @@ -689,57 +690,57 @@ static QString quote(const QString &str) QString Stringify::Str(const QString &key, const Value &v) { Scope scope(v4); - scope.result = v; - ScopedObject o(scope, scope.result); + ScopedValue value(scope, v); + ScopedObject o(scope, value); if (o) { ScopedString s(scope, v4->newString(QStringLiteral("toJSON"))); ScopedFunctionObject toJSON(scope, o->get(s)); if (!!toJSON) { - ScopedCallData callData(scope, 1); - callData->thisObject = scope.result; - callData->args[0] = v4->newString(key); - toJSON->call(scope, callData); + JSCallData jsCallData(scope, 1); + *jsCallData->thisObject = value; + jsCallData->args[0] = v4->newString(key); + value = toJSON->call(jsCallData); } } if (replacerFunction) { ScopedObject holder(scope, v4->newObject()); - holder->put(scope.engine->id_empty(), scope.result); - ScopedCallData callData(scope, 2); - callData->args[0] = v4->newString(key); - callData->args[1] = scope.result; - callData->thisObject = holder; - replacerFunction->call(scope, callData); + holder->put(scope.engine->id_empty(), value); + JSCallData jsCallData(scope, 2); + jsCallData->args[0] = v4->newString(key); + jsCallData->args[1] = value; + *jsCallData->thisObject = holder; + value = replacerFunction->call(jsCallData); } - o = scope.result.asReturnedValue(); + o = value->asReturnedValue(); if (o) { if (NumberObject *n = o->as<NumberObject>()) - scope.result = Encode(n->value()); + value = Encode(n->value()); else if (StringObject *so = o->as<StringObject>()) - scope.result = so->d()->string; + value = so->d()->string; else if (BooleanObject *b = o->as<BooleanObject>()) - scope.result = Encode(b->value()); + value = Encode(b->value()); } - if (scope.result.isNull()) + if (value->isNull()) return QStringLiteral("null"); - if (scope.result.isBoolean()) - return scope.result.booleanValue() ? QStringLiteral("true") : QStringLiteral("false"); - if (String *s = scope.result.stringValue()) - return quote(s->toQString()); - - if (scope.result.isNumber()) { - double d = scope.result.toNumber(); - return std::isfinite(d) ? scope.result.toQString() : QStringLiteral("null"); + if (value->isBoolean()) + return value->booleanValue() ? QStringLiteral("true") : QStringLiteral("false"); + if (value->isString()) + return quote(value->stringValue()->toQString()); + + if (value->isNumber()) { + double d = value->toNumber(); + return std::isfinite(d) ? value->toQString() : QStringLiteral("null"); } - if (const QV4::VariantObject *v = scope.result.as<QV4::VariantObject>()) { + if (const QV4::VariantObject *v = value->as<QV4::VariantObject>()) { return v->d()->data().toString(); } - o = scope.result.asReturnedValue(); + o = value->asReturnedValue(); if (o) { if (!o->as<FunctionObject>()) { if (o->as<ArrayObject>()) { @@ -883,25 +884,28 @@ void Heap::JsonObject::init() } -void JsonObject::method_parse(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue JsonObject::method_parse(const BuiltinFunction *b, CallData *callData) { - ScopedValue v(scope, callData->argument(0)); - QString jtext = v->toQString(); + ExecutionEngine *v4 = b->engine(); + QString jtext; + if (callData->argc() > 0) + jtext = callData->args[0].toQString(); DEBUG << "parsing source = " << jtext; - JsonParser parser(scope.engine, jtext.constData(), jtext.length()); + JsonParser parser(v4, jtext.constData(), jtext.length()); QJsonParseError error; - ScopedValue result(scope, parser.parse(&error)); + ReturnedValue result = parser.parse(&error); if (error.error != QJsonParseError::NoError) { DEBUG << "parse error" << error.errorString(); - RETURN_RESULT(scope.engine->throwSyntaxError(QStringLiteral("JSON.parse: Parse error"))); + RETURN_RESULT(v4->throwSyntaxError(QStringLiteral("JSON.parse: Parse error"))); } - scope.result = result; + return result; } -void JsonObject::method_stringify(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue JsonObject::method_stringify(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); Stringify stringify(scope.engine); ScopedObject o(scope, callData->argument(1)); @@ -946,7 +950,7 @@ void JsonObject::method_stringify(const BuiltinFunction *, Scope &scope, CallDat QString result = stringify.Str(QString(), arg0); if (result.isEmpty() || scope.engine->hasException) RETURN_UNDEFINED(); - scope.result = scope.engine->newString(result); + return Encode(scope.engine->newString(result)); } diff --git a/src/qml/jsruntime/qv4jsonobject_p.h b/src/qml/jsruntime/qv4jsonobject_p.h index a73ce1c74e..19dba14aef 100644 --- a/src/qml/jsruntime/qv4jsonobject_p.h +++ b/src/qml/jsruntime/qv4jsonobject_p.h @@ -88,8 +88,8 @@ private: typedef QSet<ObjectItem> V4ObjectSet; public: - static void method_parse(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_stringify(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_parse(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_stringify(const BuiltinFunction *, CallData *callData); static ReturnedValue fromJsonValue(ExecutionEngine *engine, const QJsonValue &value); static ReturnedValue fromJsonObject(ExecutionEngine *engine, const QJsonObject &object); diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index afadf59bd5..c603d424ed 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -38,7 +38,7 @@ ****************************************************************************/ #include "qv4lookup_p.h" #include "qv4functionobject_p.h" -#include "qv4scopedvalue_p.h" +#include "qv4jscall_p.h" #include "qv4string_p.h" #include <private/qv4identifiertable_p.h> @@ -50,7 +50,7 @@ using namespace QV4; ReturnedValue Lookup::lookup(const Value &thisObject, Object *o, PropertyAttributes *attrs) { ExecutionEngine *engine = o->engine(); - Identifier *name = engine->identifierTable->identifier(engine->current->compilationUnit->runtimeStrings[nameIndex]); + Identifier *name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); int i = 0; Heap::Object *obj = o->d(); while (i < Size && obj) { @@ -86,7 +86,7 @@ ReturnedValue Lookup::lookup(const Object *thisObject, PropertyAttributes *attrs { Heap::Object *obj = thisObject->d(); ExecutionEngine *engine = thisObject->engine(); - Identifier *name = engine->identifierTable->identifier(engine->current->compilationUnit->runtimeStrings[nameIndex]); + Identifier *name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); int i = 0; while (i < Size && obj) { classList[i] = obj->internalClass; @@ -117,140 +117,6 @@ ReturnedValue Lookup::lookup(const Object *thisObject, PropertyAttributes *attrs return Primitive::emptyValue().asReturnedValue(); } -ReturnedValue Lookup::indexedGetterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index) -{ - uint idx; - if (object.isObject() && index.asArrayIndex(idx)) { - l->indexedGetter = indexedGetterObjectInt; - return indexedGetterObjectInt(l, engine, object, index); - } - return indexedGetterFallback(l, engine, object, index); -} - -ReturnedValue Lookup::indexedGetterFallback(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index) -{ - Q_UNUSED(l); - Scope scope(engine); - uint idx = 0; - bool isInt = index.asArrayIndex(idx); - - ScopedObject o(scope, object); - if (!o) { - if (isInt) { - if (const String *str = object.as<String>()) { - if (idx >= (uint)str->toQString().length()) { - return Encode::undefined(); - } - const QString s = str->toQString().mid(idx, 1); - return scope.engine->newString(s)->asReturnedValue(); - } - } - - if (object.isNullOrUndefined()) { - QString message = QStringLiteral("Cannot read property '%1' of %2").arg(index.toQStringNoThrow()).arg(object.toQStringNoThrow()); - return engine->throwTypeError(message); - } - - o = RuntimeHelpers::convertToObject(scope.engine, object); - if (!o) // type error - return Encode::undefined(); - } - - if (isInt) { - if (o->d()->arrayData && !o->d()->arrayData->attrs) { - ScopedValue v(scope, Scoped<ArrayData>(scope, o->arrayData())->get(idx)); - if (!v->isEmpty()) - return v->asReturnedValue(); - } - - return o->getIndexed(idx); - } - - ScopedString name(scope, index.toString(scope.engine)); - if (scope.hasException()) - return Encode::undefined(); - return o->get(name); - -} - - -ReturnedValue Lookup::indexedGetterObjectInt(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index) -{ - uint idx; - if (index.asArrayIndex(idx)) { - if (Heap::Base *b = object.heapObject()) { - if (b->vtable()->isObject) { - Heap::Object *o = static_cast<Heap::Object *>(b); - if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) { - Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>(); - if (idx < s->values.size) - if (!s->data(idx).isEmpty()) - return s->data(idx).asReturnedValue(); - } - } - } - } - - return indexedGetterFallback(l, engine, object, index); -} - -void Lookup::indexedSetterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v) -{ - if (Object *o = object.objectValue()) { - uint idx; - if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple && index.asArrayIndex(idx)) { - l->indexedSetter = indexedSetterObjectInt; - indexedSetterObjectInt(l, engine, object, index, v); - return; - } - } - indexedSetterFallback(l, engine, object, index, v); -} - -void Lookup::indexedSetterFallback(Lookup *, ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) -{ - Scope scope(engine); - ScopedObject o(scope, object.toObject(scope.engine)); - if (scope.engine->hasException) - return; - - uint idx; - if (index.asArrayIndex(idx)) { - if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple) { - Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>(); - if (idx < s->values.size) { - s->setData(engine, idx, value); - return; - } - } - o->putIndexed(idx, value); - return; - } - - ScopedString name(scope, index.toString(scope.engine)); - o->put(name, value); -} - -void Lookup::indexedSetterObjectInt(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v) -{ - uint idx; - if (index.asArrayIndex(idx)) { - if (Heap::Base *b = object.heapObject()) { - if (b->vtable()->isObject) { - Heap::Object *o = static_cast<Heap::Object *>(b); - if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) { - Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>(); - if (idx < s->values.size) { - s->setData(engine, idx, v); - return; - } - } - } - } - } - indexedSetterFallback(l, engine, object, index, v); -} - ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object) { if (const Object *o = object.as<Object>()) @@ -268,7 +134,7 @@ ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Va Q_ASSERT(object.isString()); proto = engine->stringPrototype(); Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]); if (name->equals(engine->id_length())) { // special case, as the property is on the object itself l->getter = stringLengthGetter; @@ -289,9 +155,10 @@ ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Va if (attrs.isData()) { if (l->level == 0) { uint nInline = l->proto->vtable()->nInlineProperties; - if (l->index < nInline) + if (l->index < nInline) { + l->index += l->proto->vtable()->inlinePropertyOffset; l->getter = Lookup::primitiveGetter0Inline; - else { + } else { l->index -= nInline; l->getter = Lookup::primitiveGetter0MemberData; } @@ -369,7 +236,7 @@ ReturnedValue Lookup::getterFallback(Lookup *l, ExecutionEngine *engine, const V QV4::ScopedObject o(scope, object.toObject(scope.engine)); if (!o) return Encode::undefined(); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]); return o->get(name); } @@ -392,7 +259,7 @@ ReturnedValue Lookup::getter0Inline(Lookup *l, ExecutionEngine *engine, const Va Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass) - return o->inlinePropertyData(l->index)->asReturnedValue(); + return o->inlinePropertyDataWithOffset(l->index)->asReturnedValue(); } return getterTwoClasses(l, engine, object); } @@ -435,9 +302,9 @@ ReturnedValue Lookup::getter0Inlinegetter0Inline(Lookup *l, ExecutionEngine *eng Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass) - return o->inlinePropertyData(l->index)->asReturnedValue(); + return o->inlinePropertyDataWithOffset(l->index)->asReturnedValue(); if (l->classList[2] == o->internalClass) - return o->inlinePropertyData(l->index2)->asReturnedValue(); + return o->inlinePropertyDataWithOffset(l->index2)->asReturnedValue(); } l->getter = getterFallback; return getterFallback(l, engine, object); @@ -450,7 +317,7 @@ ReturnedValue Lookup::getter0Inlinegetter0MemberData(Lookup *l, ExecutionEngine Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass) - return o->inlinePropertyData(l->index)->asReturnedValue(); + return o->inlinePropertyDataWithOffset(l->index)->asReturnedValue(); if (l->classList[2] == o->internalClass) return o->memberData->values.data()[l->index2].asReturnedValue(); } @@ -480,7 +347,7 @@ ReturnedValue Lookup::getter0Inlinegetter1(Lookup *l, ExecutionEngine *engine, c Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass) - return o->inlinePropertyData(l->index)->asReturnedValue(); + return o->inlinePropertyDataWithOffset(l->index)->asReturnedValue(); if (l->classList[2] == o->internalClass && l->classList[3] == o->prototype()->internalClass) return o->prototype()->propertyData(l->index2)->asReturnedValue(); } @@ -534,10 +401,9 @@ ReturnedValue Lookup::getterAccessor0(Lookup *l, ExecutionEngine *engine, const if (!getter) return Encode::undefined(); - ScopedCallData callData(scope, 0); - callData->thisObject = object; - getter->call(scope, callData); - return scope.result.asReturnedValue(); + JSCallData jsCallData(scope); + *jsCallData->thisObject = object; + return getter->call(jsCallData); } } l->getter = getterFallback; @@ -557,10 +423,9 @@ ReturnedValue Lookup::getterAccessor1(Lookup *l, ExecutionEngine *engine, const if (!getter) return Encode::undefined(); - ScopedCallData callData(scope, 0); - callData->thisObject = object; - getter->call(scope, callData); - return scope.result.asReturnedValue(); + JSCallData jsCallData(scope); + *jsCallData->thisObject = object; + return getter->call(jsCallData); } } l->getter = getterFallback; @@ -583,10 +448,9 @@ ReturnedValue Lookup::getterAccessor2(Lookup *l, ExecutionEngine *engine, const if (!getter) return Encode::undefined(); - ScopedCallData callData(scope, 0); - callData->thisObject = object; - getter->call(scope, callData); - return scope.result.asReturnedValue(); + JSCallData jsCallData(scope); + *jsCallData->thisObject = object; + return getter->call(jsCallData); } } } @@ -600,7 +464,7 @@ ReturnedValue Lookup::primitiveGetter0Inline(Lookup *l, ExecutionEngine *engine, if (object.type() == l->type) { Heap::Object *o = l->proto; if (l->classList[0] == o->internalClass) - return o->inlinePropertyData(l->index)->asReturnedValue(); + return o->inlinePropertyDataWithOffset(l->index)->asReturnedValue(); } l->getter = getterGeneric; return getterGeneric(l, engine, object); @@ -639,10 +503,9 @@ ReturnedValue Lookup::primitiveGetterAccessor0(Lookup *l, ExecutionEngine *engin if (!getter) return Encode::undefined(); - ScopedCallData callData(scope, 0); - callData->thisObject = object; - getter->call(scope, callData); - return scope.result.asReturnedValue(); + JSCallData jsCallData(scope); + *jsCallData->thisObject = object; + return getter->call(jsCallData); } } l->getter = getterGeneric; @@ -660,10 +523,9 @@ ReturnedValue Lookup::primitiveGetterAccessor1(Lookup *l, ExecutionEngine *engin if (!getter) return Encode::undefined(); - ScopedCallData callData(scope, 0); - callData->thisObject = object; - getter->call(scope, callData); - return scope.result.asReturnedValue(); + JSCallData jsCallData(scope); + *jsCallData->thisObject = object; + return getter->call(jsCallData); } } l->getter = getterGeneric; @@ -698,9 +560,10 @@ ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine) if (attrs.isData()) { if (l->level == 0) { uint nInline = o->d()->vtable()->nInlineProperties; - if (l->index < nInline) + if (l->index < nInline) { + l->index += o->d()->vtable()->inlinePropertyOffset; l->globalGetter = globalGetter0Inline; - else { + } else { l->index -= nInline; l->globalGetter = globalGetter0MemberData; } @@ -720,7 +583,7 @@ ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine) } } Scope scope(engine); - ScopedString n(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]); + ScopedString n(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]); return engine->throwReferenceError(n); } @@ -728,7 +591,7 @@ ReturnedValue Lookup::globalGetter0Inline(Lookup *l, ExecutionEngine *engine) { Object *o = engine->globalObject; if (l->classList[0] == o->internalClass()) - return o->d()->inlinePropertyData(l->index)->asReturnedValue(); + return o->d()->inlinePropertyDataWithOffset(l->index)->asReturnedValue(); l->globalGetter = globalGetterGeneric; return globalGetterGeneric(l, engine); @@ -780,10 +643,8 @@ ReturnedValue Lookup::globalGetterAccessor0(Lookup *l, ExecutionEngine *engine) if (!getter) return Encode::undefined(); - ScopedCallData callData(scope, 0); - callData->thisObject = Primitive::undefinedValue(); - getter->call(scope, callData); - return scope.result.asReturnedValue(); + JSCallData jsCallData(scope); + return getter->call(jsCallData); } l->globalGetter = globalGetterGeneric; return globalGetterGeneric(l, engine); @@ -799,10 +660,8 @@ ReturnedValue Lookup::globalGetterAccessor1(Lookup *l, ExecutionEngine *engine) if (!getter) return Encode::undefined(); - ScopedCallData callData(scope, 0); - callData->thisObject = Primitive::undefinedValue(); - getter->call(scope, callData); - return scope.result.asReturnedValue(); + JSCallData jsCallData(scope); + return getter->call(jsCallData); } l->globalGetter = globalGetterGeneric; return globalGetterGeneric(l, engine); @@ -821,10 +680,8 @@ ReturnedValue Lookup::globalGetterAccessor2(Lookup *l, ExecutionEngine *engine) if (!getter) return Encode::undefined(); - ScopedCallData callData(scope, 0); - callData->thisObject = Primitive::undefinedValue(); - getter->call(scope, callData); - return scope.result.asReturnedValue(); + JSCallData jsCallData(scope); + return getter->call(jsCallData); } } } @@ -832,87 +689,88 @@ ReturnedValue Lookup::globalGetterAccessor2(Lookup *l, ExecutionEngine *engine) return globalGetterGeneric(l, engine); } -void Lookup::setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) +bool Lookup::setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Scope scope(engine); ScopedObject o(scope, object); if (!o) { o = RuntimeHelpers::convertToObject(scope.engine, object); if (!o) // type error - return; - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]); - o->put(name, value); - return; + return false; + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]); + return o->put(name, value); } - o->setLookup(l, value); + return o->setLookup(l, value); } -void Lookup::setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) +bool Lookup::setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Lookup l1 = *l; if (Object *o = object.as<Object>()) { - o->setLookup(l, value); + if (!o->setLookup(l, value)) + return false; if (l->setter == Lookup::setter0 || l->setter == Lookup::setter0Inline) { l->setter = setter0setter0; l->classList[1] = l1.classList[0]; l->index2 = l1.index; - return; + return true; } } l->setter = setterFallback; - setterFallback(l, engine, object, value); + return setterFallback(l, engine, object, value); } -void Lookup::setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) +bool Lookup::setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { QV4::Scope scope(engine); QV4::ScopedObject o(scope, object.toObject(scope.engine)); - if (o) { - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]); - o->put(name, value); - } + if (!o) + return false; + + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]); + return o->put(name, value); } -void Lookup::setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) +bool Lookup::setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Object *o = static_cast<Object *>(object.managed()); if (o && o->internalClass() == l->classList[0]) { o->setProperty(engine, l->index, value); - return; + return true; } - setterTwoClasses(l, engine, object, value); + return setterTwoClasses(l, engine, object, value); } -void Lookup::setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) +bool Lookup::setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Object *o = static_cast<Object *>(object.managed()); if (o && o->internalClass() == l->classList[0]) { o->d()->setInlineProperty(engine, l->index, value); - return; + return true; } - setterTwoClasses(l, engine, object, value); + return setterTwoClasses(l, engine, object, value); } -void Lookup::setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) +bool Lookup::setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Object *o = static_cast<Object *>(object.managed()); if (o && o->internalClass() == l->classList[0]) { Q_ASSERT(!o->prototype()); o->setInternalClass(l->classList[3]); o->setProperty(l->index, value); - return; + return true; } l->setter = setterFallback; - setterFallback(l, engine, object, value); + return setterFallback(l, engine, object, value); } -void Lookup::setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) +bool Lookup::setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Object *o = static_cast<Object *>(object.managed()); if (o && o->internalClass() == l->classList[0]) { @@ -922,15 +780,15 @@ void Lookup::setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, co Q_ASSERT(!p->prototype()); o->setInternalClass(l->classList[3]); o->setProperty(l->index, value); - return; + return true; } } l->setter = setterFallback; - setterFallback(l, engine, object, value); + return setterFallback(l, engine, object, value); } -void Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) +bool Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Object *o = static_cast<Object *>(object.managed()); if (o && o->internalClass() == l->classList[0]) { @@ -943,32 +801,31 @@ void Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, co Q_ASSERT(!p->prototype()); o->setInternalClass(l->classList[3]); o->setProperty(l->index, value); - return; + return true; } } } l->setter = setterFallback; - setterFallback(l, engine, object, value); + return setterFallback(l, engine, object, value); } -void Lookup::setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) +bool Lookup::setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Object *o = static_cast<Object *>(object.managed()); if (o) { if (o->internalClass() == l->classList[0]) { o->setProperty(l->index, value); - return; + return true; } if (o->internalClass() == l->classList[1]) { o->setProperty(l->index2, value); - return; + return true; } } l->setter = setterFallback; - setterFallback(l, engine, object, value); - + return setterFallback(l, engine, object, value); } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index ce5189a780..826760aa2d 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -67,11 +67,9 @@ namespace QV4 { struct Lookup { enum { Size = 4 }; union { - ReturnedValue (*indexedGetter)(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index); - void (*indexedSetter)(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v); ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object); ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine); - void (*setter)(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v); + bool (*setter)(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v); }; union { InternalClass *classList[Size]; @@ -90,14 +88,6 @@ struct Lookup { uint index; uint nameIndex; - static ReturnedValue indexedGetterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index); - static ReturnedValue indexedGetterFallback(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index); - static ReturnedValue indexedGetterObjectInt(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index); - - static void indexedSetterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v); - static void indexedSetterFallback(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &value); - static void indexedSetterObjectInt(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v); - static ReturnedValue getterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterFallback(Lookup *l, ExecutionEngine *engine, const Value &object); @@ -133,15 +123,15 @@ struct Lookup { static ReturnedValue globalGetterAccessor1(Lookup *l, ExecutionEngine *engine); static ReturnedValue globalGetterAccessor2(Lookup *l, ExecutionEngine *engine); - static void setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); - static void setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); - static void setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); - static void setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); - static void setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); - static void setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); - static void setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); - static void setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); - static void setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static bool setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static bool setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static bool setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static bool setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static bool setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static bool setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static bool setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static bool setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static bool setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); ReturnedValue lookup(const Value &thisObject, Object *obj, PropertyAttributes *attrs); ReturnedValue lookup(const Object *obj, PropertyAttributes *attrs); @@ -151,7 +141,6 @@ struct Lookup { Q_STATIC_ASSERT(std::is_standard_layout<Lookup>::value); // Ensure that these offsets are always at this point to keep generated code compatible // across 32-bit and 64-bit (matters when cross-compiling). -Q_STATIC_ASSERT(offsetof(Lookup, indexedGetter) == 0); Q_STATIC_ASSERT(offsetof(Lookup, getter) == 0); } diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp index e00eaa0d9b..200380eda0 100644 --- a/src/qml/jsruntime/qv4managed.cpp +++ b/src/qml/jsruntime/qv4managed.cpp @@ -49,7 +49,6 @@ const VTable Managed::static_vtbl = 0, 0, 0, - 0, Managed::IsExecutionContext, Managed::IsString, Managed::IsObject, diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index 40dfc244ea..f81dcf9479 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -93,7 +93,6 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} dptr->_checkIsInitialized(); \ return dptr; \ } \ - static Q_CONSTEXPR quint64 markTable = QV4::Heap::DataClass::markTable; \ V4_ASSERT_IS_TRIVIAL(QV4::Heap::DataClass) #define V4_MANAGED(DataClass, superClass) \ @@ -132,9 +131,8 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} #define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \ { \ parentVTable, \ - markTable, \ (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \ - (sizeof(classname::Data) + (std::is_same<classname, Object>::value ? 2*sizeof(QV4::Value) : 0) + QV4::Chunk::SlotSize - 1)/QV4::Chunk::SlotSize*QV4::Chunk::SlotSize/sizeof(QV4::Value) \ + (sizeof(classname::Data) + (classname::NInlineProperties*sizeof(QV4::Value)) + QV4::Chunk::SlotSize - 1)/QV4::Chunk::SlotSize*QV4::Chunk::SlotSize/sizeof(QV4::Value) \ - (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \ classname::IsExecutionContext, \ classname::IsString, \ @@ -146,9 +144,9 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} classname::MyType, \ #classname, \ Q_VTABLE_FUNCTION(classname, destroy), \ - Q_VTABLE_FUNCTION(classname, markObjects), \ + classname::Data::markObjects, \ isEqualTo \ -} +} \ #define DEFINE_MANAGED_VTABLE(classname) \ QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \ @@ -176,6 +174,7 @@ private: Q_DISABLE_COPY(Managed) public: + enum { NInlineProperties = 0 }; enum Type { Type_Invalid, @@ -221,7 +220,6 @@ public: inline void mark(MarkStack *markStack); static void destroy(Heap::Base *) {} - static void markObjects(Heap::Base *, MarkStack *) {} Q_ALWAYS_INLINE Heap::Base *heapObject() const { return m(); @@ -240,6 +238,11 @@ private: friend struct ObjectIterator; }; +inline void Managed::mark(MarkStack *markStack) +{ + Q_ASSERT(m()); + m()->mark(markStack); +} template<> inline const Managed *Value::as() const { diff --git a/src/qml/jsruntime/qv4mathobject.cpp b/src/qml/jsruntime/qv4mathobject.cpp index 46230b5bc1..252ec345d9 100644 --- a/src/qml/jsruntime/qv4mathobject.cpp +++ b/src/qml/jsruntime/qv4mathobject.cpp @@ -94,54 +94,54 @@ static Q_ALWAYS_INLINE double copySign(double x, double y) return ::copysign(x, y); } -void MathObject::method_abs(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_abs(const FunctionObject *, const Value *, const Value *argv, int argc) { - if (!callData->argc) + if (!argc) RETURN_RESULT(Encode(qt_qnan())); - if (callData->args[0].isInteger()) { - int i = callData->args[0].integerValue(); + if (argv[0].isInteger()) { + int i = argv[0].integerValue(); RETURN_RESULT(Encode(i < 0 ? - i : i)); } - double v = callData->args[0].toNumber(); + double v = argv[0].toNumber(); if (v == 0) // 0 | -0 RETURN_RESULT(Encode(0)); RETURN_RESULT(Encode(v < 0 ? -v : v)); } -void MathObject::method_acos(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_acos(const FunctionObject *, const Value *, const Value *argv, int argc) { - double v = callData->argc ? callData->args[0].toNumber() : 2; + double v = argc ? argv[0].toNumber() : 2; if (v > 1) RETURN_RESULT(Encode(qt_qnan())); RETURN_RESULT(Encode(std::acos(v))); } -void MathObject::method_asin(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_asin(const FunctionObject *, const Value *, const Value *argv, int argc) { - double v = callData->argc ? callData->args[0].toNumber() : 2; + double v = argc ? argv[0].toNumber() : 2; if (v > 1) RETURN_RESULT(Encode(qt_qnan())); else RETURN_RESULT(Encode(std::asin(v))); } -void MathObject::method_atan(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_atan(const FunctionObject *, const Value *, const Value *argv, int argc) { - double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double v = argc ? argv[0].toNumber() : qt_qnan(); if (v == 0.0) RETURN_RESULT(Encode(v)); else RETURN_RESULT(Encode(std::atan(v))); } -void MathObject::method_atan2(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_atan2(const FunctionObject *, const Value *, const Value *argv, int argc) { - double v1 = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - double v2 = callData->argc > 1 ? callData->args[1].toNumber() : qt_qnan(); + double v1 = argc ? argv[0].toNumber() : qt_qnan(); + double v2 = argc > 1 ? argv[1].toNumber() : qt_qnan(); if ((v1 < 0) && qt_is_finite(v1) && qt_is_inf(v2) && (copySign(1.0, v2) == 1.0)) RETURN_RESULT(Encode(copySign(0, -1.0))); @@ -156,24 +156,24 @@ void MathObject::method_atan2(const BuiltinFunction *, Scope &scope, CallData *c RETURN_RESULT(Encode(std::atan2(v1, v2))); } -void MathObject::method_ceil(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_ceil(const FunctionObject *, const Value *, const Value *argv, int argc) { - double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double v = argc ? argv[0].toNumber() : qt_qnan(); if (v < 0.0 && v > -1.0) RETURN_RESULT(Encode(copySign(0, -1.0))); else RETURN_RESULT(Encode(std::ceil(v))); } -void MathObject::method_cos(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_cos(const FunctionObject *, const Value *, const Value *argv, int argc) { - double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double v = argc ? argv[0].toNumber() : qt_qnan(); RETURN_RESULT(Encode(std::cos(v))); } -void MathObject::method_exp(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_exp(const FunctionObject *, const Value *, const Value *argv, int argc) { - double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double v = argc ? argv[0].toNumber() : qt_qnan(); if (qt_is_inf(v)) { if (copySign(1.0, v) == -1.0) RETURN_RESULT(Encode(0)); @@ -184,37 +184,39 @@ void MathObject::method_exp(const BuiltinFunction *, Scope &scope, CallData *cal } } -void MathObject::method_floor(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_floor(const FunctionObject *, const Value *, const Value *argv, int argc) { - double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - RETURN_RESULT(Encode(std::floor(v))); + double v = argc ? argv[0].toNumber() : qt_qnan(); + Value result = Primitive::fromDouble(std::floor(v)); + result.isInt32(); + RETURN_RESULT(result); } -void MathObject::method_log(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_log(const FunctionObject *, const Value *, const Value *argv, int argc) { - double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double v = argc ? argv[0].toNumber() : qt_qnan(); if (v < 0) RETURN_RESULT(Encode(qt_qnan())); else RETURN_RESULT(Encode(std::log(v))); } -void MathObject::method_max(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_max(const FunctionObject *, const Value *, const Value *argv, int argc) { double mx = -qt_inf(); - for (int i = 0; i < callData->argc; ++i) { - double x = callData->args[i].toNumber(); + for (int i = 0, ei = argc; i < ei; ++i) { + double x = argv[i].toNumber(); if (x > mx || std::isnan(x)) mx = x; } RETURN_RESULT(Encode(mx)); } -void MathObject::method_min(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_min(const FunctionObject *, const Value *, const Value *argv, int argc) { double mx = qt_inf(); - for (int i = 0; i < callData->argc; ++i) { - double x = callData->args[i].toNumber(); + for (int i = 0, ei = argc; i < ei; ++i) { + double x = argv[i].toNumber(); if ((x == 0 && mx == x && copySign(1.0, x) == -1.0) || (x < mx) || std::isnan(x)) { mx = x; @@ -223,10 +225,10 @@ void MathObject::method_min(const BuiltinFunction *, Scope &scope, CallData *cal RETURN_RESULT(Encode(mx)); } -void MathObject::method_pow(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_pow(const FunctionObject *, const Value *, const Value *argv, int argc) { - double x = callData->argc > 0 ? callData->args[0].toNumber() : qt_qnan(); - double y = callData->argc > 1 ? callData->args[1].toNumber() : qt_qnan(); + double x = argc > 0 ? argv[0].toNumber() : qt_qnan(); + double y = argc > 1 ? argv[1].toNumber() : qt_qnan(); if (std::isnan(y)) RETURN_RESULT(Encode(qt_qnan())); @@ -273,21 +275,21 @@ void MathObject::method_pow(const BuiltinFunction *, Scope &scope, CallData *cal RETURN_RESULT(Encode(qt_qnan())); } -void MathObject::method_random(const BuiltinFunction *, Scope &scope, CallData *) +ReturnedValue MathObject::method_random(const FunctionObject *, const Value *, const Value *, int) { RETURN_RESULT(Encode(QRandomGenerator::getReal())); } -void MathObject::method_round(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_round(const FunctionObject *, const Value *, const Value *argv, int argc) { - double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double v = argc ? argv[0].toNumber() : qt_qnan(); v = copySign(std::floor(v + 0.5), v); RETURN_RESULT(Encode(v)); } -void MathObject::method_sign(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_sign(const FunctionObject *, const Value *, const Value *argv, int argc) { - double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double v = argc ? argv[0].toNumber() : qt_qnan(); if (std::isnan(v)) RETURN_RESULT(Encode(qt_qnan())); @@ -298,21 +300,21 @@ void MathObject::method_sign(const BuiltinFunction *, Scope &scope, CallData *ca RETURN_RESULT(Encode(std::signbit(v) ? -1 : 1)); } -void MathObject::method_sin(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_sin(const FunctionObject *, const Value *, const Value *argv, int argc) { - double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double v = argc ? argv[0].toNumber() : qt_qnan(); RETURN_RESULT(Encode(std::sin(v))); } -void MathObject::method_sqrt(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_sqrt(const FunctionObject *, const Value *, const Value *argv, int argc) { - double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double v = argc ? argv[0].toNumber() : qt_qnan(); RETURN_RESULT(Encode(std::sqrt(v))); } -void MathObject::method_tan(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_tan(const FunctionObject *, const Value *, const Value *argv, int argc) { - double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double v = argc ? argv[0].toNumber() : qt_qnan(); if (v == 0.0) RETURN_RESULT(Encode(v)); else diff --git a/src/qml/jsruntime/qv4mathobject_p.h b/src/qml/jsruntime/qv4mathobject_p.h index e617712905..0bf5da9404 100644 --- a/src/qml/jsruntime/qv4mathobject_p.h +++ b/src/qml/jsruntime/qv4mathobject_p.h @@ -69,25 +69,25 @@ struct MathObject: Object V4_OBJECT2(MathObject, Object) Q_MANAGED_TYPE(MathObject) - static void method_abs(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_acos(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_asin(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_atan(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_atan2(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_ceil(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_cos(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_exp(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_floor(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_log(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_max(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_min(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_pow(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_random(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_round(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_sign(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_sin(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_sqrt(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_tan(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_abs(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_acos(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_asin(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_atan(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_atan2(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_ceil(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_cos(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_exp(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_floor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_log(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_max(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_min(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_pow(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_random(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_round(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_sign(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_sin(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_sqrt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_tan(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; } diff --git a/src/qml/jsruntime/qv4memberdata_p.h b/src/qml/jsruntime/qv4memberdata_p.h index bae524d088..3e231d693b 100644 --- a/src/qml/jsruntime/qv4memberdata_p.h +++ b/src/qml/jsruntime/qv4memberdata_p.h @@ -63,7 +63,7 @@ namespace Heap { Member(class, ValueArray, ValueArray, values) DECLARE_HEAP_OBJECT(MemberData, Base) { - DECLARE_MARK_TABLE(MemberData); + DECLARE_MARKOBJECTS(MemberData); }; V4_ASSERT_IS_TRIVIAL(MemberData) diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp index d5c80dbf80..f58ff45801 100644 --- a/src/qml/jsruntime/qv4numberobject.cpp +++ b/src/qml/jsruntime/qv4numberobject.cpp @@ -78,16 +78,16 @@ void Heap::NumberCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Number")); } -void NumberCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue NumberCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - double dbl = callData->argc ? callData->args[0].toNumber() : 0.; - scope.result = Encode(scope.engine->newNumberObject(dbl)); + double dbl = argc ? argv[0].toNumber() : 0.; + return Encode(f->engine()->newNumberObject(dbl)); } -void NumberCtor::call(const Managed *, Scope &scope, CallData *callData) +ReturnedValue NumberCtor::call(const FunctionObject *, const Value *, const Value *argv, int argc) { - double dbl = callData->argc ? callData->args[0].toNumber() : 0.; - scope.result = Encode(dbl); + double dbl = argc ? argv[0].toNumber() : 0.; + return Encode(dbl); } void NumberPrototype::init(ExecutionEngine *engine, Object *ctor) @@ -124,119 +124,101 @@ QT_WARNING_POP defineDefaultProperty(QStringLiteral("toPrecision"), method_toPrecision, 1); } -inline ReturnedValue thisNumberValue(Scope &scope, CallData *callData) +inline ReturnedValue thisNumberValue(ExecutionEngine *v4, const Value *thisObject) { - if (callData->thisObject.isNumber()) - return callData->thisObject.asReturnedValue(); - NumberObject *n = callData->thisObject.as<NumberObject>(); + if (thisObject->isNumber()) + return thisObject->asReturnedValue(); + const NumberObject *n = thisObject->as<NumberObject>(); if (!n) { - scope.engine->throwTypeError(); + v4->throwTypeError(); return Encode::undefined(); } return Encode(n->value()); } -inline double thisNumber(Scope &scope, CallData *callData) +inline double thisNumber(ExecutionEngine *engine, const Value *thisObject) { - if (callData->thisObject.isNumber()) - return callData->thisObject.asDouble(); - NumberObject *n = callData->thisObject.as<NumberObject>(); + if (thisObject->isNumber()) + return thisObject->asDouble(); + const NumberObject *n = thisObject->as<NumberObject>(); if (!n) { - scope.engine->throwTypeError(); + engine->throwTypeError(); return 0; } return n->value(); } -void NumberPrototype::method_isFinite(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue NumberPrototype::method_isFinite(const FunctionObject *, const Value *, const Value *argv, int argc) { - if (!callData->argc) { - scope.result = Encode(false); - return; - } + if (!argc) + return Encode(false); - double v = callData->args[0].toNumber(); - scope.result = Encode(!std::isnan(v) && !qt_is_inf(v)); + double v = argv[0].toNumber(); + return Encode(!std::isnan(v) && !qt_is_inf(v)); } -void NumberPrototype::method_isInteger(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue NumberPrototype::method_isInteger(const FunctionObject *, const Value *, const Value *argv, int argc) { - if (!callData->argc) { - scope.result = Encode(false); - return; - } + if (!argc) + return Encode(false); - const Value &v = callData->args[0]; - if (!v.isNumber()) { - scope.result = Encode(false); - return; - } + const Value &v = argv[0]; + if (!v.isNumber()) + return Encode(false); double dv = v.toNumber(); - if (std::isnan(dv) || qt_is_inf(dv)) { - scope.result = Encode(false); - return; - } + if (std::isnan(dv) || qt_is_inf(dv)) + return Encode(false); double iv = v.toInteger(); - scope.result = Encode(dv == iv); + return Encode(dv == iv); } -void NumberPrototype::method_isSafeInteger(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue NumberPrototype::method_isSafeInteger(const FunctionObject *, const Value *, const Value *argv, int argc) { - if (!callData->argc) { - scope.result = Encode(false); - return; - } + if (!argc) + return Encode(false); - const Value &v = callData->args[0]; - if (!v.isNumber()) { - scope.result = Encode(false); - return; - } + const Value &v = argv[0]; + if (!v.isNumber()) + return Encode(false); double dv = v.toNumber(); - if (std::isnan(dv) || qt_is_inf(dv)) { - scope.result = Encode(false); - return; - } + if (std::isnan(dv) || qt_is_inf(dv)) + return Encode(false); double iv = v.toInteger(); - scope.result = Encode(dv == iv && std::fabs(iv) <= (2^53)-1); + return Encode(dv == iv && std::fabs(iv) <= (2^53)-1); } -void NumberPrototype::method_isNaN(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue NumberPrototype::method_isNaN(const FunctionObject *, const Value *, const Value *argv, int argc) { - if (!callData->argc) { - scope.result = Encode(false); - return; - } + if (!argc) + return Encode(false); - double v = callData->args[0].toNumber(); + double v = argv[0].toNumber(); // cast to bool explicitly as std::isnan() may give us ::isnan(), which // sometimes returns an int and we don't want the Encode(int) overload. - scope.result = Encode(bool(std::isnan(v))); + return Encode(bool(std::isnan(v))); } -void NumberPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue NumberPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - double num = thisNumber(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + double num = thisNumber(v4, thisObject); + if (v4->hasException) + return QV4::Encode::undefined(); - if (callData->argc && !callData->args[0].isUndefined()) { - int radix = callData->args[0].toInt32(); + if (argc && !argv[0].isUndefined()) { + int radix = argv[0].toInt32(); if (radix < 2 || radix > 36) { - scope.result = scope.engine->throwError(QStringLiteral("Number.prototype.toString: %0 is not a valid radix") - .arg(radix)); - return; + return v4->throwError(QStringLiteral("Number.prototype.toString: %0 is not a valid radix").arg(radix)); } if (std::isnan(num)) { - scope.result = scope.engine->newString(QStringLiteral("NaN")); - return; + return Encode(v4->newString(QStringLiteral("NaN"))); } else if (qt_is_inf(num)) { - scope.result = scope.engine->newString(QLatin1String(num < 0 ? "-Infinity" : "Infinity")); - return; + return Encode(v4->newString(QLatin1String(num < 0 ? "-Infinity" : "Infinity"))); } if (radix != 10) { @@ -266,43 +248,42 @@ void NumberPrototype::method_toString(const BuiltinFunction *, Scope &scope, Cal } if (negative) str.prepend(QLatin1Char('-')); - scope.result = scope.engine->newString(str); - return; + return Encode(v4->newString(str)); } } - scope.result = Primitive::fromDouble(num).toString(scope.engine); + return Encode(Primitive::fromDouble(num).toString(v4)); } -void NumberPrototype::method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue NumberPrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *, int) { - ScopedValue v(scope, thisNumberValue(scope, callData)); - scope.result = v->toString(scope.engine); - CHECK_EXCEPTION(); + Scope scope(b); + ScopedValue v(scope, thisNumberValue(b->engine(), thisObject)); + return Encode(v->toString(scope.engine)); } -void NumberPrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue NumberPrototype::method_valueOf(const FunctionObject *b, const Value *thisObject, const Value *, int) { - scope.result = thisNumberValue(scope, callData); + return thisNumberValue(b->engine(), thisObject); } -void NumberPrototype::method_toFixed(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue NumberPrototype::method_toFixed(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - double v = thisNumber(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + double v = thisNumber(v4, thisObject); + if (v4->hasException) + return QV4::Encode::undefined(); double fdigits = 0; - if (callData->argc > 0) - fdigits = callData->args[0].toInteger(); + if (argc > 0) + fdigits = argv[0].toInteger(); if (std::isnan(fdigits)) fdigits = 0; - if (fdigits < 0 || fdigits > 20) { - scope.result = scope.engine->throwRangeError(callData->thisObject); - return; - } + if (fdigits < 0 || fdigits > 20) + return v4->throwRangeError(*thisObject); QString str; if (std::isnan(v)) @@ -312,49 +293,51 @@ void NumberPrototype::method_toFixed(const BuiltinFunction *, Scope &scope, Call else if (v < 1.e21) str = NumberLocale::instance()->toString(v, 'f', int(fdigits)); else { - scope.result = RuntimeHelpers::stringFromNumber(scope.engine, v); - return; + return Encode(RuntimeHelpers::stringFromNumber(v4, v)); } - scope.result = scope.engine->newString(str); + return Encode(v4->newString(str)); } -void NumberPrototype::method_toExponential(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue NumberPrototype::method_toExponential(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - double d = thisNumber(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + double d = thisNumber(v4, thisObject); + if (v4->hasException) + return QV4::Encode::undefined(); + int fdigits = NumberLocale::instance()->defaultDoublePrecision; - if (callData->argc && !callData->args[0].isUndefined()) { - fdigits = callData->args[0].toInt32(); + if (argc && !argv[0].isUndefined()) { + fdigits = argv[0].toInt32(); if (fdigits < 0 || fdigits > 20) { - ScopedString error(scope, scope.engine->newString(QStringLiteral("Number.prototype.toExponential: fractionDigits out of range"))); - scope.result = scope.engine->throwRangeError(error); - return; + Scope scope(v4); + ScopedString error(scope, v4->newString(QStringLiteral("Number.prototype.toExponential: fractionDigits out of range"))); + return v4->throwRangeError(error); } } QString result = NumberLocale::instance()->toString(d, 'e', fdigits); - scope.result = scope.engine->newString(result); + return Encode(v4->newString(result)); } -void NumberPrototype::method_toPrecision(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue NumberPrototype::method_toPrecision(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ScopedValue v(scope, thisNumberValue(scope, callData)); - CHECK_EXCEPTION(); + Scope scope(b); + ScopedValue v(scope, thisNumberValue(scope.engine, thisObject)); + if (scope.engine->hasException) + return QV4::Encode::undefined(); - if (!callData->argc || callData->args[0].isUndefined()) { - scope.result = v->toString(scope.engine); - return; - } - int precision = callData->args[0].toInt32(); + if (!argc || argv[0].isUndefined()) + return Encode(v->toString(scope.engine)); + + int precision = argv[0].toInt32(); if (precision < 1 || precision > 21) { ScopedString error(scope, scope.engine->newString(QStringLiteral("Number.prototype.toPrecision: precision out of range"))); - scope.result = scope.engine->throwRangeError(error); - return; + return scope.engine->throwRangeError(error); } QString result = NumberLocale::instance()->toString(v->asDouble(), 'g', precision); - scope.result = scope.engine->newString(result); + return Encode(scope.engine->newString(result)); } diff --git a/src/qml/jsruntime/qv4numberobject_p.h b/src/qml/jsruntime/qv4numberobject_p.h index 0bc2cd8c65..cfdcf9bc1d 100644 --- a/src/qml/jsruntime/qv4numberobject_p.h +++ b/src/qml/jsruntime/qv4numberobject_p.h @@ -79,8 +79,8 @@ struct NumberCtor: FunctionObject { V4_OBJECT2(NumberCtor, FunctionObject) - static void construct(const Managed *that, Scope &scope, CallData *callData); - static void call(const Managed *, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; struct NumberPrototype: NumberObject @@ -88,16 +88,16 @@ struct NumberPrototype: NumberObject V4_PROTOTYPE(objectPrototype) void init(ExecutionEngine *engine, Object *ctor); - static void method_isFinite(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_isInteger(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_isSafeInteger(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_isNaN(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toFixed(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toExponential(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toPrecision(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_isFinite(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_isInteger(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_isSafeInteger(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_isNaN(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toLocaleString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_valueOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toFixed(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toExponential(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toPrecision(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 94d5a74fe5..97296eb847 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -38,8 +38,6 @@ ****************************************************************************/ #include "qv4object_p.h" -#include "qv4jsir_p.h" -#include "qv4isel_p.h" #include "qv4objectproto_p.h" #include "qv4stringobject_p.h" #include "qv4argumentsobject_p.h" @@ -51,6 +49,7 @@ #include "qv4identifier_p.h" #include "qv4string_p.h" #include "qv4identifiertable_p.h" +#include "qv4jscall_p.h" #include <stdint.h> @@ -108,10 +107,9 @@ ReturnedValue Object::getValue(const Value &thisObject, const Value &v, Property return Encode::undefined(); Scope scope(f->engine()); - ScopedCallData callData(scope); - callData->thisObject = thisObject; - f->call(scope, callData); - return scope.result.asReturnedValue(); + JSCallData jsCallData(scope); + *jsCallData->thisObject = thisObject; + return f->call(jsCallData); } bool Object::putValue(uint memberIndex, const Value &value) @@ -127,25 +125,20 @@ bool Object::putValue(uint memberIndex, const Value &value) if (set) { Scope scope(ic->engine); ScopedFunctionObject setter(scope, set); - ScopedCallData callData(scope, 1); - callData->args[0] = value; - callData->thisObject = this; - setter->call(scope, callData); + JSCallData jsCallData(scope, 1); + jsCallData->args[0] = value; + *jsCallData->thisObject = this; + setter->call(jsCallData); return !ic->engine->hasException; } - goto reject; + return false; } if (!attrs.isWritable()) - goto reject; + return false; setProperty(memberIndex, value); return true; - - reject: - if (engine()->current->strictMode) - engine()->throwTypeError(); - return false; } void Object::defineDefaultProperty(const QString &name, const Value &value) @@ -156,7 +149,28 @@ void Object::defineDefaultProperty(const QString &name, const Value &value) defineDefaultProperty(s, value); } -void Object::defineDefaultProperty(const QString &name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount) +void Object::defineDefaultProperty(const QString &name, ReturnedValue (*code)(const BuiltinFunction *, CallData *), int argumentCount) +{ + ExecutionEngine *e = engine(); + Scope scope(e); + ScopedString s(scope, e->newIdentifier(name)); + ExecutionContext *global = e->rootContext(); + ScopedFunctionObject function(scope, BuiltinFunction::create(global, s, code)); + function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount)); + defineDefaultProperty(s, function); +} + +void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(const BuiltinFunction *, CallData *), int argumentCount) +{ + ExecutionEngine *e = engine(); + Scope scope(e); + ExecutionContext *global = e->rootContext(); + ScopedFunctionObject function(scope, BuiltinFunction::create(global, name, code)); + function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount)); + defineDefaultProperty(name, function); +} + +void Object::defineDefaultProperty(const QString &name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount) { ExecutionEngine *e = engine(); Scope scope(e); @@ -167,7 +181,7 @@ void Object::defineDefaultProperty(const QString &name, void (*code)(const Built defineDefaultProperty(s, function); } -void Object::defineDefaultProperty(String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount) +void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount) { ExecutionEngine *e = engine(); Scope scope(e); @@ -177,8 +191,29 @@ void Object::defineDefaultProperty(String *name, void (*code)(const BuiltinFunct defineDefaultProperty(name, function); } -void Object::defineAccessorProperty(const QString &name, void (*getter)(const BuiltinFunction *, Scope &, CallData *), - void (*setter)(const BuiltinFunction *, Scope &, CallData *)) +void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter)(const BuiltinFunction *, CallData *), + ReturnedValue (*setter)(const BuiltinFunction *, CallData *)) +{ + ExecutionEngine *e = engine(); + Scope scope(e); + ScopedString s(scope, e->newIdentifier(name)); + defineAccessorProperty(s, getter, setter); +} + +void Object::defineAccessorProperty(String *name, ReturnedValue (*getter)(const BuiltinFunction *, CallData *), + ReturnedValue (*setter)(const BuiltinFunction *, CallData *)) +{ + ExecutionEngine *v4 = engine(); + QV4::Scope scope(v4); + ScopedProperty p(scope); + ExecutionContext *global = v4->rootContext(); + p->setGetter(ScopedFunctionObject(scope, (getter ? BuiltinFunction::create(global, name, getter) : 0))); + p->setSetter(ScopedFunctionObject(scope, (setter ? BuiltinFunction::create(global, name, setter) : 0))); + insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); +} + +void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int), + ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int)) { ExecutionEngine *e = engine(); Scope scope(e); @@ -186,8 +221,8 @@ void Object::defineAccessorProperty(const QString &name, void (*getter)(const Bu defineAccessorProperty(s, getter, setter); } -void Object::defineAccessorProperty(String *name, void (*getter)(const BuiltinFunction *, Scope &, CallData *), - void (*setter)(const BuiltinFunction *, Scope &, CallData *)) +void Object::defineAccessorProperty(String *name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int), + ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int)) { ExecutionEngine *v4 = engine(); QV4::Scope scope(v4); @@ -198,6 +233,8 @@ void Object::defineAccessorProperty(String *name, void (*getter)(const BuiltinFu insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); } + + void Object::defineReadonlyProperty(const QString &name, const Value &value) { QV4::ExecutionEngine *e = engine(); @@ -224,9 +261,21 @@ void Object::defineReadonlyConfigurableProperty(String *name, const Value &value insertMember(name, value, Attr_ReadOnly_ButConfigurable); } -void Object::markObjects(Heap::Base *b, MarkStack *stack) +void Object::markObjects(Heap::Base *base, MarkStack *stack) { - Heap::Object *o = static_cast<Heap::Object *>(b); + Heap::Object::markObjects(base, stack); +} + +void Heap::Object::markObjects(Heap::Base *b, MarkStack *stack) +{ + Object *o = static_cast<Object *>(b); + if (o->memberData) + o->memberData->mark(stack); + if (o->arrayData) { + o->arrayData->setMarkBit(); + if (o->arrayData->needsMark) + ArrayData::markObjects(o->arrayData, stack); + } uint nInline = o->vtable()->nInlineProperties; Value *v = reinterpret_cast<Value *>(o) + o->vtable()->inlinePropertyOffset; const Value *end = v + nInline; @@ -402,14 +451,14 @@ bool Object::hasOwnProperty(uint index) const return false; } -void Object::construct(const Managed *m, Scope &scope, CallData *) +ReturnedValue Object::callAsConstructor(const FunctionObject *f, const Value *, int) { - scope.result = static_cast<const Object *>(m)->engine()->throwTypeError(); + return f->engine()->throwTypeError(); } -void Object::call(const Managed *m, Scope &scope, CallData *) +ReturnedValue Object::call(const FunctionObject *f, const Value *, const Value *, int) { - scope.result = static_cast<const Object *>(m)->engine()->throwTypeError(); + return f->engine()->throwTypeError(); } ReturnedValue Object::get(const Managed *m, String *name, bool *hasProperty) @@ -483,9 +532,10 @@ ReturnedValue Object::getLookup(const Managed *m, Lookup *l) Q_ASSERT(l->classList[0] == o->internalClass()); if (l->level == 0) { uint nInline = o->d()->vtable()->nInlineProperties; - if (l->index < nInline) + if (l->index < nInline) { + l->index += o->d()->vtable()->inlinePropertyOffset; l->getter = Lookup::getter0Inline; - else { + } else { l->index -= nInline; l->getter = Lookup::getter0MemberData; } @@ -512,11 +562,11 @@ ReturnedValue Object::getLookup(const Managed *m, Lookup *l) return Encode::undefined(); } -void Object::setLookup(Managed *m, Lookup *l, const Value &value) +bool Object::setLookup(Managed *m, Lookup *l, const Value &value) { Scope scope(static_cast<Object *>(m)->engine()); ScopedObject o(scope, static_cast<Object *>(m)); - ScopedString name(scope, scope.engine->current->compilationUnit->runtimeStrings[l->nameIndex]); + ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]); InternalClass *c = o->internalClass(); uint idx = c->find(name); @@ -526,42 +576,44 @@ void Object::setLookup(Managed *m, Lookup *l, const Value &value) l->index = idx; l->setter = idx < o->d()->vtable()->nInlineProperties ? Lookup::setter0Inline : Lookup::setter0; o->setProperty(idx, value); - return; + return true; } - if (idx != UINT_MAX) { - o->putValue(idx, value); - return; - } + if (idx != UINT_MAX) + return o->putValue(idx, value); } - o->put(name, value); + if (!o->put(name, value)) { + l->setter = Lookup::setterFallback; + return false; + } if (o->internalClass() == c) - return; + return true; idx = o->internalClass()->find(name); - if (idx == UINT_MAX) - return; + if (idx == UINT_MAX) // ### can this even happen? + return false; l->classList[0] = c; l->classList[3] = o->internalClass(); l->index = idx; if (!o->prototype()) { l->setter = Lookup::setterInsert0; - return; + return true; } o = o->prototype(); l->classList[1] = o->internalClass(); if (!o->prototype()) { l->setter = Lookup::setterInsert1; - return; + return true; } o = o->prototype(); l->classList[2] = o->internalClass(); if (!o->prototype()) { l->setter = Lookup::setterInsert2; - return; + return true; } l->setter = Lookup::setterGeneric; + return true; } void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *pd, PropertyAttributes *attrs) @@ -644,14 +696,13 @@ ReturnedValue Object::internalGet(String *name, bool *hasProperty) const name->makeIdentifier(); Identifier *id = name->identifier(); - Scope scope(engine()); - ScopedObject o(scope, this); + Heap::Object *o = d(); while (o) { - uint idx = o->internalClass()->find(id); + uint idx = o->internalClass->find(id); if (idx < UINT_MAX) { if (hasProperty) *hasProperty = true; - return getValue(*o->propertyData(idx), o->internalClass()->propertyData.at(idx)); + return getValue(*o->propertyData(idx), o->internalClass->propertyData.at(idx)); } o = o->prototype(); @@ -725,9 +776,9 @@ bool Object::internalPut(String *name, const Value &value) if (attrs.isAccessor()) { if (memberIndex->as<FunctionObject>()) goto cont; - goto reject; + return false; } else if (!attrs.isWritable()) - goto reject; + return false; else if (isArrayObject() && name->equals(engine->id_length())) { bool ok; uint l = value.asArrayLength(&ok); @@ -737,14 +788,14 @@ bool Object::internalPut(String *name, const Value &value) } ok = setArrayLength(l); if (!ok) - goto reject; + return false; } else { memberIndex.set(engine, value); } return true; } else if (!prototype()) { if (!isExtensible()) - goto reject; + return false; } else { // clause 4 Scope scope(engine); @@ -752,12 +803,12 @@ bool Object::internalPut(String *name, const Value &value) if (!memberIndex.isNull()) { if (attrs.isAccessor()) { if (!memberIndex->as<FunctionObject>()) - goto reject; + return false; } else if (!isExtensible() || !attrs.isWritable()) { - goto reject; + return false; } } else if (!isExtensible()) { - goto reject; + return false; } } @@ -769,24 +820,15 @@ bool Object::internalPut(String *name, const Value &value) Scope scope(engine); ScopedFunctionObject setter(scope, *memberIndex); - ScopedCallData callData(scope, 1); - callData->args[0] = value; - callData->thisObject = this; - setter->call(scope, callData); - return !internalClass()->engine->hasException; + JSCallData jsCallData(scope, 1); + jsCallData->args[0] = value; + *jsCallData->thisObject = this; + setter->call(jsCallData); + return !engine->hasException; } insertMember(name, value); return true; - - reject: - // ### this should be removed once everything is ported to use Object::set() - if (engine->current->strictMode) { - QString message = QLatin1String("Cannot assign to read-only property \"") + - name->toQString() + QLatin1Char('\"'); - engine->throwTypeError(message); - } - return false; } bool Object::internalPutIndexed(uint index, const Value &value) @@ -802,7 +844,7 @@ bool Object::internalPutIndexed(uint index, const Value &value) if (arrayIndex.isNull() && isStringObject()) { if (index < static_cast<StringObject *>(this)->length()) // not writable - goto reject; + return false; } // clause 1 @@ -810,15 +852,15 @@ bool Object::internalPutIndexed(uint index, const Value &value) if (attrs.isAccessor()) { if (arrayIndex->as<FunctionObject>()) goto cont; - goto reject; + return false; } else if (!attrs.isWritable()) - goto reject; + return false; arrayIndex.set(engine, value); return true; } else if (!prototype()) { if (!isExtensible()) - goto reject; + return false; } else { // clause 4 Scope scope(engine); @@ -826,12 +868,12 @@ bool Object::internalPutIndexed(uint index, const Value &value) if (!arrayIndex.isNull()) { if (attrs.isAccessor()) { if (!arrayIndex->as<FunctionObject>()) - goto reject; + return false; } else if (!isExtensible() || !attrs.isWritable()) { - goto reject; + return false; } } else if (!isExtensible()) { - goto reject; + return false; } } @@ -843,21 +885,15 @@ bool Object::internalPutIndexed(uint index, const Value &value) Scope scope(engine); ScopedFunctionObject setter(scope, *arrayIndex); - ScopedCallData callData(scope, 1); - callData->args[0] = value; - callData->thisObject = this; - setter->call(scope, callData); - return !internalClass()->engine->hasException; + JSCallData jsCallData(scope, 1); + jsCallData->args[0] = value; + *jsCallData->thisObject = this; + setter->call(jsCallData); + return !engine->hasException; } arraySet(index, value); return true; - - reject: - // ### this should be removed once everything is ported to use Object::setIndexed() - if (engine->current->strictMode) - engine->throwTypeError(); - return false; } // Section 8.12.7 @@ -878,8 +914,6 @@ bool Object::internalDeleteProperty(String *name) InternalClass::removeMember(this, name->identifier()); return true; } - if (engine()->current->strictMode) - engine()->throwTypeError(); return false; } @@ -896,8 +930,6 @@ bool Object::internalDeleteIndexedProperty(uint index) if (!ad || ad->vtable()->del(this, index)) return true; - if (engine()->current->strictMode) - engine()->throwTypeError(); return false; } @@ -921,7 +953,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const if (attrs.isEmpty() || p->isSubset(attrs, lp, cattrs)) return true; if (!cattrs.isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable()) - goto reject; + return false; bool succeeded = true; if (attrs.type() == PropertyAttributes::Data) { bool ok; @@ -938,7 +970,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const InternalClass::changeMember(this, engine->id_length(), cattrs); } if (!succeeded) - goto reject; + return false; return true; } @@ -948,7 +980,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const if (memberIndex == UINT_MAX) { // clause 3 if (!isExtensible()) - goto reject; + return false; // clause 4 ScopedProperty pd(scope); pd->copy(p, attrs); @@ -958,26 +990,18 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const } return __defineOwnProperty__(engine, memberIndex, name, p, attrs); -reject: - if (engine->current->strictMode) - engine->throwTypeError(); - return false; } bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs) { // 15.4.5.1, 4b if (isArrayObject() && index >= getLength() && !internalClass()->propertyData[Heap::ArrayObject::LengthPropertyIndex].isWritable()) - goto reject; + return false; if (ArgumentsObject::isNonStrictArgumentsObject(this)) return static_cast<ArgumentsObject *>(this)->defineOwnProperty(engine, index, p, attrs); return defineOwnProperty2(engine, index, p, attrs); -reject: - if (engine->current->strictMode) - engine->throwTypeError(); - return false; } bool Object::defineOwnProperty2(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs) @@ -994,7 +1018,7 @@ bool Object::defineOwnProperty2(ExecutionEngine *engine, uint index, const Prope if (!hasProperty) { // clause 3 if (!isExtensible()) - goto reject; + return false; // clause 4 Scope scope(engine); ScopedProperty pp(scope); @@ -1010,10 +1034,6 @@ bool Object::defineOwnProperty2(ExecutionEngine *engine, uint index, const Prope } return __defineOwnProperty__(engine, index, 0, p, attrs); -reject: - if (engine->current->strictMode) - engine->throwTypeError(); - return false; } bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String *member, const Property *p, PropertyAttributes attrs) @@ -1040,9 +1060,9 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String * // clause 7 if (!cattrs.isConfigurable()) { if (attrs.isConfigurable()) - goto reject; + return false; if (attrs.hasEnumerable() && attrs.isEnumerable() != cattrs.isEnumerable()) - goto reject; + return false; } // clause 8 @@ -1053,7 +1073,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String * if (cattrs.isData() != attrs.isData()) { // 9a if (!cattrs.isConfigurable()) - goto reject; + return false; if (cattrs.isData()) { // 9b cattrs.setType(PropertyAttributes::Accessor); @@ -1079,15 +1099,15 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String * } else if (cattrs.isData() && attrs.isData()) { // clause 10 if (!cattrs.isConfigurable() && !cattrs.isWritable()) { if (attrs.isWritable() || !current->value.sameValue(p->value)) - goto reject; + return false; } } else { // clause 10 Q_ASSERT(cattrs.isAccessor() && attrs.isAccessor()); if (!cattrs.isConfigurable()) { if (!p->value.isEmpty() && current->value.rawValue() != p->value.rawValue()) - goto reject; + return false; if (!p->set.isEmpty() && current->set.rawValue() != p->set.rawValue()) - goto reject; + return false; } } @@ -1102,10 +1122,6 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String * arrayData()->setProperty(scope.engine, index, current); } return true; - reject: - if (engine->current->strictMode) - engine->throwTypeError(); - return false; } @@ -1260,7 +1276,7 @@ void Heap::ArrayObject::init(const QStringList &list) ReturnedValue ArrayObject::getLookup(const Managed *m, Lookup *l) { Scope scope(static_cast<const Object *>(m)->engine()); - ScopedString name(scope, scope.engine->current->compilationUnit->runtimeStrings[l->nameIndex]); + ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]); if (name->equals(scope.engine->id_length())) { // special case, as the property is on the object itself l->getter = Lookup::arrayLengthGetter; diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 066a93cc61..ff3ad7c0c6 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -57,6 +57,7 @@ #include "qv4scopedvalue_p.h" #include "qv4value_p.h" #include "qv4internalclass_p.h" +#include "qv4string_p.h" QT_BEGIN_NAMESPACE @@ -71,11 +72,14 @@ namespace Heap { Member(class, Pointer, MemberData *, memberData) \ Member(class, Pointer, ArrayData *, arrayData) -DECLARE_HEAP_OBJECT(Object, Base) { - DECLARE_MARK_TABLE(Object); +DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) { + static void markObjects(Heap::Base *base, MarkStack *stack); void init() { Base::init(); } - void destroy() { Base::destroy(); } + const Value *inlinePropertyDataWithOffset(uint indexWithOffset) const { + Q_ASSERT(indexWithOffset >= vtable()->inlinePropertyOffset && indexWithOffset < vtable()->inlinePropertyOffset + vtable()->nInlineProperties); + return reinterpret_cast<const Value *>(this) + indexWithOffset; + } const Value *inlinePropertyData(uint index) const { Q_ASSERT(index < vtable()->nInlineProperties); return reinterpret_cast<const Value *>(this) + vtable()->inlinePropertyOffset + index; @@ -128,25 +132,8 @@ DECLARE_HEAP_OBJECT(Object, Base) { Heap::Object *prototype() const { return internalClass->prototype; } }; -Q_STATIC_ASSERT(Object::markTable == ((2 << 2) | (2 << 4))); - } -#define V4_OBJECT(superClass) \ - public: \ - Q_MANAGED_CHECK \ - typedef superClass SuperClass; \ - static const QV4::ObjectVTable static_vtbl; \ - static inline const QV4::VTable *staticVTable() { return &static_vtbl.vTable; } \ - V4_MANAGED_SIZE_TEST \ - Data *d_unchecked() const { return static_cast<Data *>(m()); } \ - Data *d() const { \ - Data *dptr = d_unchecked(); \ - dptr->_checkIsInitialized(); \ - return dptr; \ - } \ - V4_ASSERT_IS_TRIVIAL(Data); - #define V4_OBJECT2(DataClass, superClass) \ private: \ DataClass() Q_DECL_EQ_DELETE; \ @@ -164,8 +151,7 @@ Q_STATIC_ASSERT(Object::markTable == ((2 << 2) | (2 << 4))); dptr->_checkIsInitialized(); \ return dptr; \ } \ - V4_ASSERT_IS_TRIVIAL(QV4::Heap::DataClass); \ - static Q_CONSTEXPR quint64 markTable = QV4::Heap::DataClass::markTable; + V4_ASSERT_IS_TRIVIAL(QV4::Heap::DataClass); #define V4_PROTOTYPE(p) \ static QV4::Object *defaultPrototype(QV4::ExecutionEngine *e) \ @@ -174,8 +160,8 @@ Q_STATIC_ASSERT(Object::markTable == ((2 << 2) | (2 << 4))); struct ObjectVTable { VTable vTable; - void (*call)(const Managed *, Scope &scope, CallData *data); - void (*construct)(const Managed *, Scope &scope, CallData *data); + ReturnedValue (*call)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + ReturnedValue (*callAsConstructor)(const FunctionObject *, const Value *argv, int argc); ReturnedValue (*get)(const Managed *, String *name, bool *hasProperty); ReturnedValue (*getIndexed)(const Managed *, uint index, bool *hasProperty); bool (*put)(Managed *, String *name, const Value &value); @@ -185,7 +171,7 @@ struct ObjectVTable bool (*deleteProperty)(Managed *m, String *name); bool (*deleteIndexedProperty)(Managed *m, uint index); ReturnedValue (*getLookup)(const Managed *m, Lookup *l); - void (*setLookup)(Managed *m, Lookup *l, const Value &v); + bool (*setLookup)(Managed *m, Lookup *l, const Value &v); uint (*getLength)(const Managed *m); void (*advanceIterator)(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); ReturnedValue (*instanceOf)(const Object *typeObject, const Value &var); @@ -196,7 +182,7 @@ const QV4::ObjectVTable classname::static_vtbl = \ { \ DEFINE_MANAGED_VTABLE_INT(classname, (std::is_same<classname::SuperClass, Object>::value) ? nullptr : &classname::SuperClass::static_vtbl.vTable), \ call, \ - construct, \ + callAsConstructor, \ get, \ getIndexed, \ put, \ @@ -228,6 +214,8 @@ struct Q_QML_EXPORT Object: Managed { V4_INTERNALCLASS(Object) V4_PROTOTYPE(objectPrototype) + enum { NInlineProperties = 2 }; + enum { IsObject = true, GetterOffset = 0, @@ -287,12 +275,20 @@ struct Q_QML_EXPORT Object: Managed { insertMember(name, value, Attr_Data|Attr_NotEnumerable); } void defineDefaultProperty(const QString &name, const Value &value); - void defineDefaultProperty(const QString &name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount = 0); - void defineDefaultProperty(String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount = 0); - void defineAccessorProperty(const QString &name, void (*getter)(const BuiltinFunction *, Scope &, CallData *), - void (*setter)(const BuiltinFunction *, Scope &, CallData *)); - void defineAccessorProperty(String *name, void (*getter)(const BuiltinFunction *, Scope &, CallData *), - void (*setter)(const BuiltinFunction *, Scope &, CallData *)); + // old calling convention + void defineDefaultProperty(const QString &name, ReturnedValue (*code)(const BuiltinFunction *, CallData *), int argumentCount = 0); + void defineDefaultProperty(String *name, ReturnedValue (*code)(const BuiltinFunction *, CallData *), int argumentCount = 0); + // new calling convention + void defineDefaultProperty(const QString &name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount = 0); + void defineDefaultProperty(String *name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount = 0); + void defineAccessorProperty(const QString &name, ReturnedValue (*getter)(const BuiltinFunction *, CallData *), + ReturnedValue (*setter)(const BuiltinFunction *, CallData *)); + void defineAccessorProperty(String *name, ReturnedValue (*getter)(const BuiltinFunction *, CallData *), + ReturnedValue (*setter)(const BuiltinFunction *, CallData *)); + void defineAccessorProperty(const QString &name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int), + ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int)); + void defineAccessorProperty(String *name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int), + ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int)); /* Fixed: Writable: false, Enumerable: false, Configurable: false */ void defineReadonlyProperty(const QString &name, const Value &value); void defineReadonlyProperty(String *name, const Value &value); @@ -314,6 +310,8 @@ struct Q_QML_EXPORT Object: Managed { // Array handling public: + static void markObjects(Heap::Base *base, MarkStack *stack); + void copyArrayData(Object *other); bool setArrayLength(uint newLen); @@ -408,19 +406,6 @@ public: return ret; } - inline bool setIndexed(uint idx, const Value &v, ThrowOnFailure shouldThrow) - { - bool ret = vtable()->putIndexed(this, idx, v); - if (!ret && shouldThrow == ThrowOnFailure::DoThrowOnRejection) { - ExecutionEngine *e = engine(); - if (!e->hasException) { // allow a custom set impl to throw itself - e->throwTypeError(); - } - } - return ret; - } - - PropertyAttributes query(String *name) const { return vtable()->query(this, name); } PropertyAttributes queryIndexed(uint index) const @@ -431,21 +416,17 @@ public: { return vtable()->deleteIndexedProperty(this, index); } ReturnedValue getLookup(Lookup *l) const { return vtable()->getLookup(this, l); } - void setLookup(Lookup *l, const Value &v) - { vtable()->setLookup(this, l, v); } + bool setLookup(Lookup *l, const Value &v) + { return vtable()->setLookup(this, l, v); } void advanceIterator(ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) { vtable()->advanceIterator(this, it, name, index, p, attributes); } uint getLength() const { return vtable()->getLength(this); } ReturnedValue instanceOf(const Value &var) const { return vtable()->instanceOf(this, var); } - inline void construct(Scope &scope, CallData *d) const - { return vtable()->construct(this, scope, d); } - inline void call(Scope &scope, CallData *d) const - { vtable()->call(this, scope, d); } protected: - static void construct(const Managed *m, Scope &scope, CallData *); - static void call(const Managed *m, Scope &scope, CallData *); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); static bool put(Managed *m, String *name, const Value &value); @@ -455,11 +436,10 @@ protected: static bool deleteProperty(Managed *m, String *name); static bool deleteIndexedProperty(Managed *m, uint index); static ReturnedValue getLookup(const Managed *m, Lookup *l); - static void setLookup(Managed *m, Lookup *l, const Value &v); + static bool setLookup(Managed *m, Lookup *l, const Value &v); static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); static uint getLength(const Managed *m); static ReturnedValue instanceOf(const Object *typeObject, const Value &var); - static void markObjects(Heap::Base *, MarkStack *); private: ReturnedValue internalGet(String *name, bool *hasProperty) const; diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp index 3427ee89fe..0394c704f9 100644 --- a/src/qml/jsruntime/qv4objectiterator.cpp +++ b/src/qml/jsruntime/qv4objectiterator.cpp @@ -177,9 +177,9 @@ ReturnedValue ObjectIterator::nextPropertyNameAsString() DEFINE_OBJECT_VTABLE(ForEachIteratorObject); -void ForEachIteratorObject::markObjects(Heap::Base *that, MarkStack *markStack) +void Heap::ForEachIteratorObject::markObjects(Heap::Base *that, MarkStack *markStack) { - ForEachIteratorObject::Data *o = static_cast<ForEachIteratorObject::Data *>(that); + ForEachIteratorObject *o = static_cast<ForEachIteratorObject *>(that); o->workArea[0].mark(markStack); o->workArea[1].mark(markStack); Object::markObjects(that, markStack); diff --git a/src/qml/jsruntime/qv4objectiterator_p.h b/src/qml/jsruntime/qv4objectiterator_p.h index 6168d59914..30a6ad3025 100644 --- a/src/qml/jsruntime/qv4objectiterator_p.h +++ b/src/qml/jsruntime/qv4objectiterator_p.h @@ -116,6 +116,7 @@ struct ForEachIteratorObject : Object { ObjectIterator &it() { return *reinterpret_cast<ObjectIterator*>(&itData); } Value workArea[2]; + static void markObjects(Heap::Base *that, MarkStack *markStack); private: ObjectIteratorData itData; }; @@ -127,9 +128,6 @@ struct ForEachIteratorObject: Object { Q_MANAGED_TYPE(ForeachIteratorObject) ReturnedValue nextPropertyName() { return d()->it().nextPropertyNameAsString(); } - -protected: - static void markObjects(Heap::Base *that, MarkStack *markStack); }; inline diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp index 2e72c0f13f..5eef757ce9 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -46,6 +46,7 @@ #include "qv4runtime_p.h" #include "qv4objectiterator_p.h" #include "qv4string_p.h" +#include "qv4jscall_p.h" #include <QtCore/QDateTime> #include <QtCore/QStringList> @@ -60,27 +61,29 @@ void Heap::ObjectCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Object")); } -void ObjectCtor::construct(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue ObjectCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - const ObjectCtor *ctor = static_cast<const ObjectCtor *>(that); - if (!callData->argc || callData->args[0].isUndefined() || callData->args[0].isNull()) { + ExecutionEngine *v4 = f->engine(); + const ObjectCtor *ctor = static_cast<const ObjectCtor *>(f); + if (!argc || argv[0].isUndefined() || argv[0].isNull()) { + Scope scope(v4); ScopedObject obj(scope, scope.engine->newObject()); ScopedObject proto(scope, ctor->get(scope.engine->id_prototype())); if (!!proto) obj->setPrototype(proto); - scope.result = obj.asReturnedValue(); + return obj.asReturnedValue(); } else { - scope.result = callData->args[0].toObject(scope.engine); + return argv[0].toObject(v4)->asReturnedValue(); } } -void ObjectCtor::call(const Managed *, Scope &scope, CallData *callData) +ReturnedValue ObjectCtor::call(const FunctionObject *m, const Value *, const Value *argv, int argc) { - ExecutionEngine *v4 = scope.engine; - if (!callData->argc || callData->args[0].isUndefined() || callData->args[0].isNull()) { - scope.result = v4->newObject()->asReturnedValue(); + ExecutionEngine *v4 = m->engine(); + if (!argc || argv[0].isUndefined() || argv[0].isNull()) { + return v4->newObject()->asReturnedValue(); } else { - scope.result = callData->args[0].toObject(v4); + return argv[0].toObject(v4)->asReturnedValue(); } } @@ -123,58 +126,78 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) insertMember(v4->id___proto__(), p, Attr_Accessor|Attr_NotEnumerable); } -void ObjectPrototype::method_getPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_getPrototypeOf(const FunctionObject *b, const Value *, const Value *argv, int argc) { - ScopedObject o(scope, callData->args[0].toObject(scope.engine)); - CHECK_EXCEPTION(); + Scope scope(b); + if (argc < 1) + return scope.engine->throwTypeError(); + + ScopedObject o(scope, argv[0].toObject(scope.engine)); + if (scope.engine->hasException) + return QV4::Encode::undefined(); ScopedObject p(scope, o->prototype()); - scope.result = !!p ? p->asReturnedValue() : Encode::null(); + return (!!p ? p->asReturnedValue() : Encode::null()); } -void ObjectPrototype::method_getOwnPropertyDescriptor(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(const FunctionObject *b, const Value *, const Value *argv, int argc) { - ScopedObject O(scope, callData->args[0].toObject(scope.engine)); - CHECK_EXCEPTION(); + Scope scope(b); + if (argc < 1) + return scope.engine->throwTypeError(); + + ScopedObject O(scope, argv[0].toObject(scope.engine)); + if (scope.engine->hasException) + return QV4::Encode::undefined(); if (ArgumentsObject::isNonStrictArgumentsObject(O)) static_cast<ArgumentsObject *>(O.getPointer())->fullyCreate(); - ScopedValue v(scope, callData->argument(1)); + ScopedValue v(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); ScopedString name(scope, v->toString(scope.engine)); - CHECK_EXCEPTION(); + if (scope.engine->hasException) + return QV4::Encode::undefined(); PropertyAttributes attrs; ScopedProperty desc(scope); O->getOwnProperty(name, &attrs, desc); - scope.result = fromPropertyDescriptor(scope.engine, desc, attrs); + return fromPropertyDescriptor(scope.engine, desc, attrs); } -void ObjectPrototype::method_getOwnPropertyNames(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_getOwnPropertyNames(const FunctionObject *b, const Value *, const Value *argv, int argc) { - ScopedObject O(scope, callData->args[0].toObject(scope.engine)); - CHECK_EXCEPTION(); + Scope scope(b); + if (argc < 1) + return scope.engine->throwTypeError(); + + ScopedObject O(scope, argv[0].toObject(scope.engine)); + if (scope.engine->hasException) + return QV4::Encode::undefined(); - scope.result = getOwnPropertyNames(scope.engine, callData->args[0]); + return Encode(getOwnPropertyNames(scope.engine, argv[0])); } // 19.1.2.1 -void ObjectPrototype::method_assign(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_assign(const FunctionObject *b, const Value *, const Value *argv, int argc) { - ScopedObject to(scope, callData->args[0].toObject(scope.engine)); - CHECK_EXCEPTION(); + Scope scope(b); + if (argc < 1) + return scope.engine->throwTypeError(); - if (callData->argc == 1) { - scope.result = to; - return; - } + ScopedObject to(scope, argv[0].toObject(scope.engine)); + if (scope.engine->hasException) + return QV4::Encode::undefined(); - for (int i = 1; i < callData->argc; ++i) { - if (callData->args[i].isUndefined() || callData->args[i].isNull()) + if (argc == 1) + return to.asReturnedValue(); + + for (int i = 1, ei = argc; i < ei; ++i) { + if (argv[i].isUndefined() || argv[i].isNull()) continue; - ScopedObject from(scope, callData->args[i].toObject(scope.engine)); - CHECK_EXCEPTION(); + ScopedObject from(scope, argv[i].toObject(scope.engine)); + if (scope.engine->hasException) + return QV4::Encode::undefined(); QV4::ScopedArrayObject keys(scope, QV4::ObjectPrototype::getOwnPropertyNames(scope.engine, from)); quint32 length = keys->getLength(); @@ -195,64 +218,71 @@ void ObjectPrototype::method_assign(const BuiltinFunction *, Scope &scope, CallD propValue = from->get(nextKey); to->set(nextKey, propValue, Object::DoThrowOnRejection); - CHECK_EXCEPTION(); + if (scope.engine->hasException) + return QV4::Encode::undefined(); } } - scope.result = to; + return to.asReturnedValue(); } -void ObjectPrototype::method_create(const BuiltinFunction *builtin, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_create(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc) { - ScopedValue O(scope, callData->argument(0)); - if (!O->isObject() && !O->isNull()) { - scope.result = scope.engine->throwTypeError(); - return; - } + Scope scope(builtin); + if (!argc || (!argv[0].isObject() && !argv[0].isNull())) + return scope.engine->throwTypeError(); + + ScopedObject O(scope, argv[0]); ScopedObject newObject(scope, scope.engine->newObject()); - newObject->setPrototype(O->as<Object>()); + newObject->setPrototype(O); - if (callData->argc > 1 && !callData->args[1].isUndefined()) { - callData->args[0] = newObject; - method_defineProperties(builtin, scope, callData); - return; + + if (argc > 1 && !argv[1].isUndefined()) { + Value *arguments = scope.alloc(argc); + arguments[0] = newObject; + memcpy(arguments + 1, argv + 1, (argc - 1)*sizeof(Value)); + return method_defineProperties(builtin, thisObject, arguments, argc); } - scope.result = newObject; + return newObject.asReturnedValue(); } -void ObjectPrototype::method_defineProperty(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_defineProperty(const FunctionObject *b, const Value *, const Value *argv, int argc) { - ScopedObject O(scope, callData->argument(0)); - if (!O) { - scope.result = scope.engine->throwTypeError(); - return; - } + Scope scope(b); + if (!argc || !argv[0].isObject()) + return scope.engine->throwTypeError(); - ScopedString name(scope, callData->argument(1), ScopedString::Convert); - CHECK_EXCEPTION(); + ScopedObject O(scope, argv[0]); + ScopedString name(scope, argc > 1 ? argv[1] : Primitive::undefinedValue(), ScopedString::Convert); + if (scope.engine->hasException) + return QV4::Encode::undefined(); - ScopedValue attributes(scope, callData->argument(2)); + ScopedValue attributes(scope, argc > 2 ? argv[2] : Primitive::undefinedValue()); ScopedProperty pd(scope); PropertyAttributes attrs; toPropertyDescriptor(scope.engine, attributes, pd, &attrs); - CHECK_EXCEPTION(); + if (scope.engine->hasException) + return QV4::Encode::undefined(); if (!O->__defineOwnProperty__(scope.engine, name, pd, attrs)) THROW_TYPE_ERROR(); - scope.result = O; + return O.asReturnedValue(); } -void ObjectPrototype::method_defineProperties(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_defineProperties(const FunctionObject *b, const Value *, const Value *argv, int argc) { - ScopedObject O(scope, callData->argument(0)); - if (!O) - THROW_TYPE_ERROR(); + Scope scope(b); + if (argc < 2 || !argv[0].isObject()) + return scope.engine->throwTypeError(); + + ScopedObject O(scope, argv[0]); - ScopedObject o(scope, callData->argument(1), ScopedObject::Convert); - CHECK_EXCEPTION(); + ScopedObject o(scope, argv[1].toObject(scope.engine)); + if (scope.engine->hasException) + return QV4::Encode::undefined(); ScopedValue val(scope); @@ -269,7 +299,8 @@ void ObjectPrototype::method_defineProperties(const BuiltinFunction *, Scope &sc PropertyAttributes nattrs; val = o->getValue(pd->value, attrs); toPropertyDescriptor(scope.engine, val, n, &nattrs); - CHECK_EXCEPTION(); + if (scope.engine->hasException) + return QV4::Encode::undefined(); bool ok; if (name) ok = O->__defineOwnProperty__(scope.engine, name, n, nattrs); @@ -279,18 +310,18 @@ void ObjectPrototype::method_defineProperties(const BuiltinFunction *, Scope &sc THROW_TYPE_ERROR(); } - scope.result = O; + return O.asReturnedValue(); } -void ObjectPrototype::method_seal(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_seal(const FunctionObject *b, const Value *, const Value *argv, int argc) { - ScopedObject o(scope, callData->argument(0)); - if (!o) { + const Value a = argc ? argv[0] : Primitive::undefinedValue(); + if (!a.isObject()) // 19.1.2.17, 1 - scope.result = callData->argument(0); - return; - } + return a.asReturnedValue(); + Scope scope(b); + ScopedObject o(scope, a); o->setInternalClass(o->internalClass()->sealed()); if (o->arrayData()) { @@ -301,17 +332,18 @@ void ObjectPrototype::method_seal(const BuiltinFunction *, Scope &scope, CallDat } } - scope.result = o; + return o.asReturnedValue(); } -void ObjectPrototype::method_freeze(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_freeze(const FunctionObject *b, const Value *, const Value *argv, int argc) { - ScopedObject o(scope, callData->argument(0)); - if (!o) { + const Value a = argc ? argv[0] : Primitive::undefinedValue(); + if (!a.isObject()) // 19.1.2.5, 1 - scope.result = callData->argument(0); - return; - } + return a.asReturnedValue(); + + Scope scope(b); + ScopedObject o(scope, a); if (ArgumentsObject::isNonStrictArgumentsObject(o)) static_cast<ArgumentsObject *>(o.getPointer())->fullyCreate(); @@ -327,116 +359,109 @@ void ObjectPrototype::method_freeze(const BuiltinFunction *, Scope &scope, CallD o->arrayData()->attrs[i].setWritable(false); } } - scope.result = o; + return o.asReturnedValue(); } -void ObjectPrototype::method_preventExtensions(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_preventExtensions(const FunctionObject *b, const Value *, const Value *argv, int argc) { - ScopedObject o(scope, callData->args[0].toObject(scope.engine)); - if (!o) { - scope.result = callData->argument(0); - return; - } + Scope scope(b); + if (!argc) + return scope.engine->throwTypeError(); + + ScopedObject o(scope, argv[0].toObject(scope.engine)); + if (!o) + return argv[0].asReturnedValue(); o->setInternalClass(o->internalClass()->nonExtensible()); - scope.result = o; + return o.asReturnedValue(); } -void ObjectPrototype::method_isSealed(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_isSealed(const FunctionObject *b, const Value *, const Value *argv, int argc) { - ScopedObject o(scope, callData->args[0].toObject(scope.engine)); - if (!o) { - scope.result = Encode(true); - return; - } + Scope scope(b); + if (!argc) + return scope.engine->throwTypeError(); - if (o->isExtensible()) { - scope.result = Encode(false); - return; - } + ScopedObject o(scope, argv[0].toObject(scope.engine)); + if (!o) + return Encode(true); - if (o->internalClass() != o->internalClass()->sealed()) { - scope.result = Encode(false); - return; - } + if (o->isExtensible()) + return Encode(false); - if (!o->arrayData() || !o->arrayData()->length()) { - scope.result = Encode(true); - return; - } + if (o->internalClass() != o->internalClass()->sealed()) + return Encode(false); + + if (!o->arrayData() || !o->arrayData()->length()) + return Encode(true); Q_ASSERT(o->arrayData() && o->arrayData()->length()); - if (!o->arrayData()->attrs) { - scope.result = Encode(false); - return; - } + if (!o->arrayData()->attrs) + return Encode(false); for (uint i = 0; i < o->arrayData()->values.alloc; ++i) { if (!o->arrayData()->isEmpty(i)) - if (o->arrayData()->attributes(i).isConfigurable()) { - scope.result = Encode(false); - return; - } + if (o->arrayData()->attributes(i).isConfigurable()) + return Encode(false); } - scope.result = Encode(true); + return Encode(true); } -void ObjectPrototype::method_isFrozen(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_isFrozen(const FunctionObject *b, const Value *, const Value *argv, int argc) { - ScopedObject o(scope, callData->args[0].toObject(scope.engine)); - if (!o) { - scope.result = Encode(true); - return; - } + Scope scope(b); + if (!argc) + return scope.engine->throwTypeError(); - if (o->isExtensible()) { - scope.result = Encode(false); - return; - } + ScopedObject o(scope, argv[0].toObject(scope.engine)); + if (!o) + return Encode(true); - if (o->internalClass() != o->internalClass()->frozen()) { - scope.result = Encode(false); - return; - } + if (o->isExtensible()) + return Encode(false); - if (!o->arrayData() || !o->arrayData()->length()) { - scope.result = Encode(true); - return; - } + if (o->internalClass() != o->internalClass()->frozen()) + return Encode(false); + + if (!o->arrayData() || !o->arrayData()->length()) + return Encode(true); Q_ASSERT(o->arrayData() && o->arrayData()->length()); - if (!o->arrayData()->attrs) { - scope.result = Encode(false); - return; - } + if (!o->arrayData()->attrs) + return Encode(false); for (uint i = 0; i < o->arrayData()->values.alloc; ++i) { if (!o->arrayData()->isEmpty(i)) - if (o->arrayData()->attributes(i).isConfigurable() || o->arrayData()->attributes(i).isWritable()) { - scope.result = Encode(false); - return; - } + if (o->arrayData()->attributes(i).isConfigurable() || o->arrayData()->attributes(i).isWritable()) + return Encode(false); } - scope.result = Encode(true); + return Encode(true); } -void ObjectPrototype::method_isExtensible(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_isExtensible(const FunctionObject *b, const Value *, const Value *argv, int argc) { - ScopedObject o(scope, callData->args[0].toObject(scope.engine)); - if (!o) { - scope.result = Encode(false); - return; - } + Scope scope(b); + if (!argc) + return scope.engine->throwTypeError(); - scope.result = Encode((bool)o->isExtensible()); + ScopedObject o(scope, argv[0].toObject(scope.engine)); + if (!o) + return Encode(false); + + return Encode((bool)o->isExtensible()); } -void ObjectPrototype::method_keys(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_keys(const FunctionObject *b, const Value *, const Value *argv, int argc) { - ScopedObject o(scope, callData->args[0].toObject(scope.engine)); - CHECK_EXCEPTION(); + Scope scope(b); + if (!argc) + return scope.engine->throwTypeError(); + + ScopedObject o(scope, argv[0].toObject(scope.engine)); + if (scope.engine->hasException) + return QV4::Encode::undefined(); ScopedArrayObject a(scope, scope.engine->newArrayObject()); @@ -449,101 +474,109 @@ void ObjectPrototype::method_keys(const BuiltinFunction *, Scope &scope, CallDat a->push_back(name); } - scope.result = a; + return a.asReturnedValue(); } -void ObjectPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) { - if (callData->thisObject.isUndefined()) { - scope.result = scope.engine->newString(QStringLiteral("[object Undefined]")); - } else if (callData->thisObject.isNull()) { - scope.result = scope.engine->newString(QStringLiteral("[object Null]")); + ExecutionEngine *v4 = b->engine(); + if (thisObject->isUndefined()) { + return Encode(v4->newString(QStringLiteral("[object Undefined]"))); + } else if (thisObject->isNull()) { + return Encode(v4->newString(QStringLiteral("[object Null]"))); } else { - ScopedObject obj(scope, callData->thisObject.toObject(scope.engine)); + Scope scope(v4); + ScopedObject obj(scope, thisObject->toObject(scope.engine)); QString className = obj->className(); - scope.result = scope.engine->newString(QStringLiteral("[object %1]").arg(className)); + return Encode(v4->newString(QStringLiteral("[object %1]").arg(className))); } } -void ObjectPrototype::method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ScopedObject o(scope, callData->thisObject.toObject(scope.engine)); + Scope scope(b); + ScopedObject o(scope, thisObject->toObject(scope.engine)); if (!o) RETURN_UNDEFINED(); ScopedFunctionObject f(scope, o->get(scope.engine->id_toString())); if (!f) THROW_TYPE_ERROR(); - ScopedCallData cData(scope); - cData->thisObject = o; - f->call(scope, callData); + + return f->call(thisObject, argv, argc); } -void ObjectPrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_valueOf(const FunctionObject *b, const Value *thisObject, const Value *, int) { - scope.result = callData->thisObject.toObject(scope.engine); + return Encode(thisObject->toObject(b->engine())); } -void ObjectPrototype::method_hasOwnProperty(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_hasOwnProperty(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ScopedString P(scope, callData->argument(0), ScopedString::Convert); - CHECK_EXCEPTION(); - ScopedObject O(scope, callData->thisObject, ScopedObject::Convert); - CHECK_EXCEPTION(); + Scope scope(b); + ScopedString P(scope, argc ? argv[0] : Primitive::undefinedValue(), ScopedString::Convert); + if (scope.engine->hasException) + return QV4::Encode::undefined(); + ScopedObject O(scope, thisObject->toObject(scope.engine)); + if (scope.engine->hasException) + return QV4::Encode::undefined(); bool r = O->hasOwnProperty(P); if (!r) r = !O->query(P).isEmpty(); - scope.result = Encode(r); + return Encode(r); } -void ObjectPrototype::method_isPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_isPrototypeOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ScopedObject V(scope, callData->argument(0)); - if (!V) { - scope.result = Encode(false); - return; - } - - ScopedObject O(scope, callData->thisObject, ScopedObject::Convert); - CHECK_EXCEPTION(); + Scope scope(b); + if (!argc || !argv[0].isObject()) + return Encode(false); + + ScopedObject V(scope, argv[0]); + ScopedObject O(scope, thisObject->toObject(scope.engine)); + if (scope.engine->hasException) + return QV4::Encode::undefined(); ScopedObject proto(scope, V->prototype()); while (proto) { - if (O->d() == proto->d()) { - scope.result = Encode(true); - return; - } + if (O->d() == proto->d()) + return Encode(true); proto = proto->prototype(); } - scope.result = Encode(false); + return Encode(false); } -void ObjectPrototype::method_propertyIsEnumerable(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_propertyIsEnumerable(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ScopedString p(scope, callData->argument(0), ScopedString::Convert); - CHECK_EXCEPTION(); - - ScopedObject o(scope, callData->thisObject, ScopedObject::Convert); - CHECK_EXCEPTION(); + Scope scope(b); + ScopedString p(scope, argc ? argv[0] : Primitive::undefinedValue(), ScopedString::Convert); + if (scope.engine->hasException) + return QV4::Encode::undefined(); + + ScopedObject o(scope, thisObject->toObject(scope.engine)); + if (scope.engine->hasException) + return QV4::Encode::undefined(); PropertyAttributes attrs; o->getOwnProperty(p, &attrs); - scope.result = Encode(attrs.isEnumerable()); + return Encode(attrs.isEnumerable()); } -void ObjectPrototype::method_defineGetter(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_defineGetter(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - if (callData->argc < 2) + Scope scope(b); + if (argc < 2) THROW_TYPE_ERROR(); - ScopedFunctionObject f(scope, callData->argument(1)); + ScopedFunctionObject f(scope, argv[1]); if (!f) THROW_TYPE_ERROR(); - ScopedString prop(scope, callData->argument(0), ScopedString::Convert); - CHECK_EXCEPTION(); + ScopedString prop(scope, argv[0], ScopedString::Convert); + if (scope.engine->hasException) + return QV4::Encode::undefined(); - ScopedObject o(scope, callData->thisObject); + ScopedObject o(scope, thisObject); if (!o) { - if (!callData->thisObject.isUndefined()) + if (!thisObject->isUndefined()) RETURN_UNDEFINED(); o = scope.engine->globalObject; } @@ -551,25 +584,29 @@ void ObjectPrototype::method_defineGetter(const BuiltinFunction *, Scope &scope, ScopedProperty pd(scope); pd->value = f; pd->set = Primitive::emptyValue(); - o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor); + bool ok = o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor); + if (!ok) + THROW_TYPE_ERROR(); RETURN_UNDEFINED(); } -void ObjectPrototype::method_defineSetter(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_defineSetter(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - if (callData->argc < 2) + Scope scope(b); + if (argc < 2) THROW_TYPE_ERROR(); - ScopedFunctionObject f(scope, callData->argument(1)); + ScopedFunctionObject f(scope, argv[1]); if (!f) THROW_TYPE_ERROR(); - ScopedString prop(scope, callData->argument(0), ScopedString::Convert); - CHECK_EXCEPTION(); + ScopedString prop(scope, argv[0], ScopedString::Convert); + if (scope.engine->hasException) + return QV4::Encode::undefined(); - ScopedObject o(scope, callData->thisObject); + ScopedObject o(scope, thisObject); if (!o) { - if (!callData->thisObject.isUndefined()) + if (!thisObject->isUndefined()) RETURN_UNDEFINED(); o = scope.engine->globalObject; } @@ -577,31 +614,35 @@ void ObjectPrototype::method_defineSetter(const BuiltinFunction *, Scope &scope, ScopedProperty pd(scope); pd->value = Primitive::emptyValue(); pd->set = f; - o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor); + bool ok = o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor); + if (!ok) + THROW_TYPE_ERROR(); RETURN_UNDEFINED(); } -void ObjectPrototype::method_get_proto(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_get_proto(const FunctionObject *b, const Value *thisObject, const Value *, int) { - ScopedObject o(scope, callData->thisObject.as<Object>()); + Scope scope(b); + ScopedObject o(scope, thisObject->as<Object>()); if (!o) THROW_TYPE_ERROR(); - scope.result = o->prototype(); + return Encode(o->prototype()); } -void ObjectPrototype::method_set_proto(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_set_proto(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ScopedObject o(scope, callData->thisObject); - if (!o || !callData->argc) + Scope scope(b); + ScopedObject o(scope, thisObject); + if (!o || !argc) THROW_TYPE_ERROR(); - if (callData->args[0].isNull()) { + if (argv[0].isNull()) { o->setPrototype(0); RETURN_UNDEFINED(); } - ScopedObject p(scope, callData->args[0]); + ScopedObject p(scope, argv[0]); bool ok = false; if (!!p) { if (o->prototype() == p->d()) { @@ -610,10 +651,8 @@ void ObjectPrototype::method_set_proto(const BuiltinFunction *, Scope &scope, Ca ok = o->setPrototype(p); } } - if (!ok) { - scope.result = scope.engine->throwTypeError(QStringLiteral("Cyclic __proto__ value")); - return; - } + if (!ok) + return scope.engine->throwTypeError(QStringLiteral("Cyclic __proto__ value")); RETURN_UNDEFINED(); } diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h index 44b54267f3..2b231d46ad 100644 --- a/src/qml/jsruntime/qv4objectproto_p.h +++ b/src/qml/jsruntime/qv4objectproto_p.h @@ -70,41 +70,41 @@ struct ObjectCtor: FunctionObject { V4_OBJECT2(ObjectCtor, FunctionObject) - static void construct(const Managed *that, Scope &scope, CallData *callData); - static void call(const Managed *that, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc); }; struct ObjectPrototype: Object { void init(ExecutionEngine *engine, Object *ctor); - static void method_getPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getOwnPropertyDescriptor(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getOwnPropertyNames(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_assign(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_create(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_defineProperty(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_defineProperties(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_seal(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_freeze(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_preventExtensions(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_isSealed(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_isFrozen(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_isExtensible(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_keys(const BuiltinFunction *, Scope &scope, CallData *callData); - - static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_hasOwnProperty(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_isPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_propertyIsEnumerable(const BuiltinFunction *, Scope &scope, CallData *callData); - - static void method_defineGetter(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_defineSetter(const BuiltinFunction *, Scope &scope, CallData *callData); - - static void method_get_proto(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_set_proto(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_getPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getOwnPropertyDescriptor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getOwnPropertyNames(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_assign(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_create(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_defineProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_defineProperties(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_seal(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_freeze(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_preventExtensions(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_isSealed(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_isFrozen(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_isExtensible(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_keys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + + static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toLocaleString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_valueOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_hasOwnProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_isPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_propertyIsEnumerable(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + + static ReturnedValue method_defineGetter(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_defineSetter(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + + static ReturnedValue method_get_proto(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_set_proto(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static void toPropertyDescriptor(ExecutionEngine *engine, const Value &v, Property *desc, PropertyAttributes *attrs); static ReturnedValue fromPropertyDescriptor(ExecutionEngine *engine, const Property *desc, PropertyAttributes attrs); diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h index 6b2369e795..8a24c71815 100644 --- a/src/qml/jsruntime/qv4profiling_p.h +++ b/src/qml/jsruntime/qv4profiling_p.h @@ -59,9 +59,6 @@ #if !QT_CONFIG(qml_debug) -#define Q_V4_PROFILE_ALLOC(engine, size, type) (!engine) -#define Q_V4_PROFILE_DEALLOC(engine, size, type) (!engine) -#define Q_V4_PROFILE(engine, function) (function->code(engine, function->codeData)) QT_BEGIN_NAMESPACE @@ -75,22 +72,6 @@ QT_END_NAMESPACE #else -#define Q_V4_PROFILE_ALLOC(engine, size, type)\ - (engine->profiler() &&\ - (engine->profiler()->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\ - engine->profiler()->trackAlloc(size, type) : false) - -#define Q_V4_PROFILE_DEALLOC(engine, size, type) \ - (engine->profiler() &&\ - (engine->profiler()->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\ - engine->profiler()->trackDealloc(size, type) : false) - -#define Q_V4_PROFILE(engine, function)\ - (Q_UNLIKELY(engine->profiler()) &&\ - (engine->profiler()->featuresEnabled & (1 << Profiling::FeatureFunctionCall)) ?\ - Profiling::FunctionCallProfiler::profileCall(engine->profiler(), engine, function) :\ - function->code(engine, function->codeData)) - QT_BEGIN_NAMESPACE namespace QV4 { @@ -270,19 +251,21 @@ public: // It's enough to ref() the function in the destructor as it will probably not disappear while // it's executing ... - FunctionCallProfiler(Profiler *profiler, Function *function) : - profiler(profiler), function(function), startTime(profiler->m_timer.nsecsElapsed()) - {} - - ~FunctionCallProfiler() + FunctionCallProfiler(ExecutionEngine *engine, Function *f) + : profiler(0) { - profiler->m_data.append(FunctionCall(function, startTime, profiler->m_timer.nsecsElapsed())); + Profiler *p = engine->profiler(); + if (Q_UNLIKELY(p) && (p->featuresEnabled & (1 << Profiling::FeatureFunctionCall))) { + profiler = p; + function = f; + startTime = profiler->m_timer.nsecsElapsed(); + } } - static ReturnedValue profileCall(Profiler *profiler, ExecutionEngine *engine, Function *function) + ~FunctionCallProfiler() { - FunctionCallProfiler callProfiler(profiler, function); - return function->code(engine, function->codeData); + if (profiler) + profiler->m_data.append(FunctionCall(function, startTime, profiler->m_timer.nsecsElapsed())); } Profiler *profiler; diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 8b9ef149d6..5a165b2309 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -296,13 +296,8 @@ void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContex { Heap::ExecutionContext::init(Heap::ExecutionContext::Type_QmlContext); outer.set(internalClass->engine, outerContext->d()); - strictMode = false; - callData = outer->callData; - lookups = outer->lookups; - constantTable = outer->constantTable; - compilationUnit = outer->compilationUnit; - this->qml.set(internalClass->engine, qml->d()); + this->activation.set(internalClass->engine, qml->d()); } Heap::QmlContext *QmlContext::createWorkerContext(ExecutionContext *parent, const QUrl &source, Value *sendFunction) diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index 48c9ee2c36..a1df5cb95f 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -76,12 +76,12 @@ struct QQmlContextWrapper : Object { QQmlQPointer<QObject> scopeObject; }; -#define QmlContextMembers(class, Member) \ - Member(class, Pointer, QQmlContextWrapper *, qml) +#define QmlContextMembers(class, Member) DECLARE_HEAP_OBJECT(QmlContext, ExecutionContext) { - DECLARE_MARK_TABLE(QmlContext); + DECLARE_MARKOBJECTS(QmlContext); + QQmlContextWrapper *qml() { return static_cast<QQmlContextWrapper *>(activation.get()); } void init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml); }; @@ -109,10 +109,10 @@ struct Q_QML_EXPORT QmlContext : public ExecutionContext static Heap::QmlContext *create(QV4::ExecutionContext *parent, QQmlContextData *context, QObject *scopeObject); QObject *qmlScope() const { - return d()->qml->scopeObject; + return d()->qml()->scopeObject; } QQmlContextData *qmlContext() const { - return *d()->qml->context; + return *d()->qml()->context; } }; diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index fb3aa47d1b..6d7d929b61 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -62,6 +62,7 @@ #include <private/qv4regexpobject_p.h> #include <private/qv4dateobject_p.h> #include <private/qv4scopedvalue_p.h> +#include <private/qv4jscall_p.h> #include <private/qv4mm_p.h> #include <private/qqmlscriptstring_p.h> #include <private/qv4compileddata_p.h> @@ -468,11 +469,11 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP if (auto binding = QQmlPropertyPrivate::binding(object, QQmlPropertyIndex(property->coreIndex()))) { Q_ASSERT(!binding->isValueTypeProxy()); const auto qmlBinding = static_cast<const QQmlBinding*>(binding); - const auto stackFrame = engine->currentStackFrame(); + const auto stackFrame = engine->currentStackFrame; qCInfo(lcBindingRemoval, "Overwriting binding on %s::%s at %s:%d that was initially bound at %s", object->metaObject()->className(), qPrintable(property->name(object)), - qPrintable(stackFrame.source), stackFrame.line, + qPrintable(stackFrame->source()), stackFrame->lineNumber(), qPrintable(qmlBinding->expressionIdentifier())); } } @@ -815,18 +816,18 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase QV4::Scope scope(v4); QV4::ScopedFunctionObject f(scope, This->function.value()); - QV4::ScopedCallData callData(scope, argCount); - callData->thisObject = This->thisObject.isUndefined() ? v4->globalObject->asReturnedValue() : This->thisObject.value(); + QV4::JSCallData jsCallData(scope, argCount); + *jsCallData->thisObject = This->thisObject.isUndefined() ? v4->globalObject->asReturnedValue() : This->thisObject.value(); for (int ii = 0; ii < argCount; ++ii) { int type = argsTypes[ii + 1]; if (type == qMetaTypeId<QVariant>()) { - callData->args[ii] = v4->fromVariant(*((QVariant *)metaArgs[ii + 1])); + jsCallData->args[ii] = v4->fromVariant(*((QVariant *)metaArgs[ii + 1])); } else { - callData->args[ii] = v4->fromVariant(QVariant(type, metaArgs[ii + 1])); + jsCallData->args[ii] = v4->fromVariant(QVariant(type, metaArgs[ii + 1])); } } - f->call(scope, callData); + f->call(jsCallData); if (scope.hasException()) { QQmlError error = v4->catchExceptionAsQmlError(); if (error.description().isEmpty()) { @@ -899,9 +900,11 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase } // namespace QV4 -void QObjectWrapper::method_connect(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QObjectWrapper::method_connect(const BuiltinFunction *b, CallData *callData) { - if (callData->argc == 0) + QV4::Scope scope(b); + + if (callData->argc() == 0) THROW_GENERIC_ERROR("Function.prototype.connect: no arguments given"); QPair<QObject *, int> signalInfo = extractQtSignal(callData->thisObject); @@ -920,9 +923,9 @@ void QObjectWrapper::method_connect(const BuiltinFunction *, Scope &scope, CallD QV4::ScopedFunctionObject f(scope); QV4::ScopedValue thisObject (scope, QV4::Encode::undefined()); - if (callData->argc == 1) { + if (callData->argc() == 1) { f = callData->args[0]; - } else if (callData->argc >= 2) { + } else if (callData->argc() >= 2) { thisObject = callData->args[0]; f = callData->args[1]; } @@ -949,9 +952,11 @@ void QObjectWrapper::method_connect(const BuiltinFunction *, Scope &scope, CallD RETURN_UNDEFINED(); } -void QObjectWrapper::method_disconnect(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QObjectWrapper::method_disconnect(const BuiltinFunction *b, CallData *callData) { - if (callData->argc == 0) + QV4::Scope scope(b); + + if (callData->argc() == 0) THROW_GENERIC_ERROR("Function.prototype.disconnect: no arguments given"); QPair<QObject *, int> signalInfo = extractQtSignal(callData->thisObject); @@ -970,9 +975,9 @@ void QObjectWrapper::method_disconnect(const BuiltinFunction *, Scope &scope, Ca QV4::ScopedFunctionObject functionValue(scope); QV4::ScopedValue functionThisValue(scope, QV4::Encode::undefined()); - if (callData->argc == 1) { + if (callData->argc() == 1) { functionValue = callData->args[0]; - } else if (callData->argc >= 2) { + } else if (callData->argc() >= 2) { functionThisValue = callData->args[0]; functionValue = callData->args[1]; } @@ -1010,9 +1015,9 @@ static void markChildQObjectsRecursively(QObject *parent, QV4::MarkStack *markSt } } -void QObjectWrapper::markObjects(Heap::Base *that, QV4::MarkStack *markStack) +void Heap::QObjectWrapper::markObjects(Heap::Base *that, QV4::MarkStack *markStack) { - QObjectWrapper::Data *This = static_cast<QObjectWrapper::Data *>(that); + QObjectWrapper *This = static_cast<QObjectWrapper *>(that); if (QObject *o = This->object()) { QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(o); @@ -1027,7 +1032,7 @@ void QObjectWrapper::markObjects(Heap::Base *that, QV4::MarkStack *markStack) markChildQObjectsRecursively(o, markStack); } - QV4::Object::markObjects(that, markStack); + Object::markObjects(that, markStack); } void QObjectWrapper::destroyObject(bool lastCall) @@ -1412,7 +1417,7 @@ static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQ + QLatin1String(unknownTypeError)); } - if (args[0] > callArgs->argc) { + if (args[0] > callArgs->argc()) { QString error = QLatin1String("Insufficient arguments"); return engine->throwError(error); } @@ -1443,7 +1448,7 @@ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const QV4::ExecutionEngine *engine, QV4::CallData *callArgs, const QQmlPropertyCache *propertyCache, QMetaObject::Call callType = QMetaObject::InvokeMetaMethod) { - int argumentCount = callArgs->argc; + int argumentCount = callArgs->argc(); QQmlPropertyData best; int bestParameterScore = INT_MAX; @@ -1848,7 +1853,7 @@ const QMetaObject *Heap::QObjectMethod::metaObject() return object()->metaObject(); } -QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionContext *ctx) const +QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionEngine *engine) const { QString result; if (const QMetaObject *metaObject = d()->metaObject()) { @@ -1867,15 +1872,15 @@ QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionContext *ctx) co result = QLatin1String("null"); } - return ctx->engine()->newString(result)->asReturnedValue(); + return engine->newString(result)->asReturnedValue(); } -QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, const Value *args, int argc) const +QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionEngine *engine, const Value *args, int argc) const { if (!d()->object()) return Encode::undefined(); if (QQmlData::keepAliveDuringGarbageCollection(d()->object())) - return ctx->engine()->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object")); + return engine->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object")); int delay = 0; if (argc > 0) @@ -1889,32 +1894,24 @@ QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, con return Encode::undefined(); } -void QObjectMethod::call(const Managed *m, Scope &scope, CallData *callData) +ReturnedValue QObjectMethod::call(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc) { const QObjectMethod *This = static_cast<const QObjectMethod*>(m); - This->callInternal(callData, scope); + return This->callInternal(thisObject, argv, argc); } -void QObjectMethod::callInternal(CallData *callData, Scope &scope) const +ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *argv, int argc) const { ExecutionEngine *v4 = engine(); - ExecutionContext *context = v4->currentContext; - if (d()->index == DestroyMethod) { - scope.result = method_destroy(context, callData->args, callData->argc); - return; - } - - else if (d()->index == ToStringMethod) { - scope.result = method_toString(context); - return; - } + if (d()->index == DestroyMethod) + return method_destroy(v4, argv, argc); + else if (d()->index == ToStringMethod) + return method_toString(v4); QQmlObjectOrGadget object(d()->object()); if (!d()->object()) { - if (!d()->valueTypeWrapper) { - scope.result = Encode::undefined(); - return; - } + if (!d()->valueTypeWrapper) + return Encode::undefined(); object = QQmlObjectOrGadget(d()->propertyCache(), d()->valueTypeWrapper->gadgetPtr); } @@ -1923,20 +1920,16 @@ void QObjectMethod::callInternal(CallData *callData, Scope &scope) const if (d()->propertyCache()) { QQmlPropertyData *data = d()->propertyCache()->method(d()->index); - if (!data) { - scope.result = QV4::Encode::undefined(); - return; - } + if (!data) + return QV4::Encode::undefined(); method = *data; } else { const QMetaObject *mo = d()->object()->metaObject(); const QMetaMethod moMethod = mo->method(d()->index); method.load(moMethod); - if (method.coreIndex() == -1) { - scope.result = QV4::Encode::undefined(); - return; - } + if (method.coreIndex() == -1) + return QV4::Encode::undefined(); // Look for overloaded methods QByteArray methodName = moMethod.name(); @@ -1951,21 +1944,25 @@ void QObjectMethod::callInternal(CallData *callData, Scope &scope) const } } + Scope scope(v4); + JSCallData cData(scope, argc, argv, thisObject); + CallData *callData = cData.callData(); + if (method.isV4Function()) { - scope.result = QV4::Encode::undefined(); - QQmlV4Function func(callData, &scope.result, v4); + QV4::ScopedValue rv(scope, QV4::Primitive::undefinedValue()); + QQmlV4Function func(callData, rv, v4); QQmlV4Function *funcptr = &func; void *args[] = { 0, &funcptr }; object.metacall(QMetaObject::InvokeMetaMethod, method.coreIndex(), args); - return; + return rv->asReturnedValue(); } if (!method.isOverload()) { - scope.result = CallPrecise(object, method, v4, callData); + return CallPrecise(object, method, v4, callData); } else { - scope.result = CallOverloaded(object, method, v4, callData, d()->propertyCache()); + return CallOverloaded(object, method, v4, callData, d()->propertyCache()); } } @@ -2028,13 +2025,14 @@ void QMetaObjectWrapper::init(ExecutionEngine *) { } } -void QMetaObjectWrapper::construct(const Managed *m, Scope &scope, CallData *callData) +ReturnedValue QMetaObjectWrapper::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - const QMetaObjectWrapper *This = static_cast<const QMetaObjectWrapper*>(m); - scope.result = This->constructInternal(callData); + const QMetaObjectWrapper *This = static_cast<const QMetaObjectWrapper*>(f); + return This->constructInternal(argv, argc); } -ReturnedValue QMetaObjectWrapper::constructInternal(CallData * callData) const { +ReturnedValue QMetaObjectWrapper::constructInternal(const Value *argv, int argc) const +{ d()->ensureConstructorsCache(); @@ -2047,6 +2045,8 @@ ReturnedValue QMetaObjectWrapper::constructInternal(CallData * callData) const { Scope scope(v4); Scoped<QObjectWrapper> object(scope); + JSCallData cData(scope, argc, argv); + CallData *callData = cData.callData(); if (d()->constructorCount == 1) { object = callConstructor(d()->constructors[0], v4, callData); @@ -2071,7 +2071,7 @@ ReturnedValue QMetaObjectWrapper::callConstructor(const QQmlPropertyData &data, ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const { const int numberOfConstructors = d()->constructorCount; - const int argumentCount = callArgs->argc; + const int argumentCount = callArgs->argc(); const QQmlStaticMetaObject object(d()->metaObject); QQmlPropertyData best; diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index 018e444f7c..c00e82e4fa 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -90,6 +90,7 @@ struct Q_QML_EXPORT QObjectWrapper : Object { } QObject *object() const { return qObj.data(); } + static void markObjects(Heap::Base *that, MarkStack *markStack); private: QQmlQPointer<QObject> qObj; @@ -102,7 +103,7 @@ private: Member(class, NoMark, int, index) DECLARE_HEAP_OBJECT(QObjectMethod, FunctionObject) { - DECLARE_MARK_TABLE(QObjectMethod); + DECLARE_MARKOBJECTS(QObjectMethod); void init(QV4::ExecutionContext *scope); void destroy() @@ -196,10 +197,9 @@ protected: static bool put(Managed *m, String *name, const Value &value); static PropertyAttributes query(const Managed *, String *name); static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); - static void markObjects(Heap::Base *that, QV4::MarkStack *markStack); - static void method_connect(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_disconnect(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_connect(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_disconnect(const BuiltinFunction *, CallData *callData); private: Q_NEVER_INLINE static ReturnedValue wrap_slowPath(ExecutionEngine *engine, QObject *object); @@ -234,12 +234,12 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject int methodIndex() const { return d()->index; } QObject *object() const { return d()->object(); } - QV4::ReturnedValue method_toString(QV4::ExecutionContext *ctx) const; - QV4::ReturnedValue method_destroy(QV4::ExecutionContext *ctx, const Value *args, int argc) const; + QV4::ReturnedValue method_toString(QV4::ExecutionEngine *engine) const; + QV4::ReturnedValue method_destroy(QV4::ExecutionEngine *ctx, const Value *args, int argc) const; - static void call(const Managed *, Scope &scope, CallData *callData); + static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - void callInternal(CallData *callData, Scope &scope) const; + ReturnedValue callInternal(const Value *thisObject, const Value *argv, int argc) const; static QPair<QObject *, int> extractQtMethod(const QV4::FunctionObject *function); }; @@ -251,14 +251,14 @@ struct Q_QML_EXPORT QMetaObjectWrapper : public QV4::FunctionObject V4_NEEDS_DESTROY static ReturnedValue create(ExecutionEngine *engine, const QMetaObject* metaObject); - static void construct(const Managed *, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc); static bool isEqualTo(Managed *a, Managed *b); const QMetaObject *metaObject() const { return d()->metaObject; } private: void init(ExecutionEngine *engine); - ReturnedValue constructInternal(CallData *callData) const; + ReturnedValue constructInternal(const Value *argv, int argc) const; ReturnedValue callConstructor(const QQmlPropertyData &data, QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const; ReturnedValue callOverloadedConstructor(QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const; diff --git a/src/qml/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp index 162f0fba57..fb49def317 100644 --- a/src/qml/jsruntime/qv4regexp.cpp +++ b/src/qml/jsruntime/qv4regexp.cpp @@ -62,7 +62,7 @@ uint RegExp::match(const QString &string, int start, uint *matchOffsets) WTF::String s(string); #if ENABLE(YARR_JIT) - if (!jitCode()->isFallBack() && jitCode()->has16BitCode()) + if (d()->hasValidJITCode()) return uint(jitCode()->execute(s.characters16(), start, s.length(), (int*)matchOffsets).start); #endif @@ -82,7 +82,7 @@ Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bo return result->d(); Scope scope(engine); - Scoped<RegExp> result(scope, engine->memoryManager->alloc<RegExp>(engine, pattern, ignoreCase, multiline, global)); + Scoped<RegExp> result(scope, engine->memoryManager->alloc<RegExp>(pattern, ignoreCase, multiline, global)); result->d()->cache = cache; cachedValue.set(engine, result); @@ -90,7 +90,7 @@ Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bo return result->d(); } -void Heap::RegExp::init(ExecutionEngine* engine, const QString &pattern, bool ignoreCase, bool multiline, bool global) +void Heap::RegExp::init(const QString &pattern, bool ignoreCase, bool multiline, bool global) { Base::init(); this->pattern = new QString(pattern); @@ -98,20 +98,28 @@ void Heap::RegExp::init(ExecutionEngine* engine, const QString &pattern, bool ig this->multiLine = multiline; this->global = global; + valid = false; + const char* error = 0; - JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), ignoreCase, multiline, &error); + JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), ignoreCase, multiLine, &error); if (error) return; subPatternCount = yarrPattern.m_numSubpatterns; - OwnPtr<JSC::Yarr::BytecodePattern> p = JSC::Yarr::byteCompile(yarrPattern, engine->bumperPointerAllocator); - byteCode = p.take(); #if ENABLE(YARR_JIT) - jitCode = new JSC::Yarr::YarrCodeBlock; - if (!yarrPattern.m_containsBackreferences && engine->iselFactory->jitCompileRegexps()) { - JSC::JSGlobalData dummy(engine->regExpAllocator); + if (!yarrPattern.m_containsBackreferences) { + jitCode = new JSC::Yarr::YarrCodeBlock; + JSC::JSGlobalData dummy(internalClass->engine->regExpAllocator); JSC::Yarr::jitCompile(yarrPattern, JSC::Yarr::Char16, &dummy, *jitCode); } #endif + if (hasValidJITCode()) { + valid = true; + return; + } + OwnPtr<JSC::Yarr::BytecodePattern> p = JSC::Yarr::byteCompile(yarrPattern, internalClass->engine->bumperPointerAllocator); + byteCode = p.take(); + if (byteCode) + valid = true; } void Heap::RegExp::destroy() diff --git a/src/qml/jsruntime/qv4regexp_p.h b/src/qml/jsruntime/qv4regexp_p.h index 998f6e3da3..498468e165 100644 --- a/src/qml/jsruntime/qv4regexp_p.h +++ b/src/qml/jsruntime/qv4regexp_p.h @@ -76,7 +76,7 @@ struct RegExpCacheKey; namespace Heap { struct RegExp : Base { - void init(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline, bool global); + void init(const QString& pattern, bool ignoreCase, bool multiline, bool global); void destroy(); QString *pattern; @@ -84,11 +84,19 @@ struct RegExp : Base { #if ENABLE(YARR_JIT) JSC::Yarr::YarrCodeBlock *jitCode; #endif + bool hasValidJITCode() const { +#if ENABLE(YARR_JIT) + return jitCode && !jitCode->isFallBack() && jitCode->has16BitCode(); +#else + return false; +#endif + } RegExpCache *cache; int subPatternCount; bool ignoreCase; bool multiLine; bool global; + bool valid; int captureCount() const { return subPatternCount + 1; } }; @@ -116,7 +124,7 @@ struct RegExp : public Managed static Heap::RegExp *create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase = false, bool multiline = false, bool global = false); - bool isValid() const { return d()->byteCode; } + bool isValid() const { return d()->valid; } uint match(const QString& string, int start, uint *matchOffsets); diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 83bfe21c67..f95719b25f 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -38,20 +38,17 @@ ****************************************************************************/ #include "qv4regexpobject_p.h" -#include "qv4jsir_p.h" -#include "qv4isel_p.h" #include "qv4objectproto_p.h" #include "qv4regexp_p.h" #include "qv4stringobject_p.h" #include <private/qv4mm_p.h> #include "qv4scopedvalue_p.h" +#include "qv4jscall_p.h" #include <private/qqmljsengine_p.h> #include <private/qqmljslexer_p.h> #include <private/qqmljsparser_p.h> #include <private/qqmljsast_p.h> -#include <qv4jsir_p.h> -#include <qv4codegen_p.h> #include "private/qlocale_tools_p.h" #include <QtCore/QDebug> @@ -217,39 +214,33 @@ void Heap::RegExpCtor::clearLastMatch() lastMatchEnd = 0; } -void RegExpCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue RegExpCtor::callAsConstructor(const FunctionObject *fo, const Value *argv, int argc) { - ScopedValue r(scope, callData->argument(0)); - ScopedValue f(scope, callData->argument(1)); + Scope scope(fo->engine()); + ScopedValue r(scope, argc ? argv[0] : Primitive::undefinedValue()); + ScopedValue f(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); Scoped<RegExpObject> re(scope, r); if (re) { - if (!f->isUndefined()) { - scope.result = scope.engine->throwTypeError(); - return; - } + if (!f->isUndefined()) + return scope.engine->throwTypeError(); Scoped<RegExp> regexp(scope, re->value()); - scope.result = Encode(scope.engine->newRegExpObject(regexp)); - return; + return Encode(scope.engine->newRegExpObject(regexp)); } QString pattern; if (!r->isUndefined()) pattern = r->toQString(); - if (scope.hasException()) { - scope.result = Encode::undefined(); - return; - } + if (scope.hasException()) + return Encode::undefined(); bool global = false; bool ignoreCase = false; bool multiLine = false; if (!f->isUndefined()) { ScopedString s(scope, f->toString(scope.engine)); - if (scope.hasException()) { - scope.result = Encode::undefined(); - return; - } + if (scope.hasException()) + return Encode::undefined(); QString str = s->toQString(); for (int i = 0; i < str.length(); ++i) { if (str.at(i) == QLatin1Char('g') && !global) { @@ -259,31 +250,27 @@ void RegExpCtor::construct(const Managed *, Scope &scope, CallData *callData) } else if (str.at(i) == QLatin1Char('m') && !multiLine) { multiLine = true; } else { - scope.result = scope.engine->throwSyntaxError(QStringLiteral("Invalid flags supplied to RegExp constructor")); - return; + return scope.engine->throwSyntaxError(QStringLiteral("Invalid flags supplied to RegExp constructor")); } } } Scoped<RegExp> regexp(scope, RegExp::create(scope.engine, pattern, ignoreCase, multiLine, global)); if (!regexp->isValid()) { - scope.result = scope.engine->throwSyntaxError(QStringLiteral("Invalid regular expression")); - return; + return scope.engine->throwSyntaxError(QStringLiteral("Invalid regular expression")); } - scope.result = Encode(scope.engine->newRegExpObject(regexp)); + return Encode(scope.engine->newRegExpObject(regexp)); } -void RegExpCtor::call(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue RegExpCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) { - if (callData->argc > 0 && callData->args[0].as<RegExpObject>()) { - if (callData->argc == 1 || callData->args[1].isUndefined()) { - scope.result = callData->args[0]; - return; - } + if (argc > 0 && argv[0].as<RegExpObject>()) { + if (argc == 1 || argv[1].isUndefined()) + return Encode(argv[0]); } - construct(that, scope, callData); + return callAsConstructor(f, argv, argc); } void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor) @@ -323,13 +310,60 @@ void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor) defineDefaultProperty(QStringLiteral("compile"), method_compile, 2); } -void RegExpPrototype::method_exec(const BuiltinFunction *, Scope &scope, CallData *callData) +/* used by String.match */ +ReturnedValue RegExpPrototype::execFirstMatch(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + Scoped<RegExpObject> r(scope, thisObject->as<RegExpObject>()); + Q_ASSERT(r && r->global()); + + ScopedString str(scope, argc ? argv[0] : Primitive::undefinedValue()); + Q_ASSERT(str); + QString s = str->toQString(); + + int offset = r->lastIndex(); + if (offset < 0 || offset > s.length()) { + r->setLastIndex(0); + RETURN_RESULT(Encode::null()); + } + + Q_ALLOCA_VAR(uint, matchOffsets, r->value()->captureCount() * 2 * sizeof(uint)); + const int result = Scoped<RegExp>(scope, r->value())->match(s, offset, matchOffsets); + + RegExpCtor *regExpCtor = static_cast<RegExpCtor *>(scope.engine->regExpCtor()); + regExpCtor->d()->clearLastMatch(); + + if (result == -1) { + r->setLastIndex(0); + RETURN_RESULT(Encode::null()); + } + + ReturnedValue retVal = Encode::undefined(); + // return first match + if (r->value()->captureCount()) { + int start = matchOffsets[0]; + int end = matchOffsets[1]; + retVal = (start != -1) ? scope.engine->newString(s.mid(start, end - start))->asReturnedValue() : Encode::undefined(); + } + + RegExpCtor::Data *dd = regExpCtor->d(); + dd->lastInput.set(scope.engine, str->d()); + dd->lastMatchStart = matchOffsets[0]; + dd->lastMatchEnd = matchOffsets[1]; + + r->setLastIndex(matchOffsets[1]); + + return retVal; +} + +ReturnedValue RegExpPrototype::method_exec(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - Scoped<RegExpObject> r(scope, callData->thisObject.as<RegExpObject>()); + Scope scope(b); + Scoped<RegExpObject> r(scope, thisObject->as<RegExpObject>()); if (!r) - THROW_TYPE_ERROR(); + return scope.engine->throwTypeError(); - ScopedValue arg(scope, callData->argument(0)); + ScopedValue arg(scope, argc ? argv[0]: Primitive::undefinedValue()); ScopedString str(scope, arg->toString(scope.engine)); if (scope.hasException()) RETURN_UNDEFINED(); @@ -344,7 +378,7 @@ void RegExpPrototype::method_exec(const BuiltinFunction *, Scope &scope, CallDat Q_ALLOCA_VAR(uint, matchOffsets, r->value()->captureCount() * 2 * sizeof(uint)); const int result = Scoped<RegExp>(scope, r->value())->match(s, offset, matchOffsets); - Scoped<RegExpCtor> regExpCtor(scope, scope.engine->regExpCtor()); + RegExpCtor *regExpCtor = static_cast<RegExpCtor *>(scope.engine->regExpCtor()); regExpCtor->d()->clearLastMatch(); if (result == -1) { @@ -376,73 +410,78 @@ void RegExpPrototype::method_exec(const BuiltinFunction *, Scope &scope, CallDat if (r->global()) r->setLastIndex(matchOffsets[1]); - scope.result = array; + return array.asReturnedValue(); } -void RegExpPrototype::method_test(const BuiltinFunction *b, Scope &scope, CallData *callData) +ReturnedValue RegExpPrototype::method_test(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - method_exec(b, scope, callData); - scope.result = Encode(!scope.result.isNull()); + Value res = Value::fromReturnedValue(method_exec(b, thisObject, argv, argc)); + return Encode(!res.isNull()); } -void RegExpPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue RegExpPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) { - Scoped<RegExpObject> r(scope, callData->thisObject.as<RegExpObject>()); + ExecutionEngine *v4 = b->engine(); + const RegExpObject *r = thisObject->as<RegExpObject>(); if (!r) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); - scope.result = scope.engine->newString(r->toString()); + return Encode(v4->newString(r->toString())); } -void RegExpPrototype::method_compile(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue RegExpPrototype::method_compile(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - Scoped<RegExpObject> r(scope, callData->thisObject.as<RegExpObject>()); + Scope scope(b); + Scoped<RegExpObject> r(scope, thisObject->as<RegExpObject>()); if (!r) - THROW_TYPE_ERROR(); - - ScopedCallData cData(scope, callData->argc); - memcpy(cData->args, callData->args, callData->argc*sizeof(Value)); + return scope.engine->throwTypeError(); - scope.engine->regExpCtor()->as<FunctionObject>()->construct(scope, cData); - Scoped<RegExpObject> re(scope, scope.result.asReturnedValue()); + Scoped<RegExpObject> re(scope, scope.engine->regExpCtor()->callAsConstructor(argv, argc)); r->d()->value.set(scope.engine, re->value()); + return Encode::undefined(); } template <int index> -void RegExpPrototype::method_get_lastMatch_n(const BuiltinFunction *, Scope &scope, CallData *) +ReturnedValue RegExpPrototype::method_get_lastMatch_n(const FunctionObject *b, const Value *, const Value *, int) { + Scope scope(b); ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(scope.engine->regExpCtor())->lastMatch()); - scope.result = lastMatch ? lastMatch->getIndexed(index) : Encode::undefined(); - if (scope.result.isUndefined()) - scope.result = scope.engine->newString(); + ScopedValue res(scope, lastMatch ? lastMatch->getIndexed(index) : Encode::undefined()); + if (res->isUndefined()) + res = scope.engine->newString(); + return res->asReturnedValue(); } -void RegExpPrototype::method_get_lastParen(const BuiltinFunction *, Scope &scope, CallData *) +ReturnedValue RegExpPrototype::method_get_lastParen(const FunctionObject *b, const Value *, const Value *, int) { + Scope scope(b); ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(scope.engine->regExpCtor())->lastMatch()); - scope.result = lastMatch ? lastMatch->getIndexed(lastMatch->getLength() - 1) : Encode::undefined(); - if (scope.result.isUndefined()) - scope.result = scope.engine->newString(); + ScopedValue res(scope, lastMatch ? lastMatch->getIndexed(lastMatch->getLength() - 1) : Encode::undefined()); + if (res->isUndefined()) + res = scope.engine->newString(); + return res->asReturnedValue(); } -void RegExpPrototype::method_get_input(const BuiltinFunction *, Scope &scope, CallData *) +ReturnedValue RegExpPrototype::method_get_input(const FunctionObject *b, const Value *, const Value *, int) { - scope.result = static_cast<RegExpCtor*>(scope.engine->regExpCtor())->lastInput(); + return static_cast<RegExpCtor*>(b->engine()->regExpCtor())->lastInput()->asReturnedValue(); } -void RegExpPrototype::method_get_leftContext(const BuiltinFunction *, Scope &scope, CallData *) +ReturnedValue RegExpPrototype::method_get_leftContext(const FunctionObject *b, const Value *, const Value *, int) { + Scope scope(b); Scoped<RegExpCtor> regExpCtor(scope, scope.engine->regExpCtor()); QString lastInput = regExpCtor->lastInput()->toQString(); - scope.result = scope.engine->newString(lastInput.left(regExpCtor->lastMatchStart())); + return Encode(scope.engine->newString(lastInput.left(regExpCtor->lastMatchStart()))); } -void RegExpPrototype::method_get_rightContext(const BuiltinFunction *, Scope &scope, CallData *) +ReturnedValue RegExpPrototype::method_get_rightContext(const FunctionObject *b, const Value *, const Value *, int) { + Scope scope(b); Scoped<RegExpCtor> regExpCtor(scope, scope.engine->regExpCtor()); QString lastInput = regExpCtor->lastInput()->toQString(); - scope.result = scope.engine->newString(lastInput.mid(regExpCtor->lastMatchEnd())); + return Encode(scope.engine->newString(lastInput.mid(regExpCtor->lastMatchEnd()))); } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index 65055ccc81..181628241b 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -55,8 +55,6 @@ #include "qv4context_p.h" #include "qv4functionobject_p.h" #include "qv4string_p.h" -#include <qv4codegen_p.h> -#include <qv4isel_p.h> #include "qv4managed_p.h" #include "qv4property_p.h" #include "qv4objectiterator_p.h" @@ -78,7 +76,7 @@ namespace Heap { Member(class, Pointer, RegExp *, value) DECLARE_HEAP_OBJECT(RegExpObject, Object) { - DECLARE_MARK_TABLE(RegExpObject); + DECLARE_MARKOBJECTS(RegExpObject); void init(); void init(QV4::RegExp *value); @@ -92,7 +90,7 @@ DECLARE_HEAP_OBJECT(RegExpObject, Object) { Member(class, NoMark, int, lastMatchEnd) DECLARE_HEAP_OBJECT(RegExpCtor, FunctionObject) { - DECLARE_MARK_TABLE(RegExpCtor); + DECLARE_MARKOBJECTS(RegExpCtor); void init(QV4::ExecutionContext *scope); void clearLastMatch(); @@ -106,7 +104,7 @@ struct RegExpObject: Object { V4_INTERNALCLASS(RegExpObject) V4_PROTOTYPE(regExpPrototype) - // needs to be compatible with the flags in qv4jsir_p.h + // needs to be compatible with the flags in qv4compileddata_p.h enum Flags { RegExp_Global = 0x01, RegExp_IgnoreCase = 0x02, @@ -123,6 +121,8 @@ struct RegExpObject: Object { Index_ArrayInput = Index_ArrayIndex + 1 }; + enum { NInlineProperties = 5 }; + Heap::RegExp *value() const { return d()->value; } bool global() const { return d()->value->global; } @@ -152,25 +152,27 @@ struct RegExpCtor: FunctionObject int lastMatchStart() { return d()->lastMatchStart; } int lastMatchEnd() { return d()->lastMatchEnd; } - static void construct(const Managed *m, Scope &scope, CallData *callData); - static void call(const Managed *that, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct RegExpPrototype: RegExpObject { void init(ExecutionEngine *engine, Object *ctor); - static void method_exec(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_test(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_compile(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_exec(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_test(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_compile(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); template <int index> - static void method_get_lastMatch_n(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_get_lastParen(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_get_input(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_get_leftContext(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_get_rightContext(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_get_lastMatch_n(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get_lastParen(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get_input(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get_leftContext(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get_rightContext(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + + static ReturnedValue execFirstMatch(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); }; } diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 4b952bcbbc..c41b720610 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -55,6 +55,7 @@ #include "qv4regexpobject_p.h" #include "private/qlocale_tools_p.h" #include "qv4scopedvalue_p.h" +#include "qv4jscall_p.h" #include <private/qv4qmlcontext_p.h> #include <private/qqmltypewrapper_p.h> #include <private/qqmlengine_p.h> @@ -310,47 +311,47 @@ void RuntimeHelpers::numberToString(QString *result, double num, int radix) ReturnedValue Runtime::method_closure(ExecutionEngine *engine, int functionId) { - QV4::Function *clos = static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)->runtimeFunctions[functionId]; + QV4::Function *clos = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeFunctions[functionId]; Q_ASSERT(clos); - return FunctionObject::createScriptFunction(engine->currentContext, clos)->asReturnedValue(); + ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context); + return FunctionObject::createScriptFunction(current, clos)->asReturnedValue(); } -ReturnedValue Runtime::method_deleteElement(ExecutionEngine *engine, const Value &base, const Value &index) +bool Runtime::method_deleteElement(ExecutionEngine *engine, const Value &base, const Value &index) { Scope scope(engine); ScopedObject o(scope, base); if (o) { uint n = index.asArrayIndex(); - if (n < UINT_MAX) { - return Encode((bool)o->deleteIndexedProperty(n)); - } + if (n < UINT_MAX) + return o->deleteIndexedProperty(n); } ScopedString name(scope, index.toString(engine)); return method_deleteMemberString(engine, base, name); } -ReturnedValue Runtime::method_deleteMember(ExecutionEngine *engine, const Value &base, int nameIndex) +bool Runtime::method_deleteMember(ExecutionEngine *engine, const Value &base, int nameIndex) { Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); return method_deleteMemberString(engine, base, name); } -ReturnedValue Runtime::method_deleteMemberString(ExecutionEngine *engine, const Value &base, String *name) +bool Runtime::method_deleteMemberString(ExecutionEngine *engine, const Value &base, String *name) { Scope scope(engine); ScopedObject obj(scope, base.toObject(engine)); if (scope.engine->hasException) return Encode::undefined(); - return Encode(obj->deleteProperty(name)); + return obj->deleteProperty(name); } -ReturnedValue Runtime::method_deleteName(ExecutionEngine *engine, int nameIndex) +bool Runtime::method_deleteName(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); - return Encode(engine->currentContext->deleteProperty(name)); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + return static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).deleteProperty(name); } QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Value &lval, const Value &rval) @@ -425,14 +426,13 @@ ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeH qSwap(meth1, meth2); Scope scope(engine); - ScopedCallData callData(scope, 0); - callData->thisObject = *object; - + ScopedValue result(scope); ScopedValue conv(scope, object->get(meth1)); + if (FunctionObject *o = conv->as<FunctionObject>()) { - o->call(scope, callData); - if (scope.result.isPrimitive()) - return scope.result.asReturnedValue(); + result = o->call(object, nullptr, 0); + if (result->isPrimitive()) + return result->asReturnedValue(); } if (engine->hasException) @@ -440,9 +440,9 @@ ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeH conv = object->get(meth2); if (FunctionObject *o = conv->as<FunctionObject>()) { - o->call(scope, callData); - if (scope.result.isPrimitive()) - return scope.result.asReturnedValue(); + result = o->call(object, nullptr, 0); + if (result->isPrimitive()) + return result->asReturnedValue(); } return engine->throwTypeError(); @@ -469,8 +469,9 @@ Heap::Object *RuntimeHelpers::convertToObject(ExecutionEngine *engine, const Val } } -Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, const Value &value) +Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, Value value, TypeHint hint) { + redo: switch (value.type()) { case Value::Empty_Type: Q_ASSERT(!"empty Value encountered"); @@ -484,15 +485,15 @@ Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, const Val return engine->id_true()->d(); else return engine->id_false()->d(); - case Value::Managed_Type: - if (String *s = value.stringValue()) - return s->d(); - { - Scope scope(engine); - ScopedValue prim(scope, RuntimeHelpers::toPrimitive(value, STRING_HINT)); - Q_ASSERT(!prim->isManaged() || prim->isString()); - return RuntimeHelpers::convertToString(engine, prim); - } + case Value::Managed_Type: { + if (value.isString()) + return static_cast<const String &>(value).d(); + value = Primitive::fromReturnedValue(RuntimeHelpers::toPrimitive(value, hint)); + Q_ASSERT(value.isPrimitive()); + if (value.isString()) + return static_cast<const String &>(value).d(); + goto redo; + } case Value::Integer_Type: return RuntimeHelpers::stringFromNumber(engine, value.int_32()); default: // double @@ -502,34 +503,9 @@ Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, const Val // This is slightly different from the method above, as // the + operator requires a slightly different conversion -static Heap::String *convert_to_string_add(ExecutionEngine *engine, const Value &value) +static Heap::String *convert_to_string_add(ExecutionEngine *engine, Value value) { - switch (value.type()) { - case Value::Empty_Type: - Q_ASSERT(!"empty Value encountered"); - Q_UNREACHABLE(); - case Value::Undefined_Type: - return engine->id_undefined()->d(); - case Value::Null_Type: - return engine->id_null()->d(); - case Value::Boolean_Type: - if (value.booleanValue()) - return engine->id_true()->d(); - else - return engine->id_false()->d(); - case Value::Managed_Type: - if (String *s = value.stringValue()) - return s->d(); - { - Scope scope(engine); - ScopedValue prim(scope, RuntimeHelpers::toPrimitive(value, PREFERREDTYPE_HINT)); - return RuntimeHelpers::convertToString(engine, prim); - } - case Value::Integer_Type: - return RuntimeHelpers::stringFromNumber(engine, value.int_32()); - default: // double - return RuntimeHelpers::stringFromNumber(engine, value.doubleValue()); - } // switch + return RuntimeHelpers::convertToString(engine, value, PREFERREDTYPE_HINT); } QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Value &left, const Value &right) @@ -549,7 +525,7 @@ QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Valu pright = convert_to_string_add(engine, pright); sright = static_cast<String *>(pright.ptr); } - if (scope.engine->hasException) + if (engine->hasException) return Encode::undefined(); if (!sleft->d()->length()) return sright->asReturnedValue(); @@ -563,42 +539,14 @@ QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Valu return Encode(x + y); } -QV4::ReturnedValue Runtime::method_addString(ExecutionEngine *engine, const Value &left, const Value &right) +bool Runtime::method_storeProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value) { - Q_ASSERT(left.isString() || right.isString()); - Scope scope(engine); - ScopedValue pleft(scope, left); - ScopedValue pright(scope, right); - String *sleft = pleft->stringValue(); - String *sright = pright->stringValue(); - - if (!sleft) { - pleft = convert_to_string_add(engine, pleft); - sleft = static_cast<String *>(pleft.ptr); - } - if (!sright) { - pright = convert_to_string_add(engine, pright); - sright = static_cast<String *>(pright.ptr); - } - if (scope.engine->hasException) - return Encode::undefined(); - if (!sleft->d()->length()) - return pright->asReturnedValue(); - if (!sright->d()->length()) - return pleft->asReturnedValue(); - MemoryManager *mm = engine->memoryManager; - return (mm->alloc<String>(sleft->d(), sright->d()))->asReturnedValue(); -} - -void Runtime::method_setProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value) -{ - Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); ScopedObject o(scope, object.toObject(engine)); if (!o) - return; - o->put(name, value); + return false; + return o->put(name, value); } static Q_NEVER_INLINE ReturnedValue getElementIntFallback(ExecutionEngine *engine, const Value &object, uint idx) @@ -656,7 +604,7 @@ static Q_NEVER_INLINE ReturnedValue getElementFallback(ExecutionEngine *engine, return o->get(name); } -ReturnedValue Runtime::method_getElement(ExecutionEngine *engine, const Value &object, const Value &index) +ReturnedValue Runtime::method_loadElement(ExecutionEngine *engine, const Value &object, const Value &index) { uint idx; if (index.asArrayIndex(idx)) { @@ -677,12 +625,12 @@ ReturnedValue Runtime::method_getElement(ExecutionEngine *engine, const Value &o return getElementFallback(engine, object, index); } -static Q_NEVER_INLINE void setElementFallback(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) +static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) { Scope scope(engine); ScopedObject o(scope, object.toObject(engine)); if (engine->hasException) - return; + return false; uint idx; if (index.asArrayIndex(idx)) { @@ -690,18 +638,17 @@ static Q_NEVER_INLINE void setElementFallback(ExecutionEngine *engine, const Val Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>(); if (idx < s->values.size) { s->setData(engine, idx, value); - return; + return true; } } - o->putIndexed(idx, value); - return; + return o->putIndexed(idx, value); } ScopedString name(scope, index.toString(engine)); - o->put(name, value); + return o->put(name, value); } -void Runtime::method_setElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) +bool Runtime::method_storeElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) { uint idx; if (index.asArrayIndex(idx)) { @@ -712,7 +659,7 @@ void Runtime::method_setElement(ExecutionEngine *engine, const Value &object, co Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>(); if (idx < s->values.size) { s->setData(engine, idx, value); - return; + return true; } } } @@ -742,17 +689,31 @@ ReturnedValue Runtime::method_foreachNextPropertyName(const Value &foreach_itera } -void Runtime::method_setActivationProperty(ExecutionEngine *engine, int nameIndex, const Value &value) +void Runtime::method_storeNameSloppy(ExecutionEngine *engine, int nameIndex, const Value &value) +{ + Scope scope(engine); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + ExecutionContext::Error e = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).setProperty(name, value); + + if (e == ExecutionContext::RangeError) + engine->globalObject->put(name, value); +} + +void Runtime::method_storeNameStrict(ExecutionEngine *engine, int nameIndex, const Value &value) { Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); - engine->currentContext->setProperty(name, value); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + ExecutionContext::Error e = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).setProperty(name, value); + if (e == ExecutionContext::TypeError) + engine->throwTypeError(); + else if (e == ExecutionContext::RangeError) + engine->throwReferenceError(name); } -ReturnedValue Runtime::method_getProperty(ExecutionEngine *engine, const Value &object, int nameIndex) +ReturnedValue Runtime::method_loadProperty(ExecutionEngine *engine, const Value &object, int nameIndex) { Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); ScopedObject o(scope, object); if (o) @@ -769,11 +730,11 @@ ReturnedValue Runtime::method_getProperty(ExecutionEngine *engine, const Value & return o->get(name); } -ReturnedValue Runtime::method_getActivationProperty(ExecutionEngine *engine, int nameIndex) +ReturnedValue Runtime::method_loadName(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); - return engine->currentContext->getProperty(name); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + return static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).getProperty(name); } #endif // V4_BOOTSTRAP @@ -1002,233 +963,142 @@ uint Runtime::method_compareIn(ExecutionEngine *engine, const Value &left, const } -ReturnedValue Runtime::method_callGlobalLookup(ExecutionEngine *engine, uint index, CallData *callData) +ReturnedValue Runtime::method_callGlobalLookup(ExecutionEngine *engine, uint index, Value *argv, int argc) { - Scope scope(engine); - Q_ASSERT(callData->thisObject.isUndefined()); - - Lookup *l = engine->current->lookups + index; - ScopedFunctionObject o(scope, l->globalGetter(l, engine)); - if (!o) + Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index; + Value function = Value::fromReturnedValue(l->globalGetter(l, engine)); + if (!function.isFunctionObject()) return engine->throwTypeError(); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]); - if (o->d() == scope.engine->evalFunction()->d() && name->equals(scope.engine->id_eval())) { - static_cast<EvalFunction *>(o.getPointer())->evalCall(scope, callData, true); - } else { - o->call(scope, callData); - } - - return scope.result.asReturnedValue(); + Value thisObject = Primitive::undefinedValue(); + return static_cast<FunctionObject &>(function).call(&thisObject, argv, argc); } - -ReturnedValue Runtime::method_callActivationProperty(ExecutionEngine *engine, int nameIndex, CallData *callData) +ReturnedValue Runtime::method_callPossiblyDirectEval(ExecutionEngine *engine, Value *argv, int argc) { - Q_ASSERT(callData->thisObject.isUndefined()); Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); + ScopedValue thisObject(scope); - ScopedObject base(scope); - ScopedValue func(scope, engine->currentContext->getPropertyAndBase(name, base.getRef())); - if (scope.engine->hasException) + ExecutionContext &ctx = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context); + ScopedFunctionObject function(scope, ctx.getPropertyAndBase(engine->id_eval(), thisObject)); + if (engine->hasException) return Encode::undefined(); - if (base) - callData->thisObject = base; - - FunctionObject *o = func->as<FunctionObject>(); - if (!o) { + if (!function) { QString objectAsString = QStringLiteral("[null]"); - if (base) - objectAsString = ScopedValue(scope, base.asReturnedValue())->toQStringNoThrow(); - QString msg = QStringLiteral("Property '%1' of object %2 is not a function").arg(name->toQString()).arg(objectAsString); + if (!thisObject->isUndefined()) + objectAsString = thisObject->toQStringNoThrow(); + QString msg = QStringLiteral("Property 'eval' of object %2 is not a function").arg(objectAsString); return engine->throwTypeError(msg); } - if (o->d() == scope.engine->evalFunction()->d() && name->equals(scope.engine->id_eval())) { - static_cast<EvalFunction *>(o)->evalCall(scope, callData, true); - } else { - o->call(scope, callData); - } + if (function->d() == engine->evalFunction()->d()) + return static_cast<EvalFunction *>(function.getPointer())->evalCall(thisObject, argv, argc, true); - return scope.result.asReturnedValue(); + return function->call(thisObject, argv, argc); } -ReturnedValue Runtime::method_callQmlScopeObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData) +ReturnedValue Runtime::method_callName(ExecutionEngine *engine, int nameIndex, Value *argv, int argc) { Scope scope(engine); - ScopedFunctionObject o(scope, method_getQmlScopeObjectProperty(engine, callData->thisObject, propertyIndex, /*captureRequired*/true)); - if (!o) { - QString error = QStringLiteral("Property '%1' of scope object is not a function").arg(propertyIndex); - return engine->throwTypeError(error); - } + ScopedValue thisObject(scope); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - o->call(scope, callData); - return scope.result.asReturnedValue(); -} + ExecutionContext &ctx = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context); + ScopedFunctionObject f(scope, ctx.getPropertyAndBase(name, thisObject)); + if (engine->hasException) + return Encode::undefined(); -ReturnedValue Runtime::method_callQmlContextObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData) -{ - Scope scope(engine); - ScopedFunctionObject o(scope, method_getQmlContextObjectProperty(engine, callData->thisObject, propertyIndex, /*captureRequired*/true)); - if (!o) { - QString error = QStringLiteral("Property '%1' of context object is not a function").arg(propertyIndex); - return engine->throwTypeError(error); + if (!f) { + QString objectAsString = QStringLiteral("[null]"); + if (!thisObject->isUndefined()) + objectAsString = thisObject->toQStringNoThrow(); + QString msg = QStringLiteral("Property '%1' of object %2 is not a function") + .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(), + objectAsString); + return engine->throwTypeError(msg); } - o->call(scope, callData); - return scope.result.asReturnedValue(); + return f->call(thisObject, argv, argc); } -ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, int nameIndex, CallData *callData) +ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, Value *base, int nameIndex, Value *argv, int argc) { Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); - ScopedObject baseObject(scope, callData->thisObject); - if (!baseObject) { - Q_ASSERT(!callData->thisObject.isEmpty()); - if (callData->thisObject.isNullOrUndefined()) { - QString message = QStringLiteral("Cannot call method '%1' of %2").arg(name->toQString()).arg(callData->thisObject.toQStringNoThrow()); + + if (!base->isObject()) { + Q_ASSERT(!base->isEmpty()); + if (base->isNullOrUndefined()) { + QString message = QStringLiteral("Cannot call method '%1' of %2") + .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(), + base->toQStringNoThrow()); return engine->throwTypeError(message); } - baseObject = RuntimeHelpers::convertToObject(scope.engine, callData->thisObject); - if (!baseObject) // type error + ScopedValue thisObject(scope, RuntimeHelpers::convertToObject(engine, *base)); + if (engine->hasException) // type error return Encode::undefined(); - callData->thisObject = baseObject.asReturnedValue(); + base = thisObject; } - ScopedFunctionObject o(scope, baseObject->get(name)); - if (o) { - o->call(scope, callData); - return scope.result.asReturnedValue(); - } else { - QString error = QStringLiteral("Property '%1' of object %2 is not a function").arg(name->toQString(), callData->thisObject.toQStringNoThrow()); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + ScopedFunctionObject f(scope, static_cast<Object *>(base)->get(name)); + + if (!f) { + QString error = QStringLiteral("Property '%1' of object %2 is not a function") + .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(), + base->toQStringNoThrow()); return engine->throwTypeError(error); } + return f->call(base, argv, argc); } -ReturnedValue Runtime::method_callPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData) -{ - Lookup *l = engine->current->lookups + index; - Value v; - v = l->getter(l, engine, callData->thisObject); - Object *o = v.objectValue(); - if (Q_LIKELY(o)) { - Scope scope(engine); - o->call(scope, callData); - return scope.result.asReturnedValue(); - } - return engine->throwTypeError(); -} - -ReturnedValue Runtime::method_callElement(ExecutionEngine *engine, const Value &index, CallData *callData) +ReturnedValue Runtime::method_callPropertyLookup(ExecutionEngine *engine, Value *base, uint index, Value *argv, int argc) { - Scope scope(engine); - ScopedObject baseObject(scope, callData->thisObject.toObject(engine)); - ScopedString s(scope, index.toString(engine)); - - if (scope.engine->hasException) - return Encode::undefined(); - callData->thisObject = baseObject; + Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index; + // ok to have the value on the stack here + Value f = Value::fromReturnedValue(l->getter(l, engine, *base)); - ScopedObject o(scope, baseObject->get(s)); - if (!o) + if (!f.isFunctionObject()) return engine->throwTypeError(); - o->call(scope, callData); - return scope.result.asReturnedValue(); -} - -ReturnedValue Runtime::method_callValue(ExecutionEngine *engine, const Value &func, CallData *callData) -{ - if (Object *o = func.objectValue()) { - Scope scope(engine); - o->call(scope, callData); - return scope.result.asReturnedValue(); - } - return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow())); + return static_cast<FunctionObject &>(f).call(base, argv, argc); } - -ReturnedValue Runtime::method_constructGlobalLookup(ExecutionEngine *engine, uint index, CallData *callData) +ReturnedValue Runtime::method_callElement(ExecutionEngine *engine, Value *base, const Value &index, Value *argv, int argc) { Scope scope(engine); - Q_ASSERT(callData->thisObject.isUndefined()); - - Lookup *l = engine->current->lookups + index; - ScopedObject f(scope, l->globalGetter(l, engine)); - if (f) { - f->construct(scope, callData); - return scope.result.asReturnedValue(); - } else { - return engine->throwTypeError(); - } -} - + ScopedValue thisObject(scope, base->toObject(engine)); + base = thisObject; -ReturnedValue Runtime::method_constructActivationProperty(ExecutionEngine *engine, int nameIndex, CallData *callData) -{ - Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); - ScopedValue func(scope, engine->currentContext->getProperty(name)); - if (scope.engine->hasException) + ScopedString str(scope, index.toString(engine)); + if (engine->hasException) return Encode::undefined(); - Object *f = func->as<Object>(); + ScopedFunctionObject f(scope, static_cast<Object *>(base)->get(str)); if (!f) return engine->throwTypeError(); - f->construct(scope, callData); - return scope.result.asReturnedValue(); + return f->call(base, argv, argc); } -ReturnedValue Runtime::method_constructValue(ExecutionEngine *engine, const Value &func, CallData *callData) +ReturnedValue Runtime::method_callValue(ExecutionEngine *engine, const Value &func, Value *argv, int argc) { - const Object *f = func.as<Object>(); - if (!f) - return engine->throwTypeError(); - - Scope scope(engine); - f->construct(scope, callData); - return scope.result.asReturnedValue(); + if (!func.isFunctionObject()) + return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow())); + return static_cast<const FunctionObject &>(func).call(nullptr, argv, argc); } -ReturnedValue Runtime::method_constructProperty(ExecutionEngine *engine, int nameIndex, CallData *callData) -{ - Scope scope(engine); - ScopedObject thisObject(scope, callData->thisObject.toObject(engine)); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); - if (scope.engine->hasException) - return Encode::undefined(); - ScopedObject f(scope, thisObject->get(name)); - if (f) { - Scope scope(engine); - f->construct(scope, callData); - return scope.result.asReturnedValue(); - } else { +ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &function, Value *argv, int argc) +{ + if (!function.isFunctionObject()) return engine->throwTypeError(); - } -} -ReturnedValue Runtime::method_constructPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData) -{ - Lookup *l = engine->current->lookups + index; - Value v; - v = l->getter(l, engine, callData->thisObject); - Object *o = v.objectValue(); - if (Q_LIKELY(o)) { - Scope scope(engine); - o->construct(scope, callData); - return scope.result.asReturnedValue(); - } - return engine->throwTypeError(); + return static_cast<const FunctionObject &>(function).callAsConstructor(argv, argc); } - void Runtime::method_throwException(ExecutionEngine *engine, const Value &value) { if (!value.isEmpty()) @@ -1267,96 +1137,37 @@ ReturnedValue Runtime::method_typeofValue(ExecutionEngine *engine, const Value & QV4::ReturnedValue Runtime::method_typeofName(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); - ScopedValue prop(scope, engine->currentContext->getProperty(name)); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + ScopedValue prop(scope, static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).getProperty(name)); // typeof doesn't throw. clear any possible exception scope.engine->hasException = false; return method_typeofValue(engine, prop); } -#ifndef V4_BOOTSTRAP -ReturnedValue Runtime::method_typeofScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex) -{ - Scope scope(engine); - ScopedValue prop(scope, method_getQmlScopeObjectProperty(engine, context, propertyIndex, /*captureRequired*/true)); - if (scope.engine->hasException) - return Encode::undefined(); - return method_typeofValue(engine, prop); -} - -ReturnedValue Runtime::method_typeofContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex) -{ - Scope scope(engine); - ScopedValue prop(scope, method_getQmlContextObjectProperty(engine, context, propertyIndex, /*captureRequired*/true)); - if (scope.engine->hasException) - return Encode::undefined(); - return method_typeofValue(engine, prop); -} -#endif // V4_BOOTSTRAP - -QV4::ReturnedValue Runtime::method_typeofMember(ExecutionEngine *engine, const Value &base, int nameIndex) -{ - Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); - ScopedObject obj(scope, base.toObject(engine)); - if (scope.engine->hasException) - return Encode::undefined(); - ScopedValue prop(scope, obj->get(name)); - return method_typeofValue(engine, prop); -} - -QV4::ReturnedValue Runtime::method_typeofElement(ExecutionEngine *engine, const Value &base, const Value &index) -{ - Scope scope(engine); - ScopedString name(scope, index.toString(engine)); - ScopedObject obj(scope, base.toObject(engine)); - if (scope.engine->hasException) - return Encode::undefined(); - ScopedValue prop(scope, obj->get(name)); - return method_typeofValue(engine, prop); -} - -ReturnedValue Runtime::method_unwindException(ExecutionEngine *engine) -{ - if (!engine->hasException) - return Primitive::emptyValue().asReturnedValue(); - return engine->catchException(0); -} - /* The next three methods are a bit tricky. They can't open up a Scope, as that * would mess up the pushing of the context. * * Instead the push/pop pair acts as a non local scope. */ -void Runtime::method_pushWithScope(const Value &o, NoThrowEngine *engine) -{ - QV4::Value *v = engine->jsAlloca(1); - Heap::Object *withObject = o.toObject(engine); - *v = withObject; - engine->pushContext(engine->currentContext->newWithContext(withObject)); - Q_ASSERT(engine->jsStackTop == engine->currentContext + 2); -} - -void Runtime::method_pushCatchScope(NoThrowEngine *engine, int exceptionVarNameIndex) +ReturnedValue Runtime::method_createWithContext(ExecutionContext *parent, const Value &o) { - engine->jsAlloca(1); // keep this symmetric with pushWithScope - ExecutionContext *c = engine->currentContext; - engine->pushContext(c->newCatchContext(c->d()->compilationUnit->runtimeStrings[exceptionVarNameIndex], engine->catchException(0))); - Q_ASSERT(engine->jsStackTop == engine->currentContext + 2); + Q_ASSERT(o.isObject()); + const Object &obj = static_cast<const Object &>(o); + return parent->newWithContext(obj.d())->asReturnedValue(); } -void Runtime::method_popScope(NoThrowEngine *engine) +ReturnedValue Runtime::method_createCatchContext(ExecutionContext *parent, int exceptionVarNameIndex) { - Q_ASSERT(engine->jsStackTop == engine->currentContext + 2); - engine->popContext(); - engine->jsStackTop -= 3; + ExecutionEngine *e = parent->engine(); + return parent->newCatchContext(e->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex], + e->catchException(0))->asReturnedValue(); } void Runtime::method_declareVar(ExecutionEngine *engine, bool deletable, int nameIndex) { Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); - engine->currentContext->createMutableBinding(name, deletable); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).createMutableBinding(name, deletable); } ReturnedValue Runtime::method_arrayLiteral(ExecutionEngine *engine, Value *values, uint length) @@ -1367,7 +1178,7 @@ ReturnedValue Runtime::method_arrayLiteral(ExecutionEngine *engine, Value *value ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, const QV4::Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags) { Scope scope(engine); - QV4::InternalClass *klass = static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)->runtimeClasses[classId]; + QV4::InternalClass *klass = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeClasses[classId]; ScopedObject o(scope, engine->newObject(klass, engine->objectPrototype())); { @@ -1406,134 +1217,46 @@ ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, const QV4:: return o.asReturnedValue(); } -QV4::ReturnedValue Runtime::method_setupArgumentsObject(ExecutionEngine *engine) +QV4::ReturnedValue Runtime::method_createMappedArgumentsObject(ExecutionEngine *engine) { - Q_ASSERT(engine->current->type == Heap::ExecutionContext::Type_CallContext); - QV4::CallContext *c = static_cast<QV4::CallContext *>(engine->currentContext); - QV4::InternalClass *ic = engine->internalClasses[c->d()->strictMode ? EngineBase::Class_StrictArgumentsObject : EngineBase::Class_ArgumentsObject]; - return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->objectPrototype(), c)->asReturnedValue(); + Q_ASSERT(engine->currentContext()->d()->type == Heap::ExecutionContext::Type_CallContext); + QV4::InternalClass *ic = engine->internalClasses[EngineBase::Class_ArgumentsObject]; + return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->objectPrototype(), engine->currentStackFrame)->asReturnedValue(); } -#endif // V4_BOOTSTRAP - -QV4::ReturnedValue Runtime::method_increment(const Value &value) +QV4::ReturnedValue Runtime::method_createUnmappedArgumentsObject(ExecutionEngine *engine) { - TRACE1(value); - - if (value.isInteger() && value.integerValue() < INT_MAX) - return Encode(value.integerValue() + 1); - else { - double d = value.toNumber(); - return Encode(d + 1.); - } -} - -QV4::ReturnedValue Runtime::method_decrement(const Value &value) -{ - TRACE1(value); - - if (value.isInteger() && value.integerValue() > INT_MIN) - return Encode(value.integerValue() - 1); - else { - double d = value.toNumber(); - return Encode(d - 1.); - } -} - -ReturnedValue Runtime::method_toDouble(const Value &value) -{ - TRACE1(value); - return Encode(value.toNumber()); -} - -int Runtime::method_toInt(const Value &value) -{ - TRACE1(value); - return value.toInt32(); -} - -int Runtime::method_doubleToInt(const double &d) -{ - TRACE0(); - return Primitive::toInt32(d); -} - -unsigned Runtime::method_toUInt(const Value &value) -{ - TRACE1(value); - return value.toUInt32(); -} - -unsigned Runtime::method_doubleToUInt(const double &d) -{ - TRACE0(); - return Primitive::toUInt32(d); + QV4::InternalClass *ic = engine->internalClasses[EngineBase::Class_StrictArgumentsObject]; + return engine->memoryManager->allocObject<StrictArgumentsObject>(ic, engine->objectPrototype(), engine->currentStackFrame)->asReturnedValue(); } -#ifndef V4_BOOTSTRAP - -ReturnedValue Runtime::method_getQmlContext(NoThrowEngine *engine) +ReturnedValue Runtime::method_loadQmlContext(NoThrowEngine *engine) { return engine->qmlContext()->asReturnedValue(); } ReturnedValue Runtime::method_regexpLiteral(ExecutionEngine *engine, int id) { - Heap::RegExpObject *ro = engine->newRegExpObject( - static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit) - ->runtimeRegularExpressions[id].as<RegExp>()); + Heap::RegExpObject *ro = engine->newRegExpObject(engine->currentStackFrame->v4Function->compilationUnit->runtimeRegularExpressions[id].as<RegExp>()); return ro->asReturnedValue(); } -ReturnedValue Runtime::method_getQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired) -{ - Scope scope(engine); - QV4::Scoped<QObjectWrapper> wrapper(scope, object); - if (!wrapper) { - engine->throwTypeError(QStringLiteral("Cannot read property of null")); - return Encode::undefined(); - } - return QV4::QObjectWrapper::getProperty(scope.engine, wrapper->object(), propertyIndex, captureRequired); -} - -QV4::ReturnedValue Runtime::method_getQmlAttachedProperty(ExecutionEngine *engine, int attachedPropertiesId, int propertyIndex) -{ - QObject *scopeObject = engine->qmlScopeObject(); - QObject *attachedObject = qmlAttachedPropertiesObjectById(attachedPropertiesId, scopeObject); - - QJSEngine *jsEngine = engine->jsEngine(); - QQmlData::ensurePropertyCache(jsEngine, attachedObject); - return QV4::QObjectWrapper::getProperty(engine, attachedObject, propertyIndex, /*captureRequired*/true); -} - -ReturnedValue Runtime::method_getQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired) +ReturnedValue Runtime::method_loadQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired) { const QmlContext &c = static_cast<const QmlContext &>(context); - return QV4::QObjectWrapper::getProperty(engine, c.d()->qml->scopeObject, propertyIndex, captureRequired); + return QV4::QObjectWrapper::getProperty(engine, c.d()->qml()->scopeObject, propertyIndex, captureRequired); } -ReturnedValue Runtime::method_getQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired) +ReturnedValue Runtime::method_loadQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired) { const QmlContext &c = static_cast<const QmlContext &>(context); - return QV4::QObjectWrapper::getProperty(engine, (*c.d()->qml->context)->contextObject, propertyIndex, captureRequired); -} - -ReturnedValue Runtime::method_getQmlSingletonQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired) -{ - Scope scope(engine); - QV4::Scoped<QQmlTypeWrapper> wrapper(scope, object); - if (!wrapper) { - scope.engine->throwTypeError(QStringLiteral("Cannot read property of null")); - return Encode::undefined(); - } - return QV4::QObjectWrapper::getProperty(scope.engine, wrapper->singletonObject(), propertyIndex, captureRequired); + return QV4::QObjectWrapper::getProperty(engine, (*c.d()->qml()->context)->contextObject, propertyIndex, captureRequired); } -ReturnedValue Runtime::method_getQmlIdObject(ExecutionEngine *engine, const Value &c, uint index) +ReturnedValue Runtime::method_loadQmlIdObject(ExecutionEngine *engine, const Value &c, uint index) { - Scope scope(engine); const QmlContext &qmlContext = static_cast<const QmlContext &>(c); - QQmlContextData *context = *qmlContext.d()->qml->context; + QQmlContextData *context = *qmlContext.d()->qml()->context; if (!context || index >= (uint)context->idValueCount) return Encode::undefined(); @@ -1544,30 +1267,19 @@ ReturnedValue Runtime::method_getQmlIdObject(ExecutionEngine *engine, const Valu return QObjectWrapper::wrap(engine, context->idValues[index].data()); } -void Runtime::method_setQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value) +void Runtime::method_storeQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value) { const QmlContext &c = static_cast<const QmlContext &>(context); - return QV4::QObjectWrapper::setProperty(engine, c.d()->qml->scopeObject, propertyIndex, value); + return QV4::QObjectWrapper::setProperty(engine, c.d()->qml()->scopeObject, propertyIndex, value); } -void Runtime::method_setQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value) +void Runtime::method_storeQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value) { const QmlContext &c = static_cast<const QmlContext &>(context); - return QV4::QObjectWrapper::setProperty(engine, (*c.d()->qml->context)->contextObject, propertyIndex, value); + return QV4::QObjectWrapper::setProperty(engine, (*c.d()->qml()->context)->contextObject, propertyIndex, value); } -void Runtime::method_setQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, const Value &value) -{ - Scope scope(engine); - QV4::Scoped<QObjectWrapper> wrapper(scope, object); - if (!wrapper) { - engine->throwTypeError(QStringLiteral("Cannot write property of null")); - return; - } - wrapper->setProperty(engine, propertyIndex, value); -} - -ReturnedValue Runtime::method_getQmlImportedScripts(NoThrowEngine *engine) +ReturnedValue Runtime::method_loadQmlImportedScripts(NoThrowEngine *engine) { QQmlContextData *context = engine->callingQmlContext(); if (!context) @@ -1575,44 +1287,20 @@ ReturnedValue Runtime::method_getQmlImportedScripts(NoThrowEngine *engine) return context->importedScripts.value(); } -QV4::ReturnedValue Runtime::method_getQmlSingleton(QV4::NoThrowEngine *engine, int nameIndex) +QV4::ReturnedValue Runtime::method_loadQmlSingleton(QV4::NoThrowEngine *engine, int nameIndex) { Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); return engine->qmlSingletonWrapper(name); } -void Runtime::method_convertThisToObject(ExecutionEngine *engine) -{ - Value *t = &engine->current->callData->thisObject; - if (t->isObject()) - return; - if (t->isNullOrUndefined()) { - *t = engine->globalObject->asReturnedValue(); - } else { - *t = t->toObject(engine)->asReturnedValue(); - } -} - -ReturnedValue Runtime::method_uPlus(const Value &value) -{ - TRACE1(value); - - if (value.isNumber()) - return value.asReturnedValue(); - if (value.integerCompatible()) - return Encode(value.int_32()); - - double n = value.toNumberImpl(); - return Encode(n); -} - ReturnedValue Runtime::method_uMinus(const Value &value) { TRACE1(value); // +0 != -0, so we need to convert to double when negating 0 - if (value.isInteger() && value.integerValue()) + if (value.isInteger() && value.integerValue() && + value.integerValue() != std::numeric_limits<int>::min()) return Encode(-value.integerValue()); else { double n = RuntimeHelpers::toNumber(value); @@ -1620,56 +1308,14 @@ ReturnedValue Runtime::method_uMinus(const Value &value) } } -ReturnedValue Runtime::method_complement(const Value &value) -{ - TRACE1(value); - - int n = value.toInt32(); - return Encode((int)~n); -} - -ReturnedValue Runtime::method_uNot(const Value &value) -{ - TRACE1(value); - - bool b = value.toBoolean(); - return Encode(!b); -} - // binary operators -ReturnedValue Runtime::method_bitOr(const Value &left, const Value &right) -{ - TRACE2(left, right); - - int lval = left.toInt32(); - int rval = right.toInt32(); - return Encode(lval | rval); -} - -ReturnedValue Runtime::method_bitXor(const Value &left, const Value &right) -{ - TRACE2(left, right); - - int lval = left.toInt32(); - int rval = right.toInt32(); - return Encode(lval ^ rval); -} - -ReturnedValue Runtime::method_bitAnd(const Value &left, const Value &right) -{ - TRACE2(left, right); - - int lval = left.toInt32(); - int rval = right.toInt32(); - return Encode(lval & rval); -} #ifndef V4_BOOTSTRAP ReturnedValue Runtime::method_add(ExecutionEngine *engine, const Value &left, const Value &right) { TRACE2(left, right); - if (Q_LIKELY(left.isInteger() && right.isInteger())) + if (Q_LIKELY(left.integerCompatible() && right.integerCompatible())) return add_int32(left.integerValue(), right.integerValue()); if (left.isNumber() && right.isNumber()) return Primitive::fromDouble(left.asDouble() + right.asDouble()).asReturnedValue(); @@ -1682,7 +1328,7 @@ ReturnedValue Runtime::method_sub(const Value &left, const Value &right) { TRACE2(left, right); - if (Q_LIKELY(left.isInteger() && right.isInteger())) + if (Q_LIKELY(left.integerCompatible() && right.integerCompatible())) return sub_int32(left.integerValue(), right.integerValue()); double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl(); @@ -1695,7 +1341,7 @@ ReturnedValue Runtime::method_mul(const Value &left, const Value &right) { TRACE2(left, right); - if (Q_LIKELY(left.isInteger() && right.isInteger())) + if (Q_LIKELY(left.integerCompatible() && right.integerCompatible())) return mul_int32(left.integerValue(), right.integerValue()); double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl(); @@ -1878,11 +1524,6 @@ Bool Runtime::method_compareStrictNotEqual(const Value &left, const Value &right return ! RuntimeHelpers::strictEqual(left, right); } -Bool Runtime::method_toBoolean(const Value &value) -{ - return value.toBoolean(); -} - } // namespace QV4 QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h index 0d787714cf..3a26c23990 100644 --- a/src/qml/jsruntime/qv4runtime_p.h +++ b/src/qml/jsruntime/qv4runtime_p.h @@ -83,8 +83,8 @@ private: }; # define TRACE0() RuntimeCounters::instance->count(Q_FUNC_INFO); -# define TRACE1(x) RuntimeCounters::instance->count(Q_FUNC_INFO, x->type()); -# define TRACE2(x, y) RuntimeCounters::instance->count(Q_FUNC_INFO, x->type(), y->type()); +# define TRACE1(x) RuntimeCounters::instance->count(Q_FUNC_INFO, x.type()); +# define TRACE2(x, y) RuntimeCounters::instance->count(Q_FUNC_INFO, x.type(), y.type()); #else # define TRACE0() # define TRACE1(x) @@ -99,14 +99,14 @@ enum TypeHint { struct Q_QML_PRIVATE_EXPORT RuntimeHelpers { static ReturnedValue objectDefaultValue(const Object *object, int typeHint); - static ReturnedValue toPrimitive(const Value &value, int typeHint); + static ReturnedValue toPrimitive(const Value &value, TypeHint typeHint); static double stringToNumber(const QString &s); static Heap::String *stringFromNumber(ExecutionEngine *engine, double number); static double toNumber(const Value &value); static void numberToString(QString *result, double num, int radix = 10); - static Heap::String *convertToString(ExecutionEngine *engine, const Value &value); + static Heap::String *convertToString(ExecutionEngine *engine, Value value, TypeHint = STRING_HINT); static Heap::Object *convertToObject(ExecutionEngine *engine, const Value &value); static Bool equalHelper(const Value &x, const Value &y); @@ -118,12 +118,11 @@ struct Q_QML_PRIVATE_EXPORT RuntimeHelpers { // type conversion and testing #ifndef V4_BOOTSTRAP -inline ReturnedValue RuntimeHelpers::toPrimitive(const Value &value, int typeHint) +inline ReturnedValue RuntimeHelpers::toPrimitive(const Value &value, TypeHint typeHint) { - const Object *o = value.as<Object>(); - if (!o) + if (!value.isObject()) return value.asReturnedValue(); - return RuntimeHelpers::objectDefaultValue(o, typeHint); + return RuntimeHelpers::objectDefaultValue(&reinterpret_cast<const Object &>(value), typeHint); } #endif diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index 302facba06..ea31dfd08b 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -58,7 +58,6 @@ namespace QV4 { typedef uint Bool; struct NoThrowEngine; - namespace { template <typename T> struct ExceptionCheck { @@ -93,84 +92,64 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> { #define FOR_EACH_RUNTIME_METHOD(F) \ /* call */ \ - F(ReturnedValue, callGlobalLookup, (ExecutionEngine *engine, uint index, CallData *callData)) \ - F(ReturnedValue, callActivationProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)) \ - F(ReturnedValue, callQmlScopeObjectProperty, (ExecutionEngine *engine, int propertyIndex, CallData *callData)) \ - F(ReturnedValue, callQmlContextObjectProperty, (ExecutionEngine *engine, int propertyIndex, CallData *callData)) \ - F(ReturnedValue, callProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)) \ - F(ReturnedValue, callPropertyLookup, (ExecutionEngine *engine, uint index, CallData *callData)) \ - F(ReturnedValue, callElement, (ExecutionEngine *engine, const Value &index, CallData *callData)) \ - F(ReturnedValue, callValue, (ExecutionEngine *engine, const Value &func, CallData *callData)) \ + F(ReturnedValue, callGlobalLookup, (ExecutionEngine *engine, uint index, Value *argv, int argc)) \ + F(ReturnedValue, callName, (ExecutionEngine *engine, int nameIndex, Value *argv, int argc)) \ + F(ReturnedValue, callProperty, (ExecutionEngine *engine, Value *base, int nameIndex, Value *argv, int argc)) \ + F(ReturnedValue, callPropertyLookup, (ExecutionEngine *engine, Value *base, uint index, Value *argv, int argc)) \ + F(ReturnedValue, callElement, (ExecutionEngine *engine, Value *base, const Value &index, Value *argv, int argc)) \ + F(ReturnedValue, callValue, (ExecutionEngine *engine, const Value &func, Value *argv, int argc)) \ + F(ReturnedValue, callPossiblyDirectEval, (ExecutionEngine *engine, Value *argv, int argc)) \ \ /* construct */ \ - F(ReturnedValue, constructGlobalLookup, (ExecutionEngine *engine, uint index, CallData *callData)) \ - F(ReturnedValue, constructActivationProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)) \ - F(ReturnedValue, constructProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)) \ - F(ReturnedValue, constructPropertyLookup, (ExecutionEngine *engine, uint index, CallData *callData)) \ - F(ReturnedValue, constructValue, (ExecutionEngine *engine, const Value &func, CallData *callData)) \ + F(ReturnedValue, construct, (ExecutionEngine *engine, const Value &func, Value *argv, int argc)) \ \ - /* set & get */ \ - F(void, setActivationProperty, (ExecutionEngine *engine, int nameIndex, const Value &value)) \ - F(void, setProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)) \ - F(void, setElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)) \ - F(ReturnedValue, getProperty, (ExecutionEngine *engine, const Value &object, int nameIndex)) \ - F(ReturnedValue, getActivationProperty, (ExecutionEngine *engine, int nameIndex)) \ - F(ReturnedValue, getElement, (ExecutionEngine *engine, const Value &object, const Value &index)) \ + /* load & store */ \ + F(void, storeNameStrict, (ExecutionEngine *engine, int nameIndex, const Value &value)) \ + F(void, storeNameSloppy, (ExecutionEngine *engine, int nameIndex, const Value &value)) \ + F(bool, storeProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)) \ + F(bool, storeElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)) \ + F(ReturnedValue, loadProperty, (ExecutionEngine *engine, const Value &object, int nameIndex)) \ + F(ReturnedValue, loadName, (ExecutionEngine *engine, int nameIndex)) \ + F(ReturnedValue, loadElement, (ExecutionEngine *engine, const Value &object, const Value &index)) \ \ /* typeof */ \ F(ReturnedValue, typeofValue, (ExecutionEngine *engine, const Value &val)) \ F(ReturnedValue, typeofName, (ExecutionEngine *engine, int nameIndex)) \ - F(ReturnedValue, typeofScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex)) \ - F(ReturnedValue, typeofContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex)) \ - F(ReturnedValue, typeofMember, (ExecutionEngine *engine, const Value &base, int nameIndex)) \ - F(ReturnedValue, typeofElement, (ExecutionEngine *engine, const Value &base, const Value &index)) \ \ /* delete */ \ - F(ReturnedValue, deleteElement, (ExecutionEngine *engine, const Value &base, const Value &index)) \ - F(ReturnedValue, deleteMember, (ExecutionEngine *engine, const Value &base, int nameIndex)) \ - F(ReturnedValue, deleteMemberString, (ExecutionEngine *engine, const Value &base, String *name)) \ - F(ReturnedValue, deleteName, (ExecutionEngine *engine, int nameIndex)) \ + F(bool, deleteElement, (ExecutionEngine *engine, const Value &base, const Value &index)) \ + F(bool, deleteMember, (ExecutionEngine *engine, const Value &base, int nameIndex)) \ + F(bool, deleteMemberString, (ExecutionEngine *engine, const Value &base, String *name)) \ + F(bool, deleteName, (ExecutionEngine *engine, int nameIndex)) \ \ /* exceptions & scopes */ \ F(void, throwException, (ExecutionEngine *engine, const Value &value)) \ - F(ReturnedValue, unwindException, (ExecutionEngine *engine)) \ - F(void, pushWithScope, (const Value &o, NoThrowEngine *engine)) \ - F(void, pushCatchScope, (NoThrowEngine *engine, int exceptionVarNameIndex)) \ - F(void, popScope, (NoThrowEngine *engine)) \ + F(ReturnedValue, createWithContext, (ExecutionContext *parent, const Value &o)) \ + F(ReturnedValue, createCatchContext, (ExecutionContext *parent, int exceptionVarNameIndex)) \ \ /* closures */ \ F(ReturnedValue, closure, (ExecutionEngine *engine, int functionId)) \ \ /* function header */ \ F(void, declareVar, (ExecutionEngine *engine, bool deletable, int nameIndex)) \ - F(ReturnedValue, setupArgumentsObject, (ExecutionEngine *engine)) \ - F(void, convertThisToObject, (ExecutionEngine *engine)) \ + F(ReturnedValue, createMappedArgumentsObject, (ExecutionEngine *engine)) \ + F(ReturnedValue, createUnmappedArgumentsObject, (ExecutionEngine *engine)) \ \ /* literals */ \ F(ReturnedValue, arrayLiteral, (ExecutionEngine *engine, Value *values, uint length)) \ F(ReturnedValue, objectLiteral, (ExecutionEngine *engine, const Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags)) \ - F(ReturnedValue, regexpLiteral, (ExecutionEngine *engine, int id)) \ \ /* foreach */ \ F(ReturnedValue, foreachIterator, (ExecutionEngine *engine, const Value &in)) \ F(ReturnedValue, foreachNextPropertyName, (const Value &foreach_iterator)) \ \ /* unary operators */ \ - F(ReturnedValue, uPlus, (const Value &value)) \ F(ReturnedValue, uMinus, (const Value &value)) \ - F(ReturnedValue, uNot, (const Value &value)) \ - F(ReturnedValue, complement, (const Value &value)) \ - F(ReturnedValue, increment, (const Value &value)) \ - F(ReturnedValue, decrement, (const Value &value)) \ \ /* binary operators */ \ F(ReturnedValue, instanceof, (ExecutionEngine *engine, const Value &left, const Value &right)) \ F(ReturnedValue, in, (ExecutionEngine *engine, const Value &left, const Value &right)) \ F(ReturnedValue, add, (ExecutionEngine *engine, const Value &left, const Value &right)) \ - F(ReturnedValue, addString, (ExecutionEngine *engine, const Value &left, const Value &right)) \ - F(ReturnedValue, bitOr, (const Value &left, const Value &right)) \ - F(ReturnedValue, bitXor, (const Value &left, const Value &right)) \ - F(ReturnedValue, bitAnd, (const Value &left, const Value &right)) \ F(ReturnedValue, sub, (const Value &left, const Value &right)) \ F(ReturnedValue, mul, (const Value &left, const Value &right)) \ F(ReturnedValue, div, (const Value &left, const Value &right)) \ @@ -200,28 +179,18 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> { F(Bool, compareInstanceof, (ExecutionEngine *engine, const Value &left, const Value &right)) \ F(Bool, compareIn, (ExecutionEngine *engine, const Value &left, const Value &right)) \ \ - /* conversions */ \ - F(Bool, toBoolean, (const Value &value)) \ - F(ReturnedValue, toDouble, (const Value &value)) \ - F(int, toInt, (const Value &value)) \ - F(int, doubleToInt, (const double &d)) \ - F(unsigned, toUInt, (const Value &value)) \ - F(unsigned, doubleToUInt, (const double &d)) \ + F(ReturnedValue, regexpLiteral, (ExecutionEngine *engine, int id)) \ \ /* qml */ \ - F(ReturnedValue, getQmlContext, (NoThrowEngine *engine)) \ - F(ReturnedValue, getQmlImportedScripts, (NoThrowEngine *engine)) \ - F(ReturnedValue, getQmlSingleton, (NoThrowEngine *engine, int nameIndex)) \ - F(ReturnedValue, getQmlAttachedProperty, (ExecutionEngine *engine, int attachedPropertiesId, int propertyIndex)) \ - F(ReturnedValue, getQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)) \ - F(ReturnedValue, getQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)) \ - F(ReturnedValue, getQmlQObjectProperty, (ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired)) \ - F(ReturnedValue, getQmlSingletonQObjectProperty, (ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired)) \ - F(ReturnedValue, getQmlIdObject, (ExecutionEngine *engine, const Value &context, uint index)) \ - \ - F(void, setQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)) \ - F(void, setQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)) \ - F(void, setQmlQObjectProperty, (ExecutionEngine *engine, const Value &object, int propertyIndex, const Value &value)) + F(ReturnedValue, loadQmlContext, (NoThrowEngine *engine)) \ + F(ReturnedValue, loadQmlImportedScripts, (NoThrowEngine *engine)) \ + F(ReturnedValue, loadQmlSingleton, (NoThrowEngine *engine, int nameIndex)) \ + F(ReturnedValue, loadQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)) \ + F(ReturnedValue, loadQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)) \ + F(ReturnedValue, loadQmlIdObject, (ExecutionEngine *engine, const Value &context, uint index)) \ + \ + F(void, storeQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)) \ + F(void, storeQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)) \ struct Q_QML_PRIVATE_EXPORT Runtime { Runtime(); diff --git a/src/qml/jsruntime/qv4runtimecodegen.cpp b/src/qml/jsruntime/qv4runtimecodegen.cpp new file mode 100644 index 0000000000..9c115099b5 --- /dev/null +++ b/src/qml/jsruntime/qv4runtimecodegen.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** 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 "qv4runtimecodegen_p.h" +#include "qv4compilerscanfunctions_p.h" + +using namespace QV4; + +void RuntimeCodegen::generateFromFunctionExpression(const QString &fileName, + const QString &sourceCode, + AST::FunctionExpression *ast, + Compiler::Module *module) +{ + _module = module; + _module->fileName = fileName; + _context = 0; + + Compiler::ScanFunctions scan(this, sourceCode, Compiler::GlobalCode); + // fake a global environment + scan.enterEnvironment(0, Compiler::FunctionCode); + scan(ast); + scan.leaveEnvironment(); + + int index = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); + _module->rootContext = _module->functions.at(index); +} + +void RuntimeCodegen::throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) +{ + if (hasError) + return; + hasError = true; + engine->throwSyntaxError(detail, _module->fileName, loc.startLine, loc.startColumn); +} + +void RuntimeCodegen::throwReferenceError(const AST::SourceLocation &loc, const QString &detail) +{ + if (hasError) + return; + hasError = true; + engine->throwReferenceError(detail, _module->fileName, loc.startLine, loc.startColumn); +} + diff --git a/src/qml/jit/qv4unop_p.h b/src/qml/jsruntime/qv4runtimecodegen_p.h index fb68f80eec..be66dc57ca 100644 --- a/src/qml/jit/qv4unop_p.h +++ b/src/qml/jsruntime/qv4runtimecodegen_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** 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. @@ -36,8 +36,8 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#ifndef QV4UNOP_P_H -#define QV4UNOP_P_H +#ifndef QV4RUNTIMECODEGEN_P_H +#define QV4RUNTIMECODEGEN_P_H // // W A R N I N G @@ -50,43 +50,33 @@ // We mean it. // -#include <qv4jsir_p.h> -#include <qv4isel_masm_p.h> +#include <private/qv4codegen_p.h> QT_BEGIN_NAMESPACE -#if ENABLE(ASSEMBLER) - namespace QV4 { -namespace JIT { -template <typename JITAssembler> -struct Unop { - Unop(JITAssembler *assembler, IR::AluOp operation) - : _as(assembler) - , op(operation) +class RuntimeCodegen : public Compiler::Codegen +{ +public: + RuntimeCodegen(ExecutionEngine *engine, Compiler::JSUnitGenerator *jsUnitGenerator, bool strict) + : Codegen(jsUnitGenerator, strict) + , engine(engine) {} - using RelationalCondition = typename JITAssembler::RelationalCondition; - using PointerToValue = typename JITAssembler::PointerToValue; - using RuntimeCall = typename JITAssembler::RuntimeCall; - using TrustedImm32 = typename JITAssembler::TrustedImm32; - - void generate(IR::Expr *source, IR::Expr *target); - - void generateUMinus(IR::Expr *source, IR::Expr *target); - void generateNot(IR::Expr *source, IR::Expr *target); - void generateCompl(IR::Expr *source, IR::Expr *target); + void generateFromFunctionExpression(const QString &fileName, + const QString &sourceCode, + AST::FunctionExpression *ast, + Compiler::Module *module); - JITAssembler *_as; - IR::AluOp op; + void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) override; + void throwReferenceError(const AST::SourceLocation &loc, const QString &detail) override; +private: + ExecutionEngine *engine; }; } -} - -#endif QT_END_NAMESPACE -#endif +#endif // QV4CODEGEN_P_H diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index 04a0c74133..afb5c21d36 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -52,7 +52,6 @@ #include "qv4engine_p.h" #include "qv4value_p.h" -#include "qv4persistent_p.h" #include "qv4property_p.h" #ifdef V4_USE_VALGRIND @@ -71,56 +70,45 @@ struct ScopedValue; #define CHECK_EXCEPTION() \ do { \ if (scope.hasException()) { \ - scope.result = QV4::Encode::undefined(); \ - return; \ + return QV4::Encode::undefined(); \ } \ } while (false) #define RETURN_UNDEFINED() \ - do { \ - scope.result = QV4::Encode::undefined(); \ - return; \ - } while (false) + return QV4::Encode::undefined() #define RETURN_RESULT(r) \ - do { \ - scope.result = r; \ - return; \ - } while (false) + return QV4::Encode(r) #define THROW_TYPE_ERROR() \ - do { \ - scope.result = scope.engine->throwTypeError(); \ - return; \ - } while (false) + return scope.engine->throwTypeError() #define THROW_GENERIC_ERROR(str) \ - do { \ - scope.result = scope.engine->throwError(QString::fromUtf8(str)); \ - return; \ - } while (false) + return scope.engine->throwError(QString::fromUtf8(str)) struct Scope { - inline Scope(ExecutionContext *ctx) + explicit Scope(ExecutionContext *ctx) : engine(ctx->engine()) , mark(engine->jsStackTop) - , result(*engine->jsAlloca(1)) { - result = Encode::undefined(); } explicit Scope(ExecutionEngine *e) : engine(e) , mark(engine->jsStackTop) - , result(*engine->jsAlloca(1)) { - result = Encode::undefined(); + } + + explicit Scope(const Managed *m) + : engine(m->engine()) + , mark(engine->jsStackTop) + { } ~Scope() { #ifndef QT_NO_DEBUG Q_ASSERT(engine->jsStackTop >= mark); - Q_ASSERT(engine->currentContext < mark); +// Q_ASSERT(engine->currentContext < mark); memset(mark, 0, (engine->jsStackTop - mark)*sizeof(Value)); #endif #ifdef V4_USE_VALGRIND @@ -139,7 +127,6 @@ struct Scope { ExecutionEngine *engine; Value *mark; - Value &result; private: Q_DISABLE_COPY(Scope) @@ -363,27 +350,6 @@ struct Scoped Value *ptr; }; -struct ScopedCallData { - ScopedCallData(const Scope &scope, int argc = 0) - { - int size = int(offsetof(QV4::CallData, args)/sizeof(QV4::Value)) + qMax(argc , int(QV4::Global::ReservedArgumentCount)); - ptr = reinterpret_cast<CallData *>(scope.alloc(size)); - ptr->tag = quint32(QV4::Value::ValueTypeInternal::Integer); - ptr->argc = argc; - } - - CallData *operator->() { - return ptr; - } - - operator CallData *() const { - return ptr; - } - - CallData *ptr; -}; - - inline Value &Value::operator =(const ScopedValue &v) { _val = v.ptr->rawValue(); @@ -411,25 +377,6 @@ struct ScopedProperty Property *property; }; -struct ExecutionContextSaver -{ - Scope scope; // this makes sure that a reference to context on the JS stack goes out of scope as soon as the context is not used anymore. - ExecutionContext *savedContext; - - ExecutionContextSaver(const Scope &scope) - : scope(scope.engine) - { - savedContext = scope.engine->currentContext; - } - ~ExecutionContextSaver() - { - Q_ASSERT(scope.engine->jsStackTop > scope.engine->currentContext); - scope.engine->currentContext = savedContext; - scope.engine->current = savedContext->d(); - } -}; - - } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 62145f36cc..901f2574da 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -45,6 +45,7 @@ #include "qv4debugging_p.h" #include "qv4profiling_p.h" #include "qv4scopedvalue_p.h" +#include "qv4jscall_p.h" #include <private/qqmljsengine_p.h> #include <private/qqmljslexer_p.h> @@ -52,8 +53,7 @@ #include <private/qqmljsast_p.h> #include <private/qqmlengine_p.h> #include <private/qv4profiling_p.h> -#include <qv4jsir_p.h> -#include <qv4codegen_p.h> +#include <qv4runtimecodegen_p.h> #include <QtCore/QDebug> #include <QtCore/QString> @@ -61,7 +61,7 @@ using namespace QV4; Script::Script(ExecutionEngine *v4, QmlContext *qml, CompiledData::CompilationUnit *compilationUnit) - : line(0), column(0), scope(v4->rootContext()), strictMode(false), inheritContext(true), parsed(false) + : line(1), column(0), context(v4->rootContext()), strictMode(false), inheritContext(true), parsed(false) , compilationUnit(compilationUnit), vmFunction(0), parseAsBinding(true) { if (qml) @@ -81,16 +81,16 @@ void Script::parse() if (parsed) return; - using namespace QQmlJS; + using namespace QV4::Compiler; parsed = true; - ExecutionEngine *v4 = scope->engine(); + ExecutionEngine *v4 = context->engine(); Scope valueScope(v4); - IR::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); @@ -98,7 +98,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; @@ -117,25 +117,15 @@ void Script::parse() return; } - QStringList inheritedLocals; - if (inheritContext) { - Scoped<CallContext> ctx(valueScope, scope); - if (ctx) { - for (Identifier * const *i = ctx->variables(), * const *ei = i + ctx->variableCount(); i < ei; ++i) - inheritedLocals.append(*i ? (*i)->string : QString()); - } - } - - RuntimeCodegen cg(v4, strictMode); - cg.generateFromProgram(sourceFile, sourceCode, program, &module, QQmlJS::Codegen::EvalCode, inheritedLocals); + QV4::Compiler::JSUnitGenerator jsGenerator(&module); + RuntimeCodegen cg(v4, &jsGenerator, strictMode); + if (inheritContext) + cg.setUseFastLookups(false); + cg.generateFromProgram(sourceFile, sourceCode, program, &module, compilationMode); if (v4->hasException) return; - QV4::Compiler::JSUnitGenerator jsGenerator(&module); - QScopedPointer<EvalInstructionSelection> isel(v4->iselFactory->create(QQmlEnginePrivate::get(v4), v4->executableAllocator, &module, &jsGenerator)); - if (inheritContext) - isel->setUseFastLookups(false); - compilationUnit = isel->compile(); + compilationUnit = cg.generateCompilationUnit(); vmFunction = compilationUnit->linkToEngine(v4); } @@ -153,29 +143,16 @@ ReturnedValue Script::run() if (!vmFunction) return Encode::undefined(); - QV4::ExecutionEngine *engine = scope->engine(); + QV4::ExecutionEngine *engine = context->engine(); QV4::Scope valueScope(engine); if (qmlContext.isUndefined()) { TemporaryAssignment<Function*> savedGlobalCode(engine->globalCode, vmFunction); - ExecutionContextSaver ctxSaver(valueScope); - ContextStateSaver stateSaver(valueScope, scope); - scope->d()->strictMode = vmFunction->isStrict(); - scope->d()->lookups = vmFunction->compilationUnit->runtimeLookups; - scope->d()->constantTable = vmFunction->compilationUnit->constants; - scope->d()->compilationUnit = vmFunction->compilationUnit; - - return Q_V4_PROFILE(engine, vmFunction); + return vmFunction->call(engine->globalObject, 0, 0, context); } else { Scoped<QmlContext> qml(valueScope, qmlContext.value()); - ScopedCallData callData(valueScope); - callData->thisObject = Primitive::undefinedValue(); - if (vmFunction->canUseSimpleFunction()) - qml->simpleCall(valueScope, callData, vmFunction); - else - qml->call(valueScope, callData, vmFunction); - return valueScope.result.asReturnedValue(); + return vmFunction->call(0, 0, 0, qml); } } @@ -186,24 +163,26 @@ Function *Script::function() return vmFunction; } -QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(IR::Module *module, Compiler::JSUnitGenerator *unitGenerator, ExecutionEngine *engine, const QUrl &url, const QString &source, QList<QQmlError> *reportedErrors, QQmlJS::Directives *directivesCollector) +QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compiler::Module *module, Compiler::JSUnitGenerator *unitGenerator, + const QUrl &url, const QString &source, QList<QQmlError> *reportedErrors, + Directives *directivesCollector) { - using namespace QQmlJS; + using namespace QV4::Compiler; using namespace QQmlJS::AST; - QQmlJS::Engine ee; + Engine ee; if (directivesCollector) ee.setDirectives(directivesCollector); - QQmlJS::Lexer lexer(&ee); + Lexer lexer(&ee); lexer.setCode(source, /*line*/1, /*qml mode*/false); - QQmlJS::Parser parser(&ee); + Parser parser(&ee); parser.parseProgram(); QList<QQmlError> errors; const auto diagnosticMessages = parser.diagnosticMessages(); - for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) { + for (const DiagnosticMessage &m : diagnosticMessages) { if (m.isWarning()) { qWarning("%s:%d : %s", qPrintable(url.toString()), m.loc.startLine, qPrintable(m.message)); continue; @@ -230,8 +209,9 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(IR::Module return 0; } - QQmlJS::Codegen cg(/*strict mode*/false); - cg.generateFromProgram(url.toString(), source, program, module, QQmlJS::Codegen::EvalCode); + Codegen cg(unitGenerator, /*strict mode*/false); + cg.setUseFastLookups(false); + cg.generateFromProgram(url.toString(), source, program, module, GlobalCode); errors = cg.qmlErrors(); if (!errors.isEmpty()) { if (reportedErrors) @@ -239,9 +219,7 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(IR::Module return 0; } - QScopedPointer<EvalInstructionSelection> isel(engine->iselFactory->create(QQmlEnginePrivate::get(engine), engine->executableAllocator, module, unitGenerator)); - isel->setUseFastLookups(false); - return isel->compile(/*generate unit data*/false); + return cg.generateCompilationUnit(/*generate unit data*/false); } QV4::ReturnedValue Script::evaluate(ExecutionEngine *engine, const QString &script, QmlContext *qmlContext) diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index 4ebe2dd609..158d21c69d 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -54,6 +54,7 @@ #include "qv4engine_p.h" #include "qv4functionobject_p.h" #include "qv4qmlcontext_p.h" +#include "private/qv4compilercontext_p.h" #include <QQmlError> @@ -61,60 +62,16 @@ QT_BEGIN_NAMESPACE class QQmlContextData; -namespace QQmlJS { -class Directives; -} - namespace QV4 { -struct ContextStateSaver { - Value *savedContext; - bool strictMode; - Lookup *lookups; - const QV4::Value *constantTable; - CompiledData::CompilationUnitBase *compilationUnit; - int lineNumber; - - ContextStateSaver(const Scope &scope, ExecutionContext *context) - : savedContext(scope.alloc(1)) - , strictMode(context->d()->strictMode) - , lookups(context->d()->lookups) - , constantTable(context->d()->constantTable) - , compilationUnit(context->d()->compilationUnit) - , lineNumber(context->d()->lineNumber) - { - savedContext->setM(context->d()); - } - ContextStateSaver(const Scope &scope, Heap::ExecutionContext *context) - : savedContext(scope.alloc(1)) - , strictMode(context->strictMode) - , lookups(context->lookups) - , constantTable(context->constantTable) - , compilationUnit(context->compilationUnit) - , lineNumber(context->lineNumber) - { - savedContext->setM(context); - } - - ~ContextStateSaver() - { - Heap::ExecutionContext *ctx = static_cast<Heap::ExecutionContext *>(savedContext->m()); - ctx->strictMode = strictMode; - ctx->lookups = lookups; - ctx->constantTable = constantTable; - ctx->compilationUnit = compilationUnit; - ctx->lineNumber = lineNumber; - } -}; - struct Q_QML_EXPORT Script { - Script(ExecutionContext *scope, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) + Script(ExecutionContext *scope, QV4::Compiler::CompilationMode mode, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) : sourceFile(source), line(line), column(column), sourceCode(sourceCode) - , scope(scope), strictMode(false), inheritContext(false), parsed(false) + , context(scope), strictMode(false), inheritContext(false), parsed(false), compilationMode(mode) , vmFunction(0), parseAsBinding(false) {} Script(ExecutionEngine *engine, QmlContext *qml, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) : sourceFile(source), line(line), column(column), sourceCode(sourceCode) - , scope(engine->rootContext()), strictMode(false), inheritContext(true), parsed(false) + , context(engine->rootContext()), strictMode(false), inheritContext(true), parsed(false) , vmFunction(0), parseAsBinding(true) { if (qml) qmlContext.set(engine, *qml); @@ -125,10 +82,11 @@ struct Q_QML_EXPORT Script { int line; int column; QString sourceCode; - ExecutionContext *scope; + ExecutionContext *context; bool strictMode; bool inheritContext; bool parsed; + QV4::Compiler::CompilationMode compilationMode = QV4::Compiler::EvalCode; QV4::PersistentValue qmlContext; QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit; Function *vmFunction; @@ -139,7 +97,7 @@ struct Q_QML_EXPORT Script { Function *function(); - static QQmlRefPointer<CompiledData::CompilationUnit> precompile(IR::Module *module, Compiler::JSUnitGenerator *unitGenerator, ExecutionEngine *engine, const QUrl &url, const QString &source, + static QQmlRefPointer<CompiledData::CompilationUnit> precompile(QV4::Compiler::Module *module, Compiler::JSUnitGenerator *unitGenerator, const QUrl &url, const QString &source, QList<QQmlError> *reportedErrors = 0, QQmlJS::Directives *directivesCollector = 0); static ReturnedValue evaluate(ExecutionEngine *engine, const QString &script, QmlContext *qmlContext); diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index 8afc672aa2..78cd7529d8 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -45,6 +45,7 @@ #include <private/qv4arrayobject_p.h> #include <private/qqmlengine_p.h> #include <private/qv4scopedvalue_p.h> +#include <private/qv4jscall_p.h> #include "qv4runtime_p.h" #include "qv4objectiterator_p.h" #include <private/qqmlvaluetypewrapper_p.h> @@ -66,10 +67,10 @@ static void generateWarning(QV4::ExecutionEngine *v4, const QString& description QQmlError retn; retn.setDescription(description); - QV4::StackFrame frame = v4->currentStackFrame(); + QV4::CppStackFrame *stackFrame = v4->currentStackFrame; - retn.setLine(frame.line); - retn.setUrl(QUrl(frame.source)); + retn.setLine(stackFrame->lineNumber()); + retn.setUrl(QUrl(stackFrame->source())); QQmlEnginePrivate::warning(engine, retn); } @@ -416,13 +417,15 @@ public: bool operator()(typename Container::value_type lhs, typename Container::value_type rhs) { QV4::Scope scope(m_v4); - ScopedObject compare(scope, m_compareFn); - ScopedCallData callData(scope, 2); - callData->args[0] = convertElementToValue(m_v4, lhs); - callData->args[1] = convertElementToValue(m_v4, rhs); - callData->thisObject = m_v4->globalObject; - compare->call(scope, callData); - return scope.result.toNumber() < 0; + ScopedFunctionObject compare(scope, m_compareFn); + if (!compare) + return m_v4->throwTypeError(); + JSCallData jsCallData(scope, 2); + jsCallData->args[0] = convertElementToValue(m_v4, lhs); + jsCallData->args[1] = convertElementToValue(m_v4, rhs); + *jsCallData->thisObject = m_v4->globalObject; + QV4::ScopedValue result(scope, compare->call(jsCallData)); + return result->toNumber() < 0; } private: @@ -438,7 +441,7 @@ public: loadReference(); } - if (callData->argc == 1 && callData->args[0].as<FunctionObject>()) { + if (callData->argc() == 1 && callData->args[0].as<FunctionObject>()) { CompareFunctor cf(scope.engine, callData->args[0]); std::sort(d()->container->begin(), d()->container->end(), cf); } else { @@ -450,8 +453,9 @@ public: storeReference(); } - static void method_get_length(const BuiltinFunction *, Scope &scope, CallData *callData) + static QV4::ReturnedValue method_get_length(const BuiltinFunction *b, CallData *callData) { + QV4::Scope scope(b); QV4::Scoped<QQmlSequence<Container> > This(scope, callData->thisObject.as<QQmlSequence<Container> >()); if (!This) THROW_TYPE_ERROR(); @@ -464,8 +468,9 @@ public: RETURN_RESULT(Encode(qint32(This->d()->container->size()))); } - static void method_set_length(const BuiltinFunction *, Scope &scope, CallData *callData) + static QV4::ReturnedValue method_set_length(const BuiltinFunction *b, CallData *callData) { + QV4::Scope scope(b); QV4::Scoped<QQmlSequence<Container> > This(scope, callData->thisObject.as<QQmlSequence<Container> >()); if (!This) THROW_TYPE_ERROR(); @@ -647,14 +652,20 @@ void SequencePrototype::init() } #undef REGISTER_QML_SEQUENCE_METATYPE -void SequencePrototype::method_sort(const BuiltinFunction *b, Scope &scope, CallData *callData) +ReturnedValue SequencePrototype::method_valueOf(const BuiltinFunction *f, CallData *callData) { + return Encode(callData->thisObject.toString(f->engine())); +} + +ReturnedValue SequencePrototype::method_sort(const BuiltinFunction *b, CallData *callData) +{ + Scope scope(b); QV4::ScopedObject o(scope, callData->thisObject); if (!o || !o->isListType()) THROW_TYPE_ERROR(); - if (callData->argc >= 2) - RETURN_RESULT(o); + if (callData->argc() >= 2) + return o.asReturnedValue(); #define CALL_SORT(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue) \ if (QQml##SequenceElementTypeName##List *s = o->as<QQml##SequenceElementTypeName##List>()) { \ @@ -665,7 +676,7 @@ void SequencePrototype::method_sort(const BuiltinFunction *b, Scope &scope, Call #undef CALL_SORT {} - RETURN_RESULT(o); + return o.asReturnedValue(); } #define IS_SEQUENCE(unused1, unused2, SequenceType, unused3) \ diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h index 2b8d1ea716..169a48c2f9 100644 --- a/src/qml/jsruntime/qv4sequenceobject_p.h +++ b/src/qml/jsruntime/qv4sequenceobject_p.h @@ -68,12 +68,8 @@ struct SequencePrototype : public QV4::Object V4_PROTOTYPE(arrayPrototype) void init(); - static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData) - { - scope.result = callData->thisObject.toString(scope.engine); - } - - static void method_sort(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_valueOf(const BuiltinFunction *f, CallData *callData); + static ReturnedValue method_sort(const BuiltinFunction *, CallData *callData); static bool isSequenceType(int sequenceTypeId); static ReturnedValue newSequence(QV4::ExecutionEngine *engine, int sequenceTypeId, QObject *object, int propertyIndex, bool *succeeded); diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp index c0183a46a7..b38b01c9f7 100644 --- a/src/qml/jsruntime/qv4string.cpp +++ b/src/qml/jsruntime/qv4string.cpp @@ -52,17 +52,18 @@ using namespace QV4; #ifndef V4_BOOTSTRAP -DEFINE_MANAGED_VTABLE(String); - -void String::markObjects(Heap::Base *that, MarkStack *markStack) +void Heap::String::markObjects(Heap::Base *that, MarkStack *markStack) { - String::Data *s = static_cast<String::Data *>(that); + String *s = static_cast<String *>(that); if (s->largestSubLength) { s->left->mark(markStack); s->right->mark(markStack); } } +DEFINE_MANAGED_VTABLE(String); + + bool String::isEqualTo(Managed *t, Managed *o) { if (t == o) diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index 2f34dd6139..85345aca4d 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -65,6 +65,7 @@ struct Identifier; namespace Heap { struct Q_QML_PRIVATE_EXPORT String : Base { + static void markObjects(Heap::Base *that, MarkStack *markStack); enum StringType { StringType_Unknown, StringType_Regular, @@ -181,7 +182,8 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed { makeIdentifierImpl(); } - void makeIdentifierImpl() const; + // slow path + Q_NEVER_INLINE void makeIdentifierImpl() const; static uint createHashValue(const QChar *ch, int length, uint *subtype) { @@ -205,7 +207,6 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed { Identifier *identifier() const { return d()->identifier; } protected: - static void markObjects(Heap::Base *that, MarkStack *markStack); static bool isEqualTo(Managed *that, Managed *o); static uint getLength(const Managed *m); #endif diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index 7c65c97d73..efab51ec53 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -45,17 +45,11 @@ #include <private/qv4mm_p.h> #include "qv4scopedvalue_p.h" #include "qv4alloca_p.h" +#include "qv4jscall_p.h" #include <QtCore/QDateTime> #include <QtCore/QDebug> #include <QtCore/QStringList> -#include <private/qqmljsengine_p.h> -#include <private/qqmljslexer_p.h> -#include <private/qqmljsparser_p.h> -#include <private/qqmljsast_p.h> -#include <qv4jsir_p.h> -#include <qv4codegen_p.h> - #include <cassert> #ifndef Q_OS_WIN @@ -108,11 +102,8 @@ bool StringObject::deleteIndexedProperty(Managed *m, uint index) Scoped<StringObject> o(scope, m->as<StringObject>()); Q_ASSERT(!!o); - if (index < static_cast<uint>(o->d()->string->toQString().length())) { - if (v4->current->strictMode) - v4->throwTypeError(); + if (index < static_cast<uint>(o->d()->string->toQString().length())) return false; - } return true; } @@ -152,24 +143,25 @@ void Heap::StringCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("String")); } -void StringCtor::construct(const Managed *m, Scope &scope, CallData *callData) +ReturnedValue StringCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - ExecutionEngine *v4 = static_cast<const Object *>(m)->engine(); + ExecutionEngine *v4 = static_cast<const Object *>(f)->engine(); + Scope scope(v4); ScopedString value(scope); - if (callData->argc) - value = callData->args[0].toString(v4); + if (argc) + value = argv[0].toString(v4); else value = v4->newString(); - scope.result = Encode(v4->newStringObject(value)); + return Encode(v4->newStringObject(value)); } -void StringCtor::call(const Managed *, Scope &scope, CallData *callData) +ReturnedValue StringCtor::call(const FunctionObject *m, const Value *, const Value *argv, int argc) { - ExecutionEngine *v4 = scope.engine; - if (callData->argc) - scope.result = callData->args[0].toString(v4); + ExecutionEngine *v4 = m->engine(); + if (argc) + return argv[0].toString(v4)->asReturnedValue(); else - scope.result = v4->newString(); + return v4->newString()->asReturnedValue(); } void StringPrototype::init(ExecutionEngine *engine, Object *ctor) @@ -208,140 +200,154 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("trim"), method_trim); } -static QString getThisString(Scope &scope, CallData *callData) +static QString getThisString(ExecutionEngine *v4, const Value *thisObject) { - ScopedValue t(scope, callData->thisObject); - if (String *s = t->stringValue()) + if (String *s = thisObject->stringValue()) return s->toQString(); - if (StringObject *thisString = t->as<StringObject>()) + if (const StringObject *thisString = thisObject->as<StringObject>()) return thisString->d()->string->toQString(); - if (t->isUndefined() || t->isNull()) { - scope.engine->throwTypeError(); + if (thisObject->isUndefined() || thisObject->isNull()) { + v4->throwTypeError(); return QString(); } - return t->toQString(); + return thisObject->toQString(); } -void StringPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) { - if (callData->thisObject.isString()) - RETURN_RESULT(callData->thisObject); + if (thisObject->isString()) + return thisObject->asReturnedValue(); - StringObject *o = callData->thisObject.as<StringObject>(); + ExecutionEngine *v4 = b->engine(); + const StringObject *o = thisObject->as<StringObject>(); if (!o) - THROW_TYPE_ERROR(); - scope.result = o->d()->string; + return v4->throwTypeError(); + return o->d()->string->asReturnedValue(); } -void StringPrototype::method_charAt(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_charAt(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - const QString str = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString str = getThisString(v4, thisObject); + if (v4->hasException) + return QV4::Encode::undefined(); int pos = 0; - if (callData->argc > 0) - pos = (int) callData->args[0].toInteger(); + if (argc > 0) + pos = (int) argv[0].toInteger(); QString result; if (pos >= 0 && pos < str.length()) result += str.at(pos); - scope.result = scope.engine->newString(result); + return Encode(v4->newString(result)); } -void StringPrototype::method_charCodeAt(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_charCodeAt(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - const QString str = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString str = getThisString(v4, thisObject); + if (v4->hasException) + return QV4::Encode::undefined(); int pos = 0; - if (callData->argc > 0) - pos = (int) callData->args[0].toInteger(); + if (argc > 0) + pos = (int) argv[0].toInteger(); if (pos >= 0 && pos < str.length()) RETURN_RESULT(Encode(str.at(pos).unicode())); - scope.result = Encode(qt_qnan()); + return Encode(qt_qnan()); } -void StringPrototype::method_concat(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_concat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + QString value = getThisString(v4, thisObject); + if (v4->hasException) + return QV4::Encode::undefined(); + Scope scope(v4); ScopedString s(scope); - for (int i = 0; i < callData->argc; ++i) { - s = callData->args[i].toString(scope.engine); - CHECK_EXCEPTION(); + for (int i = 0; i < argc; ++i) { + s = argv[i].toString(scope.engine); + if (v4->hasException) + return QV4::Encode::undefined(); Q_ASSERT(s->isString()); value += s->toQString(); } - scope.result = scope.engine->newString(value); + return Encode(v4->newString(value)); } -void StringPrototype::method_endsWith(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_endsWith(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString value = getThisString(v4, thisObject); + if (v4->hasException) + return QV4::Encode::undefined(); QString searchString; - if (callData->argc) { - if (callData->args[0].as<RegExpObject>()) - THROW_TYPE_ERROR(); - searchString = callData->args[0].toQString(); + if (argc) { + if (argv[0].as<RegExpObject>()) + return v4->throwTypeError(); + searchString = argv[0].toQString(); } int pos = value.length(); - if (callData->argc > 1) - pos = (int) callData->args[1].toInteger(); + if (argc > 1) + pos = (int) argv[1].toInteger(); if (pos == value.length()) RETURN_RESULT(Encode(value.endsWith(searchString))); QStringRef stringToSearch = value.leftRef(pos); - scope.result = Encode(stringToSearch.endsWith(searchString)); + return Encode(stringToSearch.endsWith(searchString)); } -void StringPrototype::method_indexOf(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_indexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString value = getThisString(v4, thisObject); + if (v4->hasException) + return QV4::Encode::undefined(); QString searchString; - if (callData->argc) - searchString = callData->args[0].toQString(); + if (argc) + searchString = argv[0].toQString(); int pos = 0; - if (callData->argc > 1) - pos = (int) callData->args[1].toInteger(); + if (argc > 1) + pos = (int) argv[1].toInteger(); int index = -1; if (! value.isEmpty()) index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length())); - scope.result = Encode(index); + return Encode(index); } -void StringPrototype::method_includes(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_includes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString value = getThisString(v4, thisObject); + if (v4->hasException) + return QV4::Encode::undefined(); QString searchString; - if (callData->argc) { - if (callData->args[0].as<RegExpObject>()) - THROW_TYPE_ERROR(); - searchString = callData->args[0].toQString(); + if (argc) { + if (argv[0].as<RegExpObject>()) + return v4->throwTypeError(); + searchString = argv[0].toQString(); } int pos = 0; - if (callData->argc > 1) { - ScopedValue posArg(scope, callData->argument(1)); - pos = (int) posArg->toInteger(); - if (!posArg->isInteger() && posArg->isNumber() && qIsInf(posArg->toNumber())) + if (argc > 1) { + const Value &posArg = argv[1]; + pos = (int) posArg.toInteger(); + if (!posArg.isInteger() && posArg.isNumber() && qIsInf(posArg.toNumber())) pos = value.length(); } @@ -349,20 +355,21 @@ void StringPrototype::method_includes(const BuiltinFunction *, Scope &scope, Cal RETURN_RESULT(Encode(value.contains(searchString))); QStringRef stringToSearch = value.midRef(pos); - scope.result = Encode(stringToSearch.contains(searchString)); + return Encode(stringToSearch.contains(searchString)); } -void StringPrototype::method_lastIndexOf(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_lastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - const QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString value = getThisString(v4, thisObject); + if (v4->hasException) + return QV4::Encode::undefined(); QString searchString; - if (callData->argc) - searchString = callData->args[0].toQString(); + if (argc) + searchString = argv[0].toQString(); - ScopedValue posArg(scope, callData->argument(1)); - double position = RuntimeHelpers::toNumber(posArg); + double position = argc > 1 ? RuntimeHelpers::toNumber(argv[1]) : +qInf(); if (std::isnan(position)) position = +qInf(); else @@ -374,97 +381,84 @@ void StringPrototype::method_lastIndexOf(const BuiltinFunction *, Scope &scope, if (searchString.isNull() && pos == 0) RETURN_RESULT(Encode(-1)); int index = value.lastIndexOf(searchString, pos); - scope.result = Encode(index); + return Encode(index); } -void StringPrototype::method_localeCompare(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_localeCompare(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - const QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString value = getThisString(v4, thisObject); + if (v4->hasException) + return QV4::Encode::undefined(); - ScopedValue v(scope, callData->argument(0)); - const QString that = v->toQString(); - scope.result = Encode(QString::localeAwareCompare(value, that)); + const QString that = (argc ? argv[0] : Primitive::undefinedValue()).toQString(); + return Encode(QString::localeAwareCompare(value, that)); } -void StringPrototype::method_match(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_match(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - if (callData->thisObject.isUndefined() || callData->thisObject.isNull()) - THROW_TYPE_ERROR(); + ExecutionEngine *v4 = b->engine(); + if (thisObject->isNullOrUndefined()) + return v4->throwTypeError(); - ScopedString s(scope, callData->thisObject.toString(scope.engine)); - - ScopedValue regexp(scope, callData->argument(0)); - Scoped<RegExpObject> rx(scope, regexp); - if (!rx) { - ScopedCallData callData(scope, 1); - callData->args[0] = regexp; - scope.engine->regExpCtor()->construct(scope, callData); - rx = scope.result.asReturnedValue(); + Scope scope(v4); + ScopedString s(scope, thisObject->toString(v4)); + if (v4->hasException) + return Encode::undefined(); + + Scoped<RegExpObject> that(scope, argc ? argv[0] : Primitive::undefinedValue()); + if (!that) { + // convert args[0] to a regexp + that = RegExpCtor::callAsConstructor(b, argv, argc); + if (v4->hasException) + return Encode::undefined(); } + Q_ASSERT(!!that); - if (!rx) - // ### CHECK - THROW_TYPE_ERROR(); - - bool global = rx->global(); + bool global = that->global(); - // ### use the standard builtin function, not the one that might be redefined in the proto - ScopedString execString(scope, scope.engine->newString(QStringLiteral("exec"))); - ScopedFunctionObject exec(scope, scope.engine->regExpPrototype()->get(execString)); + if (!global) + return RegExpPrototype::method_exec(b, that, s, 1); - ScopedCallData cData(scope, 1); - cData->thisObject = rx; - cData->args[0] = s; - if (!global) { - exec->call(scope, cData); - return; - } - - ScopedString lastIndex(scope, scope.engine->newString(QStringLiteral("lastIndex"))); - rx->put(lastIndex, ScopedValue(scope, Primitive::fromInt32(0))); + // rx is now in thisObject + that->setLastIndex(0); ScopedArrayObject a(scope, scope.engine->newArrayObject()); - double previousLastIndex = 0; + int previousLastIndex = 0; uint n = 0; - ScopedValue matchStr(scope); - ScopedValue index(scope); while (1) { - exec->call(scope, cData); - if (scope.result.isNull()) + Value result = Primitive::fromReturnedValue(RegExpPrototype::execFirstMatch(b, that, s, 1)); + if (result.isNull()) break; - assert(scope.result.isObject()); - index = rx->get(lastIndex, 0); - double thisIndex = index->toInteger(); - if (previousLastIndex == thisIndex) { - previousLastIndex = thisIndex + 1; - rx->put(lastIndex, ScopedValue(scope, Primitive::fromDouble(previousLastIndex))); + int index = that->lastIndex(); + if (previousLastIndex == index) { + previousLastIndex = index + 1; + that->setLastIndex(previousLastIndex); } else { - previousLastIndex = thisIndex; + previousLastIndex = index; } - matchStr = scope.result.objectValue()->getIndexed(0); - a->arraySet(n, matchStr); + a->arraySet(n, result); ++n; } if (!n) - scope.result = Encode::null(); + return Encode::null(); else - scope.result = a; + return a.asReturnedValue(); } -void StringPrototype::method_repeat(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_repeat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString value = getThisString(v4, thisObject); + if (v4->hasException) + return QV4::Encode::undefined(); - double repeats = callData->args[0].toInteger(); + double repeats = (argc ? argv[0] : Primitive::undefinedValue()).toInteger(); - if (repeats < 0 || qIsInf(repeats)) { - scope.result = scope.engine->throwRangeError(QLatin1String("Invalid count value")); - return; - } + if (repeats < 0 || qIsInf(repeats)) + return v4->throwRangeError(QLatin1String("Invalid count value")); - scope.result = scope.engine->newString(value.repeated(int(repeats))); + return Encode(v4->newString(value.repeated(int(repeats)))); } static void appendReplacementString(QString *result, const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount) @@ -513,13 +507,13 @@ static void appendReplacementString(QString *result, const QString &input, const } } -void StringPrototype::method_replace(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_replace(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { QString string; - if (StringObject *thisString = callData->thisObject.as<StringObject>()) + if (const StringObject *thisString = thisObject->as<StringObject>()) string = thisString->d()->string->toQString(); else - string = callData->thisObject.toQString(); + string = thisObject->toQString(); int numCaptures = 0; int numStringMatches = 0; @@ -528,7 +522,8 @@ void StringPrototype::method_replace(const BuiltinFunction *, Scope &scope, Call uint _matchOffsets[64]; uint *matchOffsets = _matchOffsets; - ScopedValue searchValue(scope, callData->argument(0)); + Scope scope(b); + ScopedValue searchValue(scope, argc ? argv[0] : Primitive::undefinedValue()); Scoped<RegExpObject> regExp(scope, searchValue); if (regExp) { uint offset = 0; @@ -571,14 +566,14 @@ void StringPrototype::method_replace(const BuiltinFunction *, Scope &scope, Call } QString result; - ScopedValue replaceValue(scope, callData->argument(1)); + ScopedValue replacement(scope); + ScopedValue replaceValue(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); ScopedFunctionObject searchCallback(scope, replaceValue); if (!!searchCallback) { result.reserve(string.length() + 10*numStringMatches); - ScopedCallData callData(scope, numCaptures + 2); - callData->thisObject = Primitive::undefinedValue(); - int lastEnd = 0; ScopedValue entry(scope); + Value *arguments = scope.alloc(numCaptures + 2); + int lastEnd = 0; for (int i = 0; i < numStringMatches; ++i) { for (int k = 0; k < numCaptures; ++k) { int idx = (i * numCaptures + k) * 2; @@ -587,17 +582,18 @@ void StringPrototype::method_replace(const BuiltinFunction *, Scope &scope, Call entry = Primitive::undefinedValue(); if (start != JSC::Yarr::offsetNoMatch && end != JSC::Yarr::offsetNoMatch) entry = scope.engine->newString(string.mid(start, end - start)); - callData->args[k] = entry; + arguments[k] = entry; } uint matchStart = matchOffsets[i * numCaptures * 2]; Q_ASSERT(matchStart >= static_cast<uint>(lastEnd)); uint matchEnd = matchOffsets[i * numCaptures * 2 + 1]; - callData->args[numCaptures] = Primitive::fromUInt32(matchStart); - callData->args[numCaptures + 1] = scope.engine->newString(string); + arguments[numCaptures] = Primitive::fromUInt32(matchStart); + arguments[numCaptures + 1] = scope.engine->newString(string); - searchCallback->call(scope, callData); + Value that = Primitive::undefinedValue(); + replacement = searchCallback->call(&that, arguments, numCaptures + 2); result += string.midRef(lastEnd, matchStart - lastEnd); - result += scope.result.toQString(); + result += replacement->toQString(); lastEnd = matchEnd; } result += string.midRef(lastEnd); @@ -623,44 +619,45 @@ void StringPrototype::method_replace(const BuiltinFunction *, Scope &scope, Call if (matchOffsets != _matchOffsets) free(matchOffsets); - scope.result = scope.engine->newString(result); + return Encode(scope.engine->newString(result)); } -void StringPrototype::method_search(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_search(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - QString string = getThisString(scope, callData); - scope.result = callData->argument(0); - CHECK_EXCEPTION(); + Scope scope(b); + QString string = getThisString(scope.engine, thisObject); + if (scope.engine->hasException) + return QV4::Encode::undefined(); - Scoped<RegExpObject> regExp(scope, scope.result.as<RegExpObject>()); + Scoped<RegExpObject> regExp(scope, argc ? argv[0] : Primitive::undefinedValue()); if (!regExp) { - ScopedCallData callData(scope, 1); - callData->args[0] = scope.result; - scope.engine->regExpCtor()->construct(scope, callData); - CHECK_EXCEPTION(); + regExp = scope.engine->regExpCtor()->callAsConstructor(argv, 1); + if (scope.engine->hasException) + return QV4::Encode::undefined(); - regExp = scope.result.as<RegExpObject>(); Q_ASSERT(regExp); } Scoped<RegExp> re(scope, regExp->value()); Q_ALLOCA_VAR(uint, matchOffsets, regExp->value()->captureCount() * 2 * sizeof(uint)); uint result = re->match(string, /*offset*/0, matchOffsets); if (result == JSC::Yarr::offsetNoMatch) - scope.result = Encode(-1); + return Encode(-1); else - scope.result = Encode(result); + return Encode(result); } -void StringPrototype::method_slice(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - const QString text = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString text = getThisString(v4, thisObject); + if (v4->hasException) + return QV4::Encode::undefined(); const double length = text.length(); - double start = callData->argc ? callData->args[0].toInteger() : 0; - double end = (callData->argc < 2 || callData->args[1].isUndefined()) - ? length : callData->args[1].toInteger(); + double start = argc ? argv[0].toInteger() : 0; + double end = (argc < 2 || argv[1].isUndefined()) + ? length : argv[1].toInteger(); if (start < 0) start = qMax(length + start, 0.); @@ -676,16 +673,19 @@ void StringPrototype::method_slice(const BuiltinFunction *, Scope &scope, CallDa const int intEnd = int(end); int count = qMax(0, intEnd - intStart); - scope.result = scope.engine->newString(text.mid(intStart, count)); + return Encode(v4->newString(text.mid(intStart, count))); } -void StringPrototype::method_split(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_split(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - QString text = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + QString text = getThisString(v4, thisObject); + if (v4->hasException) + return QV4::Encode::undefined(); - ScopedValue separatorValue(scope, callData->argument(0)); - ScopedValue limitValue(scope, callData->argument(1)); + Scope scope(v4); + ScopedValue separatorValue(scope, argc ? argv[0] : Primitive::undefinedValue()); + ScopedValue limitValue(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); ScopedArrayObject array(scope, scope.engine->newArrayObject()); @@ -693,7 +693,7 @@ void StringPrototype::method_split(const BuiltinFunction *, Scope &scope, CallDa if (limitValue->isUndefined()) { ScopedString s(scope, scope.engine->newString(text)); array->push_back(s); - RETURN_RESULT(array); + return array.asReturnedValue(); } RETURN_RESULT(scope.engine->newString(text.left(limitValue->toInteger()))); } @@ -701,7 +701,7 @@ void StringPrototype::method_split(const BuiltinFunction *, Scope &scope, CallDa uint limit = limitValue->isUndefined() ? UINT_MAX : limitValue->toUInt32(); if (limit == 0) - RETURN_RESULT(array); + return array.asReturnedValue(); Scoped<RegExpObject> re(scope, separatorValue); if (re) { @@ -742,7 +742,7 @@ void StringPrototype::method_split(const BuiltinFunction *, Scope &scope, CallDa if (separator.isEmpty()) { for (uint i = 0; i < qMin(limit, uint(text.length())); ++i) array->push_back((s = scope.engine->newString(text.mid(i, 1)))); - RETURN_RESULT(array); + return array.asReturnedValue(); } int start = 0; @@ -756,44 +756,48 @@ void StringPrototype::method_split(const BuiltinFunction *, Scope &scope, CallDa if (array->getLength() < limit && start != -1) array->push_back((s = scope.engine->newString(text.mid(start)))); } - RETURN_RESULT(array); + return array.asReturnedValue(); } -void StringPrototype::method_startsWith(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_startsWith(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString value = getThisString(v4, thisObject); + if (v4->hasException) + return QV4::Encode::undefined(); QString searchString; - if (callData->argc) { - if (callData->args[0].as<RegExpObject>()) - THROW_TYPE_ERROR(); - searchString = callData->args[0].toQString(); + if (argc) { + if (argv[0].as<RegExpObject>()) + return v4->throwTypeError(); + searchString = argv[0].toQString(); } int pos = 0; - if (callData->argc > 1) - pos = (int) callData->args[1].toInteger(); + if (argc > 1) + pos = (int) argv[1].toInteger(); if (pos == 0) - RETURN_RESULT(Encode(value.startsWith(searchString))); + return Encode(value.startsWith(searchString)); QStringRef stringToSearch = value.midRef(pos); RETURN_RESULT(Encode(stringToSearch.startsWith(searchString))); } -void StringPrototype::method_substr(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_substr(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - const QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString value = getThisString(v4, thisObject); + if (v4->hasException) + return QV4::Encode::undefined(); double start = 0; - if (callData->argc > 0) - start = callData->args[0].toInteger(); + if (argc > 0) + start = argv[0].toInteger(); double length = +qInf(); - if (callData->argc > 1) - length = callData->args[1].toInteger(); + if (argc > 1) + length = argv[1].toInteger(); double count = value.length(); if (start < 0) @@ -803,25 +807,26 @@ void StringPrototype::method_substr(const BuiltinFunction *, Scope &scope, CallD qint32 x = Primitive::toInt32(start); qint32 y = Primitive::toInt32(length); - scope.result = scope.engine->newString(value.mid(x, y)); + return Encode(v4->newString(value.mid(x, y))); } -void StringPrototype::method_substring(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_substring(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString value = getThisString(v4, thisObject); + if (v4->hasException) + return QV4::Encode::undefined(); int length = value.length(); double start = 0; double end = length; - if (callData->argc > 0) - start = callData->args[0].toInteger(); + if (argc > 0) + start = argv[0].toInteger(); - ScopedValue endValue(scope, callData->argument(1)); - if (!endValue->isUndefined()) - end = endValue->toInteger(); + if (argc > 1 && !argv[1].isUndefined()) + end = argv[1].toInteger(); if (std::isnan(start) || start < 0) start = 0; @@ -843,50 +848,56 @@ void StringPrototype::method_substring(const BuiltinFunction *, Scope &scope, Ca qint32 x = (int)start; qint32 y = (int)(end - start); - scope.result = scope.engine->newString(value.mid(x, y)); + return Encode(v4->newString(value.mid(x, y))); } -void StringPrototype::method_toLowerCase(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_toLowerCase(const FunctionObject *b, const Value *thisObject, const Value *, int) { - QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString value = getThisString(v4, thisObject); + if (v4->hasException) + return QV4::Encode::undefined(); - scope.result = scope.engine->newString(value.toLower()); + return Encode(v4->newString(value.toLower())); } -void StringPrototype::method_toLocaleLowerCase(const BuiltinFunction *b, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_toLocaleLowerCase(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - method_toLowerCase(b, scope, callData); + return method_toLowerCase(b, thisObject, argv, argc); } -void StringPrototype::method_toUpperCase(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_toUpperCase(const FunctionObject *b, const Value *thisObject, const Value *, int) { - QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString value = getThisString(v4, thisObject); + if (v4->hasException) + return QV4::Encode::undefined(); - scope.result = scope.engine->newString(value.toUpper()); + return Encode(v4->newString(value.toUpper())); } -void StringPrototype::method_toLocaleUpperCase(const BuiltinFunction *b, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_toLocaleUpperCase(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - return method_toUpperCase(b, scope, callData); + return method_toUpperCase(b, thisObject, argv, argc); } -void StringPrototype::method_fromCharCode(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_fromCharCode(const FunctionObject *b, const Value *, const Value *argv, int argc) { - QString str(callData->argc, Qt::Uninitialized); + QString str(argc, Qt::Uninitialized); QChar *ch = str.data(); - for (int i = 0; i < callData->argc; ++i) { - *ch = QChar(callData->args[i].toUInt16()); + for (int i = 0, ei = argc; i < ei; ++i) { + *ch = QChar(argv[i].toUInt16()); ++ch; } - scope.result = scope.engine->newString(str); + return Encode(b->engine()->newString(str)); } -void StringPrototype::method_trim(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_trim(const FunctionObject *b, const Value *thisObject, const Value *, int) { - QString s = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + QString s = getThisString(v4, thisObject); + if (v4->hasException) + return QV4::Encode::undefined(); const QChar *chars = s.constData(); int start, end; @@ -899,5 +910,5 @@ void StringPrototype::method_trim(const BuiltinFunction *, Scope &scope, CallDat break; } - scope.result = scope.engine->newString(QString(chars + start, end - start + 1)); + return Encode(v4->newString(QString(chars + start, end - start + 1))); } diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h index ce046f4844..7d25678b61 100644 --- a/src/qml/jsruntime/qv4stringobject_p.h +++ b/src/qml/jsruntime/qv4stringobject_p.h @@ -64,7 +64,7 @@ namespace Heap { Member(class, Pointer, String *, string) DECLARE_HEAP_OBJECT(StringObject, Object) { - DECLARE_MARK_TABLE(StringObject); + DECLARE_MARKOBJECTS(StringObject); enum { LengthPropertyIndex = 0 @@ -106,8 +106,8 @@ struct StringCtor: FunctionObject { V4_OBJECT2(StringCtor, FunctionObject) - static void construct(const Managed *m, Scope &scope, CallData *callData); - static void call(const Managed *, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; struct StringPrototype: StringObject @@ -115,30 +115,30 @@ struct StringPrototype: StringObject V4_PROTOTYPE(objectPrototype) void init(ExecutionEngine *engine, Object *ctor); - static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_charAt(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_charCodeAt(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_concat(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_endsWith(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_indexOf(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_includes(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_lastIndexOf(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_localeCompare(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_match(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_repeat(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_replace(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_search(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_slice(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_split(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_startsWith(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_substr(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_substring(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toLowerCase(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toLocaleLowerCase(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toUpperCase(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toLocaleUpperCase(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_fromCharCode(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_trim(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_charAt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_charCodeAt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_concat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_endsWith(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_indexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_includes(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_lastIndexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_localeCompare(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_match(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_repeat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_replace(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_search(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_slice(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_split(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_startsWith(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_substr(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_substring(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toLowerCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toLocaleLowerCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toUpperCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toLocaleUpperCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_fromCharCode(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_trim(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; } diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index fe27d7c2d2..4ba31f9b6e 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -39,6 +39,7 @@ #include "qv4typedarray_p.h" #include "qv4arraybuffer_p.h" #include "qv4string_p.h" +#include "qv4jscall_p.h" #include <cmath> @@ -208,36 +209,32 @@ void Heap::TypedArrayCtor::init(QV4::ExecutionContext *scope, TypedArray::Type t type = t; } -void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callData) +ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - Scoped<TypedArrayCtor> that(scope, static_cast<const TypedArrayCtor *>(m)); + Scope scope(f->engine()); + const TypedArrayCtor *that = static_cast<const TypedArrayCtor *>(f); - if (!callData->argc || !callData->args[0].isObject()) { + if (!argc || !argv[0].isObject()) { // ECMA 6 22.2.1.1 - double l = callData->argc ? callData->args[0].toNumber() : 0; - if (scope.engine->hasException) { - scope.result = Encode::undefined(); - return; - } + double l = argc ? argv[0].toNumber() : 0; + if (scope.engine->hasException) + return Encode::undefined(); uint len = (uint)l; if (l != len) scope.engine->throwRangeError(QStringLiteral("Non integer length for typed array.")); uint byteLength = len * operations[that->d()->type].bytesPerElement; Scoped<ArrayBuffer> buffer(scope, scope.engine->newArrayBuffer(byteLength)); - if (scope.engine->hasException) { - scope.result = Encode::undefined(); - return; - } + if (scope.engine->hasException) + return Encode::undefined(); Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type)); array->d()->buffer.set(scope.engine, buffer->d()); array->d()->byteLength = byteLength; array->d()->byteOffset = 0; - scope.result = array.asReturnedValue(); - return; + return array.asReturnedValue(); } - Scoped<TypedArray> typedArray(scope, callData->argument(0)); + Scoped<TypedArray> typedArray(scope, argc ? argv[0] : Primitive::undefinedValue()); if (!!typedArray) { // ECMA 6 22.2.1.2 Scoped<ArrayBuffer> buffer(scope, typedArray->d()->buffer); @@ -247,10 +244,8 @@ void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callDat uint destByteLength = byteLength*destElementSize/srcElementSize; Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(destByteLength)); - if (scope.engine->hasException) { - scope.result = Encode::undefined(); - return; - } + if (scope.engine->hasException) + return Encode::undefined(); Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type)); array->d()->buffer.set(scope.engine, newBuffer->d()); @@ -275,39 +270,30 @@ void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callDat } } - scope.result = array.asReturnedValue(); - return; + return array.asReturnedValue(); } - Scoped<ArrayBuffer> buffer(scope, callData->argument(0)); + Scoped<ArrayBuffer> buffer(scope, argc ? argv[0] : Primitive::undefinedValue()); if (!!buffer) { // ECMA 6 22.2.1.4 - double dbyteOffset = callData->argc > 1 ? callData->args[1].toInteger() : 0; + double dbyteOffset = argc > 1 ? argv[1].toInteger() : 0; uint byteOffset = (uint)dbyteOffset; uint elementSize = operations[that->d()->type].bytesPerElement; - if (dbyteOffset < 0 || (byteOffset % elementSize) || dbyteOffset > buffer->byteLength()) { - scope.result = scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid byteOffset")); - return; - } + if (dbyteOffset < 0 || (byteOffset % elementSize) || dbyteOffset > buffer->byteLength()) + return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid byteOffset")); uint byteLength; - if (callData->argc < 3 || callData->args[2].isUndefined()) { + if (argc < 3 || argv[2].isUndefined()) { byteLength = buffer->byteLength() - byteOffset; - if (buffer->byteLength() < byteOffset || byteLength % elementSize) { - scope.result = scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length")); - return; - } + if (buffer->byteLength() < byteOffset || byteLength % elementSize) + return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length")); } else { - double l = qBound(0., callData->args[2].toInteger(), (double)UINT_MAX); - if (scope.engine->hasException) { - scope.result = Encode::undefined(); - return; - } + double l = qBound(0., argv[2].toInteger(), (double)UINT_MAX); + if (scope.engine->hasException) + return Encode::undefined(); l *= elementSize; - if (buffer->byteLength() - byteOffset < l) { - scope.result = scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length")); - return; - } + if (buffer->byteLength() - byteOffset < l) + return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length")); byteLength = (uint)l; } @@ -315,25 +301,20 @@ void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callDat array->d()->buffer.set(scope.engine, buffer->d()); array->d()->byteLength = byteLength; array->d()->byteOffset = byteOffset; - scope.result = array.asReturnedValue(); - return; + return array.asReturnedValue(); } // ECMA 6 22.2.1.3 - ScopedObject o(scope, callData->argument(0)); + ScopedObject o(scope, argc ? argv[0] : Primitive::undefinedValue()); uint l = (uint) qBound(0., ScopedValue(scope, o->get(scope.engine->id_length()))->toInteger(), (double)UINT_MAX); - if (scope.engine->hasException) { - scope.result = scope.engine->throwTypeError(); - return; - } + if (scope.engine->hasException) + return scope.engine->throwTypeError(); uint elementSize = operations[that->d()->type].bytesPerElement; Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(l * elementSize)); - if (scope.engine->hasException) { - scope.result = Encode::undefined(); - return; - } + if (scope.engine->hasException) + return Encode::undefined(); Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type)); array->d()->buffer.set(scope.engine, newBuffer->d()); @@ -346,21 +327,19 @@ void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callDat while (idx < l) { val = o->getIndexed(idx); array->d()->type->write(scope.engine, b, 0, val); - if (scope.engine->hasException) { - scope.result = Encode::undefined(); - return; - } + if (scope.engine->hasException) + return Encode::undefined(); ++idx; b += elementSize; } - scope.result = array.asReturnedValue(); + return array.asReturnedValue(); } -void TypedArrayCtor::call(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue TypedArrayCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) { - construct(that, scope, callData); + return callAsConstructor(f, argv, argc); } void Heap::TypedArray::init(Type t) @@ -406,15 +385,10 @@ bool TypedArray::putIndexed(Managed *m, uint index, const Value &value) uint bytesPerElement = a->d()->type->bytesPerElement; uint byteOffset = a->d()->byteOffset + index * bytesPerElement; if (byteOffset + bytesPerElement > (uint)a->d()->buffer->byteLength()) - goto reject; + return false; a->d()->type->write(scope.engine, a->d()->buffer->data->data(), byteOffset, value); return true; - -reject: - if (scope.engine->current->strictMode) - scope.engine->throwTypeError(); - return false; } void TypedArrayPrototype::init(ExecutionEngine *engine, TypedArrayCtor *ctor) @@ -435,52 +409,57 @@ void TypedArrayPrototype::init(ExecutionEngine *engine, TypedArrayCtor *ctor) defineDefaultProperty(QStringLiteral("subarray"), method_subarray, 0); } -void TypedArrayPrototype::method_get_buffer(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue TypedArrayPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int) { - Scoped<TypedArray> v(scope, callData->thisObject); + ExecutionEngine *v4 = b->engine(); + const TypedArray *v = thisObject->as<TypedArray>(); if (!v) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); - scope.result = v->d()->buffer; + return v->d()->buffer->asReturnedValue(); } -void TypedArrayPrototype::method_get_byteLength(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue TypedArrayPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int) { - Scoped<TypedArray> v(scope, callData->thisObject); + ExecutionEngine *v4 = b->engine(); + const TypedArray *v = thisObject->as<TypedArray>(); if (!v) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); - scope.result = Encode(v->d()->byteLength); + return Encode(v->d()->byteLength); } -void TypedArrayPrototype::method_get_byteOffset(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue TypedArrayPrototype::method_get_byteOffset(const FunctionObject *b, const Value *thisObject, const Value *, int) { - Scoped<TypedArray> v(scope, callData->thisObject); + ExecutionEngine *v4 = b->engine(); + const TypedArray *v = thisObject->as<TypedArray>(); if (!v) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); - scope.result = Encode(v->d()->byteOffset); + return Encode(v->d()->byteOffset); } -void TypedArrayPrototype::method_get_length(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue TypedArrayPrototype::method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int) { - Scoped<TypedArray> v(scope, callData->thisObject); + ExecutionEngine *v4 = b->engine(); + const TypedArray *v = thisObject->as<TypedArray>(); if (!v) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); - scope.result = Encode(v->d()->byteLength/v->d()->type->bytesPerElement); + return Encode(v->d()->byteLength/v->d()->type->bytesPerElement); } -void TypedArrayPrototype::method_set(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue TypedArrayPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - Scoped<TypedArray> a(scope, callData->thisObject); + Scope scope(b); + Scoped<TypedArray> a(scope, *thisObject); if (!a) - THROW_TYPE_ERROR(); + return scope.engine->throwTypeError(); Scoped<ArrayBuffer> buffer(scope, a->d()->buffer); if (!buffer) scope.engine->throwTypeError(); - double doffset = callData->argc >= 2 ? callData->args[1].toInteger() : 0; + double doffset = argc >= 2 ? argv[1].toInteger() : 0; if (scope.engine->hasException) RETURN_UNDEFINED(); @@ -489,17 +468,17 @@ void TypedArrayPrototype::method_set(const BuiltinFunction *, Scope &scope, Call uint offset = (uint)doffset; uint elementSize = a->d()->type->bytesPerElement; - Scoped<TypedArray> srcTypedArray(scope, callData->args[0]); + Scoped<TypedArray> srcTypedArray(scope, argv[0]); if (!srcTypedArray) { // src is a regular object - ScopedObject o(scope, callData->args[0].toObject(scope.engine)); + ScopedObject o(scope, argv[0].toObject(scope.engine)); if (scope.engine->hasException || !o) - THROW_TYPE_ERROR(); + return scope.engine->throwTypeError(); double len = ScopedValue(scope, o->get(scope.engine->id_length()))->toNumber(); uint l = (uint)len; if (scope.engine->hasException || l != len) - THROW_TYPE_ERROR(); + return scope.engine->throwTypeError(); if (offset + l > a->length()) RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range"))); @@ -521,7 +500,7 @@ void TypedArrayPrototype::method_set(const BuiltinFunction *, Scope &scope, Call // src is a typed array Scoped<ArrayBuffer> srcBuffer(scope, srcTypedArray->d()->buffer); if (!srcBuffer) - THROW_TYPE_ERROR(); + return scope.engine->throwTypeError(); uint l = srcTypedArray->length(); if (offset + l > a->length()) @@ -559,24 +538,25 @@ void TypedArrayPrototype::method_set(const BuiltinFunction *, Scope &scope, Call RETURN_UNDEFINED(); } -void TypedArrayPrototype::method_subarray(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue TypedArrayPrototype::method_subarray(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc) { - Scoped<TypedArray> a(scope, callData->thisObject); + Scope scope(builtin); + Scoped<TypedArray> a(scope, *thisObject); if (!a) - THROW_TYPE_ERROR(); + return scope.engine->throwTypeError(); Scoped<ArrayBuffer> buffer(scope, a->d()->buffer); if (!buffer) - THROW_TYPE_ERROR(); + return scope.engine->throwTypeError(); int len = a->length(); - double b = callData->argc > 0 ? callData->args[0].toInteger() : 0; + double b = argc > 0 ? argv[0].toInteger() : 0; if (b < 0) b = len + b; uint begin = (uint)qBound(0., b, (double)len); - double e = callData->argc < 2 || callData->args[1].isUndefined() ? len : callData->args[1].toInteger(); + double e = argc < 2 || argv[1].isUndefined() ? len : argv[1].toInteger(); if (e < 0) e = len + e; uint end = (uint)qBound(0., e, (double)len); @@ -590,11 +570,11 @@ void TypedArrayPrototype::method_subarray(const BuiltinFunction *, Scope &scope, ScopedFunctionObject constructor(scope, a->get(scope.engine->id_constructor())); if (!constructor) - THROW_TYPE_ERROR(); + return scope.engine->throwTypeError(); - ScopedCallData cData(scope, 3); - cData->args[0] = buffer; - cData->args[1] = Encode(a->d()->byteOffset + begin*a->d()->type->bytesPerElement); - cData->args[2] = Encode(newLen); - constructor->construct(scope, cData); + Value *arguments = scope.alloc(3); + arguments[0] = buffer; + arguments[1] = Encode(a->d()->byteOffset + begin*a->d()->type->bytesPerElement); + arguments[2] = Encode(newLen); + return constructor->callAsConstructor(arguments, 3); } diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h index a472dfa607..129c662c97 100644 --- a/src/qml/jsruntime/qv4typedarray_p.h +++ b/src/qml/jsruntime/qv4typedarray_p.h @@ -80,7 +80,7 @@ namespace Heap { Member(class, NoMark, uint, arrayType) DECLARE_HEAP_OBJECT(TypedArray, Object) { - DECLARE_MARK_TABLE(TypedArray); + DECLARE_MARKOBJECTS(TypedArray); enum Type { Int8Array, UInt8Array, @@ -141,8 +141,8 @@ struct TypedArrayCtor: FunctionObject { V4_OBJECT2(TypedArrayCtor, FunctionObject) - static void construct(const Managed *m, Scope &scope, CallData *callData); - static void call(const Managed *that, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; @@ -153,13 +153,13 @@ struct TypedArrayPrototype : Object void init(ExecutionEngine *engine, TypedArrayCtor *ctor); - static void method_get_buffer(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_get_byteLength(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_get_byteOffset(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_get_length(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_get_buffer(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get_byteLength(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get_byteOffset(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get_length(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static void method_set(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_subarray(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_subarray(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; inline void diff --git a/src/qml/jsruntime/qv4value.cpp b/src/qml/jsruntime/qv4value.cpp index f41442df7a..0d4711df3c 100644 --- a/src/qml/jsruntime/qv4value.cpp +++ b/src/qml/jsruntime/qv4value.cpp @@ -75,39 +75,29 @@ int Value::toUInt16() const return (unsigned short)number; } -bool Value::toBoolean() const +bool Value::toBooleanImpl(Value val) { - if (isInteger() || isBoolean()) - return static_cast<bool>(int_32()); - - if (isUndefined() || isNull()) - return false; - - if (isManaged()) { + if (val.isManagedOrUndefined()) { + Heap::Base *b = val.m(); + if (!b) + return false; #ifdef V4_BOOTSTRAP Q_UNIMPLEMENTED(); #else - if (String *s = stringValue()) - return s->toQString().length() > 0; + if (b->vtable()->isString) + return static_cast<Heap::String *>(b)->length() > 0; #endif return true; } // double - return doubleValue() && !std::isnan(doubleValue()); + double d = val.doubleValue(); + return d && !std::isnan(d); } -double Value::toInteger() const +double Value::toNumberImpl(Value val) { - if (integerCompatible()) - return int_32(); - - return Primitive::toInteger(toNumber()); -} - -double Value::toNumberImpl() const -{ - switch (type()) { + switch (val.type()) { case QV4::Value::Undefined_Type: return std::numeric_limits<double>::quiet_NaN(); case QV4::Value::Managed_Type: @@ -115,12 +105,13 @@ double Value::toNumberImpl() const Q_UNIMPLEMENTED(); Q_FALLTHROUGH(); #else - if (String *s = stringValue()) + if (String *s = val.stringValue()) return RuntimeHelpers::stringToNumber(s->toQString()); { - Q_ASSERT(isObject()); - Scope scope(objectValue()->engine()); - ScopedValue prim(scope, RuntimeHelpers::toPrimitive(*this, NUMBER_HINT)); + Q_ASSERT(val.isObject()); + Scope scope(val.objectValue()->engine()); + ScopedValue protectThis(scope, val); + ScopedValue prim(scope, RuntimeHelpers::toPrimitive(val, NUMBER_HINT)); if (scope.engine->hasException) return 0; return prim->toNumber(); @@ -129,7 +120,7 @@ double Value::toNumberImpl() const case QV4::Value::Null_Type: case QV4::Value::Boolean_Type: case QV4::Value::Integer_Type: - return int_32(); + return val.int_32(); default: // double Q_UNREACHABLE(); } @@ -236,83 +227,23 @@ bool Value::sameValue(Value other) const { if (s && os) return s->isEqualTo(os); if (isInteger() && other.isDouble()) - return int_32() ? (double(int_32()) == other.doubleValue()) : (other._val == 0); + return int_32() ? (double(int_32()) == other.doubleValue()) + : (other.doubleValue() == 0 && !std::signbit(other.doubleValue())); if (isDouble() && other.isInteger()) - return other.int_32() ? (doubleValue() == double(other.int_32())) : (_val == 0); + return other.int_32() ? (doubleValue() == double(other.int_32())) + : (doubleValue() == 0 && !std::signbit(doubleValue())); return false; } - -int Primitive::toInt32(double number) -{ - const double D32 = 4294967296.0; - const double D31 = D32 / 2.0; - - if ((number >= -D31 && number < D31)) - return static_cast<int>(number); - - - if (!std::isfinite(number)) - return 0; - - double d = ::floor(::fabs(number)); - if (std::signbit(number)) - d = -d; - - number = ::fmod(d , D32); - - if (number < -D31) - number += D32; - else if (number >= D31) - number -= D32; - - return int(number); -} - -unsigned int Primitive::toUInt32(double number) -{ - const double D32 = 4294967296.0; - if ((number >= 0 && number < D32)) - return static_cast<uint>(number); - - if (!std::isfinite(number)) - return +0; - - double d = ::floor(::fabs(number)); - if (std::signbit(number)) - d = -d; - - number = ::fmod(d , D32); - - if (number < 0) - number += D32; - - return unsigned(number); -} - -double Primitive::toInteger(double number) -{ - if (std::isnan(number)) - return +0; - else if (! number || std::isinf(number)) - return number; - const double v = floor(fabs(number)); - return std::signbit(number) ? -v : v; -} - #ifndef V4_BOOTSTRAP -Heap::String *Value::toString(ExecutionEngine *e) const +Heap::String *Value::toString(ExecutionEngine *e, Value val) { - if (String *s = stringValue()) - return s->d(); - return RuntimeHelpers::convertToString(e, *this); + return RuntimeHelpers::convertToString(e, val); } -Heap::Object *Value::toObject(ExecutionEngine *e) const +Heap::Object *Value::toObject(ExecutionEngine *e, Value val) { - if (Object *o = objectValue()) - return o->d(); - return RuntimeHelpers::convertToObject(e, *this); + return RuntimeHelpers::convertToObject(e, val); } uint Value::asArrayLength(bool *ok) const diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 17d0c32853..52d9f23afd 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -51,15 +51,12 @@ // #include <limits.h> +#include <cmath> #include <QtCore/QString> #include "qv4global_p.h" #include <private/qv4heap_p.h> -#if QT_POINTER_SIZE == 8 -#define QV4_USE_64_BIT_VALUE_ENCODING -#endif - QT_BEGIN_NAMESPACE namespace QV4 { @@ -72,9 +69,7 @@ struct Q_QML_PRIVATE_EXPORT Value { private: /* - We use two different ways of encoding JS values. One for 32bit and one for 64bit systems. - - In both cases, we use 8 bytes for a value and a different variant of NaN boxing. A Double + We use 8 bytes for a value and a different variant of NaN boxing. A Double NaN (actually -qNaN) is indicated by a number that has the top 13 bits set, and for a signalling NaN it is the top 14 bits. The other values are usually set to 0 by the processor, and are thus free for us to store other data. We keep pointers in there for @@ -83,18 +78,14 @@ private: only have 48 bits of addressable memory. (Note: we do leave the lower 49 bits available for pointers.) - On 32bit, we store doubles as doubles. All other values, have the high 32bits set to a value - that will make the number a NaN. The Masks below are used for encoding the other types. - - On 64 bit, we xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will + We xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will get encoded with bits 63-49 all set to 0. We then use bit 48 to distinguish between managed/undefined (0), or Null/Int/Bool/Empty (1). So, storing a 49 bit pointer will leave the top 15 bits 0, which is exactly the 'natural' representation of pointers. If bit 49 is set, bit 48 indicates Empty (0) or integer-convertible (1). Then the 3 bit below that are used to encode Null/Int/Bool. - On both 32bit and 64bit, Undefined is encoded as a managed pointer with value 0. This is - the same as a nullptr. + Undefined is encoded as a managed pointer with value 0. This is the same as a nullptr. Specific bit-sequences: 0 = always 0 @@ -102,8 +93,6 @@ private: x = stored value a,b,c,d = specific bit values, see notes - 64bit: - 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 | 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value ------------------------------------------------------------------------+-------------- @@ -112,9 +101,9 @@ private: a0000000 0000bc00 00000000 00000000 00000000 00000000 00000000 00000000 | NaN/Inf dddddddd ddddddxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double 00000000 00000010 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole) - 00000000 00000011 10000000 00000000 00000000 00000000 00000000 00000000 | Null - 00000000 00000011 01000000 00000000 00000000 00000000 00000000 0000000x | Bool - 00000000 00000011 00100000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int + 00000000 00000010 10000000 00000000 00000000 00000000 00000000 00000000 | Null + 00000000 00000011 00000000 00000000 00000000 00000000 00000000 0000000x | Bool + 00000000 00000011 10000000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int Notes: - a: xor-ed signbit, always 1 for NaN @@ -128,31 +117,8 @@ private: - Null, Bool, and Int have bit 48 set, indicating integer-convertible - xoring _val with NaNEncodeMask will convert to a double in "natural" representation, where any non double results in a NaN - - 32bit: - - 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 | - 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value - ------------------------------------------------------------------------+-------------- - 01111111 11111100 00000000 00000000 00000000 00000000 00000000 00000000 | Undefined - 01111111 11111100 00000000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Managed (heap pointer) - a1111111 1111bc00 00000000 00000000 00000000 00000000 00000000 00000000 | NaN/Inf - xddddddd ddddddxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double - 01111111 11111110 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole) - 01111111 11111111 10000000 00000000 00000000 00000000 00000000 00000000 | Null - 01111111 11111111 01000000 00000000 00000000 00000000 00000000 0000000x | Bool - 01111111 11111111 00100000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int - - Notes: - - the upper 32 bits are the tag, the lower 32 bits the value - - Undefined has a nullptr in the value, Managed has a non-nullptr stored in the value - - a: sign bit, always 0 for NaN - - b,c: 00=inf, 01 = sNaN, 10 = qNaN, 11 = boxed value - - d: stored double value, as long as not *all* of them are 1, because that's a boxed value - (see above) - - empty, Null, Bool, and Int have bit 63 set to 0, bits 62-50 set to 1 (same as undefined - and managed), and bit 49 set to 1 (where undefined and managed have it set to 0) - - Null, Bool, and Int have bit 48 set, indicating integer-convertible + - on 32bit we can use the fact that addresses are 32bits wide, so the tag part (bits 32 to + 63) are zero. No need to shift. */ quint64 _val; @@ -172,8 +138,9 @@ public: QML_NEARLY_ALWAYS_INLINE void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << 32 | value; } QML_NEARLY_ALWAYS_INLINE quint32 value() const { return _val & quint64(~quint32(0)); } QML_NEARLY_ALWAYS_INLINE quint32 tag() const { return _val >> 32; } + QML_NEARLY_ALWAYS_INLINE void setTag(quint32 tag) { setTagValue(tag, value()); } -#if defined(QV4_USE_64_BIT_VALUE_ENCODING) +#if QT_POINTER_SIZE == 8 QML_NEARLY_ALWAYS_INLINE Heap::Base *m() const { Heap::Base *b; @@ -184,7 +151,7 @@ public: { memcpy(&_val, &b, 8); } -#else // !QV4_USE_64_BIT_VALUE_ENCODING +#elif QT_POINTER_SIZE == 4 QML_NEARLY_ALWAYS_INLINE Heap::Base *m() const { Q_STATIC_ASSERT(sizeof(Heap::Base*) == sizeof(quint32)); @@ -199,6 +166,8 @@ public: memcpy(&v, &b, 4); setTagValue(Managed_Type_Internal, v); } +#else +# error "unsupported pointer size" #endif QML_NEARLY_ALWAYS_INLINE int int_32() const @@ -232,24 +201,37 @@ public: return quint32(value()); } + // ### Fix for 32 bit (easiest solution is to set highest bit to 1 for mananged/undefined/integercompatible + // and use negative numbers here + enum QuickType { + QT_ManagedOrUndefined = 0, + QT_ManagedOrUndefined1 = 1, + QT_ManagedOrUndefined2 = 2, + QT_ManagedOrUndefined3 = 3, + QT_Empty = 4, + QT_Null = 5, + QT_Bool = 6, + QT_Int = 7 + // all other values are doubles + }; + enum Type { - Undefined_Type, - Managed_Type, - Empty_Type, - Integer_Type, - Boolean_Type, - Null_Type, - Double_Type + Undefined_Type = 0, + Managed_Type = 1, + Empty_Type = 4, + Null_Type = 5, + Boolean_Type = 6, + Integer_Type = 7, + Double_Type = 8 }; inline Type type() const { - if (isUndefined()) return Undefined_Type; - if (isManaged()) return Managed_Type; - if (isEmpty()) return Empty_Type; - if (isInteger()) return Integer_Type; - if (isBoolean()) return Boolean_Type; - if (isNull()) return Null_Type; - Q_ASSERT(isDouble()); return Double_Type; + int t = quickType(); + if (t < QT_Empty) + return _val ? Managed_Type : Undefined_Type; + if (t > QT_Int) + return Double_Type; + return static_cast<Type>(t); } // Shared between 32-bit and 64-bit encoding @@ -262,19 +244,18 @@ public: enum { IsDouble_Shift = 64-14, IsManagedOrUndefined_Shift = 64-15, - IsIntegerConvertible_Shift = 64-16, - IsDoubleTag_Shift = IsDouble_Shift - Tag_Shift, - Managed_Type_Internal_64 = 0 + IsIntegerConvertible_Shift = 64-15, + IsIntegerOrBool_Shift = 64-16, + QuickType_Shift = 64 - 17 }; static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49 enum class ValueTypeInternal_64 { - Empty = Immediate_Mask_64| 0, - ConvertibleToInt = Immediate_Mask_64| 0x10000u, // bit 48 - Null = ConvertibleToInt | 0x08000u, - Boolean = ConvertibleToInt | 0x04000u, - Integer = ConvertibleToInt | 0x02000u + Empty = Immediate_Mask_64 | 0, + Null = Immediate_Mask_64 | 0x08000u, + Boolean = Immediate_Mask_64 | 0x10000u, + Integer = Immediate_Mask_64 | 0x18000u }; // Used only by 32-bit encoding @@ -285,50 +266,44 @@ public: static const quint64 Immediate_Mask_32 = NotDouble_Mask | 0x00020000u | SilentNaNBit; enum class ValueTypeInternal_32 { - Empty = Immediate_Mask_32| 0, - ConvertibleToInt = Immediate_Mask_32| 0x10000u, // bit 48 - Null = ConvertibleToInt | 0x08000u, - Boolean = ConvertibleToInt | 0x04000u, - Integer = ConvertibleToInt | 0x02000u + Empty = Immediate_Mask_32 | 0, + Null = Immediate_Mask_32 | 0x08000u, + Boolean = Immediate_Mask_32 | 0x10000u, + Integer = Immediate_Mask_32 | 0x18000u }; enum { - Managed_Type_Internal_32 = NotDouble_Mask + Managed_Type_Internal = 0 }; -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - enum { - Managed_Type_Internal = Managed_Type_Internal_64 - }; - static const quint64 Immediate_Mask = Immediate_Mask_64; using ValueTypeInternal = ValueTypeInternal_64; -#else - enum { - Managed_Type_Internal = Managed_Type_Internal_32 - }; - static const quint64 Immediate_Mask = Immediate_Mask_32; - using ValueTypeInternal = ValueTypeInternal_32; -#endif + enum { NaN_Mask = 0x7ff80000, }; + inline quint64 quickType() const { return (_val >> QuickType_Shift); } + // used internally in property inline bool isEmpty() const { return tag() == quint32(ValueTypeInternal::Empty); } inline bool isNull() const { return tag() == quint32(ValueTypeInternal::Null); } inline bool isBoolean() const { return tag() == quint32(ValueTypeInternal::Boolean); } inline bool isInteger() const { return tag() == quint32(ValueTypeInternal::Integer); } inline bool isNullOrUndefined() const { return isNull() || isUndefined(); } - inline bool isNumber() const { return isDouble() || isInteger(); } + inline bool isNumber() const { return quickType() >= QT_Int; } -#ifdef QV4_USE_64_BIT_VALUE_ENCODING inline bool isUndefined() const { return _val == 0; } inline bool isDouble() const { return (_val >> IsDouble_Shift); } - inline bool isManaged() const { return !isUndefined() && ((_val >> IsManagedOrUndefined_Shift) == 0); } + inline bool isManaged() const { return _val && ((_val >> IsManagedOrUndefined_Shift) == 0); } inline bool isManagedOrUndefined() const { return ((_val >> IsManagedOrUndefined_Shift) == 0); } + inline bool isIntOrBool() const { + return (_val >> IsIntegerOrBool_Shift) == 3; + } + inline bool integerCompatible() const { - return (_val >> IsIntegerConvertible_Shift) == 3; + Q_ASSERT(!isEmpty()); + return (_val >> IsIntegerConvertible_Shift) == 1; } static inline bool integerCompatible(Value a, Value b) { return a.integerCompatible() && b.integerCompatible(); @@ -337,39 +312,23 @@ public: return a.isDouble() && b.isDouble(); } inline bool isNaN() const { return (tag() & 0x7ffc0000 ) == 0x00040000; } -#else - inline bool isUndefined() const { return tag() == Managed_Type_Internal && value() == 0; } - inline bool isDouble() const { return (tag() & NotDouble_Mask) != NotDouble_Mask; } - inline bool isManaged() const { return tag() == Managed_Type_Internal && !isUndefined(); } - inline bool isManagedOrUndefined() const { return tag() == Managed_Type_Internal; } - inline bool integerCompatible() const { return (tag() & quint32(ValueTypeInternal::ConvertibleToInt)) == quint32(ValueTypeInternal::ConvertibleToInt); } - static inline bool integerCompatible(Value a, Value b) { - return ((a.tag() & b.tag()) & quint32(ValueTypeInternal::ConvertibleToInt)) == quint32(ValueTypeInternal::ConvertibleToInt); - } - static inline bool bothDouble(Value a, Value b) { - return ((a.tag() | b.tag()) & NotDouble_Mask) != NotDouble_Mask; - } - inline bool isNaN() const { return (tag() & QV4::Value::NotDouble_Mask) == QV4::Value::NaN_Mask; } -#endif + QML_NEARLY_ALWAYS_INLINE double doubleValue() const { Q_ASSERT(isDouble()); double d; - quint64 v = _val; -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - v ^= NaNEncodeMask; -#endif - memcpy(&d, &v, 8); + Value v = *this; + v._val ^= NaNEncodeMask; + memcpy(&d, &v._val, 8); return d; } QML_NEARLY_ALWAYS_INLINE void setDouble(double d) { memcpy(&_val, &d, 8); -#ifdef QV4_USE_64_BIT_VALUE_ENCODING _val ^= NaNEncodeMask; -#endif Q_ASSERT(isDouble()); } inline bool isString() const; inline bool isObject() const; + inline bool isFunctionObject() const; inline bool isInt32() { if (tag() == quint32(ValueTypeInternal::Integer)) return true; @@ -426,14 +385,31 @@ public: inline int toInt32() const; inline unsigned int toUInt32() const; - bool toBoolean() const; + bool toBoolean() const { + if (integerCompatible()) + return static_cast<bool>(int_32()); + + return toBooleanImpl(*this); + } + static bool toBooleanImpl(Value val); double toInteger() const; inline double toNumber() const; - double toNumberImpl() const; + static double toNumberImpl(Value v); + double toNumberImpl() const { return toNumberImpl(*this); } QString toQStringNoThrow() const; QString toQString() const; - Heap::String *toString(ExecutionEngine *e) const; - Heap::Object *toObject(ExecutionEngine *e) const; + Heap::String *toString(ExecutionEngine *e) const { + if (isString()) + return reinterpret_cast<Heap::String *>(m()); + return toString(e, *this); + } + static Heap::String *toString(ExecutionEngine *e, Value val); + Heap::Object *toObject(ExecutionEngine *e) const { + if (isObject()) + return reinterpret_cast<Heap::Object *>(m()); + return toObject(e, *this); + } + static Heap::Object *toObject(ExecutionEngine *e, Value val); inline bool isPrimitive() const; inline bool tryIntegerConversion() { @@ -509,6 +485,13 @@ public: }; V4_ASSERT_IS_TRIVIAL(Value) +inline void Value::mark(MarkStack *markStack) +{ + Heap::Base *o = heapObject(); + if (o) + o->mark(markStack); +} + inline bool Value::isString() const { Heap::Base *b = heapObject(); @@ -520,6 +503,12 @@ inline bool Value::isObject() const return b && b->vtable()->isObject; } +inline bool Value::isFunctionObject() const +{ + Heap::Base *b = heapObject(); + return b && b->vtable()->isFunctionObject; +} + inline bool Value::isPrimitive() const { return !isObject(); @@ -538,7 +527,7 @@ inline double Value::toNumber() const #ifndef V4_BOOTSTRAP inline uint Value::asArrayIndex() const { -#ifdef QV4_USE_64_BIT_VALUE_ENCODING +#if QT_POINTER_SIZE == 8 if (!isNumber()) return UINT_MAX; if (isInteger()) @@ -558,8 +547,8 @@ inline uint Value::asArrayIndex() const inline bool Value::asArrayIndex(uint &idx) const { - if (!isDouble()) { - if (isInteger() && int_32() >= 0) { + if (Q_LIKELY(!isDouble())) { + if (Q_LIKELY(isInteger() && int_32() >= 0)) { idx = (uint)int_32(); return true; } @@ -593,9 +582,9 @@ struct Q_QML_PRIVATE_EXPORT Primitive : public Value using Value::toInt32; using Value::toUInt32; - static double toInteger(double fromNumber); - static int toInt32(double value); - static unsigned int toUInt32(double value); + static double toInteger(double d); + static int toInt32(double d); + static unsigned int toUInt32(double d); }; inline Primitive Primitive::undefinedValue() @@ -658,6 +647,72 @@ inline Primitive Primitive::fromUInt32(uint i) return v; } +struct Double { + quint64 d; + + Double(double dbl) { + memcpy(&d, &dbl, sizeof(double)); + } + + int sign() const { + return (d >> 63) ? -1 : 1; + } + + bool isDenormal() const { + return static_cast<int>((d << 1) >> 53) == 0; + } + + int exponent() const { + return static_cast<int>((d << 1) >> 53) - 1023; + } + + quint64 significant() const { + quint64 m = (d << 12) >> 12; + if (!isDenormal()) + m |= (static_cast<quint64>(1) << 52); + return m; + } + + static int toInt32(double d) { + int i = static_cast<int>(d); + if (i == d) + return i; + return Double(d).toInt32(); + } + + int toInt32() { + int e = exponent() - 52; + if (e < 0) { + if (e <= -53) + return 0; + return sign() * static_cast<int>(significant() >> -e); + } else { + if (e > 31) + return 0; + return sign() * (static_cast<int>(significant()) << e); + } + } +}; + +inline double Primitive::toInteger(double d) +{ + if (std::isnan(d)) + return +0; + else if (!d || std::isinf(d)) + return d; + return d >= 0 ? std::floor(d) : std::ceil(d); +} + +inline int Primitive::toInt32(double value) +{ + return Double::toInt32(value); +} + +inline unsigned int Primitive::toUInt32(double d) +{ + return static_cast<uint>(toInt32(d)); +} + struct Encode { static ReturnedValue undefined() { return Primitive::undefinedValue().rawValue(); @@ -666,33 +721,47 @@ struct Encode { return Primitive::nullValue().rawValue(); } - Encode(bool b) { + explicit Encode(bool b) { val = Primitive::fromBoolean(b).rawValue(); } - Encode(double d) { + explicit Encode(double d) { val = Primitive::fromDouble(d).rawValue(); } - Encode(int i) { + explicit Encode(int i) { val = Primitive::fromInt32(i).rawValue(); } - Encode(uint i) { + explicit Encode(uint i) { val = Primitive::fromUInt32(i).rawValue(); } - Encode(ReturnedValue v) { + explicit Encode(ReturnedValue v) { val = v; } + Encode(Value v) { + val = v.rawValue(); + } - Encode(Heap::Base *o) { - Q_ASSERT(o); + explicit Encode(Heap::Base *o) { val = Value::fromHeapObject(o).asReturnedValue(); } + explicit Encode(Value *o) { + Q_ASSERT(o); + val = o->asReturnedValue(); + } + + static ReturnedValue smallestNumber(double d) { + if (static_cast<int>(d) == d && !(d == 0. && std::signbit(d))) + return Encode(static_cast<int>(d)); + else + return Encode(d); + } + operator ReturnedValue() const { return val; } quint64 val; private: - Encode(void *); + explicit Encode(void *); }; template<typename T> @@ -700,24 +769,29 @@ ReturnedValue value_convert(ExecutionEngine *e, const Value &v); inline int Value::toInt32() const { - if (isInteger()) + if (Q_LIKELY(integerCompatible())) return int_32(); - double d = isNumber() ? doubleValue() : toNumberImpl(); - - const double D32 = 4294967296.0; - const double D31 = D32 / 2.0; - if ((d >= -D31 && d < D31)) - return static_cast<int>(d); + if (Q_LIKELY(isDouble())) + return Double::toInt32(doubleValue()); - return Primitive::toInt32(d); + return Double::toInt32(toNumberImpl()); } inline unsigned int Value::toUInt32() const { - return (unsigned int)toInt32(); + return static_cast<unsigned int>(toInt32()); } +inline double Value::toInteger() const +{ + if (integerCompatible()) + return int_32(); + + return Primitive::toInteger(isDouble() ? doubleValue() : toNumberImpl()); +} + + } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp index f2ff5d307e..00be90b3c2 100644 --- a/src/qml/jsruntime/qv4variantobject.cpp +++ b/src/qml/jsruntime/qv4variantobject.cpp @@ -113,17 +113,17 @@ void VariantPrototype::init() defineDefaultProperty(engine()->id_toString(), method_toString, 0); } -void VariantPrototype::method_preserve(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue VariantPrototype::method_preserve(const BuiltinFunction *, CallData *callData) { - Scoped<VariantObject> o(scope, callData->thisObject.as<QV4::VariantObject>()); + VariantObject *o = callData->thisObject.as<QV4::VariantObject>(); if (o && o->d()->isScarce()) o->d()->addVmePropertyReference(); RETURN_UNDEFINED(); } -void VariantPrototype::method_destroy(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue VariantPrototype::method_destroy(const BuiltinFunction *, CallData *callData) { - Scoped<VariantObject> o(scope, callData->thisObject.as<QV4::VariantObject>()); + VariantObject *o = callData->thisObject.as<QV4::VariantObject>(); if (o) { if (o->d()->isScarce()) o->d()->addVmePropertyReference(); @@ -132,9 +132,10 @@ void VariantPrototype::method_destroy(const BuiltinFunction *, Scope &scope, Cal RETURN_UNDEFINED(); } -void VariantPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue VariantPrototype::method_toString(const BuiltinFunction *b, CallData *callData) { - Scoped<VariantObject> o(scope, callData->thisObject.as<QV4::VariantObject>()); + ExecutionEngine *v4 = b->engine(); + VariantObject *o = callData->thisObject.as<QV4::VariantObject>(); if (!o) RETURN_UNDEFINED(); QString result = o->d()->data().toString(); @@ -143,38 +144,33 @@ void VariantPrototype::method_toString(const BuiltinFunction *, Scope &scope, Ca + QLatin1String(o->d()->data().typeName()) + QLatin1Char(')'); } - scope.result = scope.engine->newString(result); + return Encode(v4->newString(result)); } -void VariantPrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue VariantPrototype::method_valueOf(const BuiltinFunction *b, CallData *callData) { - Scoped<VariantObject> o(scope, callData->thisObject.as<QV4::VariantObject>()); + VariantObject *o = callData->thisObject.as<QV4::VariantObject>(); if (o) { QVariant v = o->d()->data(); switch (v.type()) { case QVariant::Invalid: - scope.result = Encode::undefined(); - return; + return Encode::undefined(); case QVariant::String: - scope.result = scope.engine->newString(v.toString()); - return; + return Encode(b->engine()->newString(v.toString())); case QVariant::Int: - scope.result = Encode(v.toInt()); - return; + return Encode(v.toInt()); case QVariant::Double: case QVariant::UInt: - scope.result = Encode(v.toDouble()); - return; + return Encode(v.toDouble()); case QVariant::Bool: - scope.result = Encode(v.toBool()); - return; + return Encode(v.toBool()); default: if (QMetaType::typeFlags(v.userType()) & QMetaType::IsEnumeration) RETURN_RESULT(Encode(v.toInt())); break; } } - scope.result = callData->thisObject; + return callData->thisObject.asReturnedValue(); } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4variantobject_p.h b/src/qml/jsruntime/qv4variantobject_p.h index a7c6fa320c..07b3310e91 100644 --- a/src/qml/jsruntime/qv4variantobject_p.h +++ b/src/qml/jsruntime/qv4variantobject_p.h @@ -108,10 +108,10 @@ public: V4_PROTOTYPE(objectPrototype) void init(); - static void method_preserve(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_destroy(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_preserve(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_destroy(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toString(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_valueOf(const BuiltinFunction *, CallData *callData); }; } diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index fdf47e772e..adf43812cf 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -53,19 +53,16 @@ #include <private/qv4regexp_p.h> #include <private/qv4regexpobject_p.h> #include <private/qv4string_p.h> +#include <private/qv4profiling_p.h> +#include <private/qv4jscall_p.h> +#include <private/qqmljavascriptexpression_p.h> #include <iostream> #include "qv4alloca_p.h" -#undef DO_TRACE_INSTR // define to enable instruction tracing +#include <private/qv4jit_p.h> -#ifdef DO_TRACE_INSTR -# define TRACE_INSTR(I) qDebug("executing a %s\n", #I); -# define TRACE(n, str, ...) { char buf[4096]; snprintf(buf, 4096, str, __VA_ARGS__); qDebug(" %s : %s", #n, buf); } -#else -# define TRACE_INSTR(I) -# define TRACE(n, str, ...) -#endif // DO_TRACE_INSTR +#undef COUNT_INSTRUCTIONS extern "C" { @@ -146,7 +143,7 @@ Q_QML_EXPORT int qt_v4DebuggerHook(const char *json); #if QT_CONFIG(qml_debug) static int qt_v4BreakpointCount = 0; -static bool qt_v4IsDebugging = true; +static bool qt_v4IsDebugging = false; static bool qt_v4IsStepping = false; class Breakpoint @@ -169,14 +166,6 @@ public: static QVector<Breakpoint> qt_v4Breakpoints; static Breakpoint qt_v4LastStop; -static QV4::Function *qt_v4ExtractFunction(QV4::ExecutionContext *context) -{ - if (QV4::Function *function = context->getFunction()) - return function; - else - return context->engine()->globalCode; -} - static void qt_v4TriggerBreakpoint(const Breakpoint &bp, QV4::Function *function) { qt_v4LastStop = bp; @@ -223,6 +212,7 @@ int qt_v4DebuggerHook(const char *json) bp.fullName = ob.value(QLatin1String("fullName")).toString(); bp.condition = ob.value(QLatin1String("condition")).toString(); qt_v4Breakpoints.append(bp); + qt_v4IsDebugging = true; return bp.bpNumber; } @@ -231,6 +221,7 @@ int qt_v4DebuggerHook(const char *json) QString fullName = ob.value(QLatin1String("fullName")).toString(); if (qt_v4Breakpoints.last().matches(fullName, lineNumber)) { qt_v4Breakpoints.removeLast(); + qt_v4IsDebugging = !qt_v4Breakpoints.isEmpty(); return Success; } for (int i = 0; i + 1 < qt_v4Breakpoints.size(); ++i) { @@ -251,13 +242,13 @@ int qt_v4DebuggerHook(const char *json) return -NoSuchCommand; // Failure. } -static void qt_v4CheckForBreak(QV4::ExecutionContext *context) +Q_NEVER_INLINE static void qt_v4CheckForBreak(QV4::CppStackFrame *frame) { if (!qt_v4IsStepping && !qt_v4Breakpoints.size()) return; - const int lineNumber = context->d()->lineNumber; - QV4::Function *function = qt_v4ExtractFunction(context); + const int lineNumber = frame->lineNumber(); + QV4::Function *function = frame->v4Function; QString engineName = function->sourceFile(); if (engineName.isEmpty()) @@ -287,74 +278,64 @@ static void qt_v4CheckForBreak(QV4::ExecutionContext *context) } } +Q_NEVER_INLINE static void debug_slowPath(QV4::ExecutionEngine *engine) +{ + QV4::Debugging::Debugger *debugger = engine->debugger(); + if (debugger && debugger->pauseAtNextOpportunity()) + debugger->maybeBreakAtInstruction(); + if (qt_v4IsDebugging) + qt_v4CheckForBreak(engine->currentStackFrame); +} + #endif // QT_CONFIG(qml_debug) // End of debugger interface using namespace QV4; using namespace QV4::Moth; -#define MOTH_BEGIN_INSTR_COMMON(I) { \ - const InstrMeta<(int)Instr::I>::DataType &instr = InstrMeta<(int)Instr::I>::data(*genericInstr); \ - code += InstrMeta<(int)Instr::I>::Size; \ - Q_UNUSED(instr); \ - TRACE_INSTR(I) - -#ifdef MOTH_THREADED_INTERPRETER - -# define MOTH_BEGIN_INSTR(I) op_##I: \ - MOTH_BEGIN_INSTR_COMMON(I) - -# define MOTH_END_INSTR(I) } \ - genericInstr = reinterpret_cast<const Instr *>(code); \ - goto *jumpTable[genericInstr->common.instructionType]; \ - -#else - -# define MOTH_BEGIN_INSTR(I) \ - case Instr::I: \ - MOTH_BEGIN_INSTR_COMMON(I) - -# define MOTH_END_INSTR(I) } \ - continue; - -#endif - -#ifdef DO_TRACE_INSTR -Param traceParam(const Param ¶m) -{ - if (param.isConstant()) { - qDebug(" constant\n"); - } else if (param.isArgument()) { - qDebug(" argument %d@%d\n", param.index, param.scope); - } else if (param.isLocal()) { - qDebug(" local %d\n", param.index); - } else if (param.isTemp()) { - qDebug(" temp %d\n", param.index); - } else if (param.isScopedLocal()) { - qDebug(" temp %d@%d\n", param.index, param.scope); - } else { - Q_ASSERT(!"INVALID"); +#ifdef COUNT_INSTRUCTIONS +static struct InstrCount { + InstrCount() { + fprintf(stderr, "Counting instructions...\n"); + for (int i = 0; i < MOTH_NUM_INSTRUCTIONS(); ++i) + hits[i] = 0; + } + ~InstrCount() { + fprintf(stderr, "Instruction count:\n"); +#define BLAH(I) \ + fprintf(stderr, "%llu : %s\n", hits[int(Instr::Type::I)], #I); + FOR_EACH_MOTH_INSTR(BLAH) + #undef BLAH + } + quint64 hits[MOTH_NUM_INSTRUCTIONS()]; + void hit(Instr::Type i) { hits[int(i)]++; } +} instrCount; +#endif // COUNT_INSTRUCTIONS + +#define MOTH_BEGIN_INSTR_COMMON(instr) \ + { \ + INSTR_##instr(MOTH_DECODE) + +#ifdef COUNT_INSTRUCTIONS +# define MOTH_BEGIN_INSTR(instr) \ + MOTH_BEGIN_INSTR_COMMON(instr) \ + instrCount.hit(Instr::Type::instr); +#else // !COUNT_INSTRUCTIONS +# define MOTH_BEGIN_INSTR(instr) \ + MOTH_BEGIN_INSTR_COMMON(instr) +#endif // COUNT_INSTRUCTIONS + +#ifdef MOTH_COMPUTED_GOTO +#define MOTH_END_INSTR(instr) \ + MOTH_DISPATCH() \ + } +#else // !MOTH_COMPUTED_GOTO +#define MOTH_END_INSTR(instr) \ + continue; \ } - return param; -} -# define VALUE(param) (*VALUEPTR(param)) -# define VALUEPTR(param) (scopes[traceParam(param).scope].values + param.index) -#else -# define VALUE(param) (*VALUEPTR(param)) -# define VALUEPTR(param) (scopes[param.scope].values + param.index) #endif -// ### add write barrier here -#define STOREVALUE(param, value) { \ - QV4::ReturnedValue tmp = (value); \ - if (engine->hasException) \ - goto catchException; \ - if (Q_LIKELY(!engine->writeBarrierActive || !scopes[param.scope].base)) { \ - VALUE(param) = tmp; \ - } else { \ - QV4::WriteBarrier::write(engine, scopes[param.scope].base, VALUEPTR(param), QV4::Value::fromReturnedValue(tmp)); \ - } \ -} +#define STACK_VALUE(temp) stack[temp] // qv4scopedvalue_p.h also defines a CHECK_EXCEPTION macro #ifdef CHECK_EXCEPTION @@ -364,617 +345,1038 @@ Param traceParam(const Param ¶m) if (engine->hasException) \ goto catchException -QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) +static inline Heap::CallContext *getScope(Value *stack, int level) { -#ifdef DO_TRACE_INSTR - qDebug("Starting VME with context=%p and code=%p", context, code); -#endif // DO_TRACE_INSTR + Heap::ExecutionContext *scope = static_cast<ExecutionContext &>(stack[CallData::Context]).d(); + while (level > 0) { + --level; + scope = scope->outer; + } + Q_ASSERT(scope); + return static_cast<Heap::CallContext *>(scope); +} - qt_v4ResolvePendingBreakpointsHook(); +static inline const QV4::Value &constant(Function *function, int index) +{ + return function->compilationUnit->constants[index]; +} -#ifdef MOTH_THREADED_INTERPRETER -#define MOTH_INSTR_ADDR(I, FMT) &&op_##I, - static void *jumpTable[] = { - FOR_EACH_MOTH_INSTR(MOTH_INSTR_ADDR) - }; -#undef MOTH_INSTR_ADDR -#endif - QV4::Value *stack = 0; - unsigned stackSize = 0; +static bool compareEqual(Value lhs, Value rhs) +{ + redo: + if (lhs.asReturnedValue() == rhs.asReturnedValue()) + return !lhs.isNaN(); + + int lt = lhs.quickType(); + int rt = rhs.quickType(); + if (rt < lt) { + qSwap(lhs, rhs); + qSwap(lt, rt); + } - const uchar *exceptionHandler = 0; + switch (lt) { + case Value::QT_ManagedOrUndefined: + if (lhs.isUndefined()) + return rhs.isNullOrUndefined(); + Q_FALLTHROUGH(); + case Value::QT_ManagedOrUndefined1: + case Value::QT_ManagedOrUndefined2: + case Value::QT_ManagedOrUndefined3: + // LHS: Managed + switch (rt) { + case Value::QT_ManagedOrUndefined: + if (rhs.isUndefined()) + return false; + Q_FALLTHROUGH(); + case Value::QT_ManagedOrUndefined1: + case Value::QT_ManagedOrUndefined2: + case Value::QT_ManagedOrUndefined3: { + // RHS: Managed + Heap::Base *l = lhs.m(); + Heap::Base *r = rhs.m(); + Q_ASSERT(l); + Q_ASSERT(r); + if (l->vtable()->isString == r->vtable()->isString) + return static_cast<QV4::Managed &>(lhs).isEqualTo(&static_cast<QV4::Managed &>(rhs)); + if (l->vtable()->isString) { + rhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(rhs), PREFERREDTYPE_HINT)); + break; + } else { + Q_ASSERT(r->vtable()->isString); + lhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT)); + break; + } + return false; + } + case Value::QT_Empty: + Q_UNREACHABLE(); + case Value::QT_Null: + return false; + case Value::QT_Bool: + case Value::QT_Int: + rhs = Primitive::fromDouble(rhs.int_32()); + // fall through + default: // double + if (lhs.m()->vtable()->isString) + return RuntimeHelpers::toNumber(lhs) == rhs.doubleValue(); + else + lhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT)); + } + goto redo; + case Value::QT_Empty: + Q_UNREACHABLE(); + case Value::QT_Null: + return rhs.isNull(); + case Value::QT_Bool: + case Value::QT_Int: + switch (rt) { + case Value::QT_ManagedOrUndefined: + case Value::QT_ManagedOrUndefined1: + case Value::QT_ManagedOrUndefined2: + case Value::QT_ManagedOrUndefined3: + case Value::QT_Empty: + case Value::QT_Null: + Q_UNREACHABLE(); + case Value::QT_Bool: + case Value::QT_Int: + return lhs.int_32() == rhs.int_32(); + default: // double + return lhs.int_32() == rhs.doubleValue(); + } + default: // double + Q_ASSERT(rhs.isDouble()); + return lhs.doubleValue() == rhs.doubleValue(); + } +} - QV4::Scope scope(engine); - engine->current->lineNumber = -1; +static bool compareEqualInt(Value &accumulator, Value lhs, int rhs) +{ + redo: + switch (lhs.quickType()) { + case Value::QT_ManagedOrUndefined: + if (lhs.isUndefined()) + return false; + Q_FALLTHROUGH(); + case Value::QT_ManagedOrUndefined1: + case Value::QT_ManagedOrUndefined2: + case Value::QT_ManagedOrUndefined3: + // LHS: Managed + if (lhs.m()->vtable()->isString) + return RuntimeHelpers::stringToNumber(static_cast<String &>(lhs).toQString()) == rhs; + accumulator = lhs; + lhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(accumulator), PREFERREDTYPE_HINT)); + goto redo; + case Value::QT_Empty: + Q_UNREACHABLE(); + case Value::QT_Null: + return false; + case Value::QT_Bool: + case Value::QT_Int: + return lhs.int_32() == rhs; + default: // double + return lhs.doubleValue() == rhs; + } +} -#ifdef DO_TRACE_INSTR - qDebug("Starting VME with context=%p and code=%p", context, code); -#endif // DO_TRACE_INSTR +#define STORE_IP() frame.instructionPointer = int(code - codeStart); +#define STORE_ACC() accumulator = acc; +#define ACC Primitive::fromReturnedValue(acc) +#define VALUE_TO_INT(i, val) \ + int i; \ + do { \ + if (Q_LIKELY(val.integerCompatible())) { \ + i = val.int_32(); \ + } else { \ + double d; \ + if (val.isDouble()) \ + d = val.doubleValue(); \ + else { \ + d = val.toNumberImpl(); \ + CHECK_EXCEPTION; \ + } \ + i = Double::toInt32(d); \ + } \ + } while (false) + +QV4::ReturnedValue VME::exec(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc) +{ + qt_v4ResolvePendingBreakpointsHook(); + ExecutionEngine *engine; + Value *stack; + CppStackFrame frame; + frame.originalArguments = argv; + frame.originalArgumentsCount = argc; + Function *function; - // setup lookup scopes - int scopeDepth = 0; { - QV4::Heap::ExecutionContext *scope = engine->current; - while (scope) { - ++scopeDepth; - scope = scope->outer; + Heap::ExecutionContext *scope; + + quintptr d = reinterpret_cast<quintptr>(fo); + if (d & 0x1) { + // we don't have a FunctionObject, but a ExecData + ExecData *data = reinterpret_cast<ExecData *>(d - 1); + function = data->function; + scope = data->scope->d(); + fo = nullptr; + } else { + function = fo->function(); + scope = fo->scope(); } + + engine = function->internalClass->engine; + + stack = engine->jsStackTop; + CallData *callData = reinterpret_cast<CallData *>(stack); + callData->function = fo ? fo->asReturnedValue() : Encode::undefined(); + callData->context = scope; + callData->accumulator = Encode::undefined(); + callData->thisObject = thisObject ? *thisObject : Primitive::undefinedValue(); + if (argc > int(function->nFormals)) + argc = int(function->nFormals); + callData->setArgc(argc); + + int jsStackFrameSize = offsetof(CallData, args)/sizeof(Value) + function->compiledFunction->nRegisters; + engine->jsStackTop += jsStackFrameSize; + memcpy(callData->args, argv, argc*sizeof(Value)); + for (Value *v = callData->args + argc; v < engine->jsStackTop; ++v) + *v = Encode::undefined(); + + frame.parent = engine->currentStackFrame; + frame.v4Function = function; + frame.instructionPointer = 0; + frame.jsFrame = callData; + engine->currentStackFrame = &frame; } + CHECK_STACK_LIMITS(engine); - struct Scopes { - QV4::Value *values; - QV4::Heap::Base *base; // non 0 if a write barrier is required - }; - Q_ALLOCA_VAR(Scopes, scopes, sizeof(Scopes)*(2 + 2*scopeDepth)); - { - scopes[0] = { const_cast<QV4::Value *>(static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)->constants), 0 }; - // stack gets setup in push instruction - scopes[1] = { 0, 0 }; - QV4::Heap::ExecutionContext *scope = engine->current; - int i = 0; - while (scope) { - if (scope->type == QV4::Heap::ExecutionContext::Type_SimpleCallContext) { - QV4::Heap::SimpleCallContext *cc = static_cast<QV4::Heap::SimpleCallContext *>(scope); - scopes[2*i + 2] = { cc->callData->args, 0 }; - scopes[2*i + 3] = { 0, 0 }; - } else if (scope->type == QV4::Heap::ExecutionContext::Type_CallContext) { - QV4::Heap::CallContext *cc = static_cast<QV4::Heap::CallContext *>(scope); - scopes[2*i + 2] = { cc->callData->args, cc }; - scopes[2*i + 3] = { cc->locals.values, cc }; - } else { - scopes[2*i + 2] = { 0, 0 }; - scopes[2*i + 3] = { 0, 0 }; - } - ++i; - scope = scope->outer; - } + Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling + QV4::Debugging::Debugger *debugger = engine->debugger(); + + const uchar *exceptionHandler = 0; + + QV4::Value &accumulator = frame.jsFrame->accumulator; + QV4::ReturnedValue acc = Encode::undefined(); + +#ifdef V4_ENABLE_JIT + static const bool forceInterpreter = qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER"); + if (function->jittedCode == nullptr) { + if (ExecutionEngine::canJIT() && debugger == nullptr && !forceInterpreter) + QV4::JIT::BaselineJIT(function).generate(); } +#endif // V4_ENABLE_JIT + + if (debugger) + debugger->enteringFunction(); + if (function->jittedCode != nullptr && debugger == nullptr) { + acc = function->jittedCode(&frame, engine); + } else { + // interpreter + const uchar *code = function->codeData; + const uchar *codeStart = code; + + MOTH_JUMP_TABLE; for (;;) { - const Instr *genericInstr = reinterpret_cast<const Instr *>(code); -#ifdef MOTH_THREADED_INTERPRETER - goto *jumpTable[genericInstr->common.instructionType]; -#else - switch (genericInstr->common.instructionType) { -#endif + MOTH_DISPATCH() + Q_UNREACHABLE(); // only reached when the dispatch doesn't jump somewhere + + MOTH_BEGIN_INSTR(LoadConst) + acc = constant(function, index).asReturnedValue(); + MOTH_END_INSTR(LoadConst) + + MOTH_BEGIN_INSTR(LoadNull) + acc = Encode::null(); + MOTH_END_INSTR(LoadNull) + + MOTH_BEGIN_INSTR(LoadZero) + acc = Encode(static_cast<int>(0)); + MOTH_END_INSTR(LoadZero) - MOTH_BEGIN_INSTR(Move) - VALUE(instr.result) = VALUE(instr.source); - MOTH_END_INSTR(Move) + MOTH_BEGIN_INSTR(LoadTrue) + acc = Encode(true); + MOTH_END_INSTR(LoadTrue) + + MOTH_BEGIN_INSTR(LoadFalse) + acc = Encode(false); + MOTH_END_INSTR(LoadFalse) + + MOTH_BEGIN_INSTR(LoadUndefined) + acc = Encode::undefined(); + MOTH_END_INSTR(LoadUndefined) + + MOTH_BEGIN_INSTR(LoadInt) + acc = Encode(value); + MOTH_END_INSTR(LoadInt) MOTH_BEGIN_INSTR(MoveConst) - VALUE(instr.result) = instr.source; + STACK_VALUE(destTemp) = constant(function, constIndex); MOTH_END_INSTR(MoveConst) - MOTH_BEGIN_INSTR(SwapTemps) - qSwap(VALUE(instr.left), VALUE(instr.right)); - MOTH_END_INSTR(MoveTemp) + MOTH_BEGIN_INSTR(LoadReg) + acc = STACK_VALUE(reg).asReturnedValue(); + MOTH_END_INSTR(LoadReg) + + MOTH_BEGIN_INSTR(StoreReg) + STACK_VALUE(reg) = acc; + MOTH_END_INSTR(StoreReg) + + MOTH_BEGIN_INSTR(MoveReg) + STACK_VALUE(destReg) = STACK_VALUE(srcReg); + MOTH_END_INSTR(MoveReg) + + MOTH_BEGIN_INSTR(LoadLocal) + auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m()); + acc = cc->locals[index].asReturnedValue(); + MOTH_END_INSTR(LoadLocal) + + MOTH_BEGIN_INSTR(StoreLocal) + CHECK_EXCEPTION; + auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m()); + QV4::WriteBarrier::write(engine, cc, cc->locals.values + index, ACC); + MOTH_END_INSTR(StoreLocal) + + MOTH_BEGIN_INSTR(LoadScopedLocal) + auto cc = getScope(stack, scope); + acc = cc->locals[index].asReturnedValue(); + MOTH_END_INSTR(LoadScopedLocal) + + MOTH_BEGIN_INSTR(StoreScopedLocal) + CHECK_EXCEPTION; + auto cc = getScope(stack, scope); + QV4::WriteBarrier::write(engine, cc, cc->locals.values + index, ACC); + MOTH_END_INSTR(StoreScopedLocal) MOTH_BEGIN_INSTR(LoadRuntimeString) -// TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData()); - VALUE(instr.result) = engine->current->compilationUnit->runtimeStrings[instr.stringId]; + acc = function->compilationUnit->runtimeStrings[stringId]->asReturnedValue(); MOTH_END_INSTR(LoadRuntimeString) MOTH_BEGIN_INSTR(LoadRegExp) -// TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData()); - Heap::RegExpObject *ro = engine->newRegExpObject( - static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit) - ->runtimeRegularExpressions[instr.regExpId].as<RegExp>()); - VALUE(instr.result) = ro; + acc = Runtime::method_regexpLiteral(engine, regExpId); MOTH_END_INSTR(LoadRegExp) MOTH_BEGIN_INSTR(LoadClosure) - STOREVALUE(instr.result, Runtime::method_closure(engine, instr.value)); + acc = Runtime::method_closure(engine, value); MOTH_END_INSTR(LoadClosure) MOTH_BEGIN_INSTR(LoadName) - TRACE(inline, "property name = %s", runtimeStrings[instr.name]->toQString().toUtf8().constData()); - STOREVALUE(instr.result, Runtime::method_getActivationProperty(engine, instr.name)); + STORE_IP(); + acc = Runtime::method_loadName(engine, name); + CHECK_EXCEPTION; MOTH_END_INSTR(LoadName) - MOTH_BEGIN_INSTR(GetGlobalLookup) - QV4::Lookup *l = engine->current->lookups + instr.index; - STOREVALUE(instr.result, l->globalGetter(l, engine)); - MOTH_END_INSTR(GetGlobalLookup) + MOTH_BEGIN_INSTR(LoadGlobalLookup) + QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; + acc = l->globalGetter(l, engine); + CHECK_EXCEPTION; + MOTH_END_INSTR(LoadGlobalLookup) + + MOTH_BEGIN_INSTR(StoreNameStrict) + STORE_IP(); + STORE_ACC(); + Runtime::method_storeNameStrict(engine, name, accumulator); + CHECK_EXCEPTION; + MOTH_END_INSTR(StoreNameStrict) - MOTH_BEGIN_INSTR(StoreName) - TRACE(inline, "property name = %s", runtimeStrings[instr.name]->toQString().toUtf8().constData()); - Runtime::method_setActivationProperty(engine, instr.name, VALUE(instr.source)); + MOTH_BEGIN_INSTR(StoreNameSloppy) + STORE_IP(); + STORE_ACC(); + Runtime::method_storeNameSloppy(engine, name, accumulator); CHECK_EXCEPTION; - MOTH_END_INSTR(StoreName) + MOTH_END_INSTR(StoreNameSloppy) MOTH_BEGIN_INSTR(LoadElement) - STOREVALUE(instr.result, Runtime::method_getElement(engine, VALUE(instr.base), VALUE(instr.index))); + STORE_IP(); + acc = Runtime::method_loadElement(engine, STACK_VALUE(base), STACK_VALUE(index)); + CHECK_EXCEPTION; MOTH_END_INSTR(LoadElement) - MOTH_BEGIN_INSTR(LoadElementLookup) - QV4::Lookup *l = engine->current->lookups + instr.lookup; - STOREVALUE(instr.result, l->indexedGetter(l, engine, VALUE(instr.base), VALUE(instr.index))); - MOTH_END_INSTR(LoadElementLookup) + MOTH_BEGIN_INSTR(LoadElementA) + STORE_IP(); + STORE_ACC(); + acc = Runtime::method_loadElement(engine, STACK_VALUE(base), accumulator); + CHECK_EXCEPTION; + MOTH_END_INSTR(LoadElementA) MOTH_BEGIN_INSTR(StoreElement) - Runtime::method_setElement(engine, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); + STORE_IP(); + STORE_ACC(); + if (!Runtime::method_storeElement(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator) && function->isStrict()) + engine->throwTypeError(); CHECK_EXCEPTION; MOTH_END_INSTR(StoreElement) - MOTH_BEGIN_INSTR(StoreElementLookup) - QV4::Lookup *l = engine->current->lookups + instr.lookup; - l->indexedSetter(l, engine, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); - CHECK_EXCEPTION; - MOTH_END_INSTR(StoreElementLookup) - MOTH_BEGIN_INSTR(LoadProperty) - STOREVALUE(instr.result, Runtime::method_getProperty(engine, VALUE(instr.base), instr.name)); + STORE_IP(); + acc = Runtime::method_loadProperty(engine, STACK_VALUE(base), name); + CHECK_EXCEPTION; MOTH_END_INSTR(LoadProperty) + MOTH_BEGIN_INSTR(LoadPropertyA) + STORE_IP(); + STORE_ACC(); + acc = Runtime::method_loadProperty(engine, accumulator, name); + CHECK_EXCEPTION; + MOTH_END_INSTR(LoadPropertyA) + MOTH_BEGIN_INSTR(GetLookup) - QV4::Lookup *l = engine->current->lookups + instr.index; - STOREVALUE(instr.result, l->getter(l, engine, VALUE(instr.base))); + STORE_IP(); + QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; + acc = l->getter(l, engine, STACK_VALUE(base)); + CHECK_EXCEPTION; MOTH_END_INSTR(GetLookup) + MOTH_BEGIN_INSTR(GetLookupA) + STORE_IP(); + STORE_ACC(); + QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; + acc = l->getter(l, engine, accumulator); + CHECK_EXCEPTION; + MOTH_END_INSTR(GetLookupA) + MOTH_BEGIN_INSTR(StoreProperty) - Runtime::method_setProperty(engine, VALUE(instr.base), instr.name, VALUE(instr.source)); + STORE_IP(); + STORE_ACC(); + if (!Runtime::method_storeProperty(engine, STACK_VALUE(base), name, accumulator) && function->isStrict()) + engine->throwTypeError(); CHECK_EXCEPTION; MOTH_END_INSTR(StoreProperty) MOTH_BEGIN_INSTR(SetLookup) - QV4::Lookup *l = engine->current->lookups + instr.index; - l->setter(l, engine, VALUE(instr.base), VALUE(instr.source)); + STORE_IP(); + STORE_ACC(); + QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; + if (!l->setter(l, engine, STACK_VALUE(base), accumulator) && function->isStrict()) + engine->throwTypeError(); CHECK_EXCEPTION; MOTH_END_INSTR(SetLookup) - MOTH_BEGIN_INSTR(StoreQObjectProperty) - Runtime::method_setQmlQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source)); - CHECK_EXCEPTION; - MOTH_END_INSTR(StoreQObjectProperty) - - MOTH_BEGIN_INSTR(LoadQObjectProperty) - STOREVALUE(instr.result, Runtime::method_getQmlQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired)); - MOTH_END_INSTR(LoadQObjectProperty) - MOTH_BEGIN_INSTR(StoreScopeObjectProperty) - Runtime::method_setQmlScopeObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source)); + STORE_ACC(); + Runtime::method_storeQmlScopeObjectProperty(engine, STACK_VALUE(base), propertyIndex, accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(StoreScopeObjectProperty) MOTH_BEGIN_INSTR(LoadScopeObjectProperty) - STOREVALUE(instr.result, Runtime::method_getQmlScopeObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired)); + STORE_IP(); + acc = Runtime::method_loadQmlScopeObjectProperty(engine, STACK_VALUE(base), propertyIndex, captureRequired); + CHECK_EXCEPTION; MOTH_END_INSTR(LoadScopeObjectProperty) MOTH_BEGIN_INSTR(StoreContextObjectProperty) - Runtime::method_setQmlContextObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source)); + STORE_IP(); + STORE_ACC(); + Runtime::method_storeQmlContextObjectProperty(engine, STACK_VALUE(base), propertyIndex, accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(StoreContextObjectProperty) MOTH_BEGIN_INSTR(LoadContextObjectProperty) - STOREVALUE(instr.result, Runtime::method_getQmlContextObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired)); + STORE_IP(); + acc = Runtime::method_loadQmlContextObjectProperty(engine, STACK_VALUE(base), propertyIndex, captureRequired); + CHECK_EXCEPTION; MOTH_END_INSTR(LoadContextObjectProperty) MOTH_BEGIN_INSTR(LoadIdObject) - STOREVALUE(instr.result, Runtime::method_getQmlIdObject(engine, VALUE(instr.base), instr.index)); + STORE_IP(); + acc = Runtime::method_loadQmlIdObject(engine, STACK_VALUE(base), index); + CHECK_EXCEPTION; MOTH_END_INSTR(LoadIdObject) - MOTH_BEGIN_INSTR(LoadAttachedQObjectProperty) - STOREVALUE(instr.result, Runtime::method_getQmlAttachedProperty(engine, instr.attachedPropertiesId, instr.propertyIndex)); - MOTH_END_INSTR(LoadAttachedQObjectProperty) - - MOTH_BEGIN_INSTR(LoadSingletonQObjectProperty) - STOREVALUE(instr.result, Runtime::method_getQmlSingletonQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired)); - MOTH_END_INSTR(LoadSingletonQObjectProperty) - - MOTH_BEGIN_INSTR(Push) - TRACE(inline, "stack size: %u", instr.value); - stackSize = instr.value; - stack = scope.alloc(stackSize); - scopes[1].values = stack; - MOTH_END_INSTR(Push) - MOTH_BEGIN_INSTR(CallValue) -#if 0 //def DO_TRACE_INSTR - if (Debugging::Debugger *debugger = context->engine()->debugger) { - if (QV4::FunctionObject *o = (VALUE(instr.dest)).asFunctionObject()) { - if (Debugging::FunctionDebugInfo *info = debugger->debugInfo(o)) { - QString n = debugger->name(o); - std::cerr << "*** Call to \"" << (n.isNull() ? "<no name>" : qPrintable(n)) << "\" defined @" << info->startLine << ":" << info->startColumn << std::endl; - } - } + STORE_IP(); + STORE_ACC(); + Value func = Value::fromReturnedValue(acc); + if (Q_UNLIKELY(!func.isFunctionObject())) { + acc = engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow())); + goto catchException; } -#endif // DO_TRACE_INSTR - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, Runtime::method_callValue(engine, VALUE(instr.dest), callData)); + acc = static_cast<const FunctionObject &>(func).call(nullptr, stack + argv, argc); + CHECK_EXCEPTION; MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallProperty) - TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(engine)->toQString().toUtf8().constData()); - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, Runtime::method_callProperty(engine, instr.name, callData)); + STORE_IP(); + acc = Runtime::method_callProperty(engine, stack + base, name, stack + argv, argc); + CHECK_EXCEPTION; MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallPropertyLookup) - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, Runtime::method_callPropertyLookup(engine, instr.lookupIndex, callData)); - MOTH_END_INSTR(CallPropertyLookup) + STORE_IP(); + Lookup *l = function->compilationUnit->runtimeLookups + lookupIndex; + // ok to have the value on the stack here + Value f = Value::fromReturnedValue(l->getter(l, engine, stack[base])); + + if (Q_UNLIKELY(!f.isFunctionObject())) { + acc = engine->throwTypeError(); + goto catchException; + } - MOTH_BEGIN_INSTR(CallScopeObjectProperty) - TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(engine)->toQString().toUtf8().constData()); - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, Runtime::method_callQmlScopeObjectProperty(engine, instr.index, callData)); - MOTH_END_INSTR(CallScopeObjectProperty) - - MOTH_BEGIN_INSTR(CallContextObjectProperty) - TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(engine)->toQString().toUtf8().constData()); - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, Runtime::method_callQmlContextObjectProperty(engine, instr.index, callData)); - MOTH_END_INSTR(CallContextObjectProperty) + acc = static_cast<FunctionObject &>(f).call(stack + base, stack + argv, argc); + CHECK_EXCEPTION; + MOTH_END_INSTR(CallPropertyLookup) MOTH_BEGIN_INSTR(CallElement) - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, Runtime::method_callElement(engine, VALUE(instr.index), callData)); + STORE_IP(); + acc = Runtime::method_callElement(engine, stack + base, STACK_VALUE(index), stack + argv, argc); + CHECK_EXCEPTION; MOTH_END_INSTR(CallElement) - MOTH_BEGIN_INSTR(CallActivationProperty) - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, Runtime::method_callActivationProperty(engine, instr.name, callData)); - MOTH_END_INSTR(CallActivationProperty) + MOTH_BEGIN_INSTR(CallName) + STORE_IP(); + acc = Runtime::method_callName(engine, name, stack + argv, argc); + CHECK_EXCEPTION; + MOTH_END_INSTR(CallName) + + MOTH_BEGIN_INSTR(CallPossiblyDirectEval) + STORE_IP(); + acc = Runtime::method_callPossiblyDirectEval(engine, stack + argv, argc); + CHECK_EXCEPTION; + MOTH_END_INSTR(CallPossiblyDirectEval) MOTH_BEGIN_INSTR(CallGlobalLookup) - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, Runtime::method_callGlobalLookup(engine, instr.index, callData)); + STORE_IP(); + acc = Runtime::method_callGlobalLookup(engine, index, stack + argv, argc); + CHECK_EXCEPTION; MOTH_END_INSTR(CallGlobalLookup) MOTH_BEGIN_INSTR(SetExceptionHandler) - exceptionHandler = instr.offset ? ((const uchar *)&instr.offset) + instr.offset : 0; + exceptionHandler = offset ? code + offset : nullptr; MOTH_END_INSTR(SetExceptionHandler) - MOTH_BEGIN_INSTR(CallBuiltinThrow) - Runtime::method_throwException(engine, VALUE(instr.arg)); + MOTH_BEGIN_INSTR(ThrowException) + STORE_IP(); + STORE_ACC(); + Runtime::method_throwException(engine, accumulator); + goto catchException; + MOTH_END_INSTR(ThrowException) + + MOTH_BEGIN_INSTR(GetException) + acc = engine->hasException ? engine->exceptionValue->asReturnedValue() + : Primitive::emptyValue().asReturnedValue(); + engine->hasException = false; + MOTH_END_INSTR(HasException) + + MOTH_BEGIN_INSTR(SetException) + *engine->exceptionValue = acc; + engine->hasException = true; + MOTH_END_INSTR(SetException) + + MOTH_BEGIN_INSTR(PushCatchContext) + STACK_VALUE(reg) = STACK_VALUE(CallData::Context); + ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); + STACK_VALUE(CallData::Context) = Runtime::method_createCatchContext(c, name); + MOTH_END_INSTR(PushCatchContext) + + MOTH_BEGIN_INSTR(CreateCallContext) + stack[CallData::Context] = ExecutionContext::newCallContext(&frame); + MOTH_END_INSTR(CreateCallContext) + + MOTH_BEGIN_INSTR(PushWithContext) + STORE_IP(); + STORE_ACC(); + accumulator = accumulator.toObject(engine); + CHECK_EXCEPTION; + STACK_VALUE(reg) = STACK_VALUE(CallData::Context); + ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); + STACK_VALUE(CallData::Context) = Runtime::method_createWithContext(c, accumulator); + MOTH_END_INSTR(PushWithContext) + + MOTH_BEGIN_INSTR(PopContext) + STACK_VALUE(CallData::Context) = STACK_VALUE(reg); + MOTH_END_INSTR(PopContext) + + MOTH_BEGIN_INSTR(ForeachIteratorObject) + STORE_ACC(); + acc = Runtime::method_foreachIterator(engine, accumulator); CHECK_EXCEPTION; - MOTH_END_INSTR(CallBuiltinThrow) + MOTH_END_INSTR(ForeachIteratorObject) - MOTH_BEGIN_INSTR(CallBuiltinUnwindException) - STOREVALUE(instr.result, Runtime::method_unwindException(engine)); - MOTH_END_INSTR(CallBuiltinUnwindException) + MOTH_BEGIN_INSTR(ForeachNextPropertyName) + STORE_ACC(); + acc = Runtime::method_foreachNextPropertyName(accumulator); + CHECK_EXCEPTION; + MOTH_END_INSTR(ForeachNextPropertyName) + + MOTH_BEGIN_INSTR(DeleteMember) + if (!Runtime::method_deleteMember(engine, STACK_VALUE(base), member)) { + if (function->isStrict()) { + STORE_IP(); + engine->throwTypeError(); + goto catchException; + } + acc = Encode(false); + } else { + acc = Encode(true); + } + MOTH_END_INSTR(DeleteMember) + + MOTH_BEGIN_INSTR(DeleteSubscript) + if (!Runtime::method_deleteElement(engine, STACK_VALUE(base), STACK_VALUE(index))) { + if (function->isStrict()) { + STORE_IP(); + engine->throwTypeError(); + goto catchException; + } + acc = Encode(false); + } else { + acc = Encode(true); + } + MOTH_END_INSTR(DeleteSubscript) + + MOTH_BEGIN_INSTR(DeleteName) + if (!Runtime::method_deleteName(engine, name)) { + if (function->isStrict()) { + STORE_IP(); + QString n = function->compilationUnit->runtimeStrings[name]->toQString(); + engine->throwSyntaxError(QStringLiteral("Can't delete property %1").arg(n)); + goto catchException; + } + acc = Encode(false); + } else { + acc = Encode(true); + } + MOTH_END_INSTR(DeleteName) + + MOTH_BEGIN_INSTR(TypeofName) + acc = Runtime::method_typeofName(engine, name); + MOTH_END_INSTR(TypeofName) + + MOTH_BEGIN_INSTR(TypeofValue) + STORE_ACC(); + acc = Runtime::method_typeofValue(engine, accumulator); + MOTH_END_INSTR(TypeofValue) + + MOTH_BEGIN_INSTR(DeclareVar) + Runtime::method_declareVar(engine, isDeletable, varName); + MOTH_END_INSTR(DeclareVar) + + MOTH_BEGIN_INSTR(DefineArray) + QV4::Value *arguments = stack + args; + acc = Runtime::method_arrayLiteral(engine, arguments, argc); + MOTH_END_INSTR(DefineArray) + + MOTH_BEGIN_INSTR(DefineObjectLiteral) + QV4::Value *arguments = stack + args; + acc = Runtime::method_objectLiteral(engine, arguments, internalClassId, arrayValueCount, arrayGetterSetterCountAndFlags); + MOTH_END_INSTR(DefineObjectLiteral) + + MOTH_BEGIN_INSTR(CreateMappedArgumentsObject) + acc = Runtime::method_createMappedArgumentsObject(engine); + MOTH_END_INSTR(CreateMappedArgumentsObject) + + MOTH_BEGIN_INSTR(CreateUnmappedArgumentsObject) + acc = Runtime::method_createUnmappedArgumentsObject(engine); + MOTH_END_INSTR(CreateUnmappedArgumentsObject) + + MOTH_BEGIN_INSTR(ConvertThisToObject) + Value *t = &stack[CallData::This]; + if (!t->isObject()) { + if (t->isNullOrUndefined()) { + *t = engine->globalObject->asReturnedValue(); + } else { + *t = t->toObject(engine)->asReturnedValue(); + CHECK_EXCEPTION; + } + } + MOTH_END_INSTR(ConvertThisToObject) - MOTH_BEGIN_INSTR(CallBuiltinPushCatchScope) - Runtime::method_pushCatchScope(static_cast<QV4::NoThrowEngine*>(engine), instr.name); - MOTH_END_INSTR(CallBuiltinPushCatchScope) - - MOTH_BEGIN_INSTR(CallBuiltinPushScope) - Runtime::method_pushWithScope(VALUE(instr.arg), static_cast<QV4::NoThrowEngine*>(engine)); - CHECK_EXCEPTION; - MOTH_END_INSTR(CallBuiltinPushScope) - - MOTH_BEGIN_INSTR(CallBuiltinPopScope) - Runtime::method_popScope(static_cast<QV4::NoThrowEngine*>(engine)); - MOTH_END_INSTR(CallBuiltinPopScope) - - MOTH_BEGIN_INSTR(CallBuiltinForeachIteratorObject) - STOREVALUE(instr.result, Runtime::method_foreachIterator(engine, VALUE(instr.arg))); - MOTH_END_INSTR(CallBuiltinForeachIteratorObject) - - MOTH_BEGIN_INSTR(CallBuiltinForeachNextPropertyName) - STOREVALUE(instr.result, Runtime::method_foreachNextPropertyName(VALUE(instr.arg))); - MOTH_END_INSTR(CallBuiltinForeachNextPropertyName) - - MOTH_BEGIN_INSTR(CallBuiltinDeleteMember) - STOREVALUE(instr.result, Runtime::method_deleteMember(engine, VALUE(instr.base), instr.member)); - MOTH_END_INSTR(CallBuiltinDeleteMember) - - MOTH_BEGIN_INSTR(CallBuiltinDeleteSubscript) - STOREVALUE(instr.result, Runtime::method_deleteElement(engine, VALUE(instr.base), VALUE(instr.index))); - MOTH_END_INSTR(CallBuiltinDeleteSubscript) - - MOTH_BEGIN_INSTR(CallBuiltinDeleteName) - STOREVALUE(instr.result, Runtime::method_deleteName(engine, instr.name)); - MOTH_END_INSTR(CallBuiltinDeleteName) - - MOTH_BEGIN_INSTR(CallBuiltinTypeofScopeObjectProperty) - STOREVALUE(instr.result, Runtime::method_typeofScopeObjectProperty(engine, VALUE(instr.base), instr.index)); - MOTH_END_INSTR(CallBuiltinTypeofMember) - - MOTH_BEGIN_INSTR(CallBuiltinTypeofContextObjectProperty) - STOREVALUE(instr.result, Runtime::method_typeofContextObjectProperty(engine, VALUE(instr.base), instr.index)); - MOTH_END_INSTR(CallBuiltinTypeofMember) - - MOTH_BEGIN_INSTR(CallBuiltinTypeofMember) - STOREVALUE(instr.result, Runtime::method_typeofMember(engine, VALUE(instr.base), instr.member)); - MOTH_END_INSTR(CallBuiltinTypeofMember) - - MOTH_BEGIN_INSTR(CallBuiltinTypeofSubscript) - STOREVALUE(instr.result, Runtime::method_typeofElement(engine, VALUE(instr.base), VALUE(instr.index))); - MOTH_END_INSTR(CallBuiltinTypeofSubscript) - - MOTH_BEGIN_INSTR(CallBuiltinTypeofName) - STOREVALUE(instr.result, Runtime::method_typeofName(engine, instr.name)); - MOTH_END_INSTR(CallBuiltinTypeofName) - - MOTH_BEGIN_INSTR(CallBuiltinTypeofValue) - STOREVALUE(instr.result, Runtime::method_typeofValue(engine, VALUE(instr.value))); - MOTH_END_INSTR(CallBuiltinTypeofValue) - - MOTH_BEGIN_INSTR(CallBuiltinDeclareVar) - Runtime::method_declareVar(engine, instr.isDeletable, instr.varName); - MOTH_END_INSTR(CallBuiltinDeclareVar) - - MOTH_BEGIN_INSTR(CallBuiltinDefineArray) - Q_ASSERT(instr.args + instr.argc <= stackSize); - QV4::Value *args = stack + instr.args; - STOREVALUE(instr.result, Runtime::method_arrayLiteral(engine, args, instr.argc)); - MOTH_END_INSTR(CallBuiltinDefineArray) - - MOTH_BEGIN_INSTR(CallBuiltinDefineObjectLiteral) - QV4::Value *args = stack + instr.args; - STOREVALUE(instr.result, Runtime::method_objectLiteral(engine, args, instr.internalClassId, instr.arrayValueCount, instr.arrayGetterSetterCountAndFlags)); - MOTH_END_INSTR(CallBuiltinDefineObjectLiteral) - - MOTH_BEGIN_INSTR(CallBuiltinSetupArgumentsObject) - STOREVALUE(instr.result, Runtime::method_setupArgumentsObject(engine)); - MOTH_END_INSTR(CallBuiltinSetupArgumentsObject) - - MOTH_BEGIN_INSTR(CallBuiltinConvertThisToObject) - Runtime::method_convertThisToObject(engine); - CHECK_EXCEPTION; - MOTH_END_INSTR(CallBuiltinConvertThisToObject) - - MOTH_BEGIN_INSTR(CreateValue) - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, Runtime::method_constructValue(engine, VALUE(instr.func), callData)); - MOTH_END_INSTR(CreateValue) - - MOTH_BEGIN_INSTR(CreateProperty) - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, Runtime::method_constructProperty(engine, instr.name, callData)); - MOTH_END_INSTR(CreateProperty) - - MOTH_BEGIN_INSTR(ConstructPropertyLookup) - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, Runtime::method_constructPropertyLookup(engine, instr.index, callData)); - MOTH_END_INSTR(ConstructPropertyLookup) - - MOTH_BEGIN_INSTR(CreateActivationProperty) - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, Runtime::method_constructActivationProperty(engine, instr.name, callData)); - MOTH_END_INSTR(CreateActivationProperty) - - MOTH_BEGIN_INSTR(ConstructGlobalLookup) - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, Runtime::method_constructGlobalLookup(engine, instr.index, callData)); - MOTH_END_INSTR(ConstructGlobalLookup) + MOTH_BEGIN_INSTR(Construct) + STORE_IP(); + acc = Runtime::method_construct(engine, STACK_VALUE(func), stack + argv, argc); + CHECK_EXCEPTION; + MOTH_END_INSTR(Construct) MOTH_BEGIN_INSTR(Jump) - code = ((const uchar *)&instr.offset) + instr.offset; + code += offset; MOTH_END_INSTR(Jump) - MOTH_BEGIN_INSTR(JumpEq) - bool cond = VALUEPTR(instr.condition)->toBoolean(); - TRACE(condition, "%s", cond ? "TRUE" : "FALSE"); - if (cond) - code = ((const uchar *)&instr.offset) + instr.offset; - MOTH_END_INSTR(JumpEq) + MOTH_BEGIN_INSTR(JumpTrue) + if (Q_LIKELY(ACC.integerCompatible())) { + if (ACC.int_32()) + code += offset; + } else { + if (ACC.toBoolean()) + code += offset; + } + MOTH_END_INSTR(JumpTrue) + + MOTH_BEGIN_INSTR(JumpFalse) + if (Q_LIKELY(ACC.integerCompatible())) { + if (!ACC.int_32()) + code += offset; + } else { + if (!ACC.toBoolean()) + code += offset; + } + MOTH_END_INSTR(JumpFalse) + + MOTH_BEGIN_INSTR(CmpEqNull) + acc = Encode(ACC.isNullOrUndefined()); + MOTH_END_INSTR(CmpEqNull) + + MOTH_BEGIN_INSTR(CmpNeNull) + acc = Encode(!ACC.isNullOrUndefined()); + MOTH_END_INSTR(CmpNeNull) + + MOTH_BEGIN_INSTR(CmpEqInt) + if (ACC.isIntOrBool()) { + acc = Encode(ACC.int_32() == lhs); + } else { + STORE_ACC(); + acc = Encode(compareEqualInt(accumulator, ACC, lhs)); + CHECK_EXCEPTION; + } + MOTH_END_INSTR(CmpEqInt) + + MOTH_BEGIN_INSTR(CmpNeInt) + if (ACC.isIntOrBool()) { + acc = Encode(bool(ACC.int_32() != lhs)); + } else { + STORE_ACC(); + acc = Encode(!compareEqualInt(accumulator, ACC, lhs)); + CHECK_EXCEPTION; + } + MOTH_END_INSTR(CmpNeInt) + + MOTH_BEGIN_INSTR(CmpEq) + const Value left = STACK_VALUE(lhs); + if (Q_LIKELY(left.asReturnedValue() == ACC.asReturnedValue())) { + acc = Encode(!ACC.isNaN()); + } else if (Q_LIKELY(left.isInteger() && ACC.isInteger())) { + acc = Encode(left.int_32() == ACC.int_32()); + } else { + STORE_ACC(); + acc = Encode(compareEqual(left, accumulator)); + CHECK_EXCEPTION; + } + MOTH_END_INSTR(CmpEq) + + MOTH_BEGIN_INSTR(CmpNe) + const Value left = STACK_VALUE(lhs); + if (Q_LIKELY(left.isInteger() && ACC.isInteger())) { + acc = Encode(bool(left.int_32() != ACC.int_32())); + } else { + STORE_ACC(); + acc = Encode(!compareEqual(left, accumulator)); + CHECK_EXCEPTION; + } + MOTH_END_INSTR(CmpNe) + + MOTH_BEGIN_INSTR(CmpGt) + const Value left = STACK_VALUE(lhs); + if (Q_LIKELY(left.isInteger() && ACC.isInteger())) { + acc = Encode(left.int_32() > ACC.int_32()); + } else if (left.isNumber() && ACC.isNumber()) { + acc = Encode(left.asDouble() > ACC.asDouble()); + } else { + STORE_ACC(); + acc = Encode(bool(Runtime::method_compareGreaterThan(left, accumulator))); + CHECK_EXCEPTION; + } + MOTH_END_INSTR(CmpGt) + + MOTH_BEGIN_INSTR(CmpGe) + const Value left = STACK_VALUE(lhs); + if (Q_LIKELY(left.isInteger() && ACC.isInteger())) { + acc = Encode(left.int_32() >= ACC.int_32()); + } else if (left.isNumber() && ACC.isNumber()) { + acc = Encode(left.asDouble() >= ACC.asDouble()); + } else { + STORE_ACC(); + acc = Encode(bool(Runtime::method_compareGreaterEqual(left, accumulator))); + CHECK_EXCEPTION; + } + MOTH_END_INSTR(CmpGe) + + MOTH_BEGIN_INSTR(CmpLt) + const Value left = STACK_VALUE(lhs); + if (Q_LIKELY(left.isInteger() && ACC.isInteger())) { + acc = Encode(left.int_32() < ACC.int_32()); + } else if (left.isNumber() && ACC.isNumber()) { + acc = Encode(left.asDouble() < ACC.asDouble()); + } else { + STORE_ACC(); + acc = Encode(bool(Runtime::method_compareLessThan(left, accumulator))); + CHECK_EXCEPTION; + } + MOTH_END_INSTR(CmpLt) + + MOTH_BEGIN_INSTR(CmpLe) + const Value left = STACK_VALUE(lhs); + if (Q_LIKELY(left.isInteger() && ACC.isInteger())) { + acc = Encode(left.int_32() <= ACC.int_32()); + } else if (left.isNumber() && ACC.isNumber()) { + acc = Encode(left.asDouble() <= ACC.asDouble()); + } else { + STORE_ACC(); + acc = Encode(bool(Runtime::method_compareLessEqual(left, accumulator))); + CHECK_EXCEPTION; + } + MOTH_END_INSTR(CmpLe) + + MOTH_BEGIN_INSTR(CmpStrictEqual) + if (STACK_VALUE(lhs).rawValue() == ACC.rawValue() && !ACC.isNaN()) { + acc = Encode(true); + } else { + STORE_ACC(); + acc = Encode(bool(RuntimeHelpers::strictEqual(STACK_VALUE(lhs), accumulator))); + CHECK_EXCEPTION; + } + MOTH_END_INSTR(CmpStrictEqual) + + MOTH_BEGIN_INSTR(CmpStrictNotEqual) + if (STACK_VALUE(lhs).rawValue() != ACC.rawValue() || ACC.isNaN()) { + STORE_ACC(); + acc = Encode(!RuntimeHelpers::strictEqual(STACK_VALUE(lhs), accumulator)); + CHECK_EXCEPTION; + } else { + acc = Encode(false); + } + MOTH_END_INSTR(CmpStrictNotEqual) + + MOTH_BEGIN_INSTR(CmpIn) + STORE_ACC(); + acc = Runtime::method_in(engine, STACK_VALUE(lhs), accumulator); + CHECK_EXCEPTION; + MOTH_END_INSTR(CmpIn) + + MOTH_BEGIN_INSTR(CmpInstanceOf) + // 11.8.6, 5: rval must be an Object + const Object *rhs = Primitive::fromReturnedValue(acc).as<Object>(); + if (Q_UNLIKELY(!rhs)) { + acc = engine->throwTypeError(); + goto catchException; + } + + // 11.8.6, 7: call "HasInstance", which we term instanceOf, and return the result. + acc = rhs->instanceOf(STACK_VALUE(lhs)); + CHECK_EXCEPTION; + MOTH_END_INSTR(CmpInstanceOf) + + MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt) + if (STACK_VALUE(lhs).int_32() != rhs || STACK_VALUE(lhs).isUndefined()) + code += offset; + MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt) - MOTH_BEGIN_INSTR(JumpNe) - bool cond = VALUEPTR(instr.condition)->toBoolean(); - TRACE(condition, "%s", cond ? "TRUE" : "FALSE"); - if (!cond) - code = ((const uchar *)&instr.offset) + instr.offset; - MOTH_END_INSTR(JumpNe) + MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt) + if (STACK_VALUE(lhs).int_32() == rhs && !STACK_VALUE(lhs).isUndefined()) + code += offset; + MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt) MOTH_BEGIN_INSTR(UNot) - STOREVALUE(instr.result, Runtime::method_uNot(VALUE(instr.source))); + if (ACC.integerCompatible()) { + acc = Encode(!static_cast<bool>(ACC.int_32())); + } else { + acc = Encode(!Value::toBooleanImpl(ACC)); + } MOTH_END_INSTR(UNot) - MOTH_BEGIN_INSTR(UNotBool) - bool b = VALUE(instr.source).booleanValue(); - VALUE(instr.result) = QV4::Encode(!b); - MOTH_END_INSTR(UNotBool) - MOTH_BEGIN_INSTR(UPlus) - STOREVALUE(instr.result, Runtime::method_uPlus(VALUE(instr.source))); + if (Q_UNLIKELY(!ACC.isNumber())) { + acc = Encode(ACC.toNumberImpl()); + CHECK_EXCEPTION; + } MOTH_END_INSTR(UPlus) MOTH_BEGIN_INSTR(UMinus) - STOREVALUE(instr.result, Runtime::method_uMinus(VALUE(instr.source))); + if (Q_LIKELY(ACC.integerCompatible())) { + int a = ACC.int_32(); + if (a == 0 || a == std::numeric_limits<int>::min()) { + acc = Encode(-static_cast<double>(a)); + } else { + acc = sub_int32(0, ACC.int_32()); + } + } else if (ACC.isDouble()) { + acc ^= (1ull << 63); // simply flip sign bit + } else { + acc = Encode(-ACC.toNumberImpl()); + CHECK_EXCEPTION; + } MOTH_END_INSTR(UMinus) MOTH_BEGIN_INSTR(UCompl) - STOREVALUE(instr.result, Runtime::method_complement(VALUE(instr.source))); + VALUE_TO_INT(a, ACC); + acc = Encode(~a); MOTH_END_INSTR(UCompl) - MOTH_BEGIN_INSTR(UComplInt) - VALUE(instr.result) = QV4::Encode((int)~VALUE(instr.source).integerValue()); - MOTH_END_INSTR(UComplInt) - MOTH_BEGIN_INSTR(Increment) - STOREVALUE(instr.result, Runtime::method_increment(VALUE(instr.source))); + if (Q_LIKELY(ACC.integerCompatible())) { + acc = add_int32(ACC.int_32(), 1); + } else if (ACC.isDouble()) { + acc = QV4::Encode(ACC.doubleValue() + 1.); + } else { + acc = Encode(ACC.toNumberImpl() + 1.); + CHECK_EXCEPTION; + } MOTH_END_INSTR(Increment) MOTH_BEGIN_INSTR(Decrement) - STOREVALUE(instr.result, Runtime::method_decrement(VALUE(instr.source))); + if (Q_LIKELY(ACC.integerCompatible())) { + acc = sub_int32(ACC.int_32(), 1); + } else if (ACC.isDouble()) { + acc = QV4::Encode(ACC.doubleValue() - 1.); + } else { + acc = Encode(ACC.toNumberImpl() - 1.); + CHECK_EXCEPTION; + } MOTH_END_INSTR(Decrement) - MOTH_BEGIN_INSTR(Binop) - QV4::Runtime::BinaryOperation op = *reinterpret_cast<QV4::Runtime::BinaryOperation *>(reinterpret_cast<char *>(&engine->runtime.runtimeMethods[instr.alu])); - STOREVALUE(instr.result, op(VALUE(instr.lhs), VALUE(instr.rhs))); - MOTH_END_INSTR(Binop) - MOTH_BEGIN_INSTR(Add) - STOREVALUE(instr.result, Runtime::method_add(engine, VALUE(instr.lhs), VALUE(instr.rhs))); + const Value left = STACK_VALUE(lhs); + if (Q_LIKELY(Value::integerCompatible(left, ACC))) { + acc = add_int32(left.int_32(), ACC.int_32()); + } else if (left.isNumber() && ACC.isNumber()) { + acc = Encode(left.asDouble() + ACC.asDouble()); + } else { + STORE_ACC(); + acc = Runtime::method_add(engine, left, accumulator); + CHECK_EXCEPTION; + } MOTH_END_INSTR(Add) + MOTH_BEGIN_INSTR(Sub) + const Value left = STACK_VALUE(lhs); + if (Q_LIKELY(Value::integerCompatible(left, ACC))) { + acc = sub_int32(left.int_32(), ACC.int_32()); + } else if (left.isNumber() && ACC.isNumber()) { + acc = Encode(left.asDouble() - ACC.asDouble()); + } else { + STORE_ACC(); + acc = Runtime::method_sub(left, accumulator); + CHECK_EXCEPTION; + } + MOTH_END_INSTR(Sub) + + MOTH_BEGIN_INSTR(Mul) + const Value left = STACK_VALUE(lhs); + if (Q_LIKELY(Value::integerCompatible(left, ACC))) { + acc = mul_int32(left.int_32(), ACC.int_32()); + } else if (left.isNumber() && ACC.isNumber()) { + acc = Encode(left.asDouble() * ACC.asDouble()); + } else { + STORE_ACC(); + acc = Runtime::method_mul(left, accumulator); + CHECK_EXCEPTION; + } + MOTH_END_INSTR(Mul) + + MOTH_BEGIN_INSTR(Div) + STORE_ACC(); + acc = Runtime::method_div(STACK_VALUE(lhs), accumulator); + CHECK_EXCEPTION; + MOTH_END_INSTR(Div) + + MOTH_BEGIN_INSTR(Mod) + STORE_ACC(); + acc = Runtime::method_mod(STACK_VALUE(lhs), accumulator); + CHECK_EXCEPTION; + MOTH_END_INSTR(Mod) + MOTH_BEGIN_INSTR(BitAnd) - STOREVALUE(instr.result, Runtime::method_bitAnd(VALUE(instr.lhs), VALUE(instr.rhs))); + VALUE_TO_INT(l, STACK_VALUE(lhs)); + VALUE_TO_INT(a, ACC); + acc = Encode(l & a); MOTH_END_INSTR(BitAnd) MOTH_BEGIN_INSTR(BitOr) - STOREVALUE(instr.result, Runtime::method_bitOr(VALUE(instr.lhs), VALUE(instr.rhs))); + VALUE_TO_INT(l, STACK_VALUE(lhs)); + VALUE_TO_INT(a, ACC); + acc = Encode(l | a); MOTH_END_INSTR(BitOr) MOTH_BEGIN_INSTR(BitXor) - STOREVALUE(instr.result, Runtime::method_bitXor(VALUE(instr.lhs), VALUE(instr.rhs))); + VALUE_TO_INT(l, STACK_VALUE(lhs)); + VALUE_TO_INT(a, ACC); + acc = Encode(l ^ a); MOTH_END_INSTR(BitXor) + MOTH_BEGIN_INSTR(UShr) + uint l = STACK_VALUE(lhs).toUInt32(); + VALUE_TO_INT(a, ACC); + acc = Encode(l >> uint(a & 0x1f)); + MOTH_END_INSTR(UShr) + MOTH_BEGIN_INSTR(Shr) - STOREVALUE(instr.result, QV4::Encode((int)(VALUEPTR(instr.lhs)->toInt32() >> (VALUEPTR(instr.rhs)->toInt32() & 0x1f)))); + VALUE_TO_INT(l, STACK_VALUE(lhs)); + VALUE_TO_INT(a, ACC); + acc = Encode(l >> (a & 0x1f)); MOTH_END_INSTR(Shr) MOTH_BEGIN_INSTR(Shl) - STOREVALUE(instr.result, QV4::Encode((int)(VALUEPTR(instr.lhs)->toInt32() << (VALUEPTR(instr.rhs)->toInt32() & 0x1f)))); + VALUE_TO_INT(l, STACK_VALUE(lhs)); + VALUE_TO_INT(a, ACC); + acc = Encode(l << (a & 0x1f)); MOTH_END_INSTR(Shl) MOTH_BEGIN_INSTR(BitAndConst) - int lhs = VALUEPTR(instr.lhs)->toInt32(); - STOREVALUE(instr.result, QV4::Encode((int)(lhs & instr.rhs))); - MOTH_END_INSTR(BitAnd) + VALUE_TO_INT(a, ACC); + acc = Encode(a & rhs); + CHECK_EXCEPTION; + MOTH_END_INSTR(BitAndConst) MOTH_BEGIN_INSTR(BitOrConst) - int lhs = VALUEPTR(instr.lhs)->toInt32(); - STOREVALUE(instr.result, QV4::Encode((int)(lhs | instr.rhs))); - MOTH_END_INSTR(BitOr) + VALUE_TO_INT(a, ACC); + acc = Encode(a | rhs); + MOTH_END_INSTR(BitOrConst) MOTH_BEGIN_INSTR(BitXorConst) - int lhs = VALUEPTR(instr.lhs)->toInt32(); - STOREVALUE(instr.result, QV4::Encode((int)(lhs ^ instr.rhs))); - MOTH_END_INSTR(BitXor) + VALUE_TO_INT(a, ACC); + acc = Encode(a ^ rhs); + MOTH_END_INSTR(BitXorConst) + + MOTH_BEGIN_INSTR(UShrConst) + acc = Encode(ACC.toUInt32() >> uint(rhs)); + MOTH_END_INSTR(UShrConst) MOTH_BEGIN_INSTR(ShrConst) - STOREVALUE(instr.result, QV4::Encode((int)(VALUEPTR(instr.lhs)->toInt32() >> instr.rhs))); + VALUE_TO_INT(a, ACC); + acc = Encode(a >> rhs); MOTH_END_INSTR(ShrConst) MOTH_BEGIN_INSTR(ShlConst) - STOREVALUE(instr.result, QV4::Encode((int)(VALUEPTR(instr.lhs)->toInt32() << instr.rhs))); + VALUE_TO_INT(a, ACC); + acc = Encode(a << rhs); MOTH_END_INSTR(ShlConst) - MOTH_BEGIN_INSTR(Mul) - STOREVALUE(instr.result, Runtime::method_mul(VALUE(instr.lhs), VALUE(instr.rhs))); - MOTH_END_INSTR(Mul) - - MOTH_BEGIN_INSTR(Sub) - STOREVALUE(instr.result, Runtime::method_sub(VALUE(instr.lhs), VALUE(instr.rhs))); - MOTH_END_INSTR(Sub) - - MOTH_BEGIN_INSTR(BinopContext) - QV4::Runtime::BinaryOperationContext op = *reinterpret_cast<QV4::Runtime::BinaryOperationContext *>(reinterpret_cast<char *>(&engine->runtime.runtimeMethods[instr.alu])); - STOREVALUE(instr.result, op(engine, VALUE(instr.lhs), VALUE(instr.rhs))); - MOTH_END_INSTR(BinopContext) - MOTH_BEGIN_INSTR(Ret) -// TRACE(Ret, "returning value %s", result.toString(context)->toQString().toUtf8().constData()); - return VALUE(instr.result).asReturnedValue(); + goto functionExit; MOTH_END_INSTR(Ret) #if QT_CONFIG(qml_debug) MOTH_BEGIN_INSTR(Debug) - engine->current->lineNumber = instr.lineNumber; - QV4::Debugging::Debugger *debugger = engine->debugger(); - if (debugger && debugger->pauseAtNextOpportunity()) - debugger->maybeBreakAtInstruction(); - if (qt_v4IsDebugging) - qt_v4CheckForBreak(engine->currentContext); + STORE_IP(); + debug_slowPath(engine); MOTH_END_INSTR(Debug) - - MOTH_BEGIN_INSTR(Line) - engine->current->lineNumber = instr.lineNumber; - if (qt_v4IsDebugging) - qt_v4CheckForBreak(engine->currentContext); - MOTH_END_INSTR(Line) #endif // QT_CONFIG(qml_debug) - MOTH_BEGIN_INSTR(LoadThis) - VALUE(instr.result) = engine->currentContext->thisObject(); - MOTH_END_INSTR(LoadThis) - MOTH_BEGIN_INSTR(LoadQmlContext) - VALUE(instr.result) = Runtime::method_getQmlContext(static_cast<QV4::NoThrowEngine*>(engine)); + STACK_VALUE(result) = Runtime::method_loadQmlContext(static_cast<QV4::NoThrowEngine*>(engine)); MOTH_END_INSTR(LoadQmlContext) MOTH_BEGIN_INSTR(LoadQmlImportedScripts) - VALUE(instr.result) = Runtime::method_getQmlImportedScripts(static_cast<QV4::NoThrowEngine*>(engine)); + STACK_VALUE(result) = Runtime::method_loadQmlImportedScripts(static_cast<QV4::NoThrowEngine*>(engine)); MOTH_END_INSTR(LoadQmlImportedScripts) MOTH_BEGIN_INSTR(LoadQmlSingleton) - VALUE(instr.result) = Runtime::method_getQmlSingleton(static_cast<QV4::NoThrowEngine*>(engine), instr.name); + acc = Runtime::method_loadQmlSingleton(static_cast<QV4::NoThrowEngine*>(engine), name); MOTH_END_INSTR(LoadQmlSingleton) -#ifdef MOTH_THREADED_INTERPRETER - // nothing to do -#else - default: - qFatal("QQmlJS::Moth::VME: Internal error - unknown instruction %d", genericInstr->common.instructionType); - break; - } -#endif - - Q_ASSERT(false); catchException: Q_ASSERT(engine->hasException); - if (!exceptionHandler) - return QV4::Encode::undefined(); + if (!exceptionHandler) { + acc = Encode::undefined(); + goto functionExit; + } code = exceptionHandler; } -} + } -QV4::ReturnedValue VME::exec(ExecutionEngine *engine, const uchar *code) -{ - VME vme; - QV4::Debugging::Debugger *debugger = engine->debugger(); - if (debugger) - debugger->enteringFunction(); - QV4::ReturnedValue retVal = vme.run(engine, code); - if (debugger) - debugger->leavingFunction(retVal); - return retVal; +functionExit: + if (QV4::Debugging::Debugger *debugger = engine->debugger()) + debugger->leavingFunction(ACC.asReturnedValue()); + engine->currentStackFrame = frame.parent; + engine->jsStackTop = stack; + + return acc; } diff --git a/src/qml/jsruntime/qv4vme_moth_p.h b/src/qml/jsruntime/qv4vme_moth_p.h index 8d46207f2b..dbf9ed3550 100644 --- a/src/qml/jsruntime/qv4vme_moth_p.h +++ b/src/qml/jsruntime/qv4vme_moth_p.h @@ -52,8 +52,6 @@ // #include <private/qv4global_p.h> -#include <private/qv4runtime_p.h> -#include <private/qv4instr_moth_p.h> QT_REQUIRE_CONFIG(qml_interpreter); @@ -65,10 +63,17 @@ namespace Moth { class VME { public: - static QV4::ReturnedValue exec(QV4::ExecutionEngine *, const uchar *); - -private: - QV4::ReturnedValue run(QV4::ExecutionEngine *, const uchar *code); + struct ExecData { + QV4::Function *function; + const QV4::ExecutionContext *scope; + }; + static inline + QV4::ReturnedValue exec(Function *v4Function, const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context) { + ExecData data{v4Function, context}; + quintptr d = reinterpret_cast<quintptr>(&data) | 0x1; + return exec(reinterpret_cast<const FunctionObject *>(d), thisObject, argv, argc); + } + static QV4::ReturnedValue exec(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc); }; } // namespace Moth diff --git a/src/qml/memory/qv4heap_p.h b/src/qml/memory/qv4heap_p.h index f00ce4283c..363e891d5c 100644 --- a/src/qml/memory/qv4heap_p.h +++ b/src/qml/memory/qv4heap_p.h @@ -75,7 +75,6 @@ struct InternalClass; struct VTable { const VTable * const parent; - const quint64 markTable; uint inlinePropertyOffset : 16; uint nInlineProperties : 16; uint isExecutionContext : 1; @@ -97,7 +96,7 @@ namespace Heap { struct Q_QML_EXPORT Base { void *operator new(size_t) = delete; - static Q_CONSTEXPR quint64 markTable = 0; + static void markObjects(Heap::Base *, MarkStack *) {} InternalClass *internalClass; @@ -131,7 +130,9 @@ struct Q_QML_EXPORT Base { return Chunk::testBit(c->objectBitmap, h - c->realBase()); } - inline void markChildren(MarkStack *markStack); + inline void markChildren(MarkStack *markStack) { + vtable()->markObjects(this, markStack); + } void *operator new(size_t, Managed *m) { return m; } void *operator new(size_t, Heap::Base *m) { return m; } @@ -184,6 +185,22 @@ Q_STATIC_ASSERT(sizeof(Base) == QT_POINTER_SIZE); } +inline +void Heap::Base::mark(QV4::MarkStack *markStack) +{ + Q_ASSERT(inUse()); + const HeapItem *h = reinterpret_cast<const HeapItem *>(this); + Chunk *c = h->chunk(); + size_t index = h - c->realBase(); + Q_ASSERT(!Chunk::testBit(c->extendsBitmap, index)); + quintptr *bitmap = c->blackBitmap + Chunk::bitmapIndex(index); + quintptr bit = Chunk::bitForIndex(index); + if (!(*bitmap & bit)) { + *bitmap |= bit; + markStack->push(this); + } +} + #ifdef QT_NO_QOBJECT template <class T> struct QQmlQPointer { diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index de97918fb0..3ea7908e9d 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -279,64 +279,6 @@ QString binary(quintptr) { return QString(); } #define SDUMP if (1) ; else qDebug #endif -void Heap::Base::markChildren(MarkStack *markStack) -{ - if (vtable()->markObjects) - vtable()->markObjects(this, markStack); - if (quint64 m = vtable()->markTable) { -// qDebug() << "using mark table:" << hex << m << "for" << h; - void **mem = reinterpret_cast<void **>(this); - while (m) { - MarkFlags mark = static_cast<MarkFlags>(m & 3); - switch (mark) { - case Mark_NoMark: - break; - case Mark_Value: -// qDebug() << "marking value at " << mem; - reinterpret_cast<Value *>(mem)->mark(markStack); - break; - case Mark_Pointer: { -// qDebug() << "marking pointer at " << mem; - Heap::Base *p = *reinterpret_cast<Heap::Base **>(mem); - if (p) - p->mark(markStack); - break; - } - case Mark_ValueArray: { - Q_ASSERT(m == Mark_ValueArray); -// qDebug() << "marking Value Array at offset" << hex << (mem - reinterpret_cast<void **>(h)); - ValueArray<0> *a = reinterpret_cast<ValueArray<0> *>(mem); - Value *v = a->values; - const Value *end = v + a->alloc; - if (a->alloc > 32*1024) { - // drain from time to time to avoid overflows in the js stack - Heap::Base **currentBase = markStack->top; - while (v < end) { - v->mark(markStack); - ++v; - if (markStack->top >= currentBase + 32*1024) { - Heap::Base **oldBase = markStack->base; - markStack->base = currentBase; - markStack->drain(); - markStack->base = oldBase; - } - } - } else { - while (v < end) { - v->mark(markStack); - ++v; - } - } - break; - } - } - - m >>= 2; - ++mem; - } - } -} - // Stores a classname -> freed count mapping. typedef QHash<const char*, int> MMStatsHash; Q_GLOBAL_STATIC(MMStatsHash, freedObjectStatsGlobal) @@ -553,51 +495,6 @@ void Chunk::sortIntoBins(HeapItem **bins, uint nBins) #endif } - -template<typename T> -StackAllocator<T>::StackAllocator(ChunkAllocator *chunkAlloc) - : chunkAllocator(chunkAlloc) -{ - chunks.push_back(chunkAllocator->allocate()); - firstInChunk = chunks.back()->first(); - nextFree = firstInChunk; - lastInChunk = firstInChunk + (Chunk::AvailableSlots - 1)/requiredSlots*requiredSlots; -} - -template<typename T> -void StackAllocator<T>::freeAll() -{ - for (auto c : chunks) - chunkAllocator->free(c); -} - -template<typename T> -void StackAllocator<T>::nextChunk() { - Q_ASSERT(nextFree == lastInChunk); - ++currentChunk; - if (currentChunk >= chunks.size()) { - Chunk *newChunk = chunkAllocator->allocate(); - chunks.push_back(newChunk); - } - firstInChunk = chunks.at(currentChunk)->first(); - nextFree = firstInChunk; - lastInChunk = firstInChunk + (Chunk::AvailableSlots - 1)/requiredSlots*requiredSlots; -} - -template<typename T> -void QV4::StackAllocator<T>::prevChunk() { - Q_ASSERT(nextFree == firstInChunk); - Q_ASSERT(chunks.at(currentChunk) == nextFree->chunk()); - Q_ASSERT(currentChunk > 0); - --currentChunk; - firstInChunk = chunks.at(currentChunk)->first(); - lastInChunk = firstInChunk + (Chunk::AvailableSlots - 1)/requiredSlots*requiredSlots; - nextFree = lastInChunk; -} - -template struct StackAllocator<Heap::CallContext>; - - HeapItem *BlockAllocator::allocate(size_t size, bool forceAllocation) { Q_ASSERT((size % Chunk::SlotSize) == 0); size_t slotsRequired = size >> Chunk::SlotSizeShift; @@ -836,7 +733,6 @@ void HugeItemAllocator::freeAll() MemoryManager::MemoryManager(ExecutionEngine *engine) : engine(engine) , chunkAllocator(new ChunkAllocator) - , stackAllocator(chunkAllocator) , blockAllocator(chunkAllocator) , hugeItemAllocator(chunkAllocator) , m_persistentValues(new PersistentValueStorage(engine)) @@ -1261,7 +1157,6 @@ MemoryManager::~MemoryManager() sweep(/*lastSweep*/true); blockAllocator.freeAll(); hugeItemAllocator.freeAll(); - stackAllocator.freeAll(); delete m_weakValues; #ifdef V4_USE_VALGRIND @@ -1303,9 +1198,11 @@ void MemoryManager::collectFromJSStack(MarkStack *markStack) const Value *top = engine->jsStackTop; while (v < top) { Managed *m = v->managed(); - if (m && m->inUse()) + if (m) { + Q_ASSERT(m->inUse()); // Skip pointers to already freed objects, they are bogus as well m->mark(markStack); + } ++v; } } diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h index 17957d0cd6..921fea3956 100644 --- a/src/qml/memory/qv4mm_p.h +++ b/src/qml/memory/qv4mm_p.h @@ -72,51 +72,6 @@ namespace QV4 { struct ChunkAllocator; -template<typename T> -struct StackAllocator { - Q_STATIC_ASSERT(sizeof(T) < Chunk::DataSize); - static const uint requiredSlots = (sizeof(T) + sizeof(HeapItem) - 1)/sizeof(HeapItem); - - StackAllocator(ChunkAllocator *chunkAlloc); - - T *allocate() { - HeapItem *m = nextFree; - if (Q_UNLIKELY(nextFree == lastInChunk)) { - nextChunk(); - } else { - nextFree += requiredSlots; - } -#if MM_DEBUG || !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) - Chunk *c = m->chunk(); - Chunk::setBit(c->objectBitmap, m - c->realBase()); -#endif - return m->as<T>(); - } - void free() { - if (Q_UNLIKELY(nextFree == firstInChunk)) { - prevChunk(); - } else { - nextFree -= requiredSlots; - } -#if MM_DEBUG || !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) - Chunk *c = nextFree->chunk(); - Chunk::clearBit(c->objectBitmap, nextFree - c->realBase()); -#endif - } - - void nextChunk(); - void prevChunk(); - - void freeAll(); - - ChunkAllocator *chunkAllocator; - HeapItem *nextFree = 0; - HeapItem *firstInChunk = 0; - HeapItem *lastInChunk = 0; - std::vector<Chunk *> chunks; - uint currentChunk = 0; -}; - struct BlockAllocator { BlockAllocator(ChunkAllocator *chunkAllocator) : chunkAllocator(chunkAllocator) @@ -211,19 +166,6 @@ public: Q_DECL_CONSTEXPR static inline std::size_t align(std::size_t size) { return (size + Chunk::SlotSize - 1) & ~(Chunk::SlotSize - 1); } - QV4::Heap::CallContext *allocSimpleCallContext() - { - Heap::CallContext *ctxt = stackAllocator.allocate(); - memset(ctxt, 0, sizeof(Heap::SimpleCallContext)); - ctxt->internalClass = SimpleCallContext::defaultInternalClass(engine); - Q_ASSERT(ctxt->internalClass && ctxt->internalClass->vtable); - ctxt->init(); - return ctxt; - - } - void freeSimpleCallContext() - { stackAllocator.free(); } - template<typename ManagedType> inline typename ManagedType::Data *allocManaged(std::size_t size) { @@ -237,6 +179,18 @@ public: return static_cast<typename ManagedType::Data *>(o); } + template<typename ManagedType> + inline typename ManagedType::Data *allocManaged(std::size_t size, InternalClass *ic) + { + V4_ASSERT_IS_TRIVIAL(typename ManagedType::Data) + size = align(size); + Heap::Base *o = allocData(size); + o->internalClass = ic; + Q_ASSERT(o->internalClass && o->internalClass->vtable); + Q_ASSERT(ic->vtable == ManagedType::staticVTable()); + return static_cast<typename ManagedType::Data *>(o); + } + template <typename ObjectType> typename ObjectType::Data *allocateObject(InternalClass *ic) { @@ -465,7 +419,6 @@ private: public: QV4::ExecutionEngine *engine; ChunkAllocator *chunkAllocator; - StackAllocator<Heap::CallContext> stackAllocator; BlockAllocator blockAllocator; HugeItemAllocator hugeItemAllocator; PersistentValueStorage *m_persistentValues; diff --git a/src/qml/memory/qv4mmdefs_p.h b/src/qml/memory/qv4mmdefs_p.h index 328797fb5e..6d911e69f6 100644 --- a/src/qml/memory/qv4mmdefs_p.h +++ b/src/qml/memory/qv4mmdefs_p.h @@ -287,32 +287,8 @@ struct MarkStack { }; -// Some helper classes and macros to automate the generation of our -// tables used for marking objects - -enum MarkFlags { - Mark_NoMark = 0, - Mark_Value = 1, - Mark_Pointer = 2, - Mark_ValueArray = 3 -}; - -template <typename T> -struct MarkFlagEvaluator { - static Q_CONSTEXPR quint64 value = 0; -}; -template <typename T, size_t o> -struct MarkFlagEvaluator<Heap::Pointer<T, o>> { - static Q_CONSTEXPR quint64 value = static_cast<quint64>(Mark_Pointer) << (2*o / sizeof(quintptr)); -}; -template <size_t o> -struct MarkFlagEvaluator<ValueArray<o>> { - static Q_CONSTEXPR quint64 value = static_cast<quint64>(Mark_ValueArray) << (2*o / sizeof(quintptr)); -}; -template <size_t o> -struct MarkFlagEvaluator<HeapValue<o>> { - static Q_CONSTEXPR quint64 value = static_cast<quint64>(Mark_Value) << (2 *o / sizeof(quintptr)); -}; +// Some helper to automate the generation of our +// functions used for marking objects #define HEAP_OBJECT_OFFSET_MEMBER_EXPANSION(c, gcType, type, name) \ HEAP_OBJECT_OFFSET_MEMBER_EXPANSION_##gcType(c, type, name) @@ -334,25 +310,42 @@ struct MarkFlagEvaluator<HeapValue<o>> { #define HEAP_OBJECT_MEMBER_EXPANSION_ValueArray(c, type, name) \ type<offsetof(c##OffsetStruct, name) + baseOffset> name; -#define HEAP_OBJECT_MARK_EXPANSION(class, gcType, type, name) \ - MarkFlagEvaluator<decltype(class::name)>::value | +#define HEAP_OBJECT_MARKOBJECTS_EXPANSION(c, gcType, type, name) \ + HEAP_OBJECT_MARKOBJECTS_EXPANSION_##gcType(c, type, name) +#define HEAP_OBJECT_MARKOBJECTS_EXPANSION_Pointer(c, type, name) \ + if (o->name) o->name.heapObject()->mark(stack); +#define HEAP_OBJECT_MARKOBJECTS_EXPANSION_NoMark(c, type, name) +#define HEAP_OBJECT_MARKOBJECTS_EXPANSION_HeapValue(c, type, name) \ + o->name.mark(stack); +#define HEAP_OBJECT_MARKOBJECTS_EXPANSION_ValueArray(c, type, name) \ + o->name.mark(stack); -#define DECLARE_HEAP_OBJECT(name, base) \ + +#define DECLARE_HEAP_OBJECT_BASE(name, base) \ struct name##OffsetStruct { \ name##Members(name, HEAP_OBJECT_OFFSET_MEMBER_EXPANSION) \ }; \ struct name##SizeStruct : base, name##OffsetStruct {}; \ struct name##Data { \ + typedef base SuperClass; \ static Q_CONSTEXPR size_t baseOffset = sizeof(name##SizeStruct) - sizeof(name##OffsetStruct); \ name##Members(name, HEAP_OBJECT_MEMBER_EXPANSION) \ }; \ Q_STATIC_ASSERT(sizeof(name##SizeStruct) == sizeof(name##Data) + name##Data::baseOffset); \ -static Q_CONSTEXPR quint64 name##_markTable = \ - (name##Members(name##Data, HEAP_OBJECT_MARK_EXPANSION) 0) | QV4::Heap::base::markTable; \ - \ -struct name : base, name##Data -#define DECLARE_MARK_TABLE(class) static Q_CONSTEXPR quint64 markTable = class##_markTable +#define DECLARE_HEAP_OBJECT(name, base) \ +DECLARE_HEAP_OBJECT_BASE(name, base) \ +struct name : base, name##Data +#define DECLARE_EXPORTED_HEAP_OBJECT(name, base) \ +DECLARE_HEAP_OBJECT_BASE(name, base) \ +struct Q_QML_EXPORT name : base, name##Data + +#define DECLARE_MARKOBJECTS(class) \ + static void markObjects(Heap::Base *b, MarkStack *stack) { \ + class *o = static_cast<class *>(b); \ + class##Data::SuperClass::markObjects(o, stack); \ + class##Members(class, HEAP_OBJECT_MARKOBJECTS_EXPANSION) \ + } } diff --git a/src/qml/memory/qv4writebarrier_p.h b/src/qml/memory/qv4writebarrier_p.h index 86fd28000d..4ec224cc36 100644 --- a/src/qml/memory/qv4writebarrier_p.h +++ b/src/qml/memory/qv4writebarrier_p.h @@ -114,8 +114,8 @@ namespace Heap { template <typename T, size_t o> struct Pointer { static Q_CONSTEXPR size_t offset = o; - T operator->() const { return ptr; } - operator T () const { return ptr; } + T operator->() const { return get(); } + operator T () const { return get(); } Heap::Base *base() { Heap::Base *base = reinterpret_cast<Heap::Base *>(this) - (offset/sizeof(Heap::Base)); @@ -124,14 +124,18 @@ struct Pointer { } void set(EngineBase *e, T newVal) { - WriteBarrier::write(e, base(), reinterpret_cast<Heap::Base **>(&ptr), reinterpret_cast<Heap::Base *>(newVal)); + WriteBarrier::write(e, base(), &ptr, reinterpret_cast<Heap::Base *>(newVal)); } + T get() const { return reinterpret_cast<T>(ptr); } + template <typename Type> Type *cast() { return static_cast<Type *>(ptr); } + Heap::Base *heapObject() const { return ptr; } + private: - T ptr; + Heap::Base *ptr; }; typedef Pointer<char *, 0> V4PointerCheck; V4_ASSERT_IS_TRIVIAL(V4PointerCheck) @@ -192,6 +196,30 @@ struct ValueArray { values[i] = values[i + n]; } } + + void mark(MarkStack *markStack) { + Value *v = values; + const Value *end = v + alloc; + if (alloc > 32*1024) { + // drain from time to time to avoid overflows in the js stack + Heap::Base **currentBase = markStack->top; + while (v < end) { + v->mark(markStack); + ++v; + if (markStack->top >= currentBase + 32*1024) { + Heap::Base **oldBase = markStack->base; + markStack->base = currentBase; + markStack->drain(); + markStack->base = oldBase; + } + } + } else { + while (v < end) { + v->mark(markStack); + ++v; + } + } + } }; // It's really important that the offset of values in this structure is diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h index aa48accfe0..7291cf0d3d 100644 --- a/src/qml/parser/qqmljsast_p.h +++ b/src/qml/parser/qqmljsast_p.h @@ -100,7 +100,8 @@ enum Op { Sub, URShift, InplaceURightShift, - InplaceXor + InplaceXor, + Invalid }; } // namespace QSOperator diff --git a/src/qml/qml.pro b/src/qml/qml.pro index e9d7dcbd2d..c13227d8fe 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -57,14 +57,16 @@ include(memory/memory.pri) include(parser/parser.pri) include(compiler/compiler.pri) include(jsapi/jsapi.pri) -include(jit/jit.pri) include(jsruntime/jsruntime.pri) +include(jit/jit.pri) include(qml/qml.pri) include(debugger/debugger.pri) qtConfig(animation) { include(animations/animations.pri) } include(types/types.pri) +include(../3rdparty/masm/masm-defs.pri) +include(../3rdparty/masm/masm.pri) MODULE_PLUGIN_TYPES = \ qmltooling diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 43ab74138c..56ab259229 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -203,12 +203,11 @@ protected: bool isUndefined = false; - QV4::ScopedCallData callData(scope); - QQmlJavaScriptExpression::evaluate(callData, &isUndefined, scope); + QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined)); bool error = false; if (!watcher.wasDeleted() && isAddedToObject() && !hasError()) - error = !write(scope.result, isUndefined, flags); + error = !write(result, isUndefined, flags); if (!watcher.wasDeleted()) { @@ -457,12 +456,11 @@ QVariant QQmlBinding::evaluate() bool isUndefined = false; QV4::Scope scope(ep->v4engine()); - QV4::ScopedCallData callData(scope); - QQmlJavaScriptExpression::evaluate(callData, &isUndefined, scope); + QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined)); ep->dereferenceScarceResources(); - return scope.engine->toVariant(scope.result, qMetaTypeId<QList<QObject*> >()); + return scope.engine->toVariant(result, qMetaTypeId<QList<QObject*> >()); } QString QQmlBinding::expressionIdentifier() const diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index 19ece44beb..1d7a37fc99 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -55,6 +55,7 @@ #include <private/qjsvalue_p.h> #include <private/qv4value_p.h> +#include <private/qv4jscall_p.h> #include <private/qv4qobjectwrapper_p.h> #include <QtCore/qdebug.h> @@ -192,35 +193,35 @@ void QQmlBoundSignalExpression::evaluate(void **a) int *argsTypes = QQmlMetaObject(m_target).methodParameterTypes(methodIndex, &storage, 0); int argCount = argsTypes ? *argsTypes : 0; - QV4::ScopedCallData callData(scope, argCount); + QV4::JSCallData jsCall(scope, argCount); for (int ii = 0; ii < argCount; ++ii) { int type = argsTypes[ii + 1]; //### ideally we would use metaTypeToJS, however it currently gives different results // for several cases (such as QVariant type and QObject-derived types) //args[ii] = engine->metaTypeToJS(type, a[ii + 1]); if (type == qMetaTypeId<QJSValue>()) { - if (QV4::Value *v4Value = QJSValuePrivate::valueForData(reinterpret_cast<QJSValue *>(a[ii + 1]), &callData->args[ii])) - callData->args[ii] = *v4Value; + if (QV4::Value *v4Value = QJSValuePrivate::valueForData(reinterpret_cast<QJSValue *>(a[ii + 1]), &jsCall->args[ii])) + jsCall->args[ii] = *v4Value; else - callData->args[ii] = QV4::Encode::undefined(); + jsCall->args[ii] = QV4::Encode::undefined(); } else if (type == QMetaType::QVariant) { - callData->args[ii] = scope.engine->fromVariant(*((QVariant *)a[ii + 1])); + jsCall->args[ii] = scope.engine->fromVariant(*((QVariant *)a[ii + 1])); } else if (type == QMetaType::Int) { //### optimization. Can go away if we switch to metaTypeToJS, or be expanded otherwise - callData->args[ii] = QV4::Primitive::fromInt32(*reinterpret_cast<const int*>(a[ii + 1])); + jsCall->args[ii] = QV4::Primitive::fromInt32(*reinterpret_cast<const int*>(a[ii + 1])); } else if (type == qMetaTypeId<QQmlV4Handle>()) { - callData->args[ii] = *reinterpret_cast<QQmlV4Handle *>(a[ii + 1]); + jsCall->args[ii] = *reinterpret_cast<QQmlV4Handle *>(a[ii + 1]); } else if (ep->isQObject(type)) { if (!*reinterpret_cast<void* const *>(a[ii + 1])) - callData->args[ii] = QV4::Primitive::nullValue(); + jsCall->args[ii] = QV4::Primitive::nullValue(); else - callData->args[ii] = QV4::QObjectWrapper::wrap(ep->v4engine(), *reinterpret_cast<QObject* const *>(a[ii + 1])); + jsCall->args[ii] = QV4::QObjectWrapper::wrap(ep->v4engine(), *reinterpret_cast<QObject* const *>(a[ii + 1])); } else { - callData->args[ii] = scope.engine->fromVariant(QVariant(type, a[ii + 1])); + jsCall->args[ii] = scope.engine->fromVariant(QVariant(type, a[ii + 1])); } } - QQmlJavaScriptExpression::evaluate(callData, 0, scope); + QQmlJavaScriptExpression::evaluate(jsCall.callData(), 0); ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. } @@ -237,12 +238,12 @@ void QQmlBoundSignalExpression::evaluate(const QList<QVariant> &args) ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation. - QV4::ScopedCallData callData(scope, args.count()); + QV4::JSCallData jsCall(scope, args.count()); for (int ii = 0; ii < args.count(); ++ii) { - callData->args[ii] = scope.engine->fromVariant(args[ii]); + jsCall->args[ii] = scope.engine->fromVariant(args[ii]); } - QQmlJavaScriptExpression::evaluate(callData, 0, scope); + QQmlJavaScriptExpression::evaluate(jsCall.callData(), 0); ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. } diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 8ec8669c60..5a03f2dd93 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -58,6 +58,7 @@ #include <private/qv4scopedvalue_p.h> #include <private/qv4objectiterator_p.h> #include <private/qv4qobjectwrapper_p.h> +#include <private/qv4jscall_p.h> #include <QDir> #include <QStack> @@ -1097,7 +1098,7 @@ namespace Heap { Member(class, NoMark, QQmlQPointer<QObject>, parent) DECLARE_HEAP_OBJECT(QmlIncubatorObject, Object) { - DECLARE_MARK_TABLE(QmlIncubatorObject); + DECLARE_MARKOBJECTS(QmlIncubatorObject); void init(QQmlIncubator::IncubationMode = QQmlIncubator::Asynchronous); inline void destroy(); @@ -1110,11 +1111,11 @@ struct QmlIncubatorObject : public QV4::Object V4_OBJECT2(QmlIncubatorObject, Object) V4_NEEDS_DESTROY - static void method_get_statusChanged(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_set_statusChanged(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_get_status(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_get_object(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_forceCompletion(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_get_statusChanged(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_set_statusChanged(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_get_status(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_get_object(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_forceCompletion(const BuiltinFunction *, CallData *callData); void statusChanged(QQmlIncubator::Status); void setInitialState(QObject *); @@ -1224,8 +1225,7 @@ void QQmlComponentPrivate::setInitialProperties(QV4::ExecutionEngine *engine, QV if (engine->hasException) return; - QV4::ExecutionContextSaver saver(scope); - engine->pushContext(qmlContext); + QV4::ScopedStackFrame frame(scope, qmlContext->d()); while (1) { name = it.nextPropertyNameAsString(val); @@ -1460,17 +1460,19 @@ QQmlComponentExtension::QQmlComponentExtension(QV4::ExecutionEngine *v4) incubationProto.set(v4, proto); } -void QV4::QmlIncubatorObject::method_get_object(const BuiltinFunction *, Scope &scope, CallData *callData) +QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_object(const BuiltinFunction *b, CallData *callData) { + QV4::Scope scope(b); QV4::Scoped<QmlIncubatorObject> o(scope, callData->thisObject.as<QmlIncubatorObject>()); if (!o) THROW_TYPE_ERROR(); - scope.result = QV4::QObjectWrapper::wrap(scope.engine, o->d()->incubator->object()); + return QV4::QObjectWrapper::wrap(scope.engine, o->d()->incubator->object()); } -void QV4::QmlIncubatorObject::method_forceCompletion(const BuiltinFunction *, Scope &scope, CallData *callData) +QV4::ReturnedValue QV4::QmlIncubatorObject::method_forceCompletion(const BuiltinFunction *b, CallData *callData) { + QV4::Scope scope(b); QV4::Scoped<QmlIncubatorObject> o(scope, callData->thisObject.as<QmlIncubatorObject>()); if (!o) THROW_TYPE_ERROR(); @@ -1480,28 +1482,31 @@ void QV4::QmlIncubatorObject::method_forceCompletion(const BuiltinFunction *, Sc RETURN_UNDEFINED(); } -void QV4::QmlIncubatorObject::method_get_status(const BuiltinFunction *, Scope &scope, CallData *callData) +QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_status(const BuiltinFunction *b, CallData *callData) { + QV4::Scope scope(b); QV4::Scoped<QmlIncubatorObject> o(scope, callData->thisObject.as<QmlIncubatorObject>()); if (!o) THROW_TYPE_ERROR(); - scope.result = QV4::Encode(o->d()->incubator->status()); + return QV4::Encode(o->d()->incubator->status()); } -void QV4::QmlIncubatorObject::method_get_statusChanged(const BuiltinFunction *, Scope &scope, CallData *callData) +QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_statusChanged(const BuiltinFunction *b, CallData *callData) { + QV4::Scope scope(b); QV4::Scoped<QmlIncubatorObject> o(scope, callData->thisObject.as<QmlIncubatorObject>()); if (!o) THROW_TYPE_ERROR(); - scope.result = o->d()->statusChanged; + return QV4::Encode(o->d()->statusChanged); } -void QV4::QmlIncubatorObject::method_set_statusChanged(const BuiltinFunction *, Scope &scope, CallData *callData) +QV4::ReturnedValue QV4::QmlIncubatorObject::method_set_statusChanged(const BuiltinFunction *b, CallData *callData) { + QV4::Scope scope(b); QV4::Scoped<QmlIncubatorObject> o(scope, callData->thisObject.as<QmlIncubatorObject>()); - if (!o || callData->argc < 1) + if (!o || callData->argc() < 1) THROW_TYPE_ERROR(); o->d()->statusChanged.set(scope.engine, callData->args[0]); @@ -1556,10 +1561,10 @@ void QV4::QmlIncubatorObject::statusChanged(QQmlIncubator::Status s) QV4::ScopedFunctionObject f(scope, d()->statusChanged); if (f) { - QV4::ScopedCallData callData(scope, 1); - callData->thisObject = this; - callData->args[0] = QV4::Primitive::fromUInt32(s); - f->call(scope, callData); + QV4::JSCallData jsCallData(scope, 1); + *jsCallData->thisObject = this; + jsCallData->args[0] = QV4::Primitive::fromUInt32(s); + f->call(jsCallData); if (scope.hasException()) { QQmlError error = scope.engine->catchExceptionAsQmlError(); QQmlEnginePrivate::warning(QQmlEnginePrivate::get(scope.engine->qmlEngine()), error); diff --git a/src/qml/qml/qqmldelayedcallqueue.cpp b/src/qml/qml/qqmldelayedcallqueue.cpp index 13e62ec696..df4030e522 100644 --- a/src/qml/qml/qqmldelayedcallqueue.cpp +++ b/src/qml/qml/qqmldelayedcallqueue.cpp @@ -42,6 +42,7 @@ #include <private/qqmlengine_p.h> #include <private/qqmljavascriptexpression_p.h> #include <private/qv4value_p.h> +#include <private/qv4jscall_p.h> #include <private/qv4qobjectwrapper_p.h> #include <QQmlError> @@ -63,17 +64,17 @@ void QQmlDelayedCallQueue::DelayedFunctionCall::execute(QV4::ExecutionEngine *en QV4::Scope scope(engine); QV4::ArrayObject *array = m_args.as<QV4::ArrayObject>(); + const QV4::FunctionObject *callback = m_function.as<QV4::FunctionObject>(); + Q_ASSERT(callback); const int argCount = array ? array->getLength() : 0; - QV4::ScopedCallData callData(scope, argCount); - callData->thisObject = QV4::Encode::undefined(); + QV4::JSCallData jsCallData(scope, argCount); + *jsCallData->thisObject = QV4::Encode::undefined(); for (int i = 0; i < argCount; i++) { - callData->args[i] = array->getIndexed(i); + jsCallData->args[i] = array->getIndexed(i); } - const QV4::FunctionObject *callback = m_function.as<QV4::FunctionObject>(); - Q_ASSERT(callback); - callback->call(scope, callData); + callback->call(jsCallData); if (scope.engine->hasException) { QQmlError error = scope.engine->catchExceptionAsQmlError(); @@ -105,9 +106,10 @@ void QQmlDelayedCallQueue::init(QV4::ExecutionEngine* engine) m_tickedMethod = metaObject.method(methodIndex); } -void QQmlDelayedCallQueue::addUniquelyAndExecuteLater(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +QV4::ReturnedValue QQmlDelayedCallQueue::addUniquelyAndExecuteLater(const QV4::BuiltinFunction *b, QV4::CallData *callData) { - if (callData->argc == 0) + QV4::Scope scope(b); + if (callData->argc() == 0) THROW_GENERIC_ERROR("Qt.callLater: no arguments given"); const QV4::FunctionObject *func = callData->args[0].as<QV4::FunctionObject>(); @@ -158,8 +160,8 @@ void QQmlDelayedCallQueue::addUniquelyAndExecuteLater(const QV4::BuiltinFunction dfc.m_guarded = true; } else if (func->scope()->type == QV4::Heap::ExecutionContext::Type_QmlContext) { QV4::QmlContext::Data *g = static_cast<QV4::QmlContext::Data *>(func->scope()); - Q_ASSERT(g->qml->scopeObject); - dfc.m_objectGuard = QQmlGuard<QObject>(g->qml->scopeObject); + Q_ASSERT(g->qml()->scopeObject); + dfc.m_objectGuard = QQmlGuard<QObject>(g->qml()->scopeObject); dfc.m_guarded = true; } } @@ -169,22 +171,21 @@ void QQmlDelayedCallQueue::addUniquelyAndExecuteLater(const QV4::BuiltinFunction m_tickedMethod.invoke(this, Qt::QueuedConnection); m_callbackOutstanding = true; } - scope.result = QV4::Encode::undefined(); + return QV4::Encode::undefined(); } void QQmlDelayedCallQueue::storeAnyArguments(DelayedFunctionCall &dfc, const QV4::CallData *callData, int offset, QV4::ExecutionEngine *engine) { - const int length = callData->argc - offset; + const int length = callData->argc() - offset; if (length == 0) { dfc.m_args.clear(); return; } QV4::Scope scope(engine); QV4::ScopedArrayObject array(scope, engine->newArrayObject(length)); - int i = 0; - for (int j = offset; j < callData->argc; ++i, ++j) { + uint i = 0; + for (int j = offset, ej = callData->argc(); j < ej; ++i, ++j) array->putIndexed(i, callData->args[j]); - } dfc.m_args.set(engine, array); } diff --git a/src/qml/qml/qqmldelayedcallqueue_p.h b/src/qml/qml/qqmldelayedcallqueue_p.h index cffde4f0c0..5b3043cfed 100644 --- a/src/qml/qml/qqmldelayedcallqueue_p.h +++ b/src/qml/qml/qqmldelayedcallqueue_p.h @@ -70,7 +70,7 @@ public: void init(QV4::ExecutionEngine *); - void addUniquelyAndExecuteLater(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + QV4::ReturnedValue addUniquelyAndExecuteLater(const QV4::BuiltinFunction *, QV4::CallData *callData); public Q_SLOTS: void ticked(); diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp index 1e1fbcf448..35dbaccbbe 100644 --- a/src/qml/qml/qqmlexpression.cpp +++ b/src/qml/qml/qqmlexpression.cpp @@ -247,15 +247,14 @@ void QQmlExpression::setExpression(const QString &expression) } // Must be called with a valid handle scope -void QQmlExpressionPrivate::v4value(bool *isUndefined, QV4::Scope &scope) +QV4::ReturnedValue QQmlExpressionPrivate::v4value(bool *isUndefined) { if (!expressionFunctionValid) { createQmlBinding(context(), scopeObject(), expression, url, line); expressionFunctionValid = true; } - QV4::ScopedCallData callData(scope); - evaluate(callData, isUndefined, scope); + return evaluate(isUndefined); } QVariant QQmlExpressionPrivate::value(bool *isUndefined) @@ -274,9 +273,9 @@ QVariant QQmlExpressionPrivate::value(bool *isUndefined) { QV4::Scope scope(QV8Engine::getV4(ep->v8engine())); - v4value(isUndefined, scope); + QV4::ScopedValue result(scope, v4value(isUndefined)); if (!hasError()) - rv = scope.engine->toVariant(scope.result, -1); + rv = scope.engine->toVariant(result, -1); } ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. diff --git a/src/qml/qml/qqmlexpression_p.h b/src/qml/qml/qqmlexpression_p.h index 44342a957b..a94ca0fc2d 100644 --- a/src/qml/qml/qqmlexpression_p.h +++ b/src/qml/qml/qqmlexpression_p.h @@ -75,7 +75,7 @@ public: QVariant value(bool *isUndefined = 0); - void v4value(bool *isUndefined, QV4::Scope &scope); + QV4::ReturnedValue v4value(bool *isUndefined = 0); static inline QQmlExpressionPrivate *get(QQmlExpression *expr); static inline QQmlExpression *get(QQmlExpressionPrivate *expr); diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 31c277d283..006611e089 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -46,6 +46,7 @@ #include <private/qv4script_p.h> #include <private/qv4errorobject_p.h> #include <private/qv4scopedvalue_p.h> +#include <private/qv4jscall_p.h> #include <private/qqmlglobal_p.h> #include <private/qv4qobjectwrapper_p.h> #include <private/qqmlbuiltinfunctions_p.h> @@ -180,9 +181,16 @@ void QQmlJavaScriptExpression::refresh() { } +QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(bool *isUndefined) +{ + QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_context->engine); + QV4::Scope scope(v4); + QV4::JSCallData jsCall(scope); + return evaluate(jsCall.callData(), isUndefined); +} -void QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefined, QV4::Scope &scope) +QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefined) { Q_ASSERT(m_context && m_context->engine); @@ -190,7 +198,7 @@ void QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefin if (!v4Function) { if (isUndefined) *isUndefined = true; - return; + return QV4::Encode::undefined(); } QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_context->engine); @@ -210,19 +218,20 @@ void QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefin capture.guards.copyAndClearPrepend(activeGuards); QV4::ExecutionEngine *v4 = QV8Engine::getV4(ep->v8engine()); - scope.result = QV4::Primitive::undefinedValue(); callData->thisObject = v4->globalObject; if (scopeObject()) { - QV4::ScopedValue value(scope, QV4::QObjectWrapper::wrap(v4, scopeObject())); - if (value->isObject()) - callData->thisObject = value; + QV4::ReturnedValue scope = QV4::QObjectWrapper::wrap(v4, scopeObject()); + if (QV4::Value::fromReturnedValue(scope).isObject()) + callData->thisObject = scope; } - QV4::ExecutionContext *outer = static_cast<QV4::ExecutionContext *>(m_qmlScope.valueRef()); - if (v4Function->canUseSimpleFunction()) { - outer->simpleCall(scope, callData, v4Function); - } else { - outer->call(scope, callData, v4Function); + Q_ASSERT(m_qmlScope.valueRef()); + QV4::ReturnedValue res = v4Function->call(&callData->thisObject, callData->args, callData->argc(), static_cast<QV4::ExecutionContext *>(m_qmlScope.valueRef())); + QV4::Scope scope(v4); + QV4::ScopedValue result(scope, res); + if (v4Function->hasQmlDependencies) { + QV4::Heap::QmlContext *qc = m_qmlScope.as<QV4::QmlContext>()->d(); + QQmlPropertyCapture::registerQmlDependencies(qc, v4, v4Function->compiledFunction); } if (scope.hasException()) { @@ -234,7 +243,7 @@ void QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefin *isUndefined = true; } else { if (isUndefined) - *isUndefined = scope.result.isUndefined(); + *isUndefined = result->isUndefined(); if (!watcher.wasDeleted() && hasDelayedError()) delayedError()->clearError(); @@ -251,6 +260,8 @@ void QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefin g->Delete(); ep->propertyCapture = lastPropertyCapture; + + return result->asReturnedValue(); } void QQmlPropertyCapture::captureProperty(QQmlNotifier *n, Duration duration) @@ -329,12 +340,11 @@ void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, Duration dur } } -void QQmlPropertyCapture::registerQmlDependencies(const QV4::CompiledData::Function *compiledFunction, const QV4::Scope &scope) +void QQmlPropertyCapture::registerQmlDependencies(QV4::Heap::QmlContext *context, const QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction) { // Let the caller check and avoid the function call :) Q_ASSERT(compiledFunction->hasQmlDependencies()); - QV4::ExecutionEngine *engine = scope.engine; QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->qmlEngine()); if (!ep) return; @@ -347,8 +357,8 @@ void QQmlPropertyCapture::registerQmlDependencies(const QV4::CompiledData::Funct capture->expression->m_permanentDependenciesRegistered = true; - QV4::Scoped<QV4::QmlContext> context(scope, engine->qmlContext()); - QQmlContextData *qmlContext = context->qmlContext(); + QV4::Heap::QQmlContextWrapper *wrapper = context->qml(); + QQmlContextData *qmlContext = wrapper->context->contextData(); const quint32_le *idObjectDependency = compiledFunction->qmlIdObjectDependencyTable(); const int idObjectDependencyCount = compiledFunction->nDependingIdObjects; @@ -368,7 +378,7 @@ void QQmlPropertyCapture::registerQmlDependencies(const QV4::CompiledData::Funct QQmlPropertyCapture::Permanently); } - QObject *scopeObject = context->qmlScope(); + QObject *scopeObject = wrapper->scopeObject; const quint32_le *scopePropertyDependency = compiledFunction->qmlScopePropertiesDependencyTable(); const int scopePropertyDependencyCount = compiledFunction->nDependingScopeProperties; for (int i = 0; i < scopePropertyDependencyCount; ++i) { diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index e9a9f4feee..1cb6d7bfd1 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -103,7 +103,8 @@ public: virtual QString expressionIdentifier() const = 0; virtual void expressionChanged() = 0; - void evaluate(QV4::CallData *callData, bool *isUndefined, QV4::Scope &scope); + QV4::ReturnedValue evaluate(bool *isUndefined); + QV4::ReturnedValue evaluate(QV4::CallData *callData, bool *isUndefined); inline bool notifyOnValueChanged() const; @@ -204,7 +205,7 @@ public: Permanently }; - static void registerQmlDependencies(const QV4::CompiledData::Function *compiledFunction, const QV4::Scope &scope); + static void registerQmlDependencies(QV4::Heap::QmlContext *context, const QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction); void captureProperty(QQmlNotifier *, Duration duration = OnlyOnce); void captureProperty(QObject *, int, int, Duration duration = OnlyOnce, bool doNotify = true); diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp index 43677e0d78..b4be83a156 100644 --- a/src/qml/qml/qqmllistwrapper.cpp +++ b/src/qml/qml/qqmllistwrapper.cpp @@ -171,8 +171,9 @@ void PropertyListPrototype::init(ExecutionEngine *) defineDefaultProperty(QStringLiteral("push"), method_push, 1); } -void PropertyListPrototype::method_push(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue PropertyListPrototype::method_push(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); @@ -183,12 +184,13 @@ void PropertyListPrototype::method_push(const BuiltinFunction *, Scope &scope, C THROW_GENERIC_ERROR("List doesn't define an Append function"); QV4::ScopedObject so(scope); - for (int i = 0; i < callData->argc; ++i) + for (int i = 0, ei = callData->argc(); i < ei; ++i) { so = callData->args[i].toObject(scope.engine); if (QV4::QObjectWrapper *wrapper = so->as<QV4::QObjectWrapper>()) w->d()->property().append(&w->d()->property(), wrapper->object() ); } + return Encode::undefined(); } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmllistwrapper_p.h b/src/qml/qml/qqmllistwrapper_p.h index 84dadba01a..0b53395d2b 100644 --- a/src/qml/qml/qqmllistwrapper_p.h +++ b/src/qml/qml/qqmllistwrapper_p.h @@ -103,7 +103,7 @@ struct PropertyListPrototype : Object { void init(ExecutionEngine *engine); - static void method_push(const BuiltinFunction *, Scope &, CallData *callData); + static ReturnedValue method_push(const BuiltinFunction *, CallData *callData); }; } diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp index 326b36c5cd..3f2a373966 100644 --- a/src/qml/qml/qqmllocale.cpp +++ b/src/qml/qml/qqmllocale.cpp @@ -58,8 +58,7 @@ DEFINE_OBJECT_VTABLE(QQmlLocaleData); #define THROW_ERROR(string) \ do { \ - scope.result = scope.engine->throwError(QString::fromUtf8(string)); \ - return; \ + return scope.engine->throwError(QString::fromUtf8(string)); \ } while (false) @@ -87,37 +86,32 @@ void QQmlDateExtension::registerExtension(QV4::ExecutionEngine *engine) engine->dateCtor()->defineDefaultProperty(QStringLiteral("timeZoneUpdated"), method_timeZoneUpdated); } -void QQmlDateExtension::method_toLocaleString(const BuiltinFunction *b, Scope &scope, CallData *callData) +ReturnedValue QQmlDateExtension::method_toLocaleString(const BuiltinFunction *b, CallData *callData) { - if (callData->argc > 2) { - QV4::DatePrototype::method_toLocaleString(b, scope, callData); - return; - } + Scope scope(b); + if (callData->argc() > 2) + return QV4::DatePrototype::method_toLocaleString(b, &callData->thisObject, callData->args, callData->argc()); QV4::DateObject *date = callData->thisObject.as<DateObject>(); - if (!date) { - QV4::DatePrototype::method_toLocaleString(b, scope, callData); - return; - } + if (!date) + return QV4::DatePrototype::method_toLocaleString(b, &callData->thisObject, callData->args, callData->argc()); QDateTime dt = date->toQDateTime(); - if (callData->argc == 0) { + if (callData->argc() == 0) { // Use QLocale for standard toLocaleString() function QLocale locale; RETURN_RESULT(scope.engine->newString(locale.toString(dt))); } - if (!isLocaleObject(callData->args[0])) { - QV4::DatePrototype::method_toLocaleString(b, scope, callData); // Use the default Date toLocaleString() - return; - } + if (!isLocaleObject(callData->args[0])) + return QV4::DatePrototype::method_toLocaleString(b, &callData->thisObject, callData->args, callData->argc()); // Use the default Date toLocaleString() GET_LOCALE_DATA_RESOURCE(callData->args[0]); QLocale::FormatType enumFormat = QLocale::LongFormat; QString formattedDt; - if (callData->argc == 2) { + if (callData->argc() == 2) { if (String *s = callData->args[1].stringValue()) { QString format = s->toQString(); formattedDt = r->d()->locale->toString(dt, format); @@ -132,39 +126,36 @@ void QQmlDateExtension::method_toLocaleString(const BuiltinFunction *b, Scope &s formattedDt = r->d()->locale->toString(dt, enumFormat); } - scope.result = scope.engine->newString(formattedDt); + RETURN_RESULT(scope.engine->newString(formattedDt)); } -void QQmlDateExtension::method_toLocaleTimeString(const BuiltinFunction *b, Scope &scope, CallData *callData) +ReturnedValue QQmlDateExtension::method_toLocaleTimeString(const BuiltinFunction *b, CallData *callData) { - if (callData->argc > 2) { - QV4::DatePrototype::method_toLocaleTimeString(b, scope, callData); - return; - } + Scope scope(b); + if (callData->argc() > 2) + return QV4::DatePrototype::method_toLocaleTimeString(b, &callData->thisObject, callData->args, callData->argc()); QV4::DateObject *date = callData->thisObject.as<DateObject>(); - if (!date) { - QV4::DatePrototype::method_toLocaleTimeString(b, scope, callData); - return; - } + if (!date) + return QV4::DatePrototype::method_toLocaleTimeString(b, &callData->thisObject, callData->args, callData->argc()); QDateTime dt = date->toQDateTime(); QTime time = dt.time(); - if (callData->argc == 0) { + if (callData->argc() == 0) { // Use QLocale for standard toLocaleString() function QLocale locale; RETURN_RESULT(scope.engine->newString(locale.toString(time))); } if (!isLocaleObject(callData->args[0])) - return QV4::DatePrototype::method_toLocaleTimeString(b, scope, callData); // Use the default Date toLocaleTimeString() + return QV4::DatePrototype::method_toLocaleTimeString(b, &callData->thisObject, callData->args, callData->argc()); // Use the default Date toLocaleTimeString() GET_LOCALE_DATA_RESOURCE(callData->args[0]); QLocale::FormatType enumFormat = QLocale::LongFormat; QString formattedTime; - if (callData->argc == 2) { + if (callData->argc() == 2) { if (String *s = callData->args[1].stringValue()) { QString format = s->toQString(); formattedTime = r->d()->locale->toString(time, format); @@ -179,39 +170,36 @@ void QQmlDateExtension::method_toLocaleTimeString(const BuiltinFunction *b, Scop formattedTime = r->d()->locale->toString(time, enumFormat); } - scope.result = scope.engine->newString(formattedTime); + RETURN_RESULT(scope.engine->newString(formattedTime)); } -void QQmlDateExtension::method_toLocaleDateString(const BuiltinFunction *b, Scope &scope, CallData *callData) +ReturnedValue QQmlDateExtension::method_toLocaleDateString(const BuiltinFunction *b, CallData *callData) { - if (callData->argc > 2) { - QV4::DatePrototype::method_toLocaleDateString(b, scope, callData); - return; - } + Scope scope(b); + if (callData->argc() > 2) + return QV4::DatePrototype::method_toLocaleDateString(b, &callData->thisObject, callData->args, callData->argc()); QV4::DateObject *dateObj = callData->thisObject.as<DateObject>(); - if (!dateObj) { - QV4::DatePrototype::method_toLocaleDateString(b, scope, callData); - return; - } + if (!dateObj) + return QV4::DatePrototype::method_toLocaleDateString(b, &callData->thisObject, callData->args, callData->argc()); QDateTime dt = dateObj->toQDateTime(); QDate date = dt.date(); - if (callData->argc == 0) { + if (callData->argc() == 0) { // Use QLocale for standard toLocaleString() function QLocale locale; RETURN_RESULT(scope.engine->newString(locale.toString(date))); } if (!isLocaleObject(callData->args[0])) - return QV4::DatePrototype::method_toLocaleDateString(b, scope, callData); // Use the default Date toLocaleDateString() + return QV4::DatePrototype::method_toLocaleDateString(b, &callData->thisObject, callData->args, callData->argc()); // Use the default Date toLocaleDateString() GET_LOCALE_DATA_RESOURCE(callData->args[0]); QLocale::FormatType enumFormat = QLocale::LongFormat; QString formattedDate; - if (callData->argc == 2) { + if (callData->argc() == 2) { if (String *s = callData->args[1].stringValue()) { QString format = s->toQString(); formattedDate = r->d()->locale->toString(date, format); @@ -226,13 +214,14 @@ void QQmlDateExtension::method_toLocaleDateString(const BuiltinFunction *b, Scop formattedDate = r->d()->locale->toString(date, enumFormat); } - scope.result = scope.engine->newString(formattedDate); + RETURN_RESULT(scope.engine->newString(formattedDate)); } -void QQmlDateExtension::method_fromLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QQmlDateExtension::method_fromLocaleString(const BuiltinFunction *b, CallData *callData) { + QV4::Scope scope(b); QV4::ExecutionEngine * const engine = scope.engine; - if (callData->argc == 1) { + if (callData->argc() == 1) { if (String *s = callData->args[0].stringValue()) { QLocale locale; QString dateString = s->toQString(); @@ -241,7 +230,7 @@ void QQmlDateExtension::method_fromLocaleString(const BuiltinFunction *, Scope & } } - if (callData->argc < 1 || callData->argc > 3 || !isLocaleObject(callData->args[0])) + if (callData->argc() < 1 || callData->argc() > 3 || !isLocaleObject(callData->args[0])) THROW_ERROR("Locale: Date.fromLocaleString(): Invalid arguments"); GET_LOCALE_DATA_RESOURCE(callData->args[0]); @@ -249,7 +238,7 @@ void QQmlDateExtension::method_fromLocaleString(const BuiltinFunction *, Scope & QLocale::FormatType enumFormat = QLocale::LongFormat; QDateTime dt; QString dateString = callData->args[1].toQStringNoThrow(); - if (callData->argc == 3) { + if (callData->argc() == 3) { if (String *s = callData->args[2].stringValue()) { QString format = s->toQString(); dt = r->d()->locale->toDateTime(dateString, format); @@ -264,14 +253,15 @@ void QQmlDateExtension::method_fromLocaleString(const BuiltinFunction *, Scope & dt = r->d()->locale->toDateTime(dateString, enumFormat); } - scope.result = engine->newDateObject(dt); + RETURN_RESULT(engine->newDateObject(dt)); } -void QQmlDateExtension::method_fromLocaleTimeString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QQmlDateExtension::method_fromLocaleTimeString(const BuiltinFunction *b, CallData *callData) { + QV4::Scope scope(b); QV4::ExecutionEngine * const engine = scope.engine; - if (callData->argc == 1) { + if (callData->argc() == 1) { if (String *s = callData->args[0].stringValue()) { QLocale locale; QString timeString = s->toQString(); @@ -282,7 +272,7 @@ void QQmlDateExtension::method_fromLocaleTimeString(const BuiltinFunction *, Sco } } - if (callData->argc < 1 || callData->argc > 3 || !isLocaleObject(callData->args[0])) + if (callData->argc() < 1 || callData->argc() > 3 || !isLocaleObject(callData->args[0])) THROW_ERROR("Locale: Date.fromLocaleTimeString(): Invalid arguments"); GET_LOCALE_DATA_RESOURCE(callData->args[0]); @@ -290,7 +280,7 @@ void QQmlDateExtension::method_fromLocaleTimeString(const BuiltinFunction *, Sco QLocale::FormatType enumFormat = QLocale::LongFormat; QTime tm; QString dateString = callData->args[1].toQStringNoThrow(); - if (callData->argc == 3) { + if (callData->argc() == 3) { if (String *s = callData->args[2].stringValue()) { QString format = s->toQString(); tm = r->d()->locale->toTime(dateString, format); @@ -314,11 +304,12 @@ void QQmlDateExtension::method_fromLocaleTimeString(const BuiltinFunction *, Sco RETURN_RESULT(engine->newDateObject(dt)); } -void QQmlDateExtension::method_fromLocaleDateString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QQmlDateExtension::method_fromLocaleDateString(const BuiltinFunction *b, CallData *callData) { + QV4::Scope scope(b); QV4::ExecutionEngine * const engine = scope.engine; - if (callData->argc == 1) { + if (callData->argc() == 1) { if (String *s = callData->args[0].stringValue()) { QLocale locale; QString dateString = s->toQString(); @@ -327,7 +318,7 @@ void QQmlDateExtension::method_fromLocaleDateString(const BuiltinFunction *, Sco } } - if (callData->argc < 1 || callData->argc > 3 || !isLocaleObject(callData->args[0])) + if (callData->argc() < 1 || callData->argc() > 3 || !isLocaleObject(callData->args[0])) THROW_ERROR("Locale: Date.fromLocaleDateString(): Invalid arguments"); GET_LOCALE_DATA_RESOURCE(callData->args[0]); @@ -335,7 +326,7 @@ void QQmlDateExtension::method_fromLocaleDateString(const BuiltinFunction *, Sco QLocale::FormatType enumFormat = QLocale::LongFormat; QDate dt; QString dateString = callData->args[1].toQStringNoThrow(); - if (callData->argc == 3) { + if (callData->argc() == 3) { if (String *s = callData->args[2].stringValue()) { QString format = s->toQString(); dt = r->d()->locale->toDate(dateString, format); @@ -353,9 +344,10 @@ void QQmlDateExtension::method_fromLocaleDateString(const BuiltinFunction *, Sco RETURN_RESULT(engine->newDateObject(QDateTime(dt))); } -void QQmlDateExtension::method_timeZoneUpdated(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QQmlDateExtension::method_timeZoneUpdated(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 0) + QV4::Scope scope(b); + if (callData->argc() != 0) THROW_ERROR("Locale: Date.timeZoneUpdated(): Invalid arguments"); QV4::DatePrototype::timezoneUpdated(); @@ -373,28 +365,27 @@ void QQmlNumberExtension::registerExtension(QV4::ExecutionEngine *engine) engine->numberCtor()->defineDefaultProperty(QStringLiteral("fromLocaleString"), method_fromLocaleString); } -void QQmlNumberExtension::method_toLocaleString(const BuiltinFunction *b, Scope &scope, CallData *callData) +QV4::ReturnedValue QQmlNumberExtension::method_toLocaleString(const BuiltinFunction *b, CallData *callData) { - if (callData->argc > 3) + QV4::Scope scope(b); + if (callData->argc() > 3) THROW_ERROR("Locale: Number.toLocaleString(): Invalid arguments"); double number = callData->thisObject.toNumber(); - if (callData->argc == 0) { + if (callData->argc() == 0) { // Use QLocale for standard toLocaleString() function QLocale locale; RETURN_RESULT(scope.engine->newString(locale.toString(number))); } - if (!isLocaleObject(callData->args[0])) { - QV4::NumberPrototype::method_toLocaleString(b, scope, callData); // Use the default Number toLocaleString() - return; - } + if (!isLocaleObject(callData->args[0])) + return QV4::NumberPrototype::method_toLocaleString(b, &callData->thisObject, callData->args, callData->argc()); // Use the default Number toLocaleString() GET_LOCALE_DATA_RESOURCE(callData->args[0]); quint16 format = 'f'; - if (callData->argc > 1) { + if (callData->argc() > 1) { if (!callData->args[1].isString()) THROW_ERROR("Locale: Number.toLocaleString(): Invalid arguments"); QString fs = callData->args[1].toQString(); @@ -402,23 +393,24 @@ void QQmlNumberExtension::method_toLocaleString(const BuiltinFunction *b, Scope format = fs.at(0).unicode(); } int prec = 2; - if (callData->argc > 2) { + if (callData->argc() > 2) { if (!callData->args[2].isNumber()) THROW_ERROR("Locale: Number.toLocaleString(): Invalid arguments"); prec = callData->args[2].toInt32(); } - scope.result = scope.engine->newString(r->d()->locale->toString(number, (char)format, prec)); + RETURN_RESULT(scope.engine->newString(r->d()->locale->toString(number, (char)format, prec))); } -void QQmlNumberExtension::method_toLocaleCurrencyString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QQmlNumberExtension::method_toLocaleCurrencyString(const BuiltinFunction *b, CallData *callData) { - if (callData->argc > 2) + QV4::Scope scope(b); + if (callData->argc() > 2) THROW_ERROR("Locale: Number.toLocaleCurrencyString(): Invalid arguments"); double number = callData->thisObject.toNumber(); - if (callData->argc == 0) { + if (callData->argc() == 0) { // Use QLocale for standard toLocaleString() function QLocale locale; RETURN_RESULT(scope.engine->newString(locale.toString(number))); @@ -430,7 +422,7 @@ void QQmlNumberExtension::method_toLocaleCurrencyString(const BuiltinFunction *, GET_LOCALE_DATA_RESOURCE(callData->args[0]); QString symbol; - if (callData->argc > 1) { + if (callData->argc() > 1) { if (!callData->args[1].isString()) THROW_ERROR("Locale: Number.toLocaleString(): Invalid arguments"); symbol = callData->args[1].toQStringNoThrow(); @@ -439,15 +431,16 @@ void QQmlNumberExtension::method_toLocaleCurrencyString(const BuiltinFunction *, RETURN_RESULT(scope.engine->newString(r->d()->locale->toCurrencyString(number, symbol))); } -void QQmlNumberExtension::method_fromLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QQmlNumberExtension::method_fromLocaleString(const BuiltinFunction *b, CallData *callData) { - if (callData->argc < 1 || callData->argc > 2) + QV4::Scope scope(b); + if (callData->argc() < 1 || callData->argc() > 2) THROW_ERROR("Locale: Number.fromLocaleString(): Invalid arguments"); int numberIdx = 0; QLocale locale; - if (callData->argc == 2) { + if (callData->argc() == 2) { if (!isLocaleObject(callData->args[0])) THROW_ERROR("Locale: Number.fromLocaleString(): Invalid arguments"); @@ -467,45 +460,49 @@ void QQmlNumberExtension::method_fromLocaleString(const BuiltinFunction *, Scope if (!ok) THROW_ERROR("Locale: Number.fromLocaleString(): Invalid format"); - scope.result = QV4::Encode(val); + RETURN_RESULT(QV4::Encode(val)); } //-------------- // Locale object -void QQmlLocaleData::method_get_firstDayOfWeek(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QQmlLocaleData::method_get_firstDayOfWeek(const BuiltinFunction *b, CallData *callData) { + QV4::Scope scope(b); QLocale *locale = getThisLocale(scope, callData); if (!locale) - return; + return Encode::undefined(); int fdow = int(locale->firstDayOfWeek()); if (fdow == 7) fdow = 0; // Qt::Sunday = 7, but Sunday is 0 in JS Date - scope.result = QV4::Encode(fdow); + RETURN_RESULT(fdow); } -void QQmlLocaleData::method_get_measurementSystem(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QQmlLocaleData::method_get_measurementSystem(const BuiltinFunction *b, CallData *callData) { + QV4::Scope scope(b); QLocale *locale = getThisLocale(scope, callData); if (!locale) - return; - scope.result = QV4::Encode(locale->measurementSystem()); + return Encode::undefined(); + return QV4::Encode(locale->measurementSystem()); } -void QQmlLocaleData::method_get_textDirection(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QQmlLocaleData::method_get_textDirection(const BuiltinFunction *b, CallData *callData) { + QV4::Scope scope(b); QLocale *locale = getThisLocale(scope, callData); if (!locale) - return; + return Encode::undefined(); - scope.result = QV4::Encode(locale->textDirection()); + return QV4::Encode(locale->textDirection()); } -void QQmlLocaleData::method_get_weekDays(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QQmlLocaleData::method_get_weekDays(const BuiltinFunction *b, CallData *callData) { + QV4::Scope scope(b); QLocale *locale = getThisLocale(scope, callData); if (!locale) - return; + return Encode::undefined(); QList<Qt::DayOfWeek> days = locale->weekdays(); @@ -519,14 +516,15 @@ void QQmlLocaleData::method_get_weekDays(const BuiltinFunction *, Scope &scope, } result->setArrayLengthUnchecked(days.size()); - scope.result = result.asReturnedValue(); + return result.asReturnedValue(); } -void QQmlLocaleData::method_get_uiLanguages(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QQmlLocaleData::method_get_uiLanguages(const BuiltinFunction *b, CallData *callData) { + QV4::Scope scope(b); QLocale *locale = getThisLocale(scope, callData); if (!locale) - return; + return Encode::undefined(); QStringList langs = locale->uiLanguages(); QV4::ScopedArrayObject result(scope, scope.engine->newArrayObject()); @@ -537,40 +535,42 @@ void QQmlLocaleData::method_get_uiLanguages(const BuiltinFunction *, Scope &scop result->setArrayLengthUnchecked(langs.size()); - scope.result = result.asReturnedValue(); + return result.asReturnedValue(); } -void QQmlLocaleData::method_currencySymbol(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QQmlLocaleData::method_currencySymbol(const BuiltinFunction *b, CallData *callData) { + QV4::Scope scope(b); QLocale *locale = getThisLocale(scope, callData); if (!locale) - return; + return Encode::undefined(); - if (callData->argc > 1) + if (callData->argc() > 1) THROW_ERROR("Locale: currencySymbol(): Invalid arguments"); QLocale::CurrencySymbolFormat format = QLocale::CurrencySymbol; - if (callData->argc == 1) { + if (callData->argc() == 1) { quint32 intFormat = callData->args[0].toNumber(); format = QLocale::CurrencySymbolFormat(intFormat); } - scope.result = scope.engine->newString(locale->currencySymbol(format)); + RETURN_RESULT(scope.engine->newString(locale->currencySymbol(format))); } #define LOCALE_FORMAT(FUNC) \ -void QQmlLocaleData::method_ ##FUNC (const BuiltinFunction *, Scope &scope, CallData *callData) { \ +ReturnedValue QQmlLocaleData::method_ ##FUNC (const BuiltinFunction *b, CallData *callData) { \ + QV4::Scope scope(b); \ QLocale *locale = getThisLocale(scope, callData); \ if (!locale) \ - return; \ - if (callData->argc > 1) \ + return Encode::undefined(); \ + if (callData->argc() > 1) \ THROW_ERROR("Locale: " #FUNC "(): Invalid arguments"); \ QLocale::FormatType format = QLocale::LongFormat;\ - if (callData->argc == 1) { \ + if (callData->argc() == 1) { \ quint32 intFormat = callData->args[0].toUInt32(); \ format = QLocale::FormatType(intFormat); \ } \ - scope.result = scope.engine->newString(locale-> FUNC (format)); \ + RETURN_RESULT(scope.engine->newString(locale-> FUNC (format))); \ } LOCALE_FORMAT(dateTimeFormat) @@ -579,18 +579,19 @@ LOCALE_FORMAT(dateFormat) // +1 added to idx because JS is 0-based, whereas QLocale months begin at 1. #define LOCALE_FORMATTED_MONTHNAME(VARIABLE) \ -void QQmlLocaleData::method_ ## VARIABLE (const BuiltinFunction *, Scope &scope, CallData *callData) {\ +ReturnedValue QQmlLocaleData::method_ ## VARIABLE (const BuiltinFunction *b, CallData *callData) {\ + Scope scope(b); \ QLocale *locale = getThisLocale(scope, callData); \ if (!locale) \ - return; \ - if (callData->argc < 1 || callData->argc > 2) \ + return Encode::undefined(); \ + if (callData->argc() < 1 || callData->argc() > 2) \ THROW_ERROR("Locale: " #VARIABLE "(): Invalid arguments"); \ QLocale::FormatType enumFormat = QLocale::LongFormat; \ int idx = callData->args[0].toInt32() + 1; \ if (idx < 1 || idx > 12) \ THROW_ERROR("Locale: Invalid month"); \ QString name; \ - if (callData->argc == 2) { \ + if (callData->argc() == 2) { \ if (callData->args[1].isNumber()) { \ quint32 intFormat = callData->args[1].toUInt32(); \ QLocale::FormatType format = QLocale::FormatType(intFormat); \ @@ -601,16 +602,17 @@ void QQmlLocaleData::method_ ## VARIABLE (const BuiltinFunction *, Scope &scope, } else { \ name = locale-> VARIABLE(idx, enumFormat); \ } \ - scope.result = scope.engine->newString(name); \ + RETURN_RESULT(scope.engine->newString(name)); \ } // 0 -> 7 as Qt::Sunday is 7, but Sunday is 0 in JS Date #define LOCALE_FORMATTED_DAYNAME(VARIABLE) \ -void QQmlLocaleData::method_ ## VARIABLE (const BuiltinFunction *, Scope &scope, CallData *callData) {\ +ReturnedValue QQmlLocaleData::method_ ## VARIABLE (const BuiltinFunction *b, CallData *callData) {\ + Scope scope(b); \ QLocale *locale = getThisLocale(scope, callData); \ if (!locale) \ - return; \ - if (callData->argc < 1 || callData->argc > 2) \ + return Encode::undefined(); \ + if (callData->argc() < 1 || callData->argc() > 2) \ THROW_ERROR("Locale: " #VARIABLE "(): Invalid arguments"); \ QLocale::FormatType enumFormat = QLocale::LongFormat; \ int idx = callData->args[0].toInt32(); \ @@ -618,7 +620,7 @@ void QQmlLocaleData::method_ ## VARIABLE (const BuiltinFunction *, Scope &scope, THROW_ERROR("Locale: Invalid day"); \ if (idx == 0) idx = 7; \ QString name; \ - if (callData->argc == 2) { \ + if (callData->argc() == 2) { \ if (callData->args[1].isNumber()) { \ quint32 intFormat = callData->args[1].toUInt32(); \ QLocale::FormatType format = QLocale::FormatType(intFormat); \ @@ -629,7 +631,7 @@ void QQmlLocaleData::method_ ## VARIABLE (const BuiltinFunction *, Scope &scope, } else { \ name = locale-> VARIABLE(idx, enumFormat); \ } \ - scope.result = scope.engine->newString(name); \ + RETURN_RESULT(scope.engine->newString(name)); \ } LOCALE_FORMATTED_MONTHNAME(monthName) @@ -637,12 +639,14 @@ LOCALE_FORMATTED_MONTHNAME(standaloneMonthName) LOCALE_FORMATTED_DAYNAME(dayName) LOCALE_FORMATTED_DAYNAME(standaloneDayName) -#define LOCALE_STRING_PROPERTY(VARIABLE) void QQmlLocaleData::method_get_ ## VARIABLE (const BuiltinFunction *, Scope &scope, CallData *callData) \ +#define LOCALE_STRING_PROPERTY(VARIABLE) \ +ReturnedValue QQmlLocaleData::method_get_ ## VARIABLE (const BuiltinFunction *b, CallData *callData) \ { \ + Scope scope(b); \ QLocale *locale = getThisLocale(scope, callData); \ if (!locale) \ - return; \ - scope.result = scope.engine->newString(locale-> VARIABLE());\ + return Encode::undefined(); \ + RETURN_RESULT(scope.engine->newString(locale-> VARIABLE()));\ } LOCALE_STRING_PROPERTY(name) @@ -833,22 +837,18 @@ void QQmlLocale::registerStringLocaleCompare(QV4::ExecutionEngine *engine) engine->stringPrototype()->defineDefaultProperty(QStringLiteral("localeCompare"), method_localeCompare); } -void QQmlLocale::method_localeCompare(const BuiltinFunction *b, Scope &scope, CallData *callData) +ReturnedValue QQmlLocale::method_localeCompare(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 1 || (!callData->args[0].isString() && !callData->args[0].as<StringObject>())) { - QV4::StringPrototype::method_localeCompare(b, scope, callData); - return; - } + if (callData->argc() != 1 || (!callData->args[0].isString() && !callData->args[0].as<StringObject>())) + return QV4::StringPrototype::method_localeCompare(b, &callData->thisObject, callData->args, callData->argc()); - if (!callData->thisObject.isString() && !callData->thisObject.as<StringObject>()) { - QV4::StringPrototype::method_localeCompare(b, scope, callData); - return; - } + if (!callData->thisObject.isString() && !callData->thisObject.as<StringObject>()) + return QV4::StringPrototype::method_localeCompare(b, &callData->thisObject, callData->args, callData->argc()); QString thisString = callData->thisObject.toQStringNoThrow(); QString thatString = callData->args[0].toQStringNoThrow(); - scope.result = QV4::Encode(QString::localeAwareCompare(thisString, thatString)); + return QV4::Encode(QString::localeAwareCompare(thisString, thatString)); } /*! diff --git a/src/qml/qml/qqmllocale_p.h b/src/qml/qml/qqmllocale_p.h index 1a2ffc72b0..a81fe07b8e 100644 --- a/src/qml/qml/qqmllocale_p.h +++ b/src/qml/qml/qqmllocale_p.h @@ -67,13 +67,13 @@ public: static void registerExtension(QV4::ExecutionEngine *engine); private: - static void method_toLocaleString(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_toLocaleTimeString(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_toLocaleDateString(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_fromLocaleString(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_fromLocaleTimeString(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_fromLocaleDateString(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_timeZoneUpdated(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static QV4::ReturnedValue method_toLocaleString(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_toLocaleTimeString(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_toLocaleDateString(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_fromLocaleString(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_fromLocaleTimeString(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_fromLocaleDateString(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_timeZoneUpdated(const QV4::BuiltinFunction *, QV4::CallData *callData); }; @@ -83,9 +83,9 @@ public: static void registerExtension(QV4::ExecutionEngine *engine); private: - static void method_toLocaleString(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_fromLocaleString(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_toLocaleCurrencyString(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static QV4::ReturnedValue method_toLocaleString(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_fromLocaleString(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_toLocaleCurrencyString(const QV4::BuiltinFunction *, QV4::CallData *callData); }; @@ -135,7 +135,7 @@ public: private: QQmlLocale(); - static void method_localeCompare(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static QV4::ReturnedValue method_localeCompare(const QV4::BuiltinFunction *, QV4::CallData *callData); }; namespace QV4 { @@ -168,33 +168,33 @@ struct QQmlLocaleData : public QV4::Object return thisObject->d()->locale; } - static void method_currencySymbol(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_dateTimeFormat(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_timeFormat(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_dateFormat(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_monthName(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_standaloneMonthName(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_dayName(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_standaloneDayName(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - - static void method_get_firstDayOfWeek(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_measurementSystem(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_textDirection(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_weekDays(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_uiLanguages(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - - static void method_get_name(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_nativeLanguageName(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_nativeCountryName(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_decimalPoint(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_groupSeparator(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_percent(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_zeroDigit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_negativeSign(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_positiveSign(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_exponential(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_amText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_pmText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static QV4::ReturnedValue method_currencySymbol(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_dateTimeFormat(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_timeFormat(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_dateFormat(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_monthName(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_standaloneMonthName(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_dayName(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_standaloneDayName(const QV4::BuiltinFunction *, QV4::CallData *callData); + + static QV4::ReturnedValue method_get_firstDayOfWeek(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_get_measurementSystem(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_get_textDirection(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_get_weekDays(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_get_uiLanguages(const QV4::BuiltinFunction *, QV4::CallData *callData); + + static QV4::ReturnedValue method_get_name(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_get_nativeLanguageName(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_get_nativeCountryName(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_get_decimalPoint(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_get_groupSeparator(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_get_percent(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_get_zeroDigit(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_get_negativeSign(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_get_positiveSign(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_get_exponential(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_get_amText(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue method_get_pmText(const QV4::BuiltinFunction *, QV4::CallData *callData); }; } diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 4f69017ff0..5836c85666 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -260,8 +260,6 @@ void QQmlMetaTypeData::registerType(QQmlTypePrivate *priv) void QQmlType::SingletonInstanceInfo::init(QQmlEngine *e) { - QV4::ExecutionEngine *v4 = QV8Engine::getV4(e->handle()); - v4->pushGlobalContext(); if (scriptCallback && scriptApi(e).isUndefined()) { setScriptApi(e, scriptCallback(e, e)); } else if (qobjectCallback && !qobjectApi(e)) { @@ -277,7 +275,6 @@ void QQmlType::SingletonInstanceInfo::init(QQmlEngine *e) QObject *o = component.create(); setQObjectApi(e, o); } - v4->popContext(); } void QQmlType::SingletonInstanceInfo::destroy(QQmlEngine *e) diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 494b3d30bc..55e05afdaa 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -44,6 +44,7 @@ #include <private/qqmlengine_p.h> #include <private/qqmlglobal_p.h> #include <private/qqmlthread_p.h> +#include <private/qv4codegen_p.h> #include <private/qqmlcomponent_p.h> #include <private/qqmlprofiler_p.h> #include <private/qqmlmemoryprofiler_p.h> @@ -2080,10 +2081,10 @@ bool QQmlTypeData::tryLoadFromDiskCache() if (!v4) return false; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = v4->iselFactory->createUnitForLoading(); + QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading(); { QString error; - if (!unit->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), v4->iselFactory.data(), &error)) { + if (!unit->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) { qCDebug(DBG_DISK_CACHE) << "Error loading" << url().toString() << "from disk cache:" << error; return false; } @@ -2447,7 +2448,7 @@ void QQmlTypeData::restoreIR(QQmlRefPointer<QV4::CompiledData::CompilationUnit> 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(); } @@ -2564,7 +2565,7 @@ void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCach QString errorString; if (m_compiledData->saveToDisk(url(), &errorString)) { QString error; - if (!m_compiledData->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), enginePrivate->v4engine()->iselFactory.data(), &error)) { + if (!m_compiledData->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) { // ignore error, keep using the in-memory compilation unit. } } else { @@ -2881,7 +2882,7 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent ep->warning(error); } - QV4::ScopedValue retval(scope, qmlContext->d()->qml); + QV4::ScopedValue retval(scope, qmlContext->d()->qml()); if (shared) { m_value.set(scope.engine, retval); m_loaded = true; @@ -2923,19 +2924,12 @@ QQmlScriptData *QQmlScriptBlob::scriptData() const return m_scriptData; } -struct EmptyCompilationUnit : public QV4::CompiledData::CompilationUnit -{ - void linkBackendToEngine(QV4::ExecutionEngine *) override {} -}; - void QQmlScriptBlob::dataReceived(const SourceCodeData &data) { - QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_typeLoader->engine()); - if (!disableDiskCache() || forceDiskCache()) { - QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = v4->iselFactory->createUnitForLoading(); + QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading(); QString error; - if (unit->loadFromDisk(url(), data.sourceTimeStamp(), v4->iselFactory.data(), &error)) { + if (unit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) { initializeFromCompilationUnit(unit); return; } else { @@ -2957,7 +2951,7 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data) QmlIR::ScriptDirectivesCollector collector(&irUnit.jsParserEngine, &irUnit.jsGenerator); QList<QQmlError> errors; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Script::precompile(&irUnit.jsModule, &irUnit.jsGenerator, v4, finalUrl(), source, &errors, &collector); + QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Script::precompile(&irUnit.jsModule, &irUnit.jsGenerator, finalUrl(), source, &errors, &collector); // No need to addref on unit, it's initial refcount is 1 source.clear(); if (!errors.isEmpty()) { @@ -2965,7 +2959,7 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data) return; } if (!unit) { - unit.adopt(new EmptyCompilationUnit); + unit.adopt(new QV4::CompiledData::CompilationUnit); } irUnit.javaScriptCompilationUnit = unit; irUnit.imports = collector.imports; diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h index bb65093163..25ff7ba7c8 100644 --- a/src/qml/qml/qqmltypewrapper_p.h +++ b/src/qml/qml/qqmltypewrapper_p.h @@ -60,6 +60,9 @@ QT_BEGIN_NAMESPACE class QQmlTypeNameCache; +class QQmlType; +class QQmlTypePrivate; +struct QQmlImportRef; namespace QV4 { diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index a5cdccc97a..90ca08537c 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -316,8 +316,9 @@ bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const return true; } -void QQmlValueTypeWrapper::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QQmlValueTypeWrapper::method_toString(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); Object *o = callData->thisObject.as<Object>(); if (!o) THROW_TYPE_ERROR(); @@ -350,7 +351,7 @@ void QQmlValueTypeWrapper::method_toString(const BuiltinFunction *, Scope &scope } result += QLatin1Char(')'); } - scope.result = scope.engine->newString(result); + return Encode(scope.engine->newString(result)); } ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *hasProperty) @@ -472,13 +473,13 @@ bool QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) if (auto binding = QQmlPropertyPrivate::binding(referenceObject, QQmlPropertyIndex(referencePropertyIndex, pd->coreIndex()))) { Q_ASSERT(!binding->isValueTypeProxy()); const auto qmlBinding = static_cast<const QQmlBinding*>(binding); - const auto stackFrame = v4->currentStackFrame(); + const auto stackFrame = v4->currentStackFrame; qCInfo(lcBindingRemoval, "Overwriting binding on %s::%s which was initially bound at %s by setting \"%s\" at %s:%d", referenceObject->metaObject()->className(), referenceObject->metaObject()->property(referencePropertyIndex).name(), qPrintable(qmlBinding->expressionIdentifier()), metaObject->property(pd->coreIndex()).name(), - qPrintable(stackFrame.source), stackFrame.line); + qPrintable(stackFrame->source()), stackFrame->lineNumber()); } } QQmlPropertyPrivate::removeBinding(referenceObject, QQmlPropertyIndex(referencePropertyIndex, pd->coreIndex())); diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h index c8aac719ab..da03af6dbc 100644 --- a/src/qml/qml/qqmlvaluetypewrapper_p.h +++ b/src/qml/qml/qqmlvaluetypewrapper_p.h @@ -56,6 +56,7 @@ #include <private/qv4value_p.h> #include <private/qv4object_p.h> +#include <private/qqmlpropertycache_p.h> QT_BEGIN_NAMESPACE @@ -111,7 +112,7 @@ public: static PropertyAttributes query(const Managed *, String *name); static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); - static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_toString(const BuiltinFunction *, CallData *callData); static void initProto(ExecutionEngine *v4); }; diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index dde8784eb1..281d64ac79 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -55,6 +55,7 @@ #include <private/qv4variantobject_p.h> #include <private/qv4functionobject_p.h> #include <private/qv4scopedvalue_p.h> +#include <private/qv4jscall_p.h> #include <private/qv4qobjectwrapper_p.h> QT_BEGIN_NAMESPACE @@ -949,20 +950,20 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * } const unsigned int parameterCount = function->formalParameterCount(); - QV4::ScopedCallData callData(scope, parameterCount); - callData->thisObject = ep->v8engine()->global(); + QV4::JSCallData jsCallData(scope, parameterCount); + *jsCallData->thisObject = ep->v8engine()->global(); for (uint ii = 0; ii < parameterCount; ++ii) - callData->args[ii] = scope.engine->fromVariant(*(QVariant *)a[ii + 1]); + jsCallData->args[ii] = scope.engine->fromVariant(*(QVariant *)a[ii + 1]); - function->call(scope, callData); + QV4::ScopedValue result(scope, function->call(jsCallData)); if (scope.hasException()) { QQmlError error = scope.engine->catchExceptionAsQmlError(); if (error.isValid()) ep->warning(error); if (a[0]) *(QVariant *)a[0] = QVariant(); } else { - if (a[0]) *(QVariant *)a[0] = scope.engine->toVariant(scope.result, 0); + if (a[0]) *(QVariant *)a[0] = scope.engine->toVariant(result, 0); } ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 08f3d35e46..08842e714c 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -51,6 +51,7 @@ #include <private/qv4engine_p.h> #include <private/qv4functionobject_p.h> #include <private/qv4scopedvalue_p.h> +#include <private/qv4jscall_p.h> #include <QtCore/qobject.h> #include <QtQml/qjsvalue.h> @@ -74,8 +75,7 @@ using namespace QV4; #define V4THROW_REFERENCE(string) \ do { \ ScopedObject error(scope, scope.engine->newReferenceErrorObject(QStringLiteral(string))); \ - scope.result = scope.engine->throwError(error); \ - return; \ + return scope.engine->throwError(error); \ } while (false) QT_BEGIN_NAMESPACE @@ -276,25 +276,25 @@ public: static void initClass(ExecutionEngine *engine); // JS API - static void method_get_nodeName(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_nodeValue(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_nodeType(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_namespaceUri(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - - static void method_get_parentNode(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_childNodes(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_firstChild(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_lastChild(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_previousSibling(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_nextSibling(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_attributes(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - - //static void ownerDocument(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - //static void namespaceURI(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - //static void prefix(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - //static void localName(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - //static void baseURI(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - //static void textContent(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static ReturnedValue method_get_nodeName(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_get_nodeValue(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_get_nodeType(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_get_namespaceUri(const BuiltinFunction *b, QV4::CallData *callData); + + static ReturnedValue method_get_parentNode(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_get_childNodes(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_get_firstChild(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_get_lastChild(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_get_previousSibling(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_get_nextSibling(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_get_attributes(const BuiltinFunction *b, QV4::CallData *callData); + + //static ReturnedValue ownerDocument(const BuiltinFunction *b, QV4::CallData *callData); + //static ReturnedValue namespaceURI(const BuiltinFunction *b, QV4::CallData *callData); + //static ReturnedValue prefix(const BuiltinFunction *b, QV4::CallData *callData); + //static ReturnedValue localName(const BuiltinFunction *b, QV4::CallData *callData); + //static ReturnedValue baseURI(const BuiltinFunction *b, QV4::CallData *callData); + //static ReturnedValue textContent(const BuiltinFunction *b, QV4::CallData *callData); static ReturnedValue getProto(ExecutionEngine *v4); @@ -355,10 +355,10 @@ class Attr : public Node { public: // JS API - static void method_name(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static ReturnedValue method_name(const BuiltinFunction *b, QV4::CallData *callData); // static void specified(CallContext *); - static void method_value(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_ownerElement(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static ReturnedValue method_value(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_ownerElement(const BuiltinFunction *b, QV4::CallData *callData); // static void schemaTypeInfo(CallContext *); // static void isId(CallContext *c); @@ -370,7 +370,7 @@ class CharacterData : public Node { public: // JS API - static void method_length(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static ReturnedValue method_length(const BuiltinFunction *b, QV4::CallData *callData); // C++ API static ReturnedValue prototype(ExecutionEngine *v4); @@ -380,8 +380,8 @@ class Text : public CharacterData { public: // JS API - static void method_isElementContentWhitespace(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_wholeText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static ReturnedValue method_isElementContentWhitespace(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_wholeText(const BuiltinFunction *b, QV4::CallData *callData); // C++ API static ReturnedValue prototype(ExecutionEngine *); @@ -398,10 +398,10 @@ class Document : public Node { public: // JS API - static void method_xmlVersion(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_xmlEncoding(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_xmlStandalone(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_documentElement(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static ReturnedValue method_xmlVersion(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_xmlEncoding(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_xmlStandalone(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_documentElement(const BuiltinFunction *b, QV4::CallData *callData); // C++ API static ReturnedValue prototype(ExecutionEngine *); @@ -420,8 +420,9 @@ void NodeImpl::release() document->release(); } -void NodePrototype::method_get_nodeName(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue NodePrototype::method_get_nodeName(const BuiltinFunction *b, QV4::CallData *callData) { + Scope scope(b); Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) THROW_TYPE_ERROR(); @@ -441,11 +442,12 @@ void NodePrototype::method_get_nodeName(const QV4::BuiltinFunction *, QV4::Scope name = r->d()->d->name; break; } - scope.result = Encode(scope.engine->newString(name)); + return Encode(scope.engine->newString(name)); } -void NodePrototype::method_get_nodeValue(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue NodePrototype::method_get_nodeValue(const BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) THROW_TYPE_ERROR(); @@ -459,74 +461,81 @@ void NodePrototype::method_get_nodeValue(const QV4::BuiltinFunction *, QV4::Scop r->d()->d->type == NodeImpl::Notation) RETURN_RESULT(Encode::null()); - scope.result = Encode(scope.engine->newString(r->d()->d->data)); + return Encode(scope.engine->newString(r->d()->d->data)); } -void NodePrototype::method_get_nodeType(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue NodePrototype::method_get_nodeType(const BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) THROW_TYPE_ERROR(); - scope.result = Encode(r->d()->d->type); + return Encode(r->d()->d->type); } -void NodePrototype::method_get_namespaceUri(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue NodePrototype::method_get_namespaceUri(const BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) THROW_TYPE_ERROR(); - scope.result = Encode(scope.engine->newString(r->d()->d->namespaceUri)); + return Encode(scope.engine->newString(r->d()->d->namespaceUri)); } -void NodePrototype::method_get_parentNode(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue NodePrototype::method_get_parentNode(const BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) THROW_TYPE_ERROR(); if (r->d()->d->parent) - scope.result = Node::create(scope.engine, r->d()->d->parent); + return Node::create(scope.engine, r->d()->d->parent); else - scope.result = Encode::null(); + return Encode::null(); } -void NodePrototype::method_get_childNodes(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue NodePrototype::method_get_childNodes(const BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) THROW_TYPE_ERROR(); - scope.result = NodeList::create(scope.engine, r->d()->d); + return NodeList::create(scope.engine, r->d()->d); } -void NodePrototype::method_get_firstChild(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue NodePrototype::method_get_firstChild(const BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) THROW_TYPE_ERROR(); if (r->d()->d->children.isEmpty()) - scope.result = Encode::null(); + return Encode::null(); else - scope.result = Node::create(scope.engine, r->d()->d->children.constFirst()); + return Node::create(scope.engine, r->d()->d->children.constFirst()); } -void NodePrototype::method_get_lastChild(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue NodePrototype::method_get_lastChild(const BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) THROW_TYPE_ERROR(); if (r->d()->d->children.isEmpty()) - scope.result = Encode::null(); + return Encode::null(); else - scope.result = Node::create(scope.engine, r->d()->d->children.constLast()); + return Node::create(scope.engine, r->d()->d->children.constLast()); } -void NodePrototype::method_get_previousSibling(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue NodePrototype::method_get_previousSibling(const BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) THROW_TYPE_ERROR(); @@ -537,18 +546,18 @@ void NodePrototype::method_get_previousSibling(const QV4::BuiltinFunction *, QV4 for (int ii = 0; ii < r->d()->d->parent->children.count(); ++ii) { if (r->d()->d->parent->children.at(ii) == r->d()->d) { if (ii == 0) - scope.result = Encode::null(); + return Encode::null(); else - scope.result = Node::create(scope.engine, r->d()->d->parent->children.at(ii - 1)); - return; + return Node::create(scope.engine, r->d()->d->parent->children.at(ii - 1)); } } - scope.result = Encode::null(); + return Encode::null(); } -void NodePrototype::method_get_nextSibling(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue NodePrototype::method_get_nextSibling(const BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) THROW_TYPE_ERROR(); @@ -559,26 +568,26 @@ void NodePrototype::method_get_nextSibling(const QV4::BuiltinFunction *, QV4::Sc for (int ii = 0; ii < r->d()->d->parent->children.count(); ++ii) { if (r->d()->d->parent->children.at(ii) == r->d()->d) { if ((ii + 1) == r->d()->d->parent->children.count()) - scope.result = Encode::null(); + return Encode::null(); else - scope.result = Node::create(scope.engine, r->d()->d->parent->children.at(ii + 1)); - return; + return Node::create(scope.engine, r->d()->d->parent->children.at(ii + 1)); } } - scope.result = Encode::null(); + return Encode::null(); } -void NodePrototype::method_get_attributes(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue NodePrototype::method_get_attributes(const BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) THROW_TYPE_ERROR(); if (r->d()->d->type != NodeImpl::Element) - scope.result = Encode::null(); + return Encode::null(); else - scope.result = NamedNodeMap::create(scope.engine, r->d()->d, r->d()->d->attributes); + return NamedNodeMap::create(scope.engine, r->d()->d, r->d()->d->attributes); } ReturnedValue NodePrototype::getProto(ExecutionEngine *v4) @@ -659,40 +668,44 @@ ReturnedValue Attr::prototype(ExecutionEngine *engine) return d->attrPrototype.value(); } -void Attr::method_name(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue Attr::method_name(const BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) RETURN_UNDEFINED(); - scope.result = scope.engine->newString(r->d()->d->name); + return Encode(scope.engine->newString(r->d()->d->name)); } -void Attr::method_value(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue Attr::method_value(const BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) RETURN_UNDEFINED(); - scope.result = scope.engine->newString(r->d()->d->data); + return Encode(scope.engine->newString(r->d()->d->data)); } -void Attr::method_ownerElement(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue Attr::method_ownerElement(const BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) RETURN_UNDEFINED(); - scope.result = Node::create(scope.engine, r->d()->d->parent); + return Node::create(scope.engine, r->d()->d->parent); } -void CharacterData::method_length(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue CharacterData::method_length(const BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) RETURN_UNDEFINED(); - scope.result = Encode(r->d()->d->data.length()); + return Encode(r->d()->d->data.length()); } ReturnedValue CharacterData::prototype(ExecutionEngine *v4) @@ -711,22 +724,24 @@ ReturnedValue CharacterData::prototype(ExecutionEngine *v4) return d->characterDataPrototype.value(); } -void Text::method_isElementContentWhitespace(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue Text::method_isElementContentWhitespace(const BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) RETURN_UNDEFINED(); - scope.result = Encode(QStringRef(&r->d()->d->data).trimmed().isEmpty()); + return Encode(QStringRef(&r->d()->d->data).trimmed().isEmpty()); } -void Text::method_wholeText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue Text::method_wholeText(const BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) RETURN_UNDEFINED(); - scope.result = scope.engine->newString(r->d()->d->data); + return Encode(scope.engine->newString(r->d()->d->data)); } ReturnedValue Text::prototype(ExecutionEngine *v4) @@ -952,40 +967,44 @@ ReturnedValue NodeList::create(ExecutionEngine *v4, NodeImpl *data) return (v4->memoryManager->allocObject<NodeList>(data))->asReturnedValue(); } -void Document::method_documentElement(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue Document::method_documentElement(const BuiltinFunction *b, QV4::CallData *callData) { + Scope scope(b); Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r || r->d()->d->type != NodeImpl::Document) RETURN_UNDEFINED(); - scope.result = Node::create(scope.engine, static_cast<DocumentImpl *>(r->d()->d)->root); + return Node::create(scope.engine, static_cast<DocumentImpl *>(r->d()->d)->root); } -void Document::method_xmlStandalone(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue Document::method_xmlStandalone(const BuiltinFunction *b, QV4::CallData *callData) { + Scope scope(b); Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r || r->d()->d->type != NodeImpl::Document) RETURN_UNDEFINED(); - scope.result = Encode(static_cast<DocumentImpl *>(r->d()->d)->isStandalone); + return Encode(static_cast<DocumentImpl *>(r->d()->d)->isStandalone); } -void Document::method_xmlVersion(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue Document::method_xmlVersion(const BuiltinFunction *b, QV4::CallData *callData) { + Scope scope(b); Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r || r->d()->d->type != NodeImpl::Document) RETURN_UNDEFINED(); - scope.result = scope.engine->newString(static_cast<DocumentImpl *>(r->d()->d)->version); + return Encode(scope.engine->newString(static_cast<DocumentImpl *>(r->d()->d)->version)); } -void Document::method_xmlEncoding(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue Document::method_xmlEncoding(const BuiltinFunction *b, QV4::CallData *callData) { + Scope scope(b); Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r || r->d()->d->type != NodeImpl::Document) RETURN_UNDEFINED(); - scope.result = scope.engine->newString(static_cast<DocumentImpl *>(r->d()->d)->encoding); + return Encode(scope.engine->newString(static_cast<DocumentImpl *>(r->d()->d)->encoding)); } class QQmlXMLHttpRequest : public QObject @@ -1557,9 +1576,8 @@ void QQmlXMLHttpRequest::dispatchCallback(Object *thisObj, QQmlContextData *cont return; } - QV4::ScopedCallData callData(scope); - callData->thisObject = Encode::undefined(); - callback->call(scope, callData); + QV4::JSCallData jsCallData(scope); + callback->call(jsCallData); if (scope.engine->hasException) { QQmlError error = scope.engine->catchExceptionAsQmlError(); @@ -1601,7 +1619,7 @@ struct QQmlXMLHttpRequestWrapper : Object { Member(class, Pointer, Object *, proto) DECLARE_HEAP_OBJECT(QQmlXMLHttpRequestCtor, FunctionObject) { - DECLARE_MARK_TABLE(QQmlXMLHttpRequestCtor); + DECLARE_MARKOBJECTS(QQmlXMLHttpRequestCtor); void init(ExecutionEngine *engine); }; @@ -1617,42 +1635,39 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject { V4_OBJECT2(QQmlXMLHttpRequestCtor, FunctionObject) - static void construct(const Managed *that, Scope &scope, QV4::CallData *) + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *, int) { - Scoped<QQmlXMLHttpRequestCtor> ctor(scope, that->as<QQmlXMLHttpRequestCtor>()); - if (!ctor) { - scope.result = scope.engine->throwTypeError(); - return; - } + Scope scope(f->engine()); + const QQmlXMLHttpRequestCtor *ctor = static_cast<const QQmlXMLHttpRequestCtor *>(f); QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->v8Engine->networkAccessManager()); Scoped<QQmlXMLHttpRequestWrapper> w(scope, scope.engine->memoryManager->allocObject<QQmlXMLHttpRequestWrapper>(r)); ScopedObject proto(scope, ctor->d()->proto); w->setPrototype(proto); - scope.result = w.asReturnedValue(); + return w.asReturnedValue(); } - static void call(const Managed *, Scope &scope, QV4::CallData *) { - scope.result = Primitive::undefinedValue(); + static ReturnedValue call(const FunctionObject *, const Value *, const Value *, int) { + return Encode::undefined(); } void setupProto(); - static void method_open(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_setRequestHeader(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_send(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_abort(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_getResponseHeader(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_getAllResponseHeaders(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - - static void method_get_readyState(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_status(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_statusText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_responseText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_responseXML(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_response(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_responseType(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_responseType(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static ReturnedValue method_open(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_setRequestHeader(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_send(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_abort(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_getResponseHeader(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_getAllResponseHeaders(const BuiltinFunction *b, QV4::CallData *callData); + + static ReturnedValue method_get_readyState(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_get_status(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_get_statusText(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_get_responseText(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_get_responseXML(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_get_response(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_get_responseType(const BuiltinFunction *b, QV4::CallData *callData); + static ReturnedValue method_set_responseType(const BuiltinFunction *b, QV4::CallData *callData); }; } @@ -1714,14 +1729,15 @@ void QQmlXMLHttpRequestCtor::setupProto() // XMLHttpRequest methods -void QQmlXMLHttpRequestCtor::method_open(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue QQmlXMLHttpRequestCtor::method_open(const BuiltinFunction *b, QV4::CallData *callData) { + Scope scope(b); Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; - if (callData->argc < 2 || callData->argc > 5) + if (callData->argc() < 2 || callData->argc() > 5) THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count"); // Argument 0 - Method @@ -1744,15 +1760,15 @@ void QQmlXMLHttpRequestCtor::method_open(const QV4::BuiltinFunction *, QV4::Scop bool async = true; // Argument 2 - async (optional) - if (callData->argc > 2) { + if (callData->argc() > 2) { async = callData->args[2].booleanValue(); } // Argument 3/4 - user/pass (optional) QString username, password; - if (callData->argc > 3) + if (callData->argc() > 3) username = callData->args[3].toQStringNoThrow(); - if (callData->argc > 4) + if (callData->argc() > 4) password = callData->args[4].toQStringNoThrow(); // Clear the fragment (if any) @@ -1762,17 +1778,18 @@ void QQmlXMLHttpRequestCtor::method_open(const QV4::BuiltinFunction *, QV4::Scop if (!username.isNull()) url.setUserName(username); if (!password.isNull()) url.setPassword(password); - scope.result = r->open(w, scope.engine->callingQmlContext(), method, url, async ? QQmlXMLHttpRequest::AsynchronousLoad : QQmlXMLHttpRequest::SynchronousLoad); + return r->open(w, scope.engine->callingQmlContext(), method, url, async ? QQmlXMLHttpRequest::AsynchronousLoad : QQmlXMLHttpRequest::SynchronousLoad); } -void QQmlXMLHttpRequestCtor::method_setRequestHeader(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue QQmlXMLHttpRequestCtor::method_setRequestHeader(const BuiltinFunction *b, QV4::CallData *callData) { + Scope scope(b); Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; - if (callData->argc != 2) + if (callData->argc() != 2) THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count"); if (r->readyState() != QQmlXMLHttpRequest::Opened || r->sendFlag()) @@ -1811,8 +1828,9 @@ void QQmlXMLHttpRequestCtor::method_setRequestHeader(const QV4::BuiltinFunction RETURN_UNDEFINED(); } -void QQmlXMLHttpRequestCtor::method_send(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue QQmlXMLHttpRequestCtor::method_send(const BuiltinFunction *b, QV4::CallData *callData) { + Scope scope(b); Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); @@ -1823,7 +1841,7 @@ void QQmlXMLHttpRequestCtor::method_send(const QV4::BuiltinFunction *, QV4::Scop THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); QByteArray data; - if (callData->argc > 0) { + if (callData->argc() > 0) { if (const ArrayBuffer *buffer = callData->args[0].as<ArrayBuffer>()) { data = buffer->asByteArray(); } else { @@ -1831,27 +1849,29 @@ void QQmlXMLHttpRequestCtor::method_send(const QV4::BuiltinFunction *, QV4::Scop } } - scope.result = r->send(w, scope.engine->callingQmlContext(), data); + return r->send(w, scope.engine->callingQmlContext(), data); } -void QQmlXMLHttpRequestCtor::method_abort(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue QQmlXMLHttpRequestCtor::method_abort(const BuiltinFunction *b, QV4::CallData *callData) { + Scope scope(b); Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; - scope.result = r->abort(w, scope.engine->callingQmlContext()); + return r->abort(w, scope.engine->callingQmlContext()); } -void QQmlXMLHttpRequestCtor::method_getResponseHeader(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue QQmlXMLHttpRequestCtor::method_getResponseHeader(const BuiltinFunction *b, QV4::CallData *callData) { + Scope scope(b); Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; - if (callData->argc != 1) + if (callData->argc() != 1) THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count"); if (r->readyState() != QQmlXMLHttpRequest::Loading && @@ -1859,17 +1879,18 @@ void QQmlXMLHttpRequestCtor::method_getResponseHeader(const QV4::BuiltinFunction r->readyState() != QQmlXMLHttpRequest::HeadersReceived) THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); - scope.result = scope.engine->newString(r->header(callData->args[0].toQStringNoThrow())); + return Encode(scope.engine->newString(r->header(callData->args[0].toQStringNoThrow()))); } -void QQmlXMLHttpRequestCtor::method_getAllResponseHeaders(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue QQmlXMLHttpRequestCtor::method_getAllResponseHeaders(const BuiltinFunction *b, QV4::CallData *callData) { + Scope scope(b); Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; - if (callData->argc != 0) + if (callData->argc() != 0) THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count"); if (r->readyState() != QQmlXMLHttpRequest::Loading && @@ -1877,22 +1898,24 @@ void QQmlXMLHttpRequestCtor::method_getAllResponseHeaders(const QV4::BuiltinFunc r->readyState() != QQmlXMLHttpRequest::HeadersReceived) THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); - scope.result = scope.engine->newString(r->headers()); + return Encode(scope.engine->newString(r->headers())); } // XMLHttpRequest properties -void QQmlXMLHttpRequestCtor::method_get_readyState(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue QQmlXMLHttpRequestCtor::method_get_readyState(const BuiltinFunction *b, QV4::CallData *callData) { + Scope scope(b); Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; - scope.result = Encode(r->readyState()); + return Encode(r->readyState()); } -void QQmlXMLHttpRequestCtor::method_get_status(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue QQmlXMLHttpRequestCtor::method_get_status(const BuiltinFunction *b, QV4::CallData *callData) { + Scope scope(b); Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); @@ -1903,13 +1926,14 @@ void QQmlXMLHttpRequestCtor::method_get_status(const QV4::BuiltinFunction *, QV4 THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); if (r->errorFlag()) - scope.result = Encode(0); + return Encode(0); else - scope.result = Encode(r->replyStatus()); + return Encode(r->replyStatus()); } -void QQmlXMLHttpRequestCtor::method_get_statusText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue QQmlXMLHttpRequestCtor::method_get_statusText(const BuiltinFunction *b, QV4::CallData *callData) { + Scope scope(b); Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); @@ -1920,13 +1944,14 @@ void QQmlXMLHttpRequestCtor::method_get_statusText(const QV4::BuiltinFunction *, THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); if (r->errorFlag()) - scope.result = scope.engine->newString(QString()); + return Encode(scope.engine->newString(QString())); else - scope.result = scope.engine->newString(r->replyStatusText()); + return Encode(scope.engine->newString(r->replyStatusText())); } -void QQmlXMLHttpRequestCtor::method_get_responseText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseText(const BuiltinFunction *b, QV4::CallData *callData) { + Scope scope(b); Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); @@ -1934,13 +1959,14 @@ void QQmlXMLHttpRequestCtor::method_get_responseText(const QV4::BuiltinFunction if (r->readyState() != QQmlXMLHttpRequest::Loading && r->readyState() != QQmlXMLHttpRequest::Done) - scope.result = scope.engine->newString(QString()); + return Encode(scope.engine->newString(QString())); else - scope.result = scope.engine->newString(r->responseBody()); + return Encode(scope.engine->newString(r->responseBody())); } -void QQmlXMLHttpRequestCtor::method_get_responseXML(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseXML(const BuiltinFunction *b, QV4::CallData *callData) { + Scope scope(b); Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); @@ -1949,16 +1975,17 @@ void QQmlXMLHttpRequestCtor::method_get_responseXML(const QV4::BuiltinFunction * if (!r->receivedXml() || (r->readyState() != QQmlXMLHttpRequest::Loading && r->readyState() != QQmlXMLHttpRequest::Done)) { - scope.result = Encode::null(); + return Encode::null(); } else { if (r->responseType().isEmpty()) r->setResponseType(QLatin1String("document")); - scope.result = r->xmlResponseBody(scope.engine); + return r->xmlResponseBody(scope.engine); } } -void QQmlXMLHttpRequestCtor::method_get_response(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue QQmlXMLHttpRequestCtor::method_get_response(const BuiltinFunction *b, QV4::CallData *callData) { + Scope scope(b); Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); @@ -1983,29 +2010,31 @@ void QQmlXMLHttpRequestCtor::method_get_response(const QV4::BuiltinFunction *, Q } -void QQmlXMLHttpRequestCtor::method_get_responseType(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseType(const BuiltinFunction *b, QV4::CallData *callData) { + Scope scope(b); Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; - scope.result = scope.engine->newString(r->responseType()); + return Encode(scope.engine->newString(r->responseType())); } -void QQmlXMLHttpRequestCtor::method_set_responseType(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +ReturnedValue QQmlXMLHttpRequestCtor::method_set_responseType(const BuiltinFunction *b, QV4::CallData *callData) { + Scope scope(b); Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; - if (callData->argc < 1) + if (callData->argc() < 1) THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count"); // Argument 0 - response type r->setResponseType(callData->args[0].toQStringNoThrow()); - scope.result = Encode::undefined(); + return Encode::undefined(); } void qt_rem_qmlxmlhttprequest(ExecutionEngine * /* engine */, void *d) diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index 25d99e0c87..3627f29cb2 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -87,8 +87,7 @@ DEFINE_OBJECT_VTABLE(QtObject); #define THROW_TYPE_ERROR_WITH_MESSAGE(msg) \ do { \ - scope.result = scope.engine->throwTypeError(QString::fromUtf8(msg)); \ - return; \ + return scope.engine->throwTypeError(QString::fromUtf8(msg)); \ } while (false) struct StaticQtMetaObject : public QObject @@ -229,12 +228,12 @@ void QtObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint \qmlmethod bool Qt::isQtObject(object) Returns true if \c object is a valid reference to a Qt or QML object, otherwise false. */ -void QtObject::method_isQtObject(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_isQtObject(const BuiltinFunction *, CallData *callData) { - if (callData->argc == 0) + if (callData->argc() == 0) RETURN_RESULT(QV4::Encode(false)); - scope.result = QV4::Encode(callData->args[0].as<QV4::QObjectWrapper>() != 0); + return QV4::Encode(callData->args[0].as<QV4::QObjectWrapper>() != 0); } /*! @@ -243,9 +242,10 @@ void QtObject::method_isQtObject(const BuiltinFunction *, Scope &scope, CallData Returns a color with the specified \c red, \c green, \c blue and \c alpha components. All components should be in the range 0-1 inclusive. */ -void QtObject::method_rgba(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_rgba(const BuiltinFunction *builtin, CallData *callData) { - int argCount = callData->argc; + QV4::Scope scope(builtin); + int argCount = callData->argc(); if (argCount < 3 || argCount > 4) THROW_GENERIC_ERROR("Qt.rgba(): Invalid arguments"); @@ -263,7 +263,7 @@ void QtObject::method_rgba(const BuiltinFunction *, Scope &scope, CallData *call if (a < 0.0) a=0.0; if (a > 1.0) a=1.0; - scope.result = scope.engine->fromVariant(QQml_colorProvider()->fromRgbF(r, g, b, a)); + return scope.engine->fromVariant(QQml_colorProvider()->fromRgbF(r, g, b, a)); } /*! @@ -272,9 +272,10 @@ void QtObject::method_rgba(const BuiltinFunction *, Scope &scope, CallData *call Returns a color with the specified \c hue, \c saturation, \c lightness and \c alpha components. All components should be in the range 0-1 inclusive. */ -void QtObject::method_hsla(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_hsla(const BuiltinFunction *b, CallData *callData) { - int argCount = callData->argc; + QV4::Scope scope(b); + int argCount = callData->argc(); if (argCount < 3 || argCount > 4) THROW_GENERIC_ERROR("Qt.hsla(): Invalid arguments"); @@ -292,7 +293,7 @@ void QtObject::method_hsla(const BuiltinFunction *, Scope &scope, CallData *call if (a < 0.0) a=0.0; if (a > 1.0) a=1.0; - scope.result = scope.engine->fromVariant(QQml_colorProvider()->fromHslF(h, s, l, a)); + return scope.engine->fromVariant(QQml_colorProvider()->fromHslF(h, s, l, a)); } /*! @@ -303,9 +304,10 @@ All components should be in the range 0-1 inclusive. \since 5.5 */ -void QtObject::method_hsva(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_hsva(const BuiltinFunction *b, CallData *callData) { - int argCount = callData->argc; + QV4::Scope scope(b); + int argCount = callData->argc(); if (argCount < 3 || argCount > 4) THROW_GENERIC_ERROR("Qt.hsva(): Invalid arguments"); @@ -319,7 +321,7 @@ void QtObject::method_hsva(const BuiltinFunction *, Scope &scope, CallData *call v = qBound(0.0, v, 1.0); a = qBound(0.0, a, 1.0); - scope.result = scope.engine->fromVariant(QQml_colorProvider()->fromHsvF(h, s, v, a)); + return scope.engine->fromVariant(QQml_colorProvider()->fromHsvF(h, s, v, a)); } /*! @@ -330,9 +332,10 @@ may be either color values or string values. If a string value is supplied it must be convertible to a color, as described for the \l{colorbasictypedocs}{color} basic type. */ -void QtObject::method_colorEqual(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_colorEqual(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 2) + QV4::Scope scope(b); + if (callData->argc() != 2) THROW_GENERIC_ERROR("Qt.colorEqual(): Invalid arguments"); bool ok = false; @@ -358,7 +361,7 @@ void QtObject::method_colorEqual(const BuiltinFunction *, Scope &scope, CallData } bool equal = (lhs == rhs); - scope.result = QV4::Encode(equal); + return QV4::Encode(equal); } /*! @@ -368,9 +371,10 @@ Returns a \c rect with the top-left corner at \c x, \c y and the specified \c wi The returned object has \c x, \c y, \c width and \c height attributes with the given values. */ -void QtObject::method_rect(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_rect(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 4) + QV4::Scope scope(b); + if (callData->argc() != 4) THROW_GENERIC_ERROR("Qt.rect(): Invalid arguments"); double x = callData->args[0].toNumber(); @@ -378,37 +382,39 @@ void QtObject::method_rect(const BuiltinFunction *, Scope &scope, CallData *call double w = callData->args[2].toNumber(); double h = callData->args[3].toNumber(); - scope.result = scope.engine->fromVariant(QVariant::fromValue(QRectF(x, y, w, h))); + return scope.engine->fromVariant(QVariant::fromValue(QRectF(x, y, w, h))); } /*! \qmlmethod point Qt::point(int x, int y) Returns a Point with the specified \c x and \c y coordinates. */ -void QtObject::method_point(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_point(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 2) + QV4::Scope scope(b); + if (callData->argc() != 2) THROW_GENERIC_ERROR("Qt.point(): Invalid arguments"); double x = callData->args[0].toNumber(); double y = callData->args[1].toNumber(); - scope.result = scope.engine->fromVariant(QVariant::fromValue(QPointF(x, y))); + return scope.engine->fromVariant(QVariant::fromValue(QPointF(x, y))); } /*! \qmlmethod Qt::size(int width, int height) Returns a Size with the specified \c width and \c height. */ -void QtObject::method_size(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_size(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 2) + QV4::Scope scope(b); + if (callData->argc() != 2) THROW_GENERIC_ERROR("Qt.size(): Invalid arguments"); double w = callData->args[0].toNumber(); double h = callData->args[1].toNumber(); - scope.result = scope.engine->fromVariant(QVariant::fromValue(QSizeF(w, h))); + return scope.engine->fromVariant(QVariant::fromValue(QSizeF(w, h))); } /*! @@ -419,9 +425,10 @@ key-value pairs where valid keys are the \l{fontbasictypedocs}{font} type's subproperty names, and the values are valid values for each subproperty. Invalid keys will be ignored. */ -void QtObject::method_font(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_font(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 1 || !callData->args[0].isObject()) + QV4::Scope scope(b); + if (callData->argc() != 1 || !callData->args[0].isObject()) THROW_GENERIC_ERROR("Qt.font(): Invalid arguments"); QV4::ExecutionEngine *v4 = scope.engine; @@ -429,7 +436,7 @@ void QtObject::method_font(const BuiltinFunction *, Scope &scope, CallData *call QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QFont, QQmlV4Handle(callData->args[0]), v4, &ok); if (!ok) THROW_GENERIC_ERROR("Qt.font(): Invalid argument: no valid font subproperties specified"); - scope.result = scope.engine->fromVariant(v); + return scope.engine->fromVariant(v); } @@ -438,9 +445,10 @@ void QtObject::method_font(const BuiltinFunction *, Scope &scope, CallData *call \qmlmethod Qt::vector2d(real x, real y) Returns a Vector2D with the specified \c x and \c y. */ -void QtObject::method_vector2d(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_vector2d(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 2) + QV4::Scope scope(b); + if (callData->argc() != 2) THROW_GENERIC_ERROR("Qt.vector2d(): Invalid arguments"); float xy[3]; // qvector2d uses float internally @@ -448,16 +456,17 @@ void QtObject::method_vector2d(const BuiltinFunction *, Scope &scope, CallData * xy[1] = callData->args[1].toNumber(); const void *params[] = { xy }; - scope.result = scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QVector2D, 1, params)); + return scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QVector2D, 1, params)); } /*! \qmlmethod Qt::vector3d(real x, real y, real z) Returns a Vector3D with the specified \c x, \c y and \c z. */ -void QtObject::method_vector3d(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_vector3d(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 3) + QV4::Scope scope(b); + if (callData->argc() != 3) THROW_GENERIC_ERROR("Qt.vector3d(): Invalid arguments"); float xyz[3]; // qvector3d uses float internally @@ -466,16 +475,17 @@ void QtObject::method_vector3d(const BuiltinFunction *, Scope &scope, CallData * xyz[2] = callData->args[2].toNumber(); const void *params[] = { xyz }; - scope.result = scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QVector3D, 1, params)); + return scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QVector3D, 1, params)); } /*! \qmlmethod Qt::vector4d(real x, real y, real z, real w) Returns a Vector4D with the specified \c x, \c y, \c z and \c w. */ -void QtObject::method_vector4d(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_vector4d(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 4) + QV4::Scope scope(b); + if (callData->argc() != 4) THROW_GENERIC_ERROR("Qt.vector4d(): Invalid arguments"); float xyzw[4]; // qvector4d uses float internally @@ -485,16 +495,17 @@ void QtObject::method_vector4d(const BuiltinFunction *, Scope &scope, CallData * xyzw[3] = callData->args[3].toNumber(); const void *params[] = { xyzw }; - scope.result = scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QVector4D, 1, params)); + return scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QVector4D, 1, params)); } /*! \qmlmethod Qt::quaternion(real scalar, real x, real y, real z) Returns a Quaternion with the specified \c scalar, \c x, \c y, and \c z. */ -void QtObject::method_quaternion(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_quaternion(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 4) + QV4::Scope scope(b); + if (callData->argc() != 4) THROW_GENERIC_ERROR("Qt.quaternion(): Invalid arguments"); qreal sxyz[4]; // qquaternion uses qreal internally @@ -504,7 +515,7 @@ void QtObject::method_quaternion(const BuiltinFunction *, Scope &scope, CallData sxyz[3] = callData->args[3].toNumber(); const void *params[] = { sxyz }; - scope.result = scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QQuaternion, 1, params)); + return scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QQuaternion, 1, params)); } /*! @@ -516,25 +527,23 @@ matrix values. Finally, the function may be called with no arguments and the resulting matrix will be the identity matrix. */ -void QtObject::method_matrix4x4(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_matrix4x4(const BuiltinFunction *b, CallData *callData) { - QV4::ExecutionEngine *v4 = scope.engine; + QV4::Scope scope(b); - if (callData->argc == 0) { - scope.result = scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QMatrix4x4, 0, nullptr)); - return; + if (callData->argc() == 0) { + return scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QMatrix4x4, 0, nullptr)); } - if (callData->argc == 1 && callData->args[0].isObject()) { + if (callData->argc() == 1 && callData->args[0].isObject()) { bool ok = false; - QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QMatrix4x4, QQmlV4Handle(callData->args[0]), v4, &ok); + QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QMatrix4x4, QQmlV4Handle(callData->args[0]), scope.engine, &ok); if (!ok) THROW_GENERIC_ERROR("Qt.matrix4x4(): Invalid argument: not a valid matrix4x4 values array"); - scope.result = scope.engine->fromVariant(v); - return; + return scope.engine->fromVariant(v); } - if (callData->argc != 16) + if (callData->argc() != 16) THROW_GENERIC_ERROR("Qt.matrix4x4(): Invalid arguments"); qreal vals[16]; // qmatrix4x4 uses qreal internally @@ -556,7 +565,7 @@ void QtObject::method_matrix4x4(const BuiltinFunction *, Scope &scope, CallData vals[15] = callData->args[15].toNumber(); const void *params[] = { vals }; - scope.result = scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QMatrix4x4, 1, params)); + return scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QMatrix4x4, 1, params)); } /*! @@ -573,9 +582,10 @@ by factor and converts the color back to RGB. If \c factor is not supplied, returns a color 50% lighter than \c baseColor (factor 1.5). */ -void QtObject::method_lighter(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_lighter(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 1 && callData->argc != 2) + QV4::Scope scope(b); + if (callData->argc() != 1 && callData->argc() != 2) THROW_GENERIC_ERROR("Qt.lighter(): Invalid arguments"); QVariant v = scope.engine->toVariant(callData->args[0], -1); @@ -583,19 +593,17 @@ void QtObject::method_lighter(const BuiltinFunction *, Scope &scope, CallData *c bool ok = false; v = QQmlStringConverters::colorFromString(v.toString(), &ok); if (!ok) { - scope.result = QV4::Encode::null(); - return; + return QV4::Encode::null(); } } else if (v.userType() != QVariant::Color) { - scope.result = QV4::Encode::null(); - return; + return QV4::Encode::null(); } qreal factor = 1.5; - if (callData->argc == 2) + if (callData->argc() == 2) factor = callData->args[1].toNumber(); - scope.result = scope.engine->fromVariant(QQml_colorProvider()->lighter(v, factor)); + return scope.engine->fromVariant(QQml_colorProvider()->lighter(v, factor)); } /*! @@ -613,9 +621,10 @@ by factor and converts the color back to RGB. If \c factor is not supplied, returns a color 50% darker than \c baseColor (factor 2.0). */ -void QtObject::method_darker(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_darker(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 1 && callData->argc != 2) + QV4::Scope scope(b); + if (callData->argc() != 1 && callData->argc() != 2) THROW_GENERIC_ERROR("Qt.darker(): Invalid arguments"); QVariant v = scope.engine->toVariant(callData->args[0], -1); @@ -623,19 +632,17 @@ void QtObject::method_darker(const BuiltinFunction *, Scope &scope, CallData *ca bool ok = false; v = QQmlStringConverters::colorFromString(v.toString(), &ok); if (!ok) { - scope.result = QV4::Encode::null(); - return; + return QV4::Encode::null(); } } else if (v.userType() != QVariant::Color) { - scope.result = QV4::Encode::null(); - return; + return QV4::Encode::null(); } qreal factor = 2.0; - if (callData->argc == 2) + if (callData->argc() == 2) factor = callData->args[1].toNumber(); - scope.result = scope.engine->fromVariant(QQml_colorProvider()->darker(v, factor)); + return scope.engine->fromVariant(QQml_colorProvider()->darker(v, factor)); } /*! @@ -662,9 +669,10 @@ void QtObject::method_darker(const BuiltinFunction *, Scope &scope, CallData *ca Tint is most useful when a subtle change is intended to be conveyed due to some event; you can then use tinting to more effectively tune the visible color. */ -void QtObject::method_tint(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_tint(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 2) + QV4::Scope scope(b); + if (callData->argc() != 2) THROW_GENERIC_ERROR("Qt.tint(): Invalid arguments"); // base color @@ -673,12 +681,10 @@ void QtObject::method_tint(const BuiltinFunction *, Scope &scope, CallData *call bool ok = false; v1 = QQmlStringConverters::colorFromString(v1.toString(), &ok); if (!ok) { - scope.result = QV4::Encode::null(); - return; + return QV4::Encode::null(); } } else if (v1.userType() != QVariant::Color) { - scope.result = QV4::Encode::null(); - return; + return QV4::Encode::null(); } // tint color @@ -687,15 +693,13 @@ void QtObject::method_tint(const BuiltinFunction *, Scope &scope, CallData *call bool ok = false; v2 = QQmlStringConverters::colorFromString(v2.toString(), &ok); if (!ok) { - scope.result = QV4::Encode::null(); - return; + return QV4::Encode::null(); } } else if (v2.userType() != QVariant::Color) { - scope.result = QV4::Encode::null(); - return; + return QV4::Encode::null(); } - scope.result = scope.engine->fromVariant(QQml_colorProvider()->tint(v1, v2)); + return scope.engine->fromVariant(QQml_colorProvider()->tint(v1, v2)); } /*! @@ -714,15 +718,16 @@ If \a format is not specified, \a date is formatted using \sa Locale */ -void QtObject::method_formatDate(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_formatDate(const BuiltinFunction *b, CallData *callData) { - if (callData->argc < 1 || callData->argc > 2) + QV4::Scope scope(b); + if (callData->argc() < 1 || callData->argc() > 2) THROW_GENERIC_ERROR("Qt.formatDate(): Invalid arguments"); Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate; QDate date = scope.engine->toVariant(callData->args[0], -1).toDateTime().date(); QString formattedDate; - if (callData->argc == 2) { + if (callData->argc() == 2) { QV4::ScopedString s(scope, callData->args[1]); if (s) { QString format = s->toQString(); @@ -738,7 +743,7 @@ void QtObject::method_formatDate(const BuiltinFunction *, Scope &scope, CallData formattedDate = date.toString(enumFormat); } - scope.result = scope.engine->newString(formattedDate); + return Encode(scope.engine->newString(formattedDate)); } /*! @@ -756,9 +761,10 @@ If \a format is not specified, \a time is formatted using \sa Locale */ -void QtObject::method_formatTime(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_formatTime(const BuiltinFunction *b, CallData *callData) { - if (callData->argc < 1 || callData->argc > 2) + QV4::Scope scope(b); + if (callData->argc() < 1 || callData->argc() > 2) THROW_GENERIC_ERROR("Qt.formatTime(): Invalid arguments"); QVariant argVariant = scope.engine->toVariant(callData->args[0], -1); @@ -770,7 +776,7 @@ void QtObject::method_formatTime(const BuiltinFunction *, Scope &scope, CallData Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate; QString formattedTime; - if (callData->argc == 2) { + if (callData->argc() == 2) { QV4::ScopedString s(scope, callData->args[1]); if (s) { QString format = s->toQString(); @@ -786,7 +792,7 @@ void QtObject::method_formatTime(const BuiltinFunction *, Scope &scope, CallData formattedTime = time.toString(enumFormat); } - scope.result = scope.engine->newString(formattedTime); + return Encode(scope.engine->newString(formattedTime)); } /*! @@ -881,15 +887,16 @@ with the \a format values below to produce the following results: \sa Locale */ -void QtObject::method_formatDateTime(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_formatDateTime(const BuiltinFunction *b, CallData *callData) { - if (callData->argc < 1 || callData->argc > 2) + QV4::Scope scope(b); + if (callData->argc() < 1 || callData->argc() > 2) THROW_GENERIC_ERROR("Qt.formatDateTime(): Invalid arguments"); Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate; QDateTime dt = scope.engine->toVariant(callData->args[0], -1).toDateTime(); QString formattedDt; - if (callData->argc == 2) { + if (callData->argc() == 2) { QV4::ScopedString s(scope, callData->args[1]); if (s) { QString format = s->toQString(); @@ -905,7 +912,7 @@ void QtObject::method_formatDateTime(const BuiltinFunction *, Scope &scope, Call formattedDt = dt.toString(enumFormat); } - scope.result = scope.engine->newString(formattedDt); + return Encode(scope.engine->newString(formattedDt)); } /*! @@ -919,94 +926,97 @@ void QtObject::method_formatDateTime(const BuiltinFunction *, Scope &scope, Call still fail to launch or fail to open the requested URL. This result will not be reported back to the application. */ -void QtObject::method_openUrlExternally(const BuiltinFunction *b, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_openUrlExternally(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 1) { - scope.result = QV4::Encode(false); - return; + QV4::Scope scope(b); + if (callData->argc() != 1) { + return QV4::Encode(false); } - method_resolvedUrl(b, scope, callData); - QUrl url(scope.result.toQStringNoThrow()); - scope.result = scope.engine->fromVariant(QQml_guiProvider()->openUrlExternally(url)); + ScopedValue result(scope, method_resolvedUrl(b, callData)); + QUrl url(result->toQStringNoThrow()); + return scope.engine->fromVariant(QQml_guiProvider()->openUrlExternally(url)); } /*! \qmlmethod url Qt::resolvedUrl(url url) Returns \a url resolved relative to the URL of the caller. */ -void QtObject::method_resolvedUrl(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_resolvedUrl(const BuiltinFunction *b, CallData *callData) { - ExecutionEngine *v4 = scope.engine; + QV4::Scope scope(b); - QUrl url = v4->toVariant(callData->args[0], -1).toUrl(); - QQmlEngine *e = v4->qmlEngine(); + QUrl url = scope.engine->toVariant(callData->args[0], -1).toUrl(); + QQmlEngine *e = scope.engine->qmlEngine(); QQmlEnginePrivate *p = 0; if (e) p = QQmlEnginePrivate::get(e); if (p) { - QQmlContextData *ctxt = v4->callingQmlContext(); + QQmlContextData *ctxt = scope.engine->callingQmlContext(); if (ctxt) - scope.result = v4->newString(ctxt->resolvedUrl(url).toString()); + return Encode(scope.engine->newString(ctxt->resolvedUrl(url).toString())); else - scope.result = v4->newString(url.toString()); - return; + return Encode(scope.engine->newString(url.toString())); } - scope.result = v4->newString(e->baseUrl().resolved(url).toString()); + return Encode(scope.engine->newString(e->baseUrl().resolved(url).toString())); } /*! \qmlmethod list<string> Qt::fontFamilies() Returns a list of the font families available to the application. */ -void QtObject::method_fontFamilies(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_fontFamilies(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 0) + QV4::Scope scope(b); + if (callData->argc() != 0) THROW_GENERIC_ERROR("Qt.fontFamilies(): Invalid arguments"); - scope.result = scope.engine->fromVariant(QVariant(QQml_guiProvider()->fontFamilies())); + return scope.engine->fromVariant(QVariant(QQml_guiProvider()->fontFamilies())); } /*! \qmlmethod string Qt::md5(data) Returns a hex string of the md5 hash of \c data. */ -void QtObject::method_md5(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_md5(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 1) + QV4::Scope scope(b); + if (callData->argc() != 1) THROW_GENERIC_ERROR("Qt.md5(): Invalid arguments"); QByteArray data = callData->args[0].toQStringNoThrow().toUtf8(); QByteArray result = QCryptographicHash::hash(data, QCryptographicHash::Md5); - scope.result = scope.engine->newString(QLatin1String(result.toHex())); + return Encode(scope.engine->newString(QLatin1String(result.toHex()))); } /*! \qmlmethod string Qt::btoa(data) Binary to ASCII - this function returns a base64 encoding of \c data. */ -void QtObject::method_btoa(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_btoa(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 1) + QV4::Scope scope(b); + if (callData->argc() != 1) THROW_GENERIC_ERROR("Qt.btoa(): Invalid arguments"); QByteArray data = callData->args[0].toQStringNoThrow().toUtf8(); - scope.result = scope.engine->newString(QLatin1String(data.toBase64())); + return Encode(scope.engine->newString(QLatin1String(data.toBase64()))); } /*! \qmlmethod string Qt::atob(data) ASCII to binary - this function decodes the base64 encoded \a data string and returns it. */ -void QtObject::method_atob(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_atob(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 1) + QV4::Scope scope(b); + if (callData->argc() != 1) THROW_GENERIC_ERROR("Qt.atob(): Invalid arguments"); QByteArray data = callData->args[0].toQStringNoThrow().toLatin1(); - scope.result = scope.engine->newString(QString::fromUtf8(QByteArray::fromBase64(data))); + return Encode(scope.engine->newString(QString::fromUtf8(QByteArray::fromBase64(data)))); } /*! @@ -1018,10 +1028,10 @@ QQmlEngine::quit() signal to the QCoreApplication::quit() slot. \sa exit() */ -void QtObject::method_quit(const BuiltinFunction *, Scope &scope, CallData *) +ReturnedValue QtObject::method_quit(const BuiltinFunction *b, CallData *) { - QQmlEnginePrivate::get(scope.engine->qmlEngine())->sendQuit(); - scope.result = Encode::undefined(); + QQmlEnginePrivate::get(b->engine()->qmlEngine())->sendQuit(); + return Encode::undefined(); } /*! @@ -1035,15 +1045,16 @@ void QtObject::method_quit(const BuiltinFunction *, Scope &scope, CallData *) \sa quit() */ -void QtObject::method_exit(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_exit(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 1) + QV4::Scope scope(b); + if (callData->argc() != 1) THROW_GENERIC_ERROR("Qt.exit(): Invalid arguments"); int retCode = callData->args[0].toNumber(); QQmlEnginePrivate::get(scope.engine->qmlEngine())->sendExit(retCode); - scope.result = QV4::Encode::undefined(); + return QV4::Encode::undefined(); } /*! @@ -1070,9 +1081,10 @@ If this is the case, consider using \l{QtQml::Qt::createComponent()}{Qt.createCo See \l {Dynamic QML Object Creation from JavaScript} for more information on using this function. */ -void QtObject::method_createQmlObject(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_createQmlObject(const BuiltinFunction *b, CallData *callData) { - if (callData->argc < 2 || callData->argc > 3) + QV4::Scope scope(b); + if (callData->argc() < 2 || callData->argc() > 3) THROW_GENERIC_ERROR("Qt.createQmlObject(): Invalid arguments"); struct Error { @@ -1121,7 +1133,7 @@ void QtObject::method_createQmlObject(const BuiltinFunction *, Scope &scope, Cal RETURN_RESULT(Encode::null()); QUrl url; - if (callData->argc > 2) + if (callData->argc() > 2) url = QUrl(callData->args[2].toQStringNoThrow()); else url = QUrl(QLatin1String("inline")); @@ -1174,13 +1186,12 @@ void QtObject::method_createQmlObject(const BuiltinFunction *, Scope &scope, Cal if (component.isError()) { ScopedValue v(scope, Error::create(scope.engine, component.errors())); - scope.result = scope.engine->throwError(v); - return; + return scope.engine->throwError(v); } Q_ASSERT(obj); - scope.result = QV4::QObjectWrapper::wrap(scope.engine, obj); + return QV4::QObjectWrapper::wrap(scope.engine, obj); } /*! @@ -1227,9 +1238,10 @@ See \l {Dynamic QML Object Creation from JavaScript} for more information on usi To create a QML object from an arbitrary string of QML (instead of a file), use \l{QtQml::Qt::createQmlObject()}{Qt.createQmlObject()}. */ -void QtObject::method_createComponent(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_createComponent(const BuiltinFunction *b, CallData *callData) { - if (callData->argc < 1 || callData->argc > 3) + QV4::Scope scope(b); + if (callData->argc() < 1 || callData->argc() > 3) THROW_GENERIC_ERROR("Qt.createComponent(): Invalid arguments"); QV8Engine *v8engine = scope.engine->v8Engine; @@ -1249,8 +1261,8 @@ void QtObject::method_createComponent(const BuiltinFunction *, Scope &scope, Cal QObject *parentArg = 0; int consumedCount = 1; - if (callData->argc > 1) { - ScopedValue lastArg(scope, callData->args[callData->argc-1]); + if (callData->argc() > 1) { + ScopedValue lastArg(scope, callData->args[callData->argc()-1]); // The second argument could be the mode enum if (callData->args[1].isInteger()) { @@ -1261,11 +1273,11 @@ void QtObject::method_createComponent(const BuiltinFunction *, Scope &scope, Cal consumedCount += 1; } else { // The second argument could be the parent only if there are exactly two args - if ((callData->argc != 2) || !(lastArg->isObject() || lastArg->isNull())) + if ((callData->argc() != 2) || !(lastArg->isObject() || lastArg->isNull())) THROW_GENERIC_ERROR("Qt.createComponent(): Invalid arguments"); } - if (consumedCount < callData->argc) { + if (consumedCount < callData->argc()) { if (lastArg->isObject()) { Scoped<QObjectWrapper> qobjectWrapper(scope, lastArg); if (qobjectWrapper) @@ -1286,7 +1298,7 @@ void QtObject::method_createComponent(const BuiltinFunction *, Scope &scope, Cal QQmlData::get(c, true)->explicitIndestructibleSet = false; QQmlData::get(c)->indestructible = false; - scope.result = QV4::QObjectWrapper::wrap(scope.engine, c); + return QV4::QObjectWrapper::wrap(scope.engine, c); } /*! @@ -1309,18 +1321,19 @@ void QtObject::method_createComponent(const BuiltinFunction *, Scope &scope, Cal \sa Locale */ -void QtObject::method_locale(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_locale(const BuiltinFunction *b, CallData *callData) { + QV4::Scope scope(b); QString code; - if (callData->argc > 1) + if (callData->argc() > 1) THROW_GENERIC_ERROR("locale() requires 0 or 1 argument"); - if (callData->argc == 1 && !callData->args[0].isString()) + if (callData->argc() == 1 && !callData->args[0].isString()) THROW_TYPE_ERROR_WITH_MESSAGE("locale(): argument (locale code) must be a string"); - if (callData->argc == 1) + if (callData->argc() == 1) code = callData->args[0].toQStringNoThrow(); - scope.result = QQmlLocale::locale(scope.engine, code); + return QQmlLocale::locale(scope.engine, code); } void Heap::QQmlBindingFunction::init(const QV4::FunctionObject *originalFunction) @@ -1332,8 +1345,8 @@ void Heap::QQmlBindingFunction::init(const QV4::FunctionObject *originalFunction QQmlSourceLocation QQmlBindingFunction::currentLocation() const { - QV4::StackFrame frame = engine()->currentStackFrame(); - return QQmlSourceLocation(frame.source, frame.line, 0); + QV4::CppStackFrame *frame = engine()->currentStackFrame; + return QQmlSourceLocation(frame->source(), frame->lineNumber(), 0); } DEFINE_OBJECT_VTABLE(QQmlBindingFunction); @@ -1382,20 +1395,22 @@ DEFINE_OBJECT_VTABLE(QQmlBindingFunction); \since 5.0 */ -void QtObject::method_binding(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_binding(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 1) + QV4::Scope scope(b); + if (callData->argc() != 1) THROW_GENERIC_ERROR("binding() requires 1 argument"); const QV4::FunctionObject *f = callData->args[0].as<FunctionObject>(); if (!f) THROW_TYPE_ERROR_WITH_MESSAGE("binding(): argument (binding expression) must be a function"); - scope.result = scope.engine->memoryManager->allocObject<QQmlBindingFunction>(f); + return Encode(scope.engine->memoryManager->allocObject<QQmlBindingFunction>(f)); } -void QtObject::method_get_platform(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_get_platform(const BuiltinFunction *b, CallData *callData) { + QV4::Scope scope(b); // ### inefficient. Should be just a value based getter Object *o = callData->thisObject.as<Object>(); if (!o) @@ -1408,11 +1423,12 @@ void QtObject::method_get_platform(const BuiltinFunction *, Scope &scope, CallDa // Only allocate a platform object once qt->d()->platform = new QQmlPlatform(scope.engine->jsEngine()); - scope.result = QV4::QObjectWrapper::wrap(scope.engine, qt->d()->platform); + return QV4::QObjectWrapper::wrap(scope.engine, qt->d()->platform); } -void QtObject::method_get_application(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_get_application(const BuiltinFunction *b, CallData *callData) { + QV4::Scope scope(b); // ### inefficient. Should be just a value based getter Object *o = callData->thisObject.as<Object>(); if (!o) @@ -1425,19 +1441,19 @@ void QtObject::method_get_application(const BuiltinFunction *, Scope &scope, Cal // Only allocate an application object once qt->d()->application = QQml_guiProvider()->application(scope.engine->jsEngine()); - scope.result = QV4::QObjectWrapper::wrap(scope.engine, qt->d()->application); + return QV4::QObjectWrapper::wrap(scope.engine, qt->d()->application); } -void QtObject::method_get_inputMethod(const BuiltinFunction *, Scope &scope, CallData *) +ReturnedValue QtObject::method_get_inputMethod(const BuiltinFunction *b, CallData *) { QObject *o = QQml_guiProvider()->inputMethod(); - scope.result = QV4::QObjectWrapper::wrap(scope.engine, o); + return QV4::QObjectWrapper::wrap(b->engine(), o); } -void QtObject::method_get_styleHints(const BuiltinFunction *, Scope &scope, CallData *) +ReturnedValue QtObject::method_get_styleHints(const BuiltinFunction *b, CallData *) { QObject *o = QQml_guiProvider()->styleHints(); - scope.result = QV4::QObjectWrapper::wrap(scope.engine, o); + return QV4::QObjectWrapper::wrap(b->engine(), o); } @@ -1497,15 +1513,16 @@ static QString jsStack(QV4::ExecutionEngine *engine) { return stack; } -static void writeToConsole(const BuiltinFunction *, Scope &scope, CallData *callData, - ConsoleLogTypes logType, bool printStack = false) +static ReturnedValue writeToConsole(const BuiltinFunction *b, CallData *callData, + ConsoleLogTypes logType, bool printStack = false) { QLoggingCategory *loggingCategory = 0; QString result; + QV4::Scope scope(b); QV4::ExecutionEngine *v4 = scope.engine; int start = 0; - if (callData->argc > 0) { + if (callData->argc() > 0) { if (const QObjectWrapper* wrapper = callData->args[0].as<QObjectWrapper>()) { if (QQmlLoggingCategory* category = qobject_cast<QQmlLoggingCategory*>(wrapper->object())) { if (category->category()) @@ -1518,7 +1535,7 @@ static void writeToConsole(const BuiltinFunction *, Scope &scope, CallData *call } - for (int i = start; i < callData->argc; ++i) { + for (int i = start, ei = callData->argc(); i < ei; ++i) { if (i != start) result.append(QLatin1Char(' ')); @@ -1536,10 +1553,10 @@ static void writeToConsole(const BuiltinFunction *, Scope &scope, CallData *call if (!loggingCategory) loggingCategory = v4->qmlEngine() ? &qmlLoggingCategory : &jsLoggingCategory; - QV4::StackFrame frame = v4->currentStackFrame(); - const QByteArray baSource = frame.source.toUtf8(); - const QByteArray baFunction = frame.function.toUtf8(); - QMessageLogger logger(baSource.constData(), frame.line, baFunction.constData(), loggingCategory->categoryName()); + QV4::CppStackFrame *frame = v4->currentStackFrame; + const QByteArray baSource = frame->source().toUtf8(); + const QByteArray baFunction = frame->function().toUtf8(); + QMessageLogger logger(baSource.constData(), frame->lineNumber(), baFunction.constData(), loggingCategory->categoryName()); switch (logType) { case Log: @@ -1562,37 +1579,38 @@ static void writeToConsole(const BuiltinFunction *, Scope &scope, CallData *call break; } - scope.result = QV4::Encode::undefined(); + return Encode::undefined(); } DEFINE_OBJECT_VTABLE(ConsoleObject); -void ConsoleObject::method_error(const BuiltinFunction *b, Scope &scope, CallData *callData) +ReturnedValue ConsoleObject::method_error(const BuiltinFunction *b, CallData *callData) { - writeToConsole(b, scope, callData, Error); + return writeToConsole(b, callData, Error); } -void ConsoleObject::method_log(const BuiltinFunction *b, Scope &scope, CallData *callData) +ReturnedValue ConsoleObject::method_log(const BuiltinFunction *b, CallData *callData) { //console.log //console.debug //print - writeToConsole(b, scope, callData, Log); + return writeToConsole(b, callData, Log); } -void ConsoleObject::method_info(const BuiltinFunction *b, Scope &scope, CallData *callData) +ReturnedValue ConsoleObject::method_info(const BuiltinFunction *b, CallData *callData) { - writeToConsole(b, scope, callData, Info); + return writeToConsole(b, callData, Info); } -void ConsoleObject::method_profile(const BuiltinFunction *, Scope &scope, CallData *) +ReturnedValue ConsoleObject::method_profile(const BuiltinFunction *b, CallData *) { + QV4::Scope scope(b); QV4::ExecutionEngine *v4 = scope.engine; - QV4::StackFrame frame = v4->currentStackFrame(); - const QByteArray baSource = frame.source.toUtf8(); - const QByteArray baFunction = frame.function.toUtf8(); - QMessageLogger logger(baSource.constData(), frame.line, baFunction.constData()); + QV4::CppStackFrame *frame = v4->currentStackFrame; + const QByteArray baSource = frame->source().toUtf8(); + const QByteArray baFunction = frame->function().toUtf8(); + QMessageLogger logger(baSource.constData(), frame->lineNumber(), baFunction.constData()); QQmlProfilerService *service = QQmlDebugConnector::service<QQmlProfilerService>(); if (!service) { logger.warning("Cannot start profiling because debug service is disabled. Start with -qmljsdebugger=port:XXXXX."); @@ -1601,17 +1619,18 @@ void ConsoleObject::method_profile(const BuiltinFunction *, Scope &scope, CallDa logger.debug("Profiling started."); } - scope.result = QV4::Encode::undefined(); + return QV4::Encode::undefined(); } -void ConsoleObject::method_profileEnd(const BuiltinFunction *, Scope &scope, CallData *) +ReturnedValue ConsoleObject::method_profileEnd(const BuiltinFunction *b, CallData *) { + QV4::Scope scope(b); QV4::ExecutionEngine *v4 = scope.engine; - QV4::StackFrame frame = v4->currentStackFrame(); - const QByteArray baSource = frame.source.toUtf8(); - const QByteArray baFunction = frame.function.toUtf8(); - QMessageLogger logger(baSource.constData(), frame.line, baFunction.constData()); + QV4::CppStackFrame *frame = v4->currentStackFrame; + const QByteArray baSource = frame->source().toUtf8(); + const QByteArray baFunction = frame->function().toUtf8(); + QMessageLogger logger(baSource.constData(), frame->lineNumber(), baFunction.constData()); QQmlProfilerService *service = QQmlDebugConnector::service<QQmlProfilerService>(); if (!service) { @@ -1621,24 +1640,26 @@ void ConsoleObject::method_profileEnd(const BuiltinFunction *, Scope &scope, Cal logger.debug("Profiling ended."); } - scope.result = QV4::Encode::undefined(); + return QV4::Encode::undefined(); } -void ConsoleObject::method_time(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ConsoleObject::method_time(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 1) + QV4::Scope scope(b); + if (callData->argc() != 1) THROW_GENERIC_ERROR("console.time(): Invalid arguments"); QV8Engine *v8engine = scope.engine->v8Engine; QString name = callData->args[0].toQStringNoThrow(); v8engine->startTimer(name); - scope.result = QV4::Encode::undefined(); + return QV4::Encode::undefined(); } -void ConsoleObject::method_timeEnd(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ConsoleObject::method_timeEnd(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 1) + QV4::Scope scope(b); + if (callData->argc() != 1) THROW_GENERIC_ERROR("console.timeEnd(): Invalid arguments"); QV8Engine *v8engine = scope.engine->v8Engine; @@ -1649,65 +1670,68 @@ void ConsoleObject::method_timeEnd(const BuiltinFunction *, Scope &scope, CallDa if (wasRunning) { qDebug("%s: %llims", qPrintable(name), elapsed); } - scope.result = QV4::Encode::undefined(); + return QV4::Encode::undefined(); } -void ConsoleObject::method_count(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ConsoleObject::method_count(const BuiltinFunction *b, CallData *callData) { // first argument: name to print. Ignore any additional arguments QString name; - if (callData->argc > 0) + if (callData->argc() > 0) name = callData->args[0].toQStringNoThrow(); + Scope scope(b); QV4::ExecutionEngine *v4 = scope.engine; QV8Engine *v8engine = scope.engine->v8Engine; - QV4::StackFrame frame = v4->currentStackFrame(); + QV4::CppStackFrame *frame = v4->currentStackFrame; - QString scriptName = frame.source; + QString scriptName = frame->source(); - int value = v8engine->consoleCountHelper(scriptName, frame.line, frame.column); + int value = v8engine->consoleCountHelper(scriptName, frame->lineNumber(), -1); QString message = name + QLatin1String(": ") + QString::number(value); - QMessageLogger(qPrintable(scriptName), frame.line, - qPrintable(frame.function)) + QMessageLogger(qPrintable(scriptName), frame->lineNumber(), + qPrintable(frame->function())) .debug("%s", qPrintable(message)); - scope.result = QV4::Encode::undefined(); + return QV4::Encode::undefined(); } -void ConsoleObject::method_trace(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ConsoleObject::method_trace(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 0) + QV4::Scope scope(b); + if (callData->argc() != 0) THROW_GENERIC_ERROR("console.trace(): Invalid arguments"); QV4::ExecutionEngine *v4 = scope.engine; QString stack = jsStack(v4); - QV4::StackFrame frame = v4->currentStackFrame(); - QMessageLogger(frame.source.toUtf8().constData(), frame.line, - frame.function.toUtf8().constData()) + QV4::CppStackFrame *frame = v4->currentStackFrame; + QMessageLogger(frame->source().toUtf8().constData(), frame->lineNumber(), + frame->function().toUtf8().constData()) .debug("%s", qPrintable(stack)); - scope.result = QV4::Encode::undefined(); + return QV4::Encode::undefined(); } -void ConsoleObject::method_warn(const BuiltinFunction *b, Scope &scope, CallData *callData) +ReturnedValue ConsoleObject::method_warn(const BuiltinFunction *b, CallData *callData) { - return writeToConsole(b, scope, callData, Warn); + return writeToConsole(b, callData, Warn); } -void ConsoleObject::method_assert(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ConsoleObject::method_assert(const BuiltinFunction *b, CallData *callData) { - if (callData->argc == 0) + QV4::Scope scope(b); + if (callData->argc() == 0) THROW_GENERIC_ERROR("console.assert(): Missing argument"); QV4::ExecutionEngine *v4 = scope.engine; if (!callData->args[0].toBoolean()) { QString message; - for (int i = 1; i < callData->argc; ++i) { + for (int i = 1, ei = callData->argc(); i < ei; ++i) { if (i != 1) message.append(QLatin1Char(' ')); @@ -1716,23 +1740,22 @@ void ConsoleObject::method_assert(const BuiltinFunction *, Scope &scope, CallDat QString stack = jsStack(v4); - QV4::StackFrame frame = v4->currentStackFrame(); - QMessageLogger(frame.source.toUtf8().constData(), frame.line, - frame.function.toUtf8().constData()) + QV4::CppStackFrame *frame = v4->currentStackFrame; + QMessageLogger(frame->source().toUtf8().constData(), frame->lineNumber(), + frame->function().toUtf8().constData()) .critical("%s\n%s",qPrintable(message), qPrintable(stack)); } - scope.result = QV4::Encode::undefined(); + return QV4::Encode::undefined(); } -void ConsoleObject::method_exception(const BuiltinFunction *b, Scope &scope, CallData *callData) +ReturnedValue ConsoleObject::method_exception(const BuiltinFunction *b, CallData *callData) { - if (callData->argc == 0) + QV4::Scope scope(b); + if (callData->argc() == 0) THROW_GENERIC_ERROR("console.exception(): Missing argument"); - writeToConsole(b, scope, callData, Error, true); - - scope.result = QV4::Encode::undefined(); + return writeToConsole(b, callData, Error, true); } @@ -1788,30 +1811,31 @@ void QV4::GlobalExtensions::init(Object *globalObject, QJSEngine::Extensions ext \sa {Internationalization and Localization with Qt Quick} */ -void GlobalExtensions::method_qsTranslate(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalExtensions::method_qsTranslate(const BuiltinFunction *b, CallData *callData) { - if (callData->argc < 2) + QV4::Scope scope(b); + if (callData->argc() < 2) THROW_GENERIC_ERROR("qsTranslate() requires at least two arguments"); if (!callData->args[0].isString()) THROW_GENERIC_ERROR("qsTranslate(): first argument (context) must be a string"); if (!callData->args[1].isString()) THROW_GENERIC_ERROR("qsTranslate(): second argument (sourceText) must be a string"); - if ((callData->argc > 2) && !callData->args[2].isString()) + if ((callData->argc() > 2) && !callData->args[2].isString()) THROW_GENERIC_ERROR("qsTranslate(): third argument (disambiguation) must be a string"); QString context = callData->args[0].toQStringNoThrow(); QString text = callData->args[1].toQStringNoThrow(); QString comment; - if (callData->argc > 2) comment = callData->args[2].toQStringNoThrow(); + if (callData->argc() > 2) comment = callData->args[2].toQStringNoThrow(); int i = 3; - if (callData->argc > i && callData->args[i].isString()) { + if (callData->argc() > i && callData->args[i].isString()) { qWarning("qsTranslate(): specifying the encoding as fourth argument is deprecated"); ++i; } int n = -1; - if (callData->argc > i) + if (callData->argc() > i) n = callData->args[i].toInt32(); QString result = QCoreApplication::translate(context.toUtf8().constData(), @@ -1819,7 +1843,7 @@ void GlobalExtensions::method_qsTranslate(const BuiltinFunction *, Scope &scope, comment.toUtf8().constData(), n); - scope.result = scope.engine->newString(result); + return Encode(scope.engine->newString(result)); } /*! @@ -1844,12 +1868,13 @@ void GlobalExtensions::method_qsTranslate(const BuiltinFunction *, Scope &scope, \sa {Internationalization and Localization with Qt Quick} */ -void GlobalExtensions::method_qsTranslateNoOp(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalExtensions::method_qsTranslateNoOp(const BuiltinFunction *b, CallData *callData) { - if (callData->argc < 2) - scope.result = QV4::Encode::undefined(); + QV4::Scope scope(b); + if (callData->argc() < 2) + return QV4::Encode::undefined(); else - scope.result = callData->args[1]; + return callData->args[1].asReturnedValue(); } /*! @@ -1869,15 +1894,16 @@ void GlobalExtensions::method_qsTranslateNoOp(const BuiltinFunction *, Scope &sc \sa {Internationalization and Localization with Qt Quick} */ -void GlobalExtensions::method_qsTr(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalExtensions::method_qsTr(const BuiltinFunction *b, CallData *callData) { - if (callData->argc < 1) + QV4::Scope scope(b); + if (callData->argc() < 1) THROW_GENERIC_ERROR("qsTr() requires at least one argument"); if (!callData->args[0].isString()) THROW_GENERIC_ERROR("qsTr(): first argument (sourceText) must be a string"); - if ((callData->argc > 1) && !callData->args[1].isString()) + if ((callData->argc() > 1) && !callData->args[1].isString()) THROW_GENERIC_ERROR("qsTr(): second argument (disambiguation) must be a string"); - if ((callData->argc > 2) && !callData->args[2].isNumber()) + if ((callData->argc() > 2) && !callData->args[2].isNumber()) THROW_GENERIC_ERROR("qsTr(): third argument (n) must be a number"); QString context; @@ -1888,10 +1914,10 @@ void GlobalExtensions::method_qsTr(const BuiltinFunction *, Scope &scope, CallDa int length = lastDot - (lastSlash + 1); context = (lastSlash > -1) ? path.mid(lastSlash + 1, (length > -1) ? length : -1) : QString(); } else { - ExecutionContext *parentCtx = scope.engine->currentContext; + CppStackFrame *frame = scope.engine->currentStackFrame; // The first non-empty source URL in the call stack determines the translation context. - while (!!parentCtx && context.isEmpty()) { - if (CompiledData::CompilationUnit *unit = static_cast<CompiledData::CompilationUnit*>(parentCtx->d()->compilationUnit)) { + while (frame && context.isEmpty()) { + if (CompiledData::CompilationUnit *unit = frame->v4Function->compilationUnit) { QString fileName = unit->fileName(); QUrl url(unit->fileName()); if (url.isValid() && url.isRelative()) { @@ -1903,22 +1929,22 @@ void GlobalExtensions::method_qsTr(const BuiltinFunction *, Scope &scope, CallDa } context = QFileInfo(context).baseName(); } - parentCtx = scope.engine->parentContext(parentCtx); + frame = frame->parent; } } QString text = callData->args[0].toQStringNoThrow(); QString comment; - if (callData->argc > 1) + if (callData->argc() > 1) comment = callData->args[1].toQStringNoThrow(); int n = -1; - if (callData->argc > 2) + if (callData->argc() > 2) n = callData->args[2].toInt32(); QString result = QCoreApplication::translate(context.toUtf8().constData(), text.toUtf8().constData(), comment.toUtf8().constData(), n); - scope.result = scope.engine->newString(result); + return Encode(scope.engine->newString(result)); } /*! @@ -1943,12 +1969,12 @@ void GlobalExtensions::method_qsTr(const BuiltinFunction *, Scope &scope, CallDa \sa {Internationalization and Localization with Qt Quick} */ -void GlobalExtensions::method_qsTrNoOp(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalExtensions::method_qsTrNoOp(const BuiltinFunction *, CallData *callData) { - if (callData->argc < 1) - scope.result = QV4::Encode::undefined(); + if (callData->argc() < 1) + return QV4::Encode::undefined(); else - scope.result = callData->args[0]; + return callData->args[0].asReturnedValue(); } /*! @@ -1981,20 +2007,21 @@ void GlobalExtensions::method_qsTrNoOp(const BuiltinFunction *, Scope &scope, Ca \sa QT_TRID_NOOP(), {Internationalization and Localization with Qt Quick} */ -void GlobalExtensions::method_qsTrId(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalExtensions::method_qsTrId(const BuiltinFunction *b, CallData *callData) { - if (callData->argc < 1) + QV4::Scope scope(b); + if (callData->argc() < 1) THROW_GENERIC_ERROR("qsTrId() requires at least one argument"); if (!callData->args[0].isString()) THROW_TYPE_ERROR_WITH_MESSAGE("qsTrId(): first argument (id) must be a string"); - if (callData->argc > 1 && !callData->args[1].isNumber()) + if (callData->argc() > 1 && !callData->args[1].isNumber()) THROW_TYPE_ERROR_WITH_MESSAGE("qsTrId(): second argument (n) must be a number"); int n = -1; - if (callData->argc > 1) + if (callData->argc() > 1) n = callData->args[1].toInt32(); - scope.result = scope.engine->newString(qtTrId(callData->args[0].toQStringNoThrow().toUtf8().constData(), n)); + return Encode(scope.engine->newString(qtTrId(callData->args[0].toQStringNoThrow().toUtf8().constData(), n))); } /*! @@ -2013,28 +2040,29 @@ void GlobalExtensions::method_qsTrId(const BuiltinFunction *, Scope &scope, Call \sa qsTrId(), {Internationalization and Localization with Qt Quick} */ -void GlobalExtensions::method_qsTrIdNoOp(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalExtensions::method_qsTrIdNoOp(const BuiltinFunction *, CallData *callData) { - if (callData->argc < 1) - scope.result = QV4::Encode::undefined(); + if (callData->argc() < 1) + return QV4::Encode::undefined(); else - scope.result = callData->args[0]; + return callData->args[0].asReturnedValue(); } #endif // translation -void GlobalExtensions::method_gc(const BuiltinFunction *, Scope &scope, CallData *) +ReturnedValue GlobalExtensions::method_gc(const BuiltinFunction *b, CallData *) { - scope.engine->memoryManager->runGC(); + b->engine()->memoryManager->runGC(); - scope.result = QV4::Encode::undefined(); + return QV4::Encode::undefined(); } -void GlobalExtensions::method_string_arg(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalExtensions::method_string_arg(const BuiltinFunction *b, CallData *callData) { - if (callData->argc != 1) + QV4::Scope scope(b); + if (callData->argc() != 1) THROW_GENERIC_ERROR("String.arg(): Invalid arguments"); QString value = callData->thisObject.toQString(); @@ -2070,10 +2098,10 @@ be passed on to the function invoked. Note that if redundant calls are eliminated, then only the last set of arguments will be passed to the function. */ -void QtObject::method_callLater(const BuiltinFunction *b, Scope &scope, CallData *callData) +ReturnedValue QtObject::method_callLater(const BuiltinFunction *b, CallData *callData) { - QV8Engine *v8engine = scope.engine->v8Engine; - v8engine->delayedCallQueue()->addUniquelyAndExecuteLater(b, scope, callData); + QV8Engine *v8engine = b->engine()->v8Engine; + return v8engine->delayedCallQueue()->addUniquelyAndExecuteLater(b, callData); } QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h index 21613b7c10..7d61aa0ada 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h +++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h @@ -93,45 +93,45 @@ struct QtObject : Object static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); - static void method_isQtObject(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_rgba(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_hsla(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_hsva(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_colorEqual(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_font(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_rect(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_point(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_size(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_vector2d(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_vector3d(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_vector4d(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_quaternion(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_matrix4x4(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_lighter(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_darker(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_tint(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_formatDate(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_formatTime(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_formatDateTime(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_openUrlExternally(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_fontFamilies(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_md5(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_btoa(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_atob(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_quit(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_exit(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_resolvedUrl(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_createQmlObject(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_createComponent(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_locale(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_binding(const BuiltinFunction *, Scope &scope, CallData *callData); - - static void method_get_platform(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_get_application(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_get_inputMethod(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_get_styleHints(const BuiltinFunction *, Scope &scope, CallData *callData); - - static void method_callLater(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_isQtObject(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_rgba(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_hsla(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_hsva(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_colorEqual(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_font(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_rect(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_point(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_size(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_vector2d(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_vector3d(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_vector4d(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_quaternion(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_matrix4x4(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_lighter(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_darker(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_tint(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_formatDate(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_formatTime(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_formatDateTime(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_openUrlExternally(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_fontFamilies(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_md5(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_btoa(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_atob(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_quit(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_exit(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_resolvedUrl(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_createQmlObject(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_createComponent(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_locale(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_binding(const BuiltinFunction *, CallData *callData); + + static ReturnedValue method_get_platform(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_get_application(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_get_inputMethod(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_get_styleHints(const BuiltinFunction *, CallData *callData); + + static ReturnedValue method_callLater(const BuiltinFunction *, CallData *callData); private: void addAll(); @@ -142,18 +142,18 @@ struct ConsoleObject : Object { V4_OBJECT2(ConsoleObject, Object) - static void method_error(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_log(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_info(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_profile(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_profileEnd(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_time(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_timeEnd(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_count(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_trace(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_warn(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_assert(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_exception(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_error(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_log(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_info(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_profile(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_profileEnd(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_time(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_timeEnd(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_count(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_trace(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_warn(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_assert(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_exception(const BuiltinFunction *, CallData *callData); }; @@ -161,17 +161,17 @@ struct Q_QML_PRIVATE_EXPORT GlobalExtensions { static void init(Object *globalObject, QJSEngine::Extensions extensions); #if QT_CONFIG(translation) - static void method_qsTranslate(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_qsTranslateNoOp(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_qsTr(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_qsTrNoOp(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_qsTrId(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_qsTrIdNoOp(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_qsTranslate(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_qsTranslateNoOp(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_qsTr(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_qsTrNoOp(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_qsTrId(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_qsTrIdNoOp(const BuiltinFunction *, CallData *callData); #endif - static void method_gc(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_gc(const BuiltinFunction *, CallData *callData); // on String:prototype - static void method_string_arg(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_string_arg(const BuiltinFunction *, CallData *callData); }; diff --git a/src/qml/qml/v8/qv4domerrors_p.h b/src/qml/qml/v8/qv4domerrors_p.h index a9bdbe01ae..06a70a13e9 100644 --- a/src/qml/qml/v8/qv4domerrors_p.h +++ b/src/qml/qml/v8/qv4domerrors_p.h @@ -78,8 +78,7 @@ QT_BEGIN_NAMESPACE QV4::ScopedValue v(scope, scope.engine->newString(QStringLiteral(string))); \ QV4::ScopedObject ex(scope, scope.engine->newErrorObject(v)); \ ex->put(QV4::ScopedString(scope, scope.engine->newIdentifier(QStringLiteral("code"))), QV4::ScopedValue(scope, QV4::Primitive::fromInt32(error))); \ - scope.result = scope.engine->throwError(ex); \ - return; \ + return scope.engine->throwError(ex); \ } namespace QV4 { diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h index 3bd3517968..a430fba0e6 100644 --- a/src/qml/qml/v8/qv8engine_p.h +++ b/src/qml/qml/v8/qv8engine_p.h @@ -116,8 +116,8 @@ namespace QV4 { class QQmlV4Function { public: - int length() const { return callData->argc; } - QV4::ReturnedValue operator[](int idx) const { return (idx < callData->argc ? callData->args[idx].asReturnedValue() : QV4::Encode::undefined()); } + int length() const { return callData->argc(); } + QV4::ReturnedValue operator[](int idx) const { return (idx < callData->argc() ? callData->args[idx].asReturnedValue() : QV4::Encode::undefined()); } void setReturnValue(QV4::ReturnedValue rv) { *retVal = rv; } QV4::ExecutionEngine *v4engine() const { return e; } private: diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index 4d2a9746c3..967f89971d 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -96,17 +96,16 @@ struct DelegateModelGroupFunction : QV4::FunctionObject return scope->engine()->memoryManager->allocObject<DelegateModelGroupFunction>(scope, flag, code); } - static void call(const QV4::Managed *that, QV4::Scope &scope, QV4::CallData *callData) + static ReturnedValue call(const QV4::FunctionObject *that, const Value *thisObject, const Value *argv, int argc) { + QV4::Scope scope(that->engine()); QV4::Scoped<DelegateModelGroupFunction> f(scope, static_cast<const DelegateModelGroupFunction *>(that)); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject); - if (!o) { - scope.result = scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")); - return; - } + QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject); + if (!o) + return scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")); - QV4::ScopedValue v(scope, callData->argument(0)); - scope.result = f->d()->code(o->d()->item, f->d()->flag, v); + QV4::ScopedValue v(scope, argc ? argv[0] : Primitive::undefinedValue()); + return f->d()->code(o->d()->item, f->d()->flag, v); } }; @@ -1807,26 +1806,24 @@ int QQmlDelegateModelItemMetaType::parseGroups(const QV4::Value &groups) const return groupFlags; } -void QQmlDelegateModelItem::get_model(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +QV4::ReturnedValue QQmlDelegateModelItem::get_model(const QV4::BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>()); - if (!o) { - scope.result = scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")); - return; - } + if (!o) + return b->engine()->throwTypeError(QStringLiteral("Not a valid VisualData object")); if (!o->d()->item->metaType->model) RETURN_UNDEFINED(); - scope.result = o->d()->item->get(); + return o->d()->item->get(); } -void QQmlDelegateModelItem::get_groups(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +QV4::ReturnedValue QQmlDelegateModelItem::get_groups(const QV4::BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>()); - if (!o) { - scope.result = scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")); - return; - } + if (!o) + return scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")); QStringList groups; for (int i = 1; i < o->d()->item->metaType->groupCount; ++i) { @@ -1834,18 +1831,17 @@ void QQmlDelegateModelItem::get_groups(const QV4::BuiltinFunction *, QV4::Scope groups.append(o->d()->item->metaType->groupNames.at(i - 1)); } - scope.result = scope.engine->fromVariant(groups); + return scope.engine->fromVariant(groups); } -void QQmlDelegateModelItem::set_groups(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +QV4::ReturnedValue QQmlDelegateModelItem::set_groups(const QV4::BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>()); - if (!o) { - scope.result = scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")); - return; - } + if (!o) + return scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")); - if (!callData->argc) + if (!callData->argc()) THROW_TYPE_ERROR(); if (!o->d()->item->metaType->model) @@ -1856,7 +1852,7 @@ void QQmlDelegateModelItem::set_groups(const QV4::BuiltinFunction *, QV4::Scope const int cacheIndex = model->m_cache.indexOf(o->d()->item); Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); model->setGroups(it, 1, Compositor::Cache, groupFlags); - scope.result = QV4::Encode::undefined(); + return QV4::Encode::undefined(); } QV4::ReturnedValue QQmlDelegateModelItem::get_member(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &) @@ -3226,25 +3222,28 @@ struct QQmlDelegateModelGroupChange : QV4::Object return e->memoryManager->allocObject<QQmlDelegateModelGroupChange>(); } - static void method_get_index(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { + static QV4::ReturnedValue method_get_index(const QV4::BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, callData->thisObject.as<QQmlDelegateModelGroupChange>()); if (!that) THROW_TYPE_ERROR(); - scope.result = QV4::Encode(that->d()->change.index); + return QV4::Encode(that->d()->change.index); } - static void method_get_count(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { + static QV4::ReturnedValue method_get_count(const QV4::BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, callData->thisObject.as<QQmlDelegateModelGroupChange>()); if (!that) THROW_TYPE_ERROR(); - scope.result = QV4::Encode(that->d()->change.count); + return QV4::Encode(that->d()->change.count); } - static void method_get_moveId(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { + static QV4::ReturnedValue method_get_moveId(const QV4::BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, callData->thisObject.as<QQmlDelegateModelGroupChange>()); if (!that) THROW_TYPE_ERROR(); if (that->d()->change.moveId < 0) RETURN_UNDEFINED(); - scope.result = QV4::Encode(that->d()->change.moveId); + return QV4::Encode(that->d()->change.moveId); } }; diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h index 3759fe8667..e0416e93ee 100644 --- a/src/qml/types/qqmldelegatemodel_p_p.h +++ b/src/qml/types/qqmldelegatemodel_p_p.h @@ -126,9 +126,9 @@ public: virtual void setValue(const QString &role, const QVariant &value) { Q_UNUSED(role); Q_UNUSED(value); } virtual bool resolveIndex(const QQmlAdaptorModel &, int) { return false; } - static void get_model(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void get_groups(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void set_groups(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static QV4::ReturnedValue get_model(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue get_groups(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue set_groups(const QV4::BuiltinFunction *, QV4::CallData *callData); static QV4::ReturnedValue get_member(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &); static QV4::ReturnedValue set_member(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &arg); static QV4::ReturnedValue get_index(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &arg); diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index c8f8a0fc70..e32e0c75f3 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -2591,14 +2591,10 @@ bool QQmlListModelParser::applyProperty(QV4::CompiledData::CompilationUnit *comp QV4::ScopedContext context(scope, QV4::QmlContext::create(v4->rootContext(), QQmlContextData::get(qmlContext(model->m_modelCache)), nullptr)); QV4::ScopedFunctionObject function(scope, QV4::FunctionObject::createScriptFunction(context, compilationUnit->runtimeFunctions[id])); - // ### we need the inner function declaration (at this point the function has been wrapped) - const unsigned int parameterCount = function->formalParameterCount(); - QV4::ScopedCallData callData(scope, parameterCount); - callData->thisObject = v4->globalObject; - function->call(scope, callData); + QV4::ReturnedValue result = function->call(v4->globalObject, nullptr, 0); QJSValue v; - QJSValuePrivate::setValue(&v, v4, scope.result); + QJSValuePrivate::setValue(&v, v4, result); value.setValue<QJSValue>(v); } else { QByteArray script = scriptStr.toUtf8(); diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp index 6159355afc..346f2e6a61 100644 --- a/src/qml/types/qquickworkerscript.cpp +++ b/src/qml/types/qquickworkerscript.cpp @@ -65,6 +65,7 @@ #include <private/qv4functionobject_p.h> #include <private/qv4script_p.h> #include <private/qv4scopedvalue_p.h> +#include <private/qv4jscall_p.h> QT_BEGIN_NAMESPACE @@ -185,7 +186,7 @@ public: int m_nextId; - static void method_sendMessage(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static QV4::ReturnedValue method_sendMessage(const QV4::BuiltinFunction *, QV4::CallData *callData); signals: void stopThread(); @@ -239,19 +240,18 @@ void QQuickWorkerScriptEnginePrivate::WorkerEngine::init() QV4::Scope scope(m_v4Engine); QV4::ExecutionContext *globalContext = scope.engine->rootContext(); - onmessage.set(scope.engine, QV4::Script(globalContext, QString::fromUtf8(CALL_ONMESSAGE_SCRIPT)).run()); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro + onmessage.set(scope.engine, QV4::Script(globalContext, QV4::Compiler::GlobalCode, QString::fromUtf8(CALL_ONMESSAGE_SCRIPT)).run()); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro Q_ASSERT(!scope.engine->hasException); - QV4::Script createsendscript(globalContext, QString::fromUtf8(SEND_MESSAGE_CREATE_SCRIPT)); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro + QV4::Script createsendscript(globalContext, QV4::Compiler::GlobalCode, QString::fromUtf8(SEND_MESSAGE_CREATE_SCRIPT)); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro QV4::ScopedFunctionObject createsendconstructor(scope, createsendscript.run()); Q_ASSERT(!scope.engine->hasException); QV4::ScopedString name(scope, m_v4Engine->newString(QStringLiteral("sendMessage"))); QV4::ScopedValue function(scope, QV4::BuiltinFunction::create(globalContext, name, - QQuickWorkerScriptEnginePrivate::method_sendMessage)); - QV4::ScopedCallData callData(scope, 1); - callData->args[0] = function; - callData->thisObject = global(); - createsendconstructor->call(scope, callData); - createsend.set(scope.engine, scope.result.asReturnedValue()); + QQuickWorkerScriptEnginePrivate::method_sendMessage)); + QV4::JSCallData jsCallData(scope, 1); + jsCallData->args[0] = function; + *jsCallData->thisObject = global(); + createsend.set(scope.engine, createsendconstructor->call(jsCallData)); } // Requires handle and context scope @@ -264,13 +264,14 @@ QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::WorkerEngine::sendFunction(i QV4::Scope scope(v4); QV4::ScopedFunctionObject f(scope, createsend.value()); - QV4::ScopedCallData callData(scope, 1); - callData->args[0] = QV4::Primitive::fromInt32(id); - callData->thisObject = global(); - f->call(scope, callData); + QV4::ScopedValue v(scope); + QV4::JSCallData jsCallData(scope, 1); + jsCallData->args[0] = QV4::Primitive::fromInt32(id); + *jsCallData->thisObject = global(); + v = f->call(jsCallData); if (scope.hasException()) - scope.result = scope.engine->catchException(); - return scope.result.asReturnedValue(); + v = scope.engine->catchException(); + return v->asReturnedValue(); } #if QT_CONFIG(qml_network) @@ -292,11 +293,13 @@ QQuickWorkerScriptEnginePrivate::QQuickWorkerScriptEnginePrivate(QQmlEngine *eng { } -void QQuickWorkerScriptEnginePrivate::method_sendMessage(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::method_sendMessage(const QV4::BuiltinFunction *b, + QV4::CallData *callData) { + QV4::Scope scope(b); WorkerEngine *engine = (WorkerEngine*)scope.engine->v8Engine; - int id = callData->argc > 1 ? callData->args[1].toInt32() : 0; + int id = callData->argc() > 1 ? callData->args[1].toInt32() : 0; QV4::ScopedValue v(scope, callData->argument(2)); QByteArray data = QV4::Serialize::serialize(v, scope.engine); @@ -306,7 +309,7 @@ void QQuickWorkerScriptEnginePrivate::method_sendMessage(const QV4::BuiltinFunct if (script && script->owner) QCoreApplication::postEvent(script->owner, new WorkerDataEvent(0, data)); - scope.result = QV4::Encode::undefined(); + return QV4::Encode::undefined(); } QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::getWorker(WorkerScript *script) @@ -363,11 +366,11 @@ void QQuickWorkerScriptEnginePrivate::processMessage(int id, const QByteArray &d QV4::Scoped<QV4::QmlContext> qmlContext(scope, script->qmlContext.value()); Q_ASSERT(!!qmlContext); - QV4::ScopedCallData callData(scope, 2); - callData->thisObject = workerEngine->global(); - callData->args[0] = qmlContext->d()->qml; // ### - callData->args[1] = value; - f->call(scope, callData); + QV4::JSCallData jsCallData(scope, 2); + *jsCallData->thisObject = workerEngine->global(); + jsCallData->args[0] = qmlContext->d()->qml(); // ### + jsCallData->args[1] = value; + f->call(jsCallData); if (scope.hasException()) { QQmlError error = scope.engine->catchExceptionAsQmlError(); reportScriptException(script, error); diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp index 11fed281b7..d5b38f6d3e 100644 --- a/src/qml/util/qqmladaptormodel.cpp +++ b/src/qml/util/qqmladaptormodel.cpp @@ -61,8 +61,9 @@ public: V4_DEFINE_EXTENSION(QQmlAdaptorModelEngineData, engineData) -static void get_index(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +static QV4::ReturnedValue get_index(const QV4::BuiltinFunction *f, QV4::CallData *callData) { + QV4::Scope scope(f); QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>()); if (!o) RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"))); @@ -105,8 +106,8 @@ public: void setValue(const QString &role, const QVariant &value) override; bool resolveIndex(const QQmlAdaptorModel &model, int idx) override; - static QV4::ReturnedValue get_property(QV4::CallContext *ctx, uint propertyId); - static QV4::ReturnedValue set_property(QV4::CallContext *ctx, uint propertyId); + static QV4::ReturnedValue get_property(const QV4::BuiltinFunction *, QV4::CallData *); + static QV4::ReturnedValue set_property(const QV4::BuiltinFunction *, QV4::CallData *); VDMModelDelegateDataType *type; QVector<QVariant> cachedData; @@ -194,8 +195,9 @@ public: dataType->watchedRoles += newRoles; } - static void get_hasModelChildren(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) + static QV4::ReturnedValue get_hasModelChildren(const QV4::BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>()); if (!o) RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"))); @@ -341,12 +343,14 @@ bool QQmlDMCachedModelData::resolveIndex(const QQmlAdaptorModel &, int idx) } } -QV4::ReturnedValue QQmlDMCachedModelData::get_property(QV4::CallContext *ctx, uint propertyId) +QV4::ReturnedValue QQmlDMCachedModelData::get_property(const QV4::BuiltinFunction *b, QV4::CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, ctx->thisObject().as<QQmlDelegateModelItemObject>()); + QV4::Scope scope(b); + QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>()); if (!o) - return ctx->engine()->throwTypeError(QStringLiteral("Not a valid VisualData object")); + return scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")); + + uint propertyId = static_cast<const QV4::IndexedBuiltinFunction *>(b)->d()->index; QQmlDMCachedModelData *modelData = static_cast<QQmlDMCachedModelData *>(o->d()->item); if (o->d()->item->index == -1) { @@ -361,23 +365,25 @@ QV4::ReturnedValue QQmlDMCachedModelData::get_property(QV4::CallContext *ctx, ui return QV4::Encode::undefined(); } -QV4::ReturnedValue QQmlDMCachedModelData::set_property(QV4::CallContext *ctx, uint propertyId) +QV4::ReturnedValue QQmlDMCachedModelData::set_property(const QV4::BuiltinFunction *b, QV4::CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, ctx->thisObject().as<QQmlDelegateModelItemObject>()); + QV4::Scope scope(b); + QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>()); if (!o) - return ctx->engine()->throwTypeError(QStringLiteral("Not a valid VisualData object")); - if (!ctx->argc()) - return ctx->engine()->throwTypeError(); + return scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")); + if (!callData->argc()) + return scope.engine->throwTypeError(); + + uint propertyId = static_cast<const QV4::IndexedBuiltinFunction *>(b)->d()->index; if (o->d()->item->index == -1) { QQmlDMCachedModelData *modelData = static_cast<QQmlDMCachedModelData *>(o->d()->item); if (!modelData->cachedData.isEmpty()) { if (modelData->cachedData.count() > 1) { - modelData->cachedData[propertyId] = scope.engine->toVariant(ctx->args()[0], QVariant::Invalid); + modelData->cachedData[propertyId] = scope.engine->toVariant(callData->args[0], QVariant::Invalid); QMetaObject::activate(o->d()->item, o->d()->item->metaObject(), propertyId, 0); } else if (modelData->cachedData.count() == 1) { - modelData->cachedData[0] = scope.engine->toVariant(ctx->args()[0], QVariant::Invalid); + modelData->cachedData[0] = scope.engine->toVariant(callData->args[0], QVariant::Invalid); QMetaObject::activate(o->d()->item, o->d()->item->metaObject(), 0, 0); QMetaObject::activate(o->d()->item, o->d()->item->metaObject(), 1, 0); } @@ -580,25 +586,27 @@ public: } } - static void get_modelData(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) + static QV4::ReturnedValue get_modelData(const QV4::BuiltinFunction *b, QV4::CallData *callData) { - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>()); + QV4::ExecutionEngine *v4 = b->engine(); + QQmlDelegateModelItemObject *o = callData->thisObject.as<QQmlDelegateModelItemObject>(); if (!o) - RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"))); + return v4->throwTypeError(QStringLiteral("Not a valid VisualData object")); - RETURN_RESULT(scope.engine->fromVariant(static_cast<QQmlDMListAccessorData *>(o->d()->item)->cachedData)); + return v4->fromVariant(static_cast<QQmlDMListAccessorData *>(o->d()->item)->cachedData); } - static void set_modelData(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) + static QV4::ReturnedValue set_modelData(const QV4::BuiltinFunction *b, QV4::CallData *callData) { - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>()); + QV4::ExecutionEngine *v4 = b->engine(); + QQmlDelegateModelItemObject *o = callData->thisObject.as<QQmlDelegateModelItemObject>(); if (!o) - RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"))); - if (!callData->argc) - RETURN_RESULT(scope.engine->throwTypeError()); + return v4->throwTypeError(QStringLiteral("Not a valid VisualData object")); + if (!callData->argc()) + return v4->throwTypeError(); - static_cast<QQmlDMListAccessorData *>(o->d()->item)->setModelData(scope.engine->toVariant(callData->args[0], QVariant::Invalid)); - RETURN_RESULT(QV4::Encode::undefined()); + static_cast<QQmlDMListAccessorData *>(o->d()->item)->setModelData(v4->toVariant(callData->args[0], QVariant::Invalid)); + return QV4::Encode::undefined(); } QV4::ReturnedValue get() override |