diff options
Diffstat (limited to 'src/qml/jsruntime/qv4jscall_p.h')
-rw-r--r-- | src/qml/jsruntime/qv4jscall_p.h | 638 |
1 files changed, 544 insertions, 94 deletions
diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h index 31689b1ba1..ed1ca983ad 100644 --- a/src/qml/jsruntime/qv4jscall_p.h +++ b/src/qml/jsruntime/qv4jscall_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 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 #ifndef QV4JSCALL_H #define QV4JSCALL_H @@ -50,54 +14,86 @@ // We mean it. // -#include "qv4object_p.h" -#include "qv4function_p.h" -#include "qv4functionobject_p.h" -#include "qv4context_p.h" -#include "qv4scopedvalue_p.h" -#include "qv4stackframe_p.h" +#include <private/qqmlengine_p.h> +#include <private/qqmllistwrapper_p.h> +#include <private/qqmlvaluetype_p.h> +#include <private/qqmlvaluetypewrapper_p.h> +#include <private/qv4alloca_p.h> +#include <private/qv4context_p.h> +#include <private/qv4dateobject_p.h> +#include <private/qv4function_p.h> +#include <private/qv4functionobject_p.h> +#include <private/qv4object_p.h> +#include <private/qv4qobjectwrapper_p.h> +#include <private/qv4regexpobject_p.h> +#include <private/qv4scopedvalue_p.h> +#include <private/qv4stackframe_p.h> +#include <private/qv4urlobject_p.h> +#include <private/qv4variantobject_p.h> + +#if QT_CONFIG(regularexpression) +#include <QtCore/qregularexpression.h> +#endif QT_BEGIN_NAMESPACE namespace QV4 { -struct JSCallData { - JSCallData(const Scope &scope, int argc = 0, const Value *argv = nullptr, const Value *thisObject = nullptr) - : scope(scope), argc(argc) +template<typename Args> +CallData *callDatafromJS(const Scope &scope, const Args *args, const FunctionObject *f = nullptr) +{ + int size = int(offsetof(QV4::CallData, args)/sizeof(QV4::Value)) + args->argc; + CallData *ptr = reinterpret_cast<CallData *>(scope.alloc<Scope::Uninitialized>(size)); + ptr->function = Encode::undefined(); + ptr->context = Encode::undefined(); + ptr->accumulator = Encode::undefined(); + ptr->thisObject = args->thisObject ? args->thisObject->asReturnedValue() : Encode::undefined(); + ptr->newTarget = Encode::undefined(); + ptr->setArgc(args->argc); + if (args->argc) + memcpy(ptr->args, args->args, args->argc*sizeof(Value)); + if (f) + ptr->function = f->asReturnedValue(); + return ptr; +} + +struct JSCallArguments +{ + JSCallArguments(const Scope &scope, int argc = 0) + : thisObject(scope.alloc()), args(scope.alloc(argc)), argc(argc) { - if (thisObject) - this->thisObject = const_cast<Value *>(thisObject); - else - this->thisObject = scope.alloc(); - if (argv) - this->args = const_cast<Value *>(argv); - else - this->args = scope.alloc(argc); - } - - JSCallData *operator->() { - return this; - } - - CallData *callData(const FunctionObject *f = nullptr) const { - int size = int(offsetof(QV4::CallData, args)/sizeof(QV4::Value)) + argc; - CallData *ptr = reinterpret_cast<CallData *>(scope.alloc<Scope::Uninitialized>(size)); - ptr->function = Encode::undefined(); - ptr->context = Encode::undefined(); - ptr->accumulator = Encode::undefined(); - ptr->thisObject = thisObject->asReturnedValue(); - ptr->newTarget = Encode::undefined(); - ptr->setArgc(argc); - if (argc) - memcpy(ptr->args, args, argc*sizeof(Value)); - if (f) - ptr->function = f->asReturnedValue(); - return ptr; - } - const Scope &scope; - int argc; - Value *args; + } + + CallData *callData(const Scope &scope, const FunctionObject *f = nullptr) const + { + return callDatafromJS(scope, this, f); + } + Value *thisObject; + Value *args; + const int argc; +}; + +struct JSCallData +{ + JSCallData(const Value *thisObject, const Value *argv, int argc) + : thisObject(thisObject), args(argv), argc(argc) + { + } + + Q_IMPLICIT JSCallData(const JSCallArguments &args) + : thisObject(args.thisObject), args(args.args), argc(args.argc) + { + } + + CallData *callData(const Scope &scope, const FunctionObject *f = nullptr) const + { + return callDatafromJS(scope, this, f); + } + + const Value *thisObject; + const Value *args; + const int argc; }; inline @@ -112,29 +108,483 @@ ReturnedValue FunctionObject::call(const JSCallData &data) const return call(data.thisObject, data.args, data.argc); } +void populateJSCallArguments(ExecutionEngine *v4, JSCallArguments &jsCall, int argc, + void **args, const QMetaType *types); + +template<typename Callable> +ReturnedValue convertAndCall( + ExecutionEngine *engine, const Function::AOTCompiledFunction *aotFunction, + const Value *thisObject, const Value *argv, int argc, Callable call) +{ + const qsizetype numFunctionArguments = aotFunction->types.length() - 1; + Q_ALLOCA_VAR(void *, values, (numFunctionArguments + 1) * sizeof(void *)); + Q_ALLOCA_VAR(QMetaType, types, (numFunctionArguments + 1) * sizeof(QMetaType)); -struct ScopedStackFrame { - Scope &scope; - CppStackFrame frame; + for (qsizetype i = 0; i < numFunctionArguments; ++i) { + const QMetaType argumentType = aotFunction->types[i + 1]; + types[i + 1] = argumentType; + if (const qsizetype argumentSize = argumentType.sizeOf()) { + Q_ALLOCA_VAR(void, argument, argumentSize); + if (argumentType.flags() & QMetaType::NeedsConstruction) { + argumentType.construct(argument); + if (i < argc) + ExecutionEngine::metaTypeFromJS(argv[i], argumentType, argument); + } else if (i >= argc + || !ExecutionEngine::metaTypeFromJS(argv[i], argumentType, argument)) { + // If we can't convert the argument, we need to default-construct it even if it + // doesn't formally need construction. + // E.g. an int doesn't need construction, but we still want it to be 0. + argumentType.construct(argument); + } - ScopedStackFrame(Scope &scope, Heap::ExecutionContext *context) - : scope(scope) - { - frame.parent = scope.engine->currentStackFrame; - if (!context) - return; - frame.jsFrame = reinterpret_cast<CallData *>(scope.alloc(sizeof(CallData)/sizeof(Value))); - frame.jsFrame->context = context; - frame.v4Function = frame.parent ? frame.parent->v4Function : nullptr; - scope.engine->currentStackFrame = &frame; + values[i + 1] = argument; + } else { + values[i + 1] = nullptr; + } + } + + Q_ALLOCA_DECLARE(void, returnValue); + types[0] = aotFunction->types[0]; + if (const qsizetype returnSize = types[0].sizeOf()) { + Q_ALLOCA_ASSIGN(void, returnValue, returnSize); + values[0] = returnValue; + if (types[0].flags() & QMetaType::NeedsConstruction) + types[0].construct(returnValue); + } else { + values[0] = nullptr; + } + + if (const QV4::QObjectWrapper *cppThisObject = thisObject + ? thisObject->as<QV4::QObjectWrapper>() + : nullptr) { + call(cppThisObject->object(), values, types, argc); + } else { + call(nullptr, values, types, argc); } - ~ScopedStackFrame() { - scope.engine->currentStackFrame = frame.parent; + + ReturnedValue result; + if (values[0]) { + result = engine->metaTypeToJS(types[0], values[0]); + if (types[0].flags() & QMetaType::NeedsDestruction) + types[0].destruct(values[0]); + } else { + result = Encode::undefined(); + } + + for (qsizetype i = 1, end = numFunctionArguments + 1; i < end; ++i) { + if (types[i].flags() & QMetaType::NeedsDestruction) + types[i].destruct(values[i]); } + + return result; +} + +template<typename Callable> +bool convertAndCall(ExecutionEngine *engine, QObject *thisObject, + void **a, const QMetaType *types, int argc, Callable call) +{ + Scope scope(engine); + QV4::JSCallArguments jsCallData(scope, argc); + + for (int ii = 0; ii < argc; ++ii) + jsCallData.args[ii] = engine->metaTypeToJS(types[ii + 1], a[ii + 1]); + + ScopedObject jsThisObject(scope); + if (thisObject) { + // The result of wrap() can only be null, undefined, or an object. + jsThisObject = QV4::QObjectWrapper::wrap(engine, thisObject); + if (!jsThisObject) + jsThisObject = engine->globalObject; + } else { + jsThisObject = engine->globalObject; + } + + ScopedValue jsResult(scope, call(jsThisObject, jsCallData.args, argc)); + void *result = a[0]; + if (!result) + return !jsResult->isUndefined(); + + const QMetaType resultType = types[0]; + if (scope.hasException()) { + // Clear the return value + resultType.destruct(result); + resultType.construct(result); + } else if (resultType == QMetaType::fromType<QVariant>()) { + // When the return type is QVariant, JS objects are to be returned as + // QJSValue wrapped in QVariant. metaTypeFromJS unwraps them, unfortunately. + *static_cast<QVariant *>(result) = ExecutionEngine::toVariant(jsResult, QMetaType {}); + } else if (!ExecutionEngine::metaTypeFromJS(jsResult, resultType, result)) { + // If we cannot convert, also clear the return value. + // The caller may have given us an uninitialized QObject*, expecting it to be overwritten. + resultType.destruct(result); + resultType.construct(result); + } + return !jsResult->isUndefined(); +} + +inline ReturnedValue coerce( + ExecutionEngine *engine, const Value &value, const QQmlType &qmlType, bool isList); + +inline QObject *coerceQObject(const Value &value, const QQmlType &qmlType) +{ + QObject *o; + if (const QV4::QObjectWrapper *wrapper = value.as<QV4::QObjectWrapper>()) + o = wrapper->object(); + else if (const QV4::QQmlTypeWrapper *wrapper = value.as<QQmlTypeWrapper>()) + o = wrapper->object(); + else + return nullptr; + + return (o && qmlobject_can_qml_cast(o, qmlType)) ? o : nullptr; +} + +enum CoercionProblem +{ + InsufficientAnnotation, + InvalidListType }; +Q_QML_EXPORT void warnAboutCoercionToVoid( + ExecutionEngine *engine, const Value &value, CoercionProblem problem); + +inline ReturnedValue coerceListType( + ExecutionEngine *engine, const Value &value, const QQmlType &qmlType) +{ + QMetaType type = qmlType.qListTypeId(); + const auto metaSequence = [&]() { + // TODO: We should really add the metasequence to the same QQmlType that holds + // all the other type information. Then we can get rid of the extra + // QQmlMetaType::qmlListType() here. + return qmlType.isSequentialContainer() + ? qmlType.listMetaSequence() + : QQmlMetaType::qmlListType(type).listMetaSequence(); + }; + + if (const QV4::Sequence *sequence = value.as<QV4::Sequence>()) { + if (sequence->d()->listType() == type) + return value.asReturnedValue(); + } + + if (const QmlListWrapper *list = value.as<QmlListWrapper>()) { + if (list->d()->propertyType() == type) + return value.asReturnedValue(); + } + + QMetaType listValueType = qmlType.typeId(); + if (!listValueType.isValid()) { + warnAboutCoercionToVoid(engine, value, InvalidListType); + return value.asReturnedValue(); + } + + QV4::Scope scope(engine); + + const ArrayObject *array = value.as<ArrayObject>(); + if (!array) { + return (listValueType.flags() & QMetaType::PointerToQObject) + ? QmlListWrapper::create(engine, listValueType) + : SequencePrototype::fromData(engine, type, metaSequence(), nullptr); + } + + if (listValueType.flags() & QMetaType::PointerToQObject) { + QV4::Scoped<QmlListWrapper> newList(scope, QmlListWrapper::create(engine, type)); + QQmlListProperty<QObject> *listProperty = newList->d()->property(); + + const qsizetype length = array->getLength(); + qsizetype i = 0; + for (; i < length; ++i) { + ScopedValue v(scope, array->get(i)); + listProperty->append(listProperty, coerceQObject(v, qmlType)); + } + + return newList->asReturnedValue(); + } + + QV4::Scoped<Sequence> sequence( + scope, SequencePrototype::fromData(engine, type, metaSequence(), nullptr)); + const qsizetype length = array->getLength(); + for (qsizetype i = 0; i < length; ++i) + sequence->containerPutIndexed(i, array->get(i)); + return sequence->asReturnedValue(); } +inline ReturnedValue coerce( + ExecutionEngine *engine, const Value &value, const QQmlType &qmlType, bool isList) +{ + // These are all the named non-list, non-QObject builtins. Only those need special handling. + // Some of them may be wrapped in VariantObject because that is how they are stored in VME + // properties. + if (isList) + return coerceListType(engine, value, qmlType); + + const QMetaType metaType = qmlType.typeId(); + if (!metaType.isValid()) { + if (!value.isUndefined()) + warnAboutCoercionToVoid(engine, value, InsufficientAnnotation); + return value.asReturnedValue(); + } + + switch (metaType.id()) { + case QMetaType::Void: + return Encode::undefined(); + case QMetaType::QVariant: + return value.asReturnedValue(); + case QMetaType::Int: + return Encode(value.toInt32()); + case QMetaType::Double: + return value.convertedToNumber(); + case QMetaType::QString: + return value.toString(engine)->asReturnedValue(); + case QMetaType::Bool: + return Encode(value.toBoolean()); + case QMetaType::QDateTime: + if (value.as<DateObject>()) + return value.asReturnedValue(); + if (const VariantObject *varObject = value.as<VariantObject>()) { + const QVariant &var = varObject->d()->data(); + switch (var.metaType().id()) { + case QMetaType::QDateTime: + return engine->newDateObject(var.value<QDateTime>())->asReturnedValue(); + case QMetaType::QTime: + return engine->newDateObject(var.value<QTime>(), nullptr, -1, 0)->asReturnedValue(); + case QMetaType::QDate: + return engine->newDateObject(var.value<QDate>(), nullptr, -1, 0)->asReturnedValue(); + default: + break; + } + } + return engine->newDateObject(QDateTime())->asReturnedValue(); + case QMetaType::QUrl: + if (value.as<UrlObject>()) + return value.asReturnedValue(); + if (const VariantObject *varObject = value.as<VariantObject>()) { + const QVariant &var = varObject->d()->data(); + return var.metaType() == QMetaType::fromType<QUrl>() + ? engine->newUrlObject(var.value<QUrl>())->asReturnedValue() + : engine->newUrlObject()->asReturnedValue(); + } + // Since URL properties are stored as string, we need to support the string conversion here. + if (const String *string = value.stringValue()) + return engine->newUrlObject(QUrl(string->toQString()))->asReturnedValue(); + return engine->newUrlObject()->asReturnedValue(); +#if QT_CONFIG(regularexpression) + case QMetaType::QRegularExpression: + if (value.as<RegExpObject>()) + return value.asReturnedValue(); + if (const VariantObject *varObject = value.as<VariantObject>()) { + const QVariant &var = varObject->d()->data(); + if (var.metaType() == QMetaType::fromType<QRegularExpression>()) + return engine->newRegExpObject(var.value<QRegularExpression>())->asReturnedValue(); + } + return engine->newRegExpObject(QString(), 0)->asReturnedValue(); +#endif + default: + break; + } + + if (metaType.flags() & QMetaType::PointerToQObject) { + return coerceQObject(value, qmlType) + ? value.asReturnedValue() + : Encode::null(); + } + + if (const QQmlValueTypeWrapper *wrapper = value.as<QQmlValueTypeWrapper>()) { + if (wrapper->type() == metaType) + return value.asReturnedValue(); + } + + if (void *target = QQmlValueTypeProvider::heapCreateValueType(qmlType, value)) { + Heap::QQmlValueTypeWrapper *wrapper = engine->memoryManager->allocate<QQmlValueTypeWrapper>( + nullptr, metaType, qmlType.metaObjectForValueType(), + nullptr, -1, Heap::ReferenceObject::NoFlag); + Q_ASSERT(!wrapper->gadgetPtr()); + wrapper->setGadgetPtr(target); + return wrapper->asReturnedValue(); + } + + return Encode::undefined(); +} + +template<typename Callable> +ReturnedValue coerceAndCall( + ExecutionEngine *engine, + const Function::JSTypedFunction *typedFunction, const CompiledData::Function *compiledFunction, + const Value *argv, int argc, Callable call) +{ + Scope scope(engine); + + QV4::JSCallArguments jsCallData(scope, typedFunction->types.size() - 1); + const CompiledData::Parameter *formals = compiledFunction->formalsTable(); + for (qsizetype i = 0; i < jsCallData.argc; ++i) { + jsCallData.args[i] = coerce( + engine, i < argc ? argv[i] : Encode::undefined(), + typedFunction->types[i + 1], formals[i].type.isList()); + } + + ScopedValue result(scope, call(jsCallData.args, jsCallData.argc)); + return coerce(engine, result, typedFunction->types[0], compiledFunction->returnType.isList()); +} + +// Note: \a to is unininitialized here! This is in contrast to most other related functions. +inline void coerce( + ExecutionEngine *engine, QMetaType fromType, const void *from, QMetaType toType, void *to) +{ + if ((fromType.flags() & QMetaType::PointerToQObject) + && (toType.flags() & QMetaType::PointerToQObject)) { + QObject *fromObj = *static_cast<QObject * const*>(from); + *static_cast<QObject **>(to) + = (fromObj && fromObj->metaObject()->inherits(toType.metaObject())) + ? fromObj + : nullptr; + return; + } + + if (toType == QMetaType::fromType<QVariant>()) { + new (to) QVariant(fromType, from); + return; + } + + if (toType == QMetaType::fromType<QJSPrimitiveValue>()) { + new (to) QJSPrimitiveValue(fromType, from); + return; + } + + if (fromType == QMetaType::fromType<QVariant>()) { + const QVariant *fromVariant = static_cast<const QVariant *>(from); + if (fromVariant->metaType() == toType) + toType.construct(to, fromVariant->data()); + else + coerce(engine, fromVariant->metaType(), fromVariant->data(), toType, to); + return; + } + + if (fromType == QMetaType::fromType<QJSPrimitiveValue>()) { + const QJSPrimitiveValue *fromPrimitive = static_cast<const QJSPrimitiveValue *>(from); + if (fromPrimitive->metaType() == toType) + toType.construct(to, fromPrimitive->data()); + else + coerce(engine, fromPrimitive->metaType(), fromPrimitive->data(), toType, to); + return; + } + + // TODO: This is expensive. We might establish a direct C++-to-C++ type coercion, like we have + // for JS-to-JS. However, we shouldn't need this very often. Most of the time the compiler + // will generate code that passes the right arguments. + if (toType.flags() & QMetaType::NeedsConstruction) + toType.construct(to); + QV4::Scope scope(engine); + QV4::ScopedValue value(scope, engine->fromData(fromType, from)); + if (!ExecutionEngine::metaTypeFromJS(value, toType, to)) + QMetaType::convert(fromType, from, toType, to); +} + +template<typename TypedFunction, typename Callable> +void coerceAndCall( + ExecutionEngine *engine, const TypedFunction *typedFunction, + void **argv, const QMetaType *types, int argc, Callable call) +{ + const qsizetype numFunctionArguments = typedFunction->parameterCount(); + + Q_ALLOCA_DECLARE(void *, transformedArguments); + Q_ALLOCA_DECLARE(void, transformedResult); + + const QMetaType returnType = typedFunction->returnMetaType(); + const QMetaType frameReturn = types[0]; + bool returnsQVariantWrapper = false; + if (argv[0] && returnType != frameReturn) { + Q_ALLOCA_ASSIGN(void *, transformedArguments, (numFunctionArguments + 1) * sizeof(void *)); + memcpy(transformedArguments, argv, (argc + 1) * sizeof(void *)); + + if (frameReturn == QMetaType::fromType<QVariant>()) { + QVariant *returnValue = static_cast<QVariant *>(argv[0]); + *returnValue = QVariant(returnType); + transformedResult = transformedArguments[0] = returnValue->data(); + returnsQVariantWrapper = true; + } else if (returnType.sizeOf() > 0) { + Q_ALLOCA_ASSIGN(void, transformedResult, returnType.sizeOf()); + transformedArguments[0] = transformedResult; + if (returnType.flags() & QMetaType::NeedsConstruction) + returnType.construct(transformedResult); + } else { + transformedResult = transformedArguments[0] = &argc; // Some non-null marker value + } + } + + for (qsizetype i = 0; i < numFunctionArguments; ++i) { + const bool isValid = argc > i; + const QMetaType frameType = isValid ? types[i + 1] : QMetaType(); + + const QMetaType argumentType = typedFunction->parameterMetaType(i); + if (isValid && argumentType == frameType) + continue; + + if (transformedArguments == nullptr) { + Q_ALLOCA_ASSIGN(void *, transformedArguments, (numFunctionArguments + 1) * sizeof(void *)); + memcpy(transformedArguments, argv, (argc + 1) * sizeof(void *)); + } + + if (argumentType.sizeOf() == 0) { + transformedArguments[i + 1] = nullptr; + continue; + } + + void *frameVal = isValid ? argv[i + 1] : nullptr; + if (isValid && frameType == QMetaType::fromType<QVariant>()) { + QVariant *variant = static_cast<QVariant *>(frameVal); + + const QMetaType variantType = variant->metaType(); + if (variantType == argumentType) { + // Slightly nasty, but we're allowed to do this. + // We don't want to destruct() the QVariant's data() below. + transformedArguments[i + 1] = argv[i + 1] = variant->data(); + } else { + Q_ALLOCA_VAR(void, arg, argumentType.sizeOf()); + coerce(engine, variantType, variant->constData(), argumentType, arg); + transformedArguments[i + 1] = arg; + } + continue; + } + + Q_ALLOCA_VAR(void, arg, argumentType.sizeOf()); + + if (isValid) + coerce(engine, frameType, frameVal, argumentType, arg); + else + argumentType.construct(arg); + + transformedArguments[i + 1] = arg; + } + + if (!transformedArguments) { + call(argv, numFunctionArguments); + return; + } + + call(transformedArguments, numFunctionArguments); + + if (transformedResult && !returnsQVariantWrapper) { + if (frameReturn.sizeOf() > 0) { + if (frameReturn.flags() & QMetaType::NeedsDestruction) + frameReturn.destruct(argv[0]); + coerce(engine, returnType, transformedResult, frameReturn, argv[0]); + } + if (returnType.flags() & QMetaType::NeedsDestruction) + returnType.destruct(transformedResult); + } + + for (qsizetype i = 0; i < numFunctionArguments; ++i) { + void *arg = transformedArguments[i + 1]; + if (arg == nullptr) + continue; + if (i >= argc || arg != argv[i + 1]) { + const QMetaType argumentType = typedFunction->parameterMetaType(i); + if (argumentType.flags() & QMetaType::NeedsDestruction) + argumentType.destruct(arg); + } + } +} + +} // namespace QV4 + QT_END_NAMESPACE #endif // QV4JSCALL_H |