diff options
author | Oswald Buddenhagen <oswald.buddenhagen@qt.io> | 2017-02-01 20:43:29 +0100 |
---|---|---|
committer | Oswald Buddenhagen <oswald.buddenhagen@qt.io> | 2017-02-01 20:43:30 +0100 |
commit | 6ba26317d0fc0573aca7638eda8bdb91e52d1ab3 (patch) | |
tree | 061ddc3d0977fc6364eb3275a3da4287fd1a7869 /src/qml | |
parent | cd39a62bbd5c6e725547a696c297c46f929b3439 (diff) | |
parent | 835f8a90387c8d62e7ab262d23e3ab103aa6d133 (diff) |
Merge dev into 5.9
Change-Id: I8436b1be632b80aae340cc692795ba86b4e3e79b
Diffstat (limited to 'src/qml')
111 files changed, 6826 insertions, 5125 deletions
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index 1de5dfa6fa..fa66d3a6e3 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -39,7 +39,10 @@ SOURCES += \ unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp else: SOURCES += $$PWD/qv4compilationunitmapper_win.cpp -qtConfig(qml-interpreter) { +qtConfig(private_tests): LIBS_PRIVATE += $$QMAKE_LIBS_DYNLOAD +} + +qmldevtools_build|qtConfig(qml-interpreter) { HEADERS += \ $$PWD/qv4instr_moth_p.h \ $$PWD/qv4isel_moth_p.h @@ -48,6 +51,3 @@ qtConfig(qml-interpreter) { $$PWD/qv4isel_moth.cpp } - -qtConfig(private_tests): LIBS_PRIVATE += $$QMAKE_LIBS_DYNLOAD -} diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 54d0cb4f46..8a7507c92e 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -2061,4 +2061,156 @@ QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevis return 0; } +IRLoader::IRLoader(const QV4::CompiledData::Unit *qmlData, QmlIR::Document *output) + : unit(qmlData) + , output(output) +{ + pool = output->jsParserEngine.pool(); +} + +void IRLoader::load() +{ + output->jsGenerator.stringTable.clear(); + for (uint i = 0; i < unit->stringTableSize; ++i) + output->jsGenerator.stringTable.registerString(unit->stringAt(i)); + + for (quint32 i = 0; i < unit->nImports; ++i) + output->imports << unit->importAt(i); + + if (unit->flags & QV4::CompiledData::Unit::IsSingleton) { + QmlIR::Pragma *p = New<QmlIR::Pragma>(); + p->location = QV4::CompiledData::Location(); + p->type = QmlIR::Pragma::PragmaSingleton; + output->pragmas << p; + } + + output->indexOfRootObject = unit->indexOfRootObject; + + for (uint i = 0; i < unit->nObjects; ++i) { + const QV4::CompiledData::Object *serializedObject = unit->objectAt(i); + QmlIR::Object *object = loadObject(serializedObject); + output->objects.append(object); + } +} + +struct FakeExpression : public QQmlJS::AST::NullExpression +{ + FakeExpression(int start, int length) + : location(start, length) + {} + + virtual QQmlJS::AST::SourceLocation firstSourceLocation() const + { return location; } + + virtual QQmlJS::AST::SourceLocation lastSourceLocation() const + { return location; } + +private: + QQmlJS::AST::SourceLocation location; +}; + +QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedObject) +{ + QmlIR::Object *object = pool->New<QmlIR::Object>(); + object->init(pool, serializedObject->inheritedTypeNameIndex, serializedObject->idNameIndex); + + object->indexOfDefaultPropertyOrAlias = serializedObject->indexOfDefaultPropertyOrAlias; + object->defaultPropertyIsAlias = serializedObject->defaultPropertyIsAlias; + + object->location = serializedObject->location; + object->locationOfIdProperty = serializedObject->locationOfIdProperty; + + QVector<int> functionIndices; + functionIndices.reserve(serializedObject->nFunctions + serializedObject->nBindings / 2); + + for (uint i = 0; i < serializedObject->nBindings; ++i) { + QmlIR::Binding *b = pool->New<QmlIR::Binding>(); + *static_cast<QV4::CompiledData::Binding*>(b) = serializedObject->bindingTable()[i]; + object->bindings->append(b); + if (b->type == QV4::CompiledData::Binding::Type_Script) { + functionIndices.append(b->value.compiledScriptIndex); + b->value.compiledScriptIndex = functionIndices.count() - 1; + + QmlIR::CompiledFunctionOrExpression *foe = pool->New<QmlIR::CompiledFunctionOrExpression>(); + foe->disableAcceleratedLookups = true; + foe->nameIndex = 0; + + QQmlJS::AST::ExpressionNode *expr; + + if (b->stringIndex != quint32(0)) { + const int start = output->code.length(); + const QString script = output->stringAt(b->stringIndex); + const int length = script.length(); + output->code.append(script); + expr = new (pool) FakeExpression(start, length); + } else + expr = new (pool) QQmlJS::AST::NullExpression(); + foe->node = new (pool) QQmlJS::AST::ExpressionStatement(expr); // dummy + object->functionsAndExpressions->append(foe); + } + } + + Q_ASSERT(object->functionsAndExpressions->count == functionIndices.count()); + + for (uint i = 0; i < serializedObject->nSignals; ++i) { + const QV4::CompiledData::Signal *serializedSignal = serializedObject->signalAt(i); + QmlIR::Signal *s = pool->New<QmlIR::Signal>(); + s->nameIndex = serializedSignal->nameIndex; + s->location = serializedSignal->location; + s->parameters = pool->New<QmlIR::PoolList<QmlIR::SignalParameter> >(); + + for (uint i = 0; i < serializedSignal->nParameters; ++i) { + QmlIR::SignalParameter *p = pool->New<QmlIR::SignalParameter>(); + *static_cast<QV4::CompiledData::Parameter*>(p) = *serializedSignal->parameterAt(i); + s->parameters->append(p); + } + + object->qmlSignals->append(s); + } + + const QV4::CompiledData::Property *serializedProperty = serializedObject->propertyTable(); + for (uint i = 0; i < serializedObject->nProperties; ++i, ++serializedProperty) { + QmlIR::Property *p = pool->New<QmlIR::Property>(); + *static_cast<QV4::CompiledData::Property*>(p) = *serializedProperty; + object->properties->append(p); + } + + QQmlJS::Engine *jsParserEngine = &output->jsParserEngine; + + const QV4::CompiledData::LEUInt32 *functionIdx = serializedObject->functionOffsetTable(); + for (uint i = 0; i < serializedObject->nFunctions; ++i, ++functionIdx) { + QmlIR::Function *f = pool->New<QmlIR::Function>(); + const QV4::CompiledData::Function *compiledFunction = unit->functionAt(*functionIdx); + + functionIndices.append(*functionIdx); + f->index = functionIndices.count() - 1; + f->location = compiledFunction->location; + f->nameIndex = compiledFunction->nameIndex; + + QQmlJS::AST::FormalParameterList *paramList = 0; + const QV4::CompiledData::LEUInt32 *formalNameIdx = compiledFunction->formalsTable(); + for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx) { + const QString formal = unit->stringAt(*formalNameIdx); + QStringRef paramNameRef = jsParserEngine->newStringRef(formal); + + if (paramList) + paramList = new (pool) QQmlJS::AST::FormalParameterList(paramList, paramNameRef); + else + paramList = new (pool) QQmlJS::AST::FormalParameterList(paramNameRef); + } + + if (paramList) + paramList = paramList->finish(); + + const QString name = unit->stringAt(compiledFunction->nameIndex); + f->functionDeclaration = new(pool) QQmlJS::AST::FunctionDeclaration(jsParserEngine->newStringRef(name), paramList, /*body*/0); + + object->functions->append(f); + } + + object->runtimeFunctionIndices.allocate(pool, functionIndices); + + return object; +} + #endif // V4_BOOTSTRAP diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 95756845c3..2022112e07 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -622,6 +622,21 @@ private: int _importedScriptsTemp; }; +struct IRLoader { + IRLoader(const QV4::CompiledData::Unit *unit, QmlIR::Document *output); + + void load(); + +private: + QmlIR::Object *loadObject(const QV4::CompiledData::Object *serializedObject); + + template <typename _Tp> _Tp *New() { return pool->New<_Tp>(); } + + const QV4::CompiledData::Unit *unit; + QmlIR::Document *output; + QQmlJS::MemoryPool *pool; +}; + } // namespace QmlIR QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index ab2b0553a9..85267225be 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -57,12 +57,12 @@ QT_BEGIN_NAMESPACE QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, - QmlIR::Document *parsedQML, const QQmlRefPointer<QQmlTypeNameCache> &importCache, + QmlIR::Document *parsedQML, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache) : resolvedTypes(resolvedTypeCache) , engine(engine) , typeData(typeData) - , importCache(importCache) + , typeNameCache(typeNameCache) , document(parsedQML) { } @@ -138,7 +138,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() sss.scan(); } - QmlIR::JSCodeGen v4CodeGenerator(typeData->finalUrlString(), document->code, &document->jsModule, &document->jsParserEngine, document->program, importCache, &document->jsGenerator.stringTable); + QmlIR::JSCodeGen v4CodeGenerator(typeData->finalUrlString(), document->code, &document->jsModule, &document->jsParserEngine, document->program, typeNameCache, &document->jsGenerator.stringTable); QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator); if (!jsCodeGen.generateCodeForComponents()) return nullptr; @@ -164,7 +164,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() QV4::CompiledData::CompilationUnit *compilationUnit = document->javaScriptCompilationUnit; compilationUnit = document->javaScriptCompilationUnit; - compilationUnit->importCache = importCache; + compilationUnit->typeNameCache = typeNameCache; compilationUnit->resolvedTypes = resolvedTypes; compilationUnit->propertyCaches = std::move(m_propertyCaches); Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast<int>(compilationUnit->data->nObjects)); diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index de6abb4ced..2b59e7e42f 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -89,7 +89,7 @@ struct QQmlTypeCompiler { Q_DECLARE_TR_FUNCTIONS(QQmlTypeCompiler) public: - QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document, const QQmlRefPointer<QQmlTypeNameCache> &importCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache); + QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache); // --- interface used by QQmlPropertyCacheCreator typedef QmlIR::Object CompiledObject; @@ -139,7 +139,7 @@ private: QList<QQmlError> errors; QQmlEnginePrivate *engine; QQmlTypeData *typeData; - QQmlRefPointer<QQmlTypeNameCache> importCache; + QQmlRefPointer<QQmlTypeNameCache> typeNameCache; QmlIR::Document *document; // index is string index of type name (use obj->inheritedTypeNameIndex) QHash<int, QQmlCustomParser*> customParsers; diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index e0def1021b..7d3ad38f97 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -526,21 +526,21 @@ IR::Expr *Codegen::subscript(IR::Expr *base, IR::Expr *index) if (hasError) return 0; - if (! base->asTemp() || base->asArgLocal()) { + if (! base->asTemp() && !base->asArgLocal()) { const unsigned t = _block->newTemp(); move(_block->TEMP(t), base); base = _block->TEMP(t); } - if (! index->asTemp() || index->asArgLocal()) { + if (! index->asTemp() && !index->asArgLocal() && !index->asConst()) { const unsigned t = _block->newTemp(); move(_block->TEMP(t), index); index = _block->TEMP(t); } Q_ASSERT(base->asTemp() || base->asArgLocal()); - Q_ASSERT(index->asTemp() || index->asArgLocal()); - return _block->SUBSCRIPT(base->asTemp(), index->asTemp()); + Q_ASSERT(index->asTemp() || index->asArgLocal() || index->asConst()); + return _block->SUBSCRIPT(base, index); } IR::Expr *Codegen::argument(IR::Expr *expr) @@ -611,7 +611,7 @@ IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right, const AS if (IR::Const *c1 = left->asConst()) { if (IR::Const *c2 = right->asConst()) { - if (c1->type == IR::NumberType && c2->type == IR::NumberType) { + 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); @@ -659,20 +659,20 @@ IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right, const AS } } - if (!left->asTemp() && !left->asArgLocal()) { + 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()) { + 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()); - Q_ASSERT(right->asTemp() || right->asArgLocal()); + Q_ASSERT(left->asTemp() || left->asArgLocal() || left->asConst()); + Q_ASSERT(right->asTemp() || right->asArgLocal() || right->asConst()); return _block->BINOP(op, left, right); } @@ -842,9 +842,16 @@ void Codegen::variableDeclaration(VariableDeclaration *ast) Q_ASSERT(expr.code); initializer = *expr; - int initialized = _block->newTemp(); - move(_block->TEMP(initialized), initializer); - move(identifier(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn), _block->TEMP(initialized)); + IR::Expr *lhs = identifier(ast->name.toString(), ast->identifierToken.startLine, + ast->identifierToken.startColumn); + + if (lhs->asArgLocal()) { + move(lhs, initializer); + } else { + int initialized = _block->newTemp(); + move(_block->TEMP(initialized), initializer); + move(lhs, _block->TEMP(initialized)); + } } void Codegen::variableDeclarationList(VariableDeclarationList *ast) diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 6aac111897..8f8d374e24 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -52,7 +52,6 @@ #include "qv4compilationunitmapper_p.h" #include <QQmlPropertyMap> #include <QDateTime> -#include <QSaveFile> #include <QFile> #include <QFileInfo> #include <QScopedValueRollback> @@ -62,6 +61,7 @@ #include <private/qqmlirbuilder_p.h> #include <QCoreApplication> #include <QCryptographicHash> +#include <QSaveFile> #include <algorithm> @@ -77,6 +77,27 @@ namespace QV4 { namespace CompiledData { +#ifdef V4_BOOTSTRAP +static QString cacheFilePath(const QString &localSourcePath) +{ + const QString localCachePath = localSourcePath + QLatin1Char('c'); + return localCachePath; +} +#else +static QString cacheFilePath(const QUrl &url) +{ + const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url); + const QString localCachePath = localSourcePath + QLatin1Char('c'); + if (QFileInfo(QFileInfo(localSourcePath).dir().absolutePath()).isWritable()) + return localCachePath; + QCryptographicHash fileNameHash(QCryptographicHash::Sha1); + fileNameHash.addData(localSourcePath.toUtf8()); + QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/"); + QDir::root().mkpath(directory); + return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + QFileInfo(localCachePath).completeSuffix(); +} +#endif + #ifndef V4_BOOTSTRAP CompilationUnit::CompilationUnit() : data(0) @@ -207,7 +228,7 @@ void CompilationUnit::unlink() dependentScripts.at(ii)->release(); dependentScripts.clear(); - importCache = nullptr; + typeNameCache = nullptr; qDeleteAll(resolvedTypes); resolvedTypes.clear(); @@ -329,20 +350,68 @@ bool CompilationUnit::verifyChecksum(QQmlEngine *engine, sizeof(data->dependencyMD5Checksum)) == 0; } -static QString cacheFilePath(const QUrl &url) +bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString) { - const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url); - const QString localCachePath = localSourcePath + QLatin1Char('c'); - if (QFileInfo(QFileInfo(localSourcePath).dir().absolutePath()).isWritable()) - return localCachePath; - QCryptographicHash fileNameHash(QCryptographicHash::Sha1); - fileNameHash.addData(localSourcePath.toUtf8()); - QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/"); - QDir::root().mkpath(directory); - return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + QFileInfo(localCachePath).completeSuffix(); + if (!QQmlFile::isLocalFile(url)) { + *errorString = QStringLiteral("File has to be a local file."); + return false; + } + + const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url); + QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper()); + + CompiledData::Unit *mappedUnit = cacheFile->open(cacheFilePath(url), sourcePath, errorString); + if (!mappedUnit) + return false; + + const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr; + QScopedValueRollback<const Unit *> dataPtrChange(data, mappedUnit); + + if (sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) { + *errorString = QStringLiteral("QML source file has moved to a different location."); + return false; + } + + { + const QString foundArchitecture = stringAt(data->architectureIndex); + const QString expectedArchitecture = QSysInfo::buildAbi(); + if (foundArchitecture != expectedArchitecture) { + *errorString = QString::fromUtf8("Architecture mismatch. Found %1 expected %2").arg(foundArchitecture).arg(expectedArchitecture); + return false; + } + } + + { + const QString foundCodeGenerator = stringAt(data->codeGeneratorIndex); + const QString expectedCodeGenerator = iselFactory->codeGeneratorName; + 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) +{ + *errorString = QStringLiteral("Missing code mapping backend"); + return false; } +#endif // V4_BOOTSTRAP + +#if defined(V4_BOOTSTRAP) +bool CompilationUnit::saveToDisk(const QString &unitUrl, QString *errorString) +#else bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) +#endif { errorString->clear(); @@ -351,10 +420,12 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) return false; } +#if !defined(V4_BOOTSTRAP) if (!QQmlFile::isLocalFile(unitUrl)) { *errorString = QStringLiteral("File has to be a local file."); return false; } +#endif // Foo.qml -> Foo.qmlc QSaveFile cacheFile(cacheFilePath(unitUrl)); @@ -390,78 +461,105 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) return true; } -bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString) +void CompilationUnit::prepareCodeOffsetsForDiskStorage(Unit *unit) { - if (!QQmlFile::isLocalFile(url)) { - *errorString = QStringLiteral("File has to be a local file."); - return false; - } + Q_UNUSED(unit); +} - const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url); - QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper()); +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; +} - CompiledData::Unit *mappedUnit = cacheFile->open(cacheFilePath(url), sourcePath, errorString); - if (!mappedUnit) - return false; +Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument) +{ + if (!irDocument->javaScriptCompilationUnit->data) + return irDocument->jsGenerator.generateUnit(QV4::Compiler::JSUnitGenerator::GenerateWithoutStringTable); - const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr; - QScopedValueRollback<const Unit *> dataPtrChange(data, mappedUnit); + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = irDocument->javaScriptCompilationUnit; + QV4::CompiledData::Unit *jsUnit = const_cast<QV4::CompiledData::Unit*>(irDocument->javaScriptCompilationUnit->data); - if (sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) { - *errorString = QStringLiteral("QML source file has moved to a different location."); - return false; - } + QV4::Compiler::StringTableGenerator &stringTable = irDocument->jsGenerator.stringTable; - { - const QString foundArchitecture = stringAt(data->architectureIndex); - const QString expectedArchitecture = QSysInfo::buildAbi(); - if (foundArchitecture != expectedArchitecture) { - *errorString = QString::fromUtf8("Architecture mismatch. Found %1 expected %2").arg(foundArchitecture).arg(expectedArchitecture); - return false; + // Collect signals that have had a change in signature (from onClicked to onClicked(mouse) for example) + // and now need fixing in the QV4::CompiledData. Also register strings at the same time, to finalize + // the string table. + QVector<quint32> changedSignals; + QVector<QQmlJS::AST::FormalParameterList*> changedSignalParameters; + for (QmlIR::Object *o: qAsConst(irDocument->objects)) { + for (QmlIR::Binding *binding = o->firstBinding(); binding; binding = binding->next) { + if (!(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression)) + continue; + + quint32 functionIndex = binding->value.compiledScriptIndex; + QmlIR::CompiledFunctionOrExpression *foe = o->functionsAndExpressions->slowAt(functionIndex); + if (!foe) + continue; + + // save absolute index + changedSignals << o->runtimeFunctionIndices.at(functionIndex); + + Q_ASSERT(foe->node); + Q_ASSERT(QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(foe->node)); + + QQmlJS::AST::FormalParameterList *parameters = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(foe->node)->formals; + changedSignalParameters << parameters; + + for (; parameters; parameters = parameters->next) + stringTable.registerString(parameters->name.toString()); } } - { - const QString foundCodeGenerator = stringAt(data->codeGeneratorIndex); - const QString expectedCodeGenerator = iselFactory->codeGeneratorName; - if (foundCodeGenerator != expectedCodeGenerator) { - *errorString = QString::fromUtf8("Code generator mismatch. Found code generated by %1 but expected %2").arg(foundCodeGenerator).arg(expectedCodeGenerator); - return false; + QVector<quint32> signalParameterNameTable; + quint32 signalParameterNameTableOffset = jsUnit->unitSize; + + // Update signal signatures + if (!changedSignals.isEmpty()) { + if (jsUnit == compilationUnit->data) { + char *unitCopy = (char*)malloc(jsUnit->unitSize); + memcpy(unitCopy, jsUnit, jsUnit->unitSize); + jsUnit = reinterpret_cast<QV4::CompiledData::Unit*>(unitCopy); } - } - if (!memoryMapCode(errorString)) - return false; + for (int i = 0; i < changedSignals.count(); ++i) { + const uint functionIndex = changedSignals.at(i); + // The data is now read-write due to the copy above, so the const_cast is ok. + QV4::CompiledData::Function *function = const_cast<QV4::CompiledData::Function *>(jsUnit->functionAt(functionIndex)); + Q_ASSERT(function->nFormals == quint32(0)); - dataPtrChange.commit(); - free(const_cast<Unit*>(oldDataPtr)); - backingFile.reset(cacheFile.take()); - return true; -} + function->formalsOffset = signalParameterNameTableOffset - jsUnit->functionOffsetTable()[functionIndex]; -void CompilationUnit::prepareCodeOffsetsForDiskStorage(Unit *unit) -{ - Q_UNUSED(unit); -} + for (QQmlJS::AST::FormalParameterList *parameters = changedSignalParameters.at(i); + parameters; parameters = parameters->next) { + signalParameterNameTable.append(stringTable.getStringId(parameters->name.toString())); + function->nFormals = function->nFormals + 1; + } -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; -} + // Hack to ensure an activation is created. + function->flags |= QV4::CompiledData::Function::HasCatchOrWith | QV4::CompiledData::Function::HasDirectEval; -bool CompilationUnit::memoryMapCode(QString *errorString) -{ - *errorString = QStringLiteral("Missing code mapping backend"); - return false; -} -#endif // V4_BOOTSTRAP + signalParameterNameTableOffset += function->nFormals * sizeof(quint32); + } + } -Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument) -{ - return irDocument->jsGenerator.generateUnit(QV4::Compiler::JSUnitGenerator::GenerateWithoutStringTable); + if (!signalParameterNameTable.isEmpty()) { + Q_ASSERT(jsUnit != compilationUnit->data); + const uint signalParameterTableSize = signalParameterNameTable.count() * sizeof(quint32); + uint newSize = jsUnit->unitSize + signalParameterTableSize; + const uint oldSize = jsUnit->unitSize; + char *unitWithSignalParameters = (char*)realloc(jsUnit, newSize); + memcpy(unitWithSignalParameters + oldSize, signalParameterNameTable.constData(), signalParameterTableSize); + jsUnit = reinterpret_cast<QV4::CompiledData::Unit*>(unitWithSignalParameters); + jsUnit->unitSize = newSize; + } + + if (jsUnit != compilationUnit->data) + jsUnit->flags &= ~QV4::CompiledData::Unit::StaticData; + + return jsUnit; } QString Binding::valueAsString(const Unit *unit) const @@ -624,7 +722,7 @@ static QByteArray ownLibraryChecksum() if (checksumInitialized) return libraryChecksum; checksumInitialized = true; -#if defined(Q_OS_UNIX) && !defined(QT_NO_DYNAMIC_CAST) +#if defined(Q_OS_UNIX) && !defined(QT_NO_DYNAMIC_CAST) && !defined(Q_OS_INTEGRITY) Dl_info libInfo; if (dladdr(reinterpret_cast<const void *>(&ownLibraryChecksum), &libInfo) != 0) { QFile library(QFile::decodeName(libInfo.dli_fname)); diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 2682365182..13a0c4b075 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -71,7 +71,7 @@ QT_BEGIN_NAMESPACE // Bump this whenever the compiler data structures change in an incompatible way. -#define QV4_DATA_STRUCTURE_VERSION 0x08 +#define QV4_DATA_STRUCTURE_VERSION 0x09 class QIODevice; class QQmlPropertyCache; @@ -133,7 +133,7 @@ struct Location QJsonPrivate::qle_bitfield<20, 12> column; }; - Location() { line = 0; column = 0; } + Location() { line.val = 0; column.val = 0; } inline bool operator<(const Location &other) const { return line < other.line || @@ -153,7 +153,7 @@ struct RegExp QJsonPrivate::qle_bitfield<4, 28> stringIndex; }; - RegExp() { flags = 0; stringIndex = 0; } + RegExp() { flags.val = 0; stringIndex.val = 0; } }; struct Lookup @@ -171,7 +171,7 @@ struct Lookup QJsonPrivate::qle_bitfield<4, 28> nameIndex; }; - Lookup() { type_and_flags = 0; nameIndex = 0; } + Lookup() { type_and_flags.val = 0; nameIndex.val = 0; } }; struct JSClassMember @@ -625,7 +625,8 @@ struct Unit StaticData = 0x4, // Unit data persistent in memory? IsSingleton = 0x8, IsSharedLibrary = 0x10, // .pragma shared? - ContainsMachineCode = 0x20 // used to determine if we need to mmap with execute permissions + ContainsMachineCode = 0x20, // used to determine if we need to mmap with execute permissions + PendingTypeCompilation = 0x40 // the QML data structures present are incomplete and require type compilation }; LEUInt32 flags; LEUInt32 stringTableSize; @@ -777,31 +778,7 @@ struct TypeReferenceMap : QHash<int, TypeReference> }; #ifndef V4_BOOTSTRAP -struct ResolvedTypeReference -{ - ResolvedTypeReference() - : type(0) - , majorVersion(0) - , minorVersion(0) - , isFullyDynamicType(false) - {} - - QQmlType *type; - QQmlRefPointer<QQmlPropertyCache> typePropertyCache; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; - - int majorVersion; - int minorVersion; - // Types such as QQmlPropertyMap can add properties dynamically at run-time and - // therefore cannot have a property cache installed when instantiated. - bool isFullyDynamicType; - - QQmlPropertyCache *propertyCache() const; - QQmlPropertyCache *createPropertyCache(QQmlEngine *); - bool addToHash(QCryptographicHash *hash, QQmlEngine *engine); - - void doDynamicTypeCheck(); -}; +struct ResolvedTypeReference; // map from name index // While this could be a hash, a map is chosen here to provide a stable // order, which is used to calculating a check-sum on dependent meta-objects. @@ -841,10 +818,14 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount #ifndef V4_BOOTSTRAP ExecutionEngine *engine; +#endif + + QV4::Heap::String **runtimeStrings; // Array + +#ifndef V4_BOOTSTRAP QString fileName() const { return data->stringAt(data->sourceFileIndex); } QUrl url() const { if (m_url.isNull) m_url = QUrl(fileName()); return m_url; } - QV4::Heap::String **runtimeStrings; // Array QV4::Lookup *runtimeLookups; QV4::Value *runtimeRegularExpressions; QV4::InternalClass **runtimeClasses; @@ -855,7 +836,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount QQmlPropertyCacheVector propertyCaches; QQmlPropertyCache *rootPropertyCache() const { return propertyCaches.at(data->indexOfRootObject); } - QQmlRefPointer<QQmlTypeNameCache> importCache; + QQmlRefPointer<QQmlTypeNameCache> typeNameCache; // index is object index. This allows fast access to the // property data when initializing bindings, avoiding expensive @@ -918,17 +899,53 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount void destroy() Q_DECL_OVERRIDE; - bool saveToDisk(const QUrl &unitUrl, QString *errorString); bool loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString); protected: virtual void linkBackendToEngine(QV4::ExecutionEngine *engine) = 0; - virtual void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit); - virtual bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString); virtual bool memoryMapCode(QString *errorString); #endif // V4_BOOTSTRAP + +public: +#if defined(V4_BOOTSTRAP) + bool saveToDisk(const QString &unitUrl, 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 +struct ResolvedTypeReference +{ + ResolvedTypeReference() + : type(0) + , majorVersion(0) + , minorVersion(0) + , isFullyDynamicType(false) + {} + + QQmlType *type; + QQmlRefPointer<QQmlPropertyCache> typePropertyCache; + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; + + int majorVersion; + int minorVersion; + // Types such as QQmlPropertyMap can add properties dynamically at run-time and + // therefore cannot have a property cache installed when instantiated. + bool isFullyDynamicType; + + QQmlPropertyCache *propertyCache() const; + QQmlPropertyCache *createPropertyCache(QQmlEngine *); + bool addToHash(QCryptographicHash *hash, QQmlEngine *engine); + + void doDynamicTypeCheck(); +}; +#endif + } } diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index ca4e0b73d4..53d9956315 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -52,10 +52,11 @@ // #include <private/qv4global_p.h> #include <private/qv4value_p.h> -#include <private/qv4function_p.h> #include <private/qv4runtime_p.h> +#if !defined(V4_BOOTSTRAP) QT_REQUIRE_CONFIG(qml_interpreter); +#endif QT_BEGIN_NAMESPACE diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index 9dbebd1128..04844302d9 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -39,15 +39,15 @@ #include "qv4isel_util_p.h" #include "qv4isel_moth_p.h" -#include "qv4vme_moth_p.h" #include "qv4ssa_p.h" -#include <private/qv4debugging_p.h> -#include <private/qv4function_p.h> -#include <private/qv4regexpobject_p.h> #include <private/qv4compileddata_p.h> -#include <private/qqmlengine_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; @@ -1185,8 +1185,11 @@ void InstructionSelection::callBuiltinPushWithScope(IR::Expr *arg) 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) @@ -1335,8 +1338,11 @@ void InstructionSelection::callBuiltinSetupArgumentObject(IR::Expr *result) 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) @@ -1425,6 +1431,8 @@ CompilationUnit::~CompilationUnit() { } +#if !defined(V4_BOOTSTRAP) + void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine) { #ifdef MOTH_THREADED_INTERPRETER @@ -1461,6 +1469,31 @@ void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine) } } +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)); +#ifdef MOTH_THREADED_INTERPRETER + // for the threaded interpreter we need to make a copy of the data because it needs to be + // modified for the instruction handler addresses. + QByteArray code(codePtr, compiledFunction->codeSize); +#else + QByteArray code = QByteArray::fromRawData(codePtr, compiledFunction->codeSize); +#endif + codeRefs[i] = code; + } + + return true; +} + +#endif // V4_BOOTSTRAP + void CompilationUnit::prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) { const int codeAlignment = 16; @@ -1482,7 +1515,7 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit QByteArray padding; -#ifdef MOTH_THREADED_INTERPRETER +#if defined(MOTH_THREADED_INTERPRETER) && !defined(V4_BOOTSTRAP) // Map from instruction label back to instruction type. Only needed when persisting // already linked compilation units; QHash<void*, int> reverseInstructionMapping; @@ -1511,7 +1544,7 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit QByteArray code = codeRefs.at(i); -#ifdef MOTH_THREADED_INTERPRETER +#if defined(MOTH_THREADED_INTERPRETER) && !defined(V4_BOOTSTRAP) if (!reverseInstructionMapping.isEmpty()) { char *codePtr = code.data(); // detaches int index = 0; @@ -1541,29 +1574,6 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit return true; } -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)); -#ifdef MOTH_THREADED_INTERPRETER - // for the threaded interpreter we need to make a copy of the data because it needs to be - // modified for the instruction handler addresses. - QByteArray code(codePtr, compiledFunction->codeSize); -#else - QByteArray code = QByteArray::fromRawData(codePtr, compiledFunction->codeSize); -#endif - codeRefs[i] = code; - } - - return true; -} - QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory::createUnitForLoading() { QQmlRefPointer<CompiledData::CompilationUnit> result; diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h index afe5fe342e..41469f1985 100644 --- a/src/qml/compiler/qv4isel_moth_p.h +++ b/src/qml/compiler/qv4isel_moth_p.h @@ -59,7 +59,9 @@ #include <private/qv4value_p.h> #include "qv4instr_moth_p.h" +#if !defined(V4_BOOTSTRAP) QT_REQUIRE_CONFIG(qml_interpreter); +#endif QT_BEGIN_NAMESPACE @@ -69,10 +71,12 @@ namespace Moth { struct CompilationUnit : public QV4::CompiledData::CompilationUnit { virtual ~CompilationUnit(); +#if !defined(V4_BOOTSTRAP) void linkBackendToEngine(QV4::ExecutionEngine *engine) Q_DECL_OVERRIDE; + bool memoryMapCode(QString *errorString) Q_DECL_OVERRIDE; +#endif void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) Q_DECL_OVERRIDE; bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) Q_DECL_OVERRIDE; - bool memoryMapCode(QString *errorString) Q_DECL_OVERRIDE; QVector<QByteArray> codeRefs; diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index 73aa6c4975..04bc3d86e5 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -315,20 +315,20 @@ struct Q_AUTOTEST_EXPORT Expr { Expr(ExprKind exprKind): type(UnknownType), exprKind(exprKind) {} bool isLValue() const; - Const *asConst() { return as<Const>(); } - String *asString() { return as<String>(); } - RegExp *asRegExp() { return as<RegExp>(); } - Name *asName() { return as<Name>(); } - Temp *asTemp() { return as<Temp>(); } - ArgLocal *asArgLocal() { return as<ArgLocal>(); } - Closure *asClosure() { return as<Closure>(); } - Convert *asConvert() { return as<Convert>(); } - Unop *asUnop() { return as<Unop>(); } - Binop *asBinop() { return as<Binop>(); } - Call *asCall() { return as<Call>(); } - New *asNew() { return as<New>(); } - Subscript *asSubscript() { return as<Subscript>(); } - Member *asMember() { return as<Member>(); } + 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) \ @@ -773,12 +773,12 @@ struct Stmt { Stmt *asTerminator(); - Exp *asExp() { return as<Exp>(); } - Move *asMove() { return as<Move>(); } - Jump *asJump() { return as<Jump>(); } - CJump *asCJump() { return as<CJump>(); } - Ret *asRet() { return as<Ret>(); } - Phi *asPhi() { return as<Phi>(); } + Exp *asExp(); + Move *asMove(); + Jump *asJump(); + CJump *asCJump(); + Ret *asRet(); + Phi *asPhi(); int id() const { return _id; } @@ -1720,6 +1720,28 @@ inline Stmt *BasicBlock::RET(Expr *expr) 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 diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp index 1d512711b8..10f0bbcf8f 100644 --- a/src/qml/compiler/qv4ssa.cpp +++ b/src/qml/compiler/qv4ssa.cpp @@ -5123,7 +5123,7 @@ 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(Range(from, from)); + _ranges.prepend(LifeTimeIntervalRange(from, from)); if (_end == InvalidPosition) _end = from; } else { @@ -5137,17 +5137,17 @@ void LifeTimeInterval::addRange(int from, int to) { Q_ASSERT(to >= from); if (_ranges.isEmpty()) { - _ranges.prepend(Range(from, to)); + _ranges.prepend(LifeTimeIntervalRange(from, to)); _end = to; return; } - Range *p = &_ranges.first(); + 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) { - Range *p1 = p + 1; + LifeTimeIntervalRange *p1 = p + 1; if (p->end + 1 < p1->start || p1->end + 1 < p->start) break; p1->start = qMin(p->start, p1->start); @@ -5157,10 +5157,10 @@ void LifeTimeInterval::addRange(int from, int to) { } } else { if (to < p->start) { - _ranges.prepend(Range(from, to)); + _ranges.prepend(LifeTimeIntervalRange(from, to)); } else { Q_ASSERT(from > _ranges.last().end); - _ranges.push_back(Range(from, to)); + _ranges.push_back(LifeTimeIntervalRange(from, to)); } } @@ -5206,7 +5206,7 @@ LifeTimeInterval LifeTimeInterval::split(int atPosition, int newStart) } else { // find the first range where the temp will get active again: while (!newInterval._ranges.isEmpty()) { - const Range &range = newInterval._ranges.first(); + 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 diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h index db8b6edd1a..c07abd04c4 100644 --- a/src/qml/compiler/qv4ssa_p.h +++ b/src/qml/compiler/qv4ssa_p.h @@ -63,20 +63,28 @@ class QQmlEnginePrivate; namespace QV4 { namespace IR { -class Q_AUTOTEST_EXPORT LifeTimeInterval { -public: - struct Range { - int start; - int end; +struct LifeTimeIntervalRange { + int start; + int end; - Range(int start = InvalidPosition, int end = InvalidPosition) - : start(start) - , end(end) - {} + LifeTimeIntervalRange(int start = -1, int end = -1) + : start(start) + , end(end) + {} - bool covers(int position) const { return start <= position && position <= end; } - }; - typedef QVarLengthArray<Range, 4> Ranges; + 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; @@ -137,7 +145,7 @@ public: // Validate the new range if (_end != InvalidPosition) { Q_ASSERT(!_ranges.isEmpty()); - for (const Range &range : qAsConst(_ranges)) { + for (const LifeTimeIntervalRange &range : qAsConst(_ranges)) { Q_ASSERT(range.start >= 0); Q_ASSERT(range.end >= 0); Q_ASSERT(range.start <= range.end); @@ -457,7 +465,6 @@ protected: Q_DECLARE_TYPEINFO(QV4::IR::LifeTimeInterval, Q_MOVABLE_TYPE); -Q_DECLARE_TYPEINFO(QV4::IR::LifeTimeInterval::Range, Q_PRIMITIVE_TYPE); QT_END_NAMESPACE diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h index 3a507bef74..41fb2c5b7b 100644 --- a/src/qml/debugger/qqmlprofiler_p.h +++ b/src/qml/debugger/qqmlprofiler_p.h @@ -73,7 +73,7 @@ struct QQmlProfiler {}; struct QQmlBindingProfiler { - QQmlBindingProfiler(quintptr, QQmlBinding *, QV4::FunctionObject *) {} + QQmlBindingProfiler(quintptr, QV4::Function *) {} }; struct QQmlHandlingSignalProfiler diff --git a/src/qml/doc/src/qmltypereference.qdoc b/src/qml/doc/src/qmltypereference.qdoc index f32574fcc1..b2bb9ebd18 100644 --- a/src/qml/doc/src/qmltypereference.qdoc +++ b/src/qml/doc/src/qmltypereference.qdoc @@ -89,7 +89,7 @@ provided: \ingroup qtquickbasictypes \brief a date value. -The \c date type refers to a date value. +The \c date type refers to a date value, including the time of the day. To create a \c date value, specify it as a "YYYY-MM-DD" string: @@ -100,7 +100,7 @@ MyDatePicker { minDate: "2000-01-01"; maxDate: "2020-12-31" } To read a date value returned from a C++ extension class, use \l{QtQml::Qt::formatDate()}{Qt.formatDate()} and \l{QtQml::Qt::formatDateTime()}{Qt.formatDateTime()}. -When integrating with C++, note that any QDate value +When integrating with C++, note that any QDate or QDateTime value \l{qtqml-cppintegration-data.html}{passed into QML from C++} is automatically converted into a \c date value, and vice-versa. diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp index 018396318e..646d9a8871 100644 --- a/src/qml/jit/qv4assembler.cpp +++ b/src/qml/jit/qv4assembler.cpp @@ -39,11 +39,6 @@ #include "qv4isel_masm_p.h" #include "qv4runtime_p.h" -#include "qv4object_p.h" -#include "qv4functionobject_p.h" -#include "qv4regexpobject_p.h" -#include "qv4lookup_p.h" -#include "qv4function_p.h" #include "qv4ssa_p.h" #include "qv4regalloc_p.h" #include "qv4assembler_p.h" @@ -51,6 +46,10 @@ #include <assembler/LinkBuffer.h> #include <WTFStubs.h> +#if !defined(V4_BOOTSTRAP) +#include "qv4function_p.h" +#endif + #include <iostream> #include <QBuffer> #include <QCoreApplication> @@ -68,6 +67,8 @@ CompilationUnit::~CompilationUnit() { } +#if !defined(V4_BOOTSTRAP) + void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine) { runtimeFunctions.resize(data->functionTableSize); @@ -81,6 +82,26 @@ void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine) } } +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); + 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; + } + + return true; +} + +#endif // !defined(V4_BOOTSTRAP) + void CompilationUnit::prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) { const int codeAlignment = 16; @@ -128,27 +149,11 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit return true; } -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); - 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; - } - - return true; -} - -const Assembler::VoidType Assembler::Void; +template <typename TargetConfiguration> +const typename Assembler<TargetConfiguration>::VoidType Assembler<TargetConfiguration>::Void; -Assembler::Assembler(QV4::Compiler::JSUnitGenerator *jsGenerator, IR::Function* function, QV4::ExecutableAllocator *executableAllocator) +template <typename TargetConfiguration> +Assembler<TargetConfiguration>::Assembler(QV4::Compiler::JSUnitGenerator *jsGenerator, IR::Function* function, QV4::ExecutableAllocator *executableAllocator) : _function(function) , _nextBlock(0) , _executableAllocator(executableAllocator) @@ -159,14 +164,16 @@ Assembler::Assembler(QV4::Compiler::JSUnitGenerator *jsGenerator, IR::Function* _labelPatches.resize(_function->basicBlockCount()); } -void Assembler::registerBlock(IR::BasicBlock* block, IR::BasicBlock *nextBlock) +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::registerBlock(IR::BasicBlock* block, IR::BasicBlock *nextBlock) { _addrs[block->index()] = label(); catchBlock = block->catchBlock; _nextBlock = nextBlock; } -void Assembler::jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target) +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target) { Q_UNUSED(current); @@ -174,12 +181,14 @@ void Assembler::jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target) _patches[target->index()].push_back(jump()); } -void Assembler::addPatch(IR::BasicBlock* targetBlock, Jump targetJump) +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::addPatch(IR::BasicBlock* targetBlock, Jump targetJump) { _patches[targetBlock->index()].push_back(targetJump); } -void Assembler::addPatch(DataLabelPtr patch, Label target) +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::addPatch(DataLabelPtr patch, Label target) { DataLabelPatch p; p.dataLabel = patch; @@ -187,37 +196,21 @@ void Assembler::addPatch(DataLabelPtr patch, Label target) _dataLabelPatches.push_back(p); } -void Assembler::addPatch(DataLabelPtr patch, IR::BasicBlock *target) +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::addPatch(DataLabelPtr patch, IR::BasicBlock *target) { _labelPatches[target->index()].push_back(patch); } -void Assembler::generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBlock, +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) { - generateCJumpOnCompare(NotEqual, reg, TrustedImm32(0), currentBlock, trueBlock, falseBlock); -} - -#ifdef QV4_USE_64_BIT_VALUE_ENCODING -void Assembler::generateCJumpOnCompare(RelationalCondition cond, - RegisterID left, - TrustedImm64 right, - IR::BasicBlock *currentBlock, - IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) -{ - if (trueBlock == _nextBlock) { - Jump target = branch64(invert(cond), left, right); - addPatch(falseBlock, target); - } else { - Jump target = branch64(cond, left, right); - addPatch(trueBlock, target); - jumpToBlock(currentBlock, falseBlock); - } + generateCJumpOnCompare(RelationalCondition::NotEqual, reg, TrustedImm32(0), currentBlock, trueBlock, falseBlock); } -#endif -void Assembler::generateCJumpOnCompare(RelationalCondition cond, +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::generateCJumpOnCompare(RelationalCondition cond, RegisterID left, TrustedImm32 right, IR::BasicBlock *currentBlock, @@ -234,7 +227,8 @@ void Assembler::generateCJumpOnCompare(RelationalCondition cond, } } -void Assembler::generateCJumpOnCompare(RelationalCondition cond, +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::generateCJumpOnCompare(RelationalCondition cond, RegisterID left, RegisterID right, IR::BasicBlock *currentBlock, @@ -251,7 +245,8 @@ void Assembler::generateCJumpOnCompare(RelationalCondition cond, } } -Assembler::Pointer Assembler::loadAddress(RegisterID tmp, IR::Expr *e) +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>::loadAddress(RegisterID tmp, IR::Expr *e) { IR::Temp *t = e->asTemp(); if (t) @@ -260,7 +255,8 @@ Assembler::Pointer Assembler::loadAddress(RegisterID tmp, IR::Expr *e) return loadArgLocalAddress(tmp, e->asArgLocal()); } -Assembler::Pointer Assembler::loadTempAddress(IR::Temp *t) +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>::loadTempAddress(IR::Temp *t) { if (t->kind == IR::Temp::StackSlot) return stackSlotPointer(t); @@ -268,7 +264,8 @@ Assembler::Pointer Assembler::loadTempAddress(IR::Temp *t) Q_UNREACHABLE(); } -Assembler::Pointer Assembler::loadArgLocalAddress(RegisterID baseReg, IR::ArgLocal *al) +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>::loadArgLocalAddress(RegisterID baseReg, IR::ArgLocal *al) { int32_t offset = 0; int scope = al->scope; @@ -298,7 +295,8 @@ Assembler::Pointer Assembler::loadArgLocalAddress(RegisterID baseReg, IR::ArgLoc return Pointer(baseReg, offset); } -Assembler::Pointer Assembler::loadStringAddress(RegisterID reg, const QString &string) +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>::loadStringAddress(RegisterID reg, const QString &string) { loadPtr(Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), Assembler::ScratchRegister); loadPtr(Address(Assembler::ScratchRegister, qOffsetOf(QV4::Heap::ExecutionContext, compilationUnit)), Assembler::ScratchRegister); @@ -307,12 +305,14 @@ Assembler::Pointer Assembler::loadStringAddress(RegisterID reg, const QString &s return Pointer(reg, id * sizeof(QV4::String*)); } -Assembler::Address Assembler::loadConstant(IR::Const *c, RegisterID baseReg) +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Address Assembler<TargetConfiguration>::loadConstant(IR::Const *c, RegisterID baseReg) { return loadConstant(convertToValue(c), baseReg); } -Assembler::Address Assembler::loadConstant(const Primitive &v, RegisterID baseReg) +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Address Assembler<TargetConfiguration>::loadConstant(const Primitive &v, RegisterID baseReg) { loadPtr(Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), baseReg); loadPtr(Address(baseReg, qOffsetOf(QV4::Heap::ExecutionContext, constantTable)), baseReg); @@ -320,33 +320,36 @@ Assembler::Address Assembler::loadConstant(const Primitive &v, RegisterID baseRe return Address(baseReg, index * sizeof(QV4::Value)); } -void Assembler::loadStringRef(RegisterID reg, const QString &string) +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::loadStringRef(RegisterID reg, const QString &string) { const int id = _jsGenerator->registerString(string); move(TrustedImm32(id), reg); } -void Assembler::storeValue(QV4::Primitive value, IR::Expr *destination) +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::storeValue(QV4::Primitive value, IR::Expr *destination) { Address addr = loadAddress(ScratchRegister, destination); storeValue(value, addr); } -void Assembler::enterStandardStackFrame(const RegisterInformation ®ularRegistersToSave, +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::enterStandardStackFrame(const RegisterInformation ®ularRegistersToSave, const RegisterInformation &fpRegistersToSave) { platformEnterStandardStackFrame(this); - move(StackPointerRegister, FramePointerRegister); + move(StackPointerRegister, JITTargetPlatform::FramePointerRegister); const int frameSize = _stackLayout->calculateStackFrameSize(); subPtr(TrustedImm32(frameSize), StackPointerRegister); - Address slotAddr(FramePointerRegister, 0); + 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); - JSC::MacroAssembler::storeDouble(fpRegistersToSave.at(i).reg<FPRegisterID>(), slotAddr); + TargetConfiguration::MacroAssembler::storeDouble(fpRegistersToSave.at(i).reg<FPRegisterID>(), slotAddr); } for (int i = 0, ei = regularRegistersToSave.size(); i < ei; ++i) { Q_ASSERT(regularRegistersToSave.at(i).isRegularRegister()); @@ -355,10 +358,11 @@ void Assembler::enterStandardStackFrame(const RegisterInformation ®ularRegist } } -void Assembler::leaveStandardStackFrame(const RegisterInformation ®ularRegistersToSave, +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::leaveStandardStackFrame(const RegisterInformation ®ularRegistersToSave, const RegisterInformation &fpRegistersToSave) { - Address slotAddr(FramePointerRegister, -regularRegistersToSave.size() * RegisterSize - fpRegistersToSave.size() * sizeof(double)); + Address slotAddr(JITTargetPlatform::FramePointerRegister, -regularRegistersToSave.size() * RegisterSize - fpRegistersToSave.size() * sizeof(double)); // restore the callee saved registers for (int i = regularRegistersToSave.size() - 1; i >= 0; --i) { @@ -368,7 +372,7 @@ void Assembler::leaveStandardStackFrame(const RegisterInformation ®ularRegist } for (int i = fpRegistersToSave.size() - 1; i >= 0; --i) { Q_ASSERT(fpRegistersToSave.at(i).isFloatingPoint()); - JSC::MacroAssembler::loadDouble(slotAddr, fpRegistersToSave.at(i).reg<FPRegisterID>()); + TargetConfiguration::MacroAssembler::loadDouble(slotAddr, fpRegistersToSave.at(i).reg<FPRegisterID>()); slotAddr.offset += sizeof(double); } @@ -393,7 +397,8 @@ void Assembler::leaveStandardStackFrame(const RegisterInformation ®ularRegist // 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. -Assembler::Jump Assembler::genTryDoubleConversion(IR::Expr *src, Assembler::FPRegisterID dest) +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Jump Assembler<TargetConfiguration>::genTryDoubleConversion(IR::Expr *src, FPRegisterID dest) { switch (src->type) { case IR::DoubleType: @@ -436,11 +441,10 @@ Assembler::Jump Assembler::genTryDoubleConversion(IR::Expr *src, Assembler::FPRe isNoInt.link(this); #ifdef QV4_USE_64_BIT_VALUE_ENCODING rshift32(TrustedImm32(Value::IsDoubleTag_Shift), ScratchRegister); - Assembler::Jump isNoDbl = branch32(Equal, ScratchRegister, TrustedImm32(0)); + Assembler::Jump isNoDbl = branch32(RelationalCondition::Equal, JITTargetPlatform::ScratchRegister, TrustedImm32(0)); #else and32(Assembler::TrustedImm32(Value::NotDouble_Mask), Assembler::ScratchRegister); - Assembler::Jump isNoDbl = branch32(Assembler::Equal, Assembler::ScratchRegister, - Assembler::TrustedImm32(Value::NotDouble_Mask)); + Assembler::Jump isNoDbl = branch32(RelationalCondition::Equal, JITTargetPlatform::ScratchRegister, TrustedImm32(Value::NotDouble_Mask)); #endif toDoubleRegister(src, dest); intDone.link(this); @@ -448,10 +452,11 @@ Assembler::Jump Assembler::genTryDoubleConversion(IR::Expr *src, Assembler::FPRe return isNoDbl; } -Assembler::Jump Assembler::branchDouble(bool invertCondition, IR::AluOp op, +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Jump Assembler<TargetConfiguration>::branchDouble(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right) { - Assembler::DoubleCondition cond; + DoubleCondition cond; switch (op) { case IR::OpGt: cond = Assembler::DoubleGreaterThan; break; case IR::OpLt: cond = Assembler::DoubleLessThan; break; @@ -465,12 +470,13 @@ Assembler::Jump Assembler::branchDouble(bool invertCondition, IR::AluOp op, Q_UNREACHABLE(); } if (invertCondition) - cond = JSC::MacroAssembler::invert(cond); + cond = TargetConfiguration::MacroAssembler::invert(cond); - return JSC::MacroAssembler::branchDouble(cond, toDoubleRegister(left, FPGpr0), toDoubleRegister(right, FPGpr1)); + return TargetConfiguration::MacroAssembler::branchDouble(cond, toDoubleRegister(left, FPGpr0), toDoubleRegister(right, JITTargetPlatform::FPGpr1)); } -Assembler::Jump Assembler::branchInt32(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right) +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) { @@ -486,18 +492,51 @@ Assembler::Jump Assembler::branchInt32(bool invertCondition, IR::AluOp op, IR::E Q_UNREACHABLE(); } if (invertCondition) - cond = JSC::MacroAssembler::invert(cond); + cond = TargetConfiguration::MacroAssembler::invert(cond); - return JSC::MacroAssembler::branch32(cond, - toInt32Register(left, Assembler::ScratchRegister), - toInt32Register(right, Assembler::ReturnValueRegister)); + return TargetConfiguration::MacroAssembler::branch32(cond, + toInt32Register(left, Assembler::ScratchRegister), + toInt32Register(right, Assembler::ReturnValueRegister)); } -void Assembler::setStackLayout(int maxArgCountForBuiltins, int regularRegistersToSave, int fpRegistersToSave) +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()) { + QV4::Primitive retVal = convertToValue(c); + RegisterSizeDependentOps::setFunctionReturnValueFromConst(this, retVal); + } else { + Q_UNREACHABLE(); + Q_UNUSED(s); + } + + Label leaveStackFrame = label(); + + const int locals = stackLayout().calculateJSStackFrameSize(); + subPtr(TrustedImm32(sizeof(QV4::Value)*locals), JITTargetPlatform::LocalsRegister); + loadPtr(Address(JITTargetPlatform::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), JITTargetPlatform::ScratchRegister); + loadPtr(Address(JITTargetPlatform::ScratchRegister, qOffsetOf(ExecutionContext::Data, engine)), JITTargetPlatform::ScratchRegister); + storePtr(JITTargetPlatform::LocalsRegister, Address(JITTargetPlatform::ScratchRegister, qOffsetOf(ExecutionEngine, jsStackTop))); + + leaveStandardStackFrame(regularRegistersToSave, fpRegistersToSave); + ret(); + + exceptionReturnLabel = label(); + QV4::Primitive retVal = Primitive::undefinedValue(); + RegisterSizeDependentOps::setFunctionReturnValueFromConst(this, retVal); + jump(leaveStackFrame); +} namespace { class QIODevicePrintStream: public FilePrintStream @@ -563,7 +602,8 @@ static void qt_closePmap() #endif -JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) +template <typename TargetConfiguration> +JSC::MacroAssemblerCodeRef Assembler<TargetConfiguration>::link(int *codeSize) { Label endOfCode = label(); @@ -577,7 +617,7 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) } JSC::JSGlobalData dummy(_executableAllocator); - JSC::LinkBuffer linkBuffer(dummy, this, 0); + JSC::LinkBuffer<typename TargetConfiguration::MacroAssembler> linkBuffer(dummy, this, 0); for (const DataLabelPatch &p : qAsConst(_dataLabelPatches)) linkBuffer.patch(p.dataLabel, linkBuffer.locationOf(p.target)); @@ -668,4 +708,9 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) return codeRef; } +template class QV4::JIT::Assembler<DefaultAssemblerTargetConfiguration>; +#if defined(V4_BOOTSTRAP) && CPU(X86_64) +template class QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>; +#endif + #endif diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index de9c246ed6..6d8d773ff0 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -55,7 +55,8 @@ #include "private/qv4isel_p.h" #include "private/qv4isel_util_p.h" #include "private/qv4value_p.h" -#include "private/qv4lookup_p.h" +#include "private/qv4context_p.h" +#include "private/qv4engine_p.h" #include "qv4targetplatform_p.h" #include <config.h> @@ -73,46 +74,637 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace JIT { -class InstructionSelection; - struct CompilationUnit : public QV4::CompiledData::CompilationUnit { virtual ~CompilationUnit(); +#if !defined(V4_BOOTSTRAP) void linkBackendToEngine(QV4::ExecutionEngine *engine) Q_DECL_OVERRIDE; + bool memoryMapCode(QString *errorString) Q_DECL_OVERRIDE; +#endif void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) Q_DECL_OVERRIDE; bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) Q_DECL_OVERRIDE; - bool memoryMapCode(QString *errorString) Q_DECL_OVERRIDE; // Coderef + execution engine QVector<JSC::MacroAssemblerCodeRef> codeRefs; }; -struct LookupCall { - JSC::MacroAssembler::Address addr; - uint getterSetterOffset; +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 - LookupCall(const JSC::MacroAssembler::Address &addr, uint getterSetterOffset) - : addr(addr) - , getterSetterOffset(getterSetterOffset) - {} +#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(qOffsetOf(QV4::Runtime, function)), __VA_ARGS__) + + +template <typename JITAssembler, typename MacroAssembler, typename TargetPlatform, int RegisterSize> +struct RegisterSizeDependentAssembler +{ }; -struct RuntimeCall { - JSC::MacroAssembler::Address addr; +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 Address = typename JITAssembler::Address; + using Pointer = typename JITAssembler::Pointer; + using TrustedImm32 = typename JITAssembler::TrustedImm32; + using TrustedImm64 = typename JITAssembler::TrustedImm64; + using Jump = typename JITAssembler::Jump; + + static void loadDouble(JITAssembler *as, Address addr, FPRegisterID dest) + { + as->MacroAssembler::loadDouble(addr, dest); + } + + static void storeDouble(JITAssembler *as, FPRegisterID source, Address addr) + { + as->MacroAssembler::storeDouble(source, addr); + } + + static void storeDouble(JITAssembler *as, FPRegisterID source, IR::Expr* target) + { + Pointer ptr = as->loadAddress(TargetPlatform::ScratchRegister, target); + as->storeDouble(source, ptr); + } + + static void storeValue(JITAssembler *as, QV4::Primitive value, Address destination) + { + as->store32(TrustedImm32(value.int_32()), destination); + destination.offset += 4; + as->store32(TrustedImm32(value.tag()), destination); + } + + template <typename Source, typename Destination> + static void copyValueViaRegisters(JITAssembler *as, Source source, Destination destination) + { + as->loadDouble(source, TargetPlatform::FPGpr0); + as->storeDouble(TargetPlatform::FPGpr0, 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) + { + Address destination = dest; + as->store32(TargetPlatform::LowReturnValueRegister, destination); + destination.offset += 4; + as->store32(TargetPlatform::HighReturnValueRegister, destination); + } + + 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(QV4::Value::Integer_Type_Internal), highReg); + done.link(as); + } break; + case IR::SInt32Type: + as->move((RegisterID) t->index, lowReg); + as->move(TrustedImm32(QV4::Value::Integer_Type_Internal), highReg); + break; + case IR::BoolType: + as->move((RegisterID) t->index, lowReg); + as->move(TrustedImm32(QV4::Value::Boolean_Type_Internal), highReg); + break; + default: + Q_UNREACHABLE(); + } + } else { + Pointer addr = as->loadAddress(TargetPlatform::ScratchRegister, t); + as->load32(addr, lowReg); + addr.offset += 4; + as->load32(addr, highReg); + } + } + + static void setFunctionReturnValueFromConst(JITAssembler *as, QV4::Primitive retVal) + { + as->move(TrustedImm32(retVal.int_32()), 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->loadAddress(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->loadAddress(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(Value::Integer_Type_Internal)); + IR::Temp *targetTemp = target->asTemp(); + if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { + as->load32(addr, TargetPlatform::ReturnValueRegister); + Pointer targetAddr = as->loadAddress(TargetPlatform::ScratchRegister, target); + as->store32(TargetPlatform::ReturnValueRegister, targetAddr); + targetAddr.offset += 4; + as->store32(TrustedImm32(Value::Integer_Type_Internal), targetAddr); + } else { + as->load32(addr, (RegisterID) targetTemp->index); + } + Jump intDone = as->jump(); + + // not an int: + fallback.link(as); + generateRuntimeCall(as, TargetPlatform::ReturnValueRegister, toInt, + as->loadAddress(TargetPlatform::ScratchRegister, source)); + as->storeInt32(TargetPlatform::ReturnValueRegister, target); + + intDone.link(as); + } + + static void loadManagedPointer(JITAssembler *as, RegisterID registerWithPtr, Pointer destAddr) + { + as->store32(registerWithPtr, destAddr); + destAddr.offset += 4; + as->store32(TrustedImm32(QV4::Value::Managed_Type_Internal_32), 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)); + } +}; + +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 BranchTruncateType = typename JITAssembler::BranchTruncateType; + using Jump = typename JITAssembler::Jump; + + static void loadDouble(JITAssembler *as, Address addr, FPRegisterID dest) + { + as->load64(addr, TargetPlatform::ReturnValueRegister); + as->move(TrustedImm64(QV4::Value::NaNEncodeMask), TargetPlatform::ScratchRegister); + as->xor64(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); + as->move64ToDouble(TargetPlatform::ReturnValueRegister, dest); + } + + static void storeDouble(JITAssembler *as, FPRegisterID source, Address addr) + { + as->moveDoubleTo64(source, TargetPlatform::ReturnValueRegister); + as->move(TrustedImm64(QV4::Value::NaNEncodeMask), TargetPlatform::ScratchRegister); + as->xor64(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); + as->store64(TargetPlatform::ReturnValueRegister, addr); + } + + static void storeDouble(JITAssembler *as, FPRegisterID source, IR::Expr* target) + { + as->moveDoubleTo64(source, TargetPlatform::ReturnValueRegister); + as->move(TrustedImm64(QV4::Value::NaNEncodeMask), TargetPlatform::ScratchRegister); + as->xor64(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); + Pointer ptr = as->loadAddress(TargetPlatform::ScratchRegister, target); + as->store64(TargetPlatform::ReturnValueRegister, ptr); + } + + static void storeReturnValue(JITAssembler *as, FPRegisterID dest) + { + as->move(TrustedImm64(QV4::Value::NaNEncodeMask), TargetPlatform::ScratchRegister); + as->xor64(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); + as->move64ToDouble(TargetPlatform::ReturnValueRegister, dest); + } + + static void storeReturnValue(JITAssembler *as, const Pointer &dest) + { + as->store64(TargetPlatform::ReturnValueRegister, 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->move(TrustedImm64(QV4::Value::NaNEncodeMask), + TargetPlatform::ScratchRegister); + as->xor64(TargetPlatform::ScratchRegister, 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->move(TrustedImm64(QV4::Value::NaNEncodeMask), TargetPlatform::ScratchRegister); + as->xor64(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); + Jump done = as->jump(); + intRange.link(as); + as->zeroExtend32ToPtr(srcReg, TargetPlatform::ReturnValueRegister); + quint64 tag = QV4::Value::Integer_Type_Internal; + 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 = QV4::Value::Integer_Type_Internal; + break; + case IR::BoolType: + tag = QV4::Value::Boolean_Type_Internal; + break; + default: + tag = 31337; // bogus value + Q_UNREACHABLE(); + } + as->or64(TrustedImm64(tag << 32), + TargetPlatform::ReturnValueRegister); + } + } else { + as->copyValue(TargetPlatform::ReturnValueRegister, t); + } + } + + static void setFunctionReturnValueFromConst(JITAssembler *as, QV4::Primitive retVal) + { + as->move(TrustedImm64(retVal.rawValue()), TargetPlatform::ReturnValueRegister); + } + + static void storeValue(JITAssembler *as, QV4::Primitive value, Address destination) + { + as->store64(TrustedImm64(value.rawValue()), destination); + } + + template <typename Source, typename Destination> + static void copyValueViaRegisters(JITAssembler *as, Source source, Destination destination) + { + // Use ReturnValueRegister as "scratch" register because loadArgument + // and storeArgument are functions that may need a scratch register themselves. + as->loadArgumentInRegister(source, TargetPlatform::ReturnValueRegister, 0); + as->storeReturnValue(destination); + } + + 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, IR::Temp* temp, RegisterID dest, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + if (temp) { + Pointer addr = as->loadTempAddress(temp); + as->load64(addr, dest); + } else { + QV4::Value undefined = QV4::Primitive::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->loadArgLocalAddress(dest, al); + as->load64(addr, dest); + } else { + QV4::Value undefined = QV4::Primitive::undefinedValue(); + as->move(TrustedImm64(undefined.rawValue()), dest); + } + } + + static void loadArgumentInRegister(JITAssembler *as, IR::Const* c, RegisterID dest, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + QV4::Value v = convertToValue(c); + as->move(TrustedImm64(v.rawValue()), dest); + } + + static void loadArgumentInRegister(JITAssembler *as, IR::Expr* expr, RegisterID dest, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + if (!expr) { + QV4::Value undefined = QV4::Primitive::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); + } - inline RuntimeCall(uint offset = uint(INT_MIN)); - bool isValid() const { return addr.offset >= 0; } + 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->loadAddress(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->loadAddress(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->move(TrustedImm64(QV4::Value::NaNEncodeMask), TargetPlatform::ScratchRegister); + as->xor64(TargetPlatform::ScratchRegister, 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->loadAddress(TargetPlatform::ScratchRegister, source)); + + + isIntConvertible.link(as); + success.link(as); + IR::Temp *targetTemp = target->asTemp(); + if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { + Pointer targetAddr = as->loadAddress(TargetPlatform::ScratchRegister, target); + as->store32(TargetPlatform::ReturnValueRegister, targetAddr); + targetAddr.offset += 4; + as->store32(TrustedImm32(Value::Integer_Type_Internal), targetAddr); + } else { + as->storeInt32(TargetPlatform::ReturnValueRegister, target); + } + } + + static void loadManagedPointer(JITAssembler *as, RegisterID registerWithPtr, Pointer destAddr) + { + as->store64(registerWithPtr, destAddr); + } + + static Jump generateIsDoubleCheck(JITAssembler *as, RegisterID tagOrValueRegister) + { + as->rshift32(TrustedImm32(Value::IsDoubleTag_Shift), tagOrValueRegister); + return as->branch32(RelationalCondition::NotEqual, tagOrValueRegister, + TrustedImm32(0)); + } }; -class Assembler : public JSC::MacroAssembler, public TargetPlatform +template <typename TargetConfiguration> +class Assembler : public TargetConfiguration::MacroAssembler, public TargetConfiguration::Platform { Q_DISABLE_COPY(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::platformLeaveStandardStackFrame; + + using RegisterSizeDependentOps = RegisterSizeDependentAssembler<Assembler<TargetConfiguration>, MacroAssembler, JITTargetPlatform, RegisterSize>; + + struct LookupCall { + Address addr; + uint getterSetterOffset; + + LookupCall(const Address &addr, uint getterSetterOffset) + : addr(addr) + , getterSetterOffset(getterSetterOffset) + {} + }; + + struct RuntimeCall { + Address addr; + + inline RuntimeCall(uint offset = uint(INT_MIN)); + 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. @@ -319,20 +911,29 @@ public: void addPatch(DataLabelPtr patch, IR::BasicBlock *target); void generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - void generateCJumpOnCompare(RelationalCondition cond, RegisterID left, TrustedImm64 right, - IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock); -#endif 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); - Jump genTryDoubleConversion(IR::Expr *src, Assembler::FPRegisterID dest); - Assembler::Jump branchDouble(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right); - Assembler::Jump branchInt32(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right); + 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 loadAddress(RegisterID tmp, IR::Expr *t); Pointer loadTempAddress(IR::Temp *t); @@ -396,7 +997,7 @@ public: void loadArgumentInRegister(PointerToValue temp, RegisterID dest, int argumentNumber) { if (!temp.value) { - move(TrustedImmPtr(0), dest); + RegisterSizeDependentOps::zeroRegister(this, dest); } else { Pointer addr = toAddress(dest, temp.value, argumentNumber); loadArgumentInRegister(addr, dest, argumentNumber); @@ -415,70 +1016,31 @@ public: loadArgumentInRegister(addr, dest, argumentNumber); } -#ifdef VALUE_FITS_IN_REGISTER void loadArgumentInRegister(IR::Temp* temp, RegisterID dest, int argumentNumber) { - Q_UNUSED(argumentNumber); - - if (temp) { - Pointer addr = loadTempAddress(temp); - load64(addr, dest); - } else { - QV4::Value undefined = QV4::Primitive::undefinedValue(); - move(TrustedImm64(undefined.rawValue()), dest); - } + RegisterSizeDependentOps::loadArgumentInRegister(this, temp, dest, argumentNumber); } void loadArgumentInRegister(IR::ArgLocal* al, RegisterID dest, int argumentNumber) { - Q_UNUSED(argumentNumber); - - if (al) { - Pointer addr = loadArgLocalAddress(dest, al); - load64(addr, dest); - } else { - QV4::Value undefined = QV4::Primitive::undefinedValue(); - move(TrustedImm64(undefined.rawValue()), dest); - } + RegisterSizeDependentOps::loadArgumentInRegister(this, al, dest, argumentNumber); } void loadArgumentInRegister(IR::Const* c, RegisterID dest, int argumentNumber) { - Q_UNUSED(argumentNumber); - - QV4::Value v = convertToValue(c); - move(TrustedImm64(v.rawValue()), dest); + RegisterSizeDependentOps::loadArgumentInRegister(this, c, dest, argumentNumber); } void loadArgumentInRegister(IR::Expr* expr, RegisterID dest, int argumentNumber) { - Q_UNUSED(argumentNumber); - - if (!expr) { - QV4::Value undefined = QV4::Primitive::undefinedValue(); - move(TrustedImm64(undefined.rawValue()), dest); - } else if (IR::Temp *t = expr->asTemp()){ - loadArgumentInRegister(t, dest, argumentNumber); - } else if (IR::ArgLocal *al = expr->asArgLocal()) { - loadArgumentInRegister(al, dest, argumentNumber); - } else if (IR::Const *c = expr->asConst()) { - loadArgumentInRegister(c, dest, argumentNumber); - } else { - Q_ASSERT(!"unimplemented expression type in loadArgument"); - } - } -#else - void loadArgumentInRegister(IR::Expr*, RegisterID) - { - Q_ASSERT(!"unimplemented: expression in loadArgument"); + RegisterSizeDependentOps::loadArgumentInRegister(this, expr, dest, argumentNumber); } -#endif void loadArgumentInRegister(TrustedImm32 imm32, RegisterID dest, int argumentNumber) { Q_UNUSED(argumentNumber); - xorPtr(dest, dest); + RegisterSizeDependentOps::zeroRegister(this, dest); if (imm32.m_value) move(imm32, dest); } @@ -499,55 +1061,13 @@ public: void storeReturnValue(FPRegisterID dest) { -#ifdef VALUE_FITS_IN_REGISTER - move(TrustedImm64(QV4::Value::NaNEncodeMask), ScratchRegister); - xor64(ScratchRegister, ReturnValueRegister); - move64ToDouble(ReturnValueRegister, dest); -#elif defined(Q_PROCESSOR_ARM) - moveIntsToDouble(JSC::ARMRegisters::r0, JSC::ARMRegisters::r1, dest, FPGpr0); -#elif defined(Q_PROCESSOR_X86) - moveIntsToDouble(JSC::X86Registers::eax, JSC::X86Registers::edx, dest, FPGpr0); -#elif defined(Q_PROCESSOR_MIPS) - moveIntsToDouble(JSC::MIPSRegisters::v0, JSC::MIPSRegisters::v1, dest, FPGpr0); -#else - subPtr(TrustedImm32(sizeof(QV4::Value)), StackPointerRegister); - Pointer tmp(StackPointerRegister, 0); - storeReturnValue(tmp); - loadDouble(tmp, dest); - addPtr(TrustedImm32(sizeof(QV4::Value)), StackPointerRegister); -#endif + RegisterSizeDependentOps::storeReturnValue(this, dest); } -#ifdef VALUE_FITS_IN_REGISTER - void storeReturnValue(const Pointer &dest) - { - store64(ReturnValueRegister, dest); - } -#elif defined(Q_PROCESSOR_X86) - void storeReturnValue(const Pointer &dest) - { - Pointer destination = dest; - store32(JSC::X86Registers::eax, destination); - destination.offset += 4; - store32(JSC::X86Registers::edx, destination); - } -#elif defined(Q_PROCESSOR_ARM) - void storeReturnValue(const Pointer &dest) - { - Pointer destination = dest; - store32(JSC::ARMRegisters::r0, destination); - destination.offset += 4; - store32(JSC::ARMRegisters::r1, destination); - } -#elif defined(Q_PROCESSOR_MIPS) void storeReturnValue(const Pointer &dest) { - Pointer destination = dest; - store32(JSC::MIPSRegisters::v0, destination); - destination.offset += 4; - store32(JSC::MIPSRegisters::v1, destination); + RegisterSizeDependentOps::storeReturnValue(this, dest); } -#endif void storeReturnValue(IR::Expr *target) { @@ -609,7 +1129,7 @@ public: Pointer ptr = toAddress(ScratchRegister, temp.value, argumentNumber); loadArgumentOnStack<StackSlot>(ptr, argumentNumber); } else { - poke(TrustedImmPtr(0), StackSlot); + RegisterSizeDependentOps::zeroStackSlot(this, StackSlot); } } @@ -648,38 +1168,18 @@ public: moveDouble(source, (FPRegisterID) targetTemp->index); return; } -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - moveDoubleTo64(source, ReturnValueRegister); - move(TrustedImm64(QV4::Value::NaNEncodeMask), ScratchRegister); - xor64(ScratchRegister, ReturnValueRegister); - Pointer ptr = loadAddress(ScratchRegister, target); - store64(ReturnValueRegister, ptr); -#else - Pointer ptr = loadAddress(ScratchRegister, target); - storeDouble(source, ptr); -#endif + RegisterSizeDependentOps::storeDouble(this, source, target); } -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - // We need to (de)mangle the double + void loadDouble(Address addr, FPRegisterID dest) { - load64(addr, ReturnValueRegister); - move(TrustedImm64(QV4::Value::NaNEncodeMask), ScratchRegister); - xor64(ScratchRegister, ReturnValueRegister); - move64ToDouble(ReturnValueRegister, dest); + RegisterSizeDependentOps::loadDouble(this, addr, dest); } void storeDouble(FPRegisterID source, Address addr) { - moveDoubleTo64(source, ReturnValueRegister); - move(TrustedImm64(QV4::Value::NaNEncodeMask), ScratchRegister); - xor64(ScratchRegister, ReturnValueRegister); - store64(ReturnValueRegister, addr); + RegisterSizeDependentOps::storeDouble(this, source, addr); } -#else - using JSC::MacroAssembler::loadDouble; - using JSC::MacroAssembler::storeDouble; -#endif template <typename Result, typename Source> void copyValue(Result result, Source source); @@ -691,8 +1191,15 @@ public: { Q_ASSERT(!source->asTemp() || source->asTemp()->kind != IR::Temp::PhysicalRegister); Q_ASSERT(target.base != scratchRegister); - JSC::MacroAssembler::loadDouble(loadAddress(scratchRegister, source), FPGpr0); - JSC::MacroAssembler::storeDouble(FPGpr0, target); + TargetConfiguration::MacroAssembler::loadDouble(loadAddress(scratchRegister, source), FPGpr0); + TargetConfiguration::MacroAssembler::storeDouble(FPGpr0, target); + } + + // The scratch register is used to calculate the temp address for the source. + void memcopyValue(IR::Expr *target, Pointer source, FPRegisterID fpScratchRegister, RegisterID scratchRegister) + { + TargetConfiguration::MacroAssembler::loadDouble(source, fpScratchRegister); + TargetConfiguration::MacroAssembler::storeDouble(fpScratchRegister, loadAddress(scratchRegister, target)); } void storeValue(QV4::Primitive value, RegisterID destination) @@ -704,13 +1211,7 @@ public: void storeValue(QV4::Primitive value, Address destination) { -#ifdef VALUE_FITS_IN_REGISTER - store64(TrustedImm64(value.rawValue()), destination); -#else - store32(TrustedImm32(value.int_32()), destination); - destination.offset += 4; - store32(TrustedImm32(value.tag()), destination); -#endif + RegisterSizeDependentOps::storeValue(this, value, destination); } void storeValue(QV4::Primitive value, IR::Expr* temp); @@ -722,7 +1223,7 @@ public: void checkException() { load32(Address(EngineRegister, qOffsetOf(QV4::ExecutionEngine, hasException)), ScratchRegister); - Jump exceptionThrown = branch32(NotEqual, ScratchRegister, TrustedImm32(0)); + Jump exceptionThrown = branch32(RelationalCondition::NotEqual, ScratchRegister, TrustedImm32(0)); if (catchBlock) addPatch(catchBlock, exceptionThrown); else @@ -781,6 +1282,27 @@ public: 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, qOffsetOf(QV4::ExecutionEngine, current)), ScratchRegister); + loadPtr(Address(ScratchRegister, qOffsetOf(QV4::Heap::ExecutionContext, 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) { @@ -813,11 +1335,11 @@ public: loadArgumentOnStackOrRegister<2>(arg3); loadArgumentOnStackOrRegister<1>(arg2); - if (prepareCall(function, this)) + if (prepareCall(function)) loadArgumentOnStackOrRegister<0>(arg1); #ifdef RESTORE_EBX_ON_CALL - load32(ebxAddressOnStack(), JSC::X86Registers::ebx); // restore the GOT ptr + load32(this->ebxAddressOnStack(), JSC::X86Registers::ebx); // restore the GOT ptr #endif callAbsolute(functionName, function); @@ -957,7 +1479,7 @@ public: void storeUInt32(RegisterID reg, Pointer addr) { // The UInt32 representation in QV4::Value is really convoluted. See also toUInt32Register. - Jump intRange = branch32(GreaterThanOrEqual, reg, TrustedImm32(0)); + Jump intRange = branch32(RelationalCondition::GreaterThanOrEqual, reg, TrustedImm32(0)); convertUInt32ToDouble(reg, FPGpr0, ReturnValueRegister); storeDouble(FPGpr0, addr); Jump done = jump(); @@ -980,15 +1502,7 @@ public: FPRegisterID toDoubleRegister(IR::Expr *e, FPRegisterID target = FPGpr0) { if (IR::Const *c = e->asConst()) { -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - Q_STATIC_ASSERT(sizeof(int64_t) == sizeof(double)); - int64_t i; - memcpy(&i, &c->value, sizeof(double)); - move(TrustedImm64(i), ReturnValueRegister); - move64ToDouble(ReturnValueRegister, target); -#else - JSC::MacroAssembler::loadDouble(loadConstant(c, ScratchRegister), target); -#endif + RegisterSizeDependentOps::loadDoubleConstant(this, c, target); return target; } @@ -1047,7 +1561,7 @@ public: Pointer tagAddr = addr; tagAddr.offset += 4; load32(tagAddr, scratchReg); - Jump inIntRange = branch32(Equal, scratchReg, TrustedImm32(QV4::Value::Integer_Type_Internal)); + Jump inIntRange = branch32(RelationalCondition::Equal, scratchReg, TrustedImm32(QV4::Value::Integer_Type_Internal)); // it's not in signed int range, so load it as a double, and truncate it down loadDouble(addr, FPGpr0); @@ -1065,6 +1579,8 @@ public: return scratchReg; } + void returnFromFunction(IR::Ret *s, RegisterInformation regularRegistersToSave, RegisterInformation fpRegistersToSave); + JSC::MacroAssemblerCodeRef link(int *codeSize); void setStackLayout(int maxArgCountForBuiltins, int regularRegistersToSave, int fpRegistersToSave); @@ -1095,22 +1611,16 @@ private: QV4::Compiler::JSUnitGenerator *_jsGenerator; }; +template <typename TargetConfiguration> template <typename Result, typename Source> -void Assembler::copyValue(Result result, Source source) +void Assembler<TargetConfiguration>::copyValue(Result result, Source source) { -#ifdef VALUE_FITS_IN_REGISTER - // Use ReturnValueRegister as "scratch" register because loadArgument - // and storeArgument are functions that may need a scratch register themselves. - loadArgumentInRegister(source, ReturnValueRegister, 0); - storeReturnValue(result); -#else - loadDouble(source, FPGpr0); - storeDouble(FPGpr0, result); -#endif + RegisterSizeDependentOps::copyValueViaRegisters(this, source, result); } +template <typename TargetConfiguration> template <typename Result> -void Assembler::copyValue(Result result, IR::Expr* source) +void Assembler<TargetConfiguration>::copyValue(Result result, IR::Expr* source) { if (source->type == IR::BoolType) { RegisterID reg = toInt32Register(source, ScratchRegister); @@ -1124,15 +1634,7 @@ void Assembler::copyValue(Result result, IR::Expr* source) } else if (source->type == IR::DoubleType) { storeDouble(toDoubleRegister(source), result); } else if (source->asTemp() || source->asArgLocal()) { -#ifdef VALUE_FITS_IN_REGISTER - // Use ReturnValueRegister as "scratch" register because loadArgument - // and storeArgument are functions that may need a scratch register themselves. - loadArgumentInRegister(source, ReturnValueRegister, 0); - storeReturnValue(result); -#else - loadDouble(source, FPGpr0); - storeDouble(FPGpr0, result); -#endif + RegisterSizeDependentOps::copyValueViaRegisters(this, source, result); } else if (IR::Const *c = source->asConst()) { QV4::Primitive v = convertToValue(c); storeValue(v, result); @@ -1141,34 +1643,12 @@ void Assembler::copyValue(Result result, IR::Expr* source) } } -inline RuntimeCall::RuntimeCall(uint offset) +template <typename TargetConfiguration> +inline Assembler<TargetConfiguration>::RuntimeCall::RuntimeCall(uint offset) : addr(Assembler::EngineRegister, offset + qOffsetOf(QV4::ExecutionEngine, runtime)) { } - - -template <typename T> inline bool prepareCall(T &, Assembler *) -{ return true; } - -template <> inline bool prepareCall(LookupCall &lookupCall, Assembler *as) -{ - // IMPORTANT! See generateLookupCall in qv4isel_masm_p.h for details! - - // load the table from the context - as->loadPtr(Assembler::Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), Assembler::ScratchRegister); - as->loadPtr(Assembler::Address(Assembler::ScratchRegister, qOffsetOf(QV4::Heap::ExecutionContext, lookups)), - lookupCall.addr.base); - // pre-calculate the indirect address for the lookupCall table: - if (lookupCall.addr.offset) - as->addPtr(Assembler::TrustedImm32(lookupCall.addr.offset), lookupCall.addr.base); - // store it as the first argument - as->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; -} - } // end of namespace JIT } // end of namespace QV4 diff --git a/src/qml/jit/qv4binop.cpp b/src/qml/jit/qv4binop.cpp index d2758c4a47..8468bf65a6 100644 --- a/src/qml/jit/qv4binop.cpp +++ b/src/qml/jit/qv4binop.cpp @@ -57,7 +57,8 @@ using namespace JIT; #define NULL_OP \ { 0, 0, 0, 0, 0, false } -const Binop::OpInfo Binop::operations[IR::LastAluOp + 1] = { +template <typename JITAssembler> +const typename Binop<JITAssembler>::OpInfo Binop<JITAssembler>::operations[IR::LastAluOp + 1] = { NULL_OP, // OpInvalid NULL_OP, // OpIfTrue NULL_OP, // OpNot @@ -67,20 +68,20 @@ const Binop::OpInfo Binop::operations[IR::LastAluOp + 1] = { NULL_OP, // OpIncrement NULL_OP, // OpDecrement - INLINE_OP(bitAnd, &Binop::inline_and32, &Binop::inline_and32), // OpBitAnd - INLINE_OP(bitOr, &Binop::inline_or32, &Binop::inline_or32), // OpBitOr - INLINE_OP(bitXor, &Binop::inline_xor32, &Binop::inline_xor32), // OpBitXor + 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::inline_add32, &Binop::inline_add32), // OpAdd - INLINE_OP(sub, &Binop::inline_sub32, &Binop::inline_sub32), // OpSub - INLINE_OP(mul, &Binop::inline_mul32, &Binop::inline_mul32), // OpMul + 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::inline_shl32, &Binop::inline_shl32), // OpLShift - INLINE_OP(shr, &Binop::inline_shr32, &Binop::inline_shr32), // OpRShift - INLINE_OP(ushr, &Binop::inline_ushr32, &Binop::inline_ushr32), // OpURShift + 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 @@ -100,7 +101,8 @@ const Binop::OpInfo Binop::operations[IR::LastAluOp + 1] = { -void Binop::generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) +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) { @@ -125,15 +127,15 @@ void Binop::generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) info = stringAdd; } - RuntimeCall fallBack(info.fallbackImplementation); - RuntimeCall context(info.contextImplementation); + 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, - Assembler::EngineRegister, + JITAssembler::EngineRegister, PointerToValue(lhs), PointerToValue(rhs)); } else { @@ -145,14 +147,15 @@ void Binop::generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) } -void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) +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 = Assembler::FPGpr0; + targetReg = JITAssembler::FPGpr0; switch (op) { case IR::OpAdd: @@ -162,7 +165,7 @@ void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) #if CPU(X86) 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, Assembler::ScratchRegister); + Address addr = as->loadConstant(c, JITAssembler::ScratchRegister); as->addDouble(addr, targetReg); break; } @@ -174,7 +177,7 @@ void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) } } #endif - as->addDouble(as->toDoubleRegister(lhs, Assembler::FPGpr0), as->toDoubleRegister(rhs, Assembler::FPGpr1), targetReg); + as->addDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg); break; case IR::OpMul: @@ -184,7 +187,7 @@ void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) #if CPU(X86) 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, Assembler::ScratchRegister); + Address addr = as->loadConstant(c, JITAssembler::ScratchRegister); as->mulDouble(addr, targetReg); break; } @@ -196,14 +199,14 @@ void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) } } #endif - as->mulDouble(as->toDoubleRegister(lhs, Assembler::FPGpr0), as->toDoubleRegister(rhs, Assembler::FPGpr1), targetReg); + as->mulDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg); break; case IR::OpSub: #if CPU(X86) 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, Assembler::ScratchRegister); + Address addr = as->loadConstant(c, JITAssembler::ScratchRegister); as->subDouble(addr, targetReg); break; } @@ -219,19 +222,19 @@ void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) && targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister && targetTemp->index == rhs->asTemp()->index) { // Y = X - Y -> Tmp = Y; Y = X - Tmp - as->moveDouble(as->toDoubleRegister(rhs, Assembler::FPGpr1), Assembler::FPGpr1); - as->subDouble(as->toDoubleRegister(lhs, Assembler::FPGpr0), Assembler::FPGpr1, targetReg); + 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, Assembler::FPGpr0), as->toDoubleRegister(rhs, Assembler::FPGpr1), targetReg); + as->subDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg); break; case IR::OpDiv: #if CPU(X86) 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, Assembler::ScratchRegister); + Address addr = as->loadConstant(c, JITAssembler::ScratchRegister); as->divDouble(addr, targetReg); break; } @@ -248,12 +251,12 @@ void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) && targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister && targetTemp->index == rhs->asTemp()->index) { // Y = X / Y -> Tmp = Y; Y = X / Tmp - as->moveDouble(as->toDoubleRegister(rhs, Assembler::FPGpr1), Assembler::FPGpr1); - as->divDouble(as->toDoubleRegister(lhs, Assembler::FPGpr0), Assembler::FPGpr1, targetReg); + 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, Assembler::FPGpr0), as->toDoubleRegister(rhs, Assembler::FPGpr1), targetReg); + as->divDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg); break; default: { @@ -271,8 +274,8 @@ void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) as->storeDouble(targetReg, target); } - -bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *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); @@ -305,7 +308,7 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta bool inplaceOpWithAddress = false; IR::Temp *targetTemp = target->asTemp(); - RegisterID targetReg = Assembler::ReturnValueRegister; + 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) { @@ -369,12 +372,12 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta && targetTemp->index == rightSource->asTemp()->index) { // X = Y - X -> Tmp = X; X = Y; X -= Tmp targetReg = (RegisterID) targetTemp->index; - as->move(targetReg, Assembler::ScratchRegister); + as->move(targetReg, JITAssembler::ScratchRegister); as->move(as->toInt32Register(leftSource, targetReg), targetReg); - as->sub32(Assembler::ScratchRegister, targetReg); + as->sub32(JITAssembler::ScratchRegister, targetReg); } else { as->move(as->toInt32Register(leftSource, targetReg), targetReg); - as->sub32(as->toInt32Register(rightSource, Assembler::ScratchRegister), targetReg); + as->sub32(as->toInt32Register(rightSource, JITAssembler::ScratchRegister), targetReg); } as->storeInt32(targetReg, target); return true; @@ -419,7 +422,7 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta return false; } } else if (inplaceOpWithAddress) { // All cases of X = X op [address-of-Y] - Pointer rhsAddr = as->loadAddress(Assembler::ScratchRegister, rightSource); + Pointer rhsAddr = as->loadAddress(JITAssembler::ScratchRegister, rightSource); switch (op) { case IR::OpBitAnd: as->and32(rhsAddr, targetReg); break; case IR::OpBitOr: as->or32 (rhsAddr, targetReg); break; @@ -433,7 +436,7 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta return false; } } else { // All cases of Z = X op Y - RegisterID r = as->toInt32Register(rightSource, Assembler::ScratchRegister); + 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; @@ -452,18 +455,18 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta // 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, Assembler::ScratchRegister); \ - Jump notZero = as->branch32(RelationalCondition::NotEqual, Assembler::ScratchRegister, TrustedImm32(0)); \ + 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, Assembler::ScratchRegister, targetReg)); break; - case IR::OpRShift: CHECK_RHS(as->rshift32(l, Assembler::ScratchRegister, targetReg)); break; + 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, Assembler::ScratchRegister, targetReg)); + 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; @@ -481,17 +484,19 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta return true; } -static inline Assembler::FPRegisterID getFreeFPReg(IR::Expr *shouldNotOverlap, unsigned hint) +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 Assembler::FPRegisterID(hint + 1); - return Assembler::FPRegisterID(hint); + return typename JITAssembler::FPRegisterID(hint + 1); + return typename JITAssembler::FPRegisterID(hint); } -Assembler::Jump Binop::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) +template <typename JITAssembler> +typename JITAssembler::Jump Binop<JITAssembler>::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) { Jump done; @@ -505,8 +510,8 @@ Assembler::Jump Binop::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSourc // register. switch (op) { case IR::OpAdd: { - FPRegisterID lReg = getFreeFPReg(rightSource, 2); - FPRegisterID rReg = getFreeFPReg(leftSource, 4); + FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2); + FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4); Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); @@ -520,8 +525,8 @@ Assembler::Jump Binop::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSourc rightIsNoDbl.link(as); } break; case IR::OpMul: { - FPRegisterID lReg = getFreeFPReg(rightSource, 2); - FPRegisterID rReg = getFreeFPReg(leftSource, 4); + FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2); + FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4); Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); @@ -535,8 +540,8 @@ Assembler::Jump Binop::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSourc rightIsNoDbl.link(as); } break; case IR::OpSub: { - FPRegisterID lReg = getFreeFPReg(rightSource, 2); - FPRegisterID rReg = getFreeFPReg(leftSource, 4); + FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2); + FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4); Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); @@ -550,8 +555,8 @@ Assembler::Jump Binop::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSourc rightIsNoDbl.link(as); } break; case IR::OpDiv: { - FPRegisterID lReg = getFreeFPReg(rightSource, 2); - FPRegisterID rReg = getFreeFPReg(leftSource, 4); + FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2); + FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4); Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); @@ -571,4 +576,9 @@ Assembler::Jump Binop::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSourc return done; } +template struct QV4::JIT::Binop<QV4::JIT::Assembler<DefaultAssemblerTargetConfiguration>>; +#if defined(V4_BOOTSTRAP) && CPU(X86_64) +template struct QV4::JIT::Binop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>>; +#endif + #endif diff --git a/src/qml/jit/qv4binop_p.h b/src/qml/jit/qv4binop_p.h index 3742e99e5a..d2d9ba7753 100644 --- a/src/qml/jit/qv4binop_p.h +++ b/src/qml/jit/qv4binop_p.h @@ -61,21 +61,22 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace JIT { +template <typename JITAssembler> struct Binop { - Binop(Assembler *assembler, IR::AluOp operation) + Binop(JITAssembler *assembler, IR::AluOp operation) : as(assembler) , op(operation) {} - using Jump = Assembler::Jump; - using Address = Assembler::Address; - using RegisterID = Assembler::RegisterID; - using FPRegisterID = Assembler::FPRegisterID; - using TrustedImm32 = Assembler::TrustedImm32; - using ResultCondition = Assembler::ResultCondition; - using RelationalCondition = Assembler::RelationalCondition; - using Pointer = Assembler::Pointer; - using PointerToValue = Assembler::PointerToValue; + 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); @@ -103,8 +104,8 @@ struct Binop { #if HAVE(ALU_OPS_WITH_MEM_OPERAND) return as->branchAdd32(ResultCondition::Overflow, addr, reg); #else - as->load32(addr, Assembler::ScratchRegister); - return as->branchAdd32(ResultCondition::Overflow, Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + return as->branchAdd32(ResultCondition::Overflow, JITAssembler::ScratchRegister, reg); #endif } @@ -118,8 +119,8 @@ struct Binop { #if HAVE(ALU_OPS_WITH_MEM_OPERAND) return as->branchSub32(ResultCondition::Overflow, addr, reg); #else - as->load32(addr, Assembler::ScratchRegister); - return as->branchSub32(ResultCondition::Overflow, Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + return as->branchSub32(ResultCondition::Overflow, JITAssembler::ScratchRegister, reg); #endif } @@ -131,10 +132,10 @@ struct Binop { Jump inline_mul32(Address addr, RegisterID reg) { #if HAVE(ALU_OPS_WITH_MEM_OPERAND) - return as->branchMul32(Assembler::Overflow, addr, reg); + return as->branchMul32(JITAssembler::Overflow, addr, reg); #else - as->load32(addr, Assembler::ScratchRegister); - return as->branchMul32(ResultCondition::Overflow, Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + return as->branchMul32(ResultCondition::Overflow, JITAssembler::ScratchRegister, reg); #endif } @@ -145,9 +146,9 @@ struct Binop { Jump inline_shl32(Address addr, RegisterID reg) { - as->load32(addr, Assembler::ScratchRegister); - as->and32(TrustedImm32(0x1f), Assembler::ScratchRegister); - as->lshift32(Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + as->and32(TrustedImm32(0x1f), JITAssembler::ScratchRegister); + as->lshift32(JITAssembler::ScratchRegister, reg); return Jump(); } @@ -160,9 +161,9 @@ struct Binop { Jump inline_shr32(Address addr, RegisterID reg) { - as->load32(addr, Assembler::ScratchRegister); - as->and32(TrustedImm32(0x1f), Assembler::ScratchRegister); - as->rshift32(Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + as->and32(TrustedImm32(0x1f), JITAssembler::ScratchRegister); + as->rshift32(JITAssembler::ScratchRegister, reg); return Jump(); } @@ -175,9 +176,9 @@ struct Binop { Jump inline_ushr32(Address addr, RegisterID reg) { - as->load32(addr, Assembler::ScratchRegister); - as->and32(TrustedImm32(0x1f), Assembler::ScratchRegister); - as->urshift32(Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + as->and32(TrustedImm32(0x1f), JITAssembler::ScratchRegister); + as->urshift32(JITAssembler::ScratchRegister, reg); return as->branchTest32(ResultCondition::Signed, reg, reg); } @@ -193,8 +194,8 @@ struct Binop { #if HAVE(ALU_OPS_WITH_MEM_OPERAND) as->and32(addr, reg); #else - as->load32(addr, Assembler::ScratchRegister); - as->and32(Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + as->and32(JITAssembler::ScratchRegister, reg); #endif return Jump(); } @@ -210,8 +211,8 @@ struct Binop { #if HAVE(ALU_OPS_WITH_MEM_OPERAND) as->or32(addr, reg); #else - as->load32(addr, Assembler::ScratchRegister); - as->or32(Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + as->or32(JITAssembler::ScratchRegister, reg); #endif return Jump(); } @@ -227,8 +228,8 @@ struct Binop { #if HAVE(ALU_OPS_WITH_MEM_OPERAND) as->xor32(addr, reg); #else - as->load32(addr, Assembler::ScratchRegister); - as->xor32(Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + as->xor32(JITAssembler::ScratchRegister, reg); #endif return Jump(); } @@ -241,7 +242,7 @@ struct Binop { - Assembler *as; + JITAssembler *as; IR::AluOp op; }; diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index 20752b5c34..b1134d2bec 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -39,11 +39,7 @@ #include "qv4isel_masm_p.h" #include "qv4runtime_p.h" -#include "qv4object_p.h" -#include "qv4functionobject_p.h" -#include "qv4regexpobject_p.h" #include "qv4lookup_p.h" -#include "qv4function_p.h" #include "qv4ssa_p.h" #include "qv4regalloc_p.h" #include "qv4assembler_p.h" @@ -68,7 +64,8 @@ using namespace QV4; using namespace QV4::JIT; -InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory) +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) @@ -79,12 +76,14 @@ InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::Ex module->unitFlags |= QV4::CompiledData::Unit::ContainsMachineCode; } -InstructionSelection::~InstructionSelection() +template <typename JITAssembler> +InstructionSelection<JITAssembler>::~InstructionSelection() { delete _as; } -void InstructionSelection::run(int functionIndex) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::run(int functionIndex) { IR::Function *function = irModule->functions[functionIndex]; qSwap(_function, function); @@ -93,8 +92,8 @@ void InstructionSelection::run(int functionIndex) opt.run(qmlEngine); static const bool withRegisterAllocator = qEnvironmentVariableIsEmpty("QV4_NO_REGALLOC"); - if (Assembler::RegAllocIsSupported && opt.isInSSA() && withRegisterAllocator) { - RegisterAllocator regalloc(Assembler::getRegisterInfo()); + if (JITTargetPlatform::RegAllocIsSupported && opt.isInSSA() && withRegisterAllocator) { + RegisterAllocator regalloc(JITTargetPlatform::getRegisterInfo()); regalloc.run(_function, opt); calculateRegistersToSave(regalloc.usedRegisters()); } else { @@ -103,47 +102,47 @@ void InstructionSelection::run(int functionIndex) opt.convertOutOfSSA(); ConvertTemps().toStackSlots(_function); IR::Optimizer::showMeTheCode(_function, "After stack slot allocation"); - calculateRegistersToSave(Assembler::getRegisterInfo()); // FIXME: this saves all registers. We can probably do with a subset: those that are not used by the register allocator. + 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); - Assembler* oldAssembler = _as; - _as = new Assembler(jsGenerator, _function, executableAllocator); + 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); #ifdef ARGUMENTS_IN_REGISTERS - _as->move(_as->registerForArgument(0), Assembler::EngineRegister); + _as->move(_as->registerForArgument(0), JITTargetPlatform::EngineRegister); #else - _as->loadPtr(addressForArgument(0), Assembler::EngineRegister); + _as->loadPtr(addressForArgument(0), JITTargetPlatform::EngineRegister); #endif const int locals = _as->stackLayout().calculateJSStackFrameSize(); if (locals > 0) { - _as->loadPtr(Address(Assembler::EngineRegister, qOffsetOf(ExecutionEngine, jsStackTop)), Assembler::LocalsRegister); + _as->loadPtr(Address(JITTargetPlatform::EngineRegister, qOffsetOf(ExecutionEngine, jsStackTop)), JITTargetPlatform::LocalsRegister); #ifdef VALUE_FITS_IN_REGISTER - _as->move(Assembler::TrustedImm64(0), Assembler::ReturnValueRegister); - _as->move(Assembler::TrustedImm32(locals), Assembler::ScratchRegister); - Assembler::Label loop = _as->label(); - _as->store64(Assembler::ReturnValueRegister, Assembler::Address(Assembler::LocalsRegister)); - _as->add64(Assembler::TrustedImm32(8), Assembler::LocalsRegister); - Assembler::Jump jump = _as->branchSub32(Assembler::NonZero, Assembler::TrustedImm32(1), Assembler::ScratchRegister); + _as->move(TrustedImm64(0), JITTargetPlatform::ReturnValueRegister); + _as->move(TrustedImm32(locals), JITTargetPlatform::ScratchRegister); + Label loop = _as->label(); + _as->store64(JITTargetPlatform::ReturnValueRegister, Address(JITTargetPlatform::LocalsRegister)); + _as->add64(TrustedImm32(8), JITTargetPlatform::LocalsRegister); + Jump jump = _as->branchSub32(ResultCondition::NonZero, TrustedImm32(1), JITTargetPlatform::ScratchRegister); jump.linkTo(loop, _as); #else - _as->move(Assembler::TrustedImm32(0), Assembler::ReturnValueRegister); - _as->move(Assembler::TrustedImm32(locals), Assembler::ScratchRegister); - Assembler::Label loop = _as->label(); - _as->store32(Assembler::ReturnValueRegister, Assembler::Address(Assembler::LocalsRegister)); - _as->add32(Assembler::TrustedImm32(4), Assembler::LocalsRegister); - _as->store32(Assembler::ReturnValueRegister, Assembler::Address(Assembler::LocalsRegister)); - _as->add32(Assembler::TrustedImm32(4), Assembler::LocalsRegister); - Assembler::Jump jump = _as->branchSub32(Assembler::NonZero, Assembler::TrustedImm32(1), Assembler::ScratchRegister); + _as->move(TrustedImm32(0), JITTargetPlatform::ReturnValueRegister); + _as->move(TrustedImm32(locals), JITTargetPlatform::ScratchRegister); + Label loop = _as->label(); + _as->store32(JITTargetPlatform::ReturnValueRegister, Address(JITTargetPlatform::LocalsRegister)); + _as->add32(TrustedImm32(4), JITTargetPlatform::LocalsRegister); + _as->store32(JITTargetPlatform::ReturnValueRegister, Address(JITTargetPlatform::LocalsRegister)); + _as->add32(TrustedImm32(4), JITTargetPlatform::LocalsRegister); + Jump jump = _as->branchSub32(ResultCondition::NonZero, TrustedImm32(1), JITTargetPlatform::ScratchRegister); jump.linkTo(loop, _as); #endif - _as->storePtr(Assembler::LocalsRegister, Address(Assembler::EngineRegister, qOffsetOf(ExecutionEngine, jsStackTop))); + _as->storePtr(JITTargetPlatform::LocalsRegister, Address(JITTargetPlatform::EngineRegister, qOffsetOf(ExecutionEngine, jsStackTop))); } @@ -158,9 +157,9 @@ void InstructionSelection::run(int functionIndex) for (IR::Stmt *s : _block->statements()) { if (s->location.isValid()) { if (int(s->location.startLine) != lastLine) { - _as->loadPtr(Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), Assembler::ScratchRegister); - Assembler::Address lineAddr(Assembler::ScratchRegister, qOffsetOf(QV4::ExecutionContext::Data, lineNumber)); - _as->store32(Assembler::TrustedImm32(s->location.startLine), lineAddr); + _as->loadPtr(Address(JITTargetPlatform::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), JITTargetPlatform::ScratchRegister); + Address lineAddr(JITTargetPlatform::ScratchRegister, qOffsetOf(QV4::ExecutionContext::Data, lineNumber)); + _as->store32(TrustedImm32(s->location.startLine), lineAddr); lastLine = s->location.startLine; } } @@ -181,165 +180,187 @@ void InstructionSelection::run(int functionIndex) qSwap(_removableJumps, removableJumps); } -QQmlRefPointer<QV4::CompiledData::CompilationUnit> InstructionSelection::backendCompileStep() +template <typename JITAssembler> +QQmlRefPointer<QV4::CompiledData::CompilationUnit> InstructionSelection<JITAssembler>::backendCompileStep() { QQmlRefPointer<QV4::CompiledData::CompilationUnit> result; result.adopt(compilationUnit.take()); return result; } -void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *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(result, callGlobalLookup, - Assembler::EngineRegister, - Assembler::TrustedImm32(index), + generateRuntimeCall(_as, result, callGlobalLookup, + JITTargetPlatform::EngineRegister, + TrustedImm32(index), baseAddressForCallData()); } else { - generateRuntimeCall(result, callActivationProperty, - Assembler::EngineRegister, - Assembler::StringToIndex(*func->id), + generateRuntimeCall(_as, result, callActivationProperty, + JITTargetPlatform::EngineRegister, + StringToIndex(*func->id), baseAddressForCallData()); } } -void InstructionSelection::callBuiltinTypeofQmlContextProperty(IR::Expr *base, +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(result, typeofScopeObjectProperty, Assembler::EngineRegister, - Assembler::PointerToValue(base), - Assembler::TrustedImm32(propertyIndex)); + generateRuntimeCall(_as, result, typeofScopeObjectProperty, JITTargetPlatform::EngineRegister, + PointerToValue(base), + TrustedImm32(propertyIndex)); } else if (kind == IR::Member::MemberOfQmlContextObject) { - generateRuntimeCall(result, typeofContextObjectProperty, - Assembler::EngineRegister, Assembler::PointerToValue(base), - Assembler::TrustedImm32(propertyIndex)); + generateRuntimeCall(_as, result, typeofContextObjectProperty, + JITTargetPlatform::EngineRegister, PointerToValue(base), + TrustedImm32(propertyIndex)); } else { Q_UNREACHABLE(); } } -void InstructionSelection::callBuiltinTypeofMember(IR::Expr *base, const QString &name, +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinTypeofMember(IR::Expr *base, const QString &name, IR::Expr *result) { - generateRuntimeCall(result, typeofMember, Assembler::EngineRegister, - Assembler::PointerToValue(base), Assembler::StringToIndex(name)); + generateRuntimeCall(_as, result, typeofMember, JITTargetPlatform::EngineRegister, + PointerToValue(base), StringToIndex(name)); } -void InstructionSelection::callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) { - generateRuntimeCall(result, typeofElement, - Assembler::EngineRegister, - Assembler::PointerToValue(base), Assembler::PointerToValue(index)); + generateRuntimeCall(_as, result, typeofElement, + JITTargetPlatform::EngineRegister, + PointerToValue(base), PointerToValue(index)); } -void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinTypeofName(const QString &name, IR::Expr *result) { - generateRuntimeCall(result, typeofName, Assembler::EngineRegister, - Assembler::StringToIndex(name)); + generateRuntimeCall(_as, result, typeofName, JITTargetPlatform::EngineRegister, + StringToIndex(name)); } -void InstructionSelection::callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result) { - generateRuntimeCall(result, typeofValue, Assembler::EngineRegister, - Assembler::PointerToValue(value)); + generateRuntimeCall(_as, result, typeofValue, JITTargetPlatform::EngineRegister, + PointerToValue(value)); } -void InstructionSelection::callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result) { - generateRuntimeCall(result, deleteMember, Assembler::EngineRegister, - Assembler::Reference(base), Assembler::StringToIndex(name)); + generateRuntimeCall(_as, result, deleteMember, JITTargetPlatform::EngineRegister, + Reference(base), StringToIndex(name)); } -void InstructionSelection::callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index, +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) { - generateRuntimeCall(result, deleteElement, Assembler::EngineRegister, - Assembler::Reference(base), Assembler::PointerToValue(index)); + generateRuntimeCall(_as, result, deleteElement, JITTargetPlatform::EngineRegister, + Reference(base), PointerToValue(index)); } -void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinDeleteName(const QString &name, IR::Expr *result) { - generateRuntimeCall(result, deleteName, Assembler::EngineRegister, - Assembler::StringToIndex(name)); + generateRuntimeCall(_as, result, deleteName, JITTargetPlatform::EngineRegister, + StringToIndex(name)); } -void InstructionSelection::callBuiltinDeleteValue(IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinDeleteValue(IR::Expr *result) { _as->storeValue(Primitive::fromBoolean(false), result); } -void InstructionSelection::callBuiltinThrow(IR::Expr *arg) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinThrow(IR::Expr *arg) { - generateRuntimeCall(Assembler::ReturnValueRegister, throwException, Assembler::EngineRegister, - Assembler::PointerToValue(arg)); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, throwException, JITTargetPlatform::EngineRegister, + PointerToValue(arg)); } -void InstructionSelection::callBuiltinReThrow() +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinReThrow() { _as->jumpToExceptionHandler(); } -void InstructionSelection::callBuiltinUnwindException(IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinUnwindException(IR::Expr *result) { - generateRuntimeCall(result, unwindException, Assembler::EngineRegister); + generateRuntimeCall(_as, result, unwindException, JITTargetPlatform::EngineRegister); } -void InstructionSelection::callBuiltinPushCatchScope(const QString &exceptionName) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinPushCatchScope(const QString &exceptionName) { - generateRuntimeCall(Assembler::Void, pushCatchScope, Assembler::EngineRegister, Assembler::StringToIndex(exceptionName)); + generateRuntimeCall(_as, JITAssembler::Void, pushCatchScope, JITTargetPlatform::EngineRegister, StringToIndex(exceptionName)); } -void InstructionSelection::callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result) { Q_ASSERT(arg); Q_ASSERT(result); - generateRuntimeCall(result, foreachIterator, Assembler::EngineRegister, Assembler::PointerToValue(arg)); + generateRuntimeCall(_as, result, foreachIterator, JITTargetPlatform::EngineRegister, PointerToValue(arg)); } -void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result) { Q_ASSERT(arg); Q_ASSERT(result); - generateRuntimeCall(result, foreachNextPropertyName, Assembler::Reference(arg)); + generateRuntimeCall(_as, result, foreachNextPropertyName, Reference(arg)); } -void InstructionSelection::callBuiltinPushWithScope(IR::Expr *arg) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinPushWithScope(IR::Expr *arg) { Q_ASSERT(arg); - generateRuntimeCall(Assembler::Void, pushWithScope, Assembler::Reference(arg), Assembler::EngineRegister); + generateRuntimeCall(_as, JITAssembler::Void, pushWithScope, Reference(arg), JITTargetPlatform::EngineRegister); } -void InstructionSelection::callBuiltinPopScope() +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinPopScope() { - generateRuntimeCall(Assembler::Void, popScope, Assembler::EngineRegister); + generateRuntimeCall(_as, JITAssembler::Void, popScope, JITTargetPlatform::EngineRegister); } -void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinDeclareVar(bool deletable, const QString &name) { - generateRuntimeCall(Assembler::Void, declareVar, Assembler::EngineRegister, - Assembler::TrustedImm32(deletable), Assembler::StringToIndex(name)); + generateRuntimeCall(_as, JITAssembler::Void, declareVar, JITTargetPlatform::EngineRegister, + TrustedImm32(deletable), StringToIndex(name)); } -void InstructionSelection::callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args) { Q_ASSERT(result); int length = prepareVariableArguments(args); - generateRuntimeCall(result, arrayLiteral, Assembler::EngineRegister, - baseAddressForCallArguments(), Assembler::TrustedImm32(length)); + generateRuntimeCall(_as, result, arrayLiteral, JITTargetPlatform::EngineRegister, + baseAddressForCallArguments(), TrustedImm32(length)); } -void InstructionSelection::callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray) { Q_ASSERT(result); @@ -415,81 +436,89 @@ void InstructionSelection::callBuiltinDefineObjectLiteral(IR::Expr *result, int it = it->next; } - generateRuntimeCall(result, objectLiteral, Assembler::EngineRegister, - baseAddressForCallArguments(), Assembler::TrustedImm32(classId), - Assembler::TrustedImm32(arrayValueCount), Assembler::TrustedImm32(arrayGetterSetterCount | (needSparseArray << 30))); + generateRuntimeCall(_as, result, objectLiteral, JITTargetPlatform::EngineRegister, + baseAddressForCallArguments(), TrustedImm32(classId), + TrustedImm32(arrayValueCount), TrustedImm32(arrayGetterSetterCount | (needSparseArray << 30))); } -void InstructionSelection::callBuiltinSetupArgumentObject(IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinSetupArgumentObject(IR::Expr *result) { - generateRuntimeCall(result, setupArgumentsObject, Assembler::EngineRegister); + generateRuntimeCall(_as, result, setupArgumentsObject, JITTargetPlatform::EngineRegister); } -void InstructionSelection::callBuiltinConvertThisToObject() +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinConvertThisToObject() { - generateRuntimeCall(Assembler::Void, convertThisToObject, Assembler::EngineRegister); + generateRuntimeCall(_as, JITAssembler::Void, convertThisToObject, JITTargetPlatform::EngineRegister); } -void InstructionSelection::callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) +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(result, callValue, Assembler::EngineRegister, - Assembler::PointerToValue(value), + generateRuntimeCall(_as, result, callValue, JITTargetPlatform::EngineRegister, + PointerToValue(value), baseAddressForCallData()); else - generateRuntimeCall(result, callValue, Assembler::EngineRegister, - Assembler::Reference(value), + generateRuntimeCall(_as, result, callValue, JITTargetPlatform::EngineRegister, + Reference(value), baseAddressForCallData()); } -void InstructionSelection::loadThisObject(IR::Expr *temp) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::loadThisObject(IR::Expr *temp) { - _as->loadPtr(Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), Assembler::ScratchRegister); - _as->loadPtr(Address(Assembler::ScratchRegister, qOffsetOf(ExecutionContext::Data, callData)), Assembler::ScratchRegister); + _as->loadPtr(Address(JITTargetPlatform::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), JITTargetPlatform::ScratchRegister); + _as->loadPtr(Address(JITTargetPlatform::ScratchRegister, qOffsetOf(ExecutionContext::Data, callData)), JITTargetPlatform::ScratchRegister); #if defined(VALUE_FITS_IN_REGISTER) - _as->load64(Pointer(Assembler::ScratchRegister, qOffsetOf(CallData, thisObject)), - Assembler::ReturnValueRegister); + _as->load64(Pointer(JITTargetPlatform::ScratchRegister, qOffsetOf(CallData, thisObject)), + JITTargetPlatform::ReturnValueRegister); _as->storeReturnValue(temp); #else - _as->copyValue(temp, Pointer(Assembler::ScratchRegister, qOffsetOf(CallData, thisObject))); + _as->copyValue(temp, Pointer(JITTargetPlatform::ScratchRegister, qOffsetOf(CallData, thisObject))); #endif } -void InstructionSelection::loadQmlContext(IR::Expr *temp) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::loadQmlContext(IR::Expr *temp) { - generateRuntimeCall(temp, getQmlContext, Assembler::EngineRegister); + generateRuntimeCall(_as, temp, getQmlContext, JITTargetPlatform::EngineRegister); } -void InstructionSelection::loadQmlImportedScripts(IR::Expr *temp) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::loadQmlImportedScripts(IR::Expr *temp) { - generateRuntimeCall(temp, getQmlImportedScripts, Assembler::EngineRegister); + generateRuntimeCall(_as, temp, getQmlImportedScripts, JITTargetPlatform::EngineRegister); } -void InstructionSelection::loadQmlSingleton(const QString &name, IR::Expr *temp) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::loadQmlSingleton(const QString &name, IR::Expr *temp) { - generateRuntimeCall(temp, getQmlSingleton, Assembler::EngineRegister, Assembler::StringToIndex(name)); + generateRuntimeCall(_as, temp, getQmlSingleton, JITTargetPlatform::EngineRegister, StringToIndex(name)); } -void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Expr *target) +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, (Assembler::FPRegisterID) targetTemp->index); + _as->toDoubleRegister(sourceConst, (FPRegisterID) targetTemp->index); } else if (targetTemp->type == IR::SInt32Type) { Q_ASSERT(sourceConst->type == IR::SInt32Type); - _as->toInt32Register(sourceConst, (Assembler::RegisterID) targetTemp->index); + _as->toInt32Register(sourceConst, (RegisterID) targetTemp->index); } else if (targetTemp->type == IR::UInt32Type) { Q_ASSERT(sourceConst->type == IR::UInt32Type); - _as->toUInt32Register(sourceConst, (Assembler::RegisterID) targetTemp->index); + _as->toUInt32Register(sourceConst, (RegisterID) targetTemp->index); } else if (targetTemp->type == IR::BoolType) { Q_ASSERT(sourceConst->type == IR::BoolType); - _as->move(Assembler::TrustedImm32(convertToValue(sourceConst).int_32()), - (Assembler::RegisterID) targetTemp->index); + _as->move(TrustedImm32(convertToValue(sourceConst).int_32()), + (RegisterID) targetTemp->index); } else { Q_UNREACHABLE(); } @@ -500,147 +529,155 @@ void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Expr *target) _as->storeValue(convertToValue(sourceConst), target); } -void InstructionSelection::loadString(const QString &str, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::loadString(const QString &str, IR::Expr *target) { - Pointer srcAddr = _as->loadStringAddress(Assembler::ReturnValueRegister, str); - _as->loadPtr(srcAddr, Assembler::ReturnValueRegister); - Pointer destAddr = _as->loadAddress(Assembler::ScratchRegister, target); -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - _as->store64(Assembler::ReturnValueRegister, destAddr); -#else - _as->store32(Assembler::ReturnValueRegister, destAddr); - destAddr.offset += 4; - _as->store32(Assembler::TrustedImm32(QV4::Value::Managed_Type_Internal), destAddr); -#endif + Pointer srcAddr = _as->loadStringAddress(JITTargetPlatform::ReturnValueRegister, str); + _as->loadPtr(srcAddr, JITTargetPlatform::ReturnValueRegister); + Pointer destAddr = _as->loadAddress(JITTargetPlatform::ScratchRegister, target); + JITAssembler::RegisterSizeDependentOps::loadManagedPointer(_as, JITTargetPlatform::ReturnValueRegister, destAddr); } -void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target) { int id = registerRegExp(sourceRegexp); - generateRuntimeCall(target, regexpLiteral, Assembler::EngineRegister, Assembler::TrustedImm32(id)); + generateRuntimeCall(_as, target, regexpLiteral, JITTargetPlatform::EngineRegister, TrustedImm32(id)); } -void InstructionSelection::getActivationProperty(const IR::Name *name, IR::Expr *target) +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, qOffsetOf(QV4::Lookup, globalGetter), Assembler::EngineRegister, Assembler::Void); + generateLookupCall(target, index, qOffsetOf(QV4::Lookup, globalGetter), JITTargetPlatform::EngineRegister, JITAssembler::Void); return; } - generateRuntimeCall(target, getActivationProperty, Assembler::EngineRegister, Assembler::StringToIndex(*name->id)); + generateRuntimeCall(_as, target, getActivationProperty, JITTargetPlatform::EngineRegister, StringToIndex(*name->id)); } -void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::setActivationProperty(IR::Expr *source, const QString &targetName) { // ### should use a lookup call here - generateRuntimeCall(Assembler::Void, setActivationProperty, - Assembler::EngineRegister, Assembler::StringToIndex(targetName), Assembler::PointerToValue(source)); + generateRuntimeCall(_as, JITAssembler::Void, setActivationProperty, + JITTargetPlatform::EngineRegister, StringToIndex(targetName), PointerToValue(source)); } -void InstructionSelection::initClosure(IR::Closure *closure, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::initClosure(IR::Closure *closure, IR::Expr *target) { int id = closure->value; - generateRuntimeCall(target, closure, Assembler::EngineRegister, Assembler::TrustedImm32(id)); + generateRuntimeCall(_as, target, closure, JITTargetPlatform::EngineRegister, TrustedImm32(id)); } -void InstructionSelection::getProperty(IR::Expr *base, const QString &name, IR::Expr *target) +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, qOffsetOf(QV4::Lookup, getter), Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::Void); + generateLookupCall(target, index, qOffsetOf(QV4::Lookup, getter), JITTargetPlatform::EngineRegister, PointerToValue(base), JITAssembler::Void); } else { - generateRuntimeCall(target, getProperty, Assembler::EngineRegister, - Assembler::PointerToValue(base), Assembler::StringToIndex(name)); + generateRuntimeCall(_as, target, getProperty, JITTargetPlatform::EngineRegister, + PointerToValue(base), StringToIndex(name)); } } -void InstructionSelection::getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target) +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(target, getQmlScopeObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(index), Assembler::TrustedImm32(captureRequired)); + generateRuntimeCall(_as, target, getQmlScopeObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(index), TrustedImm32(captureRequired)); else if (kind == IR::Member::MemberOfQmlContextObject) - generateRuntimeCall(target, getQmlContextObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(index), Assembler::TrustedImm32(captureRequired)); + generateRuntimeCall(_as, target, getQmlContextObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(index), TrustedImm32(captureRequired)); else if (kind == IR::Member::MemberOfIdObjectsArray) - generateRuntimeCall(target, getQmlIdObject, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(index)); + generateRuntimeCall(_as, target, getQmlIdObject, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(index)); else Q_ASSERT(false); } -void InstructionSelection::getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target) +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(target, getQmlAttachedProperty, Assembler::EngineRegister, Assembler::TrustedImm32(attachedPropertiesId), Assembler::TrustedImm32(propertyIndex)); + generateRuntimeCall(_as, target, getQmlAttachedProperty, JITTargetPlatform::EngineRegister, TrustedImm32(attachedPropertiesId), TrustedImm32(propertyIndex)); else if (isSingleton) - generateRuntimeCall(target, getQmlSingletonQObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(propertyIndex), - Assembler::TrustedImm32(captureRequired)); + generateRuntimeCall(_as, target, getQmlSingletonQObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(propertyIndex), + TrustedImm32(captureRequired)); else - generateRuntimeCall(target, getQmlQObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(propertyIndex), - Assembler::TrustedImm32(captureRequired)); + generateRuntimeCall(_as, target, getQmlQObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(propertyIndex), + TrustedImm32(captureRequired)); } -void InstructionSelection::setProperty(IR::Expr *source, IR::Expr *targetBase, +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName) { if (useFastLookups) { uint index = registerSetterLookup(targetName); - generateLookupCall(Assembler::Void, index, qOffsetOf(QV4::Lookup, setter), - Assembler::EngineRegister, - Assembler::PointerToValue(targetBase), - Assembler::PointerToValue(source)); + generateLookupCall(JITAssembler::Void, index, qOffsetOf(QV4::Lookup, setter), + JITTargetPlatform::EngineRegister, + PointerToValue(targetBase), + PointerToValue(source)); } else { - generateRuntimeCall(Assembler::Void, setProperty, Assembler::EngineRegister, - Assembler::PointerToValue(targetBase), Assembler::StringToIndex(targetName), - Assembler::PointerToValue(source)); + generateRuntimeCall(_as, JITAssembler::Void, setProperty, JITTargetPlatform::EngineRegister, + PointerToValue(targetBase), StringToIndex(targetName), + PointerToValue(source)); } } -void InstructionSelection::setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) +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(Assembler::Void, setQmlScopeObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase), - Assembler::TrustedImm32(propertyIndex), Assembler::PointerToValue(source)); + generateRuntimeCall(_as, JITAssembler::Void, setQmlScopeObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(targetBase), + TrustedImm32(propertyIndex), PointerToValue(source)); else if (kind == IR::Member::MemberOfQmlContextObject) - generateRuntimeCall(Assembler::Void, setQmlContextObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase), - Assembler::TrustedImm32(propertyIndex), Assembler::PointerToValue(source)); + generateRuntimeCall(_as, JITAssembler::Void, setQmlContextObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(targetBase), + TrustedImm32(propertyIndex), PointerToValue(source)); else Q_ASSERT(false); } -void InstructionSelection::setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) { - generateRuntimeCall(Assembler::Void, setQmlQObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase), - Assembler::TrustedImm32(propertyIndex), Assembler::PointerToValue(source)); + generateRuntimeCall(_as, JITAssembler::Void, setQmlQObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(targetBase), + TrustedImm32(propertyIndex), PointerToValue(source)); } -void InstructionSelection::getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) { if (useFastLookups) { uint lookup = registerIndexedGetterLookup(); generateLookupCall(target, lookup, qOffsetOf(QV4::Lookup, indexedGetter), - Assembler::PointerToValue(base), - Assembler::PointerToValue(index)); + PointerToValue(base), + PointerToValue(index)); return; } - generateRuntimeCall(target, getElement, Assembler::EngineRegister, - Assembler::PointerToValue(base), Assembler::PointerToValue(index)); + generateRuntimeCall(_as, target, getElement, JITTargetPlatform::EngineRegister, + PointerToValue(base), PointerToValue(index)); } -void InstructionSelection::setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) { if (useFastLookups) { uint lookup = registerIndexedSetterLookup(); - generateLookupCall(Assembler::Void, lookup, qOffsetOf(QV4::Lookup, indexedSetter), - Assembler::PointerToValue(targetBase), Assembler::PointerToValue(targetIndex), - Assembler::PointerToValue(source)); + generateLookupCall(JITAssembler::Void, lookup, qOffsetOf(QV4::Lookup, indexedSetter), + PointerToValue(targetBase), PointerToValue(targetIndex), + PointerToValue(source)); return; } - generateRuntimeCall(Assembler::Void, setElement, Assembler::EngineRegister, - Assembler::PointerToValue(targetBase), Assembler::PointerToValue(targetIndex), - Assembler::PointerToValue(source)); + generateRuntimeCall(_as, JITAssembler::Void, setElement, JITTargetPlatform::EngineRegister, + PointerToValue(targetBase), PointerToValue(targetIndex), + PointerToValue(source)); } -void InstructionSelection::copyValue(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::copyValue(IR::Expr *source, IR::Expr *target) { IR::Temp *sourceTemp = source->asTemp(); IR::Temp *targetTemp = target->asTemp(); @@ -655,25 +692,25 @@ void InstructionSelection::copyValue(IR::Expr *source, IR::Expr *target) if (sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister) { if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { if (sourceTemp->type == IR::DoubleType) - _as->moveDouble((Assembler::FPRegisterID) sourceTemp->index, - (Assembler::FPRegisterID) targetTemp->index); + _as->moveDouble((FPRegisterID) sourceTemp->index, + (FPRegisterID) targetTemp->index); else - _as->move((Assembler::RegisterID) sourceTemp->index, - (Assembler::RegisterID) targetTemp->index); + _as->move((RegisterID) sourceTemp->index, + (RegisterID) targetTemp->index); return; } else { switch (sourceTemp->type) { case IR::DoubleType: - _as->storeDouble((Assembler::FPRegisterID) sourceTemp->index, target); + _as->storeDouble((FPRegisterID) sourceTemp->index, target); break; case IR::SInt32Type: - _as->storeInt32((Assembler::RegisterID) sourceTemp->index, target); + _as->storeInt32((RegisterID) sourceTemp->index, target); break; case IR::UInt32Type: - _as->storeUInt32((Assembler::RegisterID) sourceTemp->index, target); + _as->storeUInt32((RegisterID) sourceTemp->index, target); break; case IR::BoolType: - _as->storeBool((Assembler::RegisterID) sourceTemp->index, target); + _as->storeBool((RegisterID) sourceTemp->index, target); break; default: Q_ASSERT(!"Unreachable"); @@ -685,19 +722,19 @@ void InstructionSelection::copyValue(IR::Expr *source, IR::Expr *target) switch (targetTemp->type) { case IR::DoubleType: Q_ASSERT(source->type == IR::DoubleType); - _as->toDoubleRegister(source, (Assembler::FPRegisterID) targetTemp->index); + _as->toDoubleRegister(source, (FPRegisterID) targetTemp->index); return; case IR::BoolType: Q_ASSERT(source->type == IR::BoolType); - _as->toInt32Register(source, (Assembler::RegisterID) targetTemp->index); + _as->toInt32Register(source, (RegisterID) targetTemp->index); return; case IR::SInt32Type: Q_ASSERT(source->type == IR::SInt32Type); - _as->toInt32Register(source, (Assembler::RegisterID) targetTemp->index); + _as->toInt32Register(source, (RegisterID) targetTemp->index); return; case IR::UInt32Type: Q_ASSERT(source->type == IR::UInt32Type); - _as->toUInt32Register(source, (Assembler::RegisterID) targetTemp->index); + _as->toUInt32Register(source, (RegisterID) targetTemp->index); return; default: Q_ASSERT(!"Unreachable"); @@ -706,10 +743,11 @@ void InstructionSelection::copyValue(IR::Expr *source, IR::Expr *target) } // The target is not a physical register, nor is the source. So we can do a memory-to-memory copy: - _as->memcopyValue(_as->loadAddress(Assembler::ReturnValueRegister, target), source, Assembler::ScratchRegister); + _as->memcopyValue(_as->loadAddress(JITTargetPlatform::ReturnValueRegister, target), source, JITTargetPlatform::ScratchRegister); } -void InstructionSelection::swapValues(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::swapValues(IR::Expr *source, IR::Expr *target) { IR::Temp *sourceTemp = source->asTemp(); IR::Temp *targetTemp = target->asTemp(); @@ -719,26 +757,27 @@ void InstructionSelection::swapValues(IR::Expr *source, IR::Expr *target) Q_ASSERT(sourceTemp->type == targetTemp->type); if (sourceTemp->type == IR::DoubleType) { - _as->moveDouble((Assembler::FPRegisterID) targetTemp->index, Assembler::FPGpr0); - _as->moveDouble((Assembler::FPRegisterID) sourceTemp->index, - (Assembler::FPRegisterID) targetTemp->index); - _as->moveDouble(Assembler::FPGpr0, (Assembler::FPRegisterID) sourceTemp->index); + _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((Assembler::RegisterID) sourceTemp->index, - (Assembler::RegisterID) targetTemp->index); + _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. - Assembler::Pointer sAddr = _as->loadAddress(Assembler::ScratchRegister, source); - Assembler::Pointer tAddr = _as->loadAddress(Assembler::ReturnValueRegister, target); + Pointer sAddr = _as->loadAddress(JITTargetPlatform::ScratchRegister, source); + Pointer tAddr = _as->loadAddress(JITTargetPlatform::ReturnValueRegister, target); // use the implementation in JSC::MacroAssembler, as it doesn't do bit swizzling - _as->JSC::MacroAssembler::loadDouble(sAddr, Assembler::FPGpr0); - _as->JSC::MacroAssembler::loadDouble(tAddr, Assembler::FPGpr1); - _as->JSC::MacroAssembler::storeDouble(Assembler::FPGpr1, sAddr); - _as->JSC::MacroAssembler::storeDouble(Assembler::FPGpr0, tAddr); + auto platformAs = static_cast<typename JITAssembler::MacroAssembler*>(_as); + platformAs->loadDouble(sAddr, JITTargetPlatform::FPGpr0); + platformAs->loadDouble(tAddr, JITTargetPlatform::FPGpr1); + platformAs->storeDouble(JITTargetPlatform::FPGpr1, sAddr); + platformAs->storeDouble(JITTargetPlatform::FPGpr0, tAddr); return; } } @@ -749,18 +788,18 @@ void InstructionSelection::swapValues(IR::Expr *source, IR::Expr *target) Q_ASSERT(memExpr); Q_ASSERT(regTemp); - Assembler::Pointer addr = _as->loadAddress(Assembler::ReturnValueRegister, memExpr); + Pointer addr = _as->loadAddress(JITTargetPlatform::ReturnValueRegister, memExpr); if (regTemp->type == IR::DoubleType) { - _as->loadDouble(addr, Assembler::FPGpr0); - _as->storeDouble((Assembler::FPRegisterID) regTemp->index, addr); - _as->moveDouble(Assembler::FPGpr0, (Assembler::FPRegisterID) regTemp->index); + _as->loadDouble(addr, JITTargetPlatform::FPGpr0); + _as->storeDouble((FPRegisterID) regTemp->index, addr); + _as->moveDouble(JITTargetPlatform::FPGpr0, (FPRegisterID) regTemp->index); } else if (regTemp->type == IR::UInt32Type) { - _as->toUInt32Register(addr, Assembler::ScratchRegister); - _as->storeUInt32((Assembler::RegisterID) regTemp->index, addr); - _as->move(Assembler::ScratchRegister, (Assembler::RegisterID) regTemp->index); + _as->toUInt32Register(addr, JITTargetPlatform::ScratchRegister); + _as->storeUInt32((RegisterID) regTemp->index, addr); + _as->move(JITTargetPlatform::ScratchRegister, (RegisterID) regTemp->index); } else { - _as->load32(addr, Assembler::ScratchRegister); - _as->store32((Assembler::RegisterID) regTemp->index, addr); + _as->load32(addr, JITTargetPlatform::ScratchRegister); + _as->store32((RegisterID) regTemp->index, addr); if (regTemp->type != memExpr->type) { addr.offset += 4; quint32 tag; @@ -775,55 +814,59 @@ void InstructionSelection::swapValues(IR::Expr *source, IR::Expr *target) tag = 31337; // bogus value Q_UNREACHABLE(); } - _as->store32(Assembler::TrustedImm32(tag), addr); + _as->store32(TrustedImm32(tag), addr); } - _as->move(Assembler::ScratchRegister, (Assembler::RegisterID) regTemp->index); + _as->move(JITTargetPlatform::ScratchRegister, (RegisterID) regTemp->index); } } #define setOp(op, opName, operation) \ do { \ - op = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); \ + op = typename JITAssembler::RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); \ needsExceptionCheck = QV4::Runtime::Method_##operation##_NeedsExceptionCheck; \ } while (0) #define setOpContext(op, opName, operation) \ do { \ - opContext = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); \ + opContext = typename JITAssembler::RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); \ needsExceptionCheck = QV4::Runtime::Method_##operation##_NeedsExceptionCheck; \ } while (0) -void InstructionSelection::unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target) { - QV4::JIT::Unop unop(_as, oper); + QV4::JIT::Unop<JITAssembler> unop(_as, oper); unop.generate(source, target); } -void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) { - QV4::JIT::Binop binop(_as, oper); + QV4::JIT::Binop<JITAssembler> binop(_as, oper); binop.generate(leftSource, rightSource, target); } -void InstructionSelection::callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) +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(result, callQmlScopeObjectProperty, - Assembler::EngineRegister, - Assembler::TrustedImm32(propertyIndex), + generateRuntimeCall(_as, result, callQmlScopeObjectProperty, + JITTargetPlatform::EngineRegister, + TrustedImm32(propertyIndex), baseAddressForCallData()); else if (kind == IR::Member::MemberOfQmlContextObject) - generateRuntimeCall(result, callQmlContextObjectProperty, - Assembler::EngineRegister, - Assembler::TrustedImm32(propertyIndex), + generateRuntimeCall(_as, result, callQmlContextObjectProperty, + JITTargetPlatform::EngineRegister, + TrustedImm32(propertyIndex), baseAddressForCallData()); else Q_ASSERT(false); } -void InstructionSelection::callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) { Q_ASSERT(base != 0); @@ -832,29 +875,31 @@ void InstructionSelection::callProperty(IR::Expr *base, const QString &name, IR: if (useFastLookups) { uint index = registerGetterLookup(name); - generateRuntimeCall(result, callPropertyLookup, - Assembler::EngineRegister, - Assembler::TrustedImm32(index), + generateRuntimeCall(_as, result, callPropertyLookup, + JITTargetPlatform::EngineRegister, + TrustedImm32(index), baseAddressForCallData()); } else { - generateRuntimeCall(result, callProperty, Assembler::EngineRegister, - Assembler::StringToIndex(name), + generateRuntimeCall(_as, result, callProperty, JITTargetPlatform::EngineRegister, + StringToIndex(name), baseAddressForCallData()); } } -void InstructionSelection::callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, +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(result, callElement, Assembler::EngineRegister, - Assembler::PointerToValue(index), + generateRuntimeCall(_as, result, callElement, JITTargetPlatform::EngineRegister, + PointerToValue(index), baseAddressForCallData()); } -void InstructionSelection::convertType(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::convertType(IR::Expr *source, IR::Expr *target) { switch (target->type) { case IR::DoubleType: @@ -875,7 +920,8 @@ void InstructionSelection::convertType(IR::Expr *source, IR::Expr *target) } } -void InstructionSelection::convertTypeSlowPath(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::convertTypeSlowPath(IR::Expr *source, IR::Expr *target) { Q_ASSERT(target->type != IR::BoolType); @@ -885,7 +931,8 @@ void InstructionSelection::convertTypeSlowPath(IR::Expr *source, IR::Expr *targe copyValue(source, target); } -void InstructionSelection::convertTypeToDouble(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::convertTypeToDouble(IR::Expr *source, IR::Expr *target) { switch (source->type) { case IR::SInt32Type: @@ -897,51 +944,37 @@ void InstructionSelection::convertTypeToDouble(IR::Expr *source, IR::Expr *targe convertUIntToDouble(source, target); break; case IR::UndefinedType: - _as->loadDouble(_as->loadAddress(Assembler::ScratchRegister, source), Assembler::FPGpr0); - _as->storeDouble(Assembler::FPGpr0, target); + _as->loadDouble(_as->loadAddress(JITTargetPlatform::ScratchRegister, source), JITTargetPlatform::FPGpr0); + _as->storeDouble(JITTargetPlatform::FPGpr0, target); break; case IR::StringType: case IR::VarType: { // load the tag: - Assembler::Pointer tagAddr = _as->loadAddress(Assembler::ScratchRegister, source); + Pointer tagAddr = _as->loadAddress(JITTargetPlatform::ScratchRegister, source); tagAddr.offset += 4; - _as->load32(tagAddr, Assembler::ScratchRegister); + _as->load32(tagAddr, JITTargetPlatform::ScratchRegister); // check if it's an int32: - Assembler::Jump isNoInt = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister, - Assembler::TrustedImm32(Value::Integer_Type_Internal)); + Jump isNoInt = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister, + TrustedImm32(Value::Integer_Type_Internal)); convertIntToDouble(source, target); - Assembler::Jump intDone = _as->jump(); + Jump intDone = _as->jump(); // not an int, check if it's NOT a double: isNoInt.link(_as); -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - _as->rshift32(Assembler::TrustedImm32(Value::IsDoubleTag_Shift), Assembler::ScratchRegister); - Assembler::Jump isDbl = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister, - Assembler::TrustedImm32(0)); -#else - _as->and32(Assembler::TrustedImm32(Value::NotDouble_Mask), Assembler::ScratchRegister); - Assembler::Jump isDbl = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister, - Assembler::TrustedImm32(Value::NotDouble_Mask)); -#endif + Jump isDbl = _as->generateIsDoubleCheck(JITTargetPlatform::ScratchRegister); - generateRuntimeCall(target, toDouble, Assembler::PointerToValue(source)); - Assembler::Jump noDoubleDone = _as->jump(); + generateRuntimeCall(_as, target, toDouble, PointerToValue(source)); + Jump noDoubleDone = _as->jump(); // it is a double: isDbl.link(_as); - Assembler::Pointer addr2 = _as->loadAddress(Assembler::ScratchRegister, source); + Pointer addr2 = _as->loadAddress(JITTargetPlatform::ScratchRegister, source); IR::Temp *targetTemp = target->asTemp(); if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { -#if Q_PROCESSOR_WORDSIZE == 8 - _as->load64(addr2, Assembler::ScratchRegister); - _as->store64(Assembler::ScratchRegister, _as->loadAddress(Assembler::ReturnValueRegister, target)); -#else - _as->loadDouble(addr2, Assembler::FPGpr0); - _as->storeDouble(Assembler::FPGpr0, _as->loadAddress(Assembler::ReturnValueRegister, target)); -#endif + _as->memcopyValue(target, addr2, JITTargetPlatform::FPGpr0, JITTargetPlatform::ReturnValueRegister); } else { - _as->loadDouble(addr2, (Assembler::FPRegisterID) targetTemp->index); + _as->loadDouble(addr2, (FPRegisterID) targetTemp->index); } noDoubleDone.link(_as); @@ -953,7 +986,8 @@ void InstructionSelection::convertTypeToDouble(IR::Expr *source, IR::Expr *targe } } -void InstructionSelection::convertTypeToBool(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::convertTypeToBool(IR::Expr *source, IR::Expr *target) { IR::Temp *sourceTemp = source->asTemp(); switch (source->type) { @@ -965,16 +999,16 @@ void InstructionSelection::convertTypeToBool(IR::Expr *source, IR::Expr *target) // 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. - Assembler::FPRegisterID reg; + FPRegisterID reg; if (sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister) - reg = (Assembler::FPRegisterID) sourceTemp->index; + reg = (FPRegisterID) sourceTemp->index; else - reg = _as->toDoubleRegister(source, (Assembler::FPRegisterID) 1); - Assembler::Jump nonZero = _as->branchDoubleNonZero(reg, Assembler::FPGpr0); + reg = _as->toDoubleRegister(source, (FPRegisterID) 1); + Jump nonZero = _as->branchDoubleNonZero(reg, JITTargetPlatform::FPGpr0); // it's 0, so false: _as->storeBool(false, target); - Assembler::Jump done = _as->jump(); + Jump done = _as->jump(); // it's non-zero, so true: nonZero.link(_as); @@ -988,283 +1022,220 @@ void InstructionSelection::convertTypeToBool(IR::Expr *source, IR::Expr *target) _as->storeBool(false, target); break; case IR::StringType: - generateRuntimeCall(Assembler::ReturnValueRegister, toBoolean, - Assembler::PointerToValue(source)); - _as->storeBool(Assembler::ReturnValueRegister, target); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toBoolean, + PointerToValue(source)); + _as->storeBool(JITTargetPlatform::ReturnValueRegister, target); case IR::VarType: default: - Assembler::Pointer addr = _as->loadAddress(Assembler::ScratchRegister, source); - Assembler::Pointer tagAddr = addr; + Pointer addr = _as->loadAddress(JITTargetPlatform::ScratchRegister, source); + Pointer tagAddr = addr; tagAddr.offset += 4; - _as->load32(tagAddr, Assembler::ReturnValueRegister); + _as->load32(tagAddr, JITTargetPlatform::ReturnValueRegister); // checkif it's a bool: - Assembler::Jump notBool = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, - Assembler::TrustedImm32(Value::Boolean_Type_Internal)); - _as->load32(addr, Assembler::ReturnValueRegister); - Assembler::Jump boolDone = _as->jump(); + Jump notBool = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ReturnValueRegister, + TrustedImm32(Value::Boolean_Type_Internal)); + _as->load32(addr, JITTargetPlatform::ReturnValueRegister); + Jump boolDone = _as->jump(); // check if it's an int32: notBool.link(_as); - Assembler::Jump fallback = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, - Assembler::TrustedImm32(Value::Integer_Type_Internal)); - _as->load32(addr, Assembler::ReturnValueRegister); - Assembler::Jump isZero = _as->branch32(Assembler::Equal, Assembler::ReturnValueRegister, - Assembler::TrustedImm32(0)); - _as->move(Assembler::TrustedImm32(1), Assembler::ReturnValueRegister); - Assembler::Jump intDone = _as->jump(); + Jump fallback = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ReturnValueRegister, + TrustedImm32(Value::Integer_Type_Internal)); + _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(Assembler::ReturnValueRegister, toBoolean, - Assembler::PointerToValue(source)); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toBoolean, + PointerToValue(source)); isZero.link(_as); intDone.link(_as); boolDone.link(_as); - _as->storeBool(Assembler::ReturnValueRegister, target); + _as->storeBool(JITTargetPlatform::ReturnValueRegister, target); break; } } -void InstructionSelection::convertTypeToSInt32(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::convertTypeToSInt32(IR::Expr *source, IR::Expr *target) { switch (source->type) { case IR::VarType: { - -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - Assembler::Pointer addr = _as->loadAddress(Assembler::ScratchRegister, source); - _as->load64(addr, Assembler::ScratchRegister); - _as->move(Assembler::ScratchRegister, Assembler::ReturnValueRegister); - - // check if it's integer convertible - _as->urshift64(Assembler::TrustedImm32(QV4::Value::IsIntegerConvertible_Shift), Assembler::ScratchRegister); - Assembler::Jump isIntConvertible = _as->branch32(Assembler::Equal, Assembler::ScratchRegister, Assembler::TrustedImm32(3)); - - // nope, not integer convertible, so check for a double: - _as->urshift64(Assembler::TrustedImm32( - QV4::Value::IsDoubleTag_Shift - QV4::Value::IsIntegerConvertible_Shift), - Assembler::ScratchRegister); - Assembler::Jump fallback = _as->branch32(Assembler::GreaterThan, Assembler::ScratchRegister, Assembler::TrustedImm32(0)); - - // it's a double - _as->move(Assembler::TrustedImm64(QV4::Value::NaNEncodeMask), Assembler::ScratchRegister); - _as->xor64(Assembler::ScratchRegister, Assembler::ReturnValueRegister); - _as->move64ToDouble(Assembler::ReturnValueRegister, Assembler::FPGpr0); - Assembler::Jump success = - _as->branchTruncateDoubleToInt32(Assembler::FPGpr0, Assembler::ReturnValueRegister, - Assembler::BranchIfTruncateSuccessful); - - // not an int: - fallback.link(_as); - generateRuntimeCall(Assembler::ReturnValueRegister, toInt, - _as->loadAddress(Assembler::ScratchRegister, source)); - - isIntConvertible.link(_as); - success.link(_as); - IR::Temp *targetTemp = target->asTemp(); - if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { - Assembler::Pointer targetAddr = _as->loadAddress(Assembler::ScratchRegister, target); - _as->store32(Assembler::ReturnValueRegister, targetAddr); - targetAddr.offset += 4; - _as->store32(Assembler::TrustedImm32(Value::Integer_Type_Internal), targetAddr); - } else { - _as->storeInt32(Assembler::ReturnValueRegister, target); - } -#else - // load the tag: - Assembler::Pointer addr = _as->loadAddress(Assembler::ScratchRegister, source); - Assembler::Pointer tagAddr = addr; - tagAddr.offset += 4; - _as->load32(tagAddr, Assembler::ReturnValueRegister); - - // check if it's an int32: - Assembler::Jump fallback = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, - Assembler::TrustedImm32(Value::Integer_Type_Internal)); - IR::Temp *targetTemp = target->asTemp(); - if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { - _as->load32(addr, Assembler::ReturnValueRegister); - Assembler::Pointer targetAddr = _as->loadAddress(Assembler::ScratchRegister, target); - _as->store32(Assembler::ReturnValueRegister, targetAddr); - targetAddr.offset += 4; - _as->store32(Assembler::TrustedImm32(Value::Integer_Type_Internal), targetAddr); - } else { - _as->load32(addr, (Assembler::RegisterID) targetTemp->index); - } - Assembler::Jump intDone = _as->jump(); - - // not an int: - fallback.link(_as); - generateRuntimeCall(Assembler::ReturnValueRegister, toInt, - _as->loadAddress(Assembler::ScratchRegister, source)); - _as->storeInt32(Assembler::ReturnValueRegister, target); - - intDone.link(_as); -#endif - + JITAssembler::RegisterSizeDependentOps::convertVarToSInt32(_as, source, target); } break; case IR::DoubleType: { - Assembler::Jump success = + Jump success = _as->branchTruncateDoubleToInt32(_as->toDoubleRegister(source), - Assembler::ReturnValueRegister, - Assembler::BranchIfTruncateSuccessful); - generateRuntimeCall(Assembler::ReturnValueRegister, doubleToInt, - Assembler::PointerToValue(source)); + JITTargetPlatform::ReturnValueRegister, + BranchTruncateType::BranchIfTruncateSuccessful); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, doubleToInt, + PointerToValue(source)); success.link(_as); - _as->storeInt32(Assembler::ReturnValueRegister, target); + _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target); } break; case IR::UInt32Type: - _as->storeInt32(_as->toUInt32Register(source, Assembler::ReturnValueRegister), target); + _as->storeInt32(_as->toUInt32Register(source, JITTargetPlatform::ReturnValueRegister), target); break; case IR::NullType: case IR::UndefinedType: - _as->move(Assembler::TrustedImm32(0), Assembler::ReturnValueRegister); - _as->storeInt32(Assembler::ReturnValueRegister, target); + _as->move(TrustedImm32(0), JITTargetPlatform::ReturnValueRegister); + _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target); break; case IR::BoolType: - _as->storeInt32(_as->toInt32Register(source, Assembler::ReturnValueRegister), target); + _as->storeInt32(_as->toInt32Register(source, JITTargetPlatform::ReturnValueRegister), target); break; case IR::StringType: default: - generateRuntimeCall(Assembler::ReturnValueRegister, toInt, - _as->loadAddress(Assembler::ScratchRegister, source)); - _as->storeInt32(Assembler::ReturnValueRegister, target); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toInt, + _as->loadAddress(JITTargetPlatform::ScratchRegister, source)); + _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target); break; } // switch (source->type) } -void InstructionSelection::convertTypeToUInt32(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::convertTypeToUInt32(IR::Expr *source, IR::Expr *target) { switch (source->type) { case IR::VarType: { // load the tag: - Assembler::Pointer tagAddr = _as->loadAddress(Assembler::ScratchRegister, source); + Pointer tagAddr = _as->loadAddress(JITTargetPlatform::ScratchRegister, source); tagAddr.offset += 4; - _as->load32(tagAddr, Assembler::ScratchRegister); + _as->load32(tagAddr, JITTargetPlatform::ScratchRegister); // check if it's an int32: - Assembler::Jump isNoInt = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister, - Assembler::TrustedImm32(Value::Integer_Type_Internal)); - Assembler::Pointer addr = _as->loadAddress(Assembler::ScratchRegister, source); - _as->storeUInt32(_as->toInt32Register(addr, Assembler::ScratchRegister), target); - Assembler::Jump intDone = _as->jump(); + Jump isNoInt = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister, + TrustedImm32(Value::Integer_Type_Internal)); + Pointer addr = _as->loadAddress(JITTargetPlatform::ScratchRegister, source); + _as->storeUInt32(_as->toInt32Register(addr, JITTargetPlatform::ScratchRegister), target); + Jump intDone = _as->jump(); // not an int: isNoInt.link(_as); - generateRuntimeCall(Assembler::ReturnValueRegister, toUInt, - _as->loadAddress(Assembler::ScratchRegister, source)); - _as->storeInt32(Assembler::ReturnValueRegister, target); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toUInt, + _as->loadAddress(JITTargetPlatform::ScratchRegister, source)); + _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target); intDone.link(_as); } break; case IR::DoubleType: { - Assembler::FPRegisterID reg = _as->toDoubleRegister(source); - Assembler::Jump success = - _as->branchTruncateDoubleToUint32(reg, Assembler::ReturnValueRegister, - Assembler::BranchIfTruncateSuccessful); - generateRuntimeCall(Assembler::ReturnValueRegister, doubleToUInt, - Assembler::PointerToValue(source)); + 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(Assembler::ReturnValueRegister, target); + _as->storeUInt32(JITTargetPlatform::ReturnValueRegister, target); } break; case IR::NullType: case IR::UndefinedType: - _as->move(Assembler::TrustedImm32(0), Assembler::ReturnValueRegister); - _as->storeUInt32(Assembler::ReturnValueRegister, target); + _as->move(TrustedImm32(0), JITTargetPlatform::ReturnValueRegister); + _as->storeUInt32(JITTargetPlatform::ReturnValueRegister, target); break; case IR::StringType: - generateRuntimeCall(Assembler::ReturnValueRegister, toUInt, - Assembler::PointerToValue(source)); - _as->storeUInt32(Assembler::ReturnValueRegister, target); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toUInt, + PointerToValue(source)); + _as->storeUInt32(JITTargetPlatform::ReturnValueRegister, target); break; case IR::SInt32Type: case IR::BoolType: - _as->storeUInt32(_as->toInt32Register(source, Assembler::ReturnValueRegister), target); + _as->storeUInt32(_as->toInt32Register(source, JITTargetPlatform::ReturnValueRegister), target); break; default: break; } // switch (source->type) } -void InstructionSelection::constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Expr *result) +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(result, constructGlobalLookup, - Assembler::EngineRegister, - Assembler::TrustedImm32(index), baseAddressForCallData()); + generateRuntimeCall(_as, result, constructGlobalLookup, + JITTargetPlatform::EngineRegister, + TrustedImm32(index), baseAddressForCallData()); return; } - generateRuntimeCall(result, constructActivationProperty, - Assembler::EngineRegister, - Assembler::StringToIndex(*func->id), + generateRuntimeCall(_as, result, constructActivationProperty, + JITTargetPlatform::EngineRegister, + StringToIndex(*func->id), baseAddressForCallData()); } -void InstructionSelection::constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) +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(result, constructPropertyLookup, - Assembler::EngineRegister, - Assembler::TrustedImm32(index), + generateRuntimeCall(_as, result, constructPropertyLookup, + JITTargetPlatform::EngineRegister, + TrustedImm32(index), baseAddressForCallData()); return; } - generateRuntimeCall(result, constructProperty, Assembler::EngineRegister, - Assembler::StringToIndex(name), + generateRuntimeCall(_as, result, constructProperty, JITTargetPlatform::EngineRegister, + StringToIndex(name), baseAddressForCallData()); } -void InstructionSelection::constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) { Q_ASSERT(value != 0); prepareCallData(args, 0); - generateRuntimeCall(result, constructValue, - Assembler::EngineRegister, - Assembler::Reference(value), + generateRuntimeCall(_as, result, constructValue, + JITTargetPlatform::EngineRegister, + Reference(value), baseAddressForCallData()); } -void InstructionSelection::visitJump(IR::Jump *s) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::visitJump(IR::Jump *s) { if (!_removableJumps.at(_block->index())) _as->jumpToBlock(_block, s->target); } -void InstructionSelection::visitCJump(IR::CJump *s) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::visitCJump(IR::CJump *s) { IR::Temp *t = s->cond->asTemp(); if (t || s->cond->asArgLocal()) { - Assembler::RegisterID reg; + RegisterID reg; if (t && t->kind == IR::Temp::PhysicalRegister) { Q_ASSERT(t->type == IR::BoolType); - reg = (Assembler::RegisterID) t->index; + reg = (RegisterID) t->index; } else if (t && t->kind == IR::Temp::StackSlot && t->type == IR::BoolType) { - reg = Assembler::ReturnValueRegister; + reg = JITTargetPlatform::ReturnValueRegister; _as->toInt32Register(t, reg); } else { - Address temp = _as->loadAddress(Assembler::ScratchRegister, s->cond); + Address temp = _as->loadAddress(JITTargetPlatform::ScratchRegister, s->cond); Address tag = temp; tag.offset += QV4::Value::tagOffset(); - Assembler::Jump booleanConversion = _as->branch32(Assembler::NotEqual, tag, Assembler::TrustedImm32(QV4::Value::Boolean_Type_Internal)); + Jump booleanConversion = _as->branch32(RelationalCondition::NotEqual, tag, TrustedImm32(QV4::Value::Boolean_Type_Internal)); Address data = temp; data.offset += QV4::Value::valueOffset(); - _as->load32(data, Assembler::ReturnValueRegister); - Assembler::Jump testBoolean = _as->jump(); + _as->load32(data, JITTargetPlatform::ReturnValueRegister); + Jump testBoolean = _as->jump(); booleanConversion.link(_as); - reg = Assembler::ReturnValueRegister; - generateRuntimeCall(reg, toBoolean, Assembler::Reference(s->cond)); + reg = JITTargetPlatform::ReturnValueRegister; + generateRuntimeCall(_as, reg, toBoolean, Reference(s->cond)); testBoolean.link(_as); } @@ -1274,9 +1245,9 @@ void InstructionSelection::visitCJump(IR::CJump *s) } else if (IR::Const *c = s->cond->asConst()) { // TODO: SSA optimization for constant condition evaluation should remove this. // See also visitCJump() in RegAllocInfo. - generateRuntimeCall(Assembler::ReturnValueRegister, toBoolean, - Assembler::PointerToValue(c)); - _as->generateCJumpOnNonZero(Assembler::ReturnValueRegister, _block, s->iftrue, s->iffalse); + 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 @@ -1296,8 +1267,8 @@ void InstructionSelection::visitCJump(IR::CJump *s) return; } - RuntimeCall op; - RuntimeCall opContext; + typename JITAssembler::RuntimeCall op; + typename JITAssembler::RuntimeCall opContext; const char *opName = 0; bool needsExceptionCheck; switch (b->op) { @@ -1321,165 +1292,30 @@ void InstructionSelection::visitCJump(IR::CJump *s) // elimination (which isn't there either) would remove the whole else block. if (opContext.isValid()) _as->generateFunctionCallImp(needsExceptionCheck, - Assembler::ReturnValueRegister, opName, opContext, - Assembler::EngineRegister, - Assembler::PointerToValue(b->left), - Assembler::PointerToValue(b->right)); + JITTargetPlatform::ReturnValueRegister, opName, opContext, + JITTargetPlatform::EngineRegister, + PointerToValue(b->left), + PointerToValue(b->right)); else _as->generateFunctionCallImp(needsExceptionCheck, - Assembler::ReturnValueRegister, opName, op, - Assembler::PointerToValue(b->left), - Assembler::PointerToValue(b->right)); + JITTargetPlatform::ReturnValueRegister, opName, op, + PointerToValue(b->left), + PointerToValue(b->right)); - _as->generateCJumpOnNonZero(Assembler::ReturnValueRegister, _block, s->iftrue, s->iffalse); + _as->generateCJumpOnNonZero(JITTargetPlatform::ReturnValueRegister, _block, s->iftrue, s->iffalse); return; } Q_UNREACHABLE(); } -void InstructionSelection::visitRet(IR::Ret *s) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::visitRet(IR::Ret *s) { - 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()) { -#if CPU(X86) || CPU(ARM) || CPU(MIPS) - -# if CPU(X86) - Assembler::RegisterID lowReg = JSC::X86Registers::eax; - Assembler::RegisterID highReg = JSC::X86Registers::edx; -# elif CPU(MIPS) - Assembler::RegisterID lowReg = JSC::MIPSRegisters::v0; - Assembler::RegisterID highReg = JSC::MIPSRegisters::v1; -# else // CPU(ARM) - Assembler::RegisterID lowReg = JSC::ARMRegisters::r0; - Assembler::RegisterID highReg = JSC::ARMRegisters::r1; -# endif - - if (t->kind == IR::Temp::PhysicalRegister) { - switch (t->type) { - case IR::DoubleType: - _as->moveDoubleToInts((Assembler::FPRegisterID) t->index, lowReg, highReg); - break; - case IR::UInt32Type: { - Assembler::RegisterID srcReg = (Assembler::RegisterID) t->index; - Assembler::Jump intRange = _as->branch32(Assembler::GreaterThanOrEqual, srcReg, Assembler::TrustedImm32(0)); - _as->convertUInt32ToDouble(srcReg, Assembler::FPGpr0, Assembler::ReturnValueRegister); - _as->moveDoubleToInts(Assembler::FPGpr0, lowReg, highReg); - Assembler::Jump done = _as->jump(); - intRange.link(_as); - _as->move(srcReg, lowReg); - _as->move(Assembler::TrustedImm32(QV4::Value::Integer_Type_Internal), highReg); - done.link(_as); - } break; - case IR::SInt32Type: - _as->move((Assembler::RegisterID) t->index, lowReg); - _as->move(Assembler::TrustedImm32(QV4::Value::Integer_Type_Internal), highReg); - break; - case IR::BoolType: - _as->move((Assembler::RegisterID) t->index, lowReg); - _as->move(Assembler::TrustedImm32(QV4::Value::Boolean_Type_Internal), highReg); - break; - default: - Q_UNREACHABLE(); - } - } else { - Pointer addr = _as->loadAddress(Assembler::ScratchRegister, t); - _as->load32(addr, lowReg); - addr.offset += 4; - _as->load32(addr, highReg); - } -#else - if (t->kind == IR::Temp::PhysicalRegister) { - if (t->type == IR::DoubleType) { - _as->moveDoubleTo64((Assembler::FPRegisterID) t->index, - Assembler::ReturnValueRegister); - _as->move(Assembler::TrustedImm64(QV4::Value::NaNEncodeMask), - Assembler::ScratchRegister); - _as->xor64(Assembler::ScratchRegister, Assembler::ReturnValueRegister); - } else if (t->type == IR::UInt32Type) { - Assembler::RegisterID srcReg = (Assembler::RegisterID) t->index; - Assembler::Jump intRange = _as->branch32(Assembler::GreaterThanOrEqual, srcReg, Assembler::TrustedImm32(0)); - _as->convertUInt32ToDouble(srcReg, Assembler::FPGpr0, Assembler::ReturnValueRegister); - _as->moveDoubleTo64(Assembler::FPGpr0, Assembler::ReturnValueRegister); - _as->move(Assembler::TrustedImm64(QV4::Value::NaNEncodeMask), Assembler::ScratchRegister); - _as->xor64(Assembler::ScratchRegister, Assembler::ReturnValueRegister); - Assembler::Jump done = _as->jump(); - intRange.link(_as); - _as->zeroExtend32ToPtr(srcReg, Assembler::ReturnValueRegister); - quint64 tag = QV4::Value::Integer_Type_Internal; - _as->or64(Assembler::TrustedImm64(tag << 32), - Assembler::ReturnValueRegister); - done.link(_as); - } else { - _as->zeroExtend32ToPtr((Assembler::RegisterID) t->index, Assembler::ReturnValueRegister); - quint64 tag; - switch (t->type) { - case IR::SInt32Type: - tag = QV4::Value::Integer_Type_Internal; - break; - case IR::BoolType: - tag = QV4::Value::Boolean_Type_Internal; - break; - default: - tag = 31337; // bogus value - Q_UNREACHABLE(); - } - _as->or64(Assembler::TrustedImm64(tag << 32), - Assembler::ReturnValueRegister); - } - } else { - _as->copyValue(Assembler::ReturnValueRegister, t); - } -#endif - } else if (IR::Const *c = s->expr->asConst()) { - QV4::Primitive retVal = convertToValue(c); -#if CPU(X86) - _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::X86Registers::eax); - _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::X86Registers::edx); -#elif CPU(ARM) - _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::ARMRegisters::r0); - _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::ARMRegisters::r1); -#elif CPU(MIPS) - _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::MIPSRegisters::v0); - _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::MIPSRegisters::v1); -#else - _as->move(Assembler::TrustedImm64(retVal.rawValue()), Assembler::ReturnValueRegister); -#endif - } else { - Q_UNREACHABLE(); - Q_UNUSED(s); - } - - Assembler::Label leaveStackFrame = _as->label(); - - const int locals = _as->stackLayout().calculateJSStackFrameSize(); - _as->subPtr(Assembler::TrustedImm32(sizeof(QV4::Value)*locals), Assembler::LocalsRegister); - _as->loadPtr(Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), Assembler::ScratchRegister); - _as->loadPtr(Address(Assembler::ScratchRegister, qOffsetOf(ExecutionContext::Data, engine)), Assembler::ScratchRegister); - _as->storePtr(Assembler::LocalsRegister, Address(Assembler::ScratchRegister, qOffsetOf(ExecutionEngine, jsStackTop))); - - _as->leaveStandardStackFrame(regularRegistersToSave, fpRegistersToSave); - _as->ret(); - - _as->exceptionReturnLabel = _as->label(); - QV4::Primitive retVal = Primitive::undefinedValue(); -#if CPU(X86) - _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::X86Registers::eax); - _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::X86Registers::edx); -#elif CPU(ARM) - _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::ARMRegisters::r0); - _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::ARMRegisters::r1); -#elif CPU(MIPS) - _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::MIPSRegisters::v0); - _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::MIPSRegisters::v1); -#else - _as->move(Assembler::TrustedImm64(retVal.rawValue()), Assembler::ReturnValueRegister); -#endif - _as->jump(leaveStackFrame); + _as->returnFromFunction(s, regularRegistersToSave, fpRegistersToSave); } -int InstructionSelection::prepareVariableArguments(IR::ExprList* args) +template <typename JITAssembler> +int InstructionSelection<JITAssembler>::prepareVariableArguments(IR::ExprList* args) { int argc = 0; for (IR::ExprList *it = args; it; it = it->next) { @@ -1492,7 +1328,7 @@ int InstructionSelection::prepareVariableArguments(IR::ExprList* args) Q_ASSERT(arg != 0); Pointer dst(_as->stackLayout().argumentAddressForCall(i)); if (arg->asTemp() && arg->asTemp()->kind != IR::Temp::PhysicalRegister) - _as->memcopyValue(dst, arg->asTemp(), Assembler::ScratchRegister); + _as->memcopyValue(dst, arg->asTemp(), JITTargetPlatform::ScratchRegister); else _as->copyValue(dst, arg); } @@ -1500,7 +1336,8 @@ int InstructionSelection::prepareVariableArguments(IR::ExprList* args) return argc; } -int InstructionSelection::prepareCallData(IR::ExprList* args, IR::Expr *thisObject) +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) { @@ -1508,9 +1345,9 @@ int InstructionSelection::prepareCallData(IR::ExprList* args, IR::Expr *thisObje } Pointer p = _as->stackLayout().callDataAddress(qOffsetOf(CallData, tag)); - _as->store32(Assembler::TrustedImm32(QV4::Value::Integer_Type_Internal), p); + _as->store32(TrustedImm32(QV4::Value::Integer_Type_Internal), p); p = _as->stackLayout().callDataAddress(qOffsetOf(CallData, argc)); - _as->store32(Assembler::TrustedImm32(argc), p); + _as->store32(TrustedImm32(argc), p); p = _as->stackLayout().callDataAddress(qOffsetOf(CallData, thisObject)); if (!thisObject) _as->storeValue(QV4::Primitive::undefinedValue(), p); @@ -1523,19 +1360,20 @@ int InstructionSelection::prepareCallData(IR::ExprList* args, IR::Expr *thisObje Q_ASSERT(arg != 0); Pointer dst(_as->stackLayout().argumentAddressForCall(i)); if (arg->asTemp() && arg->asTemp()->kind != IR::Temp::PhysicalRegister) - _as->memcopyValue(dst, arg->asTemp(), Assembler::ScratchRegister); + _as->memcopyValue(dst, arg->asTemp(), JITTargetPlatform::ScratchRegister); else _as->copyValue(dst, arg); } return argc; } -void InstructionSelection::calculateRegistersToSave(const RegisterInformation &used) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::calculateRegistersToSave(const RegisterInformation &used) { regularRegistersToSave.clear(); fpRegistersToSave.clear(); - for (const RegisterInfo &ri : Assembler::getRegisterInfo()) { + for (const RegisterInfo &ri : JITTargetPlatform::getRegisterInfo()) { #if defined(RESTORE_EBX_ON_CALL) if (ri.isRegularRegister() && ri.reg<JSC::X86Registers::RegisterID>() == JSC::X86Registers::ebx) { regularRegistersToSave.append(ri); @@ -1564,35 +1402,38 @@ bool operator==(const Primitive &v1, const Primitive &v2) } // QV4 namespace QT_END_NAMESPACE -bool InstructionSelection::visitCJumpDouble(IR::AluOp op, IR::Expr *left, IR::Expr *right, +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) { - Assembler::Jump target = _as->branchDouble(true, op, left, right); + Jump target = _as->branchDouble(true, op, left, right); _as->addPatch(iffalse, target); } else { - Assembler::Jump target = _as->branchDouble(false, op, left, right); + Jump target = _as->branchDouble(false, op, left, right); _as->addPatch(iftrue, target); _as->jumpToBlock(_block, iffalse); } return true; } -bool InstructionSelection::visitCJumpSInt32(IR::AluOp op, IR::Expr *left, IR::Expr *right, +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) { - Assembler::Jump target = _as->branchInt32(true, op, left, right); + Jump target = _as->branchInt32(true, op, left, right); _as->addPatch(iffalse, target); } else { - Assembler::Jump target = _as->branchInt32(false, op, left, right); + Jump target = _as->branchInt32(false, op, left, right); _as->addPatch(iftrue, target); _as->jumpToBlock(_block, iffalse); } return true; } -void InstructionSelection::visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *trueBlock, +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); @@ -1607,15 +1448,16 @@ void InstructionSelection::visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *tr IR::Expr *left = binop->left; IR::Expr *right = binop->right; - generateRuntimeCall(Assembler::ReturnValueRegister, compareStrictEqual, - Assembler::PointerToValue(left), Assembler::PointerToValue(right)); - _as->generateCJumpOnCompare(binop->op == IR::OpStrictEqual ? Assembler::NotEqual : Assembler::Equal, - Assembler::ReturnValueRegister, Assembler::TrustedImm32(0), + 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. -bool InstructionSelection::visitCJumpStrictNull(IR::Binop *binop, +template <typename JITAssembler> +bool InstructionSelection<JITAssembler>::visitCJumpStrictNull(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) { @@ -1640,19 +1482,20 @@ bool InstructionSelection::visitCJumpStrictNull(IR::Binop *binop, return true; } - Assembler::Pointer tagAddr = _as->loadAddress(Assembler::ScratchRegister, varSrc); + Pointer tagAddr = _as->loadAddress(JITTargetPlatform::ScratchRegister, varSrc); tagAddr.offset += 4; - const Assembler::RegisterID tagReg = Assembler::ScratchRegister; + const RegisterID tagReg = JITTargetPlatform::ScratchRegister; _as->load32(tagAddr, tagReg); - Assembler::RelationalCondition cond = binop->op == IR::OpStrictEqual ? Assembler::Equal - : Assembler::NotEqual; - const Assembler::TrustedImm32 tag(QV4::Value::Null_Type_Internal); + RelationalCondition cond = binop->op == IR::OpStrictEqual ? RelationalCondition::Equal + : RelationalCondition::NotEqual; + const TrustedImm32 tag(QV4::Value::Null_Type_Internal); _as->generateCJumpOnCompare(cond, tagReg, tag, _block, trueBlock, falseBlock); return true; } -bool InstructionSelection::visitCJumpStrictUndefined(IR::Binop *binop, +template <typename JITAssembler> +bool InstructionSelection<JITAssembler>::visitCJumpStrictUndefined(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) { @@ -1677,28 +1520,15 @@ bool InstructionSelection::visitCJumpStrictUndefined(IR::Binop *binop, return true; } - Assembler::RelationalCondition cond = binop->op == IR::OpStrictEqual ? Assembler::Equal - : Assembler::NotEqual; - const Assembler::RegisterID tagReg = Assembler::ReturnValueRegister; -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - Assembler::Pointer addr = _as->loadAddress(Assembler::ScratchRegister, varSrc); - _as->load64(addr, tagReg); - const Assembler::TrustedImm64 tag(0); -#else // !QV4_USE_64_BIT_VALUE_ENCODING - Assembler::Pointer tagAddr = _as->loadAddress(Assembler::ScratchRegister, varSrc); - _as->load32(tagAddr, tagReg); - Assembler::Jump j = _as->branch32(Assembler::invert(cond), tagReg, Assembler::TrustedImm32(0)); - _as->addPatch(falseBlock, j); - - tagAddr.offset += 4; - _as->load32(tagAddr, tagReg); - const Assembler::TrustedImm32 tag(QV4::Value::Managed_Type_Internal); -#endif - _as->generateCJumpOnCompare(cond, tagReg, tag, _block, trueBlock, falseBlock); + 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; } -bool InstructionSelection::visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock *trueBlock, +template <typename JITAssembler> +bool InstructionSelection<JITAssembler>::visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) { IR::Expr *boolSrc = 0, *otherSrc = 0; @@ -1712,13 +1542,20 @@ bool InstructionSelection::visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock // 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; + } - Assembler::RelationalCondition cond = binop->op == IR::OpStrictEqual ? Assembler::Equal - : Assembler::NotEqual; + RelationalCondition cond = binop->op == IR::OpStrictEqual ? RelationalCondition::Equal + : RelationalCondition::NotEqual; if (otherSrc->type == IR::BoolType) { // both are boolean - Assembler::RegisterID one = _as->toBoolRegister(boolSrc, Assembler::ReturnValueRegister); - Assembler::RegisterID two = _as->toBoolRegister(otherSrc, Assembler::ScratchRegister); + RegisterID one = _as->toBoolRegister(boolSrc, JITTargetPlatform::ReturnValueRegister); + RegisterID two = _as->toBoolRegister(otherSrc, JITTargetPlatform::ScratchRegister); _as->generateCJumpOnCompare(cond, one, two, _block, trueBlock, falseBlock); return true; } @@ -1728,13 +1565,13 @@ bool InstructionSelection::visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock return true; } - Assembler::Pointer otherAddr = _as->loadAddress(Assembler::ReturnValueRegister, otherSrc); + Pointer otherAddr = _as->loadAddress(JITTargetPlatform::ReturnValueRegister, otherSrc); otherAddr.offset += 4; // tag address // check if the tag of the var operand is indicates 'boolean' - _as->load32(otherAddr, Assembler::ScratchRegister); - Assembler::Jump noBool = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister, - Assembler::TrustedImm32(QV4::Value::Boolean_Type_Internal)); + _as->load32(otherAddr, JITTargetPlatform::ScratchRegister); + Jump noBool = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister, + TrustedImm32(QV4::Value::Boolean_Type_Internal)); if (binop->op == IR::OpStrictEqual) _as->addPatch(falseBlock, noBool); else @@ -1742,14 +1579,15 @@ bool InstructionSelection::visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock // ok, both are boolean, so let's load them and compare them. otherAddr.offset -= 4; // int_32 address - _as->load32(otherAddr, Assembler::ReturnValueRegister); - Assembler::RegisterID boolReg = _as->toBoolRegister(boolSrc, Assembler::ScratchRegister); - _as->generateCJumpOnCompare(cond, boolReg, Assembler::ReturnValueRegister, _block, trueBlock, + _as->load32(otherAddr, JITTargetPlatform::ReturnValueRegister); + RegisterID boolReg = _as->toBoolRegister(boolSrc, JITTargetPlatform::ScratchRegister); + _as->generateCJumpOnCompare(cond, boolReg, JITTargetPlatform::ReturnValueRegister, _block, trueBlock, falseBlock); return true; } -bool InstructionSelection::visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Binop *binop, +template <typename JITAssembler> +bool InstructionSelection<JITAssembler>::visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) { @@ -1776,18 +1614,18 @@ bool InstructionSelection::visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Bin return true; } - Assembler::Pointer tagAddr = _as->loadAddress(Assembler::ScratchRegister, varSrc); + Pointer tagAddr = _as->loadAddress(JITTargetPlatform::ScratchRegister, varSrc); tagAddr.offset += 4; - const Assembler::RegisterID tagReg = Assembler::ReturnValueRegister; + const RegisterID tagReg = JITTargetPlatform::ReturnValueRegister; _as->load32(tagAddr, tagReg); if (binop->op == IR::OpNotEqual) qSwap(trueBlock, falseBlock); - Assembler::Jump isNull = _as->branch32(Assembler::Equal, tagReg, Assembler::TrustedImm32(int(QV4::Value::Null_Type_Internal))); - Assembler::Jump isNotUndefinedTag = _as->branch32(Assembler::NotEqual, tagReg, Assembler::TrustedImm32(int(QV4::Value::Managed_Type_Internal))); + Jump isNull = _as->branch32(RelationalCondition::Equal, tagReg, TrustedImm32(int(QV4::Value::Null_Type_Internal))); + Jump isNotUndefinedTag = _as->branch32(RelationalCondition::NotEqual, tagReg, TrustedImm32(int(QV4::Value::Managed_Type_Internal))); tagAddr.offset -= 4; _as->load32(tagAddr, tagReg); - Assembler::Jump isNotUndefinedValue = _as->branch32(Assembler::NotEqual, tagReg, Assembler::TrustedImm32(0)); + Jump isNotUndefinedValue = _as->branch32(RelationalCondition::NotEqual, tagReg, TrustedImm32(0)); _as->addPatch(trueBlock, isNull); _as->addPatch(falseBlock, isNotUndefinedTag); _as->addPatch(falseBlock, isNotUndefinedValue); @@ -1797,7 +1635,8 @@ bool InstructionSelection::visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Bin } -void InstructionSelection::visitCJumpEqual(IR::Binop *binop, IR::BasicBlock *trueBlock, +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); @@ -1808,18 +1647,54 @@ void InstructionSelection::visitCJumpEqual(IR::Binop *binop, IR::BasicBlock *tru IR::Expr *left = binop->left; IR::Expr *right = binop->right; - generateRuntimeCall(Assembler::ReturnValueRegister, compareEqual, - Assembler::PointerToValue(left), Assembler::PointerToValue(right)); - _as->generateCJumpOnCompare(binop->op == IR::OpEqual ? Assembler::NotEqual : Assembler::Equal, - Assembler::ReturnValueRegister, Assembler::TrustedImm32(0), + 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); } -QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory::createUnitForLoading() +template <typename JITAssembler> +QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory<JITAssembler>::createUnitForLoading() { QQmlRefPointer<CompiledData::CompilationUnit> result; result.adopt(new JIT::CompilationUnit); return result; } +QT_BEGIN_NAMESPACE +namespace QV4 { namespace JIT { +template class Q_QML_EXPORT InstructionSelection<>; +template class Q_QML_EXPORT ISelFactory<>; +#if defined(V4_BOOTSTRAP) && CPU(X86_64) + +Q_QML_EXPORT QV4::EvalISelFactory *createISelForArchitecture(const QString &architecture) +{ + using ARMv7CrossAssembler = QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>; + + if (architecture == QLatin1String("armv7")) + return new ISelFactory<ARMv7CrossAssembler>; + + QString hostArch; +#if CPU(ARM_THUMB2) + hostArch = QStringLiteral("armv7"); +#elif CPU(ARM64) + hostArch = QStringLiteral("armv8"); +#elif CPU(MIPS) + hostArch = QStringLiteral("mips"); +#elif CPU(X86) + hostArch = QStringLiteral("x86"); +#elif CPU(X86_64) + hostArch = QStringLiteral("x86_64"); +#endif + if (!hostArch.isEmpty() && architecture == hostArch) + return new ISelFactory<>; + + return nullptr; +} + +#endif +} } +QT_END_NAMESPACE + #endif // ENABLE(ASSEMBLER) diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h index 012745c5f2..6ae50c3260 100644 --- a/src/qml/jit/qv4isel_masm_p.h +++ b/src/qml/jit/qv4isel_masm_p.h @@ -72,6 +72,7 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace JIT { +template <typename JITAssembler = Assembler<DefaultAssemblerTargetConfiguration>> class Q_QML_EXPORT InstructionSelection: protected IR::IRDecoder, public EvalInstructionSelection @@ -136,8 +137,23 @@ protected: 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; - typedef Assembler::Address Address; - typedef Assembler::Pointer Pointer; + 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; #if !defined(ARGUMENTS_IN_REGISTERS) Address addressForArgument(int index) const @@ -145,7 +161,7 @@ protected: // 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(Assembler::FramePointerRegister, (index + 2) * sizeof(void*)); + return Address(JITTargetPlatform::FramePointerRegister, (index + 2) * sizeof(void*)); } #endif @@ -192,61 +208,55 @@ private: if (targetTemp->kind == IR::Temp::PhysicalRegister) { if (IR::Temp *sourceTemp = source->asTemp()) { if (sourceTemp->kind == IR::Temp::PhysicalRegister) { - _as->convertInt32ToDouble((Assembler::RegisterID) sourceTemp->index, - (Assembler::FPRegisterID) targetTemp->index); + _as->convertInt32ToDouble((RegisterID) sourceTemp->index, + (FPRegisterID) targetTemp->index); } else { - _as->convertInt32ToDouble(_as->loadAddress(Assembler::ReturnValueRegister, sourceTemp), - (Assembler::FPRegisterID) targetTemp->index); + _as->convertInt32ToDouble(_as->loadAddress(JITTargetPlatform::ReturnValueRegister, sourceTemp), + (FPRegisterID) targetTemp->index); } } else { - _as->convertInt32ToDouble(_as->toInt32Register(source, Assembler::ScratchRegister), - (Assembler::FPRegisterID) targetTemp->index); + _as->convertInt32ToDouble(_as->toInt32Register(source, JITTargetPlatform::ScratchRegister), + (FPRegisterID) targetTemp->index); } return; } } - _as->convertInt32ToDouble(_as->toInt32Register(source, Assembler::ScratchRegister), - Assembler::FPGpr0); - _as->storeDouble(Assembler::FPGpr0, _as->loadAddress(Assembler::ReturnValueRegister, target)); + _as->convertInt32ToDouble(_as->toInt32Register(source, JITTargetPlatform::ScratchRegister), + JITTargetPlatform::FPGpr0); + _as->storeDouble(JITTargetPlatform::FPGpr0, _as->loadAddress(JITTargetPlatform::ReturnValueRegister, target)); } void convertUIntToDouble(IR::Expr *source, IR::Expr *target) { - Assembler::RegisterID tmpReg = Assembler::ScratchRegister; - Assembler::RegisterID reg = _as->toInt32Register(source, tmpReg); + 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, (Assembler::FPRegisterID) targetTemp->index, tmpReg); + _as->convertUInt32ToDouble(reg, (FPRegisterID) targetTemp->index, tmpReg); return; } } _as->convertUInt32ToDouble(_as->toUInt32Register(source, tmpReg), - Assembler::FPGpr0, tmpReg); - _as->storeDouble(Assembler::FPGpr0, _as->loadAddress(tmpReg, target)); + JITTargetPlatform::FPGpr0, tmpReg); + _as->storeDouble(JITTargetPlatform::FPGpr0, _as->loadAddress(tmpReg, target)); } void convertIntToBool(IR::Expr *source, IR::Expr *target) { - Assembler::RegisterID reg = Assembler::ScratchRegister; + RegisterID reg = JITTargetPlatform::ScratchRegister; if (IR::Temp *targetTemp = target->asTemp()) if (targetTemp->kind == IR::Temp::PhysicalRegister) - reg = (Assembler::RegisterID) targetTemp->index; + reg = (RegisterID) targetTemp->index; _as->move(_as->toInt32Register(source, reg), reg); - _as->compare32(Assembler::NotEqual, reg, Assembler::TrustedImm32(0), reg); + _as->compare32(RelationalCondition::NotEqual, reg, TrustedImm32(0), reg); _as->storeBool(reg, target); } - #define isel_stringIfyx(s) #s - #define isel_stringIfy(s) isel_stringIfyx(s) - - #define generateRuntimeCall(t, function, ...) \ - _as->generateFunctionCallImp(Runtime::Method_##function##_NeedsExceptionCheck, t, "Runtime::" isel_stringIfy(function), RuntimeCall(qOffsetOf(QV4::Runtime, function)), __VA_ARGS__) - int prepareVariableArguments(IR::ExprList* args); int prepareCallData(IR::ExprList* args, IR::Expr *thisObject); @@ -259,22 +269,22 @@ private: // 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. - Assembler::Pointer lookupAddr(Assembler::ReturnValueRegister, index * sizeof(QV4::Lookup)); + Pointer lookupAddr(JITTargetPlatform::ReturnValueRegister, index * sizeof(QV4::Lookup)); _as->generateFunctionCallImp(true, retval, "lookup getter/setter", - LookupCall(lookupAddr, getterSetterOffset), lookupAddr, + typename JITAssembler::LookupCall(lookupAddr, getterSetterOffset), lookupAddr, arg1, arg2, arg3); } 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, Assembler::VoidType()); + generateLookupCall(retval, index, getterSetterOffset, arg1, arg2, typename JITAssembler::VoidType()); } IR::BasicBlock *_block; BitVector _removableJumps; - Assembler* _as; + JITAssembler* _as; QScopedPointer<CompilationUnit> compilationUnit; QQmlEnginePrivate *qmlEngine; @@ -282,13 +292,14 @@ private: 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) Q_DECL_OVERRIDE Q_DECL_FINAL - { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator, this); } + { return new InstructionSelection<JITAssembler>(qmlEngine, execAllocator, module, jsGenerator, this); } bool jitCompileRegexps() const Q_DECL_OVERRIDE Q_DECL_FINAL { return true; } QQmlRefPointer<CompiledData::CompilationUnit> createUnitForLoading() Q_DECL_OVERRIDE Q_DECL_FINAL; diff --git a/src/qml/jit/qv4regalloc.cpp b/src/qml/jit/qv4regalloc.cpp index 3fb0815299..d5da863ee0 100644 --- a/src/qml/jit/qv4regalloc.cpp +++ b/src/qml/jit/qv4regalloc.cpp @@ -973,7 +973,15 @@ private: break; Q_ASSERT(!i->isFixedInterval()); - _liveIntervals.push_back(i); + 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(); @@ -1521,7 +1529,7 @@ static inline int indexOfRangeCoveringPosition(const LifeTimeInterval::Ranges &r return -1; } -static inline int intersectionPosition(const LifeTimeInterval::Range &one, const LifeTimeInterval::Range &two) +static inline int intersectionPosition(const LifeTimeIntervalRange &one, const LifeTimeIntervalRange &two) { if (one.covers(two.start)) return two.start; @@ -1779,9 +1787,9 @@ int RegisterAllocator::nextIntersection(const LifeTimeInterval ¤t, return -1; for (int currentEnd = currentRanges.size(); currentIt < currentEnd; ++currentIt) { - const LifeTimeInterval::Range currentRange = currentRanges.at(currentIt); + const LifeTimeIntervalRange currentRange = currentRanges.at(currentIt); for (int anotherIt = anotherItStart, anotherEnd = anotherRanges.size(); anotherIt < anotherEnd; ++anotherIt) { - const LifeTimeInterval::Range anotherRange = anotherRanges.at(anotherIt); + const LifeTimeIntervalRange anotherRange = anotherRanges.at(anotherIt); if (anotherRange.start > currentRange.end) break; int intersectPos = intersectionPosition(currentRange, anotherRange); diff --git a/src/qml/jit/qv4targetplatform_p.h b/src/qml/jit/qv4targetplatform_p.h index 7e265258d5..1c29aa2a70 100644 --- a/src/qml/jit/qv4targetplatform_p.h +++ b/src/qml/jit/qv4targetplatform_p.h @@ -63,6 +63,11 @@ 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: @@ -79,25 +84,37 @@ namespace JIT { // 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 { -public: +}; + #if CPU(X86) && (OS(LINUX) || OS(WINDOWS) || OS(QNX) || OS(FREEBSD) || defined(Q_OS_IOS)) - enum { RegAllocIsSupported = 1 }; +template <> +class TargetPlatform<JSC::MacroAssemblerX86, NoOperatingSystemSpecialization> +{ +public: + using PlatformAssembler = JSC::MacroAssemblerX86; + using RegisterID = PlatformAssembler::RegisterID; + using FPRegisterID = PlatformAssembler::FPRegisterID; - static const JSC::MacroAssembler::RegisterID FramePointerRegister = JSC::X86Registers::ebp; - static const JSC::MacroAssembler::RegisterID StackPointerRegister = JSC::X86Registers::esp; - static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::X86Registers::edi; - static const JSC::MacroAssembler::RegisterID EngineRegister = JSC::X86Registers::esi; - static const JSC::MacroAssembler::RegisterID ReturnValueRegister = JSC::X86Registers::eax; - static const JSC::MacroAssembler::RegisterID ScratchRegister = JSC::X86Registers::ecx; - static const JSC::MacroAssembler::FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; - static const JSC::MacroAssembler::FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; + enum { RegAllocIsSupported = 1 }; - static RegisterInformation getPlatformRegisterInfo() + 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; - return RegisterInformation() + 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) @@ -109,6 +126,7 @@ public: << 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 @@ -117,19 +135,20 @@ public: # undef ARGUMENTS_IN_REGISTERS static const int RegisterArgumentCount = 0; - static JSC::MacroAssembler::RegisterID registerForArgument(int) { Q_UNREACHABLE(); } + 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(JSC::MacroAssembler *as) { as->push(FramePointerRegister); } - static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) { as->pop(FramePointerRegister); } + static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(FramePointerRegister); } + static void platformLeaveStandardStackFrame(PlatformAssembler *as) { as->pop(FramePointerRegister); } #if OS(WINDOWS) || OS(QNX) || \ ((OS(LINUX) || OS(FREEBSD)) && (defined(__PIC__) || defined(__PIE__))) #define RESTORE_EBX_ON_CALL - static JSC::MacroAssembler::Address ebxAddressOnStack() + using Address = PlatformAssembler::Address; + static Address ebxAddressOnStack() { static int ebxIdx = -1; if (ebxIdx == -1) { @@ -146,28 +165,36 @@ public: Q_ASSERT(ebxIdx >= 0); ebxIdx += 1; } - return JSC::MacroAssembler::Address(FramePointerRegister, ebxIdx * -int(sizeof(void*))); + return Address(FramePointerRegister, ebxIdx * -int(sizeof(void*))); } #endif - -#endif // Windows on x86 +}; +#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; + enum { RegAllocIsSupported = 1 }; - static const JSC::MacroAssembler::RegisterID FramePointerRegister = JSC::X86Registers::ebp; - static const JSC::MacroAssembler::RegisterID StackPointerRegister = JSC::X86Registers::esp; - static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::X86Registers::r12; - static const JSC::MacroAssembler::RegisterID EngineRegister = JSC::X86Registers::r14; - static const JSC::MacroAssembler::RegisterID ReturnValueRegister = JSC::X86Registers::eax; - static const JSC::MacroAssembler::RegisterID ScratchRegister = JSC::X86Registers::r10; - static const JSC::MacroAssembler::FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; - static const JSC::MacroAssembler::FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; + 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 FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + static const FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; - static RegisterInformation getPlatformRegisterInfo() + static RegisterInformation getRegisterInfo() { typedef RegisterInfo RI; - return RegisterInformation() + 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) @@ -185,6 +212,7 @@ public: << 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 @@ -193,9 +221,9 @@ public: #define ARGUMENTS_IN_REGISTERS static const int RegisterArgumentCount = 6; - static JSC::MacroAssembler::RegisterID registerForArgument(int index) + static RegisterID registerForArgument(int index) { - static JSC::MacroAssembler::RegisterID regs[RegisterArgumentCount] = { + static RegisterID regs[RegisterArgumentCount] = { JSC::X86Registers::edi, JSC::X86Registers::esi, JSC::X86Registers::edx, @@ -210,29 +238,38 @@ public: 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(JSC::MacroAssembler *as) { as->push(FramePointerRegister); } - static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) { as->pop(FramePointerRegister); } + static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(FramePointerRegister); } + static void platformLeaveStandardStackFrame(PlatformAssembler *as) { as->pop(FramePointerRegister); } +}; #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; + // Register allocation is not (yet) supported on win64, because the ABI related stack handling // is not completely implemented. Specifically, the saving of xmm registers, and the saving of // incoming function parameters to the shadow space is missing. enum { RegAllocIsSupported = 0 }; - static const JSC::MacroAssembler::RegisterID FramePointerRegister = JSC::X86Registers::ebp; - static const JSC::MacroAssembler::RegisterID StackPointerRegister = JSC::X86Registers::esp; - static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::X86Registers::r12; - static const JSC::MacroAssembler::RegisterID EngineRegister = JSC::X86Registers::r14; - static const JSC::MacroAssembler::RegisterID ReturnValueRegister = JSC::X86Registers::eax; - static const JSC::MacroAssembler::RegisterID ScratchRegister = JSC::X86Registers::r10; - static const JSC::MacroAssembler::FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; - static const JSC::MacroAssembler::FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; + 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 FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + static const FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; - static RegisterInformation getPlatformRegisterInfo() + static RegisterInformation getRegisterInfo() { typedef RegisterInfo RI; - return RegisterInformation() + static RegisterInformation info = RegisterInformation() << RI(JSC::X86Registers::ebx, QStringLiteral("rbx"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) << RI(JSC::X86Registers::edi, QStringLiteral("rdi"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) << RI(JSC::X86Registers::esi, QStringLiteral("rsi"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) @@ -244,6 +281,7 @@ public: << RI(JSC::X86Registers::r14, QStringLiteral("r14"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) << RI(JSC::X86Registers::r15, QStringLiteral("r15"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) ; + return info; } #define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 @@ -252,9 +290,9 @@ public: #define ARGUMENTS_IN_REGISTERS static const int RegisterArgumentCount = 4; - static JSC::MacroAssembler::RegisterID registerForArgument(int index) + static RegisterID registerForArgument(int index) { - static JSC::MacroAssembler::RegisterID regs[RegisterArgumentCount] = { + static RegisterID regs[RegisterArgumentCount] = { JSC::X86Registers::ecx, JSC::X86Registers::edx, JSC::X86Registers::r8, @@ -267,11 +305,20 @@ public: 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(JSC::MacroAssembler *as) { as->push(FramePointerRegister); } - static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) { as->pop(FramePointerRegister); } + static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(FramePointerRegister); } + static void platformLeaveStandardStackFrame(PlatformAssembler *as) { as->pop(FramePointerRegister); } +}; #endif // Windows on x86_64 -#if CPU(ARM) +#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; + enum { RegAllocIsSupported = 1 }; // The AAPCS specifies that the platform ABI has to define the usage of r9. Known are: @@ -287,23 +334,25 @@ public: // 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) - static const JSC::MacroAssembler::RegisterID FramePointerRegister = JSC::ARMRegisters::r7; - static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::ARMRegisters::r11; + static const RegisterID FramePointerRegister = JSC::ARMRegisters::r7; + static const RegisterID LocalsRegister = JSC::ARMRegisters::r11; #else // Thumbs down - static const JSC::MacroAssembler::RegisterID FramePointerRegister = JSC::ARMRegisters::r11; - static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::ARMRegisters::r7; + static const RegisterID FramePointerRegister = JSC::ARMRegisters::r11; + static const RegisterID LocalsRegister = JSC::ARMRegisters::r7; #endif - static const JSC::MacroAssembler::RegisterID StackPointerRegister = JSC::ARMRegisters::r13; - static const JSC::MacroAssembler::RegisterID ScratchRegister = JSC::ARMRegisters::r5; - static const JSC::MacroAssembler::RegisterID EngineRegister = JSC::ARMRegisters::r10; - static const JSC::MacroAssembler::RegisterID ReturnValueRegister = JSC::ARMRegisters::r0; - static const JSC::MacroAssembler::FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; - static const JSC::MacroAssembler::FPRegisterID FPGpr1 = JSC::ARMRegisters::d1; - - static RegisterInformation getPlatformRegisterInfo() + 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; - return RegisterInformation() + 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) @@ -336,6 +385,7 @@ public: << 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 @@ -344,9 +394,9 @@ public: #define ARGUMENTS_IN_REGISTERS static const int RegisterArgumentCount = 4; - static JSC::MacroAssembler::RegisterID registerForArgument(int index) + static RegisterID registerForArgument(int index) { - static JSC::MacroAssembler::RegisterID regs[RegisterArgumentCount] = { + static RegisterID regs[RegisterArgumentCount] = { JSC::ARMRegisters::r0, JSC::ARMRegisters::r1, JSC::ARMRegisters::r2, @@ -361,35 +411,44 @@ public: static const int StackShadowSpace = 0; static const int StackSpaceAllocatedUponFunctionEntry = 1 * RegisterSize; // Registers saved in platformEnterStandardStackFrame below. - static void platformEnterStandardStackFrame(JSC::MacroAssembler *as) + static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(JSC::ARMRegisters::lr); as->push(FramePointerRegister); } - static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) + static void platformLeaveStandardStackFrame(PlatformAssembler *as) { as->pop(FramePointerRegister); as->pop(JSC::ARMRegisters::lr); } +}; #endif // ARM (32 bit) #if CPU(ARM64) +template <> +class TargetPlatform<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization> +{ +public: + using PlatformAssembler = JSC::MacroAssemblerARM64; + using RegisterID = PlatformAssembler::RegisterID; + using FPRegisterID = PlatformAssembler::FPRegisterID; + enum { RegAllocIsSupported = 1 }; - static const JSC::MacroAssembler::RegisterID FramePointerRegister = JSC::ARM64Registers::fp; - static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::ARM64Registers::x28; - static const JSC::MacroAssembler::RegisterID StackPointerRegister = JSC::ARM64Registers::sp; - static const JSC::MacroAssembler::RegisterID ScratchRegister = JSC::ARM64Registers::x9; - static const JSC::MacroAssembler::RegisterID EngineRegister = JSC::ARM64Registers::x27; - static const JSC::MacroAssembler::RegisterID ReturnValueRegister = JSC::ARM64Registers::x0; - static const JSC::MacroAssembler::FPRegisterID FPGpr0 = JSC::ARM64Registers::q0; - static const JSC::MacroAssembler::FPRegisterID FPGpr1 = JSC::ARM64Registers::q1; + 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 FPRegisterID FPGpr0 = JSC::ARM64Registers::q0; + static const FPRegisterID FPGpr1 = JSC::ARM64Registers::q1; - static RegisterInformation getPlatformRegisterInfo() + static RegisterInformation getRegisterInfo() { typedef RegisterInfo RI; - return RegisterInformation() + 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) @@ -447,6 +506,7 @@ public: << 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 @@ -455,9 +515,9 @@ public: #define ARGUMENTS_IN_REGISTERS static const int RegisterArgumentCount = 8; - static JSC::MacroAssembler::RegisterID registerForArgument(int index) + static RegisterID registerForArgument(int index) { - static JSC::MacroAssembler::RegisterID regs[RegisterArgumentCount] = { + static RegisterID regs[RegisterArgumentCount] = { JSC::ARM64Registers::x0, JSC::ARM64Registers::x1, JSC::ARM64Registers::x2, @@ -476,33 +536,43 @@ public: static const int StackShadowSpace = 0; static const int StackSpaceAllocatedUponFunctionEntry = 1 * RegisterSize; // Registers saved in platformEnterStandardStackFrame below. - static void platformEnterStandardStackFrame(JSC::MacroAssembler *as) + static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->pushPair(FramePointerRegister, JSC::ARM64Registers::lr); } - static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) + static void platformLeaveStandardStackFrame(PlatformAssembler *as) { as->popPair(FramePointerRegister, JSC::ARM64Registers::lr); } +}; #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; enum { RegAllocIsSupported = 1 }; - static const JSC::MacroAssembler::RegisterID FramePointerRegister = JSC::MIPSRegisters::fp; - static const JSC::MacroAssembler::RegisterID StackPointerRegister = JSC::MIPSRegisters::sp; - static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::MIPSRegisters::s0; - static const JSC::MacroAssembler::RegisterID EngineRegister = JSC::MIPSRegisters::s1; - static const JSC::MacroAssembler::RegisterID ReturnValueRegister = JSC::MIPSRegisters::v0; - static const JSC::MacroAssembler::RegisterID ScratchRegister = JSC::MIPSRegisters::s2; - static const JSC::MacroAssembler::FPRegisterID FPGpr0 = JSC::MIPSRegisters::f0; - static const JSC::MacroAssembler::FPRegisterID FPGpr1 = JSC::MIPSRegisters::f2; - - static RegisterInformation getPlatformRegisterInfo() + 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; - return RegisterInformation() + 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) @@ -524,6 +594,7 @@ public: << 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 @@ -532,9 +603,9 @@ public: #define ARGUMENTS_IN_REGISTERS static const int RegisterArgumentCount = 4; - static JSC::MacroAssembler::RegisterID registerForArgument(int index) + static RegisterID registerForArgument(int index) { - static JSC::MacroAssembler::RegisterID regs[RegisterArgumentCount] = { + static RegisterID regs[RegisterArgumentCount] = { JSC::MIPSRegisters::a0, JSC::MIPSRegisters::a1, JSC::MIPSRegisters::a2, @@ -549,27 +620,19 @@ public: 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(JSC::MacroAssembler *as) + static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(JSC::MIPSRegisters::ra); as->push(FramePointerRegister); } - static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) + static void platformLeaveStandardStackFrame(PlatformAssembler *as) { as->pop(FramePointerRegister); as->pop(JSC::MIPSRegisters::ra); } -#endif // Linux on MIPS (32 bit) - -public: // utility functions - static const RegisterInformation getRegisterInfo() - { - static const RegisterInformation info = getPlatformRegisterInfo(); - - return info; - } }; +#endif // Linux on MIPS (32 bit) } // JIT namespace } // QV4 namespace diff --git a/src/qml/jit/qv4unop.cpp b/src/qml/jit/qv4unop.cpp index 799103849b..31355e5dce 100644 --- a/src/qml/jit/qv4unop.cpp +++ b/src/qml/jit/qv4unop.cpp @@ -48,14 +48,15 @@ using namespace JIT; #define stringIfy(s) stringIfyx(s) #define setOp(operation) \ do { \ - call = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); name = "Runtime::" stringIfy(operation); \ + call = typename JITAssembler::RuntimeCall(qOffsetOf(QV4::Runtime, operation)); name = "Runtime::" stringIfy(operation); \ needsExceptionCheck = Runtime::Method_##operation##_NeedsExceptionCheck; \ } while (0) -void Unop::generate(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void Unop<JITAssembler>::generate(IR::Expr *source, IR::Expr *target) { bool needsExceptionCheck; - RuntimeCall call; + typename JITAssembler::RuntimeCall call; const char *name = 0; switch (op) { case IR::OpNot: @@ -75,17 +76,18 @@ void Unop::generate(IR::Expr *source, IR::Expr *target) } // switch Q_ASSERT(call.isValid()); - _as->generateFunctionCallImp(needsExceptionCheck, target, name, call, Assembler::PointerToValue(source)); + _as->generateFunctionCallImp(needsExceptionCheck, target, name, call, PointerToValue(source)); } -void Unop::generateUMinus(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void Unop<JITAssembler>::generateUMinus(IR::Expr *source, IR::Expr *target) { IR::Temp *targetTemp = target->asTemp(); if (source->type == IR::SInt32Type) { - Assembler::RegisterID tReg = Assembler::ScratchRegister; + typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister; if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) - tReg = (Assembler::RegisterID) targetTemp->index; - Assembler::RegisterID sReg = _as->toInt32Register(source, tReg); + 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) @@ -93,26 +95,27 @@ void Unop::generateUMinus(IR::Expr *source, IR::Expr *target) return; } - generateRuntimeCall(target, uMinus, Assembler::PointerToValue(source)); + generateRuntimeCall(_as, target, uMinus, PointerToValue(source)); } -void Unop::generateNot(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void Unop<JITAssembler>::generateNot(IR::Expr *source, IR::Expr *target) { IR::Temp *targetTemp = target->asTemp(); if (source->type == IR::BoolType) { - Assembler::RegisterID tReg = Assembler::ScratchRegister; + typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister; if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) - tReg = (Assembler::RegisterID) targetTemp->index; - _as->xor32(Assembler::TrustedImm32(0x1), _as->toInt32Register(source, tReg), tReg); + 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) { - Assembler::RegisterID tReg = Assembler::ScratchRegister; + typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister; if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) - tReg = (Assembler::RegisterID) targetTemp->index; - _as->compare32(Assembler::Equal, - _as->toInt32Register(source, Assembler::ScratchRegister), Assembler::TrustedImm32(0), + 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); @@ -122,22 +125,28 @@ void Unop::generateNot(IR::Expr *source, IR::Expr *target) } // ## generic implementation testing for int/bool - generateRuntimeCall(target, uNot, Assembler::PointerToValue(source)); + generateRuntimeCall(_as, target, uNot, PointerToValue(source)); } -void Unop::generateCompl(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void Unop<JITAssembler>::generateCompl(IR::Expr *source, IR::Expr *target) { IR::Temp *targetTemp = target->asTemp(); if (source->type == IR::SInt32Type) { - Assembler::RegisterID tReg = Assembler::ScratchRegister; + typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister; if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) - tReg = (Assembler::RegisterID) targetTemp->index; - _as->xor32(Assembler::TrustedImm32(0xffffffff), _as->toInt32Register(source, tReg), tReg); + 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(target, complement, Assembler::PointerToValue(source)); + generateRuntimeCall(_as, target, complement, PointerToValue(source)); } +template struct QV4::JIT::Unop<QV4::JIT::Assembler<DefaultAssemblerTargetConfiguration>>; +#if defined(V4_BOOTSTRAP) && CPU(X86_64) +template struct QV4::JIT::Unop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>>; +#endif + #endif diff --git a/src/qml/jit/qv4unop_p.h b/src/qml/jit/qv4unop_p.h index 1141a84913..fb68f80eec 100644 --- a/src/qml/jit/qv4unop_p.h +++ b/src/qml/jit/qv4unop_p.h @@ -60,21 +60,25 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace JIT { -class Assembler; - +template <typename JITAssembler> struct Unop { - Unop(Assembler *assembler, IR::AluOp operation) + Unop(JITAssembler *assembler, IR::AluOp operation) : _as(assembler) , op(operation) {} + 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); - Assembler *_as; + JITAssembler *_as; IR::AluOp op; }; diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index 919524d1ed..955cf585e4 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -35,7 +35,6 @@ SOURCES += \ $$PWD/qv4regexp.cpp \ $$PWD/qv4serialize.cpp \ $$PWD/qv4script.cpp \ - $$PWD/qv4executableallocator.cpp \ $$PWD/qv4sequenceobject.cpp \ $$PWD/qv4include.cpp \ $$PWD/qv4qobjectwrapper.cpp \ @@ -113,7 +112,8 @@ HEADERS += \ SOURCES += \ $$PWD/qv4runtime.cpp \ $$PWD/qv4string.cpp \ - $$PWD/qv4value.cpp + $$PWD/qv4value.cpp \ + $$PWD/qv4executableallocator.cpp valgrind { DEFINES += V4_USE_VALGRIND diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index 5a190d6690..9354bcb1a3 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -88,10 +88,12 @@ void ArgumentsObject::fullyCreate() Scope scope(engine()); Scoped<MemberData> md(scope, d()->mappedArguments); - d()->mappedArguments = md->allocate(engine(), numAccessors); - for (uint i = 0; i < numAccessors; ++i) { - d()->mappedArguments->data[i] = context()->callData->args[i]; - arraySet(i, context()->engine->argumentsAccessors + i, Attr_Accessor); + if (numAccessors) { + d()->mappedArguments = md->allocate(engine(), numAccessors); + for (uint i = 0; i < numAccessors; ++i) { + d()->mappedArguments->data[i] = context()->callData->args[i]; + arraySet(i, context()->engine->argumentsAccessors + i, Attr_Accessor); + } } arrayPut(numAccessors, context()->callData->args + numAccessors, argCount - numAccessors); for (uint i = numAccessors; i < argCount; ++i) diff --git a/src/qml/jsruntime/qv4arraybuffer.cpp b/src/qml/jsruntime/qv4arraybuffer.cpp index 23075aa78c..ffe9aa846f 100644 --- a/src/qml/jsruntime/qv4arraybuffer.cpp +++ b/src/qml/jsruntime/qv4arraybuffer.cpp @@ -81,16 +81,19 @@ void ArrayBufferCtor::call(const Managed *that, Scope &scope, CallData *callData construct(that, scope, callData); } -ReturnedValue ArrayBufferCtor::method_isView(CallContext *ctx) +void ArrayBufferCtor::method_isView(const BuiltinFunction *, Scope &scope, CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped<TypedArray> a(scope, ctx->argument(0)); - if (!!a) - return Encode(true); - QV4::Scoped<DataView> v(scope, ctx->argument(0)); - if (!!v) - return Encode(true); - return Encode(false); + 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); } @@ -160,54 +163,48 @@ void ArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("toString"), method_toString, 0); } -ReturnedValue ArrayBufferPrototype::method_get_byteLength(CallContext *ctx) +void ArrayBufferPrototype::method_get_byteLength(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<ArrayBuffer> v(scope, ctx->thisObject()); + Scoped<ArrayBuffer> v(scope, callData->thisObject); if (!v) - return scope.engine->throwTypeError(); + THROW_TYPE_ERROR(); - return Encode(v->d()->data->size); + scope.result = Encode(v->d()->data->size); } -ReturnedValue ArrayBufferPrototype::method_slice(CallContext *ctx) +void ArrayBufferPrototype::method_slice(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<ArrayBuffer> a(scope, ctx->thisObject()); + Scoped<ArrayBuffer> a(scope, callData->thisObject); if (!a) - return scope.engine->throwTypeError(); + THROW_TYPE_ERROR(); - double start = ctx->argc() > 0 ? ctx->args()[0].toInteger() : 0; - double end = (ctx->argc() < 2 || ctx->args()[1].isUndefined()) ? - a->d()->data->size : ctx->args()[1].toInteger(); - if (scope.engine->hasException) - return Encode::undefined(); + 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 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); ScopedFunctionObject constructor(scope, a->get(scope.engine->id_constructor())); if (!constructor) - return scope.engine->throwTypeError(); + THROW_TYPE_ERROR(); - ScopedCallData callData(scope, 1); + ScopedCallData cData(scope, 1); double newLen = qMax(final - first, 0.); - callData->args[0] = QV4::Encode(newLen); - constructor->construct(scope, callData); - QV4::Scoped<ArrayBuffer> newBuffer(scope, scope.result.asReturnedValue()); + cData->args[0] = QV4::Encode(newLen); + constructor->construct(scope, cData); + QV4::Scoped<ArrayBuffer> newBuffer(scope, scope.result); if (!newBuffer || newBuffer->d()->data->size < (int)newLen) - return scope.engine->throwTypeError(); + THROW_TYPE_ERROR(); memcpy(newBuffer->d()->data->data(), a->d()->data->data() + (uint)first, newLen); - - return newBuffer.asReturnedValue(); } -ReturnedValue ArrayBufferPrototype::method_toString(CallContext *ctx) +void ArrayBufferPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<ArrayBuffer> a(scope, ctx->thisObject()); + Scoped<ArrayBuffer> a(scope, callData->thisObject); if (!a) - return Encode::undefined(); - return Encode(ctx->engine()->newString(QString::fromUtf8(a->asByteArray()))); + RETURN_UNDEFINED(); + scope.result = scope.engine->newString(QString::fromUtf8(a->asByteArray())); } diff --git a/src/qml/jsruntime/qv4arraybuffer_p.h b/src/qml/jsruntime/qv4arraybuffer_p.h index bc56d1ea31..4f7926d3dc 100644 --- a/src/qml/jsruntime/qv4arraybuffer_p.h +++ b/src/qml/jsruntime/qv4arraybuffer_p.h @@ -81,7 +81,7 @@ struct ArrayBufferCtor: FunctionObject static void construct(const Managed *m, Scope &scope, CallData *callData); static void call(const Managed *that, Scope &scope, CallData *callData); - static ReturnedValue method_isView(CallContext *ctx); + static void method_isView(const BuiltinFunction *, Scope &scope, CallData *callData); }; @@ -104,9 +104,9 @@ struct ArrayBufferPrototype: Object { void init(ExecutionEngine *engine, Object *ctor); - static ReturnedValue method_get_byteLength(CallContext *ctx); - static ReturnedValue method_slice(CallContext *ctx); - static ReturnedValue method_toString(CallContext *ctx); + 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); }; diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp index bfeb3d4699..d8a7de5466 100644 --- a/src/qml/jsruntime/qv4arraydata.cpp +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -237,8 +237,14 @@ void ArrayData::ensureAttributes(Object *o) void SimpleArrayData::markObjects(Heap::Base *d, ExecutionEngine *e) { Heap::SimpleArrayData *dd = static_cast<Heap::SimpleArrayData *>(d); - for (uint i = 0; i < dd->len; ++i) - dd->arrayData[dd->mappedIndex(i)].mark(e); + uint end = dd->offset + dd->len; + if (end > dd->alloc) { + for (uint i = 0; i < end - dd->alloc; ++i) + dd->arrayData[i].mark(e); + end = dd->alloc; + } + for (uint i = dd->offset; i < end; ++i) + dd->arrayData[i].mark(e); } ReturnedValue SimpleArrayData::get(const Heap::ArrayData *d, uint index) diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index 20ba11fd75..759354f4e2 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -44,6 +44,7 @@ #include "qv4argumentsobject_p.h" #include "qv4runtime_p.h" #include "qv4string_p.h" +#include <QtCore/qscopedvaluerollback.h> using namespace QV4; @@ -118,42 +119,40 @@ void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("reduceRight"), method_reduceRight, 1); } -ReturnedValue ArrayPrototype::method_isArray(CallContext *ctx) +void ArrayPrototype::method_isArray(const BuiltinFunction *, Scope &scope, CallData *callData) { - bool isArray = ctx->argc() && ctx->args()[0].as<ArrayObject>(); - return Encode(isArray); + bool isArray = callData->argc && callData->args[0].as<ArrayObject>(); + scope.result = Encode(isArray); } -ReturnedValue ArrayPrototype::method_toString(CallContext *ctx) +void ArrayPrototype::method_toString(const BuiltinFunction *builtin, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject o(scope, ctx->thisObject(), ScopedObject::Convert); - if (ctx->d()->engine->hasException) - return Encode::undefined(); - ScopedString s(scope, ctx->d()->engine->newString(QStringLiteral("join"))); + 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 = ctx->thisObject(); + d->thisObject = callData->thisObject; f->call(scope, d); - return scope.result.asReturnedValue(); + return; } - return ObjectPrototype::method_toString(ctx); + ObjectPrototype::method_toString(builtin, scope, callData); } -ReturnedValue ArrayPrototype::method_toLocaleString(CallContext *ctx) +void ArrayPrototype::method_toLocaleString(const BuiltinFunction *builtin, Scope &scope, CallData *callData) { - return method_toString(ctx); + return method_toString(builtin, scope, callData); } -ReturnedValue ArrayPrototype::method_concat(CallContext *ctx) +void ArrayPrototype::method_concat(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject result(scope, ctx->d()->engine->newArrayObject()); - - ScopedObject thisObject(scope, ctx->thisObject().toObject(scope.engine)); + ScopedObject thisObject(scope, callData->thisObject.toObject(scope.engine)); if (!thisObject) - return Encode::undefined(); + RETURN_UNDEFINED(); + + ScopedArrayObject result(scope, scope.engine->newArrayObject()); + if (thisObject->isArrayObject()) { result->copyArrayData(thisObject); } else { @@ -163,9 +162,9 @@ ReturnedValue ArrayPrototype::method_concat(CallContext *ctx) ScopedArrayObject elt(scope); ScopedObject eltAsObj(scope); ScopedValue entry(scope); - for (int i = 0; i < ctx->argc(); ++i) { - eltAsObj = ctx->args()[i]; - elt = ctx->args()[i]; + for (int i = 0; i < callData->argc; ++i) { + eltAsObj = callData->args[i]; + elt = callData->args[i]; if (elt) { uint n = elt->getLength(); uint newLen = ArrayData::append(result, elt, n); @@ -177,95 +176,90 @@ ReturnedValue ArrayPrototype::method_concat(CallContext *ctx) result->putIndexed(startIndex + i, entry); } } else { - result->arraySet(result->getLength(), ctx->args()[i]); + result->arraySet(result->getLength(), callData->args[i]); } } - return result.asReturnedValue(); + scope.result = result; } -ReturnedValue ArrayPrototype::method_find(CallContext *ctx) +void ArrayPrototype::method_find(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject instance(scope, ctx->thisObject().toObject(scope.engine)); + ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) - return Encode::undefined(); + RETURN_UNDEFINED(); uint len = instance->getLength(); - ScopedFunctionObject callback(scope, ctx->argument(0)); + ScopedFunctionObject callback(scope, callData->argument(0)); if (!callback) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - ScopedCallData callData(scope, 3); - callData->thisObject = ctx->argument(1); - callData->args[2] = instance; + ScopedCallData cData(scope, 3); + cData->thisObject = callData->argument(1); + cData->args[2] = instance; ScopedValue v(scope); for (uint k = 0; k < len; ++k) { v = instance->getIndexed(k); - if (scope.hasException()) - return Encode::undefined(); + CHECK_EXCEPTION(); - callData->args[0] = v; - callData->args[1] = Primitive::fromDouble(k); - callback->call(scope, callData); + cData->args[0] = v; + cData->args[1] = Primitive::fromDouble(k); + callback->call(scope, cData); - if (scope.hasException()) - return Encode::undefined(); - else if (scope.result.toBoolean()) - return v->asReturnedValue(); + CHECK_EXCEPTION(); + if (scope.result.toBoolean()) + RETURN_RESULT(v); } - return Encode::undefined(); + RETURN_UNDEFINED(); } -ReturnedValue ArrayPrototype::method_findIndex(CallContext *ctx) +void ArrayPrototype::method_findIndex(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject instance(scope, ctx->thisObject().toObject(scope.engine)); + ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) - return Encode::undefined(); + RETURN_UNDEFINED(); uint len = instance->getLength(); - ScopedFunctionObject callback(scope, ctx->argument(0)); + ScopedFunctionObject callback(scope, callData->argument(0)); if (!callback) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - ScopedCallData callData(scope, 3); - callData->thisObject = ctx->argument(1); - callData->args[2] = instance; + ScopedCallData cData(scope, 3); + cData->thisObject = callData->argument(1); + cData->args[2] = instance; ScopedValue v(scope); for (uint k = 0; k < len; ++k) { v = instance->getIndexed(k); - if (scope.hasException()) - return Encode::undefined(); + CHECK_EXCEPTION(); - callData->args[0] = v; - callData->args[1] = Primitive::fromDouble(k); - callback->call(scope, callData); + cData->args[0] = v; + cData->args[1] = Primitive::fromDouble(k); + callback->call(scope, cData); - if (scope.hasException()) - return Encode::undefined(); - else if (scope.result.toBoolean()) - return Encode(k); + CHECK_EXCEPTION(); + if (scope.result.toBoolean()) + RETURN_RESULT(Encode(k)); } - return Encode(-1); + RETURN_RESULT(Encode(-1)); } -ReturnedValue ArrayPrototype::method_join(CallContext *ctx) +void ArrayPrototype::method_join(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedValue arg(scope, ctx->argument(0)); - ScopedObject instance(scope, ctx->thisObject().toObject(scope.engine)); + ScopedValue arg(scope, callData->argument(0)); + ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); - if (!instance) - return ctx->d()->engine->newString()->asReturnedValue(); + if (!instance) { + scope.result = scope.engine->newString(); + return; + } QString r4; if (arg->isUndefined()) @@ -273,11 +267,13 @@ ReturnedValue ArrayPrototype::method_join(CallContext *ctx) else r4 = arg->toQString(); - ScopedValue length(scope, instance->get(ctx->d()->engine->id_length())); + ScopedValue length(scope, instance->get(scope.engine->id_length())); const quint32 r2 = length->isUndefined() ? 0 : length->toUInt32(); - if (!r2) - return ctx->d()->engine->newString()->asReturnedValue(); + if (!r2) { + scope.result = scope.engine->newString(); + return; + } QString R; @@ -289,8 +285,7 @@ ReturnedValue ArrayPrototype::method_join(CallContext *ctx) R += r4; e = a->getIndexed(i); - if (scope.hasException()) - return Encode::undefined(); + CHECK_EXCEPTION(); if (!e->isNullOrUndefined()) R += e->toQString(); } @@ -298,7 +293,7 @@ ReturnedValue ArrayPrototype::method_join(CallContext *ctx) // // crazy! // - ScopedString name(scope, ctx->d()->engine->newString(QStringLiteral("0"))); + ScopedString name(scope, scope.engine->newString(QStringLiteral("0"))); ScopedValue r6(scope, instance->get(name)); if (!r6->isNullOrUndefined()) R = r6->toQString(); @@ -309,99 +304,98 @@ ReturnedValue ArrayPrototype::method_join(CallContext *ctx) name = Primitive::fromDouble(k).toString(scope.engine); r12 = instance->get(name); - if (scope.hasException()) - return Encode::undefined(); + CHECK_EXCEPTION(); if (!r12->isNullOrUndefined()) R += r12->toQString(); } } - return ctx->d()->engine->newString(R)->asReturnedValue(); + scope.result = scope.engine->newString(R); } -ReturnedValue ArrayPrototype::method_pop(CallContext *ctx) +void ArrayPrototype::method_pop(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject instance(scope, ctx->thisObject().toObject(scope.engine)); + ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) - return Encode::undefined(); + RETURN_UNDEFINED(); + uint len = instance->getLength(); if (!len) { if (!instance->isArrayObject()) - instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromInt32(0))); - return Encode::undefined(); + instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromInt32(0))); + RETURN_UNDEFINED(); } ScopedValue result(scope, instance->getIndexed(len - 1)); - if (scope.hasException()) - return Encode::undefined(); + CHECK_EXCEPTION(); instance->deleteIndexedProperty(len - 1); - if (scope.hasException()) - return Encode::undefined(); + CHECK_EXCEPTION(); + if (instance->isArrayObject()) instance->setArrayLength(len - 1); else - instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - 1))); - return result->asReturnedValue(); + instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - 1))); + scope.result = result; } -ReturnedValue ArrayPrototype::method_push(CallContext *ctx) +void ArrayPrototype::method_push(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject instance(scope, ctx->thisObject().toObject(scope.engine)); + ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) - return Encode::undefined(); + RETURN_UNDEFINED(); instance->arrayCreate(); Q_ASSERT(instance->arrayData()); uint len = instance->getLength(); - if (len + ctx->argc() < len) { + if (len + callData->argc < len) { // ughh... double l = len; ScopedString s(scope); - for (int i = 0; i < ctx->argc(); ++i) { + for (int i = 0; i < callData->argc; ++i) { s = Primitive::fromDouble(l + i).toString(scope.engine); - instance->put(s, ctx->args()[i]); + instance->put(s, callData->args[i]); } - double newLen = l + ctx->argc(); + double newLen = l + callData->argc; if (!instance->isArrayObject()) - instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromDouble(newLen))); + instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(newLen))); else { - ScopedString str(scope, ctx->d()->engine->newString(QStringLiteral("Array.prototype.push: Overflow"))); - return ctx->engine()->throwRangeError(str); + ScopedString str(scope, scope.engine->newString(QStringLiteral("Array.prototype.push: Overflow"))); + scope.result = scope.engine->throwRangeError(str); + return; } - return Encode(newLen); + scope.result = Encode(newLen); + return; } - if (!ctx->argc()) + if (!callData->argc) ; else if (!instance->protoHasArray() && instance->arrayData()->length() <= len && instance->arrayData()->type == Heap::ArrayData::Simple) { - instance->arrayData()->vtable()->putArray(instance, len, ctx->args(), ctx->argc()); + instance->arrayData()->vtable()->putArray(instance, len, callData->args, callData->argc); len = instance->arrayData()->length(); } else { - for (int i = 0; i < ctx->argc(); ++i) - instance->putIndexed(len + i, ctx->args()[i]); - len += ctx->argc(); + for (int i = 0; i < callData->argc; ++i) + instance->putIndexed(len + i, callData->args[i]); + len += callData->argc; } if (instance->isArrayObject()) instance->setArrayLengthUnchecked(len); else - instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len))); + instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len))); - return Encode(len); + scope.result = Encode(len); } -ReturnedValue ArrayPrototype::method_reverse(CallContext *ctx) +void ArrayPrototype::method_reverse(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject instance(scope, ctx->thisObject().toObject(scope.engine)); + ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) - return Encode::undefined(); + RETURN_UNDEFINED(); + uint length = instance->getLength(); int lo = 0, hi = length - 1; @@ -412,28 +406,25 @@ ReturnedValue ArrayPrototype::method_reverse(CallContext *ctx) bool loExists, hiExists; lval = instance->getIndexed(lo, &loExists); hval = instance->getIndexed(hi, &hiExists); - if (scope.hasException()) - return Encode::undefined(); + CHECK_EXCEPTION(); if (hiExists) instance->putIndexed(lo, hval); else instance->deleteIndexedProperty(lo); - if (scope.hasException()) - return Encode::undefined(); + CHECK_EXCEPTION(); if (loExists) instance->putIndexed(hi, lval); else instance->deleteIndexedProperty(hi); } - return instance.asReturnedValue(); + scope.result = instance; } -ReturnedValue ArrayPrototype::method_shift(CallContext *ctx) +void ArrayPrototype::method_shift(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject instance(scope, ctx->thisObject().toObject(scope.engine)); + ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) - return Encode::undefined(); + RETURN_UNDEFINED(); instance->arrayCreate(); Q_ASSERT(instance->arrayData()); @@ -442,54 +433,46 @@ ReturnedValue ArrayPrototype::method_shift(CallContext *ctx) if (!len) { if (!instance->isArrayObject()) - instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromInt32(0))); - return Encode::undefined(); + instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromInt32(0))); + RETURN_UNDEFINED(); } - ScopedValue result(scope); - if (!instance->protoHasArray() && !instance->arrayData()->attrs && instance->arrayData()->length() <= len && instance->arrayData()->type != Heap::ArrayData::Custom) { - result = instance->arrayData()->vtable()->pop_front(instance); + scope.result = instance->arrayData()->vtable()->pop_front(instance); } else { - result = instance->getIndexed(0); - if (scope.hasException()) - return Encode::undefined(); + scope.result = instance->getIndexed(0); + CHECK_EXCEPTION(); ScopedValue v(scope); // do it the slow way for (uint k = 1; k < len; ++k) { bool exists; v = instance->getIndexed(k, &exists); - if (scope.hasException()) - return Encode::undefined(); + CHECK_EXCEPTION(); if (exists) instance->putIndexed(k - 1, v); else instance->deleteIndexedProperty(k - 1); - if (scope.hasException()) - return Encode::undefined(); + CHECK_EXCEPTION(); } instance->deleteIndexedProperty(len - 1); - if (scope.hasException()) - return Encode::undefined(); + CHECK_EXCEPTION(); } if (instance->isArrayObject()) instance->setArrayLengthUnchecked(len - 1); else - instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - 1))); - return result->asReturnedValue(); + instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - 1))); } -ReturnedValue ArrayPrototype::method_slice(CallContext *ctx) +void ArrayPrototype::method_slice(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject o(scope, ctx->thisObject().toObject(scope.engine)); + ScopedObject o(scope, callData->thisObject.toObject(scope.engine)); if (!o) - return Encode::undefined(); + RETURN_UNDEFINED(); - ScopedArrayObject result(scope, ctx->d()->engine->newArrayObject()); + ScopedArrayObject result(scope, scope.engine->newArrayObject()); uint len = o->getLength(); - double s = ScopedValue(scope, ctx->argument(0))->toInteger(); + double s = ScopedValue(scope, callData->argument(0))->toInteger(); uint start; if (s < 0) start = (uint)qMax(len + s, 0.); @@ -498,8 +481,8 @@ ReturnedValue ArrayPrototype::method_slice(CallContext *ctx) else start = (uint) s; uint end = len; - if (ctx->argc() > 1 && !ctx->args()[1].isUndefined()) { - double e = ctx->args()[1].toInteger(); + if (callData->argc > 1 && !callData->args[1].isUndefined()) { + double e = callData->args[1].toInteger(); if (e < 0) end = (uint)qMax(len + e, 0.); else if (e > len) @@ -513,115 +496,107 @@ ReturnedValue ArrayPrototype::method_slice(CallContext *ctx) for (uint i = start; i < end; ++i) { bool exists; v = o->getIndexed(i, &exists); - if (scope.hasException()) - return Encode::undefined(); + CHECK_EXCEPTION(); if (exists) result->arraySet(n, v); ++n; } - return result.asReturnedValue(); + scope.result = result; } -ReturnedValue ArrayPrototype::method_sort(CallContext *ctx) +void ArrayPrototype::method_sort(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject instance(scope, ctx->thisObject().toObject(scope.engine)); + ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) - return Encode::undefined(); + RETURN_UNDEFINED(); uint len = instance->getLength(); - ScopedValue comparefn(scope, ctx->argument(0)); + ScopedValue comparefn(scope, callData->argument(0)); ArrayData::sort(scope.engine, instance, comparefn, len); - return ctx->thisObject().asReturnedValue(); + scope.result = callData->thisObject; } -ReturnedValue ArrayPrototype::method_splice(CallContext *ctx) +void ArrayPrototype::method_splice(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject instance(scope, ctx->thisObject().toObject(scope.engine)); + ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) - return Encode::undefined(); + RETURN_UNDEFINED(); + uint len = instance->getLength(); - ScopedArrayObject newArray(scope, ctx->d()->engine->newArrayObject()); + ScopedArrayObject newArray(scope, scope.engine->newArrayObject()); - double rs = ScopedValue(scope, ctx->argument(0))->toInteger(); + double rs = ScopedValue(scope, callData->argument(0))->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, ctx->argument(1))->toInteger(), 0.), (double)(len - start)); + uint deleteCount = (uint)qMin(qMax(ScopedValue(scope, callData->argument(1))->toInteger(), 0.), (double)(len - start)); newArray->arrayReserve(deleteCount); ScopedValue v(scope); for (uint i = 0; i < deleteCount; ++i) { bool exists; v = instance->getIndexed(start + i, &exists); - if (scope.hasException()) - return Encode::undefined(); + CHECK_EXCEPTION(); if (exists) newArray->arrayPut(i, v); } newArray->setArrayLengthUnchecked(deleteCount); - uint itemCount = ctx->argc() < 2 ? 0 : ctx->argc() - 2; + uint itemCount = callData->argc < 2 ? 0 : callData->argc - 2; if (itemCount < deleteCount) { for (uint k = start; k < len - deleteCount; ++k) { bool exists; v = instance->getIndexed(k + deleteCount, &exists); - if (scope.hasException()) - return Encode::undefined(); + CHECK_EXCEPTION(); if (exists) instance->putIndexed(k + itemCount, v); else instance->deleteIndexedProperty(k + itemCount); - if (scope.hasException()) - return Encode::undefined(); + CHECK_EXCEPTION(); } for (uint k = len; k > len - deleteCount + itemCount; --k) { instance->deleteIndexedProperty(k - 1); - if (scope.hasException()) - return Encode::undefined(); + CHECK_EXCEPTION(); } } else if (itemCount > deleteCount) { uint k = len - deleteCount; while (k > start) { bool exists; v = instance->getIndexed(k + deleteCount - 1, &exists); - if (scope.hasException()) - return Encode::undefined(); + CHECK_EXCEPTION(); if (exists) instance->putIndexed(k + itemCount - 1, v); else instance->deleteIndexedProperty(k + itemCount - 1); - if (scope.hasException()) - return Encode::undefined(); + CHECK_EXCEPTION(); --k; } } for (uint i = 0; i < itemCount; ++i) { - instance->putIndexed(start + i, ctx->args()[i + 2]); - if (scope.hasException()) - return Encode::undefined(); + instance->putIndexed(start + i, callData->args[i + 2]); + CHECK_EXCEPTION(); } - ctx->d()->strictMode = true; - instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - deleteCount + itemCount))); + bool wasStrict = scope.engine->current->strictMode; + scope.engine->current->strictMode = true; + instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - deleteCount + itemCount))); - return newArray.asReturnedValue(); + scope.result = newArray; + scope.engine->current->strictMode = wasStrict; } -ReturnedValue ArrayPrototype::method_unshift(CallContext *ctx) +void ArrayPrototype::method_unshift(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject instance(scope, ctx->thisObject().toObject(scope.engine)); + ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) - return Encode::undefined(); + RETURN_UNDEFINED(); instance->arrayCreate(); Q_ASSERT(instance->arrayData()); @@ -630,50 +605,52 @@ ReturnedValue ArrayPrototype::method_unshift(CallContext *ctx) if (!instance->protoHasArray() && !instance->arrayData()->attrs && instance->arrayData()->length() <= len && instance->arrayData()->type != Heap::ArrayData::Custom) { - instance->arrayData()->vtable()->push_front(instance, ctx->args(), ctx->argc()); + instance->arrayData()->vtable()->push_front(instance, callData->args, callData->argc); } else { ScopedValue v(scope); for (uint k = len; k > 0; --k) { bool exists; v = instance->getIndexed(k - 1, &exists); if (exists) - instance->putIndexed(k + ctx->argc() - 1, v); + instance->putIndexed(k + callData->argc - 1, v); else - instance->deleteIndexedProperty(k + ctx->argc() - 1); + instance->deleteIndexedProperty(k + callData->argc - 1); } - for (int i = 0; i < ctx->argc(); ++i) - instance->putIndexed(i, ctx->args()[i]); + for (int i = 0; i < callData->argc; ++i) + instance->putIndexed(i, callData->args[i]); } - uint newLen = len + ctx->argc(); + uint newLen = len + callData->argc; if (instance->isArrayObject()) instance->setArrayLengthUnchecked(newLen); else - instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromDouble(newLen))); + instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(newLen))); - return Encode(newLen); + scope.result = Encode(newLen); } -ReturnedValue ArrayPrototype::method_indexOf(CallContext *ctx) +void ArrayPrototype::method_indexOf(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - - ScopedObject instance(scope, ctx->thisObject().toObject(scope.engine)); + ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) - return Encode::undefined(); + RETURN_UNDEFINED(); + uint len = instance->getLength(); - if (!len) - return Encode(-1); + if (!len) { + scope.result = Encode(-1); + return; + } - ScopedValue searchValue(scope, ctx->argument(0)); + ScopedValue searchValue(scope, callData->argument(0)); uint fromIndex = 0; - if (ctx->argc() >= 2) { - double f = ctx->args()[1].toInteger(); - if (scope.hasException()) - return Encode::undefined(); - if (f >= len) - return Encode(-1); + if (callData->argc >= 2) { + double f = callData->args[1].toInteger(); + CHECK_EXCEPTION(); + if (f >= len) { + scope.result = Encode(-1); + return; + } if (f < 0) f = qMax(len + f, 0.); fromIndex = (uint) f; @@ -684,10 +661,13 @@ ReturnedValue ArrayPrototype::method_indexOf(CallContext *ctx) for (uint k = fromIndex; k < len; ++k) { bool exists; v = instance->getIndexed(k, &exists); - if (exists && RuntimeHelpers::strictEqual(v, searchValue)) - return Encode(k); + if (exists && RuntimeHelpers::strictEqual(v, searchValue)) { + scope.result = Encode(k); + return; + } } - return Encode(-1); + scope.result = Encode(-1); + return; } ScopedValue value(scope); @@ -698,13 +678,15 @@ ReturnedValue ArrayPrototype::method_indexOf(CallContext *ctx) for (uint i = fromIndex; i < len; ++i) { bool exists; value = instance->getIndexed(i, &exists); - if (scope.hasException()) - return Encode::undefined(); - if (exists && RuntimeHelpers::strictEqual(value, searchValue)) - return Encode(i); + CHECK_EXCEPTION(); + if (exists && RuntimeHelpers::strictEqual(value, searchValue)) { + scope.result = Encode(i); + return; + } } } else if (!instance->arrayData()) { - return Encode(-1); + scope.result = Encode(-1); + return; } else { Q_ASSERT(instance->arrayType() == Heap::ArrayData::Simple || instance->arrayType() == Heap::ArrayData::Complex); Heap::SimpleArrayData *sa = instance->d()->arrayData.cast<Heap::SimpleArrayData>(); @@ -713,45 +695,48 @@ ReturnedValue ArrayPrototype::method_indexOf(CallContext *ctx) uint idx = fromIndex; while (idx < len) { value = sa->data(idx); - if (scope.hasException()) - return Encode::undefined(); - if (RuntimeHelpers::strictEqual(value, searchValue)) - return Encode(idx); + CHECK_EXCEPTION(); + if (RuntimeHelpers::strictEqual(value, searchValue)) { + scope.result = Encode(idx); + return; + } ++idx; } } - return Encode(-1); + scope.result = Encode(-1); } -ReturnedValue ArrayPrototype::method_lastIndexOf(CallContext *ctx) +void ArrayPrototype::method_lastIndexOf(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - - ScopedObject instance(scope, ctx->thisObject().toObject(scope.engine)); + ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) - return Encode::undefined(); + RETURN_UNDEFINED(); + uint len = instance->getLength(); - if (!len) - return Encode(-1); + if (!len) { + scope.result = Encode(-1); + return; + } ScopedValue searchValue(scope); uint fromIndex = len; - if (ctx->argc() >= 1) - searchValue = ctx->argument(0); + if (callData->argc >= 1) + searchValue = callData->argument(0); else searchValue = Primitive::undefinedValue(); - if (ctx->argc() >= 2) { - double f = ctx->args()[1].toInteger(); - if (scope.hasException()) - return Encode::undefined(); + if (callData->argc >= 2) { + double f = callData->args[1].toInteger(); + CHECK_EXCEPTION(); if (f > 0) f = qMin(f, (double)(len - 1)); else if (f < 0) { f = len + f; - if (f < 0) - return Encode(-1); + if (f < 0) { + scope.result = Encode(-1); + return; + } } fromIndex = (uint) f + 1; } @@ -761,30 +746,30 @@ ReturnedValue ArrayPrototype::method_lastIndexOf(CallContext *ctx) --k; bool exists; v = instance->getIndexed(k, &exists); - if (scope.hasException()) - return Encode::undefined(); - if (exists && RuntimeHelpers::strictEqual(v, searchValue)) - return Encode(k); + CHECK_EXCEPTION(); + if (exists && RuntimeHelpers::strictEqual(v, searchValue)) { + scope.result = Encode(k); + return; + } } - return Encode(-1); + scope.result = Encode(-1); } -ReturnedValue ArrayPrototype::method_every(CallContext *ctx) +void ArrayPrototype::method_every(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject instance(scope, ctx->thisObject().toObject(scope.engine)); + ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) - return Encode::undefined(); + RETURN_UNDEFINED(); uint len = instance->getLength(); - ScopedFunctionObject callback(scope, ctx->argument(0)); + ScopedFunctionObject callback(scope, callData->argument(0)); if (!callback) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - ScopedCallData callData(scope, 3); - callData->args[2] = instance; - callData->thisObject = ctx->argument(1); + ScopedCallData cData(scope, 3); + cData->args[2] = instance; + cData->thisObject = callData->argument(1); ScopedValue v(scope); bool ok = true; @@ -794,30 +779,29 @@ ReturnedValue ArrayPrototype::method_every(CallContext *ctx) if (!exists) continue; - callData->args[0] = v; - callData->args[1] = Primitive::fromDouble(k); - callback->call(scope, callData); + cData->args[0] = v; + cData->args[1] = Primitive::fromDouble(k); + callback->call(scope, cData); ok = scope.result.toBoolean(); } - return Encode(ok); + scope.result = Encode(ok); } -ReturnedValue ArrayPrototype::method_some(CallContext *ctx) +void ArrayPrototype::method_some(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject instance(scope, ctx->thisObject().toObject(scope.engine)); + ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) - return Encode::undefined(); + RETURN_UNDEFINED(); uint len = instance->getLength(); - ScopedFunctionObject callback(scope, ctx->argument(0)); + ScopedFunctionObject callback(scope, callData->argument(0)); if (!callback) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - ScopedCallData callData(scope, 3); - callData->thisObject = ctx->argument(1); - callData->args[2] = instance; + ScopedCallData cData(scope, 3); + cData->thisObject = callData->argument(1); + cData->args[2] = instance; ScopedValue v(scope); for (uint k = 0; k < len; ++k) { @@ -826,31 +810,32 @@ ReturnedValue ArrayPrototype::method_some(CallContext *ctx) if (!exists) continue; - callData->args[0] = v; - callData->args[1] = Primitive::fromDouble(k); - callback->call(scope, callData); - if (scope.result.toBoolean()) - return Encode(true); + cData->args[0] = v; + cData->args[1] = Primitive::fromDouble(k); + callback->call(scope, cData); + if (scope.result.toBoolean()) { + scope.result = Encode(true); + return; + } } - return Encode(false); + scope.result = Encode(false); } -ReturnedValue ArrayPrototype::method_forEach(CallContext *ctx) +void ArrayPrototype::method_forEach(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject instance(scope, ctx->thisObject().toObject(scope.engine)); + ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) - return Encode::undefined(); + RETURN_UNDEFINED(); uint len = instance->getLength(); - ScopedFunctionObject callback(scope, ctx->argument(0)); + ScopedFunctionObject callback(scope, callData->argument(0)); if (!callback) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - ScopedCallData callData(scope, 3); - callData->thisObject = ctx->argument(1); - callData->args[2] = instance; + ScopedCallData cData(scope, 3); + cData->thisObject = callData->argument(1); + cData->args[2] = instance; ScopedValue v(scope); for (uint k = 0; k < len; ++k) { @@ -859,33 +844,32 @@ ReturnedValue ArrayPrototype::method_forEach(CallContext *ctx) if (!exists) continue; - callData->args[0] = v; - callData->args[1] = Primitive::fromDouble(k); - callback->call(scope, callData); + cData->args[0] = v; + cData->args[1] = Primitive::fromDouble(k); + callback->call(scope, cData); } - return Encode::undefined(); + RETURN_UNDEFINED(); } -ReturnedValue ArrayPrototype::method_map(CallContext *ctx) +void ArrayPrototype::method_map(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject instance(scope, ctx->thisObject().toObject(scope.engine)); + ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) - return Encode::undefined(); + RETURN_UNDEFINED(); uint len = instance->getLength(); - ScopedFunctionObject callback(scope, ctx->argument(0)); + ScopedFunctionObject callback(scope, callData->argument(0)); if (!callback) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - ScopedArrayObject a(scope, ctx->d()->engine->newArrayObject()); + ScopedArrayObject a(scope, scope.engine->newArrayObject()); a->arrayReserve(len); a->setArrayLengthUnchecked(len); - ScopedCallData callData(scope, 3); - callData->thisObject = ctx->argument(1); - callData->args[2] = instance; + ScopedCallData cData(scope, 3); + cData->thisObject = callData->argument(1); + cData->args[2] = instance; ScopedValue v(scope); for (uint k = 0; k < len; ++k) { @@ -894,33 +878,32 @@ ReturnedValue ArrayPrototype::method_map(CallContext *ctx) if (!exists) continue; - callData->args[0] = v; - callData->args[1] = Primitive::fromDouble(k); - callback->call(scope, callData); + cData->args[0] = v; + cData->args[1] = Primitive::fromDouble(k); + callback->call(scope, cData); a->arraySet(k, scope.result); } - return a.asReturnedValue(); + scope.result = a.asReturnedValue(); } -ReturnedValue ArrayPrototype::method_filter(CallContext *ctx) +void ArrayPrototype::method_filter(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject instance(scope, ctx->thisObject().toObject(scope.engine)); + ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) - return Encode::undefined(); + RETURN_UNDEFINED(); uint len = instance->getLength(); - ScopedFunctionObject callback(scope, ctx->argument(0)); + ScopedFunctionObject callback(scope, callData->argument(0)); if (!callback) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - ScopedArrayObject a(scope, ctx->d()->engine->newArrayObject()); + ScopedArrayObject a(scope, scope.engine->newArrayObject()); a->arrayReserve(len); - ScopedCallData callData(scope, 3); - callData->thisObject = ctx->argument(1); - callData->args[2] = instance; + ScopedCallData cData(scope, 3); + cData->thisObject = callData->argument(1); + cData->args[2] = instance; ScopedValue v(scope); @@ -931,35 +914,34 @@ ReturnedValue ArrayPrototype::method_filter(CallContext *ctx) if (!exists) continue; - callData->args[0] = v; - callData->args[1] = Primitive::fromDouble(k); - callback->call(scope, callData); + cData->args[0] = v; + cData->args[1] = Primitive::fromDouble(k); + callback->call(scope, cData); if (scope.result.toBoolean()) { a->arraySet(to, v); ++to; } } - return a.asReturnedValue(); + scope.result = a.asReturnedValue(); } -ReturnedValue ArrayPrototype::method_reduce(CallContext *ctx) +void ArrayPrototype::method_reduce(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject instance(scope, ctx->thisObject().toObject(scope.engine)); + ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) - return Encode::undefined(); + RETURN_UNDEFINED(); uint len = instance->getLength(); - ScopedFunctionObject callback(scope, ctx->argument(0)); + ScopedFunctionObject callback(scope, callData->argument(0)); if (!callback) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); uint k = 0; ScopedValue v(scope); - if (ctx->argc() > 1) { - scope.result = ctx->argument(1); + if (callData->argc > 1) { + scope.result = callData->argument(1); } else { bool kPresent = false; while (k < len && !kPresent) { @@ -969,51 +951,50 @@ ReturnedValue ArrayPrototype::method_reduce(CallContext *ctx) ++k; } if (!kPresent) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); } - ScopedCallData callData(scope, 4); - callData->thisObject = Primitive::undefinedValue(); - callData->args[0] = scope.result; - callData->args[3] = instance; + ScopedCallData cData(scope, 4); + cData->thisObject = Primitive::undefinedValue(); + cData->args[0] = scope.result; + cData->args[3] = instance; while (k < len) { bool kPresent; v = instance->getIndexed(k, &kPresent); if (kPresent) { - callData->args[0] = scope.result; - callData->args[1] = v; - callData->args[2] = Primitive::fromDouble(k); - callback->call(scope, callData); + cData->args[0] = scope.result; + cData->args[1] = v; + cData->args[2] = Primitive::fromDouble(k); + callback->call(scope, cData); } ++k; } - return scope.result.asReturnedValue(); } -ReturnedValue ArrayPrototype::method_reduceRight(CallContext *ctx) +void ArrayPrototype::method_reduceRight(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject instance(scope, ctx->thisObject().toObject(scope.engine)); + ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) - return Encode::undefined(); + RETURN_UNDEFINED(); uint len = instance->getLength(); - ScopedFunctionObject callback(scope, ctx->argument(0)); + ScopedFunctionObject callback(scope, callData->argument(0)); if (!callback) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); if (len == 0) { - if (ctx->argc() == 1) - return ctx->engine()->throwTypeError(); - return ctx->argument(1); + if (callData->argc == 1) + THROW_TYPE_ERROR(); + scope.result = callData->argument(1); + return; } uint k = len; ScopedValue v(scope); - if (ctx->argc() > 1) { - scope.result = ctx->argument(1); + if (callData->argc > 1) { + scope.result = callData->argument(1); } else { bool kPresent = false; while (k > 0 && !kPresent) { @@ -1023,24 +1004,24 @@ ReturnedValue ArrayPrototype::method_reduceRight(CallContext *ctx) --k; } if (!kPresent) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); } - ScopedCallData callData(scope, 4); - callData->thisObject = Primitive::undefinedValue(); - callData->args[3] = instance; + ScopedCallData cData(scope, 4); + cData->thisObject = Primitive::undefinedValue(); + cData->args[3] = instance; while (k > 0) { bool kPresent; v = instance->getIndexed(k - 1, &kPresent); if (kPresent) { - callData->args[0] = scope.result; - callData->args[1] = v; - callData->args[2] = Primitive::fromDouble(k - 1); - callback->call(scope, callData); + cData->args[0] = scope.result; + cData->args[1] = v; + cData->args[2] = Primitive::fromDouble(k - 1); + callback->call(scope, cData); } --k; } - return scope.result.asReturnedValue(); + scope.result = scope.result.asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4arrayobject_p.h b/src/qml/jsruntime/qv4arrayobject_p.h index f00c0c0249..689752433b 100644 --- a/src/qml/jsruntime/qv4arrayobject_p.h +++ b/src/qml/jsruntime/qv4arrayobject_p.h @@ -78,30 +78,30 @@ struct ArrayPrototype: ArrayObject { void init(ExecutionEngine *engine, Object *ctor); - static ReturnedValue method_isArray(CallContext *ctx); - static ReturnedValue method_toString(CallContext *ctx); - static ReturnedValue method_toLocaleString(CallContext *ctx); - static ReturnedValue method_concat(CallContext *ctx); - static ReturnedValue method_find(CallContext *ctx); - static ReturnedValue method_findIndex(CallContext *ctx); - static ReturnedValue method_join(CallContext *ctx); - static ReturnedValue method_pop(CallContext *ctx); - static ReturnedValue method_push(CallContext *ctx); - static ReturnedValue method_reverse(CallContext *ctx); - static ReturnedValue method_shift(CallContext *ctx); - static ReturnedValue method_slice(CallContext *ctx); - static ReturnedValue method_sort(CallContext *ctx); - static ReturnedValue method_splice(CallContext *ctx); - static ReturnedValue method_unshift(CallContext *ctx); - static ReturnedValue method_indexOf(CallContext *ctx); - static ReturnedValue method_lastIndexOf(CallContext *ctx); - static ReturnedValue method_every(CallContext *ctx); - static ReturnedValue method_some(CallContext *ctx); - static ReturnedValue method_forEach(CallContext *ctx); - static ReturnedValue method_map(CallContext *ctx); - static ReturnedValue method_filter(CallContext *ctx); - static ReturnedValue method_reduce(CallContext *ctx); - static ReturnedValue method_reduceRight(CallContext *ctx); + 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); }; diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp index 8047993266..601066110f 100644 --- a/src/qml/jsruntime/qv4booleanobject.cpp +++ b/src/qml/jsruntime/qv4booleanobject.cpp @@ -73,29 +73,31 @@ void BooleanPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(engine->id_valueOf(), method_valueOf); } -ReturnedValue BooleanPrototype::method_toString(CallContext *ctx) +void BooleanPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) { bool result; - if (ctx->thisObject().isBoolean()) { - result = ctx->thisObject().booleanValue(); + if (callData->thisObject.isBoolean()) { + result = callData->thisObject.booleanValue(); } else { - const BooleanObject *thisObject = ctx->thisObject().as<BooleanObject>(); + const BooleanObject *thisObject = callData->thisObject.as<BooleanObject>(); if (!thisObject) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); result = thisObject->value(); } - return Encode(ctx->d()->engine->newString(QLatin1String(result ? "true" : "false"))); + scope.result = scope.engine->newString(QLatin1String(result ? "true" : "false")); } -ReturnedValue BooleanPrototype::method_valueOf(CallContext *ctx) +void BooleanPrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->thisObject().isBoolean()) - return ctx->thisObject().asReturnedValue(); + if (callData->thisObject.isBoolean()) { + scope.result = callData->thisObject.asReturnedValue(); + return; + } - const BooleanObject *thisObject = ctx->thisObject().as<BooleanObject>(); + const BooleanObject *thisObject = callData->thisObject.as<BooleanObject>(); if (!thisObject) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - return Encode(thisObject->value()); + scope.result = Encode(thisObject->value()); } diff --git a/src/qml/jsruntime/qv4booleanobject_p.h b/src/qml/jsruntime/qv4booleanobject_p.h index 4c2f3c09e7..9c8b1d67f1 100644 --- a/src/qml/jsruntime/qv4booleanobject_p.h +++ b/src/qml/jsruntime/qv4booleanobject_p.h @@ -78,8 +78,8 @@ struct BooleanPrototype: BooleanObject { void init(ExecutionEngine *engine, Object *ctor); - static ReturnedValue method_toString(CallContext *ctx); - static ReturnedValue method_valueOf(CallContext *ctx); + static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); + static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData); }; diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 544d39339b..60b90e4bf0 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -95,6 +95,17 @@ Heap::CallContext *ExecutionContext::newCallContext(Function *function, CallData return c; } +Heap::CallContext *Heap::CallContext::createSimpleContext(ExecutionEngine *v4) +{ + Heap::CallContext *ctxt = v4->memoryManager->allocSimpleCallContext(v4); + return ctxt; +} + +void Heap::CallContext::freeSimpleCallContext() +{ + engine->memoryManager->freeSimpleCallContext(); +} + Heap::WithContext *ExecutionContext::newWithContext(Heap::Object *with) { return d()->engine->memoryManager->alloc<WithContext>(d(), with); @@ -325,26 +336,27 @@ void QV4::ExecutionContext::simpleCall(Scope &scope, CallData *callData, Functio ExecutionContextSaver ctxSaver(scope); - CallContext::Data ctx = CallContext::Data::createOnStack(scope.engine); + CallContext::Data *ctx = scope.engine->memoryManager->allocSimpleCallContext(scope.engine); - 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 = this->d(); - ctx.locals = scope.alloc(function->compiledFunction->nLocals); + 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 = this->d(); + ctx->locals = scope.alloc(function->compiledFunction->nLocals); 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.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) diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index c985fdb24d..bcfee2e1f8 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -109,14 +109,8 @@ struct ExecutionContext : Base { { Base::init(); - callData = nullptr; this->engine = engine; - outer = nullptr; - lookups = nullptr; - constantTable = nullptr; - compilationUnit = nullptr; type = t; - strictMode = false; lineNumber = -1; } @@ -135,15 +129,12 @@ struct ExecutionContext : Base { V4_ASSERT_IS_TRIVIAL(ExecutionContext) struct CallContext : ExecutionContext { - static CallContext createOnStack(ExecutionEngine *v4); + static CallContext *createSimpleContext(ExecutionEngine *v4); + void freeSimpleCallContext(); void init(ExecutionEngine *engine, ContextType t = Type_SimpleCallContext) { ExecutionContext::init(engine, t); - function = 0; - v4Function = 0; - locals = 0; - activation = 0; } inline unsigned int formalParameterCount() const; @@ -247,6 +238,7 @@ struct Q_QML_EXPORT CallContext : public ExecutionContext inline ReturnedValue argument(int i) const; bool needsOwnArguments() const; + }; inline ReturnedValue CallContext::argument(int i) const { @@ -289,16 +281,6 @@ inline const WithContext *ExecutionContext::asWithContext() const return d()->type == Heap::ExecutionContext::Type_WithContext ? static_cast<const WithContext *>(this) : 0; } -inline Heap::CallContext Heap::CallContext::createOnStack(ExecutionEngine *v4) -{ - Heap::CallContext ctxt; - memset(&ctxt, 0, sizeof(Heap::CallContext)); - ctxt.mm_data = 0; - ctxt.setVtable(QV4::CallContext::staticVTable()); - ctxt.init(v4); - return ctxt; -} - } // namespace QV4 QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4dataview.cpp b/src/qml/jsruntime/qv4dataview.cpp index db8376272d..a810b38f24 100644 --- a/src/qml/jsruntime/qv4dataview.cpp +++ b/src/qml/jsruntime/qv4dataview.cpp @@ -129,90 +129,84 @@ void DataViewPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("setUInt32"), method_set<unsigned int>, 0); } -ReturnedValue DataViewPrototype::method_get_buffer(CallContext *ctx) +void DataViewPrototype::method_get_buffer(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<DataView> v(scope, ctx->thisObject()); + Scoped<DataView> v(scope, callData->thisObject); if (!v) - return scope.engine->throwTypeError(); + THROW_TYPE_ERROR(); - return Encode(v->d()->buffer->asReturnedValue()); + scope.result = v->d()->buffer; } -ReturnedValue DataViewPrototype::method_get_byteLength(CallContext *ctx) +void DataViewPrototype::method_get_byteLength(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<DataView> v(scope, ctx->thisObject()); + Scoped<DataView> v(scope, callData->thisObject); if (!v) - return scope.engine->throwTypeError(); + THROW_TYPE_ERROR(); - return Encode(v->d()->byteLength); + scope.result = Encode(v->d()->byteLength); } -ReturnedValue DataViewPrototype::method_get_byteOffset(CallContext *ctx) +void DataViewPrototype::method_get_byteOffset(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<DataView> v(scope, ctx->thisObject()); + Scoped<DataView> v(scope, callData->thisObject); if (!v) - return scope.engine->throwTypeError(); + THROW_TYPE_ERROR(); - return Encode(v->d()->byteOffset); + scope.result = Encode(v->d()->byteOffset); } template <typename T> -ReturnedValue DataViewPrototype::method_getChar(CallContext *ctx) +void DataViewPrototype::method_getChar(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<DataView> v(scope, ctx->thisObject()); - if (!v || ctx->argc() < 1) - return scope.engine->throwTypeError(); - double l = ctx->args()[0].toNumber(); + Scoped<DataView> v(scope, callData->thisObject); + if (!v || callData->argc < 1) + THROW_TYPE_ERROR(); + double l = callData->args[0].toNumber(); uint idx = (uint)l; if (l != idx || idx + sizeof(T) > v->d()->byteLength) - return scope.engine->throwTypeError(); + THROW_TYPE_ERROR(); idx += v->d()->byteOffset; T t = T(v->d()->buffer->data->data()[idx]); - return Encode((int)t); + scope.result = Encode((int)t); } template <typename T> -ReturnedValue DataViewPrototype::method_get(CallContext *ctx) +void DataViewPrototype::method_get(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<DataView> v(scope, ctx->thisObject()); - if (!v || ctx->argc() < 1) - return scope.engine->throwTypeError(); - double l = ctx->args()[0].toNumber(); + Scoped<DataView> v(scope, callData->thisObject); + if (!v || callData->argc < 1) + THROW_TYPE_ERROR(); + double l = callData->args[0].toNumber(); uint idx = (uint)l; if (l != idx || idx + sizeof(T) > v->d()->byteLength) - return scope.engine->throwTypeError(); + THROW_TYPE_ERROR(); idx += v->d()->byteOffset; - bool littleEndian = ctx->argc() < 2 ? false : ctx->args()[1].toBoolean(); + bool littleEndian = callData->argc < 2 ? false : callData->args[1].toBoolean(); T t = littleEndian ? qFromLittleEndian<T>((uchar *)v->d()->buffer->data->data() + idx) : qFromBigEndian<T>((uchar *)v->d()->buffer->data->data() + idx); - return Encode(t); + scope.result = Encode(t); } template <typename T> -ReturnedValue DataViewPrototype::method_getFloat(CallContext *ctx) +void DataViewPrototype::method_getFloat(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<DataView> v(scope, ctx->thisObject()); - if (!v || ctx->argc() < 1) - return scope.engine->throwTypeError(); - double l = ctx->args()[0].toNumber(); + Scoped<DataView> v(scope, callData->thisObject); + if (!v || callData->argc < 1) + THROW_TYPE_ERROR(); + double l = callData->args[0].toNumber(); uint idx = (uint)l; if (l != idx || idx + sizeof(T) > v->d()->byteLength) - return scope.engine->throwTypeError(); + THROW_TYPE_ERROR(); idx += v->d()->byteOffset; - bool littleEndian = ctx->argc() < 2 ? false : ctx->args()[1].toBoolean(); + bool littleEndian = callData->argc < 2 ? false : callData->args[1].toBoolean(); if (sizeof(T) == 4) { // float @@ -223,7 +217,7 @@ ReturnedValue DataViewPrototype::method_getFloat(CallContext *ctx) u.i = littleEndian ? qFromLittleEndian<uint>((uchar *)v->d()->buffer->data->data() + idx) : qFromBigEndian<uint>((uchar *)v->d()->buffer->data->data() + idx); - return Encode(u.f); + scope.result = Encode(u.f); } else { Q_ASSERT(sizeof(T) == 8); union { @@ -233,69 +227,66 @@ ReturnedValue DataViewPrototype::method_getFloat(CallContext *ctx) u.i = littleEndian ? qFromLittleEndian<quint64>((uchar *)v->d()->buffer->data->data() + idx) : qFromBigEndian<quint64>((uchar *)v->d()->buffer->data->data() + idx); - return Encode(u.d); + scope.result = Encode(u.d); } } template <typename T> -ReturnedValue DataViewPrototype::method_setChar(CallContext *ctx) +void DataViewPrototype::method_setChar(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<DataView> v(scope, ctx->thisObject()); - if (!v || ctx->argc() < 1) - return scope.engine->throwTypeError(); - double l = ctx->args()[0].toNumber(); + Scoped<DataView> v(scope, callData->thisObject); + if (!v || callData->argc < 1) + THROW_TYPE_ERROR(); + double l = callData->args[0].toNumber(); uint idx = (uint)l; if (l != idx || idx + sizeof(T) > v->d()->byteLength) - return scope.engine->throwTypeError(); + THROW_TYPE_ERROR(); idx += v->d()->byteOffset; - int val = ctx->argc() >= 2 ? ctx->args()[1].toInt32() : 0; + int val = callData->argc >= 2 ? callData->args[1].toInt32() : 0; v->d()->buffer->data->data()[idx] = (char)val; - return Encode::undefined(); + RETURN_UNDEFINED(); } template <typename T> -ReturnedValue DataViewPrototype::method_set(CallContext *ctx) +void DataViewPrototype::method_set(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<DataView> v(scope, ctx->thisObject()); - if (!v || ctx->argc() < 1) - return scope.engine->throwTypeError(); - double l = ctx->args()[0].toNumber(); + Scoped<DataView> v(scope, callData->thisObject); + if (!v || callData->argc < 1) + THROW_TYPE_ERROR(); + double l = callData->args[0].toNumber(); uint idx = (uint)l; if (l != idx || idx + sizeof(T) > v->d()->byteLength) - return scope.engine->throwTypeError(); + THROW_TYPE_ERROR(); idx += v->d()->byteOffset; - int val = ctx->argc() >= 2 ? ctx->args()[1].toInt32() : 0; + int val = callData->argc >= 2 ? callData->args[1].toInt32() : 0; - bool littleEndian = ctx->argc() < 3 ? false : ctx->args()[2].toBoolean(); + bool littleEndian = callData->argc < 3 ? false : callData->args[2].toBoolean(); if (littleEndian) qToLittleEndian<T>(val, (uchar *)v->d()->buffer->data->data() + idx); else qToBigEndian<T>(val, (uchar *)v->d()->buffer->data->data() + idx); - return Encode::undefined(); + RETURN_UNDEFINED(); } template <typename T> -ReturnedValue DataViewPrototype::method_setFloat(CallContext *ctx) +void DataViewPrototype::method_setFloat(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<DataView> v(scope, ctx->thisObject()); - if (!v || ctx->argc() < 1) - return scope.engine->throwTypeError(); - double l = ctx->args()[0].toNumber(); + Scoped<DataView> v(scope, callData->thisObject); + if (!v || callData->argc < 1) + THROW_TYPE_ERROR(); + double l = callData->args[0].toNumber(); uint idx = (uint)l; if (l != idx || idx + sizeof(T) > v->d()->byteLength) - return scope.engine->throwTypeError(); + THROW_TYPE_ERROR(); idx += v->d()->byteOffset; - double val = ctx->argc() >= 2 ? ctx->args()[1].toNumber() : qt_qnan(); - bool littleEndian = ctx->argc() < 3 ? false : ctx->args()[2].toBoolean(); + double val = callData->argc >= 2 ? callData->args[1].toNumber() : qt_qnan(); + bool littleEndian = callData->argc < 3 ? false : callData->args[2].toBoolean(); if (sizeof(T) == 4) { // float @@ -320,5 +311,5 @@ ReturnedValue DataViewPrototype::method_setFloat(CallContext *ctx) else qToBigEndian(u.i, (uchar *)v->d()->buffer->data->data() + idx); } - return Encode::undefined(); + RETURN_UNDEFINED(); } diff --git a/src/qml/jsruntime/qv4dataview_p.h b/src/qml/jsruntime/qv4dataview_p.h index 246124394a..11cc0a6bd9 100644 --- a/src/qml/jsruntime/qv4dataview_p.h +++ b/src/qml/jsruntime/qv4dataview_p.h @@ -92,21 +92,21 @@ struct DataViewPrototype: Object { void init(ExecutionEngine *engine, Object *ctor); - static ReturnedValue method_get_buffer(CallContext *ctx); - static ReturnedValue method_get_byteLength(CallContext *ctx); - static ReturnedValue method_get_byteOffset(CallContext *ctx); + 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); template <typename T> - static ReturnedValue method_getChar(CallContext *ctx); + static void method_getChar(const BuiltinFunction *, Scope &scope, CallData *callData); template <typename T> - static ReturnedValue method_get(CallContext *ctx); + static void method_get(const BuiltinFunction *, Scope &scope, CallData *callData); template <typename T> - static ReturnedValue method_getFloat(CallContext *ctx); + static void method_getFloat(const BuiltinFunction *, Scope &scope, CallData *callData); template <typename T> - static ReturnedValue method_setChar(CallContext *ctx); + static void method_setChar(const BuiltinFunction *, Scope &scope, CallData *callData); template <typename T> - static ReturnedValue method_set(CallContext *ctx); + static void method_set(const BuiltinFunction *, Scope &scope, CallData *callData); template <typename T> - static ReturnedValue method_setFloat(CallContext *ctx); + static void method_setFloat(const BuiltinFunction *, Scope &scope, CallData *callData); }; diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index 8cc6a25fea..b90c335b1c 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -780,435 +780,435 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("toJSON"), method_toJSON, 1); } -double DatePrototype::getThisDate(ExecutionContext *ctx) +double DatePrototype::getThisDate(Scope &scope, CallData *callData) { - if (DateObject *thisObject = ctx->thisObject().as<DateObject>()) + if (DateObject *thisObject = callData->thisObject.as<DateObject>()) return thisObject->date(); else { - ctx->engine()->throwTypeError(); + scope.engine->throwTypeError(); return 0; } } -ReturnedValue DatePrototype::method_parse(CallContext *ctx) +void DatePrototype::method_parse(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (!ctx->argc()) - return Encode(qt_qnan()); - return Encode(ParseString(ctx->args()[0].toQString())); + if (!callData->argc) + scope.result = Encode(qt_qnan()); + else + scope.result = Encode(ParseString(callData->args[0].toQString())); } -ReturnedValue DatePrototype::method_UTC(CallContext *ctx) +void DatePrototype::method_UTC(const BuiltinFunction *, Scope &scope, CallData *callData) { - const int numArgs = ctx->argc(); + const int numArgs = callData->argc; if (numArgs >= 2) { - double year = ctx->args()[0].toNumber(); - double month = ctx->args()[1].toNumber(); - double day = numArgs >= 3 ? ctx->args()[2].toNumber() : 1; - double hours = numArgs >= 4 ? ctx->args()[3].toNumber() : 0; - double mins = numArgs >= 5 ? ctx->args()[4].toNumber() : 0; - double secs = numArgs >= 6 ? ctx->args()[5].toNumber() : 0; - double ms = numArgs >= 7 ? ctx->args()[6].toNumber() : 0; + 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; if (year >= 0 && year <= 99) year += 1900; double t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); - return Encode(TimeClip(t)); + scope.result = Encode(TimeClip(t)); + return; } - return Encode::undefined(); + RETURN_UNDEFINED(); } -ReturnedValue DatePrototype::method_now(CallContext *ctx) +void DatePrototype::method_now(const BuiltinFunction *, Scope &scope, CallData *callData) { - Q_UNUSED(ctx); + Q_UNUSED(callData); double t = currentTime(); - return Encode(t); + scope.result = Encode(t); } -ReturnedValue DatePrototype::method_toString(CallContext *ctx) +void DatePrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - return ctx->d()->engine->newString(ToString(t))->asReturnedValue(); + double t = getThisDate(scope, callData); + scope.result = scope.engine->newString(ToString(t)); } -ReturnedValue DatePrototype::method_toDateString(CallContext *ctx) +void DatePrototype::method_toDateString(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - return ctx->d()->engine->newString(ToDateString(t))->asReturnedValue(); + double t = getThisDate(scope, callData); + scope.result = scope.engine->newString(ToDateString(t)); } -ReturnedValue DatePrototype::method_toTimeString(CallContext *ctx) +void DatePrototype::method_toTimeString(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - return ctx->d()->engine->newString(ToTimeString(t))->asReturnedValue(); + double t = getThisDate(scope, callData); + scope.result = scope.engine->newString(ToTimeString(t)); } -ReturnedValue DatePrototype::method_toLocaleString(CallContext *ctx) +void DatePrototype::method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - return ctx->d()->engine->newString(ToLocaleString(t))->asReturnedValue(); + double t = getThisDate(scope, callData); + scope.result = scope.engine->newString(ToLocaleString(t)); } -ReturnedValue DatePrototype::method_toLocaleDateString(CallContext *ctx) +void DatePrototype::method_toLocaleDateString(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - return ctx->d()->engine->newString(ToLocaleDateString(t))->asReturnedValue(); + double t = getThisDate(scope, callData); + scope.result = scope.engine->newString(ToLocaleDateString(t)); } -ReturnedValue DatePrototype::method_toLocaleTimeString(CallContext *ctx) +void DatePrototype::method_toLocaleTimeString(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - return ctx->d()->engine->newString(ToLocaleTimeString(t))->asReturnedValue(); + double t = getThisDate(scope, callData); + scope.result = scope.engine->newString(ToLocaleTimeString(t)); } -ReturnedValue DatePrototype::method_valueOf(CallContext *ctx) +void DatePrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - return Encode(t); + double t = getThisDate(scope, callData); + scope.result = Encode(t); } -ReturnedValue DatePrototype::method_getTime(CallContext *ctx) +void DatePrototype::method_getTime(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - return Encode(t); + double t = getThisDate(scope, callData); + scope.result = Encode(t); } -ReturnedValue DatePrototype::method_getYear(CallContext *ctx) +void DatePrototype::method_getYear(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - if (! std::isnan(t)) + double t = getThisDate(scope, callData); + if (!std::isnan(t)) t = YearFromTime(LocalTime(t)) - 1900; - return Encode(t); + scope.result = Encode(t); } -ReturnedValue DatePrototype::method_getFullYear(CallContext *ctx) +void DatePrototype::method_getFullYear(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - if (! std::isnan(t)) + double t = getThisDate(scope, callData); + if (!std::isnan(t)) t = YearFromTime(LocalTime(t)); - return Encode(t); + scope.result = Encode(t); } -ReturnedValue DatePrototype::method_getUTCFullYear(CallContext *ctx) +void DatePrototype::method_getUTCFullYear(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - if (! std::isnan(t)) + double t = getThisDate(scope, callData); + if (!std::isnan(t)) t = YearFromTime(t); - return Encode(t); + scope.result = Encode(t); } -ReturnedValue DatePrototype::method_getMonth(CallContext *ctx) +void DatePrototype::method_getMonth(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - if (! std::isnan(t)) + double t = getThisDate(scope, callData); + if (!std::isnan(t)) t = MonthFromTime(LocalTime(t)); - return Encode(t); + scope.result = Encode(t); } -ReturnedValue DatePrototype::method_getUTCMonth(CallContext *ctx) +void DatePrototype::method_getUTCMonth(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - if (! std::isnan(t)) + double t = getThisDate(scope, callData); + if (!std::isnan(t)) t = MonthFromTime(t); - return Encode(t); + scope.result = Encode(t); } -ReturnedValue DatePrototype::method_getDate(CallContext *ctx) +void DatePrototype::method_getDate(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - if (! std::isnan(t)) + double t = getThisDate(scope, callData); + if (!std::isnan(t)) t = DateFromTime(LocalTime(t)); - return Encode(t); + scope.result = Encode(t); } -ReturnedValue DatePrototype::method_getUTCDate(CallContext *ctx) +void DatePrototype::method_getUTCDate(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - if (! std::isnan(t)) + double t = getThisDate(scope, callData); + if (!std::isnan(t)) t = DateFromTime(t); - return Encode(t); + scope.result = Encode(t); } -ReturnedValue DatePrototype::method_getDay(CallContext *ctx) +void DatePrototype::method_getDay(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - if (! std::isnan(t)) + double t = getThisDate(scope, callData); + if (!std::isnan(t)) t = WeekDay(LocalTime(t)); - return Encode(t); + scope.result = Encode(t); } -ReturnedValue DatePrototype::method_getUTCDay(CallContext *ctx) +void DatePrototype::method_getUTCDay(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - if (! std::isnan(t)) + double t = getThisDate(scope, callData); + if (!std::isnan(t)) t = WeekDay(t); - return Encode(t); + scope.result = Encode(t); } -ReturnedValue DatePrototype::method_getHours(CallContext *ctx) +void DatePrototype::method_getHours(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - if (! std::isnan(t)) + double t = getThisDate(scope, callData); + if (!std::isnan(t)) t = HourFromTime(LocalTime(t)); - return Encode(t); + scope.result = Encode(t); } -ReturnedValue DatePrototype::method_getUTCHours(CallContext *ctx) +void DatePrototype::method_getUTCHours(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - if (! std::isnan(t)) + double t = getThisDate(scope, callData); + if (!std::isnan(t)) t = HourFromTime(t); - return Encode(t); + scope.result = Encode(t); } -ReturnedValue DatePrototype::method_getMinutes(CallContext *ctx) +void DatePrototype::method_getMinutes(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - if (! std::isnan(t)) + double t = getThisDate(scope, callData); + if (!std::isnan(t)) t = MinFromTime(LocalTime(t)); - return Encode(t); + scope.result = Encode(t); } -ReturnedValue DatePrototype::method_getUTCMinutes(CallContext *ctx) +void DatePrototype::method_getUTCMinutes(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - if (! std::isnan(t)) + double t = getThisDate(scope, callData); + if (!std::isnan(t)) t = MinFromTime(t); - return Encode(t); + scope.result = Encode(t); } -ReturnedValue DatePrototype::method_getSeconds(CallContext *ctx) +void DatePrototype::method_getSeconds(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - if (! std::isnan(t)) + double t = getThisDate(scope, callData); + if (!std::isnan(t)) t = SecFromTime(LocalTime(t)); - return Encode(t); + scope.result = Encode(t); } -ReturnedValue DatePrototype::method_getUTCSeconds(CallContext *ctx) +void DatePrototype::method_getUTCSeconds(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - if (! std::isnan(t)) + double t = getThisDate(scope, callData); + if (!std::isnan(t)) t = SecFromTime(t); - return Encode(t); + scope.result = Encode(t); } -ReturnedValue DatePrototype::method_getMilliseconds(CallContext *ctx) +void DatePrototype::method_getMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - if (! std::isnan(t)) + double t = getThisDate(scope, callData); + if (!std::isnan(t)) t = msFromTime(LocalTime(t)); - return Encode(t); + scope.result = Encode(t); } -ReturnedValue DatePrototype::method_getUTCMilliseconds(CallContext *ctx) +void DatePrototype::method_getUTCMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - if (! std::isnan(t)) + double t = getThisDate(scope, callData); + if (!std::isnan(t)) t = msFromTime(t); - return Encode(t); + scope.result = Encode(t); } -ReturnedValue DatePrototype::method_getTimezoneOffset(CallContext *ctx) +void DatePrototype::method_getTimezoneOffset(const BuiltinFunction *, Scope &scope, CallData *callData) { - double t = getThisDate(ctx); - if (! std::isnan(t)) + double t = getThisDate(scope, callData); + if (!std::isnan(t)) t = (t - LocalTime(t)) / msPerMinute; - return Encode(t); + scope.result = Encode(t); } -ReturnedValue DatePrototype::method_setTime(CallContext *ctx) +void DatePrototype::method_setTime(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<DateObject> self(scope, ctx->thisObject()); + Scoped<DateObject> self(scope, callData->thisObject); if (!self) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - double t = ctx->argc() ? ctx->args()[0].toNumber() : qt_qnan(); + double t = callData->argc ? callData->args[0].toNumber() : qt_qnan(); self->setDate(TimeClip(t)); - return Encode(self->date()); + scope.result = Encode(self->date()); } -ReturnedValue DatePrototype::method_setMilliseconds(CallContext *ctx) +void DatePrototype::method_setMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<DateObject> self(scope, ctx->thisObject()); + Scoped<DateObject> self(scope, callData->thisObject); if (!self) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); double t = LocalTime(self->date()); - double ms = ctx->argc() ? ctx->args()[0].toNumber() : qt_qnan(); + double ms = callData->argc ? callData->args[0].toNumber() : qt_qnan(); self->setDate(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); - return Encode(self->date()); + scope.result = Encode(self->date()); } -ReturnedValue DatePrototype::method_setUTCMilliseconds(CallContext *ctx) +void DatePrototype::method_setUTCMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData) { - DateObject *self = ctx->thisObject().as<DateObject>(); + DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); double t = self->date(); - double ms = ctx->argc() ? ctx->args()[0].toNumber() : qt_qnan(); + double ms = callData->argc ? callData->args[0].toNumber() : qt_qnan(); self->setDate(TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))); - return Encode(self->date()); + scope.result = Encode(self->date()); } -ReturnedValue DatePrototype::method_setSeconds(CallContext *ctx) +void DatePrototype::method_setSeconds(const BuiltinFunction *, Scope &scope, CallData *callData) { - DateObject *self = ctx->thisObject().as<DateObject>(); + DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); double t = LocalTime(self->date()); - double sec = ctx->argc() ? ctx->args()[0].toNumber() : qt_qnan(); - double ms = (ctx->argc() < 2) ? msFromTime(t) : ctx->args()[1].toNumber(); + double sec = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double ms = (callData->argc < 2) ? msFromTime(t) : callData->args[1].toNumber(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); self->setDate(t); - return Encode(self->date()); + scope.result = Encode(self->date()); } -ReturnedValue DatePrototype::method_setUTCSeconds(CallContext *ctx) +void DatePrototype::method_setUTCSeconds(const BuiltinFunction *, Scope &scope, CallData *callData) { - DateObject *self = ctx->thisObject().as<DateObject>(); + DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); double t = self->date(); - double sec = ctx->argc() ? ctx->args()[0].toNumber() : qt_qnan(); - double ms = (ctx->argc() < 2) ? msFromTime(t) : ctx->args()[1].toNumber(); + double sec = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double ms = (callData->argc < 2) ? msFromTime(t) : callData->args[1].toNumber(); t = TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms))); self->setDate(t); - return Encode(self->date()); + scope.result = Encode(self->date()); } -ReturnedValue DatePrototype::method_setMinutes(CallContext *ctx) +void DatePrototype::method_setMinutes(const BuiltinFunction *, Scope &scope, CallData *callData) { - DateObject *self = ctx->thisObject().as<DateObject>(); + DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); double t = LocalTime(self->date()); - double min = ctx->argc() ? ctx->args()[0].toNumber() : qt_qnan(); - double sec = (ctx->argc() < 2) ? SecFromTime(t) : ctx->args()[1].toNumber(); - double ms = (ctx->argc() < 3) ? msFromTime(t) : ctx->args()[2].toNumber(); + 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(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); self->setDate(t); - return Encode(self->date()); + scope.result = Encode(self->date()); } -ReturnedValue DatePrototype::method_setUTCMinutes(CallContext *ctx) +void DatePrototype::method_setUTCMinutes(const BuiltinFunction *, Scope &scope, CallData *callData) { - DateObject *self = ctx->thisObject().as<DateObject>(); + DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); double t = self->date(); - double min = ctx->argc() ? ctx->args()[0].toNumber() : qt_qnan(); - double sec = (ctx->argc() < 2) ? SecFromTime(t) : ctx->args()[1].toNumber(); - double ms = (ctx->argc() < 3) ? msFromTime(t) : ctx->args()[2].toNumber(); + 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(); t = TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms))); self->setDate(t); - return Encode(self->date()); + scope.result = Encode(self->date()); } -ReturnedValue DatePrototype::method_setHours(CallContext *ctx) +void DatePrototype::method_setHours(const BuiltinFunction *, Scope &scope, CallData *callData) { - DateObject *self = ctx->thisObject().as<DateObject>(); + DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); double t = LocalTime(self->date()); - double hour = ctx->argc() ? ctx->args()[0].toNumber() : qt_qnan(); - double min = (ctx->argc() < 2) ? MinFromTime(t) : ctx->args()[1].toNumber(); - double sec = (ctx->argc() < 3) ? SecFromTime(t) : ctx->args()[2].toNumber(); - double ms = (ctx->argc() < 4) ? msFromTime(t) : ctx->args()[3].toNumber(); + 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(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); self->setDate(t); - return Encode(self->date()); + scope.result = Encode(self->date()); } -ReturnedValue DatePrototype::method_setUTCHours(CallContext *ctx) +void DatePrototype::method_setUTCHours(const BuiltinFunction *, Scope &scope, CallData *callData) { - DateObject *self = ctx->thisObject().as<DateObject>(); + DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); double t = self->date(); - double hour = ctx->argc() ? ctx->args()[0].toNumber() : qt_qnan(); - double min = (ctx->argc() < 2) ? MinFromTime(t) : ctx->args()[1].toNumber(); - double sec = (ctx->argc() < 3) ? SecFromTime(t) : ctx->args()[2].toNumber(); - double ms = (ctx->argc() < 4) ? msFromTime(t) : ctx->args()[3].toNumber(); + 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(); t = TimeClip(MakeDate(Day(t), MakeTime(hour, min, sec, ms))); self->setDate(t); - return Encode(self->date()); + scope.result = Encode(self->date()); } -ReturnedValue DatePrototype::method_setDate(CallContext *ctx) +void DatePrototype::method_setDate(const BuiltinFunction *, Scope &scope, CallData *callData) { - DateObject *self = ctx->thisObject().as<DateObject>(); + DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); double t = LocalTime(self->date()); - double date = ctx->argc() ? ctx->args()[0].toNumber() : qt_qnan(); + double date = callData->argc ? callData->args[0].toNumber() : qt_qnan(); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); self->setDate(t); - return Encode(self->date()); + scope.result = Encode(self->date()); } -ReturnedValue DatePrototype::method_setUTCDate(CallContext *ctx) +void DatePrototype::method_setUTCDate(const BuiltinFunction *, Scope &scope, CallData *callData) { - DateObject *self = ctx->thisObject().as<DateObject>(); + DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); double t = self->date(); - double date = ctx->argc() ? ctx->args()[0].toNumber() : qt_qnan(); + double date = callData->argc ? callData->args[0].toNumber() : qt_qnan(); t = TimeClip(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t))); self->setDate(t); - return Encode(self->date()); + scope.result = Encode(self->date()); } -ReturnedValue DatePrototype::method_setMonth(CallContext *ctx) +void DatePrototype::method_setMonth(const BuiltinFunction *, Scope &scope, CallData *callData) { - DateObject *self = ctx->thisObject().as<DateObject>(); + DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); double t = LocalTime(self->date()); - double month = ctx->argc() ? ctx->args()[0].toNumber() : qt_qnan(); - double date = (ctx->argc() < 2) ? DateFromTime(t) : ctx->args()[1].toNumber(); + double month = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double date = (callData->argc < 2) ? DateFromTime(t) : callData->args[1].toNumber(); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); self->setDate(t); - return Encode(self->date()); + scope.result = Encode(self->date()); } -ReturnedValue DatePrototype::method_setUTCMonth(CallContext *ctx) +void DatePrototype::method_setUTCMonth(const BuiltinFunction *, Scope &scope, CallData *callData) { - DateObject *self = ctx->thisObject().as<DateObject>(); + DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); double t = self->date(); - double month = ctx->argc() ? ctx->args()[0].toNumber() : qt_qnan(); - double date = (ctx->argc() < 2) ? DateFromTime(t) : ctx->args()[1].toNumber(); + double month = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double date = (callData->argc < 2) ? DateFromTime(t) : callData->args[1].toNumber(); t = TimeClip(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t))); self->setDate(t); - return Encode(self->date()); + scope.result = Encode(self->date()); } -ReturnedValue DatePrototype::method_setYear(CallContext *ctx) +void DatePrototype::method_setYear(const BuiltinFunction *, Scope &scope, CallData *callData) { - DateObject *self = ctx->thisObject().as<DateObject>(); + DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); double t = self->date(); if (std::isnan(t)) t = 0; else t = LocalTime(t); - double year = ctx->argc() ? ctx->args()[0].toNumber() : qt_qnan(); + double year = callData->argc ? callData->args[0].toNumber() : qt_qnan(); double r; if (std::isnan(year)) { r = qt_qnan(); @@ -1220,49 +1220,49 @@ ReturnedValue DatePrototype::method_setYear(CallContext *ctx) r = TimeClip(r); } self->setDate(r); - return Encode(self->date()); + scope.result = Encode(self->date()); } -ReturnedValue DatePrototype::method_setUTCFullYear(CallContext *ctx) +void DatePrototype::method_setUTCFullYear(const BuiltinFunction *, Scope &scope, CallData *callData) { - DateObject *self = ctx->thisObject().as<DateObject>(); + DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); double t = self->date(); - double year = ctx->argc() ? ctx->args()[0].toNumber() : qt_qnan(); - double month = (ctx->argc() < 2) ? MonthFromTime(t) : ctx->args()[1].toNumber(); - double date = (ctx->argc() < 3) ? DateFromTime(t) : ctx->args()[2].toNumber(); + 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(); t = TimeClip(MakeDate(MakeDay(year, month, date), TimeWithinDay(t))); self->setDate(t); - return Encode(self->date()); + scope.result = Encode(self->date()); } -ReturnedValue DatePrototype::method_setFullYear(CallContext *ctx) +void DatePrototype::method_setFullYear(const BuiltinFunction *, Scope &scope, CallData *callData) { - DateObject *self = ctx->thisObject().as<DateObject>(); + DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); double t = LocalTime(self->date()); if (std::isnan(t)) t = 0; - double year = ctx->argc() ? ctx->args()[0].toNumber() : qt_qnan(); - double month = (ctx->argc() < 2) ? MonthFromTime(t) : ctx->args()[1].toNumber(); - double date = (ctx->argc() < 3) ? DateFromTime(t) : ctx->args()[2].toNumber(); + 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(); t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); self->setDate(t); - return Encode(self->date()); + scope.result = Encode(self->date()); } -ReturnedValue DatePrototype::method_toUTCString(CallContext *ctx) +void DatePrototype::method_toUTCString(const BuiltinFunction *, Scope &scope, CallData *callData) { - DateObject *self = ctx->thisObject().as<DateObject>(); + DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); double t = self->date(); - return ctx->d()->engine->newString(ToUTCString(t))->asReturnedValue(); + scope.result = scope.engine->newString(ToUTCString(t)); } static void addZeroPrefixedInt(QString &str, int num, int nDigits) @@ -1278,21 +1278,21 @@ static void addZeroPrefixedInt(QString &str, int num, int nDigits) } } -ReturnedValue DatePrototype::method_toISOString(CallContext *ctx) +void DatePrototype::method_toISOString(const BuiltinFunction *, Scope &scope, CallData *callData) { - DateObject *self = ctx->thisObject().as<DateObject>(); + DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); double t = self->date(); if (!std::isfinite(t)) - return ctx->engine()->throwRangeError(ctx->thisObject()); + RETURN_RESULT(scope.engine->throwRangeError(callData->thisObject)); QString result; int year = (int)YearFromTime(t); if (year < 0 || year > 9999) { if (qAbs(year) >= 1000000) - return ctx->d()->engine->newString(QStringLiteral("Invalid Date"))->asReturnedValue(); + RETURN_RESULT(scope.engine->newString(QStringLiteral("Invalid Date"))); result += year < 0 ? QLatin1Char('-') : QLatin1Char('+'); year = qAbs(year); addZeroPrefixedInt(result, year, 6); @@ -1313,32 +1313,29 @@ ReturnedValue DatePrototype::method_toISOString(CallContext *ctx) addZeroPrefixedInt(result, msFromTime(t), 3); result += QLatin1Char('Z'); - return ctx->d()->engine->newString(result)->asReturnedValue(); + scope.result = scope.engine->newString(result); } -ReturnedValue DatePrototype::method_toJSON(CallContext *ctx) +void DatePrototype::method_toJSON(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject O(scope, ctx->thisObject().toObject(scope.engine)); - if (scope.hasException()) - return Encode::undefined(); + ScopedObject O(scope, callData->thisObject.toObject(scope.engine)); + CHECK_EXCEPTION(); ScopedValue tv(scope, RuntimeHelpers::toPrimitive(O, NUMBER_HINT)); if (tv->isNumber() && !std::isfinite(tv->toNumber())) - return Encode::null(); + RETURN_RESULT(Encode::null()); - ScopedString s(scope, ctx->d()->engine->newString(QStringLiteral("toISOString"))); + ScopedString s(scope, scope.engine->newString(QStringLiteral("toISOString"))); ScopedValue v(scope, O->get(s)); FunctionObject *toIso = v->as<FunctionObject>(); if (!toIso) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - ScopedCallData callData(scope); - callData->thisObject = ctx->thisObject(); - toIso->call(scope, callData); - return scope.result.asReturnedValue(); + ScopedCallData cData(scope); + cData->thisObject = callData->thisObject; + toIso->call(scope, cData); } void DatePrototype::timezoneUpdated() diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index 835f6adbe0..a56d17f9b1 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -116,57 +116,57 @@ struct DatePrototype: DateObject { void init(ExecutionEngine *engine, Object *ctor); - static double getThisDate(ExecutionContext *ctx); - - static ReturnedValue method_parse(CallContext *ctx); - static ReturnedValue method_UTC(CallContext *ctx); - static ReturnedValue method_now(CallContext *ctx); - - static ReturnedValue method_toString(CallContext *ctx); - static ReturnedValue method_toDateString(CallContext *ctx); - static ReturnedValue method_toTimeString(CallContext *ctx); - static ReturnedValue method_toLocaleString(CallContext *ctx); - static ReturnedValue method_toLocaleDateString(CallContext *ctx); - static ReturnedValue method_toLocaleTimeString(CallContext *ctx); - static ReturnedValue method_valueOf(CallContext *ctx); - static ReturnedValue method_getTime(CallContext *ctx); - static ReturnedValue method_getYear(CallContext *ctx); - static ReturnedValue method_getFullYear(CallContext *ctx); - static ReturnedValue method_getUTCFullYear(CallContext *ctx); - static ReturnedValue method_getMonth(CallContext *ctx); - static ReturnedValue method_getUTCMonth(CallContext *ctx); - static ReturnedValue method_getDate(CallContext *ctx); - static ReturnedValue method_getUTCDate(CallContext *ctx); - static ReturnedValue method_getDay(CallContext *ctx); - static ReturnedValue method_getUTCDay(CallContext *ctx); - static ReturnedValue method_getHours(CallContext *ctx); - static ReturnedValue method_getUTCHours(CallContext *ctx); - static ReturnedValue method_getMinutes(CallContext *ctx); - static ReturnedValue method_getUTCMinutes(CallContext *ctx); - static ReturnedValue method_getSeconds(CallContext *ctx); - static ReturnedValue method_getUTCSeconds(CallContext *ctx); - static ReturnedValue method_getMilliseconds(CallContext *ctx); - static ReturnedValue method_getUTCMilliseconds(CallContext *ctx); - static ReturnedValue method_getTimezoneOffset(CallContext *ctx); - static ReturnedValue method_setTime(CallContext *ctx); - static ReturnedValue method_setMilliseconds(CallContext *ctx); - static ReturnedValue method_setUTCMilliseconds(CallContext *ctx); - static ReturnedValue method_setSeconds(CallContext *ctx); - static ReturnedValue method_setUTCSeconds(CallContext *ctx); - static ReturnedValue method_setMinutes(CallContext *ctx); - static ReturnedValue method_setUTCMinutes(CallContext *ctx); - static ReturnedValue method_setHours(CallContext *ctx); - static ReturnedValue method_setUTCHours(CallContext *ctx); - static ReturnedValue method_setDate(CallContext *ctx); - static ReturnedValue method_setUTCDate(CallContext *ctx); - static ReturnedValue method_setMonth(CallContext *ctx); - static ReturnedValue method_setUTCMonth(CallContext *ctx); - static ReturnedValue method_setYear(CallContext *ctx); - static ReturnedValue method_setFullYear(CallContext *ctx); - static ReturnedValue method_setUTCFullYear(CallContext *ctx); - static ReturnedValue method_toUTCString(CallContext *ctx); - static ReturnedValue method_toISOString(CallContext *ctx); - static ReturnedValue method_toJSON(CallContext *ctx); + 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 void timezoneUpdated(); }; diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index a11f7f0875..084ddc9010 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -91,7 +91,9 @@ #if USE(PTHREADS) # include <pthread.h> +#if !defined(Q_OS_INTEGRITY) # include <sys/resource.h> +#endif #if HAVE(PTHREAD_NP_H) # include <pthread_np.h> #endif @@ -168,7 +170,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) if (forceMoth) { factory = new Moth::ISelFactory; } else { - factory = new JIT::ISelFactory; + factory = new JIT::ISelFactory<>; jitDisabled = false; } #else // !V4_ENABLE_JIT @@ -1109,7 +1111,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int if (typeHint == qMetaTypeId<QJSValue>()) return QVariant::fromValue(QJSValue(e, value.asReturnedValue())); - if (value.as<Object>()) { + if (value.as<QV4::Object>()) { QV4::ScopedObject object(scope, value); if (typeHint == QMetaType::QJsonObject && !value.as<ArrayObject>() && !value.as<FunctionObject>()) { @@ -1755,7 +1757,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) return false; } -static bool convertToNativeQObject(QV4::ExecutionEngine *e, const Value &value, const QByteArray &targetType, void **result) +static bool convertToNativeQObject(QV4::ExecutionEngine *e, const QV4::Value &value, const QByteArray &targetType, void **result) { if (!targetType.endsWith('*')) return false; @@ -1770,7 +1772,7 @@ static bool convertToNativeQObject(QV4::ExecutionEngine *e, const Value &value, return false; } -static QObject *qtObjectFromJS(QV4::ExecutionEngine *engine, const Value &value) +static QObject *qtObjectFromJS(QV4::ExecutionEngine *engine, const QV4::Value &value) { if (!value.isObject()) return 0; diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp index 597ded6ae1..f290bc5136 100644 --- a/src/qml/jsruntime/qv4errorobject.cpp +++ b/src/qml/jsruntime/qv4errorobject.cpp @@ -153,12 +153,11 @@ const char *ErrorObject::className(Heap::ErrorObject::ErrorType t) Q_UNREACHABLE(); } -ReturnedValue ErrorObject::method_get_stack(CallContext *ctx) +void ErrorObject::method_get_stack(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<ErrorObject> This(scope, ctx->thisObject()); + Scoped<ErrorObject> This(scope, callData->thisObject); if (!This) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); if (!This->d()->stack) { QString trace; for (int i = 0; i < This->d()->stackTrace->count(); ++i) { @@ -169,9 +168,9 @@ ReturnedValue ErrorObject::method_get_stack(CallContext *ctx) if (frame.line >= 0) trace += QLatin1Char(':') + QString::number(frame.line); } - This->d()->stack = ctx->d()->engine->newString(trace); + This->d()->stack = scope.engine->newString(trace); } - return This->d()->stack->asReturnedValue(); + scope.result = This->d()->stack; } void ErrorObject::markObjects(Heap::Base *that, ExecutionEngine *e) @@ -335,22 +334,20 @@ void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj, He obj->defineDefaultProperty(engine->id_toString(), method_toString, 0); } -ReturnedValue ErrorPrototype::method_toString(CallContext *ctx) +void ErrorPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - - Object *o = ctx->thisObject().as<Object>(); + Object *o = callData->thisObject.as<Object>(); if (!o) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - ScopedValue name(scope, o->get(ctx->d()->engine->id_name())); + ScopedValue name(scope, o->get(scope.engine->id_name())); QString qname; if (name->isUndefined()) qname = QStringLiteral("Error"); else qname = name->toQString(); - ScopedString s(scope, ctx->d()->engine->newString(QStringLiteral("message"))); + ScopedString s(scope, scope.engine->newString(QStringLiteral("message"))); ScopedValue message(scope, o->get(s)); QString qmessage; if (!message->isUndefined()) @@ -365,5 +362,5 @@ ReturnedValue ErrorPrototype::method_toString(CallContext *ctx) str = qname + QLatin1String(": ") + qmessage; } - return ctx->d()->engine->newString(str)->asReturnedValue(); + scope.result = scope.engine->newString(str)->asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h index 2b3ab25e2d..9ba9f05234 100644 --- a/src/qml/jsruntime/qv4errorobject_p.h +++ b/src/qml/jsruntime/qv4errorobject_p.h @@ -172,7 +172,7 @@ struct ErrorObject: Object { static const char *className(Heap::ErrorObject::ErrorType t); - static ReturnedValue method_get_stack(CallContext *ctx); + static void method_get_stack(const BuiltinFunction *, Scope &scope, CallData *callData); static void markObjects(Heap::Base *that, ExecutionEngine *e); }; @@ -282,7 +282,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 ReturnedValue method_toString(CallContext *ctx); + static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); }; struct EvalErrorPrototype : ErrorObject diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 64f7b98618..b2d89220ea 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -270,23 +270,22 @@ void FunctionPrototype::init(ExecutionEngine *engine, Object *ctor) } -ReturnedValue FunctionPrototype::method_toString(CallContext *ctx) +void FunctionPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) { - FunctionObject *fun = ctx->thisObject().as<FunctionObject>(); + FunctionObject *fun = callData->thisObject.as<FunctionObject>(); if (!fun) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - return ctx->d()->engine->newString(QStringLiteral("function() { [code] }"))->asReturnedValue(); + scope.result = scope.engine->newString(QStringLiteral("function() { [code] }")); } -ReturnedValue FunctionPrototype::method_apply(CallContext *ctx) +void FunctionPrototype::method_apply(const BuiltinFunction *, Scope &scope, CallData *callData) { - FunctionObject *o = ctx->thisObject().as<FunctionObject>(); + FunctionObject *o = callData->thisObject.as<FunctionObject>(); if (!o) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - Scope scope(ctx); - ScopedValue arg(scope, ctx->argument(1)); + ScopedValue arg(scope, callData->argument(1)); ScopedObject arr(scope, arg); @@ -294,75 +293,71 @@ ReturnedValue FunctionPrototype::method_apply(CallContext *ctx) if (!arr) { len = 0; if (!arg->isNullOrUndefined()) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); } else { len = arr->getLength(); } - ScopedCallData callData(scope, len); + ScopedCallData cData(scope, 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(callData->args, a->d()->context->callData->args, l*sizeof(Value)); + memcpy(cData->args, a->d()->context->callData->args, l*sizeof(Value)); for (quint32 i = l; i < len; ++i) - callData->args[i] = Primitive::undefinedValue(); + cData->args[i] = Primitive::undefinedValue(); } else if (arr->arrayType() == Heap::ArrayData::Simple && !arr->protoHasArray()) { auto sad = static_cast<Heap::SimpleArrayData *>(arr->arrayData()); uint alen = sad ? sad->len : 0; if (alen > len) alen = len; for (uint i = 0; i < alen; ++i) - callData->args[i] = sad->data(i); + cData->args[i] = sad->data(i); for (quint32 i = alen; i < len; ++i) - callData->args[i] = Primitive::undefinedValue(); + cData->args[i] = Primitive::undefinedValue(); } else { for (quint32 i = 0; i < len; ++i) - callData->args[i] = arr->getIndexed(i); + cData->args[i] = arr->getIndexed(i); } } - callData->thisObject = ctx->argument(0); - o->call(scope, callData); - return scope.result.asReturnedValue(); + cData->thisObject = callData->argument(0); + o->call(scope, cData); } -ReturnedValue FunctionPrototype::method_call(CallContext *ctx) +void FunctionPrototype::method_call(const BuiltinFunction *, Scope &scope, CallData *callData) { - FunctionObject *o = ctx->thisObject().as<FunctionObject>(); + FunctionObject *o = callData->thisObject.as<FunctionObject>(); if (!o) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - Scope scope(ctx); - ScopedCallData callData(scope, ctx->argc() ? ctx->argc() - 1 : 0); - if (ctx->argc()) { - for (int i = 1; i < ctx->argc(); ++i) - callData->args[i - 1] = ctx->args()[i]; + 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]; } - callData->thisObject = ctx->argument(0); + cData->thisObject = callData->argument(0); - o->call(scope, callData); - return scope.result.asReturnedValue(); + o->call(scope, cData); } -ReturnedValue FunctionPrototype::method_bind(CallContext *ctx) +void FunctionPrototype::method_bind(const BuiltinFunction *, Scope &scope, CallData *callData) { - FunctionObject *target = ctx->thisObject().as<FunctionObject>(); + FunctionObject *target = callData->thisObject.as<FunctionObject>(); if (!target) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - Scope scope(ctx); - ScopedValue boundThis(scope, ctx->argument(0)); + ScopedValue boundThis(scope, callData->argument(0)); Scoped<MemberData> boundArgs(scope, (Heap::MemberData *)0); - if (ctx->argc() > 1) { - boundArgs = MemberData::allocate(scope.engine, ctx->argc() - 1); - boundArgs->d()->size = ctx->argc() - 1; - memcpy(boundArgs->data(), ctx->args() + 1, (ctx->argc() - 1)*sizeof(Value)); + if (callData->argc > 1) { + boundArgs = MemberData::allocate(scope.engine, callData->argc - 1); + boundArgs->d()->size = callData->argc - 1; + memcpy(boundArgs->data(), callData->args + 1, (callData->argc - 1)*sizeof(Value)); } ExecutionContext *global = scope.engine->rootContext(); - return BoundFunction::create(global, target, boundThis, boundArgs)->asReturnedValue(); + scope.result = BoundFunction::create(global, target, boundThis, boundArgs); } DEFINE_OBJECT_VTABLE(ScriptFunction); @@ -459,22 +454,22 @@ Heap::Object *ScriptFunction::protoForConstructor() const -DEFINE_OBJECT_VTABLE(BuiltinFunction); +DEFINE_OBJECT_VTABLE(OldBuiltinFunction); -void Heap::BuiltinFunction::init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(QV4::CallContext *)) +void Heap::OldBuiltinFunction::init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(QV4::CallContext *)) { Heap::FunctionObject::init(scope, name); this->code = code; } -void BuiltinFunction::construct(const Managed *f, Scope &scope, CallData *) +void OldBuiltinFunction::construct(const Managed *f, Scope &scope, CallData *) { - scope.result = static_cast<const BuiltinFunction *>(f)->internalClass()->engine->throwTypeError(); + scope.result = static_cast<const OldBuiltinFunction *>(f)->internalClass()->engine->throwTypeError(); } -void BuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData) +void OldBuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData) { - const BuiltinFunction *f = static_cast<const BuiltinFunction *>(that); + const OldBuiltinFunction *f = static_cast<const OldBuiltinFunction *>(that); ExecutionEngine *v4 = scope.engine; if (v4->hasException) { scope.result = Encode::undefined(); @@ -484,15 +479,41 @@ void BuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData ExecutionContextSaver ctxSaver(scope); - CallContext::Data ctx = CallContext::Data::createOnStack(v4); - ctx.strictMode = f->scope()->strictMode; // ### needed? scope or parent context? - ctx.callData = callData; - v4->pushContext(&ctx); - Q_ASSERT(v4->current == &ctx); + CallContext::Data *ctx = v4->memoryManager->allocSimpleCallContext(v4); + 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)); + v4->memoryManager->freeSimpleCallContext(); +} + +DEFINE_OBJECT_VTABLE(BuiltinFunction); + +void Heap::BuiltinFunction::init(QV4::ExecutionContext *scope, QV4::String *name, void (*code)(const QV4::BuiltinFunction *, Scope &, 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) +{ + 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); } + void IndexedBuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData) { const IndexedBuiltinFunction *f = static_cast<const IndexedBuiltinFunction *>(that); @@ -505,13 +526,14 @@ void IndexedBuiltinFunction::call(const Managed *that, Scope &scope, CallData *c ExecutionContextSaver ctxSaver(scope); - CallContext::Data ctx = CallContext::Data::createOnStack(v4); - ctx.strictMode = f->scope()->strictMode; // ### needed? scope or parent context? - ctx.callData = callData; - v4->pushContext(&ctx); - Q_ASSERT(v4->current == &ctx); + CallContext::Data *ctx = v4->memoryManager->allocSimpleCallContext(v4); + 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(); } DEFINE_OBJECT_VTABLE(IndexedBuiltinFunction); diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index a02e89e883..45d7485f1b 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -61,6 +61,8 @@ struct QQmlSourceLocation; namespace QV4 { +struct BuiltinFunction; + namespace Heap { struct Q_QML_PRIVATE_EXPORT FunctionObject : Object { @@ -93,11 +95,16 @@ struct FunctionPrototype : FunctionObject { void init(); }; -struct Q_QML_EXPORT BuiltinFunction : FunctionObject { +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 *); +}; + struct IndexedBuiltinFunction : FunctionObject { inline void init(QV4::ExecutionContext *scope, uint index, ReturnedValue (*code)(QV4::CallContext *ctx, uint index)); ReturnedValue (*code)(QV4::CallContext *, uint index); @@ -177,16 +184,27 @@ struct FunctionPrototype: FunctionObject void init(ExecutionEngine *engine, Object *ctor); - static ReturnedValue method_toString(CallContext *ctx); - static ReturnedValue method_apply(CallContext *ctx); - static ReturnedValue method_call(CallContext *ctx); - static ReturnedValue method_bind(CallContext *ctx); + 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); }; -struct Q_QML_EXPORT BuiltinFunction: FunctionObject { +struct Q_QML_EXPORT OldBuiltinFunction : FunctionObject { + V4_OBJECT2(OldBuiltinFunction, FunctionObject) + + static void construct(const Managed *, Scope &scope, CallData *); + static void call(const Managed *that, Scope &scope, CallData *callData); +}; + +struct Q_QML_EXPORT BuiltinFunction : FunctionObject { V4_OBJECT2(BuiltinFunction, FunctionObject) - static Heap::BuiltinFunction *create(ExecutionContext *scope, String *name, ReturnedValue (*code)(CallContext *)) + static Heap::OldBuiltinFunction *create(ExecutionContext *scope, String *name, ReturnedValue (*code)(CallContext *)) + { + return scope->engine()->memoryManager->allocObject<OldBuiltinFunction>(scope, name, code); + } + static Heap::BuiltinFunction *create(ExecutionContext *scope, String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *)) { return scope->engine()->memoryManager->allocObject<BuiltinFunction>(scope, name, code); } diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index 1dbc538be2..66861bf697 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -208,6 +208,7 @@ struct StringObject; struct ArrayObject; struct DateObject; struct FunctionObject; +struct BuiltinFunction; struct ErrorObject; struct ArgumentsObject; struct Managed; diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp index af92ce1ad8..1bc91f832b 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -423,18 +423,15 @@ static inline int toInt(const QChar &qc, int R) } // parseInt [15.1.2.2] -ReturnedValue GlobalFunctions::method_parseInt(CallContext *ctx) +void GlobalFunctions::method_parseInt(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedValue inputString(scope, ctx->argument(0)); - ScopedValue radix(scope, ctx->argument(1)); + ScopedValue inputString(scope, callData->argument(0)); + ScopedValue radix(scope, callData->argument(1)); int R = radix->isUndefined() ? 0 : radix->toInt32(); // [15.1.2.2] step by step: QString trimmed = inputString->toQString().trimmed(); // 1 + 2 - - if (ctx->d()->engine->hasException) - return Encode::undefined(); + CHECK_EXCEPTION(); const QChar *pos = trimmed.constData(); const QChar *end = pos + trimmed.length(); @@ -449,7 +446,7 @@ ReturnedValue GlobalFunctions::method_parseInt(CallContext *ctx) bool stripPrefix = true; // 7 if (R) { // 8 if (R < 2 || R > 36) - return Encode(std::numeric_limits<double>::quiet_NaN()); // 8a + RETURN_RESULT(Encode(std::numeric_limits<double>::quiet_NaN())); // 8a if (R != 16) stripPrefix = false; // 8b } else { // 9 @@ -466,13 +463,13 @@ ReturnedValue GlobalFunctions::method_parseInt(CallContext *ctx) // 11: Z is progressively built below // 13: this is handled by the toInt function if (pos == end) // 12 - return Encode(std::numeric_limits<double>::quiet_NaN()); + RETURN_RESULT(Encode(std::numeric_limits<double>::quiet_NaN())); bool overflow = false; qint64 v_overflow = 0; unsigned overflow_digit_count = 0; int d = toInt(*pos++, R); if (d == -1) - return Encode(std::numeric_limits<double>::quiet_NaN()); + RETURN_RESULT(Encode(std::numeric_limits<double>::quiet_NaN())); qint64 v = d; while (pos != end) { d = toInt(*pos++, R); @@ -499,155 +496,148 @@ ReturnedValue GlobalFunctions::method_parseInt(CallContext *ctx) if (overflow) { double result = (double) v_overflow * pow(static_cast<double>(R), static_cast<double>(overflow_digit_count)); result += v; - return Encode(sign * result); + RETURN_RESULT(Encode(sign * result)); } else { - return Encode(sign * (double) v); // 15 + RETURN_RESULT(Encode(sign * (double) v)); // 15 } } // parseFloat [15.1.2.3] -ReturnedValue GlobalFunctions::method_parseFloat(CallContext *ctx) +void GlobalFunctions::method_parseFloat(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - // [15.1.2.3] step by step: - ScopedString inputString(scope, ctx->argument(0), ScopedString::Convert); - if (scope.engine->hasException) - return Encode::undefined(); + ScopedString inputString(scope, callData->argument(0), ScopedString::Convert); + CHECK_EXCEPTION(); QString trimmed = inputString->toQString().trimmed(); // 2 // 4: if (trimmed.startsWith(QLatin1String("Infinity")) || trimmed.startsWith(QLatin1String("+Infinity"))) - return Encode(Q_INFINITY); + RETURN_RESULT(Encode(Q_INFINITY)); if (trimmed.startsWith(QLatin1String("-Infinity"))) - return Encode(-Q_INFINITY); + RETURN_RESULT(Encode(-Q_INFINITY)); QByteArray ba = trimmed.toLatin1(); bool ok; const char *begin = ba.constData(); const char *end = 0; double d = qstrtod(begin, &end, &ok); if (end - begin == 0) - return Encode(std::numeric_limits<double>::quiet_NaN()); // 3 + RETURN_RESULT(Encode(std::numeric_limits<double>::quiet_NaN())); // 3 else - return Encode(d); + RETURN_RESULT(Encode(d)); } /// isNaN [15.1.2.4] -ReturnedValue GlobalFunctions::method_isNaN(CallContext *ctx) +void GlobalFunctions::method_isNaN(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (!ctx->argc()) + if (!callData->argc) // undefined gets converted to NaN - return Encode(true); + RETURN_RESULT(Encode(true)); - if (ctx->args()[0].integerCompatible()) - return Encode(false); + if (callData->args[0].integerCompatible()) + RETURN_RESULT(Encode(false)); - double d = ctx->args()[0].toNumber(); - return Encode((bool)std::isnan(d)); + double d = callData->args[0].toNumber(); + RETURN_RESULT(Encode((bool)std::isnan(d))); } /// isFinite [15.1.2.5] -ReturnedValue GlobalFunctions::method_isFinite(CallContext *ctx) +void GlobalFunctions::method_isFinite(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (!ctx->argc()) + if (!callData->argc) // undefined gets converted to NaN - return Encode(false); + RETURN_RESULT(Encode(false)); - if (ctx->args()[0].integerCompatible()) - return Encode(true); + if (callData->args[0].integerCompatible()) + RETURN_RESULT(Encode(true)); - double d = ctx->args()[0].toNumber(); - return Encode((bool)std::isfinite(d)); + double d = callData->args[0].toNumber(); + RETURN_RESULT(Encode((bool)std::isfinite(d))); } /// decodeURI [15.1.3.1] -ReturnedValue GlobalFunctions::method_decodeURI(CallContext *context) +void GlobalFunctions::method_decodeURI(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (context->argc() == 0) - return Encode::undefined(); + if (callData->argc == 0) + RETURN_UNDEFINED(); - QString uriString = context->args()[0].toQString(); + QString uriString = callData->args[0].toQString(); bool ok; QString out = decode(uriString, DecodeNonReserved, &ok); if (!ok) { - Scope scope(context); - ScopedString s(scope, context->d()->engine->newString(QStringLiteral("malformed URI sequence"))); - return context->engine()->throwURIError(s); + ScopedString s(scope, scope.engine->newString(QStringLiteral("malformed URI sequence"))); + RETURN_RESULT(scope.engine->throwURIError(s)); } - return context->d()->engine->newString(out)->asReturnedValue(); + RETURN_RESULT(scope.engine->newString(out)); } /// decodeURIComponent [15.1.3.2] -ReturnedValue GlobalFunctions::method_decodeURIComponent(CallContext *context) +void GlobalFunctions::method_decodeURIComponent(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (context->argc() == 0) - return Encode::undefined(); + if (callData->argc == 0) + RETURN_UNDEFINED(); - QString uriString = context->args()[0].toQString(); + QString uriString = callData->args[0].toQString(); bool ok; QString out = decode(uriString, DecodeAll, &ok); if (!ok) { - Scope scope(context); - ScopedString s(scope, context->d()->engine->newString(QStringLiteral("malformed URI sequence"))); - return context->engine()->throwURIError(s); + ScopedString s(scope, scope.engine->newString(QStringLiteral("malformed URI sequence"))); + RETURN_RESULT(scope.engine->throwURIError(s)); } - return context->d()->engine->newString(out)->asReturnedValue(); + RETURN_RESULT(scope.engine->newString(out)); } /// encodeURI [15.1.3.3] -ReturnedValue GlobalFunctions::method_encodeURI(CallContext *context) +void GlobalFunctions::method_encodeURI(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (context->argc() == 0) - return Encode::undefined(); + if (callData->argc == 0) + RETURN_UNDEFINED(); - QString uriString = context->args()[0].toQString(); + QString uriString = callData->args[0].toQString(); bool ok; QString out = encode(uriString, uriUnescapedReserved, &ok); if (!ok) { - Scope scope(context); - ScopedString s(scope, context->d()->engine->newString(QStringLiteral("malformed URI sequence"))); - return context->engine()->throwURIError(s); + ScopedString s(scope, scope.engine->newString(QStringLiteral("malformed URI sequence"))); + RETURN_RESULT(scope.engine->throwURIError(s)); } - return context->d()->engine->newString(out)->asReturnedValue(); + RETURN_RESULT(scope.engine->newString(out)); } /// encodeURIComponent [15.1.3.4] -ReturnedValue GlobalFunctions::method_encodeURIComponent(CallContext *context) +void GlobalFunctions::method_encodeURIComponent(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (context->argc() == 0) - return Encode::undefined(); + if (callData->argc == 0) + RETURN_UNDEFINED(); - QString uriString = context->args()[0].toQString(); + QString uriString = callData->args[0].toQString(); bool ok; QString out = encode(uriString, uriUnescaped, &ok); if (!ok) { - Scope scope(context); - ScopedString s(scope, context->d()->engine->newString(QStringLiteral("malformed URI sequence"))); - return context->engine()->throwURIError(s); + ScopedString s(scope, scope.engine->newString(QStringLiteral("malformed URI sequence"))); + RETURN_RESULT(scope.engine->throwURIError(s)); } - return context->d()->engine->newString(out)->asReturnedValue(); + RETURN_RESULT(scope.engine->newString(out)); } -ReturnedValue GlobalFunctions::method_escape(CallContext *context) +void GlobalFunctions::method_escape(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (!context->argc()) - return context->d()->engine->newString(QStringLiteral("undefined"))->asReturnedValue(); + if (!callData->argc) + RETURN_RESULT(scope.engine->newString(QStringLiteral("undefined"))); - QString str = context->args()[0].toQString(); - return context->d()->engine->newString(escape(str))->asReturnedValue(); + QString str = callData->args[0].toQString(); + RETURN_RESULT(scope.engine->newString(escape(str))); } -ReturnedValue GlobalFunctions::method_unescape(CallContext *context) +void GlobalFunctions::method_unescape(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (!context->argc()) - return context->d()->engine->newString(QStringLiteral("undefined"))->asReturnedValue(); + if (!callData->argc) + RETURN_RESULT(scope.engine->newString(QStringLiteral("undefined"))); - QString str = context->args()[0].toQString(); - return context->d()->engine->newString(unescape(str))->asReturnedValue(); + QString str = callData->args[0].toQString(); + RETURN_RESULT(scope.engine->newString(unescape(str))); } diff --git a/src/qml/jsruntime/qv4globalobject_p.h b/src/qml/jsruntime/qv4globalobject_p.h index e8b3a92d34..273f1ba7ea 100644 --- a/src/qml/jsruntime/qv4globalobject_p.h +++ b/src/qml/jsruntime/qv4globalobject_p.h @@ -76,16 +76,16 @@ struct Q_QML_EXPORT EvalFunction : FunctionObject struct GlobalFunctions { - static ReturnedValue method_parseInt(CallContext *context); - static ReturnedValue method_parseFloat(CallContext *context); - static ReturnedValue method_isNaN(CallContext *context); - static ReturnedValue method_isFinite(CallContext *ctx); - static ReturnedValue method_decodeURI(CallContext *context); - static ReturnedValue method_decodeURIComponent(CallContext *context); - static ReturnedValue method_encodeURI(CallContext *context); - static ReturnedValue method_encodeURIComponent(CallContext *context); - static ReturnedValue method_escape(CallContext *context); - static ReturnedValue method_unescape(CallContext *context); + 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); }; } diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp index 1d393cf0aa..f033eb2d2d 100644 --- a/src/qml/jsruntime/qv4include.cpp +++ b/src/qml/jsruntime/qv4include.cpp @@ -195,23 +195,22 @@ void QV4Include::finished() /* Documented in qv8engine.cpp */ -QV4::ReturnedValue QV4Include::method_include(QV4::CallContext *ctx) +void QV4Include::method_include(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - if (!ctx->argc()) - return QV4::Encode::undefined(); + if (!callData->argc) + RETURN_UNDEFINED(); - QV4::Scope scope(ctx->engine()); QQmlContextData *context = scope.engine->callingQmlContext(); if (!context || !context->isJSContext) - V4THROW_ERROR("Qt.include(): Can only be called from JavaScript files"); + RETURN_RESULT(scope.engine->throwError(QString::fromUtf8("Qt.include(): Can only be called from JavaScript files"))); QV4::ScopedValue callbackFunction(scope, QV4::Primitive::undefinedValue()); - if (ctx->argc() >= 2 && ctx->args()[1].as<QV4::FunctionObject>()) - callbackFunction = ctx->args()[1]; + if (callData->argc >= 2 && callData->args[1].as<QV4::FunctionObject>()) + callbackFunction = callData->args[1]; #if QT_CONFIG(qml_network) - QUrl url(scope.engine->resolvedUrl(ctx->args()[0].toQStringNoThrow())); + QUrl url(scope.engine->resolvedUrl(callData->args[0].toQStringNoThrow())); if (scope.engine->qmlEngine() && scope.engine->qmlEngine()->urlInterceptor()) url = scope.engine->qmlEngine()->urlInterceptor()->intercept(url, QQmlAbstractUrlInterceptor::JavaScriptFile); @@ -261,12 +260,12 @@ QV4::ReturnedValue QV4Include::method_include(QV4::CallContext *ctx) callback(callbackFunction, result); } - return result->asReturnedValue(); + scope.result = result; #else QV4::ScopedValue result(scope); result = resultValue(scope.engine, NetworkError); callback(callbackFunction, result); - return result->asReturnedValue(); + scope.result = result; #endif } diff --git a/src/qml/jsruntime/qv4include_p.h b/src/qml/jsruntime/qv4include_p.h index 4c601a5e7b..5908d6bfde 100644 --- a/src/qml/jsruntime/qv4include_p.h +++ b/src/qml/jsruntime/qv4include_p.h @@ -77,7 +77,7 @@ public: Exception = 3 }; - static QV4::ReturnedValue method_include(QV4::CallContext *ctx); + static void method_include(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); private Q_SLOTS: void finished(); diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index d17da9af0c..bac71b4537 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -128,48 +128,22 @@ InternalClass::InternalClass(const QV4::InternalClass &other) static void insertHoleIntoPropertyData(Object *object, int idx) { - int inlineSize = object->d()->inlineMemberSize; int icSize = object->internalClass()->size; - int from = qMax(idx, inlineSize); + int from = idx; int to = from + 1; - if (from < icSize) { + if (from < icSize) memmove(object->propertyData(to), object->propertyData(from), (icSize - from - 1) * sizeof(Value)); - } - if (from == idx) - return; - if (inlineSize < icSize) - *object->propertyData(inlineSize) = *object->propertyData(inlineSize - 1); - from = idx; - to = from + 1; - if (from < inlineSize - 1) { - memmove(object->propertyData(to), object->propertyData(from), - (inlineSize - from - 1) * sizeof(Value)); - } } static void removeFromPropertyData(Object *object, int idx, bool accessor = false) { - int inlineSize = object->d()->inlineMemberSize; int delta = (accessor ? 2 : 1); int oldSize = object->internalClass()->size + delta; int to = idx; int from = to + delta; - if (from < inlineSize) { - memmove(object->propertyData(to), object->d()->propertyData(from), (inlineSize - from)*sizeof(Value)); - to = inlineSize - delta; - from = inlineSize; - } - if (to < inlineSize && from < oldSize) { - Q_ASSERT(from >= inlineSize); - memcpy(object->propertyData(to), object->d()->propertyData(from), (inlineSize - to)*sizeof(Value)); - to = inlineSize; - from = inlineSize + delta; - } - if (from < oldSize) { - Q_ASSERT(to >= inlineSize && from > to); + if (from < oldSize) memmove(object->propertyData(to), object->d()->propertyData(from), (oldSize - to)*sizeof(Value)); - } } void InternalClass::changeMember(Object *object, String *string, PropertyAttributes data, uint *index) diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index dcda949c97..1d8ef4b0fb 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -234,7 +234,7 @@ struct InternalClassTransition { return id == other.id && flags == other.flags; } bool operator<(const InternalClassTransition &other) const - { return id < other.id; } + { return id < other.id || (id == other.id && flags < other.flags); } }; struct InternalClass : public QQmlJS::Managed { diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp index d79e6242ba..1d571f53f3 100644 --- a/src/qml/jsruntime/qv4jsonobject.cpp +++ b/src/qml/jsruntime/qv4jsonobject.cpp @@ -883,10 +883,9 @@ void Heap::JsonObject::init() } -ReturnedValue JsonObject::method_parse(CallContext *ctx) +void JsonObject::method_parse(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedValue v(scope, ctx->argument(0)); + ScopedValue v(scope, callData->argument(0)); QString jtext = v->toQString(); DEBUG << "parsing source = " << jtext; @@ -895,19 +894,17 @@ ReturnedValue JsonObject::method_parse(CallContext *ctx) ScopedValue result(scope, parser.parse(&error)); if (error.error != QJsonParseError::NoError) { DEBUG << "parse error" << error.errorString(); - return ctx->engine()->throwSyntaxError(QStringLiteral("JSON.parse: Parse error")); + RETURN_RESULT(scope.engine->throwSyntaxError(QStringLiteral("JSON.parse: Parse error"))); } - return result->asReturnedValue(); + scope.result = result; } -ReturnedValue JsonObject::method_stringify(CallContext *ctx) +void JsonObject::method_stringify(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Stringify stringify(scope.engine); - ScopedObject o(scope, ctx->argument(1)); + ScopedObject o(scope, callData->argument(1)); if (o) { stringify.replacerFunction = o->as<FunctionObject>(); if (o->isArrayObject()) { @@ -932,7 +929,7 @@ ReturnedValue JsonObject::method_stringify(CallContext *ctx) } } - ScopedValue s(scope, ctx->argument(2)); + ScopedValue s(scope, callData->argument(2)); if (NumberObject *n = s->as<NumberObject>()) s = Encode(n->value()); else if (StringObject *so = s->as<StringObject>()) @@ -945,11 +942,11 @@ ReturnedValue JsonObject::method_stringify(CallContext *ctx) } - ScopedValue arg0(scope, ctx->argument(0)); + ScopedValue arg0(scope, callData->argument(0)); QString result = stringify.Str(QString(), arg0); if (result.isEmpty() || scope.engine->hasException) - return Encode::undefined(); - return ctx->d()->engine->newString(result)->asReturnedValue(); + RETURN_UNDEFINED(); + scope.result = scope.engine->newString(result); } diff --git a/src/qml/jsruntime/qv4jsonobject_p.h b/src/qml/jsruntime/qv4jsonobject_p.h index 43248a214d..a73ce1c74e 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 ReturnedValue method_parse(CallContext *ctx); - static ReturnedValue method_stringify(CallContext *ctx); + static void method_parse(const BuiltinFunction *, Scope &scope, CallData *callData); + static void method_stringify(const BuiltinFunction *, Scope &scope, 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_p.h b/src/qml/jsruntime/qv4lookup_p.h index 52f54e25f5..c5ee92fedd 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -54,8 +54,11 @@ #include "qv4runtime_p.h" #include "qv4engine_p.h" #include "qv4context_p.h" + +#if !defined(V4_BOOTSTRAP) #include "qv4object_p.h" #include "qv4internalclass_p.h" +#endif QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4mathobject.cpp b/src/qml/jsruntime/qv4mathobject.cpp index e03b2762cc..2d9d81c64b 100644 --- a/src/qml/jsruntime/qv4mathobject.cpp +++ b/src/qml/jsruntime/qv4mathobject.cpp @@ -92,160 +92,160 @@ static Q_ALWAYS_INLINE double copySign(double x, double y) return ::copysign(x, y); } -ReturnedValue MathObject::method_abs(CallContext *context) +void MathObject::method_abs(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (!context->argc()) - return Encode(qt_qnan()); + if (!callData->argc) + RETURN_RESULT(Encode(qt_qnan())); - if (context->args()[0].isInteger()) { - int i = context->args()[0].integerValue(); - return Encode(i < 0 ? - i : i); + if (callData->args[0].isInteger()) { + int i = callData->args[0].integerValue(); + RETURN_RESULT(Encode(i < 0 ? - i : i)); } - double v = context->args()[0].toNumber(); + double v = callData->args[0].toNumber(); if (v == 0) // 0 | -0 - return Encode(0); + RETURN_RESULT(Encode(0)); - return Encode(v < 0 ? -v : v); + RETURN_RESULT(Encode(v < 0 ? -v : v)); } -ReturnedValue MathObject::method_acos(CallContext *context) +void MathObject::method_acos(const BuiltinFunction *, Scope &scope, CallData *callData) { - double v = context->argc() ? context->args()[0].toNumber() : 2; + double v = callData->argc ? callData->args[0].toNumber() : 2; if (v > 1) - return Encode(qt_qnan()); + RETURN_RESULT(Encode(qt_qnan())); - return Encode(std::acos(v)); + RETURN_RESULT(Encode(std::acos(v))); } -ReturnedValue MathObject::method_asin(CallContext *context) +void MathObject::method_asin(const BuiltinFunction *, Scope &scope, CallData *callData) { - double v = context->argc() ? context->args()[0].toNumber() : 2; + double v = callData->argc ? callData->args[0].toNumber() : 2; if (v > 1) - return Encode(qt_qnan()); + RETURN_RESULT(Encode(qt_qnan())); else - return Encode(std::asin(v)); + RETURN_RESULT(Encode(std::asin(v))); } -ReturnedValue MathObject::method_atan(CallContext *context) +void MathObject::method_atan(const BuiltinFunction *, Scope &scope, CallData *callData) { - double v = context->argc() ? context->args()[0].toNumber() : qt_qnan(); + double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); if (v == 0.0) - return Encode(v); + RETURN_RESULT(Encode(v)); else - return Encode(std::atan(v)); + RETURN_RESULT(Encode(std::atan(v))); } -ReturnedValue MathObject::method_atan2(CallContext *context) +void MathObject::method_atan2(const BuiltinFunction *, Scope &scope, CallData *callData) { - double v1 = context->argc() ? context->args()[0].toNumber() : qt_qnan(); - double v2 = context->argc() > 1 ? context->args()[1].toNumber() : qt_qnan(); + double v1 = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double v2 = callData->argc > 1 ? callData->args[1].toNumber() : qt_qnan(); if ((v1 < 0) && qt_is_finite(v1) && qt_is_inf(v2) && (copySign(1.0, v2) == 1.0)) - return Encode(copySign(0, -1.0)); + RETURN_RESULT(Encode(copySign(0, -1.0))); if ((v1 == 0.0) && (v2 == 0.0)) { if ((copySign(1.0, v1) == 1.0) && (copySign(1.0, v2) == -1.0)) { - return Encode(M_PI); + RETURN_RESULT(Encode(M_PI)); } else if ((copySign(1.0, v1) == -1.0) && (copySign(1.0, v2) == -1.0)) { - return Encode(-M_PI); + RETURN_RESULT(Encode(-M_PI)); } } - return Encode(std::atan2(v1, v2)); + RETURN_RESULT(Encode(std::atan2(v1, v2))); } -ReturnedValue MathObject::method_ceil(CallContext *context) +void MathObject::method_ceil(const BuiltinFunction *, Scope &scope, CallData *callData) { - double v = context->argc() ? context->args()[0].toNumber() : qt_qnan(); + double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); if (v < 0.0 && v > -1.0) - return Encode(copySign(0, -1.0)); + RETURN_RESULT(Encode(copySign(0, -1.0))); else - return Encode(std::ceil(v)); + RETURN_RESULT(Encode(std::ceil(v))); } -ReturnedValue MathObject::method_cos(CallContext *context) +void MathObject::method_cos(const BuiltinFunction *, Scope &scope, CallData *callData) { - double v = context->argc() ? context->args()[0].toNumber() : qt_qnan(); - return Encode(std::cos(v)); + double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + RETURN_RESULT(Encode(std::cos(v))); } -ReturnedValue MathObject::method_exp(CallContext *context) +void MathObject::method_exp(const BuiltinFunction *, Scope &scope, CallData *callData) { - double v = context->argc() ? context->args()[0].toNumber() : qt_qnan(); + double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); if (qt_is_inf(v)) { if (copySign(1.0, v) == -1.0) - return Encode(0); + RETURN_RESULT(Encode(0)); else - return Encode(qt_inf()); + RETURN_RESULT(Encode(qt_inf())); } else { - return Encode(std::exp(v)); + RETURN_RESULT(Encode(std::exp(v))); } } -ReturnedValue MathObject::method_floor(CallContext *context) +void MathObject::method_floor(const BuiltinFunction *, Scope &scope, CallData *callData) { - double v = context->argc() ? context->args()[0].toNumber() : qt_qnan(); - return Encode(std::floor(v)); + double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + RETURN_RESULT(Encode(std::floor(v))); } -ReturnedValue MathObject::method_log(CallContext *context) +void MathObject::method_log(const BuiltinFunction *, Scope &scope, CallData *callData) { - double v = context->argc() ? context->args()[0].toNumber() : qt_qnan(); + double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); if (v < 0) - return Encode(qt_qnan()); + RETURN_RESULT(Encode(qt_qnan())); else - return Encode(std::log(v)); + RETURN_RESULT(Encode(std::log(v))); } -ReturnedValue MathObject::method_max(CallContext *context) +void MathObject::method_max(const BuiltinFunction *, Scope &scope, CallData *callData) { double mx = -qt_inf(); - for (int i = 0; i < context->argc(); ++i) { - double x = context->args()[i].toNumber(); + for (int i = 0; i < callData->argc; ++i) { + double x = callData->args[i].toNumber(); if (x > mx || std::isnan(x)) mx = x; } - return Encode(mx); + RETURN_RESULT(Encode(mx)); } -ReturnedValue MathObject::method_min(CallContext *context) +void MathObject::method_min(const BuiltinFunction *, Scope &scope, CallData *callData) { double mx = qt_inf(); - for (int i = 0; i < context->argc(); ++i) { - double x = context->args()[i].toNumber(); + for (int i = 0; i < callData->argc; ++i) { + double x = callData->args[i].toNumber(); if ((x == 0 && mx == x && copySign(1.0, x) == -1.0) || (x < mx) || std::isnan(x)) { mx = x; } } - return Encode(mx); + RETURN_RESULT(Encode(mx)); } -ReturnedValue MathObject::method_pow(CallContext *context) +void MathObject::method_pow(const BuiltinFunction *, Scope &scope, CallData *callData) { - double x = context->argc() > 0 ? context->args()[0].toNumber() : qt_qnan(); - double y = context->argc() > 1 ? context->args()[1].toNumber() : qt_qnan(); + double x = callData->argc > 0 ? callData->args[0].toNumber() : qt_qnan(); + double y = callData->argc > 1 ? callData->args[1].toNumber() : qt_qnan(); if (std::isnan(y)) - return Encode(qt_qnan()); + RETURN_RESULT(Encode(qt_qnan())); if (y == 0) { - return Encode(1); + RETURN_RESULT(Encode(1)); } else if (((x == 1) || (x == -1)) && std::isinf(y)) { - return Encode(qt_qnan()); + RETURN_RESULT(Encode(qt_qnan())); } else if (((x == 0) && copySign(1.0, x) == 1.0) && (y < 0)) { - return Encode(qInf()); + RETURN_RESULT(Encode(qInf())); } else if ((x == 0) && copySign(1.0, x) == -1.0) { if (y < 0) { if (std::fmod(-y, 2.0) == 1.0) - return Encode(-qt_inf()); + RETURN_RESULT(Encode(-qt_inf())); else - return Encode(qt_inf()); + RETURN_RESULT(Encode(qt_inf())); } else if (y > 0) { if (std::fmod(y, 2.0) == 1.0) - return Encode(copySign(0, -1.0)); + RETURN_RESULT(Encode(copySign(0, -1.0))); else - return Encode(0); + RETURN_RESULT(Encode(0)); } } @@ -253,78 +253,78 @@ ReturnedValue MathObject::method_pow(CallContext *context) else if (qt_is_inf(x) && copySign(1.0, x) == -1.0) { if (y > 0) { if (std::fmod(y, 2.0) == 1.0) - return Encode(-qt_inf()); + RETURN_RESULT(Encode(-qt_inf())); else - return Encode(qt_inf()); + RETURN_RESULT(Encode(qt_inf())); } else if (y < 0) { if (std::fmod(-y, 2.0) == 1.0) - return Encode(copySign(0, -1.0)); + RETURN_RESULT(Encode(copySign(0, -1.0))); else - return Encode(0); + RETURN_RESULT(Encode(0)); } } #endif else { - return Encode(std::pow(x, y)); + RETURN_RESULT(Encode(std::pow(x, y))); } // ### - return Encode(qt_qnan()); + RETURN_RESULT(Encode(qt_qnan())); } Q_GLOBAL_STATIC(QThreadStorage<bool *>, seedCreatedStorage); -ReturnedValue MathObject::method_random(CallContext *context) +void MathObject::method_random(const BuiltinFunction *, Scope &scope, CallData *) { if (!seedCreatedStorage()->hasLocalData()) { int msecs = QTime(0,0,0).msecsTo(QTime::currentTime()); Q_ASSERT(msecs >= 0); - qsrand(uint(uint(msecs) ^ reinterpret_cast<quintptr>(context))); + qsrand(uint(uint(msecs) ^ reinterpret_cast<quintptr>(scope.engine))); seedCreatedStorage()->setLocalData(new bool(true)); } // rand()/qrand() return a value where the upperbound is RAND_MAX inclusive. So, instead of // dividing by RAND_MAX (which would return 0..RAND_MAX inclusive), we divide by RAND_MAX + 1. qint64 upperLimit = qint64(RAND_MAX) + 1; - return Encode(qrand() / double(upperLimit)); + RETURN_RESULT(Encode(qrand() / double(upperLimit))); } -ReturnedValue MathObject::method_round(CallContext *context) +void MathObject::method_round(const BuiltinFunction *, Scope &scope, CallData *callData) { - double v = context->argc() ? context->args()[0].toNumber() : qt_qnan(); + double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); v = copySign(std::floor(v + 0.5), v); - return Encode(v); + RETURN_RESULT(Encode(v)); } -ReturnedValue MathObject::method_sign(CallContext *context) +void MathObject::method_sign(const BuiltinFunction *, Scope &scope, CallData *callData) { - double v = context->argc() ? context->args()[0].toNumber() : qt_qnan(); + double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); if (std::isnan(v)) - return Encode(qt_qnan()); + RETURN_RESULT(Encode(qt_qnan())); if (qIsNull(v)) - return v; + RETURN_RESULT(Encode(v)); - return Encode(std::signbit(v) ? -1 : 1); + RETURN_RESULT(Encode(std::signbit(v) ? -1 : 1)); } -ReturnedValue MathObject::method_sin(CallContext *context) +void MathObject::method_sin(const BuiltinFunction *, Scope &scope, CallData *callData) { - double v = context->argc() ? context->args()[0].toNumber() : qt_qnan(); - return Encode(std::sin(v)); + double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + RETURN_RESULT(Encode(std::sin(v))); } -ReturnedValue MathObject::method_sqrt(CallContext *context) +void MathObject::method_sqrt(const BuiltinFunction *, Scope &scope, CallData *callData) { - double v = context->argc() ? context->args()[0].toNumber() : qt_qnan(); - return Encode(std::sqrt(v)); + double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + RETURN_RESULT(Encode(std::sqrt(v))); } -ReturnedValue MathObject::method_tan(CallContext *context) +void MathObject::method_tan(const BuiltinFunction *, Scope &scope, CallData *callData) { - double v = context->argc() ? context->args()[0].toNumber() : qt_qnan(); + double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); if (v == 0.0) - return Encode(v); + RETURN_RESULT(Encode(v)); else - return Encode(std::tan(v)); + RETURN_RESULT(Encode(std::tan(v))); } diff --git a/src/qml/jsruntime/qv4mathobject_p.h b/src/qml/jsruntime/qv4mathobject_p.h index f6b1a4395f..e617712905 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 ReturnedValue method_abs(CallContext *context); - static ReturnedValue method_acos(CallContext *context); - static ReturnedValue method_asin(CallContext *context); - static ReturnedValue method_atan(CallContext *context); - static ReturnedValue method_atan2(CallContext *context); - static ReturnedValue method_ceil(CallContext *context); - static ReturnedValue method_cos(CallContext *context); - static ReturnedValue method_exp(CallContext *context); - static ReturnedValue method_floor(CallContext *context); - static ReturnedValue method_log(CallContext *context); - static ReturnedValue method_max(CallContext *context); - static ReturnedValue method_min(CallContext *context); - static ReturnedValue method_pow(CallContext *context); - static ReturnedValue method_random(CallContext *context); - static ReturnedValue method_round(CallContext *context); - static ReturnedValue method_sign(CallContext *context); - static ReturnedValue method_sin(CallContext *context); - static ReturnedValue method_sqrt(CallContext *context); - static ReturnedValue method_tan(CallContext *context); + 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); }; } diff --git a/src/qml/jsruntime/qv4memberdata.cpp b/src/qml/jsruntime/qv4memberdata.cpp index d5f75415cc..db45c77472 100644 --- a/src/qml/jsruntime/qv4memberdata.cpp +++ b/src/qml/jsruntime/qv4memberdata.cpp @@ -55,13 +55,14 @@ void MemberData::markObjects(Heap::Base *that, ExecutionEngine *e) Heap::MemberData *MemberData::allocate(ExecutionEngine *e, uint n, Heap::MemberData *old) { Q_ASSERT(!old || old->size < n); + Q_ASSERT(n); - uint alloc = sizeof(Heap::MemberData) + (n)*sizeof(Value); + size_t alloc = MemoryManager::align(sizeof(Heap::MemberData) + (n - 1)*sizeof(Value)); Heap::MemberData *m = e->memoryManager->allocManaged<MemberData>(alloc); if (old) - memcpy(m, old, sizeof(Heap::MemberData) + old->size * sizeof(Value)); + memcpy(m, old, sizeof(Heap::MemberData) + (old->size - 1)* sizeof(Value)); else m->init(); - m->size = n; + m->size = static_cast<uint>((alloc - sizeof(Heap::MemberData) + sizeof(Value))/sizeof(Value)); return m; } diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp index 3a6b9da763..09644c161d 100644 --- a/src/qml/jsruntime/qv4numberobject.cpp +++ b/src/qml/jsruntime/qv4numberobject.cpp @@ -120,61 +120,71 @@ QT_WARNING_POP defineDefaultProperty(QStringLiteral("toPrecision"), method_toPrecision); } -inline ReturnedValue thisNumberValue(ExecutionContext *ctx) +inline ReturnedValue thisNumberValue(Scope &scope, CallData *callData) { - if (ctx->thisObject().isNumber()) - return ctx->thisObject().asReturnedValue(); - NumberObject *n = ctx->thisObject().as<NumberObject>(); - if (!n) - return ctx->engine()->throwTypeError(); + if (callData->thisObject.isNumber()) + return callData->thisObject.asReturnedValue(); + NumberObject *n = callData->thisObject.as<NumberObject>(); + if (!n) { + scope.engine->throwTypeError(); + return Encode::undefined(); + } return Encode(n->value()); } -inline double thisNumber(ExecutionContext *ctx) +inline double thisNumber(Scope &scope, CallData *callData) { - if (ctx->thisObject().isNumber()) - return ctx->thisObject().asDouble(); - NumberObject *n = ctx->thisObject().as<NumberObject>(); - if (!n) - return ctx->engine()->throwTypeError(); + if (callData->thisObject.isNumber()) + return callData->thisObject.asDouble(); + NumberObject *n = callData->thisObject.as<NumberObject>(); + if (!n) { + scope.engine->throwTypeError(); + return 0; + } return n->value(); } -ReturnedValue NumberPrototype::method_isFinite(CallContext *ctx) +void NumberPrototype::method_isFinite(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (!ctx->argc()) - return Encode(false); + if (!callData->argc) { + scope.result = Encode(false); + return; + } - double v = ctx->args()[0].toNumber(); - return Encode(!std::isnan(v) && !qt_is_inf(v)); + double v = callData->args[0].toNumber(); + scope.result = Encode(!std::isnan(v) && !qt_is_inf(v)); } -ReturnedValue NumberPrototype::method_isNaN(CallContext *ctx) +void NumberPrototype::method_isNaN(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (!ctx->argc()) - return Encode(false); + if (!callData->argc) { + scope.result = Encode(false); + return; + } - double v = ctx->args()[0].toNumber(); - return Encode(std::isnan(v)); + double v = callData->args[0].toNumber(); + scope.result = Encode(std::isnan(v)); } -ReturnedValue NumberPrototype::method_toString(CallContext *ctx) +void NumberPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - double num = thisNumber(ctx); - if (scope.engine->hasException) - return Encode::undefined(); + double num = thisNumber(scope, callData); + CHECK_EXCEPTION(); - if (ctx->argc() && !ctx->args()[0].isUndefined()) { - int radix = ctx->args()[0].toInt32(); - if (radix < 2 || radix > 36) - return ctx->engine()->throwError(QStringLiteral("Number.prototype.toString: %0 is not a valid radix") + if (callData->argc && !callData->args[0].isUndefined()) { + int radix = callData->args[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; + } if (std::isnan(num)) { - return scope.engine->newString(QStringLiteral("NaN"))->asReturnedValue(); + scope.result = scope.engine->newString(QStringLiteral("NaN")); + return; } else if (qt_is_inf(num)) { - return scope.engine->newString(QLatin1String(num < 0 ? "-Infinity" : "Infinity"))->asReturnedValue(); + scope.result = scope.engine->newString(QLatin1String(num < 0 ? "-Infinity" : "Infinity")); + return; } if (radix != 10) { @@ -204,45 +214,43 @@ ReturnedValue NumberPrototype::method_toString(CallContext *ctx) } if (negative) str.prepend(QLatin1Char('-')); - return scope.engine->newString(str)->asReturnedValue(); + scope.result = scope.engine->newString(str); + return; } } - return Primitive::fromDouble(num).toString(scope.engine)->asReturnedValue(); + scope.result = Primitive::fromDouble(num).toString(scope.engine); } -ReturnedValue NumberPrototype::method_toLocaleString(CallContext *ctx) +void NumberPrototype::method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedValue v(scope, thisNumberValue(ctx)); - ScopedString str(scope, v->toString(scope.engine)); - if (scope.engine->hasException) - return Encode::undefined(); - return str.asReturnedValue(); + ScopedValue v(scope, thisNumberValue(scope, callData)); + scope.result = v->toString(scope.engine); + CHECK_EXCEPTION(); } -ReturnedValue NumberPrototype::method_valueOf(CallContext *ctx) +void NumberPrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData) { - return thisNumberValue(ctx); + scope.result = thisNumberValue(scope, callData); } -ReturnedValue NumberPrototype::method_toFixed(CallContext *ctx) +void NumberPrototype::method_toFixed(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - double v = thisNumber(ctx); - if (scope.engine->hasException) - return Encode::undefined(); + double v = thisNumber(scope, callData); + CHECK_EXCEPTION(); double fdigits = 0; - if (ctx->argc() > 0) - fdigits = ctx->args()[0].toInteger(); + if (callData->argc > 0) + fdigits = callData->args[0].toInteger(); if (std::isnan(fdigits)) fdigits = 0; - if (fdigits < 0 || fdigits > 20) - return ctx->engine()->throwRangeError(ctx->thisObject()); + if (fdigits < 0 || fdigits > 20) { + scope.result = scope.engine->throwRangeError(callData->thisObject); + return; + } QString str; if (std::isnan(v)) @@ -251,48 +259,50 @@ ReturnedValue NumberPrototype::method_toFixed(CallContext *ctx) str = QString::fromLatin1(v < 0 ? "-Infinity" : "Infinity"); else if (v < 1.e21) str = NumberLocale::instance()->toString(v, 'f', int(fdigits)); - else - return RuntimeHelpers::stringFromNumber(ctx->engine(), v)->asReturnedValue(); - return scope.engine->newString(str)->asReturnedValue(); + else { + scope.result = RuntimeHelpers::stringFromNumber(scope.engine, v); + return; + } + scope.result = scope.engine->newString(str); } -ReturnedValue NumberPrototype::method_toExponential(CallContext *ctx) +void NumberPrototype::method_toExponential(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - double d = thisNumber(ctx); - if (scope.engine->hasException) - return Encode::undefined(); + double d = thisNumber(scope, callData); + CHECK_EXCEPTION(); int fdigits = NumberLocale::instance()->defaultDoublePrecision; - if (ctx->argc() && !ctx->args()[0].isUndefined()) { - fdigits = ctx->args()[0].toInt32(); + if (callData->argc && !callData->args[0].isUndefined()) { + fdigits = callData->args[0].toInt32(); if (fdigits < 0 || fdigits > 20) { ScopedString error(scope, scope.engine->newString(QStringLiteral("Number.prototype.toExponential: fractionDigits out of range"))); - return ctx->engine()->throwRangeError(error); + scope.result = scope.engine->throwRangeError(error); + return; } } QString result = NumberLocale::instance()->toString(d, 'e', fdigits); - return scope.engine->newString(result)->asReturnedValue(); + scope.result = scope.engine->newString(result); } -ReturnedValue NumberPrototype::method_toPrecision(CallContext *ctx) +void NumberPrototype::method_toPrecision(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedValue v(scope, thisNumberValue(ctx)); - if (scope.engine->hasException) - return Encode::undefined(); + ScopedValue v(scope, thisNumberValue(scope, callData)); + CHECK_EXCEPTION(); - if (!ctx->argc() || ctx->args()[0].isUndefined()) - return Encode(v->toString(scope.engine)); + if (!callData->argc || callData->args[0].isUndefined()) { + scope.result = v->toString(scope.engine); + return; + } - int precision = ctx->args()[0].toInt32(); + int precision = callData->args[0].toInt32(); if (precision < 1 || precision > 21) { ScopedString error(scope, scope.engine->newString(QStringLiteral("Number.prototype.toPrecision: precision out of range"))); - return ctx->engine()->throwRangeError(error); + scope.result = scope.engine->throwRangeError(error); + return; } QString result = NumberLocale::instance()->toString(v->asDouble(), 'g', precision); - return scope.engine->newString(result)->asReturnedValue(); + scope.result = scope.engine->newString(result); } diff --git a/src/qml/jsruntime/qv4numberobject_p.h b/src/qml/jsruntime/qv4numberobject_p.h index 6022b3a029..364b866a16 100644 --- a/src/qml/jsruntime/qv4numberobject_p.h +++ b/src/qml/jsruntime/qv4numberobject_p.h @@ -87,14 +87,14 @@ struct NumberPrototype: NumberObject { void init(ExecutionEngine *engine, Object *ctor); - static ReturnedValue method_isFinite(CallContext *ctx); - static ReturnedValue method_isNaN(CallContext *ctx); - static ReturnedValue method_toString(CallContext *ctx); - static ReturnedValue method_toLocaleString(CallContext *ctx); - static ReturnedValue method_valueOf(CallContext *ctx); - static ReturnedValue method_toFixed(CallContext *ctx); - static ReturnedValue method_toExponential(CallContext *ctx); - static ReturnedValue method_toPrecision(CallContext *ctx); + static void method_isFinite(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); }; diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 8acca16dd0..2f664c6398 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -61,9 +61,8 @@ DEFINE_OBJECT_VTABLE(Object); void Object::setInternalClass(InternalClass *ic) { d()->internalClass = ic; - if ((ic->size > d()->inlineMemberSize && !d()->memberData) || - (d()->memberData && d()->memberData->size < ic->size - d()->inlineMemberSize)) - d()->memberData = MemberData::allocate(ic->engine, ic->size - d()->inlineMemberSize, d()->memberData); + if ((!d()->memberData && ic->size) || (d()->memberData->size < ic->size)) + d()->memberData = MemberData::allocate(ic->engine, ic->size, d()->memberData); } void Object::getProperty(uint index, Property *p, PropertyAttributes *attrs) const @@ -167,6 +166,17 @@ void Object::defineDefaultProperty(const QString &name, ReturnedValue (*code)(Ca defineDefaultProperty(s, function); } +void Object::defineDefaultProperty(const QString &name, void (*code)(const BuiltinFunction *, Scope &, 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->defineReadonlyProperty(e->id_length(), Primitive::fromInt32(argumentCount)); + defineDefaultProperty(s, function); +} + void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(CallContext *), int argumentCount) { ExecutionEngine *e = engine(); @@ -177,6 +187,16 @@ void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(CallConte defineDefaultProperty(name, function); } +void Object::defineDefaultProperty(String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount) +{ + ExecutionEngine *e = engine(); + Scope scope(e); + ExecutionContext *global = e->rootContext(); + ScopedFunctionObject function(scope, BuiltinFunction::create(global, name, code)); + function->defineReadonlyProperty(e->id_length(), Primitive::fromInt32(argumentCount)); + defineDefaultProperty(name, function); +} + void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *)) { ExecutionEngine *e = engine(); @@ -196,6 +216,27 @@ void Object::defineAccessorProperty(String *name, ReturnedValue (*getter)(CallCo insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); } +void Object::defineAccessorProperty(const QString &name, void (*getter)(const BuiltinFunction *, Scope &, CallData *), + void (*setter)(const BuiltinFunction *, Scope &, CallData *)) +{ + ExecutionEngine *e = engine(); + Scope scope(e); + ScopedString s(scope, e->newIdentifier(name)); + defineAccessorProperty(s, getter, setter); +} + +void Object::defineAccessorProperty(String *name, void (*getter)(const BuiltinFunction *, Scope &, CallData *), + void (*setter)(const BuiltinFunction *, Scope &, 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::defineReadonlyProperty(const QString &name, const Value &value) { QV4::ExecutionEngine *e = engine(); @@ -213,12 +254,6 @@ void Object::markObjects(Heap::Base *that, ExecutionEngine *e) { Heap::Object *o = static_cast<Heap::Object *>(that); - if (o->inlineMemberSize) { - Value *v = o->propertyData(0); - for (uint i = 0; i < o->inlineMemberSize; ++i) - v[i].mark(e); - } - if (o->memberData) o->memberData->mark(e); if (o->arrayData) @@ -1120,6 +1155,49 @@ uint Object::getLength(const Managed *m) return v->toUInt32(); } +// 'var' is 'V' in 15.3.5.3. +ReturnedValue Object::instanceOf(const Object *typeObject, const Value &var) +{ + QV4::ExecutionEngine *engine = typeObject->internalClass()->engine; + + // 15.3.5.3, Assume F is a Function object. + const FunctionObject *function = typeObject->as<FunctionObject>(); + if (!function) + return engine->throwTypeError(); + + Heap::FunctionObject *f = function->d(); + if (function->isBoundFunction()) + f = function->cast<BoundFunction>()->target(); + + // 15.3.5.3, 1: HasInstance can only be used on an object + const Object *lhs = var.as<Object>(); + if (!lhs) + return Encode(false); + + // 15.3.5.3, 2 + const Object *o = f->protoProperty(); + if (!o) // 15.3.5.3, 3 + return engine->throwTypeError(); + + Heap::Object *v = lhs->d(); + + // 15.3.5.3, 4 + while (v) { + // 15.3.5.3, 4, a + v = v->prototype; + + // 15.3.5.3, 4, b + if (!v) + break; // will return false + + // 15.3.5.3, 4, c + else if (o->d() == v) + return Encode(true); + } + + return Encode(false); +} + bool Object::setArrayLength(uint newLen) { Q_ASSERT(isArrayObject()); diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 6c679deb10..6a543ae1a8 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -63,17 +63,17 @@ QT_BEGIN_NAMESPACE namespace QV4 { +struct BuiltinFunction; + namespace Heap { struct Object : Base { void init() { Base::init(); } void destroy() { Base::destroy(); } - const Value *propertyData(uint index) const { if (index < inlineMemberSize) return reinterpret_cast<const Value *>(this) + inlineMemberOffset + index; return memberData->data + index - inlineMemberSize; } - Value *propertyData(uint index) { if (index < inlineMemberSize) return reinterpret_cast<Value *>(this) + inlineMemberOffset + index; return memberData->data + index - inlineMemberSize; } + const Value *propertyData(uint index) const { return memberData->data + index; } + Value *propertyData(uint index) { return memberData->data + index; } - uint inlineMemberOffset; - uint inlineMemberSize; InternalClass *internalClass; Pointer<Object> prototype; Pointer<MemberData> memberData; @@ -140,6 +140,7 @@ struct ObjectVTable void (*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); }; #define DEFINE_OBJECT_VTABLE_BASE(classname) \ @@ -159,7 +160,8 @@ const QV4::ObjectVTable classname::static_vtbl = \ getLookup, \ setLookup, \ getLength, \ - advanceIterator \ + advanceIterator, \ + instanceOf \ } #define DEFINE_OBJECT_VTABLE(classname) \ @@ -238,9 +240,15 @@ struct Q_QML_EXPORT Object: Managed { } void defineDefaultProperty(const QString &name, const Value &value); void defineDefaultProperty(const QString &name, ReturnedValue (*code)(CallContext *), int argumentCount = 0); + void defineDefaultProperty(const QString &name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount = 0); void defineDefaultProperty(String *name, ReturnedValue (*code)(CallContext *), int argumentCount = 0); + void defineDefaultProperty(String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount = 0); void defineAccessorProperty(const QString &name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *)); void defineAccessorProperty(String *name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *)); + 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 *)); /* Fixed: Writable: false, Enumerable: false, Configurable: false */ void defineReadonlyProperty(const QString &name, const Value &value); void defineReadonlyProperty(String *name, const Value &value); @@ -345,6 +353,8 @@ public: 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); } @@ -366,6 +376,7 @@ protected: static void 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); private: ReturnedValue internalGet(String *name, bool *hasProperty) const; diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp index 8191083544..97dbe24339 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -121,100 +121,100 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) insertMember(v4->id___proto__(), p, Attr_Accessor|Attr_NotEnumerable); } -ReturnedValue ObjectPrototype::method_getPrototypeOf(CallContext *ctx) +void ObjectPrototype::method_getPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject o(scope, ctx->argument(0)); + ScopedObject o(scope, callData->argument(0)); if (!o) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); ScopedObject p(scope, o->prototype()); - return !!p ? p->asReturnedValue() : Encode::null(); + scope.result = !!p ? p->asReturnedValue() : Encode::null(); } -ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(CallContext *ctx) +void ObjectPrototype::method_getOwnPropertyDescriptor(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject O(scope, ctx->argument(0)); - if (!O) - return ctx->engine()->throwTypeError(); + ScopedObject O(scope, callData->argument(0)); + if (!O) { + scope.result = scope.engine->throwTypeError(); + return; + } if (ArgumentsObject::isNonStrictArgumentsObject(O)) static_cast<ArgumentsObject *>(O.getPointer())->fullyCreate(); - ScopedValue v(scope, ctx->argument(1)); + ScopedValue v(scope, callData->argument(1)); ScopedString name(scope, v->toString(scope.engine)); - if (scope.hasException()) - return Encode::undefined(); + CHECK_EXCEPTION(); + PropertyAttributes attrs; ScopedProperty desc(scope); O->getOwnProperty(name, &attrs, desc); - return fromPropertyDescriptor(scope.engine, desc, attrs); + scope.result = fromPropertyDescriptor(scope.engine, desc, attrs); } -ReturnedValue ObjectPrototype::method_getOwnPropertyNames(CallContext *context) +void ObjectPrototype::method_getOwnPropertyNames(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(context); - ScopedObject O(scope, context->argument(0)); - if (!O) - return context->engine()->throwTypeError(); + ScopedObject O(scope, callData->argument(0)); + if (!O) { + scope.result = scope.engine->throwTypeError(); + return; + } - ScopedArrayObject array(scope, getOwnPropertyNames(context->d()->engine, context->args()[0])); - return array.asReturnedValue(); + scope.result = getOwnPropertyNames(scope.engine, callData->args[0]); } -ReturnedValue ObjectPrototype::method_create(CallContext *ctx) +void ObjectPrototype::method_create(const BuiltinFunction *builtin, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedValue O(scope, ctx->argument(0)); - if (!O->isObject() && !O->isNull()) - return ctx->engine()->throwTypeError(); + ScopedValue O(scope, callData->argument(0)); + if (!O->isObject() && !O->isNull()) { + scope.result = scope.engine->throwTypeError(); + return; + } - ScopedObject newObject(scope, ctx->d()->engine->newObject()); + ScopedObject newObject(scope, scope.engine->newObject()); newObject->setPrototype(O->as<Object>()); - if (ctx->argc() > 1 && !ctx->args()[1].isUndefined()) { - ctx->d()->callData->args[0] = newObject.asReturnedValue(); - return method_defineProperties(ctx); + if (callData->argc > 1 && !callData->args[1].isUndefined()) { + callData->args[0] = newObject; + method_defineProperties(builtin, scope, callData); + return; } - return newObject.asReturnedValue(); + scope.result = newObject; } -ReturnedValue ObjectPrototype::method_defineProperty(CallContext *ctx) +void ObjectPrototype::method_defineProperty(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject O(scope, ctx->argument(0)); - if (!O) - return ctx->engine()->throwTypeError(); + ScopedObject O(scope, callData->argument(0)); + if (!O) { + scope.result = scope.engine->throwTypeError(); + return; + } - ScopedString name(scope, ctx->argument(1), ScopedString::Convert); - if (scope.engine->hasException) - return Encode::undefined(); + ScopedString name(scope, callData->argument(1), ScopedString::Convert); + CHECK_EXCEPTION(); - ScopedValue attributes(scope, ctx->argument(2)); + ScopedValue attributes(scope, callData->argument(2)); ScopedProperty pd(scope); PropertyAttributes attrs; toPropertyDescriptor(scope.engine, attributes, pd, &attrs); - if (scope.engine->hasException) - return Encode::undefined(); + CHECK_EXCEPTION(); if (!O->__defineOwnProperty__(scope.engine, name, pd, attrs)) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - return O.asReturnedValue(); + scope.result = O; } -ReturnedValue ObjectPrototype::method_defineProperties(CallContext *ctx) +void ObjectPrototype::method_defineProperties(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject O(scope, ctx->argument(0)); + ScopedObject O(scope, callData->argument(0)); if (!O) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); + + ScopedObject o(scope, callData->argument(1), ScopedObject::Convert); + CHECK_EXCEPTION(); - ScopedObject o(scope, ctx->argument(1), ScopedObject::Convert); - if (scope.engine->hasException) - return Encode::undefined(); ScopedValue val(scope); ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly); @@ -230,26 +230,24 @@ ReturnedValue ObjectPrototype::method_defineProperties(CallContext *ctx) PropertyAttributes nattrs; val = o->getValue(pd->value, attrs); toPropertyDescriptor(scope.engine, val, n, &nattrs); - if (scope.engine->hasException) - return Encode::undefined(); + CHECK_EXCEPTION(); bool ok; if (name) ok = O->__defineOwnProperty__(scope.engine, name, n, nattrs); else ok = O->__defineOwnProperty__(scope.engine, index, n, nattrs); if (!ok) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); } - return O.asReturnedValue(); + scope.result = O; } -ReturnedValue ObjectPrototype::method_seal(CallContext *ctx) +void ObjectPrototype::method_seal(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject o(scope, ctx->argument(0)); + ScopedObject o(scope, callData->argument(0)); if (!o) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); o->setInternalClass(o->internalClass()->sealed()); @@ -261,15 +259,14 @@ ReturnedValue ObjectPrototype::method_seal(CallContext *ctx) } } - return o.asReturnedValue(); + scope.result = o; } -ReturnedValue ObjectPrototype::method_freeze(CallContext *ctx) +void ObjectPrototype::method_freeze(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject o(scope, ctx->argument(0)); + ScopedObject o(scope, callData->argument(0)); if (!o) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); if (ArgumentsObject::isNonStrictArgumentsObject(o)) static_cast<ArgumentsObject *>(o.getPointer())->fullyCreate(); @@ -285,96 +282,111 @@ ReturnedValue ObjectPrototype::method_freeze(CallContext *ctx) o->arrayData()->attrs[i].setWritable(false); } } - return o.asReturnedValue(); + scope.result = o; } -ReturnedValue ObjectPrototype::method_preventExtensions(CallContext *ctx) +void ObjectPrototype::method_preventExtensions(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject o(scope, ctx->argument(0)); + ScopedObject o(scope, callData->argument(0)); if (!o) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); o->setInternalClass(o->internalClass()->nonExtensible()); - return o.asReturnedValue(); + scope.result = o; } -ReturnedValue ObjectPrototype::method_isSealed(CallContext *ctx) +void ObjectPrototype::method_isSealed(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject o(scope, ctx->argument(0)); + ScopedObject o(scope, callData->argument(0)); if (!o) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - if (o->isExtensible()) - return Encode(false); + if (o->isExtensible()) { + scope.result = Encode(false); + return; + } - if (o->internalClass() != o->internalClass()->sealed()) - return Encode(false); + if (o->internalClass() != o->internalClass()->sealed()) { + scope.result = Encode(false); + return; + } - if (!o->arrayData() || !o->arrayData()->length()) - return Encode(true); + if (!o->arrayData() || !o->arrayData()->length()) { + scope.result = Encode(true); + return; + } Q_ASSERT(o->arrayData() && o->arrayData()->length()); - if (!o->arrayData()->attrs) - return Encode(false); + if (!o->arrayData()->attrs) { + scope.result = Encode(false); + return; + } for (uint i = 0; i < o->arrayData()->alloc; ++i) { if (!o->arrayData()->isEmpty(i)) - if (o->arrayData()->attributes(i).isConfigurable()) - return Encode(false); + if (o->arrayData()->attributes(i).isConfigurable()) { + scope.result = Encode(false); + return; + } } - return Encode(true); + scope.result = Encode(true); } -ReturnedValue ObjectPrototype::method_isFrozen(CallContext *ctx) +void ObjectPrototype::method_isFrozen(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject o(scope, ctx->argument(0)); + ScopedObject o(scope, callData->argument(0)); if (!o) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - if (o->isExtensible()) - return Encode(false); + if (o->isExtensible()) { + scope.result = Encode(false); + return; + } - if (o->internalClass() != o->internalClass()->frozen()) - return Encode(false); + if (o->internalClass() != o->internalClass()->frozen()) { + scope.result = Encode(false); + return; + } - if (!o->arrayData() || !o->arrayData()->length()) - return Encode(true); + if (!o->arrayData() || !o->arrayData()->length()) { + scope.result = Encode(true); + return; + } Q_ASSERT(o->arrayData() && o->arrayData()->length()); - if (!o->arrayData()->attrs) - return Encode(false); + if (!o->arrayData()->attrs) { + scope.result = Encode(false); + return; + } for (uint i = 0; i < o->arrayData()->alloc; ++i) { if (!o->arrayData()->isEmpty(i)) - if (o->arrayData()->attributes(i).isConfigurable() || o->arrayData()->attributes(i).isWritable()) - return Encode(false); + if (o->arrayData()->attributes(i).isConfigurable() || o->arrayData()->attributes(i).isWritable()) { + scope.result = Encode(false); + return; + } } - return Encode(true); + scope.result = Encode(true); } -ReturnedValue ObjectPrototype::method_isExtensible(CallContext *ctx) +void ObjectPrototype::method_isExtensible(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject o(scope, ctx->argument(0)); + ScopedObject o(scope, callData->argument(0)); if (!o) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - return Encode((bool)o->isExtensible()); + scope.result = Encode((bool)o->isExtensible()); } -ReturnedValue ObjectPrototype::method_keys(CallContext *ctx) +void ObjectPrototype::method_keys(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject o(scope, ctx->argument(0)); + ScopedObject o(scope, callData->argument(0)); if (!o) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - ScopedArrayObject a(scope, ctx->d()->engine->newArrayObject()); + ScopedArrayObject a(scope, scope.engine->newArrayObject()); ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly); ScopedValue name(scope); @@ -385,175 +397,159 @@ ReturnedValue ObjectPrototype::method_keys(CallContext *ctx) a->push_back(name); } - return a.asReturnedValue(); + scope.result = a; } -ReturnedValue ObjectPrototype::method_toString(CallContext *ctx) +void ObjectPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - if (ctx->thisObject().isUndefined()) { - return ctx->d()->engine->newString(QStringLiteral("[object Undefined]"))->asReturnedValue(); - } else if (ctx->thisObject().isNull()) { - return ctx->d()->engine->newString(QStringLiteral("[object Null]"))->asReturnedValue(); + 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]")); } else { - ScopedObject obj(scope, ctx->thisObject().toObject(scope.engine)); + ScopedObject obj(scope, callData->thisObject.toObject(scope.engine)); QString className = obj->className(); - return ctx->d()->engine->newString(QStringLiteral("[object %1]").arg(className))->asReturnedValue(); + scope.result = scope.engine->newString(QStringLiteral("[object %1]").arg(className)); } } -ReturnedValue ObjectPrototype::method_toLocaleString(CallContext *ctx) +void ObjectPrototype::method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject o(scope, ctx->thisObject().toObject(scope.engine)); + ScopedObject o(scope, callData->thisObject.toObject(scope.engine)); if (!o) - return Encode::undefined(); - ScopedFunctionObject f(scope, o->get(ctx->d()->engine->id_toString())); + RETURN_UNDEFINED(); + + ScopedFunctionObject f(scope, o->get(scope.engine->id_toString())); if (!f) - return ctx->engine()->throwTypeError(); - ScopedCallData callData(scope); - callData->thisObject = o; + THROW_TYPE_ERROR(); + ScopedCallData cData(scope); + cData->thisObject = o; f->call(scope, callData); - return scope.result.asReturnedValue(); } -ReturnedValue ObjectPrototype::method_valueOf(CallContext *ctx) +void ObjectPrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedValue v(scope, ctx->thisObject().toObject(scope.engine)); - if (ctx->d()->engine->hasException) - return Encode::undefined(); - return v->asReturnedValue(); + scope.result = callData->thisObject.toObject(scope.engine); } -ReturnedValue ObjectPrototype::method_hasOwnProperty(CallContext *ctx) +void ObjectPrototype::method_hasOwnProperty(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedString P(scope, ctx->argument(0), ScopedString::Convert); - if (scope.engine->hasException) - return Encode::undefined(); - ScopedObject O(scope, ctx->thisObject(), ScopedObject::Convert); - if (scope.engine->hasException) - return Encode::undefined(); + ScopedString P(scope, callData->argument(0), ScopedString::Convert); + CHECK_EXCEPTION(); + ScopedObject O(scope, callData->thisObject, ScopedObject::Convert); + CHECK_EXCEPTION(); bool r = O->hasOwnProperty(P); if (!r) r = !O->query(P).isEmpty(); - return Encode(r); + scope.result = Encode(r); } -ReturnedValue ObjectPrototype::method_isPrototypeOf(CallContext *ctx) +void ObjectPrototype::method_isPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject V(scope, ctx->argument(0)); - if (!V) - return Encode(false); + ScopedObject V(scope, callData->argument(0)); + if (!V) { + scope.result = Encode(false); + return; + } - ScopedObject O(scope, ctx->thisObject(), ScopedObject::Convert); - if (scope.engine->hasException) - return Encode::undefined(); + ScopedObject O(scope, callData->thisObject, ScopedObject::Convert); + CHECK_EXCEPTION(); ScopedObject proto(scope, V->prototype()); while (proto) { - if (O->d() == proto->d()) - return Encode(true); + if (O->d() == proto->d()) { + scope.result = Encode(true); + return; + } proto = proto->prototype(); } - return Encode(false); + scope.result = Encode(false); } -ReturnedValue ObjectPrototype::method_propertyIsEnumerable(CallContext *ctx) +void ObjectPrototype::method_propertyIsEnumerable(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedString p(scope, ctx->argument(0), ScopedString::Convert); - if (scope.engine->hasException) - return Encode::undefined(); + ScopedString p(scope, callData->argument(0), ScopedString::Convert); + CHECK_EXCEPTION(); - ScopedObject o(scope, ctx->thisObject(), ScopedObject::Convert); - if (scope.engine->hasException) - return Encode::undefined(); + ScopedObject o(scope, callData->thisObject, ScopedObject::Convert); + CHECK_EXCEPTION(); PropertyAttributes attrs; o->getOwnProperty(p, &attrs); - return Encode(attrs.isEnumerable()); + scope.result = Encode(attrs.isEnumerable()); } -ReturnedValue ObjectPrototype::method_defineGetter(CallContext *ctx) +void ObjectPrototype::method_defineGetter(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() < 2) - return ctx->engine()->throwTypeError(); + if (callData->argc < 2) + THROW_TYPE_ERROR(); - Scope scope(ctx); - ScopedFunctionObject f(scope, ctx->argument(1)); + ScopedFunctionObject f(scope, callData->argument(1)); if (!f) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - ScopedString prop(scope, ctx->argument(0), ScopedString::Convert); - if (scope.engine->hasException) - return Encode::undefined(); + ScopedString prop(scope, callData->argument(0), ScopedString::Convert); + CHECK_EXCEPTION(); - ScopedObject o(scope, ctx->thisObject()); + ScopedObject o(scope, callData->thisObject); if (!o) { - if (!ctx->thisObject().isUndefined()) - return Encode::undefined(); - o = ctx->d()->engine->globalObject; + if (!callData->thisObject.isUndefined()) + RETURN_UNDEFINED(); + o = scope.engine->globalObject; } ScopedProperty pd(scope); pd->value = f; pd->set = Primitive::emptyValue(); o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor); - return Encode::undefined(); + RETURN_UNDEFINED(); } -ReturnedValue ObjectPrototype::method_defineSetter(CallContext *ctx) +void ObjectPrototype::method_defineSetter(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() < 2) - return ctx->engine()->throwTypeError(); + if (callData->argc < 2) + THROW_TYPE_ERROR(); - Scope scope(ctx); - ScopedFunctionObject f(scope, ctx->argument(1)); + ScopedFunctionObject f(scope, callData->argument(1)); if (!f) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - ScopedString prop(scope, ctx->argument(0), ScopedString::Convert); - if (scope.engine->hasException) - return Encode::undefined(); + ScopedString prop(scope, callData->argument(0), ScopedString::Convert); + CHECK_EXCEPTION(); - ScopedObject o(scope, ctx->thisObject()); + ScopedObject o(scope, callData->thisObject); if (!o) { - if (!ctx->thisObject().isUndefined()) - return Encode::undefined(); - o = ctx->d()->engine->globalObject; + if (!callData->thisObject.isUndefined()) + RETURN_UNDEFINED(); + o = scope.engine->globalObject; } ScopedProperty pd(scope); pd->value = Primitive::emptyValue(); pd->set = f; o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor); - return Encode::undefined(); + RETURN_UNDEFINED(); } -ReturnedValue ObjectPrototype::method_get_proto(CallContext *ctx) +void ObjectPrototype::method_get_proto(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject o(scope, ctx->thisObject().as<Object>()); + ScopedObject o(scope, callData->thisObject.as<Object>()); if (!o) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - return o->prototype()->asReturnedValue(); + scope.result = o->prototype(); } -ReturnedValue ObjectPrototype::method_set_proto(CallContext *ctx) +void ObjectPrototype::method_set_proto(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedObject o(scope, ctx->thisObject()); - if (!o || !ctx->argc()) - return ctx->engine()->throwTypeError(); + ScopedObject o(scope, callData->thisObject); + if (!o || !callData->argc) + THROW_TYPE_ERROR(); - if (ctx->args()[0].isNull()) { + if (callData->args[0].isNull()) { o->setPrototype(0); - return Encode::undefined(); + RETURN_UNDEFINED(); } - ScopedObject p(scope, ctx->args()[0]); + ScopedObject p(scope, callData->args[0]); bool ok = false; if (!!p) { if (o->prototype() == p->d()) { @@ -562,9 +558,11 @@ ReturnedValue ObjectPrototype::method_set_proto(CallContext *ctx) ok = o->setPrototype(p); } } - if (!ok) - return ctx->engine()->throwTypeError(QStringLiteral("Cyclic __proto__ value")); - return Encode::undefined(); + if (!ok) { + scope.result = scope.engine->throwTypeError(QStringLiteral("Cyclic __proto__ value")); + return; + } + RETURN_UNDEFINED(); } void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value &v, Property *desc, PropertyAttributes *attrs) diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h index e3d85782d5..1db8615511 100644 --- a/src/qml/jsruntime/qv4objectproto_p.h +++ b/src/qml/jsruntime/qv4objectproto_p.h @@ -78,32 +78,32 @@ struct ObjectPrototype: Object { void init(ExecutionEngine *engine, Object *ctor); - static ReturnedValue method_getPrototypeOf(CallContext *ctx); - static ReturnedValue method_getOwnPropertyDescriptor(CallContext *ctx); - static ReturnedValue method_getOwnPropertyNames(CallContext *context); - static ReturnedValue method_create(CallContext *ctx); - static ReturnedValue method_defineProperty(CallContext *ctx); - static ReturnedValue method_defineProperties(CallContext *ctx); - static ReturnedValue method_seal(CallContext *ctx); - static ReturnedValue method_freeze(CallContext *ctx); - static ReturnedValue method_preventExtensions(CallContext *ctx); - static ReturnedValue method_isSealed(CallContext *ctx); - static ReturnedValue method_isFrozen(CallContext *ctx); - static ReturnedValue method_isExtensible(CallContext *ctx); - static ReturnedValue method_keys(CallContext *ctx); - - static ReturnedValue method_toString(CallContext *ctx); - static ReturnedValue method_toLocaleString(CallContext *ctx); - static ReturnedValue method_valueOf(CallContext *ctx); - static ReturnedValue method_hasOwnProperty(CallContext *ctx); - static ReturnedValue method_isPrototypeOf(CallContext *ctx); - static ReturnedValue method_propertyIsEnumerable(CallContext *ctx); - - static ReturnedValue method_defineGetter(CallContext *ctx); - static ReturnedValue method_defineSetter(CallContext *ctx); - - static ReturnedValue method_get_proto(CallContext *ctx); - static ReturnedValue method_set_proto(CallContext *ctx); + 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_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 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/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 77dbb18b50..7260e71fab 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -826,40 +826,39 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase } // namespace QV4 -ReturnedValue QObjectWrapper::method_connect(CallContext *ctx) +void QObjectWrapper::method_connect(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() == 0) - V4THROW_ERROR("Function.prototype.connect: no arguments given"); + if (callData->argc == 0) + THROW_GENERIC_ERROR("Function.prototype.connect: no arguments given"); - QPair<QObject *, int> signalInfo = extractQtSignal(ctx->thisObject()); + QPair<QObject *, int> signalInfo = extractQtSignal(callData->thisObject); QObject *signalObject = signalInfo.first; int signalIndex = signalInfo.second; // in method range, not signal range! if (signalIndex < 0) - V4THROW_ERROR("Function.prototype.connect: this object is not a signal"); + THROW_GENERIC_ERROR("Function.prototype.connect: this object is not a signal"); if (!signalObject) - V4THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject"); + THROW_GENERIC_ERROR("Function.prototype.connect: cannot connect to deleted QObject"); if (signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal) - V4THROW_ERROR("Function.prototype.connect: this object is not a signal"); + THROW_GENERIC_ERROR("Function.prototype.connect: this object is not a signal"); - QV4::Scope scope(ctx); QV4::ScopedFunctionObject f(scope); QV4::ScopedValue thisObject (scope, QV4::Encode::undefined()); - if (ctx->argc() == 1) { - f = ctx->args()[0]; - } else if (ctx->argc() >= 2) { - thisObject = ctx->args()[0]; - f = ctx->args()[1]; + if (callData->argc == 1) { + f = callData->args[0]; + } else if (callData->argc >= 2) { + thisObject = callData->args[0]; + f = callData->args[1]; } if (!f) - V4THROW_ERROR("Function.prototype.connect: target is not a function"); + THROW_GENERIC_ERROR("Function.prototype.connect: target is not a function"); if (!thisObject->isUndefined() && !thisObject->isObject()) - V4THROW_ERROR("Function.prototype.connect: target this is not an object"); + THROW_GENERIC_ERROR("Function.prototype.connect: target this is not an object"); QV4::QObjectSlotDispatcher *slot = new QV4::QObjectSlotDispatcher; slot->signalIndex = signalIndex; @@ -874,49 +873,47 @@ ReturnedValue QObjectWrapper::method_connect(CallContext *ctx) } QObjectPrivate::connect(signalObject, signalIndex, slot, Qt::AutoConnection); - return Encode::undefined(); + RETURN_UNDEFINED(); } -ReturnedValue QObjectWrapper::method_disconnect(CallContext *ctx) +void QObjectWrapper::method_disconnect(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() == 0) - V4THROW_ERROR("Function.prototype.disconnect: no arguments given"); - - QV4::Scope scope(ctx); + if (callData->argc == 0) + THROW_GENERIC_ERROR("Function.prototype.disconnect: no arguments given"); - QPair<QObject *, int> signalInfo = extractQtSignal(ctx->thisObject()); + QPair<QObject *, int> signalInfo = extractQtSignal(callData->thisObject); QObject *signalObject = signalInfo.first; int signalIndex = signalInfo.second; if (signalIndex == -1) - V4THROW_ERROR("Function.prototype.disconnect: this object is not a signal"); + THROW_GENERIC_ERROR("Function.prototype.disconnect: this object is not a signal"); if (!signalObject) - V4THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject"); + THROW_GENERIC_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject"); if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal) - V4THROW_ERROR("Function.prototype.disconnect: this object is not a signal"); + THROW_GENERIC_ERROR("Function.prototype.disconnect: this object is not a signal"); QV4::ScopedFunctionObject functionValue(scope); QV4::ScopedValue functionThisValue(scope, QV4::Encode::undefined()); - if (ctx->argc() == 1) { - functionValue = ctx->args()[0]; - } else if (ctx->argc() >= 2) { - functionThisValue = ctx->args()[0]; - functionValue = ctx->args()[1]; + if (callData->argc == 1) { + functionValue = callData->args[0]; + } else if (callData->argc >= 2) { + functionThisValue = callData->args[0]; + functionValue = callData->args[1]; } if (!functionValue) - V4THROW_ERROR("Function.prototype.disconnect: target is not a function"); + THROW_GENERIC_ERROR("Function.prototype.disconnect: target is not a function"); if (!functionThisValue->isUndefined() && !functionThisValue->isObject()) - V4THROW_ERROR("Function.prototype.disconnect: target this is not an object"); + THROW_GENERIC_ERROR("Function.prototype.disconnect: target this is not an object"); QPair<QObject *, int> functionData = QObjectMethod::extractQtMethod(functionValue); void *a[] = { - ctx->d()->engine, + scope.engine, functionValue.ptr, functionThisValue.ptr, functionData.first, @@ -925,7 +922,7 @@ ReturnedValue QObjectWrapper::method_disconnect(CallContext *ctx) QObjectPrivate::disconnect(signalObject, signalIndex, reinterpret_cast<void**>(&a)); - return Encode::undefined(); + RETURN_UNDEFINED(); } static void markChildQObjectsRecursively(QObject *parent, QV4::ExecutionEngine *e) diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index c7c4f4dd77..b09e06cec5 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -197,8 +197,8 @@ protected: static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); static void markObjects(Heap::Base *that, QV4::ExecutionEngine *e); - static ReturnedValue method_connect(CallContext *ctx); - static ReturnedValue method_disconnect(CallContext *ctx); + static void method_connect(const BuiltinFunction *, Scope &scope, CallData *callData); + static void method_disconnect(const BuiltinFunction *, Scope &scope, CallData *callData); private: Q_NEVER_INLINE static ReturnedValue wrap_slowPath(ExecutionEngine *engine, QObject *object); diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 41d8010fef..40682aaa4b 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -349,34 +349,33 @@ void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor) defineDefaultProperty(QStringLiteral("compile"), method_compile, 2); } -ReturnedValue RegExpPrototype::method_exec(CallContext *ctx) +void RegExpPrototype::method_exec(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<RegExpObject> r(scope, ctx->thisObject().as<RegExpObject>()); + Scoped<RegExpObject> r(scope, callData->thisObject.as<RegExpObject>()); if (!r) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - ScopedValue arg(scope, ctx->argument(0)); + ScopedValue arg(scope, callData->argument(0)); ScopedString str(scope, arg->toString(scope.engine)); if (scope.hasException()) - return Encode::undefined(); + RETURN_UNDEFINED(); QString s = str->toQString(); int offset = r->global() ? r->lastIndexProperty()->toInt32() : 0; if (offset < 0 || offset > s.length()) { *r->lastIndexProperty() = Primitive::fromInt32(0); - return Encode::null(); + RETURN_RESULT(Encode::null()); } uint* matchOffsets = (uint*)alloca(r->value()->captureCount() * 2 * sizeof(uint)); const int result = Scoped<RegExp>(scope, r->value())->match(s, offset, matchOffsets); - Scoped<RegExpCtor> regExpCtor(scope, ctx->d()->engine->regExpCtor()); + Scoped<RegExpCtor> regExpCtor(scope, scope.engine->regExpCtor()); regExpCtor->d()->clearLastMatch(); if (result == -1) { *r->lastIndexProperty() = Primitive::fromInt32(0); - return Encode::null(); + RETURN_RESULT(Encode::null()); } // fill in result data @@ -387,7 +386,7 @@ ReturnedValue RegExpPrototype::method_exec(CallContext *ctx) for (int i = 0; i < len; ++i) { int start = matchOffsets[i * 2]; int end = matchOffsets[i * 2 + 1]; - v = (start != -1) ? ctx->d()->engine->newString(s.mid(start, end - start))->asReturnedValue() : Encode::undefined(); + v = (start != -1) ? scope.engine->newString(s.mid(start, end - start))->asReturnedValue() : Encode::undefined(); array->arrayPut(i, v); } array->setArrayLengthUnchecked(len); @@ -403,84 +402,75 @@ ReturnedValue RegExpPrototype::method_exec(CallContext *ctx) if (r->global()) *r->lastIndexProperty() = Primitive::fromInt32(matchOffsets[1]); - return array.asReturnedValue(); + scope.result = array; } -ReturnedValue RegExpPrototype::method_test(CallContext *ctx) +void RegExpPrototype::method_test(const BuiltinFunction *b, Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedValue r(scope, method_exec(ctx)); - return Encode(!r->isNull()); + method_exec(b, scope, callData); + scope.result = Encode(!scope.result.isNull()); } -ReturnedValue RegExpPrototype::method_toString(CallContext *ctx) +void RegExpPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<RegExpObject> r(scope, ctx->thisObject().as<RegExpObject>()); + Scoped<RegExpObject> r(scope, callData->thisObject.as<RegExpObject>()); if (!r) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - return ctx->d()->engine->newString(r->toString())->asReturnedValue(); + scope.result = scope.engine->newString(r->toString()); } -ReturnedValue RegExpPrototype::method_compile(CallContext *ctx) +void RegExpPrototype::method_compile(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<RegExpObject> r(scope, ctx->thisObject().as<RegExpObject>()); + Scoped<RegExpObject> r(scope, callData->thisObject.as<RegExpObject>()); if (!r) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - ScopedCallData callData(scope, ctx->argc()); - memcpy(callData->args, ctx->args(), ctx->argc()*sizeof(Value)); + ScopedCallData cData(scope, callData->argc); + memcpy(cData->args, callData->args, callData->argc*sizeof(Value)); - ctx->d()->engine->regExpCtor()->as<FunctionObject>()->construct(scope, callData); + scope.engine->regExpCtor()->as<FunctionObject>()->construct(scope, cData); Scoped<RegExpObject> re(scope, scope.result.asReturnedValue()); r->d()->value = re->value(); r->d()->global = re->global(); - return Encode::undefined(); + RETURN_UNDEFINED(); } template <int index> -ReturnedValue RegExpPrototype::method_get_lastMatch_n(CallContext *ctx) +void RegExpPrototype::method_get_lastMatch_n(const BuiltinFunction *, Scope &scope, CallData *) { - Scope scope(ctx); - ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(ctx->d()->engine->regExpCtor())->lastMatch()); - ScopedValue result(scope, lastMatch ? lastMatch->getIndexed(index) : Encode::undefined()); - if (result->isUndefined()) - return ctx->d()->engine->newString()->asReturnedValue(); - return result->asReturnedValue(); + 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(); } -ReturnedValue RegExpPrototype::method_get_lastParen(CallContext *ctx) +void RegExpPrototype::method_get_lastParen(const BuiltinFunction *, Scope &scope, CallData *) { - Scope scope(ctx); - ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(ctx->d()->engine->regExpCtor())->lastMatch()); - ScopedValue result(scope, lastMatch ? lastMatch->getIndexed(lastMatch->getLength() - 1) : Encode::undefined()); - if (result->isUndefined()) - return ctx->d()->engine->newString()->asReturnedValue(); - return result->asReturnedValue(); + 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(); } -ReturnedValue RegExpPrototype::method_get_input(CallContext *ctx) +void RegExpPrototype::method_get_input(const BuiltinFunction *, Scope &scope, CallData *) { - return static_cast<RegExpCtor*>(ctx->d()->engine->regExpCtor())->lastInput()->asReturnedValue(); + scope.result = static_cast<RegExpCtor*>(scope.engine->regExpCtor())->lastInput(); } -ReturnedValue RegExpPrototype::method_get_leftContext(CallContext *ctx) +void RegExpPrototype::method_get_leftContext(const BuiltinFunction *, Scope &scope, CallData *) { - Scope scope(ctx); - Scoped<RegExpCtor> regExpCtor(scope, ctx->d()->engine->regExpCtor()); + Scoped<RegExpCtor> regExpCtor(scope, scope.engine->regExpCtor()); QString lastInput = regExpCtor->lastInput()->toQString(); - return ctx->d()->engine->newString(lastInput.left(regExpCtor->lastMatchStart()))->asReturnedValue(); + scope.result = scope.engine->newString(lastInput.left(regExpCtor->lastMatchStart())); } -ReturnedValue RegExpPrototype::method_get_rightContext(CallContext *ctx) +void RegExpPrototype::method_get_rightContext(const BuiltinFunction *, Scope &scope, CallData *) { - Scope scope(ctx); - Scoped<RegExpCtor> regExpCtor(scope, ctx->d()->engine->regExpCtor()); + Scoped<RegExpCtor> regExpCtor(scope, scope.engine->regExpCtor()); QString lastInput = regExpCtor->lastInput()->toQString(); - return ctx->d()->engine->newString(lastInput.mid(regExpCtor->lastMatchEnd()))->asReturnedValue(); + scope.result = 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 2c82cfdfd1..c0c7dfa78a 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -149,17 +149,17 @@ struct RegExpPrototype: RegExpObject { void init(ExecutionEngine *engine, Object *ctor); - static ReturnedValue method_exec(CallContext *ctx); - static ReturnedValue method_test(CallContext *ctx); - static ReturnedValue method_toString(CallContext *ctx); - static ReturnedValue method_compile(CallContext *ctx); + 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); template <int index> - static ReturnedValue method_get_lastMatch_n(CallContext *ctx); - static ReturnedValue method_get_lastParen(CallContext *ctx); - static ReturnedValue method_get_input(CallContext *ctx); - static ReturnedValue method_get_leftContext(CallContext *ctx); - static ReturnedValue method_get_rightContext(CallContext *ctx); + 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); }; } diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 023a739e33..7f184f8221 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -343,35 +343,15 @@ ReturnedValue Runtime::method_deleteName(ExecutionEngine *engine, int nameIndex) return Encode(engine->currentContext->deleteProperty(name)); } -QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Value &left, const Value &right) +QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Value &lval, const Value &rval) { - const FunctionObject *function = right.as<FunctionObject>(); - if (!function) - return engine->throwTypeError(); - - Heap::FunctionObject *f = function->d(); - if (function->isBoundFunction()) - f = function->cast<BoundFunction>()->target(); - - const Object *o = left.as<Object>(); - if (!o) - return Encode(false); - Heap::Object *v = o->d(); - - o = f->protoProperty(); - if (!o) - return engine->throwTypeError(); - - while (v) { - v = v->prototype; - - if (!v) - break; - else if (o->d() == v) - return Encode(true); - } + // 11.8.6, 5: rval must be an Object + const Object *rhs = rval.as<Object>(); + if (!rhs) + return engine->throwTypeError(); - return Encode(false); + // 11.8.6, 7: call "HasInstance", which we term instanceOf, and return the result. + return rhs->instanceOf(lval); } QV4::ReturnedValue Runtime::method_in(ExecutionEngine *engine, const Value &left, const Value &right) diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index 4e627e003f..6775028272 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -68,6 +68,38 @@ namespace QV4 { struct ScopedValue; +#define CHECK_EXCEPTION() \ + do { \ + if (scope.hasException()) { \ + scope.result = QV4::Encode::undefined(); \ + return; \ + } \ + } while (false) + +#define RETURN_UNDEFINED() \ + do { \ + scope.result = QV4::Encode::undefined(); \ + return; \ + } while (false) + +#define RETURN_RESULT(r) \ + do { \ + scope.result = r; \ + return; \ + } while (false) + +#define THROW_TYPE_ERROR() \ + do { \ + scope.result = scope.engine->throwTypeError(); \ + return; \ + } while (false) + +#define THROW_GENERIC_ERROR(str) \ + do { \ + scope.result = scope.engine->throwError(QString::fromUtf8(str)); \ + return; \ + } while (false) + struct Scope { inline Scope(ExecutionContext *ctx) : engine(ctx->d()->engine) diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index 58da7b9f68..8ce10e326d 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -404,28 +404,28 @@ public: struct CompareFunctor { - CompareFunctor(QV4::ExecutionContext *ctx, const QV4::Value &compareFn) - : m_ctx(ctx), m_compareFn(&compareFn) + CompareFunctor(QV4::ExecutionEngine *v4, const QV4::Value &compareFn) + : m_v4(v4), m_compareFn(&compareFn) {} bool operator()(typename Container::value_type lhs, typename Container::value_type rhs) { - QV4::Scope scope(m_ctx); + QV4::Scope scope(m_v4); ScopedObject compare(scope, m_compareFn); ScopedCallData callData(scope, 2); - callData->args[0] = convertElementToValue(this->m_ctx->d()->engine, lhs); - callData->args[1] = convertElementToValue(this->m_ctx->d()->engine, rhs); - callData->thisObject = this->m_ctx->d()->engine->globalObject; + 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; } private: - QV4::ExecutionContext *m_ctx; + QV4::ExecutionEngine *m_v4; const QV4::Value *m_compareFn; }; - void sort(QV4::CallContext *ctx) + void sort(const BuiltinFunction *, Scope &scope, CallData *callData) { if (d()->isReference) { if (!d()->object) @@ -433,9 +433,8 @@ public: loadReference(); } - QV4::Scope scope(ctx); - if (ctx->argc() == 1 && ctx->args()[0].as<FunctionObject>()) { - CompareFunctor cf(ctx, ctx->args()[0]); + 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 { DefaultCompareFunctor cf; @@ -446,45 +445,43 @@ public: storeReference(); } - static QV4::ReturnedValue method_get_length(QV4::CallContext *ctx) + static void method_get_length(const BuiltinFunction *, Scope &scope, CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped<QQmlSequence<Container> > This(scope, ctx->thisObject().as<QQmlSequence<Container> >()); + QV4::Scoped<QQmlSequence<Container> > This(scope, callData->thisObject.as<QQmlSequence<Container> >()); if (!This) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); if (This->d()->isReference) { if (!This->d()->object) - return QV4::Encode(0); + RETURN_RESULT(Encode(0)); This->loadReference(); } - return QV4::Encode(This->d()->container->count()); + RETURN_RESULT(Encode(This->d()->container->count())); } - static QV4::ReturnedValue method_set_length(QV4::CallContext* ctx) + static void method_set_length(const BuiltinFunction *, Scope &scope, CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped<QQmlSequence<Container> > This(scope, ctx->thisObject().as<QQmlSequence<Container> >()); + QV4::Scoped<QQmlSequence<Container> > This(scope, callData->thisObject.as<QQmlSequence<Container> >()); if (!This) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - quint32 newLength = ctx->args()[0].toUInt32(); + quint32 newLength = callData->args[0].toUInt32(); /* Qt containers have int (rather than uint) allowable indexes. */ if (newLength > INT_MAX) { generateWarning(scope.engine, QLatin1String("Index out of range during length set")); - return QV4::Encode::undefined(); + RETURN_UNDEFINED(); } /* Read the sequence from the QObject property if we're a reference */ if (This->d()->isReference) { if (!This->d()->object) - return QV4::Encode::undefined(); + RETURN_UNDEFINED(); This->loadReference(); } /* Determine whether we need to modify the sequence */ qint32 newCount = static_cast<qint32>(newLength); qint32 count = This->d()->container->count(); if (newCount == count) { - return QV4::Encode::undefined(); + RETURN_UNDEFINED(); } else if (newCount > count) { /* according to ECMA262r3 we need to insert */ /* undefined values increasing length to newLength. */ @@ -506,7 +503,7 @@ public: /* write back. already checked that object is non-null, so skip that check here. */ This->storeReference(); } - return QV4::Encode::undefined(); + RETURN_UNDEFINED(); } QVariant toVariant() const @@ -625,26 +622,25 @@ void SequencePrototype::init() } #undef REGISTER_QML_SEQUENCE_METATYPE -QV4::ReturnedValue SequencePrototype::method_sort(QV4::CallContext *ctx) +void SequencePrototype::method_sort(const BuiltinFunction *b, Scope &scope, CallData *callData) { - QV4::Scope scope(ctx); - QV4::ScopedObject o(scope, ctx->thisObject()); + QV4::ScopedObject o(scope, callData->thisObject); if (!o || !o->isListType()) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - if (ctx->argc() >= 2) - return o.asReturnedValue(); + if (callData->argc >= 2) + RETURN_RESULT(o); #define CALL_SORT(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue) \ if (QQml##SequenceElementTypeName##List *s = o->as<QQml##SequenceElementTypeName##List>()) { \ - s->sort(ctx); \ + s->sort(b, scope, callData); \ } else FOREACH_QML_SEQUENCE_TYPE(CALL_SORT) #undef CALL_SORT {} - return o.asReturnedValue(); + RETURN_RESULT(o); } #define IS_SEQUENCE(unused1, unused2, SequenceType, unused3) \ diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h index c0416ad639..6f96b9f760 100644 --- a/src/qml/jsruntime/qv4sequenceobject_p.h +++ b/src/qml/jsruntime/qv4sequenceobject_p.h @@ -67,12 +67,12 @@ struct SequencePrototype : public QV4::Object { void init(); - static ReturnedValue method_valueOf(QV4::CallContext *ctx) + static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData) { - return ctx->thisObject().toString(ctx->engine())->asReturnedValue(); + scope.result = callData->thisObject.toString(scope.engine); } - static ReturnedValue method_sort(QV4::CallContext *ctx); + static void method_sort(const BuiltinFunction *, Scope &scope, 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/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index 6fbf1c3c85..3c6a24e035 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -214,10 +214,9 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("trim"), method_trim); } -static QString getThisString(ExecutionContext *ctx) +static QString getThisString(Scope &scope, CallData *callData) { - Scope scope(ctx); - ScopedValue t(scope, ctx->thisObject()); + ScopedValue t(scope, callData->thisObject); if (String *s = t->stringValue()) return s->toQString(); if (StringObject *thisString = t->as<StringObject>()) @@ -229,158 +228,146 @@ static QString getThisString(ExecutionContext *ctx) return t->toQString(); } -ReturnedValue StringPrototype::method_toString(CallContext *context) +void StringPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (context->thisObject().isString()) - return context->thisObject().asReturnedValue(); + if (callData->thisObject.isString()) + RETURN_RESULT(callData->thisObject); - StringObject *o = context->thisObject().as<StringObject>(); + StringObject *o = callData->thisObject.as<StringObject>(); if (!o) - return context->engine()->throwTypeError(); - return Encode(o->d()->string); + THROW_TYPE_ERROR(); + scope.result = o->d()->string; } -ReturnedValue StringPrototype::method_charAt(CallContext *context) +void StringPrototype::method_charAt(const BuiltinFunction *, Scope &scope, CallData *callData) { - const QString str = getThisString(context); - if (context->d()->engine->hasException) - return Encode::undefined(); + const QString str = getThisString(scope, callData); + CHECK_EXCEPTION(); int pos = 0; - if (context->argc() > 0) - pos = (int) context->args()[0].toInteger(); + if (callData->argc > 0) + pos = (int) callData->args[0].toInteger(); QString result; if (pos >= 0 && pos < str.length()) result += str.at(pos); - return context->d()->engine->newString(result)->asReturnedValue(); + scope.result = scope.engine->newString(result); } -ReturnedValue StringPrototype::method_charCodeAt(CallContext *context) +void StringPrototype::method_charCodeAt(const BuiltinFunction *, Scope &scope, CallData *callData) { - const QString str = getThisString(context); - if (context->d()->engine->hasException) - return Encode::undefined(); + const QString str = getThisString(scope, callData); + CHECK_EXCEPTION(); int pos = 0; - if (context->argc() > 0) - pos = (int) context->args()[0].toInteger(); + if (callData->argc > 0) + pos = (int) callData->args[0].toInteger(); if (pos >= 0 && pos < str.length()) - return Encode(str.at(pos).unicode()); + RETURN_RESULT(Encode(str.at(pos).unicode())); - return Encode(qt_qnan()); + scope.result = Encode(qt_qnan()); } -ReturnedValue StringPrototype::method_concat(CallContext *context) +void StringPrototype::method_concat(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(context); - - QString value = getThisString(context); - if (scope.engine->hasException) - return Encode::undefined(); + QString value = getThisString(scope, callData); + CHECK_EXCEPTION(); ScopedString s(scope); - for (int i = 0; i < context->argc(); ++i) { - s = context->args()[i].toString(scope.engine); - if (scope.hasException()) - return Encode::undefined(); + for (int i = 0; i < callData->argc; ++i) { + s = callData->args[i].toString(scope.engine); + CHECK_EXCEPTION(); + Q_ASSERT(s->isString()); value += s->toQString(); } - return context->d()->engine->newString(value)->asReturnedValue(); + scope.result = scope.engine->newString(value); } -ReturnedValue StringPrototype::method_endsWith(CallContext *context) +void StringPrototype::method_endsWith(const BuiltinFunction *, Scope &scope, CallData *callData) { - QString value = getThisString(context); - if (context->d()->engine->hasException) - return Encode::undefined(); + QString value = getThisString(scope, callData); + CHECK_EXCEPTION(); QString searchString; - if (context->argc()) { - if (context->args()[0].as<RegExpObject>()) - return context->engine()->throwTypeError(); - searchString = context->args()[0].toQString(); + if (callData->argc) { + if (callData->args[0].as<RegExpObject>()) + THROW_TYPE_ERROR(); + searchString = callData->args[0].toQString(); } int pos = value.length(); - if (context->argc() > 1) - pos = (int) context->args()[1].toInteger(); + if (callData->argc > 1) + pos = (int) callData->args[1].toInteger(); if (pos == value.length()) - return Encode(value.endsWith(searchString)); + RETURN_RESULT(Encode(value.endsWith(searchString))); QStringRef stringToSearch = value.leftRef(pos); - return Encode(stringToSearch.endsWith(searchString)); + scope.result = Encode(stringToSearch.endsWith(searchString)); } -ReturnedValue StringPrototype::method_indexOf(CallContext *context) +void StringPrototype::method_indexOf(const BuiltinFunction *, Scope &scope, CallData *callData) { - QString value = getThisString(context); - if (context->d()->engine->hasException) - return Encode::undefined(); + QString value = getThisString(scope, callData); + CHECK_EXCEPTION(); QString searchString; - if (context->argc()) - searchString = context->args()[0].toQString(); + if (callData->argc) + searchString = callData->args[0].toQString(); int pos = 0; - if (context->argc() > 1) - pos = (int) context->args()[1].toInteger(); + if (callData->argc > 1) + pos = (int) callData->args[1].toInteger(); int index = -1; if (! value.isEmpty()) index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length())); - return Encode(index); + scope.result = Encode(index); } -ReturnedValue StringPrototype::method_includes(CallContext *context) +void StringPrototype::method_includes(const BuiltinFunction *, Scope &scope, CallData *callData) { - QString value = getThisString(context); - if (context->d()->engine->hasException) - return Encode::undefined(); + QString value = getThisString(scope, callData); + CHECK_EXCEPTION(); QString searchString; - if (context->argc()) { - if (context->args()[0].as<RegExpObject>()) - return context->engine()->throwTypeError(); - searchString = context->args()[0].toQString(); + if (callData->argc) { + if (callData->args[0].as<RegExpObject>()) + THROW_TYPE_ERROR(); + searchString = callData->args[0].toQString(); } int pos = 0; - if (context->argc() > 1) { - Scope scope(context); - ScopedValue posArg(scope, context->argument(1)); + if (callData->argc > 1) { + ScopedValue posArg(scope, callData->argument(1)); pos = (int) posArg->toInteger(); if (!posArg->isInteger() && posArg->isNumber() && qIsInf(posArg->toNumber())) pos = value.length(); } if (pos == 0) - return Encode(value.contains(searchString)); + RETURN_RESULT(Encode(value.contains(searchString))); QStringRef stringToSearch = value.midRef(pos); - return Encode(stringToSearch.contains(searchString)); + scope.result = Encode(stringToSearch.contains(searchString)); } -ReturnedValue StringPrototype::method_lastIndexOf(CallContext *context) +void StringPrototype::method_lastIndexOf(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(context); - - const QString value = getThisString(context); - if (scope.engine->hasException) - return Encode::undefined(); + const QString value = getThisString(scope, callData); + CHECK_EXCEPTION(); QString searchString; - if (context->argc()) - searchString = context->args()[0].toQString(); + if (callData->argc) + searchString = callData->args[0].toQString(); - ScopedValue posArg(scope, context->argument(1)); + ScopedValue posArg(scope, callData->argument(1)); double position = RuntimeHelpers::toNumber(posArg); if (std::isnan(position)) position = +qInf(); @@ -391,43 +378,40 @@ ReturnedValue StringPrototype::method_lastIndexOf(CallContext *context) if (!searchString.isEmpty() && pos == value.length()) --pos; if (searchString.isNull() && pos == 0) - return Encode(-1); + RETURN_RESULT(Encode(-1)); int index = value.lastIndexOf(searchString, pos); - return Encode(index); + scope.result = Encode(index); } -ReturnedValue StringPrototype::method_localeCompare(CallContext *context) +void StringPrototype::method_localeCompare(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(context); - const QString value = getThisString(context); - if (scope.engine->hasException) - return Encode::undefined(); + const QString value = getThisString(scope, callData); + CHECK_EXCEPTION(); - ScopedValue v(scope, context->argument(0)); + ScopedValue v(scope, callData->argument(0)); const QString that = v->toQString(); - return Encode(QString::localeAwareCompare(value, that)); + scope.result = Encode(QString::localeAwareCompare(value, that)); } -ReturnedValue StringPrototype::method_match(CallContext *context) +void StringPrototype::method_match(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (context->thisObject().isUndefined() || context->thisObject().isNull()) - return context->engine()->throwTypeError(); + if (callData->thisObject.isUndefined() || callData->thisObject.isNull()) + THROW_TYPE_ERROR(); - Scope scope(context); - ScopedString s(scope, context->thisObject().toString(scope.engine)); + ScopedString s(scope, callData->thisObject.toString(scope.engine)); - ScopedValue regexp(scope, context->argument(0)); + ScopedValue regexp(scope, callData->argument(0)); Scoped<RegExpObject> rx(scope, regexp); if (!rx) { ScopedCallData callData(scope, 1); callData->args[0] = regexp; - context->d()->engine->regExpCtor()->construct(scope, callData); + scope.engine->regExpCtor()->construct(scope, callData); rx = scope.result.asReturnedValue(); } if (!rx) // ### CHECK - return context->engine()->throwTypeError(); + THROW_TYPE_ERROR(); bool global = rx->global(); @@ -435,24 +419,24 @@ ReturnedValue StringPrototype::method_match(CallContext *context) ScopedString execString(scope, scope.engine->newString(QStringLiteral("exec"))); ScopedFunctionObject exec(scope, scope.engine->regExpPrototype()->get(execString)); - ScopedCallData callData(scope, 1); - callData->thisObject = rx; - callData->args[0] = s; + ScopedCallData cData(scope, 1); + cData->thisObject = rx; + cData->args[0] = s; if (!global) { - exec->call(scope, callData); - return scope.result.asReturnedValue(); + exec->call(scope, cData); + return; } - ScopedString lastIndex(scope, context->d()->engine->newString(QStringLiteral("lastIndex"))); + ScopedString lastIndex(scope, scope.engine->newString(QStringLiteral("lastIndex"))); rx->put(lastIndex, ScopedValue(scope, Primitive::fromInt32(0))); - ScopedArrayObject a(scope, context->d()->engine->newArrayObject()); + ScopedArrayObject a(scope, scope.engine->newArrayObject()); double previousLastIndex = 0; uint n = 0; ScopedValue matchStr(scope); ScopedValue index(scope); while (1) { - exec->call(scope, callData); + exec->call(scope, cData); if (scope.result.isNull()) break; assert(scope.result.isObject()); @@ -469,10 +453,9 @@ ReturnedValue StringPrototype::method_match(CallContext *context) ++n; } if (!n) - return Encode::null(); - - return a.asReturnedValue(); - + scope.result = Encode::null(); + else + scope.result = a; } static void appendReplacementString(QString *result, const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount) @@ -521,14 +504,13 @@ static void appendReplacementString(QString *result, const QString &input, const } } -ReturnedValue StringPrototype::method_replace(CallContext *ctx) +void StringPrototype::method_replace(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); QString string; - if (StringObject *thisString = ctx->thisObject().as<StringObject>()) + if (StringObject *thisString = callData->thisObject.as<StringObject>()) string = thisString->d()->string->toQString(); else - string = ctx->thisObject().toQString(); + string = callData->thisObject.toQString(); int numCaptures = 0; int numStringMatches = 0; @@ -537,7 +519,7 @@ ReturnedValue StringPrototype::method_replace(CallContext *ctx) uint _matchOffsets[64]; uint *matchOffsets = _matchOffsets; - ScopedValue searchValue(scope, ctx->argument(0)); + ScopedValue searchValue(scope, callData->argument(0)); Scoped<RegExpObject> regExp(scope, searchValue); if (regExp) { uint offset = 0; @@ -580,7 +562,7 @@ ReturnedValue StringPrototype::method_replace(CallContext *ctx) } QString result; - ScopedValue replaceValue(scope, ctx->argument(1)); + ScopedValue replaceValue(scope, callData->argument(1)); ScopedFunctionObject searchCallback(scope, replaceValue); if (!!searchCallback) { result.reserve(string.length() + 10*numStringMatches); @@ -595,14 +577,14 @@ ReturnedValue StringPrototype::method_replace(CallContext *ctx) uint end = matchOffsets[idx + 1]; entry = Primitive::undefinedValue(); if (start != JSC::Yarr::offsetNoMatch && end != JSC::Yarr::offsetNoMatch) - entry = ctx->d()->engine->newString(string.mid(start, end - start)); + entry = scope.engine->newString(string.mid(start, end - start)); callData->args[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] = ctx->d()->engine->newString(string); + callData->args[numCaptures + 1] = scope.engine->newString(string); searchCallback->call(scope, callData); result += string.midRef(lastEnd, matchStart - lastEnd); @@ -632,23 +614,22 @@ ReturnedValue StringPrototype::method_replace(CallContext *ctx) if (matchOffsets != _matchOffsets) free(matchOffsets); - return ctx->d()->engine->newString(result)->asReturnedValue(); + scope.result = scope.engine->newString(result); } -ReturnedValue StringPrototype::method_search(CallContext *ctx) +void StringPrototype::method_search(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - QString string = getThisString(ctx); - scope.result = ctx->argument(0); - if (scope.engine->hasException) - return Encode::undefined(); + QString string = getThisString(scope, callData); + scope.result = callData->argument(0); + CHECK_EXCEPTION(); + Scoped<RegExpObject> regExp(scope, scope.result.as<RegExpObject>()); if (!regExp) { ScopedCallData callData(scope, 1); callData->args[0] = scope.result; - ctx->d()->engine->regExpCtor()->construct(scope, callData); - if (scope.engine->hasException) - return Encode::undefined(); + scope.engine->regExpCtor()->construct(scope, callData); + CHECK_EXCEPTION(); + regExp = scope.result.as<RegExpObject>(); Q_ASSERT(regExp); } @@ -656,21 +637,21 @@ ReturnedValue StringPrototype::method_search(CallContext *ctx) uint* matchOffsets = (uint*)alloca(regExp->value()->captureCount() * 2 * sizeof(uint)); uint result = re->match(string, /*offset*/0, matchOffsets); if (result == JSC::Yarr::offsetNoMatch) - return Encode(-1); - return Encode(result); + scope.result = Encode(-1); + else + scope.result = Encode(result); } -ReturnedValue StringPrototype::method_slice(CallContext *ctx) +void StringPrototype::method_slice(const BuiltinFunction *, Scope &scope, CallData *callData) { - const QString text = getThisString(ctx); - if (ctx->d()->engine->hasException) - return Encode::undefined(); + const QString text = getThisString(scope, callData); + CHECK_EXCEPTION(); const double length = text.length(); - double start = ctx->argc() ? ctx->args()[0].toInteger() : 0; - double end = (ctx->argc() < 2 || ctx->args()[1].isUndefined()) - ? length : ctx->args()[1].toInteger(); + double start = callData->argc ? callData->args[0].toInteger() : 0; + double end = (callData->argc < 2 || callData->args[1].isUndefined()) + ? length : callData->args[1].toInteger(); if (start < 0) start = qMax(length + start, 0.); @@ -686,40 +667,38 @@ ReturnedValue StringPrototype::method_slice(CallContext *ctx) const int intEnd = int(end); int count = qMax(0, intEnd - intStart); - return ctx->d()->engine->newString(text.mid(intStart, count))->asReturnedValue(); + scope.result = scope.engine->newString(text.mid(intStart, count)); } -ReturnedValue StringPrototype::method_split(CallContext *ctx) +void StringPrototype::method_split(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - QString text = getThisString(ctx); - if (scope.engine->hasException) - return Encode::undefined(); + QString text = getThisString(scope, callData); + CHECK_EXCEPTION(); - ScopedValue separatorValue(scope, ctx->argument(0)); - ScopedValue limitValue(scope, ctx->argument(1)); + ScopedValue separatorValue(scope, callData->argument(0)); + ScopedValue limitValue(scope, callData->argument(1)); - ScopedArrayObject array(scope, ctx->d()->engine->newArrayObject()); + ScopedArrayObject array(scope, scope.engine->newArrayObject()); if (separatorValue->isUndefined()) { if (limitValue->isUndefined()) { - ScopedString s(scope, ctx->d()->engine->newString(text)); + ScopedString s(scope, scope.engine->newString(text)); array->push_back(s); - return array.asReturnedValue(); + RETURN_RESULT(array); } - return ctx->d()->engine->newString(text.left(limitValue->toInteger()))->asReturnedValue(); + RETURN_RESULT(scope.engine->newString(text.left(limitValue->toInteger()))); } uint limit = limitValue->isUndefined() ? UINT_MAX : limitValue->toUInt32(); if (limit == 0) - return array.asReturnedValue(); + RETURN_RESULT(array); Scoped<RegExpObject> re(scope, separatorValue); if (re) { if (re->value()->pattern->isEmpty()) { re = (RegExpObject *)0; - separatorValue = ctx->d()->engine->newString(); + separatorValue = scope.engine->newString(); } } @@ -733,7 +712,7 @@ ReturnedValue StringPrototype::method_split(CallContext *ctx) if (result == JSC::Yarr::offsetNoMatch) break; - array->push_back((s = ctx->d()->engine->newString(text.mid(offset, matchOffsets[0] - offset)))); + array->push_back((s = scope.engine->newString(text.mid(offset, matchOffsets[0] - offset)))); offset = qMax(offset + 1, matchOffsets[1]); if (array->getLength() >= limit) @@ -742,72 +721,70 @@ ReturnedValue StringPrototype::method_split(CallContext *ctx) for (int i = 1; i < re->value()->captureCount(); ++i) { uint start = matchOffsets[i * 2]; uint end = matchOffsets[i * 2 + 1]; - array->push_back((s = ctx->d()->engine->newString(text.mid(start, end - start)))); + array->push_back((s = scope.engine->newString(text.mid(start, end - start)))); if (array->getLength() >= limit) break; } } if (array->getLength() < limit) - array->push_back((s = ctx->d()->engine->newString(text.mid(offset)))); + array->push_back((s = scope.engine->newString(text.mid(offset)))); } else { QString separator = separatorValue->toQString(); if (separator.isEmpty()) { for (uint i = 0; i < qMin(limit, uint(text.length())); ++i) - array->push_back((s = ctx->d()->engine->newString(text.mid(i, 1)))); - return array.asReturnedValue(); + array->push_back((s = scope.engine->newString(text.mid(i, 1)))); + RETURN_RESULT(array); } int start = 0; int end; while ((end = text.indexOf(separator, start)) != -1) { - array->push_back((s = ctx->d()->engine->newString(text.mid(start, end - start)))); + array->push_back((s = scope.engine->newString(text.mid(start, end - start)))); start = end + separator.size(); if (array->getLength() >= limit) break; } if (array->getLength() < limit && start != -1) - array->push_back((s = ctx->d()->engine->newString(text.mid(start)))); + array->push_back((s = scope.engine->newString(text.mid(start)))); } - return array.asReturnedValue(); + RETURN_RESULT(array); } -ReturnedValue StringPrototype::method_startsWith(CallContext *context) +void StringPrototype::method_startsWith(const BuiltinFunction *, Scope &scope, CallData *callData) { - QString value = getThisString(context); - if (context->d()->engine->hasException) - return Encode::undefined(); + QString value = getThisString(scope, callData); + CHECK_EXCEPTION(); QString searchString; - if (context->argc()) { - if (context->args()[0].as<RegExpObject>()) - return context->engine()->throwTypeError(); - searchString = context->args()[0].toQString(); + if (callData->argc) { + if (callData->args[0].as<RegExpObject>()) + THROW_TYPE_ERROR(); + searchString = callData->args[0].toQString(); } int pos = 0; - if (context->argc() > 1) - pos = (int) context->args()[1].toInteger(); + if (callData->argc > 1) + pos = (int) callData->args[1].toInteger(); if (pos == 0) - return Encode(value.startsWith(searchString)); + RETURN_RESULT(Encode(value.startsWith(searchString))); QStringRef stringToSearch = value.midRef(pos); - return Encode(stringToSearch.startsWith(searchString)); + RETURN_RESULT(Encode(stringToSearch.startsWith(searchString))); } -ReturnedValue StringPrototype::method_substr(CallContext *context) +void StringPrototype::method_substr(const BuiltinFunction *, Scope &scope, CallData *callData) { - const QString value = getThisString(context); - if (context->d()->engine->hasException) - return Encode::undefined(); + const QString value = getThisString(scope, callData); + CHECK_EXCEPTION(); double start = 0; - if (context->argc() > 0) - start = context->args()[0].toInteger(); + if (callData->argc > 0) + start = callData->args[0].toInteger(); double length = +qInf(); - if (context->argc() > 1) - length = context->args()[1].toInteger(); + if (callData->argc > 1) + length = callData->args[1].toInteger(); double count = value.length(); if (start < 0) @@ -817,24 +794,23 @@ ReturnedValue StringPrototype::method_substr(CallContext *context) qint32 x = Primitive::toInt32(start); qint32 y = Primitive::toInt32(length); - return context->d()->engine->newString(value.mid(x, y))->asReturnedValue(); + scope.result = scope.engine->newString(value.mid(x, y)); } -ReturnedValue StringPrototype::method_substring(CallContext *context) +void StringPrototype::method_substring(const BuiltinFunction *, Scope &scope, CallData *callData) { - QString value = getThisString(context); - if (context->d()->engine->hasException) - return Encode::undefined(); + QString value = getThisString(scope, callData); + CHECK_EXCEPTION(); + int length = value.length(); double start = 0; double end = length; - if (context->argc() > 0) - start = context->args()[0].toInteger(); + if (callData->argc > 0) + start = callData->args[0].toInteger(); - Scope scope(context); - ScopedValue endValue(scope, context->argument(1)); + ScopedValue endValue(scope, callData->argument(1)); if (!endValue->isUndefined()) end = endValue->toInteger(); @@ -858,51 +834,50 @@ ReturnedValue StringPrototype::method_substring(CallContext *context) qint32 x = (int)start; qint32 y = (int)(end - start); - return context->d()->engine->newString(value.mid(x, y))->asReturnedValue(); + scope.result = scope.engine->newString(value.mid(x, y)); } -ReturnedValue StringPrototype::method_toLowerCase(CallContext *ctx) +void StringPrototype::method_toLowerCase(const BuiltinFunction *, Scope &scope, CallData *callData) { - QString value = getThisString(ctx); - if (ctx->d()->engine->hasException) - return Encode::undefined(); - return ctx->d()->engine->newString(value.toLower())->asReturnedValue(); + QString value = getThisString(scope, callData); + CHECK_EXCEPTION(); + + scope.result = scope.engine->newString(value.toLower()); } -ReturnedValue StringPrototype::method_toLocaleLowerCase(CallContext *ctx) +void StringPrototype::method_toLocaleLowerCase(const BuiltinFunction *b, Scope &scope, CallData *callData) { - return method_toLowerCase(ctx); + method_toLowerCase(b, scope, callData); } -ReturnedValue StringPrototype::method_toUpperCase(CallContext *ctx) +void StringPrototype::method_toUpperCase(const BuiltinFunction *, Scope &scope, CallData *callData) { - QString value = getThisString(ctx); - if (ctx->d()->engine->hasException) - return Encode::undefined(); - return ctx->d()->engine->newString(value.toUpper())->asReturnedValue(); + QString value = getThisString(scope, callData); + CHECK_EXCEPTION(); + + scope.result = scope.engine->newString(value.toUpper()); } -ReturnedValue StringPrototype::method_toLocaleUpperCase(CallContext *ctx) +void StringPrototype::method_toLocaleUpperCase(const BuiltinFunction *b, Scope &scope, CallData *callData) { - return method_toUpperCase(ctx); + return method_toUpperCase(b, scope, callData); } -ReturnedValue StringPrototype::method_fromCharCode(CallContext *context) +void StringPrototype::method_fromCharCode(const BuiltinFunction *, Scope &scope, CallData *callData) { - QString str(context->argc(), Qt::Uninitialized); + QString str(callData->argc, Qt::Uninitialized); QChar *ch = str.data(); - for (int i = 0; i < context->argc(); ++i) { - *ch = QChar(context->args()[i].toUInt16()); + for (int i = 0; i < callData->argc; ++i) { + *ch = QChar(callData->args[i].toUInt16()); ++ch; } - return context->d()->engine->newString(str)->asReturnedValue(); + scope.result = scope.engine->newString(str); } -ReturnedValue StringPrototype::method_trim(CallContext *ctx) +void StringPrototype::method_trim(const BuiltinFunction *, Scope &scope, CallData *callData) { - QString s = getThisString(ctx); - if (ctx->d()->engine->hasException) - return Encode::undefined(); + QString s = getThisString(scope, callData); + CHECK_EXCEPTION(); const QChar *chars = s.constData(); int start, end; @@ -915,5 +890,5 @@ ReturnedValue StringPrototype::method_trim(CallContext *ctx) break; } - return ctx->d()->engine->newString(QString(chars + start, end - start + 1))->asReturnedValue(); + scope.result = scope.engine->newString(QString(chars + start, end - start + 1)); } diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h index b9f9d44fe8..0ee7a6ece9 100644 --- a/src/qml/jsruntime/qv4stringobject_p.h +++ b/src/qml/jsruntime/qv4stringobject_p.h @@ -111,29 +111,29 @@ struct StringPrototype: StringObject { void init(ExecutionEngine *engine, Object *ctor); - static ReturnedValue method_toString(CallContext *context); - static ReturnedValue method_charAt(CallContext *context); - static ReturnedValue method_charCodeAt(CallContext *context); - static ReturnedValue method_concat(CallContext *context); - static ReturnedValue method_endsWith(CallContext *ctx); - static ReturnedValue method_indexOf(CallContext *context); - static ReturnedValue method_includes(CallContext *context); - static ReturnedValue method_lastIndexOf(CallContext *context); - static ReturnedValue method_localeCompare(CallContext *context); - static ReturnedValue method_match(CallContext *context); - static ReturnedValue method_replace(CallContext *ctx); - static ReturnedValue method_search(CallContext *ctx); - static ReturnedValue method_slice(CallContext *ctx); - static ReturnedValue method_split(CallContext *ctx); - static ReturnedValue method_startsWith(CallContext *ctx); - static ReturnedValue method_substr(CallContext *context); - static ReturnedValue method_substring(CallContext *context); - static ReturnedValue method_toLowerCase(CallContext *ctx); - static ReturnedValue method_toLocaleLowerCase(CallContext *ctx); - static ReturnedValue method_toUpperCase(CallContext *ctx); - static ReturnedValue method_toLocaleUpperCase(CallContext *ctx); - static ReturnedValue method_fromCharCode(CallContext *context); - static ReturnedValue method_trim(CallContext *ctx); + 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_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); }; } diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index 009c573bf8..cecd1e6958 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -438,79 +438,74 @@ void TypedArrayPrototype::init(ExecutionEngine *engine, TypedArrayCtor *ctor) defineDefaultProperty(QStringLiteral("subarray"), method_subarray, 0); } -ReturnedValue TypedArrayPrototype::method_get_buffer(CallContext *ctx) +void TypedArrayPrototype::method_get_buffer(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<TypedArray> v(scope, ctx->thisObject()); + Scoped<TypedArray> v(scope, callData->thisObject); if (!v) - return scope.engine->throwTypeError(); + THROW_TYPE_ERROR(); - return Encode(v->d()->buffer->asReturnedValue()); + scope.result = v->d()->buffer; } -ReturnedValue TypedArrayPrototype::method_get_byteLength(CallContext *ctx) +void TypedArrayPrototype::method_get_byteLength(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<TypedArray> v(scope, ctx->thisObject()); + Scoped<TypedArray> v(scope, callData->thisObject); if (!v) - return scope.engine->throwTypeError(); + THROW_TYPE_ERROR(); - return Encode(v->d()->byteLength); + scope.result = Encode(v->d()->byteLength); } -ReturnedValue TypedArrayPrototype::method_get_byteOffset(CallContext *ctx) +void TypedArrayPrototype::method_get_byteOffset(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<TypedArray> v(scope, ctx->thisObject()); + Scoped<TypedArray> v(scope, callData->thisObject); if (!v) - return scope.engine->throwTypeError(); + THROW_TYPE_ERROR(); - return Encode(v->d()->byteOffset); + scope.result = Encode(v->d()->byteOffset); } -ReturnedValue TypedArrayPrototype::method_get_length(CallContext *ctx) +void TypedArrayPrototype::method_get_length(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<TypedArray> v(scope, ctx->thisObject()); + Scoped<TypedArray> v(scope, callData->thisObject); if (!v) - return scope.engine->throwTypeError(); + THROW_TYPE_ERROR(); - return Encode(v->d()->byteLength/v->d()->type->bytesPerElement); + scope.result = Encode(v->d()->byteLength/v->d()->type->bytesPerElement); } -ReturnedValue TypedArrayPrototype::method_set(CallContext *ctx) +void TypedArrayPrototype::method_set(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<TypedArray> a(scope, ctx->thisObject()); + Scoped<TypedArray> a(scope, callData->thisObject); if (!a) - return scope.engine->throwTypeError(); + THROW_TYPE_ERROR(); Scoped<ArrayBuffer> buffer(scope, a->d()->buffer); if (!buffer) scope.engine->throwTypeError(); - double doffset = ctx->argc() >= 2 ? ctx->args()[1].toInteger() : 0; + double doffset = callData->argc >= 2 ? callData->args[1].toInteger() : 0; if (scope.engine->hasException) - return Encode::undefined(); + RETURN_UNDEFINED(); if (doffset < 0 || doffset >= UINT_MAX) - return scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range")); + RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range"))); uint offset = (uint)doffset; uint elementSize = a->d()->type->bytesPerElement; - Scoped<TypedArray> srcTypedArray(scope, ctx->args()[0]); + Scoped<TypedArray> srcTypedArray(scope, callData->args[0]); if (!srcTypedArray) { // src is a regular object - ScopedObject o(scope, ctx->args()[0].toObject(scope.engine)); + ScopedObject o(scope, callData->args[0].toObject(scope.engine)); if (scope.engine->hasException || !o) - return scope.engine->throwTypeError(); + THROW_TYPE_ERROR(); double len = ScopedValue(scope, o->get(scope.engine->id_length()))->toNumber(); uint l = (uint)len; if (scope.engine->hasException || l != len) - return scope.engine->throwTypeError(); + THROW_TYPE_ERROR(); if (offset + l > a->length()) - return scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range")); + RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range"))); uint idx = 0; char *b = buffer->d()->data->data() + a->d()->byteOffset + offset*elementSize; @@ -519,28 +514,28 @@ ReturnedValue TypedArrayPrototype::method_set(CallContext *ctx) val = o->getIndexed(idx); a->d()->type->write(scope.engine, b, 0, val); if (scope.engine->hasException) - return Encode::undefined(); + RETURN_UNDEFINED(); ++idx; b += elementSize; } - return Encode::undefined(); + RETURN_UNDEFINED(); } // src is a typed array Scoped<ArrayBuffer> srcBuffer(scope, srcTypedArray->d()->buffer); if (!srcBuffer) - return scope.engine->throwTypeError(); + THROW_TYPE_ERROR(); uint l = srcTypedArray->length(); if (offset + l > a->length()) - return scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range")); + RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range"))); char *dest = buffer->d()->data->data() + a->d()->byteOffset + offset*elementSize; const char *src = srcBuffer->d()->data->data() + srcTypedArray->d()->byteOffset; if (srcTypedArray->d()->type == a->d()->type) { // same type of typed arrays, use memmove (as srcbuffer and buffer could be the same) memmove(dest, src, srcTypedArray->d()->byteLength); - return Encode::undefined(); + RETURN_UNDEFINED(); } char *srcCopy = 0; @@ -564,28 +559,27 @@ ReturnedValue TypedArrayPrototype::method_set(CallContext *ctx) if (srcCopy) delete [] srcCopy; - return Encode::undefined(); + RETURN_UNDEFINED(); } -ReturnedValue TypedArrayPrototype::method_subarray(CallContext *ctx) +void TypedArrayPrototype::method_subarray(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<TypedArray> a(scope, ctx->thisObject()); + Scoped<TypedArray> a(scope, callData->thisObject); if (!a) - return scope.engine->throwTypeError(); + THROW_TYPE_ERROR(); Scoped<ArrayBuffer> buffer(scope, a->d()->buffer); if (!buffer) - return scope.engine->throwTypeError(); + THROW_TYPE_ERROR(); int len = a->length(); - double b = ctx->argc() > 0 ? ctx->args()[0].toInteger() : 0; + double b = callData->argc > 0 ? callData->args[0].toInteger() : 0; if (b < 0) b = len + b; uint begin = (uint)qBound(0., b, (double)len); - double e = ctx->argc() < 2 || ctx->args()[1].isUndefined() ? len : ctx->args()[1].toInteger(); + double e = callData->argc < 2 || callData->args[1].isUndefined() ? len : callData->args[1].toInteger(); if (e < 0) e = len + e; uint end = (uint)qBound(0., e, (double)len); @@ -593,18 +587,17 @@ ReturnedValue TypedArrayPrototype::method_subarray(CallContext *ctx) end = begin; if (scope.engine->hasException) - return Encode::undefined(); + RETURN_UNDEFINED(); int newLen = end - begin; ScopedFunctionObject constructor(scope, a->get(scope.engine->id_constructor())); if (!constructor) - return scope.engine->throwTypeError(); - - ScopedCallData callData(scope, 3); - callData->args[0] = buffer; - callData->args[1] = Encode(a->d()->byteOffset + begin*a->d()->type->bytesPerElement); - callData->args[2] = Encode(newLen); - constructor->construct(scope, callData); - return scope.result.asReturnedValue(); + THROW_TYPE_ERROR(); + + 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); } diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h index 0112d2e4a1..eefed2db4b 100644 --- a/src/qml/jsruntime/qv4typedarray_p.h +++ b/src/qml/jsruntime/qv4typedarray_p.h @@ -151,13 +151,13 @@ struct TypedArrayPrototype : Object void init(ExecutionEngine *engine, TypedArrayCtor *ctor); - static ReturnedValue method_get_buffer(CallContext *ctx); - static ReturnedValue method_get_byteLength(CallContext *ctx); - static ReturnedValue method_get_byteOffset(CallContext *ctx); - static ReturnedValue method_get_length(CallContext *ctx); + 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_set(CallContext *ctx); - static ReturnedValue method_subarray(CallContext *ctx); + static void method_set(const BuiltinFunction *, Scope &scope, CallData *callData); + static void method_subarray(const BuiltinFunction *, Scope &scope, CallData *callData); }; inline void diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 816b8fb11b..4ff0565f9b 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -254,34 +254,47 @@ public: Q_ASSERT(isDouble()); return Double_Type; } -#ifndef QV4_USE_64_BIT_VALUE_ENCODING + // Shared between 32-bit and 64-bit encoding + enum { + Tag_Shift = 32 + }; + + // Used only by 64-bit encoding + static const quint64 NaNEncodeMask = 0xfffc000000000000ll; + enum { + IsDouble_Shift = 64-14, + IsManagedOrUndefined_Shift = 64-15, + IsIntegerConvertible_Shift = 64-16, + IsDoubleTag_Shift = IsDouble_Shift - Tag_Shift, + Managed_Type_Internal_64 = 0 + }; + static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49 + + // Used only by 32-bit encoding enum Masks { SilentNaNBit = 0x00040000, - NaN_Mask = 0x7ff80000, NotDouble_Mask = 0x7ffa0000, - Immediate_Mask = NotDouble_Mask | 0x00020000u | SilentNaNBit, - Tag_Shift = 32 }; + static const quint64 Immediate_Mask_32 = NotDouble_Mask | 0x00020000u | SilentNaNBit; enum { - Managed_Type_Internal = NotDouble_Mask + Managed_Type_Internal_32 = NotDouble_Mask }; -#else - static const quint64 NaNEncodeMask = 0xfffc000000000000ll; - static const quint64 Immediate_Mask = 0x00020000u; // bit 49 - enum Masks { - NaN_Mask = 0x7ff80000, +#ifdef QV4_USE_64_BIT_VALUE_ENCODING + enum { + Managed_Type_Internal = Managed_Type_Internal_64 }; + static const quint64 Immediate_Mask = Immediate_Mask_64; +#else enum { - IsDouble_Shift = 64-14, - IsManagedOrUndefined_Shift = 64-15, - IsIntegerConvertible_Shift = 64-16, - Tag_Shift = 32, - IsDoubleTag_Shift = IsDouble_Shift - Tag_Shift, - Managed_Type_Internal = 0 + Managed_Type_Internal = Managed_Type_Internal_32 }; + static const quint64 Immediate_Mask = Immediate_Mask_32; #endif + enum { + NaN_Mask = 0x7ff80000, + }; enum ValueTypeInternal { Empty_Type_Internal = Immediate_Mask | 0, ConvertibleToInt = Immediate_Mask | 0x10000u, // bit 48 @@ -544,7 +557,7 @@ inline bool Value::asArrayIndex(uint &idx) const } double d = doubleValue(); idx = (uint)d; - return (idx == d); + return (idx == d && idx != UINT_MAX); } #endif diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp index 455a7ccb65..5cab4c5386 100644 --- a/src/qml/jsruntime/qv4variantobject.cpp +++ b/src/qml/jsruntime/qv4variantobject.cpp @@ -113,67 +113,68 @@ void VariantPrototype::init() defineDefaultProperty(engine()->id_toString(), method_toString, 0); } -QV4::ReturnedValue VariantPrototype::method_preserve(CallContext *ctx) +void VariantPrototype::method_preserve(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<VariantObject> o(scope, ctx->thisObject().as<QV4::VariantObject>()); + Scoped<VariantObject> o(scope, callData->thisObject.as<QV4::VariantObject>()); if (o && o->d()->isScarce()) o->d()->addVmePropertyReference(); - return Encode::undefined(); + RETURN_UNDEFINED(); } -QV4::ReturnedValue VariantPrototype::method_destroy(CallContext *ctx) +void VariantPrototype::method_destroy(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<VariantObject> o(scope, ctx->thisObject().as<QV4::VariantObject>()); + Scoped<VariantObject> o(scope, callData->thisObject.as<QV4::VariantObject>()); if (o) { if (o->d()->isScarce()) o->d()->addVmePropertyReference(); o->d()->data() = QVariant(); } - return Encode::undefined(); + RETURN_UNDEFINED(); } -QV4::ReturnedValue VariantPrototype::method_toString(CallContext *ctx) +void VariantPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<VariantObject> o(scope, ctx->thisObject().as<QV4::VariantObject>()); + Scoped<VariantObject> o(scope, callData->thisObject.as<QV4::VariantObject>()); if (!o) - return Encode::undefined(); + RETURN_UNDEFINED(); QString result = o->d()->data().toString(); if (result.isEmpty() && !o->d()->data().canConvert(QVariant::String)) { result = QLatin1String("QVariant(") + QLatin1String(o->d()->data().typeName()) + QLatin1Char(')'); } - return Encode(ctx->d()->engine->newString(result)); + scope.result = scope.engine->newString(result); } -QV4::ReturnedValue VariantPrototype::method_valueOf(CallContext *ctx) +void VariantPrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - Scoped<VariantObject> o(scope, ctx->thisObject().as<QV4::VariantObject>()); + Scoped<VariantObject> o(scope, callData->thisObject.as<QV4::VariantObject>()); if (o) { QVariant v = o->d()->data(); switch (v.type()) { case QVariant::Invalid: - return Encode::undefined(); + scope.result = Encode::undefined(); + return; case QVariant::String: - return Encode(ctx->d()->engine->newString(v.toString())); + scope.result = scope.engine->newString(v.toString()); + return; case QVariant::Int: - return Encode(v.toInt()); + scope.result = Encode(v.toInt()); + return; case QVariant::Double: case QVariant::UInt: - return Encode(v.toDouble()); + scope.result = Encode(v.toDouble()); + return; case QVariant::Bool: - return Encode(v.toBool()); + scope.result = Encode(v.toBool()); + return; default: if (QMetaType::typeFlags(v.userType()) & QMetaType::IsEnumeration) - return Encode(v.toInt()); + RETURN_RESULT(Encode(v.toInt())); break; } } - return ctx->thisObject().asReturnedValue(); + scope.result = callData->thisObject; } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4variantobject_p.h b/src/qml/jsruntime/qv4variantobject_p.h index 9a04069c12..ef51b6632d 100644 --- a/src/qml/jsruntime/qv4variantobject_p.h +++ b/src/qml/jsruntime/qv4variantobject_p.h @@ -107,10 +107,10 @@ struct VariantPrototype : VariantObject public: void init(); - static ReturnedValue method_preserve(CallContext *ctx); - static ReturnedValue method_destroy(CallContext *ctx); - static ReturnedValue method_toString(CallContext *ctx); - static ReturnedValue method_valueOf(CallContext *ctx); + 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); }; } diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 622359a7d9..b9183313cd 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -347,6 +347,10 @@ Param traceParam(const Param ¶m) goto catchException; \ VALUE(param) = tmp; \ } +// qv4scopedvalue_p.h also defines a CHECK_EXCEPTION macro +#ifdef CHECK_EXCEPTION +#undef CHECK_EXCEPTION +#endif #define CHECK_EXCEPTION \ if (engine->hasException) \ goto catchException diff --git a/src/qml/memory/memory.pri b/src/qml/memory/memory.pri index 04b7566ccc..38fadbf23f 100644 --- a/src/qml/memory/memory.pri +++ b/src/qml/memory/memory.pri @@ -6,8 +6,8 @@ SOURCES += \ $$PWD/qv4mm.cpp \ HEADERS += \ - $$PWD/qv4mm_p.h - + $$PWD/qv4mm_p.h \ + $$PWD/qv4mmdefs_p.h } HEADERS += \ diff --git a/src/qml/memory/qv4heap_p.h b/src/qml/memory/qv4heap_p.h index 5a3797f397..8285ef4de7 100644 --- a/src/qml/memory/qv4heap_p.h +++ b/src/qml/memory/qv4heap_p.h @@ -52,6 +52,7 @@ #include <QtCore/QString> #include <private/qv4global_p.h> +#include <private/qv4mmdefs_p.h> #include <QSharedPointer> // To check if Heap::Base::init is called (meaning, all subclasses did their init and called their @@ -90,43 +91,31 @@ namespace Heap { struct Q_QML_EXPORT Base { void *operator new(size_t) = delete; - quintptr mm_data; // vtable and markbit + const VTable *vt; inline ReturnedValue asReturnedValue() const; inline void mark(QV4::ExecutionEngine *engine); - enum { - MarkBit = 0x1, - NotInUse = 0x2, - PointerMask = ~0x3 - }; - - void setVtable(const VTable *v) { - Q_ASSERT(!(mm_data & MarkBit)); - mm_data = reinterpret_cast<quintptr>(v); - } - VTable *vtable() const { - return reinterpret_cast<VTable *>(mm_data & PointerMask); - } + void setVtable(const VTable *v) { vt = v; } + const VTable *vtable() const { return vt; } inline bool isMarked() const { - return mm_data & MarkBit; + const HeapItem *h = reinterpret_cast<const HeapItem *>(this); + Chunk *c = h->chunk(); + Q_ASSERT(!Chunk::testBit(c->extendsBitmap, h - c->realBase())); + return Chunk::testBit(c->blackBitmap, h - c->realBase()); } inline void setMarkBit() { - mm_data |= MarkBit; - } - inline void clearMarkBit() { - mm_data &= ~MarkBit; + const HeapItem *h = reinterpret_cast<const HeapItem *>(this); + Chunk *c = h->chunk(); + Q_ASSERT(!Chunk::testBit(c->extendsBitmap, h - c->realBase())); + return Chunk::setBit(c->blackBitmap, h - c->realBase()); } inline bool inUse() const { - return !(mm_data & NotInUse); - } - - Base *nextFree() { - return reinterpret_cast<Base *>(mm_data & PointerMask); - } - void setNextFree(Base *m) { - mm_data = (reinterpret_cast<quintptr>(m) | NotInUse); + const HeapItem *h = reinterpret_cast<const HeapItem *>(this); + Chunk *c = h->chunk(); + Q_ASSERT(!Chunk::testBit(c->extendsBitmap, h - c->realBase())); + return Chunk::testBit(c->objectBitmap, h - c->realBase()); } void *operator new(size_t, Managed *m) { return m; } diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 51eb82059a..6330ef6038 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -42,8 +42,12 @@ #include "qv4objectproto_p.h" #include "qv4mm_p.h" #include "qv4qobjectwrapper_p.h" +#include <QtCore/qalgorithms.h> +#include <QtCore/private/qnumeric_p.h> #include <qqmlengine.h> +#include "PageReservation.h" #include "PageAllocation.h" +#include "PageAllocationAligned.h" #include "StdLibExtras.h" #include <QElapsedTimer> @@ -56,6 +60,14 @@ #include "qv4alloca_p.h" #include "qv4profiling_p.h" +#define MM_DEBUG 0 + +#if MM_DEBUG +#define DEBUG qDebug() << "MM:" +#else +#define DEBUG if (1) ; else qDebug() << "MM:" +#endif + #ifdef V4_USE_VALGRIND #include <valgrind/valgrind.h> #include <valgrind/memcheck.h> @@ -79,310 +91,616 @@ using namespace WTF; QT_BEGIN_NAMESPACE -static uint maxShiftValue() -{ - static uint result = 0; - if (!result) { - result = 6; - if (Q_UNLIKELY(qEnvironmentVariableIsSet(QV4_MM_MAXBLOCK_SHIFT))) { - bool ok; - const uint overrideValue = qgetenv(QV4_MM_MAXBLOCK_SHIFT).toUInt(&ok); - if (ok && overrideValue <= 11 && overrideValue > 0) - result = overrideValue; +namespace QV4 { + +enum { + MinSlotsGCLimit = QV4::Chunk::AvailableSlots*16, + GCOverallocation = 200 /* Max overallocation by the GC in % */ +}; + +struct MemorySegment { + enum { + NumChunks = 8*sizeof(quint64), + SegmentSize = NumChunks*Chunk::ChunkSize, + }; + + MemorySegment(size_t size) + { + size += Chunk::ChunkSize; // make sure we can get enough 64k aligment memory + if (size < SegmentSize) + size = SegmentSize; + + pageReservation = PageReservation::reserve(size, OSAllocator::JSGCHeapPages); + base = reinterpret_cast<Chunk *>((reinterpret_cast<quintptr>(pageReservation.base()) + Chunk::ChunkSize - 1) & ~(Chunk::ChunkSize - 1)); + nChunks = NumChunks; + if (base != pageReservation.base()) + --nChunks; + } + MemorySegment(MemorySegment &&other) { + qSwap(pageReservation, other.pageReservation); + qSwap(base, other.base); + qSwap(nChunks, other.nChunks); + qSwap(allocatedMap, other.allocatedMap); + } + + ~MemorySegment() { + if (base) + pageReservation.deallocate(); + } + + void setBit(size_t index) { + Q_ASSERT(index < nChunks); + quint64 bit = static_cast<quint64>(1) << index; +// qDebug() << " setBit" << hex << index << (index & (Bits - 1)) << bit; + allocatedMap |= bit; + } + void clearBit(size_t index) { + Q_ASSERT(index < nChunks); + quint64 bit = static_cast<quint64>(1) << index; +// qDebug() << " setBit" << hex << index << (index & (Bits - 1)) << bit; + allocatedMap &= ~bit; + } + bool testBit(size_t index) const { + Q_ASSERT(index < nChunks); + quint64 bit = static_cast<quint64>(1) << index; + return (allocatedMap & bit); + } + + Chunk *allocate(size_t size); + void free(Chunk *chunk, size_t size) { + DEBUG << "freeing chunk" << chunk; + size_t index = static_cast<size_t>(chunk - base); + size_t end = index + (size - 1)/Chunk::ChunkSize + 1; + while (index < end) { + Q_ASSERT(testBit(index)); + clearBit(index); + ++index; } + + size_t pageSize = WTF::pageSize(); + size = (size + pageSize - 1) & ~(pageSize - 1); + pageReservation.decommit(chunk, size); + } + + bool contains(Chunk *c) const { + return c >= base && c < base + nChunks; } - return result; -} -static std::size_t maxChunkSizeValue() + PageReservation pageReservation; + Chunk *base = 0; + quint64 allocatedMap = 0; + uint nChunks = 0; +}; + +Chunk *MemorySegment::allocate(size_t size) { - static std::size_t result = 0; - if (!result) { - result = 32 * 1024; - if (Q_UNLIKELY(qEnvironmentVariableIsSet(QV4_MM_MAX_CHUNK_SIZE))) { - bool ok; - const std::size_t overrideValue = qgetenv(QV4_MM_MAX_CHUNK_SIZE).toUInt(&ok); - if (ok) - result = overrideValue; + size_t requiredChunks = (size + sizeof(Chunk) - 1)/sizeof(Chunk); + uint sequence = 0; + Chunk *candidate = 0; + for (uint i = 0; i < nChunks; ++i) { + if (!testBit(i)) { + if (!candidate) + candidate = base + i; + ++sequence; + } else { + candidate = 0; + sequence = 0; + } + if (sequence == requiredChunks) { + pageReservation.commit(candidate, size); + for (uint i = 0; i < requiredChunks; ++i) + setBit(candidate - base + i); + DEBUG << "allocated chunk " << candidate << hex << size; + return candidate; } } - return result; + return 0; } -using namespace QV4; - -struct MemoryManager::Data -{ - const size_t pageSize; - - struct ChunkHeader { - Heap::Base freeItems; - ChunkHeader *nextNonFull; - char *itemStart; - char *itemEnd; - unsigned itemSize; - }; +struct ChunkAllocator { + ChunkAllocator() {} - ExecutionEngine *engine; + size_t requiredChunkSize(size_t size) { + size += Chunk::HeaderSize; // space required for the Chunk header + size_t pageSize = WTF::pageSize(); + size = (size + pageSize - 1) & ~(pageSize - 1); // align to page sizes + if (size < Chunk::ChunkSize) + size = Chunk::ChunkSize; + return size; + } - std::size_t maxChunkSize; - std::vector<PageAllocation> heapChunks; - std::size_t unmanagedHeapSize; // the amount of bytes of heap that is not managed by the memory manager, but which is held onto by managed items. - std::size_t unmanagedHeapSizeGCLimit; + Chunk *allocate(size_t size = 0); + void free(Chunk *chunk, size_t size = 0); - struct LargeItem { - LargeItem *next; - size_t size; - void *data; + std::vector<MemorySegment> memorySegments; +}; - Heap::Base *heapObject() { - return reinterpret_cast<Heap::Base *>(&data); +Chunk *ChunkAllocator::allocate(size_t size) +{ + size = requiredChunkSize(size); + for (auto &m : memorySegments) { + if (~m.allocatedMap) { + Chunk *c = m.allocate(size); + if (c) + return c; } - }; - - LargeItem *largeItems; - std::size_t totalLargeItemsAllocated; + } - enum { MaxItemSize = 512 }; - ChunkHeader *nonFullChunks[MaxItemSize/16]; - uint nChunks[MaxItemSize/16]; - uint availableItems[MaxItemSize/16]; - uint allocCount[MaxItemSize/16]; - int totalItems; - int totalAlloc; - uint maxShift; + // allocate a new segment + memorySegments.push_back(MemorySegment(size)); + Chunk *c = memorySegments.back().allocate(size); + Q_ASSERT(c); + return c; +} - bool gcBlocked; - bool aggressiveGC; - bool gcStats; - bool unused; // suppress padding warning +void ChunkAllocator::free(Chunk *chunk, size_t size) +{ + size = requiredChunkSize(size); + for (auto &m : memorySegments) { + if (m.contains(chunk)) { + m.free(chunk, size); + return; + } + } + Q_ASSERT(false); +} - // statistics: -#ifdef DETAILED_MM_STATS - QVector<unsigned> allocSizeCounters; -#endif // DETAILED_MM_STATS - Data() - : pageSize(WTF::pageSize()) - , engine(0) - , maxChunkSize(maxChunkSizeValue()) - , unmanagedHeapSize(0) - , unmanagedHeapSizeGCLimit(MIN_UNMANAGED_HEAPSIZE_GC_LIMIT) - , largeItems(0) - , totalLargeItemsAllocated(0) - , totalItems(0) - , totalAlloc(0) - , maxShift(maxShiftValue()) - , gcBlocked(false) - , aggressiveGC(!qEnvironmentVariableIsEmpty("QV4_MM_AGGRESSIVE_GC")) - , gcStats(!qEnvironmentVariableIsEmpty(QV4_MM_STATS)) - { - memset(nonFullChunks, 0, sizeof(nonFullChunks)); - memset(nChunks, 0, sizeof(nChunks)); - memset(availableItems, 0, sizeof(availableItems)); - memset(allocCount, 0, sizeof(allocCount)); +void Chunk::sweep() +{ + // DEBUG << "sweeping chunk" << this << (*freeList); + HeapItem *o = realBase(); + for (uint i = 0; i < Chunk::EntriesInBitmap; ++i) { + Q_ASSERT((grayBitmap[i] | blackBitmap[i]) == blackBitmap[i]); // check that we don't have gray only objects + quintptr toFree = objectBitmap[i] ^ blackBitmap[i]; + Q_ASSERT((toFree & objectBitmap[i]) == toFree); // check all black objects are marked as being used + quintptr e = extendsBitmap[i]; + // DEBUG << hex << " index=" << i << toFree; + while (toFree) { + uint index = qCountTrailingZeroBits(toFree); + quintptr bit = (static_cast<quintptr>(1) << index); + + toFree ^= bit; // mask out freed slot + // DEBUG << " index" << hex << index << toFree; + + // remove all extends slots that have been freed + // this is a bit of bit trickery. + quintptr mask = (bit << 1) - 1; // create a mask of 1's to the right of and up to the current bit + quintptr objmask = e | mask; // or'ing mask with e gives all ones until the end of the current object + quintptr result = objmask + 1; + Q_ASSERT(qCountTrailingZeroBits(result) - index != 0); // ensure we freed something + result |= mask; // ensure we don't clear stuff to the right of the current object + e &= result; + + HeapItem *itemToFree = o + index; + Heap::Base *b = *itemToFree; + if (b->vtable()->destroy) { + b->vtable()->destroy(b); + b->_checkIsDestroyed(); + } + } + objectBitmap[i] = blackBitmap[i]; + blackBitmap[i] = 0; + extendsBitmap[i] = e; + o += Chunk::Bits; } + // DEBUG << "swept chunk" << this << "freed" << slotsFreed << "slots."; +} - ~Data() - { - for (std::vector<PageAllocation>::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) { - Q_V4_PROFILE_DEALLOC(engine, i->size(), Profiling::HeapPage); - i->deallocate(); +void Chunk::freeAll() +{ + // DEBUG << "sweeping chunk" << this << (*freeList); + HeapItem *o = realBase(); + for (uint i = 0; i < Chunk::EntriesInBitmap; ++i) { + quintptr toFree = objectBitmap[i]; + quintptr e = extendsBitmap[i]; + // DEBUG << hex << " index=" << i << toFree; + while (toFree) { + uint index = qCountTrailingZeroBits(toFree); + quintptr bit = (static_cast<quintptr>(1) << index); + + toFree ^= bit; // mask out freed slot + // DEBUG << " index" << hex << index << toFree; + + // remove all extends slots that have been freed + // this is a bit of bit trickery. + quintptr mask = (bit << 1) - 1; // create a mask of 1's to the right of and up to the current bit + quintptr objmask = e | mask; // or'ing mask with e gives all ones until the end of the current object + quintptr result = objmask + 1; + Q_ASSERT(qCountTrailingZeroBits(result) - index != 0); // ensure we freed something + result |= mask; // ensure we don't clear stuff to the right of the current object + e &= result; + + HeapItem *itemToFree = o + index; + Heap::Base *b = *itemToFree; + if (b->vtable()->destroy) { + b->vtable()->destroy(b); + b->_checkIsDestroyed(); + } } + objectBitmap[i] = 0; + blackBitmap[i] = 0; + extendsBitmap[i] = e; + o += Chunk::Bits; } -}; - -namespace { + // DEBUG << "swept chunk" << this << "freed" << slotsFreed << "slots."; +} -bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, ExecutionEngine *engine) +void Chunk::sortIntoBins(HeapItem **bins, uint nBins) { - bool isEmpty = true; - Heap::Base *tail = &header->freeItems; -// qDebug("chunkStart @ %p, size=%x, pos=%x", header->itemStart, header->itemSize, header->itemSize>>4); -#ifdef V4_USE_VALGRIND - VALGRIND_DISABLE_ERROR_REPORTING; + HeapItem *base = realBase(); +#if QT_POINTER_SIZE == 8 + const int start = 0; +#else + const int start = 1; #endif - for (char *item = header->itemStart; item <= header->itemEnd; item += header->itemSize) { - Heap::Base *m = reinterpret_cast<Heap::Base *>(item); -// qDebug("chunk @ %p, in use: %s, mark bit: %s", -// item, (m->inUse() ? "yes" : "no"), (m->isMarked() ? "true" : "false")); - - Q_ASSERT(qintptr(item) % 16 == 0); - - if (m->isMarked()) { - Q_ASSERT(m->inUse()); - m->clearMarkBit(); - isEmpty = false; - ++(*itemsInUse); - } else { - if (m->inUse()) { -// qDebug() << "-- collecting it." << m << tail << m->nextFree(); -#ifdef V4_USE_VALGRIND - VALGRIND_ENABLE_ERROR_REPORTING; + for (int i = start; i < EntriesInBitmap; ++i) { + quintptr usedSlots = (objectBitmap[i]|extendsBitmap[i]); +#if QT_POINTER_SIZE == 8 + if (!i) + usedSlots |= (static_cast<quintptr>(1) << (HeaderSize/SlotSize)) - 1; #endif - if (m->vtable()->destroy) { - m->vtable()->destroy(m); - m->_checkIsDestroyed(); - } + uint index = qCountTrailingZeroBits(usedSlots + 1); + if (index == Bits) + continue; + uint freeStart = i*Bits + index; + usedSlots &= ~((static_cast<quintptr>(1) << index) - 1); + while (i < EntriesInBitmap && !usedSlots) { + ++i; + usedSlots = (objectBitmap[i]|extendsBitmap[i]); + } + if (i == EntriesInBitmap) + usedSlots = 1; + HeapItem *freeItem = base + freeStart; + + uint freeEnd = i*Bits + qCountTrailingZeroBits(usedSlots); + uint nSlots = freeEnd - freeStart; + Q_ASSERT(freeEnd > freeStart && freeEnd <= NumSlots); + freeItem->freeData.availableSlots = nSlots; + uint bin = qMin(nBins - 1, nSlots); + freeItem->freeData.next = bins[bin]; + bins[bin] = freeItem; + // DEBUG << "binnig item" << freeItem << nSlots << bin << freeItem->freeData.availableSlots; + } +} - memset(m, 0, sizeof(Heap::Base)); -#ifdef V4_USE_VALGRIND - VALGRIND_DISABLE_ERROR_REPORTING; - VALGRIND_MEMPOOL_FREE(engine->memoryManager, m); + +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; +#if MM_DEBUG + ++allocations[bin]; #endif -#ifdef V4_USE_HEAPTRACK - heaptrack_report_free(m); + + HeapItem **last; + + HeapItem *m; + + if (slotsRequired < NumBins - 1) { + m = freeBins[slotsRequired]; + if (m) { + freeBins[slotsRequired] = m->freeData.next; + goto done; + } + } +#if 0 + for (uint b = bin + 1; b < NumBins - 1; ++b) { + if ((m = freeBins[b])) { + Q_ASSERT(binForSlots(m->freeData.availableSlots) == b); + freeBins[b] = m->freeData.next; + // DEBUG << "looking for empty bin" << bin << "size" << size << "found" << b; + uint remainingSlots = m->freeData.availableSlots - slotsRequired; + // DEBUG << "found free slots of size" << m->freeData.availableSlots << m << "remaining" << remainingSlots; + if (remainingSlots < 2) { + // avoid too much fragmentation and rather mark the memory as used + size += remainingSlots*Chunk::SlotSize; + goto done; + } + HeapItem *remainder = m + slotsRequired; + remainder->freeData.availableSlots = remainingSlots; + uint binForRemainder = binForSlots(remainingSlots); + remainder->freeData.next = freeBins[binForRemainder]; + freeBins[binForRemainder] = remainder; + goto done; + } + } #endif - Q_V4_PROFILE_DEALLOC(engine, header->itemSize, Profiling::SmallItem); - ++(*itemsInUse); + if (nFree >= slotsRequired) { + // use bump allocation + Q_ASSERT(nextFree); + m = nextFree; + nextFree += slotsRequired; + nFree -= slotsRequired; + goto done; + } + + // DEBUG << "No matching bin found for item" << size << bin; + // search last bin for a large enough item + last = &freeBins[NumBins - 1]; + while ((m = *last)) { + if (m->freeData.availableSlots >= slotsRequired) { + *last = m->freeData.next; // take it out of the list + + size_t remainingSlots = m->freeData.availableSlots - slotsRequired; + // DEBUG << "found large free slots of size" << m->freeData.availableSlots << m << "remaining" << remainingSlots; + if (remainingSlots < 2) { + // avoid too much fragmentation and rather mark the memory as used + size += remainingSlots*Chunk::SlotSize; + goto done; } - // Relink all free blocks to rewrite references to any released chunk. - tail->setNextFree(m); - tail = m; + HeapItem *remainder = m + slotsRequired; + if (remainingSlots >= 2*NumBins) { + if (nFree) { + size_t bin = binForSlots(nFree); + nextFree->freeData.next = freeBins[bin]; + nextFree->freeData.availableSlots = nFree; + freeBins[bin] = nextFree; + } + nextFree = remainder; + nFree = remainingSlots; + } else { + remainder->freeData.availableSlots = remainingSlots; + size_t binForRemainder = binForSlots(remainingSlots); + remainder->freeData.next = freeBins[binForRemainder]; + freeBins[binForRemainder] = remainder; + } + goto done; } + last = &m->freeData.next; } - tail->setNextFree(0); -#ifdef V4_USE_VALGRIND - VALGRIND_ENABLE_ERROR_REPORTING; + + if (!m) { + if (!forceAllocation) + return 0; + Chunk *newChunk = chunkAllocator->allocate(); + chunks.push_back(newChunk); + nextFree = newChunk->first(); + nFree = Chunk::AvailableSlots; + m = nextFree; + nextFree += slotsRequired; + nFree -= slotsRequired; + } + +done: + m->setAllocatedSlots(slotsRequired); + // DEBUG << " " << hex << m->chunk() << m->chunk()->objectBitmap[0] << m->chunk()->extendsBitmap[0] << (m - m->chunk()->realBase()); + return m; +} + +void BlockAllocator::sweep() +{ + nextFree = 0; + nFree = 0; + memset(freeBins, 0, sizeof(freeBins)); + +// qDebug() << "BlockAlloc: sweep"; + usedSlotsAfterLastSweep = 0; + for (auto c : chunks) { + c->sweep(); + c->sortIntoBins(freeBins, NumBins); +// qDebug() << "used slots in chunk" << c << ":" << c->nUsedSlots(); + usedSlotsAfterLastSweep += c->nUsedSlots(); + } +} + +void BlockAllocator::freeAll() +{ + for (auto c : chunks) { + c->freeAll(); + chunkAllocator->free(c); + } +} + +#if MM_DEBUG +void BlockAllocator::stats() { + DEBUG << "MM stats:"; + QString s; + for (int i = 0; i < 10; ++i) { + uint c = 0; + HeapItem *item = freeBins[i]; + while (item) { + ++c; + item = item->freeData.next; + } + s += QString::number(c) + QLatin1String(", "); + } + HeapItem *item = freeBins[NumBins - 1]; + uint c = 0; + while (item) { + ++c; + item = item->freeData.next; + } + s += QLatin1String("..., ") + QString::number(c); + DEBUG << "bins:" << s; + QString a; + for (int i = 0; i < 10; ++i) + a += QString::number(allocations[i]) + QLatin1String(", "); + a += QLatin1String("..., ") + QString::number(allocations[NumBins - 1]); + DEBUG << "allocs:" << a; + memset(allocations, 0, sizeof(allocations)); +} #endif - return isEmpty; + + +HeapItem *HugeItemAllocator::allocate(size_t size) { + Chunk *c = chunkAllocator->allocate(size); + chunks.push_back(HugeChunk{c, size}); + Chunk::setBit(c->objectBitmap, c->first() - c->realBase()); + return c->first(); +} + +static void freeHugeChunk(ChunkAllocator *chunkAllocator, const HugeItemAllocator::HugeChunk &c) +{ + HeapItem *itemToFree = c.chunk->first(); + Heap::Base *b = *itemToFree; + if (b->vtable()->destroy) { + b->vtable()->destroy(b); + b->_checkIsDestroyed(); + } + chunkAllocator->free(c.chunk, c.size); +} + +void HugeItemAllocator::sweep() { + auto isBlack = [this] (const HugeChunk &c) { + bool b = c.chunk->first()->isBlack(); + Chunk::clearBit(c.chunk->blackBitmap, c.chunk->first() - c.chunk->realBase()); + if (!b) + freeHugeChunk(chunkAllocator, c); + return !b; + }; + + auto newEnd = std::remove_if(chunks.begin(), chunks.end(), isBlack); + chunks.erase(newEnd, chunks.end()); +} + +void HugeItemAllocator::freeAll() +{ + for (auto &c : chunks) { + freeHugeChunk(chunkAllocator, c); + } } -} // namespace MemoryManager::MemoryManager(ExecutionEngine *engine) : engine(engine) - , m_d(new Data) + , chunkAllocator(new ChunkAllocator) + , stackAllocator(chunkAllocator) + , blockAllocator(chunkAllocator) + , hugeItemAllocator(chunkAllocator) , m_persistentValues(new PersistentValueStorage(engine)) , m_weakValues(new PersistentValueStorage(engine)) + , unmanagedHeapSizeGCLimit(MIN_UNMANAGED_HEAPSIZE_GC_LIMIT) + , aggressiveGC(!qEnvironmentVariableIsEmpty("QV4_MM_AGGRESSIVE_GC")) + , gcStats(!qEnvironmentVariableIsEmpty(QV4_MM_STATS)) { #ifdef V4_USE_VALGRIND VALGRIND_CREATE_MEMPOOL(this, 0, true); #endif - m_d->engine = engine; } -Heap::Base *MemoryManager::allocData(std::size_t size, std::size_t unmanagedSize) +Heap::Base *MemoryManager::allocString(std::size_t unmanagedSize) { - if (m_d->aggressiveGC) + if (aggressiveGC) runGC(); -#ifdef DETAILED_MM_STATS - willAllocate(size); -#endif // DETAILED_MM_STATS - Q_ASSERT(size >= 16); - Q_ASSERT(size % 16 == 0); - -// qDebug() << "unmanagedHeapSize:" << m_d->unmanagedHeapSize << "limit:" << m_d->unmanagedHeapSizeGCLimit << "unmanagedSize:" << unmanagedSize; - m_d->unmanagedHeapSize += unmanagedSize; + const size_t stringSize = align(sizeof(Heap::String)); + unmanagedHeapSize += unmanagedSize; bool didGCRun = false; - if (m_d->unmanagedHeapSize > m_d->unmanagedHeapSizeGCLimit) { + if (unmanagedHeapSize > unmanagedHeapSizeGCLimit) { runGC(); - if (3*m_d->unmanagedHeapSizeGCLimit <= 4*m_d->unmanagedHeapSize) + if (3*unmanagedHeapSizeGCLimit <= 4*unmanagedHeapSize) // more than 75% full, raise limit - m_d->unmanagedHeapSizeGCLimit = std::max(m_d->unmanagedHeapSizeGCLimit, m_d->unmanagedHeapSize) * 2; - else if (m_d->unmanagedHeapSize * 4 <= m_d->unmanagedHeapSizeGCLimit) + unmanagedHeapSizeGCLimit = std::max(unmanagedHeapSizeGCLimit, unmanagedHeapSize) * 2; + else if (unmanagedHeapSize * 4 <= unmanagedHeapSizeGCLimit) // less than 25% full, lower limit - m_d->unmanagedHeapSizeGCLimit = qMax(MIN_UNMANAGED_HEAPSIZE_GC_LIMIT, m_d->unmanagedHeapSizeGCLimit/2); + unmanagedHeapSizeGCLimit = qMax(MIN_UNMANAGED_HEAPSIZE_GC_LIMIT, unmanagedHeapSizeGCLimit/2); didGCRun = true; } - size_t pos = size >> 4; - - // doesn't fit into a small bucket - if (size >= MemoryManager::Data::MaxItemSize) { - if (!didGCRun && m_d->totalLargeItemsAllocated > 8 * 1024 * 1024) + HeapItem *m = blockAllocator.allocate(stringSize); + if (!m) { + if (!didGCRun && shouldRunGC()) runGC(); - - // we use malloc for this - const size_t totalSize = size + sizeof(MemoryManager::Data::LargeItem); - Q_V4_PROFILE_ALLOC(engine, totalSize, Profiling::LargeItem); - MemoryManager::Data::LargeItem *item = - static_cast<MemoryManager::Data::LargeItem *>(malloc(totalSize)); - memset(item, 0, totalSize); - item->next = m_d->largeItems; - item->size = size; - m_d->largeItems = item; - m_d->totalLargeItemsAllocated += size; - return item->heapObject(); + m = blockAllocator.allocate(stringSize, true); } - Heap::Base *m = 0; - Data::ChunkHeader *header = m_d->nonFullChunks[pos]; - if (header) { - m = header->freeItems.nextFree(); - goto found; - } + memset(m, 0, stringSize); + return *m; +} - // try to free up space, otherwise allocate - if (!didGCRun && m_d->allocCount[pos] > (m_d->availableItems[pos] >> 1) && m_d->totalAlloc > (m_d->totalItems >> 1) && !m_d->aggressiveGC) { +Heap::Base *MemoryManager::allocData(std::size_t size) +{ + if (aggressiveGC) runGC(); - header = m_d->nonFullChunks[pos]; - if (header) { - m = header->freeItems.nextFree(); - goto found; - } - } +#ifdef DETAILED_MM_STATS + willAllocate(size); +#endif // DETAILED_MM_STATS - // no free item available, allocate a new chunk - { - // allocate larger chunks at a time to avoid excessive GC, but cap at maximum chunk size (2MB by default) - uint shift = ++m_d->nChunks[pos]; - if (shift > m_d->maxShift) - shift = m_d->maxShift; - std::size_t allocSize = m_d->maxChunkSize*(size_t(1) << shift); - allocSize = roundUpToMultipleOf(m_d->pageSize, allocSize); - Q_V4_PROFILE_ALLOC(engine, allocSize, Profiling::HeapPage); - PageAllocation allocation = PageAllocation::allocate(allocSize, OSAllocator::JSGCHeapPages); - m_d->heapChunks.push_back(allocation); - - header = reinterpret_cast<Data::ChunkHeader *>(allocation.base()); - Q_ASSERT(size <= UINT_MAX); - header->itemSize = unsigned(size); - header->itemStart = reinterpret_cast<char *>(allocation.base()) + roundUpToMultipleOf(16, sizeof(Data::ChunkHeader)); - header->itemEnd = reinterpret_cast<char *>(allocation.base()) + allocation.size() - header->itemSize; - - header->nextNonFull = m_d->nonFullChunks[pos]; - m_d->nonFullChunks[pos] = header; - - Heap::Base *last = &header->freeItems; - for (char *item = header->itemStart; item <= header->itemEnd; item += header->itemSize) { - Heap::Base *o = reinterpret_cast<Heap::Base *>(item); - last->setNextFree(o); - last = o; + Q_ASSERT(size >= Chunk::SlotSize); + Q_ASSERT(size % Chunk::SlotSize == 0); - } - last->setNextFree(0); - m = header->freeItems.nextFree(); - Q_ASSERT(header->itemEnd >= header->itemStart); - const size_t increase = quintptr(header->itemEnd - header->itemStart) / header->itemSize; - m_d->availableItems[pos] += uint(increase); - m_d->totalItems += int(increase); -#ifdef V4_USE_VALGRIND - VALGRIND_MAKE_MEM_NOACCESS(allocation.base(), allocSize); - VALGRIND_MEMPOOL_ALLOC(this, header, sizeof(Data::ChunkHeader)); -#endif -#ifdef V4_USE_HEAPTRACK - heaptrack_report_alloc(header, sizeof(Data::ChunkHeader)); -#endif +// qDebug() << "unmanagedHeapSize:" << unmanagedHeapSize << "limit:" << unmanagedHeapSizeGCLimit << "unmanagedSize:" << unmanagedSize; + + if (size > Chunk::DataSize) + return *hugeItemAllocator.allocate(size); + + HeapItem *m = blockAllocator.allocate(size); + if (!m) { + if (shouldRunGC()) + runGC(); + m = blockAllocator.allocate(size, true); } - found: -#ifdef V4_USE_VALGRIND - VALGRIND_MEMPOOL_ALLOC(this, m, size); -#endif -#ifdef V4_USE_HEAPTRACK - heaptrack_report_alloc(m, size); -#endif - Q_V4_PROFILE_ALLOC(engine, size, Profiling::SmallItem); + memset(m, 0, size); + return *m; +} - ++m_d->allocCount[pos]; - ++m_d->totalAlloc; - header->freeItems.setNextFree(m->nextFree()); - if (!header->freeItems.nextFree()) - m_d->nonFullChunks[pos] = header->nextNonFull; - return m; +Heap::Object *MemoryManager::allocObjectWithMemberData(std::size_t size, uint nMembers) +{ + Heap::Object *o = static_cast<Heap::Object *>(allocData(size)); + + // ### Could optimize this and allocate both in one go through the block allocator + if (nMembers) { + std::size_t memberSize = align(sizeof(Heap::MemberData) + (nMembers - 1)*sizeof(Value)); +// qDebug() << "allocating member data for" << o << nMembers << memberSize; + Heap::Base *m; + if (memberSize > Chunk::DataSize) + m = *hugeItemAllocator.allocate(memberSize); + else + m = *blockAllocator.allocate(memberSize, true); + memset(m, 0, memberSize); + o->memberData = static_cast<Heap::MemberData *>(m); + o->memberData->setVtable(MemberData::staticVTable()); + o->memberData->size = static_cast<uint>((memberSize - sizeof(Heap::MemberData) + sizeof(Value))/sizeof(Value)); + o->memberData->init(); +// qDebug() << " got" << o->memberData << o->memberData->size; + } + return o; } static void drainMarkStack(QV4::ExecutionEngine *engine, Value *markBase) @@ -486,88 +804,34 @@ void MemoryManager::sweep(bool lastSweep) } } - bool *chunkIsEmpty = static_cast<bool *>(alloca(m_d->heapChunks.size() * sizeof(bool))); - uint itemsInUse[MemoryManager::Data::MaxItemSize/16]; - memset(itemsInUse, 0, sizeof(itemsInUse)); - memset(m_d->nonFullChunks, 0, sizeof(m_d->nonFullChunks)); - - for (size_t i = 0; i < m_d->heapChunks.size(); ++i) { - Data::ChunkHeader *header = reinterpret_cast<Data::ChunkHeader *>(m_d->heapChunks[i].base()); - chunkIsEmpty[i] = sweepChunk(header, &itemsInUse[header->itemSize >> 4], engine); - } - - std::vector<PageAllocation>::iterator chunkIter = m_d->heapChunks.begin(); - for (size_t i = 0; i < m_d->heapChunks.size(); ++i) { - Q_ASSERT(chunkIter != m_d->heapChunks.end()); - Data::ChunkHeader *header = reinterpret_cast<Data::ChunkHeader *>(chunkIter->base()); - const size_t pos = header->itemSize >> 4; - Q_ASSERT(header->itemEnd >= header->itemStart); - const size_t decrease = quintptr(header->itemEnd - header->itemStart) / header->itemSize; - - // Release that chunk if it could have been spared since the last GC run without any difference. - if (chunkIsEmpty[i] && m_d->availableItems[pos] - decrease >= itemsInUse[pos]) { - Q_V4_PROFILE_DEALLOC(engine, chunkIter->size(), Profiling::HeapPage); -#ifdef V4_USE_VALGRIND - VALGRIND_MEMPOOL_FREE(this, header); -#endif -#ifdef V4_USE_HEAPTRACK - heaptrack_report_free(header); -#endif - --m_d->nChunks[pos]; - m_d->availableItems[pos] -= uint(decrease); - m_d->totalItems -= int(decrease); - chunkIter->deallocate(); - chunkIter = m_d->heapChunks.erase(chunkIter); - continue; - } else if (header->freeItems.nextFree()) { - header->nextNonFull = m_d->nonFullChunks[pos]; - m_d->nonFullChunks[pos] = header; - } - ++chunkIter; - } - - Data::LargeItem *i = m_d->largeItems; - Data::LargeItem **last = &m_d->largeItems; - while (i) { - Heap::Base *m = i->heapObject(); - Q_ASSERT(m->inUse()); - if (m->isMarked()) { - m->clearMarkBit(); - last = &i->next; - i = i->next; - continue; - } - if (m->vtable()->destroy) - m->vtable()->destroy(m); - - *last = i->next; - Q_V4_PROFILE_DEALLOC(engine, i->size + sizeof(Data::LargeItem), Profiling::LargeItem); - free(i); - i = *last; - } + blockAllocator.sweep(); + hugeItemAllocator.sweep(); +} - // some execution contexts are allocated on the stack, make sure we clear their markBit as well - if (!lastSweep) { - QV4::ExecutionContext *ctx = engine->currentContext; - while (ctx) { - ctx->d()->clearMarkBit(); - ctx = engine->parentContext(ctx); - } - } +bool MemoryManager::shouldRunGC() const +{ + size_t total = blockAllocator.totalSlots(); + size_t usedSlots = blockAllocator.usedSlotsAfterLastSweep; + if (total > MinSlotsGCLimit && usedSlots * GCOverallocation < total * 100) + return true; + return false; } + void MemoryManager::runGC() { - if (m_d->gcBlocked) { + if (gcBlocked) { // qDebug() << "Not running GC."; return; } - QScopedValueRollback<bool> gcBlocker(m_d->gcBlocked, true); + QScopedValueRollback<bool> gcBlocker(gcBlocked, true); - if (!m_d->gcStats) { + if (!gcStats) { +// uint oldUsed = allocator.usedMem(); mark(); sweep(); +// DEBUG << "RUN GC: allocated:" << allocator.allocatedMem() << "used before" << oldUsed << "used now" << allocator.usedMem(); } else { const size_t totalMem = getAllocatedMem(); @@ -577,7 +841,6 @@ void MemoryManager::runGC() qint64 markTime = t.restart(); const size_t usedBefore = getUsedMem(); const size_t largeItemsBefore = getLargeItemsMem(); - size_t chunksBefore = m_d->heapChunks.size(); sweep(); const size_t usedAfter = getUsedMem(); const size_t largeItemsAfter = getLargeItemsMem(); @@ -586,56 +849,30 @@ void MemoryManager::runGC() qDebug() << "========== GC =========="; qDebug() << "Marked object in" << markTime << "ms."; qDebug() << "Sweeped object in" << sweepTime << "ms."; - qDebug() << "Allocated" << totalMem << "bytes in" << m_d->heapChunks.size() << "chunks."; + qDebug() << "Allocated" << totalMem << "bytes"; qDebug() << "Used memory before GC:" << usedBefore; qDebug() << "Used memory after GC:" << usedAfter; qDebug() << "Freed up bytes:" << (usedBefore - usedAfter); - qDebug() << "Released chunks:" << (chunksBefore - m_d->heapChunks.size()); qDebug() << "Large item memory before GC:" << largeItemsBefore; qDebug() << "Large item memory after GC:" << largeItemsAfter; qDebug() << "Large item memory freed up:" << (largeItemsBefore - largeItemsAfter); qDebug() << "======== End GC ========"; } - - memset(m_d->allocCount, 0, sizeof(m_d->allocCount)); - m_d->totalAlloc = 0; - m_d->totalLargeItemsAllocated = 0; } size_t MemoryManager::getUsedMem() const { - size_t usedMem = 0; - for (std::vector<PageAllocation>::const_iterator i = m_d->heapChunks.cbegin(), ei = m_d->heapChunks.cend(); i != ei; ++i) { - Data::ChunkHeader *header = reinterpret_cast<Data::ChunkHeader *>(i->base()); - for (char *item = header->itemStart; item <= header->itemEnd; item += header->itemSize) { - Heap::Base *m = reinterpret_cast<Heap::Base *>(item); - Q_ASSERT(qintptr(item) % 16 == 0); - if (m->inUse()) - usedMem += header->itemSize; - } - } - return usedMem; + return blockAllocator.usedMem(); } size_t MemoryManager::getAllocatedMem() const { - size_t total = 0; - for (size_t i = 0; i < m_d->heapChunks.size(); ++i) - total += m_d->heapChunks.at(i).size(); - return total; + return blockAllocator.allocatedMem() + hugeItemAllocator.usedMem(); } size_t MemoryManager::getLargeItemsMem() const { - size_t total = 0; - for (const Data::LargeItem *i = m_d->largeItems; i != 0; i = i->next) - total += i->size; - return total; -} - -void MemoryManager::changeUnmanagedHeapSizeUsage(qptrdiff delta) -{ - m_d->unmanagedHeapSize += delta; + return hugeItemAllocator.usedMem(); } MemoryManager::~MemoryManager() @@ -643,23 +880,26 @@ MemoryManager::~MemoryManager() delete m_persistentValues; sweep(/*lastSweep*/true); + blockAllocator.freeAll(); + hugeItemAllocator.freeAll(); + stackAllocator.freeAll(); delete m_weakValues; #ifdef V4_USE_VALGRIND VALGRIND_DESTROY_MEMPOOL(this); #endif + delete chunkAllocator; } - void MemoryManager::dumpStats() const { #ifdef DETAILED_MM_STATS std::cerr << "=================" << std::endl; std::cerr << "Allocation stats:" << std::endl; std::cerr << "Requests for each chunk size:" << std::endl; - for (int i = 0; i < m_d->allocSizeCounters.size(); ++i) { - if (unsigned count = m_d->allocSizeCounters[i]) { + for (int i = 0; i < allocSizeCounters.size(); ++i) { + if (unsigned count = allocSizeCounters[i]) { std::cerr << "\t" << (i << 4) << " bytes chunks: " << count << std::endl; } } @@ -670,7 +910,7 @@ void MemoryManager::dumpStats() const void MemoryManager::willAllocate(std::size_t size) { unsigned alignedSize = (size + 15) >> 4; - QVector<unsigned> &counters = m_d->allocSizeCounters; + QVector<unsigned> &counters = allocSizeCounters; if ((unsigned) counters.size() < alignedSize + 1) counters.resize(alignedSize + 1); counters[alignedSize]++; @@ -690,4 +930,7 @@ void MemoryManager::collectFromJSStack() const ++v; } } + +} // namespace QV4 + QT_END_NAMESPACE diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h index e6417cb2e0..00daf8a622 100644 --- a/src/qml/memory/qv4mm_p.h +++ b/src/qml/memory/qv4mm_p.h @@ -55,6 +55,7 @@ #include <private/qv4value_p.h> #include <private/qv4scopedvalue_p.h> #include <private/qv4object_p.h> +#include <private/qv4mmdefs_p.h> #include <QVector> //#define DETAILED_MM_STATS @@ -63,35 +64,166 @@ #define QV4_MM_MAX_CHUNK_SIZE "QV4_MM_MAX_CHUNK_SIZE" #define QV4_MM_STATS "QV4_MM_STATS" +#define MM_DEBUG 0 + QT_BEGIN_NAMESPACE namespace QV4 { -struct GCDeletable; +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() { + T *m = nextFree->as<T>(); + if (Q_UNLIKELY(nextFree == lastInChunk)) { + nextChunk(); + } else { + nextFree += requiredSlots; + } +#if MM_DEBUG + Chunk *c = m->chunk(); + Chunk::setBit(c->objectBitmap, m - c->realBase()); +#endif + return m; + } + void free() { +#if MM_DEBUG + Chunk::clearBit(item->chunk()->objectBitmap, item - item->chunk()->realBase()); +#endif + if (Q_UNLIKELY(nextFree == firstInChunk)) { + prevChunk(); + } else { + nextFree -= requiredSlots; + } + } + + 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) + { + memset(freeBins, 0, sizeof(freeBins)); +#if MM_DEBUG + memset(allocations, 0, sizeof(allocations)); +#endif + } + + enum { NumBins = 8 }; + + static inline size_t binForSlots(size_t nSlots) { + return nSlots >= NumBins ? NumBins - 1 : nSlots; + } + +#if MM_DEBUG + void stats(); +#endif + + HeapItem *allocate(size_t size, bool forceAllocation = false); + + size_t totalSlots() const { + return Chunk::AvailableSlots*chunks.size(); + } + + size_t allocatedMem() const { + return chunks.size()*Chunk::DataSize; + } + size_t usedMem() const { + uint used = 0; + for (auto c : chunks) + used += c->nUsedSlots()*Chunk::SlotSize; + return used; + } + + void sweep(); + void freeAll(); + + // bump allocations + HeapItem *nextFree = 0; + size_t nFree = 0; + size_t usedSlotsAfterLastSweep = 0; + HeapItem *freeBins[NumBins]; + ChunkAllocator *chunkAllocator; + std::vector<Chunk *> chunks; +#if MM_DEBUG + uint allocations[NumBins]; +#endif +}; + +struct HugeItemAllocator { + HugeItemAllocator(ChunkAllocator *chunkAllocator) + : chunkAllocator(chunkAllocator) + {} + + HeapItem *allocate(size_t size); + void sweep(); + void freeAll(); + + size_t usedMem() const { + size_t used = 0; + for (const auto &c : chunks) + used += c.size; + return used; + } + + ChunkAllocator *chunkAllocator; + struct HugeChunk { + Chunk *chunk; + size_t size; + }; + + std::vector<HugeChunk> chunks; +}; + class Q_QML_EXPORT MemoryManager { Q_DISABLE_COPY(MemoryManager); public: - struct Data; - -public: MemoryManager(ExecutionEngine *engine); ~MemoryManager(); // TODO: this is only for 64bit (and x86 with SSE/AVX), so exend it for other architectures to be slightly more efficient (meaning, align on 8-byte boundaries). // Note: all occurrences of "16" in alloc/dealloc are also due to the alignment. - static inline std::size_t align(std::size_t size) - { return (size + 15) & ~0xf; } + 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(QV4::ExecutionEngine *v4) + { + Heap::CallContext *ctxt = stackAllocator.allocate(); + memset(ctxt, 0, sizeof(Heap::CallContext)); + ctxt->setVtable(QV4::CallContext::staticVTable()); + ctxt->init(v4); + return ctxt; + + } + void freeSimpleCallContext() + { stackAllocator.free(); } template<typename ManagedType> - inline typename ManagedType::Data *allocManaged(std::size_t size, std::size_t unmanagedSize = 0) + inline typename ManagedType::Data *allocManaged(std::size_t size) { V4_ASSERT_IS_TRIVIAL(typename ManagedType::Data) size = align(size); - Heap::Base *o = allocData(size, unmanagedSize); - memset(o, 0, size); + Heap::Base *o = allocData(size); o->setVtable(ManagedType::staticVTable()); return static_cast<typename ManagedType::Data *>(o); } @@ -99,35 +231,31 @@ public: template <typename ObjectType> typename ObjectType::Data *allocateObject(InternalClass *ic) { - const int size = (sizeof(typename ObjectType::Data) + (sizeof(Value) - 1)) & ~(sizeof(Value) - 1); - typename ObjectType::Data *o = allocManaged<ObjectType>(size + ic->size*sizeof(Value)); + Heap::Object *o = allocObjectWithMemberData(align(sizeof(typename ObjectType::Data)), ic->size); + o->setVtable(ObjectType::staticVTable()); o->internalClass = ic; - o->inlineMemberSize = ic->size; - o->inlineMemberOffset = size/sizeof(Value); - return o; + return static_cast<typename ObjectType::Data *>(o); } template <typename ObjectType> typename ObjectType::Data *allocateObject() { InternalClass *ic = ObjectType::defaultInternalClass(engine); - const int size = (sizeof(typename ObjectType::Data) + (sizeof(Value) - 1)) & ~(sizeof(Value) - 1); - typename ObjectType::Data *o = allocManaged<ObjectType>(size + ic->size*sizeof(Value)); + Heap::Object *o = allocObjectWithMemberData(align(sizeof(typename ObjectType::Data)), ic->size); + o->setVtable(ObjectType::staticVTable()); Object *prototype = ObjectType::defaultPrototype(engine); o->internalClass = ic; o->prototype = prototype->d(); - o->inlineMemberSize = ic->size; - o->inlineMemberOffset = size/sizeof(Value); - return o; + return static_cast<typename ObjectType::Data *>(o); } template <typename ManagedType, typename Arg1> typename ManagedType::Data *allocWithStringData(std::size_t unmanagedSize, Arg1 arg1) { - Scope scope(engine); - Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data), unmanagedSize)); - t->d_unchecked()->init(this, arg1); - return t->d(); + typename ManagedType::Data *o = reinterpret_cast<typename ManagedType::Data *>(allocString(unmanagedSize)); + o->setVtable(ManagedType::staticVTable()); + o->init(this, arg1); + return o; } template <typename ObjectType> @@ -297,12 +425,15 @@ public: size_t getAllocatedMem() const; size_t getLargeItemsMem() const; - void changeUnmanagedHeapSizeUsage(qptrdiff delta); // called when a JS object grows itself. Specifically: Heap::String::append + // called when a JS object grows itself. Specifically: Heap::String::append + void changeUnmanagedHeapSizeUsage(qptrdiff delta) { unmanagedHeapSize += delta; } + protected: /// expects size to be aligned - // TODO: try to inline - Heap::Base *allocData(std::size_t size, std::size_t unmanagedSize); + Heap::Base *allocString(std::size_t unmanagedSize); + Heap::Base *allocData(std::size_t size); + Heap::Object *allocObjectWithMemberData(std::size_t size, uint nMembers); #ifdef DETAILED_MM_STATS void willAllocate(std::size_t size); @@ -312,13 +443,24 @@ private: void collectFromJSStack() const; void mark(); void sweep(bool lastSweep = false); + bool shouldRunGC() const; public: QV4::ExecutionEngine *engine; - QScopedPointer<Data> m_d; + ChunkAllocator *chunkAllocator; + StackAllocator<Heap::CallContext> stackAllocator; + BlockAllocator blockAllocator; + HugeItemAllocator hugeItemAllocator; PersistentValueStorage *m_persistentValues; PersistentValueStorage *m_weakValues; QVector<Value *> m_pendingFreedObjectWrapperValue; + + std::size_t unmanagedHeapSize = 0; // the amount of bytes of heap that is not managed by the memory manager, but which is held onto by managed items. + std::size_t unmanagedHeapSizeGCLimit; + + bool gcBlocked = false; + bool aggressiveGC = false; + bool gcStats = false; }; } diff --git a/src/qml/memory/qv4mmdefs_p.h b/src/qml/memory/qv4mmdefs_p.h new file mode 100644 index 0000000000..588ae21ee0 --- /dev/null +++ b/src/qml/memory/qv4mmdefs_p.h @@ -0,0 +1,262 @@ +/**************************************************************************** +** +** 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 QV4MMDEFS_P_H +#define QV4MMDEFS_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 <QtCore/qalgorithms.h> +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +/* + * Chunks are the basic structure containing GC managed objects. + * + * Chunks are 64k aligned in memory, so that retrieving the Chunk pointer from a Heap object + * is a simple masking operation. Each Chunk has 4 bitmaps for managing purposes, + * and 32byte wide slots for the objects following afterwards. + * + * The gray and black bitmaps are used for mark/sweep. + * The object bitmap has a bit set if this location represents the start of a Heap object. + * The extends bitmap denotes the extend of an object. It has a cleared bit at the start of the object + * and a set bit for all following slots used by the object. + * + * Free memory has both used and extends bits set to 0. + * + * This gives the following operations when allocating an object of size s: + * Find s/Alignment consecutive free slots in the chunk. Set the object bit for the first + * slot to 1. Set the extends bits for all following slots to 1. + * + * All used slots can be found by object|extents. + * + * When sweeping, simply copy the black bits over to the object bits. + * + */ +struct HeapItem; +struct Chunk { + enum { + ChunkSize = 64*1024, + ChunkShift = 16, + SlotSize = 32, + SlotSizeShift = 5, + NumSlots = ChunkSize/SlotSize, + BitmapSize = NumSlots/8, + HeaderSize = 4*BitmapSize, + DataSize = ChunkSize - HeaderSize, + AvailableSlots = DataSize/SlotSize, +#if QT_POINTER_SIZE == 8 + Bits = 64, + BitShift = 6, +#else + Bits = 32, + BitShift = 5, +#endif + EntriesInBitmap = BitmapSize/sizeof(quintptr) + }; + quintptr grayBitmap[BitmapSize/sizeof(quintptr)]; + quintptr blackBitmap[BitmapSize/sizeof(quintptr)]; + quintptr objectBitmap[BitmapSize/sizeof(quintptr)]; + quintptr extendsBitmap[BitmapSize/sizeof(quintptr)]; + char data[ChunkSize - HeaderSize]; + + HeapItem *realBase(); + HeapItem *first(); + + static void setBit(quintptr *bitmap, size_t index) { +// Q_ASSERT(index >= HeaderSize/SlotSize && index < ChunkSize/SlotSize); + bitmap += index >> BitShift; + quintptr bit = static_cast<quintptr>(1) << (index & (Bits - 1)); + *bitmap |= bit; + } + static void clearBit(quintptr *bitmap, size_t index) { +// Q_ASSERT(index >= HeaderSize/SlotSize && index < ChunkSize/SlotSize); + bitmap += index >> BitShift; + quintptr bit = static_cast<quintptr>(1) << (index & (Bits - 1)); + *bitmap &= ~bit; + } + static bool testBit(quintptr *bitmap, size_t index) { +// Q_ASSERT(index >= HeaderSize/SlotSize && index < ChunkSize/SlotSize); + bitmap += index >> BitShift; + quintptr bit = static_cast<quintptr>(1) << (index & (Bits - 1)); + return (*bitmap & bit); + } + static void setBits(quintptr *bitmap, size_t index, size_t nBits) { +// Q_ASSERT(index >= HeaderSize/SlotSize && index + nBits <= ChunkSize/SlotSize); + if (!nBits) + return; + bitmap += index >> BitShift; + index &= (Bits - 1); + while (1) { + size_t bitsToSet = qMin(nBits, Bits - index); + quintptr mask = static_cast<quintptr>(-1) >> (Bits - bitsToSet) << index; + *bitmap |= mask; + nBits -= bitsToSet; + if (!nBits) + return; + index = 0; + ++bitmap; + } + } + static bool hasNonZeroBit(quintptr *bitmap) { + for (uint i = 0; i < EntriesInBitmap; ++i) + if (bitmap[i]) + return true; + return false; + } + static uint lowestNonZeroBit(quintptr *bitmap) { + for (uint i = 0; i < EntriesInBitmap; ++i) { + if (bitmap[i]) { + quintptr b = bitmap[i]; + return i*Bits + qCountTrailingZeroBits(b); + } + } + return 0; + } + + uint nFreeSlots() const { + return AvailableSlots - nUsedSlots(); + } + uint nUsedSlots() const { + uint usedSlots = 0; + for (uint i = 0; i < EntriesInBitmap; ++i) { + quintptr used = objectBitmap[i] | extendsBitmap[i]; + usedSlots += qPopulationCount(used); + } + return usedSlots; + } + + void sweep(); + void freeAll(); + + void sortIntoBins(HeapItem **bins, uint nBins); +}; + +struct HeapItem { + union { + struct { + HeapItem *next; + size_t availableSlots; + } freeData; + quint64 payload[Chunk::SlotSize/sizeof(quint64)]; + }; + operator Heap::Base *() { return reinterpret_cast<Heap::Base *>(this); } + + template<typename T> + T *as() { return static_cast<T *>(reinterpret_cast<Heap::Base *>(this)); } + + Chunk *chunk() const { + return reinterpret_cast<Chunk *>(reinterpret_cast<quintptr>(this) >> Chunk::ChunkShift << Chunk::ChunkShift); + } + + bool isGray() const { + Chunk *c = chunk(); + uint index = this - c->realBase(); + return Chunk::testBit(c->grayBitmap, index); + } + bool isBlack() const { + Chunk *c = chunk(); + uint index = this - c->realBase(); + return Chunk::testBit(c->blackBitmap, index); + } + bool isInUse() const { + Chunk *c = chunk(); + uint index = this - c->realBase(); + return Chunk::testBit(c->objectBitmap, index); + } + + void setAllocatedSlots(size_t nSlots) { +// Q_ASSERT(size && !(size % sizeof(HeapItem))); + Chunk *c = chunk(); + size_t index = this - c->realBase(); +// Q_ASSERT(!Chunk::testBit(c->objectBitmap, index)); + Chunk::setBit(c->objectBitmap, index); + Chunk::setBits(c->extendsBitmap, index + 1, nSlots - 1); +// for (uint i = index + 1; i < nBits - 1; ++i) +// Q_ASSERT(Chunk::testBit(c->extendsBitmap, i)); +// Q_ASSERT(!Chunk::testBit(c->extendsBitmap, index)); + } + + // Doesn't report correctly for huge items + size_t size() const { + Chunk *c = chunk(); + uint index = this - c->realBase(); + Q_ASSERT(Chunk::testBit(c->objectBitmap, index)); + // ### optimize me + uint end = index + 1; + while (end < Chunk::NumSlots && Chunk::testBit(c->extendsBitmap, end)) + ++end; + return (end - index)*sizeof(HeapItem); + } +}; + +inline HeapItem *Chunk::realBase() +{ + return reinterpret_cast<HeapItem *>(this); +} + +inline HeapItem *Chunk::first() +{ + return reinterpret_cast<HeapItem *>(data); +} + +Q_STATIC_ASSERT(sizeof(Chunk) == Chunk::ChunkSize); +Q_STATIC_ASSERT((1 << Chunk::ChunkShift) == Chunk::ChunkSize); +Q_STATIC_ASSERT(1 << Chunk::SlotSizeShift == Chunk::SlotSize); +Q_STATIC_ASSERT(sizeof(HeapItem) == Chunk::SlotSize); +Q_STATIC_ASSERT(QT_POINTER_SIZE*8 == Chunk::Bits); +Q_STATIC_ASSERT((1 << Chunk::BitShift) == Chunk::Bits); + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index 4fbd828307..19ece44beb 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -199,7 +199,7 @@ void QQmlBoundSignalExpression::evaluate(void **a) // 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::getValue(reinterpret_cast<QJSValue *>(a[ii + 1]))) + if (QV4::Value *v4Value = QJSValuePrivate::valueForData(reinterpret_cast<QJSValue *>(a[ii + 1]), &callData->args[ii])) callData->args[ii] = *v4Value; else callData->args[ii] = QV4::Encode::undefined(); diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 50ed58e63d..a04f47e6a4 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -1063,11 +1063,11 @@ struct QmlIncubatorObject : public QV4::Object V4_OBJECT2(QmlIncubatorObject, Object) V4_NEEDS_DESTROY - static QV4::ReturnedValue method_get_statusChanged(QV4::CallContext *ctx); - static QV4::ReturnedValue method_set_statusChanged(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_status(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_object(QV4::CallContext *ctx); - static QV4::ReturnedValue method_forceCompletion(QV4::CallContext *ctx); + 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 void markObjects(QV4::Heap::Base *that, QV4::ExecutionEngine *e); @@ -1415,58 +1415,53 @@ QQmlComponentExtension::QQmlComponentExtension(QV4::ExecutionEngine *v4) incubationProto.set(v4, proto); } -QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_object(QV4::CallContext *ctx) +void QV4::QmlIncubatorObject::method_get_object(const BuiltinFunction *, Scope &scope, CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped<QmlIncubatorObject> o(scope, ctx->thisObject().as<QmlIncubatorObject>()); + QV4::Scoped<QmlIncubatorObject> o(scope, callData->thisObject.as<QmlIncubatorObject>()); if (!o) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - return QV4::QObjectWrapper::wrap(ctx->d()->engine, o->d()->incubator->object()); + scope.result = QV4::QObjectWrapper::wrap(scope.engine, o->d()->incubator->object()); } -QV4::ReturnedValue QV4::QmlIncubatorObject::method_forceCompletion(QV4::CallContext *ctx) +void QV4::QmlIncubatorObject::method_forceCompletion(const BuiltinFunction *, Scope &scope, CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped<QmlIncubatorObject> o(scope, ctx->thisObject().as<QmlIncubatorObject>()); + QV4::Scoped<QmlIncubatorObject> o(scope, callData->thisObject.as<QmlIncubatorObject>()); if (!o) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); o->d()->incubator->forceCompletion(); - return QV4::Encode::undefined(); + RETURN_UNDEFINED(); } -QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_status(QV4::CallContext *ctx) +void QV4::QmlIncubatorObject::method_get_status(const BuiltinFunction *, Scope &scope, CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped<QmlIncubatorObject> o(scope, ctx->thisObject().as<QmlIncubatorObject>()); + QV4::Scoped<QmlIncubatorObject> o(scope, callData->thisObject.as<QmlIncubatorObject>()); if (!o) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - return QV4::Encode(o->d()->incubator->status()); + scope.result = QV4::Encode(o->d()->incubator->status()); } -QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_statusChanged(QV4::CallContext *ctx) +void QV4::QmlIncubatorObject::method_get_statusChanged(const BuiltinFunction *, Scope &scope, CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped<QmlIncubatorObject> o(scope, ctx->thisObject().as<QmlIncubatorObject>()); + QV4::Scoped<QmlIncubatorObject> o(scope, callData->thisObject.as<QmlIncubatorObject>()); if (!o) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - return o->d()->statusChanged.asReturnedValue(); + scope.result = o->d()->statusChanged; } -QV4::ReturnedValue QV4::QmlIncubatorObject::method_set_statusChanged(QV4::CallContext *ctx) +void QV4::QmlIncubatorObject::method_set_statusChanged(const BuiltinFunction *, Scope &scope, CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped<QmlIncubatorObject> o(scope, ctx->thisObject().as<QmlIncubatorObject>()); - if (!o || ctx->argc() < 1) - return ctx->engine()->throwTypeError(); + QV4::Scoped<QmlIncubatorObject> o(scope, callData->thisObject.as<QmlIncubatorObject>()); + if (!o || callData->argc < 1) + THROW_TYPE_ERROR(); + o->d()->statusChanged = callData->args[0]; - o->d()->statusChanged = ctx->args()[0]; - return QV4::Encode::undefined(); + RETURN_UNDEFINED(); } QQmlComponentExtension::~QQmlComponentExtension() diff --git a/src/qml/qml/qqmldelayedcallqueue.cpp b/src/qml/qml/qqmldelayedcallqueue.cpp index 7552e1e82b..d5d2c9a28d 100644 --- a/src/qml/qml/qqmldelayedcallqueue.cpp +++ b/src/qml/qml/qqmldelayedcallqueue.cpp @@ -105,17 +105,15 @@ void QQmlDelayedCallQueue::init(QV4::ExecutionEngine* engine) m_tickedMethod = metaObject.method(methodIndex); } -QV4::ReturnedValue QQmlDelayedCallQueue::addUniquelyAndExecuteLater(QV4::CallContext *ctx) +void QQmlDelayedCallQueue::addUniquelyAndExecuteLater(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - const QV4::CallData *callData = ctx->d()->callData; - if (callData->argc == 0) - V4THROW_ERROR("Qt.callLater: no arguments given"); + THROW_GENERIC_ERROR("Qt.callLater: no arguments given"); const QV4::FunctionObject *func = callData->args[0].as<QV4::FunctionObject>(); if (!func) - V4THROW_ERROR("Qt.callLater: first argument not a function or signal"); + THROW_GENERIC_ERROR("Qt.callLater: first argument not a function or signal"); QPair<QObject *, int> functionData = QV4::QObjectMethod::extractQtMethod(func); @@ -171,7 +169,7 @@ QV4::ReturnedValue QQmlDelayedCallQueue::addUniquelyAndExecuteLater(QV4::CallCon m_tickedMethod.invoke(this, Qt::QueuedConnection); m_callbackOutstanding = true; } - return QV4::Encode::undefined(); + scope.result = QV4::Encode::undefined(); } void QQmlDelayedCallQueue::storeAnyArguments(DelayedFunctionCall &dfc, const QV4::CallData *callData, int offset, QV4::ExecutionEngine *engine) diff --git a/src/qml/qml/qqmldelayedcallqueue_p.h b/src/qml/qml/qqmldelayedcallqueue_p.h index ef899170a2..cffde4f0c0 100644 --- a/src/qml/qml/qqmldelayedcallqueue_p.h +++ b/src/qml/qml/qqmldelayedcallqueue_p.h @@ -70,7 +70,7 @@ public: void init(QV4::ExecutionEngine *); - QV4::ReturnedValue addUniquelyAndExecuteLater(QV4::CallContext *ctx); + void addUniquelyAndExecuteLater(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); public Q_SLOTS: void ticked(); diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 8712b638c5..c07d5c740a 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -1,5 +1,6 @@ /**************************************************************************** ** +** Copyright (C) 2017 Crimson AS <info@crimson.no> ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** @@ -130,42 +131,74 @@ bool isPathAbsolute(const QString &path) #endif } -// If the type does not already exist as a file import, add the type and return the new type -QQmlType *getTypeForUrl(const QString &urlString, const QHashedStringRef& typeName, +/* + \internal + + Fetches the QQmlType instance registered for \a urlString, creating a + registration for it if it is not already registered, using the associated + \a typeName, \a isCompositeSingleton, \a majorVersion and \a minorVersion + details. + + Errors (if there are any) are placed into \a errors, if it is nonzero. Note + that errors are treated as fatal if \a errors is not set. +*/ +QQmlType *fetchOrCreateTypeForUrl(const QString &urlString, const QHashedStringRef& typeName, bool isCompositeSingleton, QList<QQmlError> *errors, int majorVersion=-1, int minorVersion=-1) { - QUrl url(urlString); + QUrl url(urlString); // ### unfortunate (costly) conversion QQmlType *ret = QQmlMetaType::qmlType(url); - if (!ret) { //QQmlType not yet existing for composite or composite singleton type - int dot = typeName.indexOf(QLatin1Char('.')); - QHashedStringRef unqualifiedtype = dot < 0 ? typeName : QHashedStringRef(typeName.constData() + dot + 1, typeName.length() - dot - 1); - - //XXX: The constData of the string ref is pointing somewhere unsafe in qmlregister, so we need to create a temporary copy - QByteArray buf(unqualifiedtype.toString().toUtf8()); - - if (isCompositeSingleton) { - QQmlPrivate::RegisterCompositeSingletonType reg = { - url, - "", //Empty URI indicates loaded via file imports - majorVersion, - minorVersion, - buf.constData() - }; - ret = QQmlMetaType::qmlTypeFromIndex(QQmlPrivate::qmlregister(QQmlPrivate::CompositeSingletonRegistration, ®)); - } else { - QQmlPrivate::RegisterCompositeType reg = { - url, - "", //Empty URI indicates loaded via file imports - majorVersion, - minorVersion, - buf.constData() - }; - ret = QQmlMetaType::qmlTypeFromIndex(QQmlPrivate::qmlregister(QQmlPrivate::CompositeRegistration, ®)); - } + if (ret) + return ret; + + int dot = typeName.indexOf(QLatin1Char('.')); + QHashedStringRef unqualifiedtype = dot < 0 ? typeName : QHashedStringRef(typeName.constData() + dot + 1, typeName.length() - dot - 1); + + // We need a pointer, but we were passed a string. Take a copy so we + // can guarentee it will live long enough to reach qmlregister. + QByteArray buf(unqualifiedtype.toString().toUtf8()); + + // Register the type. Note that the URI parameters here are empty; for + // file type imports, we do not place them in a URI as we don't + // necessarily have a good and unique one (picture a library import, + // which may be found in multiple plugin locations on disk), but there + // are other reasons for this too. + // + // By not putting them in a URI, we prevent the types from being + // registered on a QQmlTypeModule; this is important, as once types are + // placed on there, they cannot be easily removed, meaning if the + // developer subsequently loads a different import (meaning different + // types) with the same URI (using, say, a different plugin path), it is + // very undesirable that we continue to associate the types from the + // "old" URI with that new module. + // + // Not having URIs also means that the types cannot be found by name + // etc, the only way to look them up is through QQmlImports -- for + // better or worse. + if (isCompositeSingleton) { + QQmlPrivate::RegisterCompositeSingletonType reg = { + url, + "", // uri + majorVersion, + minorVersion, + buf.constData() + }; + ret = QQmlMetaType::qmlTypeFromIndex(QQmlPrivate::qmlregister(QQmlPrivate::CompositeSingletonRegistration, ®)); + } else { + QQmlPrivate::RegisterCompositeType reg = { + url, + "", // uri + majorVersion, + minorVersion, + buf.constData() + }; + ret = QQmlMetaType::qmlTypeFromIndex(QQmlPrivate::qmlregister(QQmlPrivate::CompositeRegistration, ®)); } - if (!ret) {//Usually when a type name is "found" but invalid - //qDebug() << ret << urlString << QQmlMetaType::qmlType(url); + + // This means that the type couldn't be found by URL, but could not be + // registered either, meaning we most likely were passed some kind of bad + // data. + if (!ret) { if (!errors) // Cannot list errors properly, just quit qFatal("%s", QQmlMetaType::typeRegistrationFailures().join('\n').toLatin1().constData()); QQmlError error; @@ -204,44 +237,38 @@ void qmlClearEnginePlugins() typedef QPair<QStaticPlugin, QJsonArray> StaticPluginPair; #endif -class QQmlImportNamespace -{ -public: - QQmlImportNamespace() : nextNamespace(0) {} - ~QQmlImportNamespace() { qDeleteAll(imports); } - - struct Import { - QString uri; - QString url; - int majversion; - int minversion; - bool isLibrary; - QQmlDirComponents qmlDirComponents; - QQmlDirScripts qmlDirScripts; - - bool setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoader::QmldirContent *qmldir, - QQmlImportNamespace *nameSpace, QList<QQmlError> *errors); - - static QQmlDirScripts getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin); - - bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type, - int *vmajor, int *vminor, QQmlType** type_return, - QString *base = 0, bool *typeRecursionDetected = 0) const; - }; - QList<Import *> imports; +/*! + \internal + \class QQmlImportInstance - Import *findImport(const QString &uri) const; + A QQmlImportType represents a single import of a document, held within a + namespace. - bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef& type, - int *vmajor, int *vminor, QQmlType** type_return, - QString *base = 0, QList<QQmlError> *errors = 0); + \note The uri here may not necessarily be unique (e.g. for file imports). - // Prefix when used as a qualified import. Otherwise empty. - QHashedString prefix; + \note Version numbers may be -1 for file imports: this means that no + version was specified as part of the import. Type resolution will be + responsible for attempting to find the "best" possible version. +*/ - // Used by QQmlImportsPrivate::qualifiedSets - QQmlImportNamespace *nextNamespace; -}; +/*! + \internal + \class QQmlImportNamespace + + A QQmlImportNamespace is a way of seperating imports into a local namespace. + + Within a QML document, there is at least one namespace (the + "unqualified set") where imports without a qualifier are placed, i.e: + + import QtQuick 2.6 + + will have a single namespace (the unqualified set) containing a single import + for QtQuick 2.6. However, there may be others if an import statement gives + a qualifier, i.e the following will result in an additional new + QQmlImportNamespace in the qualified set: + + import MyFoo 1.0 as Foo +*/ class QQmlImportsPrivate { @@ -273,9 +300,12 @@ public: QString base; int ref; + // storage of data related to imports without a namespace mutable QQmlImportNamespace unqualifiedset; QQmlImportNamespace *findQualifiedNamespace(const QHashedStringRef &) const; + + // storage of data related to imports with a namespace mutable QFieldList<QQmlImportNamespace, &QQmlImportNamespace::nextNamespace> qualifiedSets; QQmlTypeLoader *typeLoader; @@ -284,21 +314,21 @@ public: QQmlImportDatabase *database, QString *outQmldirFilePath, QString *outUrl); - static bool validateQmldirVersion(const QQmlTypeLoader::QmldirContent *qmldir, const QString &uri, int vmaj, int vmin, + static bool validateQmldirVersion(const QQmlTypeLoaderQmldirContent *qmldir, const QString &uri, int vmaj, int vmin, QList<QQmlError> *errors); bool importExtension(const QString &absoluteFilePath, const QString &uri, int vmaj, int vmin, QQmlImportDatabase *database, - const QQmlTypeLoader::QmldirContent *qmldir, + const QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors); bool getQmldirContent(const QString &qmldirIdentifier, const QString &uri, - const QQmlTypeLoader::QmldirContent **qmldir, QList<QQmlError> *errors); + const QQmlTypeLoaderQmldirContent **qmldir, QList<QQmlError> *errors); QString resolvedUri(const QString &dir_arg, QQmlImportDatabase *database); - QQmlImportNamespace::Import *addImportToNamespace(QQmlImportNamespace *nameSpace, + QQmlImportInstance *addImportToNamespace(QQmlImportNamespace *nameSpace, const QString &uri, const QString &url, int vmaj, int vmin, QV4::CompiledData::Import::ImportType type, QList<QQmlError> *errors, bool lowPrecedence = false); @@ -363,12 +393,22 @@ QUrl QQmlImports::baseUrl() const return d->baseUrl; } +/* + \internal + + This method is responsible for populating data of all types visible in this + document's imports into the \a cache for resolution elsewhere (e.g. in JS, + or when loading additional types). + + \note This is for C++ types only. Composite types are handled separately, + as they do not have a QQmlTypeModule. +*/ void QQmlImports::populateCache(QQmlTypeNameCache *cache) const { const QQmlImportNamespace &set = d->unqualifiedset; for (int ii = set.imports.count() - 1; ii >= 0; --ii) { - const QQmlImportNamespace::Import *import = set.imports.at(ii); + const QQmlImportInstance *import = set.imports.at(ii); QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->majversion); if (module) { cache->m_anonymousImports.append(QQmlTypeModuleVersion(module, import->minversion)); @@ -379,11 +419,14 @@ void QQmlImports::populateCache(QQmlTypeNameCache *cache) const const QQmlImportNamespace &set = *ns; + // positioning is important; we must create the namespace even if there is no module. + QQmlTypeNameCache::Import &typeimport = cache->m_namedImports[set.prefix]; + typeimport.m_qualifier = set.prefix; + for (int ii = set.imports.count() - 1; ii >= 0; --ii) { - const QQmlImportNamespace::Import *import = set.imports.at(ii); + const QQmlImportInstance *import = set.imports.at(ii); QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->majversion); if (module) { - QQmlTypeNameCache::Import &typeimport = cache->m_namedImports[set.prefix]; typeimport.modules.append(QQmlTypeModuleVersion(module, import->minversion)); } } @@ -412,7 +455,7 @@ void findCompositeSingletons(const QQmlImportNamespace &set, QList<QQmlImports:: typedef QQmlDirComponents::const_iterator ConstIterator; for (int ii = set.imports.count() - 1; ii >= 0; --ii) { - const QQmlImportNamespace::Import *import = set.imports.at(ii); + const QQmlImportInstance *import = set.imports.at(ii); const QQmlDirComponents &components = import->qmlDirComponents; @@ -430,6 +473,15 @@ void findCompositeSingletons(const QQmlImportNamespace &set, QList<QQmlImports:: } } +/* + \internal + + Returns a list of all composite singletons present in this document's + imports. + + This information is used by QQmlTypeLoader to ensure that composite singletons + are marked as dependencies during type loading. +*/ QList<QQmlImports::CompositeSingletonReference> QQmlImports::resolvedCompositeSingletons() const { QList<QQmlImports::CompositeSingletonReference> compositeSingletons; @@ -445,6 +497,12 @@ QList<QQmlImports::CompositeSingletonReference> QQmlImports::resolvedCompositeSi return compositeSingletons; } +/* + \internal + + Returns a list of scripts imported by this document. This is used by + QQmlTypeLoader to properly handle dependencies on imported scripts. +*/ QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts() const { QList<QQmlImports::ScriptReference> scripts; @@ -452,7 +510,7 @@ QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts() const const QQmlImportNamespace &set = d->unqualifiedset; for (int ii = set.imports.count() - 1; ii >= 0; --ii) { - const QQmlImportNamespace::Import *import = set.imports.at(ii); + const QQmlImportInstance *import = set.imports.at(ii); for (const QQmlDirParser::Script &script : import->qmlDirScripts) { ScriptReference ref; @@ -466,7 +524,7 @@ QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts() const const QQmlImportNamespace &set = *ns; for (int ii = set.imports.count() - 1; ii >= 0; --ii) { - const QQmlImportNamespace::Import *import = set.imports.at(ii); + const QQmlImportInstance *import = set.imports.at(ii); for (const QQmlDirParser::Script &script : import->qmlDirScripts) { ScriptReference ref; @@ -590,7 +648,7 @@ bool QQmlImports::resolveType(const QHashedStringRef &type, return false; } -bool QQmlImportNamespace::Import::setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoader::QmldirContent *qmldir, QQmlImportNamespace *nameSpace, QList<QQmlError> *errors) +bool QQmlImportInstance::setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoaderQmldirContent *qmldir, QQmlImportNamespace *nameSpace, QList<QQmlError> *errors) { Q_ASSERT(resolvedUrl.endsWith(Slash)); url = resolvedUrl; @@ -600,7 +658,7 @@ bool QQmlImportNamespace::Import::setQmldirContent(const QString &resolvedUrl, c const QQmlDirScripts &scripts = qmldir->scripts(); if (!scripts.isEmpty()) { // Verify that we haven't imported these scripts already - for (QList<QQmlImportNamespace::Import *>::const_iterator it = nameSpace->imports.constBegin(); + for (QList<QQmlImportInstance *>::const_iterator it = nameSpace->imports.constBegin(); it != nameSpace->imports.constEnd(); ++it) { if ((*it != this) && ((*it)->uri == uri)) { QQmlError error; @@ -616,7 +674,7 @@ bool QQmlImportNamespace::Import::setQmldirContent(const QString &resolvedUrl, c return true; } -QQmlDirScripts QQmlImportNamespace::Import::getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin) +QQmlDirScripts QQmlImportInstance::getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin) { QMap<QString, QQmlDirParser::Script> versioned; @@ -652,7 +710,7 @@ bool QQmlImports::resolveType(QQmlImportNamespace* ns, const QHashedStringRef &t return ns->resolveType(d->typeLoader,type,vmaj,vmin,type_return); } -bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader, +bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef& type, int *vmajor, int *vminor, QQmlType** type_return, QString *base, bool *typeRecursionDetected) const { @@ -683,15 +741,17 @@ bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader, if ((candidate == end) || (c.majorVersion > candidate->majorVersion) || ((c.majorVersion == candidate->majorVersion) && (c.minorVersion > candidate->minorVersion))) { - componentUrl = resolveLocalUrl(QString(url + c.typeName + dotqml_string), c.fileName); - if (c.internal && base) { - if (resolveLocalUrl(*base, c.fileName) != componentUrl) - continue; // failed attempt to access an internal type - } - if (base && (*base == componentUrl)) { - if (typeRecursionDetected) - *typeRecursionDetected = true; - continue; // no recursion + if (base) { + componentUrl = resolveLocalUrl(QString(url + c.typeName + dotqml_string), c.fileName); + if (c.internal) { + if (resolveLocalUrl(*base, c.fileName) != componentUrl) + continue; // failed attempt to access an internal type + } + if (*base == componentUrl) { + if (typeRecursionDetected) + *typeRecursionDetected = true; + continue; // no recursion + } } // This is our best candidate so far @@ -702,9 +762,11 @@ bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader, } if (candidate != end) { + if (!base) // ensure we have a componentUrl + componentUrl = resolveLocalUrl(QString(url + candidate->typeName + dotqml_string), candidate->fileName); int major = vmajor ? *vmajor : -1; int minor = vminor ? *vminor : -1; - QQmlType *returnType = getTypeForUrl(componentUrl, type, isCompositeSingleton, 0, + QQmlType *returnType = fetchOrCreateTypeForUrl(componentUrl, type, isCompositeSingleton, 0, major, minor); if (type_return) *type_return = returnType; @@ -732,7 +794,7 @@ bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader, if (typeRecursionDetected) *typeRecursionDetected = true; } else { - QQmlType *returnType = getTypeForUrl(qmlUrl, type, false, 0); + QQmlType *returnType = fetchOrCreateTypeForUrl(qmlUrl, type, false, 0); if (type_return) *type_return = returnType; return returnType != 0; @@ -777,7 +839,7 @@ bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor, return true; if (s->imports.count() == 1 && !s->imports.at(0)->isLibrary && type_return && s != &unqualifiedset) { // qualified, and only 1 url - *type_return = getTypeForUrl(resolveLocalUrl(s->imports.at(0)->url, unqualifiedtype.toString() + QLatin1String(".qml")), type, false, errors); + *type_return = fetchOrCreateTypeForUrl(resolveLocalUrl(s->imports.at(0)->url, unqualifiedtype.toString() + QLatin1String(".qml")), type, false, errors); return (*type_return != 0); } } @@ -785,9 +847,9 @@ bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor, return false; } -QQmlImportNamespace::Import *QQmlImportNamespace::findImport(const QString &uri) const +QQmlImportInstance *QQmlImportNamespace::findImport(const QString &uri) const { - for (Import *import : imports) { + for (QQmlImportInstance *import : imports) { if (import->uri == uri) return import; } @@ -800,13 +862,13 @@ bool QQmlImportNamespace::resolveType(QQmlTypeLoader *typeLoader, const QHashedS { bool typeRecursionDetected = false; for (int i=0; i<imports.count(); ++i) { - const Import *import = imports.at(i); + const QQmlImportInstance *import = imports.at(i); if (import->resolveType(typeLoader, type, vmajor, vminor, type_return, base, &typeRecursionDetected)) { if (qmlCheckTypes()) { // check for type clashes for (int j = i+1; j<imports.count(); ++j) { - const Import *import2 = imports.at(j); + const QQmlImportInstance *import2 = imports.at(j); if (import2->resolveType(typeLoader, type, vmajor, vminor, 0, base)) { if (errors) { QString u1 = import->url; @@ -949,10 +1011,12 @@ bool QQmlImportsPrivate::populatePluginPairVector(QVector<StaticPluginPair> &res } #endif +#if defined(QT_SHARED) || !QT_CONFIG(library) static inline QString msgCannotLoadPlugin(const QString &uri, const QString &why) { return QQmlImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri, why); } +#endif /*! Import an extension defined by a qmldir file. @@ -963,7 +1027,7 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, const QString &uri, int vmaj, int vmin, QQmlImportDatabase *database, - const QQmlTypeLoader::QmldirContent *qmldir, + const QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors) { #if QT_CONFIG(library) @@ -1099,7 +1163,7 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, } bool QQmlImportsPrivate::getQmldirContent(const QString &qmldirIdentifier, const QString &uri, - const QQmlTypeLoader::QmldirContent **qmldir, QList<QQmlError> *errors) + const QQmlTypeLoaderQmldirContent **qmldir, QList<QQmlError> *errors) { Q_ASSERT(errors); Q_ASSERT(qmldir); @@ -1224,7 +1288,7 @@ bool QQmlImportsPrivate::locateQmldir(const QString &uri, int vmaj, int vmin, QQ return false; } -bool QQmlImportsPrivate::validateQmldirVersion(const QQmlTypeLoader::QmldirContent *qmldir, const QString &uri, int vmaj, int vmin, +bool QQmlImportsPrivate::validateQmldirVersion(const QQmlTypeLoaderQmldirContent *qmldir, const QString &uri, int vmaj, int vmin, QList<QQmlError> *errors) { int lowest_min = INT_MAX; @@ -1307,7 +1371,7 @@ QQmlImportNamespace *QQmlImportsPrivate::importNamespace(const QString &prefix) return nameSpace; } -QQmlImportNamespace::Import *QQmlImportsPrivate::addImportToNamespace(QQmlImportNamespace *nameSpace, +QQmlImportInstance *QQmlImportsPrivate::addImportToNamespace(QQmlImportNamespace *nameSpace, const QString &uri, const QString &url, int vmaj, int vmin, QV4::CompiledData::Import::ImportType type, QList<QQmlError> *errors, bool lowPrecedence) @@ -1317,7 +1381,7 @@ QQmlImportNamespace::Import *QQmlImportsPrivate::addImportToNamespace(QQmlImport Q_UNUSED(errors); Q_ASSERT(url.isEmpty() || url.endsWith(Slash)); - QQmlImportNamespace::Import *import = new QQmlImportNamespace::Import; + QQmlImportInstance *import = new QQmlImportInstance; import->uri = uri; import->url = url; import->majversion = vmaj; @@ -1343,11 +1407,11 @@ bool QQmlImportsPrivate::addLibraryImport(const QString& uri, const QString &pre QQmlImportNamespace *nameSpace = importNamespace(prefix); Q_ASSERT(nameSpace); - QQmlImportNamespace::Import *inserted = addImportToNamespace(nameSpace, uri, qmldirUrl, vmaj, vmin, QV4::CompiledData::Import::ImportLibrary, errors); + QQmlImportInstance *inserted = addImportToNamespace(nameSpace, uri, qmldirUrl, vmaj, vmin, QV4::CompiledData::Import::ImportLibrary, errors); Q_ASSERT(inserted); if (!incomplete) { - const QQmlTypeLoader::QmldirContent *qmldir = 0; + const QQmlTypeLoaderQmldirContent *qmldir = 0; if (!qmldirIdentifier.isEmpty()) { if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors)) @@ -1444,11 +1508,11 @@ bool QQmlImportsPrivate::addFileImport(const QString& uri, const QString &prefix if (!url.endsWith(Slash) && !url.endsWith(Backslash)) url += Slash; - QQmlImportNamespace::Import *inserted = addImportToNamespace(nameSpace, importUri, url, vmaj, vmin, QV4::CompiledData::Import::ImportFile, errors, isImplicitImport); + QQmlImportInstance *inserted = addImportToNamespace(nameSpace, importUri, url, vmaj, vmin, QV4::CompiledData::Import::ImportFile, errors, isImplicitImport); Q_ASSERT(inserted); if (!incomplete && !qmldirIdentifier.isEmpty()) { - const QQmlTypeLoader::QmldirContent *qmldir = 0; + const QQmlTypeLoaderQmldirContent *qmldir = 0; if (!getQmldirContent(qmldirIdentifier, importUri, &qmldir, errors)) return false; @@ -1471,8 +1535,8 @@ bool QQmlImportsPrivate::updateQmldirContent(const QString &uri, const QString & QQmlImportNamespace *nameSpace = importNamespace(prefix); Q_ASSERT(nameSpace); - if (QQmlImportNamespace::Import *import = nameSpace->findImport(uri)) { - const QQmlTypeLoader::QmldirContent *qmldir = 0; + if (QQmlImportInstance *import = nameSpace->findImport(uri)) { + const QQmlTypeLoaderQmldirContent *qmldir = 0; if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors)) return false; diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h index 0e7848730f..7c691a468c 100644 --- a/src/qml/qml/qqmlimport_p.h +++ b/src/qml/qml/qqmlimport_p.h @@ -68,6 +68,48 @@ class QQmlImportNamespace; class QQmlImportsPrivate; class QQmlImportDatabase; class QQmlTypeLoader; +class QQmlTypeLoaderQmldirContent; + +struct QQmlImportInstance +{ + QString uri; // e.g. QtQuick + QString url; // the base path of the import + int majversion; // the major version imported + int minversion; // the minor version imported + bool isLibrary; // true means that this is not a file import + QQmlDirComponents qmlDirComponents; // a copy of the components listed in the qmldir + QQmlDirScripts qmlDirScripts; // a copy of the scripts in the qmldir + + bool setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoaderQmldirContent *qmldir, + QQmlImportNamespace *nameSpace, QList<QQmlError> *errors); + + static QQmlDirScripts getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin); + + bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type, + int *vmajor, int *vminor, QQmlType** type_return, + QString *base = 0, bool *typeRecursionDetected = 0) const; +}; + +class QQmlImportNamespace +{ +public: + QQmlImportNamespace() : nextNamespace(0) {} + ~QQmlImportNamespace() { qDeleteAll(imports); } + + QList<QQmlImportInstance *> imports; + + QQmlImportInstance *findImport(const QString &uri) const; + + bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef& type, + int *vmajor, int *vminor, QQmlType** type_return, + QString *base = 0, QList<QQmlError> *errors = 0); + + // Prefix when used as a qualified import. Otherwise empty. + QHashedString prefix; + + // Used by QQmlImportsPrivate::qualifiedSets + QQmlImportNamespace *nextNamespace; +}; class Q_QML_PRIVATE_EXPORT QQmlImports { diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp index 3876e774c3..712da78807 100644 --- a/src/qml/qml/qqmllocale.cpp +++ b/src/qml/qml/qqmllocale.cpp @@ -56,10 +56,17 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(QQmlLocaleData); +#define THROW_ERROR(string) \ + do { \ + scope.result = scope.engine->throwError(QString::fromUtf8(string)); \ + return; \ + } while (false) + + #define GET_LOCALE_DATA_RESOURCE(OBJECT) \ QV4::Scoped<QQmlLocaleData> r(scope, OBJECT.as<QQmlLocaleData>()); \ if (!r) \ - V4THROW_ERROR("Not a valid Locale object") + THROW_ERROR("Not a valid Locale object") static bool isLocaleObject(const QV4::Value &val) { @@ -80,215 +87,219 @@ void QQmlDateExtension::registerExtension(QV4::ExecutionEngine *engine) engine->dateCtor()->defineDefaultProperty(QStringLiteral("timeZoneUpdated"), method_timeZoneUpdated); } -QV4::ReturnedValue QQmlDateExtension::method_toLocaleString(QV4::CallContext *ctx) +void QQmlDateExtension::method_toLocaleString(const BuiltinFunction *b, Scope &scope, CallData *callData) { - if (ctx->argc() > 2) - return QV4::DatePrototype::method_toLocaleString(ctx); - - QV4::Scope scope(ctx); + if (callData->argc > 2) { + QV4::DatePrototype::method_toLocaleString(b, scope, callData); + return; + } - QV4::DateObject *date = ctx->thisObject().as<DateObject>(); - if (!date) - return QV4::DatePrototype::method_toLocaleString(ctx); + QV4::DateObject *date = callData->thisObject.as<DateObject>(); + if (!date) { + QV4::DatePrototype::method_toLocaleString(b, scope, callData); + return; + } QDateTime dt = date->toQDateTime(); - if (ctx->argc() == 0) { + if (callData->argc == 0) { // Use QLocale for standard toLocaleString() function QLocale locale; - return ctx->d()->engine->newString(locale.toString(dt))->asReturnedValue(); + RETURN_RESULT(scope.engine->newString(locale.toString(dt))); } - if (!isLocaleObject(ctx->args()[0])) - return QV4::DatePrototype::method_toLocaleString(ctx); // Use the default Date toLocaleString() + if (!isLocaleObject(callData->args[0])) { + QV4::DatePrototype::method_toLocaleString(b, scope, callData); // Use the default Date toLocaleString() + return; + } - GET_LOCALE_DATA_RESOURCE(ctx->args()[0]); + GET_LOCALE_DATA_RESOURCE(callData->args[0]); QLocale::FormatType enumFormat = QLocale::LongFormat; QString formattedDt; - if (ctx->argc() == 2) { - if (String *s = ctx->args()[1].stringValue()) { + if (callData->argc == 2) { + if (String *s = callData->args[1].stringValue()) { QString format = s->toQString(); formattedDt = r->d()->locale->toString(dt, format); - } else if (ctx->args()[1].isNumber()) { - quint32 intFormat = ctx->args()[1].toNumber(); + } else if (callData->args[1].isNumber()) { + quint32 intFormat = callData->args[1].toNumber(); QLocale::FormatType format = QLocale::FormatType(intFormat); formattedDt = r->d()->locale->toString(dt, format); } else { - V4THROW_ERROR("Locale: Date.toLocaleString(): Invalid datetime format"); + THROW_ERROR("Locale: Date.toLocaleString(): Invalid datetime format"); } } else { formattedDt = r->d()->locale->toString(dt, enumFormat); } - return ctx->d()->engine->newString(formattedDt)->asReturnedValue(); + scope.result = scope.engine->newString(formattedDt); } -QV4::ReturnedValue QQmlDateExtension::method_toLocaleTimeString(QV4::CallContext *ctx) +void QQmlDateExtension::method_toLocaleTimeString(const BuiltinFunction *b, Scope &scope, CallData *callData) { - if (ctx->argc() > 2) - return QV4::DatePrototype::method_toLocaleTimeString(ctx); - - QV4::Scope scope(ctx); + if (callData->argc > 2) { + QV4::DatePrototype::method_toLocaleTimeString(b, scope, callData); + return; + } - QV4::DateObject *date = ctx->thisObject().as<DateObject>(); - if (!date) - return QV4::DatePrototype::method_toLocaleTimeString(ctx); + QV4::DateObject *date = callData->thisObject.as<DateObject>(); + if (!date) { + QV4::DatePrototype::method_toLocaleTimeString(b, scope, callData); + return; + } QDateTime dt = date->toQDateTime(); QTime time = dt.time(); - if (ctx->argc() == 0) { + if (callData->argc == 0) { // Use QLocale for standard toLocaleString() function QLocale locale; - return ctx->d()->engine->newString(locale.toString(time))->asReturnedValue(); + RETURN_RESULT(scope.engine->newString(locale.toString(time))); } - if (!isLocaleObject(ctx->args()[0])) - return QV4::DatePrototype::method_toLocaleTimeString(ctx); // Use the default Date toLocaleTimeString() + if (!isLocaleObject(callData->args[0])) + return QV4::DatePrototype::method_toLocaleTimeString(b, scope, callData); // Use the default Date toLocaleTimeString() - GET_LOCALE_DATA_RESOURCE(ctx->args()[0]); + GET_LOCALE_DATA_RESOURCE(callData->args[0]); QLocale::FormatType enumFormat = QLocale::LongFormat; QString formattedTime; - if (ctx->argc() == 2) { - if (String *s = ctx->args()[1].stringValue()) { + if (callData->argc == 2) { + if (String *s = callData->args[1].stringValue()) { QString format = s->toQString(); formattedTime = r->d()->locale->toString(time, format); - } else if (ctx->args()[1].isNumber()) { - quint32 intFormat = ctx->args()[1].toNumber(); + } else if (callData->args[1].isNumber()) { + quint32 intFormat = callData->args[1].toNumber(); QLocale::FormatType format = QLocale::FormatType(intFormat); formattedTime = r->d()->locale->toString(time, format); } else { - V4THROW_ERROR("Locale: Date.toLocaleTimeString(): Invalid time format"); + THROW_ERROR("Locale: Date.toLocaleTimeString(): Invalid time format"); } } else { formattedTime = r->d()->locale->toString(time, enumFormat); } - return ctx->d()->engine->newString(formattedTime)->asReturnedValue(); + scope.result = scope.engine->newString(formattedTime); } -QV4::ReturnedValue QQmlDateExtension::method_toLocaleDateString(QV4::CallContext *ctx) +void QQmlDateExtension::method_toLocaleDateString(const BuiltinFunction *b, Scope &scope, CallData *callData) { - if (ctx->argc() > 2) - return QV4::DatePrototype::method_toLocaleDateString(ctx); - - QV4::Scope scope(ctx); + if (callData->argc > 2) { + QV4::DatePrototype::method_toLocaleDateString(b, scope, callData); + return; + } - QV4::DateObject *dateObj = ctx->thisObject().as<DateObject>(); - if (!dateObj) - return QV4::DatePrototype::method_toLocaleDateString(ctx); + QV4::DateObject *dateObj = callData->thisObject.as<DateObject>(); + if (!dateObj) { + QV4::DatePrototype::method_toLocaleDateString(b, scope, callData); + return; + } QDateTime dt = dateObj->toQDateTime(); QDate date = dt.date(); - if (ctx->argc() == 0) { + if (callData->argc == 0) { // Use QLocale for standard toLocaleString() function QLocale locale; - return ctx->d()->engine->newString(locale.toString(date))->asReturnedValue(); + RETURN_RESULT(scope.engine->newString(locale.toString(date))); } - if (!isLocaleObject(ctx->args()[0])) - return QV4::DatePrototype::method_toLocaleDateString(ctx); // Use the default Date toLocaleDateString() + if (!isLocaleObject(callData->args[0])) + return QV4::DatePrototype::method_toLocaleDateString(b, scope, callData); // Use the default Date toLocaleDateString() - GET_LOCALE_DATA_RESOURCE(ctx->args()[0]); + GET_LOCALE_DATA_RESOURCE(callData->args[0]); QLocale::FormatType enumFormat = QLocale::LongFormat; QString formattedDate; - if (ctx->argc() == 2) { - if (String *s = ctx->args()[1].stringValue()) { + if (callData->argc == 2) { + if (String *s = callData->args[1].stringValue()) { QString format = s->toQString(); formattedDate = r->d()->locale->toString(date, format); - } else if (ctx->args()[1].isNumber()) { - quint32 intFormat = ctx->args()[1].toNumber(); + } else if (callData->args[1].isNumber()) { + quint32 intFormat = callData->args[1].toNumber(); QLocale::FormatType format = QLocale::FormatType(intFormat); formattedDate = r->d()->locale->toString(date, format); } else { - V4THROW_ERROR("Locale: Date.loLocaleDateString(): Invalid date format"); + THROW_ERROR("Locale: Date.loLocaleDateString(): Invalid date format"); } } else { formattedDate = r->d()->locale->toString(date, enumFormat); } - return ctx->d()->engine->newString(formattedDate)->asReturnedValue(); + scope.result = scope.engine->newString(formattedDate); } -QV4::ReturnedValue QQmlDateExtension::method_fromLocaleString(QV4::CallContext *ctx) +void QQmlDateExtension::method_fromLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData) { - QV4::ExecutionEngine * const engine = ctx->d()->engine; - if (ctx->argc() == 1) { - if (String *s = ctx->args()[0].stringValue()) { + QV4::ExecutionEngine * const engine = scope.engine; + if (callData->argc == 1) { + if (String *s = callData->args[0].stringValue()) { QLocale locale; QString dateString = s->toQString(); QDateTime dt = locale.toDateTime(dateString); - return QV4::Encode(engine->newDateObject(dt)); + RETURN_RESULT(engine->newDateObject(dt)); } } - QV4::Scope scope(ctx); - - if (ctx->argc() < 1 || ctx->argc() > 3 || !isLocaleObject(ctx->args()[0])) - V4THROW_ERROR("Locale: Date.fromLocaleString(): Invalid arguments"); + if (callData->argc < 1 || callData->argc > 3 || !isLocaleObject(callData->args[0])) + THROW_ERROR("Locale: Date.fromLocaleString(): Invalid arguments"); - GET_LOCALE_DATA_RESOURCE(ctx->args()[0]); + GET_LOCALE_DATA_RESOURCE(callData->args[0]); QLocale::FormatType enumFormat = QLocale::LongFormat; QDateTime dt; - QString dateString = ctx->args()[1].toQStringNoThrow(); - if (ctx->argc() == 3) { - if (String *s = ctx->args()[2].stringValue()) { + QString dateString = callData->args[1].toQStringNoThrow(); + if (callData->argc == 3) { + if (String *s = callData->args[2].stringValue()) { QString format = s->toQString(); dt = r->d()->locale->toDateTime(dateString, format); - } else if (ctx->args()[2].isNumber()) { - quint32 intFormat = ctx->args()[2].toNumber(); + } else if (callData->args[2].isNumber()) { + quint32 intFormat = callData->args[2].toNumber(); QLocale::FormatType format = QLocale::FormatType(intFormat); dt = r->d()->locale->toDateTime(dateString, format); } else { - V4THROW_ERROR("Locale: Date.fromLocaleString(): Invalid datetime format"); + THROW_ERROR("Locale: Date.fromLocaleString(): Invalid datetime format"); } } else { dt = r->d()->locale->toDateTime(dateString, enumFormat); } - return QV4::Encode(engine->newDateObject(dt)); + scope.result = engine->newDateObject(dt); } -QV4::ReturnedValue QQmlDateExtension::method_fromLocaleTimeString(QV4::CallContext *ctx) +void QQmlDateExtension::method_fromLocaleTimeString(const BuiltinFunction *, Scope &scope, CallData *callData) { - QV4::ExecutionEngine * const engine = ctx->d()->engine; + QV4::ExecutionEngine * const engine = scope.engine; - if (ctx->argc() == 1) { - if (String *s = ctx->args()[0].stringValue()) { + if (callData->argc == 1) { + if (String *s = callData->args[0].stringValue()) { QLocale locale; QString timeString = s->toQString(); QTime time = locale.toTime(timeString); QDateTime dt = QDateTime::currentDateTime(); dt.setTime(time); - return QV4::Encode(engine->newDateObject(dt)); + RETURN_RESULT(engine->newDateObject(dt)); } } - if (ctx->argc() < 1 || ctx->argc() > 3 || !isLocaleObject(ctx->args()[0])) - V4THROW_ERROR("Locale: Date.fromLocaleTimeString(): Invalid arguments"); - - QV4::Scope scope(ctx); + if (callData->argc < 1 || callData->argc > 3 || !isLocaleObject(callData->args[0])) + THROW_ERROR("Locale: Date.fromLocaleTimeString(): Invalid arguments"); - GET_LOCALE_DATA_RESOURCE(ctx->args()[0]); + GET_LOCALE_DATA_RESOURCE(callData->args[0]); QLocale::FormatType enumFormat = QLocale::LongFormat; QTime tm; - QString dateString = ctx->args()[1].toQStringNoThrow(); - if (ctx->argc() == 3) { - if (String *s = ctx->args()[2].stringValue()) { + QString dateString = callData->args[1].toQStringNoThrow(); + if (callData->argc == 3) { + if (String *s = callData->args[2].stringValue()) { QString format = s->toQString(); tm = r->d()->locale->toTime(dateString, format); - } else if (ctx->args()[2].isNumber()) { - quint32 intFormat = ctx->args()[2].toNumber(); + } else if (callData->args[2].isNumber()) { + quint32 intFormat = callData->args[2].toNumber(); QLocale::FormatType format = QLocale::FormatType(intFormat); tm = r->d()->locale->toTime(dateString, format); } else { - V4THROW_ERROR("Locale: Date.fromLocaleTimeString(): Invalid datetime format"); + THROW_ERROR("Locale: Date.fromLocaleTimeString(): Invalid datetime format"); } } else { tm = r->d()->locale->toTime(dateString, enumFormat); @@ -300,58 +311,56 @@ QV4::ReturnedValue QQmlDateExtension::method_fromLocaleTimeString(QV4::CallConte dt.setTime(tm); } - return QV4::Encode(engine->newDateObject(dt)); + RETURN_RESULT(engine->newDateObject(dt)); } -QV4::ReturnedValue QQmlDateExtension::method_fromLocaleDateString(QV4::CallContext *ctx) +void QQmlDateExtension::method_fromLocaleDateString(const BuiltinFunction *, Scope &scope, CallData *callData) { - QV4::ExecutionEngine * const engine = ctx->d()->engine; + QV4::ExecutionEngine * const engine = scope.engine; - if (ctx->argc() == 1) { - if (String *s = ctx->args()[0].stringValue()) { + if (callData->argc == 1) { + if (String *s = callData->args[0].stringValue()) { QLocale locale; QString dateString = s->toQString(); QDate date = locale.toDate(dateString); - return QV4::Encode(engine->newDateObject(QDateTime(date))); + RETURN_RESULT(engine->newDateObject(QDateTime(date))); } } - if (ctx->argc() < 1 || ctx->argc() > 3 || !isLocaleObject(ctx->args()[0])) - V4THROW_ERROR("Locale: Date.fromLocaleDateString(): Invalid arguments"); + if (callData->argc < 1 || callData->argc > 3 || !isLocaleObject(callData->args[0])) + THROW_ERROR("Locale: Date.fromLocaleDateString(): Invalid arguments"); - QV4::Scope scope(ctx); - - GET_LOCALE_DATA_RESOURCE(ctx->args()[0]); + GET_LOCALE_DATA_RESOURCE(callData->args[0]); QLocale::FormatType enumFormat = QLocale::LongFormat; QDate dt; - QString dateString = ctx->args()[1].toQStringNoThrow(); - if (ctx->argc() == 3) { - if (String *s = ctx->args()[2].stringValue()) { + QString dateString = callData->args[1].toQStringNoThrow(); + if (callData->argc == 3) { + if (String *s = callData->args[2].stringValue()) { QString format = s->toQString(); dt = r->d()->locale->toDate(dateString, format); - } else if (ctx->args()[2].isNumber()) { - quint32 intFormat = ctx->args()[2].toNumber(); + } else if (callData->args[2].isNumber()) { + quint32 intFormat = callData->args[2].toNumber(); QLocale::FormatType format = QLocale::FormatType(intFormat); dt = r->d()->locale->toDate(dateString, format); } else { - V4THROW_ERROR("Locale: Date.fromLocaleDateString(): Invalid datetime format"); + THROW_ERROR("Locale: Date.fromLocaleDateString(): Invalid datetime format"); } } else { dt = r->d()->locale->toDate(dateString, enumFormat); } - return QV4::Encode(engine->newDateObject(QDateTime(dt))); + RETURN_RESULT(engine->newDateObject(QDateTime(dt))); } -QV4::ReturnedValue QQmlDateExtension::method_timeZoneUpdated(QV4::CallContext *ctx) +void QQmlDateExtension::method_timeZoneUpdated(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() != 0) - V4THROW_ERROR("Locale: Date.timeZoneUpdated(): Invalid arguments"); + if (callData->argc != 0) + THROW_ERROR("Locale: Date.timeZoneUpdated(): Invalid arguments"); QV4::DatePrototype::timezoneUpdated(); - return QV4::Encode::undefined(); + RETURN_UNDEFINED(); } //----------------- @@ -364,148 +373,143 @@ void QQmlNumberExtension::registerExtension(QV4::ExecutionEngine *engine) engine->numberCtor()->defineDefaultProperty(QStringLiteral("fromLocaleString"), method_fromLocaleString); } -QV4::ReturnedValue QQmlNumberExtension::method_toLocaleString(QV4::CallContext *ctx) +void QQmlNumberExtension::method_toLocaleString(const BuiltinFunction *b, Scope &scope, CallData *callData) { - if (ctx->argc() > 3) - V4THROW_ERROR("Locale: Number.toLocaleString(): Invalid arguments"); + if (callData->argc > 3) + THROW_ERROR("Locale: Number.toLocaleString(): Invalid arguments"); - double number = ctx->thisObject().toNumber(); + double number = callData->thisObject.toNumber(); - if (ctx->argc() == 0) { + if (callData->argc == 0) { // Use QLocale for standard toLocaleString() function QLocale locale; - return ctx->d()->engine->newString(locale.toString(number))->asReturnedValue(); + RETURN_RESULT(scope.engine->newString(locale.toString(number))); } - if (!isLocaleObject(ctx->args()[0])) - return QV4::NumberPrototype::method_toLocaleString(ctx); // Use the default Number toLocaleString() - - QV4::Scope scope(ctx); + if (!isLocaleObject(callData->args[0])) { + QV4::NumberPrototype::method_toLocaleString(b, scope, callData); // Use the default Number toLocaleString() + return; + } - GET_LOCALE_DATA_RESOURCE(ctx->args()[0]); + GET_LOCALE_DATA_RESOURCE(callData->args[0]); quint16 format = 'f'; - if (ctx->argc() > 1) { - if (!ctx->args()[1].isString()) - V4THROW_ERROR("Locale: Number.toLocaleString(): Invalid arguments"); - QString fs = ctx->args()[1].toQString(); + if (callData->argc > 1) { + if (!callData->args[1].isString()) + THROW_ERROR("Locale: Number.toLocaleString(): Invalid arguments"); + QString fs = callData->args[1].toQString(); if (fs.length()) format = fs.at(0).unicode(); } int prec = 2; - if (ctx->argc() > 2) { - if (!ctx->args()[2].isNumber()) - V4THROW_ERROR("Locale: Number.toLocaleString(): Invalid arguments"); - prec = ctx->args()[2].toInt32(); + if (callData->argc > 2) { + if (!callData->args[2].isNumber()) + THROW_ERROR("Locale: Number.toLocaleString(): Invalid arguments"); + prec = callData->args[2].toInt32(); } - return ctx->d()->engine->newString(r->d()->locale->toString(number, (char)format, prec))->asReturnedValue(); + scope.result = scope.engine->newString(r->d()->locale->toString(number, (char)format, prec)); } -QV4::ReturnedValue QQmlNumberExtension::method_toLocaleCurrencyString(QV4::CallContext *ctx) +void QQmlNumberExtension::method_toLocaleCurrencyString(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() > 2) - V4THROW_ERROR("Locale: Number.toLocaleCurrencyString(): Invalid arguments"); + if (callData->argc > 2) + THROW_ERROR("Locale: Number.toLocaleCurrencyString(): Invalid arguments"); - double number = ctx->thisObject().toNumber(); + double number = callData->thisObject.toNumber(); - if (ctx->argc() == 0) { + if (callData->argc == 0) { // Use QLocale for standard toLocaleString() function QLocale locale; - return ctx->d()->engine->newString(locale.toString(number))->asReturnedValue(); + RETURN_RESULT(scope.engine->newString(locale.toString(number))); } - if (!isLocaleObject(ctx->args()[0])) - V4THROW_ERROR("Locale: Number.toLocaleCurrencyString(): Invalid arguments"); - - QV4::Scope scope(ctx); + if (!isLocaleObject(callData->args[0])) + THROW_ERROR("Locale: Number.toLocaleCurrencyString(): Invalid arguments"); - GET_LOCALE_DATA_RESOURCE(ctx->args()[0]); + GET_LOCALE_DATA_RESOURCE(callData->args[0]); QString symbol; - if (ctx->argc() > 1) { - if (!ctx->args()[1].isString()) - V4THROW_ERROR("Locale: Number.toLocaleString(): Invalid arguments"); - symbol = ctx->args()[1].toQStringNoThrow(); + if (callData->argc > 1) { + if (!callData->args[1].isString()) + THROW_ERROR("Locale: Number.toLocaleString(): Invalid arguments"); + symbol = callData->args[1].toQStringNoThrow(); } - return ctx->d()->engine->newString(r->d()->locale->toCurrencyString(number, symbol))->asReturnedValue(); + RETURN_RESULT(scope.engine->newString(r->d()->locale->toCurrencyString(number, symbol))); } -QV4::ReturnedValue QQmlNumberExtension::method_fromLocaleString(QV4::CallContext *ctx) +void QQmlNumberExtension::method_fromLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() < 1 || ctx->argc() > 2) - V4THROW_ERROR("Locale: Number.fromLocaleString(): Invalid arguments"); + if (callData->argc < 1 || callData->argc > 2) + THROW_ERROR("Locale: Number.fromLocaleString(): Invalid arguments"); int numberIdx = 0; QLocale locale; - QV4::Scope scope(ctx); - - if (ctx->argc() == 2) { - if (!isLocaleObject(ctx->args()[0])) - V4THROW_ERROR("Locale: Number.fromLocaleString(): Invalid arguments"); + if (callData->argc == 2) { + if (!isLocaleObject(callData->args[0])) + THROW_ERROR("Locale: Number.fromLocaleString(): Invalid arguments"); - GET_LOCALE_DATA_RESOURCE(ctx->args()[0]); + GET_LOCALE_DATA_RESOURCE(callData->args[0]); locale = *r->d()->locale; numberIdx = 1; } - QString ns = ctx->args()[numberIdx].toQString(); + QString ns = callData->args[numberIdx].toQString(); if (!ns.length()) - return QV4::Encode(Q_QNAN); + RETURN_RESULT(QV4::Encode(Q_QNAN)); bool ok = false; double val = locale.toDouble(ns, &ok); if (!ok) - V4THROW_ERROR("Locale: Number.fromLocaleString(): Invalid format") + THROW_ERROR("Locale: Number.fromLocaleString(): Invalid format"); - return QV4::Encode(val); + scope.result = QV4::Encode(val); } //-------------- // Locale object -QV4::ReturnedValue QQmlLocaleData::method_get_firstDayOfWeek(QV4::CallContext *ctx) +void QQmlLocaleData::method_get_firstDayOfWeek(const BuiltinFunction *, Scope &scope, CallData *callData) { - QLocale *locale = getThisLocale(ctx); + QLocale *locale = getThisLocale(scope, callData); if (!locale) - return QV4::Encode::undefined(); + return; int fdow = int(locale->firstDayOfWeek()); if (fdow == 7) fdow = 0; // Qt::Sunday = 7, but Sunday is 0 in JS Date - return QV4::Encode(fdow); + scope.result = QV4::Encode(fdow); } -QV4::ReturnedValue QQmlLocaleData::method_get_measurementSystem(QV4::CallContext *ctx) +void QQmlLocaleData::method_get_measurementSystem(const BuiltinFunction *, Scope &scope, CallData *callData) { - QLocale *locale = getThisLocale(ctx); + QLocale *locale = getThisLocale(scope, callData); if (!locale) - return QV4::Encode::undefined(); - return QV4::Encode(locale->measurementSystem()); + return; + scope.result = QV4::Encode(locale->measurementSystem()); } -QV4::ReturnedValue QQmlLocaleData::method_get_textDirection(QV4::CallContext *ctx) +void QQmlLocaleData::method_get_textDirection(const BuiltinFunction *, Scope &scope, CallData *callData) { - QLocale *locale = getThisLocale(ctx); + QLocale *locale = getThisLocale(scope, callData); if (!locale) - return QV4::Encode::undefined(); + return; - return QV4::Encode(locale->textDirection()); + scope.result = QV4::Encode(locale->textDirection()); } -QV4::ReturnedValue QQmlLocaleData::method_get_weekDays(QV4::CallContext *ctx) +void QQmlLocaleData::method_get_weekDays(const BuiltinFunction *, Scope &scope, CallData *callData) { - QV4::Scope scope(ctx); - QLocale *locale = getThisLocale(ctx); + QLocale *locale = getThisLocale(scope, callData); if (!locale) - return QV4::Encode::undefined(); + return; QList<Qt::DayOfWeek> days = locale->weekdays(); - QV4::ScopedArrayObject result(scope, ctx->d()->engine->newArrayObject()); + QV4::ScopedArrayObject result(scope, scope.engine->newArrayObject()); result->arrayReserve(days.size()); for (int i = 0; i < days.size(); ++i) { int day = days.at(i); @@ -515,59 +519,58 @@ QV4::ReturnedValue QQmlLocaleData::method_get_weekDays(QV4::CallContext *ctx) } result->setArrayLengthUnchecked(days.size()); - return result.asReturnedValue(); + scope.result = result.asReturnedValue(); } -QV4::ReturnedValue QQmlLocaleData::method_get_uiLanguages(QV4::CallContext *ctx) +void QQmlLocaleData::method_get_uiLanguages(const BuiltinFunction *, Scope &scope, CallData *callData) { - QV4::Scope scope(ctx); - QLocale *locale = getThisLocale(ctx); + QLocale *locale = getThisLocale(scope, callData); if (!locale) - return QV4::Encode::undefined(); + return; QStringList langs = locale->uiLanguages(); - QV4::ScopedArrayObject result(scope, ctx->d()->engine->newArrayObject()); + QV4::ScopedArrayObject result(scope, scope.engine->newArrayObject()); result->arrayReserve(langs.size()); QV4::ScopedValue v(scope); for (int i = 0; i < langs.size(); ++i) - result->arrayPut(i, (v = ctx->d()->engine->newString(langs.at(i)))); + result->arrayPut(i, (v = scope.engine->newString(langs.at(i)))); result->setArrayLengthUnchecked(langs.size()); - return result.asReturnedValue(); + scope.result = result.asReturnedValue(); } -QV4::ReturnedValue QQmlLocaleData::method_currencySymbol(QV4::CallContext *ctx) +void QQmlLocaleData::method_currencySymbol(const BuiltinFunction *, Scope &scope, CallData *callData) { - QLocale *locale = getThisLocale(ctx); + QLocale *locale = getThisLocale(scope, callData); if (!locale) - return QV4::Encode::undefined(); + return; - if (ctx->argc() > 1) - V4THROW_ERROR("Locale: currencySymbol(): Invalid arguments"); + if (callData->argc > 1) + THROW_ERROR("Locale: currencySymbol(): Invalid arguments"); QLocale::CurrencySymbolFormat format = QLocale::CurrencySymbol; - if (ctx->argc() == 1) { - quint32 intFormat = ctx->args()[0].toNumber(); + if (callData->argc == 1) { + quint32 intFormat = callData->args[0].toNumber(); format = QLocale::CurrencySymbolFormat(intFormat); } - return ctx->d()->engine->newString(locale->currencySymbol(format))->asReturnedValue(); + scope.result = scope.engine->newString(locale->currencySymbol(format)); } #define LOCALE_FORMAT(FUNC) \ -QV4::ReturnedValue QQmlLocaleData::method_ ##FUNC (QV4::CallContext *ctx) { \ - QLocale *locale = getThisLocale(ctx); \ +void QQmlLocaleData::method_ ##FUNC (const BuiltinFunction *, Scope &scope, CallData *callData) { \ + QLocale *locale = getThisLocale(scope, callData); \ if (!locale) \ - return QV4::Encode::undefined(); \ - if (ctx->argc() > 1) \ - V4THROW_ERROR("Locale: " #FUNC "(): Invalid arguments"); \ + return; \ + if (callData->argc > 1) \ + THROW_ERROR("Locale: " #FUNC "(): Invalid arguments"); \ QLocale::FormatType format = QLocale::LongFormat;\ - if (ctx->argc() == 1) { \ - quint32 intFormat = ctx->args()[0].toUInt32(); \ + if (callData->argc == 1) { \ + quint32 intFormat = callData->args[0].toUInt32(); \ format = QLocale::FormatType(intFormat); \ } \ - return ctx->engine()->newString(locale-> FUNC (format))->asReturnedValue(); \ + scope.result = scope.engine->newString(locale-> FUNC (format)); \ } LOCALE_FORMAT(dateTimeFormat) @@ -576,57 +579,57 @@ LOCALE_FORMAT(dateFormat) // +1 added to idx because JS is 0-based, whereas QLocale months begin at 1. #define LOCALE_FORMATTED_MONTHNAME(VARIABLE) \ -QV4::ReturnedValue QQmlLocaleData::method_ ## VARIABLE (QV4::CallContext *ctx) {\ - QLocale *locale = getThisLocale(ctx); \ +void QQmlLocaleData::method_ ## VARIABLE (const BuiltinFunction *, Scope &scope, CallData *callData) {\ + QLocale *locale = getThisLocale(scope, callData); \ if (!locale) \ - return QV4::Encode::undefined(); \ - if (ctx->argc() < 1 || ctx->argc() > 2) \ - V4THROW_ERROR("Locale: " #VARIABLE "(): Invalid arguments"); \ + return; \ + if (callData->argc < 1 || callData->argc > 2) \ + THROW_ERROR("Locale: " #VARIABLE "(): Invalid arguments"); \ QLocale::FormatType enumFormat = QLocale::LongFormat; \ - int idx = ctx->args()[0].toInt32() + 1; \ + int idx = callData->args[0].toInt32() + 1; \ if (idx < 1 || idx > 12) \ - V4THROW_ERROR("Locale: Invalid month"); \ + THROW_ERROR("Locale: Invalid month"); \ QString name; \ - if (ctx->argc() == 2) { \ - if (ctx->args()[1].isNumber()) { \ - quint32 intFormat = ctx->args()[1].toUInt32(); \ + if (callData->argc == 2) { \ + if (callData->args[1].isNumber()) { \ + quint32 intFormat = callData->args[1].toUInt32(); \ QLocale::FormatType format = QLocale::FormatType(intFormat); \ name = locale-> VARIABLE(idx, format); \ } else { \ - V4THROW_ERROR("Locale: Invalid datetime format"); \ + THROW_ERROR("Locale: Invalid datetime format"); \ } \ } else { \ name = locale-> VARIABLE(idx, enumFormat); \ } \ - return ctx->engine()->newString(name)->asReturnedValue(); \ + scope.result = scope.engine->newString(name); \ } // 0 -> 7 as Qt::Sunday is 7, but Sunday is 0 in JS Date #define LOCALE_FORMATTED_DAYNAME(VARIABLE) \ -QV4::ReturnedValue QQmlLocaleData::method_ ## VARIABLE (QV4::CallContext *ctx) {\ - QLocale *locale = getThisLocale(ctx); \ +void QQmlLocaleData::method_ ## VARIABLE (const BuiltinFunction *, Scope &scope, CallData *callData) {\ + QLocale *locale = getThisLocale(scope, callData); \ if (!locale) \ - return QV4::Encode::undefined(); \ - if (ctx->argc() < 1 || ctx->argc() > 2) \ - V4THROW_ERROR("Locale: " #VARIABLE "(): Invalid arguments"); \ + return; \ + if (callData->argc < 1 || callData->argc > 2) \ + THROW_ERROR("Locale: " #VARIABLE "(): Invalid arguments"); \ QLocale::FormatType enumFormat = QLocale::LongFormat; \ - int idx = ctx->args()[0].toInt32(); \ + int idx = callData->args[0].toInt32(); \ if (idx < 0 || idx > 7) \ - V4THROW_ERROR("Locale: Invalid day"); \ + THROW_ERROR("Locale: Invalid day"); \ if (idx == 0) idx = 7; \ QString name; \ - if (ctx->argc() == 2) { \ - if (ctx->args()[1].isNumber()) { \ - quint32 intFormat = ctx->args()[1].toUInt32(); \ + if (callData->argc == 2) { \ + if (callData->args[1].isNumber()) { \ + quint32 intFormat = callData->args[1].toUInt32(); \ QLocale::FormatType format = QLocale::FormatType(intFormat); \ name = locale-> VARIABLE(idx, format); \ } else { \ - V4THROW_ERROR("Locale: Invalid datetime format"); \ + THROW_ERROR("Locale: Invalid datetime format"); \ } \ } else { \ name = locale-> VARIABLE(idx, enumFormat); \ } \ - return ctx->engine()->newString(name)->asReturnedValue(); \ + scope.result = scope.engine->newString(name); \ } LOCALE_FORMATTED_MONTHNAME(monthName) @@ -634,12 +637,12 @@ LOCALE_FORMATTED_MONTHNAME(standaloneMonthName) LOCALE_FORMATTED_DAYNAME(dayName) LOCALE_FORMATTED_DAYNAME(standaloneDayName) -#define LOCALE_STRING_PROPERTY(VARIABLE) QV4::ReturnedValue QQmlLocaleData::method_get_ ## VARIABLE (QV4::CallContext* ctx) \ +#define LOCALE_STRING_PROPERTY(VARIABLE) void QQmlLocaleData::method_get_ ## VARIABLE (const BuiltinFunction *, Scope &scope, CallData *callData) \ { \ - QLocale *locale = getThisLocale(ctx); \ + QLocale *locale = getThisLocale(scope, callData); \ if (!locale) \ - return QV4::Encode::undefined(); \ - return ctx->engine()->newString(locale-> VARIABLE())->asReturnedValue();\ + return; \ + scope.result = scope.engine->newString(locale-> VARIABLE());\ } LOCALE_STRING_PROPERTY(name) @@ -830,18 +833,22 @@ void QQmlLocale::registerStringLocaleCompare(QV4::ExecutionEngine *engine) engine->stringPrototype()->defineDefaultProperty(QStringLiteral("localeCompare"), method_localeCompare); } -QV4::ReturnedValue QQmlLocale::method_localeCompare(QV4::CallContext *ctx) +void QQmlLocale::method_localeCompare(const BuiltinFunction *b, Scope &scope, CallData *callData) { - if (ctx->argc() != 1 || (!ctx->args()[0].isString() && !ctx->args()[0].as<StringObject>())) - return QV4::StringPrototype::method_localeCompare(ctx); + if (callData->argc != 1 || (!callData->args[0].isString() && !callData->args[0].as<StringObject>())) { + QV4::StringPrototype::method_localeCompare(b, scope, callData); + return; + } - if (!ctx->thisObject().isString() && !ctx->thisObject().as<StringObject>()) - return QV4::StringPrototype::method_localeCompare(ctx); + if (!callData->thisObject.isString() && !callData->thisObject.as<StringObject>()) { + QV4::StringPrototype::method_localeCompare(b, scope, callData); + return; + } - QString thisString = ctx->thisObject().toQStringNoThrow(); - QString thatString = ctx->args()[0].toQStringNoThrow(); + QString thisString = callData->thisObject.toQStringNoThrow(); + QString thatString = callData->args[0].toQStringNoThrow(); - return QV4::Encode(QString::localeAwareCompare(thisString, thatString)); + scope.result = QV4::Encode(QString::localeAwareCompare(thisString, thatString)); } /*! diff --git a/src/qml/qml/qqmllocale_p.h b/src/qml/qml/qqmllocale_p.h index 275f58db7d..1a2ffc72b0 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 QV4::ReturnedValue method_toLocaleString(QV4::CallContext *ctx); - static QV4::ReturnedValue method_toLocaleTimeString(QV4::CallContext *ctx); - static QV4::ReturnedValue method_toLocaleDateString(QV4::CallContext *ctx); - static QV4::ReturnedValue method_fromLocaleString(QV4::CallContext *ctx); - static QV4::ReturnedValue method_fromLocaleTimeString(QV4::CallContext *ctx); - static QV4::ReturnedValue method_fromLocaleDateString(QV4::CallContext *ctx); - static QV4::ReturnedValue method_timeZoneUpdated(QV4::CallContext *ctx); + 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); }; @@ -83,9 +83,9 @@ public: static void registerExtension(QV4::ExecutionEngine *engine); private: - static QV4::ReturnedValue method_toLocaleString(QV4::CallContext *ctx); - static QV4::ReturnedValue method_fromLocaleString(QV4::CallContext *ctx); - static QV4::ReturnedValue method_toLocaleCurrencyString(QV4::CallContext *ctx); + 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); }; @@ -135,7 +135,7 @@ public: private: QQmlLocale(); - static QV4::ReturnedValue method_localeCompare(QV4::CallContext *ctx); + static void method_localeCompare(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); }; namespace QV4 { @@ -158,43 +158,43 @@ struct QQmlLocaleData : public QV4::Object V4_OBJECT2(QQmlLocaleData, Object) V4_NEEDS_DESTROY - static QLocale *getThisLocale(QV4::CallContext *ctx) { - QV4::Object *o = ctx->thisObject().as<Object>(); + static QLocale *getThisLocale(QV4::Scope &scope, QV4::CallData *callData) { + QV4::Object *o = callData->thisObject.as<Object>(); QQmlLocaleData *thisObject = o ? o->as<QQmlLocaleData>() : 0; if (!thisObject) { - ctx->engine()->throwTypeError(); + scope.engine->throwTypeError(); return 0; } return thisObject->d()->locale; } - static QV4::ReturnedValue method_currencySymbol(QV4::CallContext *ctx); - static QV4::ReturnedValue method_dateTimeFormat(QV4::CallContext *ctx); - static QV4::ReturnedValue method_timeFormat(QV4::CallContext *ctx); - static QV4::ReturnedValue method_dateFormat(QV4::CallContext *ctx); - static QV4::ReturnedValue method_monthName(QV4::CallContext *ctx); - static QV4::ReturnedValue method_standaloneMonthName(QV4::CallContext *ctx); - static QV4::ReturnedValue method_dayName(QV4::CallContext *ctx); - static QV4::ReturnedValue method_standaloneDayName(QV4::CallContext *ctx); - - static QV4::ReturnedValue method_get_firstDayOfWeek(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_measurementSystem(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_textDirection(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_weekDays(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_uiLanguages(QV4::CallContext *ctx); - - static QV4::ReturnedValue method_get_name(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_nativeLanguageName(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_nativeCountryName(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_decimalPoint(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_groupSeparator(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_percent(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_zeroDigit(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_negativeSign(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_positiveSign(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_exponential(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_amText(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_pmText(QV4::CallContext *ctx); + 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); }; } diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 520c44f4da..bd6b9a1599 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -79,7 +79,7 @@ struct QQmlMetaTypeData Files urlToNonFileImportType; // For non-file imported composite and composite // singleton types. This way we can locate any // of them by url, even if it was registered as - // a module via qmlRegisterCompositeType. + // a module via QQmlPrivate::RegisterCompositeType typedef QHash<const QMetaObject *, QQmlType *> MetaObjects; MetaObjects metaObjectToType; typedef QHash<int, QQmlMetaType::StringConverter> StringConverters; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 38a16b8cde..85fbd86dc4 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -170,7 +170,7 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI context = new QQmlContextData; context->isInternal = true; - context->imports = compilationUnit->importCache; + context->imports = compilationUnit->typeNameCache; context->initFromTypeCompilationUnit(compilationUnit, subComponentIndex); context->setParent(parentContext); @@ -1150,7 +1150,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo if (customParser && obj->flags & QV4::CompiledData::Object::HasCustomParserBindings) { customParser->engine = QQmlEnginePrivate::get(engine); - customParser->imports = compilationUnit->importCache; + customParser->imports = compilationUnit->typeNameCache; QList<const QV4::CompiledData::Binding *> bindings; const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index); diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 562aa1c88a..88ce2fa1b9 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -76,7 +76,8 @@ public: int argumentsValid:1; QList<QByteArray> *names; - int arguments[0]; + + int arguments[1]; }; // Flags that do *NOT* depend on the property's QMetaProperty::userType() and thus are quick @@ -919,7 +920,7 @@ static int EnumType(const QMetaObject *metaobj, const QByteArray &str, int type) QQmlPropertyCacheMethodArguments *QQmlPropertyCache::createArgumentsObject(int argc, const QList<QByteArray> &names) { typedef QQmlPropertyCacheMethodArguments A; - A *args = static_cast<A *>(malloc(sizeof(A) + (argc + 1) * sizeof(int))); + A *args = static_cast<A *>(malloc(sizeof(A) + (argc) * sizeof(int))); args->arguments[0] = argc; args->argumentsValid = false; args->signalParameterStringForJS = 0; diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 5b1bba46dd..f4f04e12c0 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1362,7 +1362,7 @@ bool QQmlTypeLoader::Blob::updateQmldir(QQmlQmldirData *data, const QV4::Compile if (!importQualifier.isEmpty()) { // Does this library contain any qualified scripts? QUrl libraryUrl(qmldirUrl); - const QmldirContent *qmldir = typeLoader()->qmldirContent(qmldirIdentifier); + const QQmlTypeLoaderQmldirContent *qmldir = typeLoader()->qmldirContent(qmldirIdentifier); const auto qmldirScripts = qmldir->scripts(); for (const QQmlDirParser::Script &script : qmldirScripts) { QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName)); @@ -1410,7 +1410,7 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL if (!importQualifier.isEmpty()) { // Does this library contain any qualified scripts? QUrl libraryUrl(qmldirUrl); - const QmldirContent *qmldir = typeLoader()->qmldirContent(qmldirFilePath); + const QQmlTypeLoaderQmldirContent *qmldir = typeLoader()->qmldirContent(qmldirFilePath); const auto qmldirScripts = qmldir->scripts(); for (const QQmlDirParser::Script &script : qmldirScripts) { QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName)); @@ -1539,57 +1539,57 @@ bool QQmlTypeLoader::Blob::qmldirDataAvailable(QQmlQmldirData *data, QList<QQmlE } -QQmlTypeLoader::QmldirContent::QmldirContent() +QQmlTypeLoaderQmldirContent::QQmlTypeLoaderQmldirContent() { } -bool QQmlTypeLoader::QmldirContent::hasError() const +bool QQmlTypeLoaderQmldirContent::hasError() const { return m_parser.hasError(); } -QList<QQmlError> QQmlTypeLoader::QmldirContent::errors(const QString &uri) const +QList<QQmlError> QQmlTypeLoaderQmldirContent::errors(const QString &uri) const { return m_parser.errors(uri); } -QString QQmlTypeLoader::QmldirContent::typeNamespace() const +QString QQmlTypeLoaderQmldirContent::typeNamespace() const { return m_parser.typeNamespace(); } -void QQmlTypeLoader::QmldirContent::setContent(const QString &location, const QString &content) +void QQmlTypeLoaderQmldirContent::setContent(const QString &location, const QString &content) { m_location = location; m_parser.parse(content); } -void QQmlTypeLoader::QmldirContent::setError(const QQmlError &error) +void QQmlTypeLoaderQmldirContent::setError(const QQmlError &error) { m_parser.setError(error); } -QQmlDirComponents QQmlTypeLoader::QmldirContent::components() const +QQmlDirComponents QQmlTypeLoaderQmldirContent::components() const { return m_parser.components(); } -QQmlDirScripts QQmlTypeLoader::QmldirContent::scripts() const +QQmlDirScripts QQmlTypeLoaderQmldirContent::scripts() const { return m_parser.scripts(); } -QQmlDirPlugins QQmlTypeLoader::QmldirContent::plugins() const +QQmlDirPlugins QQmlTypeLoaderQmldirContent::plugins() const { return m_parser.plugins(); } -QString QQmlTypeLoader::QmldirContent::pluginLocation() const +QString QQmlTypeLoaderQmldirContent::pluginLocation() const { return m_location; } -bool QQmlTypeLoader::QmldirContent::designerSupported() const +bool QQmlTypeLoaderQmldirContent::designerSupported() const { return m_parser.designerSupported(); } @@ -1861,13 +1861,13 @@ bool QQmlTypeLoader::directoryExists(const QString &path) /*! -Return a QmldirContent for absoluteFilePath. The QmldirContent may be cached. +Return a QQmlTypeLoaderQmldirContent for absoluteFilePath. The QQmlTypeLoaderQmldirContent may be cached. \a filePath is a local file path. It can also be a remote path for a remote directory import, but it will have been cached by now in this case. */ -const QQmlTypeLoader::QmldirContent *QQmlTypeLoader::qmldirContent(const QString &filePathIn) +const QQmlTypeLoaderQmldirContent *QQmlTypeLoader::qmldirContent(const QString &filePathIn) { QUrl url(filePathIn); //May already contain http scheme if (url.scheme() == QLatin1String("http") || url.scheme() == QLatin1String("https")) @@ -1883,10 +1883,10 @@ const QQmlTypeLoader::QmldirContent *QQmlTypeLoader::qmldirContent(const QString else filePath = url.path(); - QmldirContent *qmldir; - QmldirContent **val = m_importQmlDirCache.value(filePath); + QQmlTypeLoaderQmldirContent *qmldir; + QQmlTypeLoaderQmldirContent **val = m_importQmlDirCache.value(filePath); if (!val) { - qmldir = new QmldirContent; + qmldir = new QQmlTypeLoaderQmldirContent; #define ERROR(description) { QQmlError e; e.setDescription(description); qmldir->setError(e); } #define NOT_READABLE_ERROR QString(QLatin1String("module \"$$URI$$\" definition \"%1\" not readable")) @@ -1916,12 +1916,12 @@ const QQmlTypeLoader::QmldirContent *QQmlTypeLoader::qmldirContent(const QString void QQmlTypeLoader::setQmldirContent(const QString &url, const QString &content) { - QmldirContent *qmldir; - QmldirContent **val = m_importQmlDirCache.value(url); + QQmlTypeLoaderQmldirContent *qmldir; + QQmlTypeLoaderQmldirContent **val = m_importQmlDirCache.value(url); if (val) { qmldir = *val; } else { - qmldir = new QmldirContent; + qmldir = new QQmlTypeLoaderQmldirContent; m_importQmlDirCache.insert(url, qmldir); } @@ -2075,6 +2075,11 @@ bool QQmlTypeData::tryLoadFromDiskCache() } } + if (unit->data->flags & QV4::CompiledData::Unit::PendingTypeCompilation) { + restoreIR(unit); + return true; + } + m_compiledData = unit; for (int i = 0, count = m_compiledData->objectCount(); i < count; ++i) @@ -2127,11 +2132,11 @@ bool QQmlTypeData::tryLoadFromDiskCache() return true; } -void QQmlTypeData::createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &importCache, +void QQmlTypeData::createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache) { Q_ASSERT(m_compiledData); - m_compiledData->importCache = importCache; + m_compiledData->typeNameCache = typeNameCache; m_compiledData->resolvedTypes = resolvedTypeCache; QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); @@ -2217,10 +2222,10 @@ void QQmlTypeData::done() } } - QQmlRefPointer<QQmlTypeNameCache> importCache; + QQmlRefPointer<QQmlTypeNameCache> typeNameCache; QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypeCache; { - QQmlCompileError error = buildTypeResolutionCaches(&importCache, &resolvedTypeCache); + QQmlCompileError error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache); if (error.isSet()) { setError(error); return; @@ -2240,9 +2245,9 @@ void QQmlTypeData::done() if (!m_document.isNull()) { // Compile component - compile(importCache, resolvedTypeCache); + compile(typeNameCache, resolvedTypeCache); } else { - createTypeAndPropertyCaches(importCache, resolvedTypeCache); + createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache); } if (isError()) @@ -2303,7 +2308,7 @@ void QQmlTypeData::done() qualifier = qualifier.mid(lastDotIndex+1); } - m_compiledData->importCache->add(qualifier.toString(), scriptIndex, enclosingNamespace); + m_compiledData->typeNameCache->add(qualifier.toString(), scriptIndex, enclosingNamespace); QQmlScriptData *scriptData = script.script->scriptData(); scriptData->addref(); m_compiledData->dependentScripts << scriptData; @@ -2397,6 +2402,15 @@ bool QQmlTypeData::loadFromSource() return true; } +void QQmlTypeData::restoreIR(QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit) +{ + m_document.reset(new QmlIR::Document(isDebugging())); + QmlIR::IRLoader loader(unit->data, m_document.data()); + loader.load(); + m_document->javaScriptCompilationUnit = unit; + continueLoadFromIR(); +} + void QQmlTypeData::continueLoadFromIR() { m_typeReferences.collectFromObjects(m_document->objects.constBegin(), m_document->objects.constEnd()); @@ -2489,12 +2503,12 @@ QString QQmlTypeData::stringAt(int index) const return m_document->jsGenerator.stringTable.stringForIndex(index); } -void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &importCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache) +void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache) { Q_ASSERT(m_compiledData.isNull()); QQmlEnginePrivate * const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine()); - QQmlTypeCompiler compiler(enginePrivate, this, m_document.data(), importCache, resolvedTypeCache); + QQmlTypeCompiler compiler(enginePrivate, this, m_document.data(), typeNameCache, resolvedTypeCache); m_compiledData = compiler.compile(); if (!m_compiledData) { setError(compiler.compilationErrors()); @@ -2598,20 +2612,20 @@ void QQmlTypeData::resolveTypes() } QQmlCompileError QQmlTypeData::buildTypeResolutionCaches( - QQmlRefPointer<QQmlTypeNameCache> *importCache, + QQmlRefPointer<QQmlTypeNameCache> *typeNameCache, QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache ) const { - importCache->adopt(new QQmlTypeNameCache); + typeNameCache->adopt(new QQmlTypeNameCache(m_importCache)); for (const QString &ns: m_namespaces) - (*importCache)->add(ns); + (*typeNameCache)->add(ns); // Add any Composite Singletons that were used to the import cache for (const QQmlTypeData::TypeReference &singleton: m_compositeSingletons) - (*importCache)->add(singleton.type->qmlTypeName(), singleton.type->sourceUrl(), singleton.prefix); + (*typeNameCache)->add(singleton.type->qmlTypeName(), singleton.type->sourceUrl(), singleton.prefix); - m_importCache.populateCache(*importCache); + m_importCache.populateCache(*typeNameCache); QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); @@ -2710,7 +2724,7 @@ void QQmlTypeData::scriptImported(QQmlScriptBlob *blob, const QV4::CompiledData: } QQmlScriptData::QQmlScriptData() - : importCache(0) + : typeNameCache(0) , m_loaded(false) , m_program(0) { @@ -2767,8 +2781,8 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent // For backward compatibility, if there are no imports, we need to use the // imports from the parent context. See QTBUG-17518. - if (!importCache->isEmpty()) { - ctxt->imports = importCache; + if (!typeNameCache->isEmpty()) { + ctxt->imports = typeNameCache; } else if (effectiveCtxt) { ctxt->imports = effectiveCtxt->imports; ctxt->importedScripts = effectiveCtxt->importedScripts; @@ -2823,9 +2837,9 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent void QQmlScriptData::clear() { - if (importCache) { - importCache->release(); - importCache = 0; + if (typeNameCache) { + typeNameCache->release(); + typeNameCache = 0; } for (int ii = 0; ii < scripts.count(); ++ii) @@ -2946,7 +2960,7 @@ void QQmlScriptBlob::done() } } - m_scriptData->importCache = new QQmlTypeNameCache(); + m_scriptData->typeNameCache = new QQmlTypeNameCache(m_importCache); QSet<QString> ns; @@ -2958,13 +2972,13 @@ void QQmlScriptBlob::done() if (!script.nameSpace.isNull()) { if (!ns.contains(script.nameSpace)) { ns.insert(script.nameSpace); - m_scriptData->importCache->add(script.nameSpace); + m_scriptData->typeNameCache->add(script.nameSpace); } } - m_scriptData->importCache->add(script.qualifier, scriptIndex, script.nameSpace); + m_scriptData->typeNameCache->add(script.qualifier, scriptIndex, script.nameSpace); } - m_importCache.populateCache(m_scriptData->importCache); + m_importCache.populateCache(m_scriptData->typeNameCache); } QString QQmlScriptBlob::stringAt(int index) const diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index c60435a2d6..915b1bcc4c 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -218,6 +218,34 @@ private: class QQmlTypeLoaderThread; +class QQmlTypeLoaderQmldirContent +{ +private: + friend class QQmlTypeLoader; + QQmlTypeLoaderQmldirContent(); + + void setContent(const QString &location, const QString &content); + void setError(const QQmlError &); + +public: + bool hasError() const; + QList<QQmlError> errors(const QString &uri) const; + + QString typeNamespace() const; + + QQmlDirComponents components() const; + QQmlDirScripts scripts() const; + QQmlDirPlugins plugins() const; + + QString pluginLocation() const; + + bool designerSupported() const; + +private: + QQmlDirParser m_parser; + QString m_location; +}; + class Q_AUTOTEST_EXPORT QQmlTypeLoader { Q_DECLARE_TR_FUNCTIONS(QQmlTypeLoader) @@ -256,34 +284,6 @@ public: QList<QQmlQmldirData *> m_qmldirs; }; - class QmldirContent - { - private: - friend class QQmlTypeLoader; - QmldirContent(); - - void setContent(const QString &location, const QString &content); - void setError(const QQmlError &); - - public: - bool hasError() const; - QList<QQmlError> errors(const QString &uri) const; - - QString typeNamespace() const; - - QQmlDirComponents components() const; - QQmlDirScripts scripts() const; - QQmlDirPlugins plugins() const; - - QString pluginLocation() const; - - bool designerSupported() const; - - private: - QQmlDirParser m_parser; - QString m_location; - }; - QQmlTypeLoader(QQmlEngine *); ~QQmlTypeLoader(); @@ -298,7 +298,7 @@ public: QString absoluteFilePath(const QString &path); bool directoryExists(const QString &path); - const QmldirContent *qmldirContent(const QString &filePath); + const QQmlTypeLoaderQmldirContent *qmldirContent(const QString &filePath); void setQmldirContent(const QString &filePath, const QString &content); void clearCache(); @@ -363,7 +363,7 @@ private: typedef QHash<QUrl, QQmlQmldirData *> QmldirCache; typedef QStringHash<bool> StringSet; typedef QStringHash<StringSet*> ImportDirCache; - typedef QStringHash<QmldirContent *> ImportQmlDirCache; + typedef QStringHash<QQmlTypeLoaderQmldirContent *> ImportQmlDirCache; QQmlEngine *m_engine; QQmlTypeLoaderThread *m_thread; @@ -446,15 +446,16 @@ protected: private: bool tryLoadFromDiskCache(); bool loadFromSource(); + void restoreIR(QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit); void continueLoadFromIR(); void resolveTypes(); QQmlCompileError buildTypeResolutionCaches( - QQmlRefPointer<QQmlTypeNameCache> *importCache, + QQmlRefPointer<QQmlTypeNameCache> *typeNameCache, QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache ) const; - void compile(const QQmlRefPointer<QQmlTypeNameCache> &importCache, + void compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache); - void createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &importCache, + void createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache); bool resolveType(const QString &typeName, int &majorVersion, int &minorVersion, TypeReference &ref, int lineNumber = -1, int columnNumber = -1, bool reportErrors = true); @@ -504,7 +505,7 @@ public: QUrl url; QString urlString; - QQmlTypeNameCache *importCache; + QQmlTypeNameCache *typeNameCache; QList<QQmlScriptBlob *> scripts; QV4::ReturnedValue scriptValueForContext(QQmlContextData *parentCtxt); diff --git a/src/qml/qml/qqmltypenamecache.cpp b/src/qml/qml/qqmltypenamecache.cpp index c2098bc9a1..c8e2b92c29 100644 --- a/src/qml/qml/qqmltypenamecache.cpp +++ b/src/qml/qml/qqmltypenamecache.cpp @@ -43,7 +43,8 @@ QT_BEGIN_NAMESPACE -QQmlTypeNameCache::QQmlTypeNameCache() +QQmlTypeNameCache::QQmlTypeNameCache(const QQmlImports &importCache) + : m_imports(importCache) { } @@ -70,6 +71,7 @@ void QQmlTypeNameCache::add(const QHashedString &name, int importedScriptIndex, { Import import; import.scriptIndex = importedScriptIndex; + import.m_qualifier = name; if (nameSpace.length() != 0) { Import *i = m_namedImports.value(nameSpace); @@ -94,6 +96,18 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QHashedStringRef &name) if (!result.isValid()) result = query(m_anonymousCompositeSingletons, name); + if (!result.isValid()) { + // Look up anonymous types from the imports of this document + QQmlImportNamespace *typeNamespace = 0; + QList<QQmlError> errors; + QQmlType *t = 0; + bool typeFound = m_imports.resolveType(name, &t, 0, 0, &typeNamespace, &errors); + if (typeFound) { + return Result(t); + } + + } + return result; } @@ -109,6 +123,20 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QHashedStringRef &name, if (!result.isValid()) result = query(i->compositeSingletons, name); + if (!result.isValid()) { + // Look up types from the imports of this document + // ### it would be nice if QQmlImports allowed us to resolve a namespace + // first, and then types on it. + QString qualifiedTypeName = i->m_qualifier + QLatin1Char('.') + name.toString(); + QQmlImportNamespace *typeNamespace = 0; + QList<QQmlError> errors; + QQmlType *t = 0; + bool typeFound = m_imports.resolveType(qualifiedTypeName, &t, 0, 0, &typeNamespace, &errors); + if (typeFound) { + return Result(t); + } + } + return result; } @@ -122,6 +150,19 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QV4::String *name) cons if (!result.isValid()) result = query(m_anonymousCompositeSingletons, name); + if (!result.isValid()) { + // Look up anonymous types from the imports of this document + QString typeName = name->toQStringNoThrow(); + QQmlImportNamespace *typeNamespace = 0; + QList<QQmlError> errors; + QQmlType *t = 0; + bool typeFound = m_imports.resolveType(typeName, &t, 0, 0, &typeNamespace, &errors); + if (typeFound) { + return Result(t); + } + + } + return result; } @@ -143,6 +184,20 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QV4::String *name, cons if (!r.isValid()) r = query(i->compositeSingletons, name); + if (!r.isValid()) { + // Look up types from the imports of this document + // ### it would be nice if QQmlImports allowed us to resolve a namespace + // first, and then types on it. + QString qualifiedTypeName = i->m_qualifier + QLatin1Char('.') + name->toQStringNoThrow(); + QQmlImportNamespace *typeNamespace = 0; + QList<QQmlError> errors; + QQmlType *t = 0; + bool typeFound = m_imports.resolveType(qualifiedTypeName, &t, 0, 0, &typeNamespace, &errors); + if (typeFound) { + return Result(t); + } + } + return r; } diff --git a/src/qml/qml/qqmltypenamecache_p.h b/src/qml/qml/qqmltypenamecache_p.h index 8a387bed5f..7cdcbe91b6 100644 --- a/src/qml/qml/qqmltypenamecache_p.h +++ b/src/qml/qml/qqmltypenamecache_p.h @@ -56,6 +56,7 @@ #include "qqmlmetatype_p.h" #include <private/qhashedstring_p.h> +#include <private/qqmlimport_p.h> #include <QtCore/qvector.h> @@ -66,7 +67,7 @@ class QQmlEngine; class QQmlTypeNameCache : public QQmlRefCount { public: - QQmlTypeNameCache(); + QQmlTypeNameCache(const QQmlImports &imports); virtual ~QQmlTypeNameCache(); inline bool isEmpty() const; @@ -105,6 +106,9 @@ private: // Or, imported compositeSingletons QStringHash<QUrl> compositeSingletons; + + // The qualifier of this import + QString m_qualifier; }; template<typename Key> @@ -112,6 +116,7 @@ private: { Import *i = imports.value(key); if (i) { + Q_ASSERT(!i->m_qualifier.isEmpty()); if (i->scriptIndex != -1) { return Result(i->scriptIndex); } else { @@ -151,6 +156,7 @@ private: QMap<const Import *, QStringHash<Import> > m_namespacedImports; QVector<QQmlTypeModuleVersion> m_anonymousImports; QStringHash<QUrl> m_anonymousCompositeSingletons; + QQmlImports m_imports; }; QQmlTypeNameCache::Result::Result() diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index 6ce52bb9e5..44b612e7d2 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -312,18 +312,18 @@ bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const return true; } -ReturnedValue QQmlValueTypeWrapper::method_toString(CallContext *ctx) +void QQmlValueTypeWrapper::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) { - Object *o = ctx->thisObject().as<Object>(); + Object *o = callData->thisObject.as<Object>(); if (!o) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); QQmlValueTypeWrapper *w = o->as<QQmlValueTypeWrapper>(); if (!w) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); if (QQmlValueTypeReference *ref = w->as<QQmlValueTypeReference>()) if (!ref->readReferenceValue()) - return Encode::undefined(); + RETURN_UNDEFINED(); QString result; // Prepare a buffer to pass to QMetaType::convert() @@ -346,7 +346,7 @@ ReturnedValue QQmlValueTypeWrapper::method_toString(CallContext *ctx) } result += QLatin1Char(')'); } - return Encode(ctx->engine()->newString(result)); + scope.result = scope.engine->newString(result); } ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *hasProperty) diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h index fec54df770..87f9116056 100644 --- a/src/qml/qml/qqmlvaluetypewrapper_p.h +++ b/src/qml/qml/qqmlvaluetypewrapper_p.h @@ -111,7 +111,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 QV4::ReturnedValue method_toString(CallContext *ctx); + static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); static void initProto(ExecutionEngine *v4); }; diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 545daa96f8..490a4e19ab 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -325,9 +325,12 @@ QQmlVMEMetaObject::QQmlVMEMetaObject(QObject *obj, if (compiledObject->nProperties || compiledObject->nFunctions) { Q_ASSERT(cache && cache->engine); QV4::ExecutionEngine *v4 = cache->engine; - QV4::Heap::MemberData *data = QV4::MemberData::allocate(v4, compiledObject->nProperties + compiledObject->nFunctions); - propertyAndMethodStorage.set(v4, data); - std::fill(data->data, data->data + data->size, QV4::Encode::undefined()); + uint size = compiledObject->nProperties + compiledObject->nFunctions; + if (size) { + QV4::Heap::MemberData *data = QV4::MemberData::allocate(v4, size); + propertyAndMethodStorage.set(v4, data); + std::fill(data->data, data->data + data->size, QV4::Encode::undefined()); + } // Need JS wrapper to ensure properties/methods are marked. ensureQObjectWrapper(); diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 22c3c49c58..d0d9f080da 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -71,10 +71,12 @@ using namespace QV4; #if QT_CONFIG(xmlstreamreader) && QT_CONFIG(qml_network) -#define V4THROW_REFERENCE(string) { \ - ScopedObject error(scope, ctx->engine()->newReferenceErrorObject(QStringLiteral(string))); \ - return ctx->engine()->throwError(error); \ - } +#define V4THROW_REFERENCE(string) \ + do { \ + ScopedObject error(scope, scope.engine->newReferenceErrorObject(QStringLiteral(string))); \ + scope.result = scope.engine->throwError(error); \ + return; \ + } while (false) QT_BEGIN_NAMESPACE @@ -274,25 +276,25 @@ public: static void initClass(ExecutionEngine *engine); // JS API - static ReturnedValue method_get_nodeName(CallContext *ctx); - static ReturnedValue method_get_nodeValue(CallContext *ctx); - static ReturnedValue method_get_nodeType(CallContext *ctx); - static ReturnedValue method_get_namespaceUri(CallContext *ctx); - - static ReturnedValue method_get_parentNode(CallContext *ctx); - static ReturnedValue method_get_childNodes(CallContext *ctx); - static ReturnedValue method_get_firstChild(CallContext *ctx); - static ReturnedValue method_get_lastChild(CallContext *ctx); - static ReturnedValue method_get_previousSibling(CallContext *ctx); - static ReturnedValue method_get_nextSibling(CallContext *ctx); - static ReturnedValue method_get_attributes(CallContext *ctx); - - //static ReturnedValue ownerDocument(CallContext *ctx); - //static ReturnedValue namespaceURI(CallContext *ctx); - //static ReturnedValue prefix(CallContext *ctx); - //static ReturnedValue localName(CallContext *ctx); - //static ReturnedValue baseURI(CallContext *ctx); - //static ReturnedValue textContent(CallContext *ctx); + 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 getProto(ExecutionEngine *v4); @@ -353,12 +355,12 @@ class Attr : public Node { public: // JS API - static ReturnedValue method_name(CallContext *ctx); -// static ReturnedValue specified(CallContext *); - static ReturnedValue method_value(CallContext *ctx); - static ReturnedValue method_ownerElement(CallContext *ctx); -// static ReturnedValue schemaTypeInfo(CallContext *); -// static ReturnedValue isId(CallContext *c); + static void method_name(const QV4::BuiltinFunction *, QV4::Scope &scope, 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 void schemaTypeInfo(CallContext *); +// static void isId(CallContext *c); // C++ API static ReturnedValue prototype(ExecutionEngine *); @@ -368,7 +370,7 @@ class CharacterData : public Node { public: // JS API - static ReturnedValue method_length(CallContext *ctx); + static void method_length(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); // C++ API static ReturnedValue prototype(ExecutionEngine *v4); @@ -378,8 +380,8 @@ class Text : public CharacterData { public: // JS API - static ReturnedValue method_isElementContentWhitespace(CallContext *ctx); - static ReturnedValue method_wholeText(CallContext *ctx); + 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); // C++ API static ReturnedValue prototype(ExecutionEngine *); @@ -396,10 +398,10 @@ class Document : public Node { public: // JS API - static ReturnedValue method_xmlVersion(CallContext *ctx); - static ReturnedValue method_xmlEncoding(CallContext *ctx); - static ReturnedValue method_xmlStandalone(CallContext *ctx); - static ReturnedValue method_documentElement(CallContext *ctx); + 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); // C++ API static ReturnedValue prototype(ExecutionEngine *); @@ -418,12 +420,11 @@ void NodeImpl::release() document->release(); } -ReturnedValue NodePrototype::method_get_nodeName(CallContext *ctx) +void NodePrototype::method_get_nodeName(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<Node> r(scope, ctx->thisObject().as<Node>()); + Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); QString name; switch (r->d()->d->type) { @@ -440,15 +441,14 @@ ReturnedValue NodePrototype::method_get_nodeName(CallContext *ctx) name = r->d()->d->name; break; } - return Encode(ctx->d()->engine->newString(name)); + scope.result = Encode(scope.engine->newString(name)); } -ReturnedValue NodePrototype::method_get_nodeValue(CallContext *ctx) +void NodePrototype::method_get_nodeValue(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<Node> r(scope, ctx->thisObject().as<Node>()); + Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); if (r->d()->d->type == NodeImpl::Document || r->d()->d->type == NodeImpl::DocumentFragment || @@ -457,135 +457,128 @@ ReturnedValue NodePrototype::method_get_nodeValue(CallContext *ctx) r->d()->d->type == NodeImpl::Entity || r->d()->d->type == NodeImpl::EntityReference || r->d()->d->type == NodeImpl::Notation) - return Encode::null(); + RETURN_RESULT(Encode::null()); - return Encode(ctx->d()->engine->newString(r->d()->d->data)); + scope.result = Encode(scope.engine->newString(r->d()->d->data)); } -ReturnedValue NodePrototype::method_get_nodeType(CallContext *ctx) +void NodePrototype::method_get_nodeType(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<Node> r(scope, ctx->thisObject().as<Node>()); + Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - return Encode(r->d()->d->type); + scope.result = Encode(r->d()->d->type); } -ReturnedValue NodePrototype::method_get_namespaceUri(CallContext *ctx) +void NodePrototype::method_get_namespaceUri(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<Node> r(scope, ctx->thisObject().as<Node>()); + Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - return Encode(ctx->d()->engine->newString(r->d()->d->namespaceUri)); + scope.result = Encode(scope.engine->newString(r->d()->d->namespaceUri)); } -ReturnedValue NodePrototype::method_get_parentNode(CallContext *ctx) +void NodePrototype::method_get_parentNode(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<Node> r(scope, ctx->thisObject().as<Node>()); + Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); if (r->d()->d->parent) - return Node::create(scope.engine, r->d()->d->parent); + scope.result = Node::create(scope.engine, r->d()->d->parent); else - return Encode::null(); + scope.result = Encode::null(); } -ReturnedValue NodePrototype::method_get_childNodes(CallContext *ctx) +void NodePrototype::method_get_childNodes(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<Node> r(scope, ctx->thisObject().as<Node>()); + Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); - return NodeList::create(scope.engine, r->d()->d); + scope.result = NodeList::create(scope.engine, r->d()->d); } -ReturnedValue NodePrototype::method_get_firstChild(CallContext *ctx) +void NodePrototype::method_get_firstChild(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<Node> r(scope, ctx->thisObject().as<Node>()); + Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); if (r->d()->d->children.isEmpty()) - return Encode::null(); + scope.result = Encode::null(); else - return Node::create(scope.engine, r->d()->d->children.constFirst()); + scope.result = Node::create(scope.engine, r->d()->d->children.constFirst()); } -ReturnedValue NodePrototype::method_get_lastChild(CallContext *ctx) +void NodePrototype::method_get_lastChild(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<Node> r(scope, ctx->thisObject().as<Node>()); + Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); if (r->d()->d->children.isEmpty()) - return Encode::null(); + scope.result = Encode::null(); else - return Node::create(scope.engine, r->d()->d->children.constLast()); + scope.result = Node::create(scope.engine, r->d()->d->children.constLast()); } -ReturnedValue NodePrototype::method_get_previousSibling(CallContext *ctx) +void NodePrototype::method_get_previousSibling(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<Node> r(scope, ctx->thisObject().as<Node>()); + Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); if (!r->d()->d->parent) - return Encode::null(); + RETURN_RESULT(Encode::null()); 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) - return Encode::null(); + scope.result = Encode::null(); else - return Node::create(scope.engine, r->d()->d->parent->children.at(ii - 1)); + scope.result = Node::create(scope.engine, r->d()->d->parent->children.at(ii - 1)); + return; } } - return Encode::null(); + scope.result = Encode::null(); } -ReturnedValue NodePrototype::method_get_nextSibling(CallContext *ctx) +void NodePrototype::method_get_nextSibling(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<Node> r(scope, ctx->thisObject().as<Node>()); + Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); if (!r->d()->d->parent) - return Encode::null(); + RETURN_RESULT(Encode::null()); 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()) - return Encode::null(); + scope.result = Encode::null(); else - return Node::create(scope.engine, r->d()->d->parent->children.at(ii + 1)); + scope.result = Node::create(scope.engine, r->d()->d->parent->children.at(ii + 1)); + return; } } - return Encode::null(); + scope.result = Encode::null(); } -ReturnedValue NodePrototype::method_get_attributes(CallContext *ctx) +void NodePrototype::method_get_attributes(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<Node> r(scope, ctx->thisObject().as<Node>()); + Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); if (r->d()->d->type != NodeImpl::Element) - return Encode::null(); + scope.result = Encode::null(); else - return NamedNodeMap::create(scope.engine, r->d()->d, r->d()->d->attributes); + scope.result = NamedNodeMap::create(scope.engine, r->d()->d, r->d()->d->attributes); } ReturnedValue NodePrototype::getProto(ExecutionEngine *v4) @@ -666,44 +659,40 @@ ReturnedValue Attr::prototype(ExecutionEngine *engine) return d->attrPrototype.value(); } -ReturnedValue Attr::method_name(CallContext *ctx) +void Attr::method_name(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<Node> r(scope, ctx->thisObject().as<Node>()); + Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) - return Encode::undefined(); + RETURN_UNDEFINED(); - return QV4::Encode(scope.engine->newString(r->d()->d->name)); + scope.result = scope.engine->newString(r->d()->d->name); } -ReturnedValue Attr::method_value(CallContext *ctx) +void Attr::method_value(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<Node> r(scope, ctx->thisObject().as<Node>()); + Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) - return Encode::undefined(); + RETURN_UNDEFINED(); - return QV4::Encode(scope.engine->newString(r->d()->d->data)); + scope.result = scope.engine->newString(r->d()->d->data); } -ReturnedValue Attr::method_ownerElement(CallContext *ctx) +void Attr::method_ownerElement(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<Node> r(scope, ctx->thisObject().as<Node>()); + Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) - return Encode::undefined(); + RETURN_UNDEFINED(); - return Node::create(scope.engine, r->d()->d->parent); + scope.result = Node::create(scope.engine, r->d()->d->parent); } -ReturnedValue CharacterData::method_length(CallContext *ctx) +void CharacterData::method_length(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<Node> r(scope, ctx->thisObject().as<Node>()); + Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) - return Encode::undefined(); + RETURN_UNDEFINED(); - return Encode(r->d()->d->data.length()); + scope.result = Encode(r->d()->d->data.length()); } ReturnedValue CharacterData::prototype(ExecutionEngine *v4) @@ -722,23 +711,22 @@ ReturnedValue CharacterData::prototype(ExecutionEngine *v4) return d->characterDataPrototype.value(); } -ReturnedValue Text::method_isElementContentWhitespace(CallContext *ctx) +void Text::method_isElementContentWhitespace(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<Node> r(scope, ctx->thisObject().as<Node>()); - if (!r) return Encode::undefined(); + Scoped<Node> r(scope, callData->thisObject.as<Node>()); + if (!r) + RETURN_UNDEFINED(); - return Encode(QStringRef(&r->d()->d->data).trimmed().isEmpty()); + scope.result = Encode(QStringRef(&r->d()->d->data).trimmed().isEmpty()); } -ReturnedValue Text::method_wholeText(CallContext *ctx) +void Text::method_wholeText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<Node> r(scope, ctx->thisObject().as<Node>()); + Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r) - return Encode::undefined(); + RETURN_UNDEFINED(); - return QV4::Encode(scope.engine->newString(r->d()->d->data)); + scope.result = scope.engine->newString(r->d()->d->data); } ReturnedValue Text::prototype(ExecutionEngine *v4) @@ -964,44 +952,40 @@ ReturnedValue NodeList::create(ExecutionEngine *v4, NodeImpl *data) return (v4->memoryManager->allocObject<NodeList>(data))->asReturnedValue(); } -ReturnedValue Document::method_documentElement(CallContext *ctx) +void Document::method_documentElement(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<Node> r(scope, ctx->thisObject().as<Node>()); + Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r || r->d()->d->type != NodeImpl::Document) - return Encode::undefined(); + RETURN_UNDEFINED(); - return Node::create(scope.engine, static_cast<DocumentImpl *>(r->d()->d)->root); + scope.result = Node::create(scope.engine, static_cast<DocumentImpl *>(r->d()->d)->root); } -ReturnedValue Document::method_xmlStandalone(CallContext *ctx) +void Document::method_xmlStandalone(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<Node> r(scope, ctx->thisObject().as<Node>()); + Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r || r->d()->d->type != NodeImpl::Document) - return Encode::undefined(); + RETURN_UNDEFINED(); - return Encode(static_cast<DocumentImpl *>(r->d()->d)->isStandalone); + scope.result = Encode(static_cast<DocumentImpl *>(r->d()->d)->isStandalone); } -ReturnedValue Document::method_xmlVersion(CallContext *ctx) +void Document::method_xmlVersion(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<Node> r(scope, ctx->thisObject().as<Node>()); + Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r || r->d()->d->type != NodeImpl::Document) - return Encode::undefined(); + RETURN_UNDEFINED(); - return QV4::Encode(scope.engine->newString(static_cast<DocumentImpl *>(r->d()->d)->version)); + scope.result = scope.engine->newString(static_cast<DocumentImpl *>(r->d()->d)->version); } -ReturnedValue Document::method_xmlEncoding(CallContext *ctx) +void Document::method_xmlEncoding(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<Node> r(scope, ctx->thisObject().as<Node>()); + Scoped<Node> r(scope, callData->thisObject.as<Node>()); if (!r || r->d()->d->type != NodeImpl::Document) - return Encode::undefined(); + RETURN_UNDEFINED(); - return QV4::Encode(scope.engine->newString(static_cast<DocumentImpl *>(r->d()->d)->encoding)); + scope.result = scope.engine->newString(static_cast<DocumentImpl *>(r->d()->d)->encoding); } class QQmlXMLHttpRequest : public QObject @@ -1657,21 +1641,21 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject void setupProto(); - static ReturnedValue method_open(CallContext *ctx); - static ReturnedValue method_setRequestHeader(CallContext *ctx); - static ReturnedValue method_send(CallContext *ctx); - static ReturnedValue method_abort(CallContext *ctx); - static ReturnedValue method_getResponseHeader(CallContext *ctx); - static ReturnedValue method_getAllResponseHeaders(CallContext *ctx); - - static ReturnedValue method_get_readyState(CallContext *ctx); - static ReturnedValue method_get_status(CallContext *ctx); - static ReturnedValue method_get_statusText(CallContext *ctx); - static ReturnedValue method_get_responseText(CallContext *ctx); - static ReturnedValue method_get_responseXML(CallContext *ctx); - static ReturnedValue method_get_response(CallContext *ctx); - static ReturnedValue method_get_responseType(CallContext *ctx); - static ReturnedValue method_set_responseType(CallContext *ctx); + 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); }; } @@ -1733,19 +1717,18 @@ void QQmlXMLHttpRequestCtor::setupProto() // XMLHttpRequest methods -ReturnedValue QQmlXMLHttpRequestCtor::method_open(CallContext *ctx) +void QQmlXMLHttpRequestCtor::method_open(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<QQmlXMLHttpRequestWrapper> w(scope, ctx->thisObject().as<QQmlXMLHttpRequestWrapper>()); + Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; - if (ctx->argc() < 2 || ctx->argc() > 5) - V4THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count"); + if (callData->argc < 2 || callData->argc > 5) + THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count"); // Argument 0 - Method - QString method = ctx->args()[0].toQStringNoThrow().toUpper(); + QString method = callData->args[0].toQStringNoThrow().toUpper(); if (method != QLatin1String("GET") && method != QLatin1String("PUT") && method != QLatin1String("HEAD") && @@ -1754,26 +1737,26 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_open(CallContext *ctx) method != QLatin1String("OPTIONS") && method != QLatin1String("PROPFIND") && method != QLatin1String("PATCH")) - V4THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Unsupported HTTP method type"); + THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Unsupported HTTP method type"); // Argument 1 - URL - QUrl url = QUrl(ctx->args()[1].toQStringNoThrow()); + QUrl url = QUrl(callData->args[1].toQStringNoThrow()); if (url.isRelative()) url = scope.engine->callingQmlContext()->resolvedUrl(url); bool async = true; // Argument 2 - async (optional) - if (ctx->argc() > 2) { - async = ctx->args()[2].booleanValue(); + if (callData->argc > 2) { + async = callData->args[2].booleanValue(); } // Argument 3/4 - user/pass (optional) QString username, password; - if (ctx->argc() > 3) - username = ctx->args()[3].toQStringNoThrow(); - if (ctx->argc() > 4) - password = ctx->args()[4].toQStringNoThrow(); + if (callData->argc > 3) + username = callData->args[3].toQStringNoThrow(); + if (callData->argc > 4) + password = callData->args[4].toQStringNoThrow(); // Clear the fragment (if any) url.setFragment(QString()); @@ -1782,25 +1765,24 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_open(CallContext *ctx) if (!username.isNull()) url.setUserName(username); if (!password.isNull()) url.setPassword(password); - return r->open(w, scope.engine->callingQmlContext(), method, url, async ? QQmlXMLHttpRequest::AsynchronousLoad : QQmlXMLHttpRequest::SynchronousLoad); + scope.result = r->open(w, scope.engine->callingQmlContext(), method, url, async ? QQmlXMLHttpRequest::AsynchronousLoad : QQmlXMLHttpRequest::SynchronousLoad); } -ReturnedValue QQmlXMLHttpRequestCtor::method_setRequestHeader(CallContext *ctx) +void QQmlXMLHttpRequestCtor::method_setRequestHeader(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<QQmlXMLHttpRequestWrapper> w(scope, ctx->thisObject().as<QQmlXMLHttpRequestWrapper>()); + Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; - if (ctx->argc() != 2) - V4THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count"); + if (callData->argc != 2) + THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count"); if (r->readyState() != QQmlXMLHttpRequest::Opened || r->sendFlag()) - V4THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); + THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); - QString name = ctx->args()[0].toQStringNoThrow(); - QString value = ctx->args()[1].toQStringNoThrow(); + QString name = callData->args[0].toQStringNoThrow(); + QString value = callData->args[1].toQStringNoThrow(); // ### Check that name and value are well formed @@ -1825,148 +1807,139 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_setRequestHeader(CallContext *ctx) nameUpper == QLatin1String("VIA") || nameUpper.startsWith(QLatin1String("PROXY-")) || nameUpper.startsWith(QLatin1String("SEC-"))) - return Encode::undefined(); + RETURN_UNDEFINED(); r->addHeader(name, value); - return Encode::undefined(); + RETURN_UNDEFINED(); } -ReturnedValue QQmlXMLHttpRequestCtor::method_send(CallContext *ctx) +void QQmlXMLHttpRequestCtor::method_send(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<QQmlXMLHttpRequestWrapper> w(scope, ctx->thisObject().as<QQmlXMLHttpRequestWrapper>()); + Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; if (r->readyState() != QQmlXMLHttpRequest::Opened || r->sendFlag()) - V4THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); + THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); QByteArray data; - if (ctx->argc() > 0) - data = ctx->args()[0].toQStringNoThrow().toUtf8(); + if (callData->argc > 0) + data = callData->args[0].toQStringNoThrow().toUtf8(); - return r->send(w, scope.engine->callingQmlContext(), data); + scope.result = r->send(w, scope.engine->callingQmlContext(), data); } -ReturnedValue QQmlXMLHttpRequestCtor::method_abort(CallContext *ctx) +void QQmlXMLHttpRequestCtor::method_abort(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<QQmlXMLHttpRequestWrapper> w(scope, ctx->thisObject().as<QQmlXMLHttpRequestWrapper>()); + Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; - return r->abort(w, scope.engine->callingQmlContext()); + scope.result = r->abort(w, scope.engine->callingQmlContext()); } -ReturnedValue QQmlXMLHttpRequestCtor::method_getResponseHeader(CallContext *ctx) +void QQmlXMLHttpRequestCtor::method_getResponseHeader(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<QQmlXMLHttpRequestWrapper> w(scope, ctx->thisObject().as<QQmlXMLHttpRequestWrapper>()); + Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; - if (ctx->argc() != 1) - V4THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count"); + if (callData->argc != 1) + THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count"); if (r->readyState() != QQmlXMLHttpRequest::Loading && r->readyState() != QQmlXMLHttpRequest::Done && r->readyState() != QQmlXMLHttpRequest::HeadersReceived) - V4THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); + THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); - return QV4::Encode(scope.engine->newString(r->header(ctx->args()[0].toQStringNoThrow()))); + scope.result = scope.engine->newString(r->header(callData->args[0].toQStringNoThrow())); } -ReturnedValue QQmlXMLHttpRequestCtor::method_getAllResponseHeaders(CallContext *ctx) +void QQmlXMLHttpRequestCtor::method_getAllResponseHeaders(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<QQmlXMLHttpRequestWrapper> w(scope, ctx->thisObject().as<QQmlXMLHttpRequestWrapper>()); + Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; - if (ctx->argc() != 0) - V4THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count"); + if (callData->argc != 0) + THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count"); if (r->readyState() != QQmlXMLHttpRequest::Loading && r->readyState() != QQmlXMLHttpRequest::Done && r->readyState() != QQmlXMLHttpRequest::HeadersReceived) - V4THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); + THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); - return QV4::Encode(scope.engine->newString(r->headers())); + scope.result = scope.engine->newString(r->headers()); } // XMLHttpRequest properties -ReturnedValue QQmlXMLHttpRequestCtor::method_get_readyState(CallContext *ctx) +void QQmlXMLHttpRequestCtor::method_get_readyState(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<QQmlXMLHttpRequestWrapper> w(scope, ctx->thisObject().as<QQmlXMLHttpRequestWrapper>()); + Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; - return Encode(r->readyState()); + scope.result = Encode(r->readyState()); } -ReturnedValue QQmlXMLHttpRequestCtor::method_get_status(CallContext *ctx) +void QQmlXMLHttpRequestCtor::method_get_status(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<QQmlXMLHttpRequestWrapper> w(scope, ctx->thisObject().as<QQmlXMLHttpRequestWrapper>()); + Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; if (r->readyState() == QQmlXMLHttpRequest::Unsent || r->readyState() == QQmlXMLHttpRequest::Opened) - V4THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); + THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); if (r->errorFlag()) - return Encode(0); + scope.result = Encode(0); else - return Encode(r->replyStatus()); + scope.result = Encode(r->replyStatus()); } -ReturnedValue QQmlXMLHttpRequestCtor::method_get_statusText(CallContext *ctx) +void QQmlXMLHttpRequestCtor::method_get_statusText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<QQmlXMLHttpRequestWrapper> w(scope, ctx->thisObject().as<QQmlXMLHttpRequestWrapper>()); + Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; if (r->readyState() == QQmlXMLHttpRequest::Unsent || r->readyState() == QQmlXMLHttpRequest::Opened) - V4THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); + THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); if (r->errorFlag()) - return QV4::Encode(scope.engine->newString(QString())); + scope.result = scope.engine->newString(QString()); else - return QV4::Encode(scope.engine->newString(r->replyStatusText())); + scope.result = scope.engine->newString(r->replyStatusText()); } -ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseText(CallContext *ctx) +void QQmlXMLHttpRequestCtor::method_get_responseText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<QQmlXMLHttpRequestWrapper> w(scope, ctx->thisObject().as<QQmlXMLHttpRequestWrapper>()); + Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; if (r->readyState() != QQmlXMLHttpRequest::Loading && r->readyState() != QQmlXMLHttpRequest::Done) - return QV4::Encode(scope.engine->newString(QString())); + scope.result = scope.engine->newString(QString()); else - return QV4::Encode(scope.engine->newString(r->responseBody())); + scope.result = scope.engine->newString(r->responseBody()); } -ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseXML(CallContext *ctx) +void QQmlXMLHttpRequestCtor::method_get_responseXML(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<QQmlXMLHttpRequestWrapper> w(scope, ctx->thisObject().as<QQmlXMLHttpRequestWrapper>()); + Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; @@ -1974,66 +1947,63 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseXML(CallContext *ctx) if (!r->receivedXml() || (r->readyState() != QQmlXMLHttpRequest::Loading && r->readyState() != QQmlXMLHttpRequest::Done)) { - return Encode::null(); + scope.result = Encode::null(); } else { if (r->responseType().isEmpty()) r->setResponseType(QLatin1String("document")); - return r->xmlResponseBody(scope.engine); + scope.result = r->xmlResponseBody(scope.engine); } } -ReturnedValue QQmlXMLHttpRequestCtor::method_get_response(CallContext *ctx) +void QQmlXMLHttpRequestCtor::method_get_response(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<QQmlXMLHttpRequestWrapper> w(scope, ctx->thisObject().as<QQmlXMLHttpRequestWrapper>()); + Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; if (r->readyState() != QQmlXMLHttpRequest::Loading && r->readyState() != QQmlXMLHttpRequest::Done) - return QV4::Encode(scope.engine->newString(QString())); + RETURN_RESULT(scope.engine->newString(QString())); const QString& responseType = r->responseType(); if (responseType.compare(QLatin1String("text"), Qt::CaseInsensitive) == 0 || responseType.isEmpty()) { - return QV4::Encode(scope.engine->newString(r->responseBody())); + RETURN_RESULT(scope.engine->newString(r->responseBody())); } else if (responseType.compare(QLatin1String("arraybuffer"), Qt::CaseInsensitive) == 0) { - return QV4::Encode(scope.engine->newArrayBuffer(r->rawResponseBody())); + RETURN_RESULT(scope.engine->newArrayBuffer(r->rawResponseBody())); } else if (responseType.compare(QLatin1String("json"), Qt::CaseInsensitive) == 0) { - return r->jsonResponseBody(scope.engine); + RETURN_RESULT(r->jsonResponseBody(scope.engine)); } else if (responseType.compare(QLatin1String("document"), Qt::CaseInsensitive) == 0) { - return r->xmlResponseBody(scope.engine); + RETURN_RESULT(r->xmlResponseBody(scope.engine)); } else { - return QV4::Encode(scope.engine->newString(QString())); + RETURN_RESULT(scope.engine->newString(QString())); } } -ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseType(CallContext *ctx) +void QQmlXMLHttpRequestCtor::method_get_responseType(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<QQmlXMLHttpRequestWrapper> w(scope, ctx->thisObject().as<QQmlXMLHttpRequestWrapper>()); + Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; - return QV4::Encode(scope.engine->newString(r->responseType())); + scope.result = scope.engine->newString(r->responseType()); } -ReturnedValue QQmlXMLHttpRequestCtor::method_set_responseType(CallContext *ctx) +void QQmlXMLHttpRequestCtor::method_set_responseType(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - Scope scope(ctx); - Scoped<QQmlXMLHttpRequestWrapper> w(scope, ctx->thisObject().as<QQmlXMLHttpRequestWrapper>()); + Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>()); if (!w) V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; - if (ctx->argc() < 1) - V4THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count"); + if (callData->argc < 1) + THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count"); // Argument 0 - response type - r->setResponseType(ctx->args()[0].toQStringNoThrow()); + r->setResponseType(callData->args[0].toQStringNoThrow()); - return Encode::undefined(); + scope.result = 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 19dc100f40..d359a0f62f 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -85,6 +85,12 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(QtObject); +#define THROW_TYPE_ERROR_WITH_MESSAGE(msg) \ + do { \ + scope.result = scope.engine->throwTypeError(QString::fromUtf8(msg)); \ + return; \ + } while (false) + struct StaticQtMetaObject : public QObject { static const QMetaObject *get() @@ -223,12 +229,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. */ -ReturnedValue QtObject::method_isQtObject(QV4::CallContext *ctx) +void QtObject::method_isQtObject(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() == 0) - return QV4::Encode(false); + if (callData->argc == 0) + RETURN_RESULT(QV4::Encode(false)); - return QV4::Encode(ctx->args()[0].as<QV4::QObjectWrapper>() != 0); + scope.result = QV4::Encode(callData->args[0].as<QV4::QObjectWrapper>() != 0); } /*! @@ -237,16 +243,16 @@ ReturnedValue QtObject::method_isQtObject(QV4::CallContext *ctx) 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. */ -ReturnedValue QtObject::method_rgba(QV4::CallContext *ctx) +void QtObject::method_rgba(const BuiltinFunction *, Scope &scope, CallData *callData) { - int argCount = ctx->argc(); + int argCount = callData->argc; if (argCount < 3 || argCount > 4) - V4THROW_ERROR("Qt.rgba(): Invalid arguments"); + THROW_GENERIC_ERROR("Qt.rgba(): Invalid arguments"); - double r = ctx->args()[0].toNumber(); - double g = ctx->args()[1].toNumber(); - double b = ctx->args()[2].toNumber(); - double a = (argCount == 4) ? ctx->args()[3].toNumber() : 1; + double r = callData->args[0].toNumber(); + double g = callData->args[1].toNumber(); + double b = callData->args[2].toNumber(); + double a = (argCount == 4) ? callData->args[3].toNumber() : 1; if (r < 0.0) r=0.0; if (r > 1.0) r=1.0; @@ -257,7 +263,7 @@ ReturnedValue QtObject::method_rgba(QV4::CallContext *ctx) if (a < 0.0) a=0.0; if (a > 1.0) a=1.0; - return ctx->engine()->fromVariant(QQml_colorProvider()->fromRgbF(r, g, b, a)); + scope.result = scope.engine->fromVariant(QQml_colorProvider()->fromRgbF(r, g, b, a)); } /*! @@ -266,16 +272,16 @@ ReturnedValue QtObject::method_rgba(QV4::CallContext *ctx) 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. */ -ReturnedValue QtObject::method_hsla(QV4::CallContext *ctx) +void QtObject::method_hsla(const BuiltinFunction *, Scope &scope, CallData *callData) { - int argCount = ctx->argc(); + int argCount = callData->argc; if (argCount < 3 || argCount > 4) - V4THROW_ERROR("Qt.hsla(): Invalid arguments"); + THROW_GENERIC_ERROR("Qt.hsla(): Invalid arguments"); - double h = ctx->args()[0].toNumber(); - double s = ctx->args()[1].toNumber(); - double l = ctx->args()[2].toNumber(); - double a = (argCount == 4) ? ctx->args()[3].toNumber() : 1; + double h = callData->args[0].toNumber(); + double s = callData->args[1].toNumber(); + double l = callData->args[2].toNumber(); + double a = (argCount == 4) ? callData->args[3].toNumber() : 1; if (h < 0.0) h=0.0; if (h > 1.0) h=1.0; @@ -286,7 +292,7 @@ ReturnedValue QtObject::method_hsla(QV4::CallContext *ctx) if (a < 0.0) a=0.0; if (a > 1.0) a=1.0; - return ctx->engine()->fromVariant(QQml_colorProvider()->fromHslF(h, s, l, a)); + scope.result = scope.engine->fromVariant(QQml_colorProvider()->fromHslF(h, s, l, a)); } /*! @@ -297,23 +303,23 @@ All components should be in the range 0-1 inclusive. \since 5.5 */ -ReturnedValue QtObject::method_hsva(QV4::CallContext *ctx) +void QtObject::method_hsva(const BuiltinFunction *, Scope &scope, CallData *callData) { - int argCount = ctx->argc(); + int argCount = callData->argc; if (argCount < 3 || argCount > 4) - V4THROW_ERROR("Qt.hsva(): Invalid arguments"); + THROW_GENERIC_ERROR("Qt.hsva(): Invalid arguments"); - double h = ctx->args()[0].toNumber(); - double s = ctx->args()[1].toNumber(); - double v = ctx->args()[2].toNumber(); - double a = (argCount == 4) ? ctx->args()[3].toNumber() : 1; + double h = callData->args[0].toNumber(); + double s = callData->args[1].toNumber(); + double v = callData->args[2].toNumber(); + double a = (argCount == 4) ? callData->args[3].toNumber() : 1; h = qBound(0.0, h, 1.0); s = qBound(0.0, s, 1.0); v = qBound(0.0, v, 1.0); a = qBound(0.0, a, 1.0); - return ctx->engine()->fromVariant(QQml_colorProvider()->fromHsvF(h, s, v, a)); + scope.result = scope.engine->fromVariant(QQml_colorProvider()->fromHsvF(h, s, v, a)); } /*! @@ -324,35 +330,35 @@ 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. */ -ReturnedValue QtObject::method_colorEqual(QV4::CallContext *ctx) +void QtObject::method_colorEqual(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() != 2) - V4THROW_ERROR("Qt.colorEqual(): Invalid arguments"); + if (callData->argc != 2) + THROW_GENERIC_ERROR("Qt.colorEqual(): Invalid arguments"); bool ok = false; - QVariant lhs = ctx->d()->engine->toVariant(ctx->args()[0], -1); + QVariant lhs = scope.engine->toVariant(callData->args[0], -1); if (lhs.userType() == QVariant::String) { lhs = QQmlStringConverters::colorFromString(lhs.toString(), &ok); if (!ok) { - V4THROW_ERROR("Qt.colorEqual(): Invalid color name"); + THROW_GENERIC_ERROR("Qt.colorEqual(): Invalid color name"); } } else if (lhs.userType() != QVariant::Color) { - V4THROW_ERROR("Qt.colorEqual(): Invalid arguments"); + THROW_GENERIC_ERROR("Qt.colorEqual(): Invalid arguments"); } - QVariant rhs = ctx->engine()->toVariant(ctx->args()[1], -1); + QVariant rhs = scope.engine->toVariant(callData->args[1], -1); if (rhs.userType() == QVariant::String) { rhs = QQmlStringConverters::colorFromString(rhs.toString(), &ok); if (!ok) { - V4THROW_ERROR("Qt.colorEqual(): Invalid color name"); + THROW_GENERIC_ERROR("Qt.colorEqual(): Invalid color name"); } } else if (rhs.userType() != QVariant::Color) { - V4THROW_ERROR("Qt.colorEqual(): Invalid arguments"); + THROW_GENERIC_ERROR("Qt.colorEqual(): Invalid arguments"); } bool equal = (lhs == rhs); - return QV4::Encode(equal); + scope.result = QV4::Encode(equal); } /*! @@ -362,47 +368,47 @@ 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. */ -ReturnedValue QtObject::method_rect(QV4::CallContext *ctx) +void QtObject::method_rect(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() != 4) - V4THROW_ERROR("Qt.rect(): Invalid arguments"); + if (callData->argc != 4) + THROW_GENERIC_ERROR("Qt.rect(): Invalid arguments"); - double x = ctx->args()[0].toNumber(); - double y = ctx->args()[1].toNumber(); - double w = ctx->args()[2].toNumber(); - double h = ctx->args()[3].toNumber(); + double x = callData->args[0].toNumber(); + double y = callData->args[1].toNumber(); + double w = callData->args[2].toNumber(); + double h = callData->args[3].toNumber(); - return ctx->engine()->fromVariant(QVariant::fromValue(QRectF(x, y, w, h))); + scope.result = 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. */ -ReturnedValue QtObject::method_point(QV4::CallContext *ctx) +void QtObject::method_point(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() != 2) - V4THROW_ERROR("Qt.point(): Invalid arguments"); + if (callData->argc != 2) + THROW_GENERIC_ERROR("Qt.point(): Invalid arguments"); - double x = ctx->args()[0].toNumber(); - double y = ctx->args()[1].toNumber(); + double x = callData->args[0].toNumber(); + double y = callData->args[1].toNumber(); - return ctx->engine()->fromVariant(QVariant::fromValue(QPointF(x, y))); + scope.result = 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. */ -ReturnedValue QtObject::method_size(QV4::CallContext *ctx) +void QtObject::method_size(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() != 2) - V4THROW_ERROR("Qt.size(): Invalid arguments"); + if (callData->argc != 2) + THROW_GENERIC_ERROR("Qt.size(): Invalid arguments"); - double w = ctx->args()[0].toNumber(); - double h = ctx->args()[1].toNumber(); + double w = callData->args[0].toNumber(); + double h = callData->args[1].toNumber(); - return ctx->engine()->fromVariant(QVariant::fromValue(QSizeF(w, h))); + scope.result = scope.engine->fromVariant(QVariant::fromValue(QSizeF(w, h))); } /*! @@ -413,17 +419,17 @@ 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. */ -ReturnedValue QtObject::method_font(QV4::CallContext *ctx) +void QtObject::method_font(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() != 1 || !ctx->args()[0].isObject()) - V4THROW_ERROR("Qt.font(): Invalid arguments"); + if (callData->argc != 1 || !callData->args[0].isObject()) + THROW_GENERIC_ERROR("Qt.font(): Invalid arguments"); - QV4::ExecutionEngine *v4 = ctx->d()->engine; + QV4::ExecutionEngine *v4 = scope.engine; bool ok = false; - QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QFont, QQmlV4Handle(ctx->args()[0]), v4, &ok); + QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QFont, QQmlV4Handle(callData->args[0]), v4, &ok); if (!ok) - V4THROW_ERROR("Qt.font(): Invalid argument: no valid font subproperties specified"); - return ctx->engine()->fromVariant(v); + THROW_GENERIC_ERROR("Qt.font(): Invalid argument: no valid font subproperties specified"); + scope.result = scope.engine->fromVariant(v); } @@ -432,73 +438,73 @@ ReturnedValue QtObject::method_font(QV4::CallContext *ctx) \qmlmethod Qt::vector2d(real x, real y) Returns a Vector2D with the specified \c x and \c y. */ -ReturnedValue QtObject::method_vector2d(QV4::CallContext *ctx) +void QtObject::method_vector2d(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() != 2) - V4THROW_ERROR("Qt.vector2d(): Invalid arguments"); + if (callData->argc != 2) + THROW_GENERIC_ERROR("Qt.vector2d(): Invalid arguments"); float xy[3]; // qvector2d uses float internally - xy[0] = ctx->args()[0].toNumber(); - xy[1] = ctx->args()[1].toNumber(); + xy[0] = callData->args[0].toNumber(); + xy[1] = callData->args[1].toNumber(); const void *params[] = { xy }; - return ctx->engine()->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QVector2D, 1, params)); + scope.result = 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. */ -ReturnedValue QtObject::method_vector3d(QV4::CallContext *ctx) +void QtObject::method_vector3d(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() != 3) - V4THROW_ERROR("Qt.vector3d(): Invalid arguments"); + if (callData->argc != 3) + THROW_GENERIC_ERROR("Qt.vector3d(): Invalid arguments"); float xyz[3]; // qvector3d uses float internally - xyz[0] = ctx->args()[0].toNumber(); - xyz[1] = ctx->args()[1].toNumber(); - xyz[2] = ctx->args()[2].toNumber(); + xyz[0] = callData->args[0].toNumber(); + xyz[1] = callData->args[1].toNumber(); + xyz[2] = callData->args[2].toNumber(); const void *params[] = { xyz }; - return ctx->engine()->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QVector3D, 1, params)); + scope.result = 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. */ -ReturnedValue QtObject::method_vector4d(QV4::CallContext *ctx) +void QtObject::method_vector4d(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() != 4) - V4THROW_ERROR("Qt.vector4d(): Invalid arguments"); + if (callData->argc != 4) + THROW_GENERIC_ERROR("Qt.vector4d(): Invalid arguments"); float xyzw[4]; // qvector4d uses float internally - xyzw[0] = ctx->args()[0].toNumber(); - xyzw[1] = ctx->args()[1].toNumber(); - xyzw[2] = ctx->args()[2].toNumber(); - xyzw[3] = ctx->args()[3].toNumber(); + xyzw[0] = callData->args[0].toNumber(); + xyzw[1] = callData->args[1].toNumber(); + xyzw[2] = callData->args[2].toNumber(); + xyzw[3] = callData->args[3].toNumber(); const void *params[] = { xyzw }; - return ctx->engine()->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QVector4D, 1, params)); + scope.result = 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. */ -ReturnedValue QtObject::method_quaternion(QV4::CallContext *ctx) +void QtObject::method_quaternion(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() != 4) - V4THROW_ERROR("Qt.quaternion(): Invalid arguments"); + if (callData->argc != 4) + THROW_GENERIC_ERROR("Qt.quaternion(): Invalid arguments"); qreal sxyz[4]; // qquaternion uses qreal internally - sxyz[0] = ctx->args()[0].toNumber(); - sxyz[1] = ctx->args()[1].toNumber(); - sxyz[2] = ctx->args()[2].toNumber(); - sxyz[3] = ctx->args()[3].toNumber(); + sxyz[0] = callData->args[0].toNumber(); + sxyz[1] = callData->args[1].toNumber(); + sxyz[2] = callData->args[2].toNumber(); + sxyz[3] = callData->args[3].toNumber(); const void *params[] = { sxyz }; - return ctx->engine()->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QQuaternion, 1, params)); + scope.result = scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QQuaternion, 1, params)); } /*! @@ -510,44 +516,47 @@ matrix values. Finally, the function may be called with no arguments and the resulting matrix will be the identity matrix. */ -ReturnedValue QtObject::method_matrix4x4(QV4::CallContext *ctx) +void QtObject::method_matrix4x4(const BuiltinFunction *, Scope &scope, CallData *callData) { - QV4::ExecutionEngine *v4 = ctx->d()->engine; + QV4::ExecutionEngine *v4 = scope.engine; - if (ctx->argc() == 0) - return ctx->engine()->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QMatrix4x4, 0, Q_NULLPTR)); + if (callData->argc == 0) { + scope.result = scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QMatrix4x4, 0, Q_NULLPTR)); + return; + } - if (ctx->argc() == 1 && ctx->args()[0].isObject()) { + if (callData->argc == 1 && callData->args[0].isObject()) { bool ok = false; - QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QMatrix4x4, QQmlV4Handle(ctx->args()[0]), v4, &ok); + QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QMatrix4x4, QQmlV4Handle(callData->args[0]), v4, &ok); if (!ok) - V4THROW_ERROR("Qt.matrix4x4(): Invalid argument: not a valid matrix4x4 values array"); - return ctx->engine()->fromVariant(v); + THROW_GENERIC_ERROR("Qt.matrix4x4(): Invalid argument: not a valid matrix4x4 values array"); + scope.result = scope.engine->fromVariant(v); + return; } - if (ctx->argc() != 16) - V4THROW_ERROR("Qt.matrix4x4(): Invalid arguments"); + if (callData->argc != 16) + THROW_GENERIC_ERROR("Qt.matrix4x4(): Invalid arguments"); qreal vals[16]; // qmatrix4x4 uses qreal internally - vals[0] = ctx->args()[0].toNumber(); - vals[1] = ctx->args()[1].toNumber(); - vals[2] = ctx->args()[2].toNumber(); - vals[3] = ctx->args()[3].toNumber(); - vals[4] = ctx->args()[4].toNumber(); - vals[5] = ctx->args()[5].toNumber(); - vals[6] = ctx->args()[6].toNumber(); - vals[7] = ctx->args()[7].toNumber(); - vals[8] = ctx->args()[8].toNumber(); - vals[9] = ctx->args()[9].toNumber(); - vals[10] = ctx->args()[10].toNumber(); - vals[11] = ctx->args()[11].toNumber(); - vals[12] = ctx->args()[12].toNumber(); - vals[13] = ctx->args()[13].toNumber(); - vals[14] = ctx->args()[14].toNumber(); - vals[15] = ctx->args()[15].toNumber(); + vals[0] = callData->args[0].toNumber(); + vals[1] = callData->args[1].toNumber(); + vals[2] = callData->args[2].toNumber(); + vals[3] = callData->args[3].toNumber(); + vals[4] = callData->args[4].toNumber(); + vals[5] = callData->args[5].toNumber(); + vals[6] = callData->args[6].toNumber(); + vals[7] = callData->args[7].toNumber(); + vals[8] = callData->args[8].toNumber(); + vals[9] = callData->args[9].toNumber(); + vals[10] = callData->args[10].toNumber(); + vals[11] = callData->args[11].toNumber(); + vals[12] = callData->args[12].toNumber(); + vals[13] = callData->args[13].toNumber(); + vals[14] = callData->args[14].toNumber(); + vals[15] = callData->args[15].toNumber(); const void *params[] = { vals }; - return ctx->engine()->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QMatrix4x4, 1, params)); + scope.result = scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QMatrix4x4, 1, params)); } /*! @@ -564,27 +573,29 @@ 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). */ -ReturnedValue QtObject::method_lighter(QV4::CallContext *ctx) +void QtObject::method_lighter(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() != 1 && ctx->argc() != 2) - V4THROW_ERROR("Qt.lighter(): Invalid arguments"); + if (callData->argc != 1 && callData->argc != 2) + THROW_GENERIC_ERROR("Qt.lighter(): Invalid arguments"); - QVariant v = ctx->engine()->toVariant(ctx->args()[0], -1); + QVariant v = scope.engine->toVariant(callData->args[0], -1); if (v.userType() == QVariant::String) { bool ok = false; v = QQmlStringConverters::colorFromString(v.toString(), &ok); if (!ok) { - return QV4::Encode::null(); + scope.result = QV4::Encode::null(); + return; } } else if (v.userType() != QVariant::Color) { - return QV4::Encode::null(); + scope.result = QV4::Encode::null(); + return; } qreal factor = 1.5; - if (ctx->argc() == 2) - factor = ctx->args()[1].toNumber(); + if (callData->argc == 2) + factor = callData->args[1].toNumber(); - return ctx->engine()->fromVariant(QQml_colorProvider()->lighter(v, factor)); + scope.result = scope.engine->fromVariant(QQml_colorProvider()->lighter(v, factor)); } /*! @@ -602,27 +613,29 @@ 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). */ -ReturnedValue QtObject::method_darker(QV4::CallContext *ctx) +void QtObject::method_darker(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() != 1 && ctx->argc() != 2) - V4THROW_ERROR("Qt.darker(): Invalid arguments"); + if (callData->argc != 1 && callData->argc != 2) + THROW_GENERIC_ERROR("Qt.darker(): Invalid arguments"); - QVariant v = ctx->engine()->toVariant(ctx->args()[0], -1); + QVariant v = scope.engine->toVariant(callData->args[0], -1); if (v.userType() == QVariant::String) { bool ok = false; v = QQmlStringConverters::colorFromString(v.toString(), &ok); if (!ok) { - return QV4::Encode::null(); + scope.result = QV4::Encode::null(); + return; } } else if (v.userType() != QVariant::Color) { - return QV4::Encode::null(); + scope.result = QV4::Encode::null(); + return; } qreal factor = 2.0; - if (ctx->argc() == 2) - factor = ctx->args()[1].toNumber(); + if (callData->argc == 2) + factor = callData->args[1].toNumber(); - return ctx->engine()->fromVariant(QQml_colorProvider()->darker(v, factor)); + scope.result = scope.engine->fromVariant(QQml_colorProvider()->darker(v, factor)); } /*! @@ -649,36 +662,40 @@ ReturnedValue QtObject::method_darker(QV4::CallContext *ctx) 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. */ -ReturnedValue QtObject::method_tint(QV4::CallContext *ctx) +void QtObject::method_tint(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() != 2) - V4THROW_ERROR("Qt.tint(): Invalid arguments"); + if (callData->argc != 2) + THROW_GENERIC_ERROR("Qt.tint(): Invalid arguments"); // base color - QVariant v1 = ctx->engine()->toVariant(ctx->args()[0], -1); + QVariant v1 = scope.engine->toVariant(callData->args[0], -1); if (v1.userType() == QVariant::String) { bool ok = false; v1 = QQmlStringConverters::colorFromString(v1.toString(), &ok); if (!ok) { - return QV4::Encode::null(); + scope.result = QV4::Encode::null(); + return; } } else if (v1.userType() != QVariant::Color) { - return QV4::Encode::null(); + scope.result = QV4::Encode::null(); + return; } // tint color - QVariant v2 = ctx->engine()->toVariant(ctx->args()[1], -1); + QVariant v2 = scope.engine->toVariant(callData->args[1], -1); if (v2.userType() == QVariant::String) { bool ok = false; v2 = QQmlStringConverters::colorFromString(v2.toString(), &ok); if (!ok) { - return QV4::Encode::null(); + scope.result = QV4::Encode::null(); + return; } } else if (v2.userType() != QVariant::Color) { - return QV4::Encode::null(); + scope.result = QV4::Encode::null(); + return; } - return ctx->engine()->fromVariant(QQml_colorProvider()->tint(v1, v2)); + scope.result = scope.engine->fromVariant(QQml_colorProvider()->tint(v1, v2)); } /*! @@ -697,32 +714,31 @@ If \a format is not specified, \a date is formatted using \sa Locale */ -ReturnedValue QtObject::method_formatDate(QV4::CallContext *ctx) +void QtObject::method_formatDate(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() < 1 || ctx->argc() > 2) - V4THROW_ERROR("Qt.formatDate(): Invalid arguments"); - QV4::Scope scope(ctx); + if (callData->argc < 1 || callData->argc > 2) + THROW_GENERIC_ERROR("Qt.formatDate(): Invalid arguments"); Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate; - QDate date = ctx->engine()->toVariant(ctx->args()[0], -1).toDateTime().date(); + QDate date = scope.engine->toVariant(callData->args[0], -1).toDateTime().date(); QString formattedDate; - if (ctx->argc() == 2) { - QV4::ScopedString s(scope, ctx->args()[1]); + if (callData->argc == 2) { + QV4::ScopedString s(scope, callData->args[1]); if (s) { QString format = s->toQString(); formattedDate = date.toString(format); - } else if (ctx->args()[1].isNumber()) { - quint32 intFormat = ctx->args()[1].asDouble(); + } else if (callData->args[1].isNumber()) { + quint32 intFormat = callData->args[1].asDouble(); Qt::DateFormat format = Qt::DateFormat(intFormat); formattedDate = date.toString(format); } else { - V4THROW_ERROR("Qt.formatDate(): Invalid date format"); + THROW_GENERIC_ERROR("Qt.formatDate(): Invalid date format"); } } else { formattedDate = date.toString(enumFormat); } - return ctx->d()->engine->newString(formattedDate)->asReturnedValue(); + scope.result = scope.engine->newString(formattedDate); } /*! @@ -740,38 +756,37 @@ If \a format is not specified, \a time is formatted using \sa Locale */ -ReturnedValue QtObject::method_formatTime(QV4::CallContext *ctx) +void QtObject::method_formatTime(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() < 1 || ctx->argc() > 2) - V4THROW_ERROR("Qt.formatTime(): Invalid arguments"); - QV4::Scope scope(ctx); + if (callData->argc < 1 || callData->argc > 2) + THROW_GENERIC_ERROR("Qt.formatTime(): Invalid arguments"); - QVariant argVariant = ctx->engine()->toVariant(ctx->args()[0], -1); + QVariant argVariant = scope.engine->toVariant(callData->args[0], -1); QTime time; - if (ctx->args()[0].as<DateObject>() || (argVariant.type() == QVariant::String)) + if (callData->args[0].as<DateObject>() || (argVariant.type() == QVariant::String)) time = argVariant.toDateTime().time(); else // if (argVariant.type() == QVariant::Time), or invalid. time = argVariant.toTime(); Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate; QString formattedTime; - if (ctx->argc() == 2) { - QV4::ScopedString s(scope, ctx->args()[1]); + if (callData->argc == 2) { + QV4::ScopedString s(scope, callData->args[1]); if (s) { QString format = s->toQString(); formattedTime = time.toString(format); - } else if (ctx->args()[1].isNumber()) { - quint32 intFormat = ctx->args()[1].asDouble(); + } else if (callData->args[1].isNumber()) { + quint32 intFormat = callData->args[1].asDouble(); Qt::DateFormat format = Qt::DateFormat(intFormat); formattedTime = time.toString(format); } else { - V4THROW_ERROR("Qt.formatTime(): Invalid time format"); + THROW_GENERIC_ERROR("Qt.formatTime(): Invalid time format"); } } else { formattedTime = time.toString(enumFormat); } - return ctx->d()->engine->newString(formattedTime)->asReturnedValue(); + scope.result = scope.engine->newString(formattedTime); } /*! @@ -864,32 +879,31 @@ with the \a format values below to produce the following results: \sa Locale */ -ReturnedValue QtObject::method_formatDateTime(QV4::CallContext *ctx) +void QtObject::method_formatDateTime(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() < 1 || ctx->argc() > 2) - V4THROW_ERROR("Qt.formatDateTime(): Invalid arguments"); - QV4::Scope scope(ctx); + if (callData->argc < 1 || callData->argc > 2) + THROW_GENERIC_ERROR("Qt.formatDateTime(): Invalid arguments"); Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate; - QDateTime dt = ctx->engine()->toVariant(ctx->args()[0], -1).toDateTime(); + QDateTime dt = scope.engine->toVariant(callData->args[0], -1).toDateTime(); QString formattedDt; - if (ctx->argc() == 2) { - QV4::ScopedString s(scope, ctx->args()[1]); + if (callData->argc == 2) { + QV4::ScopedString s(scope, callData->args[1]); if (s) { QString format = s->toQString(); formattedDt = dt.toString(format); - } else if (ctx->args()[1].isNumber()) { - quint32 intFormat = ctx->args()[1].asDouble(); + } else if (callData->args[1].isNumber()) { + quint32 intFormat = callData->args[1].asDouble(); Qt::DateFormat format = Qt::DateFormat(intFormat); formattedDt = dt.toString(format); } else { - V4THROW_ERROR("Qt.formatDateTime(): Invalid datetime format"); + THROW_GENERIC_ERROR("Qt.formatDateTime(): Invalid datetime format"); } } else { formattedDt = dt.toString(enumFormat); } - return ctx->d()->engine->newString(formattedDt)->asReturnedValue(); + scope.result = scope.engine->newString(formattedDt); } /*! @@ -903,90 +917,94 @@ ReturnedValue QtObject::method_formatDateTime(QV4::CallContext *ctx) still fail to launch or fail to open the requested URL. This result will not be reported back to the application. */ -ReturnedValue QtObject::method_openUrlExternally(QV4::CallContext *ctx) +void QtObject::method_openUrlExternally(const BuiltinFunction *b, Scope &scope, CallData *callData) { - if (ctx->argc() != 1) - return QV4::Encode(false); + if (callData->argc != 1) { + scope.result = QV4::Encode(false); + return; + } - QUrl url(Value::fromReturnedValue(method_resolvedUrl(ctx)).toQStringNoThrow()); - return ctx->engine()->fromVariant(QQml_guiProvider()->openUrlExternally(url)); + method_resolvedUrl(b, scope, callData); + QUrl url(scope.result.toQStringNoThrow()); + scope.result = scope.engine->fromVariant(QQml_guiProvider()->openUrlExternally(url)); } /*! \qmlmethod url Qt::resolvedUrl(url url) Returns \a url resolved relative to the URL of the caller. */ -ReturnedValue QtObject::method_resolvedUrl(QV4::CallContext *ctx) +void QtObject::method_resolvedUrl(const BuiltinFunction *, Scope &scope, CallData *callData) { - ExecutionEngine *v4 = ctx->engine(); + ExecutionEngine *v4 = scope.engine; - QUrl url = v4->toVariant(ctx->args()[0], -1).toUrl(); + QUrl url = v4->toVariant(callData->args[0], -1).toUrl(); QQmlEngine *e = v4->qmlEngine(); QQmlEnginePrivate *p = 0; if (e) p = QQmlEnginePrivate::get(e); if (p) { QQmlContextData *ctxt = v4->callingQmlContext(); if (ctxt) - return v4->newString(ctxt->resolvedUrl(url).toString())->asReturnedValue(); + scope.result = v4->newString(ctxt->resolvedUrl(url).toString()); else - return v4->newString(url.toString())->asReturnedValue(); + scope.result = v4->newString(url.toString()); + return; } - return v4->newString(e->baseUrl().resolved(url).toString())->asReturnedValue(); + scope.result = v4->newString(e->baseUrl().resolved(url).toString()); } /*! \qmlmethod list<string> Qt::fontFamilies() Returns a list of the font families available to the application. */ -ReturnedValue QtObject::method_fontFamilies(CallContext *ctx) +void QtObject::method_fontFamilies(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() != 0) - V4THROW_ERROR("Qt.fontFamilies(): Invalid arguments"); + if (callData->argc != 0) + THROW_GENERIC_ERROR("Qt.fontFamilies(): Invalid arguments"); - return ctx->engine()->fromVariant(QVariant(QQml_guiProvider()->fontFamilies())); + scope.result = scope.engine->fromVariant(QVariant(QQml_guiProvider()->fontFamilies())); } /*! \qmlmethod string Qt::md5(data) Returns a hex string of the md5 hash of \c data. */ -ReturnedValue QtObject::method_md5(CallContext *ctx) +void QtObject::method_md5(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() != 1) - V4THROW_ERROR("Qt.md5(): Invalid arguments"); + if (callData->argc != 1) + THROW_GENERIC_ERROR("Qt.md5(): Invalid arguments"); - QByteArray data = ctx->args()[0].toQStringNoThrow().toUtf8(); + QByteArray data = callData->args[0].toQStringNoThrow().toUtf8(); QByteArray result = QCryptographicHash::hash(data, QCryptographicHash::Md5); - return ctx->d()->engine->newString(QLatin1String(result.toHex()))->asReturnedValue(); + scope.result = scope.engine->newString(QLatin1String(result.toHex())); } /*! \qmlmethod string Qt::btoa(data) Binary to ASCII - this function returns a base64 encoding of \c data. */ -ReturnedValue QtObject::method_btoa(CallContext *ctx) +void QtObject::method_btoa(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() != 1) - V4THROW_ERROR("Qt.btoa(): Invalid arguments"); + if (callData->argc != 1) + THROW_GENERIC_ERROR("Qt.btoa(): Invalid arguments"); - QByteArray data = ctx->args()[0].toQStringNoThrow().toUtf8(); + QByteArray data = callData->args[0].toQStringNoThrow().toUtf8(); - return ctx->d()->engine->newString(QLatin1String(data.toBase64()))->asReturnedValue(); + scope.result = 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. */ -ReturnedValue QtObject::method_atob(CallContext *ctx) +void QtObject::method_atob(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() != 1) - V4THROW_ERROR("Qt.atob(): Invalid arguments"); + if (callData->argc != 1) + THROW_GENERIC_ERROR("Qt.atob(): Invalid arguments"); - QByteArray data = ctx->args()[0].toQStringNoThrow().toLatin1(); + QByteArray data = callData->args[0].toQStringNoThrow().toLatin1(); - return ctx->d()->engine->newString(QString::fromUtf8(QByteArray::fromBase64(data)))->asReturnedValue(); + scope.result = scope.engine->newString(QString::fromUtf8(QByteArray::fromBase64(data))); } /*! @@ -998,10 +1016,10 @@ QQmlEngine::quit() signal to the QCoreApplication::quit() slot. \sa exit() */ -ReturnedValue QtObject::method_quit(CallContext *ctx) +void QtObject::method_quit(const BuiltinFunction *, Scope &scope, CallData *) { - QQmlEnginePrivate::get(ctx->engine()->qmlEngine())->sendQuit(); - return QV4::Encode::undefined(); + QQmlEnginePrivate::get(scope.engine->qmlEngine())->sendQuit(); + scope.result = Encode::undefined(); } /*! @@ -1015,15 +1033,15 @@ ReturnedValue QtObject::method_quit(CallContext *ctx) \sa quit() */ -ReturnedValue QtObject::method_exit(CallContext *ctx) +void QtObject::method_exit(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() != 1) - V4THROW_ERROR("Qt.exit(): Invalid arguments"); + if (callData->argc != 1) + THROW_GENERIC_ERROR("Qt.exit(): Invalid arguments"); - int retCode = ctx->args()[0].toNumber(); + int retCode = callData->args[0].toNumber(); - QQmlEnginePrivate::get(ctx->engine()->qmlEngine())->sendExit(retCode); - return QV4::Encode::undefined(); + QQmlEnginePrivate::get(scope.engine->qmlEngine())->sendExit(retCode); + scope.result = QV4::Encode::undefined(); } /*! @@ -1050,11 +1068,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. */ -ReturnedValue QtObject::method_createQmlObject(CallContext *ctx) +void QtObject::method_createQmlObject(const BuiltinFunction *, Scope &scope, CallData *callData) { - Scope scope(ctx); - if (ctx->argc() < 2 || ctx->argc() > 3) - V4THROW_ERROR("Qt.createQmlObject(): Invalid arguments"); + if (callData->argc < 2 || callData->argc > 3) + THROW_GENERIC_ERROR("Qt.createQmlObject(): Invalid arguments"); struct Error { static ReturnedValue create(QV4::ExecutionEngine *v4, const QList<QQmlError> &errors) { @@ -1085,7 +1102,7 @@ ReturnedValue QtObject::method_createQmlObject(CallContext *ctx) } }; - QV8Engine *v8engine = ctx->d()->engine->v8Engine; + QV8Engine *v8engine = scope.engine->v8Engine; QQmlEngine *engine = v8engine->engine(); QQmlContextData *context = scope.engine->callingQmlContext(); @@ -1097,13 +1114,13 @@ ReturnedValue QtObject::method_createQmlObject(CallContext *ctx) effectiveContext = context->asQQmlContext(); Q_ASSERT(effectiveContext); - QString qml = ctx->args()[0].toQStringNoThrow(); + QString qml = callData->args[0].toQStringNoThrow(); if (qml.isEmpty()) - return QV4::Encode::null(); + RETURN_RESULT(Encode::null()); QUrl url; - if (ctx->argc() > 2) - url = QUrl(ctx->args()[2].toQStringNoThrow()); + if (callData->argc > 2) + url = QUrl(callData->args[2].toQStringNoThrow()); else url = QUrl(QLatin1String("inline")); @@ -1111,11 +1128,11 @@ ReturnedValue QtObject::method_createQmlObject(CallContext *ctx) url = context->resolvedUrl(url); QObject *parentArg = 0; - QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, ctx->args()[1]); + QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, callData->args[1]); if (!!qobjectWrapper) parentArg = qobjectWrapper->object(); if (!parentArg) - V4THROW_ERROR("Qt.createQmlObject(): Missing parent object"); + THROW_GENERIC_ERROR("Qt.createQmlObject(): Missing parent object"); QQmlTypeData *typeData = QQmlEnginePrivate::get(engine)->typeLoader.getType( qml.toUtf8(), url, QQmlTypeLoader::Synchronous); @@ -1126,12 +1143,12 @@ ReturnedValue QtObject::method_createQmlObject(CallContext *ctx) componentPrivate->progress = 1.0; if (component.isError()) { - ScopedValue v(scope, Error::create(ctx->d()->engine, component.errors())); - return ctx->engine()->throwError(v); + ScopedValue v(scope, Error::create(scope.engine, component.errors())); + RETURN_RESULT(scope.engine->throwError(v)); } if (!component.isReady()) - V4THROW_ERROR("Qt.createQmlObject(): Component is not ready"); + THROW_GENERIC_ERROR("Qt.createQmlObject(): Component is not ready"); QObject *obj = component.beginCreate(effectiveContext); if (obj) { @@ -1150,13 +1167,14 @@ ReturnedValue QtObject::method_createQmlObject(CallContext *ctx) component.completeCreate(); if (component.isError()) { - ScopedValue v(scope, Error::create(ctx->d()->engine, component.errors())); - return ctx->engine()->throwError(v); + ScopedValue v(scope, Error::create(scope.engine, component.errors())); + scope.result = scope.engine->throwError(v); + return; } Q_ASSERT(obj); - return QV4::QObjectWrapper::wrap(ctx->d()->engine, obj); + scope.result = QV4::QObjectWrapper::wrap(scope.engine, obj); } /*! @@ -1203,14 +1221,12 @@ 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()}. */ -ReturnedValue QtObject::method_createComponent(CallContext *ctx) +void QtObject::method_createComponent(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() < 1 || ctx->argc() > 3) - return ctx->engine()->throwError(QStringLiteral("Qt.createComponent(): Invalid arguments")); + if (callData->argc < 1 || callData->argc > 3) + THROW_GENERIC_ERROR("Qt.createComponent(): Invalid arguments"); - Scope scope(ctx); - - QV8Engine *v8engine = ctx->d()->engine->v8Engine; + QV8Engine *v8engine = scope.engine->v8Engine; QQmlEngine *engine = v8engine->engine(); QQmlContextData *context = scope.engine->callingQmlContext(); @@ -1219,41 +1235,41 @@ ReturnedValue QtObject::method_createComponent(CallContext *ctx) if (context->isPragmaLibraryContext) effectiveContext = 0; - QString arg = ctx->args()[0].toQStringNoThrow(); + QString arg = callData->args[0].toQStringNoThrow(); if (arg.isEmpty()) - return QV4::Encode::null(); + RETURN_RESULT(QV4::Encode::null()); QQmlComponent::CompilationMode compileMode = QQmlComponent::PreferSynchronous; QObject *parentArg = 0; int consumedCount = 1; - if (ctx->argc() > 1) { - ScopedValue lastArg(scope, ctx->args()[ctx->argc()-1]); + if (callData->argc > 1) { + ScopedValue lastArg(scope, callData->args[callData->argc-1]); // The second argument could be the mode enum - if (ctx->args()[1].isInteger()) { - int mode = ctx->args()[1].integerValue(); + if (callData->args[1].isInteger()) { + int mode = callData->args[1].integerValue(); if (mode != int(QQmlComponent::PreferSynchronous) && mode != int(QQmlComponent::Asynchronous)) - return ctx->engine()->throwError(QStringLiteral("Qt.createComponent(): Invalid arguments")); + THROW_GENERIC_ERROR("Qt.createComponent(): Invalid arguments"); compileMode = QQmlComponent::CompilationMode(mode); consumedCount += 1; } else { // The second argument could be the parent only if there are exactly two args - if ((ctx->argc() != 2) || !(lastArg->isObject() || lastArg->isNull())) - return ctx->engine()->throwError(QStringLiteral("Qt.createComponent(): Invalid arguments")); + if ((callData->argc != 2) || !(lastArg->isObject() || lastArg->isNull())) + THROW_GENERIC_ERROR("Qt.createComponent(): Invalid arguments"); } - if (consumedCount < ctx->argc()) { + if (consumedCount < callData->argc) { if (lastArg->isObject()) { Scoped<QObjectWrapper> qobjectWrapper(scope, lastArg); if (qobjectWrapper) parentArg = qobjectWrapper->object(); if (!parentArg) - return ctx->engine()->throwError(QStringLiteral("Qt.createComponent(): Invalid parent object")); + THROW_GENERIC_ERROR("Qt.createComponent(): Invalid parent object"); } else if (lastArg->isNull()) { parentArg = 0; } else { - return ctx->engine()->throwError(QStringLiteral("Qt.createComponent(): Invalid parent object")); + THROW_GENERIC_ERROR("Qt.createComponent(): Invalid parent object"); } } } @@ -1264,7 +1280,7 @@ ReturnedValue QtObject::method_createComponent(CallContext *ctx) QQmlData::get(c, true)->explicitIndestructibleSet = false; QQmlData::get(c)->indestructible = false; - return QV4::QObjectWrapper::wrap(ctx->d()->engine, c); + scope.result = QV4::QObjectWrapper::wrap(scope.engine, c); } /*! @@ -1287,18 +1303,18 @@ ReturnedValue QtObject::method_createComponent(CallContext *ctx) \sa Locale */ -ReturnedValue QtObject::method_locale(CallContext *ctx) +void QtObject::method_locale(const BuiltinFunction *, Scope &scope, CallData *callData) { QString code; - if (ctx->argc() > 1) - V4THROW_ERROR("locale() requires 0 or 1 argument"); - if (ctx->argc() == 1 && !ctx->args()[0].isString()) - V4THROW_TYPE("locale(): argument (locale code) must be a string"); + if (callData->argc > 1) + THROW_GENERIC_ERROR("locale() requires 0 or 1 argument"); + if (callData->argc == 1 && !callData->args[0].isString()) + THROW_TYPE_ERROR_WITH_MESSAGE("locale(): argument (locale code) must be a string"); - if (ctx->argc() == 1) - code = ctx->args()[0].toQStringNoThrow(); + if (callData->argc == 1) + code = callData->args[0].toQStringNoThrow(); - return QQmlLocale::locale(ctx->engine(), code); + scope.result = QQmlLocale::locale(scope.engine, code); } void Heap::QQmlBindingFunction::init(const QV4::FunctionObject *originalFunction) @@ -1360,62 +1376,62 @@ DEFINE_OBJECT_VTABLE(QQmlBindingFunction); \since 5.0 */ -ReturnedValue QtObject::method_binding(CallContext *ctx) +void QtObject::method_binding(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() != 1) - V4THROW_ERROR("binding() requires 1 argument"); - const QV4::FunctionObject *f = ctx->args()[0].as<FunctionObject>(); + if (callData->argc != 1) + THROW_GENERIC_ERROR("binding() requires 1 argument"); + const QV4::FunctionObject *f = callData->args[0].as<FunctionObject>(); if (!f) - V4THROW_TYPE("binding(): argument (binding expression) must be a function"); + THROW_TYPE_ERROR_WITH_MESSAGE("binding(): argument (binding expression) must be a function"); - return (ctx->d()->engine->memoryManager->allocObject<QQmlBindingFunction>(f))->asReturnedValue(); + scope.result = scope.engine->memoryManager->allocObject<QQmlBindingFunction>(f); } -ReturnedValue QtObject::method_get_platform(CallContext *ctx) +void QtObject::method_get_platform(const BuiltinFunction *, Scope &scope, CallData *callData) { // ### inefficient. Should be just a value based getter - Object *o = ctx->thisObject().as<Object>(); + Object *o = callData->thisObject.as<Object>(); if (!o) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); QtObject *qt = o->as<QtObject>(); if (!qt) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); if (!qt->d()->platform) // Only allocate a platform object once - qt->d()->platform = new QQmlPlatform(ctx->d()->engine->jsEngine()); + qt->d()->platform = new QQmlPlatform(scope.engine->jsEngine()); - return QV4::QObjectWrapper::wrap(ctx->d()->engine, qt->d()->platform); + scope.result = QV4::QObjectWrapper::wrap(scope.engine, qt->d()->platform); } -ReturnedValue QtObject::method_get_application(CallContext *ctx) +void QtObject::method_get_application(const BuiltinFunction *, Scope &scope, CallData *callData) { // ### inefficient. Should be just a value based getter - Object *o = ctx->thisObject().as<Object>(); + Object *o = callData->thisObject.as<Object>(); if (!o) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); QtObject *qt = o->as<QtObject>(); if (!qt) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); if (!qt->d()->application) // Only allocate an application object once - qt->d()->application = QQml_guiProvider()->application(ctx->d()->engine->jsEngine()); + qt->d()->application = QQml_guiProvider()->application(scope.engine->jsEngine()); - return QV4::QObjectWrapper::wrap(ctx->d()->engine, qt->d()->application); + scope.result = QV4::QObjectWrapper::wrap(scope.engine, qt->d()->application); } -ReturnedValue QtObject::method_get_inputMethod(CallContext *ctx) +void QtObject::method_get_inputMethod(const BuiltinFunction *, Scope &scope, CallData *) { QObject *o = QQml_guiProvider()->inputMethod(); - return QV4::QObjectWrapper::wrap(ctx->d()->engine, o); + scope.result = QV4::QObjectWrapper::wrap(scope.engine, o); } -ReturnedValue QtObject::method_get_styleHints(CallContext *ctx) +void QtObject::method_get_styleHints(const BuiltinFunction *, Scope &scope, CallData *) { QObject *o = QQml_guiProvider()->styleHints(); - return QV4::QObjectWrapper::wrap(ctx->d()->engine, o); + scope.result = QV4::QObjectWrapper::wrap(scope.engine, o); } @@ -1475,35 +1491,35 @@ static QString jsStack(QV4::ExecutionEngine *engine) { return stack; } -static QV4::ReturnedValue writeToConsole(ConsoleLogTypes logType, CallContext *ctx, - bool printStack = false) +static void writeToConsole(const BuiltinFunction *, Scope &scope, CallData *callData, + ConsoleLogTypes logType, bool printStack = false) { QLoggingCategory *loggingCategory = 0; QString result; - QV4::ExecutionEngine *v4 = ctx->d()->engine; + QV4::ExecutionEngine *v4 = scope.engine; int start = 0; - if (ctx->argc() > 0) { - if (const QObjectWrapper* wrapper = ctx->args()[0].as<QObjectWrapper>()) { + if (callData->argc > 0) { + if (const QObjectWrapper* wrapper = callData->args[0].as<QObjectWrapper>()) { if (QQmlLoggingCategory* category = qobject_cast<QQmlLoggingCategory*>(wrapper->object())) { if (category->category()) loggingCategory = category->category(); else - V4THROW_ERROR("A QmlLoggingCatgory was provided without a valid name"); + THROW_GENERIC_ERROR("A QmlLoggingCatgory was provided without a valid name"); start = 1; } } } - for (int i = start; i < ctx->argc(); ++i) { + for (int i = start; i < callData->argc; ++i) { if (i != start) result.append(QLatin1Char(' ')); - if (ctx->args()[i].as<ArrayObject>()) - result += QLatin1Char('[') + ctx->args()[i].toQStringNoThrow() + QLatin1Char(']'); + if (callData->args[i].as<ArrayObject>()) + result += QLatin1Char('[') + callData->args[i].toQStringNoThrow() + QLatin1Char(']'); else - result.append(ctx->args()[i].toQStringNoThrow()); + result.append(callData->args[i].toQStringNoThrow()); } if (printStack) @@ -1540,32 +1556,32 @@ static QV4::ReturnedValue writeToConsole(ConsoleLogTypes logType, CallContext *c break; } - return QV4::Encode::undefined(); + scope.result = QV4::Encode::undefined(); } DEFINE_OBJECT_VTABLE(ConsoleObject); -QV4::ReturnedValue ConsoleObject::method_error(CallContext *ctx) +void ConsoleObject::method_error(const BuiltinFunction *b, Scope &scope, CallData *callData) { - return writeToConsole(Error, ctx); + writeToConsole(b, scope, callData, Error); } -QV4::ReturnedValue ConsoleObject::method_log(CallContext *ctx) +void ConsoleObject::method_log(const BuiltinFunction *b, Scope &scope, CallData *callData) { //console.log //console.debug //print - return writeToConsole(Log, ctx); + writeToConsole(b, scope, callData, Log); } -QV4::ReturnedValue ConsoleObject::method_info(CallContext *ctx) +void ConsoleObject::method_info(const BuiltinFunction *b, Scope &scope, CallData *callData) { - return writeToConsole(Info, ctx); + writeToConsole(b, scope, callData, Info); } -QV4::ReturnedValue ConsoleObject::method_profile(CallContext *ctx) +void ConsoleObject::method_profile(const BuiltinFunction *, Scope &scope, CallData *) { - QV4::ExecutionEngine *v4 = ctx->d()->engine; + QV4::ExecutionEngine *v4 = scope.engine; QV4::StackFrame frame = v4->currentStackFrame(); const QByteArray baSource = frame.source.toUtf8(); @@ -1579,12 +1595,12 @@ QV4::ReturnedValue ConsoleObject::method_profile(CallContext *ctx) logger.debug("Profiling started."); } - return QV4::Encode::undefined(); + scope.result = QV4::Encode::undefined(); } -QV4::ReturnedValue ConsoleObject::method_profileEnd(CallContext *ctx) +void ConsoleObject::method_profileEnd(const BuiltinFunction *, Scope &scope, CallData *) { - QV4::ExecutionEngine *v4 = ctx->d()->engine; + QV4::ExecutionEngine *v4 = scope.engine; QV4::StackFrame frame = v4->currentStackFrame(); const QByteArray baSource = frame.source.toUtf8(); @@ -1599,46 +1615,46 @@ QV4::ReturnedValue ConsoleObject::method_profileEnd(CallContext *ctx) logger.debug("Profiling ended."); } - return QV4::Encode::undefined(); + scope.result = QV4::Encode::undefined(); } -QV4::ReturnedValue ConsoleObject::method_time(CallContext *ctx) +void ConsoleObject::method_time(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() != 1) - V4THROW_ERROR("console.time(): Invalid arguments"); + if (callData->argc != 1) + THROW_GENERIC_ERROR("console.time(): Invalid arguments"); - QV8Engine *v8engine = ctx->d()->engine->v8Engine; + QV8Engine *v8engine = scope.engine->v8Engine; - QString name = ctx->args()[0].toQStringNoThrow(); + QString name = callData->args[0].toQStringNoThrow(); v8engine->startTimer(name); - return QV4::Encode::undefined(); + scope.result = QV4::Encode::undefined(); } -QV4::ReturnedValue ConsoleObject::method_timeEnd(CallContext *ctx) +void ConsoleObject::method_timeEnd(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() != 1) - V4THROW_ERROR("console.timeEnd(): Invalid arguments"); + if (callData->argc != 1) + THROW_GENERIC_ERROR("console.timeEnd(): Invalid arguments"); - QV8Engine *v8engine = ctx->d()->engine->v8Engine; + QV8Engine *v8engine = scope.engine->v8Engine; - QString name = ctx->args()[0].toQStringNoThrow(); + QString name = callData->args[0].toQStringNoThrow(); bool wasRunning; qint64 elapsed = v8engine->stopTimer(name, &wasRunning); if (wasRunning) { qDebug("%s: %llims", qPrintable(name), elapsed); } - return QV4::Encode::undefined(); + scope.result = QV4::Encode::undefined(); } -QV4::ReturnedValue ConsoleObject::method_count(CallContext *ctx) +void ConsoleObject::method_count(const BuiltinFunction *, Scope &scope, CallData *callData) { // first argument: name to print. Ignore any additional arguments QString name; - if (ctx->argc() > 0) - name = ctx->args()[0].toQStringNoThrow(); + if (callData->argc > 0) + name = callData->args[0].toQStringNoThrow(); - QV4::ExecutionEngine *v4 = ctx->d()->engine; - QV8Engine *v8engine = ctx->d()->engine->v8Engine; + QV4::ExecutionEngine *v4 = scope.engine; + QV8Engine *v8engine = scope.engine->v8Engine; QV4::StackFrame frame = v4->currentStackFrame(); @@ -1651,15 +1667,15 @@ QV4::ReturnedValue ConsoleObject::method_count(CallContext *ctx) qPrintable(frame.function)) .debug("%s", qPrintable(message)); - return QV4::Encode::undefined(); + scope.result = QV4::Encode::undefined(); } -QV4::ReturnedValue ConsoleObject::method_trace(CallContext *ctx) +void ConsoleObject::method_trace(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() != 0) - V4THROW_ERROR("console.trace(): Invalid arguments"); + if (callData->argc != 0) + THROW_GENERIC_ERROR("console.trace(): Invalid arguments"); - QV4::ExecutionEngine *v4 = ctx->d()->engine; + QV4::ExecutionEngine *v4 = scope.engine; QString stack = jsStack(v4); @@ -1668,28 +1684,28 @@ QV4::ReturnedValue ConsoleObject::method_trace(CallContext *ctx) frame.function.toUtf8().constData()) .debug("%s", qPrintable(stack)); - return QV4::Encode::undefined(); + scope.result = QV4::Encode::undefined(); } -QV4::ReturnedValue ConsoleObject::method_warn(CallContext *ctx) +void ConsoleObject::method_warn(const BuiltinFunction *b, Scope &scope, CallData *callData) { - return writeToConsole(Warn, ctx); + return writeToConsole(b, scope, callData, Warn); } -QV4::ReturnedValue ConsoleObject::method_assert(CallContext *ctx) +void ConsoleObject::method_assert(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() == 0) - V4THROW_ERROR("console.assert(): Missing argument"); + if (callData->argc == 0) + THROW_GENERIC_ERROR("console.assert(): Missing argument"); - QV4::ExecutionEngine *v4 = ctx->d()->engine; + QV4::ExecutionEngine *v4 = scope.engine; - if (!ctx->args()[0].toBoolean()) { + if (!callData->args[0].toBoolean()) { QString message; - for (int i = 1; i < ctx->argc(); ++i) { + for (int i = 1; i < callData->argc; ++i) { if (i != 1) message.append(QLatin1Char(' ')); - message.append(ctx->args()[i].toQStringNoThrow()); + message.append(callData->args[i].toQStringNoThrow()); } QString stack = jsStack(v4); @@ -1700,17 +1716,17 @@ QV4::ReturnedValue ConsoleObject::method_assert(CallContext *ctx) .critical("%s\n%s",qPrintable(message), qPrintable(stack)); } - return QV4::Encode::undefined(); + scope.result = QV4::Encode::undefined(); } -QV4::ReturnedValue ConsoleObject::method_exception(CallContext *ctx) +void ConsoleObject::method_exception(const BuiltinFunction *b, Scope &scope, CallData *callData) { - if (ctx->argc() == 0) - V4THROW_ERROR("console.exception(): Missing argument"); + if (callData->argc == 0) + THROW_GENERIC_ERROR("console.exception(): Missing argument"); - writeToConsole(Error, ctx, true); + writeToConsole(b, scope, callData, Error, true); - return QV4::Encode::undefined(); + scope.result = QV4::Encode::undefined(); } @@ -1766,38 +1782,38 @@ void QV4::GlobalExtensions::init(Object *globalObject, QJSEngine::Extensions ext \sa {Internationalization and Localization with Qt Quick} */ -ReturnedValue GlobalExtensions::method_qsTranslate(CallContext *ctx) -{ - if (ctx->argc() < 2) - V4THROW_ERROR("qsTranslate() requires at least two arguments"); - if (!ctx->args()[0].isString()) - V4THROW_ERROR("qsTranslate(): first argument (context) must be a string"); - if (!ctx->args()[1].isString()) - V4THROW_ERROR("qsTranslate(): second argument (sourceText) must be a string"); - if ((ctx->argc() > 2) && !ctx->args()[2].isString()) - V4THROW_ERROR("qsTranslate(): third argument (disambiguation) must be a string"); - - QString context = ctx->args()[0].toQStringNoThrow(); - QString text = ctx->args()[1].toQStringNoThrow(); +void GlobalExtensions::method_qsTranslate(const BuiltinFunction *, Scope &scope, CallData *callData) +{ + 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()) + 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 (ctx->argc() > 2) comment = ctx->args()[2].toQStringNoThrow(); + if (callData->argc > 2) comment = callData->args[2].toQStringNoThrow(); int i = 3; - if (ctx->argc() > i && ctx->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 (ctx->argc() > i) - n = ctx->args()[i].toInt32(); + if (callData->argc > i) + n = callData->args[i].toInt32(); QString result = QCoreApplication::translate(context.toUtf8().constData(), text.toUtf8().constData(), comment.toUtf8().constData(), n); - return ctx->d()->engine->newString(result)->asReturnedValue(); + scope.result = scope.engine->newString(result); } /*! @@ -1822,11 +1838,12 @@ ReturnedValue GlobalExtensions::method_qsTranslate(CallContext *ctx) \sa {Internationalization and Localization with Qt Quick} */ -ReturnedValue GlobalExtensions::method_qsTranslateNoOp(CallContext *ctx) +void GlobalExtensions::method_qsTranslateNoOp(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() < 2) - return QV4::Encode::undefined(); - return ctx->args()[1].asReturnedValue(); + if (callData->argc < 2) + scope.result = QV4::Encode::undefined(); + else + scope.result = callData->args[1]; } /*! @@ -1846,18 +1863,17 @@ ReturnedValue GlobalExtensions::method_qsTranslateNoOp(CallContext *ctx) \sa {Internationalization and Localization with Qt Quick} */ -ReturnedValue GlobalExtensions::method_qsTr(CallContext *ctx) -{ - if (ctx->argc() < 1) - V4THROW_ERROR("qsTr() requires at least one argument"); - if (!ctx->args()[0].isString()) - V4THROW_ERROR("qsTr(): first argument (sourceText) must be a string"); - if ((ctx->argc() > 1) && !ctx->args()[1].isString()) - V4THROW_ERROR("qsTr(): second argument (disambiguation) must be a string"); - if ((ctx->argc() > 2) && !ctx->args()[2].isNumber()) - V4THROW_ERROR("qsTr(): third argument (n) must be a number"); - - Scope scope(ctx); +void GlobalExtensions::method_qsTr(const BuiltinFunction *, Scope &scope, CallData *callData) +{ + 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()) + THROW_GENERIC_ERROR("qsTr(): second argument (disambiguation) must be a string"); + if ((callData->argc > 2) && !callData->args[2].isNumber()) + THROW_GENERIC_ERROR("qsTr(): third argument (n) must be a number"); + QString context; if (QQmlContextData *ctxt = scope.engine->callingQmlContext()) { QString path = ctxt->urlString(); @@ -1866,7 +1882,7 @@ ReturnedValue GlobalExtensions::method_qsTr(CallContext *ctx) int length = lastDot - (lastSlash + 1); context = (lastSlash > -1) ? path.mid(lastSlash + 1, (length > -1) ? length : -1) : QString(); } else { - ExecutionContext *parentCtx = scope.engine->parentContext(ctx); + ExecutionContext *parentCtx = scope.engine->currentContext; // The first non-empty source URL in the call stack determines the translation context. while (!!parentCtx && context.isEmpty()) { if (QV4::CompiledData::CompilationUnit *unit = parentCtx->d()->compilationUnit) { @@ -1885,18 +1901,18 @@ ReturnedValue GlobalExtensions::method_qsTr(CallContext *ctx) } } - QString text = ctx->args()[0].toQStringNoThrow(); + QString text = callData->args[0].toQStringNoThrow(); QString comment; - if (ctx->argc() > 1) - comment = ctx->args()[1].toQStringNoThrow(); + if (callData->argc > 1) + comment = callData->args[1].toQStringNoThrow(); int n = -1; - if (ctx->argc() > 2) - n = ctx->args()[2].toInt32(); + if (callData->argc > 2) + n = callData->args[2].toInt32(); QString result = QCoreApplication::translate(context.toUtf8().constData(), text.toUtf8().constData(), comment.toUtf8().constData(), n); - return ctx->d()->engine->newString(result)->asReturnedValue(); + scope.result = scope.engine->newString(result); } /*! @@ -1921,11 +1937,12 @@ ReturnedValue GlobalExtensions::method_qsTr(CallContext *ctx) \sa {Internationalization and Localization with Qt Quick} */ -ReturnedValue GlobalExtensions::method_qsTrNoOp(CallContext *ctx) +void GlobalExtensions::method_qsTrNoOp(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() < 1) - return QV4::Encode::undefined(); - return ctx->args()[0].asReturnedValue(); + if (callData->argc < 1) + scope.result = QV4::Encode::undefined(); + else + scope.result = callData->args[0]; } /*! @@ -1958,20 +1975,20 @@ ReturnedValue GlobalExtensions::method_qsTrNoOp(CallContext *ctx) \sa QT_TRID_NOOP(), {Internationalization and Localization with Qt Quick} */ -ReturnedValue GlobalExtensions::method_qsTrId(CallContext *ctx) +void GlobalExtensions::method_qsTrId(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() < 1) - V4THROW_ERROR("qsTrId() requires at least one argument"); - if (!ctx->args()[0].isString()) - V4THROW_TYPE("qsTrId(): first argument (id) must be a string"); - if (ctx->argc() > 1 && !ctx->args()[1].isNumber()) - V4THROW_TYPE("qsTrId(): second argument (n) must be a number"); + 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()) + THROW_TYPE_ERROR_WITH_MESSAGE("qsTrId(): second argument (n) must be a number"); int n = -1; - if (ctx->argc() > 1) - n = ctx->args()[1].toInt32(); + if (callData->argc > 1) + n = callData->args[1].toInt32(); - return ctx->d()->engine->newString(qtTrId(ctx->args()[0].toQStringNoThrow().toUtf8().constData(), n))->asReturnedValue(); + scope.result = scope.engine->newString(qtTrId(callData->args[0].toQStringNoThrow().toUtf8().constData(), n)); } /*! @@ -1990,41 +2007,41 @@ ReturnedValue GlobalExtensions::method_qsTrId(CallContext *ctx) \sa qsTrId(), {Internationalization and Localization with Qt Quick} */ -ReturnedValue GlobalExtensions::method_qsTrIdNoOp(CallContext *ctx) +void GlobalExtensions::method_qsTrIdNoOp(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() < 1) - return QV4::Encode::undefined(); - return ctx->args()[0].asReturnedValue(); + if (callData->argc < 1) + scope.result = QV4::Encode::undefined(); + else + scope.result = callData->args[0]; } #endif // translation -QV4::ReturnedValue GlobalExtensions::method_gc(CallContext *ctx) +void GlobalExtensions::method_gc(const BuiltinFunction *, Scope &scope, CallData *) { - ctx->d()->engine->memoryManager->runGC(); + scope.engine->memoryManager->runGC(); - return QV4::Encode::undefined(); + scope.result = QV4::Encode::undefined(); } -ReturnedValue GlobalExtensions::method_string_arg(CallContext *ctx) +void GlobalExtensions::method_string_arg(const BuiltinFunction *, Scope &scope, CallData *callData) { - if (ctx->argc() != 1) - V4THROW_ERROR("String.arg(): Invalid arguments"); + if (callData->argc != 1) + THROW_GENERIC_ERROR("String.arg(): Invalid arguments"); - QString value = ctx->thisObject().toQString(); + QString value = callData->thisObject.toQString(); - QV4::Scope scope(ctx); - QV4::ScopedValue arg(scope, ctx->args()[0]); + QV4::ScopedValue arg(scope, callData->args[0]); if (arg->isInteger()) - return ctx->d()->engine->newString(value.arg(arg->integerValue()))->asReturnedValue(); + RETURN_RESULT(scope.engine->newString(value.arg(arg->integerValue()))); else if (arg->isDouble()) - return ctx->d()->engine->newString(value.arg(arg->doubleValue()))->asReturnedValue(); + RETURN_RESULT(scope.engine->newString(value.arg(arg->doubleValue()))); else if (arg->isBoolean()) - return ctx->d()->engine->newString(value.arg(arg->booleanValue()))->asReturnedValue(); + RETURN_RESULT(scope.engine->newString(value.arg(arg->booleanValue()))); - return ctx->d()->engine->newString(value.arg(arg->toQString()))->asReturnedValue(); + RETURN_RESULT(scope.engine->newString(value.arg(arg->toQString()))); } /*! @@ -2047,10 +2064,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. */ -ReturnedValue QtObject::method_callLater(CallContext *ctx) +void QtObject::method_callLater(const BuiltinFunction *b, Scope &scope, CallData *callData) { - QV8Engine *v8engine = ctx->engine()->v8Engine; - return v8engine->delayedCallQueue()->addUniquelyAndExecuteLater(ctx); + QV8Engine *v8engine = scope.engine->v8Engine; + v8engine->delayedCallQueue()->addUniquelyAndExecuteLater(b, scope, callData); } QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h index fe43532647..21613b7c10 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 ReturnedValue method_isQtObject(CallContext *ctx); - static ReturnedValue method_rgba(CallContext *ctx); - static ReturnedValue method_hsla(CallContext *ctx); - static ReturnedValue method_hsva(CallContext *ctx); - static ReturnedValue method_colorEqual(CallContext *ctx); - static ReturnedValue method_font(CallContext *ctx); - static ReturnedValue method_rect(CallContext *ctx); - static ReturnedValue method_point(CallContext *ctx); - static ReturnedValue method_size(CallContext *ctx); - static ReturnedValue method_vector2d(CallContext *ctx); - static ReturnedValue method_vector3d(CallContext *ctx); - static ReturnedValue method_vector4d(CallContext *ctx); - static ReturnedValue method_quaternion(CallContext *ctx); - static ReturnedValue method_matrix4x4(CallContext *ctx); - static ReturnedValue method_lighter(CallContext *ctx); - static ReturnedValue method_darker(CallContext *ctx); - static ReturnedValue method_tint(CallContext *ctx); - static ReturnedValue method_formatDate(CallContext *ctx); - static ReturnedValue method_formatTime(CallContext *ctx); - static ReturnedValue method_formatDateTime(CallContext *ctx); - static ReturnedValue method_openUrlExternally(CallContext *ctx); - static ReturnedValue method_fontFamilies(CallContext *ctx); - static ReturnedValue method_md5(CallContext *ctx); - static ReturnedValue method_btoa(CallContext *ctx); - static ReturnedValue method_atob(CallContext *ctx); - static ReturnedValue method_quit(CallContext *ctx); - static ReturnedValue method_exit(CallContext *ctx); - static ReturnedValue method_resolvedUrl(CallContext *ctx); - static ReturnedValue method_createQmlObject(CallContext *ctx); - static ReturnedValue method_createComponent(CallContext *ctx); - static ReturnedValue method_locale(CallContext *ctx); - static ReturnedValue method_binding(CallContext *ctx); - - static ReturnedValue method_get_platform(CallContext *ctx); - static ReturnedValue method_get_application(CallContext *ctx); - static ReturnedValue method_get_inputMethod(CallContext *ctx); - static ReturnedValue method_get_styleHints(CallContext *ctx); - - static ReturnedValue method_callLater(CallContext *ctx); + 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); private: void addAll(); @@ -142,18 +142,18 @@ struct ConsoleObject : Object { V4_OBJECT2(ConsoleObject, Object) - static ReturnedValue method_error(CallContext *ctx); - static ReturnedValue method_log(CallContext *ctx); - static ReturnedValue method_info(CallContext *ctx); - static ReturnedValue method_profile(CallContext *ctx); - static ReturnedValue method_profileEnd(CallContext *ctx); - static ReturnedValue method_time(CallContext *ctx); - static ReturnedValue method_timeEnd(CallContext *ctx); - static ReturnedValue method_count(CallContext *ctx); - static ReturnedValue method_trace(CallContext *ctx); - static ReturnedValue method_warn(CallContext *ctx); - static ReturnedValue method_assert(CallContext *ctx); - static ReturnedValue method_exception(CallContext *ctx); + 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); }; @@ -161,17 +161,17 @@ struct Q_QML_PRIVATE_EXPORT GlobalExtensions { static void init(Object *globalObject, QJSEngine::Extensions extensions); #if QT_CONFIG(translation) - static ReturnedValue method_qsTranslate(CallContext *ctx); - static ReturnedValue method_qsTranslateNoOp(CallContext *ctx); - static ReturnedValue method_qsTr(CallContext *ctx); - static ReturnedValue method_qsTrNoOp(CallContext *ctx); - static ReturnedValue method_qsTrId(CallContext *ctx); - static ReturnedValue method_qsTrIdNoOp(CallContext *ctx); + 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); #endif - static ReturnedValue method_gc(CallContext *ctx); + static void method_gc(const BuiltinFunction *, Scope &scope, CallData *callData); // on String:prototype - static ReturnedValue method_string_arg(CallContext *ctx); + static void method_string_arg(const BuiltinFunction *, Scope &scope, CallData *callData); }; diff --git a/src/qml/qml/v8/qv4domerrors_p.h b/src/qml/qml/v8/qv4domerrors_p.h index faa9dd8bc7..a9bdbe01ae 100644 --- a/src/qml/qml/v8/qv4domerrors_p.h +++ b/src/qml/qml/v8/qv4domerrors_p.h @@ -74,11 +74,12 @@ QT_BEGIN_NAMESPACE #define DOMEXCEPTION_VALIDATION_ERR 16 #define DOMEXCEPTION_TYPE_MISMATCH_ERR 17 -#define V4THROW_DOM(error, string) { \ +#define THROW_DOM(error, string) { \ 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))); \ - return ctx->engine()->throwError(ex); \ + scope.result = scope.engine->throwError(ex); \ + return; \ } namespace QV4 { diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index c0d75cae33..a5878dcffd 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -1795,24 +1795,26 @@ int QQmlDelegateModelItemMetaType::parseGroups(const QV4::Value &groups) const return groupFlags; } -QV4::ReturnedValue QQmlDelegateModelItem::get_model(QV4::CallContext *ctx) +void QQmlDelegateModelItem::get_model(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, ctx->thisObject().as<QQmlDelegateModelItemObject>()); - if (!o) - return ctx->engine()->throwTypeError(QStringLiteral("Not a valid VisualData object")); + 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->d()->item->metaType->model) - return QV4::Encode::undefined(); + RETURN_UNDEFINED(); - return o->d()->item->get(); + scope.result = o->d()->item->get(); } -QV4::ReturnedValue QQmlDelegateModelItem::get_groups(QV4::CallContext *ctx) +void QQmlDelegateModelItem::get_groups(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, ctx->thisObject().as<QQmlDelegateModelItemObject>()); - if (!o) - return ctx->engine()->throwTypeError(QStringLiteral("Not a valid VisualData object")); + QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>()); + if (!o) { + scope.result = scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")); + return; + } QStringList groups; for (int i = 1; i < o->d()->item->metaType->groupCount; ++i) { @@ -1820,27 +1822,29 @@ QV4::ReturnedValue QQmlDelegateModelItem::get_groups(QV4::CallContext *ctx) groups.append(o->d()->item->metaType->groupNames.at(i - 1)); } - return scope.engine->fromVariant(groups); + scope.result = scope.engine->fromVariant(groups); } -QV4::ReturnedValue QQmlDelegateModelItem::set_groups(QV4::CallContext *ctx) +void QQmlDelegateModelItem::set_groups(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, ctx->thisObject().as<QQmlDelegateModelItemObject>()); - if (!o) - return ctx->engine()->throwTypeError(QStringLiteral("Not a valid VisualData object")); - if (!ctx->argc()) - return ctx->engine()->throwTypeError(); + QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>()); + if (!o) { + scope.result = scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")); + return; + } + + if (!callData->argc) + THROW_TYPE_ERROR(); if (!o->d()->item->metaType->model) - return QV4::Encode::undefined(); + RETURN_UNDEFINED(); QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(o->d()->item->metaType->model); - const int groupFlags = model->m_cacheMetaType->parseGroups(ctx->args()[0]); + const int groupFlags = model->m_cacheMetaType->parseGroups(callData->args[0]); 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); - return QV4::Encode::undefined(); + scope.result = QV4::Encode::undefined(); } QV4::ReturnedValue QQmlDelegateModelItem::get_member(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &) @@ -3234,28 +3238,25 @@ struct QQmlDelegateModelGroupChange : QV4::Object return e->memoryManager->allocObject<QQmlDelegateModelGroupChange>(); } - static QV4::ReturnedValue method_get_index(QV4::CallContext *ctx) { - QV4::Scope scope(ctx); - QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, ctx->thisObject().as<QQmlDelegateModelGroupChange>()); + static void method_get_index(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { + QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, callData->thisObject.as<QQmlDelegateModelGroupChange>()); if (!that) - return ctx->engine()->throwTypeError(); - return QV4::Encode(that->d()->change.index); + THROW_TYPE_ERROR(); + scope.result = QV4::Encode(that->d()->change.index); } - static QV4::ReturnedValue method_get_count(QV4::CallContext *ctx) { - QV4::Scope scope(ctx); - QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, ctx->thisObject().as<QQmlDelegateModelGroupChange>()); + static void method_get_count(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { + QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, callData->thisObject.as<QQmlDelegateModelGroupChange>()); if (!that) - return ctx->engine()->throwTypeError(); - return QV4::Encode(that->d()->change.count); + THROW_TYPE_ERROR(); + scope.result = QV4::Encode(that->d()->change.count); } - static QV4::ReturnedValue method_get_moveId(QV4::CallContext *ctx) { - QV4::Scope scope(ctx); - QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, ctx->thisObject().as<QQmlDelegateModelGroupChange>()); + static void method_get_moveId(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { + QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, callData->thisObject.as<QQmlDelegateModelGroupChange>()); if (!that) - return ctx->engine()->throwTypeError(); + THROW_TYPE_ERROR(); if (that->d()->change.moveId < 0) - return QV4::Encode::undefined(); - return QV4::Encode(that->d()->change.moveId); + RETURN_UNDEFINED(); + scope.result = 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 4c2841b8ba..cb4a1f79ba 100644 --- a/src/qml/types/qqmldelegatemodel_p_p.h +++ b/src/qml/types/qqmldelegatemodel_p_p.h @@ -131,9 +131,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 QV4::ReturnedValue get_model(QV4::CallContext *ctx); - static QV4::ReturnedValue get_groups(QV4::CallContext *ctx); - static QV4::ReturnedValue set_groups(QV4::CallContext *ctx); + 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_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/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp index 5f716da17a..f35e17c34d 100644 --- a/src/qml/types/qquickworkerscript.cpp +++ b/src/qml/types/qquickworkerscript.cpp @@ -185,7 +185,7 @@ public: int m_nextId; - static QV4::ReturnedValue method_sendMessage(QV4::CallContext *ctx); + static void method_sendMessage(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); signals: void stopThread(); @@ -292,14 +292,13 @@ QQuickWorkerScriptEnginePrivate::QQuickWorkerScriptEnginePrivate(QQmlEngine *eng { } -QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::method_sendMessage(QV4::CallContext *ctx) +void QQuickWorkerScriptEnginePrivate::method_sendMessage(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - WorkerEngine *engine = (WorkerEngine*)ctx->engine()->v8Engine; + WorkerEngine *engine = (WorkerEngine*)scope.engine->v8Engine; - int id = ctx->argc() > 1 ? ctx->args()[1].toInt32() : 0; + int id = callData->argc > 1 ? callData->args[1].toInt32() : 0; - QV4::Scope scope(ctx); - QV4::ScopedValue v(scope, ctx->argument(2)); + QV4::ScopedValue v(scope, callData->argument(2)); QByteArray data = QV4::Serialize::serialize(v, scope.engine); QMutexLocker locker(&engine->p->m_lock); @@ -307,7 +306,7 @@ QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::method_sendMessage(QV4::Call if (script && script->owner) QCoreApplication::postEvent(script->owner, new WorkerDataEvent(0, data)); - return QV4::Encode::undefined(); + scope.result = QV4::Encode::undefined(); } QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::getWorker(WorkerScript *script) |