#endif auto object = globalObject().property(parent); if (object.isValid()) { auto scriptFun = newFunction(functionSignature, numArguments); object.setProperty(name, scriptFun); }} void ScriptEngineQtScript::registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter, QScriptEngine::FunctionSignature setter, const QString& parent) { if (QThread::currentThread() != QScriptEngine::thread()) {#ifdef THREAD_DEBUGGING qCDebug(scriptengine) << "*** WARNING *** ScriptEngineQtScript::registerGetterSetter() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " " name:" << name << "parent:" << parent;#endif QMetaObject::invokeMethod(this, "registerGetterSetter", Q_ARG(const QString&, name), Q_ARG(QScriptEngine::FunctionSignature, getter), Q_ARG(QScriptEngine::FunctionSignature, setter), Q_ARG(const QString&, parent)); return; }#ifdef THREAD_DEBUGGING qCDebug(scriptengine) << "ScriptEngineQtScript::registerGetterSetter() called on thread [" << QThread::currentThread() << "] name:" << name << "parent:" << parent;#endif QScriptValue setterFunction = QScriptEngine::newFunction(setter, 1); QScriptValue getterFunction = QScriptEngine::newFunction(getter); if (!parent.isNull() && !parent.isEmpty()) { QScriptValue object = QScriptEngine::globalObject().property(parent); if (object.isValid()) { object.setProperty(name, setterFunction, QScriptValue::PropertySetter); object.setProperty(name, getterFunction, QScriptValue::PropertyGetter); } } else { QScriptEngine::globalObject().setProperty(name, setterFunction, QScriptValue::PropertySetter); QScriptEngine::globalObject().setProperty(name, getterFunction, QScriptValue::PropertyGetter); }} void ScriptEngineQtScript::registerGetterSetter(const QString& name, ScriptEngine::FunctionSignature getter, ScriptEngine::FunctionSignature setter, const QString& parent) { if (QThread::currentThread() != QScriptEngine::thread()) {#ifdef THREAD_DEBUGGING qCDebug(scriptengine) << "*** WARNING *** ScriptEngineQtScript::registerGetterSetter() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " " name:" << name << "parent:" << parent;#endif QMetaObject::invokeMethod(this, "registerGetterSetter", Q_ARG(const QString&, name), Q_ARG(ScriptEngine::FunctionSignature, getter), Q_ARG(ScriptEngine::FunctionSignature, setter), Q_ARG(const QString&, parent)); return; }#ifdef THREAD_DEBUGGING qCDebug(scriptengine) << "ScriptEngineQtScript::registerGetterSetter() called on thread [" << QThread::currentThread() << "] name:" << name << "parent:" << parent;#endif auto setterFunction = newFunction(setter, 1); auto getterFunction = newFunction(getter); if (!parent.isNull() && !parent.isEmpty()) { auto object = globalObject().property(parent); if (object.isValid()) { object.setProperty(name, setterFunction, ScriptValue::PropertySetter); object.setProperty(name, getterFunction, ScriptValue::PropertyGetter); } } else { globalObject().setProperty(name, setterFunction, ScriptValue::PropertySetter); globalObject().setProperty(name, getterFunction, ScriptValue::PropertyGetter); }} ScriptValue ScriptEngineQtScript::evaluateInClosure(const ScriptValue& _closure, const ScriptProgramPointer& _program) { PROFILE_RANGE(script, "evaluateInClosure"); if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { return nullValue(); } ScriptProgramQtWrapper* unwrappedProgram = ScriptProgramQtWrapper::unwrap(_program); if (unwrappedProgram == nullptr) { return nullValue(); } const QScriptProgram& program = unwrappedProgram->toQtValue(); const auto fileName = program.fileName(); const auto shortName = QUrl(fileName).fileName(); ScriptValueQtWrapper* unwrappedClosure = ScriptValueQtWrapper::unwrap(_closure); if (unwrappedClosure == nullptr) { return nullValue(); } const QScriptValue& closure = unwrappedClosure->toQtValue(); QScriptValue oldGlobal; auto global = closure.property("global"); if (global.isObject()) {#ifdef DEBUG_JS qCDebug(shared) << " setting global = closure.global" << shortName;#endif oldGlobal = QScriptEngine::globalObject(); setGlobalObject(global); } auto context = pushContext(); auto thiz = closure.property("this"); if (thiz.isObject()) {#ifdef DEBUG_JS qCDebug(shared) << " setting this = closure.this" << shortName;#endif context->setThisObject(thiz); } context->pushScope(closure);#ifdef DEBUG_JS qCDebug(shared) << QString("[%1] evaluateInClosure %2").arg(isEvaluating()).arg(shortName);#endif ScriptValue result; { auto qResult = QScriptEngine::evaluate(program); if (hasUncaughtException()) { auto err = cloneUncaughtException(__FUNCTION__);#ifdef DEBUG_JS_EXCEPTIONS qCWarning(shared) << __FUNCTION__ << "---------- hasCaught:" << err.toString() << result.toString(); err.setProperty("_result", result);#endif result = err; } else { result = ScriptValue(new ScriptValueQtWrapper(this, std::move(qResult))); } }#ifdef DEBUG_JS qCDebug(shared) << QString("[%1] //evaluateInClosure %2").arg(isEvaluating()).arg(shortName);#endif popContext(); if (oldGlobal.isValid()) {#ifdef DEBUG_JS qCDebug(shared) << " restoring global" << shortName;#endif setGlobalObject(oldGlobal); } return result;} ScriptValue ScriptEngineQtScript::evaluate(const QString& sourceCode, const QString& fileName) { if (_manager && _manager->isStopped()) { return undefinedValue(); // bail early } if (QThread::currentThread() != QScriptEngine::thread()) { ScriptValue result;#ifdef THREAD_DEBUGGING qCDebug(scriptengine) << "*** WARNING *** ScriptEngineQtScript::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " "sourceCode:" << sourceCode << " fileName:" << fileName;#endif BLOCKING_INVOKE_METHOD(this, "evaluate", Q_RETURN_ARG(ScriptValue, result), Q_ARG(const QString&, sourceCode), Q_ARG(const QString&, fileName)); return result; } // Check syntax auto syntaxError = lintScript(sourceCode, fileName); if (syntaxError.isError()) { if (!isEvaluating()) { syntaxError.setProperty("detail", "evaluate"); } raiseException(syntaxError); maybeEmitUncaughtException("lint"); return syntaxError; } QScriptProgram program { sourceCode, fileName, 1 }; if (program.isNull()) { // can this happen? auto err = makeError(newValue("could not create QScriptProgram for " + fileName)); raiseException(err); maybeEmitUncaughtException("compile"); return err; } QScriptValue result = QScriptEngine::evaluate(program); maybeEmitUncaughtException("evaluate"); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));} Q_INVOKABLE ScriptValue ScriptEngineQtScript::evaluate(const ScriptProgramPointer& program) { if (_manager && _manager->isStopped()) { return undefinedValue(); // bail early } if (QThread::currentThread() != QScriptEngine::thread()) { ScriptValue result;#ifdef THREAD_DEBUGGING qCDebug(scriptengine) << "*** WARNING *** ScriptEngineQtScript::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " "sourceCode:" << sourceCode << " fileName:" << fileName;#endif BLOCKING_INVOKE_METHOD(this, "evaluate", Q_RETURN_ARG(ScriptValue, result), Q_ARG(const ScriptProgramPointer&, program)); return result; } ScriptProgramQtWrapper* unwrapped = ScriptProgramQtWrapper::unwrap(program); if (!unwrapped) { auto err = makeError(newValue("could not unwrap program")); raiseException(err); maybeEmitUncaughtException("compile"); return err; } const QScriptProgram& qProgram = unwrapped->toQtValue(); if (qProgram.isNull()) { // can this happen? auto err = makeError(newValue("requested program is empty")); raiseException(err); maybeEmitUncaughtException("compile"); return err; } QScriptValue result = QScriptEngine::evaluate(qProgram); maybeEmitUncaughtException("evaluate"); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));} void ScriptEngineQtScript::updateMemoryCost(const qint64& deltaSize) { if (deltaSize > 0) { // We've patched qt to fix https://highfidelity.atlassian.net/browse/BUGZ-46 on mac and windows only.#if defined(Q_OS_WIN) || defined(Q_OS_MAC) reportAdditionalMemoryCost(deltaSize);#endif }} ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ScriptEngine implementation ScriptValue ScriptEngineQtScript::globalObject() const { QScriptValue global = QScriptEngine::globalObject(); // can't cache the value as it may change return ScriptValue(new ScriptValueQtWrapper(const_cast < ScriptEngineQtScript*>(this), std::move(global)));} ScriptManager* ScriptEngineQtScript::manager() const { return _manager;} ScriptValue ScriptEngineQtScript::newArray(uint length) { QScriptValue result = QScriptEngine::newArray(length); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));} ScriptValue ScriptEngineQtScript::newArrayBuffer(const QByteArray& message) { QScriptValue data = QScriptEngine::newVariant(QVariant::fromValue(message)); QScriptValue ctor = QScriptEngine::globalObject().property("ArrayBuffer"); auto array = qscriptvalue_cast<ArrayBufferClass*>(ctor.data()); if (!array) { return undefinedValue(); } QScriptValue result = QScriptEngine::newObject(array, data); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));} ScriptValue ScriptEngineQtScript::newObject() { QScriptValue result = QScriptEngine::newObject(); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));} ScriptProgramPointer ScriptEngineQtScript::newProgram(const QString& sourceCode, const QString& fileName) { QScriptProgram result(sourceCode, fileName); return std::make_shared<ScriptProgramQtWrapper>(this, result);} ScriptValue ScriptEngineQtScript::newQObject(QObject* object, ScriptEngine::ValueOwnership ownership, const ScriptEngine::QObjectWrapOptions& options) { QScriptValue result = QScriptEngine::newQObject(object, static_cast<QScriptEngine::ValueOwnership>(ownership), (QScriptEngine::QObjectWrapOptions)((int)options | DEFAULT_QOBJECT_WRAP_OPTIONS)); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));} ScriptValue ScriptEngineQtScript::newValue(bool value) { QScriptValue result(this, value); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));} ScriptValue ScriptEngineQtScript::newValue(int value) { QScriptValue result(this, value); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));} ScriptValue ScriptEngineQtScript::newValue(uint value) { QScriptValue result(this, value); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));} ScriptValue ScriptEngineQtScript::newValue(double value) { QScriptValue result(this, value); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));} ScriptValue ScriptEngineQtScript::newValue(const QString& value) { QScriptValue result(this, value); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));} ScriptValue ScriptEngineQtScript::newValue(const QLatin1String& value) { QScriptValue result(this, value); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));} ScriptValue ScriptEngineQtScript::newValue(const char* value) { QScriptValue result(this, value); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));} ScriptValue ScriptEngineQtScript::newVariant(const QVariant& value) { QScriptValue result = QScriptEngine::newVariant(value); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));} ScriptValue ScriptEngineQtScript::nullValue() { return _nullValue;} void ScriptEngineQtScript::setDefaultPrototype(int metaTypeId, const ScriptValue& prototype){ ScriptValueQtWrapper* unwrappedPrototype = ScriptValueQtWrapper::unwrap(prototype); if (unwrappedPrototype) { QScriptEngine::setDefaultPrototype(metaTypeId, unwrappedPrototype->toQtValue()); }} ScriptValue ScriptEngineQtScript::undefinedValue() { return _undefinedValue;} void ScriptEngineQtScript::abortEvaluation() { QScriptEngine::abortEvaluation();} void ScriptEngineQtScript::clearExceptions() { QScriptEngine::clearExceptions();} ScriptContext* ScriptEngineQtScript::currentContext() const { QScriptContext* localCtx = QScriptEngine::currentContext(); if (!localCtx) { return nullptr; } if (!_currContext || _currContext->toQtValue() != localCtx) { _currContext = std::make_shared<ScriptContextQtWrapper>(const_cast<ScriptEngineQtScript*>(this), localCtx); } return _currContext.get();} bool ScriptEngineQtScript::hasUncaughtException() const { return QScriptEngine::hasUncaughtException();} bool ScriptEngineQtScript::isEvaluating() const { return QScriptEngine::isEvaluating();} ScriptValue ScriptEngineQtScript::newFunction(ScriptEngine::FunctionSignature fun, int length) { auto innerFunc = [](QScriptContext* _context, QScriptEngine* _engine) -> QScriptValue { auto callee = _context->callee(); QVariant funAddr = callee.property("_func").toVariant(); ScriptEngine::FunctionSignature fun = reinterpret_cast<ScriptEngine::FunctionSignature>(funAddr.toULongLong()); ScriptEngineQtScript* engine = static_cast<ScriptEngineQtScript*>(_engine); ScriptContextQtWrapper context(engine, _context); ScriptValue result = fun(&context, engine); ScriptValueQtWrapper* unwrapped = ScriptValueQtWrapper::unwrap(result); return unwrapped ? unwrapped->toQtValue() : QScriptValue(); }; QScriptValue result = QScriptEngine::newFunction(innerFunc, length); auto funAddr = QScriptEngine::newVariant(QVariant(reinterpret_cast<qulonglong>(fun))); result.setProperty("_func", funAddr, QScriptValue::PropertyFlags(QScriptValue::ReadOnly + QScriptValue::Undeletable + QScriptValue::SkipInEnumeration)); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));} void ScriptEngineQtScript::setObjectName(const QString& name) { QScriptEngine::setObjectName(name);} bool ScriptEngineQtScript::setProperty(const char* name, const QVariant& value) { return QScriptEngine::setProperty(name, value);} void ScriptEngineQtScript::setProcessEventsInterval(int interval) { QScriptEngine::setProcessEventsInterval(interval);} QThread* ScriptEngineQtScript::thread() const { return QScriptEngine::thread();} void ScriptEngineQtScript::setThread(QThread* thread) { moveToThread(thread);} ScriptValue ScriptEngineQtScript::uncaughtException() const { QScriptValue result = QScriptEngine::uncaughtException(); return ScriptValue(new ScriptValueQtWrapper(const_cast<ScriptEngineQtScript*>(this), std::move(result)));} QStringList ScriptEngineQtScript::uncaughtExceptionBacktrace() const { return QScriptEngine::uncaughtExceptionBacktrace();} int ScriptEngineQtScript::uncaughtExceptionLineNumber() const { return QScriptEngine::uncaughtExceptionLineNumber();} bool ScriptEngineQtScript::raiseException(const ScriptValue& exception) { ScriptValueQtWrapper* unwrapped = ScriptValueQtWrapper::unwrap(exception); QScriptValue qException = unwrapped ? unwrapped->toQtValue() : QScriptEngine::newVariant(exception.toVariant()); return raiseException(qException);} ScriptValue ScriptEngineQtScript::create(int type, const void* ptr) { QScriptValue result = qScriptValueFromValue_helper(this, type, ptr); return ScriptValue(new ScriptValueQtWrapper(const_cast<ScriptEngineQtScript*>(this), std::move(result)));} bool ScriptEngineQtScript::convert(const ScriptValue& value, int type, void* ptr) { ScriptValueQtWrapper* unwrapped = ScriptValueQtWrapper::unwrap(value); if (unwrapped == nullptr) { return false; } return qscriptvalue_cast_helper(unwrapped->toQtValue(), type, ptr);} template <int i>class CustomTypeInstance {public: static ScriptEngine::MarshalFunction marshalFunc; static ScriptEngine::DemarshalFunction demarshalFunc; static QScriptValue internalMarshalFunc(QScriptEngine* engine, const void* src) { ScriptEngineQtScript* unwrappedEngine = static_cast<ScriptEngineQtScript*>(engine); ScriptValue dest = marshalFunc(unwrappedEngine, src); return ScriptValueQtWrapper::fullUnwrap(unwrappedEngine, dest); } static void internalDemarshalFunc(const QScriptValue& src, void* dest) { ScriptEngineQtScript* unwrappedEngine = static_cast<ScriptEngineQtScript*>(src.engine()); ScriptValue wrappedSrc(new ScriptValueQtWrapper(unwrappedEngine, src)); demarshalFunc(wrappedSrc, dest); }};template <int i>ScriptEngine::MarshalFunction CustomTypeInstance<i>::marshalFunc;template <int i>ScriptEngine::DemarshalFunction CustomTypeInstance<i>::demarshalFunc; // I would *LOVE* it if there was a different way to do this, jeez!// Qt requires two functions that have no parameters that give any context,// one of the must return a QScriptValue (so we can't void* them into generics and stick them in the templates).// This *has* to be done via templates but the whole point of this is to avoid leaking types into the rest of// the system that would require anyone other than us to have a dependency on QtScript#define CUSTOM_TYPE_ENTRY(idx) \ case idx: \ CustomTypeInstance<idx>::marshalFunc = marshalFunc; \ CustomTypeInstance<idx>::demarshalFunc = demarshalFunc; \ internalMarshalFunc = CustomTypeInstance<idx>::internalMarshalFunc; \ internalDemarshalFunc = CustomTypeInstance<idx>::internalDemarshalFunc; \ break;#define CUSTOM_TYPE_ENTRY_10(idx) \ CUSTOM_TYPE_ENTRY((idx * 10)); \ CUSTOM_TYPE_ENTRY((idx * 10) + 1); \ CUSTOM_TYPE_ENTRY((idx * 10) + 2); \ CUSTOM_TYPE_ENTRY((idx * 10) + 3); \ CUSTOM_TYPE_ENTRY((idx * 10) + 4); \ CUSTOM_TYPE_ENTRY((idx * 10) + 5); \ CUSTOM_TYPE_ENTRY((idx * 10) + 6); \ CUSTOM_TYPE_ENTRY((idx * 10) + 7); \ CUSTOM_TYPE_ENTRY((idx * 10) + 8); \ CUSTOM_TYPE_ENTRY((idx * 10) + 9); void ScriptEngineQtScript::registerCustomType(int type, ScriptEngine::MarshalFunction marshalFunc, ScriptEngine::DemarshalFunction demarshalFunc, const ScriptValue& prototype){ QScriptValue unwrapped = ScriptValueQtWrapper::fullUnwrap(this, prototype); QScriptEngine::MarshalFunction internalMarshalFunc; QScriptEngine::DemarshalFunction internalDemarshalFunc; if (_nextCustomType >= 300) { // have we ran out of translators? Q_ASSERT(false);Lines 501–1000 of 1039. Previous lines Load more
#endif auto object = globalObject().property(parent); if (object.isValid()) { auto scriptFun = newFunction(functionSignature, numArguments); object.setProperty(name, scriptFun); } } void ScriptEngineQtScript::registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter, QScriptEngine::FunctionSignature setter, const QString& parent) { if (QThread::currentThread() != QScriptEngine::thread()) { #ifdef THREAD_DEBUGGING qCDebug(scriptengine) << "*** WARNING *** ScriptEngineQtScript::registerGetterSetter() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " " name:" << name << "parent:" << parent; #endif QMetaObject::invokeMethod(this, "registerGetterSetter", Q_ARG(const QString&, name), Q_ARG(QScriptEngine::FunctionSignature, getter), Q_ARG(QScriptEngine::FunctionSignature, setter), Q_ARG(const QString&, parent)); return; } #ifdef THREAD_DEBUGGING qCDebug(scriptengine) << "ScriptEngineQtScript::registerGetterSetter() called on thread [" << QThread::currentThread() << "] name:" << name << "parent:" << parent; #endif QScriptValue setterFunction = QScriptEngine::newFunction(setter, 1); QScriptValue getterFunction = QScriptEngine::newFunction(getter); if (!parent.isNull() && !parent.isEmpty()) { QScriptValue object = QScriptEngine::globalObject().property(parent); if (object.isValid()) { object.setProperty(name, setterFunction, QScriptValue::PropertySetter); object.setProperty(name, getterFunction, QScriptValue::PropertyGetter); } } else { QScriptEngine::globalObject().setProperty(name, setterFunction, QScriptValue::PropertySetter); QScriptEngine::globalObject().setProperty(name, getterFunction, QScriptValue::PropertyGetter); } } void ScriptEngineQtScript::registerGetterSetter(const QString& name, ScriptEngine::FunctionSignature getter, ScriptEngine::FunctionSignature setter, const QString& parent) { if (QThread::currentThread() != QScriptEngine::thread()) { #ifdef THREAD_DEBUGGING qCDebug(scriptengine) << "*** WARNING *** ScriptEngineQtScript::registerGetterSetter() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " " name:" << name << "parent:" << parent; #endif QMetaObject::invokeMethod(this, "registerGetterSetter", Q_ARG(const QString&, name), Q_ARG(ScriptEngine::FunctionSignature, getter), Q_ARG(ScriptEngine::FunctionSignature, setter), Q_ARG(const QString&, parent)); return; } #ifdef THREAD_DEBUGGING qCDebug(scriptengine) << "ScriptEngineQtScript::registerGetterSetter() called on thread [" << QThread::currentThread() << "] name:" << name << "parent:" << parent; #endif auto setterFunction = newFunction(setter, 1); auto getterFunction = newFunction(getter); if (!parent.isNull() && !parent.isEmpty()) { auto object = globalObject().property(parent); if (object.isValid()) { object.setProperty(name, setterFunction, ScriptValue::PropertySetter); object.setProperty(name, getterFunction, ScriptValue::PropertyGetter); } } else { globalObject().setProperty(name, setterFunction, ScriptValue::PropertySetter); globalObject().setProperty(name, getterFunction, ScriptValue::PropertyGetter); } } ScriptValue ScriptEngineQtScript::evaluateInClosure(const ScriptValue& _closure, const ScriptProgramPointer& _program) { PROFILE_RANGE(script, "evaluateInClosure"); if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { return nullValue(); } ScriptProgramQtWrapper* unwrappedProgram = ScriptProgramQtWrapper::unwrap(_program); if (unwrappedProgram == nullptr) { return nullValue(); } const QScriptProgram& program = unwrappedProgram->toQtValue(); const auto fileName = program.fileName(); const auto shortName = QUrl(fileName).fileName(); ScriptValueQtWrapper* unwrappedClosure = ScriptValueQtWrapper::unwrap(_closure); if (unwrappedClosure == nullptr) { return nullValue(); } const QScriptValue& closure = unwrappedClosure->toQtValue(); QScriptValue oldGlobal; auto global = closure.property("global"); if (global.isObject()) { #ifdef DEBUG_JS qCDebug(shared) << " setting global = closure.global" << shortName; #endif oldGlobal = QScriptEngine::globalObject(); setGlobalObject(global); } auto context = pushContext(); auto thiz = closure.property("this"); if (thiz.isObject()) { #ifdef DEBUG_JS qCDebug(shared) << " setting this = closure.this" << shortName; #endif context->setThisObject(thiz); } context->pushScope(closure); #ifdef DEBUG_JS qCDebug(shared) << QString("[%1] evaluateInClosure %2").arg(isEvaluating()).arg(shortName); #endif ScriptValue result; { auto qResult = QScriptEngine::evaluate(program); if (hasUncaughtException()) { auto err = cloneUncaughtException(__FUNCTION__); #ifdef DEBUG_JS_EXCEPTIONS qCWarning(shared) << __FUNCTION__ << "---------- hasCaught:" << err.toString() << result.toString(); err.setProperty("_result", result); #endif result = err; } else { result = ScriptValue(new ScriptValueQtWrapper(this, std::move(qResult))); } } #ifdef DEBUG_JS qCDebug(shared) << QString("[%1] //evaluateInClosure %2").arg(isEvaluating()).arg(shortName); #endif popContext(); if (oldGlobal.isValid()) { #ifdef DEBUG_JS qCDebug(shared) << " restoring global" << shortName; #endif setGlobalObject(oldGlobal); } return result; } ScriptValue ScriptEngineQtScript::evaluate(const QString& sourceCode, const QString& fileName) { if (_manager && _manager->isStopped()) { return undefinedValue(); // bail early } if (QThread::currentThread() != QScriptEngine::thread()) { ScriptValue result; #ifdef THREAD_DEBUGGING qCDebug(scriptengine) << "*** WARNING *** ScriptEngineQtScript::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " "sourceCode:" << sourceCode << " fileName:" << fileName; #endif BLOCKING_INVOKE_METHOD(this, "evaluate", Q_RETURN_ARG(ScriptValue, result), Q_ARG(const QString&, sourceCode), Q_ARG(const QString&, fileName)); return result; } // Check syntax auto syntaxError = lintScript(sourceCode, fileName); if (syntaxError.isError()) { if (!isEvaluating()) { syntaxError.setProperty("detail", "evaluate"); } raiseException(syntaxError); maybeEmitUncaughtException("lint"); return syntaxError; } QScriptProgram program { sourceCode, fileName, 1 }; if (program.isNull()) { // can this happen? auto err = makeError(newValue("could not create QScriptProgram for " + fileName)); raiseException(err); maybeEmitUncaughtException("compile"); return err; } QScriptValue result = QScriptEngine::evaluate(program); maybeEmitUncaughtException("evaluate"); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result))); } Q_INVOKABLE ScriptValue ScriptEngineQtScript::evaluate(const ScriptProgramPointer& program) { if (_manager && _manager->isStopped()) { return undefinedValue(); // bail early } if (QThread::currentThread() != QScriptEngine::thread()) { ScriptValue result; #ifdef THREAD_DEBUGGING qCDebug(scriptengine) << "*** WARNING *** ScriptEngineQtScript::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " "sourceCode:" << sourceCode << " fileName:" << fileName; #endif BLOCKING_INVOKE_METHOD(this, "evaluate", Q_RETURN_ARG(ScriptValue, result), Q_ARG(const ScriptProgramPointer&, program)); return result; } ScriptProgramQtWrapper* unwrapped = ScriptProgramQtWrapper::unwrap(program); if (!unwrapped) { auto err = makeError(newValue("could not unwrap program")); raiseException(err); maybeEmitUncaughtException("compile"); return err; } const QScriptProgram& qProgram = unwrapped->toQtValue(); if (qProgram.isNull()) { // can this happen? auto err = makeError(newValue("requested program is empty")); raiseException(err); maybeEmitUncaughtException("compile"); return err; } QScriptValue result = QScriptEngine::evaluate(qProgram); maybeEmitUncaughtException("evaluate"); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result))); } void ScriptEngineQtScript::updateMemoryCost(const qint64& deltaSize) { if (deltaSize > 0) { // We've patched qt to fix https://highfidelity.atlassian.net/browse/BUGZ-46 on mac and windows only. #if defined(Q_OS_WIN) || defined(Q_OS_MAC) reportAdditionalMemoryCost(deltaSize); #endif } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ScriptEngine implementation ScriptValue ScriptEngineQtScript::globalObject() const { QScriptValue global = QScriptEngine::globalObject(); // can't cache the value as it may change return ScriptValue(new ScriptValueQtWrapper(const_cast < ScriptEngineQtScript*>(this), std::move(global))); } ScriptManager* ScriptEngineQtScript::manager() const { return _manager; } ScriptValue ScriptEngineQtScript::newArray(uint length) { QScriptValue result = QScriptEngine::newArray(length); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result))); } ScriptValue ScriptEngineQtScript::newArrayBuffer(const QByteArray& message) { QScriptValue data = QScriptEngine::newVariant(QVariant::fromValue(message)); QScriptValue ctor = QScriptEngine::globalObject().property("ArrayBuffer"); auto array = qscriptvalue_cast<ArrayBufferClass*>(ctor.data()); if (!array) { return undefinedValue(); } QScriptValue result = QScriptEngine::newObject(array, data); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result))); } ScriptValue ScriptEngineQtScript::newObject() { QScriptValue result = QScriptEngine::newObject(); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result))); } ScriptProgramPointer ScriptEngineQtScript::newProgram(const QString& sourceCode, const QString& fileName) { QScriptProgram result(sourceCode, fileName); return std::make_shared<ScriptProgramQtWrapper>(this, result); } ScriptValue ScriptEngineQtScript::newQObject(QObject* object, ScriptEngine::ValueOwnership ownership, const ScriptEngine::QObjectWrapOptions& options) { QScriptValue result = QScriptEngine::newQObject(object, static_cast<QScriptEngine::ValueOwnership>(ownership), (QScriptEngine::QObjectWrapOptions)((int)options | DEFAULT_QOBJECT_WRAP_OPTIONS)); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result))); } ScriptValue ScriptEngineQtScript::newValue(bool value) { QScriptValue result(this, value); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result))); } ScriptValue ScriptEngineQtScript::newValue(int value) { QScriptValue result(this, value); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result))); } ScriptValue ScriptEngineQtScript::newValue(uint value) { QScriptValue result(this, value); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result))); } ScriptValue ScriptEngineQtScript::newValue(double value) { QScriptValue result(this, value); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result))); } ScriptValue ScriptEngineQtScript::newValue(const QString& value) { QScriptValue result(this, value); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result))); } ScriptValue ScriptEngineQtScript::newValue(const QLatin1String& value) { QScriptValue result(this, value); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result))); } ScriptValue ScriptEngineQtScript::newValue(const char* value) { QScriptValue result(this, value); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result))); } ScriptValue ScriptEngineQtScript::newVariant(const QVariant& value) { QScriptValue result = QScriptEngine::newVariant(value); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result))); } ScriptValue ScriptEngineQtScript::nullValue() { return _nullValue; } void ScriptEngineQtScript::setDefaultPrototype(int metaTypeId, const ScriptValue& prototype){ ScriptValueQtWrapper* unwrappedPrototype = ScriptValueQtWrapper::unwrap(prototype); if (unwrappedPrototype) { QScriptEngine::setDefaultPrototype(metaTypeId, unwrappedPrototype->toQtValue()); } } ScriptValue ScriptEngineQtScript::undefinedValue() { return _undefinedValue; } void ScriptEngineQtScript::abortEvaluation() { QScriptEngine::abortEvaluation(); } void ScriptEngineQtScript::clearExceptions() { QScriptEngine::clearExceptions(); } ScriptContext* ScriptEngineQtScript::currentContext() const { QScriptContext* localCtx = QScriptEngine::currentContext(); if (!localCtx) { return nullptr; } if (!_currContext || _currContext->toQtValue() != localCtx) { _currContext = std::make_shared<ScriptContextQtWrapper>(const_cast<ScriptEngineQtScript*>(this), localCtx); } return _currContext.get(); } bool ScriptEngineQtScript::hasUncaughtException() const { return QScriptEngine::hasUncaughtException(); } bool ScriptEngineQtScript::isEvaluating() const { return QScriptEngine::isEvaluating(); } ScriptValue ScriptEngineQtScript::newFunction(ScriptEngine::FunctionSignature fun, int length) { auto innerFunc = [](QScriptContext* _context, QScriptEngine* _engine) -> QScriptValue { auto callee = _context->callee(); QVariant funAddr = callee.property("_func").toVariant(); ScriptEngine::FunctionSignature fun = reinterpret_cast<ScriptEngine::FunctionSignature>(funAddr.toULongLong()); ScriptEngineQtScript* engine = static_cast<ScriptEngineQtScript*>(_engine); ScriptContextQtWrapper context(engine, _context); ScriptValue result = fun(&context, engine); ScriptValueQtWrapper* unwrapped = ScriptValueQtWrapper::unwrap(result); return unwrapped ? unwrapped->toQtValue() : QScriptValue(); }; QScriptValue result = QScriptEngine::newFunction(innerFunc, length); auto funAddr = QScriptEngine::newVariant(QVariant(reinterpret_cast<qulonglong>(fun))); result.setProperty("_func", funAddr, QScriptValue::PropertyFlags(QScriptValue::ReadOnly + QScriptValue::Undeletable + QScriptValue::SkipInEnumeration)); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result))); } void ScriptEngineQtScript::setObjectName(const QString& name) { QScriptEngine::setObjectName(name); } bool ScriptEngineQtScript::setProperty(const char* name, const QVariant& value) { return QScriptEngine::setProperty(name, value); } void ScriptEngineQtScript::setProcessEventsInterval(int interval) { QScriptEngine::setProcessEventsInterval(interval); } QThread* ScriptEngineQtScript::thread() const { return QScriptEngine::thread(); } void ScriptEngineQtScript::setThread(QThread* thread) { moveToThread(thread); } ScriptValue ScriptEngineQtScript::uncaughtException() const { QScriptValue result = QScriptEngine::uncaughtException(); return ScriptValue(new ScriptValueQtWrapper(const_cast<ScriptEngineQtScript*>(this), std::move(result))); } QStringList ScriptEngineQtScript::uncaughtExceptionBacktrace() const { return QScriptEngine::uncaughtExceptionBacktrace(); } int ScriptEngineQtScript::uncaughtExceptionLineNumber() const { return QScriptEngine::uncaughtExceptionLineNumber(); } bool ScriptEngineQtScript::raiseException(const ScriptValue& exception) { ScriptValueQtWrapper* unwrapped = ScriptValueQtWrapper::unwrap(exception); QScriptValue qException = unwrapped ? unwrapped->toQtValue() : QScriptEngine::newVariant(exception.toVariant()); return raiseException(qException); } ScriptValue ScriptEngineQtScript::create(int type, const void* ptr) { QScriptValue result = qScriptValueFromValue_helper(this, type, ptr); return ScriptValue(new ScriptValueQtWrapper(const_cast<ScriptEngineQtScript*>(this), std::move(result))); } bool ScriptEngineQtScript::convert(const ScriptValue& value, int type, void* ptr) { ScriptValueQtWrapper* unwrapped = ScriptValueQtWrapper::unwrap(value); if (unwrapped == nullptr) { return false; } return qscriptvalue_cast_helper(unwrapped->toQtValue(), type, ptr); } template <int i> class CustomTypeInstance { public: static ScriptEngine::MarshalFunction marshalFunc; static ScriptEngine::DemarshalFunction demarshalFunc; static QScriptValue internalMarshalFunc(QScriptEngine* engine, const void* src) { ScriptEngineQtScript* unwrappedEngine = static_cast<ScriptEngineQtScript*>(engine); ScriptValue dest = marshalFunc(unwrappedEngine, src); return ScriptValueQtWrapper::fullUnwrap(unwrappedEngine, dest); } static void internalDemarshalFunc(const QScriptValue& src, void* dest) { ScriptEngineQtScript* unwrappedEngine = static_cast<ScriptEngineQtScript*>(src.engine()); ScriptValue wrappedSrc(new ScriptValueQtWrapper(unwrappedEngine, src)); demarshalFunc(wrappedSrc, dest); } }; template <int i> ScriptEngine::MarshalFunction CustomTypeInstance<i>::marshalFunc; template <int i> ScriptEngine::DemarshalFunction CustomTypeInstance<i>::demarshalFunc; // I would *LOVE* it if there was a different way to do this, jeez! // Qt requires two functions that have no parameters that give any context, // one of the must return a QScriptValue (so we can't void* them into generics and stick them in the templates). // This *has* to be done via templates but the whole point of this is to avoid leaking types into the rest of // the system that would require anyone other than us to have a dependency on QtScript #define CUSTOM_TYPE_ENTRY(idx) \ case idx: \ CustomTypeInstance<idx>::marshalFunc = marshalFunc; \ CustomTypeInstance<idx>::demarshalFunc = demarshalFunc; \ internalMarshalFunc = CustomTypeInstance<idx>::internalMarshalFunc; \ internalDemarshalFunc = CustomTypeInstance<idx>::internalDemarshalFunc; \ break; #define CUSTOM_TYPE_ENTRY_10(idx) \ CUSTOM_TYPE_ENTRY((idx * 10)); \ CUSTOM_TYPE_ENTRY((idx * 10) + 1); \ CUSTOM_TYPE_ENTRY((idx * 10) + 2); \ CUSTOM_TYPE_ENTRY((idx * 10) + 3); \ CUSTOM_TYPE_ENTRY((idx * 10) + 4); \ CUSTOM_TYPE_ENTRY((idx * 10) + 5); \ CUSTOM_TYPE_ENTRY((idx * 10) + 6); \ CUSTOM_TYPE_ENTRY((idx * 10) + 7); \ CUSTOM_TYPE_ENTRY((idx * 10) + 8); \ CUSTOM_TYPE_ENTRY((idx * 10) + 9); void ScriptEngineQtScript::registerCustomType(int type, ScriptEngine::MarshalFunction marshalFunc, ScriptEngine::DemarshalFunction demarshalFunc, const ScriptValue& prototype) { QScriptValue unwrapped = ScriptValueQtWrapper::fullUnwrap(this, prototype); QScriptEngine::MarshalFunction internalMarshalFunc; QScriptEngine::DemarshalFunction internalDemarshalFunc; if (_nextCustomType >= 300) { // have we ran out of translators? Q_ASSERT(false);