mirror of
https://github.com/overte-org/overte.git
synced 2025-04-11 12:20:26 +02:00
working to clean up the QtScript implementation and move towards completion of the proxy interface
This commit is contained in:
parent
1e018dbc64
commit
2dd9d784a9
38 changed files with 2239 additions and 922 deletions
|
@ -1,10 +0,0 @@
|
|||
set(TARGET_NAME script-engine-qtscript)
|
||||
# FIXME Move undo scripting interface to application and remove Widgets
|
||||
setup_hifi_library(Gui Script ScriptTools)
|
||||
|
||||
link_hifi_libraries()
|
||||
include_hifi_library_headers(animation)
|
||||
include_hifi_library_headers(entities)
|
||||
include_hifi_library_headers(networking)
|
||||
include_hifi_library_headers(shared)
|
||||
include_hifi_library_headers(script-engine)
|
|
@ -1,332 +0,0 @@
|
|||
//
|
||||
// BaseScriptEngine.cpp
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Timothy Dedischew on 02/01/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "BaseScriptEngine.h"
|
||||
#include "SharedLogging.h"
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtScript/QScriptValue>
|
||||
#include <QtScript/QScriptValueIterator>
|
||||
#include <QtScript/QScriptContextInfo>
|
||||
|
||||
#include "Profile.h"
|
||||
|
||||
bool BaseScriptEngine::IS_THREADSAFE_INVOCATION(const QThread *thread, const QString& method) {
|
||||
if (QThread::currentThread() == thread) {
|
||||
return true;
|
||||
}
|
||||
qCCritical(shared) << QString("Scripting::%1 @ %2 -- ignoring thread-unsafe call from %3")
|
||||
.arg(method).arg(thread ? thread->objectName() : "(!thread)").arg(QThread::currentThread()->objectName());
|
||||
qCDebug(shared) << "(please resolve on the calling side by using invokeMethod, executeOnScriptThread, etc.)";
|
||||
Q_ASSERT(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// engine-aware JS Error copier and factory
|
||||
QScriptValue BaseScriptEngine::makeError(const QScriptValue& _other, const QString& type) {
|
||||
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
||||
return unboundNullValue();
|
||||
}
|
||||
auto other = _other;
|
||||
if (other.isString()) {
|
||||
other = newObject();
|
||||
other.setProperty("message", _other.toString());
|
||||
}
|
||||
auto proto = globalObject().property(type);
|
||||
if (!proto.isFunction()) {
|
||||
proto = globalObject().property(other.prototype().property("constructor").property("name").toString());
|
||||
}
|
||||
if (!proto.isFunction()) {
|
||||
#ifdef DEBUG_JS_EXCEPTIONS
|
||||
qCDebug(shared) << "BaseScriptEngine::makeError -- couldn't find constructor for" << type << " -- using Error instead";
|
||||
#endif
|
||||
proto = globalObject().property("Error");
|
||||
}
|
||||
if (other.engine() != this) {
|
||||
// JS Objects are parented to a specific script engine instance
|
||||
// -- this effectively ~clones it locally by routing through a QVariant and back
|
||||
other = toScriptValue(other.toVariant());
|
||||
}
|
||||
// ~ var err = new Error(other.message)
|
||||
auto err = proto.construct(QScriptValueList({other.property("message")}));
|
||||
|
||||
// transfer over any existing properties
|
||||
QScriptValueIterator it(other);
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
err.setProperty(it.name(), it.value());
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
// check syntax and when there are issues returns an actual "SyntaxError" with the details
|
||||
QScriptValue BaseScriptEngine::lintScript(const QString& sourceCode, const QString& fileName, const int lineNumber) {
|
||||
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
||||
return unboundNullValue();
|
||||
}
|
||||
const auto syntaxCheck = checkSyntax(sourceCode);
|
||||
if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {
|
||||
auto err = globalObject().property("SyntaxError")
|
||||
.construct(QScriptValueList({syntaxCheck.errorMessage()}));
|
||||
err.setProperty("fileName", fileName);
|
||||
err.setProperty("lineNumber", syntaxCheck.errorLineNumber());
|
||||
err.setProperty("expressionBeginOffset", syntaxCheck.errorColumnNumber());
|
||||
err.setProperty("stack", currentContext()->backtrace().join(ScriptManager::SCRIPT_BACKTRACE_SEP));
|
||||
{
|
||||
const auto error = syntaxCheck.errorMessage();
|
||||
const auto line = QString::number(syntaxCheck.errorLineNumber());
|
||||
const auto column = QString::number(syntaxCheck.errorColumnNumber());
|
||||
// for compatibility with legacy reporting
|
||||
const auto message = QString("[SyntaxError] %1 in %2:%3(%4)").arg(error, fileName, line, column);
|
||||
err.setProperty("formatted", message);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
// this pulls from the best available information to create a detailed snapshot of the current exception
|
||||
QScriptValue BaseScriptEngine::cloneUncaughtException(const QString& extraDetail) {
|
||||
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
||||
return unboundNullValue();
|
||||
}
|
||||
if (!hasUncaughtException()) {
|
||||
return unboundNullValue();
|
||||
}
|
||||
auto exception = uncaughtException();
|
||||
// ensure the error object is engine-local
|
||||
auto err = makeError(exception);
|
||||
|
||||
// not sure why Qt does't offer uncaughtExceptionFileName -- but the line number
|
||||
// on its own is often useless/wrong if arbitrarily married to a filename.
|
||||
// when the error object already has this info, it seems to be the most reliable
|
||||
auto fileName = exception.property("fileName").toString();
|
||||
auto lineNumber = exception.property("lineNumber").toInt32();
|
||||
|
||||
// the backtrace, on the other hand, seems most reliable taken from uncaughtExceptionBacktrace
|
||||
auto backtrace = uncaughtExceptionBacktrace();
|
||||
if (backtrace.isEmpty()) {
|
||||
// fallback to the error object
|
||||
backtrace = exception.property("stack").toString().split(ScriptManager::SCRIPT_BACKTRACE_SEP);
|
||||
}
|
||||
// the ad hoc "detail" property can be used now to embed additional clues
|
||||
auto detail = exception.property("detail").toString();
|
||||
if (detail.isEmpty()) {
|
||||
detail = extraDetail;
|
||||
} else if (!extraDetail.isEmpty()) {
|
||||
detail += "(" + extraDetail + ")";
|
||||
}
|
||||
if (lineNumber <= 0) {
|
||||
lineNumber = uncaughtExceptionLineNumber();
|
||||
}
|
||||
if (fileName.isEmpty()) {
|
||||
// climb the stack frames looking for something useful to display
|
||||
for (auto c = currentContext(); c && fileName.isEmpty(); c = c->parentContext()) {
|
||||
QScriptContextInfo info { c };
|
||||
if (!info.fileName().isEmpty()) {
|
||||
// take fileName:lineNumber as a pair
|
||||
fileName = info.fileName();
|
||||
lineNumber = info.lineNumber();
|
||||
if (backtrace.isEmpty()) {
|
||||
backtrace = c->backtrace();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
err.setProperty("fileName", fileName);
|
||||
err.setProperty("lineNumber", lineNumber );
|
||||
err.setProperty("detail", detail);
|
||||
err.setProperty("stack", backtrace.join(ScriptManager::SCRIPT_BACKTRACE_SEP));
|
||||
|
||||
#ifdef DEBUG_JS_EXCEPTIONS
|
||||
err.setProperty("_fileName", exception.property("fileName").toString());
|
||||
err.setProperty("_stack", uncaughtExceptionBacktrace().join(SCRIPT_BACKTRACE_SEP));
|
||||
err.setProperty("_lineNumber", uncaughtExceptionLineNumber());
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
bool BaseScriptEngine::raiseException(const QScriptValue& exception) {
|
||||
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
||||
return false;
|
||||
}
|
||||
if (currentContext()) {
|
||||
// we have an active context / JS stack frame so throw the exception per usual
|
||||
currentContext()->throwValue(makeError(exception));
|
||||
return true;
|
||||
} else {
|
||||
// we are within a pure C++ stack frame (ie: being called directly by other C++ code)
|
||||
// in this case no context information is available so just emit the exception for reporting
|
||||
emit unhandledException(makeError(exception));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BaseScriptEngine::maybeEmitUncaughtException(const QString& debugHint) {
|
||||
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
||||
return false;
|
||||
}
|
||||
if (!isEvaluating() && hasUncaughtException()) {
|
||||
emit unhandledException(cloneUncaughtException(debugHint));
|
||||
clearExceptions();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QScriptValue BaseScriptEngine::evaluateInClosure(const QScriptValue& closure, const QScriptProgram& program) {
|
||||
PROFILE_RANGE(script, "evaluateInClosure");
|
||||
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
||||
return unboundNullValue();
|
||||
}
|
||||
const auto fileName = program.fileName();
|
||||
const auto shortName = QUrl(fileName).fileName();
|
||||
|
||||
QScriptValue result;
|
||||
QScriptValue oldGlobal;
|
||||
auto global = closure.property("global");
|
||||
if (global.isObject()) {
|
||||
#ifdef DEBUG_JS
|
||||
qCDebug(shared) << " setting global = closure.global" << shortName;
|
||||
#endif
|
||||
oldGlobal = 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
|
||||
{
|
||||
result = BaseScriptEngine::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;
|
||||
}
|
||||
}
|
||||
#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;
|
||||
}
|
||||
|
||||
// Lambda
|
||||
QScriptValue BaseScriptEngine::newLambdaFunction(std::function<QScriptValue(QScriptContext *, QScriptEngine*)> operation, const QScriptValue& data, const QScriptEngine::ValueOwnership& ownership) {
|
||||
auto lambda = new Lambda(this, operation, data);
|
||||
auto object = newQObject(lambda, ownership);
|
||||
auto call = object.property("call");
|
||||
call.setPrototype(object); // context->callee().prototype() === Lambda QObject
|
||||
call.setData(data); // context->callee().data() will === data param
|
||||
return call;
|
||||
}
|
||||
QString Lambda::toString() const {
|
||||
return QString("[Lambda%1]").arg(data.isValid() ? " " + data.toString() : data.toString());
|
||||
}
|
||||
|
||||
Lambda::~Lambda() {
|
||||
#ifdef DEBUG_JS_LAMBDA_FUNCS
|
||||
qDebug() << "~Lambda" << "this" << this;
|
||||
#endif
|
||||
}
|
||||
|
||||
Lambda::Lambda(QScriptEngine *engine, std::function<QScriptValue(QScriptContext *, QScriptEngine*)> operation, QScriptValue data)
|
||||
: engine(engine), operation(operation), data(data) {
|
||||
#ifdef DEBUG_JS_LAMBDA_FUNCS
|
||||
qDebug() << "Lambda" << data.toString();
|
||||
#endif
|
||||
}
|
||||
QScriptValue Lambda::call() {
|
||||
if (!BaseScriptEngine::IS_THREADSAFE_INVOCATION(engine->thread(), __FUNCTION__)) {
|
||||
return BaseScriptEngine::unboundNullValue();
|
||||
}
|
||||
return operation(engine->currentContext(), engine);
|
||||
}
|
||||
|
||||
QScriptValue makeScopedHandlerObject(QScriptValue scopeOrCallback, QScriptValue methodOrName) {
|
||||
auto engine = scopeOrCallback.engine();
|
||||
if (!engine) {
|
||||
return scopeOrCallback;
|
||||
}
|
||||
auto scope = QScriptValue();
|
||||
auto callback = scopeOrCallback;
|
||||
if (scopeOrCallback.isObject()) {
|
||||
if (methodOrName.isString()) {
|
||||
scope = scopeOrCallback;
|
||||
callback = scope.property(methodOrName.toString());
|
||||
} else if (methodOrName.isFunction()) {
|
||||
scope = scopeOrCallback;
|
||||
callback = methodOrName;
|
||||
} else if (!methodOrName.isValid()) {
|
||||
// instantiate from an existing scoped handler object
|
||||
if (scopeOrCallback.property("callback").isFunction()) {
|
||||
scope = scopeOrCallback.property("scope");
|
||||
callback = scopeOrCallback.property("callback");
|
||||
}
|
||||
}
|
||||
}
|
||||
auto handler = engine->newObject();
|
||||
handler.setProperty("scope", scope);
|
||||
handler.setProperty("callback", callback);
|
||||
return handler;
|
||||
}
|
||||
|
||||
QScriptValue callScopedHandlerObject(QScriptValue handler, QScriptValue err, QScriptValue result) {
|
||||
return handler.property("callback").call(handler.property("scope"), QScriptValueList({ err, result }));
|
||||
}
|
||||
|
||||
#ifdef DEBUG_JS
|
||||
void BaseScriptEngine::_debugDump(const QString& header, const QScriptValue& object, const QString& footer) {
|
||||
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
||||
return;
|
||||
}
|
||||
if (!header.isEmpty()) {
|
||||
qCDebug(shared) << header;
|
||||
}
|
||||
if (!object.isObject()) {
|
||||
qCDebug(shared) << "(!isObject)" << object.toVariant().toString() << object.toString();
|
||||
return;
|
||||
}
|
||||
QScriptValueIterator it(object);
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
qCDebug(shared) << it.name() << ":" << it.value().toString();
|
||||
}
|
||||
if (!footer.isEmpty()) {
|
||||
qCDebug(shared) << footer;
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -1,493 +0,0 @@
|
|||
//
|
||||
// ScriptEngineQtScript.cpp
|
||||
// libraries/script-engine-qtscript/src
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/14/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "ScriptEngineQtScript.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QEventLoop>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QRegularExpression>
|
||||
|
||||
#include <QtCore/QFuture>
|
||||
#include <QtConcurrent/QtConcurrentRun>
|
||||
|
||||
#include <QtWidgets/QMainWindow>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QMenuBar>
|
||||
#include <QtWidgets/QMenu>
|
||||
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
#include <QtScript/QScriptContextInfo>
|
||||
#include <QtScript/QScriptValue>
|
||||
#include <QtScript/QScriptValueIterator>
|
||||
|
||||
#include <QtScriptTools/QScriptEngineDebugger>
|
||||
|
||||
#include <shared/LocalFileAccessGate.h>
|
||||
#include <shared/QtHelpers.h>
|
||||
#include <shared/AbstractLoggerInterface.h>
|
||||
//#include <AudioConstants.h>
|
||||
//#include <AudioEffectOptions.h>
|
||||
//#include <AvatarData.h>
|
||||
//#include <DebugDraw.h>
|
||||
//#include <EntityScriptingInterface.h>
|
||||
//#include <MessagesClient.h>
|
||||
//#include <NetworkAccessManager.h>
|
||||
//#include <PathUtils.h>
|
||||
//#include <ResourceScriptingInterface.h>
|
||||
//#include <UserActivityLoggerScriptingInterface.h>
|
||||
//#include <NodeList.h>
|
||||
//#include <ScriptAvatarData.h>
|
||||
//#include <udt/PacketHeaders.h>
|
||||
//#include <UUID.h>
|
||||
|
||||
//#include <controllers/ScriptingInterface.h>
|
||||
//#include <AnimationObject.h>
|
||||
#include <ScriptEngineLogging.h>
|
||||
|
||||
//#include "ArrayBufferViewClass.h"
|
||||
//#include "AssetScriptingInterface.h"
|
||||
//#include "BatchLoader.h"
|
||||
//#include "BaseScriptEngine.h"
|
||||
//#include "DataViewClass.h"
|
||||
//#include "EventTypes.h"
|
||||
//#include "FileScriptingInterface.h" // unzip project
|
||||
//#include "MenuItemProperties.h"
|
||||
//#include "ScriptAudioInjector.h"
|
||||
//#include "ScriptAvatarData.h"
|
||||
//#include "ScriptCache.h"
|
||||
//#include "TypedArrays.h"
|
||||
//#include "XMLHttpRequestClass.h"
|
||||
//#include "WebSocketClass.h"
|
||||
//#include "RecordingScriptingInterface.h"
|
||||
//#include "ScriptEngines.h"
|
||||
//#include "StackTestScriptingInterface.h"
|
||||
//#include "ModelScriptingInterface.h"
|
||||
|
||||
//#include <Profile.h>
|
||||
|
||||
//#include "../../midi/src/Midi.h" // FIXME why won't a simpler include work?
|
||||
//#include "MIDIEvent.h"
|
||||
|
||||
//#include "SettingHandle.h"
|
||||
//#include <AddressManager.h>
|
||||
//#include <NetworkingConstants.h>
|
||||
//#include <ThreadHelpers.h>
|
||||
|
||||
static const int MAX_MODULE_ID_LENGTH { 4096 };
|
||||
static const int MAX_DEBUG_VALUE_LENGTH { 80 };
|
||||
|
||||
static const QScriptEngine::QObjectWrapOptions DEFAULT_QOBJECT_WRAP_OPTIONS =
|
||||
QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects;
|
||||
static const QScriptValue::PropertyFlags READONLY_PROP_FLAGS { QScriptValue::ReadOnly | QScriptValue::Undeletable };
|
||||
static const QScriptValue::PropertyFlags READONLY_HIDDEN_PROP_FLAGS { READONLY_PROP_FLAGS | QScriptValue::SkipInEnumeration };
|
||||
|
||||
static const bool HIFI_AUTOREFRESH_FILE_SCRIPTS { true };
|
||||
|
||||
Q_DECLARE_METATYPE(QScriptEngine::FunctionSignature)
|
||||
int functionSignatureMetaID = qRegisterMetaType<QScriptEngine::FunctionSignature>();
|
||||
|
||||
int scriptEnginePointerMetaID = qRegisterMetaType<ScriptEngineQtScriptPointer>();
|
||||
|
||||
static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine) {
|
||||
// assemble the message by concatenating our arguments
|
||||
QString message = "";
|
||||
for (int i = 0; i < context->argumentCount(); i++) {
|
||||
if (i > 0) {
|
||||
message += " ";
|
||||
}
|
||||
message += context->argument(i).toString();
|
||||
}
|
||||
|
||||
// was this generated by a script engine? If we don't recognize it then send the message and exit
|
||||
ScriptEngineQtScript* scriptEngine = qobject_cast<ScriptEngineQtScript*>(engine);
|
||||
if (!scriptEngine) {
|
||||
qCDebug(scriptengine_script, "%s", qUtf8Printable(message));
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
QString filename;
|
||||
auto scriptManager = scriptEngine->manager();
|
||||
if (scriptManager) {
|
||||
filename = scriptManager->getFilename();
|
||||
}
|
||||
|
||||
// This message was sent by one of our script engines, let's try to see if we can find the source.
|
||||
// Note that the first entry in the backtrace should be "print" and is somewhat useless to us
|
||||
AbstractLoggerInterface* loggerInterface = AbstractLoggerInterface::get();
|
||||
if (loggerInterface && loggerInterface->showSourceDebugging()) {
|
||||
QScriptContext* userContext = context;
|
||||
while (userContext && QScriptContextInfo(userContext).functionType() == QScriptContextInfo::NativeFunction) {
|
||||
userContext = userContext->parentContext();
|
||||
}
|
||||
QString location;
|
||||
if (userContext) {
|
||||
QScriptContextInfo contextInfo(userContext);
|
||||
QString fileName = contextInfo.fileName();
|
||||
int lineNumber = contextInfo.lineNumber();
|
||||
QString functionName = contextInfo.functionName();
|
||||
|
||||
location = functionName;
|
||||
if (!fileName.isEmpty()) {
|
||||
if (location.isEmpty()) {
|
||||
location = fileName;
|
||||
} else {
|
||||
location = QString("%1 at %2").arg(location).arg(fileName);
|
||||
}
|
||||
}
|
||||
if (lineNumber != -1) {
|
||||
location = QString("%1:%2").arg(location).arg(lineNumber);
|
||||
}
|
||||
}
|
||||
if (location.isEmpty()) {
|
||||
location = filename;
|
||||
}
|
||||
|
||||
// give the script engine a chance to notify the system about this message
|
||||
scriptEngine->print(message);
|
||||
|
||||
// send the message to debug log
|
||||
qCDebug(scriptengine_script, "[%s] %s", qUtf8Printable(location), qUtf8Printable(message));
|
||||
} else {
|
||||
scriptEngine->print(message);
|
||||
// prefix the script engine name to help disambiguate messages in the main debug log
|
||||
qCDebug(scriptengine_script, "[%s] %s", qUtf8Printable(filename), qUtf8Printable(message));
|
||||
}
|
||||
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
ScriptEngineQtScript::ScriptEngineQtScript(ScriptManager* scriptManager) :
|
||||
BaseScriptEngine(),
|
||||
_manager(scriptManager),
|
||||
_arrayBufferClass(new ArrayBufferClass(this))
|
||||
{
|
||||
if (_manager) {
|
||||
connect(this, &QScriptEngine::signalHandlerException, this, [this](const QScriptValue& exception) {
|
||||
if (Base::hasUncaughtException()) {
|
||||
// the engine's uncaughtException() seems to produce much better stack traces here
|
||||
emit _manager->unhandledException(Base::cloneUncaughtException("signalHandlerException"));
|
||||
Base::clearExceptions();
|
||||
} else {
|
||||
// ... but may not always be available -- so if needed we fallback to the passed exception
|
||||
emit _manager->unhandledException(exception);
|
||||
}
|
||||
}, Qt::DirectConnection);
|
||||
}
|
||||
|
||||
setProcessEventsInterval(MSECS_PER_SECOND);
|
||||
}
|
||||
|
||||
bool ScriptEngineQtScript::isDebugMode() const {
|
||||
#if defined(DEBUG)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
ScriptEngineQtScript::~ScriptEngineQtScript() {}
|
||||
|
||||
void ScriptEngineQtScript::disconnectNonEssentialSignals() {
|
||||
disconnect();
|
||||
QThread* workerThread;
|
||||
// Ensure the thread should be running, and does exist
|
||||
if (_isRunning && _isThreaded && (workerThread = Base::thread())) {
|
||||
connect(this, &QObject::destroyed, workerThread, &QThread::quit);
|
||||
connect(workerThread, &QThread::finished, workerThread, &QObject::deleteLater);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngineQtScript::executeOnScriptThread(std::function<void()> function, const Qt::ConnectionType& type ) {
|
||||
if (QThread::currentThread() != Base::thread()) {
|
||||
QMetaObject::invokeMethod(this, "executeOnScriptThread", type, Q_ARG(std::function<void()>, function));
|
||||
return;
|
||||
}
|
||||
|
||||
function();
|
||||
}
|
||||
|
||||
void ScriptEngineQtScript::registerValue(const QString& valueName, QScriptValue value) {
|
||||
if (QThread::currentThread() != Base::thread()) {
|
||||
#ifdef THREAD_DEBUGGING
|
||||
qCDebug(scriptengine) << "*** WARNING *** ScriptEngineQtScript::registerValue() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";
|
||||
#endif
|
||||
QMetaObject::invokeMethod(this, "registerValue",
|
||||
Q_ARG(const QString&, valueName),
|
||||
Q_ARG(QScriptValue, value));
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList pathToValue = valueName.split(".");
|
||||
int partsToGo = pathToValue.length();
|
||||
QScriptValue partObject = globalObject();
|
||||
|
||||
for (const auto& pathPart : pathToValue) {
|
||||
partsToGo--;
|
||||
if (!partObject.property(pathPart).isValid()) {
|
||||
if (partsToGo > 0) {
|
||||
//QObject *object = new QObject;
|
||||
QScriptValue partValue = newArray(); //newQObject(object, QScriptEngine::ScriptOwnership);
|
||||
partObject.setProperty(pathPart, partValue);
|
||||
} else {
|
||||
partObject.setProperty(pathPart, value);
|
||||
}
|
||||
}
|
||||
partObject = partObject.property(pathPart);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngineQtScript::registerGlobalObject(const QString& name, QObject* object) {
|
||||
if (QThread::currentThread() != Base::thread()) {
|
||||
#ifdef THREAD_DEBUGGING
|
||||
qCDebug(scriptengine) << "*** WARNING *** ScriptEngineQtScript::registerGlobalObject() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name;
|
||||
#endif
|
||||
QMetaObject::invokeMethod(this, "registerGlobalObject",
|
||||
Q_ARG(const QString&, name),
|
||||
Q_ARG(QObject*, object));
|
||||
return;
|
||||
}
|
||||
#ifdef THREAD_DEBUGGING
|
||||
qCDebug(scriptengine) << "ScriptEngineQtScript::registerGlobalObject() called on thread [" << QThread::currentThread() << "] name:" << name;
|
||||
#endif
|
||||
|
||||
if (!Base::globalObject().property(name).isValid()) {
|
||||
if (object) {
|
||||
QScriptValue value = Base::newQObject(object, QScriptEngine::QtOwnership, DEFAULT_QOBJECT_WRAP_OPTIONS);
|
||||
Base::globalObject().setProperty(name, value);
|
||||
} else {
|
||||
Base::globalObject().setProperty(name, QScriptValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngineQtScript::registerFunction(const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) {
|
||||
if (QThread::currentThread() != Base::thread()) {
|
||||
#ifdef THREAD_DEBUGGING
|
||||
qCDebug(scriptengine) << "*** WARNING *** ScriptEngineQtScript::registerFunction() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name;
|
||||
#endif
|
||||
QMetaObject::invokeMethod(this, "registerFunction",
|
||||
Q_ARG(const QString&, name),
|
||||
Q_ARG(QScriptEngine::FunctionSignature, functionSignature),
|
||||
Q_ARG(int, numArguments));
|
||||
return;
|
||||
}
|
||||
#ifdef THREAD_DEBUGGING
|
||||
qCDebug(scriptengine) << "ScriptEngineQtScript::registerFunction() called on thread [" << QThread::currentThread() << "] name:" << name;
|
||||
#endif
|
||||
|
||||
QScriptValue scriptFun = Base::newFunction(functionSignature, numArguments);
|
||||
Base::globalObject().setProperty(name, scriptFun);
|
||||
}
|
||||
|
||||
void ScriptEngineQtScript::registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) {
|
||||
if (QThread::currentThread() != Base::thread()) {
|
||||
#ifdef THREAD_DEBUGGING
|
||||
qCDebug(scriptengine) << "*** WARNING *** ScriptEngineQtScript::registerFunction() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] parent:" << parent << "name:" << name;
|
||||
#endif
|
||||
QMetaObject::invokeMethod(this, "registerFunction",
|
||||
Q_ARG(const QString&, name),
|
||||
Q_ARG(QScriptEngine::FunctionSignature, functionSignature),
|
||||
Q_ARG(int, numArguments));
|
||||
return;
|
||||
}
|
||||
#ifdef THREAD_DEBUGGING
|
||||
qCDebug(scriptengine) << "ScriptEngineQtScript::registerFunction() called on thread [" << QThread::currentThread() << "] parent:" << parent << "name:" << name;
|
||||
#endif
|
||||
|
||||
QScriptValue object = Base::globalObject().property(parent);
|
||||
if (object.isValid()) {
|
||||
QScriptValue scriptFun = Base::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() != Base::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 = Base::newFunction(setter, 1);
|
||||
QScriptValue getterFunction = Base::newFunction(getter);
|
||||
|
||||
if (!parent.isNull() && !parent.isEmpty()) {
|
||||
QScriptValue object = Base::globalObject().property(parent);
|
||||
if (object.isValid()) {
|
||||
object.setProperty(name, setterFunction, QScriptValue::PropertySetter);
|
||||
object.setProperty(name, getterFunction, QScriptValue::PropertyGetter);
|
||||
}
|
||||
} else {
|
||||
Base::globalObject().setProperty(name, setterFunction, QScriptValue::PropertySetter);
|
||||
Base::globalObject().setProperty(name, getterFunction, QScriptValue::PropertyGetter);
|
||||
}
|
||||
}
|
||||
|
||||
// this is not redundant -- the version in BaseScriptEngine is specifically not Q_INVOKABLE
|
||||
QScriptValue ScriptEngineQtScript::evaluateInClosure(const QScriptValue& closure, const QScriptProgram& program) {
|
||||
return BaseScriptEngine::evaluateInClosure(closure, program);
|
||||
}
|
||||
|
||||
QScriptValue ScriptEngineQtScript::evaluate(const QString& sourceCode, const QString& fileName, int lineNumber) {
|
||||
QSharedPointer<ScriptEngines> scriptEngines(_scriptEngines);
|
||||
if (!scriptEngines || scriptEngines->isStopped()) {
|
||||
return QScriptValue(); // bail early
|
||||
}
|
||||
|
||||
if (QThread::currentThread() != Base::thread()) {
|
||||
QScriptValue result;
|
||||
#ifdef THREAD_DEBUGGING
|
||||
qCDebug(scriptengine) << "*** WARNING *** ScriptEngineQtScript::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
|
||||
"sourceCode:" << sourceCode << " fileName:" << fileName << "lineNumber:" << lineNumber;
|
||||
#endif
|
||||
BLOCKING_INVOKE_METHOD(this, "evaluate",
|
||||
Q_RETURN_ARG(QScriptValue, result),
|
||||
Q_ARG(const QString&, sourceCode),
|
||||
Q_ARG(const QString&, fileName),
|
||||
Q_ARG(int, lineNumber));
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check syntax
|
||||
auto syntaxError = Base::lintScript(sourceCode, fileName);
|
||||
if (syntaxError.isError()) {
|
||||
if (!Base::isEvaluating()) {
|
||||
syntaxError.setProperty("detail", "evaluate");
|
||||
}
|
||||
Base::raiseException(syntaxError);
|
||||
Base::maybeEmitUncaughtException("lint");
|
||||
return syntaxError;
|
||||
}
|
||||
QScriptProgram program { sourceCode, fileName, lineNumber };
|
||||
if (program.isNull()) {
|
||||
// can this happen?
|
||||
auto err = Base::makeError("could not create QScriptProgram for " + fileName);
|
||||
Base::raiseException(err);
|
||||
Base::maybeEmitUncaughtException("compile");
|
||||
return err;
|
||||
}
|
||||
|
||||
QScriptValue result;
|
||||
{
|
||||
result = BaseScriptEngine::evaluate(program);
|
||||
Base::maybeEmitUncaughtException("evaluate");
|
||||
}
|
||||
return 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
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngineQtScript::print(const QString& message) {
|
||||
QString filename;
|
||||
auto scriptManager = manager();
|
||||
if (scriptManager) {
|
||||
filename = scriptManager->getFilename();
|
||||
}
|
||||
|
||||
emit printedMessage(message, filename);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ScriptEngine implementation
|
||||
/*
|
||||
ScriptValuePointer ScriptEngineQtScript::globalObject() const {
|
||||
return Base::globalObject();
|
||||
}
|
||||
|
||||
ScriptManager* ScriptEngineQtScript::manager() const {
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptEngineQtScript::newArray(uint length) {
|
||||
return Base::newArray(length);
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptEngineQtScript::newArrayBuffer(const QByteArray& message) {
|
||||
QScriptValue data = Base::newVariant(QVariant::fromValue(message));
|
||||
QScriptValue ctor = Base::globalObject().property("ArrayBuffer");
|
||||
auto array = qscriptvalue_cast<ArrayBufferClass*>(ctor.data());
|
||||
if (!array) {
|
||||
return undefinedValue()
|
||||
}
|
||||
return Base::newObject(array, data);
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptEngineQtScript::newObject() {
|
||||
return Base::newObject();
|
||||
}
|
||||
|
||||
ScriptProgramPointer ScriptEngineQtScript::newProgram(const QString& sourceCode, const QString& fileName) {
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptEngineQtScript::newQObject(QObject* obj) {
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptEngineQtScript::newValue(bool value) {
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptEngineQtScript::newValue(int value) {
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptEngineQtScript::newValue(uint value) {
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptEngineQtScript::newValue(double value) {
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptEngineQtScript::newValue(const QString& value) {
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptEngineQtScript::newValue(const QLatin1String& value) {
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptEngineQtScript::newValue(const char* value) {
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptEngineQtScript::newVariant(const QVariant& value) {
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptEngineQtScript::nullValue() {
|
||||
return Base::nullValue();
|
||||
}
|
||||
|
||||
void ScriptEngineQtScript::setDefaultPrototype(int metaTypeId, const ScriptValuePointer& prototype) {
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptEngineQtScript::undefinedValue() {
|
||||
return Base::undefinedValue();
|
||||
}
|
||||
*/
|
|
@ -1,13 +1,13 @@
|
|||
set(TARGET_NAME script-engine)
|
||||
# FIXME Move undo scripting interface to application and remove Widgets
|
||||
setup_hifi_library(Network WebSockets)
|
||||
setup_hifi_library(Gui Network Script ScriptTools WebSockets)
|
||||
|
||||
target_zlib()
|
||||
if (NOT ANDROID)
|
||||
target_quazip()
|
||||
endif ()
|
||||
|
||||
link_hifi_libraries(script-engine-qtscript shaders)
|
||||
link_hifi_libraries(shaders)
|
||||
include_hifi_library_headers(animation)
|
||||
include_hifi_library_headers(audio)
|
||||
include_hifi_library_headers(avatars)
|
||||
|
|
|
@ -14,11 +14,16 @@
|
|||
|
||||
#include <QtCore/QSharedPointer>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QStringList>
|
||||
|
||||
class ScriptContext;
|
||||
class ScriptEngine;
|
||||
class ScriptFunctionContext;
|
||||
class ScriptValue;
|
||||
using ScriptValuePointer = QSharedPointer<ScriptValue>;
|
||||
using ScriptContextPointer = QSharedPointer<ScriptContext>;
|
||||
using ScriptFunctionContextPointer = QSharedPointer<ScriptFunctionContext>;
|
||||
using ScriptEnginePointer = QSharedPointer<ScriptEngine>;
|
||||
using ScriptValuePointer = QSharedPointer<ScriptValue>;
|
||||
|
||||
class ScriptFunctionContext {
|
||||
public:
|
||||
|
@ -43,8 +48,8 @@ public:
|
|||
virtual QStringList backtrace() const = 0;
|
||||
virtual ScriptValuePointer callee() const = 0;
|
||||
virtual ScriptEnginePointer engine() const = 0;
|
||||
virtual ScriptFunctionContext* functionContext() const = 0;
|
||||
virtual ScriptContext* parentContext() const = 0;
|
||||
virtual ScriptFunctionContextPointer functionContext() const = 0;
|
||||
virtual ScriptContextPointer parentContext() const = 0;
|
||||
virtual ScriptValuePointer thisObject() const = 0;
|
||||
virtual ScriptValuePointer throwError(const QString& text) = 0;
|
||||
virtual ScriptValuePointer throwValue(const ScriptValuePointer& value) = 0;
|
||||
|
|
67
libraries/script-engine/src/ScriptEngine.cpp
Normal file
67
libraries/script-engine/src/ScriptEngine.cpp
Normal file
|
@ -0,0 +1,67 @@
|
|||
//
|
||||
// ScriptEngine.cpp
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/14/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "ScriptEngine.h"
|
||||
|
||||
#include "ScriptEngineLogging.h"
|
||||
#include "ScriptValue.h"
|
||||
#include "qtscript/ScriptEngineQtScript.h"
|
||||
|
||||
ScriptEnginePointer newScriptEngine(ScriptManager* manager) {
|
||||
return QSharedPointer<ScriptEngineQtScript>(new ScriptEngineQtScript(manager));
|
||||
}
|
||||
|
||||
ScriptValuePointer makeScopedHandlerObject(ScriptValuePointer scopeOrCallback, ScriptValuePointer methodOrName) {
|
||||
auto engine = scopeOrCallback->engine();
|
||||
if (!engine) {
|
||||
return scopeOrCallback;
|
||||
}
|
||||
ScriptValuePointer scope;
|
||||
ScriptValuePointer callback = scopeOrCallback;
|
||||
if (scopeOrCallback->isObject()) {
|
||||
if (methodOrName->isString()) {
|
||||
scope = scopeOrCallback;
|
||||
callback = scope->property(methodOrName->toString());
|
||||
} else if (methodOrName->isFunction()) {
|
||||
scope = scopeOrCallback;
|
||||
callback = methodOrName;
|
||||
} else if (!methodOrName->isValid()) {
|
||||
// instantiate from an existing scoped handler object
|
||||
if (scopeOrCallback->property("callback")->isFunction()) {
|
||||
scope = scopeOrCallback->property("scope");
|
||||
callback = scopeOrCallback->property("callback");
|
||||
}
|
||||
}
|
||||
}
|
||||
auto handler = engine->newObject();
|
||||
handler->setProperty("scope", scope);
|
||||
handler->setProperty("callback", callback);
|
||||
return handler;
|
||||
}
|
||||
|
||||
ScriptValuePointer callScopedHandlerObject(ScriptValuePointer handler, ScriptValuePointer err, ScriptValuePointer result) {
|
||||
return handler->property("callback")->call(handler->property("scope"), ScriptValueList({ err, result }));
|
||||
}
|
||||
|
||||
bool ScriptEngine::IS_THREADSAFE_INVOCATION(const QString& method) {
|
||||
QThread* thread = this->thread();
|
||||
if (QThread::currentThread() == thread) {
|
||||
return true;
|
||||
}
|
||||
qCCritical(scriptengine) << QString("Scripting::%1 @ %2 -- ignoring thread-unsafe call from %3")
|
||||
.arg(method)
|
||||
.arg(thread ? thread->objectName() : "(!thread)")
|
||||
.arg(QThread::currentThread()->objectName());
|
||||
qCDebug(scriptengine) << "(please resolve on the calling side by using invokeMethod, executeOnScriptThread, etc.)";
|
||||
Q_ASSERT(false);
|
||||
return false;
|
||||
}
|
|
@ -106,6 +106,7 @@ public:
|
|||
virtual ScriptValuePointer uncaughtException() const = 0;
|
||||
virtual QStringList uncaughtExceptionBacktrace() const = 0;
|
||||
virtual int uncaughtExceptionLineNumber() const = 0;
|
||||
virtual void updateMemoryCost(const qint64& deltaSize) = 0;
|
||||
|
||||
public:
|
||||
// helper to detect and log warnings when other code invokes QScriptEngine/BaseScriptEngine in thread-unsafe ways
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
// Object conversion helpers (copied from QScriptEngine)
|
||||
|
||||
#include <QtCore/QMetaType>
|
||||
|
||||
#include "ScriptEngine.h"
|
||||
#include "ScriptValue.h"
|
||||
|
||||
|
|
|
@ -53,11 +53,9 @@
|
|||
#include <controllers/ScriptingInterface.h>
|
||||
#include <AnimationObject.h>
|
||||
|
||||
#include "ArrayBufferViewClass.h"
|
||||
#include "AudioScriptingInterface.h"
|
||||
#include "AssetScriptingInterface.h"
|
||||
#include "BatchLoader.h"
|
||||
#include "DataViewClass.h"
|
||||
#include "EventTypes.h"
|
||||
#include "FileScriptingInterface.h" // unzip project
|
||||
#include "MenuItemProperties.h"
|
||||
|
@ -67,7 +65,6 @@
|
|||
#include "ScriptContext.h"
|
||||
#include "ScriptEngineCast.h"
|
||||
#include "ScriptEngineLogging.h"
|
||||
#include "TypedArrays.h"
|
||||
#include "XMLHttpRequestClass.h"
|
||||
#include "WebSocketClass.h"
|
||||
#include "RecordingScriptingInterface.h"
|
||||
|
@ -133,8 +130,10 @@ static ScriptValuePointer debugPrint(ScriptContext* context, ScriptEngine* engin
|
|||
AbstractLoggerInterface* loggerInterface = AbstractLoggerInterface::get();
|
||||
if (loggerInterface && loggerInterface->showSourceDebugging()) {
|
||||
ScriptContext* userContext = context;
|
||||
ScriptContextPointer parentContext; // using this variable to maintain parent variable lifespan
|
||||
while (userContext && userContext->functionContext()->functionType() == ScriptFunctionContext::NativeFunction) {
|
||||
userContext = userContext->parentContext();
|
||||
parentContext = userContext->parentContext();
|
||||
userContext = parentContext.data();
|
||||
}
|
||||
QString location;
|
||||
if (userContext) {
|
||||
|
@ -560,9 +559,10 @@ static ScriptValuePointer scriptableResourceToScriptValue(ScriptEngine* engine,
|
|||
// in that case it would be too difficult to tell which one should track the memory, and
|
||||
// this serves the common case (use in a single script).
|
||||
auto data = resource->getResource();
|
||||
if (data && !resource->isInScript()) {
|
||||
auto manager = engine->manager();
|
||||
if (data && manager && !resource->isInScript()) {
|
||||
resource->setInScript(true);
|
||||
QObject::connect(data.data(), SIGNAL(updateSize(qint64)), engine, SLOT(updateMemoryCost(qint64)));
|
||||
QObject::connect(data.data(), SIGNAL(updateSize(qint64)), manager, SLOT(updateMemoryCost(qint64)));
|
||||
}
|
||||
|
||||
auto object = engine->newQObject(
|
||||
|
@ -963,6 +963,11 @@ void ScriptManager::addEventHandler(const EntityItemID& entityID, const QString&
|
|||
handlersForEvent << handlerData; // Note that the same handler can be added many times. See removeEntityEventHandler().
|
||||
}
|
||||
|
||||
bool ScriptManager::isStopped() const {
|
||||
QSharedPointer<ScriptEngines> scriptEngines(_scriptEngines);
|
||||
return !scriptEngines || scriptEngines->isStopped();
|
||||
}
|
||||
|
||||
void ScriptManager::run() {
|
||||
if (QThread::currentThread() != qApp->thread() && _context == Context::CLIENT_SCRIPT) {
|
||||
// Flag that we're allowed to access local HTML files on UI created from C++ calls on this thread
|
||||
|
@ -974,8 +979,7 @@ void ScriptManager::run() {
|
|||
auto name = filenameParts.size() > 0 ? filenameParts[filenameParts.size() - 1] : "unknown";
|
||||
PROFILE_SET_THREAD_NAME("Script: " + name);
|
||||
|
||||
QSharedPointer<ScriptEngines> scriptEngines(_scriptEngines);
|
||||
if (!scriptEngines || scriptEngines->isStopped()) {
|
||||
if (isStopped()) {
|
||||
return; // bail early - avoid setting state in init(), as evaluate() will bail too
|
||||
}
|
||||
|
||||
|
@ -1228,21 +1232,13 @@ void ScriptManager::callAnimationStateHandler(ScriptValuePointer callback, AnimV
|
|||
}
|
||||
|
||||
void ScriptManager::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
|
||||
}
|
||||
_engine->updateMemoryCost(deltaSize);
|
||||
}
|
||||
|
||||
void ScriptManager::timerFired() {
|
||||
{
|
||||
QSharedPointer<ScriptEngines> scriptEngines(_scriptEngines);
|
||||
if (!scriptEngines || scriptEngines->isStopped()) {
|
||||
scriptWarningMessage("Script.timerFired() while shutting down is ignored... parent script:" + getFilename());
|
||||
return; // bail early
|
||||
}
|
||||
if (isStopped()) {
|
||||
scriptWarningMessage("Script.timerFired() while shutting down is ignored... parent script:" + getFilename());
|
||||
return; // bail early
|
||||
}
|
||||
|
||||
QTimer* callingTimer = reinterpret_cast<QTimer*>(sender());
|
||||
|
@ -1292,8 +1288,7 @@ QObject* ScriptManager::setupTimerWithInterval(const ScriptValuePointer& functio
|
|||
}
|
||||
|
||||
QObject* ScriptManager::setInterval(const ScriptValuePointer& function, int intervalMS) {
|
||||
QSharedPointer<ScriptEngines> scriptEngines(_scriptEngines);
|
||||
if (!scriptEngines || scriptEngines->isStopped()) {
|
||||
if (isStopped()) {
|
||||
scriptWarningMessage("Script.setInterval() while shutting down is ignored... parent script:" + getFilename());
|
||||
return NULL; // bail early
|
||||
}
|
||||
|
@ -1302,8 +1297,7 @@ QObject* ScriptManager::setInterval(const ScriptValuePointer& function, int inte
|
|||
}
|
||||
|
||||
QObject* ScriptManager::setTimeout(const ScriptValuePointer& function, int timeoutMS) {
|
||||
QSharedPointer<ScriptEngines> scriptEngines(_scriptEngines);
|
||||
if (!scriptEngines || scriptEngines->isStopped()) {
|
||||
if (isStopped()) {
|
||||
scriptWarningMessage("Script.setTimeout() while shutting down is ignored... parent script:" + getFilename());
|
||||
return NULL; // bail early
|
||||
}
|
||||
|
@ -1335,10 +1329,12 @@ QUrl ScriptManager::resolvePath(const QString& include) const {
|
|||
// to the first absolute URL in the JS scope chain
|
||||
QUrl parentURL;
|
||||
auto context = _engine->currentContext();
|
||||
ScriptContextPointer parentContext; // using this variable to maintain parent variable lifespan
|
||||
do {
|
||||
auto contextInfo = context->functionContext();
|
||||
parentURL = QUrl(contextInfo->fileName());
|
||||
context = context->parentContext();
|
||||
parentContext = context->parentContext();
|
||||
context = parentContext.data();
|
||||
} while (parentURL.isRelative() && context);
|
||||
|
||||
if (parentURL.isRelative()) {
|
||||
|
@ -1478,8 +1474,9 @@ ScriptValuePointer ScriptManager::currentModule() {
|
|||
auto jsRequire = _engine->globalObject()->property("Script")->property("require");
|
||||
auto cache = jsRequire->property("cache");
|
||||
auto candidate = ScriptValuePointer();
|
||||
for (auto c = _engine->currentContext(); c && !candidate->isObject(); c = c->parentContext()) {
|
||||
auto contextInfo = c->functionContext();
|
||||
ScriptContextPointer parentContext; // using this variable to maintain parent variable lifespan
|
||||
for (auto context = _engine->currentContext(); context && !candidate->isObject(); parentContext = context->parentContext(), context = parentContext.data()) {
|
||||
auto contextInfo = context->functionContext();
|
||||
candidate = cache->property(contextInfo->fileName());
|
||||
}
|
||||
if (!candidate->isObject()) {
|
||||
|
@ -1737,8 +1734,7 @@ void ScriptManager::include(const QStringList& includeFiles, ScriptValuePointer
|
|||
if (!_engine->IS_THREADSAFE_INVOCATION(__FUNCTION__)) {
|
||||
return;
|
||||
}
|
||||
QSharedPointer<ScriptEngines> scriptEngines(_scriptEngines);
|
||||
if (!scriptEngines || scriptEngines->isStopped()) {
|
||||
if (isStopped()) {
|
||||
scriptWarningMessage("Script.include() while shutting down is ignored... includeFiles:"
|
||||
+ includeFiles.join(",") + "parent script:" + getFilename());
|
||||
return; // bail early
|
||||
|
@ -1832,8 +1828,7 @@ void ScriptManager::include(const QStringList& includeFiles, ScriptValuePointer
|
|||
}
|
||||
|
||||
void ScriptManager::include(const QString& includeFile, ScriptValuePointer callback) {
|
||||
QSharedPointer<ScriptEngines> scriptEngines(_scriptEngines);
|
||||
if (!scriptEngines || scriptEngines->isStopped()) {
|
||||
if (isStopped()) {
|
||||
scriptWarningMessage("Script.include() while shutting down is ignored... includeFile:"
|
||||
+ includeFile + "parent script:" + getFilename());
|
||||
return; // bail early
|
||||
|
@ -1851,8 +1846,7 @@ void ScriptManager::load(const QString& loadFile) {
|
|||
if (!_engine->IS_THREADSAFE_INVOCATION(__FUNCTION__)) {
|
||||
return;
|
||||
}
|
||||
QSharedPointer<ScriptEngines> scriptEngines(_scriptEngines);
|
||||
if (!scriptEngines || scriptEngines->isStopped()) {
|
||||
if (isStopped()) {
|
||||
scriptWarningMessage("Script.load() while shutting down is ignored... loadFile:"
|
||||
+ loadFile + "parent script:" + getFilename());
|
||||
return; // bail early
|
||||
|
@ -2689,3 +2683,7 @@ QString ScriptManager::formatException(const ScriptValuePointer& exception, bool
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptManager::evaluate(const QString& program, const QString& fileName) {
|
||||
return _engine->evaluate(program, fileName);
|
||||
}
|
||||
|
|
|
@ -177,10 +177,12 @@ public:
|
|||
|
||||
QString getFilename() const;
|
||||
|
||||
ScriptEnginePointer engine();
|
||||
inline ScriptEnginePointer engine() { return _engine; }
|
||||
|
||||
QList<EntityItemID> getListOfEntityScriptIDs();
|
||||
|
||||
bool isStopped() const;
|
||||
|
||||
/**jsdoc
|
||||
* Stops and unloads the current script.
|
||||
* <p><strong>Warning:</strong> If an assignment client script, the script gets restarted after stopping.</p>
|
||||
|
|
|
@ -52,6 +52,7 @@ public:
|
|||
virtual ScriptValuePointer call(const ScriptValuePointer& thisObject, const ScriptValuePointer& arguments) = 0;
|
||||
virtual ScriptValuePointer construct(const ScriptValueList& args = ScriptValueList()) = 0;
|
||||
virtual ScriptValuePointer construct(const ScriptValuePointer& arguments) = 0;
|
||||
virtual ScriptValuePointer data() const = 0;
|
||||
virtual ScriptEnginePointer engine() const = 0;
|
||||
inline bool equals(const ScriptValuePointer& other) const;
|
||||
inline bool isArray() const;
|
||||
|
@ -68,6 +69,7 @@ public:
|
|||
virtual ScriptValueIteratorPointer newIterator() = 0;
|
||||
virtual ScriptValuePointer property(const QString& name, const ResolveFlags& mode = ResolvePrototype) const = 0;
|
||||
virtual ScriptValuePointer property(quint32 arrayIndex, const ResolveFlags& mode = ResolvePrototype) const = 0;
|
||||
virtual void setData(const ScriptValuePointer& val) = 0;
|
||||
virtual void setProperty(const QString& name,
|
||||
const ScriptValuePointer& value,
|
||||
const PropertyFlags& flags = KeepExistingFlags) = 0;
|
||||
|
|
24
libraries/script-engine/src/Scriptable.cpp
Normal file
24
libraries/script-engine/src/Scriptable.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// Scriptable.cpp
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Heather Anderson on 5/22/21.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "Scriptable.h"
|
||||
|
||||
#include <QtCore/QThreadStorage>
|
||||
|
||||
static QThreadStorage<ScriptContext*> ScriptContextStore;
|
||||
|
||||
ScriptContext* Scriptable::context() {
|
||||
return ScriptContextStore.localData();
|
||||
}
|
||||
|
||||
void Scriptable::setContext(ScriptContext* context) {
|
||||
ScriptContextStore.setLocalData(context);
|
||||
}
|
180
libraries/script-engine/src/qtscript/ArrayBufferClass.cpp
Normal file
180
libraries/script-engine/src/qtscript/ArrayBufferClass.cpp
Normal file
|
@ -0,0 +1,180 @@
|
|||
//
|
||||
// ArrayBufferClass.cpp
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/3/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "ArrayBufferClass.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "ArrayBufferPrototype.h"
|
||||
#include "DataViewClass.h"
|
||||
#include "ScriptEngineQtScript.h"
|
||||
#include "TypedArrays.h"
|
||||
|
||||
|
||||
static const QString CLASS_NAME = "ArrayBuffer";
|
||||
|
||||
// FIXME: Q_DECLARE_METATYPE is global and really belongs in a shared header file, not per .cpp like this
|
||||
// (see DataViewClass.cpp, etc. which would also have to be updated to resolve)
|
||||
Q_DECLARE_METATYPE(QScriptClass*)
|
||||
Q_DECLARE_METATYPE(QByteArray*)
|
||||
int qScriptClassPointerMetaTypeId = qRegisterMetaType<QScriptClass*>();
|
||||
int qByteArrayPointerMetaTypeId = qRegisterMetaType<QByteArray*>();
|
||||
|
||||
ArrayBufferClass::ArrayBufferClass(ScriptEngineQtScript* scriptEngine) :
|
||||
QObject(scriptEngine),
|
||||
QScriptClass(scriptEngine) {
|
||||
qScriptRegisterMetaType<QByteArray>(engine(), toScriptValue, fromScriptValue);
|
||||
QScriptValue global = engine()->globalObject();
|
||||
|
||||
// Save string handles for quick lookup
|
||||
_name = engine()->toStringHandle(CLASS_NAME.toLatin1());
|
||||
_byteLength = engine()->toStringHandle(BYTE_LENGTH_PROPERTY_NAME.toLatin1());
|
||||
|
||||
// build prototype
|
||||
_proto = engine()->newQObject(new ArrayBufferPrototype(this),
|
||||
QScriptEngine::QtOwnership,
|
||||
QScriptEngine::SkipMethodsInEnumeration |
|
||||
QScriptEngine::ExcludeSuperClassMethods |
|
||||
QScriptEngine::ExcludeSuperClassProperties);
|
||||
_proto.setPrototype(global.property("Object").property("prototype"));
|
||||
|
||||
// Register constructor
|
||||
_ctor = engine()->newFunction(construct, _proto);
|
||||
_ctor.setData(engine()->toScriptValue(this));
|
||||
|
||||
engine()->globalObject().setProperty(name(), _ctor);
|
||||
|
||||
// Registering other array types
|
||||
// The script engine is there parent so it'll delete them with itself
|
||||
new DataViewClass(scriptEngine);
|
||||
new Int8ArrayClass(scriptEngine);
|
||||
new Uint8ArrayClass(scriptEngine);
|
||||
new Uint8ClampedArrayClass(scriptEngine);
|
||||
new Int16ArrayClass(scriptEngine);
|
||||
new Uint16ArrayClass(scriptEngine);
|
||||
new Int32ArrayClass(scriptEngine);
|
||||
new Uint32ArrayClass(scriptEngine);
|
||||
new Float32ArrayClass(scriptEngine);
|
||||
new Float64ArrayClass(scriptEngine);
|
||||
}
|
||||
|
||||
QScriptValue ArrayBufferClass::newInstance(qint32 size) {
|
||||
const qint32 MAX_LENGTH = 100000000;
|
||||
if (size < 0) {
|
||||
engine()->evaluate("throw \"ArgumentError: negative length\"");
|
||||
return QScriptValue();
|
||||
}
|
||||
if (size > MAX_LENGTH) {
|
||||
engine()->evaluate("throw \"ArgumentError: absurd length\"");
|
||||
return QScriptValue();
|
||||
}
|
||||
// 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)
|
||||
engine()->reportAdditionalMemoryCost(size);
|
||||
#endif
|
||||
QScriptEngine* eng = engine();
|
||||
QVariant variant = QVariant::fromValue(QByteArray(size, 0));
|
||||
QScriptValue data = eng->newVariant(variant);
|
||||
return engine()->newObject(this, data);
|
||||
}
|
||||
|
||||
QScriptValue ArrayBufferClass::newInstance(const QByteArray& ba) {
|
||||
QScriptValue data = engine()->newVariant(QVariant::fromValue(ba));
|
||||
return engine()->newObject(this, data);
|
||||
}
|
||||
|
||||
QScriptValue ArrayBufferClass::construct(QScriptContext* context, QScriptEngine* engine) {
|
||||
ArrayBufferClass* cls = qscriptvalue_cast<ArrayBufferClass*>(context->callee().data());
|
||||
if (!cls) {
|
||||
// return if callee (function called) is not of type ArrayBuffer
|
||||
return QScriptValue();
|
||||
}
|
||||
QScriptValue arg = context->argument(0);
|
||||
if (!arg.isValid() || !arg.isNumber()) {
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
quint32 size = arg.toInt32();
|
||||
QScriptValue newObject = cls->newInstance(size);
|
||||
|
||||
if (context->isCalledAsConstructor()) {
|
||||
// if called with keyword new, replace this object.
|
||||
context->setThisObject(newObject);
|
||||
return engine->undefinedValue();
|
||||
}
|
||||
|
||||
return newObject;
|
||||
}
|
||||
|
||||
QScriptClass::QueryFlags ArrayBufferClass::queryProperty(const QScriptValue& object,
|
||||
const QScriptString& name,
|
||||
QueryFlags flags, uint* id) {
|
||||
QByteArray* ba = qscriptvalue_cast<QByteArray*>(object.data());
|
||||
if (ba && name == _byteLength) {
|
||||
// if the property queried is byteLength, only handle read access
|
||||
return flags &= HandlesReadAccess;
|
||||
}
|
||||
return 0; // No access
|
||||
}
|
||||
|
||||
QScriptValue ArrayBufferClass::property(const QScriptValue& object,
|
||||
const QScriptString& name, uint id) {
|
||||
QByteArray* ba = qscriptvalue_cast<QByteArray*>(object.data());
|
||||
if (ba && name == _byteLength) {
|
||||
return ba->length();
|
||||
}
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
QScriptValue::PropertyFlags ArrayBufferClass::propertyFlags(const QScriptValue& object,
|
||||
const QScriptString& name, uint id) {
|
||||
return QScriptValue::Undeletable;
|
||||
}
|
||||
|
||||
QString ArrayBufferClass::name() const {
|
||||
return _name.toString();
|
||||
}
|
||||
|
||||
QScriptValue ArrayBufferClass::prototype() const {
|
||||
return _proto;
|
||||
}
|
||||
|
||||
QScriptValue ArrayBufferClass::toScriptValue(QScriptEngine* engine, const QByteArray& ba) {
|
||||
QScriptValue ctor = engine->globalObject().property(CLASS_NAME);
|
||||
ArrayBufferClass* cls = qscriptvalue_cast<ArrayBufferClass*>(ctor.data());
|
||||
if (!cls) {
|
||||
if (engine->currentContext()) {
|
||||
engine->currentContext()->throwError("arrayBufferClass::toScriptValue -- could not get " + CLASS_NAME + " class constructor");
|
||||
}
|
||||
return QScriptValue::NullValue;
|
||||
}
|
||||
return cls->newInstance(ba);
|
||||
}
|
||||
|
||||
void ArrayBufferClass::fromScriptValue(const QScriptValue& object, QByteArray& byteArray) {
|
||||
if (object.isString()) {
|
||||
// UTF-8 encoded String
|
||||
byteArray = object.toString().toUtf8();
|
||||
} else if (object.isArray()) {
|
||||
// Array of uint8s eg: [ 128, 3, 25, 234 ]
|
||||
auto Uint8Array = object.engine()->globalObject().property("Uint8Array");
|
||||
auto typedArray = Uint8Array.construct(QScriptValueList{object});
|
||||
if (QByteArray* buffer = qscriptvalue_cast<QByteArray*>(typedArray.property("buffer"))) {
|
||||
byteArray = *buffer;
|
||||
}
|
||||
} else if (object.isObject()) {
|
||||
// ArrayBuffer instance (or any JS class that supports coercion into QByteArray*)
|
||||
if (QByteArray* buffer = qscriptvalue_cast<QByteArray*>(object.data())) {
|
||||
byteArray = *buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
#include <QtCore/QBuffer>
|
||||
#include <QtGui/QImage>
|
||||
|
||||
#include "ArrayBufferClass.h"
|
||||
#include <QtScript/QScriptEngine>
|
||||
|
||||
static const int QCOMPRESS_HEADER_POSITION = 0;
|
||||
static const int QCOMPRESS_HEADER_SIZE = 4;
|
|
@ -13,12 +13,14 @@
|
|||
|
||||
#include <QDebug>
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtScript/QScriptEngine>
|
||||
#include <QtScript/QScriptValue>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "DataViewClass.h"
|
||||
#include "ArrayBufferViewClass.h"
|
||||
|
||||
Q_DECLARE_METATYPE(QByteArray*)
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// ScriptContextQtAgent.cpp
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Heather Anderson on 5/22/21.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "ScriptContextQtAgent.h"
|
||||
|
||||
#include <QtScript/QScriptEngine>
|
||||
|
||||
#include "../Scriptable.h"
|
||||
#include "ScriptContextQtWrapper.h"
|
||||
#include "ScriptEngineQtScript.h"
|
||||
|
||||
void ScriptContextQtAgent::contextPop() {
|
||||
if (_prevAgent) {
|
||||
_prevAgent->contextPop();
|
||||
}
|
||||
if (_engine->currentContext() == _currContext) {
|
||||
_currContext.reset();
|
||||
if (!_contextActive && !_contextStack.empty()) {
|
||||
_currContext = _contextStack.back();
|
||||
_contextStack.pop_back();
|
||||
_contextActive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptContextQtAgent::contextPush() {
|
||||
if (_prevAgent) {
|
||||
_prevAgent->contextPush();
|
||||
}
|
||||
if (_contextActive && _currContext) {
|
||||
_contextStack.push_back(_currContext);
|
||||
_contextActive = false;
|
||||
}
|
||||
_currContext.reset();
|
||||
}
|
||||
|
||||
void ScriptContextQtAgent::functionEntry(qint64 scriptId) {
|
||||
if (_prevAgent) {
|
||||
_prevAgent->functionEntry(scriptId);
|
||||
}
|
||||
if (scriptId != -1) {
|
||||
return;
|
||||
}
|
||||
if (!_currContext) {
|
||||
_currContext = ScriptContextQtPointer(new ScriptContextQtWrapper(_engine, static_cast<QScriptEngine*>(_engine)->currentContext()));
|
||||
}
|
||||
Scriptable::setContext(_currContext.get());
|
||||
_contextActive = true;
|
||||
}
|
||||
|
||||
void ScriptContextQtAgent::functionExit(qint64 scriptId, const QScriptValue& returnValue) {
|
||||
if (_prevAgent) {
|
||||
_prevAgent->functionExit(scriptId, returnValue);
|
||||
}
|
||||
if (scriptId != -1) {
|
||||
return;
|
||||
}
|
||||
_contextActive = false;
|
||||
if (!_contextActive && !_contextStack.empty()) {
|
||||
_currContext = _contextStack.back();
|
||||
_contextStack.pop_back();
|
||||
Scriptable::setContext(_currContext.get());
|
||||
_contextActive = true;
|
||||
}
|
||||
}
|
47
libraries/script-engine/src/qtscript/ScriptContextQtAgent.h
Normal file
47
libraries/script-engine/src/qtscript/ScriptContextQtAgent.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// ScriptContextQtAgent.h
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Heather Anderson on 5/22/21.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_ScriptContextQtAgent_h
|
||||
#define hifi_ScriptContextQtAgent_h
|
||||
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QSharedPointer>
|
||||
#include <QtScript/QScriptEngineAgent>
|
||||
|
||||
#include "ScriptEngineQtScript.h"
|
||||
|
||||
class QScriptContext;
|
||||
class QScriptEngine;
|
||||
class QScriptValue;
|
||||
class ScriptContextQtWrapper;
|
||||
using ScriptContextQtPointer = QSharedPointer<ScriptContextQtWrapper>;
|
||||
|
||||
class ScriptContextQtAgent : public QScriptEngineAgent {
|
||||
public: // construction
|
||||
inline ScriptContextQtAgent(ScriptEngineQtScript* engine, QScriptEngineAgent* prevAgent) :
|
||||
QScriptEngineAgent(engine), _engine(engine), _prevAgent(prevAgent) {}
|
||||
virtual ~ScriptContextQtAgent() {}
|
||||
|
||||
public: // QScriptEngineAgent implementation
|
||||
virtual void contextPop();
|
||||
virtual void contextPush();
|
||||
virtual void functionEntry(qint64 scriptId);
|
||||
virtual void functionExit(qint64 scriptId, const QScriptValue& returnValue);
|
||||
|
||||
private: // storage
|
||||
bool _contextActive = false;
|
||||
QList<ScriptContextQtPointer> _contextStack;
|
||||
ScriptContextQtPointer _currContext;
|
||||
ScriptEngineQtScript* _engine;
|
||||
QScriptEngineAgent* _prevAgent;
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptContextQtAgent_h
|
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// ScriptContextQtWrapper.cpp
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Heather Anderson on 5/22/21.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "ScriptContextQtWrapper.h"
|
||||
|
||||
#include <QtScript/QScriptContext>
|
||||
|
||||
#include "ScriptEngineQtScript.h"
|
||||
#include "ScriptValueQtWrapper.h"
|
||||
|
||||
ScriptContextQtWrapper* ScriptContextQtWrapper::unwrap(ScriptContext* val) {
|
||||
if (!val) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dynamic_cast<ScriptContextQtWrapper*>(val);
|
||||
}
|
||||
|
||||
int ScriptContextQtWrapper::argumentCount() const {
|
||||
return _context->argumentCount();
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptContextQtWrapper::argument(int index) const {
|
||||
QScriptValue result = _context->argument(index);
|
||||
return ScriptValuePointer(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
QStringList ScriptContextQtWrapper::backtrace() const {
|
||||
return _context->backtrace();
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptContextQtWrapper::callee() const {
|
||||
QScriptValue result = _context->callee();
|
||||
return ScriptValuePointer(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptEnginePointer ScriptContextQtWrapper::engine() const {
|
||||
return _engine->sharedFromThis();
|
||||
}
|
||||
|
||||
ScriptFunctionContextPointer ScriptContextQtWrapper::functionContext() const {
|
||||
return ScriptFunctionContextPointer(new ScriptFunctionContextQtWrapper(_context));
|
||||
}
|
||||
|
||||
ScriptContextPointer ScriptContextQtWrapper::parentContext() const {
|
||||
QScriptContext* result = _context->parentContext();
|
||||
return ScriptContextPointer(new ScriptContextQtWrapper(_engine, result));
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptContextQtWrapper::thisObject() const {
|
||||
QScriptValue result = _context->thisObject();
|
||||
return ScriptValuePointer(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptContextQtWrapper::throwError(const QString& text) {
|
||||
QScriptValue result = _context->throwError(text);
|
||||
return ScriptValuePointer(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptContextQtWrapper::throwValue(const ScriptValuePointer& value) {
|
||||
ScriptValueQtWrapper* unwrapped = ScriptValueQtWrapper::unwrap(value);
|
||||
if (!unwrapped) {
|
||||
return _engine->undefinedValue();
|
||||
}
|
||||
QScriptValue result = _context->throwValue(unwrapped->toQtValue());
|
||||
return ScriptValuePointer(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
|
||||
QString ScriptFunctionContextQtWrapper::fileName() const {
|
||||
return _value.fileName();
|
||||
}
|
||||
|
||||
QString ScriptFunctionContextQtWrapper::functionName() const {
|
||||
return _value.functionName();
|
||||
}
|
||||
|
||||
ScriptFunctionContext::FunctionType ScriptFunctionContextQtWrapper::functionType() const {
|
||||
return static_cast<ScriptFunctionContext::FunctionType>(_value.functionType());
|
||||
}
|
||||
|
||||
int ScriptFunctionContextQtWrapper::lineNumber() const {
|
||||
return _value.lineNumber();
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
//
|
||||
// ScriptContextQtWrapper.h
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Heather Anderson on 5/22/21.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_ScriptContextQtWrapper_h
|
||||
#define hifi_ScriptContextQtWrapper_h
|
||||
|
||||
#include <QtCore/QSharedPointer>
|
||||
#include <QtCore/QString>
|
||||
#include <QtScript/QScriptContextInfo>
|
||||
|
||||
#include "../ScriptContext.h"
|
||||
|
||||
class QScriptContext;
|
||||
class ScriptEngineQtScript;
|
||||
class ScriptValue;
|
||||
using ScriptValuePointer = QSharedPointer<ScriptValue>;
|
||||
|
||||
class ScriptContextQtWrapper : public ScriptContext {
|
||||
public: // construction
|
||||
inline ScriptContextQtWrapper(ScriptEngineQtScript* engine, QScriptContext* context) : _engine(engine), _context(context) {}
|
||||
static ScriptContextQtWrapper* unwrap(ScriptContext* val);
|
||||
inline QScriptContext* toQtValue() const { return _context; }
|
||||
|
||||
public: // ScriptContext implementation
|
||||
virtual int argumentCount() const;
|
||||
virtual ScriptValuePointer argument(int index) const;
|
||||
virtual QStringList backtrace() const;
|
||||
virtual ScriptValuePointer callee() const;
|
||||
virtual ScriptEnginePointer engine() const;
|
||||
virtual ScriptFunctionContextPointer functionContext() const;
|
||||
virtual ScriptContextPointer parentContext() const;
|
||||
virtual ScriptValuePointer thisObject() const;
|
||||
virtual ScriptValuePointer throwError(const QString& text);
|
||||
virtual ScriptValuePointer throwValue(const ScriptValuePointer& value);
|
||||
|
||||
private: // storage
|
||||
QScriptContext* _context;
|
||||
ScriptEngineQtScript* _engine;
|
||||
};
|
||||
|
||||
class ScriptFunctionContextQtWrapper : public ScriptFunctionContext {
|
||||
public: // construction
|
||||
inline ScriptFunctionContextQtWrapper(QScriptContext* context) : _value(context) {}
|
||||
|
||||
public: // ScriptFunctionContext implementation
|
||||
virtual QString fileName() const;
|
||||
virtual QString functionName() const;
|
||||
virtual FunctionType functionType() const;
|
||||
virtual int lineNumber() const;
|
||||
|
||||
private: // storage
|
||||
QScriptContextInfo _value;
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptContextQtWrapper_h
|
1037
libraries/script-engine/src/qtscript/ScriptEngineQtScript.cpp
Normal file
1037
libraries/script-engine/src/qtscript/ScriptEngineQtScript.cpp
Normal file
File diff suppressed because it is too large
Load diff
|
@ -13,50 +13,27 @@
|
|||
#ifndef hifi_ScriptEngineQtScript_h
|
||||
#define hifi_ScriptEngineQtScript_h
|
||||
|
||||
//#include <unordered_map>
|
||||
//#include <vector>
|
||||
|
||||
//#include <QtCore/QUrl>
|
||||
//#include <QtCore/QSet>
|
||||
//#include <QtCore/QWaitCondition>
|
||||
//#include <QtCore/QStringList>
|
||||
//#include <QMap>
|
||||
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QEnableSharedFromThis>
|
||||
#include <QtCore/QMetaEnum>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtScript/QScriptEngine>
|
||||
#include <QtScriptTools/QScriptEngineDebugger>
|
||||
#include <QtCore/QSharedPointer>
|
||||
#include <QtCore/QString>
|
||||
|
||||
//#include <AnimationCache.h>
|
||||
//#include <AnimVariant.h>
|
||||
//#include <AvatarData.h>
|
||||
//#include <AvatarHashMap.h>
|
||||
//#include <LimitedNodeList.h>
|
||||
//#include <EntityItemID.h>
|
||||
//#include <EntityScriptUtils.h>
|
||||
#include <ScriptEngine.h>
|
||||
#include <ScriptManager.h>
|
||||
#include <QtScript/QScriptEngine>
|
||||
#include <QtScriptTools/QScriptEngineDebugger>
|
||||
|
||||
#include "../ScriptEngine.h"
|
||||
#include "../ScriptManager.h"
|
||||
|
||||
//#include "PointerEvent.h"
|
||||
#include "ArrayBufferClass.h"
|
||||
//#include "AssetScriptingInterface.h"
|
||||
//#include "AudioScriptingInterface.h"
|
||||
#include "BaseScriptEngine.h"
|
||||
//#include "ExternalResource.h"
|
||||
//#include "Quat.h"
|
||||
//#include "Mat4.h"
|
||||
//#include "ScriptCache.h"
|
||||
//#include "ScriptUUID.h"
|
||||
//#include "Vec3.h"
|
||||
//#include "ConsoleScriptingInterface.h"
|
||||
//#include "SettingHandle.h"
|
||||
//#include "Profile.h"
|
||||
|
||||
//class QScriptEngineDebugger;
|
||||
class ScriptContextQtWrapper;
|
||||
class ScriptEngineQtScript;
|
||||
class ScriptManager;
|
||||
using ScriptEngineQtScriptPointer = QSharedPointer<ScriptEngineQtScript>;
|
||||
using ScriptContextQtPointer = QSharedPointer<ScriptContextQtWrapper>;
|
||||
|
||||
Q_DECLARE_METATYPE(ScriptEngineQtScriptPointer)
|
||||
|
||||
|
@ -92,18 +69,34 @@ Q_DECLARE_METATYPE(ScriptEngineQtScriptPointer)
|
|||
* <em>Read-only.</em>
|
||||
* @property {Script.ResourceBuckets} ExternalPaths - External resource buckets.
|
||||
*/
|
||||
class ScriptEngineQtScript : public BaseScriptEngine, public ScriptEngine {
|
||||
class ScriptEngineQtScript : public QScriptEngine, public ScriptEngine, public QEnableSharedFromThis<ScriptEngineQtScript> {
|
||||
Q_OBJECT
|
||||
using Base = BaseScriptEngine;
|
||||
|
||||
public: // ScriptEngine implementation
|
||||
virtual void abortEvaluation();
|
||||
virtual void clearExceptions();
|
||||
virtual ScriptValuePointer cloneUncaughtException(const QString& detail = QString());
|
||||
virtual ScriptContext* currentContext() const;
|
||||
//virtual ScriptValuePointer evaluate(const QString& program, const QString& fileName = QString());
|
||||
//virtual ScriptValuePointer evaluate(const ScriptProgramPointer &program);
|
||||
//virtual ScriptValuePointer evaluateInClosure(const ScriptValuePointer& locals, const ScriptProgramPointer& program);
|
||||
virtual ScriptValuePointer globalObject() const;
|
||||
virtual bool hasUncaughtException() const;
|
||||
virtual bool isEvaluating() const;
|
||||
virtual ScriptValuePointer lintScript(const QString& sourceCode, const QString& fileName, const int lineNumber = 1);
|
||||
virtual ScriptValuePointer makeError(const ScriptValuePointer& other, const QString& type = "Error");
|
||||
virtual ScriptManager* manager() const;
|
||||
|
||||
// if there is a pending exception and we are at the top level (non-recursive) stack frame, this emits and resets it
|
||||
virtual bool maybeEmitUncaughtException(const QString& debugHint = QString());
|
||||
|
||||
virtual ScriptValuePointer newArray(uint length = 0);
|
||||
virtual ScriptValuePointer newArrayBuffer(const QByteArray& message);
|
||||
virtual ScriptValuePointer newFunction(ScriptEngine::FunctionSignature fun, int length = 0);
|
||||
virtual ScriptValuePointer newObject();
|
||||
virtual ScriptProgramPointer newProgram(const QString& sourceCode, const QString& fileName);
|
||||
virtual ScriptValuePointer newQObject(QObject* obj);
|
||||
virtual ScriptValuePointer newQObject(QObject *object, ScriptEngine::ValueOwnership ownership = ScriptEngine::QtOwnership,
|
||||
const ScriptEngine::QObjectWrapOptions &options = ScriptEngine::QObjectWrapOptions());
|
||||
virtual ScriptValuePointer newValue(bool value);
|
||||
virtual ScriptValuePointer newValue(int value);
|
||||
virtual ScriptValuePointer newValue(uint value);
|
||||
|
@ -113,8 +106,41 @@ public: // ScriptEngine implementation
|
|||
virtual ScriptValuePointer newValue(const char* value);
|
||||
virtual ScriptValuePointer newVariant(const QVariant& value);
|
||||
virtual ScriptValuePointer nullValue();
|
||||
virtual bool raiseException(const ScriptValuePointer& exception);
|
||||
//virtual void registerEnum(const QString& enumName, QMetaEnum newEnum);
|
||||
//Q_INVOKABLE virtual void registerFunction(const QString& name, ScriptEngine::FunctionSignature fun, int numArguments = -1);
|
||||
//Q_INVOKABLE virtual void registerFunction(const QString& parent, const QString& name, ScriptEngine::FunctionSignature fun, int numArguments = -1);
|
||||
//Q_INVOKABLE virtual void registerGetterSetter(const QString& name, ScriptEngine::FunctionSignature getter, ScriptEngine::FunctionSignature setter, const QString& parent = QString(""));
|
||||
//virtual void registerGlobalObject(const QString& name, QObject* object);
|
||||
virtual void setDefaultPrototype(int metaTypeId, const ScriptValuePointer& prototype);
|
||||
virtual void setObjectName(const QString& name);
|
||||
virtual bool setProperty(const char* name, const QVariant& value);
|
||||
virtual void setProcessEventsInterval(int interval);
|
||||
virtual QThread* thread() const;
|
||||
virtual ScriptValuePointer undefinedValue();
|
||||
virtual ScriptValuePointer uncaughtException() const;
|
||||
virtual QStringList uncaughtExceptionBacktrace() const;
|
||||
virtual int uncaughtExceptionLineNumber() const;
|
||||
|
||||
// helper to detect and log warnings when other code invokes QScriptEngine/BaseScriptEngine in thread-unsafe ways
|
||||
inline bool IS_THREADSAFE_INVOCATION(const QString& method) { return ScriptEngine::IS_THREADSAFE_INVOCATION(method); }
|
||||
|
||||
protected: // brought over from BaseScriptEngine
|
||||
/**jsdoc
|
||||
* @function Script.makeError
|
||||
* @param {object} [other] - Other.
|
||||
* @param {string} [type="Error"] - Error.
|
||||
* @returns {object} Object.
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
*/
|
||||
Q_INVOKABLE QScriptValue makeError(const QScriptValue& other = QScriptValue(), const QString& type = "Error");
|
||||
|
||||
// if the currentContext() is valid then throw the passed exception; otherwise, immediately emit it.
|
||||
// note: this is used in cases where C++ code might call into JS API methods directly
|
||||
bool raiseException(const QScriptValue& exception);
|
||||
|
||||
// helper to detect and log warnings when other code invokes QScriptEngine/BaseScriptEngine in thread-unsafe ways
|
||||
static bool IS_THREADSAFE_INVOCATION(const QThread* thread, const QString& method);
|
||||
|
||||
public:
|
||||
ScriptEngineQtScript(ScriptManager* scriptManager = nullptr);
|
||||
|
@ -131,7 +157,7 @@ public:
|
|||
* @deprecated This function is deprecated and will be removed.
|
||||
*/
|
||||
/// registers a global object by name
|
||||
Q_INVOKABLE void registerGlobalObject(const QString& name, QObject* object);
|
||||
Q_INVOKABLE virtual void registerGlobalObject(const QString& name, QObject* object);
|
||||
|
||||
/**jsdoc
|
||||
* @function Script.registerGetterSetter
|
||||
|
@ -145,6 +171,8 @@ public:
|
|||
Q_INVOKABLE void registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter,
|
||||
QScriptEngine::FunctionSignature setter, const QString& parent = QString(""));
|
||||
|
||||
Q_INVOKABLE virtual void registerGetterSetter(const QString& name, ScriptEngine::FunctionSignature getter, ScriptEngine::FunctionSignature setter, const QString& parent = QString(""));
|
||||
|
||||
/**jsdoc
|
||||
* @function Script.registerFunction
|
||||
* @param {string} name - Name.
|
||||
|
@ -155,6 +183,9 @@ public:
|
|||
/// register a global function
|
||||
Q_INVOKABLE void registerFunction(const QString& name, QScriptEngine::FunctionSignature fun, int numArguments = -1);
|
||||
|
||||
|
||||
Q_INVOKABLE virtual void registerFunction(const QString& name, ScriptEngine::FunctionSignature fun, int numArguments = -1);
|
||||
|
||||
/**jsdoc
|
||||
* @function Script.registerFunction
|
||||
* @param {string} parent - Parent.
|
||||
|
@ -167,6 +198,9 @@ public:
|
|||
Q_INVOKABLE void registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature fun,
|
||||
int numArguments = -1);
|
||||
|
||||
|
||||
Q_INVOKABLE virtual void registerFunction(const QString& parent, const QString& name, ScriptEngine::FunctionSignature fun, int numArguments = -1);
|
||||
|
||||
/**jsdoc
|
||||
* @function Script.registerEnum
|
||||
* @param {string} name - Name.
|
||||
|
@ -176,7 +210,7 @@ public:
|
|||
// WARNING: This function must be called after a registerGlobalObject that creates the namespace this enum is located in, or
|
||||
// the globalObject won't function. E.g., if you have a Foo object and a Foo.FooType enum, Foo must be registered first.
|
||||
/// registers a global enum
|
||||
Q_INVOKABLE void registerEnum(const QString& enumName, QMetaEnum newEnum);
|
||||
Q_INVOKABLE virtual void registerEnum(const QString& enumName, QMetaEnum newEnum);
|
||||
|
||||
/**jsdoc
|
||||
* @function Script.registerValue
|
||||
|
@ -196,7 +230,10 @@ public:
|
|||
* @deprecated This function is deprecated and will be removed.
|
||||
*/
|
||||
/// evaluate some code in the context of the ScriptEngineQtScript and return the result
|
||||
Q_INVOKABLE QScriptValue evaluate(const QString& program, const QString& fileName, int lineNumber = 1); // this is also used by the script tool widget
|
||||
Q_INVOKABLE virtual ScriptValuePointer evaluate(const QString& program, const QString& fileName); // this is also used by the script tool widget
|
||||
|
||||
|
||||
Q_INVOKABLE virtual ScriptValuePointer evaluate(const ScriptProgramPointer& program);
|
||||
|
||||
/**jsdoc
|
||||
* @function Script.evaluateInClosure
|
||||
|
@ -205,7 +242,7 @@ public:
|
|||
* @returns {object} Object.
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
*/
|
||||
Q_INVOKABLE QScriptValue evaluateInClosure(const QScriptValue& locals, const QScriptProgram& program);
|
||||
Q_INVOKABLE virtual ScriptValuePointer evaluateInClosure(const ScriptValuePointer& locals, const ScriptProgramPointer& program);
|
||||
|
||||
/**jsdoc
|
||||
* Checks whether the application was compiled as a debug build.
|
||||
|
@ -245,14 +282,12 @@ public:
|
|||
// NOTE - this is used by the TypedArray implementation. we need to review this for thread safety
|
||||
ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; }
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* @function Script.updateMemoryCost
|
||||
* @param {number} deltaSize - Delta size.
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
*/
|
||||
void updateMemoryCost(const qint64&);
|
||||
virtual void updateMemoryCost(const qint64& deltaSize);
|
||||
|
||||
signals:
|
||||
|
||||
|
@ -372,6 +407,20 @@ signals:
|
|||
// script is updated (goes from RUNNING to ERROR_RUNNING_SCRIPT, for example)
|
||||
void entityScriptDetailsUpdated();
|
||||
|
||||
public: // not for public use, but I don't like how Qt strings this along with private friend functions
|
||||
virtual ScriptValuePointer create(int type, const void* ptr);
|
||||
virtual bool convert(const ScriptValuePointer& value, int type, void* ptr);
|
||||
virtual void registerCustomType(int type, ScriptEngine::MarshalFunction mf, ScriptEngine::DemarshalFunction df, const ScriptValuePointer& prototype);
|
||||
|
||||
protected:
|
||||
// like `newFunction`, but allows mapping inline C++ lambdas with captures as callable QScriptValues
|
||||
// even though the context/engine parameters are redundant in most cases, the function signature matches `newFunction`
|
||||
// anyway so that newLambdaFunction can be used to rapidly prototype / test utility APIs and then if becoming
|
||||
// permanent more easily promoted into regular static newFunction scenarios.
|
||||
QScriptValue newLambdaFunction(std::function<QScriptValue(QScriptContext* context, ScriptEngineQtScript* engine)> operation,
|
||||
const QScriptValue& data = QScriptValue(),
|
||||
const QScriptEngine::ValueOwnership& ownership = QScriptEngine::AutoOwnership);
|
||||
|
||||
protected:
|
||||
|
||||
/**jsdoc
|
||||
|
@ -384,6 +433,10 @@ protected:
|
|||
|
||||
QPointer<ScriptManager> _manager;
|
||||
|
||||
ScriptValuePointer _nullValue;
|
||||
ScriptValuePointer _undefinedValue;
|
||||
mutable ScriptContextQtPointer _currContext;
|
||||
|
||||
std::atomic<bool> _isRunning { false };
|
||||
|
||||
bool _isThreaded { false };
|
||||
|
@ -394,4 +447,23 @@ protected:
|
|||
ArrayBufferClass* _arrayBufferClass;
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptEngineQtScript_h
|
||||
// Lambda helps create callable QScriptValues out of std::functions:
|
||||
// (just meant for use from within the script engine itself)
|
||||
class Lambda : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Lambda(ScriptEngineQtScript* engine,
|
||||
std::function<QScriptValue(QScriptContext* context, ScriptEngineQtScript* engine)> operation,
|
||||
QScriptValue data);
|
||||
~Lambda();
|
||||
public slots:
|
||||
QScriptValue call();
|
||||
QString toString() const;
|
||||
|
||||
private:
|
||||
ScriptEngineQtScript* engine;
|
||||
std::function<QScriptValue(QScriptContext* context, ScriptEngineQtScript* engine)> operation;
|
||||
QScriptValue data;
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptEngineQtScript_h
|
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// ScriptProgramQtWrapper.cpp
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Heather Anderson on 8/24/21.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "ScriptProgramQtWrapper.h"
|
||||
|
||||
#include <QtScript/QScriptContext>
|
||||
|
||||
#include "ScriptEngineQtScript.h"
|
||||
#include "ScriptValueQtWrapper.h"
|
||||
|
||||
ScriptProgramQtWrapper* ScriptProgramQtWrapper::unwrap(ScriptProgramPointer val) {
|
||||
if (!val) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dynamic_cast<ScriptProgramQtWrapper*>(val.get());
|
||||
}
|
||||
|
||||
ScriptSyntaxCheckResultPointer ScriptProgramQtWrapper::checkSyntax() const {
|
||||
QScriptSyntaxCheckResult result = _engine->checkSyntax(_value.sourceCode());
|
||||
return ScriptSyntaxCheckResultPointer(new ScriptSyntaxCheckResultQtWrapper(std::move(result)));
|
||||
}
|
||||
|
||||
QString ScriptProgramQtWrapper::fileName() const {
|
||||
return _value.fileName();
|
||||
}
|
||||
|
||||
QString ScriptProgramQtWrapper::sourceCode() const {
|
||||
return _value.sourceCode();
|
||||
}
|
||||
|
||||
|
||||
int ScriptSyntaxCheckResultQtWrapper::errorColumnNumber() const {
|
||||
return _value.errorColumnNumber();
|
||||
}
|
||||
|
||||
int ScriptSyntaxCheckResultQtWrapper::errorLineNumber() const {
|
||||
return _value.errorLineNumber();
|
||||
}
|
||||
|
||||
QString ScriptSyntaxCheckResultQtWrapper::errorMessage() const {
|
||||
return _value.errorMessage();
|
||||
}
|
||||
|
||||
ScriptSyntaxCheckResult::State ScriptSyntaxCheckResultQtWrapper::state() const {
|
||||
return static_cast<ScriptSyntaxCheckResult::State>(_value.state());
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// ScriptProgramQtWrapper.h
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Heather Anderson on 5/21/21.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_ScriptProgramQtWrapper_h
|
||||
#define hifi_ScriptProgramQtWrapper_h
|
||||
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtScript/QScriptProgram>
|
||||
|
||||
#include "../ScriptProgram.h"
|
||||
#include "ScriptEngineQtScript.h"
|
||||
|
||||
class ScriptProgramQtWrapper : public ScriptProgram {
|
||||
public: // construction
|
||||
inline ScriptProgramQtWrapper(ScriptEngineQtScript* engine, const QScriptProgram& value) :
|
||||
_engine(engine), _value(value) {}
|
||||
inline ScriptProgramQtWrapper(ScriptEngineQtScript* engine, QScriptProgram&& value) :
|
||||
_engine(engine), _value(std::move(value)) {}
|
||||
static ScriptProgramQtWrapper* unwrap(ScriptProgramPointer val);
|
||||
inline const QScriptProgram& toQtValue() const { return _value; }
|
||||
|
||||
public: // ScriptProgram implementation
|
||||
virtual ScriptSyntaxCheckResultPointer checkSyntax() const;
|
||||
virtual QString fileName() const;
|
||||
virtual QString sourceCode() const;
|
||||
|
||||
private: // storage
|
||||
QPointer<ScriptEngineQtScript> _engine;
|
||||
QScriptProgram _value;
|
||||
};
|
||||
|
||||
class ScriptSyntaxCheckResultQtWrapper : public ScriptSyntaxCheckResult {
|
||||
public: // construction
|
||||
inline ScriptSyntaxCheckResultQtWrapper(QScriptSyntaxCheckResult&& value) :
|
||||
_value(std::move(value)) {}
|
||||
|
||||
public: // ScriptSyntaxCheckResult implementation
|
||||
virtual int errorColumnNumber() const;
|
||||
virtual int errorLineNumber() const;
|
||||
virtual QString errorMessage() const;
|
||||
virtual State state() const;
|
||||
|
||||
private: // storage
|
||||
QScriptSyntaxCheckResult _value;
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptValueQtWrapper_h
|
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// ScriptValueIteratorQtWrapper.cpp
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Heather Anderson on 8/29/21.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "ScriptValueIteratorQtWrapper.h"
|
||||
|
||||
ScriptValue::PropertyFlags ScriptValueIteratorQtWrapper::flags() const {
|
||||
return (ScriptValue::PropertyFlags)(int)_value.flags();
|
||||
}
|
||||
|
||||
bool ScriptValueIteratorQtWrapper::hasNext() const {
|
||||
return _value.hasNext();
|
||||
}
|
||||
|
||||
QString ScriptValueIteratorQtWrapper::name() const {
|
||||
return _value.name();
|
||||
}
|
||||
|
||||
void ScriptValueIteratorQtWrapper::next() {
|
||||
_value.next();
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptValueIteratorQtWrapper::value() const {
|
||||
QScriptValue result = _value.value();
|
||||
return ScriptValuePointer(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// ScriptValueIteratorQtWrapper.h
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Heather Anderson on 8/29/21.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_ScriptValueIteratorQtWrapper_h
|
||||
#define hifi_ScriptValueIteratorQtWrapper_h
|
||||
|
||||
//#include <QtCore/QPointer>
|
||||
#include <QtScript/QScriptValueIterator>
|
||||
|
||||
#include "../ScriptValueIterator.h"
|
||||
#include "ScriptEngineQtScript.h"
|
||||
#include "ScriptValueQtWrapper.h"
|
||||
|
||||
class ScriptValueIteratorQtWrapper : public ScriptValueIterator {
|
||||
public: // construction
|
||||
inline ScriptValueIteratorQtWrapper(ScriptEngineQtScript* engine, const ScriptValuePointer& object) :
|
||||
_engine(engine), _value(ScriptValueQtWrapper::fullUnwrap(engine, object)) {}
|
||||
inline ScriptValueIteratorQtWrapper(ScriptEngineQtScript* engine, const QScriptValue& object) :
|
||||
_engine(engine), _value(object) {}
|
||||
|
||||
public: // ScriptValueIterator implementation
|
||||
virtual ScriptValue::PropertyFlags flags() const;
|
||||
virtual bool hasNext() const;
|
||||
virtual QString name() const;
|
||||
virtual void next();
|
||||
virtual ScriptValuePointer value() const;
|
||||
|
||||
private: // storage
|
||||
QPointer<ScriptEngineQtScript> _engine;
|
||||
QScriptValueIterator _value;
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptValueIteratorQtWrapper_h
|
216
libraries/script-engine/src/qtscript/ScriptValueQtWrapper.cpp
Normal file
216
libraries/script-engine/src/qtscript/ScriptValueQtWrapper.cpp
Normal file
|
@ -0,0 +1,216 @@
|
|||
//
|
||||
// ScriptValueQtWrapper.cpp
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Heather Anderson on 5/16/21.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "ScriptValueQtWrapper.h"
|
||||
|
||||
#include "ScriptValueIteratorQtWrapper.h"
|
||||
|
||||
ScriptValueQtWrapper* ScriptValueQtWrapper::unwrap(ScriptValuePointer val) {
|
||||
if (!val) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dynamic_cast<ScriptValueQtWrapper*>(val.data());
|
||||
}
|
||||
|
||||
QScriptValue ScriptValueQtWrapper::fullUnwrap(const ScriptValuePointer& value) const {
|
||||
if (!value) {
|
||||
return QScriptValue();
|
||||
}
|
||||
ScriptValueQtWrapper* unwrapped = unwrap(value);
|
||||
if (unwrapped) {
|
||||
return unwrapped->toQtValue();
|
||||
}
|
||||
QVariant varValue = value->toVariant();
|
||||
return static_cast<QScriptEngine*>(_engine)->newVariant(varValue);
|
||||
}
|
||||
|
||||
QScriptValue ScriptValueQtWrapper::fullUnwrap(ScriptEngineQtScript* engine, const ScriptValuePointer& value) {
|
||||
if (!value) {
|
||||
return QScriptValue();
|
||||
}
|
||||
ScriptValueQtWrapper* unwrapped = unwrap(value);
|
||||
if (unwrapped) {
|
||||
return unwrapped->toQtValue();
|
||||
}
|
||||
QVariant varValue = value->toVariant();
|
||||
return static_cast<QScriptEngine*>(engine)->newVariant(varValue);
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptValueQtWrapper::call(const ScriptValuePointer& thisObject, const ScriptValueList& args) {
|
||||
QScriptValue qThis = fullUnwrap(thisObject);
|
||||
QScriptValueList qArgs;
|
||||
for (ScriptValueList::const_iterator iter = args.begin(); iter != args.end(); ++iter) {
|
||||
qArgs.push_back(fullUnwrap(*iter));
|
||||
}
|
||||
QScriptValue result = _value.call(qThis, qArgs);
|
||||
return ScriptValuePointer(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptValueQtWrapper::call(const ScriptValuePointer& thisObject, const ScriptValuePointer& arguments) {
|
||||
QScriptValue qThis = fullUnwrap(thisObject);
|
||||
QScriptValue qArgs = fullUnwrap(arguments);
|
||||
QScriptValue result = _value.call(qThis, qArgs);
|
||||
return ScriptValuePointer(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptValueQtWrapper::construct(const ScriptValueList& args) {
|
||||
QScriptValueList qArgs;
|
||||
for (ScriptValueList::const_iterator iter = args.begin(); iter != args.end(); ++iter) {
|
||||
qArgs.push_back(fullUnwrap(*iter));
|
||||
}
|
||||
QScriptValue result = _value.construct(qArgs);
|
||||
return ScriptValuePointer(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptValueQtWrapper::construct(const ScriptValuePointer& arguments) {
|
||||
QScriptValue unwrapped = fullUnwrap(arguments);
|
||||
QScriptValue result = _value.construct(unwrapped);
|
||||
return ScriptValuePointer(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptValueQtWrapper::data() const {
|
||||
QScriptValue result = _value.data();
|
||||
return ScriptValuePointer(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptEnginePointer ScriptValueQtWrapper::engine() const {
|
||||
if (!_engine) {
|
||||
return ScriptEnginePointer();
|
||||
}
|
||||
return _engine->sharedFromThis();
|
||||
}
|
||||
|
||||
ScriptValueIteratorPointer ScriptValueQtWrapper::newIterator() {
|
||||
return ScriptValueIteratorPointer(new ScriptValueIteratorQtWrapper(_engine, _value));
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptValueQtWrapper::property(const QString& name, const ResolveFlags& mode) const {
|
||||
QScriptValue result = _value.property(name, (QScriptValue::ResolveFlags)(int)mode);
|
||||
return ScriptValuePointer(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValuePointer ScriptValueQtWrapper::property(quint32 arrayIndex, const ResolveFlags& mode) const {
|
||||
QScriptValue result = _value.property(arrayIndex, (QScriptValue::ResolveFlags)(int)mode);
|
||||
return ScriptValuePointer(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
void ScriptValueQtWrapper::setData(const ScriptValuePointer& value) {
|
||||
QScriptValue unwrapped = fullUnwrap(value);
|
||||
_value.setData(unwrapped);
|
||||
}
|
||||
|
||||
void ScriptValueQtWrapper::setProperty(const QString& name, const ScriptValuePointer& value, const PropertyFlags& flags) {
|
||||
QScriptValue unwrapped = fullUnwrap(value);
|
||||
_value.setProperty(name, unwrapped, (QScriptValue::PropertyFlags)(int)flags);
|
||||
}
|
||||
|
||||
void ScriptValueQtWrapper::setProperty(quint32 arrayIndex, const ScriptValuePointer& value, const PropertyFlags& flags) {
|
||||
QScriptValue unwrapped = fullUnwrap(value);
|
||||
_value.setProperty(arrayIndex, unwrapped, (QScriptValue::PropertyFlags)(int)flags);
|
||||
}
|
||||
|
||||
void ScriptValueQtWrapper::setPrototype(const ScriptValuePointer& prototype) {
|
||||
ScriptValueQtWrapper* unwrappedPrototype = unwrap(prototype);
|
||||
if (unwrappedPrototype) {
|
||||
_value.setPrototype(unwrappedPrototype->toQtValue());
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::strictlyEquals(const ScriptValuePointer& other) const {
|
||||
ScriptValueQtWrapper* unwrappedOther = unwrap(other);
|
||||
return unwrappedOther ? _value.strictlyEquals(unwrappedOther->toQtValue()) : false;
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::toBool() const {
|
||||
return _value.toBool();
|
||||
}
|
||||
|
||||
qint32 ScriptValueQtWrapper::toInt32() const {
|
||||
return _value.toInt32();
|
||||
}
|
||||
|
||||
double ScriptValueQtWrapper::toInteger() const {
|
||||
return _value.toInteger();
|
||||
}
|
||||
|
||||
double ScriptValueQtWrapper::toNumber() const {
|
||||
return _value.toNumber();
|
||||
}
|
||||
|
||||
QString ScriptValueQtWrapper::toString() const {
|
||||
return _value.toString();
|
||||
}
|
||||
|
||||
quint16 ScriptValueQtWrapper::toUInt16() const {
|
||||
return _value.toUInt16();
|
||||
}
|
||||
|
||||
quint32 ScriptValueQtWrapper::toUInt32() const {
|
||||
return _value.toUInt32();
|
||||
}
|
||||
|
||||
QVariant ScriptValueQtWrapper::toVariant() const {
|
||||
return _value.toVariant();
|
||||
}
|
||||
|
||||
QObject* ScriptValueQtWrapper::toQObject() const {
|
||||
return _value.toQObject();
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::equalsInternal(const ScriptValuePointer& other) const {
|
||||
ScriptValueQtWrapper* unwrappedOther = unwrap(other);
|
||||
return unwrappedOther ? _value.equals(unwrappedOther->toQtValue()) : false;
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::isArrayInternal() const {
|
||||
return _value.isArray();
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::isBoolInternal() const {
|
||||
return _value.isBool();
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::isErrorInternal() const {
|
||||
return _value.isError();
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::isFunctionInternal() const {
|
||||
return _value.isFunction();
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::isNumberInternal() const {
|
||||
return _value.isNumber();
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::isNullInternal() const {
|
||||
return _value.isNull();
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::isObjectInternal() const {
|
||||
return _value.isObject();
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::isStringInternal() const {
|
||||
return _value.isString();
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::isUndefinedInternal() const {
|
||||
return _value.isUndefined();
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::isValidInternal() const {
|
||||
return _value.isValid();
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::isVariantInternal() const {
|
||||
return _value.isVariant();
|
||||
}
|
86
libraries/script-engine/src/qtscript/ScriptValueQtWrapper.h
Normal file
86
libraries/script-engine/src/qtscript/ScriptValueQtWrapper.h
Normal file
|
@ -0,0 +1,86 @@
|
|||
//
|
||||
// ScriptValueQtWrapper.h
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Heather Anderson on 5/16/21.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_ScriptValueQtWrapper_h
|
||||
#define hifi_ScriptValueQtWrapper_h
|
||||
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtScript/QScriptValue>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "../ScriptValue.h"
|
||||
#include "ScriptEngineQtScript.h"
|
||||
|
||||
class ScriptValueQtWrapper : public ScriptValue {
|
||||
public: // construction
|
||||
inline ScriptValueQtWrapper(ScriptEngineQtScript* engine, const QScriptValue& value) :
|
||||
_engine(engine), _value(value) {}
|
||||
inline ScriptValueQtWrapper(ScriptEngineQtScript* engine, QScriptValue&& value) :
|
||||
_engine(engine), _value(std::move(value)) {}
|
||||
static ScriptValueQtWrapper* unwrap(ScriptValuePointer val);
|
||||
inline const QScriptValue& toQtValue() const { return _value; }
|
||||
static QScriptValue fullUnwrap(ScriptEngineQtScript* engine, const ScriptValuePointer& value);
|
||||
|
||||
public: // ScriptValue implementation
|
||||
virtual ScriptValuePointer call(const ScriptValuePointer& thisObject = ScriptValuePointer(),
|
||||
const ScriptValueList& args = ScriptValueList());
|
||||
virtual ScriptValuePointer call(const ScriptValuePointer& thisObject, const ScriptValuePointer& arguments);
|
||||
virtual ScriptValuePointer construct(const ScriptValueList& args = ScriptValueList());
|
||||
virtual ScriptValuePointer construct(const ScriptValuePointer& arguments);
|
||||
virtual ScriptValuePointer data() const;
|
||||
virtual ScriptEnginePointer engine() const;
|
||||
virtual ScriptValueIteratorPointer newIterator();
|
||||
virtual ScriptValuePointer property(const QString& name, const ResolveFlags& mode = ResolvePrototype) const;
|
||||
virtual ScriptValuePointer property(quint32 arrayIndex, const ResolveFlags& mode = ResolvePrototype) const;
|
||||
virtual void setData(const ScriptValuePointer& val);
|
||||
virtual void setProperty(const QString& name,
|
||||
const ScriptValuePointer& value,
|
||||
const PropertyFlags& flags = KeepExistingFlags);
|
||||
virtual void setProperty(quint32 arrayIndex,
|
||||
const ScriptValuePointer& value,
|
||||
const PropertyFlags& flags = KeepExistingFlags);
|
||||
virtual void setPrototype(const ScriptValuePointer& prototype);
|
||||
virtual bool strictlyEquals(const ScriptValuePointer& other) const;
|
||||
|
||||
virtual bool toBool() const;
|
||||
virtual qint32 toInt32() const;
|
||||
virtual double toInteger() const;
|
||||
virtual double toNumber() const;
|
||||
virtual QString toString() const;
|
||||
virtual quint16 toUInt16() const;
|
||||
virtual quint32 toUInt32() const;
|
||||
virtual QVariant toVariant() const;
|
||||
virtual QObject* toQObject() const;
|
||||
|
||||
protected: // ScriptValue implementation
|
||||
virtual bool equalsInternal(const ScriptValuePointer& other) const;
|
||||
virtual bool isArrayInternal() const;
|
||||
virtual bool isBoolInternal() const;
|
||||
virtual bool isErrorInternal() const;
|
||||
virtual bool isFunctionInternal() const;
|
||||
virtual bool isNumberInternal() const;
|
||||
virtual bool isNullInternal() const;
|
||||
virtual bool isObjectInternal() const;
|
||||
virtual bool isStringInternal() const;
|
||||
virtual bool isUndefinedInternal() const;
|
||||
virtual bool isValidInternal() const;
|
||||
virtual bool isVariantInternal() const;
|
||||
|
||||
private: // helper functions
|
||||
QScriptValue fullUnwrap(const ScriptValuePointer& value) const;
|
||||
|
||||
private: // storage
|
||||
QPointer<ScriptEngineQtScript> _engine;
|
||||
QScriptValue _value;
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptValueQtWrapper_h
|
Loading…
Reference in a new issue