This commit is contained in:
ksuprynowicz 2023-02-19 15:28:32 +01:00
parent f65e72c8f2
commit 5cf32a968c
14 changed files with 142 additions and 17 deletions

View file

@ -1,4 +1,4 @@
'use strict'; 'no use strict';
// //
// activator-doppleganger.js // activator-doppleganger.js
// //
@ -23,6 +23,8 @@
autoUpdate: true 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) { this.preload = function(entityID) {
thisEntityID = entityID; thisEntityID = entityID;
} }

View file

@ -77,6 +77,7 @@ WindowScriptingInterface::~WindowScriptingInterface() {
} }
ScriptValue WindowScriptingInterface::hasFocus() { ScriptValue WindowScriptingInterface::hasFocus() {
Q_ASSERT(engine);
return engine()->newValue(qApp->hasFocus()); return engine()->newValue(qApp->hasFocus());
} }
@ -105,6 +106,7 @@ void WindowScriptingInterface::alert(const QString& message) {
/// \param const QString& message message to display /// \param const QString& message message to display
/// \return ScriptValue `true` if 'Yes' was clicked, `false` otherwise /// \return ScriptValue `true` if 'Yes' was clicked, `false` otherwise
ScriptValue WindowScriptingInterface::confirm(const QString& message) { ScriptValue WindowScriptingInterface::confirm(const QString& message) {
Q_ASSERT(engine);
return engine()->newValue((QMessageBox::Yes == OffscreenUi::question("", message, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes))); 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. /// \return ScriptValue string text value in text box if the dialog was accepted, `null` otherwise.
ScriptValue WindowScriptingInterface::prompt(const QString& message, const QString& defaultText) { ScriptValue WindowScriptingInterface::prompt(const QString& message, const QString& defaultText) {
QString result = OffscreenUi::getText(nullptr, "", message, QLineEdit::Normal, defaultText); QString result = OffscreenUi::getText(nullptr, "", message, QLineEdit::Normal, defaultText);
Q_ASSERT(engine);
auto sResult = engine()->newValue(result); auto sResult = engine()->newValue(result);
if (sResult.equals(engine()->newValue(""))) { if (sResult.equals(engine()->newValue(""))) {
return engine()->nullValue(); return engine()->nullValue();
@ -232,6 +235,7 @@ ScriptValue WindowScriptingInterface::browseDir(const QString& title, const QStr
if (!result.isEmpty()) { if (!result.isEmpty()) {
setPreviousBrowseLocation(QFileInfo(result).absolutePath()); setPreviousBrowseLocation(QFileInfo(result).absolutePath());
} }
Q_ASSERT(engine);
return result.isEmpty() ? engine()->nullValue() : engine()->newValue(result); return result.isEmpty() ? engine()->nullValue() : engine()->newValue(result);
} }
@ -276,6 +280,7 @@ ScriptValue WindowScriptingInterface::browse(const QString& title, const QString
if (!result.isEmpty()) { if (!result.isEmpty()) {
setPreviousBrowseLocation(QFileInfo(result).absolutePath()); setPreviousBrowseLocation(QFileInfo(result).absolutePath());
} }
Q_ASSERT(engine);
return result.isEmpty() ? engine()->nullValue() : engine()->newValue(result); return result.isEmpty() ? engine()->nullValue() : engine()->newValue(result);
} }
@ -323,6 +328,7 @@ ScriptValue WindowScriptingInterface::save(const QString& title, const QString&
if (!result.isEmpty()) { if (!result.isEmpty()) {
setPreviousBrowseLocation(QFileInfo(result).absolutePath()); setPreviousBrowseLocation(QFileInfo(result).absolutePath());
} }
Q_ASSERT(engine);
return result.isEmpty() ? engine()->nullValue() : engine()->newValue(result); return result.isEmpty() ? engine()->nullValue() : engine()->newValue(result);
} }
@ -373,6 +379,7 @@ ScriptValue WindowScriptingInterface::browseAssets(const QString& title, const Q
if (!result.isEmpty()) { if (!result.isEmpty()) {
setPreviousBrowseAssetLocation(QFileInfo(result).absolutePath()); setPreviousBrowseAssetLocation(QFileInfo(result).absolutePath());
} }
Q_ASSERT(engine);
return result.isEmpty() ? engine()->nullValue() : engine()->newValue(result); return result.isEmpty() ? engine()->nullValue() : engine()->newValue(result);
} }

View file

@ -2557,6 +2557,7 @@ QDataStream& operator>>(QDataStream& in, AttachmentData& attachment) {
void AttachmentDataObject::setModelURL(const QString& modelURL) { void AttachmentDataObject::setModelURL(const QString& modelURL) {
AttachmentData data = scriptvalue_cast<AttachmentData>(thisObject()); AttachmentData data = scriptvalue_cast<AttachmentData>(thisObject());
data.modelURL = modelURL; data.modelURL = modelURL;
Q_ASSERT(engine);
thisObject() = engine()->toScriptValue(data); thisObject() = engine()->toScriptValue(data);
} }
@ -2567,6 +2568,7 @@ QString AttachmentDataObject::getModelURL() const {
void AttachmentDataObject::setJointName(const QString& jointName) { void AttachmentDataObject::setJointName(const QString& jointName) {
AttachmentData data = scriptvalue_cast<AttachmentData>(thisObject()); AttachmentData data = scriptvalue_cast<AttachmentData>(thisObject());
data.jointName = jointName; data.jointName = jointName;
Q_ASSERT(engine);
thisObject() = engine()->toScriptValue(data); thisObject() = engine()->toScriptValue(data);
} }
@ -2577,6 +2579,7 @@ QString AttachmentDataObject::getJointName() const {
void AttachmentDataObject::setTranslation(const glm::vec3& translation) { void AttachmentDataObject::setTranslation(const glm::vec3& translation) {
AttachmentData data = scriptvalue_cast<AttachmentData>(thisObject()); AttachmentData data = scriptvalue_cast<AttachmentData>(thisObject());
data.translation = translation; data.translation = translation;
Q_ASSERT(engine);
thisObject() = engine()->toScriptValue(data); thisObject() = engine()->toScriptValue(data);
} }
@ -2587,6 +2590,7 @@ glm::vec3 AttachmentDataObject::getTranslation() const {
void AttachmentDataObject::setRotation(const glm::quat& rotation) { void AttachmentDataObject::setRotation(const glm::quat& rotation) {
AttachmentData data = scriptvalue_cast<AttachmentData>(thisObject()); AttachmentData data = scriptvalue_cast<AttachmentData>(thisObject());
data.rotation = rotation; data.rotation = rotation;
Q_ASSERT(engine);
thisObject() = engine()->toScriptValue(data); thisObject() = engine()->toScriptValue(data);
} }
@ -2597,6 +2601,7 @@ glm::quat AttachmentDataObject::getRotation() const {
void AttachmentDataObject::setScale(float scale) { void AttachmentDataObject::setScale(float scale) {
AttachmentData data = scriptvalue_cast<AttachmentData>(thisObject()); AttachmentData data = scriptvalue_cast<AttachmentData>(thisObject());
data.scale = scale; data.scale = scale;
Q_ASSERT(engine);
thisObject() = engine()->toScriptValue(data); thisObject() = engine()->toScriptValue(data);
} }
@ -2607,6 +2612,7 @@ float AttachmentDataObject::getScale() const {
void AttachmentDataObject::setIsSoft(bool isSoft) { void AttachmentDataObject::setIsSoft(bool isSoft) {
AttachmentData data = scriptvalue_cast<AttachmentData>(thisObject()); AttachmentData data = scriptvalue_cast<AttachmentData>(thisObject());
data.isSoft = isSoft; data.isSoft = isSoft;
Q_ASSERT(engine);
thisObject() = engine()->toScriptValue(data); thisObject() = engine()->toScriptValue(data);
} }

View file

@ -73,6 +73,7 @@ void AssetScriptingInterface::uploadData(QString data, const ScriptValue& callba
auto upload = DependencyManager::get<AssetClient>()->createUpload(dataByteArray); auto upload = DependencyManager::get<AssetClient>()->createUpload(dataByteArray);
Promise deferred = makePromise(__FUNCTION__); Promise deferred = makePromise(__FUNCTION__);
Q_ASSERT(engine);
auto scriptEngine = engine(); auto scriptEngine = engine();
deferred->ready([=](QString error, QVariantMap result) { deferred->ready([=](QString error, QVariantMap result) {
auto url = result.value("url").toString(); 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 handler = jsBindCallback(thisObject(), callback);
auto setMappingRequest = assetClient()->createSetMappingRequest(path, hash); auto setMappingRequest = assetClient()->createSetMappingRequest(path, hash);
Promise deferred = makePromise(__FUNCTION__); Promise deferred = makePromise(__FUNCTION__);
Q_ASSERT(engine);
auto scriptEngine = engine(); auto scriptEngine = engine();
deferred->ready([=](QString error, QVariantMap result) { deferred->ready([=](QString error, QVariantMap result) {
jsCallback(handler, scriptEngine->newValue(error), result); jsCallback(handler, scriptEngine->newValue(error), result);
@ -133,6 +135,7 @@ void AssetScriptingInterface::downloadData(QString urlString, const ScriptValue&
auto assetRequest = assetClient->createRequest(hash); auto assetRequest = assetClient->createRequest(hash);
Promise deferred = makePromise(__FUNCTION__); Promise deferred = makePromise(__FUNCTION__);
Q_ASSERT(engine);
auto scriptEngine = engine(); auto scriptEngine = engine();
deferred->ready([=](QString error, QVariantMap result) { deferred->ready([=](QString error, QVariantMap result) {
// FIXME: to remain backwards-compatible the signature here is "callback(data, n/a)" // 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(AssetUtils::isValidFilePath(path), "invalid ATP file path: " + asset + "(path:"+path+")");
JS_VERIFY(callback.isFunction(), "expected second parameter to be a callback function"); JS_VERIFY(callback.isFunction(), "expected second parameter to be a callback function");
Promise promise = getAssetInfo(path); Promise promise = getAssetInfo(path);
Q_ASSERT(engine);
auto scriptEngine = engine(); auto scriptEngine = engine();
promise->ready([=](QString error, QVariantMap result) { promise->ready([=](QString error, QVariantMap result) {
jsCallback(handler, scriptEngine->newValue(error), scriptEngine->newValue(result.value("hash").toString())); 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")) { if (!jsVerify(handler.isValid(), "jsPromiseReady -- invalid callback handler")) {
return nullptr; return nullptr;
} }
Q_ASSERT(engine);
auto scriptEngine = engine(); auto scriptEngine = engine();
return promise->ready([this, handler, scriptEngine](QString error, QVariantMap result) { return promise->ready([this, handler, scriptEngine](QString error, QVariantMap result) {
jsCallback(handler, scriptEngine->newValue(error), result); jsCallback(handler, scriptEngine->newValue(error), result);
@ -238,6 +243,7 @@ Promise AssetScriptingInterface::jsPromiseReady(Promise promise, const ScriptVal
void AssetScriptingInterface::jsCallback(const ScriptValue& handler, void AssetScriptingInterface::jsCallback(const ScriptValue& handler,
const ScriptValue& error, const ScriptValue& result) { const ScriptValue& error, const ScriptValue& result) {
Q_ASSERT(thread() == QThread::currentThread()); Q_ASSERT(thread() == QThread::currentThread());
Q_ASSERT(engine);
auto errorValue = !error.toBool() ? engine()->nullValue() : error; auto errorValue = !error.toBool() ? engine()->nullValue() : error;
JS_VERIFY(handler.isObject() && handler.property("callback").isFunction(), JS_VERIFY(handler.isObject() && handler.property("callback").isFunction(),
QString("jsCallback -- .callback is not a function (%1)") 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) { bool AssetScriptingInterface::canWriteCacheValue(const QUrl& url) {
Q_ASSERT(engine);
auto scriptManager = engine()->manager(); auto scriptManager = engine()->manager();
if (!scriptManager) { if (!scriptManager) {
return false; return false;

View file

@ -80,12 +80,14 @@ ScriptValue ConsoleScriptingInterface::exception(ScriptContext* context, ScriptE
void ConsoleScriptingInterface::time(QString labelName) { void ConsoleScriptingInterface::time(QString labelName) {
_timerDetails.insert(labelName, QDateTime::currentDateTime().toUTC()); _timerDetails.insert(labelName, QDateTime::currentDateTime().toUTC());
QString message = QString("%1: Timer started").arg(labelName); QString message = QString("%1: Timer started").arg(labelName);
Q_ASSERT(engine);
if (ScriptManager* scriptManager = engine()->manager()) { if (ScriptManager* scriptManager = engine()->manager()) {
scriptManager->scriptPrintedMessage(message); scriptManager->scriptPrintedMessage(message);
} }
} }
void ConsoleScriptingInterface::timeEnd(QString labelName) { void ConsoleScriptingInterface::timeEnd(QString labelName) {
Q_ASSERT(engine);
if (ScriptManager* scriptManager = engine()->manager()) { if (ScriptManager* scriptManager = engine()->manager()) {
if (!_timerDetails.contains(labelName)) { if (!_timerDetails.contains(labelName)) {
scriptManager->scriptErrorMessage("No such label found " + labelName); scriptManager->scriptErrorMessage("No such label found " + labelName);
@ -134,6 +136,7 @@ ScriptValue ConsoleScriptingInterface::assertion(ScriptContext* context, ScriptE
} }
void ConsoleScriptingInterface::trace() { void ConsoleScriptingInterface::trace() {
Q_ASSERT(engine);
ScriptEnginePointer scriptEngine = engine(); ScriptEnginePointer scriptEngine = engine();
if (ScriptManager* scriptManager = scriptEngine->manager()) { if (ScriptManager* scriptManager = scriptEngine->manager()) {
scriptManager->scriptPrintedMessage scriptManager->scriptPrintedMessage
@ -143,6 +146,7 @@ void ConsoleScriptingInterface::trace() {
} }
void ConsoleScriptingInterface::clear() { void ConsoleScriptingInterface::clear() {
Q_ASSERT(engine);
if (ScriptManager* scriptManager = engine()->manager()) { if (ScriptManager* scriptManager = engine()->manager()) {
scriptManager->clearDebugLogWindow(); scriptManager->clearDebugLogWindow();
} }

View file

@ -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)); QString message = QString("%1 %2").arg(qPrintable(label));
message = message.arg(glm::to_string(out).c_str()); message = message.arg(glm::to_string(out).c_str());
qCDebug(scriptengine) << message; qCDebug(scriptengine) << message;
Q_ASSERT(engine);
if (ScriptManager* scriptManager = engine()->manager()) { if (ScriptManager* scriptManager = engine()->manager()) {
scriptManager->print(message); scriptManager->print(message);
} }

View file

@ -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()); message = message.arg(glm::to_string(glm::dquat(q)).c_str());
} }
qCDebug(scriptengine) << message; qCDebug(scriptengine) << message;
Q_ASSERT(engine);
if (ScriptManager* scriptManager = engine()->manager()) { if (ScriptManager* scriptManager = engine()->manager()) {
scriptManager->print(message); scriptManager->print(message);
} }

View file

@ -43,6 +43,7 @@ void ScriptUUID::print(const QString& label, const QUuid& id) {
QString message = QString("%1 %2").arg(qPrintable(label)); QString message = QString("%1 %2").arg(qPrintable(label));
message = message.arg(id.toString()); message = message.arg(id.toString());
qCDebug(scriptengine) << message; qCDebug(scriptengine) << message;
Q_ASSERT(engine);
if (ScriptManager* scriptManager = engine()->manager()) { if (ScriptManager* scriptManager = engine()->manager()) {
scriptManager->print(message); scriptManager->print(message);
} }

View file

@ -37,6 +37,7 @@ void Vec3::print(const QString& label, const glm::vec3& v) {
QString message = QString("%1 %2").arg(qPrintable(label)); QString message = QString("%1 %2").arg(qPrintable(label));
message = message.arg(glm::to_string(glm::dvec3(v)).c_str()); message = message.arg(glm::to_string(glm::dvec3(v)).c_str());
qCDebug(scriptengine) << message; qCDebug(scriptengine) << message;
Q_ASSERT(engine);
if (ScriptManager* scriptManager = engine()->manager()) { if (ScriptManager* scriptManager = engine()->manager()) {
scriptManager->print(message); scriptManager->print(message);
} }

View file

@ -427,7 +427,6 @@ ScriptEngineV8::ScriptEngineV8(ScriptManager* scriptManager) :
v8::Context::Scope contextScope(context); v8::Context::Scope contextScope(context);
_contexts.append(std::make_shared<ScriptContextV8Wrapper>(this,context, ScriptContextPointer())); _contexts.append(std::make_shared<ScriptContextV8Wrapper>(this,context, ScriptContextPointer()));
V8ScriptValue nullScriptValue(this, v8::Null(_v8Isolate)); V8ScriptValue nullScriptValue(this, v8::Null(_v8Isolate));
_nullValue = ScriptValue(new ScriptValueV8Wrapper(this, nullScriptValue)); _nullValue = ScriptValue(new ScriptValueV8Wrapper(this, nullScriptValue));
@ -802,6 +801,32 @@ const v8::Local<v8::Context> ScriptEngineV8::getConstContext() const {
Q_ASSERT(!_contexts.isEmpty()); Q_ASSERT(!_contexts.isEmpty());
return handleScope.Escape(_contexts.last().get()->toV8Value()); 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<v8::Object> 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, ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure,
const ScriptProgramPointer& _program) { const ScriptProgramPointer& _program) {
PROFILE_RANGE(script, "evaluateInClosure"); PROFILE_RANGE(script, "evaluateInClosure");
@ -812,6 +837,7 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure,
v8::Locker locker(_v8Isolate); v8::Locker locker(_v8Isolate);
v8::Isolate::Scope isolateScope(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate);
v8::HandleScope handleScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate);
storeGlobalObjectContents();
v8::Local<v8::Object> closureObject; v8::Local<v8::Object> closureObject;
//v8::Local<v8::Value> oldGlobal; //v8::Local<v8::Value> oldGlobal;
@ -896,6 +922,7 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure,
if (!unwrappedProgram->compile()) { if (!unwrappedProgram->compile()) {
qDebug(scriptengine) << "Can't compile script for evaluating in closure"; qDebug(scriptengine) << "Can't compile script for evaluating in closure";
Q_ASSERT(false); Q_ASSERT(false);
popContext();
return nullValue(); return nullValue();
} }
const V8ScriptProgram& program = unwrappedProgram->toV8Value(); const V8ScriptProgram& program = unwrappedProgram->toV8Value();
@ -928,18 +955,35 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure,
{ {
v8::TryCatch tryCatch(getIsolate()); 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 // 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(); auto globalObjectContents = _globalObjectContents.Get(_v8Isolate);
for (size_t i = 0; i < oldGlobalMemberNames->Length(); i++) { auto globalMemberNames = globalObjectContents->GetPropertyNames(globalObjectContents->CreationContext()).ToLocalChecked();
auto name = oldGlobalMemberNames->Get(closureContext, i).ToLocalChecked(); for (size_t i = 0; i < globalMemberNames->Length(); i++) {
if(!closureContext->Global()->Set(closureContext, name, oldContext->Global()->Get(oldContext, name).ToLocalChecked()).FromMaybe(false)) { auto name = globalMemberNames->Get(closureContext, i).ToLocalChecked();
if(!closureContext->Global()->Set(closureContext, name, globalObjectContents->Get(globalObjectContents->CreationContext(), name).ToLocalChecked()).FromMaybe(false)) {
Q_ASSERT(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 // 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(closureContext).ToLocalChecked();
//auto closureMemberNames = closureObject->GetPropertyNames(oldContext).ToLocalChecked();
for (size_t i = 0; i < closureMemberNames->Length(); i++) { for (size_t i = 0; i < closureMemberNames->Length(); i++) {
auto name = closureMemberNames->Get(closureContext, i).ToLocalChecked(); 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(closureContext, name).ToLocalChecked()).FromMaybe(false)) {
//if(!closureContext->Global()->Set(closureContext, name, closureObject->Get(oldContext, name).ToLocalChecked()).FromMaybe(false)) {
Q_ASSERT(false); Q_ASSERT(false);
} }
} }
@ -1455,6 +1499,7 @@ ScriptContext* ScriptEngineV8::currentContext() const {
// I'm not sure how to do this without discarding const // I'm not sure how to do this without discarding const
_currContext = std::make_shared<ScriptContextV8Wrapper>(const_cast<ScriptEngineV8*>(this)); _currContext = std::make_shared<ScriptContextV8Wrapper>(const_cast<ScriptEngineV8*>(this));
}*/ }*/
// V8TODO: add FunctionCallbackInfo or PropertyCallbackInfo when necessary
return _contexts.last().get(); return _contexts.last().get();
} }
@ -1501,7 +1546,7 @@ ScriptValue ScriptEngineV8::newFunction(ScriptEngine::FunctionSignature fun, int
ScriptEngineV8 *scriptEngine = reinterpret_cast<ScriptEngineV8*> ScriptEngineV8 *scriptEngine = reinterpret_cast<ScriptEngineV8*>
(object->GetAlignedPointerFromInternalField(1)); (object->GetAlignedPointerFromInternalField(1));
ScriptContextV8Wrapper scriptContext(scriptEngine, &info, scriptEngine->getContext(), scriptEngine->currentContext()->parentContext()); 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); ScriptValue result = function(&scriptContext, scriptEngine);
ScriptValueV8Wrapper* unwrapped = ScriptValueV8Wrapper::unwrap(result); ScriptValueV8Wrapper* unwrapped = ScriptValueV8Wrapper::unwrap(result);
if (unwrapped) { if (unwrapped) {

View file

@ -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<v8::Context> context); ScriptContextV8Pointer pushContext(v8::Local<v8::Context> context);
void popContext(); void popContext();
// V8TODO: call this after initializing global object
void storeGlobalObjectContents();
protected: protected:
// like `newFunction`, but allows mapping inline C++ lambdas with captures as callable V8ScriptValues // like `newFunction`, but allows mapping inline C++ lambdas with captures as callable V8ScriptValues
@ -240,6 +242,8 @@ protected:
//mutable ScriptContextV8Pointer _currContext; //mutable ScriptContextV8Pointer _currContext;
// Current context stack. Main context is first on the list and current one is last. // Current context stack. Main context is first on the list and current one is last.
QList<ScriptContextV8Pointer> _contexts; QList<ScriptContextV8Pointer> _contexts;
v8::Persistent<v8::Object> _globalObjectContents;
bool areGlobalObjectContentsStored {false};
//V8TODO //V8TODO
//ArrayBufferClass* _arrayBufferClass; //ArrayBufferClass* _arrayBufferClass;

View file

@ -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 // 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 // if we find it necessary to, but hopefully not needed
class ScriptPropertyContextQtWrapper final : public ScriptContext { class ScriptPropertyContextV8Wrapper final : public ScriptContext {
public: // construction public: // construction
inline ScriptPropertyContextQtWrapper(const ScriptValue& object, ScriptContext* parentContext) : inline ScriptPropertyContextV8Wrapper(const ScriptValue& object, ScriptContext* parentContext) :
_parent(parentContext), _object(object) {} _parent(parentContext), _object(object) {}
public: // ScriptContext implementation public: // ScriptContext implementation
@ -568,7 +568,7 @@ V8ScriptValue ScriptObjectV8Proxy::property(const V8ScriptValue& object, const V
QMetaProperty prop = metaObject->property(propId); QMetaProperty prop = metaObject->property(propId);
ScriptValue scriptThis = ScriptValue(new ScriptValueV8Wrapper(_engine, object)); ScriptValue scriptThis = ScriptValue(new ScriptValueV8Wrapper(_engine, object));
ScriptPropertyContextQtWrapper ourContext(scriptThis, _engine->currentContext()); ScriptPropertyContextV8Wrapper ourContext(scriptThis, _engine->currentContext());
ScriptContextGuard guard(&ourContext); ScriptContextGuard guard(&ourContext);
QVariant varValue = prop.read(qobject); QVariant varValue = prop.read(qobject);
@ -611,7 +611,7 @@ V8ScriptValue ScriptObjectV8Proxy::property(const V8ScriptValue& object, const V
ScriptEngine::QObjectWrapOptions options = ScriptEngine::ExcludeSuperClassContents | ScriptEngine::QObjectWrapOptions options = ScriptEngine::ExcludeSuperClassContents |
//V8TODO ScriptEngine::ExcludeDeleteLater | //V8TODO ScriptEngine::ExcludeDeleteLater |
ScriptEngine::PreferExistingWrapperObject; 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 ScriptObjectV8Proxy::newQObject(_engine, proxy, ScriptEngine::ScriptOwnership, options);
//return _engine->newQObject(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); QMetaProperty prop = metaObject->property(propId);
ScriptValue scriptThis = ScriptValue(new ScriptValueV8Wrapper(_engine, object)); ScriptValue scriptThis = ScriptValue(new ScriptValueV8Wrapper(_engine, object));
ScriptPropertyContextQtWrapper ourContext(scriptThis, _engine->currentContext()); ScriptPropertyContextV8Wrapper ourContext(scriptThis, _engine->currentContext());
ScriptContextGuard guard(&ourContext); ScriptContextGuard guard(&ourContext);
int propTypeId = prop.userType(); int propTypeId = prop.userType();
@ -883,8 +883,10 @@ void ScriptMethodV8Proxy::call(const v8::FunctionCallbackInfo<v8::Value>& argume
} }
if (isValidMetaSelected) { if (isValidMetaSelected) {
//ScriptContextV8Wrapper ourContext(_engine, context); // V8TODO: is this the correct wrapper?
//ScriptContextGuard guard(&ourContext); ScriptContextV8Wrapper ourContext(_engine, &arguments, _engine->getContext(),
_engine->currentContext()->parentContext());
ScriptContextGuard guard(&ourContext);
const QMetaMethod& meta = _metas[bestMeta]; const QMetaMethod& meta = _metas[bestMeta];
int returnTypeId = meta.returnType(); int returnTypeId = meta.returnType();
QVector <QGenericArgument> &qGenArgs = qGenArgsVectors[bestMeta]; QVector <QGenericArgument> &qGenArgs = qGenArgsVectors[bestMeta];
@ -1333,8 +1335,15 @@ void ScriptSignalV8Proxy::connect(ScriptValue arg0, ScriptValue arg1) {
v8::Local<v8::Function> destFunction = v8::Local<v8::Function>::Cast(callback.get()); v8::Local<v8::Function> destFunction = v8::Local<v8::Function>::Cast(callback.get());
v8::Local<v8::String> destDataName = v8::String::NewFromUtf8(isolate, "__data__").ToLocalChecked(); v8::Local<v8::String> destDataName = v8::String::NewFromUtf8(isolate, "__data__").ToLocalChecked();
v8::Local<v8::Value> destData; v8::Local<v8::Value> 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()); V8ScriptValue v8ThisObject = ScriptValueV8Wrapper::fullUnwrap(_engine, thisObject());
Q_ASSERT(ScriptObjectV8Proxy::unwrapProxy(v8ThisObject));
ScriptSignalV8Proxy* thisProxy = dynamic_cast<ScriptSignalV8Proxy*>(ScriptObjectV8Proxy::unwrapProxy(v8ThisObject)->toQObject());
Q_ASSERT(thisProxy);
qDebug(scriptengine) << "ScriptSignalV8Proxy::connect: " << thisProxy->fullName() << " fullName: " << fullName();
//Q_ASSERT(destFunction->InternalFieldCount() == 4); //Q_ASSERT(destFunction->InternalFieldCount() == 4);
//Q_ASSERT(destData.get()->IsArray()); //Q_ASSERT(destData.get()->IsArray());
//v8::Local<v8::Value> destData = destFunction->GetInternalField(3); //v8::Local<v8::Value> destData = destFunction->GetInternalField(3);
@ -1344,15 +1353,27 @@ void ScriptSignalV8Proxy::connect(ScriptValue arg0, ScriptValue arg1) {
if (destData->IsArray()) { if (destData->IsArray()) {
v8::Local<v8::Array> destArray = v8::Local<v8::Array>::Cast(destData); v8::Local<v8::Array> destArray = v8::Local<v8::Array>::Cast(destData);
int length = destArray->Length();//destData.property("length").toInteger(); int length = destArray->Length();//destData.property("length").toInteger();
// V8TODO: Maybe copying array is unnecessary?
v8::Local<v8::Array> newArray = v8::Array::New(isolate, length + 1); v8::Local<v8::Array> newArray = v8::Array::New(isolate, length + 1);
bool foundIt = false; bool foundIt = false;
for (int idx = 0; idx < length && !foundIt; ++idx) { for (int idx = 0; idx < length && !foundIt; ++idx) {
v8::Local<v8::Value> entry = destArray->Get(destFunctionContext, idx).ToLocalChecked(); v8::Local<v8::Value> 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<ScriptSignalV8Proxy*>(ScriptObjectV8Proxy::unwrapProxy(v8EntryObject)->toQObject());
Q_ASSERT(thisProxy);
qDebug(scriptengine) << "ScriptSignalV8Proxy::connect: entry proxy: " << entryProxy->fullName();
}
if (!newArray->Set(destFunctionContext, idx, entry).FromMaybe(false)) { if (!newArray->Set(destFunctionContext, idx, entry).FromMaybe(false)) {
Q_ASSERT(false); Q_ASSERT(false);
} }
} }
if (!newArray->Set(destFunctionContext, length, v8ThisObject.get()).FromMaybe(false)) { if (!newArray->Set(destFunctionContext, length, v8ThisObject.get()).FromMaybe(false)) {
//if (!newArray->Set(destFunctionContext, length, v8ThisObject.get()).FromMaybe(false)) {
Q_ASSERT(false); Q_ASSERT(false);
} }
if (!destFunction->Set(destFunctionContext, destDataName, newArray).FromMaybe(false)) { if (!destFunction->Set(destFunctionContext, destDataName, newArray).FromMaybe(false)) {
@ -1448,8 +1469,16 @@ void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) {
v8::Local<v8::Function> destFunction = v8::Local<v8::Function>::Cast(callback.get()); v8::Local<v8::Function> destFunction = v8::Local<v8::Function>::Cast(callback.get());
v8::Local<v8::String> destDataName = v8::String::NewFromUtf8(isolate, "__data__").ToLocalChecked(); v8::Local<v8::String> destDataName = v8::String::NewFromUtf8(isolate, "__data__").ToLocalChecked();
v8::Local<v8::Value> destData; v8::Local<v8::Value> destData;
auto destFunctionContext = destFunction->CreationContext();
//auto destFunctionContext = destFunction->CreationContext();
auto destFunctionContext = _engine->getContext();
Q_ASSERT(thisObject().isObject());
V8ScriptValue v8ThisObject = ScriptValueV8Wrapper::fullUnwrap(_engine, thisObject()); V8ScriptValue v8ThisObject = ScriptValueV8Wrapper::fullUnwrap(_engine, thisObject());
Q_ASSERT(ScriptObjectV8Proxy::unwrapProxy(v8ThisObject));
// For debugging
ScriptSignalV8Proxy* thisProxy = dynamic_cast<ScriptSignalV8Proxy*>(ScriptObjectV8Proxy::unwrapProxy(v8ThisObject)->toQObject());
Q_ASSERT(thisProxy);
qDebug(scriptengine) << "ScriptSignalV8Proxy::disconnect: " << thisProxy->fullName() << " fullName: " << fullName();
//V8ScriptValue destData = callback.data(); //V8ScriptValue destData = callback.data();
//Q_ASSERT(destData->IsArray()); //Q_ASSERT(destData->IsArray());
if (!destFunction->Get(destFunctionContext, destDataName).ToLocal(&destData)) { if (!destFunction->Get(destFunctionContext, destDataName).ToLocal(&destData)) {
@ -1463,8 +1492,22 @@ void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) {
int newIndex = 0; int newIndex = 0;
for (int idx = 0; idx < length && !foundIt; ++idx) { for (int idx = 0; idx < length && !foundIt; ++idx) {
v8::Local<v8::Value> entry = destArray->Get(destFunctionContext, idx).ToLocalChecked(); v8::Local<v8::Value> 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<ScriptSignalV8Proxy*>(ScriptObjectV8Proxy::unwrapProxy(v8EntryObject)->toQObject());
Q_ASSERT(thisProxy);
qDebug(scriptengine) << "ScriptSignalV8Proxy::disconnect: entry proxy: " << entryProxy->fullName();
}
if (entry->StrictEquals(v8ThisObject.get())) { if (entry->StrictEquals(v8ThisObject.get())) {
//V8TODO: compare proxies instead?
foundIt = true; foundIt = true;
qDebug() << "ScriptSignalV8Proxy::disconnect foundIt";
//V8ScriptValueList args; //V8ScriptValueList args;
//args << idx << 1; //args << idx << 1;
//destData.property("splice").call(destData, args); //destData.property("splice").call(destData, args);

View file

@ -235,12 +235,14 @@ private: // implementation
virtual int qt_metacall(QMetaObject::Call call, int id, void** arguments) override; virtual int qt_metacall(QMetaObject::Call call, int id, void** arguments) override;
int discoverMetaCallIdx(); int discoverMetaCallIdx();
ConnectionList::iterator findConnection(V8ScriptValue thisObject, V8ScriptValue callback); ConnectionList::iterator findConnection(V8ScriptValue thisObject, V8ScriptValue callback);
QString fullName() const; //QString fullName() const;
public: // API public: // API
// arg1 was had Null default value, but that needs isolate pointer to create Null in V8 // 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 connect(ScriptValue arg0, ScriptValue arg1 = ScriptValue()) override;
virtual void disconnect(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 connect(V8ScriptValue arg0) override;
//virtual void disconnect(V8ScriptValue arg0) override; //virtual void disconnect(V8ScriptValue arg0) override;

View file

@ -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 * settings = null; // optional best pratice; allows the object to be reclaimed ASAP by the JS garbage collector
*/ */
ScriptValue getScopedSettings(const QString& scope) { ScriptValue getScopedSettings(const QString& scope) {
Q_ASSERT(engine);
auto engine = Scriptable::engine(); auto engine = Scriptable::engine();
if (!engine) { if (!engine) {
return ScriptValue(); return ScriptValue();