Initial V8 support, not working yet

This commit is contained in:
ksuprynowicz 2022-10-08 12:05:27 +02:00
parent df507a741b
commit 7e9ad39b32
40 changed files with 4039 additions and 3086 deletions

View file

@ -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
/// @}

View file

@ -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
/// @}

View file

@ -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();
}

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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
/// @}

View file

@ -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());
}

View file

@ -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
/// @}

View file

@ -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)));
}

View file

@ -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
/// @}

View file

@ -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();
}

View file

@ -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
/// @}

View file

@ -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
}
}
}
*/

View 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
/// @}

View file

@ -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());
}
}*/

View file

@ -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
/// @}

View file

@ -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;
}*/

View 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
/// @}

View file

@ -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;
}
*/

View file

@ -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

View file

@ -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\"");
}
}
*/

View file

@ -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
/// @}

View 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;
}

View file

@ -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
/// @}

File diff suppressed because it is too large Load diff

View file

@ -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
/// @}

View file

@ -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);
}
}

File diff suppressed because it is too large Load diff

View 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
/// @}

View 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;
}

View 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
/// @}

View file

@ -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)));
}

View file

@ -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
/// @}

View 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();
}

View file

@ -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
/// @}

View file

@ -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);
}
}
*/

View file

@ -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
/// @}

View file

@ -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();
}
}
}*/

View 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
/// @}

View 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