mirror of
https://github.com/overte-org/overte.git
synced 2025-07-23 12:04:03 +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)
|
set(TARGET_NAME script-engine)
|
||||||
# FIXME Move undo scripting interface to application and remove Widgets
|
# 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()
|
target_zlib()
|
||||||
if (NOT ANDROID)
|
if (NOT ANDROID)
|
||||||
target_quazip()
|
target_quazip()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
link_hifi_libraries(script-engine-qtscript shaders)
|
link_hifi_libraries(shaders)
|
||||||
include_hifi_library_headers(animation)
|
include_hifi_library_headers(animation)
|
||||||
include_hifi_library_headers(audio)
|
include_hifi_library_headers(audio)
|
||||||
include_hifi_library_headers(avatars)
|
include_hifi_library_headers(avatars)
|
||||||
|
|
|
@ -14,11 +14,16 @@
|
||||||
|
|
||||||
#include <QtCore/QSharedPointer>
|
#include <QtCore/QSharedPointer>
|
||||||
#include <QtCore/QString>
|
#include <QtCore/QString>
|
||||||
|
#include <QtCore/QStringList>
|
||||||
|
|
||||||
|
class ScriptContext;
|
||||||
class ScriptEngine;
|
class ScriptEngine;
|
||||||
|
class ScriptFunctionContext;
|
||||||
class ScriptValue;
|
class ScriptValue;
|
||||||
using ScriptValuePointer = QSharedPointer<ScriptValue>;
|
using ScriptContextPointer = QSharedPointer<ScriptContext>;
|
||||||
|
using ScriptFunctionContextPointer = QSharedPointer<ScriptFunctionContext>;
|
||||||
using ScriptEnginePointer = QSharedPointer<ScriptEngine>;
|
using ScriptEnginePointer = QSharedPointer<ScriptEngine>;
|
||||||
|
using ScriptValuePointer = QSharedPointer<ScriptValue>;
|
||||||
|
|
||||||
class ScriptFunctionContext {
|
class ScriptFunctionContext {
|
||||||
public:
|
public:
|
||||||
|
@ -43,8 +48,8 @@ public:
|
||||||
virtual QStringList backtrace() const = 0;
|
virtual QStringList backtrace() const = 0;
|
||||||
virtual ScriptValuePointer callee() const = 0;
|
virtual ScriptValuePointer callee() const = 0;
|
||||||
virtual ScriptEnginePointer engine() const = 0;
|
virtual ScriptEnginePointer engine() const = 0;
|
||||||
virtual ScriptFunctionContext* functionContext() const = 0;
|
virtual ScriptFunctionContextPointer functionContext() const = 0;
|
||||||
virtual ScriptContext* parentContext() const = 0;
|
virtual ScriptContextPointer parentContext() const = 0;
|
||||||
virtual ScriptValuePointer thisObject() const = 0;
|
virtual ScriptValuePointer thisObject() const = 0;
|
||||||
virtual ScriptValuePointer throwError(const QString& text) = 0;
|
virtual ScriptValuePointer throwError(const QString& text) = 0;
|
||||||
virtual ScriptValuePointer throwValue(const ScriptValuePointer& value) = 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 ScriptValuePointer uncaughtException() const = 0;
|
||||||
virtual QStringList uncaughtExceptionBacktrace() const = 0;
|
virtual QStringList uncaughtExceptionBacktrace() const = 0;
|
||||||
virtual int uncaughtExceptionLineNumber() const = 0;
|
virtual int uncaughtExceptionLineNumber() const = 0;
|
||||||
|
virtual void updateMemoryCost(const qint64& deltaSize) = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// helper to detect and log warnings when other code invokes QScriptEngine/BaseScriptEngine in thread-unsafe ways
|
// 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)
|
// Object conversion helpers (copied from QScriptEngine)
|
||||||
|
|
||||||
|
#include <QtCore/QMetaType>
|
||||||
|
|
||||||
#include "ScriptEngine.h"
|
#include "ScriptEngine.h"
|
||||||
#include "ScriptValue.h"
|
#include "ScriptValue.h"
|
||||||
|
|
||||||
|
|
|
@ -53,11 +53,9 @@
|
||||||
#include <controllers/ScriptingInterface.h>
|
#include <controllers/ScriptingInterface.h>
|
||||||
#include <AnimationObject.h>
|
#include <AnimationObject.h>
|
||||||
|
|
||||||
#include "ArrayBufferViewClass.h"
|
|
||||||
#include "AudioScriptingInterface.h"
|
#include "AudioScriptingInterface.h"
|
||||||
#include "AssetScriptingInterface.h"
|
#include "AssetScriptingInterface.h"
|
||||||
#include "BatchLoader.h"
|
#include "BatchLoader.h"
|
||||||
#include "DataViewClass.h"
|
|
||||||
#include "EventTypes.h"
|
#include "EventTypes.h"
|
||||||
#include "FileScriptingInterface.h" // unzip project
|
#include "FileScriptingInterface.h" // unzip project
|
||||||
#include "MenuItemProperties.h"
|
#include "MenuItemProperties.h"
|
||||||
|
@ -67,7 +65,6 @@
|
||||||
#include "ScriptContext.h"
|
#include "ScriptContext.h"
|
||||||
#include "ScriptEngineCast.h"
|
#include "ScriptEngineCast.h"
|
||||||
#include "ScriptEngineLogging.h"
|
#include "ScriptEngineLogging.h"
|
||||||
#include "TypedArrays.h"
|
|
||||||
#include "XMLHttpRequestClass.h"
|
#include "XMLHttpRequestClass.h"
|
||||||
#include "WebSocketClass.h"
|
#include "WebSocketClass.h"
|
||||||
#include "RecordingScriptingInterface.h"
|
#include "RecordingScriptingInterface.h"
|
||||||
|
@ -133,8 +130,10 @@ static ScriptValuePointer debugPrint(ScriptContext* context, ScriptEngine* engin
|
||||||
AbstractLoggerInterface* loggerInterface = AbstractLoggerInterface::get();
|
AbstractLoggerInterface* loggerInterface = AbstractLoggerInterface::get();
|
||||||
if (loggerInterface && loggerInterface->showSourceDebugging()) {
|
if (loggerInterface && loggerInterface->showSourceDebugging()) {
|
||||||
ScriptContext* userContext = context;
|
ScriptContext* userContext = context;
|
||||||
|
ScriptContextPointer parentContext; // using this variable to maintain parent variable lifespan
|
||||||
while (userContext && userContext->functionContext()->functionType() == ScriptFunctionContext::NativeFunction) {
|
while (userContext && userContext->functionContext()->functionType() == ScriptFunctionContext::NativeFunction) {
|
||||||
userContext = userContext->parentContext();
|
parentContext = userContext->parentContext();
|
||||||
|
userContext = parentContext.data();
|
||||||
}
|
}
|
||||||
QString location;
|
QString location;
|
||||||
if (userContext) {
|
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
|
// 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).
|
// this serves the common case (use in a single script).
|
||||||
auto data = resource->getResource();
|
auto data = resource->getResource();
|
||||||
if (data && !resource->isInScript()) {
|
auto manager = engine->manager();
|
||||||
|
if (data && manager && !resource->isInScript()) {
|
||||||
resource->setInScript(true);
|
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(
|
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().
|
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() {
|
void ScriptManager::run() {
|
||||||
if (QThread::currentThread() != qApp->thread() && _context == Context::CLIENT_SCRIPT) {
|
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
|
// 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";
|
auto name = filenameParts.size() > 0 ? filenameParts[filenameParts.size() - 1] : "unknown";
|
||||||
PROFILE_SET_THREAD_NAME("Script: " + name);
|
PROFILE_SET_THREAD_NAME("Script: " + name);
|
||||||
|
|
||||||
QSharedPointer<ScriptEngines> scriptEngines(_scriptEngines);
|
if (isStopped()) {
|
||||||
if (!scriptEngines || scriptEngines->isStopped()) {
|
|
||||||
return; // bail early - avoid setting state in init(), as evaluate() will bail too
|
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) {
|
void ScriptManager::updateMemoryCost(const qint64& deltaSize) {
|
||||||
if (deltaSize > 0) {
|
_engine->updateMemoryCost(deltaSize);
|
||||||
// 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 ScriptManager::timerFired() {
|
void ScriptManager::timerFired() {
|
||||||
{
|
if (isStopped()) {
|
||||||
QSharedPointer<ScriptEngines> scriptEngines(_scriptEngines);
|
scriptWarningMessage("Script.timerFired() while shutting down is ignored... parent script:" + getFilename());
|
||||||
if (!scriptEngines || scriptEngines->isStopped()) {
|
return; // bail early
|
||||||
scriptWarningMessage("Script.timerFired() while shutting down is ignored... parent script:" + getFilename());
|
|
||||||
return; // bail early
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QTimer* callingTimer = reinterpret_cast<QTimer*>(sender());
|
QTimer* callingTimer = reinterpret_cast<QTimer*>(sender());
|
||||||
|
@ -1292,8 +1288,7 @@ QObject* ScriptManager::setupTimerWithInterval(const ScriptValuePointer& functio
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject* ScriptManager::setInterval(const ScriptValuePointer& function, int intervalMS) {
|
QObject* ScriptManager::setInterval(const ScriptValuePointer& function, int intervalMS) {
|
||||||
QSharedPointer<ScriptEngines> scriptEngines(_scriptEngines);
|
if (isStopped()) {
|
||||||
if (!scriptEngines || scriptEngines->isStopped()) {
|
|
||||||
scriptWarningMessage("Script.setInterval() while shutting down is ignored... parent script:" + getFilename());
|
scriptWarningMessage("Script.setInterval() while shutting down is ignored... parent script:" + getFilename());
|
||||||
return NULL; // bail early
|
return NULL; // bail early
|
||||||
}
|
}
|
||||||
|
@ -1302,8 +1297,7 @@ QObject* ScriptManager::setInterval(const ScriptValuePointer& function, int inte
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject* ScriptManager::setTimeout(const ScriptValuePointer& function, int timeoutMS) {
|
QObject* ScriptManager::setTimeout(const ScriptValuePointer& function, int timeoutMS) {
|
||||||
QSharedPointer<ScriptEngines> scriptEngines(_scriptEngines);
|
if (isStopped()) {
|
||||||
if (!scriptEngines || scriptEngines->isStopped()) {
|
|
||||||
scriptWarningMessage("Script.setTimeout() while shutting down is ignored... parent script:" + getFilename());
|
scriptWarningMessage("Script.setTimeout() while shutting down is ignored... parent script:" + getFilename());
|
||||||
return NULL; // bail early
|
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
|
// to the first absolute URL in the JS scope chain
|
||||||
QUrl parentURL;
|
QUrl parentURL;
|
||||||
auto context = _engine->currentContext();
|
auto context = _engine->currentContext();
|
||||||
|
ScriptContextPointer parentContext; // using this variable to maintain parent variable lifespan
|
||||||
do {
|
do {
|
||||||
auto contextInfo = context->functionContext();
|
auto contextInfo = context->functionContext();
|
||||||
parentURL = QUrl(contextInfo->fileName());
|
parentURL = QUrl(contextInfo->fileName());
|
||||||
context = context->parentContext();
|
parentContext = context->parentContext();
|
||||||
|
context = parentContext.data();
|
||||||
} while (parentURL.isRelative() && context);
|
} while (parentURL.isRelative() && context);
|
||||||
|
|
||||||
if (parentURL.isRelative()) {
|
if (parentURL.isRelative()) {
|
||||||
|
@ -1478,8 +1474,9 @@ ScriptValuePointer ScriptManager::currentModule() {
|
||||||
auto jsRequire = _engine->globalObject()->property("Script")->property("require");
|
auto jsRequire = _engine->globalObject()->property("Script")->property("require");
|
||||||
auto cache = jsRequire->property("cache");
|
auto cache = jsRequire->property("cache");
|
||||||
auto candidate = ScriptValuePointer();
|
auto candidate = ScriptValuePointer();
|
||||||
for (auto c = _engine->currentContext(); c && !candidate->isObject(); c = c->parentContext()) {
|
ScriptContextPointer parentContext; // using this variable to maintain parent variable lifespan
|
||||||
auto contextInfo = c->functionContext();
|
for (auto context = _engine->currentContext(); context && !candidate->isObject(); parentContext = context->parentContext(), context = parentContext.data()) {
|
||||||
|
auto contextInfo = context->functionContext();
|
||||||
candidate = cache->property(contextInfo->fileName());
|
candidate = cache->property(contextInfo->fileName());
|
||||||
}
|
}
|
||||||
if (!candidate->isObject()) {
|
if (!candidate->isObject()) {
|
||||||
|
@ -1737,8 +1734,7 @@ void ScriptManager::include(const QStringList& includeFiles, ScriptValuePointer
|
||||||
if (!_engine->IS_THREADSAFE_INVOCATION(__FUNCTION__)) {
|
if (!_engine->IS_THREADSAFE_INVOCATION(__FUNCTION__)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QSharedPointer<ScriptEngines> scriptEngines(_scriptEngines);
|
if (isStopped()) {
|
||||||
if (!scriptEngines || scriptEngines->isStopped()) {
|
|
||||||
scriptWarningMessage("Script.include() while shutting down is ignored... includeFiles:"
|
scriptWarningMessage("Script.include() while shutting down is ignored... includeFiles:"
|
||||||
+ includeFiles.join(",") + "parent script:" + getFilename());
|
+ includeFiles.join(",") + "parent script:" + getFilename());
|
||||||
return; // bail early
|
return; // bail early
|
||||||
|
@ -1832,8 +1828,7 @@ void ScriptManager::include(const QStringList& includeFiles, ScriptValuePointer
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptManager::include(const QString& includeFile, ScriptValuePointer callback) {
|
void ScriptManager::include(const QString& includeFile, ScriptValuePointer callback) {
|
||||||
QSharedPointer<ScriptEngines> scriptEngines(_scriptEngines);
|
if (isStopped()) {
|
||||||
if (!scriptEngines || scriptEngines->isStopped()) {
|
|
||||||
scriptWarningMessage("Script.include() while shutting down is ignored... includeFile:"
|
scriptWarningMessage("Script.include() while shutting down is ignored... includeFile:"
|
||||||
+ includeFile + "parent script:" + getFilename());
|
+ includeFile + "parent script:" + getFilename());
|
||||||
return; // bail early
|
return; // bail early
|
||||||
|
@ -1851,8 +1846,7 @@ void ScriptManager::load(const QString& loadFile) {
|
||||||
if (!_engine->IS_THREADSAFE_INVOCATION(__FUNCTION__)) {
|
if (!_engine->IS_THREADSAFE_INVOCATION(__FUNCTION__)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QSharedPointer<ScriptEngines> scriptEngines(_scriptEngines);
|
if (isStopped()) {
|
||||||
if (!scriptEngines || scriptEngines->isStopped()) {
|
|
||||||
scriptWarningMessage("Script.load() while shutting down is ignored... loadFile:"
|
scriptWarningMessage("Script.load() while shutting down is ignored... loadFile:"
|
||||||
+ loadFile + "parent script:" + getFilename());
|
+ loadFile + "parent script:" + getFilename());
|
||||||
return; // bail early
|
return; // bail early
|
||||||
|
@ -2689,3 +2683,7 @@ QString ScriptManager::formatException(const ScriptValuePointer& exception, bool
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ScriptValuePointer ScriptManager::evaluate(const QString& program, const QString& fileName) {
|
||||||
|
return _engine->evaluate(program, fileName);
|
||||||
|
}
|
||||||
|
|
|
@ -177,10 +177,12 @@ public:
|
||||||
|
|
||||||
QString getFilename() const;
|
QString getFilename() const;
|
||||||
|
|
||||||
ScriptEnginePointer engine();
|
inline ScriptEnginePointer engine() { return _engine; }
|
||||||
|
|
||||||
QList<EntityItemID> getListOfEntityScriptIDs();
|
QList<EntityItemID> getListOfEntityScriptIDs();
|
||||||
|
|
||||||
|
bool isStopped() const;
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Stops and unloads the current script.
|
* Stops and unloads the current script.
|
||||||
* <p><strong>Warning:</strong> If an assignment client script, the script gets restarted after stopping.</p>
|
* <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 call(const ScriptValuePointer& thisObject, const ScriptValuePointer& arguments) = 0;
|
||||||
virtual ScriptValuePointer construct(const ScriptValueList& args = ScriptValueList()) = 0;
|
virtual ScriptValuePointer construct(const ScriptValueList& args = ScriptValueList()) = 0;
|
||||||
virtual ScriptValuePointer construct(const ScriptValuePointer& arguments) = 0;
|
virtual ScriptValuePointer construct(const ScriptValuePointer& arguments) = 0;
|
||||||
|
virtual ScriptValuePointer data() const = 0;
|
||||||
virtual ScriptEnginePointer engine() const = 0;
|
virtual ScriptEnginePointer engine() const = 0;
|
||||||
inline bool equals(const ScriptValuePointer& other) const;
|
inline bool equals(const ScriptValuePointer& other) const;
|
||||||
inline bool isArray() const;
|
inline bool isArray() const;
|
||||||
|
@ -68,6 +69,7 @@ public:
|
||||||
virtual ScriptValueIteratorPointer newIterator() = 0;
|
virtual ScriptValueIteratorPointer newIterator() = 0;
|
||||||
virtual ScriptValuePointer property(const QString& name, const ResolveFlags& mode = ResolvePrototype) const = 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 ScriptValuePointer property(quint32 arrayIndex, const ResolveFlags& mode = ResolvePrototype) const = 0;
|
||||||
|
virtual void setData(const ScriptValuePointer& val) = 0;
|
||||||
virtual void setProperty(const QString& name,
|
virtual void setProperty(const QString& name,
|
||||||
const ScriptValuePointer& value,
|
const ScriptValuePointer& value,
|
||||||
const PropertyFlags& flags = KeepExistingFlags) = 0;
|
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 <QtCore/QBuffer>
|
||||||
#include <QtGui/QImage>
|
#include <QtGui/QImage>
|
||||||
|
#include <QtScript/QScriptEngine>
|
||||||
#include "ArrayBufferClass.h"
|
|
||||||
|
|
||||||
static const int QCOMPRESS_HEADER_POSITION = 0;
|
static const int QCOMPRESS_HEADER_POSITION = 0;
|
||||||
static const int QCOMPRESS_HEADER_SIZE = 4;
|
static const int QCOMPRESS_HEADER_SIZE = 4;
|
|
@ -13,12 +13,14 @@
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QtCore/QDataStream>
|
#include <QtCore/QDataStream>
|
||||||
|
#include <QtScript/QScriptEngine>
|
||||||
|
#include <QtScript/QScriptValue>
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
|
|
||||||
#include "DataViewClass.h"
|
#include "ArrayBufferViewClass.h"
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(QByteArray*)
|
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
|
#ifndef hifi_ScriptEngineQtScript_h
|
||||||
#define 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/QByteArray>
|
||||||
|
#include <QtCore/QEnableSharedFromThis>
|
||||||
#include <QtCore/QMetaEnum>
|
#include <QtCore/QMetaEnum>
|
||||||
#include <QtCore/QObject>
|
#include <QtCore/QObject>
|
||||||
#include <QtCore/QPointer>
|
#include <QtCore/QPointer>
|
||||||
#include <QtScript/QScriptEngine>
|
#include <QtCore/QSharedPointer>
|
||||||
#include <QtScriptTools/QScriptEngineDebugger>
|
|
||||||
#include <QtCore/QString>
|
#include <QtCore/QString>
|
||||||
|
|
||||||
//#include <AnimationCache.h>
|
#include <QtScript/QScriptEngine>
|
||||||
//#include <AnimVariant.h>
|
#include <QtScriptTools/QScriptEngineDebugger>
|
||||||
//#include <AvatarData.h>
|
|
||||||
//#include <AvatarHashMap.h>
|
#include "../ScriptEngine.h"
|
||||||
//#include <LimitedNodeList.h>
|
#include "../ScriptManager.h"
|
||||||
//#include <EntityItemID.h>
|
|
||||||
//#include <EntityScriptUtils.h>
|
|
||||||
#include <ScriptEngine.h>
|
|
||||||
#include <ScriptManager.h>
|
|
||||||
|
|
||||||
//#include "PointerEvent.h"
|
|
||||||
#include "ArrayBufferClass.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;
|
class ScriptManager;
|
||||||
|
using ScriptEngineQtScriptPointer = QSharedPointer<ScriptEngineQtScript>;
|
||||||
|
using ScriptContextQtPointer = QSharedPointer<ScriptContextQtWrapper>;
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(ScriptEngineQtScriptPointer)
|
Q_DECLARE_METATYPE(ScriptEngineQtScriptPointer)
|
||||||
|
|
||||||
|
@ -92,18 +69,34 @@ Q_DECLARE_METATYPE(ScriptEngineQtScriptPointer)
|
||||||
* <em>Read-only.</em>
|
* <em>Read-only.</em>
|
||||||
* @property {Script.ResourceBuckets} ExternalPaths - External resource buckets.
|
* @property {Script.ResourceBuckets} ExternalPaths - External resource buckets.
|
||||||
*/
|
*/
|
||||||
class ScriptEngineQtScript : public BaseScriptEngine, public ScriptEngine {
|
class ScriptEngineQtScript : public QScriptEngine, public ScriptEngine, public QEnableSharedFromThis<ScriptEngineQtScript> {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
using Base = BaseScriptEngine;
|
|
||||||
|
|
||||||
public: // ScriptEngine implementation
|
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 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;
|
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 newArray(uint length = 0);
|
||||||
virtual ScriptValuePointer newArrayBuffer(const QByteArray& message);
|
virtual ScriptValuePointer newArrayBuffer(const QByteArray& message);
|
||||||
|
virtual ScriptValuePointer newFunction(ScriptEngine::FunctionSignature fun, int length = 0);
|
||||||
virtual ScriptValuePointer newObject();
|
virtual ScriptValuePointer newObject();
|
||||||
virtual ScriptProgramPointer newProgram(const QString& sourceCode, const QString& fileName);
|
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(bool value);
|
||||||
virtual ScriptValuePointer newValue(int value);
|
virtual ScriptValuePointer newValue(int value);
|
||||||
virtual ScriptValuePointer newValue(uint value);
|
virtual ScriptValuePointer newValue(uint value);
|
||||||
|
@ -113,8 +106,41 @@ public: // ScriptEngine implementation
|
||||||
virtual ScriptValuePointer newValue(const char* value);
|
virtual ScriptValuePointer newValue(const char* value);
|
||||||
virtual ScriptValuePointer newVariant(const QVariant& value);
|
virtual ScriptValuePointer newVariant(const QVariant& value);
|
||||||
virtual ScriptValuePointer nullValue();
|
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 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 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:
|
public:
|
||||||
ScriptEngineQtScript(ScriptManager* scriptManager = nullptr);
|
ScriptEngineQtScript(ScriptManager* scriptManager = nullptr);
|
||||||
|
@ -131,7 +157,7 @@ public:
|
||||||
* @deprecated This function is deprecated and will be removed.
|
* @deprecated This function is deprecated and will be removed.
|
||||||
*/
|
*/
|
||||||
/// registers a global object by name
|
/// 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
|
/**jsdoc
|
||||||
* @function Script.registerGetterSetter
|
* @function Script.registerGetterSetter
|
||||||
|
@ -145,6 +171,8 @@ public:
|
||||||
Q_INVOKABLE void registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter,
|
Q_INVOKABLE void registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter,
|
||||||
QScriptEngine::FunctionSignature setter, const QString& parent = QString(""));
|
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
|
/**jsdoc
|
||||||
* @function Script.registerFunction
|
* @function Script.registerFunction
|
||||||
* @param {string} name - Name.
|
* @param {string} name - Name.
|
||||||
|
@ -155,6 +183,9 @@ public:
|
||||||
/// register a global function
|
/// register a global function
|
||||||
Q_INVOKABLE void registerFunction(const QString& name, QScriptEngine::FunctionSignature fun, int numArguments = -1);
|
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
|
/**jsdoc
|
||||||
* @function Script.registerFunction
|
* @function Script.registerFunction
|
||||||
* @param {string} parent - Parent.
|
* @param {string} parent - Parent.
|
||||||
|
@ -167,6 +198,9 @@ public:
|
||||||
Q_INVOKABLE void registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature fun,
|
Q_INVOKABLE void registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature fun,
|
||||||
int numArguments = -1);
|
int numArguments = -1);
|
||||||
|
|
||||||
|
|
||||||
|
Q_INVOKABLE virtual void registerFunction(const QString& parent, const QString& name, ScriptEngine::FunctionSignature fun, int numArguments = -1);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* @function Script.registerEnum
|
* @function Script.registerEnum
|
||||||
* @param {string} name - Name.
|
* @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
|
// 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.
|
// 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
|
/// registers a global enum
|
||||||
Q_INVOKABLE void registerEnum(const QString& enumName, QMetaEnum newEnum);
|
Q_INVOKABLE virtual void registerEnum(const QString& enumName, QMetaEnum newEnum);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* @function Script.registerValue
|
* @function Script.registerValue
|
||||||
|
@ -196,7 +230,10 @@ public:
|
||||||
* @deprecated This function is deprecated and will be removed.
|
* @deprecated This function is deprecated and will be removed.
|
||||||
*/
|
*/
|
||||||
/// evaluate some code in the context of the ScriptEngineQtScript and return the result
|
/// 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
|
/**jsdoc
|
||||||
* @function Script.evaluateInClosure
|
* @function Script.evaluateInClosure
|
||||||
|
@ -205,7 +242,7 @@ public:
|
||||||
* @returns {object} Object.
|
* @returns {object} Object.
|
||||||
* @deprecated This function is deprecated and will be removed.
|
* @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
|
/**jsdoc
|
||||||
* Checks whether the application was compiled as a debug build.
|
* 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
|
// NOTE - this is used by the TypedArray implementation. we need to review this for thread safety
|
||||||
ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; }
|
ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; }
|
||||||
|
|
||||||
public slots:
|
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* @function Script.updateMemoryCost
|
* @function Script.updateMemoryCost
|
||||||
* @param {number} deltaSize - Delta size.
|
* @param {number} deltaSize - Delta size.
|
||||||
* @deprecated This function is deprecated and will be removed.
|
* @deprecated This function is deprecated and will be removed.
|
||||||
*/
|
*/
|
||||||
void updateMemoryCost(const qint64&);
|
virtual void updateMemoryCost(const qint64& deltaSize);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
|
@ -372,6 +407,20 @@ signals:
|
||||||
// script is updated (goes from RUNNING to ERROR_RUNNING_SCRIPT, for example)
|
// script is updated (goes from RUNNING to ERROR_RUNNING_SCRIPT, for example)
|
||||||
void entityScriptDetailsUpdated();
|
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:
|
protected:
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
@ -384,6 +433,10 @@ protected:
|
||||||
|
|
||||||
QPointer<ScriptManager> _manager;
|
QPointer<ScriptManager> _manager;
|
||||||
|
|
||||||
|
ScriptValuePointer _nullValue;
|
||||||
|
ScriptValuePointer _undefinedValue;
|
||||||
|
mutable ScriptContextQtPointer _currContext;
|
||||||
|
|
||||||
std::atomic<bool> _isRunning { false };
|
std::atomic<bool> _isRunning { false };
|
||||||
|
|
||||||
bool _isThreaded { false };
|
bool _isThreaded { false };
|
||||||
|
@ -394,4 +447,23 @@ protected:
|
||||||
ArrayBufferClass* _arrayBufferClass;
|
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