mirror of
https://github.com/lubosz/overte.git
synced 2025-04-09 09:44:33 +02:00
Initial V8 support, not working yet
This commit is contained in:
parent
df507a741b
commit
7e9ad39b32
40 changed files with 4039 additions and 3086 deletions
|
@ -1,66 +0,0 @@
|
|||
//
|
||||
// ArrayBufferClass.h
|
||||
//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
/// @addtogroup ScriptEngine
|
||||
/// @{
|
||||
|
||||
#ifndef hifi_ArrayBufferClass_h
|
||||
#define hifi_ArrayBufferClass_h
|
||||
|
||||
#include <QtScript/QScriptClass>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtScript/QScriptClass>
|
||||
#include <QtScript/QScriptContext>
|
||||
#include <QtScript/QScriptEngine>
|
||||
#include <QtScript/QScriptString>
|
||||
#include <QtScript/QScriptValue>
|
||||
#include <QtCore/QDateTime>
|
||||
|
||||
class ScriptEngineQtScript;
|
||||
|
||||
/// [QtScript] Implements the <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer">ArrayBuffer</a></code> scripting class
|
||||
class ArrayBufferClass : public QObject, public QScriptClass {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ArrayBufferClass(ScriptEngineQtScript* scriptEngine);
|
||||
QScriptValue newInstance(qint32 size);
|
||||
QScriptValue newInstance(const QByteArray& ba);
|
||||
|
||||
QueryFlags queryProperty(const QScriptValue& object,
|
||||
const QScriptString& name,
|
||||
QueryFlags flags, uint* id) override;
|
||||
QScriptValue property(const QScriptValue& object,
|
||||
const QScriptString& name, uint id) override;
|
||||
QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object,
|
||||
const QScriptString& name, uint id) override;
|
||||
|
||||
QString name() const override;
|
||||
QScriptValue prototype() const override;
|
||||
|
||||
|
||||
private:
|
||||
static QScriptValue construct(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
static QScriptValue toScriptValue(QScriptEngine* eng, const QByteArray& ba);
|
||||
static void fromScriptValue(const QScriptValue& obj, QByteArray& ba);
|
||||
|
||||
QScriptValue _proto;
|
||||
QScriptValue _ctor;
|
||||
|
||||
// JS Object attributes
|
||||
QScriptString _name;
|
||||
QScriptString _byteLength;
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_ArrayBufferClass_h
|
||||
|
||||
/// @}
|
|
@ -1,58 +0,0 @@
|
|||
//
|
||||
// ArrayBufferViewClass.h
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/8/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
|
||||
//
|
||||
|
||||
/// @addtogroup ScriptEngine
|
||||
/// @{
|
||||
|
||||
#ifndef hifi_ArrayBufferViewClass_h
|
||||
#define hifi_ArrayBufferViewClass_h
|
||||
|
||||
#include <QtScript/QScriptClass>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtScript/QScriptClass>
|
||||
#include <QtScript/QScriptContext>
|
||||
#include <QtScript/QScriptEngine>
|
||||
#include <QtScript/QScriptString>
|
||||
#include <QtScript/QScriptValue>
|
||||
|
||||
class ScriptEngineQtScript;
|
||||
|
||||
static const QString BUFFER_PROPERTY_NAME = "buffer";
|
||||
static const QString BYTE_OFFSET_PROPERTY_NAME = "byteOffset";
|
||||
static const QString BYTE_LENGTH_PROPERTY_NAME = "byteLength";
|
||||
|
||||
/// [QtScript] The base class containing common code for ArrayBuffer views
|
||||
class ArrayBufferViewClass : public QObject, public QScriptClass {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ArrayBufferViewClass(ScriptEngineQtScript* scriptEngine);
|
||||
|
||||
ScriptEngineQtScript* getScriptEngine() { return _scriptEngine; }
|
||||
|
||||
virtual QueryFlags queryProperty(const QScriptValue& object,
|
||||
const QScriptString& name,
|
||||
QueryFlags flags, uint* id) override;
|
||||
virtual QScriptValue property(const QScriptValue& object,
|
||||
const QScriptString& name, uint id) override;
|
||||
virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object,
|
||||
const QScriptString& name, uint id) override;
|
||||
protected:
|
||||
// JS Object attributes
|
||||
QScriptString _bufferName;
|
||||
QScriptString _byteOffsetName;
|
||||
QScriptString _byteLengthName;
|
||||
|
||||
ScriptEngineQtScript* _scriptEngine;
|
||||
};
|
||||
|
||||
#endif // hifi_ArrayBufferViewClass_h
|
||||
|
||||
/// @}
|
|
@ -1,92 +0,0 @@
|
|||
//
|
||||
// ScriptContextQtWrapper.cpp
|
||||
// libraries/script-engine/src/qtscript
|
||||
//
|
||||
// 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();
|
||||
}
|
||||
|
||||
ScriptValue ScriptContextQtWrapper::argument(int index) const {
|
||||
QScriptValue result = _context->argument(index);
|
||||
return ScriptValue(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
QStringList ScriptContextQtWrapper::backtrace() const {
|
||||
return _context->backtrace();
|
||||
}
|
||||
|
||||
ScriptValue ScriptContextQtWrapper::callee() const {
|
||||
QScriptValue result = _context->callee();
|
||||
return ScriptValue(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptEnginePointer ScriptContextQtWrapper::engine() const {
|
||||
return _engine->shared_from_this();
|
||||
}
|
||||
|
||||
ScriptFunctionContextPointer ScriptContextQtWrapper::functionContext() const {
|
||||
return std::make_shared<ScriptFunctionContextQtWrapper>(_context);
|
||||
}
|
||||
|
||||
ScriptContextPointer ScriptContextQtWrapper::parentContext() const {
|
||||
QScriptContext* result = _context->parentContext();
|
||||
return result ? std::make_shared<ScriptContextQtWrapper>(_engine, result) : ScriptContextPointer();
|
||||
}
|
||||
|
||||
ScriptValue ScriptContextQtWrapper::thisObject() const {
|
||||
QScriptValue result = _context->thisObject();
|
||||
return ScriptValue(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValue ScriptContextQtWrapper::throwError(const QString& text) {
|
||||
QScriptValue result = _context->throwError(text);
|
||||
return ScriptValue(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValue ScriptContextQtWrapper::throwValue(const ScriptValue& value) {
|
||||
ScriptValueQtWrapper* unwrapped = ScriptValueQtWrapper::unwrap(value);
|
||||
if (!unwrapped) {
|
||||
return _engine->undefinedValue();
|
||||
}
|
||||
QScriptValue result = _context->throwValue(unwrapped->toQtValue());
|
||||
return ScriptValue(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();
|
||||
}
|
|
@ -1,919 +0,0 @@
|
|||
//
|
||||
// ScriptEngineQtScript.cpp
|
||||
// libraries/script-engine/src/qtscript
|
||||
//
|
||||
// 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 <Profile.h>
|
||||
|
||||
#include "../ScriptEngineLogging.h"
|
||||
#include "../ScriptProgram.h"
|
||||
#include "../ScriptValue.h"
|
||||
|
||||
#include "ScriptContextQtWrapper.h"
|
||||
#include "ScriptObjectQtProxy.h"
|
||||
#include "ScriptProgramQtWrapper.h"
|
||||
#include "ScriptValueQtWrapper.h"
|
||||
|
||||
static const int MAX_DEBUG_VALUE_LENGTH { 80 };
|
||||
|
||||
bool ScriptEngineQtScript::IS_THREADSAFE_INVOCATION(const QThread* thread, const QString& method) {
|
||||
const QThread* currentThread = QThread::currentThread();
|
||||
if (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;
|
||||
}
|
||||
|
||||
// engine-aware JS Error copier and factory
|
||||
QScriptValue ScriptEngineQtScript::makeError(const QScriptValue& _other, const QString& type) {
|
||||
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
||||
return QScriptEngine::nullValue();
|
||||
}
|
||||
auto other = _other;
|
||||
if (other.isString()) {
|
||||
other = QScriptEngine::newObject();
|
||||
other.setProperty("message", _other.toString());
|
||||
}
|
||||
auto proto = QScriptEngine::globalObject().property(type);
|
||||
if (!proto.isFunction()) {
|
||||
proto = QScriptEngine::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 = QScriptEngine::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 = QScriptEngine::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;
|
||||
}
|
||||
|
||||
ScriptValue ScriptEngineQtScript::makeError(const ScriptValue& _other, const QString& type) {
|
||||
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
||||
return nullValue();
|
||||
}
|
||||
ScriptValueQtWrapper* unwrapped = ScriptValueQtWrapper::unwrap(_other);
|
||||
QScriptValue other;
|
||||
if (_other.isString()) {
|
||||
other = QScriptEngine::newObject();
|
||||
other.setProperty("message", _other.toString());
|
||||
} else if (unwrapped) {
|
||||
other = unwrapped->toQtValue();
|
||||
} else {
|
||||
other = QScriptEngine::newVariant(_other.toVariant());
|
||||
}
|
||||
QScriptValue result = makeError(other, type);
|
||||
return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));
|
||||
}
|
||||
|
||||
// check syntax and when there are issues returns an actual "SyntaxError" with the details
|
||||
ScriptValue ScriptEngineQtScript::lintScript(const QString& sourceCode, const QString& fileName, const int lineNumber) {
|
||||
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
||||
return nullValue();
|
||||
}
|
||||
const auto syntaxCheck = checkSyntax(sourceCode);
|
||||
if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {
|
||||
auto err = QScriptEngine::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 ScriptValue(new ScriptValueQtWrapper(this, std::move(err)));
|
||||
}
|
||||
return undefinedValue();
|
||||
}
|
||||
|
||||
// this pulls from the best available information to create a detailed snapshot of the current exception
|
||||
ScriptValue ScriptEngineQtScript::cloneUncaughtException(const QString& extraDetail) {
|
||||
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
||||
return nullValue();
|
||||
}
|
||||
if (!hasUncaughtException()) {
|
||||
return nullValue();
|
||||
}
|
||||
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 = QScriptEngine::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 ScriptEngineQtScript::raiseException(const QScriptValue& exception) {
|
||||
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
||||
return false;
|
||||
}
|
||||
if (QScriptEngine::currentContext()) {
|
||||
// we have an active context / JS stack frame so throw the exception per usual
|
||||
QScriptEngine::currentContext()->throwValue(makeError(exception));
|
||||
return true;
|
||||
} else if (_scriptManager) {
|
||||
// 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
|
||||
QScriptValue thrown = makeError(exception);
|
||||
emit _scriptManager->unhandledException(ScriptValue(new ScriptValueQtWrapper(this, std::move(thrown))));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ScriptEngineQtScript::maybeEmitUncaughtException(const QString& debugHint) {
|
||||
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
||||
return false;
|
||||
}
|
||||
if (!isEvaluating() && hasUncaughtException() && _scriptManager) {
|
||||
emit _scriptManager->unhandledException(cloneUncaughtException(debugHint));
|
||||
clearExceptions();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Lambda
|
||||
QScriptValue ScriptEngineQtScript::newLambdaFunction(std::function<QScriptValue(QScriptContext*, ScriptEngineQtScript*)> operation,
|
||||
const QScriptValue& data,
|
||||
const QScriptEngine::ValueOwnership& ownership) {
|
||||
auto lambda = new Lambda(this, operation, data);
|
||||
auto object = QScriptEngine::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(ScriptEngineQtScript* engine,
|
||||
std::function<QScriptValue(QScriptContext*, ScriptEngineQtScript*)> operation,
|
||||
QScriptValue data) :
|
||||
engine(engine),
|
||||
operation(operation), data(data) {
|
||||
#ifdef DEBUG_JS_LAMBDA_FUNCS
|
||||
qDebug() << "Lambda" << data.toString();
|
||||
#endif
|
||||
}
|
||||
QScriptValue Lambda::call() {
|
||||
if (!engine->IS_THREADSAFE_INVOCATION(__FUNCTION__)) {
|
||||
return static_cast<QScriptEngine*>(engine)->nullValue();
|
||||
}
|
||||
return operation(static_cast<QScriptEngine*>(engine)->currentContext(), engine);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_JS
|
||||
void ScriptEngineQtScript::_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
|
||||
|
||||
ScriptEngineQtScript::ScriptEngineQtScript(ScriptManager* scriptManager) :
|
||||
QScriptEngine(),
|
||||
_scriptManager(scriptManager),
|
||||
_arrayBufferClass(new ArrayBufferClass(this))
|
||||
{
|
||||
registerSystemTypes();
|
||||
|
||||
if (_scriptManager) {
|
||||
connect(this, &QScriptEngine::signalHandlerException, this, [this](const QScriptValue& exception) {
|
||||
if (hasUncaughtException()) {
|
||||
// the engine's uncaughtException() seems to produce much better stack traces here
|
||||
emit _scriptManager->unhandledException(cloneUncaughtException("signalHandlerException"));
|
||||
clearExceptions();
|
||||
} else {
|
||||
// ... but may not always be available -- so if needed we fallback to the passed exception
|
||||
QScriptValue thrown = makeError(exception);
|
||||
emit _scriptManager->unhandledException(ScriptValue(new ScriptValueQtWrapper(this, std::move(thrown))));
|
||||
}
|
||||
}, Qt::DirectConnection);
|
||||
moveToThread(scriptManager->thread());
|
||||
}
|
||||
|
||||
QScriptValue null = QScriptEngine::nullValue();
|
||||
_nullValue = ScriptValue(new ScriptValueQtWrapper(this, std::move(null)));
|
||||
|
||||
QScriptValue undefined = QScriptEngine::undefinedValue();
|
||||
_undefinedValue = ScriptValue(new ScriptValueQtWrapper(this, std::move(undefined)));
|
||||
|
||||
QScriptEngine::setProcessEventsInterval(MSECS_PER_SECOND);
|
||||
}
|
||||
|
||||
void ScriptEngineQtScript::registerEnum(const QString& enumName, QMetaEnum newEnum) {
|
||||
if (!newEnum.isValid()) {
|
||||
qCCritical(scriptengine) << "registerEnum called on invalid enum with name " << enumName;
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < newEnum.keyCount(); i++) {
|
||||
const char* keyName = newEnum.key(i);
|
||||
QString fullName = enumName + "." + keyName;
|
||||
registerValue(fullName, newEnum.keyToValue(keyName));
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngineQtScript::registerValue(const QString& valueName, QScriptValue value) {
|
||||
if (QThread::currentThread() != QScriptEngine::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 = QScriptEngine::globalObject();
|
||||
|
||||
for (const auto& pathPart : pathToValue) {
|
||||
partsToGo--;
|
||||
if (!partObject.property(pathPart).isValid()) {
|
||||
if (partsToGo > 0) {
|
||||
//QObject *object = new QObject;
|
||||
QScriptValue partValue = QScriptEngine::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() != QScriptEngine::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 (!QScriptEngine::globalObject().property(name).isValid()) {
|
||||
if (object) {
|
||||
QScriptValue value = ScriptObjectQtProxy::newQObject(this, object, ScriptEngine::QtOwnership);
|
||||
QScriptEngine::globalObject().setProperty(name, value);
|
||||
} else {
|
||||
QScriptEngine::globalObject().setProperty(name, QScriptValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngineQtScript::registerFunction(const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) {
|
||||
if (QThread::currentThread() != QScriptEngine::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 = QScriptEngine::newFunction(functionSignature, numArguments);
|
||||
QScriptEngine::globalObject().setProperty(name, scriptFun);
|
||||
}
|
||||
|
||||
void ScriptEngineQtScript::registerFunction(const QString& name, ScriptEngine::FunctionSignature functionSignature, int numArguments) {
|
||||
if (QThread::currentThread() != QScriptEngine::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(ScriptEngine::FunctionSignature, functionSignature),
|
||||
Q_ARG(int, numArguments));
|
||||
return;
|
||||
}
|
||||
#ifdef THREAD_DEBUGGING
|
||||
qCDebug(scriptengine) << "ScriptEngineQtScript::registerFunction() called on thread [" << QThread::currentThread() << "] name:" << name;
|
||||
#endif
|
||||
|
||||
auto scriptFun = newFunction(functionSignature, numArguments);
|
||||
globalObject().setProperty(name, scriptFun);
|
||||
}
|
||||
|
||||
void ScriptEngineQtScript::registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) {
|
||||
if (QThread::currentThread() != QScriptEngine::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 = QScriptEngine::globalObject().property(parent);
|
||||
if (object.isValid()) {
|
||||
QScriptValue scriptFun = QScriptEngine::newFunction(functionSignature, numArguments);
|
||||
object.setProperty(name, scriptFun);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngineQtScript::registerFunction(const QString& parent, const QString& name, ScriptEngine::FunctionSignature functionSignature, int numArguments) {
|
||||
if (QThread::currentThread() != QScriptEngine::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(ScriptEngine::FunctionSignature, functionSignature),
|
||||
Q_ARG(int, numArguments));
|
||||
return;
|
||||
}
|
||||
#ifdef THREAD_DEBUGGING
|
||||
qCDebug(scriptengine) << "ScriptEngineQtScript::registerFunction() called on thread [" << QThread::currentThread() << "] parent:" << parent << "name:" << name;
|
||||
#endif
|
||||
|
||||
auto object = globalObject().property(parent);
|
||||
if (object.isValid()) {
|
||||
auto scriptFun = newFunction(functionSignature, numArguments);
|
||||
object.setProperty(name, scriptFun);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngineQtScript::registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter,
|
||||
QScriptEngine::FunctionSignature setter, const QString& parent) {
|
||||
if (QThread::currentThread() != QScriptEngine::thread()) {
|
||||
#ifdef THREAD_DEBUGGING
|
||||
qCDebug(scriptengine) << "*** WARNING *** ScriptEngineQtScript::registerGetterSetter() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
|
||||
" name:" << name << "parent:" << parent;
|
||||
#endif
|
||||
QMetaObject::invokeMethod(this, "registerGetterSetter",
|
||||
Q_ARG(const QString&, name),
|
||||
Q_ARG(QScriptEngine::FunctionSignature, getter),
|
||||
Q_ARG(QScriptEngine::FunctionSignature, setter),
|
||||
Q_ARG(const QString&, parent));
|
||||
return;
|
||||
}
|
||||
#ifdef THREAD_DEBUGGING
|
||||
qCDebug(scriptengine) << "ScriptEngineQtScript::registerGetterSetter() called on thread [" << QThread::currentThread() << "] name:" << name << "parent:" << parent;
|
||||
#endif
|
||||
|
||||
QScriptValue setterFunction = QScriptEngine::newFunction(setter, 1);
|
||||
QScriptValue getterFunction = QScriptEngine::newFunction(getter);
|
||||
|
||||
if (!parent.isNull() && !parent.isEmpty()) {
|
||||
QScriptValue object = QScriptEngine::globalObject().property(parent);
|
||||
if (object.isValid()) {
|
||||
object.setProperty(name, setterFunction, QScriptValue::PropertySetter);
|
||||
object.setProperty(name, getterFunction, QScriptValue::PropertyGetter);
|
||||
}
|
||||
} else {
|
||||
QScriptEngine::globalObject().setProperty(name, setterFunction, QScriptValue::PropertySetter);
|
||||
QScriptEngine::globalObject().setProperty(name, getterFunction, QScriptValue::PropertyGetter);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngineQtScript::registerGetterSetter(const QString& name, ScriptEngine::FunctionSignature getter,
|
||||
ScriptEngine::FunctionSignature setter, const QString& parent) {
|
||||
if (QThread::currentThread() != QScriptEngine::thread()) {
|
||||
#ifdef THREAD_DEBUGGING
|
||||
qCDebug(scriptengine) << "*** WARNING *** ScriptEngineQtScript::registerGetterSetter() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
|
||||
" name:" << name << "parent:" << parent;
|
||||
#endif
|
||||
QMetaObject::invokeMethod(this, "registerGetterSetter",
|
||||
Q_ARG(const QString&, name),
|
||||
Q_ARG(ScriptEngine::FunctionSignature, getter),
|
||||
Q_ARG(ScriptEngine::FunctionSignature, setter),
|
||||
Q_ARG(const QString&, parent));
|
||||
return;
|
||||
}
|
||||
#ifdef THREAD_DEBUGGING
|
||||
qCDebug(scriptengine) << "ScriptEngineQtScript::registerGetterSetter() called on thread [" << QThread::currentThread() << "] name:" << name << "parent:" << parent;
|
||||
#endif
|
||||
|
||||
auto setterFunction = newFunction(setter, 1);
|
||||
auto getterFunction = newFunction(getter);
|
||||
|
||||
if (!parent.isNull() && !parent.isEmpty()) {
|
||||
auto object = globalObject().property(parent);
|
||||
if (object.isValid()) {
|
||||
object.setProperty(name, setterFunction, ScriptValue::PropertySetter);
|
||||
object.setProperty(name, getterFunction, ScriptValue::PropertyGetter);
|
||||
}
|
||||
} else {
|
||||
globalObject().setProperty(name, setterFunction, ScriptValue::PropertySetter);
|
||||
globalObject().setProperty(name, getterFunction, ScriptValue::PropertyGetter);
|
||||
}
|
||||
}
|
||||
|
||||
ScriptValue ScriptEngineQtScript::evaluateInClosure(const ScriptValue& _closure,
|
||||
const ScriptProgramPointer& _program) {
|
||||
PROFILE_RANGE(script, "evaluateInClosure");
|
||||
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
||||
return nullValue();
|
||||
}
|
||||
ScriptProgramQtWrapper* unwrappedProgram = ScriptProgramQtWrapper::unwrap(_program);
|
||||
if (unwrappedProgram == nullptr) {
|
||||
return nullValue();
|
||||
}
|
||||
const QScriptProgram& program = unwrappedProgram->toQtValue();
|
||||
|
||||
const auto fileName = program.fileName();
|
||||
const auto shortName = QUrl(fileName).fileName();
|
||||
|
||||
ScriptValueQtWrapper* unwrappedClosure = ScriptValueQtWrapper::unwrap(_closure);
|
||||
if (unwrappedClosure == nullptr) {
|
||||
return nullValue();
|
||||
}
|
||||
const QScriptValue& closure = unwrappedClosure->toQtValue();
|
||||
|
||||
QScriptValue oldGlobal;
|
||||
auto global = closure.property("global");
|
||||
if (global.isObject()) {
|
||||
#ifdef DEBUG_JS
|
||||
qCDebug(shared) << " setting global = closure.global" << shortName;
|
||||
#endif
|
||||
oldGlobal = QScriptEngine::globalObject();
|
||||
setGlobalObject(global);
|
||||
}
|
||||
|
||||
auto context = pushContext();
|
||||
|
||||
auto thiz = closure.property("this");
|
||||
if (thiz.isObject()) {
|
||||
#ifdef DEBUG_JS
|
||||
qCDebug(shared) << " setting this = closure.this" << shortName;
|
||||
#endif
|
||||
context->setThisObject(thiz);
|
||||
}
|
||||
|
||||
context->pushScope(closure);
|
||||
#ifdef DEBUG_JS
|
||||
qCDebug(shared) << QString("[%1] evaluateInClosure %2").arg(isEvaluating()).arg(shortName);
|
||||
#endif
|
||||
ScriptValue result;
|
||||
{
|
||||
auto qResult = QScriptEngine::evaluate(program);
|
||||
|
||||
if (hasUncaughtException()) {
|
||||
auto err = cloneUncaughtException(__FUNCTION__);
|
||||
#ifdef DEBUG_JS_EXCEPTIONS
|
||||
qCWarning(shared) << __FUNCTION__ << "---------- hasCaught:" << err.toString() << result.toString();
|
||||
err.setProperty("_result", result);
|
||||
#endif
|
||||
result = err;
|
||||
} else {
|
||||
result = ScriptValue(new ScriptValueQtWrapper(this, std::move(qResult)));
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG_JS
|
||||
qCDebug(shared) << QString("[%1] //evaluateInClosure %2").arg(isEvaluating()).arg(shortName);
|
||||
#endif
|
||||
popContext();
|
||||
|
||||
if (oldGlobal.isValid()) {
|
||||
#ifdef DEBUG_JS
|
||||
qCDebug(shared) << " restoring global" << shortName;
|
||||
#endif
|
||||
setGlobalObject(oldGlobal);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ScriptValue ScriptEngineQtScript::evaluate(const QString& sourceCode, const QString& fileName) {
|
||||
if (_scriptManager && _scriptManager->isStopped()) {
|
||||
return undefinedValue(); // bail early
|
||||
}
|
||||
|
||||
if (QThread::currentThread() != QScriptEngine::thread()) {
|
||||
ScriptValue result;
|
||||
#ifdef THREAD_DEBUGGING
|
||||
qCDebug(scriptengine) << "*** WARNING *** ScriptEngineQtScript::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
|
||||
"sourceCode:" << sourceCode << " fileName:" << fileName;
|
||||
#endif
|
||||
BLOCKING_INVOKE_METHOD(this, "evaluate",
|
||||
Q_RETURN_ARG(ScriptValue, result),
|
||||
Q_ARG(const QString&, sourceCode),
|
||||
Q_ARG(const QString&, fileName));
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check syntax
|
||||
auto syntaxError = lintScript(sourceCode, fileName);
|
||||
if (syntaxError.isError()) {
|
||||
if (!isEvaluating()) {
|
||||
syntaxError.setProperty("detail", "evaluate");
|
||||
}
|
||||
raiseException(syntaxError);
|
||||
maybeEmitUncaughtException("lint");
|
||||
return syntaxError;
|
||||
}
|
||||
QScriptProgram program { sourceCode, fileName, 1 };
|
||||
if (program.isNull()) {
|
||||
// can this happen?
|
||||
auto err = makeError(newValue("could not create QScriptProgram for " + fileName));
|
||||
raiseException(err);
|
||||
maybeEmitUncaughtException("compile");
|
||||
return err;
|
||||
}
|
||||
|
||||
QScriptValue result = QScriptEngine::evaluate(program);
|
||||
maybeEmitUncaughtException("evaluate");
|
||||
|
||||
return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));
|
||||
}
|
||||
|
||||
Q_INVOKABLE ScriptValue ScriptEngineQtScript::evaluate(const ScriptProgramPointer& program) {
|
||||
if (_scriptManager && _scriptManager->isStopped()) {
|
||||
return undefinedValue(); // bail early
|
||||
}
|
||||
|
||||
if (QThread::currentThread() != QScriptEngine::thread()) {
|
||||
ScriptValue result;
|
||||
#ifdef THREAD_DEBUGGING
|
||||
qCDebug(scriptengine) << "*** WARNING *** ScriptEngineQtScript::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
|
||||
"sourceCode:" << sourceCode << " fileName:" << fileName;
|
||||
#endif
|
||||
BLOCKING_INVOKE_METHOD(this, "evaluate",
|
||||
Q_RETURN_ARG(ScriptValue, result),
|
||||
Q_ARG(const ScriptProgramPointer&, program));
|
||||
return result;
|
||||
}
|
||||
|
||||
ScriptProgramQtWrapper* unwrapped = ScriptProgramQtWrapper::unwrap(program);
|
||||
if (!unwrapped) {
|
||||
auto err = makeError(newValue("could not unwrap program"));
|
||||
raiseException(err);
|
||||
maybeEmitUncaughtException("compile");
|
||||
return err;
|
||||
}
|
||||
|
||||
const QScriptProgram& qProgram = unwrapped->toQtValue();
|
||||
if (qProgram.isNull()) {
|
||||
// can this happen?
|
||||
auto err = makeError(newValue("requested program is empty"));
|
||||
raiseException(err);
|
||||
maybeEmitUncaughtException("compile");
|
||||
return err;
|
||||
}
|
||||
|
||||
QScriptValue result = QScriptEngine::evaluate(qProgram);
|
||||
maybeEmitUncaughtException("evaluate");
|
||||
|
||||
return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));
|
||||
}
|
||||
|
||||
|
||||
void ScriptEngineQtScript::updateMemoryCost(const qint64& deltaSize) {
|
||||
if (deltaSize > 0) {
|
||||
// We've patched qt to fix https://highfidelity.atlassian.net/browse/BUGZ-46 on mac and windows only.
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
|
||||
reportAdditionalMemoryCost(deltaSize);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ScriptEngine implementation
|
||||
|
||||
ScriptValue ScriptEngineQtScript::globalObject() const {
|
||||
QScriptValue global = QScriptEngine::globalObject(); // can't cache the value as it may change
|
||||
return ScriptValue(new ScriptValueQtWrapper(const_cast<ScriptEngineQtScript*>(this), std::move(global)));
|
||||
}
|
||||
|
||||
ScriptManager* ScriptEngineQtScript::manager() const {
|
||||
return _scriptManager;
|
||||
}
|
||||
|
||||
ScriptValue ScriptEngineQtScript::newArray(uint length) {
|
||||
QScriptValue result = QScriptEngine::newArray(length);
|
||||
return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValue ScriptEngineQtScript::newArrayBuffer(const QByteArray& message) {
|
||||
QScriptValue data = QScriptEngine::newVariant(QVariant::fromValue(message));
|
||||
QScriptValue ctor = QScriptEngine::globalObject().property("ArrayBuffer");
|
||||
auto array = qscriptvalue_cast<ArrayBufferClass*>(ctor.data());
|
||||
if (!array) {
|
||||
return undefinedValue();
|
||||
}
|
||||
QScriptValue result = QScriptEngine::newObject(array, data);
|
||||
return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValue ScriptEngineQtScript::newObject() {
|
||||
QScriptValue result = QScriptEngine::newObject();
|
||||
return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptProgramPointer ScriptEngineQtScript::newProgram(const QString& sourceCode, const QString& fileName) {
|
||||
QScriptProgram result(sourceCode, fileName);
|
||||
return std::make_shared<ScriptProgramQtWrapper>(this, result);
|
||||
}
|
||||
|
||||
ScriptValue ScriptEngineQtScript::newQObject(QObject* object,
|
||||
ScriptEngine::ValueOwnership ownership,
|
||||
const ScriptEngine::QObjectWrapOptions& options) {
|
||||
QScriptValue result = ScriptObjectQtProxy::newQObject(this, object, ownership, options);
|
||||
return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValue ScriptEngineQtScript::newValue(bool value) {
|
||||
QScriptValue result(this, value);
|
||||
return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValue ScriptEngineQtScript::newValue(int value) {
|
||||
QScriptValue result(this, value);
|
||||
return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValue ScriptEngineQtScript::newValue(uint value) {
|
||||
QScriptValue result(this, value);
|
||||
return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValue ScriptEngineQtScript::newValue(double value) {
|
||||
QScriptValue result(this, value);
|
||||
return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValue ScriptEngineQtScript::newValue(const QString& value) {
|
||||
QScriptValue result(this, value);
|
||||
return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValue ScriptEngineQtScript::newValue(const QLatin1String& value) {
|
||||
QScriptValue result(this, value);
|
||||
return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValue ScriptEngineQtScript::newValue(const char* value) {
|
||||
QScriptValue result(this, value);
|
||||
return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValue ScriptEngineQtScript::newVariant(const QVariant& value) {
|
||||
QScriptValue result = castVariantToValue(value);
|
||||
return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValue ScriptEngineQtScript::nullValue() {
|
||||
return _nullValue;
|
||||
}
|
||||
|
||||
ScriptValue ScriptEngineQtScript::undefinedValue() {
|
||||
return _undefinedValue;
|
||||
}
|
||||
|
||||
void ScriptEngineQtScript::abortEvaluation() {
|
||||
QScriptEngine::abortEvaluation();
|
||||
}
|
||||
|
||||
void ScriptEngineQtScript::clearExceptions() {
|
||||
QScriptEngine::clearExceptions();
|
||||
}
|
||||
|
||||
ScriptContext* ScriptEngineQtScript::currentContext() const {
|
||||
QScriptContext* localCtx = QScriptEngine::currentContext();
|
||||
if (!localCtx) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!_currContext || _currContext->toQtValue() != localCtx) {
|
||||
_currContext = std::make_shared<ScriptContextQtWrapper>(const_cast<ScriptEngineQtScript*>(this), localCtx);
|
||||
}
|
||||
return _currContext.get();
|
||||
}
|
||||
|
||||
bool ScriptEngineQtScript::hasUncaughtException() const {
|
||||
return QScriptEngine::hasUncaughtException();
|
||||
}
|
||||
|
||||
bool ScriptEngineQtScript::isEvaluating() const {
|
||||
return QScriptEngine::isEvaluating();
|
||||
}
|
||||
|
||||
ScriptValue ScriptEngineQtScript::newFunction(ScriptEngine::FunctionSignature fun, int length) {
|
||||
auto innerFunc = [](QScriptContext* _context, QScriptEngine* _engine) -> QScriptValue {
|
||||
auto callee = _context->callee();
|
||||
QVariant funAddr = callee.property("_func").toVariant();
|
||||
ScriptEngine::FunctionSignature fun = reinterpret_cast<ScriptEngine::FunctionSignature>(funAddr.toULongLong());
|
||||
ScriptEngineQtScript* engine = static_cast<ScriptEngineQtScript*>(_engine);
|
||||
ScriptContextQtWrapper context(engine, _context);
|
||||
ScriptValue result = fun(&context, engine);
|
||||
ScriptValueQtWrapper* unwrapped = ScriptValueQtWrapper::unwrap(result);
|
||||
return unwrapped ? unwrapped->toQtValue() : QScriptValue();
|
||||
};
|
||||
|
||||
QScriptValue result = QScriptEngine::newFunction(innerFunc, length);
|
||||
auto funAddr = QScriptEngine::newVariant(QVariant(reinterpret_cast<qulonglong>(fun)));
|
||||
result.setProperty("_func", funAddr, QScriptValue::PropertyFlags(QScriptValue::ReadOnly + QScriptValue::Undeletable + QScriptValue::SkipInEnumeration));
|
||||
return ScriptValue(new ScriptValueQtWrapper(this, std::move(result)));
|
||||
}
|
||||
|
||||
void ScriptEngineQtScript::setObjectName(const QString& name) {
|
||||
QScriptEngine::setObjectName(name);
|
||||
}
|
||||
|
||||
bool ScriptEngineQtScript::setProperty(const char* name, const QVariant& value) {
|
||||
return QScriptEngine::setProperty(name, value);
|
||||
}
|
||||
|
||||
void ScriptEngineQtScript::setProcessEventsInterval(int interval) {
|
||||
QScriptEngine::setProcessEventsInterval(interval);
|
||||
}
|
||||
|
||||
QThread* ScriptEngineQtScript::thread() const {
|
||||
return QScriptEngine::thread();
|
||||
}
|
||||
|
||||
void ScriptEngineQtScript::setThread(QThread* thread) {
|
||||
moveToThread(thread);
|
||||
}
|
||||
|
||||
ScriptValue ScriptEngineQtScript::uncaughtException() const {
|
||||
QScriptValue result = QScriptEngine::uncaughtException();
|
||||
return ScriptValue(new ScriptValueQtWrapper(const_cast<ScriptEngineQtScript*>(this), std::move(result)));
|
||||
}
|
||||
|
||||
QStringList ScriptEngineQtScript::uncaughtExceptionBacktrace() const {
|
||||
return QScriptEngine::uncaughtExceptionBacktrace();
|
||||
}
|
||||
|
||||
int ScriptEngineQtScript::uncaughtExceptionLineNumber() const {
|
||||
return QScriptEngine::uncaughtExceptionLineNumber();
|
||||
}
|
||||
|
||||
bool ScriptEngineQtScript::raiseException(const ScriptValue& exception) {
|
||||
ScriptValueQtWrapper* unwrapped = ScriptValueQtWrapper::unwrap(exception);
|
||||
QScriptValue qException = unwrapped ? unwrapped->toQtValue() : QScriptEngine::newVariant(exception.toVariant());
|
||||
return raiseException(qException);
|
||||
}
|
||||
|
||||
ScriptValue ScriptEngineQtScript::create(int type, const void* ptr) {
|
||||
QVariant variant(type, ptr);
|
||||
QScriptValue scriptValue = castVariantToValue(variant);
|
||||
return ScriptValue(new ScriptValueQtWrapper(this, std::move(scriptValue)));
|
||||
}
|
||||
|
||||
QVariant ScriptEngineQtScript::convert(const ScriptValue& value, int typeId) {
|
||||
ScriptValueQtWrapper* unwrapped = ScriptValueQtWrapper::unwrap(value);
|
||||
if (unwrapped == nullptr) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant var;
|
||||
if (!castValueToVariant(unwrapped->toQtValue(), var, typeId)) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
int destType = var.userType();
|
||||
if (destType != typeId) {
|
||||
var.convert(typeId); // if conversion fails then var is set to QVariant()
|
||||
}
|
||||
|
||||
return var;
|
||||
}
|
|
@ -1,794 +0,0 @@
|
|||
//
|
||||
// ScriptObjectQtProxy.cpp
|
||||
// libraries/script-engine/src/qtscript
|
||||
//
|
||||
// Created by Heather Anderson on 12/5/21.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
// Copyright 2022 Overte e.V.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "ScriptObjectQtProxy.h"
|
||||
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QSharedPointer>
|
||||
|
||||
#include <QtScript/QScriptContext>
|
||||
|
||||
#include "../ScriptEngineLogging.h"
|
||||
|
||||
#include "ScriptContextQtWrapper.h"
|
||||
#include "ScriptValueQtWrapper.h"
|
||||
|
||||
Q_DECLARE_METATYPE(QScriptContext*)
|
||||
Q_DECLARE_METATYPE(ScriptValue)
|
||||
Q_DECLARE_METATYPE(QScriptValue)
|
||||
|
||||
Q_DECLARE_METATYPE(QSharedPointer<ScriptObjectQtProxy>)
|
||||
Q_DECLARE_METATYPE(QSharedPointer<ScriptVariantQtProxy>)
|
||||
|
||||
// Used strictly to replace the "this" object value for property access. May expand to a full context element
|
||||
// if we find it necessary to, but hopefully not needed
|
||||
class ScriptPropertyContextQtWrapper final : public ScriptContext {
|
||||
public: // construction
|
||||
inline ScriptPropertyContextQtWrapper(const ScriptValue& object, ScriptContext* parentContext) :
|
||||
_parent(parentContext), _object(object) {}
|
||||
|
||||
public: // ScriptContext implementation
|
||||
virtual int argumentCount() const override { return _parent->argumentCount(); }
|
||||
virtual ScriptValue argument(int index) const override { return _parent->argument(index); }
|
||||
virtual QStringList backtrace() const override { return _parent->backtrace(); }
|
||||
virtual ScriptValue callee() const override { return _parent->callee(); }
|
||||
virtual ScriptEnginePointer engine() const override { return _parent->engine(); }
|
||||
virtual ScriptFunctionContextPointer functionContext() const override { return _parent->functionContext(); }
|
||||
virtual ScriptContextPointer parentContext() const override { return _parent->parentContext(); }
|
||||
virtual ScriptValue thisObject() const override { return _object; }
|
||||
virtual ScriptValue throwError(const QString& text) override { return _parent->throwError(text); }
|
||||
virtual ScriptValue throwValue(const ScriptValue& value) override { return _parent->throwValue(value); }
|
||||
|
||||
private: // storage
|
||||
ScriptContext* _parent;
|
||||
const ScriptValue& _object;
|
||||
};
|
||||
|
||||
QScriptValue ScriptObjectQtProxy::newQObject(ScriptEngineQtScript* engine, QObject* object,
|
||||
ScriptEngine::ValueOwnership ownership,
|
||||
const ScriptEngine::QObjectWrapOptions& options) {
|
||||
QScriptEngine* qengine = static_cast<QScriptEngine*>(engine);
|
||||
|
||||
// do we already have a valid wrapper for this QObject?
|
||||
{
|
||||
QMutexLocker guard(&engine->_qobjectWrapperMapProtect);
|
||||
ScriptEngineQtScript::ObjectWrapperMap::const_iterator lookup = engine->_qobjectWrapperMap.find(object);
|
||||
if (lookup != engine->_qobjectWrapperMap.end()) {
|
||||
QSharedPointer<ScriptObjectQtProxy> proxy = lookup.value().lock();
|
||||
if (proxy) return qengine->newObject(proxy.get(), qengine->newVariant(QVariant::fromValue(proxy)));;
|
||||
}
|
||||
}
|
||||
|
||||
bool ownsObject;
|
||||
switch (ownership) {
|
||||
case ScriptEngine::QtOwnership:
|
||||
ownsObject = false;
|
||||
break;
|
||||
case ScriptEngine::ScriptOwnership:
|
||||
ownsObject = true;
|
||||
break;
|
||||
case ScriptEngine::AutoOwnership:
|
||||
ownsObject = !object->parent();
|
||||
break;
|
||||
default:
|
||||
ownsObject = false;
|
||||
qCritical() << "Wrong ScriptEngine::ValueOwnership value: " << ownership;
|
||||
break;
|
||||
}
|
||||
|
||||
// create the wrapper
|
||||
auto proxy = QSharedPointer<ScriptObjectQtProxy>::create(engine, object, ownsObject, options);
|
||||
|
||||
{
|
||||
QMutexLocker guard(&engine->_qobjectWrapperMapProtect);
|
||||
|
||||
// check again to see if someone else created the wrapper while we were busy
|
||||
ScriptEngineQtScript::ObjectWrapperMap::const_iterator lookup = engine->_qobjectWrapperMap.find(object);
|
||||
if (lookup != engine->_qobjectWrapperMap.end()) {
|
||||
QSharedPointer<ScriptObjectQtProxy> proxy = lookup.value().lock();
|
||||
if (proxy) return qengine->newObject(proxy.get(), qengine->newVariant(QVariant::fromValue(proxy)));;
|
||||
}
|
||||
|
||||
// register the wrapper with the engine and make sure it cleans itself up
|
||||
engine->_qobjectWrapperMap.insert(object, proxy);
|
||||
QPointer<ScriptEngineQtScript> enginePtr = engine;
|
||||
object->connect(object, &QObject::destroyed, engine, [enginePtr, object]() {
|
||||
if (!enginePtr) return;
|
||||
QMutexLocker guard(&enginePtr->_qobjectWrapperMapProtect);
|
||||
ScriptEngineQtScript::ObjectWrapperMap::iterator lookup = enginePtr->_qobjectWrapperMap.find(object);
|
||||
if (lookup != enginePtr->_qobjectWrapperMap.end()) {
|
||||
enginePtr->_qobjectWrapperMap.erase(lookup);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return qengine->newObject(proxy.get(), qengine->newVariant(QVariant::fromValue(proxy)));
|
||||
}
|
||||
|
||||
ScriptObjectQtProxy* ScriptObjectQtProxy::unwrapProxy(const QScriptValue& val) {
|
||||
QScriptClass* scriptClass = val.scriptClass();
|
||||
return scriptClass ? dynamic_cast<ScriptObjectQtProxy*>(scriptClass) : nullptr;
|
||||
}
|
||||
|
||||
QObject* ScriptObjectQtProxy::unwrap(const QScriptValue& val) {
|
||||
if (val.isQObject()) {
|
||||
return val.toQObject();
|
||||
}
|
||||
ScriptObjectQtProxy* proxy = unwrapProxy(val);
|
||||
return proxy ? proxy->toQtValue() : nullptr;
|
||||
}
|
||||
|
||||
ScriptObjectQtProxy::~ScriptObjectQtProxy() {
|
||||
if (_ownsObject) {
|
||||
QObject* qobject = _object;
|
||||
if(qobject) qobject->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptObjectQtProxy::investigate() {
|
||||
QObject* qobject = _object;
|
||||
Q_ASSERT(qobject);
|
||||
if (!qobject) return;
|
||||
|
||||
const QMetaObject* metaObject = qobject->metaObject();
|
||||
|
||||
// discover properties
|
||||
int startIdx = _wrapOptions & ScriptEngine::ExcludeSuperClassProperties ? metaObject->propertyOffset() : 0;
|
||||
int num = metaObject->propertyCount();
|
||||
for (int idx = startIdx; idx < num; ++idx) {
|
||||
QMetaProperty prop = metaObject->property(idx);
|
||||
if (!prop.isScriptable()) continue;
|
||||
|
||||
// always exclude child objects (at least until we decide otherwise)
|
||||
int metaTypeId = prop.userType();
|
||||
if (metaTypeId != QMetaType::UnknownType) {
|
||||
QMetaType metaType(metaTypeId);
|
||||
if (metaType.flags() & QMetaType::PointerToQObject) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
PropertyDef& propDef = _props.insert(idx, PropertyDef()).value();
|
||||
propDef.name = _engine->toStringHandle(QString::fromLatin1(prop.name()));
|
||||
propDef.flags = QScriptValue::Undeletable | QScriptValue::PropertyGetter | QScriptValue::PropertySetter |
|
||||
QScriptValue::QObjectMember;
|
||||
if (prop.isConstant()) propDef.flags |= QScriptValue::ReadOnly;
|
||||
}
|
||||
|
||||
// discover methods
|
||||
startIdx = (_wrapOptions & ScriptEngine::ExcludeSuperClassMethods) ? metaObject->methodOffset() : 0;
|
||||
num = metaObject->methodCount();
|
||||
QHash<QScriptString, int> methodNames;
|
||||
for (int idx = startIdx; idx < num; ++idx) {
|
||||
QMetaMethod method = metaObject->method(idx);
|
||||
|
||||
// perhaps keep this comment? Calls (like AudioScriptingInterface::playSound) seem to expect non-public methods to be script-accessible
|
||||
/* if (method.access() != QMetaMethod::Public) continue;*/
|
||||
|
||||
bool isSignal = false;
|
||||
QByteArray szName = method.name();
|
||||
|
||||
switch (method.methodType()) {
|
||||
case QMetaMethod::Constructor:
|
||||
continue;
|
||||
case QMetaMethod::Signal:
|
||||
isSignal = true;
|
||||
break;
|
||||
case QMetaMethod::Slot:
|
||||
if (_wrapOptions & ScriptEngine::ExcludeSlots) {
|
||||
continue;
|
||||
}
|
||||
if (szName == "deleteLater") {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
QScriptString name = _engine->toStringHandle(QString::fromLatin1(szName));
|
||||
auto nameLookup = methodNames.find(name);
|
||||
if (isSignal) {
|
||||
if (nameLookup == methodNames.end()) {
|
||||
SignalDef& signalDef = _signals.insert(idx, SignalDef()).value();
|
||||
signalDef.name = name;
|
||||
signalDef.signal = method;
|
||||
methodNames.insert(name, idx);
|
||||
} else {
|
||||
int originalMethodId = nameLookup.value();
|
||||
SignalDefMap::iterator signalLookup = _signals.find(originalMethodId);
|
||||
Q_ASSERT(signalLookup != _signals.end());
|
||||
SignalDef& signalDef = signalLookup.value();
|
||||
Q_ASSERT(signalDef.signal.parameterCount() != method.parameterCount());
|
||||
if (signalDef.signal.parameterCount() < method.parameterCount()) {
|
||||
signalDef.signal = method;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int parameterCount = method.parameterCount();
|
||||
if(method.returnType() == QMetaType::UnknownType) {
|
||||
qCritical(scriptengine) << "Method " << metaObject->className() << "::" << name << " has QMetaType::UnknownType return value";
|
||||
}
|
||||
for (int i = 0; i < method.parameterCount(); i++) {
|
||||
if (method.parameterType(i) == QMetaType::UnknownType) {
|
||||
qCritical(scriptengine) << "Parameter " << i << "in method " << metaObject->className() << "::" << name << " is of type QMetaType::UnknownType";
|
||||
}
|
||||
}
|
||||
if (nameLookup == methodNames.end()) {
|
||||
MethodDef& methodDef = _methods.insert(idx, MethodDef()).value();
|
||||
methodDef.name = name;
|
||||
methodDef.numMaxParms = parameterCount;
|
||||
methodDef.methods.append(method);
|
||||
methodNames.insert(name, idx);
|
||||
} else {
|
||||
int originalMethodId = nameLookup.value();
|
||||
MethodDefMap::iterator methodLookup = _methods.find(originalMethodId);
|
||||
Q_ASSERT(methodLookup != _methods.end());
|
||||
MethodDef& methodDef = methodLookup.value();
|
||||
if(methodDef.numMaxParms < parameterCount) methodDef.numMaxParms = parameterCount;
|
||||
methodDef.methods.append(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString ScriptObjectQtProxy::name() const {
|
||||
Q_ASSERT(_object);
|
||||
if (!_object) return "";
|
||||
return _object ? _object->objectName() : "";
|
||||
QString objectName = _object->objectName();
|
||||
if (!objectName.isEmpty()) return objectName;
|
||||
return _object->metaObject()->className();
|
||||
}
|
||||
|
||||
QScriptClass::QueryFlags ScriptObjectQtProxy::queryProperty(const QScriptValue& object, const QScriptString& name, QueryFlags flags, uint* id) {
|
||||
// check for properties
|
||||
for (PropertyDefMap::const_iterator trans = _props.cbegin(); trans != _props.cend(); ++trans) {
|
||||
const PropertyDef& propDef = trans.value();
|
||||
if (propDef.name != name) continue;
|
||||
*id = trans.key() | PROPERTY_TYPE;
|
||||
return flags & (HandlesReadAccess | HandlesWriteAccess);
|
||||
}
|
||||
|
||||
// check for methods
|
||||
for (MethodDefMap::const_iterator trans = _methods.cbegin(); trans != _methods.cend(); ++trans) {
|
||||
if (trans.value().name != name) continue;
|
||||
*id = trans.key() | METHOD_TYPE;
|
||||
return flags & (HandlesReadAccess | HandlesWriteAccess);
|
||||
}
|
||||
|
||||
// check for signals
|
||||
for (SignalDefMap::const_iterator trans = _signals.cbegin(); trans != _signals.cend(); ++trans) {
|
||||
if (trans.value().name != name) continue;
|
||||
*id = trans.key() | SIGNAL_TYPE;
|
||||
return flags & (HandlesReadAccess | HandlesWriteAccess);
|
||||
}
|
||||
|
||||
return QueryFlags();
|
||||
}
|
||||
|
||||
QScriptValue::PropertyFlags ScriptObjectQtProxy::propertyFlags(const QScriptValue& object, const QScriptString& name, uint id) {
|
||||
QObject* qobject = _object;
|
||||
if (!qobject) {
|
||||
return QScriptValue::PropertyFlags();
|
||||
}
|
||||
|
||||
switch (id & TYPE_MASK) {
|
||||
case PROPERTY_TYPE: {
|
||||
PropertyDefMap::const_iterator lookup = _props.find(id & ~TYPE_MASK);
|
||||
if (lookup == _props.cend()) return QScriptValue::PropertyFlags();
|
||||
const PropertyDef& propDef = lookup.value();
|
||||
return propDef.flags;
|
||||
}
|
||||
case METHOD_TYPE: {
|
||||
MethodDefMap::const_iterator lookup = _methods.find(id & ~TYPE_MASK);
|
||||
if (lookup == _methods.cend()) return QScriptValue::PropertyFlags();
|
||||
return QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::QObjectMember;
|
||||
}
|
||||
case SIGNAL_TYPE: {
|
||||
SignalDefMap::const_iterator lookup = _signals.find(id & ~TYPE_MASK);
|
||||
if (lookup == _signals.cend()) return QScriptValue::PropertyFlags();
|
||||
return QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::QObjectMember;
|
||||
}
|
||||
}
|
||||
return QScriptValue::PropertyFlags();
|
||||
}
|
||||
|
||||
QScriptValue ScriptObjectQtProxy::property(const QScriptValue& object, const QScriptString& name, uint id) {
|
||||
QObject* qobject = _object;
|
||||
if (!qobject) {
|
||||
QScriptContext* currentContext = static_cast<QScriptEngine*>(_engine)->currentContext();
|
||||
currentContext->throwError(QScriptContext::ReferenceError, "Referencing deleted native object");
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
const QMetaObject* metaObject = qobject->metaObject();
|
||||
|
||||
switch (id & TYPE_MASK) {
|
||||
case PROPERTY_TYPE: {
|
||||
int propId = id & ~TYPE_MASK;
|
||||
PropertyDefMap::const_iterator lookup = _props.find(propId);
|
||||
if (lookup == _props.cend()) return QScriptValue();
|
||||
|
||||
QMetaProperty prop = metaObject->property(propId);
|
||||
ScriptValue scriptThis = ScriptValue(new ScriptValueQtWrapper(_engine, object));
|
||||
ScriptPropertyContextQtWrapper ourContext(scriptThis, _engine->currentContext());
|
||||
ScriptContextGuard guard(&ourContext);
|
||||
|
||||
QVariant varValue = prop.read(qobject);
|
||||
return _engine->castVariantToValue(varValue);
|
||||
}
|
||||
case METHOD_TYPE: {
|
||||
int methodId = id & ~TYPE_MASK;
|
||||
MethodDefMap::const_iterator lookup = _methods.find(methodId);
|
||||
if (lookup == _methods.cend()) return QScriptValue();
|
||||
const MethodDef& methodDef = lookup.value();
|
||||
for (auto iter = methodDef.methods.begin(); iter != methodDef.methods.end(); iter++ ) {
|
||||
if((*iter).returnType() == QMetaType::UnknownType) {
|
||||
qDebug(scriptengine) << "Method with QMetaType::UnknownType " << metaObject->className() << " " << (*iter).name();
|
||||
}
|
||||
}
|
||||
return static_cast<QScriptEngine*>(_engine)->newObject(
|
||||
new ScriptMethodQtProxy(_engine, qobject, object, methodDef.methods, methodDef.numMaxParms));
|
||||
}
|
||||
case SIGNAL_TYPE: {
|
||||
int signalId = id & ~TYPE_MASK;
|
||||
SignalDefMap::const_iterator defLookup = _signals.find(signalId);
|
||||
if (defLookup == _signals.cend()) return QScriptValue();
|
||||
|
||||
InstanceMap::const_iterator instLookup = _signalInstances.find(signalId);
|
||||
if (instLookup == _signalInstances.cend() || instLookup.value().isNull()) {
|
||||
instLookup = _signalInstances.insert(signalId,
|
||||
new ScriptSignalQtProxy(_engine, qobject, object, defLookup.value().signal));
|
||||
Q_ASSERT(instLookup != _signalInstances.cend());
|
||||
}
|
||||
ScriptSignalQtProxy* proxy = instLookup.value();
|
||||
|
||||
QScriptEngine::QObjectWrapOptions options = QScriptEngine::ExcludeSuperClassContents |
|
||||
QScriptEngine::ExcludeDeleteLater |
|
||||
QScriptEngine::PreferExistingWrapperObject;
|
||||
return static_cast<QScriptEngine*>(_engine)->newQObject(proxy, QScriptEngine::ScriptOwnership, options);
|
||||
}
|
||||
}
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
void ScriptObjectQtProxy::setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) {
|
||||
if (!(id & PROPERTY_TYPE)) return;
|
||||
QObject* qobject = _object;
|
||||
if (!qobject) {
|
||||
QScriptContext* currentContext = static_cast<QScriptEngine*>(_engine)->currentContext();
|
||||
currentContext->throwError(QScriptContext::ReferenceError, "Referencing deleted native object");
|
||||
return;
|
||||
}
|
||||
|
||||
int propId = id & ~TYPE_MASK;
|
||||
PropertyDefMap::const_iterator lookup = _props.find(propId);
|
||||
if (lookup == _props.cend()) return;
|
||||
const PropertyDef& propDef = lookup.value();
|
||||
if (propDef.flags & QScriptValue::ReadOnly) return;
|
||||
|
||||
const QMetaObject* metaObject = qobject->metaObject();
|
||||
QMetaProperty prop = metaObject->property(propId);
|
||||
|
||||
ScriptValue scriptThis = ScriptValue(new ScriptValueQtWrapper(_engine, object));
|
||||
ScriptPropertyContextQtWrapper ourContext(scriptThis, _engine->currentContext());
|
||||
ScriptContextGuard guard(&ourContext);
|
||||
|
||||
int propTypeId = prop.userType();
|
||||
Q_ASSERT(propTypeId != QMetaType::UnknownType);
|
||||
QVariant varValue;
|
||||
if(!_engine->castValueToVariant(value, varValue, propTypeId)) {
|
||||
QByteArray propTypeName = QMetaType(propTypeId).name();
|
||||
QByteArray valTypeName = _engine->valueType(value).toLatin1();
|
||||
QScriptContext* currentContext = static_cast<QScriptEngine*>(_engine)->currentContext();
|
||||
currentContext->throwError(QScriptContext::TypeError, QString("Cannot convert %1 to %2").arg(valTypeName, propTypeName));
|
||||
return;
|
||||
}
|
||||
prop.write(qobject, varValue);
|
||||
}
|
||||
|
||||
ScriptVariantQtProxy::ScriptVariantQtProxy(ScriptEngineQtScript* engine, const QVariant& variant, QScriptValue scriptProto, ScriptObjectQtProxy* proto) :
|
||||
QScriptClass(engine), _engine(engine), _variant(variant), _scriptProto(scriptProto), _proto(proto) {
|
||||
_name = QString::fromLatin1(variant.typeName());
|
||||
}
|
||||
|
||||
QScriptValue ScriptVariantQtProxy::newVariant(ScriptEngineQtScript* engine, const QVariant& variant, QScriptValue proto) {
|
||||
QScriptEngine* qengine = static_cast<QScriptEngine*>(engine);
|
||||
ScriptObjectQtProxy* protoProxy = ScriptObjectQtProxy::unwrapProxy(proto);
|
||||
if (!protoProxy) {
|
||||
Q_ASSERT(protoProxy);
|
||||
return qengine->newVariant(variant);
|
||||
}
|
||||
auto proxy = QSharedPointer<ScriptVariantQtProxy>::create(engine, variant, proto, protoProxy);
|
||||
return qengine->newObject(proxy.get(), qengine->newVariant(QVariant::fromValue(proxy)));
|
||||
}
|
||||
|
||||
ScriptVariantQtProxy* ScriptVariantQtProxy::unwrapProxy(const QScriptValue& val) {
|
||||
QScriptClass* scriptClass = val.scriptClass();
|
||||
return scriptClass ? dynamic_cast<ScriptVariantQtProxy*>(scriptClass) : nullptr;
|
||||
}
|
||||
|
||||
QVariant ScriptVariantQtProxy::unwrap(const QScriptValue& val) {
|
||||
ScriptVariantQtProxy* proxy = unwrapProxy(val);
|
||||
return proxy ? proxy->toQtValue() : QVariant();
|
||||
}
|
||||
|
||||
QString ScriptMethodQtProxy::fullName() const {
|
||||
Q_ASSERT(_object);
|
||||
if (!_object) return "";
|
||||
Q_ASSERT(!_metas.isEmpty());
|
||||
const QMetaMethod& firstMethod = _metas.front();
|
||||
QString objectName = _object->objectName();
|
||||
if (!objectName.isEmpty()) {
|
||||
return QString("%1.%2").arg(objectName, firstMethod.name());
|
||||
}
|
||||
return QString("%1::%2").arg(_object->metaObject()->className(), firstMethod.name());
|
||||
}
|
||||
|
||||
bool ScriptMethodQtProxy::supportsExtension(Extension extension) const {
|
||||
switch (extension) {
|
||||
case Callable:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QVariant ScriptMethodQtProxy::extension(Extension extension, const QVariant& argument) {
|
||||
if (extension != Callable) return QVariant();
|
||||
QScriptContext* context = qvariant_cast<QScriptContext*>(argument);
|
||||
|
||||
QObject* qobject = _object;
|
||||
if (!qobject) {
|
||||
context->throwError(QScriptContext::ReferenceError, "Referencing deleted native object");
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
int scriptNumArgs = context->argumentCount();
|
||||
int numArgs = std::min(scriptNumArgs, _numMaxParms);
|
||||
|
||||
const int scriptValueTypeId = qMetaTypeId<ScriptValue>();
|
||||
|
||||
int parameterConversionFailureId = 0;
|
||||
int parameterConversionFailureCount = 0;
|
||||
|
||||
int num_metas = _metas.size();
|
||||
QVector< QList<ScriptValue> > qScriptArgLists;
|
||||
QVector< QVector <QGenericArgument> > qGenArgsVectors;
|
||||
QVector< QList<QVariant> > qVarArgLists;
|
||||
qScriptArgLists.resize(num_metas);
|
||||
qGenArgsVectors.resize(num_metas);
|
||||
qVarArgLists.resize(num_metas);
|
||||
bool isValidMetaSelected = false;
|
||||
int bestMeta = 0;
|
||||
int bestConversionPenaltyScore = 0;
|
||||
|
||||
for (int i = 0; i < num_metas; i++) {
|
||||
const QMetaMethod& meta = _metas[i];
|
||||
int methodNumArgs = meta.parameterCount();
|
||||
if (methodNumArgs != numArgs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
qGenArgsVectors[i].resize(10);
|
||||
int conversionPenaltyScore = 0;
|
||||
int conversionFailures = 0;
|
||||
|
||||
for (int arg = 0; arg < numArgs; ++arg) {
|
||||
int methodArgTypeId = meta.parameterType(arg);
|
||||
Q_ASSERT(methodArgTypeId != QMetaType::UnknownType);
|
||||
QScriptValue argVal = context->argument(arg);
|
||||
if (methodArgTypeId == scriptValueTypeId) {
|
||||
qScriptArgLists[i].append(ScriptValue(new ScriptValueQtWrapper(_engine, argVal)));
|
||||
qGenArgsVectors[i][arg] = Q_ARG(ScriptValue, qScriptArgLists[i].back());
|
||||
} else if (methodArgTypeId == QMetaType::QVariant) {
|
||||
qVarArgLists[i].append(argVal.toVariant());
|
||||
qGenArgsVectors[i][arg] = Q_ARG(QVariant, qVarArgLists[i].back());
|
||||
} else {
|
||||
QVariant varArgVal;
|
||||
if (!_engine->castValueToVariant(argVal, varArgVal, methodArgTypeId)) {
|
||||
conversionFailures++;
|
||||
} else {
|
||||
qVarArgLists[i].append(varArgVal);
|
||||
const QVariant& converted = qVarArgLists[i].back();
|
||||
conversionPenaltyScore = _engine->computeCastPenalty(argVal, methodArgTypeId);
|
||||
|
||||
// a lot of type conversion assistance thanks to https://stackoverflow.com/questions/28457819/qt-invoke-method-with-qvariant
|
||||
// A const_cast is needed because calling data() would detach the QVariant.
|
||||
qGenArgsVectors[i][arg] =
|
||||
QGenericArgument(QMetaType::typeName(converted.userType()), const_cast<void*>(converted.constData()));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (conversionFailures) {
|
||||
if (conversionFailures < parameterConversionFailureCount || !parameterConversionFailureCount) {
|
||||
parameterConversionFailureCount = conversionFailures;
|
||||
parameterConversionFailureId = meta.methodIndex();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isValidMetaSelected) {
|
||||
isValidMetaSelected = true;
|
||||
bestMeta = i;
|
||||
bestConversionPenaltyScore = conversionPenaltyScore;
|
||||
}
|
||||
if (isValidMetaSelected && bestConversionPenaltyScore > conversionPenaltyScore) {
|
||||
bestMeta = i;
|
||||
bestConversionPenaltyScore = conversionPenaltyScore;
|
||||
}
|
||||
}
|
||||
|
||||
if (isValidMetaSelected) {
|
||||
ScriptContextQtWrapper ourContext(_engine, context);
|
||||
ScriptContextGuard guard(&ourContext);
|
||||
const QMetaMethod& meta = _metas[bestMeta];
|
||||
int returnTypeId = meta.returnType();
|
||||
QVector <QGenericArgument> &qGenArgs = qGenArgsVectors[bestMeta];
|
||||
|
||||
// The Qt MOC engine will automatically call qRegisterMetaType on invokable parameters and properties, but there's
|
||||
// nothing in there for return values so these need to be explicitly runtime-registered!
|
||||
Q_ASSERT(returnTypeId != QMetaType::UnknownType);
|
||||
if (returnTypeId == QMetaType::UnknownType) {
|
||||
context->throwError(QString("Cannot call native function %1, its return value has not been registered with Qt").arg(fullName()));
|
||||
return QVariant();
|
||||
} else if (returnTypeId == QMetaType::Void) {
|
||||
bool success = meta.invoke(qobject, Qt::DirectConnection, qGenArgs[0], qGenArgs[1], qGenArgs[2], qGenArgs[3],
|
||||
qGenArgs[4], qGenArgs[5], qGenArgs[6], qGenArgs[7], qGenArgs[8], qGenArgs[9]);
|
||||
if (!success) {
|
||||
context->throwError(QString("Unexpected: Native call of %1 failed").arg(fullName()));
|
||||
}
|
||||
return QVariant();
|
||||
} else if (returnTypeId == scriptValueTypeId) {
|
||||
ScriptValue result;
|
||||
bool success = meta.invoke(qobject, Qt::DirectConnection, Q_RETURN_ARG(ScriptValue, result), qGenArgs[0],
|
||||
qGenArgs[1], qGenArgs[2], qGenArgs[3], qGenArgs[4], qGenArgs[5], qGenArgs[6],
|
||||
qGenArgs[7], qGenArgs[8], qGenArgs[9]);
|
||||
if (!success) {
|
||||
context->throwError(QString("Unexpected: Native call of %1 failed").arg(fullName()));
|
||||
return QVariant();
|
||||
}
|
||||
QScriptValue qResult = ScriptValueQtWrapper::fullUnwrap(_engine, result);
|
||||
return QVariant::fromValue(qResult);
|
||||
} else {
|
||||
// a lot of type conversion assistance thanks to https://stackoverflow.com/questions/28457819/qt-invoke-method-with-qvariant
|
||||
const char* typeName = meta.typeName();
|
||||
QVariant qRetVal(returnTypeId, static_cast<void*>(NULL));
|
||||
QGenericReturnArgument sRetVal(typeName, const_cast<void*>(qRetVal.constData()));
|
||||
|
||||
bool success =
|
||||
meta.invoke(qobject, Qt::DirectConnection, sRetVal, qGenArgs[0], qGenArgs[1], qGenArgs[2], qGenArgs[3],
|
||||
qGenArgs[4], qGenArgs[5], qGenArgs[6], qGenArgs[7], qGenArgs[8], qGenArgs[9]);
|
||||
if (!success) {
|
||||
context->throwError(QString("Unexpected: Native call of %1 failed").arg(fullName()));
|
||||
return QVariant();
|
||||
}
|
||||
QScriptValue qResult = _engine->castVariantToValue(qRetVal);
|
||||
return QVariant::fromValue(qResult);
|
||||
}
|
||||
}
|
||||
|
||||
// we failed to convert the call to C++, try to create a somewhat sane error message
|
||||
if (parameterConversionFailureCount == 0) {
|
||||
context->throwError(QString("Native call of %1 failed: unexpected parameter count").arg(fullName()));
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
const QMetaMethod& meta = _object->metaObject()->method(parameterConversionFailureId);
|
||||
int methodNumArgs = meta.parameterCount();
|
||||
Q_ASSERT(methodNumArgs == numArgs);
|
||||
|
||||
for (int arg = 0; arg < numArgs; ++arg) {
|
||||
int methodArgTypeId = meta.parameterType(arg);
|
||||
Q_ASSERT(methodArgTypeId != QMetaType::UnknownType);
|
||||
QScriptValue argVal = context->argument(arg);
|
||||
if (methodArgTypeId != scriptValueTypeId && methodArgTypeId != QMetaType::QVariant) {
|
||||
QVariant varArgVal;
|
||||
if (!_engine->castValueToVariant(argVal, varArgVal, methodArgTypeId)) {
|
||||
QByteArray methodTypeName = QMetaType(methodArgTypeId).name();
|
||||
QByteArray argTypeName = _engine->valueType(argVal).toLatin1();
|
||||
context->throwError(QScriptContext::TypeError, QString("Native call of %1 failed: Cannot convert parameter %2 from %3 to %4")
|
||||
.arg(fullName()).arg(arg+1).arg(argTypeName, methodTypeName));
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context->throwError(QString("Native call of %1 failed: could not locate an overload with the requested arguments").arg(fullName()));
|
||||
Q_ASSERT(false); // really shouldn't have gotten here -- it didn't work before and it's working now?
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QString ScriptSignalQtProxy::fullName() const {
|
||||
Q_ASSERT(_object);
|
||||
if (!_object) return "";
|
||||
QString objectName = _object->objectName();
|
||||
if (!objectName.isEmpty()) {
|
||||
return QString("%1.%2").arg(objectName, _meta.name());
|
||||
}
|
||||
return QString("%1::%2").arg(_object->metaObject()->className(), _meta.name());
|
||||
}
|
||||
|
||||
// Adapted from https://doc.qt.io/archives/qq/qq16-dynamicqobject.html, for connecting to a signal without a compile-time definition for it
|
||||
int ScriptSignalQtProxy::qt_metacall(QMetaObject::Call call, int id, void** arguments) {
|
||||
id = ScriptSignalQtProxyBase::qt_metacall(call, id, arguments);
|
||||
if (id != 0 || call != QMetaObject::InvokeMetaMethod) {
|
||||
return id;
|
||||
}
|
||||
|
||||
QScriptValueList args;
|
||||
int numArgs = _meta.parameterCount();
|
||||
for (int arg = 0; arg < numArgs; ++arg) {
|
||||
int methodArgTypeId = _meta.parameterType(arg);
|
||||
Q_ASSERT(methodArgTypeId != QMetaType::UnknownType);
|
||||
QVariant argValue(methodArgTypeId, arguments[arg+1]);
|
||||
args.append(_engine->castVariantToValue(argValue));
|
||||
}
|
||||
|
||||
QList<Connection> connections;
|
||||
withReadLock([&]{
|
||||
connections = _connections;
|
||||
});
|
||||
|
||||
for (ConnectionList::iterator iter = connections.begin(); iter != connections.end(); ++iter) {
|
||||
Connection& conn = *iter;
|
||||
conn.callback.call(conn.thisValue, args);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ScriptSignalQtProxy::discoverMetaCallIdx() {
|
||||
const QMetaObject* ourMeta = metaObject();
|
||||
return ourMeta->methodCount();
|
||||
}
|
||||
|
||||
ScriptSignalQtProxy::ConnectionList::iterator ScriptSignalQtProxy::findConnection(QScriptValue thisObject, QScriptValue callback) {
|
||||
auto iterOut = resultWithReadLock<ScriptSignalQtProxy::ConnectionList::iterator>([&]{
|
||||
ConnectionList::iterator iter;
|
||||
for (iter = _connections.begin(); iter != _connections.end(); ++iter) {
|
||||
Connection& conn = *iter;
|
||||
if (conn.callback.strictlyEquals(callback) && conn.thisValue.strictlyEquals(thisObject)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return iter;
|
||||
});
|
||||
return iterOut;
|
||||
}
|
||||
|
||||
|
||||
void ScriptSignalQtProxy::connect(QScriptValue arg0, QScriptValue arg1) {
|
||||
QObject* qobject = _object;
|
||||
if (!qobject) {
|
||||
QScriptContext* currentContext = static_cast<QScriptEngine*>(_engine)->currentContext();
|
||||
currentContext->throwError(QScriptContext::ReferenceError, "Referencing deleted native object");
|
||||
return;
|
||||
}
|
||||
|
||||
// untangle the arguments
|
||||
QScriptValue callback;
|
||||
QScriptValue callbackThis;
|
||||
if (arg1.isFunction()) {
|
||||
callbackThis = arg0;
|
||||
callback = arg1;
|
||||
} else {
|
||||
callback = arg0;
|
||||
}
|
||||
if (!callback.isFunction()) {
|
||||
QScriptContext* currentContext = static_cast<QScriptEngine*>(_engine)->currentContext();
|
||||
currentContext->throwError(QScriptContext::TypeError, "Function expected as argument to 'connect'");
|
||||
return;
|
||||
}
|
||||
|
||||
// are we already connected?
|
||||
{
|
||||
ConnectionList::iterator lookup = findConnection(callbackThis, callback);
|
||||
if (lookup != _connections.end()) {
|
||||
return; // already exists
|
||||
}
|
||||
}
|
||||
|
||||
// add a reference to ourselves to the destination callback
|
||||
QScriptValue destData = callback.data();
|
||||
Q_ASSERT(!destData.isValid() || destData.isArray());
|
||||
if (!destData.isArray()) {
|
||||
destData = static_cast<QScriptEngine*>(_engine)->newArray();
|
||||
}
|
||||
{
|
||||
QScriptValueList args;
|
||||
args << thisObject();
|
||||
destData.property("push").call(destData, args);
|
||||
}
|
||||
callback.setData(destData);
|
||||
|
||||
// add this to our internal list of connections
|
||||
Connection newConn;
|
||||
newConn.callback = callback;
|
||||
newConn.thisValue = callbackThis;
|
||||
|
||||
withWriteLock([&]{
|
||||
_connections.append(newConn);
|
||||
});
|
||||
|
||||
// inform Qt that we're connecting to this signal
|
||||
if (!_isConnected) {
|
||||
auto result = QMetaObject::connect(qobject, _meta.methodIndex(), this, _metaCallId);
|
||||
Q_ASSERT(result);
|
||||
_isConnected = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptSignalQtProxy::disconnect(QScriptValue arg0, QScriptValue arg1) {
|
||||
QObject* qobject = _object;
|
||||
if (!qobject) {
|
||||
QScriptContext* currentContext = static_cast<QScriptEngine*>(_engine)->currentContext();
|
||||
currentContext->throwError(QScriptContext::ReferenceError, "Referencing deleted native object");
|
||||
return;
|
||||
}
|
||||
|
||||
// untangle the arguments
|
||||
QScriptValue callback;
|
||||
QScriptValue callbackThis;
|
||||
if (arg1.isFunction()) {
|
||||
callbackThis = arg0;
|
||||
callback = arg1;
|
||||
} else {
|
||||
callback = arg0;
|
||||
}
|
||||
if (!callback.isFunction()) {
|
||||
QScriptContext* currentContext = static_cast<QScriptEngine*>(_engine)->currentContext();
|
||||
currentContext->throwError(QScriptContext::TypeError, "Function expected as argument to 'disconnect'");
|
||||
return;
|
||||
}
|
||||
|
||||
// locate this connection in our list of connections
|
||||
{
|
||||
ConnectionList::iterator lookup = findConnection(callbackThis, callback);
|
||||
if (lookup == _connections.end()) {
|
||||
return; // not here
|
||||
}
|
||||
|
||||
// remove it from our internal list of connections
|
||||
withWriteLock([&]{
|
||||
_connections.erase(lookup);
|
||||
});
|
||||
}
|
||||
|
||||
// remove a reference to ourselves from the destination callback
|
||||
QScriptValue destData = callback.data();
|
||||
Q_ASSERT(destData.isArray());
|
||||
if (destData.isArray()) {
|
||||
QScriptValue qThis = thisObject();
|
||||
int len = destData.property("length").toInteger();
|
||||
bool foundIt = false;
|
||||
for (int idx = 0; idx < len && !foundIt; ++idx) {
|
||||
QScriptValue entry = destData.property(idx);
|
||||
if (entry.strictlyEquals(qThis)) {
|
||||
foundIt = true;
|
||||
QScriptValueList args;
|
||||
args << idx << 1;
|
||||
destData.property("splice").call(destData, args);
|
||||
}
|
||||
}
|
||||
Q_ASSERT(foundIt);
|
||||
}
|
||||
|
||||
// inform Qt that we're no longer connected to this signal
|
||||
if (_connections.empty()) {
|
||||
Q_ASSERT(_isConnected);
|
||||
bool result = QMetaObject::disconnect(qobject, _meta.methodIndex(), this, _metaCallId);
|
||||
Q_ASSERT(result);
|
||||
_isConnected = false;
|
||||
}
|
||||
}
|
|
@ -1,213 +0,0 @@
|
|||
//
|
||||
// ScriptObjectQtProxy.h
|
||||
// libraries/script-engine/src/qtscript
|
||||
//
|
||||
// Created by Heather Anderson on 12/5/21.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
// Copyright 2022 Overte e.V.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
/// @addtogroup ScriptEngine
|
||||
/// @{
|
||||
|
||||
#ifndef hifi_ScriptObjectQtProxy_h
|
||||
#define hifi_ScriptObjectQtProxy_h
|
||||
|
||||
#include <QtCore/QHash>
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtCore/QString>
|
||||
|
||||
#include <QtScript/QScriptable>
|
||||
#include <QtScript/QScriptClass>
|
||||
#include <QtScript/QScriptString>
|
||||
#include <QtScript/QScriptValue>
|
||||
|
||||
#include "../ScriptEngine.h"
|
||||
#include "ScriptEngineQtScript.h"
|
||||
|
||||
#include <shared/ReadWriteLockable.h>
|
||||
|
||||
class ScriptEngineQtScript;
|
||||
class ScriptSignalQtProxy;
|
||||
|
||||
/// [QtScript] (re-)implements the translation layer between ScriptValue and QObject. This object
|
||||
/// will focus exclusively on property get/set until function calls appear to be a problem
|
||||
class ScriptObjectQtProxy final : public QScriptClass {
|
||||
private: // implementation
|
||||
struct PropertyDef {
|
||||
QScriptString name;
|
||||
QScriptValue::PropertyFlags flags;
|
||||
};
|
||||
struct MethodDef {
|
||||
QScriptString name;
|
||||
int numMaxParms;
|
||||
QList<QMetaMethod> methods;
|
||||
};
|
||||
struct SignalDef {
|
||||
QScriptString name;
|
||||
QMetaMethod signal;
|
||||
};
|
||||
using PropertyDefMap = QHash<uint, PropertyDef>;
|
||||
using MethodDefMap = QHash<uint, MethodDef>;
|
||||
using SignalDefMap = QHash<uint, SignalDef>;
|
||||
using InstanceMap = QHash<uint, QPointer<ScriptSignalQtProxy> >;
|
||||
|
||||
static constexpr uint PROPERTY_TYPE = 0x1000;
|
||||
static constexpr uint METHOD_TYPE = 0x2000;
|
||||
static constexpr uint SIGNAL_TYPE = 0x3000;
|
||||
static constexpr uint TYPE_MASK = 0xF000;
|
||||
|
||||
public: // construction
|
||||
inline ScriptObjectQtProxy(ScriptEngineQtScript* engine, QObject* object, bool ownsObject, const ScriptEngine::QObjectWrapOptions& options) :
|
||||
QScriptClass(engine), _engine(engine), _wrapOptions(options), _ownsObject(ownsObject), _object(object) {
|
||||
investigate();
|
||||
}
|
||||
virtual ~ScriptObjectQtProxy();
|
||||
|
||||
static QScriptValue newQObject(ScriptEngineQtScript* engine,
|
||||
QObject* object,
|
||||
ScriptEngine::ValueOwnership ownership = ScriptEngine::QtOwnership,
|
||||
const ScriptEngine::QObjectWrapOptions& options = ScriptEngine::QObjectWrapOptions());
|
||||
static ScriptObjectQtProxy* unwrapProxy(const QScriptValue& val);
|
||||
static QObject* unwrap(const QScriptValue& val);
|
||||
inline QObject* toQtValue() const { return _object; }
|
||||
|
||||
public: // QScriptClass implementation
|
||||
virtual QString name() const override;
|
||||
|
||||
virtual QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override;
|
||||
virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, const QScriptString& name, uint id) override;
|
||||
virtual QueryFlags queryProperty(const QScriptValue& object, const QScriptString& name, QueryFlags flags, uint* id) override;
|
||||
virtual void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override;
|
||||
|
||||
private: // implementation
|
||||
void investigate();
|
||||
|
||||
private: // storage
|
||||
ScriptEngineQtScript* _engine;
|
||||
const ScriptEngine::QObjectWrapOptions _wrapOptions;
|
||||
PropertyDefMap _props;
|
||||
MethodDefMap _methods;
|
||||
SignalDefMap _signals;
|
||||
InstanceMap _signalInstances;
|
||||
const bool _ownsObject;
|
||||
QPointer<QObject> _object;
|
||||
|
||||
Q_DISABLE_COPY(ScriptObjectQtProxy)
|
||||
};
|
||||
|
||||
/// [QtScript] (re-)implements the translation layer between ScriptValue and QVariant where a prototype is set.
|
||||
/// This object depends on a ScriptObjectQtProxy to provide the prototype's behavior
|
||||
class ScriptVariantQtProxy final : public QScriptClass {
|
||||
public: // construction
|
||||
ScriptVariantQtProxy(ScriptEngineQtScript* engine, const QVariant& variant, QScriptValue scriptProto, ScriptObjectQtProxy* proto);
|
||||
|
||||
static QScriptValue newVariant(ScriptEngineQtScript* engine, const QVariant& variant, QScriptValue proto);
|
||||
static ScriptVariantQtProxy* unwrapProxy(const QScriptValue& val);
|
||||
static QVariant unwrap(const QScriptValue& val);
|
||||
inline QVariant toQtValue() const { return _variant; }
|
||||
|
||||
public: // QScriptClass implementation
|
||||
virtual QString name() const override { return _name; }
|
||||
|
||||
virtual QScriptValue prototype() const override { return _scriptProto; }
|
||||
|
||||
virtual QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override {
|
||||
return _proto->property(object, name, id);
|
||||
}
|
||||
virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, const QScriptString& name, uint id) override {
|
||||
return _proto->propertyFlags(object, name, id);
|
||||
}
|
||||
virtual QueryFlags queryProperty(const QScriptValue& object, const QScriptString& name, QueryFlags flags, uint* id) override {
|
||||
return _proto->queryProperty(object, name, flags, id);
|
||||
}
|
||||
virtual void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override {
|
||||
return _proto->setProperty(object, name, id, value);
|
||||
}
|
||||
|
||||
private: // storage
|
||||
ScriptEngineQtScript* _engine;
|
||||
QVariant _variant;
|
||||
QScriptValue _scriptProto;
|
||||
ScriptObjectQtProxy* _proto;
|
||||
QString _name;
|
||||
|
||||
Q_DISABLE_COPY(ScriptVariantQtProxy)
|
||||
};
|
||||
|
||||
class ScriptMethodQtProxy final : public QScriptClass {
|
||||
public: // construction
|
||||
inline ScriptMethodQtProxy(ScriptEngineQtScript* engine, QObject* object, QScriptValue lifetime,
|
||||
const QList<QMetaMethod>& metas, int numMaxParms) :
|
||||
QScriptClass(engine),
|
||||
_numMaxParms(numMaxParms), _engine(engine), _object(object), _objectLifetime(lifetime), _metas(metas) {}
|
||||
|
||||
public: // QScriptClass implementation
|
||||
virtual QString name() const override { return fullName(); }
|
||||
virtual bool supportsExtension(Extension extension) const override;
|
||||
virtual QVariant extension(Extension extension, const QVariant& argument = QVariant()) override;
|
||||
|
||||
private:
|
||||
QString fullName() const;
|
||||
|
||||
private: // storage
|
||||
const int _numMaxParms;
|
||||
ScriptEngineQtScript* _engine;
|
||||
QPointer<QObject> _object;
|
||||
QScriptValue _objectLifetime;
|
||||
const QList<QMetaMethod> _metas;
|
||||
|
||||
Q_DISABLE_COPY(ScriptMethodQtProxy)
|
||||
};
|
||||
|
||||
// This abstract base class serves solely to declare the Q_INVOKABLE methods for ScriptSignalQtProxy
|
||||
// as we're overriding qt_metacall later for the signal callback yet still want to support
|
||||
// metacalls for the connect/disconnect API
|
||||
class ScriptSignalQtProxyBase : public QObject, protected QScriptable {
|
||||
Q_OBJECT
|
||||
public: // API
|
||||
Q_INVOKABLE virtual void connect(QScriptValue arg0, QScriptValue arg1 = QScriptValue()) = 0;
|
||||
Q_INVOKABLE virtual void disconnect(QScriptValue arg0, QScriptValue arg1 = QScriptValue()) = 0;
|
||||
};
|
||||
|
||||
class ScriptSignalQtProxy final : public ScriptSignalQtProxyBase, public ReadWriteLockable {
|
||||
private: // storage
|
||||
struct Connection {
|
||||
QScriptValue thisValue;
|
||||
QScriptValue callback;
|
||||
};
|
||||
using ConnectionList = QList<Connection>;
|
||||
|
||||
public: // construction
|
||||
inline ScriptSignalQtProxy(ScriptEngineQtScript* engine, QObject* object, QScriptValue lifetime, const QMetaMethod& meta) :
|
||||
_engine(engine), _object(object), _objectLifetime(lifetime), _meta(meta), _metaCallId(discoverMetaCallIdx()) {}
|
||||
|
||||
private: // implementation
|
||||
virtual int qt_metacall(QMetaObject::Call call, int id, void** arguments) override;
|
||||
int discoverMetaCallIdx();
|
||||
ConnectionList::iterator findConnection(QScriptValue thisObject, QScriptValue callback);
|
||||
QString fullName() const;
|
||||
|
||||
public: // API
|
||||
virtual void connect(QScriptValue arg0, QScriptValue arg1 = QScriptValue()) override;
|
||||
virtual void disconnect(QScriptValue arg0, QScriptValue arg1 = QScriptValue()) override;
|
||||
|
||||
private: // storage
|
||||
ScriptEngineQtScript* _engine;
|
||||
QPointer<QObject> _object;
|
||||
QScriptValue _objectLifetime;
|
||||
const QMetaMethod _meta;
|
||||
const int _metaCallId;
|
||||
ConnectionList _connections;
|
||||
bool _isConnected{ false };
|
||||
|
||||
Q_DISABLE_COPY(ScriptSignalQtProxy)
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptObjectQtProxy_h
|
||||
|
||||
/// @}
|
|
@ -1,55 +0,0 @@
|
|||
//
|
||||
// ScriptProgramQtWrapper.cpp
|
||||
// libraries/script-engine/src/qtscript
|
||||
//
|
||||
// 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 std::make_shared<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());
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
//
|
||||
// ScriptProgramQtWrapper.h
|
||||
// libraries/script-engine/src/qtscript
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
/// @addtogroup ScriptEngine
|
||||
/// @{
|
||||
|
||||
#ifndef hifi_ScriptProgramQtWrapper_h
|
||||
#define hifi_ScriptProgramQtWrapper_h
|
||||
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtScript/QScriptProgram>
|
||||
|
||||
#include "../ScriptProgram.h"
|
||||
#include "ScriptEngineQtScript.h"
|
||||
|
||||
/// [QtScript] Implements ScriptProgram for QtScript and translates calls for QScriptProgram
|
||||
class ScriptProgramQtWrapper final : 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 override;
|
||||
virtual QString fileName() const override;
|
||||
virtual QString sourceCode() const override;
|
||||
|
||||
private: // storage
|
||||
QPointer<ScriptEngineQtScript> _engine;
|
||||
QScriptProgram _value;
|
||||
};
|
||||
|
||||
class ScriptSyntaxCheckResultQtWrapper final : public ScriptSyntaxCheckResult {
|
||||
public: // construction
|
||||
inline ScriptSyntaxCheckResultQtWrapper(QScriptSyntaxCheckResult&& value) :
|
||||
_value(std::move(value)) {}
|
||||
|
||||
public: // ScriptSyntaxCheckResult implementation
|
||||
virtual int errorColumnNumber() const override;
|
||||
virtual int errorLineNumber() const override;
|
||||
virtual QString errorMessage() const override;
|
||||
virtual State state() const override;
|
||||
|
||||
private: // storage
|
||||
QScriptSyntaxCheckResult _value;
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptValueQtWrapper_h
|
||||
|
||||
/// @}
|
|
@ -1,33 +0,0 @@
|
|||
//
|
||||
// ScriptValueIteratorQtWrapper.cpp
|
||||
// libraries/script-engine/src/qtscript
|
||||
//
|
||||
// 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();
|
||||
}
|
||||
|
||||
ScriptValue ScriptValueIteratorQtWrapper::value() const {
|
||||
QScriptValue result = _value.value();
|
||||
return ScriptValue(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
//
|
||||
// ScriptValueIteratorQtWrapper.h
|
||||
// libraries/script-engine/src/qtscript
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
/// @addtogroup ScriptEngine
|
||||
/// @{
|
||||
|
||||
#ifndef hifi_ScriptValueIteratorQtWrapper_h
|
||||
#define hifi_ScriptValueIteratorQtWrapper_h
|
||||
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtScript/QScriptValueIterator>
|
||||
|
||||
#include "../ScriptValueIterator.h"
|
||||
#include "ScriptEngineQtScript.h"
|
||||
#include "ScriptValueQtWrapper.h"
|
||||
|
||||
/// [QtScript] Implements ScriptValueIterator for QtScript and translates calls for QScriptValueIterator
|
||||
class ScriptValueIteratorQtWrapper final : public ScriptValueIterator {
|
||||
public: // construction
|
||||
inline ScriptValueIteratorQtWrapper(ScriptEngineQtScript* engine, const ScriptValue& 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 override;
|
||||
virtual bool hasNext() const override;
|
||||
virtual QString name() const override;
|
||||
virtual void next() override;
|
||||
virtual ScriptValue value() const override;
|
||||
|
||||
private: // storage
|
||||
QPointer<ScriptEngineQtScript> _engine;
|
||||
QScriptValueIterator _value;
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptValueIteratorQtWrapper_h
|
||||
|
||||
/// @}
|
|
@ -1,234 +0,0 @@
|
|||
//
|
||||
// ScriptValueQtWrapper.cpp
|
||||
// libraries/script-engine/src/qtscript
|
||||
//
|
||||
// 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"
|
||||
|
||||
void ScriptValueQtWrapper::release() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
ScriptValueProxy* ScriptValueQtWrapper::copy() const {
|
||||
return new ScriptValueQtWrapper(_engine, _value);
|
||||
}
|
||||
|
||||
ScriptValueQtWrapper* ScriptValueQtWrapper::unwrap(const ScriptValue& val) {
|
||||
return dynamic_cast<ScriptValueQtWrapper*>(val.ptr());
|
||||
}
|
||||
|
||||
QScriptValue ScriptValueQtWrapper::fullUnwrap(const ScriptValue& value) const {
|
||||
ScriptValueQtWrapper* unwrapped = unwrap(value);
|
||||
if (unwrapped) {
|
||||
if (unwrapped->engine().get() != _engine) {
|
||||
return static_cast<QScriptEngine*>(_engine)->toScriptValue(unwrapped->toVariant());
|
||||
} else {
|
||||
return unwrapped->toQtValue();
|
||||
}
|
||||
}
|
||||
QVariant varValue = value.toVariant();
|
||||
return _engine->castVariantToValue(varValue);
|
||||
}
|
||||
|
||||
QScriptValue ScriptValueQtWrapper::fullUnwrap(ScriptEngineQtScript* engine, const ScriptValue& value) {
|
||||
ScriptValueQtWrapper* unwrapped = unwrap(value);
|
||||
if (unwrapped) {
|
||||
if (unwrapped->engine().get() != engine) {
|
||||
return static_cast<QScriptEngine*>(engine)->toScriptValue(unwrapped->toVariant());
|
||||
} else {
|
||||
return unwrapped->toQtValue();
|
||||
}
|
||||
}
|
||||
QVariant varValue = value.toVariant();
|
||||
return engine->castVariantToValue(varValue);
|
||||
}
|
||||
|
||||
ScriptValue ScriptValueQtWrapper::call(const ScriptValue& 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 ScriptValue(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValue ScriptValueQtWrapper::call(const ScriptValue& thisObject, const ScriptValue& arguments) {
|
||||
QScriptValue qThis = fullUnwrap(thisObject);
|
||||
QScriptValue qArgs = fullUnwrap(arguments);
|
||||
QScriptValue result = _value.call(qThis, qArgs);
|
||||
return ScriptValue(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValue 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 ScriptValue(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValue ScriptValueQtWrapper::construct(const ScriptValue& arguments) {
|
||||
QScriptValue unwrapped = fullUnwrap(arguments);
|
||||
QScriptValue result = _value.construct(unwrapped);
|
||||
return ScriptValue(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValue ScriptValueQtWrapper::data() const {
|
||||
QScriptValue result = _value.data();
|
||||
return ScriptValue(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptEnginePointer ScriptValueQtWrapper::engine() const {
|
||||
if (!_engine) {
|
||||
return ScriptEnginePointer();
|
||||
}
|
||||
return _engine->shared_from_this();
|
||||
}
|
||||
|
||||
ScriptValueIteratorPointer ScriptValueQtWrapper::newIterator() const {
|
||||
return std::make_shared<ScriptValueIteratorQtWrapper>(_engine, _value);
|
||||
}
|
||||
|
||||
ScriptValue ScriptValueQtWrapper::property(const QString& name, const ScriptValue::ResolveFlags& mode) const {
|
||||
QScriptValue result = _value.property(name, (QScriptValue::ResolveFlags)(int)mode);
|
||||
return ScriptValue(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValue ScriptValueQtWrapper::property(quint32 arrayIndex, const ScriptValue::ResolveFlags& mode) const {
|
||||
QScriptValue result = _value.property(arrayIndex, (QScriptValue::ResolveFlags)(int)mode);
|
||||
return ScriptValue(new ScriptValueQtWrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
void ScriptValueQtWrapper::setData(const ScriptValue& value) {
|
||||
QScriptValue unwrapped = fullUnwrap(value);
|
||||
_value.setData(unwrapped);
|
||||
}
|
||||
|
||||
void ScriptValueQtWrapper::setProperty(const QString& name, const ScriptValue& value, const ScriptValue::PropertyFlags& flags) {
|
||||
QScriptValue unwrapped = fullUnwrap(value);
|
||||
_value.setProperty(name, unwrapped, (QScriptValue::PropertyFlags)(int)flags);
|
||||
}
|
||||
|
||||
void ScriptValueQtWrapper::setProperty(quint32 arrayIndex, const ScriptValue& value, const ScriptValue::PropertyFlags& flags) {
|
||||
QScriptValue unwrapped = fullUnwrap(value);
|
||||
_value.setProperty(arrayIndex, unwrapped, (QScriptValue::PropertyFlags)(int)flags);
|
||||
}
|
||||
|
||||
void ScriptValueQtWrapper::setPrototype(const ScriptValue& prototype) {
|
||||
ScriptValueQtWrapper* unwrappedPrototype = unwrap(prototype);
|
||||
if (unwrappedPrototype) {
|
||||
_value.setPrototype(unwrappedPrototype->toQtValue());
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::strictlyEquals(const ScriptValue& 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 {
|
||||
QVariant dest;
|
||||
if (_engine->castValueToVariant(_value, dest, QMetaType::UnknownType)) {
|
||||
return dest;
|
||||
} else {
|
||||
Q_ASSERT(false);
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
QObject* ScriptValueQtWrapper::toQObject() const {
|
||||
QVariant dest;
|
||||
if (_engine->castValueToVariant(_value, dest, QMetaType::QObjectStar)) {
|
||||
return dest.value<QObject*>();
|
||||
} else {
|
||||
Q_ASSERT(false);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::equals(const ScriptValue& other) const {
|
||||
ScriptValueQtWrapper* unwrappedOther = unwrap(other);
|
||||
return unwrappedOther ? _value.equals(unwrappedOther->toQtValue()) : false;
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::isArray() const {
|
||||
return _value.isArray();
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::isBool() const {
|
||||
return _value.isBool();
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::isError() const {
|
||||
return _value.isError();
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::isFunction() const {
|
||||
return _value.isFunction();
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::isNumber() const {
|
||||
return _value.isNumber();
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::isNull() const {
|
||||
return _value.isNull();
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::isObject() const {
|
||||
return _value.isObject();
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::isString() const {
|
||||
return _value.isString();
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::isUndefined() const {
|
||||
return _value.isUndefined();
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::isValid() const {
|
||||
return _value.isValid();
|
||||
}
|
||||
|
||||
bool ScriptValueQtWrapper::isVariant() const {
|
||||
return _value.isVariant();
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
//
|
||||
// TypedArrays.h
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/9/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
|
||||
//
|
||||
|
||||
/// @addtogroup ScriptEngine
|
||||
/// @{
|
||||
|
||||
#ifndef hifi_TypedArrays_h
|
||||
#define hifi_TypedArrays_h
|
||||
|
||||
#include "ArrayBufferViewClass.h"
|
||||
|
||||
static const QString BYTES_PER_ELEMENT_PROPERTY_NAME = "BYTES_PER_ELEMENT";
|
||||
static const QString LENGTH_PROPERTY_NAME = "length";
|
||||
|
||||
static const QString INT_8_ARRAY_CLASS_NAME = "Int8Array";
|
||||
static const QString UINT_8_ARRAY_CLASS_NAME = "Uint8Array";
|
||||
static const QString UINT_8_CLAMPED_ARRAY_CLASS_NAME = "Uint8ClampedArray";
|
||||
static const QString INT_16_ARRAY_CLASS_NAME = "Int16Array";
|
||||
static const QString UINT_16_ARRAY_CLASS_NAME = "Uint16Array";
|
||||
static const QString INT_32_ARRAY_CLASS_NAME = "Int32Array";
|
||||
static const QString UINT_32_ARRAY_CLASS_NAME = "Uint32Array";
|
||||
static const QString FLOAT_32_ARRAY_CLASS_NAME = "Float32Array";
|
||||
static const QString FLOAT_64_ARRAY_CLASS_NAME = "Float64Array";
|
||||
|
||||
/// [QtScript] Implements the <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray">TypedArray</a></code> scripting class
|
||||
class TypedArray : public ArrayBufferViewClass {
|
||||
Q_OBJECT
|
||||
public:
|
||||
TypedArray(ScriptEngineQtScript* scriptEngine, QString name);
|
||||
virtual QScriptValue newInstance(quint32 length);
|
||||
virtual QScriptValue newInstance(QScriptValue array);
|
||||
virtual QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length);
|
||||
|
||||
virtual QueryFlags queryProperty(const QScriptValue& object,
|
||||
const QScriptString& name,
|
||||
QueryFlags flags, uint* id) override;
|
||||
virtual QScriptValue property(const QScriptValue& object,
|
||||
const QScriptString& name, uint id) override;
|
||||
virtual void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override = 0;
|
||||
virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object,
|
||||
const QScriptString& name, uint id) override;
|
||||
|
||||
QString name() const override;
|
||||
QScriptValue prototype() const override;
|
||||
|
||||
protected:
|
||||
static QScriptValue construct(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
void setBytesPerElement(quint32 bytesPerElement);
|
||||
|
||||
QScriptValue _proto;
|
||||
QScriptValue _ctor;
|
||||
|
||||
QScriptString _name;
|
||||
QScriptString _bytesPerElementName;
|
||||
QScriptString _lengthName;
|
||||
|
||||
quint32 _bytesPerElement;
|
||||
|
||||
friend class TypedArrayPrototype;
|
||||
};
|
||||
|
||||
class Int8ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Int8ArrayClass(ScriptEngineQtScript* scriptEngine);
|
||||
|
||||
QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override;
|
||||
void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override;
|
||||
};
|
||||
|
||||
class Uint8ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Uint8ArrayClass(ScriptEngineQtScript* scriptEngine);
|
||||
|
||||
QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override;
|
||||
void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override;
|
||||
};
|
||||
|
||||
class Uint8ClampedArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Uint8ClampedArrayClass(ScriptEngineQtScript* scriptEngine);
|
||||
|
||||
QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override;
|
||||
void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override;
|
||||
};
|
||||
|
||||
class Int16ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Int16ArrayClass(ScriptEngineQtScript* scriptEngine);
|
||||
|
||||
QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override;
|
||||
void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override;
|
||||
};
|
||||
|
||||
class Uint16ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Uint16ArrayClass(ScriptEngineQtScript* scriptEngine);
|
||||
|
||||
QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override;
|
||||
void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override;
|
||||
};
|
||||
|
||||
class Int32ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Int32ArrayClass(ScriptEngineQtScript* scriptEngine);
|
||||
|
||||
QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override;
|
||||
void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override;
|
||||
};
|
||||
|
||||
class Uint32ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Uint32ArrayClass(ScriptEngineQtScript* scriptEngine);
|
||||
|
||||
QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override;
|
||||
void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override;
|
||||
};
|
||||
|
||||
class Float32ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Float32ArrayClass(ScriptEngineQtScript* scriptEngine);
|
||||
|
||||
QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override;
|
||||
void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override;
|
||||
};
|
||||
|
||||
class Float64ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Float64ArrayClass(ScriptEngineQtScript* scriptEngine);
|
||||
|
||||
QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override;
|
||||
void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override;
|
||||
};
|
||||
|
||||
#endif // hifi_TypedArrays_h
|
||||
|
||||
/// @}
|
|
@ -15,21 +15,22 @@
|
|||
|
||||
#include "ArrayBufferPrototype.h"
|
||||
#include "DataViewClass.h"
|
||||
#include "ScriptEngineQtScript.h"
|
||||
#include "ScriptEngineV8.h"
|
||||
#include "TypedArrays.h"
|
||||
|
||||
|
||||
static const QString CLASS_NAME = "ArrayBuffer";
|
||||
// V8TODO
|
||||
/*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(QByteArray*)
|
||||
|
||||
ArrayBufferClass::ArrayBufferClass(ScriptEngineQtScript* scriptEngine) :
|
||||
ArrayBufferClass::ArrayBufferClass(ScriptEngineV8* scriptEngine) :
|
||||
QObject(scriptEngine),
|
||||
QScriptClass(scriptEngine) {
|
||||
qScriptRegisterMetaType<QByteArray>(engine(), toScriptValue, fromScriptValue);
|
||||
QScriptValue global = engine()->globalObject();
|
||||
V8ScriptValue global = engine()->globalObject();
|
||||
|
||||
// Save string handles for quick lookup
|
||||
_name = engine()->toStringHandle(CLASS_NAME.toLatin1());
|
||||
|
@ -63,15 +64,15 @@ QScriptClass(scriptEngine) {
|
|||
new Float64ArrayClass(scriptEngine);
|
||||
}
|
||||
|
||||
QScriptValue ArrayBufferClass::newInstance(qint32 size) {
|
||||
V8ScriptValue ArrayBufferClass::newInstance(qint32 size) {
|
||||
const qint32 MAX_LENGTH = 100000000;
|
||||
if (size < 0) {
|
||||
engine()->evaluate("throw \"ArgumentError: negative length\"");
|
||||
return QScriptValue();
|
||||
return V8ScriptValue();
|
||||
}
|
||||
if (size > MAX_LENGTH) {
|
||||
engine()->evaluate("throw \"ArgumentError: absurd length\"");
|
||||
return QScriptValue();
|
||||
return V8ScriptValue();
|
||||
}
|
||||
// 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)
|
||||
|
@ -79,29 +80,29 @@ QScriptValue ArrayBufferClass::newInstance(qint32 size) {
|
|||
#endif
|
||||
QScriptEngine* eng = engine();
|
||||
QVariant variant = QVariant::fromValue(QByteArray(size, 0));
|
||||
QScriptValue data = eng->newVariant(variant);
|
||||
V8ScriptValue data = eng->newVariant(variant);
|
||||
return engine()->newObject(this, data);
|
||||
}
|
||||
|
||||
QScriptValue ArrayBufferClass::newInstance(const QByteArray& ba) {
|
||||
V8ScriptValue ArrayBufferClass::newInstance(const QByteArray& ba) {
|
||||
QScriptEngine* eng = engine();
|
||||
QScriptValue data = eng->newVariant(QVariant::fromValue(ba));
|
||||
V8ScriptValue data = eng->newVariant(QVariant::fromValue(ba));
|
||||
return eng->newObject(this, data);
|
||||
}
|
||||
|
||||
QScriptValue ArrayBufferClass::construct(QScriptContext* context, QScriptEngine* engine) {
|
||||
V8ScriptValue ArrayBufferClass::construct(V8ScriptContext* 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();
|
||||
return V8ScriptValue();
|
||||
}
|
||||
QScriptValue arg = context->argument(0);
|
||||
V8ScriptValue arg = context->argument(0);
|
||||
if (!arg.isValid() || !arg.isNumber()) {
|
||||
return QScriptValue();
|
||||
return V8ScriptValue();
|
||||
}
|
||||
|
||||
quint32 size = arg.toInt32();
|
||||
QScriptValue newObject = cls->newInstance(size);
|
||||
V8ScriptValue newObject = cls->newInstance(size);
|
||||
|
||||
if (context->isCalledAsConstructor()) {
|
||||
// if called with keyword new, replace this object.
|
||||
|
@ -112,59 +113,59 @@ QScriptValue ArrayBufferClass::construct(QScriptContext* context, QScriptEngine*
|
|||
return newObject;
|
||||
}
|
||||
|
||||
QScriptClass::QueryFlags ArrayBufferClass::queryProperty(const QScriptValue& object,
|
||||
const QScriptString& name,
|
||||
ScriptObjectV8Proxy::QueryFlags ArrayBufferClass::queryProperty(const V8ScriptValue& object,
|
||||
const V8ScriptString& 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 QScriptClass::QueryFlags(); // No access
|
||||
return ScriptObjectV8Proxy::QueryFlags(); // No access
|
||||
}
|
||||
|
||||
QScriptValue ArrayBufferClass::property(const QScriptValue& object,
|
||||
const QScriptString& name, uint id) {
|
||||
V8ScriptValue ArrayBufferClass::property(const V8ScriptValue& object,
|
||||
const V8ScriptString& name, uint id) {
|
||||
QByteArray* ba = qscriptvalue_cast<QByteArray*>(object.data());
|
||||
if (ba && name == _byteLength) {
|
||||
return ba->length();
|
||||
}
|
||||
return QScriptValue();
|
||||
return V8ScriptValue();
|
||||
}
|
||||
|
||||
QScriptValue::PropertyFlags ArrayBufferClass::propertyFlags(const QScriptValue& object,
|
||||
const QScriptString& name, uint id) {
|
||||
return QScriptValue::Undeletable;
|
||||
V8ScriptValue::PropertyFlags ArrayBufferClass::propertyFlags(const V8ScriptValue& object,
|
||||
const V8ScriptString& name, uint id) {
|
||||
return V8ScriptValue::Undeletable;
|
||||
}
|
||||
|
||||
QString ArrayBufferClass::name() const {
|
||||
return _name.toString();
|
||||
}
|
||||
|
||||
QScriptValue ArrayBufferClass::prototype() const {
|
||||
V8ScriptValue ArrayBufferClass::prototype() const {
|
||||
return _proto;
|
||||
}
|
||||
|
||||
QScriptValue ArrayBufferClass::toScriptValue(QScriptEngine* engine, const QByteArray& ba) {
|
||||
QScriptValue ctor = engine->globalObject().property(CLASS_NAME);
|
||||
V8ScriptValue ArrayBufferClass::toScriptValue(QScriptEngine* engine, const QByteArray& ba) {
|
||||
V8ScriptValue 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 V8ScriptValue::NullValue;
|
||||
}
|
||||
return cls->newInstance(ba);
|
||||
}
|
||||
|
||||
void ArrayBufferClass::fromScriptValue(const QScriptValue& object, QByteArray& byteArray) {
|
||||
void ArrayBufferClass::fromScriptValue(const V8ScriptValue& 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});
|
||||
auto typedArray = Uint8Array.construct(V8ScriptValueList{object});
|
||||
if (QByteArray* buffer = qscriptvalue_cast<QByteArray*>(typedArray.property("buffer"))) {
|
||||
byteArray = *buffer;
|
||||
}
|
||||
|
@ -175,4 +176,4 @@ void ArrayBufferClass::fromScriptValue(const QScriptValue& object, QByteArray& b
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
66
libraries/script-engine/src/v8/ArrayBufferClass.h
Normal file
66
libraries/script-engine/src/v8/ArrayBufferClass.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
//
|
||||
// ArrayBufferClass.h
|
||||
//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
/// @addtogroup ScriptEngine
|
||||
/// @{
|
||||
|
||||
#ifndef hifi_ArrayBufferClass_h
|
||||
#define hifi_ArrayBufferClass_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include "libplatform/libplatform.h"
|
||||
#include "v8.h"
|
||||
#include <QtCore/QDateTime>
|
||||
|
||||
#include "V8Types.h"
|
||||
// V8TODO
|
||||
/*
|
||||
class ScriptEngineV8;
|
||||
|
||||
/// [V8] Implements the <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer">ArrayBuffer</a></code> scripting class
|
||||
class ArrayBufferClass : public QObject, public ScriptClass {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ArrayBufferClass(ScriptEngineV8* scriptEngine);
|
||||
V8ScriptValue newInstance(qint32 size);
|
||||
V8ScriptValue newInstance(const QByteArray& ba);
|
||||
|
||||
QueryFlags queryProperty(const V8ScriptValue& object,
|
||||
const V8ScriptString& name,
|
||||
QueryFlags flags, uint* id) override;
|
||||
V8ScriptValue property(const V8ScriptValue& object,
|
||||
const V8ScriptString& name, uint id) override;
|
||||
V8ScriptValue::PropertyFlags propertyFlags(const V8ScriptValue& object,
|
||||
const V8ScriptString& name, uint id) override;
|
||||
|
||||
QString name() const override;
|
||||
V8ScriptValue prototype() const override;
|
||||
|
||||
|
||||
private:
|
||||
static V8ScriptValue construct(V8ScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
static V8ScriptValue toScriptValue(QScriptEngine* eng, const QByteArray& ba);
|
||||
static void fromScriptValue(const V8ScriptValue& obj, QByteArray& ba);
|
||||
|
||||
V8ScriptValue _proto;
|
||||
V8ScriptValue _ctor;
|
||||
|
||||
// JS Object attributes
|
||||
V8ScriptString _name;
|
||||
V8ScriptString _byteLength;
|
||||
|
||||
};
|
||||
|
||||
*/
|
||||
#endif // hifi_ArrayBufferClass_h
|
||||
|
||||
/// @}
|
|
@ -15,12 +15,14 @@
|
|||
|
||||
#include <QtCore/QBuffer>
|
||||
#include <QtGui/QImage>
|
||||
#include <QtScript/QScriptEngine>
|
||||
#include "libplatform/libplatform.h"
|
||||
#include "v8.h"
|
||||
|
||||
static const int QCOMPRESS_HEADER_POSITION = 0;
|
||||
static const int QCOMPRESS_HEADER_SIZE = 4;
|
||||
|
||||
Q_DECLARE_METATYPE(QByteArray*)
|
||||
// V8TODO
|
||||
/*Q_DECLARE_METATYPE(QByteArray*)
|
||||
|
||||
ArrayBufferPrototype::ArrayBufferPrototype(QObject* parent) : QObject(parent) {
|
||||
}
|
||||
|
@ -86,4 +88,4 @@ QByteArray ArrayBufferPrototype::recodeImage(const QString& sourceFormat, const
|
|||
|
||||
QByteArray* ArrayBufferPrototype::thisArrayBuffer() const {
|
||||
return qscriptvalue_cast<QByteArray*>(thisObject().data());
|
||||
}
|
||||
}*/
|
|
@ -15,11 +15,13 @@
|
|||
#ifndef hifi_ArrayBufferPrototype_h
|
||||
#define hifi_ArrayBufferPrototype_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtScript/QScriptable>
|
||||
// V8TODO
|
||||
/*#include <QtCore/QObject>
|
||||
|
||||
/// [QtScript] The javascript functions associated with an <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer">ArrayBuffer</a></code> instance prototype
|
||||
class ArrayBufferPrototype : public QObject, public QScriptable {
|
||||
#include "../Scriptable.h"
|
||||
|
||||
/// [V8] The javascript functions associated with an <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer">ArrayBuffer</a></code> instance prototype
|
||||
class ArrayBufferPrototype : public QObject, public Scriptable {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ArrayBufferPrototype(QObject* parent = NULL);
|
||||
|
@ -33,7 +35,7 @@ public slots:
|
|||
private:
|
||||
QByteArray* thisArrayBuffer() const;
|
||||
};
|
||||
|
||||
*/
|
||||
#endif // hifi_ArrayBufferPrototype_h
|
||||
|
||||
/// @}
|
|
@ -10,11 +10,12 @@
|
|||
//
|
||||
|
||||
#include "ArrayBufferViewClass.h"
|
||||
#include "ScriptEngineQtScript.h"
|
||||
#include "ScriptEngineV8.h"
|
||||
|
||||
Q_DECLARE_METATYPE(QByteArray*)
|
||||
|
||||
ArrayBufferViewClass::ArrayBufferViewClass(ScriptEngineQtScript* scriptEngine) :
|
||||
// V8TODO
|
||||
/*ArrayBufferViewClass::ArrayBufferViewClass(ScriptEngineV8* scriptEngine) :
|
||||
QObject(scriptEngine),
|
||||
QScriptClass(scriptEngine),
|
||||
_scriptEngine(scriptEngine)
|
||||
|
@ -25,8 +26,8 @@ ArrayBufferViewClass::ArrayBufferViewClass(ScriptEngineQtScript* scriptEngine) :
|
|||
_byteLengthName = engine()->toStringHandle(BYTE_LENGTH_PROPERTY_NAME.toLatin1());
|
||||
}
|
||||
|
||||
QScriptClass::QueryFlags ArrayBufferViewClass::queryProperty(const QScriptValue& object,
|
||||
const QScriptString& name,
|
||||
QScriptClass::QueryFlags ArrayBufferViewClass::queryProperty(const V8ScriptValue& object,
|
||||
const V8ScriptString& name,
|
||||
QueryFlags flags, uint* id) {
|
||||
if (name == _bufferName || name == _byteOffsetName || name == _byteLengthName) {
|
||||
return flags &= HandlesReadAccess; // Only keep read access flags
|
||||
|
@ -34,8 +35,8 @@ QScriptClass::QueryFlags ArrayBufferViewClass::queryProperty(const QScriptValue&
|
|||
return QScriptClass::QueryFlags(); // No access
|
||||
}
|
||||
|
||||
QScriptValue ArrayBufferViewClass::property(const QScriptValue& object,
|
||||
const QScriptString& name, uint id) {
|
||||
V8ScriptValue ArrayBufferViewClass::property(const V8ScriptValue& object,
|
||||
const V8ScriptString& name, uint id) {
|
||||
if (name == _bufferName) {
|
||||
return object.data().property(_bufferName);
|
||||
}
|
||||
|
@ -45,10 +46,10 @@ QScriptValue ArrayBufferViewClass::property(const QScriptValue& object,
|
|||
if (name == _byteLengthName) {
|
||||
return object.data().property(_byteLengthName);
|
||||
}
|
||||
return QScriptValue();
|
||||
return V8ScriptValue();
|
||||
}
|
||||
|
||||
QScriptValue::PropertyFlags ArrayBufferViewClass::propertyFlags(const QScriptValue& object,
|
||||
const QScriptString& name, uint id) {
|
||||
return QScriptValue::Undeletable;
|
||||
}
|
||||
V8ScriptValue::PropertyFlags ArrayBufferViewClass::propertyFlags(const V8ScriptValue& object,
|
||||
const V8ScriptString& name, uint id) {
|
||||
return V8ScriptValue::Undeletable;
|
||||
}*/
|
54
libraries/script-engine/src/v8/ArrayBufferViewClass.h
Normal file
54
libraries/script-engine/src/v8/ArrayBufferViewClass.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// ArrayBufferViewClass.h
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/8/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
|
||||
//
|
||||
|
||||
/// @addtogroup ScriptEngine
|
||||
/// @{
|
||||
|
||||
#ifndef hifi_ArrayBufferViewClass_h
|
||||
#define hifi_ArrayBufferViewClass_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include "V8Types.h"
|
||||
|
||||
// V8TODO
|
||||
/*class ScriptEngineV8;
|
||||
|
||||
static const QString BUFFER_PROPERTY_NAME = "buffer";
|
||||
static const QString BYTE_OFFSET_PROPERTY_NAME = "byteOffset";
|
||||
static const QString BYTE_LENGTH_PROPERTY_NAME = "byteLength";
|
||||
|
||||
/// [V8] The base class containing common code for ArrayBuffer views
|
||||
class ArrayBufferViewClass : public QObject, public QScriptClass {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ArrayBufferViewClass(ScriptEngineV8* scriptEngine);
|
||||
|
||||
ScriptEngineV8* getScriptEngine() { return _scriptEngine; }
|
||||
|
||||
virtual QueryFlags queryProperty(const V8ScriptValue& object,
|
||||
const V8ScriptString& name,
|
||||
QueryFlags flags, uint* id) override;
|
||||
virtual V8ScriptValue property(const V8ScriptValue& object,
|
||||
const V8ScriptString& name, uint id) override;
|
||||
virtual V8ScriptValue::PropertyFlags propertyFlags(const V8ScriptValue& object,
|
||||
const V8ScriptString& name, uint id) override;
|
||||
protected:
|
||||
// JS Object attributes
|
||||
V8ScriptString _bufferName;
|
||||
V8ScriptString _byteOffsetName;
|
||||
V8ScriptString _byteLengthName;
|
||||
|
||||
ScriptEngineV8* _scriptEngine;
|
||||
};
|
||||
*/
|
||||
#endif // hifi_ArrayBufferViewClass_h
|
||||
|
||||
/// @}
|
|
@ -11,14 +11,15 @@
|
|||
|
||||
#include "DataViewClass.h"
|
||||
|
||||
#include "DataViewPrototype.h"
|
||||
// V8TODO
|
||||
/*#include "DataViewPrototype.h"
|
||||
|
||||
Q_DECLARE_METATYPE(QByteArray*)
|
||||
|
||||
static const QString DATA_VIEW_NAME = "DataView";
|
||||
|
||||
DataViewClass::DataViewClass(ScriptEngineQtScript* scriptEngine) : ArrayBufferViewClass(scriptEngine) {
|
||||
QScriptValue global = engine()->globalObject();
|
||||
DataViewClass::DataViewClass(ScriptEngineV8* scriptEngine) : ArrayBufferViewClass(scriptEngine) {
|
||||
V8ScriptValue global = engine()->globalObject();
|
||||
|
||||
// Save string handles for quick lookup
|
||||
_name = engine()->toStringHandle(DATA_VIEW_NAME.toLatin1());
|
||||
|
@ -37,8 +38,8 @@ DataViewClass::DataViewClass(ScriptEngineQtScript* scriptEngine) : ArrayBufferVi
|
|||
engine()->globalObject().setProperty(name(), _ctor);
|
||||
}
|
||||
|
||||
QScriptValue DataViewClass::newInstance(QScriptValue buffer, quint32 byteOffset, quint32 byteLentgh) {
|
||||
QScriptValue data = engine()->newObject();
|
||||
V8ScriptValue DataViewClass::newInstance(V8ScriptValue buffer, quint32 byteOffset, quint32 byteLentgh) {
|
||||
V8ScriptValue data = engine()->newObject();
|
||||
data.setProperty(_bufferName, buffer);
|
||||
data.setProperty(_byteOffsetName, byteOffset);
|
||||
data.setProperty(_byteLengthName, byteLentgh);
|
||||
|
@ -46,36 +47,36 @@ QScriptValue DataViewClass::newInstance(QScriptValue buffer, quint32 byteOffset,
|
|||
return engine()->newObject(this, data);
|
||||
}
|
||||
|
||||
QScriptValue DataViewClass::construct(QScriptContext* context, QScriptEngine* engine) {
|
||||
V8ScriptValue DataViewClass::construct(V8ScriptContext* context, QScriptEngine* engine) {
|
||||
DataViewClass* cls = qscriptvalue_cast<DataViewClass*>(context->callee().data());
|
||||
if (!cls || context->argumentCount() < 1) {
|
||||
return QScriptValue();
|
||||
return V8ScriptValue();
|
||||
}
|
||||
|
||||
QScriptValue bufferArg = context->argument(0);
|
||||
QScriptValue byteOffsetArg = (context->argumentCount() >= 2) ? context->argument(1) : QScriptValue();
|
||||
QScriptValue byteLengthArg = (context->argumentCount() >= 3) ? context->argument(2) : QScriptValue();
|
||||
V8ScriptValue bufferArg = context->argument(0);
|
||||
V8ScriptValue byteOffsetArg = (context->argumentCount() >= 2) ? context->argument(1) : V8ScriptValue();
|
||||
V8ScriptValue byteLengthArg = (context->argumentCount() >= 3) ? context->argument(2) : V8ScriptValue();
|
||||
|
||||
QByteArray* arrayBuffer = (bufferArg.isValid()) ? qscriptvalue_cast<QByteArray*>(bufferArg.data()) :NULL;
|
||||
if (!arrayBuffer) {
|
||||
engine->evaluate("throw \"TypeError: 1st argument not a ArrayBuffer\"");
|
||||
return QScriptValue();
|
||||
return V8ScriptValue();
|
||||
}
|
||||
if (byteOffsetArg.isNumber() &&
|
||||
(byteOffsetArg.toInt32() < 0 ||
|
||||
byteOffsetArg.toInt32() > arrayBuffer->size())) {
|
||||
engine->evaluate("throw \"RangeError: byteOffset out of range\"");
|
||||
return QScriptValue();
|
||||
return V8ScriptValue();
|
||||
}
|
||||
if (byteLengthArg.isNumber() &&
|
||||
(byteLengthArg.toInt32() < 0 ||
|
||||
byteOffsetArg.toInt32() + byteLengthArg.toInt32() > arrayBuffer->size())) {
|
||||
engine->evaluate("throw \"RangeError: byteLength out of range\"");
|
||||
return QScriptValue();
|
||||
return V8ScriptValue();
|
||||
}
|
||||
quint32 byteOffset = (byteOffsetArg.isNumber()) ? byteOffsetArg.toInt32() : 0;
|
||||
quint32 byteLength = (byteLengthArg.isNumber()) ? byteLengthArg.toInt32() : arrayBuffer->size() - byteOffset;
|
||||
QScriptValue newObject = cls->newInstance(bufferArg, byteOffset, byteLength);
|
||||
V8ScriptValue newObject = cls->newInstance(bufferArg, byteOffset, byteLength);
|
||||
|
||||
if (context->isCalledAsConstructor()) {
|
||||
context->setThisObject(newObject);
|
||||
|
@ -89,6 +90,7 @@ QString DataViewClass::name() const {
|
|||
return _name.toString();
|
||||
}
|
||||
|
||||
QScriptValue DataViewClass::prototype() const {
|
||||
V8ScriptValue DataViewClass::prototype() const {
|
||||
return _proto;
|
||||
}
|
||||
*/
|
|
@ -15,27 +15,29 @@
|
|||
#ifndef hifi_DataViewClass_h
|
||||
#define hifi_DataViewClass_h
|
||||
|
||||
// V8TODO
|
||||
/*
|
||||
#include "ArrayBufferViewClass.h"
|
||||
|
||||
/// [QtScript] Implements the <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView">DataView</a></code> scripting class
|
||||
/// [V8] Implements the <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView">DataView</a></code> scripting class
|
||||
class DataViewClass : public ArrayBufferViewClass {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DataViewClass(ScriptEngineQtScript* scriptEngine);
|
||||
QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 byteLength);
|
||||
DataViewClass(ScriptEngineV8* scriptEngine);
|
||||
V8ScriptValue newInstance(V8ScriptValue buffer, quint32 byteOffset, quint32 byteLength);
|
||||
|
||||
QString name() const override;
|
||||
QScriptValue prototype() const override;
|
||||
V8ScriptValue prototype() const override;
|
||||
|
||||
private:
|
||||
static QScriptValue construct(QScriptContext* context, QScriptEngine* engine);
|
||||
static V8ScriptValue construct(V8ScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
QScriptValue _proto;
|
||||
QScriptValue _ctor;
|
||||
V8ScriptValue _proto;
|
||||
V8ScriptValue _ctor;
|
||||
|
||||
QScriptString _name;
|
||||
V8ScriptString _name;
|
||||
};
|
||||
|
||||
*/
|
||||
|
||||
#endif // hifi_DataViewClass_h
|
||||
|
|
@ -13,8 +13,8 @@
|
|||
|
||||
#include <QDebug>
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtScript/QScriptEngine>
|
||||
#include <QtScript/QScriptValue>
|
||||
#include "libplatform/libplatform.h"
|
||||
#include "v8.h"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
|
@ -22,13 +22,14 @@
|
|||
|
||||
#include "ArrayBufferViewClass.h"
|
||||
|
||||
Q_DECLARE_METATYPE(QByteArray*)
|
||||
// V8TODO
|
||||
/*Q_DECLARE_METATYPE(QByteArray*)
|
||||
|
||||
DataViewPrototype::DataViewPrototype(QObject* parent) : QObject(parent) {
|
||||
}
|
||||
|
||||
QByteArray* DataViewPrototype::thisArrayBuffer() const {
|
||||
QScriptValue bufferObject = thisObject().data().property(BUFFER_PROPERTY_NAME);
|
||||
V8ScriptValue bufferObject = thisObject().data().property(BUFFER_PROPERTY_NAME);
|
||||
return qscriptvalue_cast<QByteArray*>(bufferObject.data());
|
||||
}
|
||||
|
||||
|
@ -124,7 +125,7 @@ quint32 DataViewPrototype::getUint32(qint32 byteOffset, bool littleEndian) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
QScriptValue DataViewPrototype::getFloat32(qint32 byteOffset, bool littleEndian) {
|
||||
V8ScriptValue DataViewPrototype::getFloat32(qint32 byteOffset, bool littleEndian) {
|
||||
if (realOffset(byteOffset, sizeof(float))) {
|
||||
QDataStream stream(*thisArrayBuffer());
|
||||
stream.skipRawData(byteOffset);
|
||||
|
@ -134,16 +135,16 @@ QScriptValue DataViewPrototype::getFloat32(qint32 byteOffset, bool littleEndian)
|
|||
float result;
|
||||
stream >> result;
|
||||
if (isNaN(result)) {
|
||||
return QScriptValue();
|
||||
return V8ScriptValue();
|
||||
}
|
||||
|
||||
return QScriptValue(result);
|
||||
return V8ScriptValue(result);
|
||||
}
|
||||
thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\"");
|
||||
return QScriptValue();
|
||||
return V8ScriptValue();
|
||||
}
|
||||
|
||||
QScriptValue DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) {
|
||||
V8ScriptValue DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) {
|
||||
if (realOffset(byteOffset, sizeof(double))) {
|
||||
QDataStream stream(*thisArrayBuffer());
|
||||
stream.skipRawData(byteOffset);
|
||||
|
@ -153,13 +154,13 @@ QScriptValue DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian)
|
|||
double result;
|
||||
stream >> result;
|
||||
if (isNaN(result)) {
|
||||
return QScriptValue();
|
||||
return V8ScriptValue();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\"");
|
||||
return QScriptValue();
|
||||
return V8ScriptValue();
|
||||
}
|
||||
|
||||
void DataViewPrototype::setInt8(qint32 byteOffset, qint32 value) {
|
||||
|
@ -257,5 +258,5 @@ void DataViewPrototype::setFloat64(qint32 byteOffset, double value, bool littleE
|
|||
thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\"");
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
|
@ -16,10 +16,13 @@
|
|||
#define hifi_DataViewPrototype_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtScript/QScriptable>
|
||||
|
||||
/// [QtScript] The javascript functions associated with a <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView">DataView</a></code> instance prototype
|
||||
class DataViewPrototype : public QObject, public QScriptable {
|
||||
#include "V8Types.h"
|
||||
#include "../Scriptable.h"
|
||||
|
||||
// V8TODO
|
||||
/*/// [V8] The javascript functions associated with a <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView">DataView</a></code> instance prototype
|
||||
class DataViewPrototype : public QObject, public Scriptable {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DataViewPrototype(QObject* parent = NULL);
|
||||
|
@ -41,8 +44,8 @@ public slots:
|
|||
quint32 getUint16(qint32 byteOffset, bool littleEndian = false);
|
||||
qint32 getInt32(qint32 byteOffset, bool littleEndian = false);
|
||||
quint32 getUint32(qint32 byteOffset, bool littleEndian = false);
|
||||
QScriptValue getFloat32(qint32 byteOffset, bool littleEndian = false);
|
||||
QScriptValue getFloat64(qint32 byteOffset, bool littleEndian = false);
|
||||
V8ScriptValue getFloat32(qint32 byteOffset, bool littleEndian = false);
|
||||
V8ScriptValue getFloat64(qint32 byteOffset, bool littleEndian = false);
|
||||
|
||||
// Stores a value of the given type at the specified byte offset
|
||||
// from the start of the view. There is no alignment constraint;
|
||||
|
@ -68,7 +71,7 @@ private:
|
|||
QByteArray* thisArrayBuffer() const;
|
||||
bool realOffset(qint32& offset, size_t size) const;
|
||||
};
|
||||
|
||||
*/
|
||||
#endif // hifi_DataViewPrototype_h
|
||||
|
||||
/// @}
|
135
libraries/script-engine/src/v8/ScriptContextV8Wrapper.cpp
Normal file
135
libraries/script-engine/src/v8/ScriptContextV8Wrapper.cpp
Normal file
|
@ -0,0 +1,135 @@
|
|||
//
|
||||
// ScriptContextV8Wrapper.cpp
|
||||
// libraries/script-engine/src/v8
|
||||
//
|
||||
// Created by Heather Anderson on 5/22/21.
|
||||
// Modified for V8 by dr Karol Suprynowicz on 2022/10/08
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
// Copyright 2022 Overte e.V.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "ScriptContextV8Wrapper.h"
|
||||
|
||||
#include "ScriptEngineV8.h"
|
||||
#include "ScriptValueV8Wrapper.h"
|
||||
|
||||
ScriptContextV8Wrapper::ScriptContextV8Wrapper(ScriptEngineV8* engine, const v8::Local<v8::Context> context) : _engine(engine) {
|
||||
_context.Reset(_engine->getIsolate(), _engine->getConstContext());
|
||||
}
|
||||
|
||||
ScriptContextV8Wrapper::ScriptContextV8Wrapper(ScriptEngineV8* engine, const v8::Local<v8::Context> context, std::shared_ptr<v8::FunctionCallbackInfo<v8::Value>> functionCallbackInfo) : _context(engine->getIsolate(), engine->getConstContext()), _functionCallbackInfo(functionCallbackInfo), _engine(engine) {
|
||||
_context.Reset(_engine->getIsolate(), _engine->getConstContext());
|
||||
}
|
||||
|
||||
ScriptContextV8Wrapper* ScriptContextV8Wrapper::unwrap(ScriptContext* val) {
|
||||
if (!val) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dynamic_cast<ScriptContextV8Wrapper*>(val);
|
||||
}
|
||||
|
||||
v8::Local<v8::Context> ScriptContextV8Wrapper::toV8Value() const {
|
||||
return _context.Get(_engine->getIsolate());
|
||||
}
|
||||
|
||||
int ScriptContextV8Wrapper::argumentCount() const {
|
||||
Q_ASSERT(_functionCallbackInfo);
|
||||
return _functionCallbackInfo->Length();
|
||||
}
|
||||
|
||||
ScriptValue ScriptContextV8Wrapper::argument(int index) const {
|
||||
Q_ASSERT(_functionCallbackInfo);
|
||||
v8::Local<v8::Value> result = (*_functionCallbackInfo)[index];
|
||||
//V8ScriptValue result = _context->argument(index);
|
||||
return ScriptValue(new ScriptValueV8Wrapper(_engine, V8ScriptValue(_engine->getIsolate(), result)));
|
||||
}
|
||||
|
||||
QStringList ScriptContextV8Wrapper::backtrace() const {
|
||||
auto isolate = _engine->getIsolate();
|
||||
v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(isolate, 40);
|
||||
QStringList backTrace;
|
||||
//V8TODO nicer formatting
|
||||
for (int i = 0; i < stackTrace->GetFrameCount(); i++) {
|
||||
v8::Local<v8::StackFrame> stackFrame = stackTrace->GetFrame(isolate, i);
|
||||
backTrace.append(QString(*v8::String::Utf8Value(isolate, stackFrame->GetScriptNameOrSourceURL())) +
|
||||
QString(" ") +
|
||||
QString(*v8::String::Utf8Value(isolate, stackFrame->GetFunctionName())) +
|
||||
QString(":") +
|
||||
QString(stackFrame->GetLineNumber())
|
||||
);
|
||||
}
|
||||
return backTrace;
|
||||
}
|
||||
|
||||
ScriptValue ScriptContextV8Wrapper::callee() const {
|
||||
//V8TODO
|
||||
//Can this be done with CurrentStackTrace?
|
||||
//V8ScriptValue result = _context->callee();
|
||||
//return ScriptValue(new ScriptValueV8Wrapper(_engine, std::move(result)));
|
||||
return ScriptValue();
|
||||
}
|
||||
|
||||
ScriptEnginePointer ScriptContextV8Wrapper::engine() const {
|
||||
return _engine->shared_from_this();
|
||||
}
|
||||
|
||||
ScriptFunctionContextPointer ScriptContextV8Wrapper::functionContext() const {
|
||||
return std::make_shared<ScriptFunctionContextV8Wrapper>(_context.Get(_engine->getIsolate()));
|
||||
}
|
||||
|
||||
ScriptContextPointer ScriptContextV8Wrapper::parentContext() const {
|
||||
//V8TODO
|
||||
//V8ScriptContext* result = _context->parentContext();
|
||||
//return result ? std::make_shared<ScriptContextV8Wrapper>(_engine, result) : ScriptContextPointer();
|
||||
return ScriptContextPointer();
|
||||
}
|
||||
|
||||
ScriptValue ScriptContextV8Wrapper::thisObject() const {
|
||||
Q_ASSERT(_functionCallbackInfo);
|
||||
v8::Local<v8::Value> result = _functionCallbackInfo->This();
|
||||
return ScriptValue(new ScriptValueV8Wrapper(_engine, V8ScriptValue(_engine->getIsolate(), result)));
|
||||
return ScriptValue();
|
||||
}
|
||||
|
||||
ScriptValue ScriptContextV8Wrapper::throwError(const QString& text) {
|
||||
V8ScriptValue result(_engine->getIsolate(), _engine->getIsolate()->ThrowError(v8::String::NewFromUtf8(_engine->getIsolate(), text.toStdString().c_str()).ToLocalChecked()));
|
||||
return ScriptValue(new ScriptValueV8Wrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValue ScriptContextV8Wrapper::throwValue(const ScriptValue& value) {
|
||||
ScriptValueV8Wrapper* unwrapped = ScriptValueV8Wrapper::unwrap(value);
|
||||
if (!unwrapped) {
|
||||
return _engine->undefinedValue();
|
||||
}
|
||||
V8ScriptValue result(_engine->getIsolate(), _engine->getIsolate()->ThrowException(unwrapped->toV8Value().constGet()));
|
||||
return ScriptValue(new ScriptValueV8Wrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
|
||||
QString ScriptFunctionContextV8Wrapper::fileName() const {
|
||||
//V8TODO
|
||||
//return _value.fileName();
|
||||
return QString("");
|
||||
}
|
||||
|
||||
QString ScriptFunctionContextV8Wrapper::functionName() const {
|
||||
//V8TODO
|
||||
//return _value.functionName();
|
||||
return QString("");
|
||||
}
|
||||
|
||||
ScriptFunctionContext::FunctionType ScriptFunctionContextV8Wrapper::functionType() const {
|
||||
//V8TODO
|
||||
//return static_cast<ScriptFunctionContext::FunctionType>(_value.functionType());
|
||||
return ScriptFunctionContext::FunctionType::ScriptFunction;
|
||||
}
|
||||
|
||||
int ScriptFunctionContextV8Wrapper::lineNumber() const {
|
||||
//V8TODO
|
||||
//return _value.lineNumber();
|
||||
return 0;
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
//
|
||||
// ScriptContextQtWrapper.h
|
||||
// libraries/script-engine/src/qtscript
|
||||
// ScriptContextV8Wrapper.h
|
||||
// libraries/script-engine/src/v8
|
||||
//
|
||||
// Created by Heather Anderson on 5/22/21.
|
||||
// Modified for V8 by dr Karol Suprynowicz on 2022/10/08
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
// Copyright 2022 Overte e.V.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -12,24 +14,27 @@
|
|||
/// @addtogroup ScriptEngine
|
||||
/// @{
|
||||
|
||||
#ifndef hifi_ScriptContextQtWrapper_h
|
||||
#define hifi_ScriptContextQtWrapper_h
|
||||
#ifndef hifi_ScriptContextV8Wrapper_h
|
||||
#define hifi_ScriptContextV8Wrapper_h
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtScript/QScriptContextInfo>
|
||||
|
||||
#include "../ScriptContext.h"
|
||||
#include "../ScriptValue.h"
|
||||
|
||||
class QScriptContext;
|
||||
class ScriptEngineQtScript;
|
||||
#include <libplatform/libplatform.h>
|
||||
#include <v8.h>
|
||||
|
||||
/// [QtScript] Implements ScriptContext for QtScript and translates calls for QScriptContextInfo
|
||||
class ScriptContextQtWrapper final : public ScriptContext {
|
||||
//class V8ScriptContext;
|
||||
class ScriptEngineV8;
|
||||
|
||||
/// [V8] Implements ScriptContext for V8 and translates calls for V8ScriptContextInfo
|
||||
class ScriptContextV8Wrapper final : public ScriptContext {
|
||||
public: // construction
|
||||
inline ScriptContextQtWrapper(ScriptEngineQtScript* engine, QScriptContext* context) : _context(context) , _engine(engine) {}
|
||||
static ScriptContextQtWrapper* unwrap(ScriptContext* val);
|
||||
inline QScriptContext* toQtValue() const { return _context; }
|
||||
ScriptContextV8Wrapper(ScriptEngineV8* engine, const v8::Local<v8::Context> context);
|
||||
ScriptContextV8Wrapper(ScriptEngineV8* engine, const v8::Local<v8::Context> context, std::shared_ptr<v8::FunctionCallbackInfo<v8::Value>> functionCallbackInfo);
|
||||
static ScriptContextV8Wrapper* unwrap(ScriptContext* val);
|
||||
v8::Local<v8::Context> toV8Value() const;
|
||||
|
||||
public: // ScriptContext implementation
|
||||
virtual int argumentCount() const override;
|
||||
|
@ -44,13 +49,14 @@ public: // ScriptContext implementation
|
|||
virtual ScriptValue throwValue(const ScriptValue& value) override;
|
||||
|
||||
private: // storage
|
||||
QScriptContext* _context;
|
||||
ScriptEngineQtScript* _engine;
|
||||
v8::Persistent<v8::Context> _context;
|
||||
std::shared_ptr<v8::FunctionCallbackInfo<v8::Value>> _functionCallbackInfo;
|
||||
ScriptEngineV8* _engine;
|
||||
};
|
||||
|
||||
class ScriptFunctionContextQtWrapper final : public ScriptFunctionContext {
|
||||
class ScriptFunctionContextV8Wrapper final : public ScriptFunctionContext {
|
||||
public: // construction
|
||||
inline ScriptFunctionContextQtWrapper(QScriptContext* context) : _value(context) {}
|
||||
inline ScriptFunctionContextV8Wrapper(v8::Local<v8::Context> context) { }
|
||||
|
||||
public: // ScriptFunctionContext implementation
|
||||
virtual QString fileName() const override;
|
||||
|
@ -58,10 +64,10 @@ public: // ScriptFunctionContext implementation
|
|||
virtual FunctionType functionType() const override;
|
||||
virtual int lineNumber() const override;
|
||||
|
||||
private: // storage
|
||||
QScriptContextInfo _value;
|
||||
//private: // storage
|
||||
//V8ScriptContextInfo _value;
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptContextQtWrapper_h
|
||||
#endif // hifi_ScriptContextV8Wrapper_h
|
||||
|
||||
/// @}
|
1093
libraries/script-engine/src/v8/ScriptEngineV8.cpp
Normal file
1093
libraries/script-engine/src/v8/ScriptEngineV8.cpp
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,10 +1,12 @@
|
|||
//
|
||||
// ScriptEngineQtScript.h
|
||||
// ScriptEngineV8.h
|
||||
// libraries/script-engine/src/qtscript
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/14/13.
|
||||
// Modified for V8 by dr Karol Suprynowicz on 2022/10/08
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
// Copyright 2022 Overte e.V.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -13,8 +15,8 @@
|
|||
/// @addtogroup ScriptEngine
|
||||
/// @{
|
||||
|
||||
#ifndef hifi_ScriptEngineQtScript_h
|
||||
#define hifi_ScriptEngineQtScript_h
|
||||
#ifndef hifi_ScriptEngineV8_h
|
||||
#define hifi_ScriptEngineV8_h
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
@ -27,29 +29,33 @@
|
|||
#include <QtCore/QSharedPointer>
|
||||
#include <QtCore/QString>
|
||||
|
||||
#include <QtScript/QScriptEngine>
|
||||
#include "libplatform/libplatform.h"
|
||||
#include "v8.h"
|
||||
|
||||
#include "../ScriptEngine.h"
|
||||
#include "../ScriptManager.h"
|
||||
#include "V8Types.h"
|
||||
|
||||
#include "ArrayBufferClass.h"
|
||||
|
||||
class ScriptContextQtWrapper;
|
||||
class ScriptEngineQtScript;
|
||||
class ScriptContextV8Wrapper;
|
||||
class ScriptEngineV8;
|
||||
class ScriptManager;
|
||||
class ScriptObjectQtProxy;
|
||||
using ScriptContextQtPointer = std::shared_ptr<ScriptContextQtWrapper>;
|
||||
class ScriptObjectV8Proxy;
|
||||
class ScriptMethodV8Proxy;
|
||||
using ScriptContextQtPointer = std::shared_ptr<ScriptContextV8Wrapper>;
|
||||
|
||||
const double GARBAGE_COLLECTION_TIME_LIMIT_S = 1.0;
|
||||
|
||||
Q_DECLARE_METATYPE(ScriptEngine::FunctionSignature)
|
||||
|
||||
/// [QtScript] Implements ScriptEngine for QtScript and translates calls for QScriptEngine
|
||||
class ScriptEngineQtScript final : public QScriptEngine,
|
||||
public ScriptEngine,
|
||||
public std::enable_shared_from_this<ScriptEngineQtScript> {
|
||||
/// [V8] Implements ScriptEngine for V8 and translates calls for QScriptEngine
|
||||
class ScriptEngineV8 final : public ScriptEngine,
|
||||
public std::enable_shared_from_this<ScriptEngineV8> {
|
||||
Q_OBJECT
|
||||
|
||||
public: // construction
|
||||
ScriptEngineQtScript(ScriptManager* scriptManager = nullptr);
|
||||
ScriptEngineV8(ScriptManager* scriptManager = nullptr);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// NOTE - these are NOT intended to be public interfaces available to scripts, the are only Q_INVOKABLE so we can
|
||||
|
@ -66,7 +72,8 @@ public: // ScriptEngine implementation
|
|||
virtual ScriptValue globalObject() const override;
|
||||
virtual bool hasUncaughtException() const override;
|
||||
virtual bool isEvaluating() const override;
|
||||
virtual ScriptValue lintScript(const QString& sourceCode, const QString& fileName, const int lineNumber = 1) override;
|
||||
//virtual ScriptValue lintScript(const QString& sourceCode, const QString& fileName, const int lineNumber = 1) override;
|
||||
virtual ScriptValue cheskScriptSyntax(ScriptProgramPointer program) override;
|
||||
virtual ScriptValue makeError(const ScriptValue& other, const QString& type = "Error") override;
|
||||
virtual ScriptManager* manager() const override;
|
||||
|
||||
|
@ -77,6 +84,9 @@ public: // ScriptEngine implementation
|
|||
virtual ScriptValue newArrayBuffer(const QByteArray& message) override;
|
||||
virtual ScriptValue newFunction(ScriptEngine::FunctionSignature fun, int length = 0) override;
|
||||
virtual ScriptValue newObject() override;
|
||||
//virtual ScriptValue newObject( v8::Local<v8::ObjectTemplate> );
|
||||
virtual ScriptValue newMethod(QObject* object, V8ScriptValue lifetime,
|
||||
const QList<QMetaMethod>& metas, int numMaxParams);
|
||||
virtual ScriptProgramPointer newProgram(const QString& sourceCode, const QString& fileName) override;
|
||||
virtual ScriptValue newQObject(QObject *object, ScriptEngine::ValueOwnership ownership = ScriptEngine::QtOwnership,
|
||||
const ScriptEngine::QObjectWrapOptions& options = ScriptEngine::QObjectWrapOptions()) override;
|
||||
|
@ -104,7 +114,8 @@ public: // ScriptEngine implementation
|
|||
const QString& parent = QString("")) override;
|
||||
Q_INVOKABLE virtual void registerGlobalObject(const QString& name, QObject* object) override;
|
||||
virtual void setDefaultPrototype(int metaTypeId, const ScriptValue& prototype) override;
|
||||
virtual void setObjectName(const QString& name) override;
|
||||
// Already implemented by QObject
|
||||
//virtual void setObjectName(const QString& name) override;
|
||||
virtual bool setProperty(const char* name, const QVariant& value) override;
|
||||
virtual void setProcessEventsInterval(int interval) override;
|
||||
virtual QThread* thread() const override;
|
||||
|
@ -114,71 +125,88 @@ public: // ScriptEngine implementation
|
|||
virtual QStringList uncaughtExceptionBacktrace() const override;
|
||||
virtual int uncaughtExceptionLineNumber() const override;
|
||||
virtual void updateMemoryCost(const qint64& deltaSize) override;
|
||||
virtual void requestCollectGarbage() override { collectGarbage(); }
|
||||
virtual void requestCollectGarbage() override { while(!_v8Isolate->IdleNotificationDeadline(getV8Platform()->MonotonicallyIncreasingTime() + GARBAGE_COLLECTION_TIME_LIMIT_S)) {}; }
|
||||
|
||||
// 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
|
||||
QScriptValue makeError(const QScriptValue& other = QScriptValue(), const QString& type = "Error");
|
||||
V8ScriptValue makeError(const V8ScriptValue& other, 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);
|
||||
bool raiseException(const V8ScriptValue& 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 non-interface methods for other QtScript-specific classes to use
|
||||
/*typedef V8ScriptValue (*FunctionSignature)(v8::Local<v8::Context>, ScriptEngineV8 *);
|
||||
typedef V8ScriptValue (*FunctionWithArgSignature)(v8::Local<v8::Context>, ScriptEngineV8 *, void *);
|
||||
/// registers a global getter/setter
|
||||
Q_INVOKABLE void registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter,
|
||||
QScriptEngine::FunctionSignature setter, const QString& parent = QString(""));
|
||||
Q_INVOKABLE void registerGetterSetter(const QString& name, FunctionSignature getter,
|
||||
FunctionSignature setter, const QString& parent = QString(""));
|
||||
|
||||
/// register a global function
|
||||
Q_INVOKABLE void registerFunction(const QString& name, QScriptEngine::FunctionSignature fun, int numArguments = -1);
|
||||
Q_INVOKABLE void registerFunction(const QString& name, FunctionSignature fun, int numArguments = -1);
|
||||
|
||||
/// register a function as a method on a previously registered global object
|
||||
Q_INVOKABLE void registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature fun,
|
||||
int numArguments = -1);
|
||||
Q_INVOKABLE void registerFunction(const QString& parent, const QString& name, FunctionSignature fun,
|
||||
int numArguments = -1);*/
|
||||
|
||||
/// registers a global object by name
|
||||
Q_INVOKABLE void registerValue(const QString& valueName, QScriptValue value);
|
||||
Q_INVOKABLE void registerValue(const QString& valueName, V8ScriptValue value);
|
||||
|
||||
// NOTE - this is used by the TypedArray implementation. we need to review this for thread safety
|
||||
inline ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; }
|
||||
// V8TODO
|
||||
//inline ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; }
|
||||
|
||||
public: // not for public use, but I don't like how Qt strings this along with private friend functions
|
||||
virtual ScriptValue create(int type, const void* ptr) override;
|
||||
virtual QVariant convert(const ScriptValue& value, int typeId) override;
|
||||
virtual void registerCustomType(int type, ScriptEngine::MarshalFunction marshalFunc,
|
||||
ScriptEngine::DemarshalFunction demarshalFunc) override;
|
||||
int computeCastPenalty(QScriptValue& val, int destTypeId);
|
||||
bool castValueToVariant(const QScriptValue& val, QVariant& dest, int destTypeId);
|
||||
QScriptValue castVariantToValue(const QVariant& val);
|
||||
static QString valueType(const QScriptValue& val);
|
||||
int computeCastPenalty(const V8ScriptValue& val, int destTypeId);
|
||||
bool castValueToVariant(const V8ScriptValue& val, QVariant& dest, int destTypeId);
|
||||
V8ScriptValue castVariantToValue(const QVariant& val);
|
||||
static QString valueType(const V8ScriptValue& val);
|
||||
v8::Isolate* getIsolate() {return _v8Isolate;}
|
||||
v8::Local<v8::Context> getContext() {
|
||||
v8::EscapableHandleScope handleScope(_v8Isolate);
|
||||
return handleScope.Escape(_v8Context.Get(_v8Isolate));
|
||||
}
|
||||
const v8::Local<v8::Context> getConstContext() const {return _v8Context.Get(_v8Isolate);}
|
||||
|
||||
using ObjectWrapperMap = QMap<QObject*, QWeakPointer<ScriptObjectQtProxy>>;
|
||||
using ObjectWrapperMap = QMap<QObject*, QWeakPointer<ScriptObjectV8Proxy>>;
|
||||
mutable QMutex _qobjectWrapperMapProtect;
|
||||
ObjectWrapperMap _qobjectWrapperMap;
|
||||
|
||||
|
||||
protected:
|
||||
// like `newFunction`, but allows mapping inline C++ lambdas with captures as callable QScriptValues
|
||||
// like `newFunction`, but allows mapping inline C++ lambdas with captures as callable V8ScriptValues
|
||||
// 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);
|
||||
ScriptValue newLambdaFunction(std::function<V8ScriptValue(V8ScriptContext* context, ScriptEngineV8* engine)> operation,
|
||||
const V8ScriptValue& data,
|
||||
const ValueOwnership& ownership = AutoOwnership);
|
||||
|
||||
void registerSystemTypes();
|
||||
|
||||
protected:
|
||||
static QMutex _v8InitMutex;
|
||||
static std::once_flag _v8InitOnceFlag;
|
||||
static v8::Platform* getV8Platform();
|
||||
|
||||
v8::Isolate* _v8Isolate;
|
||||
v8::UniquePersistent<v8::Context> _v8Context;
|
||||
|
||||
struct CustomMarshal {
|
||||
ScriptEngine::MarshalFunction marshalFunc;
|
||||
ScriptEngine::DemarshalFunction demarshalFunc;
|
||||
};
|
||||
using CustomMarshalMap = QHash<int, CustomMarshal>;
|
||||
using CustomPrototypeMap = QHash<int, QScriptValue>;
|
||||
using CustomPrototypeMap = QHash<int, V8ScriptValue>;
|
||||
|
||||
QPointer<ScriptManager> _scriptManager;
|
||||
|
||||
|
@ -188,29 +216,31 @@ protected:
|
|||
ScriptValue _nullValue;
|
||||
ScriptValue _undefinedValue;
|
||||
mutable ScriptContextQtPointer _currContext;
|
||||
QThread *_currentThread;
|
||||
|
||||
ArrayBufferClass* _arrayBufferClass;
|
||||
//V8TODO
|
||||
//ArrayBufferClass* _arrayBufferClass;
|
||||
};
|
||||
|
||||
// Lambda helps create callable QScriptValues out of std::functions:
|
||||
// Lambda helps create callable V8ScriptValues 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(ScriptEngineV8* engine,
|
||||
std::function<V8ScriptValue(V8ScriptContext* context, ScriptEngineV8* engine)> operation,
|
||||
V8ScriptValue data);
|
||||
~Lambda();
|
||||
public slots:
|
||||
QScriptValue call();
|
||||
V8ScriptValue call();
|
||||
QString toString() const;
|
||||
|
||||
private:
|
||||
ScriptEngineQtScript* engine;
|
||||
std::function<QScriptValue(QScriptContext* context, ScriptEngineQtScript* engine)> operation;
|
||||
QScriptValue data;
|
||||
ScriptEngineV8* _engine;
|
||||
std::function<V8ScriptValue(V8ScriptContext* context, ScriptEngineV8* engine)> _operation;
|
||||
V8ScriptValue _data;
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptEngineQtScript_h
|
||||
#endif // hifi_ScriptEngineV8_h
|
||||
|
||||
/// @}
|
|
@ -1,8 +1,9 @@
|
|||
//
|
||||
// ScriptEngineQtScript_cast.cpp
|
||||
// libraries/script-engine/src/qtscript
|
||||
// ScriptEngineV8_cast.cpp
|
||||
// libraries/script-engine/src/v8
|
||||
//
|
||||
// Created by Heather Anderson 12/9/2021
|
||||
// Modified for V8 by dr Karol Suprynowicz on 2022/10/08
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
// Copyright 2022 Overte e.V.
|
||||
//
|
||||
|
@ -10,30 +11,31 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "ScriptEngineQtScript.h"
|
||||
#include "ScriptEngineV8.h"
|
||||
|
||||
#include <QtCore/QJsonArray>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QJsonValue>
|
||||
#include <QtScript/QScriptEngine>
|
||||
#include "libplatform/libplatform.h"
|
||||
#include "v8.h"
|
||||
|
||||
#include "../ScriptEngineCast.h"
|
||||
#include "../ScriptValueIterator.h"
|
||||
|
||||
#include "ScriptObjectQtProxy.h"
|
||||
#include "ScriptValueQtWrapper.h"
|
||||
#include "ScriptObjectV8Proxy.h"
|
||||
#include "ScriptValueV8Wrapper.h"
|
||||
|
||||
void ScriptEngineQtScript::setDefaultPrototype(int metaTypeId, const ScriptValue& prototype) {
|
||||
ScriptValueQtWrapper* unwrappedPrototype = ScriptValueQtWrapper::unwrap(prototype);
|
||||
void ScriptEngineV8::setDefaultPrototype(int metaTypeId, const ScriptValue& prototype) {
|
||||
ScriptValueV8Wrapper* unwrappedPrototype = ScriptValueV8Wrapper::unwrap(prototype);
|
||||
if (unwrappedPrototype) {
|
||||
const QScriptValue& scriptPrototype = unwrappedPrototype->toQtValue();
|
||||
const V8ScriptValue& scriptPrototype = unwrappedPrototype->toV8Value();
|
||||
_customTypeProtect.lockForWrite();
|
||||
_customPrototypes.insert(metaTypeId, scriptPrototype);
|
||||
_customTypeProtect.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngineQtScript::registerCustomType(int type,
|
||||
void ScriptEngineV8::registerCustomType(int type,
|
||||
ScriptEngine::MarshalFunction marshalFunc,
|
||||
ScriptEngine::DemarshalFunction demarshalFunc)
|
||||
{
|
||||
|
@ -48,13 +50,13 @@ void ScriptEngineQtScript::registerCustomType(int type,
|
|||
|
||||
Q_DECLARE_METATYPE(ScriptValue);
|
||||
|
||||
static QScriptValue ScriptValueToQScriptValue(QScriptEngine* engine, const ScriptValue& src) {
|
||||
return ScriptValueQtWrapper::fullUnwrap(static_cast<ScriptEngineQtScript*>(engine), src);
|
||||
static V8ScriptValue ScriptValueToV8ScriptValue(ScriptEngineV8* engine, const ScriptValue& src) {
|
||||
return ScriptValueV8Wrapper::fullUnwrap(static_cast<ScriptEngineV8*>(engine), src);
|
||||
}
|
||||
|
||||
static void ScriptValueFromQScriptValue(const QScriptValue& src, ScriptValue& dest) {
|
||||
ScriptEngineQtScript* engine = static_cast<ScriptEngineQtScript*>(src.engine());
|
||||
dest = ScriptValue(new ScriptValueQtWrapper(engine, src));
|
||||
static void ScriptValueFromV8ScriptValue(ScriptEngineV8* engine, const V8ScriptValue& src, ScriptValue& dest) {
|
||||
//ScriptEngineV8* engine = static_cast<ScriptEngineV8*>(src.engine());
|
||||
dest = ScriptValue(new ScriptValueV8Wrapper(engine, src));
|
||||
}
|
||||
|
||||
static ScriptValue StringListToScriptValue(ScriptEngine* engine, const QStringList& src) {
|
||||
|
@ -183,10 +185,10 @@ static bool JsonArrayFromScriptValue(const ScriptValue& src, QJsonArray& dest) {
|
|||
|
||||
// QMetaType::QJsonArray
|
||||
|
||||
void ScriptEngineQtScript::registerSystemTypes() {
|
||||
qScriptRegisterMetaType(this, ScriptValueToQScriptValue, ScriptValueFromQScriptValue);
|
||||
void ScriptEngineV8::registerSystemTypes() {
|
||||
//qScriptRegisterMetaType(this, ScriptValueToV8ScriptValue, ScriptValueFromV8ScriptValue);
|
||||
|
||||
scriptRegisterMetaType<QStringList, StringListToScriptValue, StringListFromScriptValue>(this);
|
||||
scriptRegisterMetaType<QStringList, StringListToScriptValue, StringListFromScriptValue>(static_cast<ScriptEngine*>(this));
|
||||
scriptRegisterMetaType<QVariantList, VariantListToScriptValue, VariantListFromScriptValue>(this);
|
||||
scriptRegisterMetaType<QVariantMap, VariantMapToScriptValue, VariantMapFromScriptValue>(this);
|
||||
scriptRegisterMetaType<QVariantHash, VariantHashToScriptValue, VariantHashFromScriptValue>(this);
|
||||
|
@ -195,8 +197,9 @@ void ScriptEngineQtScript::registerSystemTypes() {
|
|||
scriptRegisterMetaType<QJsonArray, JsonArrayToScriptValue, JsonArrayFromScriptValue>(this);
|
||||
}
|
||||
|
||||
int ScriptEngineQtScript::computeCastPenalty(QScriptValue& val, int destTypeId) {
|
||||
if (val.isNumber()) {
|
||||
int ScriptEngineV8::computeCastPenalty(const V8ScriptValue& v8Val, int destTypeId) {
|
||||
const v8::Local<v8::Value> val = v8Val.constGet();
|
||||
if (val->IsNumber()) {
|
||||
switch (destTypeId){
|
||||
case QMetaType::Bool:
|
||||
// Conversion to bool is acceptable, but numbers are preferred
|
||||
|
@ -226,7 +229,7 @@ int ScriptEngineQtScript::computeCastPenalty(QScriptValue& val, int destTypeId)
|
|||
// Other, not predicted cases
|
||||
return 5;
|
||||
}
|
||||
} else if (val.isString() || val.isDate() || val.isRegExp()) {
|
||||
} else if (val->IsString() || val->IsDate() || val->IsRegExp()) {
|
||||
switch (destTypeId){
|
||||
case QMetaType::Bool:
|
||||
// Conversion from to bool should be avoided if possible, it's probably not what we want
|
||||
|
@ -254,7 +257,7 @@ int ScriptEngineQtScript::computeCastPenalty(QScriptValue& val, int destTypeId)
|
|||
default:
|
||||
return 5;
|
||||
}
|
||||
} else if (val.isBool() || val.isBoolean()) {
|
||||
} else if (val->IsBoolean()) {
|
||||
switch (destTypeId){
|
||||
case QMetaType::Bool:
|
||||
// Perfect case
|
||||
|
@ -287,11 +290,12 @@ int ScriptEngineQtScript::computeCastPenalty(QScriptValue& val, int destTypeId)
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool ScriptEngineQtScript::castValueToVariant(const QScriptValue& val, QVariant& dest, int destTypeId) {
|
||||
bool ScriptEngineV8::castValueToVariant(const V8ScriptValue& v8Val, QVariant& dest, int destTypeId) {
|
||||
const v8::Local<v8::Value> val = v8Val.constGet();
|
||||
|
||||
// if we're not particularly interested in a specific type, try to detect if we're dealing with a registered type
|
||||
if (destTypeId == QMetaType::UnknownType) {
|
||||
QObject* obj = ScriptObjectQtProxy::unwrap(val);
|
||||
QObject* obj = ScriptObjectV8Proxy::unwrap(v8Val);
|
||||
if (obj) {
|
||||
for (const QMetaObject* metaObject = obj->metaObject(); metaObject; metaObject = metaObject->superClass()) {
|
||||
QByteArray typeName = QByteArray(metaObject->className()) + "*";
|
||||
|
@ -305,7 +309,7 @@ bool ScriptEngineQtScript::castValueToVariant(const QScriptValue& val, QVariant&
|
|||
}
|
||||
|
||||
if (destTypeId == qMetaTypeId<ScriptValue>()) {
|
||||
dest = QVariant::fromValue(ScriptValue(new ScriptValueQtWrapper(this, val)));
|
||||
dest = QVariant::fromValue(ScriptValue(new ScriptValueV8Wrapper(this, v8Val)));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -321,93 +325,111 @@ bool ScriptEngineQtScript::castValueToVariant(const QScriptValue& val, QVariant&
|
|||
}
|
||||
if (demarshalFunc) {
|
||||
dest = QVariant(destTypeId, static_cast<void*>(NULL));
|
||||
ScriptValue wrappedVal(new ScriptValueQtWrapper(this, val));
|
||||
ScriptValue wrappedVal(new ScriptValueV8Wrapper(this, v8Val));
|
||||
bool success = demarshalFunc(wrappedVal, const_cast<void*>(dest.constData()));
|
||||
if(!success) dest = QVariant();
|
||||
return success;
|
||||
} else {
|
||||
switch (destTypeId) {
|
||||
case QMetaType::UnknownType:
|
||||
if (val.isUndefined()) {
|
||||
if (val->IsUndefined()) {
|
||||
dest = QVariant();
|
||||
break;
|
||||
}
|
||||
if (val.isNull()) {
|
||||
if (val->IsNull()) {
|
||||
dest = QVariant::fromValue(nullptr);
|
||||
break;
|
||||
}
|
||||
if (val.isBool()) {
|
||||
dest = QVariant::fromValue(val.toBool());
|
||||
if (val->IsBoolean()) {
|
||||
//V8TODO is it right isolate? What if value from different script engine is used here
|
||||
dest = QVariant::fromValue(val->ToBoolean(_v8Isolate)->Value());
|
||||
break;
|
||||
}
|
||||
if (val.isString()) {
|
||||
dest = QVariant::fromValue(val.toString());
|
||||
if (val->IsString()) {
|
||||
//V8TODO is it right context? What if value from different script engine is used here
|
||||
v8::String::Utf8Value string(_v8Isolate, val);
|
||||
Q_ASSERT(*string != nullptr);
|
||||
dest = QVariant::fromValue(QString(*string));
|
||||
//dest = QVariant::fromValue(val->ToString(_v8Context.Get(_v8Isolate)).ToLocalChecked()->);
|
||||
break;
|
||||
}
|
||||
if (val.isNumber()) {
|
||||
dest = QVariant::fromValue(val.toNumber());
|
||||
if (val->IsNumber()) {
|
||||
dest = QVariant::fromValue(val->ToNumber(_v8Context.Get(_v8Isolate)).ToLocalChecked()->Value());
|
||||
break;
|
||||
}
|
||||
{
|
||||
QObject* obj = ScriptObjectQtProxy::unwrap(val);
|
||||
QObject* obj = ScriptObjectV8Proxy::unwrap(v8Val);
|
||||
if (obj) {
|
||||
dest = QVariant::fromValue(obj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
{
|
||||
QVariant var = ScriptVariantQtProxy::unwrap(val);
|
||||
QVariant var = ScriptVariantV8Proxy::unwrap(v8Val);
|
||||
if (var.isValid()) {
|
||||
dest = var;
|
||||
break;
|
||||
}
|
||||
}
|
||||
dest = val.toVariant();
|
||||
// V8TODO
|
||||
Q_ASSERT(false);
|
||||
//dest = val->ToVariant();
|
||||
break;
|
||||
case QMetaType::Bool:
|
||||
dest = QVariant::fromValue(val.toBool());
|
||||
dest = QVariant::fromValue(val->ToBoolean(_v8Isolate)->Value());
|
||||
break;
|
||||
case QMetaType::QDateTime:
|
||||
case QMetaType::QDate:
|
||||
Q_ASSERT(val.isDate());
|
||||
dest = QVariant::fromValue(val.toDateTime());
|
||||
if (val->IsDate()){
|
||||
double timeMs = v8::Date::Cast(*val)->NumberValue(_v8Context.Get(_v8Isolate)).ToChecked();
|
||||
dest = QVariant::fromValue(QDateTime::fromMSecsSinceEpoch(timeMs));
|
||||
} else if (val->IsNumber()) {
|
||||
//V8TODO should we automatically cast numbers to datetime?
|
||||
dest = QVariant::fromValue(QDateTime::fromMSecsSinceEpoch(val->ToNumber(_v8Context.Get(_v8Isolate)).ToLocalChecked()->Value()));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case QMetaType::UInt:
|
||||
case QMetaType::ULong:
|
||||
if ( val.isArray() || val.isObject() ){
|
||||
if ( val->IsArray() || val->IsObject() ){
|
||||
return false;
|
||||
}
|
||||
dest = QVariant::fromValue(val.toUInt32());
|
||||
dest = QVariant::fromValue(val->ToUint32(_v8Context.Get(_v8Isolate)).ToLocalChecked()->Value());
|
||||
break;
|
||||
case QMetaType::Int:
|
||||
case QMetaType::Long:
|
||||
case QMetaType::Short:
|
||||
if ( val.isArray() || val.isObject() ){
|
||||
if ( val->IsArray() || val->IsObject() ){
|
||||
return false;
|
||||
}
|
||||
dest = QVariant::fromValue(val.toInt32());
|
||||
dest = QVariant::fromValue(val->ToInt32(_v8Context.Get(_v8Isolate)).ToLocalChecked()->Value());
|
||||
break;
|
||||
case QMetaType::Double:
|
||||
case QMetaType::Float:
|
||||
case QMetaType::ULongLong:
|
||||
case QMetaType::LongLong:
|
||||
if ( val.isArray() || val.isObject() ){
|
||||
if ( val->IsArray() || val->IsObject() ){
|
||||
return false;
|
||||
}
|
||||
dest = QVariant::fromValue(val.toNumber());
|
||||
dest = QVariant::fromValue(val->ToNumber(_v8Context.Get(_v8Isolate)).ToLocalChecked()->Value());
|
||||
break;
|
||||
case QMetaType::QString:
|
||||
case QMetaType::QByteArray:
|
||||
dest = QVariant::fromValue(val.toString());
|
||||
{
|
||||
v8::String::Utf8Value string(_v8Isolate, val);
|
||||
Q_ASSERT(*string != nullptr);
|
||||
dest = QVariant::fromValue(QString(*string));
|
||||
}
|
||||
break;
|
||||
case QMetaType::UShort:
|
||||
if ( val.isArray() || val.isObject() ){
|
||||
if ( val->IsArray() || val->IsObject() ){
|
||||
return false;
|
||||
}
|
||||
dest = QVariant::fromValue(val.toUInt16());
|
||||
dest = QVariant::fromValue(static_cast<uint16_t>(val->ToUint32(_v8Context.Get(_v8Isolate)).ToLocalChecked()->Value()));
|
||||
break;
|
||||
case QMetaType::QObjectStar:
|
||||
dest = QVariant::fromValue(ScriptObjectQtProxy::unwrap(val));
|
||||
dest = QVariant::fromValue(ScriptObjectV8Proxy::unwrap(v8Val));
|
||||
break;
|
||||
default:
|
||||
// check to see if this is a pointer to a QObject-derived object
|
||||
|
@ -417,7 +439,7 @@ bool ScriptEngineQtScript::castValueToVariant(const QScriptValue& val, QVariant&
|
|||
dest = QVariant::fromValue(nullptr);
|
||||
break;
|
||||
}*/
|
||||
QObject* obj = ScriptObjectQtProxy::unwrap(val);
|
||||
QObject* obj = ScriptObjectV8Proxy::unwrap(v8Val);
|
||||
if (!obj) return false;
|
||||
const QMetaObject* destMeta = QMetaType::metaObjectForType(destTypeId);
|
||||
Q_ASSERT(destMeta);
|
||||
|
@ -428,14 +450,16 @@ bool ScriptEngineQtScript::castValueToVariant(const QScriptValue& val, QVariant&
|
|||
}
|
||||
// check to see if we have a registered prototype
|
||||
{
|
||||
QVariant var = ScriptVariantQtProxy::unwrap(val);
|
||||
QVariant var = ScriptVariantV8Proxy::unwrap(v8Val);
|
||||
if (var.isValid()) {
|
||||
dest = var;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// last chance, just convert it to a variant
|
||||
dest = val.toVariant();
|
||||
// V8TODO
|
||||
Q_ASSERT(false);
|
||||
//dest = val->ToVariant();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -443,24 +467,26 @@ bool ScriptEngineQtScript::castValueToVariant(const QScriptValue& val, QVariant&
|
|||
return destTypeId == QMetaType::UnknownType || dest.userType() == destTypeId || dest.convert(destTypeId);
|
||||
}
|
||||
|
||||
QString ScriptEngineQtScript::valueType(const QScriptValue& val) {
|
||||
if (val.isUndefined()) {
|
||||
QString ScriptEngineV8::valueType(const V8ScriptValue& v8Val) {
|
||||
const v8::Local<v8::Value> val = v8Val.constGet();
|
||||
|
||||
if (val->IsUndefined()) {
|
||||
return "undefined";
|
||||
}
|
||||
if (val.isNull()) {
|
||||
if (val->IsNull()) {
|
||||
return "null";
|
||||
}
|
||||
if (val.isBool()) {
|
||||
if (val->IsBoolean()) {
|
||||
return "boolean";
|
||||
}
|
||||
if (val.isString()) {
|
||||
if (val->IsString()) {
|
||||
return "string";
|
||||
}
|
||||
if (val.isNumber()) {
|
||||
if (val->IsNumber()) {
|
||||
return "number";
|
||||
}
|
||||
{
|
||||
QObject* obj = ScriptObjectQtProxy::unwrap(val);
|
||||
QObject* obj = ScriptObjectV8Proxy::unwrap(v8Val);
|
||||
if (obj) {
|
||||
QString objectName = obj->objectName();
|
||||
if (!objectName.isEmpty()) return objectName;
|
||||
|
@ -468,21 +494,24 @@ QString ScriptEngineQtScript::valueType(const QScriptValue& val) {
|
|||
}
|
||||
}
|
||||
{
|
||||
QVariant var = ScriptVariantQtProxy::unwrap(val);
|
||||
QVariant var = ScriptVariantV8Proxy::unwrap(v8Val);
|
||||
if (var.isValid()) {
|
||||
return var.typeName();
|
||||
}
|
||||
}
|
||||
return val.toVariant().typeName();
|
||||
//V8TODO
|
||||
Q_ASSERT(false);
|
||||
//return val->toVariant().typeName();
|
||||
return "undefined";
|
||||
}
|
||||
|
||||
QScriptValue ScriptEngineQtScript::castVariantToValue(const QVariant& val) {
|
||||
V8ScriptValue ScriptEngineV8::castVariantToValue(const QVariant& val) {
|
||||
int valTypeId = val.userType();
|
||||
|
||||
if (valTypeId == qMetaTypeId<ScriptValue>()) {
|
||||
// this is a wrapped ScriptValue, so just unwrap it and call it good
|
||||
ScriptValue innerVal = val.value<ScriptValue>();
|
||||
return ScriptValueQtWrapper::fullUnwrap(this, innerVal);
|
||||
return ScriptValueV8Wrapper::fullUnwrap(this, innerVal);
|
||||
}
|
||||
|
||||
// do we have a registered handler for this type?
|
||||
|
@ -497,61 +526,72 @@ QScriptValue ScriptEngineQtScript::castVariantToValue(const QVariant& val) {
|
|||
}
|
||||
if (marshalFunc) {
|
||||
ScriptValue wrappedVal = marshalFunc(this, val.constData());
|
||||
return ScriptValueQtWrapper::fullUnwrap(this, wrappedVal);
|
||||
return ScriptValueV8Wrapper::fullUnwrap(this, wrappedVal);
|
||||
}
|
||||
|
||||
switch (valTypeId) {
|
||||
case QMetaType::UnknownType:
|
||||
case QMetaType::Void:
|
||||
return QScriptValue(this, QScriptValue::UndefinedValue);
|
||||
return V8ScriptValue(_v8Isolate, v8::Undefined(_v8Isolate));
|
||||
case QMetaType::Nullptr:
|
||||
return QScriptValue(this, QScriptValue::NullValue);
|
||||
return V8ScriptValue(_v8Isolate, v8::Null(_v8Isolate));
|
||||
case QMetaType::Bool:
|
||||
return QScriptValue(this, val.toBool());
|
||||
return V8ScriptValue(_v8Isolate, v8::Boolean::New(_v8Isolate, val.toBool()));
|
||||
case QMetaType::Int:
|
||||
case QMetaType::Long:
|
||||
case QMetaType::Short:
|
||||
return QScriptValue(this, val.toInt());
|
||||
return V8ScriptValue(_v8Isolate, v8::Integer::New(_v8Isolate, val.toInt()));
|
||||
case QMetaType::UInt:
|
||||
case QMetaType::ULong:
|
||||
case QMetaType::UShort:
|
||||
return QScriptValue(this, val.toUInt());
|
||||
case QMetaType::Float:
|
||||
case QMetaType::LongLong:
|
||||
case QMetaType::ULong:
|
||||
return V8ScriptValue(_v8Isolate, v8::Uint32::New(_v8Isolate, val.toUInt()));
|
||||
case QMetaType::ULongLong:
|
||||
return V8ScriptValue(_v8Isolate, v8::Number::New(_v8Isolate, val.toULongLong()));
|
||||
case QMetaType::LongLong:
|
||||
return V8ScriptValue(_v8Isolate, v8::Number::New(_v8Isolate, val.toLongLong()));
|
||||
case QMetaType::Float:
|
||||
case QMetaType::Double:
|
||||
return QScriptValue(this, val.toFloat());
|
||||
return V8ScriptValue(_v8Isolate, v8::Number::New(_v8Isolate, val.toDouble()));
|
||||
case QMetaType::QString:
|
||||
case QMetaType::QByteArray:
|
||||
return QScriptValue(this, val.toString());
|
||||
return V8ScriptValue(_v8Isolate, v8::String::NewFromUtf8(_v8Isolate, val.toString().toStdString().c_str()).ToLocalChecked());
|
||||
case QMetaType::QVariant:
|
||||
return castVariantToValue(val.value<QVariant>());
|
||||
case QMetaType::QObjectStar: {
|
||||
QObject* obj = val.value<QObject*>();
|
||||
if (obj == nullptr) return QScriptValue(this, QScriptValue::NullValue);
|
||||
return ScriptObjectQtProxy::newQObject(this, obj);
|
||||
if (obj == nullptr) return V8ScriptValue(_v8Isolate, v8::Null(_v8Isolate));
|
||||
return ScriptObjectV8Proxy::newQObject(this, obj);
|
||||
}
|
||||
case QMetaType::QDateTime:
|
||||
return static_cast<QScriptEngine*>(this)->newDate(val.value<QDateTime>());
|
||||
{
|
||||
double timeMs = val.value<QDateTime>().currentMSecsSinceEpoch();
|
||||
return V8ScriptValue(_v8Isolate, v8::Date::New(_v8Context.Get(_v8Isolate), timeMs).ToLocalChecked());
|
||||
}
|
||||
case QMetaType::QDate:
|
||||
return static_cast<QScriptEngine*>(this)->newDate(val.value<QDate>().startOfDay());
|
||||
{
|
||||
double timeMs = val.value<QDate>().startOfDay().currentMSecsSinceEpoch();
|
||||
return V8ScriptValue(_v8Isolate, v8::Date::New(_v8Context.Get(_v8Isolate), timeMs).ToLocalChecked());
|
||||
}
|
||||
default:
|
||||
// check to see if this is a pointer to a QObject-derived object
|
||||
if (QMetaType::typeFlags(valTypeId) & (QMetaType::PointerToQObject | QMetaType::TrackingPointerToQObject)) {
|
||||
QObject* obj = val.value<QObject*>();
|
||||
if (obj == nullptr) return QScriptValue(this, QScriptValue::NullValue);
|
||||
return ScriptObjectQtProxy::newQObject(this, obj);
|
||||
if (obj == nullptr) return V8ScriptValue(_v8Isolate, v8::Null(_v8Isolate));
|
||||
return ScriptObjectV8Proxy::newQObject(this, obj);
|
||||
}
|
||||
// have we set a prototype'd variant?
|
||||
{
|
||||
_customTypeProtect.lockForRead();
|
||||
CustomPrototypeMap::const_iterator lookup = _customPrototypes.find(valTypeId);
|
||||
if (lookup != _customPrototypes.cend()) {
|
||||
return ScriptVariantQtProxy::newVariant(this, val, lookup.value());
|
||||
return ScriptVariantV8Proxy::newVariant(this, val, lookup.value());
|
||||
}
|
||||
_customTypeProtect.unlock();
|
||||
}
|
||||
// just do a generic variant
|
||||
return QScriptEngine::newVariant(val);
|
||||
//V8TODO
|
||||
Q_ASSERT(false);
|
||||
return V8ScriptValue(_v8Isolate, v8::Undefined(_v8Isolate));
|
||||
//return QScriptEngine::newVariant(val);
|
||||
}
|
||||
}
|
1105
libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp
Normal file
1105
libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp
Normal file
File diff suppressed because it is too large
Load diff
245
libraries/script-engine/src/v8/ScriptObjectV8Proxy.h
Normal file
245
libraries/script-engine/src/v8/ScriptObjectV8Proxy.h
Normal file
|
@ -0,0 +1,245 @@
|
|||
//
|
||||
// ScriptObjectV8Proxy.h
|
||||
// libraries/script-engine/src/v8
|
||||
//
|
||||
// Created by Heather Anderson on 12/5/21.
|
||||
// Modified for V8 by dr Karol Suprynowicz on 2022/10/08
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
// Copyright 2022 Overte e.V.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
/// @addtogroup ScriptEngine
|
||||
/// @{
|
||||
|
||||
#ifndef hifi_ScriptObjectV8Proxy_h
|
||||
#define hifi_ScriptObjectV8Proxy_h
|
||||
|
||||
#include <QtCore/QHash>
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtCore/QString>
|
||||
|
||||
#include "../ScriptEngine.h"
|
||||
#include "../Scriptable.h"
|
||||
#include "ScriptEngineV8.h"
|
||||
|
||||
#include <shared/ReadWriteLockable.h>
|
||||
|
||||
class ScriptEngineV8;
|
||||
class ScriptSignalV8Proxy;
|
||||
|
||||
/// [V8] (re-)implements the translation layer between ScriptValue and QObject. This object
|
||||
/// will focus exclusively on property get/set until function calls appear to be a problem
|
||||
class ScriptObjectV8Proxy final {
|
||||
private: // implementation
|
||||
class PropertyDef {
|
||||
public:
|
||||
PropertyDef(v8::Isolate *isolate, v8::Local<v8::String> string) : name(isolate, string) {};
|
||||
V8ScriptString name;
|
||||
ScriptValue::PropertyFlags flags;
|
||||
};
|
||||
class MethodDef {
|
||||
public:
|
||||
MethodDef(v8::Isolate *isolate, v8::Local<v8::String> string) : name(isolate, string) {};
|
||||
V8ScriptString name;
|
||||
int numMaxParms;
|
||||
QList<QMetaMethod> methods;
|
||||
};
|
||||
class SignalDef {
|
||||
public:
|
||||
SignalDef(v8::Isolate *isolate, v8::Local<v8::String> string) : name(isolate, string) {};
|
||||
V8ScriptString name;
|
||||
QMetaMethod signal;
|
||||
};
|
||||
using PropertyDefMap = QHash<uint, PropertyDef>;
|
||||
using MethodDefMap = QHash<uint, MethodDef>;
|
||||
using SignalDefMap = QHash<uint, SignalDef>;
|
||||
using InstanceMap = QHash<uint, QPointer<ScriptSignalV8Proxy> >;
|
||||
|
||||
static constexpr uint PROPERTY_TYPE = 0x1000;
|
||||
static constexpr uint METHOD_TYPE = 0x2000;
|
||||
static constexpr uint SIGNAL_TYPE = 0x3000;
|
||||
static constexpr uint TYPE_MASK = 0xF000;
|
||||
|
||||
public: // construction
|
||||
ScriptObjectV8Proxy(ScriptEngineV8* engine, QObject* object, bool ownsObject, const ScriptEngine::QObjectWrapOptions& options);
|
||||
virtual ~ScriptObjectV8Proxy();
|
||||
|
||||
static V8ScriptValue newQObject(ScriptEngineV8* engine,
|
||||
QObject* object,
|
||||
ScriptEngine::ValueOwnership ownership = ScriptEngine::QtOwnership,
|
||||
const ScriptEngine::QObjectWrapOptions& options = ScriptEngine::QObjectWrapOptions());
|
||||
static ScriptObjectV8Proxy* unwrapProxy(const V8ScriptValue& val);
|
||||
static QObject* unwrap(const V8ScriptValue& val);
|
||||
inline QObject* toQObject() const { return _object; }
|
||||
inline v8::Local<v8::Object> toV8Value() const {
|
||||
v8::EscapableHandleScope handleScope(_engine->getIsolate());
|
||||
return handleScope.Escape(_v8Object.Get(_engine->getIsolate()));
|
||||
}
|
||||
|
||||
public:
|
||||
enum QueryFlag
|
||||
{
|
||||
HandlesReadAccess = 0x00000001,
|
||||
HandlesWriteAccess = 0x00000002,
|
||||
};
|
||||
Q_DECLARE_FLAGS(QueryFlags, QueryFlag);
|
||||
|
||||
virtual QString name() const;
|
||||
|
||||
virtual V8ScriptValue property(const V8ScriptValue& object, const V8ScriptString& name, uint id);
|
||||
virtual ScriptValue::PropertyFlags propertyFlags(const V8ScriptValue& object, const V8ScriptString& name, uint id);
|
||||
//V8TODO
|
||||
virtual QueryFlags queryProperty(const V8ScriptValue& object, const V8ScriptString& name, QueryFlags flags, uint* id);
|
||||
virtual void setProperty(V8ScriptValue& object, const V8ScriptString& name, uint id, const V8ScriptValue& value);
|
||||
static void v8Get(v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info);
|
||||
static void v8Set(v8::Local<v8::Name> name, v8::Local<v8::Value> value_obj, const v8::PropertyCallbackInfo<v8::Value>& info);
|
||||
|
||||
private: // implementation
|
||||
void investigate();
|
||||
|
||||
private: // storage
|
||||
ScriptEngineV8* _engine;
|
||||
const ScriptEngine::QObjectWrapOptions _wrapOptions;
|
||||
PropertyDefMap _props;
|
||||
MethodDefMap _methods;
|
||||
SignalDefMap _signals;
|
||||
InstanceMap _signalInstances;
|
||||
const bool _ownsObject;
|
||||
QPointer<QObject> _object;
|
||||
// V8TODO Is this necessary?
|
||||
v8::UniquePersistent<v8::ObjectTemplate> _v8ObjectTemplate;
|
||||
// V8TODO Maybe it doesn't really need to point to itsef?
|
||||
v8::UniquePersistent<v8::Object> _v8Object;
|
||||
|
||||
Q_DISABLE_COPY(ScriptObjectV8Proxy)
|
||||
};
|
||||
|
||||
/// [V8] (re-)implements the translation layer between ScriptValue and QVariant where a prototype is set.
|
||||
/// This object depends on a ScriptObjectV8Proxy to provide the prototype's behavior
|
||||
class ScriptVariantV8Proxy final {
|
||||
public: // construction
|
||||
ScriptVariantV8Proxy(ScriptEngineV8* engine, const QVariant& variant, V8ScriptValue scriptProto, ScriptObjectV8Proxy* proto);
|
||||
|
||||
static V8ScriptValue newVariant(ScriptEngineV8* engine, const QVariant& variant, V8ScriptValue proto);
|
||||
static ScriptVariantV8Proxy* unwrapProxy(const V8ScriptValue& val);
|
||||
static QVariant unwrap(const V8ScriptValue& val);
|
||||
inline QVariant toQVariant() const { return _variant; }
|
||||
//inline QVariant toV8Value() const { return _variant; }
|
||||
inline v8::Local<v8::Object> toV8Value() const {
|
||||
v8::EscapableHandleScope handleScope(_engine->getIsolate());
|
||||
return handleScope.Escape(_v8Object.Get(_engine->getIsolate()));
|
||||
}
|
||||
|
||||
public: // QScriptClass implementation
|
||||
virtual QString name() const { return _name; }
|
||||
|
||||
virtual V8ScriptValue prototype() const { return _scriptProto; }
|
||||
|
||||
virtual V8ScriptValue property(const V8ScriptValue& object, const V8ScriptString& name, uint id) {
|
||||
return _proto->property(object, name, id);
|
||||
}
|
||||
virtual ScriptValue::PropertyFlags propertyFlags(const V8ScriptValue& object, const V8ScriptString& name, uint id) {
|
||||
return _proto->propertyFlags(object, name, id);
|
||||
}
|
||||
/*virtual QueryFlags queryProperty(const V8ScriptValue& object, const V8ScriptString& name, QueryFlags flags, uint* id) {
|
||||
return _proto->queryProperty(object, name, flags, id);
|
||||
}*/
|
||||
virtual void setProperty(V8ScriptValue& object, const V8ScriptString& name, uint id, const V8ScriptValue& value) {
|
||||
return _proto->setProperty(object, name, id, value);
|
||||
}
|
||||
|
||||
private: // storage
|
||||
ScriptEngineV8* _engine;
|
||||
QVariant _variant;
|
||||
V8ScriptValue _scriptProto;
|
||||
ScriptObjectV8Proxy* _proto;
|
||||
QString _name;
|
||||
v8::UniquePersistent<v8::ObjectTemplate> _v8ObjectTemplate;
|
||||
v8::UniquePersistent<v8::Object> _v8Object;
|
||||
|
||||
Q_DISABLE_COPY(ScriptVariantV8Proxy)
|
||||
};
|
||||
|
||||
class ScriptMethodV8Proxy final {
|
||||
public: // construction
|
||||
ScriptMethodV8Proxy(ScriptEngineV8* engine, QObject* object, V8ScriptValue lifetime,
|
||||
const QList<QMetaMethod>& metas, int numMaxParms);
|
||||
|
||||
public: // QScriptClass implementation
|
||||
virtual QString name() const { return fullName(); }
|
||||
//virtual bool supportsExtension(Extension extension) const;
|
||||
static void callback(const v8::FunctionCallbackInfo<v8::Value>& arguments);
|
||||
void call(const v8::FunctionCallbackInfo<v8::Value>& arguments);
|
||||
//virtual QVariant extension(Extension extension, const QVariant& argument = QVariant());
|
||||
static V8ScriptValue newMethod(ScriptEngineV8* engine, QObject* object, V8ScriptValue lifetime,
|
||||
const QList<QMetaMethod>& metas, int numMaxParams);
|
||||
|
||||
private:
|
||||
QString fullName() const;
|
||||
|
||||
private: // storage
|
||||
const int _numMaxParms;
|
||||
ScriptEngineV8* _engine;
|
||||
QPointer<QObject> _object;
|
||||
V8ScriptValue _objectLifetime;
|
||||
const QList<QMetaMethod> _metas;
|
||||
|
||||
Q_DISABLE_COPY(ScriptMethodV8Proxy)
|
||||
};
|
||||
|
||||
// This abstract base class serves solely to declare the Q_INVOKABLE methods for ScriptSignalV8Proxy
|
||||
// as we're overriding qt_metacall later for the signal callback yet still want to support
|
||||
// metacalls for the connect/disconnect API
|
||||
class ScriptSignalV8ProxyBase : public QObject, protected Scriptable {
|
||||
Q_OBJECT
|
||||
public: // API
|
||||
// arg1 was had Null default value, but that needs isolate pointer in V8
|
||||
Q_INVOKABLE virtual void connect(V8ScriptValue arg0, V8ScriptValue arg1) = 0;
|
||||
Q_INVOKABLE virtual void disconnect(V8ScriptValue arg0, V8ScriptValue arg1) = 0;
|
||||
};
|
||||
|
||||
class ScriptSignalV8Proxy final : public ScriptSignalV8ProxyBase, public ReadWriteLockable {
|
||||
private: // storage
|
||||
class Connection {
|
||||
public:
|
||||
V8ScriptValue thisValue;
|
||||
V8ScriptValue callback;
|
||||
Connection(const V8ScriptValue &v8ThisValue, const V8ScriptValue &v8Callback) :
|
||||
thisValue(v8ThisValue), callback(v8Callback) {};
|
||||
};
|
||||
using ConnectionList = QList<Connection>;
|
||||
|
||||
public: // construction
|
||||
inline ScriptSignalV8Proxy(ScriptEngineV8* engine, QObject* object, V8ScriptValue lifetime, const QMetaMethod& meta) :
|
||||
_engine(engine), _object(object), _objectLifetime(lifetime), _meta(meta), _metaCallId(discoverMetaCallIdx()) {}
|
||||
|
||||
private: // implementation
|
||||
virtual int qt_metacall(QMetaObject::Call call, int id, void** arguments) override;
|
||||
int discoverMetaCallIdx();
|
||||
ConnectionList::iterator findConnection(V8ScriptValue thisObject, V8ScriptValue callback);
|
||||
QString fullName() const;
|
||||
|
||||
public: // API
|
||||
// arg1 was had Null default value, but that needs isolate pointer to cerate Null in V8
|
||||
virtual void connect(V8ScriptValue arg0, V8ScriptValue arg1) override;
|
||||
virtual void disconnect(V8ScriptValue arg0, V8ScriptValue arg1) override;
|
||||
|
||||
private: // storage
|
||||
ScriptEngineV8* _engine;
|
||||
QPointer<QObject> _object;
|
||||
V8ScriptValue _objectLifetime;
|
||||
const QMetaMethod _meta;
|
||||
const int _metaCallId;
|
||||
ConnectionList _connections;
|
||||
bool _isConnected{ false };
|
||||
|
||||
Q_DISABLE_COPY(ScriptSignalV8Proxy)
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptObjectV8Proxy_h
|
||||
|
||||
/// @}
|
64
libraries/script-engine/src/v8/ScriptProgramV8Wrapper.cpp
Normal file
64
libraries/script-engine/src/v8/ScriptProgramV8Wrapper.cpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
// ScriptProgramV8Wrapper.cpp
|
||||
// libraries/script-engine/src/v8
|
||||
//
|
||||
// Created by Heather Anderson on 8/24/21.
|
||||
// Modified for V8 by dr Karol Suprynowicz on 2022/10/08
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
// Copyright 2022 Overte e.V.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "ScriptProgramV8Wrapper.h"
|
||||
|
||||
#include "ScriptEngineV8.h"
|
||||
#include "ScriptValueV8Wrapper.h"
|
||||
|
||||
ScriptProgramV8Wrapper* ScriptProgramV8Wrapper::unwrap(ScriptProgramPointer val) {
|
||||
if (!val) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dynamic_cast<ScriptProgramV8Wrapper*>(val.get());
|
||||
}
|
||||
|
||||
ScriptSyntaxCheckResultPointer ScriptProgramV8Wrapper::checkSyntax() {
|
||||
if (!_isCompiled) {
|
||||
compile();
|
||||
}
|
||||
return std::make_shared<ScriptSyntaxCheckResultV8Wrapper>(_compileResult);
|
||||
}
|
||||
|
||||
bool ScriptProgramV8Wrapper::compile() {
|
||||
int errorColumnNumber = 0;
|
||||
int errorLineNumber = 0;
|
||||
QString errorMessage = "";
|
||||
QString errorBacktrace = "";
|
||||
ScriptSyntaxCheckResult::State state;
|
||||
v8::TryCatch tryCatch(_engine->getIsolate());
|
||||
v8::ScriptOrigin scriptOrigin(_engine->getIsolate(), v8::String::NewFromUtf8(_engine->getIsolate(), _url.toStdString().c_str()).ToLocalChecked());
|
||||
v8::Local<v8::Script> script;
|
||||
if (v8::Script::Compile(_engine->getContext(), v8::String::NewFromUtf8(_engine->getIsolate(), _source.toStdString().c_str()).ToLocalChecked(), &scriptOrigin).ToLocal(&script)) {
|
||||
_compileResult = ScriptSyntaxCheckResultV8Wrapper(ScriptSyntaxCheckResult::Valid);
|
||||
_value = V8ScriptProgram(_engine->getIsolate(), script);
|
||||
return true;
|
||||
}
|
||||
v8::String::Utf8Value utf8Value(_engine->getIsolate(), tryCatch.Exception());
|
||||
errorMessage = QString(*utf8Value);
|
||||
v8::Local<v8::Message> exceptionMessage = tryCatch.Message();
|
||||
if (!exceptionMessage.IsEmpty()) {
|
||||
errorLineNumber = exceptionMessage->GetLineNumber(_engine->getContext()).FromJust();
|
||||
errorColumnNumber = exceptionMessage->GetStartColumn(_engine->getContext()).FromJust();
|
||||
v8::Local<v8::Value> backtraceV8String;
|
||||
if (tryCatch.StackTrace(_engine->getContext()).ToLocal(&backtraceV8String) && backtraceV8String->IsString() &&
|
||||
v8::Local<v8::String>::Cast(backtraceV8String)->Length() > 0) {
|
||||
v8::String::Utf8Value backtraceUtf8Value(_engine->getIsolate(), backtraceV8String);
|
||||
errorBacktrace = *backtraceUtf8Value;
|
||||
}
|
||||
}
|
||||
//V8TODO
|
||||
_compileResult = ScriptSyntaxCheckResultV8Wrapper(ScriptSyntaxCheckResult::Error, errorColumnNumber, errorLineNumber, errorMessage, errorBacktrace);
|
||||
return false;
|
||||
}
|
76
libraries/script-engine/src/v8/ScriptProgramV8Wrapper.h
Normal file
76
libraries/script-engine/src/v8/ScriptProgramV8Wrapper.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// ScriptProgramV8Wrapper.h
|
||||
// libraries/script-engine/src/v8
|
||||
//
|
||||
// Created by Heather Anderson on 5/21/21.
|
||||
// Modified for V8 by dr Karol Suprynowicz on 2022/10/08
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
// Copyright 2022 Overte e.V.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
/// @addtogroup ScriptEngine
|
||||
/// @{
|
||||
|
||||
#ifndef hifi_ScriptProgramV8Wrapper_h
|
||||
#define hifi_ScriptProgramV8Wrapper_h
|
||||
|
||||
#include <QtCore/QPointer>
|
||||
|
||||
#include "../ScriptProgram.h"
|
||||
#include "ScriptEngineV8.h"
|
||||
|
||||
class ScriptSyntaxCheckResultV8Wrapper final : public ScriptSyntaxCheckResult {
|
||||
public: // construction
|
||||
inline ScriptSyntaxCheckResultV8Wrapper() : _errorColumnNumber(0), _errorLineNumber(0), _errorMessage("Not compiled"), _state(ScriptSyntaxCheckResult::Error) {}
|
||||
inline ScriptSyntaxCheckResultV8Wrapper(State state, int columnNumber = 0, int lineNumber = 0, const QString &message = QString(""), const QString &errorBacktrace = QString("")) :
|
||||
_errorColumnNumber(columnNumber), _errorLineNumber(lineNumber), _errorMessage(message), _state(state) {}
|
||||
|
||||
public: // ScriptSyntaxCheckResult implementation
|
||||
virtual int errorColumnNumber() const override {return _errorColumnNumber;}
|
||||
virtual int errorLineNumber() const override {return _errorLineNumber;}
|
||||
virtual QString errorMessage() const override {return _errorMessage;}
|
||||
virtual QString errorBacktrace() const override {return _errorBacktrace;}
|
||||
virtual State state() const override {return _state;}
|
||||
|
||||
private: // storage
|
||||
int _errorColumnNumber;
|
||||
int _errorLineNumber;
|
||||
QString _errorMessage;
|
||||
QString _errorBacktrace;
|
||||
State _state;
|
||||
};
|
||||
|
||||
/// [V8] Implements ScriptProgram for V8 and translates calls for V8ScriptProgram
|
||||
class ScriptProgramV8Wrapper final : public ScriptProgram {
|
||||
public: // construction
|
||||
/*inline ScriptProgramV8Wrapper(ScriptEngineV8* engine, const V8ScriptProgram& value) :
|
||||
_engine(engine), _value(value) {}*/
|
||||
//inline ScriptProgramV8Wrapper(ScriptEngineV8* engine, V8ScriptProgram&& value) :
|
||||
// _engine(engine), _value(std::move(value)) {}
|
||||
inline ScriptProgramV8Wrapper(ScriptEngineV8* engine, QString source, QString url) :
|
||||
_engine(engine), _source(source), _url(url), _value(engine->getIsolate(), v8::Local<v8::Script>()) {}
|
||||
static ScriptProgramV8Wrapper* unwrap(ScriptProgramPointer val);
|
||||
bool compile();
|
||||
inline const V8ScriptProgram& toV8Value() const { return _value; }
|
||||
|
||||
public: // ScriptProgram implementation
|
||||
virtual ScriptSyntaxCheckResultPointer checkSyntax() override;
|
||||
virtual QString fileName() const override {return _url;}
|
||||
virtual QString sourceCode() const override {return _source;}
|
||||
|
||||
private: // storage
|
||||
ScriptEngineV8 *_engine;
|
||||
QString _source;
|
||||
QString _url;
|
||||
V8ScriptProgram _value;
|
||||
bool _isCompiled = false;
|
||||
ScriptSyntaxCheckResultV8Wrapper _compileResult;
|
||||
//V8TODO: how to make program run on multiple isolates?
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptValueV8Wrapper_h
|
||||
|
||||
/// @}
|
|
@ -0,0 +1,82 @@
|
|||
//
|
||||
// ScriptValueIteratorV8Wrapper.cpp
|
||||
// libraries/script-engine/src/v8
|
||||
//
|
||||
// Created by Heather Anderson on 8/29/21.
|
||||
// Modified for V8 by dr Karol Suprynowicz on 2022/10/08
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
// Copyright 2022 Overte e.V.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "ScriptValueIteratorV8Wrapper.h"
|
||||
|
||||
V8ScriptValueIterator::V8ScriptValueIterator(ScriptEngineV8* engine, v8::Local<v8::Value> object) : _engine(engine) {
|
||||
_context.Reset(_engine->getIsolate(), _engine->getContext());
|
||||
auto context = _context.Get(_engine->getIsolate());
|
||||
v8::Local<v8::Object> v8Object;
|
||||
if (!object->ToObject(context).ToLocal(&v8Object)) {
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
_object.Reset(_engine->getIsolate(), v8Object);
|
||||
_propertyNames.Reset(_engine->getIsolate(), v8Object->GetOwnPropertyNames(context).ToLocalChecked());
|
||||
_length = _propertyNames.Get(_engine->getIsolate())->Length();
|
||||
_currentIndex = 0;
|
||||
}
|
||||
|
||||
bool V8ScriptValueIterator::hasNext() const {
|
||||
return _currentIndex < _length - 1;
|
||||
}
|
||||
|
||||
QString V8ScriptValueIterator::name() const {
|
||||
auto context = _context.Get(_engine->getIsolate());
|
||||
v8::Local<v8::Value> propertyName;
|
||||
if (!_propertyNames.Get(_engine->getIsolate())->Get(context, _length).ToLocal(&propertyName)) {
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
return QString(*v8::String::Utf8Value(_engine->getIsolate(), propertyName));
|
||||
}
|
||||
|
||||
void V8ScriptValueIterator::next() {
|
||||
if (_length < _currentIndex - 1) {
|
||||
_length++;
|
||||
}
|
||||
}
|
||||
|
||||
V8ScriptValue V8ScriptValueIterator::value() {
|
||||
auto context = _context.Get(_engine->getIsolate());
|
||||
v8::Local<v8::Value> v8Value;
|
||||
v8::Local<v8::Value> propertyName;
|
||||
if (!_propertyNames.Get(_engine->getIsolate())->Get(context, _length).ToLocal(&propertyName)) {
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
if (!_object.Get(_engine->getIsolate())->Get(context, propertyName->ToString(context).ToLocalChecked()).ToLocal(&v8Value)) {
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
return V8ScriptValue(_engine->getIsolate(), v8Value);
|
||||
}
|
||||
|
||||
ScriptValue::PropertyFlags ScriptValueIteratorV8Wrapper::flags() const {
|
||||
//V8TODO
|
||||
//return (ScriptValue::PropertyFlags)(int)_value.flags();
|
||||
return (ScriptValue::PropertyFlags)(0);
|
||||
}
|
||||
|
||||
bool ScriptValueIteratorV8Wrapper::hasNext() const {
|
||||
return _value->hasNext();
|
||||
}
|
||||
|
||||
QString ScriptValueIteratorV8Wrapper::name() const {
|
||||
return _value->name();
|
||||
}
|
||||
|
||||
void ScriptValueIteratorV8Wrapper::next() {
|
||||
_value->next();
|
||||
}
|
||||
|
||||
ScriptValue ScriptValueIteratorV8Wrapper::value() const {
|
||||
V8ScriptValue result = _value->value();
|
||||
return ScriptValue(new ScriptValueV8Wrapper(_engine, std::move(result)));
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
// ScriptValueIteratorV8Wrapper.h
|
||||
// libraries/script-engine/src/v8
|
||||
//
|
||||
// Created by Heather Anderson on 8/29/21.
|
||||
// Modified for V8 by dr Karol Suprynowicz on 2022/10/08
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
// Copyright 2022 Overte e.V.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
/// @addtogroup ScriptEngine
|
||||
/// @{
|
||||
|
||||
#ifndef hifi_ScriptValueIteratorV8Wrapper_h
|
||||
#define hifi_ScriptValueIteratorV8Wrapper_h
|
||||
|
||||
#include <QtCore/QPointer>
|
||||
|
||||
#include "../ScriptValueIterator.h"
|
||||
#include "ScriptEngineV8.h"
|
||||
#include "ScriptValueV8Wrapper.h"
|
||||
|
||||
class V8ScriptValueIterator {
|
||||
public:
|
||||
V8ScriptValueIterator(ScriptEngineV8* engine, v8::Local<v8::Value> object);
|
||||
bool hasNext() const;
|
||||
QString name() const;
|
||||
void next();
|
||||
V8ScriptValue value();
|
||||
private:
|
||||
v8::UniquePersistent<v8::Array> _propertyNames;
|
||||
v8::UniquePersistent<v8::Object> _object;
|
||||
v8::UniquePersistent<v8::Context> _context;
|
||||
int _length;
|
||||
int _currentIndex;
|
||||
ScriptEngineV8 *_engine;
|
||||
};
|
||||
|
||||
/// [V8] Implements ScriptValueIterator for V8 and translates calls for V8ScriptValueIterator
|
||||
class ScriptValueIteratorV8Wrapper final : public ScriptValueIterator {
|
||||
public: // construction
|
||||
inline ScriptValueIteratorV8Wrapper(ScriptEngineV8* engine, const ScriptValue& object) :
|
||||
_engine(engine), _value(new V8ScriptValueIterator(engine, ScriptValueV8Wrapper::fullUnwrap(engine, object).get())) {}
|
||||
inline ScriptValueIteratorV8Wrapper(ScriptEngineV8* engine, const V8ScriptValue& object) :
|
||||
_engine(engine), _value(new V8ScriptValueIterator(engine, object.constGet())) {}
|
||||
|
||||
public: // ScriptValueIterator implementation
|
||||
virtual ScriptValue::PropertyFlags flags() const override;
|
||||
virtual bool hasNext() const override;
|
||||
virtual QString name() const override;
|
||||
virtual void next() override;
|
||||
virtual ScriptValue value() const override;
|
||||
|
||||
private: // storage
|
||||
ScriptEngineV8 *_engine;
|
||||
std::shared_ptr<V8ScriptValueIterator> _value;
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptValueIteratorV8Wrapper_h
|
||||
|
||||
/// @}
|
367
libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp
Normal file
367
libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp
Normal file
|
@ -0,0 +1,367 @@
|
|||
//
|
||||
// ScriptValueV8Wrapper.cpp
|
||||
// libraries/script-engine/src/v8
|
||||
//
|
||||
// Created by Heather Anderson on 5/16/21.
|
||||
// Modified for V8 by dr Karol Suprynowicz on 2022/10/08
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
// Copyright 2022 Overte e.V.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "ScriptValueV8Wrapper.h"
|
||||
|
||||
#include "ScriptValueIteratorV8Wrapper.h"
|
||||
|
||||
#include "../ScriptEngineLogging.h"
|
||||
|
||||
|
||||
void ScriptValueV8Wrapper::release() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
ScriptValueProxy* ScriptValueV8Wrapper::copy() const {
|
||||
//V8TODO: check if the value needs to be copied or just wrapper
|
||||
return new ScriptValueV8Wrapper(_engine, _value);
|
||||
}
|
||||
|
||||
ScriptValueV8Wrapper* ScriptValueV8Wrapper::unwrap(const ScriptValue& val) {
|
||||
return dynamic_cast<ScriptValueV8Wrapper*>(val.ptr());
|
||||
}
|
||||
|
||||
V8ScriptValue ScriptValueV8Wrapper::fullUnwrap(const ScriptValue& value) const {
|
||||
ScriptValueV8Wrapper* unwrapped = unwrap(value);
|
||||
if (unwrapped) {
|
||||
if (unwrapped->engine().get() != _engine) {
|
||||
//return _engine->toScriptValue(unwrapped->toVariant());
|
||||
return _engine->castVariantToValue(unwrapped->toVariant());
|
||||
} else {
|
||||
return unwrapped->toV8Value();
|
||||
}
|
||||
}
|
||||
QVariant varValue = value.toVariant();
|
||||
return _engine->castVariantToValue(varValue);
|
||||
}
|
||||
|
||||
V8ScriptValue ScriptValueV8Wrapper::fullUnwrap(ScriptEngineV8* engine, const ScriptValue& value) {
|
||||
ScriptValueV8Wrapper* unwrapped = unwrap(value);
|
||||
if (unwrapped) {
|
||||
if (unwrapped->engine().get() != engine) {
|
||||
//return static_cast<QScriptEngine*>(engine)->toScriptValue(unwrapped->toVariant());
|
||||
return engine->castVariantToValue(unwrapped->toVariant());
|
||||
} else {
|
||||
return unwrapped->toV8Value();
|
||||
}
|
||||
}
|
||||
QVariant varValue = value.toVariant();
|
||||
return engine->castVariantToValue(varValue);
|
||||
}
|
||||
|
||||
ScriptValue ScriptValueV8Wrapper::call(const ScriptValue& thisObject, const ScriptValueList& args) {
|
||||
V8ScriptValue v8This = fullUnwrap(thisObject);
|
||||
//V8ScriptValueList qArgs;
|
||||
Q_ASSERT(args.length() <= Q_METAMETHOD_INVOKE_MAX_ARGS);
|
||||
//V8TODO I'm not sure how else to do this since v8::Local should probably be on stack, not heap
|
||||
v8::Local<v8::Value> v8Args[Q_METAMETHOD_INVOKE_MAX_ARGS];
|
||||
int argIndex = 0;
|
||||
for (ScriptValueList::const_iterator iter = args.begin(); iter != args.end(); ++iter) {
|
||||
v8Args[argIndex++] = fullUnwrap(*iter).get();
|
||||
}
|
||||
//V8TODO should there be a v8 try-catch here?
|
||||
// IsFunction check should be here probably
|
||||
v8::Local<v8::Function> v8Function = v8::Local<v8::Function>::Cast(_value.get());
|
||||
auto maybeResult = v8Function->Call(_engine->getContext(), v8This.get(), args.length(), v8Args);
|
||||
v8::Local<v8::Value> result;
|
||||
if (maybeResult.ToLocal(&result)) {
|
||||
return ScriptValue(new ScriptValueV8Wrapper(_engine, V8ScriptValue(_engine->getIsolate(), result)));
|
||||
} else {
|
||||
//V8TODO Add more details
|
||||
qWarning("JS function call failed");
|
||||
return _engine->undefinedValue();
|
||||
}
|
||||
}
|
||||
|
||||
ScriptValue ScriptValueV8Wrapper::call(const ScriptValue& thisObject, const ScriptValue& arguments) {
|
||||
V8ScriptValue v8This = fullUnwrap(thisObject);
|
||||
V8ScriptValue v8Args = fullUnwrap(arguments);
|
||||
// V8TODO should there be a v8 try-catch here?
|
||||
// IsFunction check should be here probably
|
||||
// V8TODO I'm not sure in what format arguments are yet, backtrace will show how it is used
|
||||
Q_ASSERT(false);
|
||||
return _engine->undefinedValue();
|
||||
/*v8::Local<v8::Function> v8Function = v8::Local<v8::Function>::Cast(_value.get());
|
||||
auto maybeResult = v8Function->Call(_engine->getContext(), v8This, v8Args);
|
||||
v8::Local<v8::Value> result;
|
||||
if (maybeResult.ToLocal(&result)) {
|
||||
return ScriptValue(new ScriptValueV8Wrapper(_engine, V8ScriptValue(_engine->getContext(), result)));
|
||||
} else {
|
||||
//V8TODO Add more details
|
||||
qWarning("JS function call failed");
|
||||
return _engine->undefinedValue();
|
||||
}*/
|
||||
}
|
||||
|
||||
ScriptValue ScriptValueV8Wrapper::construct(const ScriptValueList& args) {
|
||||
Q_ASSERT(args.length() <= Q_METAMETHOD_INVOKE_MAX_ARGS);
|
||||
//V8TODO I'm not sure how else to do this since v8::Local should probably be on stack, not heap
|
||||
v8::Local<v8::Value> v8Args[Q_METAMETHOD_INVOKE_MAX_ARGS];
|
||||
int argIndex = 0;
|
||||
for (ScriptValueList::const_iterator iter = args.begin(); iter != args.end(); ++iter) {
|
||||
v8Args[argIndex++] = fullUnwrap(*iter).get();
|
||||
}
|
||||
//V8TODO should there be a v8 try-catch here?
|
||||
// IsFunction check should be here probably
|
||||
v8::Local<v8::Function> v8Function = v8::Local<v8::Function>::Cast(_value.get());
|
||||
auto maybeResult = v8Function->NewInstance(_engine->getContext(), args.length(), v8Args);
|
||||
v8::Local<v8::Object> result;
|
||||
if (maybeResult.ToLocal(&result)) {
|
||||
return ScriptValue(new ScriptValueV8Wrapper(_engine, V8ScriptValue(_engine->getIsolate(), result)));
|
||||
} else {
|
||||
//V8TODO Add more details
|
||||
qWarning("JS function call failed");
|
||||
return _engine->undefinedValue();
|
||||
}
|
||||
}
|
||||
|
||||
ScriptValue ScriptValueV8Wrapper::construct(const ScriptValue& arguments) {
|
||||
// V8TODO I'm not sure in what format arguments are yet, backtrace will show how it is used
|
||||
Q_ASSERT(false);
|
||||
return _engine->undefinedValue();
|
||||
//V8ScriptValue unwrapped = fullUnwrap(arguments);
|
||||
//V8ScriptValue result = _value.construct(unwrapped);
|
||||
//return ScriptValue(new ScriptValueV8Wrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValue ScriptValueV8Wrapper::data() const {
|
||||
//V8TODO I'm not sure how this would work in V8
|
||||
//V8ScriptValue result = _value.data();
|
||||
//return ScriptValue(new ScriptValueV8Wrapper(_engine, std::move(result)));
|
||||
return _engine->nullValue();
|
||||
}
|
||||
|
||||
ScriptEnginePointer ScriptValueV8Wrapper::engine() const {
|
||||
if (!_engine) {
|
||||
return ScriptEnginePointer();
|
||||
}
|
||||
return _engine->shared_from_this();
|
||||
}
|
||||
|
||||
ScriptValueIteratorPointer ScriptValueV8Wrapper::newIterator() const {
|
||||
return std::make_shared<ScriptValueIteratorV8Wrapper>(_engine, _value);
|
||||
}
|
||||
|
||||
ScriptValue ScriptValueV8Wrapper::property(const QString& name, const ScriptValue::ResolveFlags& mode) const {
|
||||
if (!_value.constGet()->IsObject()) {
|
||||
//V8TODO: what about flags?
|
||||
v8::Local<v8::Value> resultLocal;
|
||||
v8::Local<v8::String> key = v8::String::NewFromUtf8(_engine->getIsolate(), name.toStdString().c_str(),v8::NewStringType::kNormal).ToLocalChecked();
|
||||
if (_value.constGet()->ToObject(_value.constGetContext()).ToLocalChecked()
|
||||
->Get(_value.constGetContext(), key).ToLocal(&resultLocal)) {
|
||||
V8ScriptValue result(_engine->getIsolate(), resultLocal);
|
||||
return ScriptValue(new ScriptValueV8Wrapper(_engine, std::move(result)));
|
||||
}
|
||||
}
|
||||
v8::Local<v8::Value> nullValue = v8::Null(_engine->getIsolate());
|
||||
V8ScriptValue nullScriptValue(_engine->getIsolate(), std::move(nullValue));
|
||||
return ScriptValue(new ScriptValueV8Wrapper(_engine, nullScriptValue));
|
||||
//V8ScriptValue result = _value.property(name, (V8ScriptValue::ResolveFlags)(int)mode);
|
||||
//return ScriptValue(new ScriptValueV8Wrapper(_engine, std::move(result)));
|
||||
}
|
||||
|
||||
ScriptValue ScriptValueV8Wrapper::property(quint32 arrayIndex, const ScriptValue::ResolveFlags& mode) const {
|
||||
if (!_value.constGet()->IsObject()) {
|
||||
//V8TODO: what about flags?
|
||||
v8::Local<v8::Value> resultLocal;
|
||||
if (_value.constGet()->ToObject(_value.constGetContext()).ToLocalChecked()
|
||||
->Get(_value.constGetContext(), arrayIndex).ToLocal(&resultLocal)) {
|
||||
V8ScriptValue result(_engine->getIsolate(), resultLocal);
|
||||
return ScriptValue(new ScriptValueV8Wrapper(_engine, std::move(result)));
|
||||
}
|
||||
}
|
||||
v8::Local<v8::Value> nullValue = v8::Null(_engine->getIsolate());
|
||||
V8ScriptValue nullScriptValue(_engine->getIsolate(), std::move(nullValue));
|
||||
return ScriptValue(new ScriptValueV8Wrapper(_engine, nullScriptValue));
|
||||
}
|
||||
|
||||
void ScriptValueV8Wrapper::setData(const ScriptValue& value) {
|
||||
V8ScriptValue unwrapped = fullUnwrap(value);
|
||||
(**_value.get()) = (**unwrapped.get());
|
||||
}
|
||||
|
||||
void ScriptValueV8Wrapper::setProperty(const QString& name, const ScriptValue& value, const ScriptValue::PropertyFlags& flags) {
|
||||
V8ScriptValue unwrapped = fullUnwrap(value);
|
||||
if(_value.constGet()->IsObject()) {
|
||||
v8::Local<v8::String> key = v8::String::NewFromUtf8(_engine->getIsolate(), name.toStdString().c_str(),v8::NewStringType::kNormal).ToLocalChecked();
|
||||
v8::Maybe<bool> retVal = _value.get()->ToObject(_value.getContext()).ToLocalChecked()
|
||||
->Set(_value.getContext(), key, unwrapped.constGet());
|
||||
if (retVal.IsJust() ? !retVal.FromJust() : true){
|
||||
qDebug(scriptengine) << "Failed to set property";
|
||||
}
|
||||
} else {
|
||||
qDebug(scriptengine) << "Failed to set property - parent is not an object";
|
||||
}
|
||||
//V8TODO: what about flags?
|
||||
//_value.setProperty(name, unwrapped, (V8ScriptValue::PropertyFlags)(int)flags);
|
||||
}
|
||||
|
||||
void ScriptValueV8Wrapper::setProperty(quint32 arrayIndex, const ScriptValue& value, const ScriptValue::PropertyFlags& flags) {
|
||||
V8ScriptValue unwrapped = fullUnwrap(value);
|
||||
if(_value.constGet()->IsObject()) {
|
||||
v8::Maybe<bool> retVal(_value.get()->ToObject(_value.getContext()).ToLocalChecked()
|
||||
->Set(_value.getContext(), arrayIndex, unwrapped.constGet()));
|
||||
if (retVal.IsJust() ? !retVal.FromJust() : true){
|
||||
qDebug(scriptengine) << "Failed to set property";
|
||||
}
|
||||
} else {
|
||||
qDebug(scriptengine) << "Failed to set property - parent is not an object";
|
||||
}
|
||||
//V8TODO: what about flags?
|
||||
//_value.setProperty(arrayIndex, unwrapped, (V8ScriptValue::PropertyFlags)(int)flags);
|
||||
}
|
||||
|
||||
void ScriptValueV8Wrapper::setPrototype(const ScriptValue& prototype) {
|
||||
ScriptValueV8Wrapper* unwrappedPrototype = unwrap(prototype);
|
||||
if (unwrappedPrototype) {
|
||||
if(unwrappedPrototype->toV8Value().constGet()->IsObject() && _value.constGet()->IsObject()) {
|
||||
v8::Maybe<bool> retVal = _value.get()->ToObject(_value.getContext()).ToLocalChecked()
|
||||
->SetPrototype(_value.getContext(), unwrappedPrototype->toV8Value().constGet());
|
||||
if (retVal.IsJust() ? !retVal.FromJust() : true){
|
||||
qDebug(scriptengine) << "Failed to assign prototype";
|
||||
}
|
||||
} else {
|
||||
qDebug(scriptengine) << "Failed to assign prototype - one of values is not an object";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptValueV8Wrapper::strictlyEquals(const ScriptValue& other) const {
|
||||
ScriptValueV8Wrapper* unwrappedOther = unwrap(other);
|
||||
return unwrappedOther ? _value.constGet()->StrictEquals(unwrappedOther->toV8Value().constGet()) : false;
|
||||
}
|
||||
|
||||
bool ScriptValueV8Wrapper::toBool() const {
|
||||
return _value.constGet()->ToBoolean(_engine->getIsolate())->Value();
|
||||
}
|
||||
|
||||
qint32 ScriptValueV8Wrapper::toInt32() const {
|
||||
v8::Local<v8::Integer> *integer;
|
||||
Q_ASSERT(_value.constGet()->ToInteger(_value.constGetContext()).ToLocal(integer));
|
||||
return static_cast<int32_t>((*integer)->Value());
|
||||
}
|
||||
|
||||
double ScriptValueV8Wrapper::toInteger() const {
|
||||
v8::Local<v8::Integer> *integer;
|
||||
Q_ASSERT(_value.constGet()->ToInteger(_value.constGetContext()).ToLocal(integer));
|
||||
return (*integer)->Value();
|
||||
}
|
||||
|
||||
double ScriptValueV8Wrapper::toNumber() const {
|
||||
v8::Local<v8::Number> *number;
|
||||
Q_ASSERT(_value.constGet()->ToNumber(_value.constGetContext()).ToLocal(number));
|
||||
return (*number)->Value();
|
||||
}
|
||||
|
||||
QString ScriptValueV8Wrapper::toString() const {
|
||||
v8::String::Utf8Value string(_engine->getIsolate(), _value.constGet());
|
||||
Q_ASSERT(*string != nullptr);
|
||||
return QString(*string);
|
||||
}
|
||||
|
||||
quint16 ScriptValueV8Wrapper::toUInt16() const {
|
||||
v8::Local<v8::Uint32> *integer;
|
||||
Q_ASSERT(_value.constGet()->ToUint32(_value.constGetContext()).ToLocal(integer));
|
||||
return static_cast<uint16_t>((*integer)->Value());
|
||||
}
|
||||
|
||||
quint32 ScriptValueV8Wrapper::toUInt32() const {
|
||||
v8::Local<v8::Uint32> *integer;
|
||||
Q_ASSERT(_value.constGet()->ToUint32(_value.constGetContext()).ToLocal(integer));
|
||||
return (*integer)->Value();
|
||||
}
|
||||
|
||||
QVariant ScriptValueV8Wrapper::toVariant() const {
|
||||
QVariant dest;
|
||||
if (_engine->castValueToVariant(_value, dest, QMetaType::UnknownType)) {
|
||||
return dest;
|
||||
} else {
|
||||
Q_ASSERT(false);
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
QObject* ScriptValueV8Wrapper::toQObject() const {
|
||||
QVariant dest;
|
||||
if (_engine->castValueToVariant(_value, dest, QMetaType::QObjectStar)) {
|
||||
return dest.value<QObject*>();
|
||||
} else {
|
||||
Q_ASSERT(false);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptValueV8Wrapper::equals(const ScriptValue& other) const {
|
||||
ScriptValueV8Wrapper* unwrappedOther = unwrap(other);
|
||||
//V8TODO: does this work with different contexts/isolates?
|
||||
// in such case conversion will probably be necessary
|
||||
if (!unwrappedOther) {
|
||||
return false;
|
||||
}else{
|
||||
if (_value.constGet()->Equals(unwrappedOther->toV8Value().constGetContext(), unwrappedOther->toV8Value().constGet()).IsNothing()) {
|
||||
return false;
|
||||
} else {
|
||||
return _value.constGet()->Equals(unwrappedOther->toV8Value().constGetContext(), unwrappedOther->toV8Value().constGet()).FromJust();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptValueV8Wrapper::isArray() const {
|
||||
return _value.constGet()->IsArray();
|
||||
}
|
||||
|
||||
bool ScriptValueV8Wrapper::isBool() const {
|
||||
return _value.constGet()->IsBoolean();
|
||||
}
|
||||
|
||||
bool ScriptValueV8Wrapper::isError() const {
|
||||
//V8TODO
|
||||
return false;
|
||||
//return _value.constGet()->IsError();
|
||||
}
|
||||
|
||||
bool ScriptValueV8Wrapper::isFunction() const {
|
||||
return _value.constGet()->IsFunction();
|
||||
}
|
||||
|
||||
bool ScriptValueV8Wrapper::isNumber() const {
|
||||
return _value.constGet()->IsNumber();
|
||||
}
|
||||
|
||||
bool ScriptValueV8Wrapper::isNull() const {
|
||||
return _value.constGet()->IsNull();
|
||||
}
|
||||
|
||||
bool ScriptValueV8Wrapper::isObject() const {
|
||||
return _value.constGet()->IsObject();
|
||||
}
|
||||
|
||||
bool ScriptValueV8Wrapper::isString() const {
|
||||
return _value.constGet()->IsString();
|
||||
}
|
||||
|
||||
bool ScriptValueV8Wrapper::isUndefined() const {
|
||||
return _value.constGet()->IsUndefined();
|
||||
}
|
||||
|
||||
bool ScriptValueV8Wrapper::isValid() const {
|
||||
//V8TODO
|
||||
return true;
|
||||
//return _value.constGet()->IsValid();
|
||||
}
|
||||
|
||||
bool ScriptValueV8Wrapper::isVariant() const {
|
||||
//V8TODO
|
||||
return false;
|
||||
//return _value.isVariant();
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
//
|
||||
// ScriptValueQtWrapper.h
|
||||
// libraries/script-engine/src/qtscript
|
||||
// ScriptValueV8Wrapper.h
|
||||
// libraries/script-engine/src/v8
|
||||
//
|
||||
// Created by Heather Anderson on 5/16/21.
|
||||
// Modified for V8 by dr Karol Suprynowicz on 2022/10/08
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
// Copyright 2022 Overte e.V.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -12,27 +14,27 @@
|
|||
/// @addtogroup ScriptEngine
|
||||
/// @{
|
||||
|
||||
#ifndef hifi_ScriptValueQtWrapper_h
|
||||
#define hifi_ScriptValueQtWrapper_h
|
||||
#ifndef hifi_ScriptValueV8Wrapper_h
|
||||
#define hifi_ScriptValueV8Wrapper_h
|
||||
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtScript/QScriptValue>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "../ScriptValue.h"
|
||||
#include "ScriptEngineQtScript.h"
|
||||
#include "ScriptEngineV8.h"
|
||||
|
||||
/// [QtScript] Implements ScriptValue for QtScript and translates calls for QScriptValue
|
||||
class ScriptValueQtWrapper final : public ScriptValueProxy {
|
||||
/// [V8] Implements ScriptValue for V8 and translates calls for V8ScriptValue
|
||||
class ScriptValueV8Wrapper final : public ScriptValueProxy {
|
||||
public: // construction
|
||||
inline ScriptValueQtWrapper(ScriptEngineQtScript* engine, const QScriptValue& value) :
|
||||
inline ScriptValueV8Wrapper(ScriptEngineV8* engine, const V8ScriptValue& value) :
|
||||
_engine(engine), _value(value) {}
|
||||
inline ScriptValueQtWrapper(ScriptEngineQtScript* engine, QScriptValue&& value) :
|
||||
// _engine(engine), _value(std::move(value.copy())) {}
|
||||
inline ScriptValueV8Wrapper(ScriptEngineV8* engine, V8ScriptValue&& value) :
|
||||
_engine(engine), _value(std::move(value)) {}
|
||||
static ScriptValueQtWrapper* unwrap(const ScriptValue& val);
|
||||
inline const QScriptValue& toQtValue() const { return _value; }
|
||||
static QScriptValue fullUnwrap(ScriptEngineQtScript* engine, const ScriptValue& value);
|
||||
static ScriptValueV8Wrapper* unwrap(const ScriptValue& val);
|
||||
inline const V8ScriptValue& toV8Value() const { return _value; }
|
||||
static V8ScriptValue fullUnwrap(ScriptEngineV8* engine, const ScriptValue& value);
|
||||
|
||||
public:
|
||||
virtual void release() override;
|
||||
|
@ -84,13 +86,13 @@ public: // ScriptValue implementation
|
|||
virtual QObject* toQObject() const override;
|
||||
|
||||
private: // helper functions
|
||||
QScriptValue fullUnwrap(const ScriptValue& value) const;
|
||||
V8ScriptValue fullUnwrap(const ScriptValue& value) const;
|
||||
|
||||
private: // storage
|
||||
QPointer<ScriptEngineQtScript> _engine;
|
||||
QScriptValue _value;
|
||||
ScriptEngineV8 *_engine;
|
||||
V8ScriptValue _value;
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptValueQtWrapper_h
|
||||
#endif // hifi_ScriptValueV8Wrapper_h
|
||||
|
||||
/// @}
|
|
@ -15,17 +15,18 @@
|
|||
|
||||
#include "TypedArrays.h"
|
||||
|
||||
Q_DECLARE_METATYPE(QByteArray*)
|
||||
// V8TODO
|
||||
/*Q_DECLARE_METATYPE(QByteArray*)
|
||||
|
||||
TypedArrayPrototype::TypedArrayPrototype(QObject* parent) : QObject(parent) {
|
||||
}
|
||||
|
||||
QByteArray* TypedArrayPrototype::thisArrayBuffer() const {
|
||||
QScriptValue bufferObject = thisObject().data().property(BUFFER_PROPERTY_NAME);
|
||||
V8ScriptValue bufferObject = thisObject().data().property(BUFFER_PROPERTY_NAME);
|
||||
return qscriptvalue_cast<QByteArray*>(bufferObject.data());
|
||||
}
|
||||
|
||||
void TypedArrayPrototype::set(QScriptValue array, qint32 offset) {
|
||||
void TypedArrayPrototype::set(V8ScriptValue array, qint32 offset) {
|
||||
TypedArray* typedArray = static_cast<TypedArray*>(parent());
|
||||
if (array.isArray() || typedArray) {
|
||||
if (offset < 0) {
|
||||
|
@ -44,9 +45,9 @@ void TypedArrayPrototype::set(QScriptValue array, qint32 offset) {
|
|||
}
|
||||
}
|
||||
|
||||
QScriptValue TypedArrayPrototype::subarray(qint32 begin) {
|
||||
V8ScriptValue TypedArrayPrototype::subarray(qint32 begin) {
|
||||
TypedArray* typedArray = static_cast<TypedArray*>(parent());
|
||||
QScriptValue arrayBuffer = thisObject().data().property(typedArray->_bufferName);
|
||||
V8ScriptValue arrayBuffer = thisObject().data().property(typedArray->_bufferName);
|
||||
qint32 byteOffset = thisObject().data().property(typedArray->_byteOffsetName).toInt32();
|
||||
qint32 length = thisObject().data().property(typedArray->_lengthName).toInt32();
|
||||
qint32 bytesPerElement = typedArray->_bytesPerElement;
|
||||
|
@ -61,9 +62,9 @@ QScriptValue TypedArrayPrototype::subarray(qint32 begin) {
|
|||
return typedArray->newInstance(arrayBuffer, byteOffset, length - begin);
|
||||
}
|
||||
|
||||
QScriptValue TypedArrayPrototype::subarray(qint32 begin, qint32 end) {
|
||||
V8ScriptValue TypedArrayPrototype::subarray(qint32 begin, qint32 end) {
|
||||
TypedArray* typedArray = static_cast<TypedArray*>(parent());
|
||||
QScriptValue arrayBuffer = thisObject().data().property(typedArray->_bufferName);
|
||||
V8ScriptValue arrayBuffer = thisObject().data().property(typedArray->_bufferName);
|
||||
qint32 byteOffset = thisObject().data().property(typedArray->_byteOffsetName).toInt32();
|
||||
qint32 length = thisObject().data().property(typedArray->_lengthName).toInt32();
|
||||
qint32 bytesPerElement = typedArray->_bytesPerElement;
|
||||
|
@ -83,9 +84,9 @@ QScriptValue TypedArrayPrototype::subarray(qint32 begin, qint32 end) {
|
|||
return typedArray->newInstance(arrayBuffer, byteOffset, length);
|
||||
}
|
||||
|
||||
QScriptValue TypedArrayPrototype::get(quint32 index) {
|
||||
V8ScriptValue TypedArrayPrototype::get(quint32 index) {
|
||||
TypedArray* typedArray = static_cast<TypedArray*>(parent());
|
||||
QScriptString name = engine()->toStringHandle(QString::number(index));
|
||||
V8ScriptString name = engine()->toStringHandle(QString::number(index));
|
||||
uint id;
|
||||
QScriptClass::QueryFlags flags = typedArray->queryProperty(thisObject(),
|
||||
name,
|
||||
|
@ -93,13 +94,13 @@ QScriptValue TypedArrayPrototype::get(quint32 index) {
|
|||
if (QScriptClass::HandlesReadAccess & flags) {
|
||||
return typedArray->property(thisObject(), name, id);
|
||||
}
|
||||
return QScriptValue();
|
||||
return V8ScriptValue();
|
||||
}
|
||||
|
||||
void TypedArrayPrototype::set(quint32 index, QScriptValue& value) {
|
||||
void TypedArrayPrototype::set(quint32 index, V8ScriptValue& value) {
|
||||
TypedArray* typedArray = static_cast<TypedArray*>(parent());
|
||||
QScriptValue object = thisObject();
|
||||
QScriptString name = engine()->toStringHandle(QString::number(index));
|
||||
V8ScriptValue object = thisObject();
|
||||
V8ScriptString name = engine()->toStringHandle(QString::number(index));
|
||||
uint id;
|
||||
QScriptClass::QueryFlags flags = typedArray->queryProperty(object,
|
||||
name,
|
||||
|
@ -108,3 +109,4 @@ void TypedArrayPrototype::set(quint32 index, QScriptValue& value) {
|
|||
typedArray->setProperty(object, name, id, value);
|
||||
}
|
||||
}
|
||||
*/
|
|
@ -16,26 +16,28 @@
|
|||
#define hifi_TypedArrayPrototype_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtScript/QScriptable>
|
||||
#include <QtScript/QScriptValue>
|
||||
|
||||
/// [QtScript] The javascript functions associated with a <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray">TypedArray</a></code> instance prototype
|
||||
class TypedArrayPrototype : public QObject, public QScriptable {
|
||||
#include "V8Types.h"
|
||||
#include "../Scriptable.h"
|
||||
|
||||
// V8TODO
|
||||
/// [V8] The javascript functions associated with a <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray">TypedArray</a></code> instance prototype
|
||||
/*class TypedArrayPrototype : public QObject, public Scriptable {
|
||||
Q_OBJECT
|
||||
public:
|
||||
TypedArrayPrototype(QObject* parent = NULL);
|
||||
|
||||
public slots:
|
||||
void set(QScriptValue array, qint32 offset = 0);
|
||||
QScriptValue subarray(qint32 begin);
|
||||
QScriptValue subarray(qint32 begin, qint32 end);
|
||||
void set(V8ScriptValue array, qint32 offset = 0);
|
||||
V8ScriptValue subarray(qint32 begin);
|
||||
V8ScriptValue subarray(qint32 begin, qint32 end);
|
||||
|
||||
QScriptValue get(quint32 index);
|
||||
void set(quint32 index, QScriptValue& value);
|
||||
V8ScriptValue get(quint32 index);
|
||||
void set(quint32 index, V8ScriptValue& value);
|
||||
private:
|
||||
QByteArray* thisArrayBuffer() const;
|
||||
};
|
||||
|
||||
*/
|
||||
#endif // hifi_TypedArrayPrototype_h
|
||||
|
||||
/// @}
|
|
@ -18,17 +18,18 @@
|
|||
#include <SharedUtil.h>
|
||||
|
||||
#include "ArrayBufferClass.h"
|
||||
#include "ScriptEngineQtScript.h"
|
||||
#include "ScriptEngineV8.h"
|
||||
#include "TypedArrayPrototype.h"
|
||||
|
||||
Q_DECLARE_METATYPE(QByteArray*)
|
||||
// V8TODO
|
||||
/*Q_DECLARE_METATYPE(QByteArray*)
|
||||
|
||||
TypedArray::TypedArray(ScriptEngineQtScript* scriptEngine, QString name) : ArrayBufferViewClass(scriptEngine) {
|
||||
TypedArray::TypedArray(ScriptEngineV8* scriptEngine, QString name) : ArrayBufferViewClass(scriptEngine) {
|
||||
_bytesPerElementName = engine()->toStringHandle(BYTES_PER_ELEMENT_PROPERTY_NAME.toLatin1());
|
||||
_lengthName = engine()->toStringHandle(LENGTH_PROPERTY_NAME.toLatin1());
|
||||
_name = engine()->toStringHandle(name.toLatin1());
|
||||
|
||||
QScriptValue global = engine()->globalObject();
|
||||
V8ScriptValue global = engine()->globalObject();
|
||||
|
||||
// build prototype
|
||||
_proto = engine()->newQObject(new TypedArrayPrototype(this),
|
||||
|
@ -44,30 +45,30 @@ TypedArray::TypedArray(ScriptEngineQtScript* scriptEngine, QString name) : Array
|
|||
engine()->globalObject().setProperty(_name, _ctor);
|
||||
}
|
||||
|
||||
QScriptValue TypedArray::newInstance(quint32 length) {
|
||||
V8ScriptValue TypedArray::newInstance(quint32 length) {
|
||||
ArrayBufferClass* array = getScriptEngine()->getArrayBufferClass();
|
||||
QScriptValue buffer = array->newInstance(length * _bytesPerElement);
|
||||
V8ScriptValue buffer = array->newInstance(length * _bytesPerElement);
|
||||
return newInstance(buffer, 0, length);
|
||||
}
|
||||
|
||||
QScriptValue TypedArray::newInstance(QScriptValue array) {
|
||||
V8ScriptValue TypedArray::newInstance(V8ScriptValue array) {
|
||||
const QString ARRAY_LENGTH_HANDLE = "length";
|
||||
if (array.property(ARRAY_LENGTH_HANDLE).isValid()) {
|
||||
quint32 length = array.property(ARRAY_LENGTH_HANDLE).toInt32();
|
||||
QScriptValue newArray = newInstance(length);
|
||||
V8ScriptValue newArray = newInstance(length);
|
||||
for (quint32 i = 0; i < length; ++i) {
|
||||
QScriptValue value = array.property(QString::number(i));
|
||||
V8ScriptValue value = array.property(QString::number(i));
|
||||
setProperty(newArray, engine()->toStringHandle(QString::number(i)),
|
||||
i * _bytesPerElement, (value.isNumber()) ? value : QScriptValue(0));
|
||||
i * _bytesPerElement, (value.isNumber()) ? value : V8ScriptValue(0));
|
||||
}
|
||||
return newArray;
|
||||
}
|
||||
engine()->evaluate("throw \"ArgumentError: not an array\"");
|
||||
return QScriptValue();
|
||||
return V8ScriptValue();
|
||||
}
|
||||
|
||||
QScriptValue TypedArray::newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length) {
|
||||
QScriptValue data = engine()->newObject();
|
||||
V8ScriptValue TypedArray::newInstance(V8ScriptValue buffer, quint32 byteOffset, quint32 length) {
|
||||
V8ScriptValue data = engine()->newObject();
|
||||
data.setProperty(_bufferName, buffer);
|
||||
data.setProperty(_byteOffsetName, byteOffset);
|
||||
data.setProperty(_byteLengthName, length * _bytesPerElement);
|
||||
|
@ -76,17 +77,17 @@ QScriptValue TypedArray::newInstance(QScriptValue buffer, quint32 byteOffset, qu
|
|||
return engine()->newObject(this, data);
|
||||
}
|
||||
|
||||
QScriptValue TypedArray::construct(QScriptContext* context, QScriptEngine* engine) {
|
||||
V8ScriptValue TypedArray::construct(V8ScriptContext* context, QScriptEngine* engine) {
|
||||
TypedArray* cls = qscriptvalue_cast<TypedArray*>(context->callee().data());
|
||||
if (!cls) {
|
||||
return QScriptValue();
|
||||
return V8ScriptValue();
|
||||
}
|
||||
if (context->argumentCount() == 0) {
|
||||
return cls->newInstance(0);
|
||||
}
|
||||
|
||||
QScriptValue newObject;
|
||||
QScriptValue bufferArg = context->argument(0);
|
||||
V8ScriptValue newObject;
|
||||
V8ScriptValue bufferArg = context->argument(0);
|
||||
QByteArray* arrayBuffer = qscriptvalue_cast<QByteArray*>(bufferArg.data());
|
||||
|
||||
// parse arguments
|
||||
|
@ -99,14 +100,14 @@ QScriptValue TypedArray::construct(QScriptContext* context, QScriptEngine* engin
|
|||
quint32 length = arrayBuffer->size() / cls->_bytesPerElement;
|
||||
newObject = cls->newInstance(bufferArg, 0, length);
|
||||
} else {
|
||||
QScriptValue byteOffsetArg = context->argument(1);
|
||||
V8ScriptValue byteOffsetArg = context->argument(1);
|
||||
if (!byteOffsetArg.isNumber()) {
|
||||
engine->evaluate("throw \"ArgumentError: 2nd arg is not a number\"");
|
||||
return QScriptValue();
|
||||
return V8ScriptValue();
|
||||
}
|
||||
if (byteOffsetArg.toInt32() < 0 || byteOffsetArg.toInt32() > arrayBuffer->size()) {
|
||||
engine->evaluate("throw \"RangeError: byteOffset out of range\"");
|
||||
return QScriptValue();
|
||||
return V8ScriptValue();
|
||||
}
|
||||
if (byteOffsetArg.toInt32() % cls->_bytesPerElement != 0) {
|
||||
engine->evaluate("throw \"RangeError: byteOffset not a multiple of BYTES_PER_ELEMENT\"");
|
||||
|
@ -122,15 +123,15 @@ QScriptValue TypedArray::construct(QScriptContext* context, QScriptEngine* engin
|
|||
newObject = cls->newInstance(bufferArg, byteOffset, length);
|
||||
} else {
|
||||
|
||||
QScriptValue lengthArg = (context->argumentCount() > 2) ? context->argument(2) : QScriptValue();
|
||||
V8ScriptValueee lengthArg = (context->argumentCount() > 2) ? context->argument(2) : V8ScriptValue();
|
||||
if (!lengthArg.isNumber()) {
|
||||
engine->evaluate("throw \"ArgumentError: 3nd arg is not a number\"");
|
||||
return QScriptValue();
|
||||
return V8ScriptValue();
|
||||
}
|
||||
if (lengthArg.toInt32() < 0 ||
|
||||
byteOffsetArg.toInt32() + lengthArg.toInt32() * (qint32)(cls->_bytesPerElement) > arrayBuffer->size()) {
|
||||
engine->evaluate("throw \"RangeError: byteLength out of range\"");
|
||||
return QScriptValue();
|
||||
return V8ScriptValue();
|
||||
}
|
||||
quint32 length = lengthArg.toInt32();
|
||||
|
||||
|
@ -154,8 +155,8 @@ QScriptValue TypedArray::construct(QScriptContext* context, QScriptEngine* engin
|
|||
return newObject;
|
||||
}
|
||||
|
||||
QScriptClass::QueryFlags TypedArray::queryProperty(const QScriptValue& object,
|
||||
const QScriptString& name,
|
||||
QScriptClass::QueryFlags TypedArray::queryProperty(const V8ScriptValue& object,
|
||||
const V8ScriptString& name,
|
||||
QueryFlags flags, uint* id) {
|
||||
if (name == _bytesPerElementName || name == _lengthName) {
|
||||
return flags &= HandlesReadAccess; // Only keep read access flags
|
||||
|
@ -175,10 +176,10 @@ QScriptClass::QueryFlags TypedArray::queryProperty(const QScriptValue& object,
|
|||
return ArrayBufferViewClass::queryProperty(object, name, flags, id);
|
||||
}
|
||||
|
||||
QScriptValue TypedArray::property(const QScriptValue& object,
|
||||
const QScriptString& name, uint id) {
|
||||
V8ScriptValue TypedArray::property(const V8ScriptValue& object,
|
||||
const V8ScriptString& name, uint id) {
|
||||
if (name == _bytesPerElementName) {
|
||||
return QScriptValue(_bytesPerElement);
|
||||
return V8ScriptValue(_bytesPerElement);
|
||||
}
|
||||
if (name == _lengthName) {
|
||||
return object.data().property(_lengthName);
|
||||
|
@ -186,16 +187,16 @@ QScriptValue TypedArray::property(const QScriptValue& object,
|
|||
return ArrayBufferViewClass::property(object, name, id);
|
||||
}
|
||||
|
||||
QScriptValue::PropertyFlags TypedArray::propertyFlags(const QScriptValue& object,
|
||||
const QScriptString& name, uint id) {
|
||||
return QScriptValue::Undeletable;
|
||||
V8ScriptValue::PropertyFlags TypedArray::propertyFlags(const V8ScriptValue& object,
|
||||
const V8ScriptString& name, uint id) {
|
||||
return V8ScriptValue::Undeletable;
|
||||
}
|
||||
|
||||
QString TypedArray::name() const {
|
||||
return _name.toString();
|
||||
}
|
||||
|
||||
QScriptValue TypedArray::prototype() const {
|
||||
V8ScriptValue TypedArray::prototype() const {
|
||||
return _proto;
|
||||
}
|
||||
|
||||
|
@ -207,7 +208,7 @@ void TypedArray::setBytesPerElement(quint32 bytesPerElement) {
|
|||
// templated helper functions
|
||||
// don't work for floats as they require single precision settings
|
||||
template<class T>
|
||||
QScriptValue propertyHelper(const QByteArray* arrayBuffer, const QScriptString& name, uint id) {
|
||||
V8ScriptValue propertyHelper(const QByteArray* arrayBuffer, const V8ScriptString& name, uint id) {
|
||||
bool ok = false;
|
||||
name.toArrayIndex(&ok);
|
||||
|
||||
|
@ -220,11 +221,11 @@ QScriptValue propertyHelper(const QByteArray* arrayBuffer, const QScriptString&
|
|||
stream >> result;
|
||||
return result;
|
||||
}
|
||||
return QScriptValue();
|
||||
return V8ScriptValue();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void setPropertyHelper(QByteArray* arrayBuffer, const QScriptString& name, uint id, const QScriptValue& value) {
|
||||
void setPropertyHelper(QByteArray* arrayBuffer, const V8ScriptString& name, uint id, const V8ScriptValue& value) {
|
||||
if (arrayBuffer && value.isNumber()) {
|
||||
QDataStream stream(arrayBuffer, QIODevice::ReadWrite);
|
||||
stream.skipRawData(id);
|
||||
|
@ -234,50 +235,50 @@ void setPropertyHelper(QByteArray* arrayBuffer, const QScriptString& name, uint
|
|||
}
|
||||
}
|
||||
|
||||
Int8ArrayClass::Int8ArrayClass(ScriptEngineQtScript* scriptEngine) : TypedArray(scriptEngine, INT_8_ARRAY_CLASS_NAME) {
|
||||
Int8ArrayClass::Int8ArrayClass(ScriptEngineV8* scriptEngine) : TypedArray(scriptEngine, INT_8_ARRAY_CLASS_NAME) {
|
||||
setBytesPerElement(sizeof(qint8));
|
||||
}
|
||||
|
||||
QScriptValue Int8ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) {
|
||||
V8ScriptValue Int8ArrayClass::property(const V8ScriptValue& object, const V8ScriptString& name, uint id) {
|
||||
QByteArray* arrayBuffer = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
QScriptValue result = propertyHelper<qint8>(arrayBuffer, name, id);
|
||||
V8ScriptValue result = propertyHelper<qint8>(arrayBuffer, name, id);
|
||||
return (result.isValid()) ? result : TypedArray::property(object, name, id);
|
||||
}
|
||||
|
||||
void Int8ArrayClass::setProperty(QScriptValue &object, const QScriptString &name,
|
||||
uint id, const QScriptValue& value) {
|
||||
void Int8ArrayClass::setProperty(V8ScriptValue &object, const V8ScriptString &name,
|
||||
uint id, const V8ScriptValue& value) {
|
||||
QByteArray* ba = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
setPropertyHelper<qint8>(ba, name, id, value);
|
||||
}
|
||||
|
||||
Uint8ArrayClass::Uint8ArrayClass(ScriptEngineQtScript* scriptEngine) : TypedArray(scriptEngine, UINT_8_ARRAY_CLASS_NAME) {
|
||||
Uint8ArrayClass::Uint8ArrayClass(ScriptEngineV8ptEngine) : TypedArray(scriptEngine, UINT_8_ARRAY_CLASS_NAME) {
|
||||
setBytesPerElement(sizeof(quint8));
|
||||
}
|
||||
|
||||
QScriptValue Uint8ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) {
|
||||
V8ScriptValue Uint8ArrayClass::property(const V8ScriptValue& object, const V8ScriptString& name, uint id) {
|
||||
QByteArray* arrayBuffer = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
QScriptValue result = propertyHelper<quint8>(arrayBuffer, name, id);
|
||||
V8ScriptValue result = propertyHelper<quint8>(arrayBuffer, name, id);
|
||||
return (result.isValid()) ? result : TypedArray::property(object, name, id);
|
||||
}
|
||||
|
||||
void Uint8ArrayClass::setProperty(QScriptValue& object, const QScriptString& name,
|
||||
uint id, const QScriptValue& value) {
|
||||
void Uint8ArrayClass::setProperty(V8ScriptValue& object, const V8ScriptString& name,
|
||||
uint id, const V8ScriptValue& value) {
|
||||
QByteArray* ba = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
setPropertyHelper<quint8>(ba, name, id, value);
|
||||
}
|
||||
|
||||
Uint8ClampedArrayClass::Uint8ClampedArrayClass(ScriptEngineQtScript* scriptEngine) : TypedArray(scriptEngine, UINT_8_CLAMPED_ARRAY_CLASS_NAME) {
|
||||
Uint8ClampedArrayClass::Uint8ClampedArrayClass(ScriptEngineV8* scriptEngine) : TypedArray(scriptEngine, UINT_8_CLAMPED_ARRAY_CLASS_NAME) {
|
||||
setBytesPerElement(sizeof(quint8));
|
||||
}
|
||||
|
||||
QScriptValue Uint8ClampedArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) {
|
||||
V8ScriptValue Uint8ClampedArrayClass::property(const V8ScriptValue& object, const V8ScriptString& name, uint id) {
|
||||
QByteArray* arrayBuffer = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
QScriptValue result = propertyHelper<quint8>(arrayBuffer, name, id);
|
||||
V8ScriptValue result = propertyHelper<quint8>(arrayBuffer, name, id);
|
||||
return (result.isValid()) ? result : TypedArray::property(object, name, id);
|
||||
}
|
||||
|
||||
void Uint8ClampedArrayClass::setProperty(QScriptValue& object, const QScriptString& name,
|
||||
uint id, const QScriptValue& value) {
|
||||
void Uint8ClampedArrayClass::setProperty(V8ScriptValue& object, const V8ScriptString& name,
|
||||
uint id, const V8ScriptValue& value) {
|
||||
QByteArray* ba = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
if (ba && value.isNumber()) {
|
||||
QDataStream stream(ba, QIODevice::ReadWrite);
|
||||
|
@ -292,76 +293,76 @@ void Uint8ClampedArrayClass::setProperty(QScriptValue& object, const QScriptStri
|
|||
}
|
||||
}
|
||||
|
||||
Int16ArrayClass::Int16ArrayClass(ScriptEngineQtScript* scriptEngine) : TypedArray(scriptEngine, INT_16_ARRAY_CLASS_NAME) {
|
||||
Int16ArrayClass::Int16ArrayClass(ScriptEngineV8* scriptEngine) : TypedArray(scriptEngine, INT_16_ARRAY_CLASS_NAME) {
|
||||
setBytesPerElement(sizeof(qint16));
|
||||
}
|
||||
|
||||
QScriptValue Int16ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) {
|
||||
V8ScriptValue Int16ArrayClass::property(const V8ScriptValue& object, const V8ScriptString& name, uint id) {
|
||||
QByteArray* arrayBuffer = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
QScriptValue result = propertyHelper<qint16>(arrayBuffer, name, id);
|
||||
V8ScriptValue result = propertyHelper<qint16>(arrayBuffer, name, id);
|
||||
return (result.isValid()) ? result : TypedArray::property(object, name, id);
|
||||
}
|
||||
|
||||
void Int16ArrayClass::setProperty(QScriptValue& object, const QScriptString& name,
|
||||
uint id, const QScriptValue& value) {
|
||||
void Int16ArrayClass::setProperty(V8ScriptValue& object, const V8ScriptString& name,
|
||||
uint id, const V8ScriptValue& value) {
|
||||
QByteArray* ba = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
setPropertyHelper<qint16>(ba, name, id, value);
|
||||
}
|
||||
|
||||
Uint16ArrayClass::Uint16ArrayClass(ScriptEngineQtScript* scriptEngine) : TypedArray(scriptEngine, UINT_16_ARRAY_CLASS_NAME) {
|
||||
Uint16ArrayClass::Uint16ArrayClass(ScriptEngineV8* scriptEngine) : TypedArray(scriptEngine, UINT_16_ARRAY_CLASS_NAME) {
|
||||
setBytesPerElement(sizeof(quint16));
|
||||
}
|
||||
|
||||
QScriptValue Uint16ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) {
|
||||
V8ScriptValue Uint16ArrayClass::property(const V8ScriptValue& object, const V8ScriptString& name, uint id) {
|
||||
QByteArray* arrayBuffer = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
QScriptValue result = propertyHelper<quint16>(arrayBuffer, name, id);
|
||||
V8ScriptValue result = propertyHelper<quint16>(arrayBuffer, name, id);
|
||||
return (result.isValid()) ? result : TypedArray::property(object, name, id);
|
||||
}
|
||||
|
||||
void Uint16ArrayClass::setProperty(QScriptValue& object, const QScriptString& name,
|
||||
uint id, const QScriptValue& value) {
|
||||
void Uint16ArrayClass::setProperty(V8ScriptValue& object, const V8ScriptString& name,
|
||||
uint id, const V8ScriptValue& value) {
|
||||
QByteArray* ba = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
setPropertyHelper<quint16>(ba, name, id, value);
|
||||
}
|
||||
|
||||
Int32ArrayClass::Int32ArrayClass(ScriptEngineQtScript* scriptEngine) : TypedArray(scriptEngine, INT_32_ARRAY_CLASS_NAME) {
|
||||
Int32ArrayClass::Int32ArrayClass(ScriptEngineV8* scriptEngine) : TypedArray(scriptEngine, INT_32_ARRAY_CLASS_NAME) {
|
||||
setBytesPerElement(sizeof(qint32));
|
||||
}
|
||||
|
||||
QScriptValue Int32ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) {
|
||||
V8ScriptValue Int32ArrayClass::property(const V8ScriptValue& object, const V8ScriptString& name, uint id) {
|
||||
QByteArray* arrayBuffer = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
QScriptValue result = propertyHelper<qint32>(arrayBuffer, name, id);
|
||||
V8ScriptValue result = propertyHelper<qint32>(arrayBuffer, name, id);
|
||||
return (result.isValid()) ? result : TypedArray::property(object, name, id);
|
||||
}
|
||||
|
||||
void Int32ArrayClass::setProperty(QScriptValue& object, const QScriptString& name,
|
||||
uint id, const QScriptValue& value) {
|
||||
void Int32ArrayClass::setProperty(V8ScriptValue& object, const V8ScriptString& name,
|
||||
uint id, const V8ScriptValue& value) {
|
||||
QByteArray* ba = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
setPropertyHelper<qint32>(ba, name, id, value);
|
||||
}
|
||||
|
||||
Uint32ArrayClass::Uint32ArrayClass(ScriptEngineQtScript* scriptEngine) : TypedArray(scriptEngine, UINT_32_ARRAY_CLASS_NAME) {
|
||||
Uint32ArrayClass::Uint32ArrayClass(ScriptEngineV8* scriptEngine) : TypedArray(scriptEngine, UINT_32_ARRAY_CLASS_NAME) {
|
||||
setBytesPerElement(sizeof(quint32));
|
||||
}
|
||||
|
||||
QScriptValue Uint32ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) {
|
||||
V8ScriptValue Uint32ArrayClass::property(const V8ScriptValue& object, const V8ScriptString& name, uint id) {
|
||||
|
||||
QByteArray* arrayBuffer = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
QScriptValue result = propertyHelper<quint32>(arrayBuffer, name, id);
|
||||
V8ScriptValue result = propertyHelper<quint32>(arrayBuffer, name, id);
|
||||
return (result.isValid()) ? result : TypedArray::property(object, name, id);
|
||||
}
|
||||
|
||||
void Uint32ArrayClass::setProperty(QScriptValue& object, const QScriptString& name,
|
||||
uint id, const QScriptValue& value) {
|
||||
void Uint32ArrayClass::setProperty(V8ScriptValue& object, const V8ScriptString& name,
|
||||
uint id, const V8ScriptValue& value) {
|
||||
QByteArray* ba = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
setPropertyHelper<quint32>(ba, name, id, value);
|
||||
}
|
||||
|
||||
Float32ArrayClass::Float32ArrayClass(ScriptEngineQtScript* scriptEngine) : TypedArray(scriptEngine, FLOAT_32_ARRAY_CLASS_NAME) {
|
||||
Float32ArrayClass::Float32ArrayClass(ScriptEngineV8* scriptEngine) : TypedArray(scriptEngine, FLOAT_32_ARRAY_CLASS_NAME) {
|
||||
setBytesPerElement(sizeof(float));
|
||||
}
|
||||
|
||||
QScriptValue Float32ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) {
|
||||
V8ScriptValue Float32ArrayClass::property(const V8ScriptValuee& object, const V8ScriptString& name, uint id) {
|
||||
QByteArray* arrayBuffer = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());bool ok = false;
|
||||
name.toArrayIndex(&ok);
|
||||
|
||||
|
@ -374,15 +375,15 @@ QScriptValue Float32ArrayClass::property(const QScriptValue& object, const QScri
|
|||
float result;
|
||||
stream >> result;
|
||||
if (isNaN(result)) {
|
||||
return QScriptValue();
|
||||
return V8ScriptValuee();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return TypedArray::property(object, name, id);
|
||||
}
|
||||
|
||||
void Float32ArrayClass::setProperty(QScriptValue& object, const QScriptString& name,
|
||||
uint id, const QScriptValue& value) {
|
||||
void Float32ArrayClass::setProperty(V8ScriptValue& object, const V8ScriptString& name,
|
||||
uint id, const V8ScriptValue& value) {
|
||||
QByteArray* ba = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
if (ba && value.isNumber()) {
|
||||
QDataStream stream(ba, QIODevice::ReadWrite);
|
||||
|
@ -394,11 +395,11 @@ void Float32ArrayClass::setProperty(QScriptValue& object, const QScriptString& n
|
|||
}
|
||||
}
|
||||
|
||||
Float64ArrayClass::Float64ArrayClass(ScriptEngineQtScript* scriptEngine) : TypedArray(scriptEngine, FLOAT_64_ARRAY_CLASS_NAME) {
|
||||
Float64ArrayClass::Float64ArrayClass(ScriptEngineV8* scriptEngine) : TypedArray(scriptEngine, FLOAT_64_ARRAY_CLASS_NAME) {
|
||||
setBytesPerElement(sizeof(double));
|
||||
}
|
||||
|
||||
QScriptValue Float64ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) {
|
||||
V8ScriptValue Float64ArrayClass::property(const V8ScriptValue& object, const V8ScriptString& name, uint id) {
|
||||
QByteArray* arrayBuffer = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());bool ok = false;
|
||||
name.toArrayIndex(&ok);
|
||||
|
||||
|
@ -411,15 +412,15 @@ QScriptValue Float64ArrayClass::property(const QScriptValue& object, const QScri
|
|||
double result;
|
||||
stream >> result;
|
||||
if (isNaN(result)) {
|
||||
return QScriptValue();
|
||||
return V8ScriptValue();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return TypedArray::property(object, name, id);
|
||||
}
|
||||
|
||||
void Float64ArrayClass::setProperty(QScriptValue& object, const QScriptString& name,
|
||||
uint id, const QScriptValue& value) {
|
||||
void Float64ArrayClass::setProperty(V8ScriptValue& object, const V8ScriptString& name,
|
||||
uint id, const V8ScriptValue& value) {
|
||||
QByteArray* ba = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
if (ba && value.isNumber()) {
|
||||
QDataStream stream(ba, QIODevice::ReadWrite);
|
||||
|
@ -429,5 +430,5 @@ void Float64ArrayClass::setProperty(QScriptValue& object, const QScriptString& n
|
|||
|
||||
stream << (double)value.toNumber();
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
155
libraries/script-engine/src/v8/TypedArrays.h
Normal file
155
libraries/script-engine/src/v8/TypedArrays.h
Normal file
|
@ -0,0 +1,155 @@
|
|||
//
|
||||
// TypedArrays.h
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/9/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
|
||||
//
|
||||
|
||||
/// @addtogroup ScriptEngine
|
||||
/// @{
|
||||
|
||||
#ifndef hifi_TypedArrays_h
|
||||
#define hifi_TypedArrays_h
|
||||
|
||||
// V8TODO
|
||||
/*#include "ArrayBufferViewClass.h"
|
||||
|
||||
static const QString BYTES_PER_ELEMENT_PROPERTY_NAME = "BYTES_PER_ELEMENT";
|
||||
static const QString LENGTH_PROPERTY_NAME = "length";
|
||||
|
||||
static const QString INT_8_ARRAY_CLASS_NAME = "Int8Array";
|
||||
static const QString UINT_8_ARRAY_CLASS_NAME = "Uint8Array";
|
||||
static const QString UINT_8_CLAMPED_ARRAY_CLASS_NAME = "Uint8ClampedArray";
|
||||
static const QString INT_16_ARRAY_CLASS_NAME = "Int16Array";
|
||||
static const QString UINT_16_ARRAY_CLASS_NAME = "Uint16Array";
|
||||
static const QString INT_32_ARRAY_CLASS_NAME = "Int32Array";
|
||||
static const QString UINT_32_ARRAY_CLASS_NAME = "Uint32Array";
|
||||
static const QString FLOAT_32_ARRAY_CLASS_NAME = "Float32Array";
|
||||
static const QString FLOAT_64_ARRAY_CLASS_NAME = "Float64Array";
|
||||
|
||||
/// [QtScript] Implements the <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray">TypedArray</a></code> scripting class
|
||||
class TypedArray : public ArrayBufferViewClass {
|
||||
Q_OBJECT
|
||||
public:
|
||||
TypedArray(ScriptEngineV8* scriptEngine, QString name);
|
||||
virtual V8ScriptValue newInstance(quint32 length);
|
||||
virtual V8ScriptValue newInstance(V8ScriptValue array);
|
||||
virtual V8ScriptValue newInstance(V8ScriptValue buffer, quint32 byteOffset, quint32 length);
|
||||
|
||||
virtual QueryFlags queryProperty(const V8ScriptValue& object,
|
||||
const V8ScriptString& name,
|
||||
QueryFlags flags, uint* id) override;
|
||||
virtual V8ScriptValue property(const V8ScriptValue& object,
|
||||
const V8ScriptString& name, uint id) override;
|
||||
virtual void setProperty(V8ScriptValue& object, const V8ScriptString& name, uint id, const V8ScriptValue& value) override = 0;
|
||||
virtual V8ScriptValue::PropertyFlags propertyFlags(const V8ScriptValue& object,
|
||||
const V8ScriptString& name, uint id) override;
|
||||
|
||||
QString name() const override;
|
||||
V8ScriptValue prototype() const override;
|
||||
|
||||
protected:
|
||||
static V8ScriptValue construct(V8ScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
void setBytesPerElement(quint32 bytesPerElement);
|
||||
|
||||
V8ScriptValue _proto;
|
||||
V8ScriptValue _ctor;
|
||||
|
||||
V8ScriptString _name;
|
||||
V8ScriptString _bytesPerElementName;
|
||||
V8ScriptString _lengthName;
|
||||
|
||||
quint32 _bytesPerElement;
|
||||
|
||||
friend class TypedArrayPrototype;
|
||||
};
|
||||
|
||||
class Int8ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Int8ArrayClass(ScriptEngineV8* scriptEngine);
|
||||
|
||||
V8ScriptValue property(const V8ScriptValue& object, const V8ScriptString& name, uint id) override;
|
||||
void setProperty(V8ScriptValue& object, const V8ScriptString& name, uint id, const V8ScriptValue& value) override;
|
||||
};
|
||||
|
||||
class Uint8ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Uint8ArrayClass(ScriptEngineV8* scriptEngine);
|
||||
|
||||
V8ScriptValue property(const V8ScriptValue& object, const V8ScriptString& name, uint id) override;
|
||||
void setProperty(V8ScriptValue& object, const V8ScriptString& name, uint id, const V8ScriptValue& value) override;
|
||||
};
|
||||
|
||||
class Uint8ClampedArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Uint8ClampedArrayClass(ScriptEngineV8* scriptEngine);
|
||||
|
||||
V8ScriptValue property(const V8ScriptValue& object, const V8ScriptString& name, uint id) override;
|
||||
void setProperty(V8ScriptValue& object, const V8ScriptString& name, uint id, const V8ScriptValue& value) override;
|
||||
};
|
||||
|
||||
class Int16ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Int16ArrayClass(ScriptEngineV8* scriptEngine);
|
||||
|
||||
V8ScriptValue property(const V8ScriptValue& object, const V8ScriptString& name, uint id) override;
|
||||
void setProperty(V8ScriptValue& object, const V8ScriptString& name, uint id, const V8ScriptValue& value) override;
|
||||
};
|
||||
|
||||
class Uint16ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Uint16ArrayClass(ScriptEngineV8* scriptEngine);
|
||||
|
||||
V8ScriptValue property(const V8ScriptValue& object, const V8ScriptString& name, uint id) override;
|
||||
void setProperty(V8ScriptValue& object, const V8ScriptString& name, uint id, const V8ScriptValue& value) override;
|
||||
};
|
||||
|
||||
class Int32ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Int32ArrayClass(ScriptEngineV8* scriptEngine);
|
||||
|
||||
V8ScriptValue property(const V8ScriptValue& object, const V8ScriptString& name, uint id) override;
|
||||
void setProperty(V8ScriptValue& object, const V8ScriptString& name, uint id, const V8ScriptValue& value) override;
|
||||
};
|
||||
|
||||
class Uint32ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Uint32ArrayClass(ScriptEngineV8* scriptEngine);
|
||||
|
||||
V8ScriptValue property(const V8ScriptValue& object, const V8ScriptString& name, uint id) override;
|
||||
void setProperty(V8ScriptValue& object, const V8ScriptString& name, uint id, const V8ScriptValue& value) override;
|
||||
};
|
||||
|
||||
class Float32ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Float32ArrayClass(ScriptEngineV8* scriptEngine);
|
||||
|
||||
V8ScriptValue property(const V8ScriptValue& object, const V8ScriptString& name, uint id) override;
|
||||
void setProperty(V8ScriptValue& object, const V8ScriptString& name, uint id, const V8ScriptValue& value) override;
|
||||
};
|
||||
|
||||
class Float64ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Float64ArrayClass(ScriptEngineV8* scriptEngine);
|
||||
|
||||
V8ScriptValue property(const V8ScriptValue& object, const V8ScriptString& name, uint id) override;
|
||||
void setProperty(V8ScriptValue& object, const V8ScriptString& name, uint id, const V8ScriptValue& value) override;
|
||||
};
|
||||
*/
|
||||
#endif // hifi_TypedArrays_h
|
||||
|
||||
/// @}
|
76
libraries/script-engine/src/v8/V8Types.h
Normal file
76
libraries/script-engine/src/v8/V8Types.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// V8Types.h
|
||||
// libraries/script-engine/src/v8
|
||||
//
|
||||
// Created by dr Karol Suprynowicz on 2022/10/08
|
||||
// Copyright 2022 Overte e.V.
|
||||
//
|
||||
// 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_V8Types_h
|
||||
#define hifi_V8Types_h
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <libplatform/libplatform.h>
|
||||
#include <v8.h>
|
||||
|
||||
template <typename T>
|
||||
class V8ScriptValueTemplate {
|
||||
public:
|
||||
V8ScriptValueTemplate() = delete;
|
||||
//V8ScriptValueTemplate(v8::Isolate *isolate, v8::Local<T> value) : _isolate(isolate) {
|
||||
//_value.reset(v8::UniquePersistent<T>::New(_isolate, value));
|
||||
//};
|
||||
V8ScriptValueTemplate(v8::Isolate *isolate, const v8::Local<T> value) : _isolate(isolate), _context(isolate, isolate->GetCurrentContext()) {
|
||||
//_value.reset(_isolate, value);
|
||||
_value.reset(new v8::UniquePersistent<T>(_isolate, std::move(value)));
|
||||
};
|
||||
v8::Local<T> get() {return _value.get()->Get(_isolate);};
|
||||
const v8::Local<T> constGet() const {return _value.get()->Get(_isolate);};
|
||||
V8ScriptValueTemplate<T>&& copy() const {return new V8ScriptValueTemplate(_isolate, v8::Local<T>::New(_isolate, constGet()));};
|
||||
const v8::Local<v8::Context> constGetContext() const {
|
||||
v8::EscapableHandleScope handleScope(_isolate);
|
||||
return handleScope.Escape(_context.Get(_isolate));
|
||||
};
|
||||
const v8::Isolate* constGetIsolate() const { return _isolate;};
|
||||
v8::Isolate* getIsolate() { return _isolate;};
|
||||
//v8::Persistent<v8::Context, v8::CopyablePersistentTraits<v8::Context>>& getContext() { return _context;};
|
||||
v8::Local<v8::Context> getContext() {
|
||||
v8::EscapableHandleScope handleScope(_isolate);
|
||||
return handleScope.Escape(_context.Get(_isolate));
|
||||
};
|
||||
void reset(v8::Isolate *isolate, v8::Local<T>) {};
|
||||
private:
|
||||
std::shared_ptr<v8::UniquePersistent<T>> _value;
|
||||
// V8TODO: maybe make it weak
|
||||
// does it need , CopyablePersistentTraits<Value>?
|
||||
// V8TODO: is context needed at all?
|
||||
v8::Isolate *_isolate;
|
||||
v8::Persistent<v8::Context, v8::CopyablePersistentTraits<v8::Context>> _context;
|
||||
};
|
||||
|
||||
typedef V8ScriptValueTemplate<v8::Value> V8ScriptValue;
|
||||
typedef V8ScriptValueTemplate<v8::Script> V8ScriptProgram;
|
||||
// V8TODO: Maybe weak?
|
||||
typedef v8::Persistent<v8::Context> V8ScriptContext;
|
||||
|
||||
class V8ScriptString : public V8ScriptValueTemplate<v8::String> {
|
||||
public:
|
||||
V8ScriptString() = delete;
|
||||
V8ScriptString(v8::Isolate *isolate, const v8::Local<v8::String> value) : V8ScriptValueTemplate<v8::String>(isolate, value) {};
|
||||
const QString toQString() const {
|
||||
return QString(*v8::String::Utf8Value(const_cast<v8::Isolate*>(constGetIsolate()), constGet()));
|
||||
};
|
||||
bool operator==(const V8ScriptString& string) const {
|
||||
return constGet()->StringEquals(string.constGet());
|
||||
}
|
||||
};
|
||||
|
||||
inline uint qHash(const V8ScriptString &key, uint seed = 0) {
|
||||
return qHash(key.toQString(), seed);
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue