static bool NPClass_HasProperty(NPObject *npobj, NPIdentifier name)
{
    NPClass_Prolog;
    const QByteArray qname = NPN_UTF8FromIdentifier(name);
    const QMetaObject *metaObject = qobject->metaObject();
    int propertyIndex = metaObject->indexOfProperty(qname);
    if (propertyIndex == -1 || propertyIndex < metaOffset(metaObject, MetaProperty))
        return false;
    QMetaProperty property = qobject->metaObject()->property(propertyIndex);
    if (!property.isScriptable())
        return false;

    return true;
}
static int publicMethodIndex(NPObject *npobj, NPIdentifier name)
{
    NPClass_Prolog;
    QByteArray qname = NPN_UTF8FromIdentifier(name);
    const QMetaObject *metaObject = qobject->metaObject();
    for (int slotIndex = metaOffset(metaObject, MetaMethod); slotIndex < metaObject->methodCount(); ++slotIndex) {
        const QMetaMethod slot = qobject->metaObject()->method(slotIndex);
        if (slot.access() != QMetaMethod::Public || slot.methodType() == QMetaMethod::Signal)
            continue;
        QByteArray signature = slot.signature();
        if (signature.left(signature.indexOf('(')) == qname)
            return slotIndex;
    }
    return -1;
}
static int publicMethodIndex(NPObject *npobj, const QByteArray &slotName, int argCount = -1)
{
    NPClass_Prolog;
    const QMetaObject *metaObject = qobject->metaObject();
    for (int slotIndex = metaOffset(metaObject, MetaMethod); slotIndex < metaObject->methodCount(); ++slotIndex) {
        const QMetaMethod slot = qobject->metaObject()->method(slotIndex);
        if (slot.access() != QMetaMethod::Public || slot.methodType() == QMetaMethod::Signal)
            continue;
        QByteArray signature = slot.signature();
        if (signature.left(signature.indexOf('(')) == slotName) {
            if (argCount == -1 || slot.parameterTypes().count() == argCount)
                return slotIndex;
        }
    }
    return -1;
}
int QtSignalForwarder::qt_metacall(QMetaObject::Call call, int index, void **args)
{
    // no support for QObject method/properties etc!
    if (!This || !This->npp || call != QMetaObject::InvokeMetaMethod
        || !This->qt.object)
        return index;

    switch (index) {
    case -1:
        {
            QString msg = *(QString*)args[1];
            NPN_Status(This->npp, msg.toLocal8Bit().constData());
        }
        break;
    default:
        {
            QObject *qobject = This->qt.object;
            if (!domNode)
                NPN_GetValue(This->npp, NPNVPluginElementNPObject, &domNode);
            if (!domNode)
                break;
            const QMetaObject *metaObject = qobject->metaObject();
            if (index < metaOffset(metaObject, MetaMethod))
                break;

            const QMetaMethod method = metaObject->method(index);
            Q_ASSERT(method.methodType() == QMetaMethod::Signal);

            QByteArray signalSignature = method.signature();
            QByteArray scriptFunction = signalSignature.left(signalSignature.indexOf('('));
            NPIdentifier id = NPN_GetStringIdentifier(scriptFunction.constData());
            if (NPN_HasMethod(This->npp, domNode, id)) {
                QList<QByteArray> parameterTypes = method.parameterTypes();
                QVector<NPVariant> parameters;
                NPVariant result;
                bool error = false;
                for (int p = 0; p < parameterTypes.count(); ++p) {
                    QVariant::Type type = QVariant::nameToType(parameterTypes.at(p));
                    if (type == QVariant::Invalid) {
                        NPN_SetException(domNode, QByteArray("Unsupported parameter type in ") + scriptFunction);
                        error = true;
                        break;
                    }
                    QVariant qvar(type, args[p + 1]);
                    NPVariant npvar = NPVariant::fromQVariant(This, qvar);
                    if (npvar.type == NPVariant::Null || npvar.type == NPVariant::Void) {
                        NPN_SetException(domNode, QByteArray("Unsupported parameter value in ") + scriptFunction);
                        error =true;
                        break;
                    }
                    parameters += npvar;
                }
                if (error)
                    break;

                NPError nperror = NPN_Invoke(This->npp, domNode, id, parameters.constData(), parameters.count(), &result);
                if (nperror != NPERR_NO_ERROR && false) { // disabled, as NPN_Invoke seems to always return GENERICERROR
                    NPN_SetException(domNode, QByteArray("Error invoking event handler ") + scriptFunction);
                }
                // ### TODO: update return value (args[0]) (out-parameters not supported anyway)
                NPN_ReleaseVariantValue(&result);
            }
        }
        break;
    }

    return index;
}