History

Initial V8 support, not working yet

ksuprynowicz committed 4 years ago

Commit 7e9ad391 parent df507a740 files · +4040 −3087Browse files

Showing the changes to libraries/script-engine/src/v8/ScriptEngineV8.cpp (1 of 40 files). Show all

Unified · Split

libraries/script-engine/src/v8/ScriptEngineV8.cpp+1093−0
@@ -0,0 +1,1093 @@
1+//
2+// ScriptEngineV8.cpp
3+// libraries/script-engine/src/v8
4+//
5+// Created by Brad Hefta-Gaub on 12/14/13.
6+// Modified for V8 by dr Karol Suprynowicz on 2022/10/08
7+// Copyright 2013 High Fidelity, Inc.
8+// Copyright 2022 Overte e.V.
9+//
10+// Distributed under the Apache License, Version 2.0.
11+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
12+//
13+
14+#include "ScriptEngineV8.h"
15+
16+#include <chrono>
17+#include <mutex>
18+#include <thread>
19+
20+#include <QtCore/QCoreApplication>
21+#include <QtCore/QEventLoop>
22+#include <QtCore/QFileInfo>
23+#include <QtCore/QTimer>
24+#include <QtCore/QThread>
25+#include <QtCore/QRegularExpression>
26+
27+#include <QtCore/QFuture>
28+#include <QtConcurrent/QtConcurrentRun>
29+
30+#include <QtWidgets/QMainWindow>
31+#include <QtWidgets/QApplication>
32+#include <QtWidgets/QMenuBar>
33+#include <QtWidgets/QMenu>
34+
35+#include <QtNetwork/QNetworkRequest>
36+#include <QtNetwork/QNetworkReply>
37+
38+#include <shared/LocalFileAccessGate.h>
39+#include <shared/QtHelpers.h>
40+#include <shared/AbstractLoggerInterface.h>
41+
42+#include <Profile.h>
43+
44+#include "../ScriptEngineLogging.h"
45+#include "../ScriptProgram.h"
46+#include "../ScriptValue.h"
47+
48+#include "ScriptContextV8Wrapper.h"
49+#include "ScriptObjectV8Proxy.h"
50+#include "ScriptProgramV8Wrapper.h"
51+#include "ScriptValueV8Wrapper.h"
52+
53+static const int MAX_DEBUG_VALUE_LENGTH { 80 };
54+
55+std::once_flag ScriptEngineV8::_v8InitOnceFlag;
56+QMutex ScriptEngineV8::_v8InitMutex;
57+
58+bool ScriptEngineV8::IS_THREADSAFE_INVOCATION(const QThread* thread, const QString& method) {
59+ const QThread* currentThread = QThread::currentThread();
60+ if (currentThread == thread) {
61+ return true;
62+ }
63+ qCCritical(scriptengine) << QString("Scripting::%1 @ %2 -- ignoring thread-unsafe call from %3")
64+ .arg(method)
65+ .arg(thread ? thread->objectName() : "(!thread)")
66+ .arg(QThread::currentThread()->objectName());
67+ qCDebug(scriptengine) << "(please resolve on the calling side by using invokeMethod, executeOnScriptThread, etc.)";
68+ Q_ASSERT(false);
69+ return false;
70+}
71+
72+// engine-aware JS Error copier and factory
73+V8ScriptValue ScriptEngineV8::makeError(const V8ScriptValue& _other, const QString& type) {
74+ if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
75+ return V8ScriptValue(_v8Isolate, v8::Null(_v8Isolate));
76+ }
77+ return V8ScriptValue(_v8Isolate, v8::Null(_v8Isolate));
78+ //V8TODO
79+ /*
80+ auto other = _other;
81+ if (_other.constGet()->IsString()) {
82+ other = QScriptEngine::newObject();
83+ other.setProperty("message", _other.toString());
84+ }
85+ auto proto = QScriptEngine::globalObject().property(type);
86+ if (!proto.isFunction()) {
87+ proto = QScriptEngine::globalObject().property(other.prototype().property("constructor").property("name").toString());
88+ }
89+ if (!proto.isFunction()) {
90+#ifdef DEBUG_JS_EXCEPTIONS
91+ qCDebug(shared) << "BaseScriptEngine::makeError -- couldn't find constructor for" << type << " -- using Error instead";
92+#endif
93+ proto = QScriptEngine::globalObject().property("Error");
94+ }
95+ if (other.engine() != this) {
96+ // JS Objects are parented to a specific script engine instance
97+ // -- this effectively ~clones it locally by routing through a QVariant and back
98+ other = QScriptEngine::toScriptValue(other.toVariant());
99+ }
100+ // ~ var err = new Error(other.message)
101+ auto err = proto.construct(V8ScriptValueList({ other.property("message") }));
102+
103+ // transfer over any existing properties
104+ V8ScriptValueIterator it(other);
105+ while (it.hasNext()) {
106+ it.next();
107+ err.setProperty(it.name(), it.value());
108+ }
109+ return err;*/
110+}
111+
112+ScriptValue ScriptEngineV8::makeError(const ScriptValue& _other, const QString& type) {
113+ if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
114+ return nullValue();
115+ }
116+ return nullValue();
117+
118+ //V8TODO
119+ //what does makeError actually do?
120+ /*ScriptValueV8Wrapper* unwrapped = ScriptValueV8Wrapper::unwrap(_other);
121+ V8ScriptValue other;
122+ if (_other.isString()) {
123+ other = QScriptEngine::newObject();
124+ other.setProperty("message", _other.toString());
125+ } else if (unwrapped) {
126+ other = unwrapped->toV8Value();
127+ } else {
128+ other = QScriptEngine::newVariant(_other.toVariant());
129+ }
130+ V8ScriptValue result = makeError(other, type);
131+ return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result)));*/
132+}
133+
134+// check syntax and when there are issues returns an actual "SyntaxError" with the details
135+ScriptValue ScriptEngineV8::cheskScriptSyntax(ScriptProgramPointer program) {
136+ if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
137+ return nullValue();
138+ }
139+ ScriptSyntaxCheckResultPointer syntaxCheck = program->checkSyntax();
140+ //V8TODO
141+ if (syntaxCheck->state() != ScriptSyntaxCheckResult::Valid) {
142+ auto err = globalObject().property("SyntaxError").construct(ScriptValueList({ newValue(syntaxCheck->errorMessage()) }));
143+ err.setProperty("fileName", program->fileName());
144+ err.setProperty("lineNumber", syntaxCheck->errorLineNumber());
145+ err.setProperty("expressionBeginOffset", syntaxCheck->errorColumnNumber());
146+ err.setProperty("stack", syntaxCheck->errorBacktrace());
147+ //err.setProperty("stack", currentContext()->backtrace().join(ScriptManager::SCRIPT_BACKTRACE_SEP));
148+ {
149+ const auto error = syntaxCheck->errorMessage();
150+ const auto line = QString::number(syntaxCheck->errorLineNumber());
151+ const auto column = QString::number(syntaxCheck->errorColumnNumber());
152+ // for compatibility with legacy reporting
153+ const auto message = QString("[SyntaxError] %1 in %2:%3(%4)").arg(error, program->fileName(), line, column);
154+ err.setProperty("formatted", message);
155+ }
156+ return err;
157+ }
158+ return undefinedValue();
159+}
160+/*ScriptValue ScriptEngineV8::lintScript(const QString& sourceCode, const QString& fileName, const int lineNumber) {
161+ if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
162+ return nullValue();
163+ }
164+ //V8TODO
165+ const auto syntaxCheck = checkSyntax(sourceCode);
166+ if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {
167+ auto err = QScriptEngine::globalObject().property("SyntaxError").construct(V8ScriptValueList({ syntaxCheck.errorMessage() }));
168+ err.setProperty("fileName", fileName);
169+ err.setProperty("lineNumber", syntaxCheck.errorLineNumber());
170+ err.setProperty("expressionBeginOffset", syntaxCheck.errorColumnNumber());
171+ err.setProperty("stack", currentContext()->backtrace().join(ScriptManager::SCRIPT_BACKTRACE_SEP));
172+ {
173+ const auto error = syntaxCheck.errorMessage();
174+ const auto line = QString::number(syntaxCheck.errorLineNumber());
175+ const auto column = QString::number(syntaxCheck.errorColumnNumber());
176+ // for compatibility with legacy reporting
177+ const auto message = QString("[SyntaxError] %1 in %2:%3(%4)").arg(error, fileName, line, column);
178+ err.setProperty("formatted", message);
179+ }
180+ return ScriptValue(new ScriptValueV8Wrapper(this, std::move(err)));
181+ }
182+ return undefinedValue();
183+}*/
184+
185+// this pulls from the best available information to create a detailed snapshot of the current exception
186+ScriptValue ScriptEngineV8::cloneUncaughtException(const QString& extraDetail) {
187+ if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
188+ return nullValue();
189+ }
190+ if (!hasUncaughtException()) {
191+ return nullValue();
192+ }
193+ return nullValue();
194+ //V8TODO
195+ /*
196+ auto exception = uncaughtException();
197+ // ensure the error object is engine-local
198+ auto err = makeError(exception);
199+
200+ // not sure why Qt does't offer uncaughtExceptionFileName -- but the line number
201+ // on its own is often useless/wrong if arbitrarily married to a filename.
202+ // when the error object already has this info, it seems to be the most reliable
203+ auto fileName = exception.property("fileName").toString();
204+ auto lineNumber = exception.property("lineNumber").toInt32();
205+
206+ // the backtrace, on the other hand, seems most reliable taken from uncaughtExceptionBacktrace
207+ auto backtrace = uncaughtExceptionBacktrace();
208+ if (backtrace.isEmpty()) {
209+ // fallback to the error object
210+ backtrace = exception.property("stack").toString().split(ScriptManager::SCRIPT_BACKTRACE_SEP);
211+ }
212+ // the ad hoc "detail" property can be used now to embed additional clues
213+ auto detail = exception.property("detail").toString();
214+ if (detail.isEmpty()) {
215+ detail = extraDetail;
216+ } else if (!extraDetail.isEmpty()) {
217+ detail += "(" + extraDetail + ")";
218+ }
219+ if (lineNumber <= 0) {
220+ lineNumber = uncaughtExceptionLineNumber();
221+ }
222+ if (fileName.isEmpty()) {
223+ // climb the stack frames looking for something useful to display
224+ for (auto c = QScriptEngine::currentContext(); c && fileName.isEmpty(); c = c->parentContext()) {
225+ V8ScriptContextInfo info{ c };
226+ if (!info.fileName().isEmpty()) {
227+ // take fileName:lineNumber as a pair
228+ fileName = info.fileName();
229+ lineNumber = info.lineNumber();
230+ if (backtrace.isEmpty()) {
231+ backtrace = c->backtrace();
232+ }
233+ break;
234+ }
235+ }
236+ }
237+ err.setProperty("fileName", fileNlintame);
238+ err.setProperty("lineNumber", lineNumber);
239+ err.setProperty("detail", detail);
240+ err.setProperty("stack", backtrace.join(ScriptManager::SCRIPT_BACKTRACE_SEP));
241+
242+#ifdef DEBUG_JS_EXCEPTIONS
243+ err.setProperty("_fileName", exception.property("fileName").toString());
244+ err.setProperty("_stack", uncaughtExceptionBacktrace().join(SCRIPT_BACKTRACE_SEP));
245+ err.setProperty("_lineNumber", uncaughtExceptionLineNumber());
246+#endif
247+ return err;
248+ */
249+}
250+
251+bool ScriptEngineV8::raiseException(const V8ScriptValue& exception) {
252+ if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
253+ return false;
254+ }
255+ //V8TODO
256+ _v8Isolate->ThrowException(makeError(exception).get());
257+ /*if (QScriptEngine::currentContext()) {
258+ // we have an active context / JS stack frame so throw the exception per usual
259+ QScriptEngine::currentContext()->throwValue(makeError(exception));
260+ return true;
261+ } else if (_scriptManager) {
262+ // we are within a pure C++ stack frame (ie: being called directly by other C++ code)
263+ // in this case no context information is available so just emit the exception for reporting
264+ V8ScriptValue thrown = makeError(exception);
265+ emit _scriptManager->unhandledException(ScriptValue(new ScriptValueV8Wrapper(this, std::move(thrown))));
266+ }*/
267+ //emit _scriptManager->unhandledException(ScriptValue(new ScriptValueV8Wrapper(this, std::move(thrown))));
268+ return false;
269+}
270+
271+bool ScriptEngineV8::maybeEmitUncaughtException(const QString& debugHint) {
272+ if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
273+ return false;
274+ }
275+ if (!isEvaluating() && hasUncaughtException() && _scriptManager) {
276+ emit _scriptManager->unhandledException(cloneUncaughtException(debugHint));
277+ clearExceptions();
278+ return true;
279+ }
280+ return false;
281+}
282+
283+// Lambda
284+ScriptValue ScriptEngineV8::newLambdaFunction(std::function<V8ScriptValue(V8ScriptContext*, ScriptEngineV8*)> operation,
285+ const V8ScriptValue& data,
286+ const ValueOwnership& ownership) {
287+ auto lambda = new Lambda(this, operation, data);
288+ auto object = newQObject(lambda, ownership);
289+ //V8TODO?
290+ auto call = object.property("call");
291+ call.setPrototype(object); // context->callee().prototype() === Lambda QObject
292+ call.setData(ScriptValue(new ScriptValueV8Wrapper(this, data))); // context->callee().data() will === data param
293+ return call;
294+}
295+QString Lambda::toString() const {
296+ v8::Local<v8::String> string;
297+ QString qString("");
298+ if (_data.constGet()->ToString(_engine->getContext()).ToLocal(&string)) {
299+ v8::String::Utf8Value utf8Value(_engine->getIsolate(), string);
300+ qString = QString(*utf8Value);
301+ }
302+ //V8TODO it was data.isValid() originally
303+ //I have no idea what happens here
304+ return QString("[Lambda%1]").arg((!_data.constGet()->IsNullOrUndefined()) ? " " + qString : qString);
305+}
306+
307+Lambda::~Lambda() {
308+#ifdef DEBUG_JS_LAMBDA_FUNCS
309+ qDebug() << "~Lambda"
310+ << "this" << this;
311+#endif
312+}
313+
314+Lambda::Lambda(ScriptEngineV8* engine,
315+ std::function<V8ScriptValue(V8ScriptContext*, ScriptEngineV8*)> operation,
316+ V8ScriptValue data) :
317+ _engine(engine),
318+ _operation(operation), _data(data) {
319+#ifdef DEBUG_JS_LAMBDA_FUNCS
320+ qDebug() << "Lambda" << data.toString();
321+#endif
322+}
323+V8ScriptValue Lambda::call() {
324+ if (!_engine->IS_THREADSAFE_INVOCATION(__FUNCTION__)) {
325+ return V8ScriptValue(_engine->getIsolate(), v8::Null(_engine->getIsolate()));
326+ }
327+ return V8ScriptValue(_engine->getIsolate(), v8::Null(_engine->getIsolate()));
328+ // V8TODO: something is obviously wrong here, there's no call at all?
329+ //return operation(static_cast<QScriptEngine*>(engine)->currentContext(), engine);
330+}
331+
332+#ifdef DEBUG_JS
333+void ScriptEngineV8::_debugDump(const QString& header, const V8ScriptValue& object, const QString& footer) {
334+ if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
335+ return;
336+ }
337+ if (!header.isEmpty()) {
338+ qCDebug(shared) << header;
339+ }
340+ if (!object.isObject()) {
341+ qCDebug(shared) << "(!isObject)" << object.toVariant().toString() << object.toString();
342+ return;
343+ }
344+ V8ScriptValueIterator it(object);
345+ while (it.hasNext()) {
346+ it.next();
347+ qCDebug(shared) << it.name() << ":" << it.value().toString();
348+ }
349+ if (!footer.isEmpty()) {
350+ qCDebug(shared) << footer;
351+ }
352+}
353+#endif
354+
355+v8::Platform* ScriptEngineV8::getV8Platform() {
356+ static std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
357+ return platform.get();
358+}
359+
360+ScriptEngineV8::ScriptEngineV8(ScriptManager* scriptManager) :
361+ _scriptManager(scriptManager)
362+ //V8TODO
363+ //_arrayBufferClass(new ArrayBufferClass(this))
364+{
365+ _v8InitMutex.lock();
366+ std::call_once ( _v8InitOnceFlag, [ ]{
367+ v8::Platform* platform = getV8Platform();
368+ v8::V8::InitializePlatform(platform);
369+ v8::V8::Initialize();
370+ } );
371+ _v8InitMutex.unlock();
372+ v8::Isolate::CreateParams isolateParams;
373+ isolateParams.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
374+ _v8Isolate = v8::Isolate::New(isolateParams);
375+ v8::Local<v8::Context> context = v8::Context::New(_v8Isolate);
376+ _v8Context = v8::UniquePersistent<v8::Context>(_v8Isolate, context);
377+
378+ registerSystemTypes();
379+
380+ _currentThread = QThread::currentThread();
381+ // V8TODO: port to V8
382+ /*
383+ if (_scriptManager) {
384+ connect(this, &QScriptEngine::signalHandlerException, this, [this](const V8ScriptValue& exception) {
385+ if (hasUncaughtException()) {
386+ // the engine's uncaughtException() seems to produce much better stack traces here
387+ emit _scriptManager->unhandledException(cloneUncaughtException("signalHandlerException"));
388+ clearExceptions();
389+ } else {
390+ // ... but may not always be available -- so if needed we fallback to the passed exception
391+ V8ScriptValue thrown = makeError(exception);
392+ emit _scriptManager->unhandledException(ScriptValue(new ScriptValueV8Wrapper(this, std::move(thrown))));
393+ }
394+ }, Qt::DirectConnection);
395+ moveToThread(scriptManager->thread());
396+ }*/
397+
398+ // V8TODO: dispose of isolate on ScriptEngineV8 destruction
399+ //v8::UniquePersistent<v8::Value> null = v8::UniquePersistent<v8::Value>(_v8Isolate, v8::Null(_v8Isolate));
400+ //_nullValue = ScriptValue(new ScriptValueV8Wrapper(this, std::move(null)));
401+ V8ScriptValue nullScriptValue(_v8Isolate, v8::Null(_v8Isolate));
402+ _nullValue = ScriptValue(new ScriptValueV8Wrapper(this, nullScriptValue));
403+
404+ //V8ScriptValue undefined = v8::UniquePersistent<v8::Value>(_v8Isolate,v8::Undefined(_v8Isolate));
405+ //_undefinedValue = ScriptValue(new ScriptValueV8Wrapper(this, std::move(undefined)));
406+ V8ScriptValue undefined(_v8Isolate, v8::Undefined(_v8Isolate));
407+ _undefinedValue = ScriptValue(new ScriptValueV8Wrapper(this, undefined));
408+
409+ // V8TODO:
410+ //QScriptEngine::setProcessEventsInterval(MSECS_PER_SECOND);
411+}
412+
413+void ScriptEngineV8::registerEnum(const QString& enumName, QMetaEnum newEnum) {
414+ if (!newEnum.isValid()) {
415+ qCCritical(scriptengine) << "registerEnum called on invalid enum with name " << enumName;
416+ return;
417+ }
418+
419+ for (int i = 0; i < newEnum.keyCount(); i++) {
420+ const char* keyName = newEnum.key(i);
421+ QString fullName = enumName + "." + keyName;
422+ registerValue(fullName, V8ScriptValue(_v8Isolate, v8::Integer::New(_v8Isolate, newEnum.keyToValue(keyName))));
423+ }
424+}
425+
426+void ScriptEngineV8::registerValue(const QString& valueName, V8ScriptValue value) {
427+ //V8TODO
428+ /*if (QThread::currentThread() != QScriptEngine::thread()) {
429+#ifdef THREAD_DEBUGGING
430+ qCDebug(scriptengine) << "*** WARNING *** ScriptEngineV8::registerValue() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";
431+#endif
432+ QMetaObject::invokeMethod(this, "registerValue",
433+ Q_ARG(const QString&, valueName),
434+ Q_ARG(V8ScriptValue, value));
435+ return;
436+ }*/
437+
438+ QStringList pathToValue = valueName.split(".");
439+ int partsToGo = pathToValue.length();
440+ v8::Local<v8::Object> partObject = _v8Context.Get(_v8Isolate)->Global();
441+
442+ for (const auto& pathPart : pathToValue) {
443+ partsToGo--;
444+ v8::Local<v8::String> pathPartV8 = v8::String::NewFromUtf8(_v8Isolate, pathPart.toStdString().c_str(),v8::NewStringType::kNormal).ToLocalChecked();
445+ v8::Local<v8::Value> currentPath;
446+ if (!partObject->Get(_v8Context.Get(_v8Isolate), pathPartV8).ToLocal(&currentPath)) {
447+ if (partsToGo > 0) {
448+ //This was commented out
449+ //QObject *object = new QObject;
450+ v8::Local<v8::Object> partValue = v8::Object::New(_v8Isolate); //newQObject(object, QScriptEngine::ScriptOwnership);
451+ //V8ScriptValue partValue = QScriptEngine::newArray(); //newQObject(object, QScriptEngine::ScriptOwnership);
452+ Q_ASSERT(partObject->Set(_v8Context.Get(_v8Isolate), pathPartV8, partValue).FromMaybe(false));
453+ } else {
454+ //partObject = currentPath->ToObject();
455+ //V8TODO: do these still happen if asserts are disabled?
456+ Q_ASSERT(partObject->Set(_v8Context.Get(_v8Isolate), pathPartV8, value.constGet()).FromMaybe(false));
457+ }
458+ }
459+ v8::Local<v8::Value> child;
460+ Q_ASSERT(partObject->Get(_v8Context.Get(_v8Isolate), pathPartV8).ToLocal(&child));
461+ }
462+}
463+
464+void ScriptEngineV8::registerGlobalObject(const QString& name, QObject* object) {
465+ //V8TODO
466+ /*if (QThread::currentThread() != QScriptEngine::thread()) {
467+#ifdef THREAD_DEBUGGING
468+ qCDebug(scriptengine) << "*** WARNING *** ScriptEngineV8::registerGlobalObject() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name;
469+#endif
470+ QMetaObject::invokeMethod(this, "registerGlobalObject",
471+ Q_ARG(const QString&, name),
472+ Q_ARG(QObject*, object));
473+ return;
474+ }
475+#ifdef THREAD_DEBUGGING
476+ qCDebug(scriptengine) << "ScriptEngineV8::registerGlobalObject() called on thread [" << QThread::currentThread() << "] name:" << name;
477+#endif*/
478+ v8::Local<v8::Object> globalObject = getContext()->Global();
479+ v8::Local<v8::String> v8Name = v8::String::NewFromUtf8(_v8Isolate, name.toStdString().c_str()).ToLocalChecked();
480+
481+ // V8TODO: Is IsEmpty check enough or IsValid is needed too?
482+ if (!globalObject->Get(getContext(), v8Name).IsEmpty()) {
483+ if (object) {
484+ V8ScriptValue value = ScriptObjectV8Proxy::newQObject(this, object, ScriptEngine::QtOwnership);
485+ globalObject->Set(getContext(), v8Name, value.get());
486+ } else {
487+ globalObject->Set(getContext(), v8Name, v8::Null(_v8Isolate));
488+ }
489+ }
490+}
491+
492+void ScriptEngineV8::registerFunction(const QString& name, ScriptEngine::FunctionSignature functionSignature, int numArguments) {
493+ //V8TODO
494+ /*if (QThread::currentThread() != QScriptEngine::thread()) {
495+#ifdef THREAD_DEBUGGING
496+ qCDebug(scriptengine) << "*** WARNING *** ScriptEngineV8::registerFunction() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name;
497+#endif
498+ QMetaObject::invokeMethod(this, "registerFunction",
499+ Q_ARG(const QString&, name),
500+ Q_ARG(QScriptEngine::FunctionSignature, functionSignature),
501+ Q_ARG(int, numArguments));
502+ return;
503+ }
504+#ifdef THREAD_DEBUGGING
505+ qCDebug(scriptengine) << "ScriptEngineV8::registerFunction() called on thread [" << QThread::currentThread() << "] name:" << name;
506+#endif*/
507+
508+ //auto scriptFun = static_cast<ScriptValueV8Wrapper*>(newFunction(functionSignature, numArguments).ptr())->toV8Value().constGet();
509+ auto scriptFun = newFunction(functionSignature, numArguments);
510+
511+ //getContext()->Global().Set();
512+ globalObject().setProperty(name, scriptFun);
513+}
514+
515+void ScriptEngineV8::registerFunction(const QString& parent, const QString& name, ScriptEngine::FunctionSignature functionSignature, int numArguments) {
516+ //V8TODO
517+ /*if (QThread::currentThread() != QScriptEngine::thread()) {
518+#ifdef THREAD_DEBUGGING
519+ qCDebug(scriptengine) << "*** WARNING *** ScriptEngineV8::registerFunction() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] parent:" << parent << "name:" << name;
520+#endif
521+ QMetaObject::invokeMethod(this, "registerFunction",
522+ Q_ARG(const QString&, name),
523+ Q_ARG(QScriptEngine::FunctionSignature, functionSignature),
524+ Q_ARG(int, numArguments));
525+ return;
526+ }
527+#ifdef THREAD_DEBUGGING
528+ qCDebug(scriptengine) << "ScriptEngineV8::registerFunction() called on thread [" << QThread::currentThread() << "] parent:" << parent << "name:" << name;
529+#endif*/
530+
531+ ScriptValue object = globalObject().property(parent);
532+ if (object.isValid()) {
533+ ScriptValue scriptFun = ScriptEngine::newFunction(functionSignature, numArguments);
534+ object.setProperty(name, scriptFun);
535+ }
536+}
537+
538+void ScriptEngineV8::registerGetterSetter(const QString& name, ScriptEngine::FunctionSignature getter,
539+ ScriptEngine::FunctionSignature setter, const QString& parent) {
540+ //V8TODO
541+ /*if (QThread::currentThread() != QScriptEngine::thread()) {
542+#ifdef THREAD_DEBUGGING
543+ qCDebug(scriptengine) << "*** WARNING *** ScriptEngineV8::registerGetterSetter() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
544+ " name:" << name << "parent:" << parent;
545+#endif
546+ QMetaObject::invokeMethod(this, "registerGetterSetter",
547+ Q_ARG(const QString&, name),
548+ Q_ARG(ScriptEngine::FunctionSignature, getter),
549+ Q_ARG(ScriptEngine::FunctionSignature, setter),
550+ Q_ARG(const QString&, parent));
551+ return;
552+ }
553+#ifdef THREAD_DEBUGGING
554+ qCDebug(scriptengine) << "ScriptEngineV8::registerGetterSetter() called on thread [" << QThread::currentThread() << "] name:" << name << "parent:" << parent;
555+#endif*/
556+
557+ ScriptValue setterFunction = newFunction(setter, 1);
558+ ScriptValue getterFunction = newFunction(getter);
559+
560+ if (!parent.isNull() && !parent.isEmpty()) {
561+ ScriptValue object = ScriptEngine::globalObject().property(parent);
562+ if (object.isValid()) {
563+ object.setProperty(name, setterFunction, ScriptValue::PropertySetter);
564+ object.setProperty(name, getterFunction, ScriptValue::PropertyGetter);
565+ }
566+ } else {
567+ globalObject().setProperty(name, setterFunction, ScriptValue::PropertySetter);
568+ globalObject().setProperty(name, getterFunction, ScriptValue::PropertyGetter);
569+ }
570+}
571+
572+ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure,
573+ const ScriptProgramPointer& _program) {
574+ PROFILE_RANGE(script, "evaluateInClosure");
575+ if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
576+ return nullValue();
577+ }
578+ ScriptProgramV8Wrapper* unwrappedProgram = ScriptProgramV8Wrapper::unwrap(_program);
579+ if (unwrappedProgram == nullptr) {
580+ return nullValue();
581+ }
582+ const V8ScriptProgram& program = unwrappedProgram->toV8Value();
583+
584+ const auto fileName = unwrappedProgram->fileName();
585+ const auto shortName = QUrl(fileName).fileName();
586+
587+ ScriptValueV8Wrapper* unwrappedClosure = ScriptValueV8Wrapper::unwrap(_closure);
588+ if (unwrappedClosure == nullptr) {
589+ return nullValue();
590+ }
591+ const V8ScriptValue& closure = unwrappedClosure->toV8Value();
592+ if (!closure.constGet()->IsObject()) {
593+ return nullValue();
594+ }
595+ const v8::Local<v8::Object> closureObject = v8::Local<v8::Object>::Cast(closure.constGet());
596+
597+ v8::Local<v8::Value> oldGlobal;
598+ v8::Local<v8::Value> closureGlobal;
599+ if (!closureObject->Get(closure.constGetContext() ,v8::String::NewFromUtf8(_v8Isolate, "global").ToLocalChecked()).ToLocal(&closureGlobal)) {
600+ return nullValue();
601+ }
602+
603+ _v8Context.Get(_v8Isolate)->Exit();
604+ _v8Context.Get(_v8Isolate)->DetachGlobal();
605+ oldGlobal = _v8Context.Get(_v8Isolate)->Global();
606+ v8::Local<v8::Context> closureContext;
607+
608+ if (closureGlobal->IsObject()) {
609+#ifdef DEBUG_JS
610+ qCDebug(shared) << " setting global = closure.global" << shortName;
611+#endif
612+ closureContext = v8::Context::New(_v8Isolate, nullptr, v8::Local<v8::ObjectTemplate>(), closureGlobal);
613+ //setGlobalObject(global);
614+ } else {
615+ closureContext = v8::Context::New(_v8Isolate, nullptr, v8::Local<v8::ObjectTemplate>(), oldGlobal);
616+ }
617+
618+ //auto context = pushContext();
619+
620+ v8::Local<v8::Value> thiz;
621+ // V8TODO: not sure if "this" doesn't exist or is empty in some cases
622+ if (!closureObject->Get(closure.constGetContext() ,v8::String::NewFromUtf8(_v8Isolate, "this").ToLocalChecked()).ToLocal(&thiz)) {
623+ return nullValue();
624+ }
625+ //thiz = closure.property("this");
626+ if (thiz->IsObject()) {
627+#ifdef DEBUG_JS
628+ qCDebug(shared) << " setting this = closure.this" << shortName;
629+#endif
630+ //V8TODO I don't know how to do this in V8, will adding "this" to global object work?
631+ closureContext->Global()->Set(closureContext, v8::String::NewFromUtf8(_v8Isolate, "this").ToLocalChecked(), thiz);
632+ //context->setThisObject(thiz);
633+ }
634+
635+ //context->pushScope(closure);
636+#ifdef DEBUG_JS
637+ qCDebug(shared) << QString("[%1] evaluateInClosure %2").arg(isEvaluating()).arg(shortName);
638+#endif
639+ ScriptValue result;
640+ {
641+ v8::TryCatch tryCatch(getIsolate());
642+ auto maybeResult = program.constGet()->Run(closureContext);
643+ v8::Local<v8::Value> v8Result;
644+ if (!maybeResult.ToLocal(&v8Result)) {
645+ v8::String::Utf8Value utf8Value(getIsolate(), tryCatch.Exception());
646+ QString errorMessage = QString(*utf8Value);
647+ qWarning() << __FUNCTION__ << "---------- hasCaught:" << errorMessage;
648+ //V8TODO: better error reporting
649+ }
650+
651+ if (hasUncaughtException()) {
652+ auto err = cloneUncaughtException(__FUNCTION__);
653+#ifdef DEBUG_JS_EXCEPTIONS
654+ qCWarning(shared) << __FUNCTION__ << "---------- hasCaught:" << err.toString() << result.toString();
655+ err.setProperty("_result", result);
656+#endif
657+ result = err;
658+ } else {
659+ result = ScriptValue(new ScriptValueV8Wrapper(this, V8ScriptValue(_v8Isolate, v8Result)));
660+ }
661+ }
662+#ifdef DEBUG_JS
663+ qCDebug(shared) << QString("[%1] //evaluateInClosure %2").arg(isEvaluating()).arg(shortName);
664+#endif
665+ //popContext();
666+ closureContext->Exit();
667+ _v8Context.Get(_v8Isolate)->Enter();
668+
669+ //This is probably unnecessary in V8
670+ /*if (oldGlobal.isValid()) {
671+#ifdef DEBUG_JS
672+ qCDebug(shared) << " restoring global" << shortName;
673+#endif
674+ setGlobalObject(oldGlobal);
675+ }*/
676+
677+ _v8Context.Get(_v8Isolate)->Enter();
678+ return result;
679+}
680+
681+ScriptValue ScriptEngineV8::evaluate(const QString& sourceCode, const QString& fileName) {
682+ if (_scriptManager && _scriptManager->isStopped()) {
683+ return undefinedValue(); // bail early
684+ }
685+
686+ //V8TODO
687+
688+ /*if (QThread::currentThread() != QScriptEngine::thread()) {
689+ ScriptValue result;
690+#ifdef THREAD_DEBUGGING
691+ qCDebug(scriptengine) << "*** WARNING *** ScriptEngineV8::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
692+ "sourceCode:" << sourceCode << " fileName:" << fileName;
693+#endif
694+ BLOCKING_INVOKE_METHOD(this, "evaluate",
695+ Q_RETURN_ARG(ScriptValue, result),
696+ Q_ARG(const QString&, sourceCode),
697+ Q_ARG(const QString&, fileName));
698+ return result;
699+ }*/
700+ // Compile and check syntax
701+ // V8TODO: Could these all be replaced with checkSyntax function from wrapper?
702+ v8::TryCatch tryCatch(getIsolate());
703+ v8::ScriptOrigin scriptOrigin(getIsolate(), v8::String::NewFromUtf8(getIsolate(), fileName.toStdString().c_str()).ToLocalChecked());
704+ v8::Local<v8::Script> script;
705+ if (!v8::Script::Compile(getContext(), v8::String::NewFromUtf8(getIsolate(), sourceCode.toStdString().c_str()).ToLocalChecked(), &scriptOrigin).ToLocal(&script)) {
706+ int errorColumnNumber = 0;
707+ int errorLineNumber = 0;
708+ QString errorMessage = "";
709+ QString errorBacktrace = "";
710+ v8::String::Utf8Value utf8Value(getIsolate(), tryCatch.Exception());
711+ errorMessage = QString(*utf8Value);
712+ v8::Local<v8::Message> exceptionMessage = tryCatch.Message();
713+ if (!exceptionMessage.IsEmpty()) {
714+ errorLineNumber = exceptionMessage->GetLineNumber(getContext()).FromJust();
715+ errorColumnNumber = exceptionMessage->GetStartColumn(getContext()).FromJust();
716+ v8::Local<v8::Value> backtraceV8String;
717+ if (tryCatch.StackTrace(getContext()).ToLocal(&backtraceV8String) && backtraceV8String->IsString() &&
718+ v8::Local<v8::String>::Cast(backtraceV8String)->Length() > 0) {
719+ v8::String::Utf8Value backtraceUtf8Value(getIsolate(), backtraceV8String);
720+ errorBacktrace = *backtraceUtf8Value;
721+ }
722+ }
723+ auto err = makeError(newValue(errorMessage));
724+ raiseException(err);
725+ maybeEmitUncaughtException("compile");
726+ return err;
727+ }
728+
729+ //V8TODO
730+ /*auto syntaxError = lintScript(sourceCode, fileName);
731+ if (syntaxError.isError()) {
732+ if (!isEvaluating()) {
733+ syntaxError.setProperty("detail", "evaluate");
734+ }
735+ raiseException(syntaxError);
736+ maybeEmitUncaughtException("lint");
737+ return syntaxError;
738+ }*/
739+ //V8TODO
740+ /*if (script->IsNull()) {
741+ // can this happen?
742+ auto err = makeError(newValue("could not create V8ScriptProgram for " + fileName));
743+ raiseException(err);
744+ maybeEmitUncaughtException("compile");
745+ return err;
746+ }*/
747+
748+ v8::Local<v8::Value> result;
749+ v8::TryCatch tryCatchRun(getIsolate());
750+ if (!script->Run(getContext()).ToLocal(&result)) {
751+ Q_ASSERT(tryCatchRun.HasCaught());
752+ auto runError = tryCatchRun.Message();
753+ ScriptValue errorValue(new ScriptValueV8Wrapper(this, V8ScriptValue(_v8Isolate, runError->Get())));
754+ raiseException(errorValue);
755+ maybeEmitUncaughtException("evaluate");
756+ return errorValue;
757+ }
758+ V8ScriptValue resultValue(_v8Isolate, result);
759+ return ScriptValue(new ScriptValueV8Wrapper(this, std::move(resultValue)));
760+}
761+
762+Q_INVOKABLE ScriptValue ScriptEngineV8::evaluate(const ScriptProgramPointer& program) {
763+ if (_scriptManager && _scriptManager->isStopped()) {
764+ return undefinedValue(); // bail early
765+ }
766+
767+ //V8TODO
768+ /*if (QThread::currentThread() != QScriptEngine::thread()) {
769+ ScriptValue result;
770+#ifdef THREAD_DEBUGGING
771+ qCDebug(scriptengine) << "*** WARNING *** ScriptEngineV8::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
772+ "sourceCode:" << sourceCode << " fileName:" << fileName;
773+#endif
774+ BLOCKING_INVOKE_METHOD(this, "evaluate",
775+ Q_RETURN_ARG(ScriptValue, result),
776+ Q_ARG(const ScriptProgramPointer&, program));
777+ return result;
778+ }*/
779+
780+ ScriptProgramV8Wrapper* unwrapped = ScriptProgramV8Wrapper::unwrap(program);
781+ if (!unwrapped) {
782+ auto err = makeError(newValue("could not unwrap program"));
783+ raiseException(err);
784+ maybeEmitUncaughtException("compile");
785+ return err;
786+ }
787+ ScriptSyntaxCheckResultPointer syntaxCheck = unwrapped->checkSyntax();
788+ if (syntaxCheck->state() == ScriptSyntaxCheckResult::Error) {
789+ auto err = makeError(newValue(syntaxCheck->errorMessage()));
790+ raiseException(err);
791+ maybeEmitUncaughtException("compile");
792+ return err;
793+ }
794+
795+ const V8ScriptProgram& v8Program = unwrapped->toV8Value();
796+ // V8TODO
797+ /*if (qProgram.isNull()) {
798+ // can this happen?
799+ auto err = makeError(newValue("requested program is empty"));
800+ raiseException(err);
801+ maybeEmitUncaughtException("compile");
802+ return err;
803+ }*/
804+
805+ v8::Local<v8::Value> result;
806+ v8::TryCatch tryCatchRun(getIsolate());
807+ if (!v8Program.constGet()->Run(getContext()).ToLocal(&result)) {
808+ Q_ASSERT(tryCatchRun.HasCaught());
809+ auto runError = tryCatchRun.Message();
810+ ScriptValue errorValue(new ScriptValueV8Wrapper(this, V8ScriptValue(_v8Isolate, runError->Get())));
811+ raiseException(errorValue);
812+ maybeEmitUncaughtException("evaluate");
813+ return errorValue;
814+ }
815+ V8ScriptValue resultValue(_v8Isolate, result);
816+ return ScriptValue(new ScriptValueV8Wrapper(this, std::move(resultValue)));
817+}
818+
819+
820+void ScriptEngineV8::updateMemoryCost(const qint64& deltaSize) {
821+ if (deltaSize > 0) {
822+ // We've patched qt to fix https://highfidelity.atlassian.net/browse/BUGZ-46 on mac and windows only.
823+#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
824+ reportAdditionalMemoryCost(deltaSize);
825+#endif
826+ }
827+}
828+
829+
830+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
831+// ScriptEngine implementation
832+
833+ScriptValue ScriptEngineV8::globalObject() const {
834+ V8ScriptValue global(_v8Isolate, getConstContext()->Global());// = QScriptEngine::globalObject(); // can't cache the value as it may change
835+ return ScriptValue(new ScriptValueV8Wrapper(const_cast<ScriptEngineV8*>(this), std::move(global)));
836+}
837+
838+ScriptManager* ScriptEngineV8::manager() const {
839+ return _scriptManager;
840+}
841+
842+ScriptValue ScriptEngineV8::newArray(uint length) {
843+ V8ScriptValue result(_v8Isolate, v8::Array::New(_v8Isolate, static_cast<int>(length)));
844+ return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result)));
845+}
846+
847+ScriptValue ScriptEngineV8::newArrayBuffer(const QByteArray& message) {
848+ //V8TODO: this will leak memory
849+ std::shared_ptr<v8::BackingStore> backingStore(v8::ArrayBuffer::NewBackingStore(_v8Isolate, message.size()));
850+ std::memcpy(backingStore.get()->Data(), message.constData(), message.size());
851+ auto arrayBuffer = v8::ArrayBuffer::New(_v8Isolate, backingStore);
852+ /*V8ScriptValue data = QScriptEngine::newVariant(QVariant::fromValue(message));
853+ V8ScriptValue ctor = QScriptEngine::globalObject().property("ArrayBuffer");
854+ auto array = qscriptvalue_cast<ArrayBufferClass*>(ctor.data());
855+ if (!array) {
856+ return undefinedValue();
857+ }*/
858+ V8ScriptValue result(_v8Isolate, arrayBuffer);//QScriptEngine::newObject(array, data);
859+ return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result)));
860+}
861+
862+ScriptValue ScriptEngineV8::newObject() {
863+ V8ScriptValue result(_v8Isolate, v8::Object::New(_v8Isolate));
864+ return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result)));
865+}
866+
867+ScriptValue ScriptEngineV8::newMethod(QObject* object, V8ScriptValue lifetime,
868+ const QList<QMetaMethod>& metas, int numMaxParams) {
869+ V8ScriptValue result(ScriptMethodV8Proxy::newMethod(this, object, lifetime, metas, numMaxParams));
870+ return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result)));
871+}
872+
873+ScriptProgramPointer ScriptEngineV8::newProgram(const QString& sourceCode, const QString& fileName) {
874+ //V8TODO: is it used between isolates?
875+ //V8TODO: should it be compiled on creation?
876+ //V8ScriptProgram result(sourceCode, fileName);
877+ return std::make_shared<ScriptProgramV8Wrapper>(this, sourceCode, fileName);
878+}
879+
880+ScriptValue ScriptEngineV8::newQObject(QObject* object,
881+ ScriptEngine::ValueOwnership ownership,
882+ const ScriptEngine::QObjectWrapOptions& options) {
883+ V8ScriptValue result = ScriptObjectV8Proxy::newQObject(this, object, ownership, options);
884+ return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result)));
885+}
886+
887+ScriptValue ScriptEngineV8::newValue(bool value) {
888+ V8ScriptValue result(_v8Isolate, v8::Boolean::New(_v8Isolate, value));
889+ return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result)));
890+}
891+
892+ScriptValue ScriptEngineV8::newValue(int value) {
893+ V8ScriptValue result(_v8Isolate, v8::Integer::New(_v8Isolate, value));
894+ return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result)));
895+}
896+
897+ScriptValue ScriptEngineV8::newValue(uint value) {
898+ V8ScriptValue result(_v8Isolate, v8::Uint32::New(_v8Isolate, value));
899+ return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result)));
900+}
901+
902+ScriptValue ScriptEngineV8::newValue(double value) {
903+ V8ScriptValue result(_v8Isolate, v8::Number::New(_v8Isolate, value));
904+ return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result)));
905+}
906+
907+ScriptValue ScriptEngineV8::newValue(const QString& value) {
908+ v8::Local<v8::String> valueV8 = v8::String::NewFromUtf8(_v8Isolate, value.toStdString().c_str(), v8::NewStringType::kNormal).ToLocalChecked();
909+ V8ScriptValue result(_v8Isolate, valueV8);
910+ return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result)));
911+}
912+
913+ScriptValue ScriptEngineV8::newValue(const QLatin1String& value) {
914+ v8::Local<v8::String> valueV8 = v8::String::NewFromUtf8(_v8Isolate, value.latin1(), v8::NewStringType::kNormal).ToLocalChecked();
915+ V8ScriptValue result(_v8Isolate, valueV8);
916+ return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result)));
917+}
918+
919+ScriptValue ScriptEngineV8::newValue(const char* value) {
920+ v8::Local<v8::String> valueV8 = v8::String::NewFromUtf8(_v8Isolate, value, v8::NewStringType::kNormal).ToLocalChecked();
921+ V8ScriptValue result(_v8Isolate, valueV8);
922+ return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result)));
923+}
924+
925+ScriptValue ScriptEngineV8::newVariant(const QVariant& value) {
926+ V8ScriptValue result = castVariantToValue(value);
927+ return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result)));
928+}
929+
930+ScriptValue ScriptEngineV8::nullValue() {
931+ return _nullValue;
932+}
933+
934+ScriptValue ScriptEngineV8::undefinedValue() {
935+ return _undefinedValue;
936+}
937+
938+void ScriptEngineV8::abortEvaluation() {
939+ //V8TODO
940+ //QScriptEngine::abortEvaluation();
941+}
942+
943+void ScriptEngineV8::clearExceptions() {
944+ //V8TODO
945+ //QScriptEngine::clearExceptions();
946+}
947+
948+ScriptContext* ScriptEngineV8::currentContext() const {
949+ //V8TODO
950+ /*V8ScriptContext* localCtx = QScriptEngine::currentContext();
951+ if (!localCtx) {
952+ return nullptr;
953+ }
954+ if (!_currContext || _currContext->toV8Value() != localCtx) {
955+ _currContext = std::make_shared<ScriptContextV8Wrapper>(const_cast<ScriptEngineV8*>(this), localCtx);
956+ }*/
957+ //_currContext = std::make_shared<ScriptContextV8Wrapper>(const_cast<ScriptEngineV8*>(this), localCtx);
958+ if (!_currContext) {
959+ // I'm not sure how to do this without discardin const
960+ _currContext = std::make_shared<ScriptContextV8Wrapper>(const_cast<ScriptEngineV8*>(this), getConstContext());
961+ }
962+ return _currContext.get();
963+}
964+
965+bool ScriptEngineV8::hasUncaughtException() const {
966+ //V8TODO
967+ //return QScriptEngine::hasUncaughtException();
968+}
969+
970+bool ScriptEngineV8::isEvaluating() const {
971+ //V8TODO
972+ //return QScriptEngine::isEvaluating();
973+}
974+
975+ScriptValue ScriptEngineV8::newFunction(ScriptEngine::FunctionSignature fun, int length) {
976+ /*auto innerFunc = [](V8ScriptContext* _context, QScriptEngine* _engine) -> V8ScriptValue {
977+ auto callee = _context->callee();
978+ QVariant funAddr = callee.property("_func").toVariant();
979+ ScriptEngine::FunctionSignature fun = reinterpret_cast<ScriptEngine::FunctionSignature>(funAddr.toULongLong());
980+ ScriptEngineV8* engine = static_cast<ScriptEngineV8*>(_engine);
981+ ScriptContextV8Wrapper context(engine, _context);
982+ ScriptValue result = fun(&context, engine);
983+ ScriptValueV8Wrapper* unwrapped = ScriptValueV8Wrapper::unwrap(result);
984+ return unwrapped ? unwrapped->toV8Value() : V8ScriptValue();
985+ };*/
986+
987+ auto v8FunctionCallback = [](const v8::FunctionCallbackInfo<v8::Value>& info) {
988+ //V8TODO: is using GetCurrentContext ok, or context wrapper needs to be added?
989+ auto context = info.GetIsolate()->GetCurrentContext();
990+ auto function = reinterpret_cast<ScriptEngine::FunctionSignature>
991+ (info.Data()->ToObject(context).ToLocalChecked()->GetAlignedPointerFromInternalField(0));
992+ ScriptContext *scriptContext = reinterpret_cast<ScriptContext*>
993+ (info.Data()->ToObject(context).ToLocalChecked()->GetAlignedPointerFromInternalField(1));;
994+ ScriptEngineV8 *scriptEngine = reinterpret_cast<ScriptEngineV8*>
995+ (info.Data()->ToObject(context).ToLocalChecked()->GetAlignedPointerFromInternalField(2));;
996+ ScriptValue result = function(scriptContext, scriptEngine);
997+ ScriptValueV8Wrapper* unwrapped = ScriptValueV8Wrapper::unwrap(result);
998+ info.GetReturnValue().Set(unwrapped->toV8Value().constGet());
999+ };
1000+ //auto functionTemplate = v8::FunctionTemplate::New(_v8Isolate, v8FunctionCallback, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), length);
1001+ //auto functionData = v8::Object::New(_v8Isolate);
1002+ //functionData->setIn
1003+ auto functionDataTemplate = v8::ObjectTemplate::New(_v8Isolate);
1004+ functionDataTemplate->SetInternalFieldCount(3);
1005+ auto functionData = functionDataTemplate->NewInstance(getContext()).ToLocalChecked();
1006+ functionData->SetAlignedPointerInInternalField(0, reinterpret_cast<void*>(fun));
1007+ functionData->SetAlignedPointerInInternalField(1, reinterpret_cast<void*>(this));
1008+ functionData->SetAlignedPointerInInternalField(2, reinterpret_cast<void*>(currentContext()));
1009+ //functionData->SetInternalField(3, v8::Null(_v8Isolate));
1010+ auto v8Function = v8::Function::New(getContext(), v8FunctionCallback, functionData, length).ToLocalChecked();
1011+ //auto functionObjectTemplate = functionTemplate->InstanceTemplate();
1012+ //auto function =
1013+ V8ScriptValue result(_v8Isolate, v8Function); // = QScriptEngine::newFunction(innerFunc, length);
1014+ //auto funAddr = QScriptEngine::newVariant(QVariant(reinterpret_cast<qulonglong>(fun)));
1015+ // V8TODO
1016+ //result.setProperty("_func", funAddr, V8ScriptValue::PropertyFlags(V8ScriptValue::ReadOnly + V8ScriptValue::Undeletable + V8ScriptValue::SkipInEnumeration));
1017+ return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result)));
1018+}
1019+
1020+//V8TODO
1021+/*void ScriptEngineV8::setObjectName(const QString& name) {
1022+ QScriptEngine::setObjectName(name);
1023+}*/
1024+
1025+//V8TODO
1026+bool ScriptEngineV8::setProperty(const char* name, const QVariant& value) {
1027+ v8::Local<v8::Object> global = getContext()->Global();
1028+ auto v8Name = v8::String::NewFromUtf8(getIsolate(), name).ToLocalChecked();
1029+ V8ScriptValue v8Value = castVariantToValue(value);
1030+ return global->Set(getContext(), v8Name, v8Value.get()).FromMaybe(false);
1031+}
1032+
1033+void ScriptEngineV8::setProcessEventsInterval(int interval) {
1034+ //V8TODO
1035+ //QScriptEngine::setProcessEventsInterval(interval);
1036+}
1037+
1038+QThread* ScriptEngineV8::thread() const {
1039+ return _currentThread;
1040+}
1041+
1042+void ScriptEngineV8::setThread(QThread* thread) {
1043+ moveToThread(thread);
1044+}
1045+
1046+ScriptValue ScriptEngineV8::uncaughtException() const {
1047+ //V8TODO
1048+ //V8ScriptValue result = QScriptEngine::uncaughtException();
1049+ //return ScriptValue(new ScriptValueV8Wrapper(const_cast<ScriptEngineV8*>(this), std::move(result)));
1050+}
1051+
1052+QStringList ScriptEngineV8::uncaughtExceptionBacktrace() const {
1053+ //V8TODO
1054+ //return QScriptEngine::uncaughtExceptionBacktrace();
1055+}
1056+
1057+int ScriptEngineV8::uncaughtExceptionLineNumber() const {
1058+ //V8TODO
1059+ //return QScriptEngine::uncaughtExceptionLineNumber();
1060+}
1061+
1062+bool ScriptEngineV8::raiseException(const ScriptValue& exception) {
1063+ //V8TODO
1064+ Q_ASSERT(false);
1065+ /*ScriptValueV8Wrapper* unwrapped = ScriptValueV8Wrapper::unwrap(exception);
1066+ V8ScriptValue qException = unwrapped ? unwrapped->toV8Value() : QScriptEngine::newVariant(exception.toVariant());
1067+ return raiseException(qException);*/
1068+}
1069+
1070+ScriptValue ScriptEngineV8::create(int type, const void* ptr) {
1071+ QVariant variant(type, ptr);
1072+ V8ScriptValue scriptValue = castVariantToValue(variant);
1073+ return ScriptValue(new ScriptValueV8Wrapper(this, std::move(scriptValue)));
1074+}
1075+
1076+QVariant ScriptEngineV8::convert(const ScriptValue& value, int typeId) {
1077+ ScriptValueV8Wrapper* unwrapped = ScriptValueV8Wrapper::unwrap(value);
1078+ if (unwrapped == nullptr) {
1079+ return QVariant();
1080+ }
1081+
1082+ QVariant var;
1083+ if (!castValueToVariant(unwrapped->toV8Value(), var, typeId)) {
1084+ return QVariant();
1085+ }
1086+
1087+ int destType = var.userType();
1088+ if (destType != typeId) {
1089+ var.convert(typeId); // if conversion fails then var is set to QVariant()
1090+ }
1091+
1092+ return var;
1093+}