Skip to content

ArrayBufferView access from C++ script API #1497

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions libraries/script-engine/src/ScriptManagerScriptingInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,35 @@ QString ScriptManagerScriptingInterface::btoa(const QByteArray &binary) {
QByteArray ScriptManagerScriptingInterface::atob(const QString &base64) {
return QByteArray::fromBase64(base64.toUtf8());
}

void ScriptManagerScriptingInterface::test__ArrayBufferView(const ScriptValue& value) {
if (!value.isArrayBufferView()) {
qInfo() << "value is not an ArrayBufferView";
return;
}

auto view = value.toArrayBufferView();

QString str;
QTextStream stream(&str);

const char* data = reinterpret_cast<const char*>(view->buffer());
auto offset = view->byteOffset();
auto length = view->byteLength();

stream << "ArrayBufferView(" << offset << ", " << length << ") [ ";

if (data) {
for (size_t i = 0; i < length; i++) {
stream << (uint8_t)data[offset + i];

if (i < length - 1) { stream << ", "; }
}
} else {
stream << "(no buffer)";
}

stream << " ]";

qInfo() << str;
}
2 changes: 2 additions & 0 deletions libraries/script-engine/src/ScriptManagerScriptingInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,8 @@ class ScriptManagerScriptingInterface : public QObject {
*/
Q_INVOKABLE QString btoa(const QByteArray &binary);

Q_INVOKABLE void test__ArrayBufferView(const ScriptValue& value);

signals:

/*@jsdoc
Expand Down
10 changes: 10 additions & 0 deletions libraries/script-engine/src/ScriptValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class ScriptValueProxyNull final : public ScriptValueProxy {
virtual bool isUndefined() const override;
virtual bool isValid() const override;
virtual bool isVariant() const override;
virtual bool isArrayBufferView() const override;
virtual ScriptValueIteratorPointer newIterator() const override;
virtual ScriptValue property(const QString& name,
const ScriptValue::ResolveFlags& mode = ScriptValue::ResolvePrototype) const override;
Expand Down Expand Up @@ -67,6 +68,7 @@ class ScriptValueProxyNull final : public ScriptValueProxy {
virtual quint32 toUInt32() const override;
virtual QVariant toVariant() const override;
virtual QObject* toQObject() const override;
virtual std::shared_ptr<ScriptBufferView> toArrayBufferView() const override;
};

static ScriptValueProxyNull SCRIPT_VALUE_NULL;
Expand Down Expand Up @@ -164,6 +166,10 @@ bool ScriptValueProxyNull::isVariant() const {
return false;
}

bool ScriptValueProxyNull::isArrayBufferView() const {
return false;
}

ScriptValueIteratorPointer ScriptValueProxyNull::newIterator() const {
Q_ASSERT(false);
qCWarning(scriptengine_script, "ScriptValue::newIterator called on empty value");
Expand Down Expand Up @@ -254,3 +260,7 @@ QVariant ScriptValueProxyNull::toVariant() const {
QObject* ScriptValueProxyNull::toQObject() const {
return nullptr;
}

std::shared_ptr<ScriptBufferView> ScriptValueProxyNull::toArrayBufferView() const {
return nullptr;
}
21 changes: 21 additions & 0 deletions libraries/script-engine/src/ScriptValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ using ScriptEnginePointer = std::shared_ptr<ScriptEngine>;
using ScriptValueList = QList<ScriptValue>;
using ScriptValueIteratorPointer = std::shared_ptr<ScriptValueIterator>;

class ScriptBufferView {
public:
virtual void* buffer() const = 0;
virtual size_t byteOffset() const = 0;
virtual size_t byteLength() const = 0;
};

/// [ScriptInterface] Provides an engine-independent interface for QScriptValue
class ScriptValue {
public:
Expand Down Expand Up @@ -87,6 +94,7 @@ class ScriptValue {
inline bool isUndefined() const;
inline bool isValid() const;
inline bool isVariant() const;
inline bool isArrayBufferView() const;
inline ScriptValueIteratorPointer newIterator() const;
inline ScriptValue property(const QString& name, const ResolveFlags& mode = ResolvePrototype) const;
inline ScriptValue property(quint32 arrayIndex, const ResolveFlags& mode = ResolvePrototype) const;
Expand Down Expand Up @@ -118,6 +126,7 @@ class ScriptValue {
inline quint32 toUInt32() const;
inline QVariant toVariant() const;
inline QObject* toQObject() const;
inline std::shared_ptr<ScriptBufferView> toArrayBufferView() const;

protected:
ScriptValueProxy* _proxy;
Expand Down Expand Up @@ -150,6 +159,7 @@ class ScriptValueProxy {
virtual bool isUndefined() const = 0;
virtual bool isValid() const = 0;
virtual bool isVariant() const = 0;
virtual bool isArrayBufferView() const = 0;
virtual ScriptValueIteratorPointer newIterator() const = 0;
virtual ScriptValue property(const QString& name,
const ScriptValue::ResolveFlags& mode = ScriptValue::ResolvePrototype) const = 0;
Expand Down Expand Up @@ -177,6 +187,7 @@ class ScriptValueProxy {
virtual quint32 toUInt32() const = 0;
virtual QVariant toVariant() const = 0;
virtual QObject* toQObject() const = 0;
virtual std::shared_ptr<ScriptBufferView> toArrayBufferView() const = 0;

protected:
virtual ~ScriptValueProxy() {} // prevent explicit deletion of base class
Expand Down Expand Up @@ -305,6 +316,11 @@ bool ScriptValue::isVariant() const {
return _proxy->isVariant();
}

bool ScriptValue::isArrayBufferView() const {
Q_ASSERT(_proxy != nullptr);
return _proxy->isArrayBufferView();
}

ScriptValueIteratorPointer ScriptValue::newIterator() const {
Q_ASSERT(_proxy != nullptr);
return _proxy->newIterator();
Expand Down Expand Up @@ -406,6 +422,11 @@ QObject* ScriptValue::toQObject() const {
return _proxy->toQObject();
}

std::shared_ptr<ScriptBufferView> ScriptValue::toArrayBufferView() const {
Q_ASSERT(_proxy != nullptr);
return _proxy->toArrayBufferView();
}

#endif // hifi_ScriptValue_h

/// @}
21 changes: 21 additions & 0 deletions libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,18 @@ QObject* ScriptValueV8Wrapper::toQObject() const {
}
}

std::shared_ptr<ScriptBufferView> ScriptValueV8Wrapper::toArrayBufferView() const {
auto value = _value.constGet();
if (value->IsArrayBufferView()) {
v8::Isolate* isolate = _engine->getIsolate();
auto view = v8::ArrayBufferView::Cast(*value);
auto persistent = std::make_shared<v8::UniquePersistent<v8::ArrayBuffer>>(isolate, view->Buffer());
return std::make_shared<ScriptBufferViewV8Wrapper>(isolate, persistent, view->ByteOffset(), view->ByteLength());
} else {
return nullptr;
}
}

bool ScriptValueV8Wrapper::equals(const ScriptValue& other) const {
auto isolate = _engine->getIsolate();
v8::Locker locker(isolate);
Expand Down Expand Up @@ -782,3 +794,12 @@ bool ScriptValueV8Wrapper::isVariant() const {
return false;
//return _value.isVariant();
}

bool ScriptValueV8Wrapper::isArrayBufferView() const {
auto isolate = _engine->getIsolate();
v8::Locker locker(isolate);
v8::Isolate::Scope isolateScope(isolate);
v8::HandleScope handleScope(isolate);
v8::Context::Scope contextScope(_engine->getContext());
return _value.constGet()->IsArrayBufferView();
}
26 changes: 26 additions & 0 deletions libraries/script-engine/src/v8/ScriptValueV8Wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,30 @@

//#define OVERTE_V8_SCRIPT_VALUE_WRAPPER_DELETE_GUARD

class ScriptBufferViewV8Wrapper final : public ScriptBufferView {
public:
void* buffer() const override {
return _buffer->Get(_isolate)->Data();
}

size_t byteOffset() const override {
return _offset;
}

size_t byteLength() const override {
return _length;
}

ScriptBufferViewV8Wrapper(v8::Isolate *isolate, std::shared_ptr<v8::UniquePersistent<v8::ArrayBuffer>> buffer, size_t offset, size_t length) : _isolate(isolate), _buffer(buffer), _offset(offset), _length(length) {}

private:
v8::Isolate *_isolate;
std::shared_ptr<v8::UniquePersistent<v8::ArrayBuffer>> _buffer;

size_t _offset;
size_t _length;
};

/// [V8] Implements ScriptValue for V8 and translates calls for V8ScriptValue
class ScriptValueV8Wrapper final : public ScriptValueProxy {
public: // construction
Expand Down Expand Up @@ -94,6 +118,7 @@ class ScriptValueV8Wrapper final : public ScriptValueProxy {
virtual bool isUndefined() const override;
virtual bool isValid() const override;
virtual bool isVariant() const override;
virtual bool isArrayBufferView() const override;
virtual bool toBool() const override;
virtual qint32 toInt32() const override;
virtual double toInteger() const override;
Expand All @@ -103,6 +128,7 @@ class ScriptValueV8Wrapper final : public ScriptValueProxy {
virtual quint32 toUInt32() const override;
virtual QVariant toVariant() const override;
virtual QObject* toQObject() const override;
virtual std::shared_ptr<ScriptBufferView> toArrayBufferView() const override;

#ifdef OVERTE_V8_SCRIPT_VALUE_WRAPPER_DELETE_GUARD
// These can be used for debugging crashes caused access after delete
Expand Down