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
53static const int MAX_DEBUG_VALUE_LENGTH { 80 };
54
55std::once_flag ScriptEngineV8::_v8InitOnceFlag;
56QMutex ScriptEngineV8::_v8InitMutex;
57
58bool 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
73V8ScriptValue 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
112ScriptValue 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
135ScriptValue 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
186ScriptValue 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
251bool 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
271bool 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
284ScriptValue 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}
295QString 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
307Lambda::~Lambda() {
308#ifdef DEBUG_JS_LAMBDA_FUNCS
309 qDebug() << "~Lambda"
310 << "this" << this;
311#endif
312}
313
314Lambda::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}
323V8ScriptValue 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
333void 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
355v8::Platform* ScriptEngineV8::getV8Platform() {
356 static std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
357 return platform.get();
358}
359
360ScriptEngineV8::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
413void 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
426void 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
464void 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
492void 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
515void 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
538void 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
572ScriptValue 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
681ScriptValue 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
762Q_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
820void 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
833ScriptValue 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
838ScriptManager* ScriptEngineV8::manager() const {
839 return _scriptManager;
840}
841
842ScriptValue 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
847ScriptValue 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
862ScriptValue ScriptEngineV8::newObject() {
863 V8ScriptValue result(_v8Isolate, v8::Object::New(_v8Isolate));
864 return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result)));
865}
866
867ScriptValue 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
873ScriptProgramPointer 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
880ScriptValue 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
887ScriptValue ScriptEngineV8::newValue(bool value) {
888 V8ScriptValue result(_v8Isolate, v8::Boolean::New(_v8Isolate, value));
889 return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result)));
890}
891
892ScriptValue ScriptEngineV8::newValue(int value) {
893 V8ScriptValue result(_v8Isolate, v8::Integer::New(_v8Isolate, value));
894 return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result)));
895}
896
897ScriptValue ScriptEngineV8::newValue(uint value) {
898 V8ScriptValue result(_v8Isolate, v8::Uint32::New(_v8Isolate, value));
899 return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result)));
900}
901
902ScriptValue ScriptEngineV8::newValue(double value) {
903 V8ScriptValue result(_v8Isolate, v8::Number::New(_v8Isolate, value));
904 return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result)));
905}
906
907ScriptValue 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
913ScriptValue 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
919ScriptValue 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
925ScriptValue ScriptEngineV8::newVariant(const QVariant& value) {
926 V8ScriptValue result = castVariantToValue(value);
927 return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result)));
928}
929
930ScriptValue ScriptEngineV8::nullValue() {
931 return _nullValue;
932}
933
934ScriptValue ScriptEngineV8::undefinedValue() {
935 return _undefinedValue;
936}
937
938void ScriptEngineV8::abortEvaluation() {
939 //V8TODO
940 //QScriptEngine::abortEvaluation();
941}
942
943void ScriptEngineV8::clearExceptions() {
944 //V8TODO
945 //QScriptEngine::clearExceptions();
946}
947
948ScriptContext* 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
965bool ScriptEngineV8::hasUncaughtException() const {
966 //V8TODO
967 //return QScriptEngine::hasUncaughtException();
968}
969
970bool ScriptEngineV8::isEvaluating() const {
971 //V8TODO
972 //return QScriptEngine::isEvaluating();
973}
974
975ScriptValue 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
1026bool 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
1033void ScriptEngineV8::setProcessEventsInterval(int interval) {
1034 //V8TODO
1035 //QScriptEngine::setProcessEventsInterval(interval);
1036}
1037
1038QThread* ScriptEngineV8::thread() const {
1039 return _currentThread;
1040}
1041
1042void ScriptEngineV8::setThread(QThread* thread) {
1043 moveToThread(thread);
1044}
1045
1046ScriptValue ScriptEngineV8::uncaughtException() const {
1047 //V8TODO
1048 //V8ScriptValue result = QScriptEngine::uncaughtException();
1049 //return ScriptValue(new ScriptValueV8Wrapper(const_cast<ScriptEngineV8*>(this), std::move(result)));
1050}
1051
1052QStringList ScriptEngineV8::uncaughtExceptionBacktrace() const {
1053 //V8TODO
1054 //return QScriptEngine::uncaughtExceptionBacktrace();
1055}
1056
1057int ScriptEngineV8::uncaughtExceptionLineNumber() const {
1058 //V8TODO
1059 //return QScriptEngine::uncaughtExceptionLineNumber();
1060}
1061
1062bool 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
1070ScriptValue 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
1076QVariant 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}