value = QVariant::fromValue((QObject *)0);
} else {
value = ep->v8engine.toVariant(result, d->property.propertyType());
- if (value.userType() == QMetaType::QObjectStar && !qvariant_cast<QObject*>(value)) {
- // If the object is null, we extract the predicted type. While this isn't
- // 100% reliable, in many cases it gives us better error messages if we
- // assign this null-object to an incompatible property
- // XXX aakenned
- // int type = ep->objectClass->objectType(scriptValue);
- int type = QMetaType::QObjectStar;
- QObject *o = 0;
- value = QVariant(type, (void *)&o);
- }
}
state->bindValues.at(ii);
for (int jj = 0; jj < bv.count; ++jj) {
if(bv.at(jj)) {
- // XXX akennedy
bv.at(jj)->m_mePtr = 0;
bv.at(jj)->setEnabled(true, QDeclarativePropertyPrivate::BypassInterceptor |
QDeclarativePropertyPrivate::DontRemoveBinding);
QObject *contextObject;
// Any script blocks that exist on this context
- // XXX aakenned
QList<v8::Persistent<v8::Object> > importedScripts;
-// QList<QScriptValue> importedScripts;
// Context base url
QUrl url;
}
QDeclarativeQtScriptExpression::QDeclarativeQtScriptExpression()
-: dataRef(0), expressionFunctionMode(ExplicitContext), scopeObject(0), trackChange(false),
- guardList(0), guardListLength(0), guardObject(0), guardObjectNotifyIndex(-1), deleted(0)
+: dataRef(0), extractExpressionFromFunction(false), expressionFunctionMode(ExplicitContext),
+ scopeObject(0), trackChange(false), guardList(0), guardListLength(0), guardObject(0),
+ guardObjectNotifyIndex(-1), deleted(0)
{
}
void QDeclarativeExpressionPrivate::init(QDeclarativeContextData *ctxt, v8::Handle<v8::Function> func,
QObject *me)
{
- // XXX aakenned
- // expression = func.toString();
-
QDeclarativeAbstractExpression::setContext(ctxt);
scopeObject = me;
v8function = qPersistentNew<v8::Function>(func);
expressionFunctionMode = ExplicitContext;
expressionFunctionValid = true;
+ extractExpressionFromFunction = true;
}
void QDeclarativeExpressionPrivate::init(QDeclarativeContextData *ctxt, void *expr,
QDeclarativeEngine *engine = ctxt->engine;
QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
- // XXX aakenned optimize
+ // XXX TODO: Implement script caching, like we used to do with QScriptProgram in the
+ // QtScript days
v8::HandleScope handle_scope;
v8::Context::Scope ctxtscope(ep->v8engine.context());
- // XXX try/catch?
-
+ v8::TryCatch tc;
v8::Local<v8::Object> scopeobject = ep->v8engine.qmlScope(ctxt, scope);
v8::Local<v8::Script> script = ep->v8engine.qmlModeCompile(code, filename, line);
v8::Local<v8::Value> result = script->Run(scopeobject);
+ if (tc.HasCaught()) return v8::Persistent<v8::Function>();
if (qmlscope) *qmlscope = qPersistentNew<v8::Object>(scopeobject);
return qPersistentNew<v8::Function>(v8::Local<v8::Function>::Cast(result));
}
QString QDeclarativeExpression::expression() const
{
Q_D(const QDeclarativeExpression);
+ if (d->extractExpressionFromFunction && context()->engine()) {
+ QV8Engine *v8engine = QDeclarativeEnginePrivate::getV8Engine(context()->engine());
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(v8engine->context());
+
+ return v8engine->toString(v8::Handle<v8::Value>(d->v8function));
+ }
return d->expression;
}
restoreSecondaryScope = ep->v8engine.contextWrapper()->setSecondaryScope(v8qmlscope, secondaryScope);
v8::TryCatch try_catch;
- v8::Context::Scope scope(ep->v8engine.context()); // XXX is this needed?
v8::Handle<v8::Object> This;
if (watcher.wasDeleted()) {
} else if (try_catch.HasCaught()) {
+ v8::Context::Scope scope(ep->v8engine.context());
v8::Local<v8::Message> message = try_catch.Message();
if (!message.IsEmpty()) {
QDeclarativeExpressionPrivate::exceptionToError(message, error);
QDeclarativeRefCount *dataRef;
QString expression;
+ bool extractExpressionFromFunction;
Mode expressionFunctionMode;
v8::Persistent<v8::Function> v8function;
m_scriptData->pragmas = m_pragmas;
- // XXX aakenned - what about error handling?
+ // XXX TODO: Handle errors that occur duing the script compile
QV8Engine *v8engine = &QDeclarativeEnginePrivate::get(engine)->v8engine;
v8::HandleScope handle_scope;
v8::Context::Scope scope(v8engine->context());
ctxt->importedScripts << run(ctxt, script->scripts.at(ii)->scriptData());
}
- // XXX aakenned optimize
v8::HandleScope handle_scope;
v8::Context::Scope scope(v8engine->context());
QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(ctxt->engine);
ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
- // XXX aakenned
v8::Handle<v8::Function> function = method(id);
QDeclarativeVMEMetaData::MethodData *data = metaData->methodData() + id;
0, v8::External::Wrap(engine));
d->nodePrototype->SetAccessor(v8::String::New("attributes"), attributes,
0, v8::External::Wrap(engine));
- // XXX freeze
+ engine->freezeObject(d->nodePrototype);
}
return d->nodePrototype;
}
d->elementPrototype->SetPrototype(Node::prototype(engine));
d->elementPrototype->SetAccessor(v8::String::New("tagName"), nodeName,
0, v8::External::Wrap(engine));
- // XXX freeze
+ engine->freezeObject(d->elementPrototype);
}
return d->elementPrototype;
}
0, v8::External::Wrap(engine));
d->attrPrototype->SetAccessor(v8::String::New("ownerElement"), ownerElement,
0, v8::External::Wrap(engine));
- // XXX freeze
+ engine->freezeObject(d->attrPrototype);
}
return d->attrPrototype;
}
0, v8::External::Wrap(engine));
d->characterDataPrototype->SetAccessor(v8::String::New("length"), length,
0, v8::External::Wrap(engine));
- // XXX freeze
+ engine->freezeObject(d->characterDataPrototype);
}
return d->characterDataPrototype;
}
0, v8::External::Wrap(engine));
d->textPrototype->SetAccessor(v8::String::New("wholeText"), wholeText,
0, v8::External::Wrap(engine));
- // XXX freeze
+ engine->freezeObject(d->textPrototype);
}
return d->textPrototype;
}
if (d->cdataPrototype.IsEmpty()) {
d->cdataPrototype = qPersistentNew<v8::Object>(v8::Object::New());
d->cdataPrototype->SetPrototype(Text::prototype(engine));
- // XXX freeze
+ engine->freezeObject(d->cdataPrototype);
}
return d->cdataPrototype;
}
0, v8::External::Wrap(engine));
d->documentPrototype->SetAccessor(v8::String::New("documentElement"), documentElement,
0, v8::External::Wrap(engine));
- // XXX freeze
+ engine->freezeObject(d->documentPrototype);
}
return d->documentPrototype;
}
ot->SetIndexedPropertyHandler(indexed, 0, 0, 0, 0, v8::External::Wrap(engine));
ot->SetFallbackPropertyHandler(named, 0, 0, 0, 0, v8::External::Wrap(engine));
d->namedNodeMapPrototype = qPersistentNew<v8::Object>(ot->NewInstance());
- // XXX freeze
+ engine->freezeObject(d->namedNodeMapPrototype);
}
return d->namedNodeMapPrototype;
}
if (index < r->d->children.count()) {
return Node::create(engine, r->d->children.at(index));
} else {
- // XXX RangeError exception?
return v8::Undefined();
}
}
ot->SetAccessor(v8::String::New("length"), length, 0, v8::External::Wrap(engine));
ot->SetIndexedPropertyHandler(indexed, 0, 0, 0, 0, v8::External::Wrap(engine));
d->nodeListPrototype = qPersistentNew<v8::Object>(ot->NewInstance());
- // XXX freeze
+ engine->freezeObject(d->nodeListPrototype);
}
return d->nodeListPrototype;
}
QObject *secondaryScope;
- // XXX aakenned - this is somewhat of a horrible abuse of external strings :)
+ // This is a pretty horrible hack, and an abuse of external strings. When we create a
+ // sub-context (a context created by a Qt.include() in an external javascript file),
+ // we pass a specially crafted SubContext external string as the v8::Script::Data() to
+ // the script, which contains a pointer to the context. We can then access the
+ // v8::Script::Data() later on to resolve names and URLs against the sub-context instead
+ // of the main outer context.
struct SubContext : public v8::String::ExternalStringResource {
SubContext(QDeclarativeContextData *context) : context(context) {}
QDeclarativeGuardedContextData context;
v8::Local<v8::Object> QV8ContextWrapper::qmlScope(QDeclarativeContextData *ctxt, QObject *scope)
{
- // XXX aakenned - NewInstance() is slow for our case
+ // XXX NewInstance() should be optimized
v8::Local<v8::Object> rv = m_constructor->NewInstance();
QV8ContextResource *r = new QV8ContextResource(m_engine, ctxt, scope);
rv->SetExternalResource(r);
context->isInternal = true;
context->isJSContext = true;
- // XXX aakenned - NewInstance() is slow for our case
+ // XXX NewInstance() should be optimized
v8::Local<v8::Object> rv = m_urlConstructor->NewInstance();
QV8ContextResource *r = new QV8ContextResource(m_engine, context, 0);
r->ownsContext = true;
QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(info.This());
if (!resource)
- return v8::Undefined(); // XXX Should we throw here?
+ return v8::Undefined();
QV8Engine *engine = resource->engine;
QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(info.This());
if (!resource)
- return v8::Undefined(); // XXX Should we throw here?
+ return v8::Undefined();
- // XXX aakenned too agressive
+ // Its possible we could delay the calculation of the "actual" context (in the case
+ // of sub contexts) until it is definately needed.
QDeclarativeContextData *context = resource->getContext();
if (!context)
- return v8::Undefined(); // XXX Should we throw here?
+ return v8::Undefined();
// Search type (attached property/enum/imported scripts) names
// Secondary scope object
QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(info.This());
if (!resource)
- return v8::Undefined(); // XXX Should we throw here?
+ return v8::Handle<v8::Value>();
QV8Engine *engine = resource->engine;
QString error = QLatin1String("Invalid write to global property \"") + engine->toString(property) +
QLatin1String("\"");
v8::ThrowException(v8::Exception::Error(engine->toString(error)));
- return v8::Undefined();
+ return v8::Handle<v8::Value>();
}
}
QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(info.This());
if (!resource)
- return v8::Undefined(); // XXX Should we throw here?
+ return v8::Undefined();
- // XXX aakenned too agressive
+ // Its possible we could delay the calculation of the "actual" context (in the case
+ // of sub contexts) until it is definately needed.
QDeclarativeContextData *context = resource->getContext();
if (!context)
- return v8::Undefined(); // XXX Should we throw here?
+ return v8::Undefined();
// See QV8ContextWrapper::Getter for resolution order
void addSubContext(v8::Handle<v8::Object> qmlglobal, v8::Handle<v8::Script>,
QDeclarativeContextData *ctxt);
- // XXX aakenned - remove this abomination
+ // XXX We only use the secondary scope to pass the "arguments" of the signal to
+ // on<SignalName> properties. Instead of doing this we should rewrite the
+ // JavaScript closure function to accept these arguments as named parameters.
+ // To keep backwards compatibility we have to check that the argument names are
+ // not members of the QV8Engine::illegalNames() set.
QObject *setSecondaryScope(v8::Handle<v8::Object>, QObject *);
QDeclarativeContextData *callingContext();
#include <private/qdeclarativexmlhttprequest_p.h>
#include <private/qdeclarativesqldatabase_p.h>
-// XXX Need to check all the global functions will also work in a worker script where the QDeclarativeEngine
-// is not available
+// XXX TODO: Need to check all the global functions will also work in a worker script where the
+// QDeclarativeEngine is not available
QT_BEGIN_NAMESPACE
QV8Engine::QV8Engine()
delete m_listModelData;
m_listModelData = 0;
+ qPersistentDispose(m_freezeObject);
qPersistentDispose(m_getOwnPropertyNames);
m_valueTypeWrapper.destroy();
m_variantWrapper.destroy();
}
initializeGlobal(m_context->Global());
- freezeGlobal();
+ freezeObject(m_context->Global());
}
QString QV8Engine::toStringStatic(v8::Handle<v8::Value> jsstr)
return v8::Null();
}
} else if (type == qMetaTypeId<QList<QObject *> >()) {
- // XXX aakenned Can this be more optimal? Just use Array as a prototype and
- // implement directly against QList<QObject*>?
+ // XXX Can this be made more by using Array as a prototype and implementing
+ // directly against QList<QObject*>?
const QList<QObject *> &list = *(QList<QObject *>*)ptr;
v8::Local<v8::Array> array = v8::Array::New(list.count());
for (int ii = 0; ii < list.count(); ++ii)
return newQObject(obj);
}
- return m_variantWrapper.newVariant(variant);
-
- // XXX aakenned
-#if 0
-#ifndef QT_NO_REGEXP
- case QMetaType::QRegExp:
- result = newRegExp(exec, *reinterpret_cast<const QRegExp *>(ptr));
- break;
-#endif
-#ifndef QT_NO_QOBJECT
-#endif
- case QMetaType::QVariant:
- result = eng->newVariant(*reinterpret_cast<const QVariant*>(ptr));
- break;
- default:
- if (type == qMetaTypeId<QScriptValue>()) {
- result = eng->scriptValueToJSCValue(*reinterpret_cast<const QScriptValue*>(ptr));
- if (!result)
- return JSC::jsUndefined();
- }
-
-#ifndef QT_NO_QOBJECT
- // lazy registration of some common list types
- else if (type == qMetaTypeId<QObjectList>()) {
- qScriptRegisterSequenceMetaType<QObjectList>(eng->q_func());
- return create(exec, type, ptr);
- }
-#endif
- else if (type == qMetaTypeId<QList<int> >()) {
- qScriptRegisterSequenceMetaType<QList<int> >(eng->q_func());
- return create(exec, type, ptr);
- }
+ // XXX TODO: To be compatible, we still need to handle:
+ // + QScriptValue
+ // + QObjectList
+ // + QList<int>
- else {
- QByteArray typeName = QMetaType::typeName(type);
- if (typeName.endsWith('*') && !*reinterpret_cast<void* const *>(ptr))
- return JSC::jsNull();
- else
- result = eng->newVariant(QVariant(type, ptr));
- }
- }
- }
-#endif
+ return m_variantWrapper.newVariant(variant);
}
// A handle scope and context must be entered
console->Set(v8::String::New("log"), printFn);
console->Set(v8::String::New("debug"), printFn);
- // XXX - Qt global object properties
-
v8::Local<v8::Object> qt = v8::Object::New();
// Set all the enums from the "Qt" namespace
qt->Set(v8::String::New("md5"), V8FUNCTION(md5, this));
qt->Set(v8::String::New("btoa"), V8FUNCTION(btoa, this));
qt->Set(v8::String::New("atob"), V8FUNCTION(atob, this));
- qt->Set(v8::String::New("quit"), V8FUNCTION(quit, this));
qt->Set(v8::String::New("resolvedUrl"), V8FUNCTION(resolvedUrl, this));
if (m_engine) {
+ qt->Set(v8::String::New("quit"), V8FUNCTION(quit, this));
qt->Set(v8::String::New("createQmlObject"), V8FUNCTION(createQmlObject, this));
qt->Set(v8::String::New("createComponent"), V8FUNCTION(createComponent, this));
}
- // XXX translator functions
+ // XXX TODO - Implement translator functions
global->Set(v8::String::New("print"), printFn);
global->Set(v8::String::New("console"), console);
global->Set(v8::String::New("Qt"), qt);
global->Set(v8::String::New("gc"), V8FUNCTION(gc, this));
- // XXX mainthread only
m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(this);
m_sqlDatabaseData = qt_add_qmlsqldatabase(this);
for (quint32 ii = 0; ii < namesArray->Length(); ++ii)
m_illegalNames.insert(toString(namesArray->Get(ii)));
}
+
+ {
+#define FREEZE_SOURCE "(function freeze_recur(obj) { "\
+ " if (Qt.isQtObject(obj)) return;"\
+ " if (obj != Function.connect && obj != Function.disconnect && "\
+ " obj instanceof Object) {"\
+ " var properties = Object.getOwnPropertyNames(obj);"\
+ " for (var prop in properties) { "\
+ " if (prop == \"connect\" || prop == \"disconnect\") {"\
+ " Object.freeze(obj[prop]); "\
+ " continue;"\
+ " }"\
+ " freeze_recur(obj[prop]);"\
+ " }"\
+ " }"\
+ " if (obj instanceof Object) {"\
+ " Object.freeze(obj);"\
+ " }"\
+ "})"
+
+ v8::Local<v8::Script> freeze = v8::Script::New(v8::String::New(FREEZE_SOURCE));
+ v8::Local<v8::Value> result = freeze->Run();
+ Q_ASSERT(result->IsFunction());
+ m_freezeObject = qPersistentNew(v8::Local<v8::Function>::Cast(result));
+#undef FREEZE_SOURCE
+ }
}
-void QV8Engine::freezeGlobal()
+void QV8Engine::freezeObject(v8::Handle<v8::Value> value)
{
- // Freeze the global object
- // XXX I don't think this is sufficient as it misses non-enumerable properties
-#define FREEZE "(function freeze_recur(obj) { "\
- " if (Qt.isQtObject(obj)) return;"\
- " for (var prop in obj) { " \
- " if (prop == \"connect\" || prop == \"disconnect\") {" \
- " Object.freeze(obj[prop]); "\
- " continue;" \
- " }" \
- " freeze_recur(obj[prop]);" \
- " }" \
- " if (obj instanceof Object) {" \
- " Object.freeze(obj);" \
- " }"\
- "})(this);"
- v8::Local<v8::Script> test = v8::Script::New(v8::String::New(FREEZE));
-#undef FREEZE
-
- test->Run();
+ v8::Handle<v8::Value> args[] = { value };
+ m_freezeObject->Call(global(), 1, args);
}
void QV8Engine::gc()
v8::Handle<v8::Value> QV8Engine::resolvedUrl(const v8::Arguments &args)
{
QUrl url = V8ENGINE()->toVariant(args[0], -1).toUrl();
- // XXX uses QDeclarativeEngine which means it wont work in worker script?
QDeclarativeEngine *e = V8ENGINE()->engine();
QDeclarativeEnginePrivate *p = 0;
if (e) p = QDeclarativeEnginePrivate::get(e);
*/
v8::Handle<v8::Value> QV8Engine::quit(const v8::Arguments &args)
{
- // XXX worker script?
QDeclarativeEnginePrivate::get(V8ENGINE()->engine())->sendQuit();
return v8::Undefined();
}
QDeclarativeContextData *callingContext();
v8::Local<v8::Array> getOwnPropertyNames(v8::Handle<v8::Object>);
+ void freezeObject(v8::Handle<v8::Value>);
inline QString toString(v8::Handle<v8::Value> string);
inline QString toString(v8::Handle<v8::String> string);
QV8ValueTypeWrapper m_valueTypeWrapper;
v8::Persistent<v8::Function> m_getOwnPropertyNames;
+ v8::Persistent<v8::Function> m_freezeObject;
void *m_xmlHttpRequestData;
void *m_sqlDatabaseData;
QVariant toBasicVariant(v8::Handle<v8::Value>);
void initializeGlobal(v8::Handle<v8::Object>);
- void freezeGlobal();
static v8::Handle<v8::Value> gc(const v8::Arguments &args);
static v8::Handle<v8::Value> print(const v8::Arguments &args);
return m_valueTypeWrapper.newValueType(value, type);
}
-// XXX perf?
+// XXX Can this be made more optimal? It is called prior to resolving each and every
+// unqualified name in QV8ContextWrapper.
bool QV8Engine::startsWithUpper(v8::Handle<v8::String> string)
{
uint16_t buffer[2];
v8::Local<v8::Object> QV8Include::resultValue(Status status)
{
- // XXX aakenned inefficient
+ // XXX It seems inefficient to create this object from scratch each time.
v8::Local<v8::Object> result = v8::Object::New();
result->Set(v8::String::New("OK"), v8::Integer::New(Ok));
result->Set(v8::String::New("LOADING"), v8::Integer::New(Loading));
{
if (!callback.IsEmpty()) {
v8::Handle<v8::Value> args[] = { status };
- // XXX TryCatch?
+ v8::TryCatch tc;
callback->Call(engine->global(), 1, args);
}
}
if (!object || propId == -1)
return v8::Null();
- // XXX aakenned - NewInstance() is slow for our case
+ // XXX NewInstance() should be optimized
v8::Local<v8::Object> rv = m_constructor->NewInstance();
QV8ListResource *r = new QV8ListResource(m_engine);
r->object = object;
v8::Handle<v8::Value> QV8ListWrapper::newList(const QDeclarativeListProperty<QObject> &prop, int propType)
{
- // XXX aakenned - NewInstance() is slow for our case
+ // XXX NewInstance() should be optimized
v8::Local<v8::Object> rv = m_constructor->NewInstance();
QV8ListResource *r = new QV8ListResource(m_engine);
r->object = prop.object;
#define QOBJECT_TOSTRING_INDEX -2
#define QOBJECT_DESTROY_INDEX -3
-// XXX Need to check all calls to QDeclarativeEngine *engine() to confirm this class works
+// XXX TODO: Need to review all calls to QDeclarativeEngine *engine() to confirm QObjects work
// correctly in a worker thread
class QV8QObjectResource : public QV8ObjectResource
v8::Handle<v8::String> property,
QV8QObjectWrapper::RevisionMode revisionMode)
{
- // XXX aakenned This can't possibly be the best solution!!!
+ // XXX More recent versions of V8 introduced "Callable" objects. It is possible that these
+ // will be a faster way of creating QObject method objects.
struct MethodClosure {
static v8::Handle<v8::Value> create(QV8Engine *engine, QObject *object,
v8::Handle<v8::Value> *objectHandle,
return v8engine->typeWrapper()->newObject(object, data->typeNamespace, QV8TypeWrapper::ExcludeEnums);
}
}
+ }
- return v8::Undefined();
- } else {
- // XXX throw?
- return v8::Undefined();
- }
+ return v8::Undefined();
}
v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property,
if (ddata) { cache->addref(); ddata->propertyCache = cache; }
} else {
// Not cachable - fall back to QMetaObject (eg. dynamic meta object)
- // XXX QDeclarativeOpenMetaObject has a cache, so this is suboptimal.
- // XXX This is a workaround for QTBUG-9420.
const QMetaObject *mo = object->metaObject();
int pc = mo->propertyCount();
int po = mo->propertyOffset();
QString toString = QLatin1String("toString");
QString destroy = QLatin1String("destroy");
- // XXX Enables fast property accessors. These more than double the property access
+ // XXX TODO: Enables fast property accessors. These more than double the property access
// performance, but the cost of setting up this structure hasn't been measured so
- // its not guarenteed that this is a win overall
+ // its not guarenteed that this is a win overall. We need to try and measure the cost.
for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
Data *property = *iter;
if (property->isFunction() || !property->isWritable() ||
if (ddata->propertyCache) {
rv = ddata->propertyCache->newQObject(object, engine);
} else {
- // XXX aakenned - NewInstance() is slow for our case
+ // XXX NewInstance() should be optimized
rv = m_constructor->NewInstance();
QV8QObjectResource *r = new QV8QObjectResource(engine, object);
rv->SetExternalResource(r);
~QV8QObjectConnectionList();
struct Connection {
+ Connection()
+ : needsDestroy(false) {}
+ Connection(const Connection &other)
+ : thisObject(other.thisObject), function(other.function), needsDestroy(false) {}
+ Connection &operator=(const Connection &other) {
+ thisObject = other.thisObject;
+ function = other.function;
+ needsDestroy = other.needsDestroy;
+ return *this;
+ }
+
v8::Persistent<v8::Object> thisObject;
v8::Persistent<v8::Function> function;
+
+ void dispose() {
+ qPersistentDispose(thisObject);
+ qPersistentDispose(function);
+ }
+
+ bool needsDestroy;
+ };
+
+ struct ConnectionList : public QList<Connection> {
+ ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {}
+ int connectionsInUse;
+ bool connectionsNeedClean;
};
QV8Engine *engine;
- typedef QHash<int, QList<Connection> > SlotHash;
+ typedef QHash<int, ConnectionList> SlotHash;
SlotHash slotHash;
+ bool needsDestroy;
+ int inUse;
virtual void objectDestroyed(QObject *);
virtual int qt_metacall(QMetaObject::Call, int, void **);
};
QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
-: QDeclarativeGuard<QObject>(object), engine(engine)
+: QDeclarativeGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0)
{
}
void QV8QObjectConnectionList::objectDestroyed(QObject *object)
{
engine->qobjectWrapper()->m_connections.remove(object);
- delete this;
+
+ if (inUse)
+ needsDestroy = true;
+ else
+ delete this;
}
int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
SlotHash::Iterator iter = slotHash.find(index);
if (iter == slotHash.end())
return -1;
- QList<Connection> &connections = *iter;
- if (connections.isEmpty())
+ ConnectionList &connectionList = *iter;
+ if (connectionList.isEmpty())
return -1;
- // XXX optimize
+ inUse++;
+
+ connectionList.connectionsInUse++;
+
+ QList<Connection> connections = connectionList;
+
QMetaMethod method = data()->metaObject()->method(index);
Q_ASSERT(method.methodType() == QMetaMethod::Signal);
+ // XXX TODO: We should figure out a way to cache the parameter types to avoid resolving
+ // them each time.
QList<QByteArray> params = method.parameterTypes();
v8::HandleScope handle_scope;
}
}
- // XXX what if this list changes or this object is deleted during the calls?
for (int ii = 0; ii < connections.count(); ++ii) {
Connection &connection = connections[ii];
+ if (connection.needsDestroy)
+ continue;
if (connection.thisObject.IsEmpty()) {
connection.function->Call(engine->global(), argCount, args.data());
} else {
connection.function->Call(connection.thisObject, argCount, args.data());
}
}
+
+ connectionList.connectionsInUse--;
+ if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) {
+ for (QList<Connection>::Iterator iter = connectionList.begin();
+ iter != connectionList.end(); ) {
+ if (iter->needsDestroy) {
+ iter->dispose();
+ iter = connectionList.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+ }
+
+ inUse--;
+ if (inUse == 0 && needsDestroy)
+ delete this;
}
return -1;
QV8QObjectConnectionList *connectionList = *iter;
QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
if (slotIter == connectionList->slotHash.end()) {
- slotIter = connectionList->slotHash.insert(signalIndex, QList<QV8QObjectConnectionList::Connection>());
+ slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList());
QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
}
if (slotIter == connectionList->slotHash.end())
return v8::Undefined(); // Nothing to disconnect from
- QList<QV8QObjectConnectionList::Connection> &connections = *slotIter;
+ QV8QObjectConnectionList::ConnectionList &connections = *slotIter;
v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
if (connectedFunctionData == functionData) {
// Match!
- qPersistentDispose(connection.thisObject);
- qPersistentDispose(connection.function);
- connections.removeAt(ii);
+ if (connections.connectionsInUse) {
+ connection.needsDestroy = true;
+ } else {
+ connection.dispose();
+ connections.removeAt(ii);
+ }
return v8::Undefined();
}
}
connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
(connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
// Match!
- qPersistentDispose(connection.thisObject);
- qPersistentDispose(connection.function);
- connections.removeAt(ii);
+ if (connections.connectionsInUse) {
+ connection.needsDestroy = true;
+ } else {
+ connection.dispose();
+ connections.removeAt(ii);
+ }
return v8::Undefined();
}
}
QDeclarativeData::get(object, true)->setImplicitDestructible();
return engine->newQObject(object);
} else if (type == qMetaTypeId<QList<QObject *> >()) {
- // XXX aakenned Can this be more optimal? Just use Array as a prototype and
- // implement directly against QList<QObject*>?
+ // XXX Can this be made more by using Array as a prototype and implementing
+ // directly against QList<QObject*>?
QList<QObject *> &list = *(QList<QObject *>*)&data;
v8::Local<v8::Array> array = v8::Array::New(list.count());
for (int ii = 0; ii < list.count(); ++ii)
v8::Local<v8::Object> QV8TypeWrapper::newObject(QObject *o, QDeclarativeType *t, TypeNameMode mode)
{
Q_ASSERT(t);
- // XXX aakenned - NewInstance() is slow for our case
+ // XXX NewInstance() should be optimized
v8::Local<v8::Object> rv = m_constructor->NewInstance();
QV8TypeResource *r = new QV8TypeResource(m_engine);
r->mode = mode; r->object = o; r->type = t;
v8::Local<v8::Object> QV8TypeWrapper::newObject(QObject *o, QDeclarativeTypeNameCache *t, TypeNameMode mode)
{
Q_ASSERT(t);
- // XXX aakenned - NewInstance() is slow for our case
+ // XXX NewInstance() should be optimized
v8::Local<v8::Object> rv = m_constructor->NewInstance();
QV8TypeResource *r = new QV8TypeResource(m_engine);
t->addref();
if (d && d->type) {
return v8engine->typeWrapper()->newObject(object, d->type, resource->mode);
} else if (QDeclarativeMetaType::ModuleApiInstance *moduleApi = typeNamespace->moduleApi()) {
-
- // XXX QtScript/JSC required
+ // XXX TODO: Currently module APIs are implemented against QScriptValues. Consequently we
+ // can't do anything here until the QtScript/V8 binding is complete.
return v8::Undefined();
}
QV8Engine *v8engine = resource->engine;
- // XXX module api
+ // XXX TODO: Implement writes to module API objects
if (resource->type && resource->object) {
QDeclarativeType *type = resource->type;
v8::Local<v8::Object> QV8ValueTypeWrapper::newValueType(QObject *object, int property, QDeclarativeValueType *type)
{
- // XXX aakenned - NewInstance() is slow for our case
+ // XXX NewInstance() should be optimized
v8::Local<v8::Object> rv = m_constructor->NewInstance();
QV8ValueTypeReferenceResource *r = new QV8ValueTypeReferenceResource(m_engine);
r->type = type; r->object = object; r->property = property;
v8::Local<v8::Object> QV8ValueTypeWrapper::newValueType(const QVariant &value, QDeclarativeValueType *type)
{
- // XXX aakenned - NewInstance() is slow for our case
+ // XXX NewInstance() should be optimized
v8::Local<v8::Object> rv = m_constructor->NewInstance();
QV8ValueTypeCopyResource *r = new QV8ValueTypeCopyResource(m_engine);
r->type = type; r->value = value;
QV8ValueTypeResource *r = v8_resource_cast<QV8ValueTypeResource>(info.This());
if (!r) return v8::Undefined();
- // XXX aakenned - this is horribly inefficient. People seem to have taken a
- // liking to value type properties, so we should probably try and optimize it
- // a little.
+ // XXX This is horribly inefficient. Sadly people seem to have taken a liking to
+ // value type properties, so we should probably try and optimize it a little.
+ // We should probably just replace all value properties with dedicated accessors.
QByteArray propName = r->engine->toString(property).toUtf8();
int index = r->type->metaObject()->indexOfProperty(propName.constData());
bool scarceResource = value.type() == QVariant::Pixmap ||
value.type() == QVariant::Image;
- // XXX aakenned - NewInstance() is slow for our case
+ // XXX NewInstance() should be optimized
v8::Local<v8::Object> rv;
QV8VariantResource *r = new QV8VariantResource(m_engine, value);
return rv;
}
-// XXX double check exception safety
+// XXX TODO: Check that worker script is exception safe in the case of
+// serialization/deserialization failures
-#include <QDebug>
#define ALIGN(size) (((size) + 3) & ~3)
void QV8Worker::serialize(QByteArray &data, v8::Handle<v8::Value> v, QV8Engine *engine)
{
string->Write((uint16_t*)buffer);
} else if (v->IsFunction()) {
- // XXX
+ // XXX TODO: Implement passing function objects between the main and
+ // worker scripts
push(data, valueheader(WorkerUndefined));
} else if (v->IsArray()) {
v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(v);
}
}
} else if (engine->isQObject(v)) {
- // XXX Can we generalize this?
+ // XXX TODO: Generalize passing objects between the main thread and worker scripts so
+ // that others can trivially plug in their elements.
QDeclarativeListModel *lm = qobject_cast<QDeclarativeListModel *>(engine->toQObject(v));
if (lm && lm->agent()) {
QDeclarativeListModelWorkerAgent *agent = lm->agent();