Script engine thread safety improvements

This commit is contained in:
ksuprynowicz 2023-01-28 18:29:03 +01:00
parent 6bd8a8d503
commit 044cd506a6
21 changed files with 374 additions and 204 deletions

View file

@ -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

View file

@ -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);

View file

@ -20,7 +20,7 @@
#include "v8.h"
#include <QtCore/QDateTime>
#include "V8Types.h"
//#include "V8Types.h"
// V8TODO
/*
class ScriptEngineV8;

View file

@ -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<v8::Value> 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<v8::StackTrace> 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<ScriptFunctionContextV8Wrapper>(_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<ScriptFunctionContextV8Wrapper>(_engine, _context.Get(_engine->getIsolate()));
return scriptFunctionContextPointer;
}
ScriptContextPointer ScriptContextV8Wrapper::parentContext() const {
//V8TODO
//Q_ASSERT(false);
//V8ScriptContext* result = _context->parentContext();
//return result ? std::make_shared<ScriptContextV8Wrapper>(_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<v8::Value> 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<v8::Value> 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<v8::Context> 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<v8::String> 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<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(_engine->getIsolate(), 1);
v8::Local<v8::StackFrame> stackFrame = stackTrace->GetFrame(_engine->getIsolate(), 0);
v8::Local<v8::String> 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<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(_engine->getIsolate(), 1);
v8::Local<v8::StackFrame> stackFrame = stackTrace->GetFrame(_engine->getIsolate(), 0);
return stackFrame->GetLineNumber();

View file

@ -57,6 +57,7 @@ private: // storage
const v8::FunctionCallbackInfo<v8::Value> *_functionCallbackInfo;
const v8::PropertyCallbackInfo<v8::Value> *_propertyCallbackInfo;
ScriptEngineV8* _engine;
// V8TODO: Is custom copy constructor needed for thread safety?
v8::Persistent<v8::Context> _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<v8::Context> 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<v8::Context> _context;
//V8ScriptContextInfo _value;
};

View file

@ -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<V8ScriptValue(V8ScriptContext*, ScriptEngineV8*)> operation,
/*ScriptValue ScriptEngineV8::newLambdaFunction(std::function<V8ScriptValue(V8ScriptContext*, ScriptEngineV8*)> operation,
const V8ScriptValue& data,
const ValueOwnership& ownership) {
v8::HandleScope handleScope(_v8Isolate);
@ -305,7 +306,7 @@ ScriptValue ScriptEngineV8::newLambdaFunction(std::function<V8ScriptValue(V8Scri
call.setPrototype(object); // context->callee().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<ScriptContextV8Wrapper>(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<v8::String> v8propertyName =
v8::String::NewFromUtf8(_v8Isolate, name.toStdString().c_str()).ToLocalChecked();
v8::Local<v8::Object> 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<v8::Object>();
@ -833,13 +834,14 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure,
Q_ASSERT(false);
return nullValue();
}
Q_ASSERT(closure.constGet()->IsObject());
closureObject = v8::Local<v8::Object>::Cast(closure.constGet());
qDebug() << "Closure object members:" << scriptValueDebugListMembersV8(closure);
v8::Local<v8::Object> 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<v8::Value> backtraceV8String;
if (tryCatch.StackTrace(getContext()).ToLocal(&backtraceV8String) && backtraceV8String->IsString() &&
v8::Local<v8::String>::Cast(backtraceV8String)->Length() > 0) {
v8::String::Utf8Value backtraceUtf8Value(getIsolate(), backtraceV8String);
errorBacktrace = *backtraceUtf8Value;
if (tryCatch.StackTrace(getContext()).ToLocal(&backtraceV8String)) {
if (backtraceV8String->IsString()) {
if (v8::Local<v8::String>::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<v8::Value> backtraceV8String;
if (tryCatch.StackTrace(getContext()).ToLocal(&backtraceV8String) && backtraceV8String->IsString() &&
v8::Local<v8::String>::Cast(backtraceV8String)->Length() > 0) {
v8::String::Utf8Value backtraceUtf8Value(getIsolate(), backtraceV8String);
errorBacktrace = *backtraceUtf8Value;
if (tryCatch.StackTrace(getContext()).ToLocal(&backtraceV8String)) {
if (backtraceV8String->IsString()) {
if (v8::Local<v8::String>::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<ScriptEngineV8*>(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<int>(length)));
V8ScriptValue result(this, v8::Array::New(_v8Isolate, static_cast<int>(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<v8::String> 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<v8::String> 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<v8::String> 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<v8::Object>::Cast(info.Data());
Q_ASSERT(object->InternalFieldCount() == 2);
auto function = reinterpret_cast<ScriptEngine::FunctionSignature>
@ -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<qulonglong>(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<v8::StackFrame> stackFrame = stackTrace->GetFrame(_v8Isolate, n);
}
}*/
}*/
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<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(isolate, 100);
QStringList scriptURLs;
//V8TODO nicer formatting
for (int i = 0; i < stackTrace->GetFrameCount(); i++) {
v8::Local<v8::StackFrame> stackFrame = stackTrace->GetFrame(isolate, i);
scriptURLs.append(QString(*v8::String::Utf8Value(isolate, stackFrame->GetScriptNameOrSourceURL())));
}
return scriptURLs;
}

View file

@ -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 <typename T> class V8ScriptValueTemplate;
typedef V8ScriptValueTemplate<v8::Value> V8ScriptValue;
typedef V8ScriptValueTemplate<v8::Script> V8ScriptProgram;
using ScriptContextV8Pointer = std::shared_ptr<ScriptContextV8Wrapper>;
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<QObject*, QWeakPointer<ScriptObjectV8Proxy>>;
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<QObject*, QSharedPointer<ScriptObjectV8Proxy>> _qobjectWrapperMapV8;
ScriptContextV8Pointer pushContext(v8::Local<v8::Context> &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<V8ScriptValue(V8ScriptContext* context, ScriptEngineV8* engine)> operation,
/*ScriptValue newLambdaFunction(std::function<V8ScriptValue(V8ScriptContext* context, ScriptEngineV8* engine)> 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<v8::Context> &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<V8ScriptValue(V8ScriptContext* context, ScriptEngineV8* engine)> operation,
V8ScriptValue data);
~Lambda();
public slots:
V8ScriptValue call();
QString toString() const;
private:
ScriptEngineV8* _engine;
std::function<V8ScriptValue(V8ScriptContext* context, ScriptEngineV8* engine)> _operation;
V8ScriptValue _data;
};
#include "V8Types.h"
#endif // hifi_ScriptEngineV8_h

View file

@ -573,7 +573,7 @@ bool ScriptEngineV8::convertJSArrayToVariant(v8::Local<v8::Array> 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<v8::Object> 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<QVariant>());
case QMetaType::QObjectStar: {
QObject* obj = val.value<QObject*>();
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<QDateTime>().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<QDate>().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<QObject*>();
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);
}
}

View file

@ -76,7 +76,7 @@ V8ScriptValue ScriptObjectV8Proxy::newQObject(ScriptEngineV8* engine, QObject* o
QSharedPointer<ScriptObjectV8Proxy> 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<ScriptObjectV8Proxy> 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<ScriptObjectV8Proxy*>(v8Object->GetAlignedPointerFromInternalField(1));
}
ScriptObjectV8Proxy* ScriptObjectV8Proxy::unwrapProxy(v8::Isolate* isolate, v8::Local<v8::Value> &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<v8::Object> v8Object = v8::Local<v8::Object>::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<ScriptObjectV8Proxy*>(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<v8::Isolate*>(_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<v8::Name> 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<v8::Value> 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<v8::String>::Cast(name));
V8ScriptValue object(proxy->_engine, objectV8);
Q_ASSERT(name->IsString());
V8ScriptString nameString(proxy->_engine, v8::Local<v8::String>::Cast(name));
uint id;
QueryFlags flags = proxy->queryProperty(object, nameString, HandlesReadAccess, &id);
if (flags) {
@ -415,18 +437,20 @@ void ScriptObjectV8Proxy::v8Set(v8::Local<v8::Name> name, v8::Local<v8::Value> 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<v8::Value> 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<v8::String>::Cast(name));
V8ScriptValue object(proxy->_engine, objectV8);
Q_ASSERT(name->IsString());
V8ScriptString nameString(proxy->_engine, v8::Local<v8::String>::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<v8::Object>?
@ -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<v8::Value> property;
if(_v8Object.Get(_engine->getIsolate())->GetInternalField(2).As<v8::Object>()->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<void*>(internalPointsToQVariantProxy));
variantData->SetAlignedPointerInInternalField(1, reinterpret_cast<void*>(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<void*>(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<v8::Value>& argume
Q_ASSERT(methodArgTypeId != QMetaType::UnknownType);
v8::Local<v8::Value> 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<v8::Value>& 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<v8::Value>& argume
v8::Local<v8::Value> 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<v8::Function> callback = v8::Local<v8::Function>::Cast(conn.callback.get());
v8::Local<v8::Value> 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<v8::Function> callback = v8::Local<v8::Function>::Cast(conn.callback.get());
auto functionContext = callback->CreationContext();
_engine->pushContext(functionContext);
v8::Context::Scope functionContextScope(functionContext);
v8::Local<v8::Value> 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<v8::Function> destFunction = v8::Local<v8::Function>::Cast(callback.get());
v8::Local<v8::String> destDataName = v8::String::NewFromUtf8(isolate, "__data__").ToLocalChecked();
v8::Local<v8::Value> destData;
@ -1166,7 +1204,10 @@ void ScriptSignalV8Proxy::connect(ScriptValue arg0, ScriptValue arg1) {
//Q_ASSERT(destFunction->InternalFieldCount() == 4);
//Q_ASSERT(destData.get()->IsArray());
//v8::Local<v8::Value> 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<v8::Array> destArray = v8::Local<v8::Array>::Cast(destData);
int length = destArray->Length();//destData.property("length").toInteger();
v8::Local<v8::Array> 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<v8::Array> destArray = v8::Local<v8::Array>::Cast(destData);
int length = destArray->Length();//destData.property("length").toInteger();
v8::Local<v8::Array> newArray = v8::Array::New(isolate, length - 1);

View file

@ -25,6 +25,7 @@
#include "../ScriptEngine.h"
#include "../Scriptable.h"
#include "ScriptEngineV8.h"
#include "V8Types.h"
#include <shared/ReadWriteLockable.h>
@ -37,20 +38,20 @@ class ScriptObjectV8Proxy final {
private: // implementation
class PropertyDef {
public:
PropertyDef(v8::Isolate *isolate, v8::Local<v8::String> string) : name(isolate, string) {};
PropertyDef(ScriptEngineV8 *engine, v8::Local<v8::String> string) : name(engine, string) {};
V8ScriptString name;
ScriptValue::PropertyFlags flags;
};
class MethodDef {
public:
MethodDef(v8::Isolate *isolate, v8::Local<v8::String> string) : name(isolate, string) {};
MethodDef(ScriptEngineV8 *engine, v8::Local<v8::String> string) : name(engine, string) {};
V8ScriptString name;
int numMaxParms;
QList<QMetaMethod> methods;
};
class SignalDef {
public:
SignalDef(v8::Isolate *isolate, v8::Local<v8::String> string) : name(isolate, string) {};
SignalDef(ScriptEngineV8 *engine, v8::Local<v8::String> 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<v8::Value>& value);
static QObject* unwrap(const V8ScriptValue& val);
inline QObject* toQObject() const { return _object; }
inline v8::Local<v8::Object> toV8Value() const {

View file

@ -52,9 +52,9 @@ bool ScriptProgramV8Wrapper::compile() {
v8::ScriptOrigin scriptOrigin(isolate, v8::String::NewFromUtf8(isolate, _url.toStdString().c_str()).ToLocalChecked());
v8::Local<v8::Script> 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<v8::Value> backtraceV8String;
if (tryCatch.StackTrace(context).ToLocal(&backtraceV8String) && backtraceV8String->IsString() &&
v8::Local<v8::String>::Cast(backtraceV8String)->Length() > 0) {
v8::String::Utf8Value backtraceUtf8Value(isolate, backtraceV8String);
errorBacktrace = *backtraceUtf8Value;
if (tryCatch.StackTrace(context).ToLocal(&backtraceV8String)) {
if (backtraceV8String->IsString()) {
if (v8::Local<v8::String>::Cast(backtraceV8String)->Length() > 0) {
v8::String::Utf8Value backtraceUtf8Value(isolate, backtraceV8String);
errorBacktrace = *backtraceUtf8Value;
}
}
}
}
//V8TODO

View file

@ -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<v8::Script>()) {}
_engine(engine), _source(source), _url(url), _value(engine, v8::Local<v8::Script>()) {}
static ScriptProgramV8Wrapper* unwrap(ScriptProgramPointer val);
bool compile();
inline const V8ScriptProgram& toV8Value() const { return _value; }

View file

@ -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 {

View file

@ -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<v8::Function> v8Function = v8::Local<v8::Function>::Cast(_value.get());
v8::TryCatch tryCatch(isolate);
v8::Local<v8::Value> recv;
@ -101,7 +101,7 @@ ScriptValue ScriptValueV8Wrapper::call(const ScriptValue& thisObject, const Scri
}
v8::Local<v8::Value> 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<v8::Function> v8Function = v8::Local<v8::Function>::Cast(_value.get());
auto maybeResult = v8Function->NewInstance(_engine->getContext(), args.length(), v8Args);
v8::Local<v8::Object> 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<v8::Value> resultLocal;
const v8::Local<v8::Object> object = v8::Local<v8::Object>::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)));
}
}

View file

@ -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 {

View file

@ -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<V8ScriptValue(V8ScriptContext* context, ScriptEngineV8* engine)> operation,
V8ScriptValue data);
~Lambda();
public slots:
V8ScriptValue call();
QString toString() const;
private:
ScriptEngineV8* _engine;
std::function<V8ScriptValue(V8ScriptContext* context, ScriptEngineV8* engine)> _operation;
V8ScriptValue _data;
};
#endif

View file

@ -17,6 +17,8 @@
#include <libplatform/libplatform.h>
#include <v8.h>
#include "ScriptEngineV8.h"
template <typename T>
class V8ScriptValueTemplate {
public:
@ -24,66 +26,83 @@ public:
//V8ScriptValueTemplate(v8::Isolate *isolate, v8::Local<T> value) : _isolate(isolate) {
//_value.reset(v8::UniquePersistent<T>::New(_isolate, value));
//};
V8ScriptValueTemplate(v8::Isolate *isolate, const v8::Local<T> 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<T>(_isolate, std::move(value)));
V8ScriptValueTemplate(ScriptEngineV8 *engine, const v8::Local<T> 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<T>(_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<T>(_engine->getIsolate(), std::move(copied.constGet())));
}
v8::Local<T> 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<T> 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<T>&& copy() const {
Q_ASSERT(_isolate->IsCurrent());
v8::HandleScope handleScope(_isolate);
return new V8ScriptValueTemplate(_isolate, v8::Local<T>::New(_isolate, constGet()));};
/*V8ScriptValueTemplate<T>&& copy() const {
Q_ASSERT(_engine->getIsolate()->IsCurrent());
v8::HandleScope handleScope(_engine->getIsolate());
return new V8ScriptValueTemplate(_engine->getIsolate(), v8::Local<T>::New(_engine->getIsolate(), constGet()));
};*/
const v8::Local<v8::Context> 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<v8::Context, v8::CopyablePersistentTraits<v8::Context>>& getContext() { return _context;};
ScriptEngineV8* getEngine() const { return _engine; };
v8::Local<v8::Context> 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<T>) {};
void reset(v8::Isolate *isolate, v8::Local<T>) {
Q_ASSERT(false);
};
private:
std::shared_ptr<v8::UniquePersistent<T>> _value;
// V8TODO: maybe make it weak
// does it need , CopyablePersistentTraits<Value>?
// V8TODO: is context needed at all?
v8::Isolate *_isolate;
v8::Persistent<v8::Context, v8::CopyablePersistentTraits<v8::Context>> _context;
//v8::Isolate *_isolate;
ScriptEngineV8 *_engine;
//v8::Persistent<v8::Context, v8::CopyablePersistentTraits<v8::Context>> _context;
};
typedef V8ScriptValueTemplate<v8::Value> V8ScriptValue;
typedef V8ScriptValueTemplate<v8::Script> V8ScriptProgram;
//typedef V8ScriptValueTemplate<v8::Value> V8ScriptValue;
//typedef V8ScriptValueTemplate<v8::Script> V8ScriptProgram;
// V8TODO: Maybe weak?
typedef v8::Persistent<v8::Context> V8ScriptContext;
class V8ScriptString : public V8ScriptValueTemplate<v8::String> {
public:
V8ScriptString() = delete;
V8ScriptString(v8::Isolate *isolate, const v8::Local<v8::String> value) : V8ScriptValueTemplate<v8::String>(isolate, value) {};
V8ScriptString(ScriptEngineV8 *engine, const v8::Local<v8::String> value) : V8ScriptValueTemplate<v8::String>(engine, value) {};
const QString toQString() const {
Q_ASSERT(constGetIsolate()->IsCurrent());
Q_ASSERT(constGet()->IsString());

View file

@ -1,4 +1,4 @@
"use strict";
"no use strict";
// inEditMode.js
//

View file

@ -1,4 +1,4 @@
"use strict";
"no use strict";
// inVREditMode.js
//

View file

@ -1,4 +1,4 @@
"use strict";
"no use strict";
// stylusInput.js
//

View file

@ -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;