qt_instance.cpp 12.1 KB
Newer Older
andersca@apple.com's avatar
andersca@apple.com committed
1
/*
2
 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
andersca@apple.com's avatar
andersca@apple.com committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include "config.h"
#include "qt_instance.h"

23
#include "Error.h"
24
#include "JSDOMBinding.h"
andersca@apple.com's avatar
andersca@apple.com committed
25
#include "JSGlobalObject.h"
ap@webkit.org's avatar
ap@webkit.org committed
26
#include "JSLock.h"
27
28
#include "ObjectPrototype.h"
#include "PropertyNameArray.h"
andersca@apple.com's avatar
andersca@apple.com committed
29
30
31
32
33
34
#include "qt_class.h"
#include "qt_runtime.h"
#include "runtime_object.h"

#include <qdebug.h>
#include <qhash.h>
35
36
#include <qmetaobject.h>
#include <qmetatype.h>
andersca@apple.com's avatar
andersca@apple.com committed
37

38
namespace JSC {
andersca@apple.com's avatar
andersca@apple.com committed
39
40
41
42
43
44
45
namespace Bindings {

// Cache QtInstances
typedef QMultiHash<void*, QtInstance*> QObjectInstanceMap;
static QObjectInstanceMap cachedInstances;

// Derived RuntimeObject
46
class QtRuntimeObject : public RuntimeObject {
47
public:
oliver@apple.com's avatar
oliver@apple.com committed
48
    QtRuntimeObject(ExecState*, JSGlobalObject*, PassRefPtr<Instance>);
49
50
51

    static const ClassInfo s_info;

52
    virtual void markChildren(MarkStack& markStack)
53
    {
54
        RuntimeObject::markChildren(markStack);
55
56
        QtInstance* instance = static_cast<QtInstance*>(getInternalInstance());
        if (instance)
57
            instance->markAggregate(markStack);
58
59
    }

60
61
    static PassRefPtr<Structure> createStructure(JSValue prototype)
    {
62
        return Structure::create(prototype, TypeInfo(ObjectType,  StructureFlags), AnonymousSlotCount);
63
64
65
    }

protected:
66
    static const unsigned StructureFlags = RuntimeObject::StructureFlags | OverridesMarkChildren;
67

68
69
private:
    virtual const ClassInfo* classInfo() const { return &s_info; }
andersca@apple.com's avatar
andersca@apple.com committed
70
71
};

72
const ClassInfo QtRuntimeObject::s_info = { "QtRuntimeObject", &RuntimeObject::s_info, 0, 0 };
73

oliver@apple.com's avatar
oliver@apple.com committed
74
75
QtRuntimeObject::QtRuntimeObject(ExecState* exec, JSGlobalObject* globalObject, PassRefPtr<Instance> instance)
    : RuntimeObject(exec, globalObject, WebCore::deprecatedGetDOMStructure<QtRuntimeObject>(exec), instance)
andersca@apple.com's avatar
andersca@apple.com committed
76
77
78
79
{
}

// QtInstance
hausmann@webkit.org's avatar
hausmann@webkit.org committed
80
QtInstance::QtInstance(QObject* o, PassRefPtr<RootObject> rootObject, QScriptEngine::ValueOwnership ownership)
andersca@apple.com's avatar
andersca@apple.com committed
81
82
83
84
85
    : Instance(rootObject)
    , m_class(0)
    , m_object(o)
    , m_hashkey(o)
    , m_defaultMethod(0)
hausmann@webkit.org's avatar
hausmann@webkit.org committed
86
    , m_ownership(ownership)
andersca@apple.com's avatar
andersca@apple.com committed
87
88
89
90
91
{
}

QtInstance::~QtInstance()
{
ap@webkit.org's avatar
ap@webkit.org committed
92
    JSLock lock(SilenceAssertionsOnly);
ap@webkit.org's avatar
ap@webkit.org committed
93

andersca@apple.com's avatar
andersca@apple.com committed
94
95
96
97
98
    cachedInstances.remove(m_hashkey);

    // clean up (unprotect from gc) the JSValues we've created
    m_methods.clear();

hausmann@webkit.org's avatar
hausmann@webkit.org committed
99
    qDeleteAll(m_fields);
andersca@apple.com's avatar
andersca@apple.com committed
100
    m_fields.clear();
hausmann@webkit.org's avatar
hausmann@webkit.org committed
101
102
103
104
105
106
107
108
109
110
111
112
113
114

    if (m_object) {
        switch (m_ownership) {
        case QScriptEngine::QtOwnership:
            break;
        case QScriptEngine::AutoOwnership:
            if (m_object->parent())
                break;
            // fall through!
        case QScriptEngine::ScriptOwnership:
            delete m_object;
            break;
        }
    }
andersca@apple.com's avatar
andersca@apple.com committed
115
116
}

hausmann@webkit.org's avatar
hausmann@webkit.org committed
117
PassRefPtr<QtInstance> QtInstance::getQtInstance(QObject* o, PassRefPtr<RootObject> rootObject, QScriptEngine::ValueOwnership ownership)
andersca@apple.com's avatar
andersca@apple.com committed
118
{
ap@webkit.org's avatar
ap@webkit.org committed
119
    JSLock lock(SilenceAssertionsOnly);
ap@webkit.org's avatar
ap@webkit.org committed
120

121
    foreach (QtInstance* instance, cachedInstances.values(o))
122
123
124
125
126
127
128
129
130
131
        if (instance->rootObject() == rootObject) {
            // The garbage collector removes instances, but it may happen that the wrapped
            // QObject dies before the gc kicks in. To handle that case we have to do an additional
            // check if to see if the instance's wrapped object is still alive. If it isn't, then
            // we have to create a new wrapper.
            if (!instance->getObject())
                cachedInstances.remove(instance->hashKey());
            else
                return instance;
        }
andersca@apple.com's avatar
andersca@apple.com committed
132

hausmann@webkit.org's avatar
hausmann@webkit.org committed
133
    RefPtr<QtInstance> ret = QtInstance::create(o, rootObject, ownership);
134
    cachedInstances.insert(o, ret.get());
andersca@apple.com's avatar
andersca@apple.com committed
135

136
    return ret.release();
andersca@apple.com's avatar
andersca@apple.com committed
137
138
}

139
bool QtInstance::getOwnPropertySlot(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
andersca@apple.com's avatar
andersca@apple.com committed
140
{
141
    return object->JSObject::getOwnPropertySlot(exec, propertyName, slot);
142
143
}

ggaren@apple.com's avatar
ggaren@apple.com committed
144
void QtInstance::put(JSObject* object, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
145
{
146
    object->JSObject::put(exec, propertyName, value, slot);
andersca@apple.com's avatar
andersca@apple.com committed
147
148
}

hausmann@webkit.org's avatar
hausmann@webkit.org committed
149
150
151
152
153
void QtInstance::removeCachedMethod(JSObject* method)
{
    if (m_defaultMethod == method)
        m_defaultMethod = 0;

154
    for (QHash<QByteArray, JSObject*>::Iterator it = m_methods.begin(),
hausmann@webkit.org's avatar
hausmann@webkit.org committed
155
156
157
158
159
160
161
        end = m_methods.end(); it != end; ++it)
        if (it.value() == method) {
            m_methods.erase(it);
            return;
        }
}

162
QtInstance* QtInstance::getInstance(JSObject* object)
163
164
165
{
    if (!object)
        return 0;
166
    if (!object->inherits(&QtRuntimeObject::s_info))
167
        return 0;
168
    return static_cast<QtInstance*>(static_cast<RuntimeObject*>(object)->getInternalInstance());
169
170
}

andersca@apple.com's avatar
andersca@apple.com committed
171
172
Class* QtInstance::getClass() const
{
173
174
175
    if (!m_class) {
        if (!m_object)
            return 0;
andersca@apple.com's avatar
andersca@apple.com committed
176
        m_class = QtClass::classForObject(m_object);
177
    }
andersca@apple.com's avatar
andersca@apple.com committed
178
179
180
    return m_class;
}

181
RuntimeObject* QtInstance::newRuntimeObject(ExecState* exec)
182
{
ap@webkit.org's avatar
ap@webkit.org committed
183
    JSLock lock(SilenceAssertionsOnly);
oliver@apple.com's avatar
oliver@apple.com committed
184
    return new (exec) QtRuntimeObject(exec, exec->lexicalGlobalObject(), this);
185
186
}

187
void QtInstance::markAggregate(MarkStack& markStack)
188
{
189
190
    if (m_defaultMethod)
        markStack.append(m_defaultMethod);
191
    foreach (JSObject* val, m_methods.values()) {
192
193
        if (val)
            markStack.append(val);
194
195
196
    }
}

andersca@apple.com's avatar
andersca@apple.com committed
197
198
199
200
201
202
203
204
205
206
void QtInstance::begin()
{
    // Do nothing.
}

void QtInstance::end()
{
    // Do nothing.
}

207
void QtInstance::getPropertyNames(ExecState* exec, PropertyNameArray& array)
andersca@apple.com's avatar
andersca@apple.com committed
208
209
210
211
212
213
214
215
216
217
{
    // This is the enumerable properties, so put:
    // properties
    // dynamic properties
    // slots
    QObject* obj = getObject();
    if (obj) {
        const QMetaObject* meta = obj->metaObject();

        int i;
218
        for (i = 0; i < meta->propertyCount(); i++) {
andersca@apple.com's avatar
andersca@apple.com committed
219
            QMetaProperty prop = meta->property(i);
220
            if (prop.isScriptable())
221
                array.add(Identifier(exec, prop.name()));
andersca@apple.com's avatar
andersca@apple.com committed
222
223
        }

224
#ifndef QT_NO_PROPERTIES
andersca@apple.com's avatar
andersca@apple.com committed
225
        QList<QByteArray> dynProps = obj->dynamicPropertyNames();
226
        foreach (const QByteArray& ba, dynProps)
227
            array.add(Identifier(exec, ba.constData()));
228
#endif
andersca@apple.com's avatar
andersca@apple.com committed
229

230
231
        const int methodCount = meta->methodCount();
        for (i = 0; i < methodCount; i++) {
andersca@apple.com's avatar
andersca@apple.com committed
232
            QMetaMethod method = meta->method(i);
233
            if (method.access() != QMetaMethod::Private)
234
                array.add(Identifier(exec, method.signature()));
andersca@apple.com's avatar
andersca@apple.com committed
235
236
237
238
        }
    }
}

239
240
JSValue QtInstance::getMethod(ExecState* exec, const Identifier& propertyName)
{
241
242
243
    if (!getClass())
        return jsNull();
    MethodList methodList = m_class->methodsNamed(propertyName, this);
oliver@apple.com's avatar
oliver@apple.com committed
244
    return new (exec) RuntimeMethod(exec, exec->lexicalGlobalObject(), propertyName, methodList);
245
246
}

247
JSValue QtInstance::invokeMethod(ExecState*, RuntimeMethod*)
andersca@apple.com's avatar
andersca@apple.com committed
248
249
250
251
252
{
    // Implemented via fallbackMethod & QtRuntimeMetaMethod::callAsFunction
    return jsUndefined();
}

ggaren@apple.com's avatar
ggaren@apple.com committed
253
JSValue QtInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
andersca@apple.com's avatar
andersca@apple.com committed
254
{
darin@apple.com's avatar
darin@apple.com committed
255
    if (hint == PreferString)
ap@webkit.org's avatar
ap@webkit.org committed
256
        return stringValue(exec);
darin@apple.com's avatar
darin@apple.com committed
257
    if (hint == PreferNumber)
ap@webkit.org's avatar
ap@webkit.org committed
258
259
        return numberValue(exec);
    return valueOf(exec);
andersca@apple.com's avatar
andersca@apple.com committed
260
261
}

ggaren@apple.com's avatar
ggaren@apple.com committed
262
JSValue QtInstance::stringValue(ExecState* exec) const
andersca@apple.com's avatar
andersca@apple.com committed
263
{
264
265
266
267
    QObject* obj = getObject();
    if (!obj)
        return jsNull();

andersca@apple.com's avatar
andersca@apple.com committed
268
269
270
271
    // Hmm.. see if there is a toString defined
    QByteArray buf;
    bool useDefault = true;
    getClass();
272
    if (m_class) {
andersca@apple.com's avatar
andersca@apple.com committed
273
274
275
276
277
278
279
        // Cheat and don't use the full name resolution
        int index = obj->metaObject()->indexOfMethod("toString()");
        if (index >= 0) {
            QMetaMethod m = obj->metaObject()->method(index);
            // Check to see how much we can call it
            if (m.access() != QMetaMethod::Private
                && m.methodType() != QMetaMethod::Signal
280
                && m.parameterTypes().isEmpty()) {
andersca@apple.com's avatar
andersca@apple.com committed
281
282
283
284
285
286
                const char* retsig = m.typeName();
                if (retsig && *retsig) {
                    QVariant ret(QMetaType::type(retsig), (void*)0);
                    void * qargs[1];
                    qargs[0] = ret.data();

287
                    if (QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, index, qargs) < 0) {
andersca@apple.com's avatar
andersca@apple.com committed
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
                        if (ret.isValid() && ret.canConvert(QVariant::String)) {
                            buf = ret.toString().toLatin1().constData(); // ### Latin 1? Ascii?
                            useDefault = false;
                        }
                    }
                }
            }
        }
    }

    if (useDefault) {
        const QMetaObject* meta = obj ? obj->metaObject() : &QObject::staticMetaObject;
        QString name = obj ? obj->objectName() : QString::fromUtf8("unnamed");
        QString str = QString::fromUtf8("%0(name = \"%1\")")
                      .arg(QLatin1String(meta->className())).arg(name);

        buf = str.toLatin1();
    }
ap@webkit.org's avatar
ap@webkit.org committed
306
    return jsString(exec, buf.constData());
andersca@apple.com's avatar
andersca@apple.com committed
307
308
}

ggaren@apple.com's avatar
ggaren@apple.com committed
309
JSValue QtInstance::numberValue(ExecState* exec) const
andersca@apple.com's avatar
andersca@apple.com committed
310
{
ap@webkit.org's avatar
ap@webkit.org committed
311
    return jsNumber(exec, 0);
andersca@apple.com's avatar
andersca@apple.com committed
312
313
}

ggaren@apple.com's avatar
ggaren@apple.com committed
314
JSValue QtInstance::booleanValue() const
andersca@apple.com's avatar
andersca@apple.com committed
315
316
{
    // ECMA 9.2
317
    return jsBoolean(getObject());
andersca@apple.com's avatar
andersca@apple.com committed
318
319
}

ggaren@apple.com's avatar
ggaren@apple.com committed
320
JSValue QtInstance::valueOf(ExecState* exec) const
andersca@apple.com's avatar
andersca@apple.com committed
321
{
ap@webkit.org's avatar
ap@webkit.org committed
322
    return stringValue(exec);
andersca@apple.com's avatar
andersca@apple.com committed
323
324
325
}

// In qt_runtime.cpp
ggaren@apple.com's avatar
ggaren@apple.com committed
326
327
JSValue convertQVariantToValue(ExecState*, PassRefPtr<RootObject> root, const QVariant& variant);
QVariant convertValueToQVariant(ExecState*, JSValue, QMetaType::Type hint, int *distance);
andersca@apple.com's avatar
andersca@apple.com committed
328

329
QByteArray QtField::name() const
andersca@apple.com's avatar
andersca@apple.com committed
330
331
332
{
    if (m_type == MetaProperty)
        return m_property.name();
333
    if (m_type == ChildObject && m_childObject)
andersca@apple.com's avatar
andersca@apple.com committed
334
        return m_childObject->objectName().toLatin1();
335
#ifndef QT_NO_PROPERTIES
336
    if (m_type == DynamicProperty)
337
        return m_dynamicProperty;
338
#endif
339
    return QByteArray(); // deleted child object
andersca@apple.com's avatar
andersca@apple.com committed
340
341
}

ggaren@apple.com's avatar
ggaren@apple.com committed
342
JSValue QtField::valueFromInstance(ExecState* exec, const Instance* inst) const
andersca@apple.com's avatar
andersca@apple.com committed
343
344
345
346
347
348
349
350
351
352
353
354
355
{
    const QtInstance* instance = static_cast<const QtInstance*>(inst);
    QObject* obj = instance->getObject();

    if (obj) {
        QVariant val;
        if (m_type == MetaProperty) {
            if (m_property.isReadable())
                val = m_property.read(obj);
            else
                return jsUndefined();
        } else if (m_type == ChildObject)
            val = QVariant::fromValue((QObject*) m_childObject);
356
#ifndef QT_NO_PROPERTIES
andersca@apple.com's avatar
andersca@apple.com committed
357
358
        else if (m_type == DynamicProperty)
            val = obj->property(m_dynamicProperty);
359
#endif
hausmann@webkit.org's avatar
hausmann@webkit.org committed
360
        return convertQVariantToValue(exec, inst->rootObject(), val);
andersca@apple.com's avatar
andersca@apple.com committed
361
    }
362
    QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name()));
363
    return throwError(exec, createError(exec, msg.toLatin1().constData()));
andersca@apple.com's avatar
andersca@apple.com committed
364
365
}

ggaren@apple.com's avatar
ggaren@apple.com committed
366
void QtField::setValueToInstance(ExecState* exec, const Instance* inst, JSValue aValue) const
andersca@apple.com's avatar
andersca@apple.com committed
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
{
    if (m_type == ChildObject) // QtScript doesn't allow setting to a named child
        return;

    const QtInstance* instance = static_cast<const QtInstance*>(inst);
    QObject* obj = instance->getObject();
    if (obj) {
        QMetaType::Type argtype = QMetaType::Void;
        if (m_type == MetaProperty)
            argtype = (QMetaType::Type) QMetaType::type(m_property.typeName());

        // dynamic properties just get any QVariant
        QVariant val = convertValueToQVariant(exec, aValue, argtype, 0);
        if (m_type == MetaProperty) {
            if (m_property.isWritable())
                m_property.write(obj, val);
383
384
385
        }
#ifndef QT_NO_PROPERTIES
        else if (m_type == DynamicProperty)
andersca@apple.com's avatar
andersca@apple.com committed
386
            obj->setProperty(m_dynamicProperty.constData(), val);
387
#endif
andersca@apple.com's avatar
andersca@apple.com committed
388
    } else {
389
        QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name()));
390
        throwError(exec, createError(exec, msg.toLatin1().constData()));
andersca@apple.com's avatar
andersca@apple.com committed
391
392
393
394
395
396
    }
}


}
}