diff --git a/interface/resources/serverless/Scripts/activator-doppleganger.js b/interface/resources/serverless/Scripts/activator-doppleganger.js index bcb9b7c509..c555aa6a7d 100644 --- a/interface/resources/serverless/Scripts/activator-doppleganger.js +++ b/interface/resources/serverless/Scripts/activator-doppleganger.js @@ -1,4 +1,4 @@ -'use strict'; +'no use strict'; // // activator-doppleganger.js // @@ -23,6 +23,8 @@ autoUpdate: true }); + // V8TODO: does this need to be fixed? Right now it refers to global object in non-strict mode, + // and in strict mode it's undefined this.preload = function(entityID) { thisEntityID = entityID; } diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 13af6f5386..2f07eba911 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -77,6 +77,7 @@ WindowScriptingInterface::~WindowScriptingInterface() { } ScriptValue WindowScriptingInterface::hasFocus() { + Q_ASSERT(engine); return engine()->newValue(qApp->hasFocus()); } @@ -105,6 +106,7 @@ void WindowScriptingInterface::alert(const QString& message) { /// \param const QString& message message to display /// \return ScriptValue `true` if 'Yes' was clicked, `false` otherwise ScriptValue WindowScriptingInterface::confirm(const QString& message) { + Q_ASSERT(engine); return engine()->newValue((QMessageBox::Yes == OffscreenUi::question("", message, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes))); } @@ -114,6 +116,7 @@ ScriptValue WindowScriptingInterface::confirm(const QString& message) { /// \return ScriptValue string text value in text box if the dialog was accepted, `null` otherwise. ScriptValue WindowScriptingInterface::prompt(const QString& message, const QString& defaultText) { QString result = OffscreenUi::getText(nullptr, "", message, QLineEdit::Normal, defaultText); + Q_ASSERT(engine); auto sResult = engine()->newValue(result); if (sResult.equals(engine()->newValue(""))) { return engine()->nullValue(); @@ -232,6 +235,7 @@ ScriptValue WindowScriptingInterface::browseDir(const QString& title, const QStr if (!result.isEmpty()) { setPreviousBrowseLocation(QFileInfo(result).absolutePath()); } + Q_ASSERT(engine); return result.isEmpty() ? engine()->nullValue() : engine()->newValue(result); } @@ -276,6 +280,7 @@ ScriptValue WindowScriptingInterface::browse(const QString& title, const QString if (!result.isEmpty()) { setPreviousBrowseLocation(QFileInfo(result).absolutePath()); } + Q_ASSERT(engine); return result.isEmpty() ? engine()->nullValue() : engine()->newValue(result); } @@ -323,6 +328,7 @@ ScriptValue WindowScriptingInterface::save(const QString& title, const QString& if (!result.isEmpty()) { setPreviousBrowseLocation(QFileInfo(result).absolutePath()); } + Q_ASSERT(engine); return result.isEmpty() ? engine()->nullValue() : engine()->newValue(result); } @@ -373,6 +379,7 @@ ScriptValue WindowScriptingInterface::browseAssets(const QString& title, const Q if (!result.isEmpty()) { setPreviousBrowseAssetLocation(QFileInfo(result).absolutePath()); } + Q_ASSERT(engine); return result.isEmpty() ? engine()->nullValue() : engine()->newValue(result); } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index c3272b0345..af56b01e13 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2557,6 +2557,7 @@ QDataStream& operator>>(QDataStream& in, AttachmentData& attachment) { void AttachmentDataObject::setModelURL(const QString& modelURL) { AttachmentData data = scriptvalue_cast(thisObject()); data.modelURL = modelURL; + Q_ASSERT(engine); thisObject() = engine()->toScriptValue(data); } @@ -2567,6 +2568,7 @@ QString AttachmentDataObject::getModelURL() const { void AttachmentDataObject::setJointName(const QString& jointName) { AttachmentData data = scriptvalue_cast(thisObject()); data.jointName = jointName; + Q_ASSERT(engine); thisObject() = engine()->toScriptValue(data); } @@ -2577,6 +2579,7 @@ QString AttachmentDataObject::getJointName() const { void AttachmentDataObject::setTranslation(const glm::vec3& translation) { AttachmentData data = scriptvalue_cast(thisObject()); data.translation = translation; + Q_ASSERT(engine); thisObject() = engine()->toScriptValue(data); } @@ -2587,6 +2590,7 @@ glm::vec3 AttachmentDataObject::getTranslation() const { void AttachmentDataObject::setRotation(const glm::quat& rotation) { AttachmentData data = scriptvalue_cast(thisObject()); data.rotation = rotation; + Q_ASSERT(engine); thisObject() = engine()->toScriptValue(data); } @@ -2597,6 +2601,7 @@ glm::quat AttachmentDataObject::getRotation() const { void AttachmentDataObject::setScale(float scale) { AttachmentData data = scriptvalue_cast(thisObject()); data.scale = scale; + Q_ASSERT(engine); thisObject() = engine()->toScriptValue(data); } @@ -2607,6 +2612,7 @@ float AttachmentDataObject::getScale() const { void AttachmentDataObject::setIsSoft(bool isSoft) { AttachmentData data = scriptvalue_cast(thisObject()); data.isSoft = isSoft; + Q_ASSERT(engine); thisObject() = engine()->toScriptValue(data); } diff --git a/libraries/script-engine/src/AssetScriptingInterface.cpp b/libraries/script-engine/src/AssetScriptingInterface.cpp index 5231223eb5..e23984ddb1 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.cpp +++ b/libraries/script-engine/src/AssetScriptingInterface.cpp @@ -73,6 +73,7 @@ void AssetScriptingInterface::uploadData(QString data, const ScriptValue& callba auto upload = DependencyManager::get()->createUpload(dataByteArray); Promise deferred = makePromise(__FUNCTION__); + Q_ASSERT(engine); auto scriptEngine = engine(); deferred->ready([=](QString error, QVariantMap result) { auto url = result.value("url").toString(); @@ -96,6 +97,7 @@ void AssetScriptingInterface::setMapping(QString path, QString hash, const Scrip auto handler = jsBindCallback(thisObject(), callback); auto setMappingRequest = assetClient()->createSetMappingRequest(path, hash); Promise deferred = makePromise(__FUNCTION__); + Q_ASSERT(engine); auto scriptEngine = engine(); deferred->ready([=](QString error, QVariantMap result) { jsCallback(handler, scriptEngine->newValue(error), result); @@ -133,6 +135,7 @@ void AssetScriptingInterface::downloadData(QString urlString, const ScriptValue& auto assetRequest = assetClient->createRequest(hash); Promise deferred = makePromise(__FUNCTION__); + Q_ASSERT(engine); auto scriptEngine = engine(); deferred->ready([=](QString error, QVariantMap result) { // FIXME: to remain backwards-compatible the signature here is "callback(data, n/a)" @@ -196,6 +199,7 @@ void AssetScriptingInterface::getMapping(QString asset, const ScriptValue& callb JS_VERIFY(AssetUtils::isValidFilePath(path), "invalid ATP file path: " + asset + "(path:"+path+")"); JS_VERIFY(callback.isFunction(), "expected second parameter to be a callback function"); Promise promise = getAssetInfo(path); + Q_ASSERT(engine); auto scriptEngine = engine(); promise->ready([=](QString error, QVariantMap result) { jsCallback(handler, scriptEngine->newValue(error), scriptEngine->newValue(result.value("hash").toString())); @@ -229,6 +233,7 @@ Promise AssetScriptingInterface::jsPromiseReady(Promise promise, const ScriptVal if (!jsVerify(handler.isValid(), "jsPromiseReady -- invalid callback handler")) { return nullptr; } + Q_ASSERT(engine); auto scriptEngine = engine(); return promise->ready([this, handler, scriptEngine](QString error, QVariantMap result) { jsCallback(handler, scriptEngine->newValue(error), result); @@ -238,6 +243,7 @@ Promise AssetScriptingInterface::jsPromiseReady(Promise promise, const ScriptVal void AssetScriptingInterface::jsCallback(const ScriptValue& handler, const ScriptValue& error, const ScriptValue& result) { Q_ASSERT(thread() == QThread::currentThread()); + Q_ASSERT(engine); auto errorValue = !error.toBool() ? engine()->nullValue() : error; JS_VERIFY(handler.isObject() && handler.property("callback").isFunction(), QString("jsCallback -- .callback is not a function (%1)") @@ -536,6 +542,7 @@ void AssetScriptingInterface::loadFromCache(const ScriptValue& options, const Sc } bool AssetScriptingInterface::canWriteCacheValue(const QUrl& url) { + Q_ASSERT(engine); auto scriptManager = engine()->manager(); if (!scriptManager) { return false; diff --git a/libraries/script-engine/src/ConsoleScriptingInterface.cpp b/libraries/script-engine/src/ConsoleScriptingInterface.cpp index e8a4de24ad..a0736bbac1 100644 --- a/libraries/script-engine/src/ConsoleScriptingInterface.cpp +++ b/libraries/script-engine/src/ConsoleScriptingInterface.cpp @@ -80,12 +80,14 @@ ScriptValue ConsoleScriptingInterface::exception(ScriptContext* context, ScriptE void ConsoleScriptingInterface::time(QString labelName) { _timerDetails.insert(labelName, QDateTime::currentDateTime().toUTC()); QString message = QString("%1: Timer started").arg(labelName); + Q_ASSERT(engine); if (ScriptManager* scriptManager = engine()->manager()) { scriptManager->scriptPrintedMessage(message); } } void ConsoleScriptingInterface::timeEnd(QString labelName) { + Q_ASSERT(engine); if (ScriptManager* scriptManager = engine()->manager()) { if (!_timerDetails.contains(labelName)) { scriptManager->scriptErrorMessage("No such label found " + labelName); @@ -134,6 +136,7 @@ ScriptValue ConsoleScriptingInterface::assertion(ScriptContext* context, ScriptE } void ConsoleScriptingInterface::trace() { + Q_ASSERT(engine); ScriptEnginePointer scriptEngine = engine(); if (ScriptManager* scriptManager = scriptEngine->manager()) { scriptManager->scriptPrintedMessage @@ -143,6 +146,7 @@ void ConsoleScriptingInterface::trace() { } void ConsoleScriptingInterface::clear() { + Q_ASSERT(engine); if (ScriptManager* scriptManager = engine()->manager()) { scriptManager->clearDebugLogWindow(); } diff --git a/libraries/script-engine/src/Mat4.cpp b/libraries/script-engine/src/Mat4.cpp index 755744777d..147511fe0d 100644 --- a/libraries/script-engine/src/Mat4.cpp +++ b/libraries/script-engine/src/Mat4.cpp @@ -88,6 +88,7 @@ void Mat4::print(const QString& label, const glm::mat4& m, bool transpose) const QString message = QString("%1 %2").arg(qPrintable(label)); message = message.arg(glm::to_string(out).c_str()); qCDebug(scriptengine) << message; + Q_ASSERT(engine); if (ScriptManager* scriptManager = engine()->manager()) { scriptManager->print(message); } diff --git a/libraries/script-engine/src/Quat.cpp b/libraries/script-engine/src/Quat.cpp index 724ad34416..6d9a88b5bb 100644 --- a/libraries/script-engine/src/Quat.cpp +++ b/libraries/script-engine/src/Quat.cpp @@ -124,6 +124,7 @@ void Quat::print(const QString& label, const glm::quat& q, bool asDegrees) { message = message.arg(glm::to_string(glm::dquat(q)).c_str()); } qCDebug(scriptengine) << message; + Q_ASSERT(engine); if (ScriptManager* scriptManager = engine()->manager()) { scriptManager->print(message); } diff --git a/libraries/script-engine/src/ScriptUUID.cpp b/libraries/script-engine/src/ScriptUUID.cpp index b9661e31ed..d4207540f6 100644 --- a/libraries/script-engine/src/ScriptUUID.cpp +++ b/libraries/script-engine/src/ScriptUUID.cpp @@ -43,6 +43,7 @@ void ScriptUUID::print(const QString& label, const QUuid& id) { QString message = QString("%1 %2").arg(qPrintable(label)); message = message.arg(id.toString()); qCDebug(scriptengine) << message; + Q_ASSERT(engine); if (ScriptManager* scriptManager = engine()->manager()) { scriptManager->print(message); } diff --git a/libraries/script-engine/src/Vec3.cpp b/libraries/script-engine/src/Vec3.cpp index b50aabf7df..c5cc4814f8 100644 --- a/libraries/script-engine/src/Vec3.cpp +++ b/libraries/script-engine/src/Vec3.cpp @@ -37,6 +37,7 @@ void Vec3::print(const QString& label, const glm::vec3& v) { QString message = QString("%1 %2").arg(qPrintable(label)); message = message.arg(glm::to_string(glm::dvec3(v)).c_str()); qCDebug(scriptengine) << message; + Q_ASSERT(engine); if (ScriptManager* scriptManager = engine()->manager()) { scriptManager->print(message); } diff --git a/libraries/script-engine/src/v8/ScriptEngineV8.cpp b/libraries/script-engine/src/v8/ScriptEngineV8.cpp index de4e8fa264..d7458d8655 100644 --- a/libraries/script-engine/src/v8/ScriptEngineV8.cpp +++ b/libraries/script-engine/src/v8/ScriptEngineV8.cpp @@ -427,7 +427,6 @@ ScriptEngineV8::ScriptEngineV8(ScriptManager* scriptManager) : v8::Context::Scope contextScope(context); _contexts.append(std::make_shared(this,context, ScriptContextPointer())); - V8ScriptValue nullScriptValue(this, v8::Null(_v8Isolate)); _nullValue = ScriptValue(new ScriptValueV8Wrapper(this, nullScriptValue)); @@ -802,6 +801,32 @@ const v8::Local ScriptEngineV8::getConstContext() const { Q_ASSERT(!_contexts.isEmpty()); return handleScope.Escape(_contexts.last().get()->toV8Value()); } + +// Stored objects are used to create global objects for evaluateInClosure +void ScriptEngineV8::storeGlobalObjectContents() { + if (areGlobalObjectContentsStored) { + return; + } + v8::Locker locker(_v8Isolate); + v8::Isolate::Scope isolateScope(_v8Isolate); + v8::HandleScope handleScope(_v8Isolate); + auto context = getContext(); + v8::Context::Scope contextScope(context); + v8::Local globalMemberObjects = v8::Object::New(_v8Isolate); + + auto globalMemberNames = context->Global()->GetPropertyNames(context).ToLocalChecked(); + for (size_t i = 0; i < globalMemberNames->Length(); i++) { + auto name = globalMemberNames->Get(context, i).ToLocalChecked(); + if(!globalMemberObjects->Set(context, name, context->Global()->Get(context, name).ToLocalChecked()).FromMaybe(false)) { + Q_ASSERT(false); + } + } + + _globalObjectContents.Reset(_v8Isolate, globalMemberObjects); + qDebug() << "ScriptEngineV8::storeGlobalObjectContents: " << globalMemberNames->Length() << " objects stored"; + areGlobalObjectContentsStored = true; +} + ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure, const ScriptProgramPointer& _program) { PROFILE_RANGE(script, "evaluateInClosure"); @@ -812,6 +837,7 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure, v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); + storeGlobalObjectContents(); v8::Local closureObject; //v8::Local oldGlobal; @@ -896,6 +922,7 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure, if (!unwrappedProgram->compile()) { qDebug(scriptengine) << "Can't compile script for evaluating in closure"; Q_ASSERT(false); + popContext(); return nullValue(); } const V8ScriptProgram& program = unwrappedProgram->toV8Value(); @@ -928,18 +955,35 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure, { v8::TryCatch tryCatch(getIsolate()); // Since V8 cannot use arbitrary object as global object, objects from main global need to be copied to closure's global object - auto oldGlobalMemberNames = oldContext->Global()->GetPropertyNames(oldContext).ToLocalChecked(); - for (size_t i = 0; i < oldGlobalMemberNames->Length(); i++) { - auto name = oldGlobalMemberNames->Get(closureContext, i).ToLocalChecked(); - if(!closureContext->Global()->Set(closureContext, name, oldContext->Global()->Get(oldContext, name).ToLocalChecked()).FromMaybe(false)) { + auto globalObjectContents = _globalObjectContents.Get(_v8Isolate); + auto globalMemberNames = globalObjectContents->GetPropertyNames(globalObjectContents->CreationContext()).ToLocalChecked(); + for (size_t i = 0; i < globalMemberNames->Length(); i++) { + auto name = globalMemberNames->Get(closureContext, i).ToLocalChecked(); + if(!closureContext->Global()->Set(closureContext, name, globalObjectContents->Get(globalObjectContents->CreationContext(), name).ToLocalChecked()).FromMaybe(false)) { Q_ASSERT(false); } } + qDebug() << "ScriptEngineV8::evaluateInClosure: " << globalMemberNames->Length() << " objects added to global"; + + /*auto oldGlobalMemberNames = oldContext->Global()->GetPropertyNames(oldContext).ToLocalChecked(); + //auto oldGlobalMemberNames = oldContext->Global()->GetPropertyNames(closureContext).ToLocalChecked(); + for (size_t i = 0; i < oldGlobalMemberNames->Length(); i++) { + auto name = oldGlobalMemberNames->Get(closureContext, i).ToLocalChecked(); + //auto name = oldGlobalMemberNames->Get(oldContext, i).ToLocalChecked(); + if(!closureContext->Global()->Set(closureContext, name, oldContext->Global()->Get(oldContext, name).ToLocalChecked()).FromMaybe(false)) { + //if(!closureContext->Global()->Set(closureContext, name, oldContext->Global()->Get(closureContext, name).ToLocalChecked()).FromMaybe(false)) { + Q_ASSERT(false); + } + }*/ // Objects from closure need to be copied to global object too + // V8TODO: I'm not sure which context to use with Get auto closureMemberNames = closureObject->GetPropertyNames(closureContext).ToLocalChecked(); + //auto closureMemberNames = closureObject->GetPropertyNames(oldContext).ToLocalChecked(); for (size_t i = 0; i < closureMemberNames->Length(); i++) { auto name = closureMemberNames->Get(closureContext, i).ToLocalChecked(); + //auto name = closureMemberNames->Get(oldContext, i).ToLocalChecked(); if(!closureContext->Global()->Set(closureContext, name, closureObject->Get(closureContext, name).ToLocalChecked()).FromMaybe(false)) { + //if(!closureContext->Global()->Set(closureContext, name, closureObject->Get(oldContext, name).ToLocalChecked()).FromMaybe(false)) { Q_ASSERT(false); } } @@ -1455,6 +1499,7 @@ ScriptContext* ScriptEngineV8::currentContext() const { // I'm not sure how to do this without discarding const _currContext = std::make_shared(const_cast(this)); }*/ + // V8TODO: add FunctionCallbackInfo or PropertyCallbackInfo when necessary return _contexts.last().get(); } @@ -1501,7 +1546,7 @@ ScriptValue ScriptEngineV8::newFunction(ScriptEngine::FunctionSignature fun, int ScriptEngineV8 *scriptEngine = reinterpret_cast (object->GetAlignedPointerFromInternalField(1)); ScriptContextV8Wrapper scriptContext(scriptEngine, &info, scriptEngine->getContext(), scriptEngine->currentContext()->parentContext()); - //V8TODO: this scriptContext needs to have FunctionCallbackInfo added + ScriptContextGuard scriptContextGuard(&scriptContext); ScriptValue result = function(&scriptContext, scriptEngine); ScriptValueV8Wrapper* unwrapped = ScriptValueV8Wrapper::unwrap(result); if (unwrapped) { diff --git a/libraries/script-engine/src/v8/ScriptEngineV8.h b/libraries/script-engine/src/v8/ScriptEngineV8.h index e6f89d0b7d..98138b5555 100644 --- a/libraries/script-engine/src/v8/ScriptEngineV8.h +++ b/libraries/script-engine/src/v8/ScriptEngineV8.h @@ -202,6 +202,8 @@ public: // not for public use, but I don't like how Qt strings this along with p ScriptContextV8Pointer pushContext(v8::Local context); void popContext(); + // V8TODO: call this after initializing global object + void storeGlobalObjectContents(); protected: // like `newFunction`, but allows mapping inline C++ lambdas with captures as callable V8ScriptValues @@ -240,6 +242,8 @@ protected: //mutable ScriptContextV8Pointer _currContext; // Current context stack. Main context is first on the list and current one is last. QList _contexts; + v8::Persistent _globalObjectContents; + bool areGlobalObjectContentsStored {false}; //V8TODO //ArrayBufferClass* _arrayBufferClass; diff --git a/libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp b/libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp index 43f0cc42b5..76b049bc5e 100644 --- a/libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp +++ b/libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp @@ -39,9 +39,9 @@ static const void *internalPointsToMethodProxy = (void *)0x13373000; // Used strictly to replace the "this" object value for property access. May expand to a full context element // if we find it necessary to, but hopefully not needed -class ScriptPropertyContextQtWrapper final : public ScriptContext { +class ScriptPropertyContextV8Wrapper final : public ScriptContext { public: // construction - inline ScriptPropertyContextQtWrapper(const ScriptValue& object, ScriptContext* parentContext) : + inline ScriptPropertyContextV8Wrapper(const ScriptValue& object, ScriptContext* parentContext) : _parent(parentContext), _object(object) {} public: // ScriptContext implementation @@ -568,7 +568,7 @@ V8ScriptValue ScriptObjectV8Proxy::property(const V8ScriptValue& object, const V QMetaProperty prop = metaObject->property(propId); ScriptValue scriptThis = ScriptValue(new ScriptValueV8Wrapper(_engine, object)); - ScriptPropertyContextQtWrapper ourContext(scriptThis, _engine->currentContext()); + ScriptPropertyContextV8Wrapper ourContext(scriptThis, _engine->currentContext()); ScriptContextGuard guard(&ourContext); QVariant varValue = prop.read(qobject); @@ -611,7 +611,7 @@ V8ScriptValue ScriptObjectV8Proxy::property(const V8ScriptValue& object, const V ScriptEngine::QObjectWrapOptions options = ScriptEngine::ExcludeSuperClassContents | //V8TODO ScriptEngine::ExcludeDeleteLater | ScriptEngine::PreferExistingWrapperObject; - //V8TODO: why is it returning new object every time? + // It's not necessarily new, newQObject looks for it first in object wrapper map return ScriptObjectV8Proxy::newQObject(_engine, proxy, ScriptEngine::ScriptOwnership, options); //return _engine->newQObject(proxy, ScriptEngine::ScriptOwnership, options); } @@ -642,7 +642,7 @@ void ScriptObjectV8Proxy::setProperty(V8ScriptValue& object, const V8ScriptStrin QMetaProperty prop = metaObject->property(propId); ScriptValue scriptThis = ScriptValue(new ScriptValueV8Wrapper(_engine, object)); - ScriptPropertyContextQtWrapper ourContext(scriptThis, _engine->currentContext()); + ScriptPropertyContextV8Wrapper ourContext(scriptThis, _engine->currentContext()); ScriptContextGuard guard(&ourContext); int propTypeId = prop.userType(); @@ -883,8 +883,10 @@ void ScriptMethodV8Proxy::call(const v8::FunctionCallbackInfo& argume } if (isValidMetaSelected) { - //ScriptContextV8Wrapper ourContext(_engine, context); - //ScriptContextGuard guard(&ourContext); + // V8TODO: is this the correct wrapper? + ScriptContextV8Wrapper ourContext(_engine, &arguments, _engine->getContext(), + _engine->currentContext()->parentContext()); + ScriptContextGuard guard(&ourContext); const QMetaMethod& meta = _metas[bestMeta]; int returnTypeId = meta.returnType(); QVector &qGenArgs = qGenArgsVectors[bestMeta]; @@ -1333,8 +1335,15 @@ void ScriptSignalV8Proxy::connect(ScriptValue arg0, ScriptValue arg1) { v8::Local destFunction = v8::Local::Cast(callback.get()); v8::Local destDataName = v8::String::NewFromUtf8(isolate, "__data__").ToLocalChecked(); v8::Local destData; - auto destFunctionContext = destFunction->CreationContext(); + // V8TODO: I'm not sure which context to use here + //auto destFunctionContext = destFunction->CreationContext(); + auto destFunctionContext = _engine->getContext(); + Q_ASSERT(thisObject().isObject()); V8ScriptValue v8ThisObject = ScriptValueV8Wrapper::fullUnwrap(_engine, thisObject()); + Q_ASSERT(ScriptObjectV8Proxy::unwrapProxy(v8ThisObject)); + ScriptSignalV8Proxy* thisProxy = dynamic_cast(ScriptObjectV8Proxy::unwrapProxy(v8ThisObject)->toQObject()); + Q_ASSERT(thisProxy); + qDebug(scriptengine) << "ScriptSignalV8Proxy::connect: " << thisProxy->fullName() << " fullName: " << fullName(); //Q_ASSERT(destFunction->InternalFieldCount() == 4); //Q_ASSERT(destData.get()->IsArray()); //v8::Local destData = destFunction->GetInternalField(3); @@ -1344,15 +1353,27 @@ void ScriptSignalV8Proxy::connect(ScriptValue arg0, ScriptValue arg1) { if (destData->IsArray()) { v8::Local destArray = v8::Local::Cast(destData); int length = destArray->Length();//destData.property("length").toInteger(); + // V8TODO: Maybe copying array is unnecessary? v8::Local newArray = v8::Array::New(isolate, length + 1); bool foundIt = false; for (int idx = 0; idx < length && !foundIt; ++idx) { v8::Local entry = destArray->Get(destFunctionContext, idx).ToLocalChecked(); + { + qDebug() << "ScriptSignalV8Proxy::connect: entry details: " << _engine->scriptValueDebugDetailsV8(V8ScriptValue(_engine, entry)); + Q_ASSERT(entry->IsObject()); + V8ScriptValue v8EntryObject(_engine, entry); + Q_ASSERT(ScriptObjectV8Proxy::unwrapProxy(v8EntryObject)); + // For debugging + ScriptSignalV8Proxy* entryProxy = dynamic_cast(ScriptObjectV8Proxy::unwrapProxy(v8EntryObject)->toQObject()); + Q_ASSERT(thisProxy); + qDebug(scriptengine) << "ScriptSignalV8Proxy::connect: entry proxy: " << entryProxy->fullName(); + } if (!newArray->Set(destFunctionContext, idx, entry).FromMaybe(false)) { Q_ASSERT(false); } } if (!newArray->Set(destFunctionContext, length, v8ThisObject.get()).FromMaybe(false)) { + //if (!newArray->Set(destFunctionContext, length, v8ThisObject.get()).FromMaybe(false)) { Q_ASSERT(false); } if (!destFunction->Set(destFunctionContext, destDataName, newArray).FromMaybe(false)) { @@ -1448,8 +1469,16 @@ void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) { v8::Local destFunction = v8::Local::Cast(callback.get()); v8::Local destDataName = v8::String::NewFromUtf8(isolate, "__data__").ToLocalChecked(); v8::Local destData; - auto destFunctionContext = destFunction->CreationContext(); + + //auto destFunctionContext = destFunction->CreationContext(); + auto destFunctionContext = _engine->getContext(); + Q_ASSERT(thisObject().isObject()); V8ScriptValue v8ThisObject = ScriptValueV8Wrapper::fullUnwrap(_engine, thisObject()); + Q_ASSERT(ScriptObjectV8Proxy::unwrapProxy(v8ThisObject)); + // For debugging + ScriptSignalV8Proxy* thisProxy = dynamic_cast(ScriptObjectV8Proxy::unwrapProxy(v8ThisObject)->toQObject()); + Q_ASSERT(thisProxy); + qDebug(scriptengine) << "ScriptSignalV8Proxy::disconnect: " << thisProxy->fullName() << " fullName: " << fullName(); //V8ScriptValue destData = callback.data(); //Q_ASSERT(destData->IsArray()); if (!destFunction->Get(destFunctionContext, destDataName).ToLocal(&destData)) { @@ -1463,8 +1492,22 @@ void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) { int newIndex = 0; for (int idx = 0; idx < length && !foundIt; ++idx) { v8::Local entry = destArray->Get(destFunctionContext, idx).ToLocalChecked(); + // For debugging: + { + _engine->logBacktrace("ScriptSignalV8Proxy::disconnect"); + qDebug() << "ScriptSignalV8Proxy::disconnect: entry details: " << _engine->scriptValueDebugDetailsV8(V8ScriptValue(_engine, entry)); + Q_ASSERT(entry->IsObject()); + V8ScriptValue v8EntryObject(_engine, entry); + Q_ASSERT(ScriptObjectV8Proxy::unwrapProxy(v8EntryObject)); + // For debugging + ScriptSignalV8Proxy* entryProxy = dynamic_cast(ScriptObjectV8Proxy::unwrapProxy(v8EntryObject)->toQObject()); + Q_ASSERT(thisProxy); + qDebug(scriptengine) << "ScriptSignalV8Proxy::disconnect: entry proxy: " << entryProxy->fullName(); + } if (entry->StrictEquals(v8ThisObject.get())) { + //V8TODO: compare proxies instead? foundIt = true; + qDebug() << "ScriptSignalV8Proxy::disconnect foundIt"; //V8ScriptValueList args; //args << idx << 1; //destData.property("splice").call(destData, args); diff --git a/libraries/script-engine/src/v8/ScriptObjectV8Proxy.h b/libraries/script-engine/src/v8/ScriptObjectV8Proxy.h index d65e507ff2..c7d243e51b 100644 --- a/libraries/script-engine/src/v8/ScriptObjectV8Proxy.h +++ b/libraries/script-engine/src/v8/ScriptObjectV8Proxy.h @@ -235,12 +235,14 @@ private: // implementation virtual int qt_metacall(QMetaObject::Call call, int id, void** arguments) override; int discoverMetaCallIdx(); ConnectionList::iterator findConnection(V8ScriptValue thisObject, V8ScriptValue callback); - QString fullName() const; + //QString fullName() const; public: // API // arg1 was had Null default value, but that needs isolate pointer to create Null in V8 virtual void connect(ScriptValue arg0, ScriptValue arg1 = ScriptValue()) override; virtual void disconnect(ScriptValue arg0, ScriptValue arg1 = ScriptValue()) override; + //Moved to public temporarily for debugging: + QString fullName() const; //virtual void connect(V8ScriptValue arg0) override; //virtual void disconnect(V8ScriptValue arg0) override; diff --git a/plugins/JSAPIExample/src/JSAPIExample.cpp b/plugins/JSAPIExample/src/JSAPIExample.cpp index 8dec23fd44..4e78aa4557 100644 --- a/plugins/JSAPIExample/src/JSAPIExample.cpp +++ b/plugins/JSAPIExample/src/JSAPIExample.cpp @@ -162,6 +162,7 @@ namespace REPLACE_ME_WITH_UNIQUE_NAME { * settings = null; // optional best pratice; allows the object to be reclaimed ASAP by the JS garbage collector */ ScriptValue getScopedSettings(const QString& scope) { + Q_ASSERT(engine); auto engine = Scriptable::engine(); if (!engine) { return ScriptValue();