if (!name->IsString() && !name->IsSymbol()) { QString notStringMessage("ScriptObjectV8Proxy::v8Get: " + proxy->_engine->scriptValueDebugDetailsV8(V8ScriptValue(proxy->_engine, name))); qCDebug(scriptengine_v8) << notStringMessage; Q_ASSERT(false); } v8::Local<v8::String> v8NameString; ContextScopeV8 contextScopeV8(proxy->_engine); if (name->IsString()) { V8ScriptString nameString(proxy->_engine, v8::Local<v8::String>::Cast(name)); uint id; QueryFlags flags = proxy->queryProperty(object, nameString, HandlesReadAccess, &id); if (flags) { V8ScriptValue value = proxy->property(object, nameString, id); info.GetReturnValue().Set(value.get()); return; } } v8::Local<v8::Value> property; if(info.This()->GetInternalField(2).As<v8::Object>()->Get(proxy->_engine->getContext(), name).ToLocal(&property)) { info.GetReturnValue().Set(property); } else { qCDebug(scriptengine_v8) << "Value not found: " << *utf8Value; }} void ScriptObjectV8Proxy::v8Set(v8::Local<v8::Name> name, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::HandleScope handleScope(info.GetIsolate()); v8::String::Utf8Value utf8Value(info.GetIsolate(), name); v8::Local<v8::Value> objectV8 = info.This(); ScriptObjectV8Proxy *proxy = ScriptObjectV8Proxy::unwrapProxy(info.GetIsolate(), objectV8); if (!proxy) { qCDebug(scriptengine_v8) << "Proxy object not found when setting: " << *utf8Value; return; } V8ScriptValue object(proxy->_engine, objectV8); if (!name->IsString() && !name->IsSymbol()) { QString notStringMessage("ScriptObjectV8Proxy::v8Set: " + proxy->_engine->scriptValueDebugDetailsV8(V8ScriptValue(proxy->_engine, name))); qCDebug(scriptengine_v8) << notStringMessage; Q_ASSERT(false); } ContextScopeV8 contextScopeV8(proxy->_engine); if (name->IsString()) { V8ScriptString nameString(proxy->_engine, v8::Local<v8::String>::Cast(name)); uint id; QueryFlags flags = proxy->queryProperty(object, nameString, HandlesWriteAccess, &id); if (flags) { proxy->setProperty(object, nameString, id, V8ScriptValue(proxy->_engine, value)); info.GetReturnValue().Set(value); return; } } if (info.This()->GetInternalField(2).As<v8::Object>()->Set(proxy->_engine->getContext(), name, value).FromMaybe(false)) { info.GetReturnValue().Set(value); } else { qCDebug(scriptengine_v8) << "Set failed: " << *utf8Value; }} void ScriptObjectV8Proxy::v8GetPropertyNames(const v8::PropertyCallbackInfo<v8::Array>& info) { v8::HandleScope handleScope(info.GetIsolate()); auto context = info.GetIsolate()->GetCurrentContext(); v8::Context::Scope contextScope(context); v8::Local<v8::Value> objectV8 = info.This(); ScriptObjectV8Proxy *proxy = ScriptObjectV8Proxy::unwrapProxy(info.GetIsolate(), objectV8); if (!proxy) { qCDebug(scriptengine_v8) << "ScriptObjectV8Proxy::v8GetPropertyNames: Proxy object not found when listing"; return; } V8ScriptValue object(proxy->_engine, objectV8); v8::Local<v8::Array> properties = proxy->getPropertyNames(); v8::Local<v8::Array> objectProperties; uint32_t propertiesLength = properties->Length(); if (info.This()->GetInternalField(2).As<v8::Object>()->GetPropertyNames(context).ToLocal(&objectProperties)) { for (uint32_t n = 0; n < objectProperties->Length(); n++) { if(!properties->Set(context, propertiesLength+n, objectProperties->Get(context, n).ToLocalChecked()).FromMaybe(false)) { qCDebug(scriptengine_v8) << "ScriptObjectV8Proxy::v8GetPropertyNames: Cannot add member name"; } } } info.GetReturnValue().Set(properties);} v8::Local<v8::Array> ScriptObjectV8Proxy::getPropertyNames() { auto isolate = _engine->getIsolate(); v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::EscapableHandleScope handleScope(_engine->getIsolate()); auto context = _engine->getContext(); v8::Context::Scope contextScope(context); //V8TODO: this is really slow. It could be cached if this is called often. v8::Local<v8::Array> properties = v8::Array::New(isolate, _props.size() + _methods.size() + _signals.size()); uint32_t position = 0; for (PropertyDefMap::const_iterator i = _props.begin(); i != _props.end(); i++){ v8::Local<v8::String> name = v8::String::NewFromUtf8(isolate, i.value().name.toStdString().c_str()).ToLocalChecked(); if(!properties->Set(context, position++, name).FromMaybe(false)) { qCDebug(scriptengine_v8) << "ScriptObjectV8Proxy::getPropertyNames: Cannot add property member name"; } } for (MethodDefMap::const_iterator i = _methods.begin(); i != _methods.end(); i++){ v8::Local<v8::String> name = v8::String::NewFromUtf8(isolate, i.value().name.toStdString().c_str()).ToLocalChecked(); if(!properties->Set(context, position++, name).FromMaybe(false)) { qCDebug(scriptengine_v8) << "ScriptObjectV8Proxy::getPropertyNames: Cannot add property member name"; } } for (SignalDefMap::const_iterator i = _signals.begin(); i != _signals.end(); i++){ v8::Local<v8::String> name = v8::String::NewFromUtf8(isolate, i.value().name.toStdString().c_str()).ToLocalChecked(); if(!properties->Set(context, position++, name).FromMaybe(false)) { qCDebug(scriptengine_v8) << "ScriptObjectV8Proxy::getPropertyNames: Cannot add property member name"; } } return handleScope.Escape(properties);} V8ScriptValue ScriptObjectV8Proxy::property(const V8ScriptValue& object, const V8ScriptString& name, uint id) { auto isolate = _engine->getIsolate(); v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); auto context = _engine->getContext(); v8::Context::Scope contextScope(context); QObject* qobject = _object; if (!qobject) { _engine->getIsolate()->ThrowError("Referencing deleted native object"); return V8ScriptValue(_engine, v8::Null(isolate)); } const QMetaObject* metaObject = qobject->metaObject(); switch (id & TYPE_MASK) { case PROPERTY_TYPE: { int propId = id & ~TYPE_MASK; PropertyDefMap::const_iterator lookup = _props.find(propId); if (lookup == _props.cend()) return V8ScriptValue(_engine, v8::Null(isolate)); QMetaProperty prop = metaObject->property(propId); ScriptValue scriptThis = ScriptValue(new ScriptValueV8Wrapper(_engine, object)); ScriptPropertyContextV8Wrapper ourContext(scriptThis, _engine->currentContext()); ScriptContextGuard guard(&ourContext); QVariant varValue = prop.read(qobject); return _engine->castVariantToValue(varValue); } case METHOD_TYPE: { int methodId = id & ~TYPE_MASK; MethodDefMap::const_iterator lookup = _methods.find(methodId); if (lookup == _methods.cend()) return V8ScriptValue(_engine, v8::Null(isolate)); const MethodDef& methodDef = lookup.value(); for (auto iter = methodDef.methods.begin(); iter != methodDef.methods.end(); iter++ ) { if((*iter).returnType() == QMetaType::UnknownType) { qCDebug(scriptengine_v8) << "Method with QMetaType::UnknownType " << metaObject->className() << " " << (*iter).name(); } } //V8TODO: is new method created during every call? It needs to be cached instead v8::Local<v8::Value> property; if(_v8Object.Get(isolate)->GetInternalField(2).As<v8::Object>()->Get(context, name.constGet()).ToLocal(&property)) { if (!property->IsUndefined()) { return V8ScriptValue(_engine, property); } } Q_ASSERT(false); qCDebug(scriptengine_v8) << "(This should not happen) Creating new method object for " << metaObject->className() << " " << name.toQString(); return ScriptMethodV8Proxy::newMethod(_engine, qobject, object, methodDef.methods, methodDef.numMaxParams); } case SIGNAL_TYPE: { int signalId = id & ~TYPE_MASK; SignalDefMap::const_iterator defLookup = _signals.find(signalId); if (defLookup == _signals.cend()) return V8ScriptValue(_engine, v8::Null(isolate)); InstanceMap::const_iterator instLookup = _signalInstances.find(signalId); if (instLookup == _signalInstances.cend() || instLookup.value().isNull()) { instLookup = _signalInstances.insert(signalId, new ScriptSignalV8Proxy(_engine, qobject, object, defLookup.value().signal)); Q_ASSERT(instLookup != _signalInstances.cend()); } ScriptSignalV8Proxy* proxy = instLookup.value(); ScriptEngine::QObjectWrapOptions options = ScriptEngine::ExcludeSuperClassContents | //V8TODO ScriptEngine::ExcludeDeleteLater | ScriptEngine::PreferExistingWrapperObject; // It's not necessarily new, newQObject looks for it first in object wrapper map // V8TODO: won't ScriptEngine::ScriptOwnership cause trouble here? return ScriptObjectV8Proxy::newQObject(_engine, proxy, ScriptEngine::ScriptOwnership, options); } } return V8ScriptValue(_engine, v8::Null(isolate));} void ScriptObjectV8Proxy::setProperty(V8ScriptValue& object, const V8ScriptString& name, uint id, const V8ScriptValue& value) { auto isolate = _engine->getIsolate(); v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); v8::Context::Scope contextScope(_engine->getContext()); if (!(id & PROPERTY_TYPE)) return; QObject* qobject = _object; if (!qobject) { _engine->getIsolate()->ThrowError("Referencing deleted native object"); return; } int propId = id & ~TYPE_MASK; PropertyDefMap::const_iterator lookup = _props.find(propId); if (lookup == _props.cend()) return; const PropertyDef& propDef = lookup.value(); if (propDef.flags & ScriptValue::ReadOnly) return; const QMetaObject* metaObject = qobject->metaObject(); QMetaProperty prop = metaObject->property(propId); ScriptValue scriptThis = ScriptValue(new ScriptValueV8Wrapper(_engine, object)); ScriptPropertyContextV8Wrapper ourContext(scriptThis, _engine->currentContext()); ScriptContextGuard guard(&ourContext); int propTypeId = prop.userType(); Q_ASSERT(propTypeId != QMetaType::UnknownType); QVariant varValue; if(!_engine->castValueToVariant(value, varValue, propTypeId)) { QByteArray propTypeName = QMetaType(propTypeId).name(); QByteArray valTypeName = _engine->valueType(value).toLatin1(); isolate->ThrowError(v8::String::NewFromUtf8(isolate, QString("Cannot convert %1 to %2").arg(valTypeName, propTypeName).toStdString().c_str()).ToLocalChecked()); return; } prop.write(qobject, varValue);} ScriptVariantV8Proxy::ScriptVariantV8Proxy(ScriptEngineV8* engine, const QVariant& variant, V8ScriptValue scriptProto, ScriptObjectV8Proxy* proto) : _engine(engine), _variant(variant), _scriptProto(scriptProto), _proto(proto) { auto isolate = engine->getIsolate(); v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); auto context = engine->getContext(); v8::Context::Scope contextScope(context); auto variantDataTemplate = _engine->getVariantDataTemplate(); auto variantData = variantDataTemplate->NewInstance(context).ToLocalChecked(); variantData->SetAlignedPointerInInternalField(0, const_cast<void*>(internalPointsToQVariantInProxy)); // Internal field doesn't point directly to QVariant, because then alignment would need to be guaranteed in all compilers variantData->SetAlignedPointerInInternalField(1, reinterpret_cast<void*>(this)); _v8Object.Reset(isolate, v8::Local<v8::Object>::Cast(variantData)); _name = QString::fromLatin1(variant.typeName());} ScriptVariantV8Proxy::~ScriptVariantV8Proxy() { auto isolate = _engine->getIsolate(); v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); // V8TODO: Add similar deletion handling as for object proxy _v8Object.Reset();} V8ScriptValue ScriptVariantV8Proxy::newVariant(ScriptEngineV8* engine, const QVariant& variant, V8ScriptValue proto) { qDebug() << "ScriptVariantV8Proxy::newVariant"; auto isolate = engine->getIsolate(); v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); auto context = engine->getContext(); v8::Context::Scope contextScope(context); ScriptObjectV8Proxy* protoProxy = ScriptObjectV8Proxy::unwrapProxy(proto); if (!protoProxy) { Q_ASSERT(protoProxy); //return engine->newVariant(variant); return V8ScriptValue(engine, v8::Undefined(isolate)); } // V8TODO probably needs connection to be deleted auto proxy = new ScriptVariantV8Proxy(engine, variant, proto, protoProxy); auto variantProxyTemplate = engine->getVariantProxyTemplate(); auto variantProxy = variantProxyTemplate->NewInstance(context).ToLocalChecked(); variantProxy->SetAlignedPointerInInternalField(0, const_cast<void*>(internalPointsToQVariantProxy)); variantProxy->SetAlignedPointerInInternalField(1, reinterpret_cast<void*>(proxy)); return V8ScriptValue(engine, variantProxy);} ScriptVariantV8Proxy* ScriptVariantV8Proxy::unwrapProxy(const V8ScriptValue& val) { auto isolate = val.getEngine()->getIsolate(); v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); v8::Context::Scope contextScope(val.getEngine()->getContext()); auto v8Value = val.constGet(); if (!v8Value->IsObject()) { return nullptr; } v8::Local<v8::Object> v8Object = v8::Local<v8::Object>::Cast(v8Value); if (v8Object->InternalFieldCount() != 2) { return nullptr; } if (v8Object->GetAlignedPointerFromInternalField(0) != internalPointsToQVariantProxy) { return nullptr; } return reinterpret_cast<ScriptVariantV8Proxy*>(v8Object->GetAlignedPointerFromInternalField(1));} ScriptVariantV8Proxy* ScriptVariantV8Proxy::unwrapProxy(v8::Isolate* isolate, v8::Local<v8::Value> &value) { v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); if (!value->IsObject()) { return nullptr; } v8::Local<v8::Object> v8Object = v8::Local<v8::Object>::Cast(value); if (v8Object->InternalFieldCount() != 2) { return nullptr; } if (v8Object->GetAlignedPointerFromInternalField(0) != internalPointsToQVariantProxy) { return nullptr; } return reinterpret_cast<ScriptVariantV8Proxy*>(v8Object->GetAlignedPointerFromInternalField(1));} QVariant* ScriptVariantV8Proxy::unwrapQVariantPointer(v8::Isolate* isolate, const v8::Local<v8::Value> &value) { v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); if (!value->IsObject()) { return nullptr; } v8::Local<v8::Object> v8Object = v8::Local<v8::Object>::Cast(value); if (v8Object->InternalFieldCount() != 2) { return nullptr; } if (v8Object->GetAlignedPointerFromInternalField(0) != internalPointsToQVariantInProxy) { return nullptr; } auto proxy = reinterpret_cast<ScriptVariantV8Proxy*>(v8Object->GetAlignedPointerFromInternalField(1)); return &(proxy->_variant);} void ScriptVariantV8Proxy::v8Get(v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::HandleScope handleScope(info.GetIsolate()); v8::String::Utf8Value utf8Name(info.GetIsolate(), name); v8::Local<v8::Value> objectV8 = info.This(); ScriptVariantV8Proxy *proxy = ScriptVariantV8Proxy::unwrapProxy(info.GetIsolate(), objectV8); if (!proxy) { qCDebug(scriptengine_v8) << "Proxy object not found when getting: " << *utf8Name; return; } V8ScriptValue object(proxy->_engine, proxy->_v8Object.Get(info.GetIsolate())); ContextScopeV8 contextScopeV8(proxy->_engine); if (name->IsString()) { V8ScriptString nameString(proxy->_engine, v8::Local<v8::String>::Cast(name)); uint id; ScriptObjectV8Proxy::QueryFlags flags = proxy->_proto->queryProperty(object, nameString, ScriptObjectV8Proxy::HandlesReadAccess, &id); if (flags) { V8ScriptValue value = proxy->property(object, nameString, id); info.GetReturnValue().Set(value.get()); return; } } qCDebug(scriptengine_v8) << "Value not found: " << *utf8Name;} void ScriptVariantV8Proxy::v8Set(v8::Local<v8::Name> name, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::HandleScope handleScope(info.GetIsolate()); v8::String::Utf8Value utf8Name(info.GetIsolate(), name); v8::Local<v8::Value> objectV8 = info.This(); ScriptVariantV8Proxy *proxy = ScriptVariantV8Proxy::unwrapProxy(info.GetIsolate(), objectV8); if (!proxy) { qCDebug(scriptengine_v8) << "Proxy object not found when getting: " << *utf8Name; return; } V8ScriptValue object(proxy->_engine, objectV8); if (!name->IsString() && !name->IsSymbol()) { QString notStringMessage("ScriptObjectV8Proxy::v8Set: " + proxy->_engine->scriptValueDebugDetailsV8(V8ScriptValue(proxy->_engine, name))); qCDebug(scriptengine_v8) << notStringMessage; Q_ASSERT(false); } ContextScopeV8 contextScopeV8(proxy->_engine); if (name->IsString()) { V8ScriptString nameString(proxy->_engine, v8::Local<v8::String>::Cast(name)); uint id; ScriptObjectV8Proxy::QueryFlags flags = proxy->_proto->queryProperty(object, nameString, ScriptObjectV8Proxy::HandlesWriteAccess, &id); if (flags) { proxy->setProperty(object, nameString, id, V8ScriptValue(proxy->_engine, value)); info.GetReturnValue().Set(value); return; } } qCDebug(scriptengine_v8) << "Set failed: " << *utf8Name;} void ScriptVariantV8Proxy::v8GetPropertyNames(const v8::PropertyCallbackInfo<v8::Array>& info) { //V8TODO: Only methods from the prototype should be listed. v8::HandleScope handleScope(info.GetIsolate()); auto context = info.GetIsolate()->GetCurrentContext(); v8::Context::Scope contextScope(context); v8::Local<v8::Value> objectV8 = info.This(); ScriptVariantV8Proxy *proxy = ScriptVariantV8Proxy::unwrapProxy(info.GetIsolate(), objectV8); if (!proxy) { qCDebug(scriptengine_v8) << "ScriptObjectV8Proxy::v8GetPropertyNames: Proxy object not found when listing"; return; } V8ScriptValue object(proxy->_engine, objectV8); v8::Local<v8::Array> properties = proxy->_proto->getPropertyNames(); v8::Local<v8::Array> objectProperties; info.GetReturnValue().Set(properties);} QVariant ScriptVariantV8Proxy::unwrap(const V8ScriptValue& val) { ScriptVariantV8Proxy* proxy = unwrapProxy(val); return proxy ? proxy->toQVariant() : QVariant(); // V8TODO} ScriptMethodV8Proxy::ScriptMethodV8Proxy(ScriptEngineV8* engine, QObject* object, V8ScriptValue lifetime, const QList<QMetaMethod>& metas, int numMaxParams) : _numMaxParams(numMaxParams), _engine(engine), _object(object), /*_objectLifetime(lifetime),*/ _metas(metas) { auto isolate = engine->getIsolate(); v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); v8::Context::Scope contextScope(engine->getContext()); _objectLifetime.Reset(isolate, lifetime.get()); _objectLifetime.SetWeak(this, weakHandleCallback, v8::WeakCallbackType::kParameter);} ScriptMethodV8Proxy::~ScriptMethodV8Proxy() { auto isolate = _engine->getIsolate(); v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); _objectLifetime.Reset();} void ScriptMethodV8Proxy::weakHandleCallback(const v8::WeakCallbackInfo<ScriptMethodV8Proxy>& info) { auto proxy = info.GetParameter(); proxy->_objectLifetime.Reset(); info.GetParameter()->deleteLater();} V8ScriptValue ScriptMethodV8Proxy::newMethod(ScriptEngineV8* engine, QObject* object, V8ScriptValue lifetime, const QList<QMetaMethod>& metas, int numMaxParams) { auto isolate = engine->getIsolate(); v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); auto context = engine->getContext(); v8::Context::Scope contextScope(context); auto methodDataTemplate = engine->getMethodDataTemplate(); auto methodData = methodDataTemplate->NewInstance(context).ToLocalChecked(); methodData->SetAlignedPointerInInternalField(0, const_cast<void*>(internalPointsToMethodProxy)); methodData->SetAlignedPointerInInternalField(1, reinterpret_cast<void*>(new ScriptMethodV8Proxy(engine, object, lifetime, metas, numMaxParams))); auto v8Function = v8::Function::New(context, callback, methodData, numMaxParams).ToLocalChecked(); return V8ScriptValue(engine, v8Function);} QString ScriptMethodV8Proxy::fullName() const { Q_ASSERT(_object); if (!_object) return ""; Q_ASSERT(!_metas.isEmpty()); const QMetaMethod& firstMethod = _metas.front(); QString objectName = _object->objectName(); if (!objectName.isEmpty()) { return QString("%1.%2").arg(objectName, firstMethod.name()); } return QString("%1::%2").arg(_object->metaObject()->className(), firstMethod.name());} void ScriptMethodV8Proxy::callback(const v8::FunctionCallbackInfo<v8::Value>& arguments) { v8::Locker locker(arguments.GetIsolate()); v8::Isolate::Scope isolateScope(arguments.GetIsolate()); v8::HandleScope handleScope(arguments.GetIsolate()); v8::Context::Scope contextScope(arguments.GetIsolate()->GetCurrentContext()); if (!arguments.Data()->IsObject()) { arguments.GetIsolate()->ThrowError("Method value is not an object"); return; } v8::Local<v8::Object> data = v8::Local<v8::Object>::Cast(arguments.Data()); if (data->InternalFieldCount() != 2) { arguments.GetIsolate()->ThrowError("Incorrect number of internal fields during method call"); return; } if (data->GetAlignedPointerFromInternalField(0) != internalPointsToMethodProxy) { arguments.GetIsolate()->ThrowError("Internal field 0 of ScriptMethodV8Proxy V8 object has wrong value"); return; } ScriptMethodV8Proxy *proxy = reinterpret_cast<ScriptMethodV8Proxy*>(data->GetAlignedPointerFromInternalField(1)); ContextScopeV8 contextScopeV8(proxy->_engine); proxy->call(arguments);} Lines 501–1000 of 1613. Previous lines Load more
if (!name->IsString() && !name->IsSymbol()) { QString notStringMessage("ScriptObjectV8Proxy::v8Get: " + proxy->_engine->scriptValueDebugDetailsV8(V8ScriptValue(proxy->_engine, name))); qCDebug(scriptengine_v8) << notStringMessage; Q_ASSERT(false); } v8::Local<v8::String> v8NameString; ContextScopeV8 contextScopeV8(proxy->_engine); if (name->IsString()) { V8ScriptString nameString(proxy->_engine, v8::Local<v8::String>::Cast(name)); uint id; QueryFlags flags = proxy->queryProperty(object, nameString, HandlesReadAccess, &id); if (flags) { V8ScriptValue value = proxy->property(object, nameString, id); info.GetReturnValue().Set(value.get()); return; } } v8::Local<v8::Value> property; if(info.This()->GetInternalField(2).As<v8::Object>()->Get(proxy->_engine->getContext(), name).ToLocal(&property)) { info.GetReturnValue().Set(property); } else { qCDebug(scriptengine_v8) << "Value not found: " << *utf8Value; } } void ScriptObjectV8Proxy::v8Set(v8::Local<v8::Name> name, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::HandleScope handleScope(info.GetIsolate()); v8::String::Utf8Value utf8Value(info.GetIsolate(), name); v8::Local<v8::Value> objectV8 = info.This(); ScriptObjectV8Proxy *proxy = ScriptObjectV8Proxy::unwrapProxy(info.GetIsolate(), objectV8); if (!proxy) { qCDebug(scriptengine_v8) << "Proxy object not found when setting: " << *utf8Value; return; } V8ScriptValue object(proxy->_engine, objectV8); if (!name->IsString() && !name->IsSymbol()) { QString notStringMessage("ScriptObjectV8Proxy::v8Set: " + proxy->_engine->scriptValueDebugDetailsV8(V8ScriptValue(proxy->_engine, name))); qCDebug(scriptengine_v8) << notStringMessage; Q_ASSERT(false); } ContextScopeV8 contextScopeV8(proxy->_engine); if (name->IsString()) { V8ScriptString nameString(proxy->_engine, v8::Local<v8::String>::Cast(name)); uint id; QueryFlags flags = proxy->queryProperty(object, nameString, HandlesWriteAccess, &id); if (flags) { proxy->setProperty(object, nameString, id, V8ScriptValue(proxy->_engine, value)); info.GetReturnValue().Set(value); return; } } if (info.This()->GetInternalField(2).As<v8::Object>()->Set(proxy->_engine->getContext(), name, value).FromMaybe(false)) { info.GetReturnValue().Set(value); } else { qCDebug(scriptengine_v8) << "Set failed: " << *utf8Value; } } void ScriptObjectV8Proxy::v8GetPropertyNames(const v8::PropertyCallbackInfo<v8::Array>& info) { v8::HandleScope handleScope(info.GetIsolate()); auto context = info.GetIsolate()->GetCurrentContext(); v8::Context::Scope contextScope(context); v8::Local<v8::Value> objectV8 = info.This(); ScriptObjectV8Proxy *proxy = ScriptObjectV8Proxy::unwrapProxy(info.GetIsolate(), objectV8); if (!proxy) { qCDebug(scriptengine_v8) << "ScriptObjectV8Proxy::v8GetPropertyNames: Proxy object not found when listing"; return; } V8ScriptValue object(proxy->_engine, objectV8); v8::Local<v8::Array> properties = proxy->getPropertyNames(); v8::Local<v8::Array> objectProperties; uint32_t propertiesLength = properties->Length(); if (info.This()->GetInternalField(2).As<v8::Object>()->GetPropertyNames(context).ToLocal(&objectProperties)) { for (uint32_t n = 0; n < objectProperties->Length(); n++) { if(!properties->Set(context, propertiesLength+n, objectProperties->Get(context, n).ToLocalChecked()).FromMaybe(false)) { qCDebug(scriptengine_v8) << "ScriptObjectV8Proxy::v8GetPropertyNames: Cannot add member name"; } } } info.GetReturnValue().Set(properties); } v8::Local<v8::Array> ScriptObjectV8Proxy::getPropertyNames() { auto isolate = _engine->getIsolate(); v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::EscapableHandleScope handleScope(_engine->getIsolate()); auto context = _engine->getContext(); v8::Context::Scope contextScope(context); //V8TODO: this is really slow. It could be cached if this is called often. v8::Local<v8::Array> properties = v8::Array::New(isolate, _props.size() + _methods.size() + _signals.size()); uint32_t position = 0; for (PropertyDefMap::const_iterator i = _props.begin(); i != _props.end(); i++){ v8::Local<v8::String> name = v8::String::NewFromUtf8(isolate, i.value().name.toStdString().c_str()).ToLocalChecked(); if(!properties->Set(context, position++, name).FromMaybe(false)) { qCDebug(scriptengine_v8) << "ScriptObjectV8Proxy::getPropertyNames: Cannot add property member name"; } } for (MethodDefMap::const_iterator i = _methods.begin(); i != _methods.end(); i++){ v8::Local<v8::String> name = v8::String::NewFromUtf8(isolate, i.value().name.toStdString().c_str()).ToLocalChecked(); if(!properties->Set(context, position++, name).FromMaybe(false)) { qCDebug(scriptengine_v8) << "ScriptObjectV8Proxy::getPropertyNames: Cannot add property member name"; } } for (SignalDefMap::const_iterator i = _signals.begin(); i != _signals.end(); i++){ v8::Local<v8::String> name = v8::String::NewFromUtf8(isolate, i.value().name.toStdString().c_str()).ToLocalChecked(); if(!properties->Set(context, position++, name).FromMaybe(false)) { qCDebug(scriptengine_v8) << "ScriptObjectV8Proxy::getPropertyNames: Cannot add property member name"; } } return handleScope.Escape(properties); } V8ScriptValue ScriptObjectV8Proxy::property(const V8ScriptValue& object, const V8ScriptString& name, uint id) { auto isolate = _engine->getIsolate(); v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); auto context = _engine->getContext(); v8::Context::Scope contextScope(context); QObject* qobject = _object; if (!qobject) { _engine->getIsolate()->ThrowError("Referencing deleted native object"); return V8ScriptValue(_engine, v8::Null(isolate)); } const QMetaObject* metaObject = qobject->metaObject(); switch (id & TYPE_MASK) { case PROPERTY_TYPE: { int propId = id & ~TYPE_MASK; PropertyDefMap::const_iterator lookup = _props.find(propId); if (lookup == _props.cend()) return V8ScriptValue(_engine, v8::Null(isolate)); QMetaProperty prop = metaObject->property(propId); ScriptValue scriptThis = ScriptValue(new ScriptValueV8Wrapper(_engine, object)); ScriptPropertyContextV8Wrapper ourContext(scriptThis, _engine->currentContext()); ScriptContextGuard guard(&ourContext); QVariant varValue = prop.read(qobject); return _engine->castVariantToValue(varValue); } case METHOD_TYPE: { int methodId = id & ~TYPE_MASK; MethodDefMap::const_iterator lookup = _methods.find(methodId); if (lookup == _methods.cend()) return V8ScriptValue(_engine, v8::Null(isolate)); const MethodDef& methodDef = lookup.value(); for (auto iter = methodDef.methods.begin(); iter != methodDef.methods.end(); iter++ ) { if((*iter).returnType() == QMetaType::UnknownType) { qCDebug(scriptengine_v8) << "Method with QMetaType::UnknownType " << metaObject->className() << " " << (*iter).name(); } } //V8TODO: is new method created during every call? It needs to be cached instead v8::Local<v8::Value> property; if(_v8Object.Get(isolate)->GetInternalField(2).As<v8::Object>()->Get(context, name.constGet()).ToLocal(&property)) { if (!property->IsUndefined()) { return V8ScriptValue(_engine, property); } } Q_ASSERT(false); qCDebug(scriptengine_v8) << "(This should not happen) Creating new method object for " << metaObject->className() << " " << name.toQString(); return ScriptMethodV8Proxy::newMethod(_engine, qobject, object, methodDef.methods, methodDef.numMaxParams); } case SIGNAL_TYPE: { int signalId = id & ~TYPE_MASK; SignalDefMap::const_iterator defLookup = _signals.find(signalId); if (defLookup == _signals.cend()) return V8ScriptValue(_engine, v8::Null(isolate)); InstanceMap::const_iterator instLookup = _signalInstances.find(signalId); if (instLookup == _signalInstances.cend() || instLookup.value().isNull()) { instLookup = _signalInstances.insert(signalId, new ScriptSignalV8Proxy(_engine, qobject, object, defLookup.value().signal)); Q_ASSERT(instLookup != _signalInstances.cend()); } ScriptSignalV8Proxy* proxy = instLookup.value(); ScriptEngine::QObjectWrapOptions options = ScriptEngine::ExcludeSuperClassContents | //V8TODO ScriptEngine::ExcludeDeleteLater | ScriptEngine::PreferExistingWrapperObject; // It's not necessarily new, newQObject looks for it first in object wrapper map // V8TODO: won't ScriptEngine::ScriptOwnership cause trouble here? return ScriptObjectV8Proxy::newQObject(_engine, proxy, ScriptEngine::ScriptOwnership, options); } } return V8ScriptValue(_engine, v8::Null(isolate)); } void ScriptObjectV8Proxy::setProperty(V8ScriptValue& object, const V8ScriptString& name, uint id, const V8ScriptValue& value) { auto isolate = _engine->getIsolate(); v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); v8::Context::Scope contextScope(_engine->getContext()); if (!(id & PROPERTY_TYPE)) return; QObject* qobject = _object; if (!qobject) { _engine->getIsolate()->ThrowError("Referencing deleted native object"); return; } int propId = id & ~TYPE_MASK; PropertyDefMap::const_iterator lookup = _props.find(propId); if (lookup == _props.cend()) return; const PropertyDef& propDef = lookup.value(); if (propDef.flags & ScriptValue::ReadOnly) return; const QMetaObject* metaObject = qobject->metaObject(); QMetaProperty prop = metaObject->property(propId); ScriptValue scriptThis = ScriptValue(new ScriptValueV8Wrapper(_engine, object)); ScriptPropertyContextV8Wrapper ourContext(scriptThis, _engine->currentContext()); ScriptContextGuard guard(&ourContext); int propTypeId = prop.userType(); Q_ASSERT(propTypeId != QMetaType::UnknownType); QVariant varValue; if(!_engine->castValueToVariant(value, varValue, propTypeId)) { QByteArray propTypeName = QMetaType(propTypeId).name(); QByteArray valTypeName = _engine->valueType(value).toLatin1(); isolate->ThrowError(v8::String::NewFromUtf8(isolate, QString("Cannot convert %1 to %2").arg(valTypeName, propTypeName).toStdString().c_str()).ToLocalChecked()); return; } prop.write(qobject, varValue); } ScriptVariantV8Proxy::ScriptVariantV8Proxy(ScriptEngineV8* engine, const QVariant& variant, V8ScriptValue scriptProto, ScriptObjectV8Proxy* proto) : _engine(engine), _variant(variant), _scriptProto(scriptProto), _proto(proto) { auto isolate = engine->getIsolate(); v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); auto context = engine->getContext(); v8::Context::Scope contextScope(context); auto variantDataTemplate = _engine->getVariantDataTemplate(); auto variantData = variantDataTemplate->NewInstance(context).ToLocalChecked(); variantData->SetAlignedPointerInInternalField(0, const_cast<void*>(internalPointsToQVariantInProxy)); // Internal field doesn't point directly to QVariant, because then alignment would need to be guaranteed in all compilers variantData->SetAlignedPointerInInternalField(1, reinterpret_cast<void*>(this)); _v8Object.Reset(isolate, v8::Local<v8::Object>::Cast(variantData)); _name = QString::fromLatin1(variant.typeName()); } ScriptVariantV8Proxy::~ScriptVariantV8Proxy() { auto isolate = _engine->getIsolate(); v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); // V8TODO: Add similar deletion handling as for object proxy _v8Object.Reset(); } V8ScriptValue ScriptVariantV8Proxy::newVariant(ScriptEngineV8* engine, const QVariant& variant, V8ScriptValue proto) { qDebug() << "ScriptVariantV8Proxy::newVariant"; auto isolate = engine->getIsolate(); v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); auto context = engine->getContext(); v8::Context::Scope contextScope(context); ScriptObjectV8Proxy* protoProxy = ScriptObjectV8Proxy::unwrapProxy(proto); if (!protoProxy) { Q_ASSERT(protoProxy); //return engine->newVariant(variant); return V8ScriptValue(engine, v8::Undefined(isolate)); } // V8TODO probably needs connection to be deleted auto proxy = new ScriptVariantV8Proxy(engine, variant, proto, protoProxy); auto variantProxyTemplate = engine->getVariantProxyTemplate(); auto variantProxy = variantProxyTemplate->NewInstance(context).ToLocalChecked(); variantProxy->SetAlignedPointerInInternalField(0, const_cast<void*>(internalPointsToQVariantProxy)); variantProxy->SetAlignedPointerInInternalField(1, reinterpret_cast<void*>(proxy)); return V8ScriptValue(engine, variantProxy); } ScriptVariantV8Proxy* ScriptVariantV8Proxy::unwrapProxy(const V8ScriptValue& val) { auto isolate = val.getEngine()->getIsolate(); v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); v8::Context::Scope contextScope(val.getEngine()->getContext()); auto v8Value = val.constGet(); if (!v8Value->IsObject()) { return nullptr; } v8::Local<v8::Object> v8Object = v8::Local<v8::Object>::Cast(v8Value); if (v8Object->InternalFieldCount() != 2) { return nullptr; } if (v8Object->GetAlignedPointerFromInternalField(0) != internalPointsToQVariantProxy) { return nullptr; } return reinterpret_cast<ScriptVariantV8Proxy*>(v8Object->GetAlignedPointerFromInternalField(1)); } ScriptVariantV8Proxy* ScriptVariantV8Proxy::unwrapProxy(v8::Isolate* isolate, v8::Local<v8::Value> &value) { v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); if (!value->IsObject()) { return nullptr; } v8::Local<v8::Object> v8Object = v8::Local<v8::Object>::Cast(value); if (v8Object->InternalFieldCount() != 2) { return nullptr; } if (v8Object->GetAlignedPointerFromInternalField(0) != internalPointsToQVariantProxy) { return nullptr; } return reinterpret_cast<ScriptVariantV8Proxy*>(v8Object->GetAlignedPointerFromInternalField(1)); } QVariant* ScriptVariantV8Proxy::unwrapQVariantPointer(v8::Isolate* isolate, const v8::Local<v8::Value> &value) { v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); if (!value->IsObject()) { return nullptr; } v8::Local<v8::Object> v8Object = v8::Local<v8::Object>::Cast(value); if (v8Object->InternalFieldCount() != 2) { return nullptr; } if (v8Object->GetAlignedPointerFromInternalField(0) != internalPointsToQVariantInProxy) { return nullptr; } auto proxy = reinterpret_cast<ScriptVariantV8Proxy*>(v8Object->GetAlignedPointerFromInternalField(1)); return &(proxy->_variant); } void ScriptVariantV8Proxy::v8Get(v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::HandleScope handleScope(info.GetIsolate()); v8::String::Utf8Value utf8Name(info.GetIsolate(), name); v8::Local<v8::Value> objectV8 = info.This(); ScriptVariantV8Proxy *proxy = ScriptVariantV8Proxy::unwrapProxy(info.GetIsolate(), objectV8); if (!proxy) { qCDebug(scriptengine_v8) << "Proxy object not found when getting: " << *utf8Name; return; } V8ScriptValue object(proxy->_engine, proxy->_v8Object.Get(info.GetIsolate())); ContextScopeV8 contextScopeV8(proxy->_engine); if (name->IsString()) { V8ScriptString nameString(proxy->_engine, v8::Local<v8::String>::Cast(name)); uint id; ScriptObjectV8Proxy::QueryFlags flags = proxy->_proto->queryProperty(object, nameString, ScriptObjectV8Proxy::HandlesReadAccess, &id); if (flags) { V8ScriptValue value = proxy->property(object, nameString, id); info.GetReturnValue().Set(value.get()); return; } } qCDebug(scriptengine_v8) << "Value not found: " << *utf8Name; } void ScriptVariantV8Proxy::v8Set(v8::Local<v8::Name> name, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::HandleScope handleScope(info.GetIsolate()); v8::String::Utf8Value utf8Name(info.GetIsolate(), name); v8::Local<v8::Value> objectV8 = info.This(); ScriptVariantV8Proxy *proxy = ScriptVariantV8Proxy::unwrapProxy(info.GetIsolate(), objectV8); if (!proxy) { qCDebug(scriptengine_v8) << "Proxy object not found when getting: " << *utf8Name; return; } V8ScriptValue object(proxy->_engine, objectV8); if (!name->IsString() && !name->IsSymbol()) { QString notStringMessage("ScriptObjectV8Proxy::v8Set: " + proxy->_engine->scriptValueDebugDetailsV8(V8ScriptValue(proxy->_engine, name))); qCDebug(scriptengine_v8) << notStringMessage; Q_ASSERT(false); } ContextScopeV8 contextScopeV8(proxy->_engine); if (name->IsString()) { V8ScriptString nameString(proxy->_engine, v8::Local<v8::String>::Cast(name)); uint id; ScriptObjectV8Proxy::QueryFlags flags = proxy->_proto->queryProperty(object, nameString, ScriptObjectV8Proxy::HandlesWriteAccess, &id); if (flags) { proxy->setProperty(object, nameString, id, V8ScriptValue(proxy->_engine, value)); info.GetReturnValue().Set(value); return; } } qCDebug(scriptengine_v8) << "Set failed: " << *utf8Name; } void ScriptVariantV8Proxy::v8GetPropertyNames(const v8::PropertyCallbackInfo<v8::Array>& info) { //V8TODO: Only methods from the prototype should be listed. v8::HandleScope handleScope(info.GetIsolate()); auto context = info.GetIsolate()->GetCurrentContext(); v8::Context::Scope contextScope(context); v8::Local<v8::Value> objectV8 = info.This(); ScriptVariantV8Proxy *proxy = ScriptVariantV8Proxy::unwrapProxy(info.GetIsolate(), objectV8); if (!proxy) { qCDebug(scriptengine_v8) << "ScriptObjectV8Proxy::v8GetPropertyNames: Proxy object not found when listing"; return; } V8ScriptValue object(proxy->_engine, objectV8); v8::Local<v8::Array> properties = proxy->_proto->getPropertyNames(); v8::Local<v8::Array> objectProperties; info.GetReturnValue().Set(properties); } QVariant ScriptVariantV8Proxy::unwrap(const V8ScriptValue& val) { ScriptVariantV8Proxy* proxy = unwrapProxy(val); return proxy ? proxy->toQVariant() : QVariant(); // V8TODO } ScriptMethodV8Proxy::ScriptMethodV8Proxy(ScriptEngineV8* engine, QObject* object, V8ScriptValue lifetime, const QList<QMetaMethod>& metas, int numMaxParams) : _numMaxParams(numMaxParams), _engine(engine), _object(object), /*_objectLifetime(lifetime),*/ _metas(metas) { auto isolate = engine->getIsolate(); v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); v8::Context::Scope contextScope(engine->getContext()); _objectLifetime.Reset(isolate, lifetime.get()); _objectLifetime.SetWeak(this, weakHandleCallback, v8::WeakCallbackType::kParameter); } ScriptMethodV8Proxy::~ScriptMethodV8Proxy() { auto isolate = _engine->getIsolate(); v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); _objectLifetime.Reset(); } void ScriptMethodV8Proxy::weakHandleCallback(const v8::WeakCallbackInfo<ScriptMethodV8Proxy>& info) { auto proxy = info.GetParameter(); proxy->_objectLifetime.Reset(); info.GetParameter()->deleteLater(); } V8ScriptValue ScriptMethodV8Proxy::newMethod(ScriptEngineV8* engine, QObject* object, V8ScriptValue lifetime, const QList<QMetaMethod>& metas, int numMaxParams) { auto isolate = engine->getIsolate(); v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); auto context = engine->getContext(); v8::Context::Scope contextScope(context); auto methodDataTemplate = engine->getMethodDataTemplate(); auto methodData = methodDataTemplate->NewInstance(context).ToLocalChecked(); methodData->SetAlignedPointerInInternalField(0, const_cast<void*>(internalPointsToMethodProxy)); methodData->SetAlignedPointerInInternalField(1, reinterpret_cast<void*>(new ScriptMethodV8Proxy(engine, object, lifetime, metas, numMaxParams))); auto v8Function = v8::Function::New(context, callback, methodData, numMaxParams).ToLocalChecked(); return V8ScriptValue(engine, v8Function); } QString ScriptMethodV8Proxy::fullName() const { Q_ASSERT(_object); if (!_object) return ""; Q_ASSERT(!_metas.isEmpty()); const QMetaMethod& firstMethod = _metas.front(); QString objectName = _object->objectName(); if (!objectName.isEmpty()) { return QString("%1.%2").arg(objectName, firstMethod.name()); } return QString("%1::%2").arg(_object->metaObject()->className(), firstMethod.name()); } void ScriptMethodV8Proxy::callback(const v8::FunctionCallbackInfo<v8::Value>& arguments) { v8::Locker locker(arguments.GetIsolate()); v8::Isolate::Scope isolateScope(arguments.GetIsolate()); v8::HandleScope handleScope(arguments.GetIsolate()); v8::Context::Scope contextScope(arguments.GetIsolate()->GetCurrentContext()); if (!arguments.Data()->IsObject()) { arguments.GetIsolate()->ThrowError("Method value is not an object"); return; } v8::Local<v8::Object> data = v8::Local<v8::Object>::Cast(arguments.Data()); if (data->InternalFieldCount() != 2) { arguments.GetIsolate()->ThrowError("Incorrect number of internal fields during method call"); return; } if (data->GetAlignedPointerFromInternalField(0) != internalPointsToMethodProxy) { arguments.GetIsolate()->ThrowError("Internal field 0 of ScriptMethodV8Proxy V8 object has wrong value"); return; } ScriptMethodV8Proxy *proxy = reinterpret_cast<ScriptMethodV8Proxy*>(data->GetAlignedPointerFromInternalField(1)); ContextScopeV8 contextScopeV8(proxy->_engine); proxy->call(arguments); }