From 044cd506a670dc32152c8208957f1a392460463d Mon Sep 17 00:00:00 2001 From: ksuprynowicz Date: Sat, 28 Jan 2023 18:29:03 +0100 Subject: [PATCH] Script engine thread safety improvements --- libraries/script-engine/src/ScriptEngine.h | 3 +- libraries/script-engine/src/ScriptManager.cpp | 1 + .../script-engine/src/v8/ArrayBufferClass.h | 2 +- .../src/v8/ScriptContextV8Wrapper.cpp | 79 +++++++--- .../src/v8/ScriptContextV8Wrapper.h | 5 +- .../script-engine/src/v8/ScriptEngineV8.cpp | 99 +++++++----- .../script-engine/src/v8/ScriptEngineV8.h | 38 ++--- .../src/v8/ScriptEngineV8_cast.cpp | 32 ++-- .../src/v8/ScriptObjectV8Proxy.cpp | 142 ++++++++++++------ .../src/v8/ScriptObjectV8Proxy.h | 8 +- .../src/v8/ScriptProgramV8Wrapper.cpp | 15 +- .../src/v8/ScriptProgramV8Wrapper.h | 3 +- .../src/v8/ScriptValueIteratorV8Wrapper.cpp | 2 +- .../src/v8/ScriptValueV8Wrapper.cpp | 15 +- .../src/v8/ScriptValueV8Wrapper.h | 1 + libraries/script-engine/src/v8/V8Lambda.h | 41 +++++ libraries/script-engine/src/v8/V8Types.h | 83 ++++++---- .../controllerModules/inEditMode.js | 2 +- .../controllerModules/inVREditMode.js | 2 +- .../controllerModules/stylusInput.js | 2 +- scripts/system/create/modules/createWindow.js | 3 +- 21 files changed, 374 insertions(+), 204 deletions(-) create mode 100644 libraries/script-engine/src/v8/V8Lambda.h diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 4fe16e0854..065337f0e7 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -78,7 +78,7 @@ public: virtual ScriptValue evaluate(const QString& program, const QString& fileName = QString()) = 0; virtual ScriptValue evaluate(const ScriptProgramPointer &program) = 0; virtual ScriptValue evaluateInClosure(const ScriptValue& locals, const ScriptProgramPointer& program) = 0; - virtual ScriptValue globalObject() const { + virtual ScriptValue globalObject() { Q_ASSERT(false); return ScriptValue(); } @@ -148,6 +148,7 @@ public: // not for public use, but I don't like how Qt strings this along with p virtual ScriptValue create(int type, const void* ptr) = 0; virtual QVariant convert(const ScriptValue& value, int type) = 0; virtual void registerCustomType(int type, MarshalFunction mf, DemarshalFunction df) = 0; + virtual QStringList getCurrentScriptURLs() const = 0; protected: ~ScriptEngine() {} // prevent explicit deletion of base class diff --git a/libraries/script-engine/src/ScriptManager.cpp b/libraries/script-engine/src/ScriptManager.cpp index 1dc87cc2f4..53d198269a 100644 --- a/libraries/script-engine/src/ScriptManager.cpp +++ b/libraries/script-engine/src/ScriptManager.cpp @@ -1139,6 +1139,7 @@ QUrl ScriptManager::resolvePath(const QString& include) const { do { auto contextInfo = context->functionContext(); parentURL = QUrl(contextInfo->fileName()); + qDebug(scriptengine) << "ScriptManager::resolvePath: URL get: " << parentURL << " backtrace: " << context->backtrace() << " " << _engine->getCurrentScriptURLs(); parentContext = context->parentContext(); context = parentContext.get(); } while (parentURL.isRelative() && context); diff --git a/libraries/script-engine/src/v8/ArrayBufferClass.h b/libraries/script-engine/src/v8/ArrayBufferClass.h index 4aff1e17f6..6f6ceb1001 100644 --- a/libraries/script-engine/src/v8/ArrayBufferClass.h +++ b/libraries/script-engine/src/v8/ArrayBufferClass.h @@ -20,7 +20,7 @@ #include "v8.h" #include -#include "V8Types.h" +//#include "V8Types.h" // V8TODO /* class ScriptEngineV8; diff --git a/libraries/script-engine/src/v8/ScriptContextV8Wrapper.cpp b/libraries/script-engine/src/v8/ScriptContextV8Wrapper.cpp index d02abbb308..1250b68fa8 100644 --- a/libraries/script-engine/src/v8/ScriptContextV8Wrapper.cpp +++ b/libraries/script-engine/src/v8/ScriptContextV8Wrapper.cpp @@ -57,6 +57,11 @@ int ScriptContextV8Wrapper::argumentCount() const { v8::Context::Scope contextScope(_engine->getContext());*/ //Q_ASSERT(_functionCallbackInfo);A // V8TODO + auto isolate = _engine->getIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + v8::Context::Scope contextScope(_context.Get(isolate)); if (_functionCallbackInfo) { return _functionCallbackInfo->Length(); } else if (_propertyCallbackInfo) { @@ -72,12 +77,13 @@ int ScriptContextV8Wrapper::argumentCount() const { ScriptValue ScriptContextV8Wrapper::argument(int index) const { if (_functionCallbackInfo) { auto isolate = _engine->getIsolate(); - Q_ASSERT(isolate->IsCurrent()); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); + v8::Context::Scope contextScope(_context.Get(isolate)); v8::Local result = (*_functionCallbackInfo)[index]; if (index < _functionCallbackInfo->kArgsLength) { - return ScriptValue(new ScriptValueV8Wrapper(_engine, V8ScriptValue(_engine->getIsolate(), result))); + return ScriptValue(new ScriptValueV8Wrapper(_engine, V8ScriptValue(_engine, result))); } else { return _engine->undefinedValue(); } @@ -90,9 +96,10 @@ ScriptValue ScriptContextV8Wrapper::argument(int index) const { QStringList ScriptContextV8Wrapper::backtrace() const { auto isolate = _engine->getIsolate(); - Q_ASSERT(isolate->IsCurrent()); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); + v8::Context::Scope contextScope(_context.Get(isolate)); v8::Local stackTrace = v8::StackTrace::CurrentStackTrace(isolate, 40); QStringList backTrace; //V8TODO nicer formatting @@ -122,32 +129,36 @@ ScriptEnginePointer ScriptContextV8Wrapper::engine() const { } ScriptFunctionContextPointer ScriptContextV8Wrapper::functionContext() const { - return std::make_shared(_engine); + auto isolate = _engine->getIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + v8::Context::Scope contextScope(_context.Get(isolate)); + auto scriptFunctionContextPointer = std::make_shared(_engine, _context.Get(_engine->getIsolate())); + return scriptFunctionContextPointer; } ScriptContextPointer ScriptContextV8Wrapper::parentContext() const { - //V8TODO - //Q_ASSERT(false); - //V8ScriptContext* result = _context->parentContext(); - //return result ? std::make_shared(_engine, result) : ScriptContextPointer(); return _parentContext; } ScriptValue ScriptContextV8Wrapper::thisObject() const { if (_functionCallbackInfo) { auto isolate = _engine->getIsolate(); - Q_ASSERT(isolate->IsCurrent()); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); + v8::Context::Scope contextScope(_context.Get(isolate)); v8::Local result = _functionCallbackInfo->This(); - return ScriptValue(new ScriptValueV8Wrapper(_engine, V8ScriptValue(_engine->getIsolate(), result))); + return ScriptValue(new ScriptValueV8Wrapper(_engine, V8ScriptValue(_engine, result))); } else if (_propertyCallbackInfo) { auto isolate = _engine->getIsolate(); - Q_ASSERT(isolate->IsCurrent()); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); + v8::Context::Scope contextScope(_context.Get(isolate)); v8::Local result = _propertyCallbackInfo->This(); - return ScriptValue(new ScriptValueV8Wrapper(_engine, V8ScriptValue(_engine->getIsolate(), result))); + return ScriptValue(new ScriptValueV8Wrapper(_engine, V8ScriptValue(_engine, result))); } else { return _engine->undefinedValue(); } @@ -157,9 +168,11 @@ ScriptValue ScriptContextV8Wrapper::throwError(const QString& text) { auto isolate = _engine->getIsolate(); // V8TODO: I have no idea how to do this yet because it happens on another thread if(isolate->IsCurrent()) { + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); - V8ScriptValue result(_engine->getIsolate(), + v8::Context::Scope contextScope(_context.Get(isolate)); + V8ScriptValue result(_engine, _engine->getIsolate()->ThrowError( v8::String::NewFromUtf8(_engine->getIsolate(), text.toStdString().c_str()).ToLocalChecked())); return ScriptValue(new ScriptValueV8Wrapper(_engine, std::move(result))); @@ -171,21 +184,34 @@ ScriptValue ScriptContextV8Wrapper::throwError(const QString& text) { ScriptValue ScriptContextV8Wrapper::throwValue(const ScriptValue& value) { auto isolate = _engine->getIsolate(); - Q_ASSERT(isolate->IsCurrent()); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); + v8::Context::Scope contextScope(_context.Get(isolate)); ScriptValueV8Wrapper* unwrapped = ScriptValueV8Wrapper::unwrap(value); if (!unwrapped) { return _engine->undefinedValue(); } - V8ScriptValue result(_engine->getIsolate(), _engine->getIsolate()->ThrowException(unwrapped->toV8Value().constGet())); + V8ScriptValue result(_engine, _engine->getIsolate()->ThrowException(unwrapped->toV8Value().constGet())); return ScriptValue(new ScriptValueV8Wrapper(_engine, std::move(result))); } +ScriptFunctionContextV8Wrapper::ScriptFunctionContextV8Wrapper(ScriptEngineV8* engine, const v8::Local context) : _engine(engine) { + v8::Locker locker(); + v8::Isolate::Scope isolateScope(engine->getIsolate()); + v8::HandleScope handleScope(engine->getIsolate()); + _context.Reset(engine->getIsolate(), context); +} QString ScriptFunctionContextV8Wrapper::fileName() const { //V8TODO: It's not exactly like in QtScript, because there's no such context object in V8, let's return the current one for now //Maybe fetch data on creation or store stack frame? + auto isolate = _engine->getIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + v8::Context::Scope contextScope(_context.Get(isolate)); + // V8TODO: does this work for context selected with contextScope? v8::Local name = v8::StackTrace::CurrentScriptNameOrSourceURL(_engine->getIsolate()); v8::String::Utf8Value nameUTF(_engine->getIsolate(), name); return QString(*nameUTF); @@ -194,6 +220,12 @@ QString ScriptFunctionContextV8Wrapper::fileName() const { QString ScriptFunctionContextV8Wrapper::functionName() const { //V8TODO: It's not exactly like in QtScript, because there's no such context object in V8, let's return the current one for now //Maybe fetch data on creation? + auto isolate = _engine->getIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + v8::Context::Scope contextScope(_context.Get(isolate)); + // V8TODO: does this work for context selected with contextScope? v8::Local stackTrace = v8::StackTrace::CurrentStackTrace(_engine->getIsolate(), 1); v8::Local stackFrame = stackTrace->GetFrame(_engine->getIsolate(), 0); v8::Local name = stackFrame->GetFunctionName(); @@ -210,6 +242,11 @@ ScriptFunctionContext::FunctionType ScriptFunctionContextV8Wrapper::functionType int ScriptFunctionContextV8Wrapper::lineNumber() const { //V8TODO: It's not exactly like in QtScript, because there's no such context object in V8, let's return the current one for now //Maybe fetch data on creation? + auto isolate = _engine->getIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + v8::Context::Scope contextScope(_context.Get(isolate)); v8::Local stackTrace = v8::StackTrace::CurrentStackTrace(_engine->getIsolate(), 1); v8::Local stackFrame = stackTrace->GetFrame(_engine->getIsolate(), 0); return stackFrame->GetLineNumber(); diff --git a/libraries/script-engine/src/v8/ScriptContextV8Wrapper.h b/libraries/script-engine/src/v8/ScriptContextV8Wrapper.h index 2d9abcf911..fdd1b00ce0 100644 --- a/libraries/script-engine/src/v8/ScriptContextV8Wrapper.h +++ b/libraries/script-engine/src/v8/ScriptContextV8Wrapper.h @@ -57,6 +57,7 @@ private: // storage const v8::FunctionCallbackInfo *_functionCallbackInfo; const v8::PropertyCallbackInfo *_propertyCallbackInfo; ScriptEngineV8* _engine; + // V8TODO: Is custom copy constructor needed for thread safety? v8::Persistent _context; ScriptContextPointer _parentContext; }; @@ -64,7 +65,7 @@ private: // storage class ScriptFunctionContextV8Wrapper final : public ScriptFunctionContext { public: // construction //V8TODO - inline ScriptFunctionContextV8Wrapper(ScriptEngineV8* engine) : _engine(engine) { } + ScriptFunctionContextV8Wrapper(ScriptEngineV8* engine, const v8::Local context); public: // ScriptFunctionContext implementation virtual QString fileName() const override; @@ -74,6 +75,8 @@ public: // ScriptFunctionContext implementation private: // storage ScriptEngineV8* _engine; + // V8TODO: Is custom copy constructor needed for thread safety? + v8::Persistent _context; //V8ScriptContextInfo _value; }; diff --git a/libraries/script-engine/src/v8/ScriptEngineV8.cpp b/libraries/script-engine/src/v8/ScriptEngineV8.cpp index 763ab33c57..3736768e47 100644 --- a/libraries/script-engine/src/v8/ScriptEngineV8.cpp +++ b/libraries/script-engine/src/v8/ScriptEngineV8.cpp @@ -49,6 +49,7 @@ #include "ScriptObjectV8Proxy.h" #include "ScriptProgramV8Wrapper.h" #include "ScriptValueV8Wrapper.h" +#include "V8Lambda.h" static const int MAX_DEBUG_VALUE_LENGTH { 80 }; @@ -72,9 +73,9 @@ bool ScriptEngineV8::IS_THREADSAFE_INVOCATION(const QThread* thread, const QStri // engine-aware JS Error copier and factory V8ScriptValue ScriptEngineV8::makeError(const V8ScriptValue& _other, const QString& type) { if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { - return V8ScriptValue(_v8Isolate, v8::Null(_v8Isolate)); + return V8ScriptValue(this, v8::Null(_v8Isolate)); } - return V8ScriptValue(_v8Isolate, v8::Null(_v8Isolate)); + return V8ScriptValue(this, v8::Null(_v8Isolate)); //V8TODO /* auto other = _other; @@ -293,7 +294,7 @@ bool ScriptEngineV8::maybeEmitUncaughtException(const QString& debugHint) { } // Lambda -ScriptValue ScriptEngineV8::newLambdaFunction(std::function operation, +/*ScriptValue ScriptEngineV8::newLambdaFunction(std::function operation, const V8ScriptValue& data, const ValueOwnership& ownership) { v8::HandleScope handleScope(_v8Isolate); @@ -305,7 +306,7 @@ ScriptValue ScriptEngineV8::newLambdaFunction(std::functioncallee().prototype() === Lambda QObject call.setData(ScriptValue(new ScriptValueV8Wrapper(this, data))); // context->callee().data() will === data param return call; -} +}*/ QString Lambda::toString() const { v8::HandleScope handleScope(_engine->getIsolate()); v8::Context::Scope contextScope(_engine->getContext()); @@ -338,7 +339,7 @@ Lambda::Lambda(ScriptEngineV8* engine, } V8ScriptValue Lambda::call() { if (!_engine->IS_THREADSAFE_INVOCATION(__FUNCTION__)) { - return V8ScriptValue(_engine->getIsolate(), v8::Null(_engine->getIsolate())); + return V8ScriptValue(_engine, v8::Null(_engine->getIsolate())); } // V8TODO: it needs to be done in entirely different way for V8 Q_ASSERT(false); @@ -411,10 +412,10 @@ ScriptEngineV8::ScriptEngineV8(ScriptManager* scriptManager) : _contexts.append(std::make_shared(this,context, ScriptContextPointer())); - V8ScriptValue nullScriptValue(_v8Isolate, v8::Null(_v8Isolate)); + V8ScriptValue nullScriptValue(this, v8::Null(_v8Isolate)); _nullValue = ScriptValue(new ScriptValueV8Wrapper(this, nullScriptValue)); - V8ScriptValue undefined(_v8Isolate, v8::Undefined(_v8Isolate)); + V8ScriptValue undefined(this, v8::Undefined(_v8Isolate)); _undefinedValue = ScriptValue(new ScriptValueV8Wrapper(this, undefined)); registerSystemTypes(); @@ -463,7 +464,7 @@ void ScriptEngineV8::registerEnum(const QString& enumName, QMetaEnum newEnum) { for (int i = 0; i < newEnum.keyCount(); i++) { const char* keyName = newEnum.key(i); QString fullName = enumName + "." + keyName; - registerValue(fullName, V8ScriptValue(_v8Isolate, v8::Integer::New(_v8Isolate, newEnum.keyToValue(keyName)))); + registerValue(fullName, V8ScriptValue(this, v8::Integer::New(_v8Isolate, newEnum.keyToValue(keyName)))); } } @@ -741,7 +742,7 @@ void ScriptEngineV8::registerGetterSetter(const QString& name, ScriptEngine::Fun v8::Local v8propertyName = v8::String::NewFromUtf8(_v8Isolate, name.toStdString().c_str()).ToLocalChecked(); v8::Local v8ObjectToSetProperty; - ScriptObjectV8Proxy *proxy = ScriptObjectV8Proxy::unwrapProxy(V8ScriptValue(_v8Isolate, v8ParentObject)); + ScriptObjectV8Proxy *proxy = ScriptObjectV8Proxy::unwrapProxy(V8ScriptValue(this, v8ParentObject)); // If object is ScriptObjectV8Proxy, then setting property needs to be handled differently if (proxy) { v8ObjectToSetProperty = v8ParentObject->GetInternalField(2).As(); @@ -833,13 +834,14 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure, Q_ASSERT(false); return nullValue(); } + Q_ASSERT(closure.constGet()->IsObject()); closureObject = v8::Local::Cast(closure.constGet()); qDebug() << "Closure object members:" << scriptValueDebugListMembersV8(closure); v8::Local testObject = v8::Object::New(_v8Isolate); if(!testObject->Set(getContext(), v8::String::NewFromUtf8(_v8Isolate, "test_value").ToLocalChecked(), closureObject).FromMaybe(false)) { Q_ASSERT(false); } - qDebug() << "Test object members:" << scriptValueDebugListMembersV8(V8ScriptValue(_v8Isolate, testObject)); + qDebug() << "Test object members:" << scriptValueDebugListMembersV8(V8ScriptValue(this, testObject)); if (!closureObject->Get(closure.constGetContext(), v8::String::NewFromUtf8(_v8Isolate, "global").ToLocalChecked()) .ToLocal(&closureGlobal)) { @@ -958,7 +960,7 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure, #endif result = err; } else { - result = ScriptValue(new ScriptValueV8Wrapper(this, V8ScriptValue(_v8Isolate, v8Result))); + result = ScriptValue(new ScriptValueV8Wrapper(this, V8ScriptValue(this, v8Result))); } } #ifdef DEBUG_JS @@ -1022,10 +1024,13 @@ ScriptValue ScriptEngineV8::evaluate(const QString& sourceCode, const QString& f errorLineNumber = exceptionMessage->GetLineNumber(getContext()).FromJust(); errorColumnNumber = exceptionMessage->GetStartColumn(getContext()).FromJust(); v8::Local backtraceV8String; - if (tryCatch.StackTrace(getContext()).ToLocal(&backtraceV8String) && backtraceV8String->IsString() && - v8::Local::Cast(backtraceV8String)->Length() > 0) { - v8::String::Utf8Value backtraceUtf8Value(getIsolate(), backtraceV8String); - errorBacktrace = *backtraceUtf8Value; + if (tryCatch.StackTrace(getContext()).ToLocal(&backtraceV8String)) { + if (backtraceV8String->IsString()) { + if (v8::Local::Cast(backtraceV8String)->Length() > 0) { + v8::String::Utf8Value backtraceUtf8Value(getIsolate(), backtraceV8String); + errorBacktrace = *backtraceUtf8Value; + } + } } qCDebug(scriptengine) << "Compiling script \"" << fileName << "\" failed on line " << errorLineNumber << " column " << errorColumnNumber << " with message: \"" << errorMessage <<"\" backtrace: " << errorBacktrace; } @@ -1061,7 +1066,7 @@ ScriptValue ScriptEngineV8::evaluate(const QString& sourceCode, const QString& f if (!script->Run(getContext()).ToLocal(&result)) { Q_ASSERT(tryCatchRun.HasCaught()); auto runError = tryCatchRun.Message(); - ScriptValue errorValue(new ScriptValueV8Wrapper(this, V8ScriptValue(_v8Isolate, runError->Get()))); + ScriptValue errorValue(new ScriptValueV8Wrapper(this, V8ScriptValue(this, runError->Get()))); qCDebug(scriptengine) << "Running script: \"" << fileName << "\" " << formatErrorMessageFromTryCatch(tryCatchRun); //V8TODO //raiseException(errorValue); @@ -1069,7 +1074,7 @@ ScriptValue ScriptEngineV8::evaluate(const QString& sourceCode, const QString& f _evaluatingCounter--; return errorValue; } - V8ScriptValue resultValue(_v8Isolate, result); + V8ScriptValue resultValue(this, result); _evaluatingCounter--; return ScriptValue(new ScriptValueV8Wrapper(this, std::move(resultValue))); } @@ -1092,10 +1097,13 @@ QString ScriptEngineV8::formatErrorMessageFromTryCatch(v8::TryCatch &tryCatch) { errorLineNumber = exceptionMessage->GetLineNumber(getContext()).FromJust(); errorColumnNumber = exceptionMessage->GetStartColumn(getContext()).FromJust(); v8::Local backtraceV8String; - if (tryCatch.StackTrace(getContext()).ToLocal(&backtraceV8String) && backtraceV8String->IsString() && - v8::Local::Cast(backtraceV8String)->Length() > 0) { - v8::String::Utf8Value backtraceUtf8Value(getIsolate(), backtraceV8String); - errorBacktrace = *backtraceUtf8Value; + if (tryCatch.StackTrace(getContext()).ToLocal(&backtraceV8String)) { + if (backtraceV8String->IsString()) { + if (v8::Local::Cast(backtraceV8String)->Length() > 0) { + v8::String::Utf8Value backtraceUtf8Value(getIsolate(), backtraceV8String); + errorBacktrace = *backtraceUtf8Value; + } + } } QTextStream resultStream(&result); resultStream << "failed on line " << errorLineNumber << " column " << errorColumnNumber << " with message: \"" << errorMessage <<"\" backtrace: " << errorBacktrace; @@ -1185,7 +1193,7 @@ Q_INVOKABLE ScriptValue ScriptEngineV8::evaluate(const ScriptProgramPointer& pro if (!v8Program.constGet()->Run(getContext()).ToLocal(&result)) { Q_ASSERT(tryCatchRun.HasCaught()); auto runError = tryCatchRun.Message(); - errorValue = ScriptValue(new ScriptValueV8Wrapper(this, V8ScriptValue(_v8Isolate, runError->Get()))); + errorValue = ScriptValue(new ScriptValueV8Wrapper(this, V8ScriptValue(this, runError->Get()))); raiseException(errorValue); maybeEmitUncaughtException("evaluate"); hasFailed = true; @@ -1195,7 +1203,7 @@ Q_INVOKABLE ScriptValue ScriptEngineV8::evaluate(const ScriptProgramPointer& pro } } if(!hasFailed) { - V8ScriptValue resultValueV8(_v8Isolate, result); + V8ScriptValue resultValueV8(this, result); resultValue = ScriptValue(new ScriptValueV8Wrapper(this, std::move(resultValueV8))); } } @@ -1225,12 +1233,12 @@ void ScriptEngineV8::updateMemoryCost(const qint64& deltaSize) { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ScriptEngine implementation -ScriptValue ScriptEngineV8::globalObject() const { +ScriptValue ScriptEngineV8::globalObject() { v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); v8::Context::Scope contextScope(getConstContext()); - V8ScriptValue global(_v8Isolate, getConstContext()->Global());// = QScriptEngine::globalObject(); // can't cache the value as it may change + V8ScriptValue global(this, getConstContext()->Global());// = QScriptEngine::globalObject(); // can't cache the value as it may change return ScriptValue(new ScriptValueV8Wrapper(const_cast(this), std::move(global))); } @@ -1243,7 +1251,7 @@ ScriptValue ScriptEngineV8::newArray(uint length) { v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); v8::Context::Scope contextScope(getContext()); - V8ScriptValue result(_v8Isolate, v8::Array::New(_v8Isolate, static_cast(length))); + V8ScriptValue result(this, v8::Array::New(_v8Isolate, static_cast(length))); return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result))); } @@ -1262,7 +1270,7 @@ ScriptValue ScriptEngineV8::newArrayBuffer(const QByteArray& message) { if (!array) { return undefinedValue(); }*/ - V8ScriptValue result(_v8Isolate, arrayBuffer);//QScriptEngine::newObject(array, data); + V8ScriptValue result(this, arrayBuffer);//QScriptEngine::newObject(array, data); return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result))); } @@ -1280,7 +1288,7 @@ ScriptValue ScriptEngineV8::newObject() { v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); v8::Context::Scope contextScope(getContext()); - V8ScriptValue resultV8 = V8ScriptValue(_v8Isolate, v8::Object::New(_v8Isolate)); + V8ScriptValue resultV8 = V8ScriptValue(this, v8::Object::New(_v8Isolate)); result = ScriptValue(new ScriptValueV8Wrapper(this, std::move(resultV8))); } /*if (is_isolate_exit_needed) { @@ -1328,7 +1336,7 @@ ScriptValue ScriptEngineV8::newValue(bool value) { v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); v8::Context::Scope contextScope(getContext()); - V8ScriptValue result(_v8Isolate, v8::Boolean::New(_v8Isolate, value)); + V8ScriptValue result(this, v8::Boolean::New(_v8Isolate, value)); return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result))); } @@ -1337,7 +1345,7 @@ ScriptValue ScriptEngineV8::newValue(int value) { v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); v8::Context::Scope contextScope(getContext()); - V8ScriptValue result(_v8Isolate, v8::Integer::New(_v8Isolate, value)); + V8ScriptValue result(this, v8::Integer::New(_v8Isolate, value)); return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result))); } @@ -1346,7 +1354,7 @@ ScriptValue ScriptEngineV8::newValue(uint value) { v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); v8::Context::Scope contextScope(getContext()); - V8ScriptValue result(_v8Isolate, v8::Uint32::New(_v8Isolate, value)); + V8ScriptValue result(this, v8::Uint32::New(_v8Isolate, value)); return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result))); } @@ -1355,7 +1363,7 @@ ScriptValue ScriptEngineV8::newValue(double value) { v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); v8::Context::Scope contextScope(getContext()); - V8ScriptValue result(_v8Isolate, v8::Number::New(_v8Isolate, value)); + V8ScriptValue result(this, v8::Number::New(_v8Isolate, value)); return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result))); } @@ -1365,7 +1373,7 @@ ScriptValue ScriptEngineV8::newValue(const QString& value) { v8::HandleScope handleScope(_v8Isolate); v8::Context::Scope contextScope(getContext()); v8::Local valueV8 = v8::String::NewFromUtf8(_v8Isolate, value.toStdString().c_str(), v8::NewStringType::kNormal).ToLocalChecked(); - V8ScriptValue result(_v8Isolate, valueV8); + V8ScriptValue result(this, valueV8); return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result))); } @@ -1375,7 +1383,7 @@ ScriptValue ScriptEngineV8::newValue(const QLatin1String& value) { v8::HandleScope handleScope(_v8Isolate); v8::Context::Scope contextScope(getContext()); v8::Local valueV8 = v8::String::NewFromUtf8(_v8Isolate, value.latin1(), v8::NewStringType::kNormal).ToLocalChecked(); - V8ScriptValue result(_v8Isolate, valueV8); + V8ScriptValue result(this, valueV8); return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result))); } @@ -1385,7 +1393,7 @@ ScriptValue ScriptEngineV8::newValue(const char* value) { v8::HandleScope handleScope(_v8Isolate); v8::Context::Scope contextScope(getContext()); v8::Local valueV8 = v8::String::NewFromUtf8(_v8Isolate, value, v8::NewStringType::kNormal).ToLocalChecked(); - V8ScriptValue result(_v8Isolate, valueV8); + V8ScriptValue result(this, valueV8); return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result))); } @@ -1463,6 +1471,7 @@ ScriptValue ScriptEngineV8::newFunction(ScriptEngine::FunctionSignature fun, int v8::HandleScope handleScope(info.GetIsolate()); auto context = info.GetIsolate()->GetCurrentContext(); v8::Context::Scope contextScope(context); + Q_ASSERT(info.Data()->IsObject()); auto object = v8::Local::Cast(info.Data()); Q_ASSERT(object->InternalFieldCount() == 2); auto function = reinterpret_cast @@ -1493,7 +1502,7 @@ ScriptValue ScriptEngineV8::newFunction(ScriptEngine::FunctionSignature fun, int auto v8Function = v8::Function::New(getContext(), v8FunctionCallback, functionData, length).ToLocalChecked(); //auto functionObjectTemplate = functionTemplate->InstanceTemplate(); //auto function = - V8ScriptValue result(_v8Isolate, v8Function); // = QScriptEngine::newFunction(innerFunc, length); + V8ScriptValue result(this, v8Function); // = QScriptEngine::newFunction(innerFunc, length); //auto funAddr = QScriptEngine::newVariant(QVariant(reinterpret_cast(fun))); // V8TODO //result.setProperty("_func", funAddr, V8ScriptValue::PropertyFlags(V8ScriptValue::ReadOnly + V8ScriptValue::Undeletable + V8ScriptValue::SkipInEnumeration)); @@ -1685,4 +1694,20 @@ QString ScriptEngineV8::scriptValueDebugDetailsV8(const V8ScriptValue &v8Value) v8::Local stackFrame = stackTrace->GetFrame(_v8Isolate, n); } -}*/ \ No newline at end of file +}*/ + +QStringList ScriptEngineV8::getCurrentScriptURLs() const { + auto isolate = _v8Isolate; + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + v8::Context::Scope contextScope(_v8Isolate->GetCurrentContext()); + v8::Local stackTrace = v8::StackTrace::CurrentStackTrace(isolate, 100); + QStringList scriptURLs; + //V8TODO nicer formatting + for (int i = 0; i < stackTrace->GetFrameCount(); i++) { + v8::Local stackFrame = stackTrace->GetFrame(isolate, i); + scriptURLs.append(QString(*v8::String::Utf8Value(isolate, stackFrame->GetScriptNameOrSourceURL()))); + } + return scriptURLs; +} diff --git a/libraries/script-engine/src/v8/ScriptEngineV8.h b/libraries/script-engine/src/v8/ScriptEngineV8.h index 660af501bf..b617844cc2 100644 --- a/libraries/script-engine/src/v8/ScriptEngineV8.h +++ b/libraries/script-engine/src/v8/ScriptEngineV8.h @@ -34,7 +34,7 @@ #include "../ScriptEngine.h" #include "../ScriptManager.h" -#include "V8Types.h" +//#include "V8Types.h" #include "ArrayBufferClass.h" @@ -43,6 +43,11 @@ class ScriptEngineV8; class ScriptManager; class ScriptObjectV8Proxy; class ScriptMethodV8Proxy; + +template class V8ScriptValueTemplate; +typedef V8ScriptValueTemplate V8ScriptValue; +typedef V8ScriptValueTemplate V8ScriptProgram; + using ScriptContextV8Pointer = std::shared_ptr; const double GARBAGE_COLLECTION_TIME_LIMIT_S = 1.0; @@ -69,7 +74,7 @@ public: // ScriptEngine implementation Q_INVOKABLE virtual ScriptValue evaluate(const QString& program, const QString& fileName = QString()) override; Q_INVOKABLE virtual ScriptValue evaluate(const ScriptProgramPointer& program) override; Q_INVOKABLE virtual ScriptValue evaluateInClosure(const ScriptValue& locals, const ScriptProgramPointer& program) override; - virtual ScriptValue globalObject() const override; + virtual ScriptValue globalObject() override; virtual bool hasUncaughtException() const override; virtual bool isEvaluating() const override; //virtual ScriptValue lintScript(const QString& sourceCode, const QString& fileName, const int lineNumber = 1) override; @@ -185,6 +190,7 @@ public: // not for public use, but I don't like how Qt strings this along with p QString formatErrorMessageFromTryCatch(v8::TryCatch &tryCatch); // Useful for debugging //QStringList getCurrentStackTrace(); + virtual QStringList getCurrentScriptURLs() const override; using ObjectWrapperMap = QMap>; mutable QMutex _qobjectWrapperMapProtect; @@ -193,14 +199,17 @@ public: // not for public use, but I don't like how Qt strings this along with p // V8TODO add a V8 callback that removes pointer from the map so that it gets deleted QMap> _qobjectWrapperMapV8; + ScriptContextV8Pointer pushContext(v8::Local &context); + void popContext(); + protected: // like `newFunction`, but allows mapping inline C++ lambdas with captures as callable V8ScriptValues // even though the context/engine parameters are redundant in most cases, the function signature matches `newFunction` // anyway so that newLambdaFunction can be used to rapidly prototype / test utility APIs and then if becoming // permanent more easily promoted into regular static newFunction scenarios. - ScriptValue newLambdaFunction(std::function operation, + /*ScriptValue newLambdaFunction(std::function operation, const V8ScriptValue& data, - const ValueOwnership& ownership = AutoOwnership); + const ValueOwnership& ownership = AutoOwnership);*/ void registerSystemTypes(); @@ -208,8 +217,6 @@ protected: static QMutex _v8InitMutex; static std::once_flag _v8InitOnceFlag; static v8::Platform* getV8Platform(); - ScriptContextV8Pointer pushContext(v8::Local &context); - void popContext(); // V8TODO: clean up isolate when script engine is destroyed? v8::Isolate* _v8Isolate; @@ -239,24 +246,7 @@ protected: int _evaluatingCounter; }; -// Lambda helps create callable V8ScriptValues out of std::functions: -// (just meant for use from within the script engine itself) -class Lambda : public QObject { - Q_OBJECT -public: - Lambda(ScriptEngineV8* engine, - std::function operation, - V8ScriptValue data); - ~Lambda(); -public slots: - V8ScriptValue call(); - QString toString() const; - -private: - ScriptEngineV8* _engine; - std::function _operation; - V8ScriptValue _data; -}; +#include "V8Types.h" #endif // hifi_ScriptEngineV8_h diff --git a/libraries/script-engine/src/v8/ScriptEngineV8_cast.cpp b/libraries/script-engine/src/v8/ScriptEngineV8_cast.cpp index 4caa5b8c9f..1552d17c03 100644 --- a/libraries/script-engine/src/v8/ScriptEngineV8_cast.cpp +++ b/libraries/script-engine/src/v8/ScriptEngineV8_cast.cpp @@ -573,7 +573,7 @@ bool ScriptEngineV8::convertJSArrayToVariant(v8::Local array, QVarian } QVariant property; // Maybe QMetaType::QVariant? - if (castValueToVariant(V8ScriptValue(_v8Isolate, v8Property), property, QMetaType::UnknownType)) { + if (castValueToVariant(V8ScriptValue(this, v8Property), property, QMetaType::UnknownType)) { properties.append(property); } else { qDebug() << "ScriptEngineV8::convertJSArrayToVariant could cast property to variant: " + QString(i); @@ -603,7 +603,7 @@ bool ScriptEngineV8::convertJSObjectToVariant(v8::Local object, QVar } QVariant property; // Maybe QMetaType::QVariant? - if (castValueToVariant(V8ScriptValue(_v8Isolate, v8Property), property, QMetaType::UnknownType)) { + if (castValueToVariant(V8ScriptValue(this, v8Property), property, QMetaType::UnknownType)) { properties.insert( name, property); } else { qDebug() << "ScriptEngineV8::convertJSObjectToVariant could cast property to variant: " + name; @@ -696,51 +696,51 @@ V8ScriptValue ScriptEngineV8::castVariantToValue(const QVariant& val) { switch (valTypeId) { case QMetaType::UnknownType: case QMetaType::Void: - return V8ScriptValue(_v8Isolate, v8::Undefined(_v8Isolate)); + return V8ScriptValue(this, v8::Undefined(_v8Isolate)); case QMetaType::Nullptr: - return V8ScriptValue(_v8Isolate, v8::Null(_v8Isolate)); + return V8ScriptValue(this, v8::Null(_v8Isolate)); case QMetaType::Bool: - return V8ScriptValue(_v8Isolate, v8::Boolean::New(_v8Isolate, val.toBool())); + return V8ScriptValue(this, v8::Boolean::New(_v8Isolate, val.toBool())); case QMetaType::Int: case QMetaType::Long: case QMetaType::Short: - return V8ScriptValue(_v8Isolate, v8::Integer::New(_v8Isolate, val.toInt())); + return V8ScriptValue(this, v8::Integer::New(_v8Isolate, val.toInt())); case QMetaType::UInt: case QMetaType::UShort: case QMetaType::ULong: - return V8ScriptValue(_v8Isolate, v8::Uint32::New(_v8Isolate, val.toUInt())); + return V8ScriptValue(this, v8::Uint32::New(_v8Isolate, val.toUInt())); case QMetaType::ULongLong: - return V8ScriptValue(_v8Isolate, v8::Number::New(_v8Isolate, val.toULongLong())); + return V8ScriptValue(this, v8::Number::New(_v8Isolate, val.toULongLong())); case QMetaType::LongLong: - return V8ScriptValue(_v8Isolate, v8::Number::New(_v8Isolate, val.toLongLong())); + return V8ScriptValue(this, v8::Number::New(_v8Isolate, val.toLongLong())); case QMetaType::Float: case QMetaType::Double: - return V8ScriptValue(_v8Isolate, v8::Number::New(_v8Isolate, val.toDouble())); + return V8ScriptValue(this, v8::Number::New(_v8Isolate, val.toDouble())); case QMetaType::QString: case QMetaType::QByteArray: - return V8ScriptValue(_v8Isolate, v8::String::NewFromUtf8(_v8Isolate, val.toString().toStdString().c_str()).ToLocalChecked()); + return V8ScriptValue(this, v8::String::NewFromUtf8(_v8Isolate, val.toString().toStdString().c_str()).ToLocalChecked()); case QMetaType::QVariant: return castVariantToValue(val.value()); case QMetaType::QObjectStar: { QObject* obj = val.value(); - if (obj == nullptr) return V8ScriptValue(_v8Isolate, v8::Null(_v8Isolate)); + if (obj == nullptr) return V8ScriptValue(this, v8::Null(_v8Isolate)); return ScriptObjectV8Proxy::newQObject(this, obj); } case QMetaType::QDateTime: { double timeMs = val.value().currentMSecsSinceEpoch(); - return V8ScriptValue(_v8Isolate, v8::Date::New(getContext(), timeMs).ToLocalChecked()); + return V8ScriptValue(this, v8::Date::New(getContext(), timeMs).ToLocalChecked()); } case QMetaType::QDate: { double timeMs = val.value().startOfDay().currentMSecsSinceEpoch(); - return V8ScriptValue(_v8Isolate, v8::Date::New(getContext(), timeMs).ToLocalChecked()); + return V8ScriptValue(this, v8::Date::New(getContext(), timeMs).ToLocalChecked()); } default: // check to see if this is a pointer to a QObject-derived object if (QMetaType::typeFlags(valTypeId) & (QMetaType::PointerToQObject | QMetaType::TrackingPointerToQObject)) { QObject* obj = val.value(); - if (obj == nullptr) return V8ScriptValue(_v8Isolate, v8::Null(_v8Isolate)); + if (obj == nullptr) return V8ScriptValue(this, v8::Null(_v8Isolate)); return ScriptObjectV8Proxy::newQObject(this, obj); } // have we set a prototype'd variant? @@ -755,7 +755,7 @@ V8ScriptValue ScriptEngineV8::castVariantToValue(const QVariant& val) { // just do a generic variant //V8TODO Q_ASSERT(false); - return V8ScriptValue(_v8Isolate, v8::Undefined(_v8Isolate)); + return V8ScriptValue(this, v8::Undefined(_v8Isolate)); //return QScriptEngine::newVariant(val); } } diff --git a/libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp b/libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp index 0a17345fad..7f88e6dd75 100644 --- a/libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp +++ b/libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp @@ -76,7 +76,7 @@ V8ScriptValue ScriptObjectV8Proxy::newQObject(ScriptEngineV8* engine, QObject* o QSharedPointer proxy = lookup.value().lock(); // V8TODO: is conversion to QVariant and back needed? if (proxy) { - return V8ScriptValue(engine->getIsolate(), proxy.get()->toV8Value()); + return V8ScriptValue(engine, proxy.get()->toV8Value()); } //if (proxy) return engine->newObject(proxy.get(), engine->newVariant(QVariant::fromValue(proxy)));; } @@ -111,7 +111,7 @@ V8ScriptValue ScriptObjectV8Proxy::newQObject(ScriptEngineV8* engine, QObject* o QSharedPointer proxy = lookup.value().lock(); //if (proxy) return qengine->newObject(proxy.get(), qengine->newVariant(QVariant::fromValue(proxy)));; if (proxy) { - return V8ScriptValue(engine->getIsolate(), proxy.get()->toV8Value()); + return V8ScriptValue(engine, proxy.get()->toV8Value()); } } // V8TODO add a V8 callback that removes pointer for the script engine owned ob from the map so that it gets deleted @@ -133,7 +133,7 @@ V8ScriptValue ScriptObjectV8Proxy::newQObject(ScriptEngineV8* engine, QObject* o }); } - return V8ScriptValue(engine->getIsolate(), proxy.get()->toV8Value()); + return V8ScriptValue(engine, proxy.get()->toV8Value()); //return qengine->newObject(proxy.get(), qengine->newVariant(QVariant::fromValue(proxy))); } @@ -157,6 +157,25 @@ ScriptObjectV8Proxy* ScriptObjectV8Proxy::unwrapProxy(const V8ScriptValue& val) return reinterpret_cast(v8Object->GetAlignedPointerFromInternalField(1)); } +ScriptObjectV8Proxy* ScriptObjectV8Proxy::unwrapProxy(v8::Isolate* isolate, v8::Local &value) { + //V8TODO This shouldn't cause problems but I'm not sure if it's ok + v8::HandleScope handleScope(isolate); + if (!value->IsObject()) { + //qDebug(scriptengine) << "Cannot unwrap proxy - value is not an object"; + return nullptr; + } + v8::Local v8Object = v8::Local::Cast(value); + if (v8Object->InternalFieldCount() != 3) { + //qDebug(scriptengine) << "Cannot unwrap proxy - wrong number of internal fields"; + return nullptr; + } + if (v8Object->GetAlignedPointerFromInternalField(0) != internalPointsToQObjectProxy) { + qDebug(scriptengine) << "Cannot unwrap proxy - internal fields don't point to object proxy"; + return nullptr; + } + return reinterpret_cast(v8Object->GetAlignedPointerFromInternalField(1)); +} + QObject* ScriptObjectV8Proxy::unwrap(const V8ScriptValue& val) { ScriptObjectV8Proxy* proxy = unwrapProxy(val); return proxy ? proxy->toQObject() : nullptr; @@ -209,7 +228,7 @@ void ScriptObjectV8Proxy::investigate() { } auto v8Name = v8::String::NewFromUtf8(_engine->getIsolate(), prop.name()).ToLocalChecked(); - PropertyDef& propDef = _props.insert(idx, PropertyDef(_engine->getIsolate(), v8Name)).value(); + PropertyDef& propDef = _props.insert(idx, PropertyDef(_engine, v8Name)).value(); propDef.flags = ScriptValue::Undeletable | ScriptValue::PropertyGetter | ScriptValue::PropertySetter | ScriptValue::QObjectMember; if (prop.isConstant()) propDef.flags |= ScriptValue::ReadOnly; @@ -248,11 +267,11 @@ void ScriptObjectV8Proxy::investigate() { } auto nameString = v8::String::NewFromUtf8(_engine->getIsolate(), szName.data(), v8::NewStringType::kNormal, szName.length()).ToLocalChecked(); - V8ScriptString name(_engine->getIsolate(), nameString); + V8ScriptString name(_engine, nameString); auto nameLookup = methodNames.find(name); if (isSignal) { if (nameLookup == methodNames.end()) { - SignalDef& signalDef = _signals.insert(idx, SignalDef(_engine->getIsolate(), name.get())).value(); + SignalDef& signalDef = _signals.insert(idx, SignalDef(_engine, name.get())).value(); signalDef.name = name; signalDef.signal = method; //qDebug(scriptengine) << "Utf8Value 1: " << QString(*v8::String::Utf8Value(const_cast(_engine->getIsolate()), nameString)); @@ -280,7 +299,7 @@ void ScriptObjectV8Proxy::investigate() { } } if (nameLookup == methodNames.end()) { - MethodDef& methodDef = _methods.insert(idx, MethodDef(_engine->getIsolate(), name.get())).value(); + MethodDef& methodDef = _methods.insert(idx, MethodDef(_engine, name.get())).value(); methodDef.name = name; methodDef.numMaxParms = parameterCount; methodDef.methods.append(method); @@ -308,7 +327,8 @@ void ScriptObjectV8Proxy::investigate() { // Add all the methods objects as properties - this allows adding properties to a given method later. Is used by Script.request. // V8TODO: Should these be deleted when the script-owned object is destroyed? It needs checking if script-owned objects will be garbage-collected, or will self-referencing prevent it. for (auto i = _methods.begin(); i != _methods.end(); i++) { - V8ScriptValue method = ScriptMethodV8Proxy::newMethod(_engine, qobject, V8ScriptValue(_engine->getIsolate(), v8Object), i.value().methods, i.value().numMaxParms); + V8ScriptValue method = ScriptMethodV8Proxy::newMethod(_engine, qobject, V8ScriptValue(_engine, v8Object), + i.value().methods, i.value().numMaxParms); if(!propertiesObject->Set(_engine->getContext(), i.value().name.constGet(), method.get()).FromMaybe(false)) { Q_ASSERT(false); } @@ -389,13 +409,15 @@ void ScriptObjectV8Proxy::v8Get(v8::Local name, const v8::PropertyCall v8::HandleScope handleScope(info.GetIsolate()); v8::String::Utf8Value utf8Value(info.GetIsolate(), name); //qDebug(scriptengine) << "Get: " << *utf8Value; - V8ScriptValue object(info.GetIsolate(), info.This()); - ScriptObjectV8Proxy *proxy = ScriptObjectV8Proxy::unwrapProxy(object); + v8::Local objectV8 = info.This(); + ScriptObjectV8Proxy *proxy = ScriptObjectV8Proxy::unwrapProxy(info.GetIsolate(), objectV8); if (!proxy) { qDebug(scriptengine) << "Proxy object not found when getting: " << *utf8Value; return; } - V8ScriptString nameString(info.GetIsolate(), v8::Local::Cast(name)); + V8ScriptValue object(proxy->_engine, objectV8); + Q_ASSERT(name->IsString()); + V8ScriptString nameString(proxy->_engine, v8::Local::Cast(name)); uint id; QueryFlags flags = proxy->queryProperty(object, nameString, HandlesReadAccess, &id); if (flags) { @@ -415,18 +437,20 @@ void ScriptObjectV8Proxy::v8Set(v8::Local name, v8::Local v v8::HandleScope handleScope(info.GetIsolate()); v8::String::Utf8Value utf8Value(info.GetIsolate(), name); //qDebug(scriptengine) << "Set: " << *utf8Value; - V8ScriptValue object(info.GetIsolate(), info.This()); - ScriptObjectV8Proxy *proxy = ScriptObjectV8Proxy::unwrapProxy(object); + v8::Local objectV8 = info.This(); + ScriptObjectV8Proxy *proxy = ScriptObjectV8Proxy::unwrapProxy(info.GetIsolate(), objectV8); if (!proxy) { qDebug(scriptengine) << "Proxy object not found when setting: " << *utf8Value; return; } - V8ScriptString nameString(info.GetIsolate(), v8::Local::Cast(name)); + V8ScriptValue object(proxy->_engine, objectV8); + Q_ASSERT(name->IsString()); + V8ScriptString nameString(proxy->_engine, v8::Local::Cast(name)); //V8ScriptString nameString(info.GetIsolate(), name->ToString(proxy->_engine->getContext()).ToLocalChecked()); uint id; QueryFlags flags = proxy->queryProperty(object, nameString, HandlesWriteAccess, &id); if (flags) { - proxy->setProperty(object, nameString, id, V8ScriptValue(info.GetIsolate(), value)); + proxy->setProperty(object, nameString, id, V8ScriptValue(proxy->_engine, value)); info.GetReturnValue().Set(value); } else { // V8TODO: Should it be v8::Object or v8::Local? @@ -447,7 +471,7 @@ V8ScriptValue ScriptObjectV8Proxy::property(const V8ScriptValue& object, const V QObject* qobject = _object; if (!qobject) { _engine->getIsolate()->ThrowError("Referencing deleted native object"); - return V8ScriptValue(_engine->getIsolate(), v8::Null(_engine->getIsolate())); + return V8ScriptValue(_engine, v8::Null(_engine->getIsolate())); } const QMetaObject* metaObject = qobject->metaObject(); @@ -456,7 +480,7 @@ V8ScriptValue ScriptObjectV8Proxy::property(const V8ScriptValue& object, const V case PROPERTY_TYPE: { int propId = id & ~TYPE_MASK; PropertyDefMap::const_iterator lookup = _props.find(propId); - if (lookup == _props.cend()) return V8ScriptValue(_engine->getIsolate(), v8::Null(_engine->getIsolate())); + if (lookup == _props.cend()) return V8ScriptValue(_engine, v8::Null(_engine->getIsolate())); QMetaProperty prop = metaObject->property(propId); ScriptValue scriptThis = ScriptValue(new ScriptValueV8Wrapper(_engine, object)); @@ -469,7 +493,7 @@ V8ScriptValue ScriptObjectV8Proxy::property(const V8ScriptValue& object, const V case METHOD_TYPE: { int methodId = id & ~TYPE_MASK; MethodDefMap::const_iterator lookup = _methods.find(methodId); - if (lookup == _methods.cend()) return V8ScriptValue(_engine->getIsolate(), v8::Null(_engine->getIsolate())); + if (lookup == _methods.cend()) return V8ScriptValue(_engine, v8::Null(_engine->getIsolate())); const MethodDef& methodDef = lookup.value(); for (auto iter = methodDef.methods.begin(); iter != methodDef.methods.end(); iter++ ) { if((*iter).returnType() == QMetaType::UnknownType) { @@ -480,7 +504,7 @@ V8ScriptValue ScriptObjectV8Proxy::property(const V8ScriptValue& object, const V v8::Local property; if(_v8Object.Get(_engine->getIsolate())->GetInternalField(2).As()->Get(_engine->getContext(), name.constGet()).ToLocal(&property)) { if (!property->IsUndefined()) { - return V8ScriptValue(_engine->getIsolate(), property); + return V8ScriptValue(_engine, property); } } Q_ASSERT(false); @@ -490,7 +514,7 @@ V8ScriptValue ScriptObjectV8Proxy::property(const V8ScriptValue& object, const V case SIGNAL_TYPE: { int signalId = id & ~TYPE_MASK; SignalDefMap::const_iterator defLookup = _signals.find(signalId); - if (defLookup == _signals.cend()) return V8ScriptValue(_engine->getIsolate(), v8::Null(_engine->getIsolate())); + if (defLookup == _signals.cend()) return V8ScriptValue(_engine, v8::Null(_engine->getIsolate())); InstanceMap::const_iterator instLookup = _signalInstances.find(signalId); if (instLookup == _signalInstances.cend() || instLookup.value().isNull()) { @@ -508,7 +532,7 @@ V8ScriptValue ScriptObjectV8Proxy::property(const V8ScriptValue& object, const V //return _engine->newQObject(proxy, ScriptEngine::ScriptOwnership, options); } } - return V8ScriptValue(_engine->getIsolate(), v8::Null(_engine->getIsolate())); + return V8ScriptValue(_engine, v8::Null(_engine->getIsolate())); } void ScriptObjectV8Proxy::setProperty(V8ScriptValue& object, const V8ScriptString& name, uint id, const V8ScriptValue& value) { @@ -562,7 +586,7 @@ V8ScriptValue ScriptVariantV8Proxy::newVariant(ScriptEngineV8* engine, const QVa if (!protoProxy) { Q_ASSERT(protoProxy); //return engine->newVariant(variant); - return V8ScriptValue(engine->getIsolate(), v8::Undefined(engine->getIsolate())); + return V8ScriptValue(engine, v8::Undefined(engine->getIsolate())); } // V8TODO probably needs connection to be deleted // V8TODO what to do with proto variable? @@ -572,7 +596,7 @@ V8ScriptValue ScriptVariantV8Proxy::newVariant(ScriptEngineV8* engine, const QVa auto variantData = variantDataTemplate->NewInstance(engine->getContext()).ToLocalChecked(); variantData->SetAlignedPointerInInternalField(0, const_cast(internalPointsToQVariantProxy)); variantData->SetAlignedPointerInInternalField(1, reinterpret_cast(proxy)); - return V8ScriptValue(engine->getIsolate(), variantData); + return V8ScriptValue(engine, variantData); } ScriptVariantV8Proxy* ScriptVariantV8Proxy::unwrapProxy(const V8ScriptValue& val) { @@ -620,7 +644,7 @@ V8ScriptValue ScriptMethodV8Proxy::newMethod(ScriptEngineV8* engine, QObject* ob // V8TODO it needs to be deleted somehow on object destruction methodData->SetAlignedPointerInInternalField(1, reinterpret_cast(new ScriptMethodV8Proxy(engine, object, lifetime, metas, numMaxParams))); auto v8Function = v8::Function::New(engine->getContext(), callback, methodData, numMaxParams).ToLocalChecked(); - return V8ScriptValue(engine->getIsolate(), v8Function); + return V8ScriptValue(engine, v8Function); } QString ScriptMethodV8Proxy::fullName() const { @@ -722,11 +746,11 @@ void ScriptMethodV8Proxy::call(const v8::FunctionCallbackInfo& argume Q_ASSERT(methodArgTypeId != QMetaType::UnknownType); v8::Local argVal = arguments[arg]; if (methodArgTypeId == scriptValueTypeId) { - qScriptArgLists[i].append(ScriptValue(new ScriptValueV8Wrapper(_engine, V8ScriptValue(isolate, argVal)))); + qScriptArgLists[i].append(ScriptValue(new ScriptValueV8Wrapper(_engine, V8ScriptValue(_engine, argVal)))); qGenArgsVectors[i][arg] = Q_ARG(ScriptValue, qScriptArgLists[i].back()); } else if (methodArgTypeId == QMetaType::QVariant) { QVariant varArgVal; - if (!_engine->castValueToVariant(V8ScriptValue(isolate, argVal), varArgVal, methodArgTypeId)) { + if (!_engine->castValueToVariant(V8ScriptValue(_engine, argVal), varArgVal, methodArgTypeId)) { conversionFailures++; } else { qVarArgLists[i].append(varArgVal); @@ -734,12 +758,12 @@ void ScriptMethodV8Proxy::call(const v8::FunctionCallbackInfo& argume } } else { QVariant varArgVal; - if (!_engine->castValueToVariant(V8ScriptValue(isolate, argVal), varArgVal, methodArgTypeId)) { + if (!_engine->castValueToVariant(V8ScriptValue(_engine, argVal), varArgVal, methodArgTypeId)) { conversionFailures++; } else { qVarArgLists[i].append(varArgVal); const QVariant& converted = qVarArgLists[i].back(); - conversionPenaltyScore = _engine->computeCastPenalty(V8ScriptValue(isolate, argVal), methodArgTypeId); + conversionPenaltyScore = _engine->computeCastPenalty(V8ScriptValue(_engine, argVal), methodArgTypeId); // a lot of type conversion assistance thanks to https://stackoverflow.com/questions/28457819/qt-invoke-method-with-qvariant // A const_cast is needed because calling data() would detach the QVariant. @@ -834,9 +858,9 @@ void ScriptMethodV8Proxy::call(const v8::FunctionCallbackInfo& argume v8::Local argVal = arguments[arg]; if (methodArgTypeId != scriptValueTypeId) { QVariant varArgVal; - if (!_engine->castValueToVariant(V8ScriptValue(isolate, argVal), varArgVal, methodArgTypeId)) { + if (!_engine->castValueToVariant(V8ScriptValue(_engine, argVal), varArgVal, methodArgTypeId)) { QByteArray methodTypeName = QMetaType(methodArgTypeId).name(); - QByteArray argTypeName = _engine->valueType(V8ScriptValue(isolate, argVal)).toLatin1(); + QByteArray argTypeName = _engine->valueType(V8ScriptValue(_engine, argVal)).toLatin1(); QString errorMessage = QString("Native call of %1 failed: Cannot convert parameter %2 from %3 to %4") .arg(fullName()).arg(arg+1).arg(argTypeName, methodTypeName); qDebug(scriptengine) << errorMessage << "\n Backtrace:" << _engine->currentContext()->backtrace(); @@ -1043,6 +1067,8 @@ int ScriptSignalV8Proxy::qt_metacall(QMetaObject::Call call, int id, void** argu v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); + // V8TODO: should we use function creation context, or context in which connect happened? + auto context = _engine->getContext(); v8::Context::Scope contextScope(context); @@ -1063,20 +1089,29 @@ int ScriptSignalV8Proxy::qt_metacall(QMetaObject::Call call, int id, void** argu for (ConnectionList::iterator iter = connections.begin(); iter != connections.end(); ++iter) { Connection& conn = *iter; - v8::Local callback = v8::Local::Cast(conn.callback.get()); - v8::Local v8This; - if (conn.thisValue.get()->IsObject()) { - v8This = conn.thisValue.get(); - } else { - v8This = _engine->getContext()->Global(); - } + Q_ASSERT(!conn.callback.get().IsEmpty()); + Q_ASSERT(!conn.callback.get()->IsUndefined()); + Q_ASSERT(conn.callback.get()->IsFunction()); + { + v8::Local callback = v8::Local::Cast(conn.callback.get()); + auto functionContext = callback->CreationContext(); + _engine->pushContext(functionContext); + v8::Context::Scope functionContextScope(functionContext); + v8::Local v8This; + if (conn.thisValue.get()->IsObject()) { + v8This = conn.thisValue.get(); + } else { + v8This = _engine->getContext()->Global(); + } - v8::TryCatch tryCatch(isolate); - callback->Call(_engine->getContext(), v8This, numArgs, args); - if (tryCatch.HasCaught()) { - qCDebug(scriptengine) << "Signal proxy " << fullName() << " connection call failed: \"" - << _engine->formatErrorMessageFromTryCatch(tryCatch) - << "\nThis provided: " << conn.thisValue.get()->IsObject(); + v8::TryCatch tryCatch(isolate); + callback->Call(_engine->getContext(), v8This, numArgs, args); + if (tryCatch.HasCaught()) { + qCDebug(scriptengine) << "Signal proxy " << fullName() << " connection call failed: \"" + << _engine->formatErrorMessageFromTryCatch(tryCatch) + << "\nThis provided: " << conn.thisValue.get()->IsObject(); + } + _engine->popContext(); } } @@ -1125,8 +1160,8 @@ void ScriptSignalV8Proxy::connect(ScriptValue arg0, ScriptValue arg1) { //v8::HandleScope handleScope(isolate); // untangle the arguments - V8ScriptValue callback(isolate, v8::Null(isolate)); - V8ScriptValue callbackThis(isolate, v8::Null(isolate)); + V8ScriptValue callback(_engine, v8::Null(isolate)); + V8ScriptValue callbackThis(_engine, v8::Null(isolate)); if (arg1.isFunction()) { auto unwrappedArg0 = ScriptValueV8Wrapper::unwrap(arg0); auto unwrappedArg1 = ScriptValueV8Wrapper::unwrap(arg1); @@ -1158,6 +1193,9 @@ void ScriptSignalV8Proxy::connect(ScriptValue arg0, ScriptValue arg1) { } // add a reference to ourselves to the destination callback + Q_ASSERT(!callback.get().IsEmpty()); + Q_ASSERT(!callback.get()->IsUndefined()); + Q_ASSERT(callback.get()->IsFunction()); v8::Local destFunction = v8::Local::Cast(callback.get()); v8::Local destDataName = v8::String::NewFromUtf8(isolate, "__data__").ToLocalChecked(); v8::Local destData; @@ -1166,7 +1204,10 @@ void ScriptSignalV8Proxy::connect(ScriptValue arg0, ScriptValue arg1) { //Q_ASSERT(destFunction->InternalFieldCount() == 4); //Q_ASSERT(destData.get()->IsArray()); //v8::Local destData = destFunction->GetInternalField(3); - if (destFunction->Get(destFunctionContext, destDataName).ToLocal(&destData) && destData->IsArray()) { + if (!destFunction->Get(destFunctionContext, destDataName).ToLocal(&destData)) { + Q_ASSERT(false); + } + if (destData->IsArray()) { v8::Local destArray = v8::Local::Cast(destData); int length = destArray->Length();//destData.property("length").toInteger(); v8::Local newArray = v8::Array::New(isolate, length + 1); @@ -1232,8 +1273,8 @@ void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) { v8::Context::Scope contextScope(_engine->getContext()); // untangle the arguments - V8ScriptValue callback(isolate, v8::Null(isolate)); - V8ScriptValue callbackThis(isolate, v8::Null(isolate)); + V8ScriptValue callback(_engine, v8::Null(isolate)); + V8ScriptValue callbackThis(_engine, v8::Null(isolate)); if (arg1.isFunction()) { auto unwrappedArg0 = ScriptValueV8Wrapper::unwrap(arg0); auto unwrappedArg1 = ScriptValueV8Wrapper::unwrap(arg1); @@ -1277,7 +1318,10 @@ void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) { V8ScriptValue v8ThisObject = ScriptValueV8Wrapper::fullUnwrap(_engine, thisObject()); //V8ScriptValue destData = callback.data(); //Q_ASSERT(destData->IsArray()); - if (destFunction->Get(destFunctionContext, destDataName).ToLocal(&destData) && destData->IsArray()) { + if (!destFunction->Get(destFunctionContext, destDataName).ToLocal(&destData)) { + Q_ASSERT(false); + } + if (destData->IsArray()) { v8::Local destArray = v8::Local::Cast(destData); int length = destArray->Length();//destData.property("length").toInteger(); v8::Local newArray = v8::Array::New(isolate, length - 1); diff --git a/libraries/script-engine/src/v8/ScriptObjectV8Proxy.h b/libraries/script-engine/src/v8/ScriptObjectV8Proxy.h index f0a798bcab..39de65bb87 100644 --- a/libraries/script-engine/src/v8/ScriptObjectV8Proxy.h +++ b/libraries/script-engine/src/v8/ScriptObjectV8Proxy.h @@ -25,6 +25,7 @@ #include "../ScriptEngine.h" #include "../Scriptable.h" #include "ScriptEngineV8.h" +#include "V8Types.h" #include @@ -37,20 +38,20 @@ class ScriptObjectV8Proxy final { private: // implementation class PropertyDef { public: - PropertyDef(v8::Isolate *isolate, v8::Local string) : name(isolate, string) {}; + PropertyDef(ScriptEngineV8 *engine, v8::Local string) : name(engine, string) {}; V8ScriptString name; ScriptValue::PropertyFlags flags; }; class MethodDef { public: - MethodDef(v8::Isolate *isolate, v8::Local string) : name(isolate, string) {}; + MethodDef(ScriptEngineV8 *engine, v8::Local string) : name(engine, string) {}; V8ScriptString name; int numMaxParms; QList methods; }; class SignalDef { public: - SignalDef(v8::Isolate *isolate, v8::Local string) : name(isolate, string) {}; + SignalDef(ScriptEngineV8 *engine, v8::Local string) : name(engine, string) {}; V8ScriptString name; QMetaMethod signal; }; @@ -73,6 +74,7 @@ public: // construction ScriptEngine::ValueOwnership ownership = ScriptEngine::QtOwnership, const ScriptEngine::QObjectWrapOptions& options = ScriptEngine::QObjectWrapOptions()); static ScriptObjectV8Proxy* unwrapProxy(const V8ScriptValue& val); + static ScriptObjectV8Proxy* unwrapProxy(v8::Isolate* isolate, v8::Local& value); static QObject* unwrap(const V8ScriptValue& val); inline QObject* toQObject() const { return _object; } inline v8::Local toV8Value() const { diff --git a/libraries/script-engine/src/v8/ScriptProgramV8Wrapper.cpp b/libraries/script-engine/src/v8/ScriptProgramV8Wrapper.cpp index 973fffeede..d9b20de7a3 100644 --- a/libraries/script-engine/src/v8/ScriptProgramV8Wrapper.cpp +++ b/libraries/script-engine/src/v8/ScriptProgramV8Wrapper.cpp @@ -52,9 +52,9 @@ bool ScriptProgramV8Wrapper::compile() { v8::ScriptOrigin scriptOrigin(isolate, v8::String::NewFromUtf8(isolate, _url.toStdString().c_str()).ToLocalChecked()); v8::Local script; if (v8::Script::Compile(context, v8::String::NewFromUtf8(isolate, _source.toStdString().c_str()).ToLocalChecked(), &scriptOrigin).ToLocal(&script)) { - qDebug() << "Script compilation succesful: " << _url; + qDebug() << "Script compilation successful: " << _url; _compileResult = ScriptSyntaxCheckResultV8Wrapper(ScriptSyntaxCheckResult::Valid); - _value = V8ScriptProgram(isolate, script); + _value = V8ScriptProgram(_engine, script); _isCompiled = true; return true; } @@ -66,10 +66,13 @@ bool ScriptProgramV8Wrapper::compile() { errorLineNumber = exceptionMessage->GetLineNumber(context).FromJust(); errorColumnNumber = exceptionMessage->GetStartColumn(context).FromJust(); v8::Local backtraceV8String; - if (tryCatch.StackTrace(context).ToLocal(&backtraceV8String) && backtraceV8String->IsString() && - v8::Local::Cast(backtraceV8String)->Length() > 0) { - v8::String::Utf8Value backtraceUtf8Value(isolate, backtraceV8String); - errorBacktrace = *backtraceUtf8Value; + if (tryCatch.StackTrace(context).ToLocal(&backtraceV8String)) { + if (backtraceV8String->IsString()) { + if (v8::Local::Cast(backtraceV8String)->Length() > 0) { + v8::String::Utf8Value backtraceUtf8Value(isolate, backtraceV8String); + errorBacktrace = *backtraceUtf8Value; + } + } } } //V8TODO diff --git a/libraries/script-engine/src/v8/ScriptProgramV8Wrapper.h b/libraries/script-engine/src/v8/ScriptProgramV8Wrapper.h index ff98a3177e..eb7a14bbfe 100644 --- a/libraries/script-engine/src/v8/ScriptProgramV8Wrapper.h +++ b/libraries/script-engine/src/v8/ScriptProgramV8Wrapper.h @@ -21,6 +21,7 @@ #include "../ScriptProgram.h" #include "ScriptEngineV8.h" +#include "V8Types.h" class ScriptSyntaxCheckResultV8Wrapper final : public ScriptSyntaxCheckResult { public: // construction @@ -51,7 +52,7 @@ public: // construction //inline ScriptProgramV8Wrapper(ScriptEngineV8* engine, V8ScriptProgram&& value) : // _engine(engine), _value(std::move(value)) {} inline ScriptProgramV8Wrapper(ScriptEngineV8* engine, QString source, QString url) : - _engine(engine), _source(source), _url(url), _value(engine->getIsolate(), v8::Local()) {} + _engine(engine), _source(source), _url(url), _value(engine, v8::Local()) {} static ScriptProgramV8Wrapper* unwrap(ScriptProgramPointer val); bool compile(); inline const V8ScriptProgram& toV8Value() const { return _value; } diff --git a/libraries/script-engine/src/v8/ScriptValueIteratorV8Wrapper.cpp b/libraries/script-engine/src/v8/ScriptValueIteratorV8Wrapper.cpp index d38cd1fe19..f0e7719e68 100644 --- a/libraries/script-engine/src/v8/ScriptValueIteratorV8Wrapper.cpp +++ b/libraries/script-engine/src/v8/ScriptValueIteratorV8Wrapper.cpp @@ -71,7 +71,7 @@ V8ScriptValue V8ScriptValueIterator::value() { if (!_object.Get(isolate)->Get(context, propertyName->ToString(context).ToLocalChecked()).ToLocal(&v8Value)) { Q_ASSERT(false); } - return V8ScriptValue(isolate, v8Value); + return V8ScriptValue(_engine, v8Value); } ScriptValue::PropertyFlags ScriptValueIteratorV8Wrapper::flags() const { diff --git a/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp b/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp index 7f99f709bb..c1aa010f6c 100644 --- a/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp +++ b/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp @@ -82,7 +82,7 @@ ScriptValue ScriptValueV8Wrapper::call(const ScriptValue& thisObject, const Scri for (ScriptValueList::const_iterator iter = args.begin(); iter != args.end(); ++iter) { v8Args[argIndex++] = fullUnwrap(*iter).get(); } - // V8TODO: IsFunction check should be here probably + Q_ASSERT(_value.get()->IsFunction()); v8::Local v8Function = v8::Local::Cast(_value.get()); v8::TryCatch tryCatch(isolate); v8::Local recv; @@ -101,7 +101,7 @@ ScriptValue ScriptValueV8Wrapper::call(const ScriptValue& thisObject, const Scri } v8::Local result; if (maybeResult.ToLocal(&result)) { - return ScriptValue(new ScriptValueV8Wrapper(_engine, V8ScriptValue(_engine->getIsolate(), result))); + return ScriptValue(new ScriptValueV8Wrapper(_engine, V8ScriptValue(_engine, result))); } else { //V8TODO Add more details qWarning("JS function call failed"); @@ -135,6 +135,7 @@ ScriptValue ScriptValueV8Wrapper::call(const ScriptValue& thisObject, const Scri } ScriptValue ScriptValueV8Wrapper::construct(const ScriptValueList& args) { + //V8TODO: there is CallAsContructor in V8 auto isolate = _engine->getIsolate(); v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); @@ -148,12 +149,12 @@ ScriptValue ScriptValueV8Wrapper::construct(const ScriptValueList& args) { v8Args[argIndex++] = fullUnwrap(*iter).get(); } //V8TODO should there be a v8 try-catch here? - // IsFunction check should be here probably + Q_ASSERT(_value.get()->IsFunction()); v8::Local v8Function = v8::Local::Cast(_value.get()); auto maybeResult = v8Function->NewInstance(_engine->getContext(), args.length(), v8Args); v8::Local result; if (maybeResult.ToLocal(&result)) { - return ScriptValue(new ScriptValueV8Wrapper(_engine, V8ScriptValue(_engine->getIsolate(), result))); + return ScriptValue(new ScriptValueV8Wrapper(_engine, V8ScriptValue(_engine, result))); } else { //V8TODO Add more details qWarning("JS function call failed"); @@ -203,7 +204,7 @@ ScriptValue ScriptValueV8Wrapper::data() const { Q_ASSERT(false); } }*/ - V8ScriptValue result(isolate, data); + V8ScriptValue result(_engine, data); return ScriptValue(new ScriptValueV8Wrapper(_engine, std::move(result))); } else { qDebug() << "ScriptValueV8Wrapper::data() was called on a value that is not an object"; @@ -268,7 +269,7 @@ ScriptValue ScriptValueV8Wrapper::property(const QString& name, const ScriptValu //V8TODO: Which context? if (object->Get(_engine->getContext(), key).ToLocal(&resultLocal)) { //if (object->Get(_value.constGetContext(), key).ToLocal(&resultLocal)) { - V8ScriptValue result(_engine->getIsolate(), resultLocal); + V8ScriptValue result(_engine, resultLocal); return ScriptValue(new ScriptValueV8Wrapper(_engine, std::move(result))); } else { QString parentValueQString(""); @@ -303,7 +304,7 @@ ScriptValue ScriptValueV8Wrapper::property(quint32 arrayIndex, const ScriptValue v8::Local resultLocal; const v8::Local object = v8::Local::Cast(_value.constGet()); if (object->Get(_value.constGetContext(), arrayIndex).ToLocal(&resultLocal)) { - V8ScriptValue result(_engine->getIsolate(), resultLocal); + V8ScriptValue result(_engine, resultLocal); return ScriptValue(new ScriptValueV8Wrapper(_engine, std::move(result))); } } diff --git a/libraries/script-engine/src/v8/ScriptValueV8Wrapper.h b/libraries/script-engine/src/v8/ScriptValueV8Wrapper.h index 3a9471879a..44ac8533a8 100644 --- a/libraries/script-engine/src/v8/ScriptValueV8Wrapper.h +++ b/libraries/script-engine/src/v8/ScriptValueV8Wrapper.h @@ -23,6 +23,7 @@ #include "../ScriptValue.h" #include "ScriptEngineV8.h" +#include "V8Types.h" /// [V8] Implements ScriptValue for V8 and translates calls for V8ScriptValue class ScriptValueV8Wrapper final : public ScriptValueProxy { diff --git a/libraries/script-engine/src/v8/V8Lambda.h b/libraries/script-engine/src/v8/V8Lambda.h new file mode 100644 index 0000000000..1f30177c16 --- /dev/null +++ b/libraries/script-engine/src/v8/V8Lambda.h @@ -0,0 +1,41 @@ +// +// V8Lambda.h +// split from ScriptEngineV8.h +// libraries/script-engine/src/qtscript +// +// Created by Brad Hefta-Gaub on 12/14/13. +// Modified for V8 by dr Karol Suprynowicz on 2022/10/08 +// Copyright 2013 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. +// Copyright 2022 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 +// + +#ifndef hifi_V8Lambda_h +#define hifi_V8Lambda_h + +#include "ScriptEngineV8.h" +#include "V8Types.h" + +// Lambda helps create callable V8ScriptValues out of std::functions: +// (just meant for use from within the script engine itself) +class Lambda : public QObject { + Q_OBJECT +public: + Lambda(ScriptEngineV8* engine, + std::function operation, + V8ScriptValue data); + ~Lambda(); +public slots: + V8ScriptValue call(); + QString toString() const; + +private: + ScriptEngineV8* _engine; + std::function _operation; + V8ScriptValue _data; +}; + +#endif diff --git a/libraries/script-engine/src/v8/V8Types.h b/libraries/script-engine/src/v8/V8Types.h index daa161c414..d11506521e 100644 --- a/libraries/script-engine/src/v8/V8Types.h +++ b/libraries/script-engine/src/v8/V8Types.h @@ -17,6 +17,8 @@ #include #include +#include "ScriptEngineV8.h" + template class V8ScriptValueTemplate { public: @@ -24,66 +26,83 @@ public: //V8ScriptValueTemplate(v8::Isolate *isolate, v8::Local value) : _isolate(isolate) { //_value.reset(v8::UniquePersistent::New(_isolate, value)); //}; - V8ScriptValueTemplate(v8::Isolate *isolate, const v8::Local value) : _isolate(isolate) { - //_value.reset(_isolate, value); - v8::HandleScope handleScope(_isolate); - Q_ASSERT(isolate->IsCurrent()); - Q_ASSERT(!isolate->GetCurrentContext().IsEmpty()); - _context.Reset(isolate, isolate->GetCurrentContext()); - _value.reset(new v8::UniquePersistent(_isolate, std::move(value))); + V8ScriptValueTemplate(ScriptEngineV8 *engine, const v8::Local value) : _engine(engine) { + v8::Locker locker(_engine->getIsolate()); + v8::Isolate::Scope isolateScope(_engine->getIsolate()); + v8::HandleScope handleScope(_engine->getIsolate()); + v8::Context::Scope(_engine->getContext()); + _value.reset(new v8::UniquePersistent(_engine->getIsolate(), std::move(value))); }; - /*V8ScriptValueTemplate(const V8ScriptValueTemplate &copied) { - ; - }*/ + V8ScriptValueTemplate(const V8ScriptValueTemplate &copied) : _engine(copied.getEngine()) { + v8::Locker locker(_engine->getIsolate()); + v8::Isolate::Scope isolateScope(_engine->getIsolate()); + v8::HandleScope handleScope(_engine->getIsolate()); + v8::Context::Scope(_engine->getContext()); + _value.reset(new v8::UniquePersistent(_engine->getIsolate(), std::move(copied.constGet()))); + } v8::Local get() { - v8::EscapableHandleScope handleScope(_isolate); - return handleScope.Escape(_value.get()->Get(_isolate)); + Q_ASSERT(_engine->getIsolate()->IsCurrent()); + v8::EscapableHandleScope handleScope(_engine->getIsolate()); + return handleScope.Escape(_value.get()->Get(_engine->getIsolate())); }; + const v8::Local constGet() const { - v8::EscapableHandleScope handleScope(_isolate); - return handleScope.Escape(_value.get()->Get(_isolate)); + Q_ASSERT(_engine->getIsolate()->IsCurrent()); + v8::EscapableHandleScope handleScope(_engine->getIsolate()); + return handleScope.Escape(_value.get()->Get(_engine->getIsolate())); }; - V8ScriptValueTemplate&& copy() const { - Q_ASSERT(_isolate->IsCurrent()); - v8::HandleScope handleScope(_isolate); - return new V8ScriptValueTemplate(_isolate, v8::Local::New(_isolate, constGet()));}; + + /*V8ScriptValueTemplate&& copy() const { + Q_ASSERT(_engine->getIsolate()->IsCurrent()); + v8::HandleScope handleScope(_engine->getIsolate()); + return new V8ScriptValueTemplate(_engine->getIsolate(), v8::Local::New(_engine->getIsolate(), constGet())); + };*/ const v8::Local constGetContext() const { - v8::EscapableHandleScope handleScope(_isolate); - Q_ASSERT(!_isolate->GetCurrentContext().IsEmpty()); - return handleScope.Escape(_isolate->GetCurrentContext()); + Q_ASSERT(_engine->getIsolate()->IsCurrent()); + v8::EscapableHandleScope handleScope(_engine->getIsolate()); + return handleScope.Escape(_engine->getIsolate()->GetCurrentContext()); //return handleScope.Escape(_context.Get(_isolate)); }; - const v8::Isolate* constGetIsolate() const { return _isolate;}; - v8::Isolate* getIsolate() { return _isolate;}; + + const v8::Isolate* constGetIsolate() const { return _engine->getIsolate(); }; + v8::Isolate* getIsolate() { return _engine->getIsolate();}; + //v8::Persistent>& getContext() { return _context;}; + + ScriptEngineV8* getEngine() const { return _engine; }; + v8::Local getContext() { - v8::EscapableHandleScope handleScope(_isolate); - Q_ASSERT(!_isolate->GetCurrentContext().IsEmpty()); - return handleScope.Escape(_isolate->GetCurrentContext()); + Q_ASSERT(_engine->getIsolate()->IsCurrent()); + v8::EscapableHandleScope handleScope(_engine->getIsolate()); + return handleScope.Escape(_engine->getIsolate()->GetCurrentContext()); //return handleScope.Escape(_context.Get(_isolate)); }; - void reset(v8::Isolate *isolate, v8::Local) {}; + + void reset(v8::Isolate *isolate, v8::Local) { + Q_ASSERT(false); + }; private: std::shared_ptr> _value; // V8TODO: maybe make it weak // does it need , CopyablePersistentTraits? // V8TODO: is context needed at all? - v8::Isolate *_isolate; - v8::Persistent> _context; + //v8::Isolate *_isolate; + ScriptEngineV8 *_engine; + //v8::Persistent> _context; }; -typedef V8ScriptValueTemplate V8ScriptValue; -typedef V8ScriptValueTemplate V8ScriptProgram; +//typedef V8ScriptValueTemplate V8ScriptValue; +//typedef V8ScriptValueTemplate V8ScriptProgram; // V8TODO: Maybe weak? typedef v8::Persistent V8ScriptContext; class V8ScriptString : public V8ScriptValueTemplate { public: V8ScriptString() = delete; - V8ScriptString(v8::Isolate *isolate, const v8::Local value) : V8ScriptValueTemplate(isolate, value) {}; + V8ScriptString(ScriptEngineV8 *engine, const v8::Local value) : V8ScriptValueTemplate(engine, value) {}; const QString toQString() const { Q_ASSERT(constGetIsolate()->IsCurrent()); Q_ASSERT(constGet()->IsString()); diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js index 568d0b6c14..39fa19c329 100644 --- a/scripts/system/controllers/controllerModules/inEditMode.js +++ b/scripts/system/controllers/controllerModules/inEditMode.js @@ -1,4 +1,4 @@ -"use strict"; +"no use strict"; // inEditMode.js // diff --git a/scripts/system/controllers/controllerModules/inVREditMode.js b/scripts/system/controllers/controllerModules/inVREditMode.js index 104e37d76c..7b5b2f36df 100644 --- a/scripts/system/controllers/controllerModules/inVREditMode.js +++ b/scripts/system/controllers/controllerModules/inVREditMode.js @@ -1,4 +1,4 @@ -"use strict"; +"no use strict"; // inVREditMode.js // diff --git a/scripts/system/controllers/controllerModules/stylusInput.js b/scripts/system/controllers/controllerModules/stylusInput.js index cbef45050e..48dadc2d49 100644 --- a/scripts/system/controllers/controllerModules/stylusInput.js +++ b/scripts/system/controllers/controllerModules/stylusInput.js @@ -1,4 +1,4 @@ -"use strict"; +"no use strict"; // stylusInput.js // diff --git a/scripts/system/create/modules/createWindow.js b/scripts/system/create/modules/createWindow.js index 18cc2d6ebd..434afc09cf 100644 --- a/scripts/system/create/modules/createWindow.js +++ b/scripts/system/create/modules/createWindow.js @@ -1,4 +1,4 @@ -"use strict"; +"no use strict"; // createWindow.js // @@ -71,6 +71,7 @@ var CallableEvent = (function() { module.exports = (function() { function CreateWindow(qmlPath, title, settingsKey, defaultRect, createOnStartup) { this.qmlPath = qmlPath; + print("QML path: " + qmlPath); this.title = title; this.settingsKey = settingsKey; this.defaultRect = defaultRect;