//// ScriptEngineV8_cast.cpp// libraries/script-engine/src/v8//// Created by Heather Anderson 12/9/2021// Modified for V8 by dr Karol Suprynowicz on 2022/10/08// Copyright 2021 Vircadia contributors.// Copyright 2022-2023 Overte e.V.//// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// SPDX-License-Identifier: Apache-2.0// #include "ScriptEngineV8.h" #include <QtCore/QJsonArray>#include <QtCore/QJsonObject>#include <QtCore/QJsonValue>#include "libplatform/libplatform.h"#include "v8.h" #include "../ScriptEngineCast.h"#include "../ScriptValueIterator.h" #include "ScriptObjectV8Proxy.h"#include "ScriptValueV8Wrapper.h"#include "ScriptEngineLoggingV8.h" void ScriptEngineV8::setDefaultPrototype(int metaTypeId, const ScriptValue& prototype) { ScriptValueV8Wrapper* unwrappedPrototype = ScriptValueV8Wrapper::unwrap(prototype); if (unwrappedPrototype) { const V8ScriptValue& scriptPrototype = unwrappedPrototype->toV8Value(); _customTypeProtect.lockForWrite(); _customPrototypes.insert(metaTypeId, scriptPrototype); _customTypeProtect.unlock(); }} void ScriptEngineV8::registerCustomType(int type, ScriptEngine::MarshalFunction marshalFunc, ScriptEngine::DemarshalFunction demarshalFunc){ _customTypeProtect.lockForWrite(); // storing it in a map for our own benefit CustomMarshal& customType = _customTypes.insert(type, CustomMarshal()).value(); customType.demarshalFunc = demarshalFunc; customType.marshalFunc = marshalFunc; _customTypeProtect.unlock();} Q_DECLARE_METATYPE(ScriptValue);Q_DECLARE_METATYPE(QVariantMap); static ScriptValue StringListToScriptValue(ScriptEngine* engine, const QStringList& src) { int len = src.length(); ScriptValue dest = engine->newArray(len); for (int idx = 0; idx < len; ++idx) { dest.setProperty(idx, engine->newValue(src.at(idx))); } return dest;} static bool StringListFromScriptValue(const ScriptValue& src, QStringList& dest) { if(!src.isArray()) return false; int len = src.property("length").toInteger(); dest.clear(); for (int idx = 0; idx < len; ++idx) { dest.append(src.property(idx).toString()); } return true;} static ScriptValue VariantListToScriptValue(ScriptEngine* engine, const QVariantList& src) { int len = src.length(); ScriptValue dest = engine->newArray(len); for (int idx = 0; idx < len; ++idx) { dest.setProperty(idx, engine->newVariant(src.at(idx))); } return dest;} static bool VariantListFromScriptValue(const ScriptValue& src, QVariantList& dest) { if(!src.isArray()) return false; int len = src.property("length").toInteger(); dest.clear(); for (int idx = 0; idx < len; ++idx) { dest.append(src.property(idx).toVariant()); } return true;} static ScriptValue VariantMapToScriptValue(ScriptEngine* engine, const QVariantMap& src) { ScriptValue dest = engine->newObject(); for (QVariantMap::const_iterator iter = src.cbegin(); iter != src.cend(); ++iter) { dest.setProperty(iter.key(), engine->newVariant(iter.value())); } return dest;} static bool VariantMapFromScriptValue(const ScriptValue& src, QVariantMap& dest) { dest.clear(); ScriptValueIteratorPointer iter = src.newIterator(); while (iter->hasNext()) { iter->next(); dest.insert(iter->name(), iter->value().toVariant()); } return true;} static ScriptValue VariantHashToScriptValue(ScriptEngine* engine, const QVariantHash& src) { ScriptValue dest = engine->newObject(); for (QVariantHash::const_iterator iter = src.cbegin(); iter != src.cend(); ++iter) { dest.setProperty(iter.key(), engine->newVariant(iter.value())); } return dest;} static bool VariantHashFromScriptValue(const ScriptValue& src, QVariantHash& dest) { dest.clear(); ScriptValueIteratorPointer iter = src.newIterator(); while (iter->hasNext()) { iter->next(); dest.insert(iter->name(), iter->value().toVariant()); } return true;} static ScriptValue JsonValueToScriptValue(ScriptEngine* engine, const QJsonValue& src) { return engine->newVariant(src.toVariant());} static bool JsonValueFromScriptValue(const ScriptValue& src, QJsonValue& dest) { dest = QJsonValue::fromVariant(src.toVariant()); return true;} static ScriptValue JsonObjectToScriptValue(ScriptEngine* engine, const QJsonObject& src) { QVariantMap map = src.toVariantMap(); ScriptValue dest = engine->newObject(); for (QVariantMap::const_iterator iter = map.cbegin(); iter != map.cend(); ++iter) { dest.setProperty(iter.key(), engine->newVariant(iter.value())); } return dest;} static bool JsonObjectFromScriptValue(const ScriptValue& src, QJsonObject& dest) { QVariantMap map; ScriptValueIteratorPointer iter = src.newIterator(); while (iter->hasNext()) { iter->next(); map.insert(iter->name(), iter->value().toVariant()); } dest = QJsonObject::fromVariantMap(map); return true;} static ScriptValue JsonArrayToScriptValue(ScriptEngine* engine, const QJsonArray& src) { QVariantList list = src.toVariantList(); int len = list.length(); ScriptValue dest = engine->newArray(len); for (int idx = 0; idx < len; ++idx) { dest.setProperty(idx, engine->newVariant(list.at(idx))); } return dest;} static bool JsonArrayFromScriptValue(const ScriptValue& src, QJsonArray& dest) { if(!src.isArray()) return false; QVariantList list; int len = src.property("length").toInteger(); for (int idx = 0; idx < len; ++idx) { list.append(src.property(idx).toVariant()); } dest = QJsonArray::fromVariantList(list); return true;} // QMetaType::QJsonArray void ScriptEngineV8::registerSystemTypes() { scriptRegisterMetaType<QStringList, StringListToScriptValue, StringListFromScriptValue>(static_cast<ScriptEngine*>(this)); scriptRegisterMetaType<QVariantList, VariantListToScriptValue, VariantListFromScriptValue>(this); scriptRegisterMetaType<QVariantMap, VariantMapToScriptValue, VariantMapFromScriptValue>(this); scriptRegisterMetaType<QVariantHash, VariantHashToScriptValue, VariantHashFromScriptValue>(this); scriptRegisterMetaType<QJsonValue, JsonValueToScriptValue, JsonValueFromScriptValue>(this); scriptRegisterMetaType<QJsonObject, JsonObjectToScriptValue, JsonObjectFromScriptValue>(this); scriptRegisterMetaType<QJsonArray, JsonArrayToScriptValue, JsonArrayFromScriptValue>(this);} int ScriptEngineV8::computeCastPenalty(const V8ScriptValue& v8Val, int destTypeId) { Q_ASSERT(_v8Isolate->IsCurrent()); v8::HandleScope handleScope(_v8Isolate); v8::Local<v8::Context> context = getContext(); v8::Context::Scope contextScope(context); const v8::Local<v8::Value> val = v8Val.constGet(); if (val->IsNumber()) { switch (destTypeId){ case QMetaType::Bool: // Conversion to bool is acceptable, but numbers are preferred return 5; break; case QMetaType::UInt: case QMetaType::ULong: case QMetaType::Int: case QMetaType::Long: case QMetaType::Short: case QMetaType::Double: case QMetaType::Float: case QMetaType::ULongLong: case QMetaType::LongLong: case QMetaType::UShort: // Perfect case. JS doesn't have separate integer and floating point type return 0; break; case QMetaType::QString: case QMetaType::QByteArray: case QMetaType::QDateTime: case QMetaType::QDate: // Conversion to string should be avoided, it's probably not what we want return 100; break; default: // Other, not predicted cases return 5; } } else if (val->IsString() || val->IsDate() || val->IsRegExp()) { switch (destTypeId){ case QMetaType::Bool: // Conversion from to bool should be avoided if possible, it's probably not what we want return 100; case QMetaType::UInt: case QMetaType::ULong: case QMetaType::Int: case QMetaType::Long: case QMetaType::Short: case QMetaType::Double: case QMetaType::Float: case QMetaType::ULongLong: case QMetaType::LongLong: case QMetaType::UShort: // Conversion from to number should be avoided if possible, it's probably not what we want return 100; case QMetaType::QString: // Perfect case return 0; case QMetaType::QByteArray: case QMetaType::QDateTime: case QMetaType::QDate: // String to string should be slightly preferred return 5; default: return 5; } } else if (val->IsBoolean()) { switch (destTypeId){ case QMetaType::Bool: // Perfect case return 0; break; case QMetaType::UInt: case QMetaType::ULong: case QMetaType::Int: case QMetaType::Long: case QMetaType::Short: case QMetaType::Double: case QMetaType::Float: case QMetaType::ULongLong: case QMetaType::LongLong: case QMetaType::UShort: // Using function with bool parameter should be preferred over converted bool to nimber return 5; break; case QMetaType::QString: case QMetaType::QByteArray: case QMetaType::QDateTime: case QMetaType::QDate: // Bool probably shouldn't be converted to string if there are better alternatives return 50; break; default: return 5; } } return 0;} bool ScriptEngineV8::castValueToVariant(const V8ScriptValue& v8Val, QVariant& dest, int destTypeId) { Q_ASSERT(_v8Isolate->IsCurrent()); v8::HandleScope handleScope(_v8Isolate); v8::Local<v8::Context> context = getContext(); v8::Context::Scope contextScope(context); const v8::Local<v8::Value> val = v8Val.constGet(); // if we're not particularly interested in a specific type, try to detect if we're dealing with a registered type if (destTypeId == QMetaType::UnknownType) { QObject* obj = ScriptObjectV8Proxy::unwrap(v8Val); if (obj) { for (const QMetaObject* metaObject = obj->metaObject(); metaObject; metaObject = metaObject->superClass()) { QByteArray typeName = QByteArray(metaObject->className()) + "*"; int typeId = QMetaType::type(typeName.constData()); if (typeId != QMetaType::UnknownType) { destTypeId = typeId; break; } } } } if (destTypeId == qMetaTypeId<ScriptValue>()) { dest = QVariant::fromValue(ScriptValue(new ScriptValueV8Wrapper(this, v8Val))); return true; } QString errorMessage(""); // do we have a registered handler for this type? ScriptEngine::DemarshalFunction demarshalFunc = nullptr; { _customTypeProtect.lockForRead(); CustomMarshalMap::const_iterator lookup = _customTypes.find(destTypeId); if (lookup != _customTypes.cend()) { demarshalFunc = lookup.value().demarshalFunc; } _customTypeProtect.unlock(); } if (demarshalFunc) { dest = QVariant(); ScriptValue wrappedVal(new ScriptValueV8Wrapper(this, v8Val)); bool success = demarshalFunc(wrappedVal, dest); if(!success) dest = QVariant(); return success; } else { switch (destTypeId) { case QMetaType::UnknownType: if (val->IsUndefined()) { dest = QVariant(); break; } if (val->IsNull()) { dest = QVariant::fromValue(nullptr); break; } if (val->IsBoolean()) { dest = QVariant::fromValue(val->ToBoolean(_v8Isolate)->Value()); break; } if (val->IsString()) { v8::String::Utf8Value string(_v8Isolate, val); Q_ASSERT(*string != nullptr); dest = QVariant::fromValue(QString(*string)); break; } if (val->IsNumber()) { dest = QVariant::fromValue(val->ToNumber(context).ToLocalChecked()->Value()); break; } { QObject* obj = ScriptObjectV8Proxy::unwrap(v8Val); if (obj) { dest = QVariant::fromValue(obj); break; } } { QVariant var = ScriptVariantV8Proxy::unwrap(v8Val); if (var.isValid()) { dest = var; break; } } if (val->IsArray()) { if (convertJSArrayToVariant(v8::Local<v8::Array>::Cast(val), dest)) { return true; } } // This is for generic JS objects if (val->IsObject()) { if (convertJSObjectToVariant(v8::Local<v8::Object>::Cast(val), dest)) { break; } } errorMessage = QString() + "Conversion failure: " + QString(*v8::String::Utf8Value(_v8Isolate, val->ToDetailString(getConstContext()).ToLocalChecked())) + "to variant. Destination type: " + QMetaType::typeName(destTypeId) +" details: "+ scriptValueDebugDetailsV8(v8Val); qCDebug(scriptengine_v8) << errorMessage; // V8TODO: this doesn't seem to be necessary anymore but I'm keeping it until all the API is tested //dest = val->ToVariant(); break; case QMetaType::Bool: dest = QVariant::fromValue(val->ToBoolean(_v8Isolate)->Value()); break; case QMetaType::QDateTime: case QMetaType::QDate: if (val->IsDate()){ double timeMs = v8::Date::Cast(*val)->NumberValue(context).ToChecked(); dest = QVariant::fromValue(QDateTime::fromMSecsSinceEpoch(timeMs)); } else if (val->IsNumber()) { dest = QVariant::fromValue(QDateTime::fromMSecsSinceEpoch(val->ToNumber(context).ToLocalChecked()->Value())); } else { return false; } break; case QMetaType::UInt: case QMetaType::ULong: if ( val->IsArray() || val->IsObject() ){ return false; } dest = QVariant::fromValue(val->ToUint32(context).ToLocalChecked()->Value()); break; case QMetaType::Int: case QMetaType::Long: case QMetaType::Short: if ( val->IsArray() || val->IsObject() ){ return false; } dest = QVariant::fromValue(val->ToInt32(context).ToLocalChecked()->Value()); break; case QMetaType::Double: case QMetaType::Float: case QMetaType::ULongLong: case QMetaType::LongLong: if ( val->IsArray() || val->IsObject() ){ return false; } dest = QVariant::fromValue(val->ToNumber(context).ToLocalChecked()->Value()); break; case QMetaType::QString: case QMetaType::QByteArray: { v8::String::Utf8Value string(_v8Isolate, val); Q_ASSERT(*string != nullptr); dest = QVariant::fromValue(QString(*string)); } break; case QMetaType::UShort: if ( val->IsArray() || val->IsObject() ){ return false; } dest = QVariant::fromValue(static_cast<uint16_t>(val->ToUint32(context).ToLocalChecked()->Value())); break; case QMetaType::QObjectStar: { // V8TODO: This is to diagnose a really weird segfault where it looks like only half of the QPointer is set // to null upon object deletion, it doesn't seem to happen anymore, but I'm keeping it for now uint64_t ptrVal = (uint64_t)(ScriptObjectV8Proxy::unwrap(v8Val)); if ((uint32_t)(ptrVal) == 0 && ptrVal != 0) { qCDebug(scriptengine_v8) << "ScriptEngineV8::castValueToVariant pointer bug happened"; //Q_ASSERT(false); return false; } } dest = QVariant::fromValue(ScriptObjectV8Proxy::unwrap(v8Val)); break; case QMetaType::QVariant: if (val->IsUndefined()) { dest = QVariant(); return true; } if (val->IsNull()) { dest = QVariant::fromValue(nullptr); return true; } if (val->IsBoolean()) { dest = QVariant::fromValue(val->BooleanValue(_v8Isolate)); return true; } if (val->IsString()) { v8::String::Utf8Value string(_v8Isolate, val); Q_ASSERT(*string != nullptr); dest = QVariant::fromValue(QString(*string)); return true; } if (val->IsNumber()) { dest = QVariant::fromValue(val->ToNumber(context).ToLocalChecked()->Value()); return true; } { QObject* obj = ScriptObjectV8Proxy::unwrap(v8Val); if (obj) { dest = QVariant::fromValue(obj); return true; } } { QVariant var = ScriptVariantV8Proxy::unwrap(v8Val); if (var.isValid()) { dest = var; return true; } } if (val->IsArray()) { if (convertJSArrayToVariant(v8::Local<v8::Array>::Cast(val), dest)) { return true; } } if (val->IsObject()) { if (convertJSObjectToVariant(v8::Local<v8::Object>::Cast(val), dest)) { return true;Lines 1–500 of 757. Load more
// // ScriptEngineV8_cast.cpp // libraries/script-engine/src/v8 // // Created by Heather Anderson 12/9/2021 // Modified for V8 by dr Karol Suprynowicz on 2022/10/08 // Copyright 2021 Vircadia contributors. // Copyright 2022-2023 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // SPDX-License-Identifier: Apache-2.0 // #include "ScriptEngineV8.h" #include <QtCore/QJsonArray> #include <QtCore/QJsonObject> #include <QtCore/QJsonValue> #include "libplatform/libplatform.h" #include "v8.h" #include "../ScriptEngineCast.h" #include "../ScriptValueIterator.h" #include "ScriptObjectV8Proxy.h" #include "ScriptValueV8Wrapper.h" #include "ScriptEngineLoggingV8.h" void ScriptEngineV8::setDefaultPrototype(int metaTypeId, const ScriptValue& prototype) { ScriptValueV8Wrapper* unwrappedPrototype = ScriptValueV8Wrapper::unwrap(prototype); if (unwrappedPrototype) { const V8ScriptValue& scriptPrototype = unwrappedPrototype->toV8Value(); _customTypeProtect.lockForWrite(); _customPrototypes.insert(metaTypeId, scriptPrototype); _customTypeProtect.unlock(); } } void ScriptEngineV8::registerCustomType(int type, ScriptEngine::MarshalFunction marshalFunc, ScriptEngine::DemarshalFunction demarshalFunc) { _customTypeProtect.lockForWrite(); // storing it in a map for our own benefit CustomMarshal& customType = _customTypes.insert(type, CustomMarshal()).value(); customType.demarshalFunc = demarshalFunc; customType.marshalFunc = marshalFunc; _customTypeProtect.unlock(); } Q_DECLARE_METATYPE(ScriptValue); Q_DECLARE_METATYPE(QVariantMap); static ScriptValue StringListToScriptValue(ScriptEngine* engine, const QStringList& src) { int len = src.length(); ScriptValue dest = engine->newArray(len); for (int idx = 0; idx < len; ++idx) { dest.setProperty(idx, engine->newValue(src.at(idx))); } return dest; } static bool StringListFromScriptValue(const ScriptValue& src, QStringList& dest) { if(!src.isArray()) return false; int len = src.property("length").toInteger(); dest.clear(); for (int idx = 0; idx < len; ++idx) { dest.append(src.property(idx).toString()); } return true; } static ScriptValue VariantListToScriptValue(ScriptEngine* engine, const QVariantList& src) { int len = src.length(); ScriptValue dest = engine->newArray(len); for (int idx = 0; idx < len; ++idx) { dest.setProperty(idx, engine->newVariant(src.at(idx))); } return dest; } static bool VariantListFromScriptValue(const ScriptValue& src, QVariantList& dest) { if(!src.isArray()) return false; int len = src.property("length").toInteger(); dest.clear(); for (int idx = 0; idx < len; ++idx) { dest.append(src.property(idx).toVariant()); } return true; } static ScriptValue VariantMapToScriptValue(ScriptEngine* engine, const QVariantMap& src) { ScriptValue dest = engine->newObject(); for (QVariantMap::const_iterator iter = src.cbegin(); iter != src.cend(); ++iter) { dest.setProperty(iter.key(), engine->newVariant(iter.value())); } return dest; } static bool VariantMapFromScriptValue(const ScriptValue& src, QVariantMap& dest) { dest.clear(); ScriptValueIteratorPointer iter = src.newIterator(); while (iter->hasNext()) { iter->next(); dest.insert(iter->name(), iter->value().toVariant()); } return true; } static ScriptValue VariantHashToScriptValue(ScriptEngine* engine, const QVariantHash& src) { ScriptValue dest = engine->newObject(); for (QVariantHash::const_iterator iter = src.cbegin(); iter != src.cend(); ++iter) { dest.setProperty(iter.key(), engine->newVariant(iter.value())); } return dest; } static bool VariantHashFromScriptValue(const ScriptValue& src, QVariantHash& dest) { dest.clear(); ScriptValueIteratorPointer iter = src.newIterator(); while (iter->hasNext()) { iter->next(); dest.insert(iter->name(), iter->value().toVariant()); } return true; } static ScriptValue JsonValueToScriptValue(ScriptEngine* engine, const QJsonValue& src) { return engine->newVariant(src.toVariant()); } static bool JsonValueFromScriptValue(const ScriptValue& src, QJsonValue& dest) { dest = QJsonValue::fromVariant(src.toVariant()); return true; } static ScriptValue JsonObjectToScriptValue(ScriptEngine* engine, const QJsonObject& src) { QVariantMap map = src.toVariantMap(); ScriptValue dest = engine->newObject(); for (QVariantMap::const_iterator iter = map.cbegin(); iter != map.cend(); ++iter) { dest.setProperty(iter.key(), engine->newVariant(iter.value())); } return dest; } static bool JsonObjectFromScriptValue(const ScriptValue& src, QJsonObject& dest) { QVariantMap map; ScriptValueIteratorPointer iter = src.newIterator(); while (iter->hasNext()) { iter->next(); map.insert(iter->name(), iter->value().toVariant()); } dest = QJsonObject::fromVariantMap(map); return true; } static ScriptValue JsonArrayToScriptValue(ScriptEngine* engine, const QJsonArray& src) { QVariantList list = src.toVariantList(); int len = list.length(); ScriptValue dest = engine->newArray(len); for (int idx = 0; idx < len; ++idx) { dest.setProperty(idx, engine->newVariant(list.at(idx))); } return dest; } static bool JsonArrayFromScriptValue(const ScriptValue& src, QJsonArray& dest) { if(!src.isArray()) return false; QVariantList list; int len = src.property("length").toInteger(); for (int idx = 0; idx < len; ++idx) { list.append(src.property(idx).toVariant()); } dest = QJsonArray::fromVariantList(list); return true; } // QMetaType::QJsonArray void ScriptEngineV8::registerSystemTypes() { scriptRegisterMetaType<QStringList, StringListToScriptValue, StringListFromScriptValue>(static_cast<ScriptEngine*>(this)); scriptRegisterMetaType<QVariantList, VariantListToScriptValue, VariantListFromScriptValue>(this); scriptRegisterMetaType<QVariantMap, VariantMapToScriptValue, VariantMapFromScriptValue>(this); scriptRegisterMetaType<QVariantHash, VariantHashToScriptValue, VariantHashFromScriptValue>(this); scriptRegisterMetaType<QJsonValue, JsonValueToScriptValue, JsonValueFromScriptValue>(this); scriptRegisterMetaType<QJsonObject, JsonObjectToScriptValue, JsonObjectFromScriptValue>(this); scriptRegisterMetaType<QJsonArray, JsonArrayToScriptValue, JsonArrayFromScriptValue>(this); } int ScriptEngineV8::computeCastPenalty(const V8ScriptValue& v8Val, int destTypeId) { Q_ASSERT(_v8Isolate->IsCurrent()); v8::HandleScope handleScope(_v8Isolate); v8::Local<v8::Context> context = getContext(); v8::Context::Scope contextScope(context); const v8::Local<v8::Value> val = v8Val.constGet(); if (val->IsNumber()) { switch (destTypeId){ case QMetaType::Bool: // Conversion to bool is acceptable, but numbers are preferred return 5; break; case QMetaType::UInt: case QMetaType::ULong: case QMetaType::Int: case QMetaType::Long: case QMetaType::Short: case QMetaType::Double: case QMetaType::Float: case QMetaType::ULongLong: case QMetaType::LongLong: case QMetaType::UShort: // Perfect case. JS doesn't have separate integer and floating point type return 0; break; case QMetaType::QString: case QMetaType::QByteArray: case QMetaType::QDateTime: case QMetaType::QDate: // Conversion to string should be avoided, it's probably not what we want return 100; break; default: // Other, not predicted cases return 5; } } else if (val->IsString() || val->IsDate() || val->IsRegExp()) { switch (destTypeId){ case QMetaType::Bool: // Conversion from to bool should be avoided if possible, it's probably not what we want return 100; case QMetaType::UInt: case QMetaType::ULong: case QMetaType::Int: case QMetaType::Long: case QMetaType::Short: case QMetaType::Double: case QMetaType::Float: case QMetaType::ULongLong: case QMetaType::LongLong: case QMetaType::UShort: // Conversion from to number should be avoided if possible, it's probably not what we want return 100; case QMetaType::QString: // Perfect case return 0; case QMetaType::QByteArray: case QMetaType::QDateTime: case QMetaType::QDate: // String to string should be slightly preferred return 5; default: return 5; } } else if (val->IsBoolean()) { switch (destTypeId){ case QMetaType::Bool: // Perfect case return 0; break; case QMetaType::UInt: case QMetaType::ULong: case QMetaType::Int: case QMetaType::Long: case QMetaType::Short: case QMetaType::Double: case QMetaType::Float: case QMetaType::ULongLong: case QMetaType::LongLong: case QMetaType::UShort: // Using function with bool parameter should be preferred over converted bool to nimber return 5; break; case QMetaType::QString: case QMetaType::QByteArray: case QMetaType::QDateTime: case QMetaType::QDate: // Bool probably shouldn't be converted to string if there are better alternatives return 50; break; default: return 5; } } return 0; } bool ScriptEngineV8::castValueToVariant(const V8ScriptValue& v8Val, QVariant& dest, int destTypeId) { Q_ASSERT(_v8Isolate->IsCurrent()); v8::HandleScope handleScope(_v8Isolate); v8::Local<v8::Context> context = getContext(); v8::Context::Scope contextScope(context); const v8::Local<v8::Value> val = v8Val.constGet(); // if we're not particularly interested in a specific type, try to detect if we're dealing with a registered type if (destTypeId == QMetaType::UnknownType) { QObject* obj = ScriptObjectV8Proxy::unwrap(v8Val); if (obj) { for (const QMetaObject* metaObject = obj->metaObject(); metaObject; metaObject = metaObject->superClass()) { QByteArray typeName = QByteArray(metaObject->className()) + "*"; int typeId = QMetaType::type(typeName.constData()); if (typeId != QMetaType::UnknownType) { destTypeId = typeId; break; } } } } if (destTypeId == qMetaTypeId<ScriptValue>()) { dest = QVariant::fromValue(ScriptValue(new ScriptValueV8Wrapper(this, v8Val))); return true; } QString errorMessage(""); // do we have a registered handler for this type? ScriptEngine::DemarshalFunction demarshalFunc = nullptr; { _customTypeProtect.lockForRead(); CustomMarshalMap::const_iterator lookup = _customTypes.find(destTypeId); if (lookup != _customTypes.cend()) { demarshalFunc = lookup.value().demarshalFunc; } _customTypeProtect.unlock(); } if (demarshalFunc) { dest = QVariant(); ScriptValue wrappedVal(new ScriptValueV8Wrapper(this, v8Val)); bool success = demarshalFunc(wrappedVal, dest); if(!success) dest = QVariant(); return success; } else { switch (destTypeId) { case QMetaType::UnknownType: if (val->IsUndefined()) { dest = QVariant(); break; } if (val->IsNull()) { dest = QVariant::fromValue(nullptr); break; } if (val->IsBoolean()) { dest = QVariant::fromValue(val->ToBoolean(_v8Isolate)->Value()); break; } if (val->IsString()) { v8::String::Utf8Value string(_v8Isolate, val); Q_ASSERT(*string != nullptr); dest = QVariant::fromValue(QString(*string)); break; } if (val->IsNumber()) { dest = QVariant::fromValue(val->ToNumber(context).ToLocalChecked()->Value()); break; } { QObject* obj = ScriptObjectV8Proxy::unwrap(v8Val); if (obj) { dest = QVariant::fromValue(obj); break; } } { QVariant var = ScriptVariantV8Proxy::unwrap(v8Val); if (var.isValid()) { dest = var; break; } } if (val->IsArray()) { if (convertJSArrayToVariant(v8::Local<v8::Array>::Cast(val), dest)) { return true; } } // This is for generic JS objects if (val->IsObject()) { if (convertJSObjectToVariant(v8::Local<v8::Object>::Cast(val), dest)) { break; } } errorMessage = QString() + "Conversion failure: " + QString(*v8::String::Utf8Value(_v8Isolate, val->ToDetailString(getConstContext()).ToLocalChecked())) + "to variant. Destination type: " + QMetaType::typeName(destTypeId) +" details: "+ scriptValueDebugDetailsV8(v8Val); qCDebug(scriptengine_v8) << errorMessage; // V8TODO: this doesn't seem to be necessary anymore but I'm keeping it until all the API is tested //dest = val->ToVariant(); break; case QMetaType::Bool: dest = QVariant::fromValue(val->ToBoolean(_v8Isolate)->Value()); break; case QMetaType::QDateTime: case QMetaType::QDate: if (val->IsDate()){ double timeMs = v8::Date::Cast(*val)->NumberValue(context).ToChecked(); dest = QVariant::fromValue(QDateTime::fromMSecsSinceEpoch(timeMs)); } else if (val->IsNumber()) { dest = QVariant::fromValue(QDateTime::fromMSecsSinceEpoch(val->ToNumber(context).ToLocalChecked()->Value())); } else { return false; } break; case QMetaType::UInt: case QMetaType::ULong: if ( val->IsArray() || val->IsObject() ){ return false; } dest = QVariant::fromValue(val->ToUint32(context).ToLocalChecked()->Value()); break; case QMetaType::Int: case QMetaType::Long: case QMetaType::Short: if ( val->IsArray() || val->IsObject() ){ return false; } dest = QVariant::fromValue(val->ToInt32(context).ToLocalChecked()->Value()); break; case QMetaType::Double: case QMetaType::Float: case QMetaType::ULongLong: case QMetaType::LongLong: if ( val->IsArray() || val->IsObject() ){ return false; } dest = QVariant::fromValue(val->ToNumber(context).ToLocalChecked()->Value()); break; case QMetaType::QString: case QMetaType::QByteArray: { v8::String::Utf8Value string(_v8Isolate, val); Q_ASSERT(*string != nullptr); dest = QVariant::fromValue(QString(*string)); } break; case QMetaType::UShort: if ( val->IsArray() || val->IsObject() ){ return false; } dest = QVariant::fromValue(static_cast<uint16_t>(val->ToUint32(context).ToLocalChecked()->Value())); break; case QMetaType::QObjectStar: { // V8TODO: This is to diagnose a really weird segfault where it looks like only half of the QPointer is set // to null upon object deletion, it doesn't seem to happen anymore, but I'm keeping it for now uint64_t ptrVal = (uint64_t)(ScriptObjectV8Proxy::unwrap(v8Val)); if ((uint32_t)(ptrVal) == 0 && ptrVal != 0) { qCDebug(scriptengine_v8) << "ScriptEngineV8::castValueToVariant pointer bug happened"; //Q_ASSERT(false); return false; } } dest = QVariant::fromValue(ScriptObjectV8Proxy::unwrap(v8Val)); break; case QMetaType::QVariant: if (val->IsUndefined()) { dest = QVariant(); return true; } if (val->IsNull()) { dest = QVariant::fromValue(nullptr); return true; } if (val->IsBoolean()) { dest = QVariant::fromValue(val->BooleanValue(_v8Isolate)); return true; } if (val->IsString()) { v8::String::Utf8Value string(_v8Isolate, val); Q_ASSERT(*string != nullptr); dest = QVariant::fromValue(QString(*string)); return true; } if (val->IsNumber()) { dest = QVariant::fromValue(val->ToNumber(context).ToLocalChecked()->Value()); return true; } { QObject* obj = ScriptObjectV8Proxy::unwrap(v8Val); if (obj) { dest = QVariant::fromValue(obj); return true; } } { QVariant var = ScriptVariantV8Proxy::unwrap(v8Val); if (var.isValid()) { dest = var; return true; } } if (val->IsArray()) { if (convertJSArrayToVariant(v8::Local<v8::Array>::Cast(val), dest)) { return true; } } if (val->IsObject()) { if (convertJSObjectToVariant(v8::Local<v8::Object>::Cast(val), dest)) { return true;