diff options
Diffstat (limited to 'src/qml/jsruntime/qv4function.cpp')
-rw-r--r-- | src/qml/jsruntime/qv4function.cpp | 257 |
1 files changed, 187 insertions, 70 deletions
diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 51a9b92967..ae36b563e0 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -1,83 +1,114 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#include "qml/qqmlprivate.h" #include "qv4function_p.h" -#include "qv4functionobject_p.h" #include "qv4managed_p.h" #include "qv4string_p.h" #include "qv4value_p.h" #include "qv4engine_p.h" -#include "qv4lookup_p.h" #include <private/qv4mm_p.h> #include <private/qv4identifiertable_p.h> +#include <private/qv4functiontable_p.h> #include <assembler/MacroAssemblerCodeRef.h> #include <private/qv4vme_moth_p.h> #include <private/qqmlglobal_p.h> +#include <private/qv4jscall_p.h> +#include <private/qqmlpropertycachecreator_p.h> QT_BEGIN_NAMESPACE -using namespace QV4; +namespace QV4 { + +bool Function::call(QObject *thisObject, void **a, const QMetaType *types, int argc, + ExecutionContext *context) +{ + if (kind != AotCompiled) { + return QV4::convertAndCall( + context->engine(), thisObject, a, types, argc, + [this, context](const Value *thisObject, const Value *argv, int argc) { + return call(thisObject, argv, argc, context); + }); + } -ReturnedValue Function::call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context) { ExecutionEngine *engine = context->engine(); - CppStackFrame frame; - frame.init(engine, this, argv, argc); - frame.setupJSFrame(engine->jsStackTop, Value::undefinedValue(), context->d(), - thisObject ? *thisObject : Value::undefinedValue(), - Value::undefinedValue()); + MetaTypesStackFrame frame; + frame.init(this, thisObject, context, a, types, argc); + frame.push(engine); + Moth::VME::exec(&frame, engine); + frame.pop(engine); + return !frame.isReturnValueUndefined(); +} - frame.push(); +static ReturnedValue doCall( + QV4::Function *self, const QV4::Value *thisObject, const QV4::Value *argv, int argc, + QV4::ExecutionContext *context) +{ + ExecutionEngine *engine = context->engine(); + JSTypesStackFrame frame; + frame.init(self, argv, argc); + frame.setupJSFrame(engine->jsStackTop, Value::undefinedValue(), context->d(), + thisObject ? *thisObject : Value::undefinedValue()); engine->jsStackTop += frame.requiredJSStackFrameSize(); - + frame.push(engine); ReturnedValue result = Moth::VME::exec(&frame, engine); + frame.pop(engine); + return result; +} - frame.pop(); +ReturnedValue Function::call( + const Value *thisObject, const Value *argv, int argc, ExecutionContext *context) { + switch (kind) { + case AotCompiled: + return QV4::convertAndCall( + context->engine(), &aotCompiledFunction, thisObject, argv, argc, + [this, context]( + QObject *thisObject, void **a, const QMetaType *types, int argc) { + call(thisObject, a, types, argc, context); + }); + case JsTyped: + return QV4::coerceAndCall( + context->engine(), &jsTypedFunction, compiledFunction, argv, argc, + [this, context, thisObject](const Value *argv, int argc) { + return doCall(this, thisObject, argv, argc, context); + }); + default: + break; + } - return result; + return doCall(this, thisObject, argv, argc, context); } -Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function) - : compiledFunction(function) - , compilationUnit(unit) +Function *Function::create(ExecutionEngine *engine, ExecutableCompilationUnit *unit, + const CompiledData::Function *function, + const QQmlPrivate::AOTCompiledFunction *aotFunction) +{ + return new Function(engine, unit, function, aotFunction); +} + +void Function::destroy() +{ + delete this; +} + +void Function::mark(MarkStack *ms) +{ + if (internalClass) + internalClass->mark(ms); +} + +static bool isSpecificType(const CompiledData::ParameterType &type) +{ + return type.typeNameIndexOrCommonType() + != (type.indexIsCommonType() ? quint32(CompiledData::CommonType::Invalid) : 0); +} + +Function::Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit, + const CompiledData::Function *function, + const QQmlPrivate::AOTCompiledFunction *aotFunction) + : FunctionData(engine, unit) + , compiledFunction(function) , codeData(function->code()) - , jittedCode(nullptr) - , codeRef(nullptr) { Scope scope(engine); Scoped<InternalClass> ic(scope, engine->internalClasses(EngineBase::Class_CallContext)); @@ -87,17 +118,80 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, for (quint32 i = 0; i < compiledFunction->nLocals; ++i) ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable); - const quint32_le *formalsIndices = compiledFunction->formalsTable(); - for (quint32 i = 0; i < compiledFunction->nFormals; ++i) - ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[formalsIndices[i]]), Attr_NotConfigurable); - internalClass = ic->d(); + const CompiledData::Parameter *formalsIndices = compiledFunction->formalsTable(); + bool enforceJsTypes = !unit->ignoresFunctionSignature(); + + for (quint32 i = 0; i < compiledFunction->nFormals; ++i) { + ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[formalsIndices[i].nameIndex]), Attr_NotConfigurable); + if (enforceJsTypes && !isSpecificType(formalsIndices[i].type)) + enforceJsTypes = false; + } + internalClass.set(engine, ic->d()); nFormals = compiledFunction->nFormals; + + if (!enforceJsTypes) + return; + + if (aotFunction) { + aotCompiledCode = aotFunction->functionPtr; + new (&aotCompiledFunction) AOTCompiledFunction; + kind = AotCompiled; + aotCompiledFunction.types.resize(aotFunction->numArguments + 1); + aotFunction->signature(unit, aotCompiledFunction.types.data()); + return; + } + + // If a function has any typed arguments, but an untyped return value, the return value is void. + // If it doesn't have any arguments at all and the return value is untyped, the function is + // untyped. Users can specifically set the return type to "void" to have it enforced. + if (nFormals == 0 && !isSpecificType(compiledFunction->returnType)) + return; + + QQmlTypeLoader *typeLoader = engine->typeLoader(); + + auto findQmlType = [&](const CompiledData::ParameterType ¶m) { + const quint32 type = param.typeNameIndexOrCommonType(); + if (param.indexIsCommonType()) { + return QQmlMetaType::qmlType(QQmlPropertyCacheCreatorBase::metaTypeForPropertyType( + QV4::CompiledData::CommonType(type))); + } + + if (type == 0 || !typeLoader) + return QQmlType(); + + const auto &base = unit->baseCompilationUnit(); + const QQmlType qmltype = QQmlTypePrivate::compositeQmlType( + base, typeLoader, base->stringAt(type)); + return qmltype.typeId().isValid() ? qmltype : QQmlType(); + }; + + new (&jsTypedFunction) JSTypedFunction; + kind = JsTyped; + jsTypedFunction.types.reserve(nFormals + 1); + jsTypedFunction.types.append(findQmlType(compiledFunction->returnType)); + for (quint16 i = 0; i < nFormals; ++i) + jsTypedFunction.types.append(findQmlType(formalsIndices[i].type)); } Function::~Function() { - delete codeRef; + if (codeRef) { + destroyFunctionTable(this, codeRef); + delete codeRef; + } + + switch (kind) { + case JsTyped: + jsTypedFunction.~JSTypedFunction(); + break; + case AotCompiled: + aotCompiledFunction.~AOTCompiledFunction(); + break; + case JsUntyped: + case Eval: + break; + } } void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> ¶meters) @@ -105,7 +199,7 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr QStringList parameterNames; // Resolve duplicate parameter names: - for (int i = 0, ei = parameters.count(); i != ei; ++i) { + for (int i = 0, ei = parameters.size(); i != ei; ++i) { const QByteArray ¶m = parameters.at(i); int duplicate = -1; @@ -120,33 +214,56 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr if (duplicate == -1) { parameterNames.append(QString::fromUtf8(param)); } else { - const QString &dup = parameterNames[duplicate]; + const QString dup = parameterNames[duplicate]; parameterNames.append(dup); parameterNames[duplicate] = - QString(0xfffe) + QString::number(duplicate) + dup; + QString(QChar(0xfffe)) + QString::number(duplicate) + dup; } } - internalClass = engine->internalClasses(EngineBase::Class_CallContext); + Scope scope(engine); + Scoped<InternalClass> ic(scope, engine->internalClasses(EngineBase::Class_CallContext)); // first locals const quint32_le *localsIndices = compiledFunction->localsTable(); - for (quint32 i = 0; i < compiledFunction->nLocals; ++i) - internalClass = internalClass->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable); + for (quint32 i = 0; i < compiledFunction->nLocals; ++i) { + ic = ic->addMember( + engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), + Attr_NotConfigurable); + } - Scope scope(engine); ScopedString arg(scope); for (const QString ¶meterName : parameterNames) { arg = engine->newIdentifier(parameterName); - internalClass = internalClass->addMember(arg->propertyKey(), Attr_NotConfigurable); + ic = ic->addMember(arg->propertyKey(), Attr_NotConfigurable); } + internalClass.set(engine, ic->d()); nFormals = parameters.size(); } +QString Function::prettyName(const Function *function, const void *code) +{ + QString prettyName = function ? function->name()->toQString() : QString(); + if (prettyName.isEmpty()) { + prettyName = QString::number(reinterpret_cast<quintptr>(code), 16); + prettyName.prepend(QLatin1String("QV4::Function(0x")); + prettyName.append(QLatin1Char(')')); + } + return prettyName; +} + QQmlSourceLocation Function::sourceLocation() const { - return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column); + return QQmlSourceLocation( + sourceFile(), compiledFunction->location.line(), compiledFunction->location.column()); +} + +FunctionData::FunctionData(EngineBase *engine, ExecutableCompilationUnit *compilationUnit_) +{ + compilationUnit.set(engine, compilationUnit_); } +} // namespace QV4 + QT_END_NAMESPACE |