diff --git a/cmake/macros/MemoryDebugger.cmake b/cmake/macros/MemoryDebugger.cmake index 00964c1044..1c407c8161 100644 --- a/cmake/macros/MemoryDebugger.cmake +++ b/cmake/macros/MemoryDebugger.cmake @@ -16,7 +16,7 @@ if ("$ENV{OVERTE_MEMORY_DEBUGGING}") SET( OVERTE_MEMORY_DEBUGGING true ) endif () -if (OVERTE_MEMORY_DEBUGGING) +if ( OVERTE_MEMORY_DEBUGGING) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer -fsanitize=undefined -fsanitize=address -fsanitize-recover=address") SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined -fsanitize=address -fsanitize-recover=address") diff --git a/libraries/script-engine/src/v8/ScriptContextV8Wrapper.cpp b/libraries/script-engine/src/v8/ScriptContextV8Wrapper.cpp index 7c498549d0..44d8338b0e 100644 --- a/libraries/script-engine/src/v8/ScriptContextV8Wrapper.cpp +++ b/libraries/script-engine/src/v8/ScriptContextV8Wrapper.cpp @@ -20,13 +20,21 @@ _context.Reset(_engine->getIsolate(), context); }*/ -ScriptContextV8Wrapper::ScriptContextV8Wrapper(ScriptEngineV8* engine) : _functionCallbackInfo(nullptr), _propertyCallbackInfo(nullptr), _engine(engine) { +ScriptContextV8Wrapper::ScriptContextV8Wrapper(ScriptEngineV8* engine, const v8::Local context, ScriptContextPointer parent) : + _functionCallbackInfo(nullptr), _propertyCallbackInfo(nullptr), _engine(engine), + _context(engine->getIsolate(), context), _parentContext(parent) { } -ScriptContextV8Wrapper::ScriptContextV8Wrapper(ScriptEngineV8* engine, const v8::FunctionCallbackInfo *functionCallbackInfo) : _functionCallbackInfo(functionCallbackInfo), _propertyCallbackInfo(nullptr), _engine(engine) { +ScriptContextV8Wrapper::ScriptContextV8Wrapper(ScriptEngineV8* engine, const v8::FunctionCallbackInfo *functionCallbackInfo, + const v8::Local context, ScriptContextPointer parent) : + _functionCallbackInfo(functionCallbackInfo), _propertyCallbackInfo(nullptr), _engine(engine), + _context(engine->getIsolate(), context), _parentContext(parent) { } -ScriptContextV8Wrapper::ScriptContextV8Wrapper(ScriptEngineV8* engine, const v8::PropertyCallbackInfo *propertyCallbackInfo) : _functionCallbackInfo(nullptr), _propertyCallbackInfo(propertyCallbackInfo), _engine(engine) { +ScriptContextV8Wrapper::ScriptContextV8Wrapper(ScriptEngineV8* engine, const v8::PropertyCallbackInfo *propertyCallbackInfo, + const v8::Local context, ScriptContextPointer parent) : + _functionCallbackInfo(nullptr), _propertyCallbackInfo(propertyCallbackInfo), _engine(engine), + _context(engine->getIsolate(), context), _parentContext(parent) { } ScriptContextV8Wrapper* ScriptContextV8Wrapper::unwrap(ScriptContext* val) { @@ -39,7 +47,7 @@ ScriptContextV8Wrapper* ScriptContextV8Wrapper::unwrap(ScriptContext* val) { v8::Local ScriptContextV8Wrapper::toV8Value() const { v8::EscapableHandleScope handleScope(_engine->getIsolate()); - return handleScope.Escape(_engine->getContext()); + return handleScope.Escape(_context.Get(_engine->getIsolate())); } int ScriptContextV8Wrapper::argumentCount() const { @@ -122,7 +130,7 @@ ScriptContextPointer ScriptContextV8Wrapper::parentContext() const { //Q_ASSERT(false); //V8ScriptContext* result = _context->parentContext(); //return result ? std::make_shared(_engine, result) : ScriptContextPointer(); - return ScriptContextPointer(); + return _parentContext; } ScriptValue ScriptContextV8Wrapper::thisObject() const { diff --git a/libraries/script-engine/src/v8/ScriptContextV8Wrapper.h b/libraries/script-engine/src/v8/ScriptContextV8Wrapper.h index c46e28744d..2d9abcf911 100644 --- a/libraries/script-engine/src/v8/ScriptContextV8Wrapper.h +++ b/libraries/script-engine/src/v8/ScriptContextV8Wrapper.h @@ -31,11 +31,12 @@ class ScriptEngineV8; /// [V8] Implements ScriptContext for V8 and translates calls for V8ScriptContextInfo class ScriptContextV8Wrapper final : public ScriptContext { public: // construction - ScriptContextV8Wrapper(ScriptEngineV8* engine); - ScriptContextV8Wrapper(ScriptEngineV8* engine, const v8::FunctionCallbackInfo *functionCallbackInfo); - ScriptContextV8Wrapper(ScriptEngineV8* engine, const v8::PropertyCallbackInfo *propertyCallbackInfo); + ScriptContextV8Wrapper(ScriptEngineV8* engine, const v8::Local context, ScriptContextPointer parent); + ScriptContextV8Wrapper(ScriptEngineV8* engine, const v8::FunctionCallbackInfo *functionCallbackInfo, + const v8::Local context, ScriptContextPointer parent); + ScriptContextV8Wrapper(ScriptEngineV8* engine, const v8::PropertyCallbackInfo *propertyCallbackInfo, + const v8::Local context, ScriptContextPointer parent); static ScriptContextV8Wrapper* unwrap(ScriptContext* val); - v8::Local toV8Value() const; public: // ScriptContext implementation virtual int argumentCount() const override; @@ -49,10 +50,15 @@ public: // ScriptContext implementation virtual ScriptValue throwError(const QString& text) override; virtual ScriptValue throwValue(const ScriptValue& value) override; +public: // For use by V8-related functions + v8::Local toV8Value() const; + private: // storage const v8::FunctionCallbackInfo *_functionCallbackInfo; const v8::PropertyCallbackInfo *_propertyCallbackInfo; ScriptEngineV8* _engine; + v8::Persistent _context; + ScriptContextPointer _parentContext; }; class ScriptFunctionContextV8Wrapper final : public ScriptFunctionContext { diff --git a/libraries/script-engine/src/v8/ScriptEngineV8.cpp b/libraries/script-engine/src/v8/ScriptEngineV8.cpp index 0a7871c7a4..614fb1e851 100644 --- a/libraries/script-engine/src/v8/ScriptEngineV8.cpp +++ b/libraries/script-engine/src/v8/ScriptEngineV8.cpp @@ -116,7 +116,7 @@ ScriptValue ScriptEngineV8::makeError(const ScriptValue& _other, const QString& v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); return nullValue(); //V8TODO @@ -143,7 +143,7 @@ ScriptValue ScriptEngineV8::checkScriptSyntax(ScriptProgramPointer program) { v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); ScriptSyntaxCheckResultPointer syntaxCheck = program->checkSyntax(); //V8TODO if (syntaxCheck->state() != ScriptSyntaxCheckResult::Valid) { @@ -201,7 +201,7 @@ ScriptValue ScriptEngineV8::cloneUncaughtException(const QString& extraDetail) { v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); return nullValue(); //V8TODO /* @@ -297,7 +297,7 @@ ScriptValue ScriptEngineV8::newLambdaFunction(std::function context = v8::Context::New(_v8Isolate); Q_ASSERT(!context.IsEmpty()); v8::Context::Scope contextScope(context); - _v8Context = v8::UniquePersistent(_v8Isolate, context); + _contexts.append(std::make_shared(this,context, ScriptContextPointer())); V8ScriptValue nullScriptValue(_v8Isolate, v8::Null(_v8Isolate)); @@ -458,7 +458,7 @@ void ScriptEngineV8::registerEnum(const QString& enumName, QMetaEnum newEnum) { v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); for (int i = 0; i < newEnum.keyCount(); i++) { const char* keyName = newEnum.key(i); @@ -480,34 +480,35 @@ void ScriptEngineV8::registerValue(const QString& valueName, V8ScriptValue value v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Local context = getContext(); + v8::Context::Scope contextScope(getContext()); QStringList pathToValue = valueName.split("."); int partsToGo = pathToValue.length(); - v8::Local partObject = _v8Context.Get(_v8Isolate)->Global(); + v8::Local partObject = context->Global(); for (const auto& pathPart : pathToValue) { partsToGo--; v8::Local pathPartV8 = v8::String::NewFromUtf8(_v8Isolate, pathPart.toStdString().c_str(),v8::NewStringType::kNormal).ToLocalChecked(); v8::Local currentPath; - if (!partObject->Get(_v8Context.Get(_v8Isolate), pathPartV8).ToLocal(¤tPath)) { + if (!partObject->Get(context, pathPartV8).ToLocal(¤tPath)) { if (partsToGo > 0) { //This was commented out //QObject *object = new QObject; v8::Local partValue = v8::Object::New(_v8Isolate); //newQObject(object, QScriptEngine::ScriptOwnership); //V8ScriptValue partValue = QScriptEngine::newArray(); //newQObject(object, QScriptEngine::ScriptOwnership); - if (!partObject->Set(_v8Context.Get(_v8Isolate), pathPartV8, partValue).FromMaybe(false)) { + if (!partObject->Set(context, pathPartV8, partValue).FromMaybe(false)) { Q_ASSERT(false); } } else { //partObject = currentPath->ToObject(); //V8TODO: do these still happen if asserts are disabled? - if (!partObject->Set(_v8Context.Get(_v8Isolate), pathPartV8, value.constGet()).FromMaybe(false)) { + if (!partObject->Set(context, pathPartV8, value.constGet()).FromMaybe(false)) { Q_ASSERT(false); } } } v8::Local child; - if (!partObject->Get(_v8Context.Get(_v8Isolate), pathPartV8).ToLocal(&child)) { + if (!partObject->Get(context, pathPartV8).ToLocal(&child)) { Q_ASSERT(false); } } @@ -538,7 +539,8 @@ void ScriptEngineV8::registerGlobalObject(const QString& name, QObject* object) v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); Q_ASSERT(_v8Isolate->IsCurrent()); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Local context = getContext(); + v8::Context::Scope contextScope(context); v8::Local v8GlobalObject = getContext()->Global(); v8::Local v8Name = v8::String::NewFromUtf8(_v8Isolate, name.toStdString().c_str()).ToLocalChecked(); @@ -591,7 +593,7 @@ void ScriptEngineV8::registerFunction(const QString& name, ScriptEngine::Functio v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); auto scriptFun = newFunction(functionSignature, numArguments); //getContext()->Global().Set(); @@ -629,7 +631,7 @@ void ScriptEngineV8::registerFunction(const QString& parent, const QString& name v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); ScriptValue object = globalObject().property(parent); if (object.isValid()) { ScriptValue scriptFun = newFunction(functionSignature, numArguments); @@ -748,6 +750,17 @@ void ScriptEngineV8::registerGetterSetter(const QString& name, ScriptEngine::Fun }*/ } +v8::Local ScriptEngineV8::getContext() { + v8::EscapableHandleScope handleScope(_v8Isolate); + Q_ASSERT(!_contexts.isEmpty()); + return handleScope.Escape(_contexts.last().get()->toV8Value()); +} + +const v8::Local ScriptEngineV8::getConstContext() const { + v8::EscapableHandleScope handleScope(_v8Isolate); + Q_ASSERT(!_contexts.isEmpty()); + return handleScope.Escape(_contexts.last().get()->toV8Value()); +} ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure, const ScriptProgramPointer& _program) { PROFILE_RANGE(script, "evaluateInClosure"); @@ -767,7 +780,7 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure, v8::Local oldContext = getContext(); { - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); unwrappedProgram = ScriptProgramV8Wrapper::unwrap(_program); if (unwrappedProgram == nullptr) { _evaluatingCounter--; @@ -813,7 +826,6 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure, } //qDebug() << "Closure global details:" << scriptValueDebugDetailsV8(V8ScriptValue(_v8Isolate, closureGlobal)); } - //_v8Context.Get(_v8Isolate)->DetachGlobal(); //oldGlobal = _v8Context.Get(_v8Isolate)->Global(); v8::Local closureContext; @@ -829,6 +841,7 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure, closureContext = v8::Context::New(_v8Isolate); }*/ closureContext = v8::Context::New(_v8Isolate); + pushContext(closureContext); ScriptValue result; //auto context = pushContext(); @@ -927,7 +940,7 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure, #ifdef DEBUG_JS qCDebug(shared) << QString("[%1] //evaluateInClosure %2").arg(isEvaluating()).arg(shortName); #endif - //popContext(); + popContext(); } //This is probably unnecessary in V8 /*if (oldGlobal.isValid()) { @@ -967,7 +980,7 @@ ScriptValue ScriptEngineV8::evaluate(const QString& sourceCode, const QString& f v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); v8::TryCatch tryCatch(getIsolate()); v8::ScriptOrigin scriptOrigin(getIsolate(), v8::String::NewFromUtf8(getIsolate(), fileName.toStdString().c_str()).ToLocalChecked()); v8::Local script; @@ -1038,6 +1051,10 @@ ScriptValue ScriptEngineV8::evaluate(const QString& sourceCode, const QString& f } QString ScriptEngineV8::formatErrorMessageFromTryCatch(v8::TryCatch &tryCatch) { + v8::Locker locker(_v8Isolate); + v8::Isolate::Scope isolateScope(_v8Isolate); + v8::HandleScope handleScope(_v8Isolate); + v8::Context::Scope contextScope(getContext()); QString result(""); int errorColumnNumber = 0; int errorLineNumber = 0; @@ -1062,6 +1079,18 @@ QString ScriptEngineV8::formatErrorMessageFromTryCatch(v8::TryCatch &tryCatch) { return result; } +ScriptContextV8Pointer ScriptEngineV8::pushContext(v8::Local &context) { + Q_ASSERT(!_contexts.isEmpty()); + ScriptContextPointer parent = _contexts.last(); + _contexts.append(std::make_shared(this, context, ScriptContextPointer())); + return _contexts.last(); +} + +void ScriptEngineV8::popContext() { + Q_ASSERT(!_contexts.isEmpty()); + _contexts.pop_back(); +} + Q_INVOKABLE ScriptValue ScriptEngineV8::evaluate(const ScriptProgramPointer& program) { if (_scriptManager && _scriptManager->isStopped()) { return undefinedValue(); // bail early @@ -1093,7 +1122,7 @@ Q_INVOKABLE ScriptValue ScriptEngineV8::evaluate(const ScriptProgramPointer& pro v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); ScriptProgramV8Wrapper* unwrapped = ScriptProgramV8Wrapper::unwrap(program); if (!unwrapped) { errorValue = makeError(newValue("could not unwrap program")); @@ -1169,7 +1198,7 @@ ScriptValue ScriptEngineV8::globalObject() const { v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getConstContext()); V8ScriptValue global(_v8Isolate, getConstContext()->Global());// = QScriptEngine::globalObject(); // can't cache the value as it may change return ScriptValue(new ScriptValueV8Wrapper(const_cast(this), std::move(global))); } @@ -1182,7 +1211,7 @@ ScriptValue ScriptEngineV8::newArray(uint length) { v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); V8ScriptValue result(_v8Isolate, v8::Array::New(_v8Isolate, static_cast(length))); return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result))); } @@ -1191,7 +1220,7 @@ ScriptValue ScriptEngineV8::newArrayBuffer(const QByteArray& message) { v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); //V8TODO: this will leak memory std::shared_ptr backingStore(v8::ArrayBuffer::NewBackingStore(_v8Isolate, message.size())); std::memcpy(backingStore.get()->Data(), message.constData(), message.size()); @@ -1219,7 +1248,7 @@ ScriptValue ScriptEngineV8::newObject() { v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); V8ScriptValue resultV8 = V8ScriptValue(_v8Isolate, v8::Object::New(_v8Isolate)); result = ScriptValue(new ScriptValueV8Wrapper(this, std::move(resultV8))); } @@ -1235,7 +1264,7 @@ ScriptValue ScriptEngineV8::newMethod(QObject* object, V8ScriptValue lifetime, v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); V8ScriptValue result(ScriptMethodV8Proxy::newMethod(this, object, lifetime, metas, numMaxParams)); return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result))); } @@ -1248,7 +1277,7 @@ ScriptProgramPointer ScriptEngineV8::newProgram(const QString& sourceCode, const v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); return std::make_shared(this, sourceCode, fileName); } @@ -1258,7 +1287,7 @@ ScriptValue ScriptEngineV8::newQObject(QObject* object, v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); V8ScriptValue result = ScriptObjectV8Proxy::newQObject(this, object, ownership, options); return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result))); } @@ -1267,7 +1296,7 @@ ScriptValue ScriptEngineV8::newValue(bool value) { v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); V8ScriptValue result(_v8Isolate, v8::Boolean::New(_v8Isolate, value)); return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result))); } @@ -1276,7 +1305,7 @@ ScriptValue ScriptEngineV8::newValue(int value) { v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); V8ScriptValue result(_v8Isolate, v8::Integer::New(_v8Isolate, value)); return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result))); } @@ -1285,7 +1314,7 @@ ScriptValue ScriptEngineV8::newValue(uint value) { v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); V8ScriptValue result(_v8Isolate, v8::Uint32::New(_v8Isolate, value)); return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result))); } @@ -1294,7 +1323,7 @@ ScriptValue ScriptEngineV8::newValue(double value) { v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); V8ScriptValue result(_v8Isolate, v8::Number::New(_v8Isolate, value)); return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result))); } @@ -1303,7 +1332,7 @@ ScriptValue ScriptEngineV8::newValue(const QString& value) { v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_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); return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result))); @@ -1313,7 +1342,7 @@ ScriptValue ScriptEngineV8::newValue(const QLatin1String& value) { v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); v8::Local valueV8 = v8::String::NewFromUtf8(_v8Isolate, value.latin1(), v8::NewStringType::kNormal).ToLocalChecked(); V8ScriptValue result(_v8Isolate, valueV8); return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result))); @@ -1323,7 +1352,7 @@ ScriptValue ScriptEngineV8::newValue(const char* value) { v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); v8::Local valueV8 = v8::String::NewFromUtf8(_v8Isolate, value, v8::NewStringType::kNormal).ToLocalChecked(); V8ScriptValue result(_v8Isolate, valueV8); return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result))); @@ -1333,7 +1362,7 @@ ScriptValue ScriptEngineV8::newVariant(const QVariant& value) { v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); V8ScriptValue result = castVariantToValue(value); return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result))); } @@ -1366,11 +1395,11 @@ ScriptContext* ScriptEngineV8::currentContext() const { _currContext = std::make_shared(const_cast(this), localCtx); }*/ //_currContext = std::make_shared(const_cast(this), localCtx); - if (!_currContext) { + /*if (!_currContext) { // I'm not sure how to do this without discarding const _currContext = std::make_shared(const_cast(this)); - } - return _currContext.get(); + }*/ + return _contexts.last().get(); } bool ScriptEngineV8::hasUncaughtException() const { @@ -1386,6 +1415,7 @@ bool ScriptEngineV8::isEvaluating() const { } ScriptValue ScriptEngineV8::newFunction(ScriptEngine::FunctionSignature fun, int length) { + //V8TODO is callee() used for anything? /*auto innerFunc = [](V8ScriptContext* _context, QScriptEngine* _engine) -> V8ScriptValue { auto callee = _context->callee(); QVariant funAddr = callee.property("_func").toVariant(); @@ -1408,7 +1438,7 @@ ScriptValue ScriptEngineV8::newFunction(ScriptEngine::FunctionSignature fun, int (object->GetAlignedPointerFromInternalField(0)); ScriptEngineV8 *scriptEngine = reinterpret_cast (object->GetAlignedPointerFromInternalField(1)); - ScriptContextV8Wrapper scriptContext(scriptEngine, &info); + ScriptContextV8Wrapper scriptContext(scriptEngine, &info, scriptEngine->getContext(), scriptEngine->currentContext()->parentContext()); //V8TODO: this scriptContext needs to have FunctionCallbackInfo added ScriptValue result = function(&scriptContext, scriptEngine); ScriptValueV8Wrapper* unwrapped = ScriptValueV8Wrapper::unwrap(result); @@ -1422,7 +1452,7 @@ ScriptValue ScriptEngineV8::newFunction(ScriptEngine::FunctionSignature fun, int v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); auto functionDataTemplate = v8::ObjectTemplate::New(_v8Isolate); functionDataTemplate->SetInternalFieldCount(2); auto functionData = functionDataTemplate->NewInstance(getContext()).ToLocalChecked(); @@ -1449,7 +1479,7 @@ bool ScriptEngineV8::setProperty(const char* name, const QVariant& value) { v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); v8::Local global = getContext()->Global(); auto v8Name = v8::String::NewFromUtf8(getIsolate(), name).ToLocalChecked(); V8ScriptValue v8Value = castVariantToValue(value); @@ -1522,7 +1552,7 @@ ScriptValue ScriptEngineV8::create(int type, const void* ptr) { v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); QVariant variant(type, ptr); V8ScriptValue scriptValue = castVariantToValue(variant); return ScriptValue(new ScriptValueV8Wrapper(this, std::move(scriptValue))); @@ -1532,7 +1562,7 @@ QVariant ScriptEngineV8::convert(const ScriptValue& value, int typeId) { v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); ScriptValueV8Wrapper* unwrapped = ScriptValueV8Wrapper::unwrap(value); if (unwrapped == nullptr) { return QVariant(); @@ -1556,7 +1586,7 @@ void ScriptEngineV8::compileTest() { v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Context::Scope contextScope(getContext()); v8::Local script; v8::ScriptOrigin scriptOrigin(getIsolate(), v8::String::NewFromUtf8(getIsolate(),"test").ToLocalChecked()); if (v8::Script::Compile(getContext(), v8::String::NewFromUtf8(getIsolate(), "print(\"hello world\");").ToLocalChecked(), &scriptOrigin).ToLocal(&script)) { diff --git a/libraries/script-engine/src/v8/ScriptEngineV8.h b/libraries/script-engine/src/v8/ScriptEngineV8.h index dc9311509b..b7fb07c445 100644 --- a/libraries/script-engine/src/v8/ScriptEngineV8.h +++ b/libraries/script-engine/src/v8/ScriptEngineV8.h @@ -43,7 +43,7 @@ class ScriptEngineV8; class ScriptManager; class ScriptObjectV8Proxy; class ScriptMethodV8Proxy; -using ScriptContextQtPointer = std::shared_ptr; +using ScriptContextV8Pointer = std::shared_ptr; const double GARBAGE_COLLECTION_TIME_LIMIT_S = 1.0; @@ -179,14 +179,8 @@ public: // not for public use, but I don't like how Qt strings this along with p V8ScriptValue castVariantToValue(const QVariant& val); QString valueType(const V8ScriptValue& val); v8::Isolate* getIsolate() {return _v8Isolate;} - v8::Local getContext() { - v8::EscapableHandleScope handleScope(_v8Isolate); - return handleScope.Escape(_v8Context.Get(_v8Isolate)); - } - const v8::Local getConstContext() const { - v8::EscapableHandleScope handleScope(_v8Isolate); - return handleScope.Escape(_v8Context.Get(_v8Isolate)); - } + v8::Local getContext(); + const v8::Local getConstContext() const; // Useful for debugging //QStringList getCurrentStackTrace(); @@ -213,9 +207,12 @@ protected: static std::once_flag _v8InitOnceFlag; static v8::Platform* getV8Platform(); QString formatErrorMessageFromTryCatch(v8::TryCatch &tryCatch); - + ScriptContextV8Pointer pushContext(v8::Local &context); + void popContext(); + + // V8TODO: clean up isolate when script engine is destroyed? v8::Isolate* _v8Isolate; - v8::UniquePersistent _v8Context; + //v8::UniquePersistent _v8Context; struct CustomMarshal { ScriptEngine::MarshalFunction marshalFunc; @@ -231,10 +228,9 @@ protected: CustomPrototypeMap _customPrototypes; ScriptValue _nullValue; ScriptValue _undefinedValue; - mutable ScriptContextQtPointer _currContext; - //QThread *_currentThread; - //V8TODO: probably should be removed and its occurrences replaced with local locker - //std::unique_ptr _v8Locker; + //mutable ScriptContextV8Pointer _currContext; + // Current context stack. Main context is first on the list and current one is last. + QList _contexts; //V8TODO //ArrayBufferClass* _arrayBufferClass; diff --git a/libraries/script-engine/src/v8/ScriptEngineV8_cast.cpp b/libraries/script-engine/src/v8/ScriptEngineV8_cast.cpp index ccad5ec24d..d54e9f03ab 100644 --- a/libraries/script-engine/src/v8/ScriptEngineV8_cast.cpp +++ b/libraries/script-engine/src/v8/ScriptEngineV8_cast.cpp @@ -198,7 +198,12 @@ void ScriptEngineV8::registerSystemTypes() { } int ScriptEngineV8::computeCastPenalty(const V8ScriptValue& v8Val, int destTypeId) { + v8::Locker locker(_v8Isolate); + v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); + v8::Local context = getContext(); + v8::Context::Scope contextScope(context); + const v8::Local val = v8Val.constGet(); if (val->IsNumber()) { switch (destTypeId){ @@ -295,7 +300,8 @@ bool ScriptEngineV8::castValueToVariant(const V8ScriptValue& v8Val, QVariant& de v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(_v8Context.Get(_v8Isolate)); + v8::Local context = getContext(); + v8::Context::Scope contextScope(context); const v8::Local val = v8Val.constGet(); // Conversion debugging: @@ -366,7 +372,7 @@ bool ScriptEngineV8::castValueToVariant(const V8ScriptValue& v8Val, QVariant& de break; } if (val->IsNumber()) { - dest = QVariant::fromValue(val->ToNumber(_v8Context.Get(_v8Isolate)).ToLocalChecked()->Value()); + dest = QVariant::fromValue(val->ToNumber(context).ToLocalChecked()->Value()); break; } { @@ -403,11 +409,11 @@ bool ScriptEngineV8::castValueToVariant(const V8ScriptValue& v8Val, QVariant& de case QMetaType::QDateTime: case QMetaType::QDate: if (val->IsDate()){ - double timeMs = v8::Date::Cast(*val)->NumberValue(_v8Context.Get(_v8Isolate)).ToChecked(); + double timeMs = v8::Date::Cast(*val)->NumberValue(context).ToChecked(); dest = QVariant::fromValue(QDateTime::fromMSecsSinceEpoch(timeMs)); } else if (val->IsNumber()) { //V8TODO should we automatically cast numbers to datetime? - dest = QVariant::fromValue(QDateTime::fromMSecsSinceEpoch(val->ToNumber(_v8Context.Get(_v8Isolate)).ToLocalChecked()->Value())); + dest = QVariant::fromValue(QDateTime::fromMSecsSinceEpoch(val->ToNumber(context).ToLocalChecked()->Value())); } else { return false; } @@ -417,7 +423,7 @@ bool ScriptEngineV8::castValueToVariant(const V8ScriptValue& v8Val, QVariant& de if ( val->IsArray() || val->IsObject() ){ return false; } - dest = QVariant::fromValue(val->ToUint32(_v8Context.Get(_v8Isolate)).ToLocalChecked()->Value()); + dest = QVariant::fromValue(val->ToUint32(context).ToLocalChecked()->Value()); break; case QMetaType::Int: case QMetaType::Long: @@ -425,7 +431,7 @@ bool ScriptEngineV8::castValueToVariant(const V8ScriptValue& v8Val, QVariant& de if ( val->IsArray() || val->IsObject() ){ return false; } - dest = QVariant::fromValue(val->ToInt32(_v8Context.Get(_v8Isolate)).ToLocalChecked()->Value()); + dest = QVariant::fromValue(val->ToInt32(context).ToLocalChecked()->Value()); break; case QMetaType::Double: case QMetaType::Float: @@ -434,7 +440,7 @@ bool ScriptEngineV8::castValueToVariant(const V8ScriptValue& v8Val, QVariant& de if ( val->IsArray() || val->IsObject() ){ return false; } - dest = QVariant::fromValue(val->ToNumber(_v8Context.Get(_v8Isolate)).ToLocalChecked()->Value()); + dest = QVariant::fromValue(val->ToNumber(context).ToLocalChecked()->Value()); break; case QMetaType::QString: case QMetaType::QByteArray: @@ -448,7 +454,7 @@ bool ScriptEngineV8::castValueToVariant(const V8ScriptValue& v8Val, QVariant& de if ( val->IsArray() || val->IsObject() ){ return false; } - dest = QVariant::fromValue(static_cast(val->ToUint32(_v8Context.Get(_v8Isolate)).ToLocalChecked()->Value())); + dest = QVariant::fromValue(static_cast(val->ToUint32(context).ToLocalChecked()->Value())); break; case QMetaType::QObjectStar: dest = QVariant::fromValue(ScriptObjectV8Proxy::unwrap(v8Val)); @@ -476,7 +482,7 @@ bool ScriptEngineV8::castValueToVariant(const V8ScriptValue& v8Val, QVariant& de return true; } if (val->IsNumber()) { - dest = QVariant::fromValue(val->ToNumber(_v8Context.Get(_v8Isolate)).ToLocalChecked()->Value()); + dest = QVariant::fromValue(val->ToNumber(context).ToLocalChecked()->Value()); return true; } { @@ -545,6 +551,7 @@ bool ScriptEngineV8::castValueToVariant(const V8ScriptValue& v8Val, QVariant& de } bool ScriptEngineV8::convertJSObjectToVariant(v8::Local object, QVariant &dest) { + v8::HandleScope handleScope(_v8Isolate); auto context = getContext(); v8::Local names; if(!object->GetPropertyNames(context).ToLocal(&names)) { @@ -574,8 +581,14 @@ bool ScriptEngineV8::convertJSObjectToVariant(v8::Local object, QVar } QString ScriptEngineV8::valueType(const V8ScriptValue& v8Val) { - // V8TODO - v8::HandleScope handleScope(const_cast(v8Val.constGetIsolate())); + // V8TODO I'm not sure why is there a TODO here + v8::Locker locker(_v8Isolate); + v8::Isolate::Scope isolateScope(_v8Isolate); + v8::HandleScope handleScope(_v8Isolate); + v8::Local context = getContext(); + v8::Context::Scope contextScope(context); + + //v8::HandleScope handleScope(const_cast(v8Val.constGetIsolate())); const v8::Local val = v8Val.constGet(); if (val->IsUndefined()) { @@ -617,6 +630,12 @@ QString ScriptEngineV8::valueType(const V8ScriptValue& v8Val) { } V8ScriptValue ScriptEngineV8::castVariantToValue(const QVariant& val) { + v8::Locker locker(_v8Isolate); + v8::Isolate::Scope isolateScope(_v8Isolate); + v8::HandleScope handleScope(_v8Isolate); + v8::Local context = getContext(); + v8::Context::Scope contextScope(context); + int valTypeId = val.userType(); if (valTypeId == qMetaTypeId()) { @@ -676,12 +695,12 @@ V8ScriptValue ScriptEngineV8::castVariantToValue(const QVariant& val) { case QMetaType::QDateTime: { double timeMs = val.value().currentMSecsSinceEpoch(); - return V8ScriptValue(_v8Isolate, v8::Date::New(_v8Context.Get(_v8Isolate), timeMs).ToLocalChecked()); + return V8ScriptValue(_v8Isolate, v8::Date::New(getContext(), timeMs).ToLocalChecked()); } case QMetaType::QDate: { double timeMs = val.value().startOfDay().currentMSecsSinceEpoch(); - return V8ScriptValue(_v8Isolate, v8::Date::New(_v8Context.Get(_v8Isolate), timeMs).ToLocalChecked()); + return V8ScriptValue(_v8Isolate, v8::Date::New(getContext(), timeMs).ToLocalChecked()); } default: // check to see if this is a pointer to a QObject-derived object diff --git a/libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp b/libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp index e84dd9550b..a06ed27742 100644 --- a/libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp +++ b/libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp @@ -1155,7 +1155,7 @@ void ScriptSignalV8Proxy::connect(ScriptValue arg0, ScriptValue arg1) { if (!newArray->Set(destFunctionContext, 0, v8ThisObject.get()).FromMaybe(false)) { Q_ASSERT(false); } - if (destFunction->Set(destFunctionContext, destDataName, newArray).FromMaybe(false)) { + if (!destFunction->Set(destFunctionContext, destDataName, newArray).FromMaybe(false)) { Q_ASSERT(false); } } diff --git a/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp b/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp index d2b5e6df2e..e0c40d1dca 100644 --- a/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp +++ b/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp @@ -24,7 +24,14 @@ void ScriptValueV8Wrapper::release() { ScriptValueProxy* ScriptValueV8Wrapper::copy() const { //V8TODO: check if the value needs to be copied or just wrapper - return new ScriptValueV8Wrapper(_engine, _value); + v8::Isolate *isolate = _engine->getIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + // V8TODO: I'm not sure if this part is right: + v8::Context::Scope contextScope(_engine->getContext()); + ScriptValueV8Wrapper *copiedWrapper = new ScriptValueV8Wrapper(_engine, _value); + return copiedWrapper; } ScriptValueV8Wrapper* ScriptValueV8Wrapper::unwrap(const ScriptValue& val) { diff --git a/libraries/script-engine/src/v8/V8Types.h b/libraries/script-engine/src/v8/V8Types.h index dd98e61c06..eb18f8fedb 100644 --- a/libraries/script-engine/src/v8/V8Types.h +++ b/libraries/script-engine/src/v8/V8Types.h @@ -44,6 +44,7 @@ public: Q_ASSERT(_isolate->IsCurrent()); v8::HandleScope handleScope(_isolate); return new V8ScriptValueTemplate(_isolate, v8::Local::New(_isolate, constGet()));}; + const v8::Local constGetContext() const { v8::EscapableHandleScope handleScope(_isolate); return handleScope.Escape(_context.Get(_isolate)); @@ -75,11 +76,13 @@ public: V8ScriptString() = delete; V8ScriptString(v8::Isolate *isolate, const v8::Local value) : V8ScriptValueTemplate(isolate, value) {}; const QString toQString() const { - Q_ASSERT(constGet()->IsString()); Q_ASSERT(constGetIsolate()->IsCurrent()); + Q_ASSERT(constGet()->IsString()); return QString(*v8::String::Utf8Value(const_cast(constGetIsolate()), constGet())); }; bool operator==(const V8ScriptString& string) const { + Q_ASSERT(constGetIsolate()->IsCurrent()); + Q_ASSERT(constGet()->IsString()); return constGet()->StringEquals(string.constGet()); } };