Further work on evaluateInClosure

This commit is contained in:
ksuprynowicz 2023-01-08 22:19:17 +01:00
parent 70d4a43009
commit ed968e1ebd
5 changed files with 63 additions and 10 deletions

View file

@ -127,7 +127,7 @@ public:
virtual void updateMemoryCost(const qint64& deltaSize) = 0;
virtual void requestCollectGarbage() = 0;
virtual void compileTest() = 0;
virtual QString scriptValueDebugDetails(ScriptValue &value) = 0;
virtual QString scriptValueDebugDetails(const ScriptValue &value) = 0;
public:
// helper to detect and log warnings when other code invokes QScriptEngine/BaseScriptEngine in thread-unsafe ways

View file

@ -1422,6 +1422,7 @@ ScriptValue ScriptManager::instantiateModule(const ScriptValue& module, const QS
closure.setProperty("require", module.property("require"));
closure.setProperty("__filename", modulePath, READONLY_HIDDEN_PROP_FLAGS);
closure.setProperty("__dirname", QString(modulePath).replace(QRegExp("/[^/]*$"), ""), READONLY_HIDDEN_PROP_FLAGS);
_engine->scriptValueDebugDetails(module);
result = _engine->evaluateInClosure(closure, _engine->newProgram( sourceCode, modulePath ));
}
_engine->maybeEmitUncaughtException(__FUNCTION__);

View file

@ -800,20 +800,24 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure,
Q_ASSERT(false);
return nullValue();
}
//qDebug() << "Closure global details:" << scriptValueDebugDetailsV8(V8ScriptValue(_v8Isolate, closureGlobal));
}
//_v8Context.Get(_v8Isolate)->DetachGlobal();
//oldGlobal = _v8Context.Get(_v8Isolate)->Global();
v8::Local<v8::Context> closureContext;
if (closureGlobal->IsObject()) {
// V8TODO
/*if (closureGlobal->IsObject()) {
#ifdef DEBUG_JS
qCDebug(shared) << " setting global = closure.global" << shortName;
#endif
closureContext = v8::Context::New(_v8Isolate, nullptr, v8::Local<v8::ObjectTemplate>(), closureGlobal);
closureContext = v8::Context::New(_v8Isolate, nullptr, v8::Local<v8::ObjectTemplate>(), closureGlobal);
//setGlobalObject(global);
} else {
closureContext = v8::Context::New(_v8Isolate);
}
}*/
closureContext = v8::Context::New(_v8Isolate, nullptr, v8::Local<v8::ObjectTemplate>(), closureObject);
ScriptValue result;
//auto context = pushContext();
@ -828,23 +832,25 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure,
const V8ScriptProgram& program = unwrappedProgram->toV8Value();
v8::Local<v8::Value> thiz;
// V8TODO: not sure if "this" doesn't exist or is empty in some cases
if (!closureObject->Get(closure.constGetContext(), v8::String::NewFromUtf8(_v8Isolate, "this").ToLocalChecked())
// V8TODO: not sure if "this" is used at all here
/*if (!closureObject->Get(closure.constGetContext(), v8::String::NewFromUtf8(_v8Isolate, "this").ToLocalChecked())
.ToLocal(&thiz)) {
_evaluatingCounter--;
qDebug(scriptengine) << "Empty this object in closure";
Q_ASSERT(false);
return nullValue();
}
}*/
//thiz = closure.property("this");
if (thiz->IsObject()) {
//qDebug() << "Closure this details:" << scriptValueDebugDetailsV8(V8ScriptValue(_v8Isolate, thiz));
// V8TODO:
/*if (thiz->IsObject()) {
#ifdef DEBUG_JS
qCDebug(shared) << " setting this = closure.this" << shortName;
#endif
//V8TODO I don't know how to do this in V8, will adding "this" to global object work?
closureContext->Global()->Set(closureContext, v8::String::NewFromUtf8(_v8Isolate, "this").ToLocalChecked(), thiz);
//context->setThisObject(thiz);
}
}*/
//context->pushScope(closure);
#ifdef DEBUG_JS
@ -852,12 +858,15 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure,
#endif
{
v8::TryCatch tryCatch(getIsolate());
//qDebug(scriptengine) << "Closure before run:" << scriptValueDebugDetailsV8(closure);
auto maybeResult = program.constGet()->Run(closureContext);
//qDebug(scriptengine) << "Closure after run:" << scriptValueDebugDetailsV8(closure);
v8::Local<v8::Value> v8Result;
if (!maybeResult.ToLocal(&v8Result)) {
v8::String::Utf8Value utf8Value(getIsolate(), tryCatch.Exception());
QString errorMessage = QString(*utf8Value);
qWarning(scriptengine) << __FUNCTION__ << "---------- hasCaught:" << errorMessage;
qWarning(scriptengine) << __FUNCTION__ << "---------- tryCatch details:" << formatErrorMessageFromTryCatch(tryCatch);
//V8TODO: better error reporting
}
@ -1513,7 +1522,7 @@ void ScriptEngineV8::compileTest() {
}
}
QString ScriptEngineV8::scriptValueDebugDetails(ScriptValue &value) {
QString ScriptEngineV8::scriptValueDebugDetails(const ScriptValue &value) {
V8ScriptValue v8Value = ScriptValueV8Wrapper::fullUnwrap(this, value);
return scriptValueDebugDetailsV8(v8Value);
}

View file

@ -127,7 +127,7 @@ public: // ScriptEngine implementation
virtual void updateMemoryCost(const qint64& deltaSize) override;
virtual void requestCollectGarbage() override { while(!_v8Isolate->IdleNotificationDeadline(getV8Platform()->MonotonicallyIncreasingTime() + GARBAGE_COLLECTION_TIME_LIMIT_S)) {}; }
virtual void compileTest() override;
virtual QString scriptValueDebugDetails(ScriptValue &value) override;
virtual QString scriptValueDebugDetails(const ScriptValue &value) override;
QString scriptValueDebugDetailsV8(const V8ScriptValue &value);
// helper to detect and log warnings when other code invokes QScriptEngine/BaseScriptEngine in thread-unsafe ways
@ -171,6 +171,9 @@ public: // not for public use, but I don't like how Qt strings this along with p
ScriptEngine::DemarshalFunction demarshalFunc) override;
int computeCastPenalty(const V8ScriptValue& val, int destTypeId);
bool castValueToVariant(const V8ScriptValue& val, QVariant& dest, int destTypeId);
// Converts JS objects created in V8 to variants. Iterates over all properties and converts them to variants.
bool convertJSObjectToVariant(v8::Local<v8::Object> object, QVariant &dest);
V8ScriptValue castVariantToValue(const QVariant& val);
QString valueType(const V8ScriptValue& val);
v8::Isolate* getIsolate() {return _v8Isolate;}

View file

@ -383,6 +383,12 @@ bool ScriptEngineV8::castValueToVariant(const V8ScriptValue& v8Val, QVariant& de
break;
}
}
// This is for generic JS objects
if (val->IsObject()) {
if (convertJSObjectToVariant(v8::Local<v8::Object>::Cast(val), dest)) {
break;
}
}
// V8TODO
errorMessage = QString() + "Conversion failure: " + QString(*v8::String::Utf8Value(_v8Isolate, val->ToDetailString(getConstContext()).ToLocalChecked()))
+ "to variant. Destination type: " + QMetaType::typeName(destTypeId) +" details: "+ scriptValueDebugDetailsV8(v8Val);
@ -487,6 +493,12 @@ bool ScriptEngineV8::castValueToVariant(const V8ScriptValue& v8Val, QVariant& de
return true;
}
}
if (val->IsObject()) {
if (convertJSObjectToVariant(v8::Local<v8::Object>::Cast(val), dest)) {
return true;
}
}
//V8TODO is v8::Array to QVariant conversion used anywhere?
errorMessage = QString() + "Conversion to variant failed: " + QString(*v8::String::Utf8Value(_v8Isolate, val->ToDetailString(getConstContext()).ToLocalChecked()))
+ " Destination type: " + QMetaType::typeName(destTypeId) + " Value details: " + scriptValueDebugDetailsV8(v8Val);
qDebug() << errorMessage;
@ -532,6 +544,34 @@ bool ScriptEngineV8::castValueToVariant(const V8ScriptValue& v8Val, QVariant& de
return destTypeId == QMetaType::UnknownType || dest.userType() == destTypeId || dest.convert(destTypeId);
}
bool ScriptEngineV8::convertJSObjectToVariant(v8::Local<v8::Object> object, QVariant &dest) {
auto context = getContext();
v8::Local<v8::Array> names;
if(object->GetPropertyNames(context).ToLocal(&names)) {
qDebug() << "ScriptEngineV8::convertJSObjectToVariant could not get property names";
return false;
}
int length = names->Length();
QHash<QString, QVariant> properties;
for (int i = 0; i < length; i++) {
v8::Local<v8::Value> v8Property;
QString name = *v8::String::Utf8Value(_v8Isolate, names->Get(context, i).ToLocalChecked());
if (!object->Get(context, names->Get(context, i).ToLocalChecked()).ToLocal(&v8Property)) {
qDebug() << "ScriptEngineV8::convertJSObjectToVariant could not get property: " + name;
continue;
}
QVariant property;
// Maybe QMetaType::QVariant?
if (castValueToVariant(V8ScriptValue(_v8Isolate, v8Property), property, QMetaType::UnknownType)) {
properties.insert( name, property);
} else {
qDebug() << "ScriptEngineV8::convertJSObjectToVariant could cast property to variant: " + name;
;
}
}
dest = QVariant(properties);
}
QString ScriptEngineV8::valueType(const V8ScriptValue& v8Val) {
// V8TODO
v8::HandleScope handleScope(const_cast<v8::Isolate*>(v8Val.constGetIsolate()));