From c246205692e94c4e4500788baabc66a0c3986d06 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 7 Jul 2014 15:04:45 -0700 Subject: [PATCH 01/24] Base implementation of ArrayBuffer --- libraries/script-engine/src/ArrayBuffer.cpp | 105 ++++++++++++++++++ libraries/script-engine/src/ArrayBuffer.h | 56 ++++++++++ .../src/ArrayBufferPrototype.cpp | 29 +++++ .../script-engine/src/ArrayBufferPrototype.h | 32 ++++++ 4 files changed, 222 insertions(+) create mode 100644 libraries/script-engine/src/ArrayBuffer.cpp create mode 100644 libraries/script-engine/src/ArrayBuffer.h create mode 100644 libraries/script-engine/src/ArrayBufferPrototype.cpp create mode 100644 libraries/script-engine/src/ArrayBufferPrototype.h diff --git a/libraries/script-engine/src/ArrayBuffer.cpp b/libraries/script-engine/src/ArrayBuffer.cpp new file mode 100644 index 0000000000..561a8cd5bf --- /dev/null +++ b/libraries/script-engine/src/ArrayBuffer.cpp @@ -0,0 +1,105 @@ +// +// ArrayBuffer.cpp +// +// +// Created by Clement on 7/3/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +#include "ArrayBufferPrototype.h" + +#include "ArrayBuffer.h" + +Q_DECLARE_METATYPE(QByteArray*) +Q_DECLARE_METATYPE(ArrayBuffer*) + +ArrayBuffer::ArrayBuffer(QScriptEngine* engine) : QObject(engine), QScriptClass(engine) { + qScriptRegisterMetaType(engine, toScriptValue, fromScriptValue); + + _byteLength = engine->toStringHandle(QLatin1String("byteLength")); + +// _proto = engine->newQObject(new ArrayBufferPrototype(this), +// QScriptEngine::QtOwnership, +// QScriptEngine::SkipMethodsInEnumeration +// | QScriptEngine::ExcludeSuperClassMethods +// | QScriptEngine::ExcludeSuperClassProperties); + QScriptValue global = engine->globalObject(); + _proto.setPrototype(global.property("Object").property("prototype")); + + _ctor = engine->newFunction(construct, _proto); + _ctor.setData(engine->toScriptValue(this)); +} + +QScriptValue ArrayBuffer::newInstance(unsigned long size) { + engine()->reportAdditionalMemoryCost(size); + QScriptValue data = engine()->newVariant(QVariant::fromValue(QByteArray(size, 0))); + return engine()->newObject(this, data); +} + +QScriptValue ArrayBuffer::newInstance(const QByteArray& ba) { + QScriptValue data = engine()->newVariant(QVariant::fromValue(ba)); + return engine()->newObject(this, data); +} + +QScriptValue ArrayBuffer::construct(QScriptContext* ctx, QScriptEngine* eng) { + ArrayBuffer* cls = qscriptvalue_cast(ctx->callee().data()); + QScriptValue arg = ctx->argument(0); + int size = arg.toInt32(); + return cls ? cls->newInstance(size) : QScriptValue(); +} + +QScriptClass::QueryFlags ArrayBuffer::queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id) { + QByteArray* ba = qscriptvalue_cast(object.data()); + if (ba && name == _byteLength) { + // if the property queried is byteLength, only handle read access + return flags &= HandlesReadAccess; + } + return 0; // No access +} + +QScriptValue ArrayBuffer::property(const QScriptValue &object, + const QScriptString &name, uint id) { + QByteArray* ba = qscriptvalue_cast(object.data()); + if (ba && name == _byteLength) { + return ba->length(); + } + return QScriptValue(); +} + +QScriptValue::PropertyFlags ArrayBuffer::propertyFlags(const QScriptValue& object, + const QScriptString& name, uint id) { + return QScriptValue::Undeletable; +} + +QString ArrayBuffer::name() const { + return QLatin1String("ArrayBuffer"); +} + +QScriptValue ArrayBuffer::prototype() const { + return _proto; +} + +QScriptValue ArrayBuffer::constructor() const { + return _ctor; +} + +QScriptValue ArrayBuffer::toScriptValue(QScriptEngine* eng, const QByteArray& ba) +{ + QScriptValue ctor = eng->globalObject().property("ArrayBuffer"); + ArrayBuffer *cls = qscriptvalue_cast(ctor.data()); + if (!cls) { + return eng->newVariant(QVariant::fromValue(ba)); + } + return cls->newInstance(ba); +} + +void ArrayBuffer::fromScriptValue(const QScriptValue& obj, QByteArray& ba) { + ba = qvariant_cast(obj.data().toVariant()); +} + diff --git a/libraries/script-engine/src/ArrayBuffer.h b/libraries/script-engine/src/ArrayBuffer.h new file mode 100644 index 0000000000..f951a1248a --- /dev/null +++ b/libraries/script-engine/src/ArrayBuffer.h @@ -0,0 +1,56 @@ +// +// ArrayBuffer.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 +// + +#ifndef hifi_ArrayBuffer_h +#define hifi_ArrayBuffer_h + +#include +#include +#include +#include +#include +#include +#include + +class ArrayBuffer : public QObject, public QScriptClass { +public: + ArrayBuffer(QScriptEngine* engine); + QScriptValue newInstance(unsigned long size = 0); + QScriptValue newInstance(const QByteArray& ba); + + QueryFlags queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id); + QScriptValue property(const QScriptValue& object, + const QScriptString& name, + uint id); + QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, + const QScriptString& name, + uint id); + + QString name() const; + QScriptValue prototype() const; + QScriptValue constructor() const; + +private: + static QScriptValue construct(QScriptContext* ctx, QScriptEngine* eng); + + static QScriptValue toScriptValue(QScriptEngine* eng, const QByteArray& ba); + static void fromScriptValue(const QScriptValue& obj, QByteArray& ba); + + QScriptValue _proto; + QScriptValue _ctor; + + // JS Object attributes + QScriptString _byteLength; +}; + +#endif // hifi_ArrayBuffer_h \ No newline at end of file diff --git a/libraries/script-engine/src/ArrayBufferPrototype.cpp b/libraries/script-engine/src/ArrayBufferPrototype.cpp new file mode 100644 index 0000000000..01736cc511 --- /dev/null +++ b/libraries/script-engine/src/ArrayBufferPrototype.cpp @@ -0,0 +1,29 @@ +// +// ArrayBufferPrototype.cpp +// +// +// Created by Clement on 7/3/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +#include "ArrayBufferPrototype.h" + +ArrayBufferPrototype::ArrayBufferPrototype(QObject* parent) : QObject(parent) { +} + +QByteArray ArrayBufferPrototype::slice(long begin, long end) const { + if (end == -1) { + return thisArrayBuffer().mid(begin); + } else { + return thisArrayBuffer().mid(begin, end); + } +} + +QByteArray ArrayBufferPrototype::thisArrayBuffer() const { + return qscriptvalue_cast(thisObject().data()); +} diff --git a/libraries/script-engine/src/ArrayBufferPrototype.h b/libraries/script-engine/src/ArrayBufferPrototype.h new file mode 100644 index 0000000000..829fb8e3ec --- /dev/null +++ b/libraries/script-engine/src/ArrayBufferPrototype.h @@ -0,0 +1,32 @@ +// +// ArrayBufferPrototype.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 +// + +#ifndef hifi_ArrayBufferPrototype_h +#define hifi_ArrayBufferPrototype_h + +#include +#include +#include +#include + +class ArrayBufferPrototype : public QObject, public QScriptable { + Q_OBJECT +public: + ArrayBufferPrototype(QObject* parent = NULL); + +public slots: + QByteArray slice(long begin, long end = -1) const; + +private: + QByteArray thisArrayBuffer() const; +}; + +#endif // hifi_ArrayBufferPrototype_h \ No newline at end of file From f9b739a0d1500864495f2da7f6066abcd016b8c4 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 7 Jul 2014 18:02:33 -0700 Subject: [PATCH 02/24] Functional ArrayBuffer class following Qt standard --- libraries/script-engine/src/ArrayBuffer.cpp | 105 ---------------- .../script-engine/src/ArrayBufferClass.cpp | 119 ++++++++++++++++++ .../src/{ArrayBuffer.h => ArrayBufferClass.h} | 27 ++-- .../src/ArrayBufferPrototype.cpp | 14 +-- .../script-engine/src/ArrayBufferPrototype.h | 2 +- libraries/script-engine/src/ScriptEngine.cpp | 3 + 6 files changed, 143 insertions(+), 127 deletions(-) delete mode 100644 libraries/script-engine/src/ArrayBuffer.cpp create mode 100644 libraries/script-engine/src/ArrayBufferClass.cpp rename libraries/script-engine/src/{ArrayBuffer.h => ArrayBufferClass.h} (67%) diff --git a/libraries/script-engine/src/ArrayBuffer.cpp b/libraries/script-engine/src/ArrayBuffer.cpp deleted file mode 100644 index 561a8cd5bf..0000000000 --- a/libraries/script-engine/src/ArrayBuffer.cpp +++ /dev/null @@ -1,105 +0,0 @@ -// -// ArrayBuffer.cpp -// -// -// Created by Clement on 7/3/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - - -#include "ArrayBufferPrototype.h" - -#include "ArrayBuffer.h" - -Q_DECLARE_METATYPE(QByteArray*) -Q_DECLARE_METATYPE(ArrayBuffer*) - -ArrayBuffer::ArrayBuffer(QScriptEngine* engine) : QObject(engine), QScriptClass(engine) { - qScriptRegisterMetaType(engine, toScriptValue, fromScriptValue); - - _byteLength = engine->toStringHandle(QLatin1String("byteLength")); - -// _proto = engine->newQObject(new ArrayBufferPrototype(this), -// QScriptEngine::QtOwnership, -// QScriptEngine::SkipMethodsInEnumeration -// | QScriptEngine::ExcludeSuperClassMethods -// | QScriptEngine::ExcludeSuperClassProperties); - QScriptValue global = engine->globalObject(); - _proto.setPrototype(global.property("Object").property("prototype")); - - _ctor = engine->newFunction(construct, _proto); - _ctor.setData(engine->toScriptValue(this)); -} - -QScriptValue ArrayBuffer::newInstance(unsigned long size) { - engine()->reportAdditionalMemoryCost(size); - QScriptValue data = engine()->newVariant(QVariant::fromValue(QByteArray(size, 0))); - return engine()->newObject(this, data); -} - -QScriptValue ArrayBuffer::newInstance(const QByteArray& ba) { - QScriptValue data = engine()->newVariant(QVariant::fromValue(ba)); - return engine()->newObject(this, data); -} - -QScriptValue ArrayBuffer::construct(QScriptContext* ctx, QScriptEngine* eng) { - ArrayBuffer* cls = qscriptvalue_cast(ctx->callee().data()); - QScriptValue arg = ctx->argument(0); - int size = arg.toInt32(); - return cls ? cls->newInstance(size) : QScriptValue(); -} - -QScriptClass::QueryFlags ArrayBuffer::queryProperty(const QScriptValue& object, - const QScriptString& name, - QueryFlags flags, uint* id) { - QByteArray* ba = qscriptvalue_cast(object.data()); - if (ba && name == _byteLength) { - // if the property queried is byteLength, only handle read access - return flags &= HandlesReadAccess; - } - return 0; // No access -} - -QScriptValue ArrayBuffer::property(const QScriptValue &object, - const QScriptString &name, uint id) { - QByteArray* ba = qscriptvalue_cast(object.data()); - if (ba && name == _byteLength) { - return ba->length(); - } - return QScriptValue(); -} - -QScriptValue::PropertyFlags ArrayBuffer::propertyFlags(const QScriptValue& object, - const QScriptString& name, uint id) { - return QScriptValue::Undeletable; -} - -QString ArrayBuffer::name() const { - return QLatin1String("ArrayBuffer"); -} - -QScriptValue ArrayBuffer::prototype() const { - return _proto; -} - -QScriptValue ArrayBuffer::constructor() const { - return _ctor; -} - -QScriptValue ArrayBuffer::toScriptValue(QScriptEngine* eng, const QByteArray& ba) -{ - QScriptValue ctor = eng->globalObject().property("ArrayBuffer"); - ArrayBuffer *cls = qscriptvalue_cast(ctor.data()); - if (!cls) { - return eng->newVariant(QVariant::fromValue(ba)); - } - return cls->newInstance(ba); -} - -void ArrayBuffer::fromScriptValue(const QScriptValue& obj, QByteArray& ba) { - ba = qvariant_cast(obj.data().toVariant()); -} - diff --git a/libraries/script-engine/src/ArrayBufferClass.cpp b/libraries/script-engine/src/ArrayBufferClass.cpp new file mode 100644 index 0000000000..099e77b485 --- /dev/null +++ b/libraries/script-engine/src/ArrayBufferClass.cpp @@ -0,0 +1,119 @@ +// +// ArrayBufferClass.cpp +// +// +// Created by Clement on 7/3/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +#include "ArrayBufferPrototype.h" + +#include "ArrayBufferClass.h" + +static const QString CLASS_NAME = "ArrayBuffer"; +static const QString BYTE_LENGTH_PROPERTY_NAME = "byteLength"; + +Q_DECLARE_METATYPE(QByteArray*) + +ArrayBufferClass::ArrayBufferClass(QScriptEngine* engine) : QObject(engine), QScriptClass(engine) { + qScriptRegisterMetaType(engine, toScriptValue, fromScriptValue); + QScriptValue global = engine->globalObject(); + + // Save string handles for quick lookup + _name = engine->toStringHandle(CLASS_NAME.toLatin1()); + _byteLength = engine->toStringHandle(BYTE_LENGTH_PROPERTY_NAME.toLatin1()); + + // build prototype + _proto = engine->newQObject(new ArrayBufferPrototype(this), + QScriptEngine::QtOwnership, + QScriptEngine::SkipMethodsInEnumeration | + QScriptEngine::ExcludeSuperClassMethods | + QScriptEngine::ExcludeSuperClassProperties); + + _proto.setPrototype(global.property("Object").property("prototype")); + + _ctor = engine->newFunction(construct, _proto); + _ctor.setData(engine->toScriptValue(this)); + + engine->globalObject().setProperty(name(), _ctor); +} + +QScriptValue ArrayBufferClass::newInstance(unsigned long size) { + engine()->reportAdditionalMemoryCost(size); + QScriptValue data = engine()->newVariant(QVariant::fromValue(QByteArray(size, 0))); + return engine()->newObject(this, data); +} + +QScriptValue ArrayBufferClass::newInstance(const QByteArray& ba) { + QScriptValue data = engine()->newVariant(QVariant::fromValue(ba)); + return engine()->newObject(this, data); +} + +QScriptValue ArrayBufferClass::construct(QScriptContext* context, QScriptEngine* engine) { + ArrayBufferClass* cls = qscriptvalue_cast(context->callee().data()); + if (!cls) { + return QScriptValue(); + } + + QScriptValue arg = context->argument(0); + unsigned long size = arg.toInt32(); + QScriptValue newObject = cls->newInstance(size); + + if (context->isCalledAsConstructor()) { + context->setThisObject(newObject); + return engine->undefinedValue(); + } + + return newObject; +} + +QScriptClass::QueryFlags ArrayBufferClass::queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id) { + QByteArray* ba = qscriptvalue_cast(object.data()); + if (ba && name == _byteLength) { + // if the property queried is byteLength, only handle read access + return flags &= HandlesReadAccess; + } + return 0; // No access +} + +QScriptValue ArrayBufferClass::property(const QScriptValue &object, + const QScriptString &name, uint id) { + QByteArray* ba = qscriptvalue_cast(object.data()); + if (ba && name == _byteLength) { + return ba->length(); + } + return QScriptValue(); +} + +QScriptValue::PropertyFlags ArrayBufferClass::propertyFlags(const QScriptValue& object, + const QScriptString& name, uint id) { + return QScriptValue::Undeletable; +} + +QString ArrayBufferClass::name() const { + return _name.toString(); +} + +QScriptValue ArrayBufferClass::prototype() const { + return _proto; +} + +QScriptValue ArrayBufferClass::toScriptValue(QScriptEngine* engine, const QByteArray& ba) { + QScriptValue ctor = engine->globalObject().property(CLASS_NAME); + ArrayBufferClass* cls = qscriptvalue_cast(ctor.data()); + if (!cls) { + return engine->newVariant(QVariant::fromValue(ba)); + } + return cls->newInstance(ba); +} + +void ArrayBufferClass::fromScriptValue(const QScriptValue& obj, QByteArray& ba) { + ba = qvariant_cast(obj.data().toVariant()); +} + diff --git a/libraries/script-engine/src/ArrayBuffer.h b/libraries/script-engine/src/ArrayBufferClass.h similarity index 67% rename from libraries/script-engine/src/ArrayBuffer.h rename to libraries/script-engine/src/ArrayBufferClass.h index f951a1248a..b106d27934 100644 --- a/libraries/script-engine/src/ArrayBuffer.h +++ b/libraries/script-engine/src/ArrayBufferClass.h @@ -1,5 +1,5 @@ // -// ArrayBuffer.h +// ArrayBufferClass.h // // // Created by Clement on 7/3/14. @@ -9,8 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_ArrayBuffer_h -#define hifi_ArrayBuffer_h +#ifndef hifi_ArrayBufferClass_h +#define hifi_ArrayBufferClass_h #include #include @@ -20,28 +20,26 @@ #include #include -class ArrayBuffer : public QObject, public QScriptClass { +class ArrayBufferClass : public QObject, public QScriptClass { + Q_OBJECT public: - ArrayBuffer(QScriptEngine* engine); - QScriptValue newInstance(unsigned long size = 0); + ArrayBufferClass(QScriptEngine* engine); + QScriptValue newInstance(unsigned long size); QScriptValue newInstance(const QByteArray& ba); QueryFlags queryProperty(const QScriptValue& object, const QScriptString& name, QueryFlags flags, uint* id); - QScriptValue property(const QScriptValue& object, - const QScriptString& name, - uint id); + QScriptValue property(const QScriptValue &object, + const QScriptString &name, uint id); QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, - const QScriptString& name, - uint id); + const QScriptString& name, uint id); QString name() const; QScriptValue prototype() const; - QScriptValue constructor() const; private: - static QScriptValue construct(QScriptContext* ctx, QScriptEngine* eng); + static QScriptValue construct(QScriptContext* context, QScriptEngine* engine); static QScriptValue toScriptValue(QScriptEngine* eng, const QByteArray& ba); static void fromScriptValue(const QScriptValue& obj, QByteArray& ba); @@ -50,7 +48,8 @@ private: QScriptValue _ctor; // JS Object attributes + QScriptString _name; QScriptString _byteLength; }; -#endif // hifi_ArrayBuffer_h \ No newline at end of file +#endif // hifi_ArrayBufferClass_h \ No newline at end of file diff --git a/libraries/script-engine/src/ArrayBufferPrototype.cpp b/libraries/script-engine/src/ArrayBufferPrototype.cpp index 01736cc511..087ab3d177 100644 --- a/libraries/script-engine/src/ArrayBufferPrototype.cpp +++ b/libraries/script-engine/src/ArrayBufferPrototype.cpp @@ -11,19 +11,19 @@ #include +#include "ArrayBufferClass.h" + #include "ArrayBufferPrototype.h" +Q_DECLARE_METATYPE(QByteArray*) + ArrayBufferPrototype::ArrayBufferPrototype(QObject* parent) : QObject(parent) { } QByteArray ArrayBufferPrototype::slice(long begin, long end) const { - if (end == -1) { - return thisArrayBuffer().mid(begin); - } else { - return thisArrayBuffer().mid(begin, end); - } + return thisArrayBuffer()->mid(begin, end); } -QByteArray ArrayBufferPrototype::thisArrayBuffer() const { - return qscriptvalue_cast(thisObject().data()); +QByteArray* ArrayBufferPrototype::thisArrayBuffer() const { + return qscriptvalue_cast(thisObject().data()); } diff --git a/libraries/script-engine/src/ArrayBufferPrototype.h b/libraries/script-engine/src/ArrayBufferPrototype.h index 829fb8e3ec..52caf5c90a 100644 --- a/libraries/script-engine/src/ArrayBufferPrototype.h +++ b/libraries/script-engine/src/ArrayBufferPrototype.h @@ -26,7 +26,7 @@ public slots: QByteArray slice(long begin, long end = -1) const; private: - QByteArray thisArrayBuffer() const; + QByteArray* thisArrayBuffer() const; }; #endif // hifi_ArrayBufferPrototype_h \ No newline at end of file diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index e93a7125b9..fab50784a2 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -32,6 +32,7 @@ #include #include "AnimationObject.h" +#include "ArrayBufferClass.h" #include "MenuItemProperties.h" #include "MIDIEvent.h" #include "LocalVoxels.h" @@ -211,6 +212,8 @@ void ScriptEngine::init() { return; // only initialize once } + new ArrayBufferClass(&_engine); + _isInitialized = true; _voxelsScriptingInterface.init(); From 96a1390d8f658328158823be0140e216ef5083e7 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 7 Jul 2014 18:58:53 -0700 Subject: [PATCH 03/24] Fixed up ArrayBuffer::slice() behaviour --- .../src/ArrayBufferPrototype.cpp | 25 ++++++++++++++++++- .../script-engine/src/ArrayBufferPrototype.h | 5 ++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/libraries/script-engine/src/ArrayBufferPrototype.cpp b/libraries/script-engine/src/ArrayBufferPrototype.cpp index 087ab3d177..45810f39c5 100644 --- a/libraries/script-engine/src/ArrayBufferPrototype.cpp +++ b/libraries/script-engine/src/ArrayBufferPrototype.cpp @@ -9,8 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include +#include + #include "ArrayBufferClass.h" #include "ArrayBufferPrototype.h" @@ -21,7 +24,27 @@ ArrayBufferPrototype::ArrayBufferPrototype(QObject* parent) : QObject(parent) { } QByteArray ArrayBufferPrototype::slice(long begin, long end) const { - return thisArrayBuffer()->mid(begin, end); + QByteArray* ba = thisArrayBuffer(); + // if indices < 0 then they start from the end of the array + begin = (begin < 0) ? ba->size() + begin : begin; + end = (end < 0) ? ba->size() + end : end; + + // here we clamp the indices to fit the array + begin = glm::clamp(begin, 0l, (long)(ba->size() - 1)); + end = glm::clamp(end, 0l, (long)(ba->size() - 1)); + + return (end - begin > 0) ? ba->mid(begin, end - begin) : QByteArray(); +} + +QByteArray ArrayBufferPrototype::slice(long begin) const { + QByteArray* ba = thisArrayBuffer(); + // if indices < 0 then they start from the end of the array + begin = (begin < 0) ? ba->size() + begin : begin; + + // here we clamp the indices to fit the array + begin = glm::clamp(begin, 0l, (long)(ba->size() - 1)); + + return ba->mid(begin, -1); } QByteArray* ArrayBufferPrototype::thisArrayBuffer() const { diff --git a/libraries/script-engine/src/ArrayBufferPrototype.h b/libraries/script-engine/src/ArrayBufferPrototype.h index 52caf5c90a..6f3c8bbd4d 100644 --- a/libraries/script-engine/src/ArrayBufferPrototype.h +++ b/libraries/script-engine/src/ArrayBufferPrototype.h @@ -22,8 +22,9 @@ class ArrayBufferPrototype : public QObject, public QScriptable { public: ArrayBufferPrototype(QObject* parent = NULL); -public slots: - QByteArray slice(long begin, long end = -1) const; + public slots: + QByteArray slice(long begin, long end) const; + QByteArray slice(long begin) const; private: QByteArray* thisArrayBuffer() const; From 05d74e2bb1988a87c5d5170a661015ddc8ecc6db Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 7 Jul 2014 18:59:57 -0700 Subject: [PATCH 04/24] Added ArrayBuffer unit tests --- examples/typedArraysUnitTest.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 examples/typedArraysUnitTest.js diff --git a/examples/typedArraysUnitTest.js b/examples/typedArraysUnitTest.js new file mode 100644 index 0000000000..98f69f3e71 --- /dev/null +++ b/examples/typedArraysUnitTest.js @@ -0,0 +1,22 @@ +// +// typedArraysunitTest.js +// examples +// +// Created by Clément Brisset on 7/7/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 +// + +Script.include("Test.js"); + +test("ArrayBuffer", function(finished) { + this.assertEquals(new ArrayBuffer().byteLength, 0, 'no length'); + + this.assertEquals(typeof(new ArrayBuffer(0)), 'object', 'creation'); + this.assertEquals(typeof(new ArrayBuffer(1)), 'object', 'creation'); + this.assertEquals(typeof(new ArrayBuffer(123)), 'object', 'creation'); + + this.assertEquals(new ArrayBuffer(123).byteLength, 123, 'length'); +}); \ No newline at end of file From 1f6e1b9509635a49f3d5de90088a8e62484b73d7 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 8 Jul 2014 16:08:11 -0700 Subject: [PATCH 05/24] Modifications and improvments of the ArrayBuffers --- libraries/script-engine/src/ArrayBufferClass.cpp | 9 +++++++-- libraries/script-engine/src/ArrayBufferClass.h | 2 +- .../script-engine/src/ArrayBufferPrototype.cpp | 13 +++++-------- libraries/script-engine/src/ArrayBufferPrototype.h | 8 +++----- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/libraries/script-engine/src/ArrayBufferClass.cpp b/libraries/script-engine/src/ArrayBufferClass.cpp index 099e77b485..d003125c1e 100644 --- a/libraries/script-engine/src/ArrayBufferClass.cpp +++ b/libraries/script-engine/src/ArrayBufferClass.cpp @@ -42,7 +42,7 @@ ArrayBufferClass::ArrayBufferClass(QScriptEngine* engine) : QObject(engine), QSc engine->globalObject().setProperty(name(), _ctor); } -QScriptValue ArrayBufferClass::newInstance(unsigned long size) { +QScriptValue ArrayBufferClass::newInstance(quint32 size) { engine()->reportAdditionalMemoryCost(size); QScriptValue data = engine()->newVariant(QVariant::fromValue(QByteArray(size, 0))); return engine()->newObject(this, data); @@ -60,7 +60,12 @@ QScriptValue ArrayBufferClass::construct(QScriptContext* context, QScriptEngine* } QScriptValue arg = context->argument(0); - unsigned long size = arg.toInt32(); + + if (!arg.isValid() || !arg.isNumber()) { + return QScriptValue(); + } + + quint32 size = arg.toInt32(); QScriptValue newObject = cls->newInstance(size); if (context->isCalledAsConstructor()) { diff --git a/libraries/script-engine/src/ArrayBufferClass.h b/libraries/script-engine/src/ArrayBufferClass.h index b106d27934..2f863e77a5 100644 --- a/libraries/script-engine/src/ArrayBufferClass.h +++ b/libraries/script-engine/src/ArrayBufferClass.h @@ -24,7 +24,7 @@ class ArrayBufferClass : public QObject, public QScriptClass { Q_OBJECT public: ArrayBufferClass(QScriptEngine* engine); - QScriptValue newInstance(unsigned long size); + QScriptValue newInstance(quint32 size); QScriptValue newInstance(const QByteArray& ba); QueryFlags queryProperty(const QScriptValue& object, diff --git a/libraries/script-engine/src/ArrayBufferPrototype.cpp b/libraries/script-engine/src/ArrayBufferPrototype.cpp index 45810f39c5..37db5f3eec 100644 --- a/libraries/script-engine/src/ArrayBufferPrototype.cpp +++ b/libraries/script-engine/src/ArrayBufferPrototype.cpp @@ -9,9 +9,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include - #include #include "ArrayBufferClass.h" @@ -23,26 +20,26 @@ Q_DECLARE_METATYPE(QByteArray*) ArrayBufferPrototype::ArrayBufferPrototype(QObject* parent) : QObject(parent) { } -QByteArray ArrayBufferPrototype::slice(long begin, long end) const { +QByteArray ArrayBufferPrototype::slice(qint32 begin, qint32 end) const { QByteArray* ba = thisArrayBuffer(); // if indices < 0 then they start from the end of the array begin = (begin < 0) ? ba->size() + begin : begin; end = (end < 0) ? ba->size() + end : end; // here we clamp the indices to fit the array - begin = glm::clamp(begin, 0l, (long)(ba->size() - 1)); - end = glm::clamp(end, 0l, (long)(ba->size() - 1)); + begin = glm::clamp(begin, 0, (ba->size() - 1)); + end = glm::clamp(end, 0, (ba->size() - 1)); return (end - begin > 0) ? ba->mid(begin, end - begin) : QByteArray(); } -QByteArray ArrayBufferPrototype::slice(long begin) const { +QByteArray ArrayBufferPrototype::slice(qint32 begin) const { QByteArray* ba = thisArrayBuffer(); // if indices < 0 then they start from the end of the array begin = (begin < 0) ? ba->size() + begin : begin; // here we clamp the indices to fit the array - begin = glm::clamp(begin, 0l, (long)(ba->size() - 1)); + begin = glm::clamp(begin, 0, (ba->size() - 1)); return ba->mid(begin, -1); } diff --git a/libraries/script-engine/src/ArrayBufferPrototype.h b/libraries/script-engine/src/ArrayBufferPrototype.h index 6f3c8bbd4d..09d4596f28 100644 --- a/libraries/script-engine/src/ArrayBufferPrototype.h +++ b/libraries/script-engine/src/ArrayBufferPrototype.h @@ -12,19 +12,17 @@ #ifndef hifi_ArrayBufferPrototype_h #define hifi_ArrayBufferPrototype_h -#include #include #include -#include class ArrayBufferPrototype : public QObject, public QScriptable { Q_OBJECT public: ArrayBufferPrototype(QObject* parent = NULL); - public slots: - QByteArray slice(long begin, long end) const; - QByteArray slice(long begin) const; +public slots: + QByteArray slice(qint32 begin, qint32 end) const; + QByteArray slice(qint32 begin) const; private: QByteArray* thisArrayBuffer() const; From 13fe8b579fc1eeed993a44731b50e29cc497538c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 8 Jul 2014 16:09:06 -0700 Subject: [PATCH 06/24] Added ArrayBufferView scriptable objects --- .../src/ArrayBufferViewClass.cpp | 103 ++++++++++++++++++ .../script-engine/src/ArrayBufferViewClass.h | 47 ++++++++ 2 files changed, 150 insertions(+) create mode 100644 libraries/script-engine/src/ArrayBufferViewClass.cpp create mode 100644 libraries/script-engine/src/ArrayBufferViewClass.h diff --git a/libraries/script-engine/src/ArrayBufferViewClass.cpp b/libraries/script-engine/src/ArrayBufferViewClass.cpp new file mode 100644 index 0000000000..73d833dab4 --- /dev/null +++ b/libraries/script-engine/src/ArrayBufferViewClass.cpp @@ -0,0 +1,103 @@ +// +// ArrayBufferViewClass.cpp +// +// +// 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 +// + +#include "ArrayBufferViewClass.h" + +Q_DECLARE_METATYPE(QByteArray*) + +ArrayBufferViewClass::ArrayBufferViewClass(QScriptEngine* engine) : +QObject(engine), +QScriptClass(engine) { + // Save string handles for quick lookup + _bufferName = engine->toStringHandle(BUFFER_PROPERTY_NAME.toLatin1()); + _byteOffsetName = engine->toStringHandle(BYTE_OFFSET_PROPERTY_NAME.toLatin1()); + _byteLengthName = engine->toStringHandle(BYTE_LENGTH_PROPERTY_NAME.toLatin1()); +} + +QScriptClass::QueryFlags ArrayBufferViewClass::queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id) { + if (name == _bufferName || name == _byteOffsetName || name == _byteLengthName) { + return flags &= HandlesReadAccess; // Only keep read access flags + } + return 0; // No access +} + +QScriptValue ArrayBufferViewClass::property(const QScriptValue &object, + const QScriptString &name, uint id) { + if (name == _bufferName) { + return object.data().property(_bufferName); + } + if (name == _byteOffsetName) { + return object.data().property(_byteOffsetName); + } + if (name == _byteLengthName) { + return object.data().property(_byteLengthName); + } + return QScriptValue(); +} + +QScriptValue::PropertyFlags ArrayBufferViewClass::propertyFlags(const QScriptValue& object, + const QScriptString& name, uint id) { + return QScriptValue::Undeletable; +} + +//QScriptClass::QueryFlags DataViewClass::queryProperty(const QScriptValue& object, +// const QScriptString& name, +// QueryFlags flags, uint* id) { +// QByteArray* arrayBuffer = qscriptvalue_cast(object.property(_bufferName).data()); +// bool ok = false; +// int pos = name.toArrayIndex(&ok); +// +// // Check that name is a valid index and arrayBuffer exists +// if (ok && arrayBuffer && pos > 0 && pos < arrayBuffer->size()) { +// *id = pos; // save pos to avoid recomputation +// return HandlesReadAccess | HandlesWriteAccess; // Read/Write access +// } +// +// return ArrayBufferViewClass::queryProperty(object, name, flags, id); +//} +// +//QScriptValue DataViewClass::property(const QScriptValue &object, +// const QScriptString &name, uint id) { +// QByteArray* arrayBuffer = qscriptvalue_cast(object.property(_bufferName).data()); +// bool ok = false; +// name.toArrayIndex(&ok); +// +// if (ok && arrayBuffer) { +// return (*arrayBuffer)[id]; +// } +// +// return ArrayBufferViewClass::queryProperty(object, name, flags, id); +//} +// +//void DataViewClass::setProperty(QScriptValue &object, +// const QScriptString &name, +// uint id, const QScriptValue &value) { +// QByteArray *ba = qscriptvalue_cast(object.data()); +// if (!ba) +// return; +// if (name == length) { +// resize(*ba, value.toInt32()); +// } else { +// qint32 pos = id; +// if (pos < 0) +// return; +// if (ba->size() <= pos) +// resize(*ba, pos + 1); +// (*ba)[pos] = char(value.toInt32()); +// } +//} +// +//QScriptValue::PropertyFlags DataViewClass::propertyFlags(const QScriptValue& object, +// const QScriptString& name, uint id) { +// return QScriptValue::Undeletable; +//} diff --git a/libraries/script-engine/src/ArrayBufferViewClass.h b/libraries/script-engine/src/ArrayBufferViewClass.h new file mode 100644 index 0000000000..eb4a372843 --- /dev/null +++ b/libraries/script-engine/src/ArrayBufferViewClass.h @@ -0,0 +1,47 @@ +// +// 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 +// + +#ifndef hifi_ArrayBufferViewClass_h +#define hifi_ArrayBufferViewClass_h + +#include +#include +#include +#include +#include +#include +#include + +static const QString BUFFER_PROPERTY_NAME = "buffer"; +static const QString BYTE_OFFSET_PROPERTY_NAME = "byteOffset"; +static const QString BYTE_LENGTH_PROPERTY_NAME = "byteLength"; + +class ArrayBufferViewClass : public QObject, public QScriptClass { + Q_OBJECT +public: + ArrayBufferViewClass(QScriptEngine* engine); + + + QueryFlags queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id); + QScriptValue property(const QScriptValue &object, + const QScriptString &name, uint id); + QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, + const QScriptString& name, uint id); +protected: + // JS Object attributes + QScriptString _bufferName; + QScriptString _byteOffsetName; + QScriptString _byteLengthName; +}; + +#endif // hifi_ArrayBufferViewClass_h \ No newline at end of file From 2e480764088cba80f67dbc9fa7f73a86f0d116cc Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 8 Jul 2014 16:09:45 -0700 Subject: [PATCH 07/24] Added first draft of DataView scriptable objects --- libraries/script-engine/src/DataViewClass.cpp | 81 +++++++ libraries/script-engine/src/DataViewClass.h | 36 +++ .../script-engine/src/DataViewPrototype.cpp | 209 ++++++++++++++++++ .../script-engine/src/DataViewPrototype.h | 68 ++++++ 4 files changed, 394 insertions(+) create mode 100644 libraries/script-engine/src/DataViewClass.cpp create mode 100644 libraries/script-engine/src/DataViewClass.h create mode 100644 libraries/script-engine/src/DataViewPrototype.cpp create mode 100644 libraries/script-engine/src/DataViewPrototype.h diff --git a/libraries/script-engine/src/DataViewClass.cpp b/libraries/script-engine/src/DataViewClass.cpp new file mode 100644 index 0000000000..7afd43e71c --- /dev/null +++ b/libraries/script-engine/src/DataViewClass.cpp @@ -0,0 +1,81 @@ +// +// DataViewClass.cpp +// +// +// 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 +// + +#include "DataViewPrototype.h" + +#include "DataViewClass.h" + +Q_DECLARE_METATYPE(QByteArray*) + +static const QString DATA_VIEW_NAME = "DataView"; + +DataViewClass::DataViewClass(QScriptEngine* engine) : ArrayBufferViewClass(engine) { + QScriptValue global = engine->globalObject(); + + // Save string handles for quick lookup + _name = engine->toStringHandle(DATA_VIEW_NAME.toLatin1()); + + // build prototype + _proto = engine->newQObject(new DataViewPrototype(this), + QScriptEngine::QtOwnership, + QScriptEngine::SkipMethodsInEnumeration | + QScriptEngine::ExcludeSuperClassMethods | + QScriptEngine::ExcludeSuperClassProperties); + + _proto.setPrototype(global.property("Object").property("prototype")); + + _ctor = engine->newFunction(construct, _proto); + _ctor.setData(engine->toScriptValue(this)); + + engine->globalObject().setProperty(name(), _ctor); +} + +QScriptValue DataViewClass::newInstance(QScriptValue buffer, quint32 byteOffset, quint32 byteLentgh) { + QScriptValue data = engine()->newObject(); + data.setProperty(_bufferName, buffer); + data.setProperty(_byteOffsetName, byteOffset); + data.setProperty(_byteLengthName, byteLentgh); + + return engine()->newObject(this, data); +} + +QScriptValue DataViewClass::construct(QScriptContext *context, QScriptEngine *engine) { + DataViewClass* cls = qscriptvalue_cast(context->callee().data()); + if (!cls || context->argumentCount() < 1) { + return QScriptValue(); + } + + QScriptValue bufferArg = context->argument(0); + QScriptValue byteOffsetArg = (context->argumentCount() >= 2) ? context->argument(1) : QScriptValue(-1); + QScriptValue byteLengthArg = (context->argumentCount() >= 3) ? context->argument(2) : QScriptValue(-1); + + QByteArray* arrayBuffer = (bufferArg.isValid()) ? qscriptvalue_cast(bufferArg.data()) :NULL; + if (!arrayBuffer) { + return QScriptValue(); + } + + QScriptValue newObject = cls->newInstance(bufferArg, byteOffsetArg.toInt32(), byteLengthArg.toInt32()); + + if (context->isCalledAsConstructor()) { + context->setThisObject(newObject); + return engine->undefinedValue(); + } + + return newObject; +} + +QString DataViewClass::name() const { + return _name.toString(); +} + +QScriptValue DataViewClass::prototype() const { + return _proto; +} \ No newline at end of file diff --git a/libraries/script-engine/src/DataViewClass.h b/libraries/script-engine/src/DataViewClass.h new file mode 100644 index 0000000000..7e4d427f09 --- /dev/null +++ b/libraries/script-engine/src/DataViewClass.h @@ -0,0 +1,36 @@ +// +// DataViewClass.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 +// + +#ifndef hifi_DataViewClass_h +#define hifi_DataViewClass_h + +#include "ArrayBufferViewClass.h" + +class DataViewClass : public ArrayBufferViewClass { + Q_OBJECT +public: + DataViewClass(QScriptEngine* engine); + QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset = -1, quint32 byteLength = -1); + + QString name() const; + QScriptValue prototype() const; + +private: + static QScriptValue construct(QScriptContext* context, QScriptEngine* engine); + + QScriptValue _proto; + QScriptValue _ctor; + + QScriptString _name; +}; + + +#endif // hifi_DataViewClass_h \ No newline at end of file diff --git a/libraries/script-engine/src/DataViewPrototype.cpp b/libraries/script-engine/src/DataViewPrototype.cpp new file mode 100644 index 0000000000..345df743b4 --- /dev/null +++ b/libraries/script-engine/src/DataViewPrototype.cpp @@ -0,0 +1,209 @@ +// +// DataViewPrototype.cpp +// +// +// 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 +// +#include + +#include + +#include "DataViewClass.h" + +#include "DataViewPrototype.h" + +Q_DECLARE_METATYPE(QByteArray*) + +DataViewPrototype::DataViewPrototype(QObject* parent) : QObject(parent) { +} + +QByteArray* DataViewPrototype::thisArrayBuffer() const { + QScriptValue bufferObject = thisObject().data().property(BUFFER_PROPERTY_NAME); + return qscriptvalue_cast(bufferObject.data()); +} + +bool DataViewPrototype::realOffset(quint32& offset, size_t size) const { + quint32 viewOffset = thisObject().data().property(BYTE_OFFSET_PROPERTY_NAME).toInt32(); + quint32 viewLength = thisObject().data().property(BYTE_LENGTH_PROPERTY_NAME).toInt32(); + qDebug() << "View Offset: " << viewOffset << ", View Lenght: " << viewLength; + qDebug() << "Offset: " << offset << ", Size: " << size; + offset += viewOffset; + qDebug() << "New offset: " << offset << ", bool: " << ((offset + size) <= viewOffset + viewLength); + return (offset + size) <= viewOffset + viewLength; +} + +///////////////// GETTERS //////////////////////////// + +qint8 DataViewPrototype::getInt8(quint32 byteOffset) { + if (realOffset(byteOffset, sizeof(qint8))) { + qDebug() << "Value: " << (qint8)thisArrayBuffer()->at(byteOffset); + + return (qint8)thisArrayBuffer()->at(byteOffset); + } + qDebug() << "42 powaaaa!!!"; + return 42; +} + +quint8 DataViewPrototype::getUint8(quint32 byteOffset) { + if (realOffset(byteOffset, sizeof(quint8))) { return thisArrayBuffer()->at(byteOffset); + } + return 42; +} + +qint16 DataViewPrototype::getInt16(quint32 byteOffset, bool littleEndian) { + if (realOffset(byteOffset, sizeof(qint16))) { + QDataStream stream(*thisArrayBuffer()); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.skipRawData(byteOffset); + + qint16 result; + stream >> result; + return result; + } + return 42; +} + +quint16 DataViewPrototype::getUint16(quint32 byteOffset, bool littleEndian) { + if (realOffset(byteOffset, sizeof(quint16))) { + QDataStream stream(*thisArrayBuffer()); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.skipRawData(byteOffset); + + qint16 result; + stream >> result; + return result; + } + return 42; +} + +qint32 DataViewPrototype::getInt32(quint32 byteOffset, bool littleEndian) { + if (realOffset(byteOffset, sizeof(qint32))) { + QDataStream stream(*thisArrayBuffer()); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.skipRawData(byteOffset); + + qint16 result; + stream >> result; + return result; + } + return 42; +} + +quint32 DataViewPrototype::getUint32(quint32 byteOffset, bool littleEndian) { + if (realOffset(byteOffset, sizeof(quint32))) { + QDataStream stream(*thisArrayBuffer()); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.skipRawData(byteOffset); + + qint16 result; + stream >> result; + return result; + } + return 42; +} + +float DataViewPrototype::getFloat32(quint32 byteOffset, bool littleEndian) { + if (realOffset(byteOffset, sizeof(float))) { + QDataStream stream(*thisArrayBuffer()); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.skipRawData(byteOffset); + + qint16 result; + stream >> result; + return result; + } + return 42; +} + +double DataViewPrototype::getFloat64(quint32 byteOffset, bool littleEndian) { + if (realOffset(byteOffset, sizeof(double))) { + QDataStream stream(*thisArrayBuffer()); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.skipRawData(byteOffset); + + qint16 result; + stream >> result; + return result; + } + return 42; +} + +///////////////// SETTERS //////////////////////////// + +void DataViewPrototype::setInt8(quint32 byteOffset, qint8 value) { + if (realOffset(byteOffset, sizeof(qint8))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + +void DataViewPrototype::setUint8(quint32 byteOffset, quint8 value) { + if (realOffset(byteOffset, sizeof(quint8))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + +void DataViewPrototype::setInt16(quint32 byteOffset, qint16 value, bool littleEndian) { + if (realOffset(byteOffset, sizeof(qint16))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + +void DataViewPrototype::setUint16(quint32 byteOffset, quint16 value, bool littleEndian) { + if (realOffset(byteOffset, sizeof(quint16))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + +void DataViewPrototype::setInt32(quint32 byteOffset, quint32 value, bool littleEndian) { + if (realOffset(byteOffset, sizeof(qint32))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + +void DataViewPrototype::setUint32(quint32 byteOffset, quint32 value, bool littleEndian) { + if (realOffset(byteOffset, sizeof(quint32))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + +void DataViewPrototype::setFloat32(quint32 byteOffset, float value, bool littleEndian) { + if (realOffset(byteOffset, sizeof(float))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + +void DataViewPrototype::setFloat64(quint32 byteOffset, double value, bool littleEndian) { + if (realOffset(byteOffset, sizeof(double))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + + diff --git a/libraries/script-engine/src/DataViewPrototype.h b/libraries/script-engine/src/DataViewPrototype.h new file mode 100644 index 0000000000..a024e8938f --- /dev/null +++ b/libraries/script-engine/src/DataViewPrototype.h @@ -0,0 +1,68 @@ +// +// DataViewPrototype.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 +// + +#ifndef hifi_DataViewPrototype_h +#define hifi_DataViewPrototype_h + +#include +#include + +class DataViewPrototype : public QObject, public QScriptable { + Q_OBJECT +public: + DataViewPrototype(QObject* parent = NULL); + +public slots: + // Gets the value of the given type at the specified byte offset + // from the start of the view. There is no alignment constraint; + // multi-byte values may be fetched from any offset. + // + // For multi-byte values, the optional littleEndian argument + // indicates whether a big-endian or little-endian value should be + // read. If false or undefined, a big-endian value is read. + // + // These methods raise an exception if they would read + // beyond the end of the view. + qint8 getInt8(quint32 byteOffset); + quint8 getUint8(quint32 byteOffset); + qint16 getInt16(quint32 byteOffset, bool littleEndian = false); + quint16 getUint16(quint32 byteOffset, bool littleEndian = false); + qint32 getInt32(quint32 byteOffset, bool littleEndian = false); + quint32 getUint32(quint32 byteOffset, bool littleEndian = false); + float getFloat32(quint32 byteOffset, bool littleEndian = false); + double getFloat64(quint32 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; + // multi-byte values may be stored at any offset. + // + // For multi-byte values, the optional littleEndian argument + // indicates whether the value should be stored in big-endian or + // little-endian byte order. If false or undefined, the value is + // stored in big-endian byte order. + // + // These methods raise an exception if they would write + // beyond the end of the view. + void setInt8(quint32 byteOffset, qint8 value); + void setUint8(quint32 byteOffset, quint8 value); + void setInt16(quint32 byteOffset, qint16 value, bool littleEndian = false); + void setUint16(quint32 byteOffset, quint16 value, bool littleEndian = false); + void setInt32(quint32 byteOffset, quint32 value, bool littleEndian = false); + void setUint32(quint32 byteOffset, quint32 value, bool littleEndian = false); + void setFloat32(quint32 byteOffset, float value, bool littleEndian = false); + void setFloat64(quint32 byteOffset, double value, bool littleEndian = false); + +private: + QByteArray* thisArrayBuffer() const; + bool realOffset(quint32& offset, size_t size) const; +}; + +#endif // hifi_DataViewPrototype_h \ No newline at end of file From 7fe88a4426cb609757733a7cbf087b4608e71620 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 8 Jul 2014 16:10:22 -0700 Subject: [PATCH 08/24] Hooked up ArrayBuffer, ArrayBufferView and DataView to ScriptEngine --- libraries/script-engine/src/ScriptEngine.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index fab50784a2..59a9563c56 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -33,6 +33,8 @@ #include "AnimationObject.h" #include "ArrayBufferClass.h" +#include "ArrayBufferViewClass.h" +#include "DataViewClass.h" #include "MenuItemProperties.h" #include "MIDIEvent.h" #include "LocalVoxels.h" @@ -213,6 +215,8 @@ void ScriptEngine::init() { } new ArrayBufferClass(&_engine); + new ArrayBufferViewClass(&_engine); + new DataViewClass(&_engine); _isInitialized = true; From 4b2e4b7b28a35a33f0dc167225047e6efbf195b5 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 8 Jul 2014 16:59:27 -0700 Subject: [PATCH 09/24] Some type modifications in DataViewPrototype --- .../script-engine/src/DataViewPrototype.cpp | 66 +++++++++++-------- .../script-engine/src/DataViewPrototype.h | 18 ++--- 2 files changed, 46 insertions(+), 38 deletions(-) diff --git a/libraries/script-engine/src/DataViewPrototype.cpp b/libraries/script-engine/src/DataViewPrototype.cpp index 345df743b4..2965f98809 100644 --- a/libraries/script-engine/src/DataViewPrototype.cpp +++ b/libraries/script-engine/src/DataViewPrototype.cpp @@ -38,23 +38,31 @@ bool DataViewPrototype::realOffset(quint32& offset, size_t size) const { ///////////////// GETTERS //////////////////////////// -qint8 DataViewPrototype::getInt8(quint32 byteOffset) { +qint32 DataViewPrototype::getInt8(quint32 byteOffset) { if (realOffset(byteOffset, sizeof(qint8))) { - qDebug() << "Value: " << (qint8)thisArrayBuffer()->at(byteOffset); - - return (qint8)thisArrayBuffer()->at(byteOffset); - } - qDebug() << "42 powaaaa!!!"; - return 42; -} - -quint8 DataViewPrototype::getUint8(quint32 byteOffset) { - if (realOffset(byteOffset, sizeof(quint8))) { return thisArrayBuffer()->at(byteOffset); + QDataStream stream(*thisArrayBuffer()); + stream.skipRawData(byteOffset); + + qint8 result; + stream >> result; + return result; } return 42; } -qint16 DataViewPrototype::getInt16(quint32 byteOffset, bool littleEndian) { +quint32 DataViewPrototype::getUint8(quint32 byteOffset) { + if (realOffset(byteOffset, sizeof(quint8))) { + QDataStream stream(*thisArrayBuffer()); + stream.skipRawData(byteOffset); + + quint8 result; + stream >> result; + return result; + } + return 42; +} + +qint32 DataViewPrototype::getInt16(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint16))) { QDataStream stream(*thisArrayBuffer()); stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); @@ -67,13 +75,13 @@ qint16 DataViewPrototype::getInt16(quint32 byteOffset, bool littleEndian) { return 42; } -quint16 DataViewPrototype::getUint16(quint32 byteOffset, bool littleEndian) { +quint32 DataViewPrototype::getUint16(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint16))) { QDataStream stream(*thisArrayBuffer()); stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); - qint16 result; + quint16 result; stream >> result; return result; } @@ -86,7 +94,7 @@ qint32 DataViewPrototype::getInt32(quint32 byteOffset, bool littleEndian) { stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); - qint16 result; + qint32 result; stream >> result; return result; } @@ -99,7 +107,7 @@ quint32 DataViewPrototype::getUint32(quint32 byteOffset, bool littleEndian) { stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); - qint16 result; + quint32 result; stream >> result; return result; } @@ -112,7 +120,7 @@ float DataViewPrototype::getFloat32(quint32 byteOffset, bool littleEndian) { stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); - qint16 result; + float result; stream >> result; return result; } @@ -125,7 +133,7 @@ double DataViewPrototype::getFloat64(quint32 byteOffset, bool littleEndian) { stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); - qint16 result; + double result; stream >> result; return result; } @@ -134,48 +142,48 @@ double DataViewPrototype::getFloat64(quint32 byteOffset, bool littleEndian) { ///////////////// SETTERS //////////////////////////// -void DataViewPrototype::setInt8(quint32 byteOffset, qint8 value) { +void DataViewPrototype::setInt8(quint32 byteOffset, qint32 value) { if (realOffset(byteOffset, sizeof(qint8))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); - stream << value; + stream << (qint8)value; } } -void DataViewPrototype::setUint8(quint32 byteOffset, quint8 value) { +void DataViewPrototype::setUint8(quint32 byteOffset, quint32 value) { if (realOffset(byteOffset, sizeof(quint8))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); - stream << value; + stream << (quint8)value; } } -void DataViewPrototype::setInt16(quint32 byteOffset, qint16 value, bool littleEndian) { +void DataViewPrototype::setInt16(quint32 byteOffset, qint32 value, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint16))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); - stream << value; + stream << (qint16)value; } } -void DataViewPrototype::setUint16(quint32 byteOffset, quint16 value, bool littleEndian) { +void DataViewPrototype::setUint16(quint32 byteOffset, quint32 value, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint16))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); - stream << value; + stream << (quint16)value; } } -void DataViewPrototype::setInt32(quint32 byteOffset, quint32 value, bool littleEndian) { +void DataViewPrototype::setInt32(quint32 byteOffset, qint32 value, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint32))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); - stream << value; + stream << (qint32)value; } } @@ -184,7 +192,7 @@ void DataViewPrototype::setUint32(quint32 byteOffset, quint32 value, bool little QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); - stream << value; + stream << (quint32)value; } } diff --git a/libraries/script-engine/src/DataViewPrototype.h b/libraries/script-engine/src/DataViewPrototype.h index a024e8938f..97c94c58ce 100644 --- a/libraries/script-engine/src/DataViewPrototype.h +++ b/libraries/script-engine/src/DataViewPrototype.h @@ -31,10 +31,10 @@ public slots: // // These methods raise an exception if they would read // beyond the end of the view. - qint8 getInt8(quint32 byteOffset); - quint8 getUint8(quint32 byteOffset); - qint16 getInt16(quint32 byteOffset, bool littleEndian = false); - quint16 getUint16(quint32 byteOffset, bool littleEndian = false); + qint32 getInt8(quint32 byteOffset); + quint32 getUint8(quint32 byteOffset); + qint32 getInt16(quint32 byteOffset, bool littleEndian = false); + quint32 getUint16(quint32 byteOffset, bool littleEndian = false); qint32 getInt32(quint32 byteOffset, bool littleEndian = false); quint32 getUint32(quint32 byteOffset, bool littleEndian = false); float getFloat32(quint32 byteOffset, bool littleEndian = false); @@ -51,11 +51,11 @@ public slots: // // These methods raise an exception if they would write // beyond the end of the view. - void setInt8(quint32 byteOffset, qint8 value); - void setUint8(quint32 byteOffset, quint8 value); - void setInt16(quint32 byteOffset, qint16 value, bool littleEndian = false); - void setUint16(quint32 byteOffset, quint16 value, bool littleEndian = false); - void setInt32(quint32 byteOffset, quint32 value, bool littleEndian = false); + void setInt8(quint32 byteOffset, qint32 value); + void setUint8(quint32 byteOffset, quint32 value); + void setInt16(quint32 byteOffset, qint32 value, bool littleEndian = false); + void setUint16(quint32 byteOffset, quint32 value, bool littleEndian = false); + void setInt32(quint32 byteOffset, qint32 value, bool littleEndian = false); void setUint32(quint32 byteOffset, quint32 value, bool littleEndian = false); void setFloat32(quint32 byteOffset, float value, bool littleEndian = false); void setFloat64(quint32 byteOffset, double value, bool littleEndian = false); From 28438d66b1a5e31f25b085cf6bda2fc6fbeefe93 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 9 Jul 2014 10:27:23 -0700 Subject: [PATCH 10/24] Fixed exceptions/endian issues with DataViews --- libraries/script-engine/src/DataViewClass.cpp | 23 ++++++- libraries/script-engine/src/DataViewClass.h | 2 +- .../script-engine/src/DataViewPrototype.cpp | 64 ++++++++++++++----- 3 files changed, 68 insertions(+), 21 deletions(-) diff --git a/libraries/script-engine/src/DataViewClass.cpp b/libraries/script-engine/src/DataViewClass.cpp index 7afd43e71c..38a85e55a8 100644 --- a/libraries/script-engine/src/DataViewClass.cpp +++ b/libraries/script-engine/src/DataViewClass.cpp @@ -54,15 +54,32 @@ QScriptValue DataViewClass::construct(QScriptContext *context, QScriptEngine *en } QScriptValue bufferArg = context->argument(0); - QScriptValue byteOffsetArg = (context->argumentCount() >= 2) ? context->argument(1) : QScriptValue(-1); - QScriptValue byteLengthArg = (context->argumentCount() >= 3) ? context->argument(2) : QScriptValue(-1); + QScriptValue byteOffsetArg = (context->argumentCount() >= 2) ? context->argument(1) : QScriptValue(); + QScriptValue byteLengthArg = (context->argumentCount() >= 3) ? context->argument(2) : QScriptValue(); QByteArray* arrayBuffer = (bufferArg.isValid()) ? qscriptvalue_cast(bufferArg.data()) :NULL; if (!arrayBuffer) { + engine->evaluate("throw \"TypeError: 1st argument not a ArrayBuffer\""); return QScriptValue(); } + if (byteOffsetArg.isNumber() && + (byteOffsetArg.toInt32() < 0 || + byteOffsetArg.toInt32() >= arrayBuffer->size())) { + engine->evaluate("throw \"RangeError: byteOffset out of range\""); + return QScriptValue(); + } + if (byteLengthArg.isNumber() && + (byteLengthArg.toInt32() < 0 || + byteOffsetArg.toInt32() + byteLengthArg.toInt32() > arrayBuffer->size())) { + engine->evaluate("throw \"RangeError: byteLength out of range\""); + return QScriptValue(); + } - QScriptValue newObject = cls->newInstance(bufferArg, byteOffsetArg.toInt32(), byteLengthArg.toInt32()); + QScriptValue newObject = cls->newInstance(bufferArg, + (byteOffsetArg.isNumber()) ? byteOffsetArg.toInt32() + : 0, + (byteLengthArg.isNumber()) ? byteLengthArg.toInt32() + : arrayBuffer->size()); if (context->isCalledAsConstructor()) { context->setThisObject(newObject); diff --git a/libraries/script-engine/src/DataViewClass.h b/libraries/script-engine/src/DataViewClass.h index 7e4d427f09..6b6d6d25e7 100644 --- a/libraries/script-engine/src/DataViewClass.h +++ b/libraries/script-engine/src/DataViewClass.h @@ -18,7 +18,7 @@ class DataViewClass : public ArrayBufferViewClass { Q_OBJECT public: DataViewClass(QScriptEngine* engine); - QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset = -1, quint32 byteLength = -1); + QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 byteLength); QString name() const; QScriptValue prototype() const; diff --git a/libraries/script-engine/src/DataViewPrototype.cpp b/libraries/script-engine/src/DataViewPrototype.cpp index 2965f98809..17734e109c 100644 --- a/libraries/script-engine/src/DataViewPrototype.cpp +++ b/libraries/script-engine/src/DataViewPrototype.cpp @@ -29,10 +29,10 @@ QByteArray* DataViewPrototype::thisArrayBuffer() const { bool DataViewPrototype::realOffset(quint32& offset, size_t size) const { quint32 viewOffset = thisObject().data().property(BYTE_OFFSET_PROPERTY_NAME).toInt32(); quint32 viewLength = thisObject().data().property(BYTE_LENGTH_PROPERTY_NAME).toInt32(); - qDebug() << "View Offset: " << viewOffset << ", View Lenght: " << viewLength; - qDebug() << "Offset: " << offset << ", Size: " << size; + //qDebug() << "View Offset: " << viewOffset << ", View Lenght: " << viewLength; + //qDebug() << "Offset: " << offset << ", Size: " << size; offset += viewOffset; - qDebug() << "New offset: " << offset << ", bool: " << ((offset + size) <= viewOffset + viewLength); + //qDebug() << "New offset: " << offset << ", bool: " << ((offset + size) <= viewOffset + viewLength); return (offset + size) <= viewOffset + viewLength; } @@ -47,7 +47,8 @@ qint32 DataViewPrototype::getInt8(quint32 byteOffset) { stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } quint32 DataViewPrototype::getUint8(quint32 byteOffset) { @@ -59,85 +60,92 @@ quint32 DataViewPrototype::getUint8(quint32 byteOffset) { stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } qint32 DataViewPrototype::getInt16(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint16))) { QDataStream stream(*thisArrayBuffer()); - stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); qint16 result; stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } quint32 DataViewPrototype::getUint16(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint16))) { QDataStream stream(*thisArrayBuffer()); - stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); quint16 result; stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } qint32 DataViewPrototype::getInt32(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint32))) { QDataStream stream(*thisArrayBuffer()); - stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); qint32 result; stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } quint32 DataViewPrototype::getUint32(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint32))) { QDataStream stream(*thisArrayBuffer()); - stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); quint32 result; stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } float DataViewPrototype::getFloat32(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(float))) { QDataStream stream(*thisArrayBuffer()); - stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); float result; stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } double DataViewPrototype::getFloat64(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(double))) { QDataStream stream(*thisArrayBuffer()); - stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); double result; stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } ///////////////// SETTERS //////////////////////////// @@ -148,6 +156,8 @@ void DataViewPrototype::setInt8(quint32 byteOffset, qint32 value) { stream.skipRawData(byteOffset); stream << (qint8)value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } @@ -157,6 +167,8 @@ void DataViewPrototype::setUint8(quint32 byteOffset, quint32 value) { stream.skipRawData(byteOffset); stream << (quint8)value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } @@ -164,8 +176,11 @@ void DataViewPrototype::setInt16(quint32 byteOffset, qint32 value, bool littleEn if (realOffset(byteOffset, sizeof(qint16))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream << (qint16)value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } @@ -173,8 +188,11 @@ void DataViewPrototype::setUint16(quint32 byteOffset, quint32 value, bool little if (realOffset(byteOffset, sizeof(quint16))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream << (quint16)value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } @@ -182,8 +200,11 @@ void DataViewPrototype::setInt32(quint32 byteOffset, qint32 value, bool littleEn if (realOffset(byteOffset, sizeof(qint32))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream << (qint32)value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } @@ -191,8 +212,11 @@ void DataViewPrototype::setUint32(quint32 byteOffset, quint32 value, bool little if (realOffset(byteOffset, sizeof(quint32))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream << (quint32)value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } @@ -200,8 +224,11 @@ void DataViewPrototype::setFloat32(quint32 byteOffset, float value, bool littleE if (realOffset(byteOffset, sizeof(float))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream << value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } @@ -209,8 +236,11 @@ void DataViewPrototype::setFloat64(quint32 byteOffset, double value, bool little if (realOffset(byteOffset, sizeof(double))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream << value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } From a76ebae771c6bd4e0e5e73e94492b8219d224659 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 9 Jul 2014 10:28:50 -0700 Subject: [PATCH 11/24] Added typed array unit test --- examples/typedArraysUnitTest.js | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/examples/typedArraysUnitTest.js b/examples/typedArraysUnitTest.js index 98f69f3e71..6bd35365e8 100644 --- a/examples/typedArraysUnitTest.js +++ b/examples/typedArraysUnitTest.js @@ -11,12 +11,31 @@ Script.include("Test.js"); -test("ArrayBuffer", function(finished) { - this.assertEquals(new ArrayBuffer().byteLength, 0, 'no length'); +test('ArrayBuffer', function(finished) { + this.assertEquals(new ArrayBuffer(0).byteLength, 0, 'no length'); this.assertEquals(typeof(new ArrayBuffer(0)), 'object', 'creation'); this.assertEquals(typeof(new ArrayBuffer(1)), 'object', 'creation'); this.assertEquals(typeof(new ArrayBuffer(123)), 'object', 'creation'); this.assertEquals(new ArrayBuffer(123).byteLength, 123, 'length'); -}); \ No newline at end of file +}); + +test('DataView constructors', function (finished) { + var d = new DataView(new ArrayBuffer(8)); + + d.setUint32(0, 0x12345678); + this.assertEquals(d.getUint32(0), 0x12345678, 'big endian/big endian'); + + d.setUint32(0, 0x12345678, true); + this.assertEquals(d.getUint32(0, true), 0x12345678, 'little endian/little endian'); + + d.setUint32(0, 0x12345678, true); + this.assertEquals(d.getUint32(0), 0x78563412, 'little endian/big endian'); + + d.setUint32(0, 0x12345678); + this.assertEquals(d.getUint32(0, true), 0x78563412, 'big endian/little endian'); + + // raises(function () { return new DataView({}); }, 'non-ArrayBuffer argument'); + // raises(function () { return new DataView("bogus"); }, TypeError, 'non-ArrayBuffer argument'); +}); From b2b17019658055b9c08221421e3fdff4ebd17eee Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 9 Jul 2014 17:47:09 -0700 Subject: [PATCH 12/24] - Bunch of fixes to typed arrays - First working prototype for Int8Array - Base classes for all other typed arrays --- .../script-engine/src/ArrayBufferClass.cpp | 28 ++- .../script-engine/src/ArrayBufferClass.h | 8 +- .../src/ArrayBufferViewClass.cpp | 13 +- .../script-engine/src/ArrayBufferViewClass.h | 13 +- libraries/script-engine/src/DataViewClass.cpp | 14 +- libraries/script-engine/src/DataViewClass.h | 2 +- libraries/script-engine/src/ScriptEngine.cpp | 28 ++- libraries/script-engine/src/ScriptEngine.h | 6 + libraries/script-engine/src/TypedArrays.cpp | 233 ++++++++++++++++++ libraries/script-engine/src/TypedArrays.h | 104 ++++++++ 10 files changed, 412 insertions(+), 37 deletions(-) create mode 100644 libraries/script-engine/src/TypedArrays.cpp create mode 100644 libraries/script-engine/src/TypedArrays.h diff --git a/libraries/script-engine/src/ArrayBufferClass.cpp b/libraries/script-engine/src/ArrayBufferClass.cpp index d003125c1e..d86e229a1a 100644 --- a/libraries/script-engine/src/ArrayBufferClass.cpp +++ b/libraries/script-engine/src/ArrayBufferClass.cpp @@ -9,8 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include "ArrayBufferPrototype.h" +#include "ScriptEngine.h" #include "ArrayBufferClass.h" @@ -19,16 +21,20 @@ static const QString BYTE_LENGTH_PROPERTY_NAME = "byteLength"; Q_DECLARE_METATYPE(QByteArray*) -ArrayBufferClass::ArrayBufferClass(QScriptEngine* engine) : QObject(engine), QScriptClass(engine) { - qScriptRegisterMetaType(engine, toScriptValue, fromScriptValue); - QScriptValue global = engine->globalObject(); +ArrayBufferClass::ArrayBufferClass(ScriptEngine* scriptEngine) : +QObject(scriptEngine->getScriptEngine()), +QScriptClass(scriptEngine->getScriptEngine()), +_scriptEngine(scriptEngine) { + qDebug() << "Created with engine: " << engine(); + qScriptRegisterMetaType(engine(), toScriptValue, fromScriptValue); + QScriptValue global = engine()->globalObject(); // Save string handles for quick lookup - _name = engine->toStringHandle(CLASS_NAME.toLatin1()); - _byteLength = engine->toStringHandle(BYTE_LENGTH_PROPERTY_NAME.toLatin1()); + _name = engine()->toStringHandle(CLASS_NAME.toLatin1()); + _byteLength = engine()->toStringHandle(BYTE_LENGTH_PROPERTY_NAME.toLatin1()); // build prototype - _proto = engine->newQObject(new ArrayBufferPrototype(this), + _proto = engine()->newQObject(new ArrayBufferPrototype(this), QScriptEngine::QtOwnership, QScriptEngine::SkipMethodsInEnumeration | QScriptEngine::ExcludeSuperClassMethods | @@ -36,15 +42,17 @@ ArrayBufferClass::ArrayBufferClass(QScriptEngine* engine) : QObject(engine), QSc _proto.setPrototype(global.property("Object").property("prototype")); - _ctor = engine->newFunction(construct, _proto); - _ctor.setData(engine->toScriptValue(this)); + _ctor = engine()->newFunction(construct, _proto); + _ctor.setData(engine()->toScriptValue(this)); - engine->globalObject().setProperty(name(), _ctor); + engine()->globalObject().setProperty(name(), _ctor); } QScriptValue ArrayBufferClass::newInstance(quint32 size) { engine()->reportAdditionalMemoryCost(size); - QScriptValue data = engine()->newVariant(QVariant::fromValue(QByteArray(size, 0))); + QScriptEngine* eng = engine(); + QVariant variant = QVariant::fromValue(QByteArray(size, 0)); + QScriptValue data = eng->newVariant(variant); return engine()->newObject(this, data); } diff --git a/libraries/script-engine/src/ArrayBufferClass.h b/libraries/script-engine/src/ArrayBufferClass.h index 2f863e77a5..c56b659eff 100644 --- a/libraries/script-engine/src/ArrayBufferClass.h +++ b/libraries/script-engine/src/ArrayBufferClass.h @@ -20,13 +20,17 @@ #include #include +class ScriptEngine; + class ArrayBufferClass : public QObject, public QScriptClass { Q_OBJECT public: - ArrayBufferClass(QScriptEngine* engine); + ArrayBufferClass(ScriptEngine* scriptEngine); QScriptValue newInstance(quint32 size); QScriptValue newInstance(const QByteArray& ba); + ScriptEngine* getScriptEngine() { return _scriptEngine; } + QueryFlags queryProperty(const QScriptValue& object, const QScriptString& name, QueryFlags flags, uint* id); @@ -50,6 +54,8 @@ private: // JS Object attributes QScriptString _name; QScriptString _byteLength; + + ScriptEngine* _scriptEngine; }; #endif // hifi_ArrayBufferClass_h \ No newline at end of file diff --git a/libraries/script-engine/src/ArrayBufferViewClass.cpp b/libraries/script-engine/src/ArrayBufferViewClass.cpp index 73d833dab4..a01f239512 100644 --- a/libraries/script-engine/src/ArrayBufferViewClass.cpp +++ b/libraries/script-engine/src/ArrayBufferViewClass.cpp @@ -13,13 +13,14 @@ Q_DECLARE_METATYPE(QByteArray*) -ArrayBufferViewClass::ArrayBufferViewClass(QScriptEngine* engine) : -QObject(engine), -QScriptClass(engine) { +ArrayBufferViewClass::ArrayBufferViewClass(ScriptEngine* scriptEngine) : +QObject(scriptEngine->getScriptEngine()), +QScriptClass(scriptEngine->getScriptEngine()), +_scriptEngine(scriptEngine) { // Save string handles for quick lookup - _bufferName = engine->toStringHandle(BUFFER_PROPERTY_NAME.toLatin1()); - _byteOffsetName = engine->toStringHandle(BYTE_OFFSET_PROPERTY_NAME.toLatin1()); - _byteLengthName = engine->toStringHandle(BYTE_LENGTH_PROPERTY_NAME.toLatin1()); + _bufferName = engine()->toStringHandle(BUFFER_PROPERTY_NAME.toLatin1()); + _byteOffsetName = engine()->toStringHandle(BYTE_OFFSET_PROPERTY_NAME.toLatin1()); + _byteLengthName = engine()->toStringHandle(BYTE_LENGTH_PROPERTY_NAME.toLatin1()); } QScriptClass::QueryFlags ArrayBufferViewClass::queryProperty(const QScriptValue& object, diff --git a/libraries/script-engine/src/ArrayBufferViewClass.h b/libraries/script-engine/src/ArrayBufferViewClass.h index eb4a372843..acc061c8a3 100644 --- a/libraries/script-engine/src/ArrayBufferViewClass.h +++ b/libraries/script-engine/src/ArrayBufferViewClass.h @@ -20,6 +20,8 @@ #include #include +#include "ScriptEngine.h" + static const QString BUFFER_PROPERTY_NAME = "buffer"; static const QString BYTE_OFFSET_PROPERTY_NAME = "byteOffset"; static const QString BYTE_LENGTH_PROPERTY_NAME = "byteLength"; @@ -27,21 +29,24 @@ static const QString BYTE_LENGTH_PROPERTY_NAME = "byteLength"; class ArrayBufferViewClass : public QObject, public QScriptClass { Q_OBJECT public: - ArrayBufferViewClass(QScriptEngine* engine); + ArrayBufferViewClass(ScriptEngine* scriptEngine); + ScriptEngine* getScriptEngine() { return _scriptEngine; } - QueryFlags queryProperty(const QScriptValue& object, + virtual QueryFlags queryProperty(const QScriptValue& object, const QScriptString& name, QueryFlags flags, uint* id); - QScriptValue property(const QScriptValue &object, + virtual QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, + virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, const QScriptString& name, uint id); protected: // JS Object attributes QScriptString _bufferName; QScriptString _byteOffsetName; QScriptString _byteLengthName; + + ScriptEngine* _scriptEngine; }; #endif // hifi_ArrayBufferViewClass_h \ No newline at end of file diff --git a/libraries/script-engine/src/DataViewClass.cpp b/libraries/script-engine/src/DataViewClass.cpp index 38a85e55a8..4a9420e4e9 100644 --- a/libraries/script-engine/src/DataViewClass.cpp +++ b/libraries/script-engine/src/DataViewClass.cpp @@ -17,14 +17,14 @@ Q_DECLARE_METATYPE(QByteArray*) static const QString DATA_VIEW_NAME = "DataView"; -DataViewClass::DataViewClass(QScriptEngine* engine) : ArrayBufferViewClass(engine) { - QScriptValue global = engine->globalObject(); +DataViewClass::DataViewClass(ScriptEngine* scriptEngine) : ArrayBufferViewClass(scriptEngine) { + QScriptValue global = engine()->globalObject(); // Save string handles for quick lookup - _name = engine->toStringHandle(DATA_VIEW_NAME.toLatin1()); + _name = engine()->toStringHandle(DATA_VIEW_NAME.toLatin1()); // build prototype - _proto = engine->newQObject(new DataViewPrototype(this), + _proto = engine()->newQObject(new DataViewPrototype(this), QScriptEngine::QtOwnership, QScriptEngine::SkipMethodsInEnumeration | QScriptEngine::ExcludeSuperClassMethods | @@ -32,10 +32,10 @@ DataViewClass::DataViewClass(QScriptEngine* engine) : ArrayBufferViewClass(engin _proto.setPrototype(global.property("Object").property("prototype")); - _ctor = engine->newFunction(construct, _proto); - _ctor.setData(engine->toScriptValue(this)); + _ctor = engine()->newFunction(construct, _proto); + _ctor.setData(engine()->toScriptValue(this)); - engine->globalObject().setProperty(name(), _ctor); + engine()->globalObject().setProperty(name(), _ctor); } QScriptValue DataViewClass::newInstance(QScriptValue buffer, quint32 byteOffset, quint32 byteLentgh) { diff --git a/libraries/script-engine/src/DataViewClass.h b/libraries/script-engine/src/DataViewClass.h index 6b6d6d25e7..b87803f4b4 100644 --- a/libraries/script-engine/src/DataViewClass.h +++ b/libraries/script-engine/src/DataViewClass.h @@ -17,7 +17,7 @@ class DataViewClass : public ArrayBufferViewClass { Q_OBJECT public: - DataViewClass(QScriptEngine* engine); + DataViewClass(ScriptEngine* scriptEngine); QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 byteLength); QString name() const; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index f9161b9e51..73983161ff 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -32,13 +32,13 @@ #include #include "AnimationObject.h" -#include "ArrayBufferClass.h" #include "ArrayBufferViewClass.h" #include "DataViewClass.h" #include "MenuItemProperties.h" #include "MIDIEvent.h" #include "LocalVoxels.h" #include "ScriptEngine.h" +#include "TypedArrays.h" #include "XMLHttpRequestClass.h" VoxelsScriptingInterface ScriptEngine::_voxelsScriptingInterface; @@ -93,8 +93,15 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam _quatLibrary(), _vec3Library(), _uuidLibrary(), - _animationCache(this) + _animationCache(this), + _arrayBufferClass(NULL) { + _arrayBufferClass = new ArrayBufferClass(this); + qDebug() << "Engine: " << this; + qDebug() << "ArrayBuffer: " <<_arrayBufferClass; + qDebug() << "DataView: " << new DataViewClass(this); + qDebug() << "Int8Array: " << new Int8ArrayClass(this); + qDebug() << "Initial thread: " << QThread::currentThread() << " " << thread(); } ScriptEngine::ScriptEngine(const QUrl& scriptURL, @@ -118,13 +125,21 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, _quatLibrary(), _vec3Library(), _uuidLibrary(), - _animationCache(this) + _animationCache(this), + _arrayBufferClass(NULL) { QString scriptURLString = scriptURL.toString(); _fileNameString = scriptURLString; QUrl url(scriptURL); - + + _arrayBufferClass = new ArrayBufferClass(this); + qDebug() << "Engine: " << this; + qDebug() << "ArrayBuffer: " <<_arrayBufferClass; + qDebug() << "DataView: " << new DataViewClass(this); + qDebug() << "Int8Array: " << new Int8ArrayClass(this); + qDebug() << "Initial thread: " << QThread::currentThread() << " " << thread(); + // if the scheme length is one or lower, maybe they typed in a file, let's try const int WINDOWS_DRIVE_LETTER_SIZE = 1; if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) { @@ -141,6 +156,7 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, QTextStream in(&scriptFile); _scriptContents = in.readAll(); } else { + qDebug() << "ERROR Loading file:" << fileName; emit errorMessage("ERROR Loading file:" + fileName); } @@ -213,10 +229,6 @@ void ScriptEngine::init() { if (_isInitialized) { return; // only initialize once } - - new ArrayBufferClass(&_engine); - new ArrayBufferViewClass(&_engine); - new DataViewClass(&_engine); _isInitialized = true; diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 5b01b8124a..3dda172044 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -25,6 +25,7 @@ #include #include "AbstractControllerScriptingInterface.h" +#include "ArrayBufferClass.h" #include "Quat.h" #include "ScriptUUID.h" #include "Vec3.h" @@ -56,6 +57,9 @@ public: /// Access the ModelsScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener static ModelsScriptingInterface* getModelsScriptingInterface() { return &_modelsScriptingInterface; } + QScriptEngine* getScriptEngine() { return &_engine; } + ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; } + /// sets the script contents, will return false if failed, will fail if script is already running bool setScriptContents(const QString& scriptContents, const QString& fileNameString = QString("")); @@ -147,6 +151,8 @@ private: Vec3 _vec3Library; ScriptUUID _uuidLibrary; AnimationCache _animationCache; + + ArrayBufferClass* _arrayBufferClass; QHash _outgoingScriptAudioSequenceNumbers; }; diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp new file mode 100644 index 0000000000..dc208635b8 --- /dev/null +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -0,0 +1,233 @@ +// +// TypedArrays.cpp +// +// +// 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 +// + +#include "ScriptEngine.h" + +#include "TypedArrays.h" + +Q_DECLARE_METATYPE(QByteArray*) + +TypedArray::TypedArray(ScriptEngine* 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 TypedArray::construct(QScriptContext *context, QScriptEngine *engine) { + TypedArray* cls = qscriptvalue_cast(context->callee().data()); + if (!cls || context->argumentCount() < 1) { + return QScriptValue(); + } + + QScriptValue newObject; + QScriptValue bufferArg = context->argument(0); + QByteArray* arrayBuffer = qscriptvalue_cast(bufferArg.data()); + TypedArray* typedArray = qscriptvalue_cast(bufferArg); + + if (arrayBuffer) { + if (context->argumentCount() == 1) { + // Case for entire ArrayBuffer + newObject = cls->newInstance(bufferArg, 0, arrayBuffer->size()); + } else { + QScriptValue byteOffsetArg = context->argument(1); + if (!byteOffsetArg.isNumber()) { + engine->evaluate("throw \"ArgumentError: 2nd arg is not a number\""); + return QScriptValue(); + } + if (byteOffsetArg.toInt32() < 0 || byteOffsetArg.toInt32() >= arrayBuffer->size()) { + engine->evaluate("throw \"RangeError: byteOffset out of range\""); + return QScriptValue(); + } + quint32 byteOffset = byteOffsetArg.toInt32(); + + if (context->argumentCount() == 2) { + // case for end of ArrayBuffer + newObject = cls->newInstance(bufferArg, byteOffset, arrayBuffer->size() - byteOffset); + } else { + + QScriptValue lengthArg = (context->argumentCount() > 2) ? context->argument(2) : QScriptValue(); + if (!lengthArg.isNumber()) { + engine->evaluate("throw \"ArgumentError: 3nd arg is not a number\""); + return QScriptValue(); + } + if (lengthArg.toInt32() < 0 || + byteOffsetArg.toInt32() + lengthArg.toInt32() * cls->_bytesPerElement > arrayBuffer->size()) { + engine->evaluate("throw \"RangeError: byteLength out of range\""); + return QScriptValue(); + } + quint32 length = lengthArg.toInt32() * cls->_bytesPerElement; + + // case for well-defined range + newObject = cls->newInstance(bufferArg, byteOffset, length); + } + } + } else if (typedArray) { + // case for another reference to another TypedArray + newObject = cls->newInstance(bufferArg, true); + } else if (context->argument(0).isNumber()) { + // case for new ArrayBuffer + newObject = cls->newInstance(context->argument(0).toInt32()); + } else { + // TODO: Standard array case + } + + if (context->isCalledAsConstructor()) { + context->setThisObject(newObject); + return engine->undefinedValue(); + } + + return newObject; +} + +QScriptClass::QueryFlags TypedArray::queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id) { + if (name == _bytesPerElementName || name == _lengthName) { + return flags &= HandlesReadAccess; // Only keep read access flags + } + return ArrayBufferViewClass::queryProperty(object, name, flags, id); +} + +QScriptValue TypedArray::property(const QScriptValue &object, + const QScriptString &name, uint id) { + if (name == _bytesPerElementName) { + return QScriptValue(_bytesPerElement); + } + if (name == _lengthName) { + return object.data().property(_lengthName); + } + return ArrayBufferViewClass::property(object, name, id); +} + +QScriptValue::PropertyFlags TypedArray::propertyFlags(const QScriptValue& object, + const QScriptString& name, uint id) { + return QScriptValue::Undeletable; +} + +QString TypedArray::name() const { + return _name.toString(); +} + +QScriptValue TypedArray::prototype() const { + return _proto; +} + +Int8ArrayClass::Int8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_8_ARRAY_CLASS_NAME) { + QScriptValue global = engine()->globalObject(); + + _bytesPerElement = sizeof(qint8); + + // build prototype +// _proto = engine->newQObject(new DataViewPrototype(this), +// QScriptEngine::QtOwnership, +// QScriptEngine::SkipMethodsInEnumeration | +// QScriptEngine::ExcludeSuperClassMethods | +// QScriptEngine::ExcludeSuperClassProperties); + + _proto.setPrototype(global.property("Object").property("prototype")); + + _ctor = engine()->newFunction(construct, _proto); + _ctor.setData(engine()->toScriptValue(this)); + + engine()->globalObject().setProperty(name(), _ctor); +} + +QScriptValue Int8ArrayClass::newInstance(quint32 length) { + ArrayBufferClass* array = getScriptEngine()->getArrayBufferClass(); + QScriptValue buffer = array->newInstance(length * _bytesPerElement); + return newInstance(buffer, 0, length); +} + +QScriptValue Int8ArrayClass::newInstance(QScriptValue array, bool isTypedArray) { + // TODO + assert(false); + if (isTypedArray) { + QByteArray* originalArray = qscriptvalue_cast(array.data().property(_bufferName).data()); + ArrayBufferClass* arrayBufferClass = reinterpret_cast(engine())->getArrayBufferClass(); + quint32 byteOffset = array.data().property(_byteOffsetName).toInt32(); + quint32 byteLength = array.data().property(_byteLengthName).toInt32(); + quint32 length = byteLength / _bytesPerElement; + QByteArray newArray = originalArray->mid(byteOffset, byteLength); + + if (byteLength % _bytesPerElement != 0) { + length += 1; + byteLength = length * _bytesPerElement; + } + + + return newInstance(arrayBufferClass->newInstance(newArray), + array.data().property(_byteOffsetName).toInt32(), + array.data().property(_lengthName).toInt32()); + } + return QScriptValue(); +} + +QScriptValue Int8ArrayClass::newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length) { + QScriptValue data = engine()->newObject(); + data.setProperty(_bufferName, buffer); + data.setProperty(_byteOffsetName, byteOffset); + data.setProperty(_byteLengthName, length * _bytesPerElement); + data.setProperty(_lengthName, length); + + return engine()->newObject(this, data); +} + +QScriptClass::QueryFlags Int8ArrayClass::queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id) { + quint32 byteOffset = object.data().property(_byteOffsetName).toInt32(); + quint32 length = object.data().property(_lengthName).toInt32(); + bool ok = false; + int pos = name.toArrayIndex(&ok); + + // Check that name is a valid index and arrayBuffer exists + if (ok && pos >= 0 && pos < length) { + *id = byteOffset + pos * _bytesPerElement; // save pos to avoid recomputation + return HandlesReadAccess | HandlesWriteAccess; // Read/Write access + } + + return TypedArray::queryProperty(object, name, flags, id); +} + +QScriptValue Int8ArrayClass::property(const QScriptValue &object, + const QScriptString &name, uint id) { + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + bool ok = false; + name.toArrayIndex(&ok); + + if (ok && arrayBuffer) { + QDataStream stream(*arrayBuffer); + stream.skipRawData(id); + + qint8 result; + stream >> result; + return result; + } + + return TypedArray::property(object, name, id); +} + +void Int8ArrayClass::setProperty(QScriptValue &object, + const QScriptString &name, + uint id, const QScriptValue &value) { + qDebug() << "ID: " << id << ", Value: " << value.toInt32(); + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + if (ba && value.isNumber()) { + QDataStream stream(ba, QIODevice::ReadWrite); + stream.skipRawData(id); + + qDebug() << "Adding: " << (qint8)value.toInt32(); + stream << (qint8)value.toInt32(); + } +} + + + diff --git a/libraries/script-engine/src/TypedArrays.h b/libraries/script-engine/src/TypedArrays.h new file mode 100644 index 0000000000..ea43ca2494 --- /dev/null +++ b/libraries/script-engine/src/TypedArrays.h @@ -0,0 +1,104 @@ +// +// 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 +// + +#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"; + +class TypedArray : public ArrayBufferViewClass { + Q_OBJECT +public: + TypedArray(ScriptEngine* scriptEngine, QString name); + virtual QScriptValue newInstance(quint32 length) = 0; + virtual QScriptValue newInstance(QScriptValue array, bool isTypedArray) = 0; + virtual QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length) = 0; + + virtual QueryFlags queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id); + virtual QScriptValue property(const QScriptValue &object, + const QScriptString &name, uint id); + virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, + const QScriptString& name, uint id); + + QString name() const; + QScriptValue prototype() const; + +protected: + static QScriptValue construct(QScriptContext* context, QScriptEngine* engine); + + QScriptValue _proto; + QScriptValue _ctor; + + QScriptString _name; + QScriptString _bytesPerElementName; + QScriptString _lengthName; + + quint32 _bytesPerElement; +}; + +class Int8ArrayClass : public TypedArray { + Q_OBJECT +public: + Int8ArrayClass(ScriptEngine* scriptEngine); + QScriptValue newInstance(quint32 length); + QScriptValue newInstance(QScriptValue array, bool isTypedArray); + QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length); + + QueryFlags queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id); + QScriptValue property(const QScriptValue &object, + const QScriptString &name, uint id); + void setProperty(QScriptValue &object, + const QScriptString &name, + uint id, const QScriptValue &value); +}; + +class Uint8ArrayClass : public TypedArray { + +}; + +class Uint8ClampedArrayClass : public TypedArray { + +}; + +class Int16ArrayClass : public TypedArray { + +}; + +class Uint16ArrayClass : public TypedArray { + +}; + +class Int32ArrayClass : public TypedArray { + +}; + +class Uint32ArrayClass : public TypedArray { + +}; + +class Float32ArrayClass : public TypedArray { + +}; + +class Float64ArrayClass : public TypedArray { + +}; + +#endif // hifi_TypedArrays_h \ No newline at end of file From b4eb6a2eb7a618120a83d09fb9aa73b5e7eb5a36 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 10 Jul 2014 10:09:49 -0700 Subject: [PATCH 13/24] Some refactoring --- libraries/script-engine/src/TypedArrays.cpp | 63 +++++++++------------ libraries/script-engine/src/TypedArrays.h | 3 - 2 files changed, 28 insertions(+), 38 deletions(-) diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp index dc208635b8..121870443f 100644 --- a/libraries/script-engine/src/TypedArrays.cpp +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -19,6 +19,22 @@ TypedArray::TypedArray(ScriptEngine* scriptEngine, QString name) : ArrayBufferVi _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(); + + // build prototype TODO + // _proto = engine->newQObject(new DataViewPrototype(this), + // QScriptEngine::QtOwnership, + // QScriptEngine::SkipMethodsInEnumeration | + // QScriptEngine::ExcludeSuperClassMethods | + // QScriptEngine::ExcludeSuperClassProperties); + + _proto.setPrototype(global.property("Object").property("prototype")); + + _ctor = engine()->newFunction(construct, _proto); + _ctor.setData(engine()->toScriptValue(this)); + + engine()->globalObject().setProperty(_name, _ctor); } QScriptValue TypedArray::construct(QScriptContext *context, QScriptEngine *engine) { @@ -93,6 +109,18 @@ QScriptClass::QueryFlags TypedArray::queryProperty(const QScriptValue& object, if (name == _bytesPerElementName || name == _lengthName) { return flags &= HandlesReadAccess; // Only keep read access flags } + + quint32 byteOffset = object.data().property(_byteOffsetName).toInt32(); + quint32 length = object.data().property(_lengthName).toInt32(); + bool ok = false; + int pos = name.toArrayIndex(&ok); + + // Check that name is a valid index and arrayBuffer exists + if (ok && pos >= 0 && pos < length) { + *id = byteOffset + pos * _bytesPerElement; // save pos to avoid recomputation + return HandlesReadAccess | HandlesWriteAccess; // Read/Write access + } + return ArrayBufferViewClass::queryProperty(object, name, flags, id); } @@ -121,23 +149,7 @@ QScriptValue TypedArray::prototype() const { } Int8ArrayClass::Int8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_8_ARRAY_CLASS_NAME) { - QScriptValue global = engine()->globalObject(); - _bytesPerElement = sizeof(qint8); - - // build prototype -// _proto = engine->newQObject(new DataViewPrototype(this), -// QScriptEngine::QtOwnership, -// QScriptEngine::SkipMethodsInEnumeration | -// QScriptEngine::ExcludeSuperClassMethods | -// QScriptEngine::ExcludeSuperClassProperties); - - _proto.setPrototype(global.property("Object").property("prototype")); - - _ctor = engine()->newFunction(construct, _proto); - _ctor.setData(engine()->toScriptValue(this)); - - engine()->globalObject().setProperty(name(), _ctor); } QScriptValue Int8ArrayClass::newInstance(quint32 length) { @@ -180,23 +192,6 @@ QScriptValue Int8ArrayClass::newInstance(QScriptValue buffer, quint32 byteOffset return engine()->newObject(this, data); } -QScriptClass::QueryFlags Int8ArrayClass::queryProperty(const QScriptValue& object, - const QScriptString& name, - QueryFlags flags, uint* id) { - quint32 byteOffset = object.data().property(_byteOffsetName).toInt32(); - quint32 length = object.data().property(_lengthName).toInt32(); - bool ok = false; - int pos = name.toArrayIndex(&ok); - - // Check that name is a valid index and arrayBuffer exists - if (ok && pos >= 0 && pos < length) { - *id = byteOffset + pos * _bytesPerElement; // save pos to avoid recomputation - return HandlesReadAccess | HandlesWriteAccess; // Read/Write access - } - - return TypedArray::queryProperty(object, name, flags, id); -} - QScriptValue Int8ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); @@ -218,13 +213,11 @@ QScriptValue Int8ArrayClass::property(const QScriptValue &object, void Int8ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value) { - qDebug() << "ID: " << id << ", Value: " << value.toInt32(); QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); if (ba && value.isNumber()) { QDataStream stream(ba, QIODevice::ReadWrite); stream.skipRawData(id); - qDebug() << "Adding: " << (qint8)value.toInt32(); stream << (qint8)value.toInt32(); } } diff --git a/libraries/script-engine/src/TypedArrays.h b/libraries/script-engine/src/TypedArrays.h index ea43ca2494..2c63d83f93 100644 --- a/libraries/script-engine/src/TypedArrays.h +++ b/libraries/script-engine/src/TypedArrays.h @@ -59,9 +59,6 @@ public: QScriptValue newInstance(QScriptValue array, bool isTypedArray); QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length); - QueryFlags queryProperty(const QScriptValue& object, - const QScriptString& name, - QueryFlags flags, uint* id); QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); void setProperty(QScriptValue &object, From 0e5af5afeab679aad91a891fd03dc9146d089839 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 10 Jul 2014 18:32:55 -0700 Subject: [PATCH 14/24] More work on typed arrays --- libraries/script-engine/src/TypedArrays.cpp | 341 ++++++++++++++++---- libraries/script-engine/src/TypedArrays.h | 89 ++++- 2 files changed, 351 insertions(+), 79 deletions(-) diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp index 121870443f..d6ffc8f5a6 100644 --- a/libraries/script-engine/src/TypedArrays.cpp +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include "ScriptEngine.h" #include "TypedArrays.h" @@ -22,12 +24,12 @@ TypedArray::TypedArray(ScriptEngine* scriptEngine, QString name) : ArrayBufferVi QScriptValue global = engine()->globalObject(); - // build prototype TODO - // _proto = engine->newQObject(new DataViewPrototype(this), - // QScriptEngine::QtOwnership, - // QScriptEngine::SkipMethodsInEnumeration | - // QScriptEngine::ExcludeSuperClassMethods | - // QScriptEngine::ExcludeSuperClassProperties); + // build prototype + _proto = engine()->newQObject(new TypedArrayPrototype(this), + QScriptEngine::QtOwnership, + QScriptEngine::SkipMethodsInEnumeration | + QScriptEngine::ExcludeSuperClassMethods | + QScriptEngine::ExcludeSuperClassProperties); _proto.setPrototype(global.property("Object").property("prototype")); @@ -37,16 +39,49 @@ TypedArray::TypedArray(ScriptEngine* scriptEngine, QString name) : ArrayBufferVi engine()->globalObject().setProperty(_name, _ctor); } +QScriptValue TypedArray::newInstance(quint32 length) { + ArrayBufferClass* array = getScriptEngine()->getArrayBufferClass(); + QScriptValue buffer = array->newInstance(length * _bytesPerElement); + return newInstance(buffer, 0, length); +} + +QScriptValue TypedArray::newInstance(QScriptValue array) { + if (array.property("length").isValid()) { + quint32 length = array.property("length").toInt32(); + QScriptValue newArray = newInstance(length); + for (int i = 0; i < length; ++i) { + QScriptValue value = array.property(QString::number(i)); + setProperty(newArray, engine()->toStringHandle(QString::number(i)), + i * _bytesPerElement, (value.isNumber()) ? value : QScriptValue(0)); + } + return newArray; + } + return QScriptValue(); +} + +QScriptValue TypedArray::newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length) { + QScriptValue data = engine()->newObject(); + data.setProperty(_bufferName, buffer); + data.setProperty(_byteOffsetName, byteOffset); + data.setProperty(_byteLengthName, length * _bytesPerElement); + data.setProperty(_lengthName, length); + + return engine()->newObject(this, data); +} + QScriptValue TypedArray::construct(QScriptContext *context, QScriptEngine *engine) { TypedArray* cls = qscriptvalue_cast(context->callee().data()); - if (!cls || context->argumentCount() < 1) { + if (!cls) { return QScriptValue(); } + if (context->argumentCount() == 0) { + return cls->newInstance(0); + } + QScriptValue newObject; QScriptValue bufferArg = context->argument(0); QByteArray* arrayBuffer = qscriptvalue_cast(bufferArg.data()); - TypedArray* typedArray = qscriptvalue_cast(bufferArg); if (arrayBuffer) { if (context->argumentCount() == 1) { @@ -58,15 +93,22 @@ QScriptValue TypedArray::construct(QScriptContext *context, QScriptEngine *engin engine->evaluate("throw \"ArgumentError: 2nd arg is not a number\""); return QScriptValue(); } - if (byteOffsetArg.toInt32() < 0 || byteOffsetArg.toInt32() >= arrayBuffer->size()) { + if (byteOffsetArg.toInt32() < 0 || byteOffsetArg.toInt32() > arrayBuffer->size()) { engine->evaluate("throw \"RangeError: byteOffset out of range\""); return QScriptValue(); } + if (byteOffsetArg.toInt32() % cls->_bytesPerElement != 0) { + engine->evaluate("throw \"RangeError: byteOffset not a multiple of BYTES_PER_ELEMENT\""); + } quint32 byteOffset = byteOffsetArg.toInt32(); if (context->argumentCount() == 2) { // case for end of ArrayBuffer - newObject = cls->newInstance(bufferArg, byteOffset, arrayBuffer->size() - byteOffset); + if ((arrayBuffer->size() - byteOffset) % cls->_bytesPerElement != 0) { + engine->evaluate("throw \"RangeError: byteLength - byteOffset not a multiple of BYTES_PER_ELEMENT\""); + } + quint32 length = (arrayBuffer->size() - byteOffset) / cls->_bytesPerElement; + newObject = cls->newInstance(bufferArg, byteOffset, length); } else { QScriptValue lengthArg = (context->argumentCount() > 2) ? context->argument(2) : QScriptValue(); @@ -79,20 +121,17 @@ QScriptValue TypedArray::construct(QScriptContext *context, QScriptEngine *engin engine->evaluate("throw \"RangeError: byteLength out of range\""); return QScriptValue(); } - quint32 length = lengthArg.toInt32() * cls->_bytesPerElement; + quint32 length = lengthArg.toInt32(); // case for well-defined range newObject = cls->newInstance(bufferArg, byteOffset, length); } } - } else if (typedArray) { - // case for another reference to another TypedArray - newObject = cls->newInstance(bufferArg, true); } else if (context->argument(0).isNumber()) { // case for new ArrayBuffer newObject = cls->newInstance(context->argument(0).toInt32()); } else { - // TODO: Standard array case + newObject = cls->newInstance(bufferArg); } if (context->isCalledAsConstructor()) { @@ -148,79 +187,257 @@ QScriptValue TypedArray::prototype() const { return _proto; } -Int8ArrayClass::Int8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_8_ARRAY_CLASS_NAME) { - _bytesPerElement = sizeof(qint8); +void TypedArray::setBytesPerElement(quint32 bytesPerElement) { + _bytesPerElement = bytesPerElement; + _ctor.setProperty(_bytesPerElementName, _bytesPerElement); } -QScriptValue Int8ArrayClass::newInstance(quint32 length) { - ArrayBufferClass* array = getScriptEngine()->getArrayBufferClass(); - QScriptValue buffer = array->newInstance(length * _bytesPerElement); - return newInstance(buffer, 0, length); +TypedArrayPrototype::TypedArrayPrototype(QObject* parent) : QObject(parent) { } -QScriptValue Int8ArrayClass::newInstance(QScriptValue array, bool isTypedArray) { - // TODO - assert(false); - if (isTypedArray) { - QByteArray* originalArray = qscriptvalue_cast(array.data().property(_bufferName).data()); - ArrayBufferClass* arrayBufferClass = reinterpret_cast(engine())->getArrayBufferClass(); - quint32 byteOffset = array.data().property(_byteOffsetName).toInt32(); - quint32 byteLength = array.data().property(_byteLengthName).toInt32(); - quint32 length = byteLength / _bytesPerElement; - QByteArray newArray = originalArray->mid(byteOffset, byteLength); - - if (byteLength % _bytesPerElement != 0) { - length += 1; - byteLength = length * _bytesPerElement; +QByteArray* TypedArrayPrototype::thisArrayBuffer() const { + QScriptValue bufferObject = thisObject().data().property(BUFFER_PROPERTY_NAME); + return qscriptvalue_cast(bufferObject.data()); +} + +void TypedArrayPrototype::set(QScriptValue array, quint32 offset) { + TypedArray* typedArray = static_cast(parent()); + if (array.isArray()) { + quint32 length = array.property("length").toInt32(); + if (offset + length > thisObject().data().property(typedArray->_lengthName).toInt32()) { + // TODO throw an error maybe? + return; } - - - return newInstance(arrayBufferClass->newInstance(newArray), - array.data().property(_byteOffsetName).toInt32(), - array.data().property(_lengthName).toInt32()); + for (int i = 0; i < length; ++i) { + thisObject().setProperty(QString::number(offset + i), array.property(QString::number(i))); + } + } + // TODO handle typed arrays +} + +QScriptValue TypedArrayPrototype::subarray(qint32 begin) { + TypedArray* typedArray = static_cast(parent()); + QScriptValue 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; + + // if indices < 0 then they start from the end of the array + begin = (begin < 0) ? length + begin : begin; + + // here we clamp the indices to fit the array + begin = glm::clamp(begin, 0, (length - 1)); + + byteOffset += begin * bytesPerElement; + return typedArray->newInstance(arrayBuffer, byteOffset, length - begin); +} + +QScriptValue TypedArrayPrototype::subarray(qint32 begin, qint32 end) { + TypedArray* typedArray = static_cast(parent()); + QScriptValue 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; + + // if indices < 0 then they start from the end of the array + begin = (begin < 0) ? length + begin : begin; + end = (end < 0) ? length + end : end; + + // here we clamp the indices to fit the array + begin = glm::clamp(begin, 0, (length - 1)); + end = glm::clamp(end, 0, (length - 1)); + + byteOffset += begin * bytesPerElement; + length = (end - begin > 0) ? end - begin : 0; + return typedArray->newInstance(arrayBuffer, byteOffset, length); +} + +QScriptValue TypedArrayPrototype::get(quint32 index) { + TypedArray* typedArray = static_cast(parent()); + QScriptString name = engine()->toStringHandle(QString::number(index)); + uint id; + QScriptClass::QueryFlags flags = typedArray->queryProperty(thisObject(), + name, + QScriptClass::HandlesReadAccess, &id); + if (QScriptClass::HandlesReadAccess & flags) { + return typedArray->property(thisObject(), name, id); } return QScriptValue(); } -QScriptValue Int8ArrayClass::newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length) { - QScriptValue data = engine()->newObject(); - data.setProperty(_bufferName, buffer); - data.setProperty(_byteOffsetName, byteOffset); - data.setProperty(_byteLengthName, length * _bytesPerElement); - data.setProperty(_lengthName, length); - - return engine()->newObject(this, data); +void TypedArrayPrototype::set(quint32 index, QScriptValue& value) { + TypedArray* typedArray = static_cast(parent()); + QScriptValue object = thisObject(); + QScriptString name = engine()->toStringHandle(QString::number(index)); + uint id; + QScriptClass::QueryFlags flags = typedArray->queryProperty(object, + name, + QScriptClass::HandlesWriteAccess, &id); + if (QScriptClass::HandlesWriteAccess & flags) { + typedArray->setProperty(object, name, id, value); + } } -QScriptValue Int8ArrayClass::property(const QScriptValue &object, - const QScriptString &name, uint id) { - QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); +template +QScriptValue propertyHelper(const QByteArray* arrayBuffer, const QScriptString &name, uint id) { bool ok = false; name.toArrayIndex(&ok); - + if (ok && arrayBuffer) { QDataStream stream(*arrayBuffer); stream.skipRawData(id); - qint8 result; + T result; stream >> result; return result; } - - return TypedArray::property(object, name, id); + return QScriptValue(); } -void Int8ArrayClass::setProperty(QScriptValue &object, - const QScriptString &name, - uint id, const QScriptValue &value) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); - if (ba && value.isNumber()) { - QDataStream stream(ba, QIODevice::ReadWrite); +template +void setPropertyHelper(QByteArray* arrayBuffer, const QScriptString &name, uint id, const QScriptValue &value) { + if (arrayBuffer && value.isNumber()) { + QDataStream stream(arrayBuffer, QIODevice::ReadWrite); stream.skipRawData(id); - stream << (qint8)value.toInt32(); + stream << (T)value.toNumber(); } } +Int8ArrayClass::Int8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_8_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(qint8)); +} + +QScriptValue Int8ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(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) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + +Uint8ArrayClass::Uint8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, UINT_8_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(quint8)); +} + +QScriptValue Uint8ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(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) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + +Int16ArrayClass::Int16ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_16_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(qint16)); +} + +QScriptValue Int16ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(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) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + +Uint16ArrayClass::Uint16ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, UINT_16_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(quint16)); +} + +QScriptValue Uint16ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(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) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + +Int32ArrayClass::Int32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_32_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(qint32)); +} + +QScriptValue Int32ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(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) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + +Uint32ArrayClass::Uint32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, UINT_32_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(quint32)); +} + +QScriptValue Uint32ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(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) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + +Float32ArrayClass::Float32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, FLOAT_32_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(float)); +} + +QScriptValue Float32ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(arrayBuffer, name, id); + return (result.isValid()) ? result : TypedArray::property(object, name, id); +} + +void Float32ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + +Float64ArrayClass::Float64ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, FLOAT_64_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(double)); +} + +QScriptValue Float64ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(arrayBuffer, name, id); + return (result.isValid()) ? result : TypedArray::property(object, name, id); +} + +void Float64ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + diff --git a/libraries/script-engine/src/TypedArrays.h b/libraries/script-engine/src/TypedArrays.h index 2c63d83f93..37d341b2ad 100644 --- a/libraries/script-engine/src/TypedArrays.h +++ b/libraries/script-engine/src/TypedArrays.h @@ -18,20 +18,28 @@ 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 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"; class TypedArray : public ArrayBufferViewClass { Q_OBJECT public: TypedArray(ScriptEngine* scriptEngine, QString name); - virtual QScriptValue newInstance(quint32 length) = 0; - virtual QScriptValue newInstance(QScriptValue array, bool isTypedArray) = 0; - virtual QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length) = 0; + 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); - virtual QScriptValue property(const QScriptValue &object, - const QScriptString &name, uint id); + virtual QScriptValue property(const QScriptValue& object, + const QScriptString& name, uint id); + virtual void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) = 0; virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, const QScriptString& name, uint id); @@ -41,6 +49,8 @@ public: protected: static QScriptValue construct(QScriptContext* context, QScriptEngine* engine); + void setBytesPerElement(quint32 bytesPerElement); + QScriptValue _proto; QScriptValue _ctor; @@ -49,53 +59,98 @@ protected: QScriptString _lengthName; quint32 _bytesPerElement; + + friend class TypedArrayPrototype; +}; + +class TypedArrayPrototype : public QObject, public QScriptable { + Q_OBJECT +public: + TypedArrayPrototype(QObject* parent = NULL); + +public slots: + void set(QScriptValue array, quint32 offset = 0); + QScriptValue subarray(qint32 begin); + QScriptValue subarray(qint32 begin, qint32 end); + + QScriptValue get(quint32 index); + void set(quint32 index, QScriptValue& value); +private: + QByteArray* thisArrayBuffer() const; }; class Int8ArrayClass : public TypedArray { Q_OBJECT public: Int8ArrayClass(ScriptEngine* scriptEngine); - QScriptValue newInstance(quint32 length); - QScriptValue newInstance(QScriptValue array, bool isTypedArray); - QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length); - - QScriptValue property(const QScriptValue &object, - const QScriptString &name, uint id); - void setProperty(QScriptValue &object, - const QScriptString &name, - uint id, const QScriptValue &value); + + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); }; class Uint8ArrayClass : public TypedArray { + Q_OBJECT +public: + Uint8ArrayClass(ScriptEngine* scriptEngine); + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); }; class Uint8ClampedArrayClass : public TypedArray { - + // TODO }; class Int16ArrayClass : public TypedArray { + Q_OBJECT +public: + Int16ArrayClass(ScriptEngine* scriptEngine); + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); }; class Uint16ArrayClass : public TypedArray { + Q_OBJECT +public: + Uint16ArrayClass(ScriptEngine* scriptEngine); -}; + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value);}; class Int32ArrayClass : public TypedArray { + Q_OBJECT +public: + Int32ArrayClass(ScriptEngine* scriptEngine); + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); }; class Uint32ArrayClass : public TypedArray { + Q_OBJECT +public: + Uint32ArrayClass(ScriptEngine* scriptEngine); + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); }; class Float32ArrayClass : public TypedArray { + Q_OBJECT +public: + Float32ArrayClass(ScriptEngine* scriptEngine); -}; + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value);}; class Float64ArrayClass : public TypedArray { + Q_OBJECT +public: + Float64ArrayClass(ScriptEngine* scriptEngine); + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); }; #endif // hifi_TypedArrays_h \ No newline at end of file From ff378b60acf9eddebbd680788c1955c82190b93e Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 10 Jul 2014 18:33:24 -0700 Subject: [PATCH 15/24] Added functionnalities to JS UnitTests --- examples/Test.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/examples/Test.js b/examples/Test.js index 056ec3cbbf..646ae5c71d 100644 --- a/examples/Test.js +++ b/examples/Test.js @@ -65,4 +65,16 @@ UnitTest.prototype.assertNull = function(value, message) { if (value !== null) { throw new AssertionException(value, null, message); } -}; +} + +UnitTest.prototype.arrayEqual = function(array1, array2, message) { + this.numAssertions++; + if (array1.length !== array2.length) { + throw new AssertionException(array1.length , array2.length , message); + } + for (var i = 0; i < array1.length; ++i) { + if (array1[i] !== array2[i]) { + throw new AssertionException(array1[i], array2[i], message); + } + } +} \ No newline at end of file From 3070ac457f12e141a8df6997bcd2580cdf177ead Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 10 Jul 2014 18:33:57 -0700 Subject: [PATCH 16/24] Typed arrays bug fixing --- libraries/script-engine/src/DataViewClass.cpp | 9 ++---- libraries/script-engine/src/ScriptEngine.cpp | 28 ++++++++++++------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/libraries/script-engine/src/DataViewClass.cpp b/libraries/script-engine/src/DataViewClass.cpp index 4a9420e4e9..938770ecf5 100644 --- a/libraries/script-engine/src/DataViewClass.cpp +++ b/libraries/script-engine/src/DataViewClass.cpp @@ -74,12 +74,9 @@ QScriptValue DataViewClass::construct(QScriptContext *context, QScriptEngine *en engine->evaluate("throw \"RangeError: byteLength out of range\""); return QScriptValue(); } - - QScriptValue newObject = cls->newInstance(bufferArg, - (byteOffsetArg.isNumber()) ? byteOffsetArg.toInt32() - : 0, - (byteLengthArg.isNumber()) ? byteLengthArg.toInt32() - : arrayBuffer->size()); + quint32 byteOffset = (byteOffsetArg.isNumber()) ? byteOffsetArg.toInt32() : 0; + quint32 byteLength = (byteLengthArg.isNumber()) ? byteLengthArg.toInt32() : arrayBuffer->size() - byteOffset; + QScriptValue newObject = cls->newInstance(bufferArg, byteOffset, byteLength); if (context->isCalledAsConstructor()) { context->setThisObject(newObject); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 73983161ff..4074beb88e 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -97,11 +97,15 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam _arrayBufferClass(NULL) { _arrayBufferClass = new ArrayBufferClass(this); - qDebug() << "Engine: " << this; - qDebug() << "ArrayBuffer: " <<_arrayBufferClass; - qDebug() << "DataView: " << new DataViewClass(this); - qDebug() << "Int8Array: " << new Int8ArrayClass(this); - qDebug() << "Initial thread: " << QThread::currentThread() << " " << thread(); + new DataViewClass(this); + new Int8ArrayClass(this); + new Uint8ArrayClass(this); + new Int16ArrayClass(this); + new Uint16ArrayClass(this); + new Int32ArrayClass(this); + new Uint32ArrayClass(this); + new Float32ArrayClass(this); + new Float64ArrayClass(this); } ScriptEngine::ScriptEngine(const QUrl& scriptURL, @@ -134,11 +138,15 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, QUrl url(scriptURL); _arrayBufferClass = new ArrayBufferClass(this); - qDebug() << "Engine: " << this; - qDebug() << "ArrayBuffer: " <<_arrayBufferClass; - qDebug() << "DataView: " << new DataViewClass(this); - qDebug() << "Int8Array: " << new Int8ArrayClass(this); - qDebug() << "Initial thread: " << QThread::currentThread() << " " << thread(); + new DataViewClass(this); + new Int8ArrayClass(this); + new Uint8ArrayClass(this); + new Int16ArrayClass(this); + new Uint16ArrayClass(this); + new Int32ArrayClass(this); + new Uint32ArrayClass(this); + new Float32ArrayClass(this); + new Float64ArrayClass(this); // if the scheme length is one or lower, maybe they typed in a file, let's try const int WINDOWS_DRIVE_LETTER_SIZE = 1; From 02c951fba5ed9a4c22e1fadc5102454e1f9b45a2 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 10 Jul 2014 18:34:17 -0700 Subject: [PATCH 17/24] Added bunch of Typed arrays unit tests --- examples/typedArraysUnitTest.js | 343 ++++++++++++++++++++++++++++++++ 1 file changed, 343 insertions(+) diff --git a/examples/typedArraysUnitTest.js b/examples/typedArraysUnitTest.js index 6bd35365e8..de9b487c32 100644 --- a/examples/typedArraysUnitTest.js +++ b/examples/typedArraysUnitTest.js @@ -19,6 +19,9 @@ test('ArrayBuffer', function(finished) { this.assertEquals(typeof(new ArrayBuffer(123)), 'object', 'creation'); this.assertEquals(new ArrayBuffer(123).byteLength, 123, 'length'); + + // raises(function () { return new ArrayBuffer(-1); }, RangeError, 'negative length'); + // raises(function () { return new ArrayBuffer(0x80000000); }, RangeError, 'absurd length'); }); test('DataView constructors', function (finished) { @@ -39,3 +42,343 @@ test('DataView constructors', function (finished) { // raises(function () { return new DataView({}); }, 'non-ArrayBuffer argument'); // raises(function () { return new DataView("bogus"); }, TypeError, 'non-ArrayBuffer argument'); }); + + +test('ArrayBufferView', function () { + var ab = new ArrayBuffer(48); + var i32 = new Int32Array(ab, 16); + i32.set([1, 2, 3, 4, 5, 6, 7, 8]); + + this.assertEquals(i32.buffer, ab, 'ArrayBuffers equal'); + this.assertEquals(i32.byteOffset, 16, 'byteOffset'); + this.assertEquals(i32.byteLength, 32, 'byteLength'); + + var da = new DataView(i32.buffer, 8); + this.assertEquals(da.buffer, ab, 'DataView: ArrayBuffers equal'); + this.assertEquals(da.byteOffset, 8, 'DataView: byteOffset'); + this.assertEquals(da.byteLength, 40, 'DataView: byteLength'); +}); + +test('TypedArrays', function () { + var a; + + this.assertEquals(Int8Array.BYTES_PER_ELEMENT, 1, 'Int8Array.BYTES_PER_ELEMENT'); + a = new Int8Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 1, 'int8Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'int8Array.byteOffset'); + this.assertEquals(a.byteLength, 8, 'int8Array.byteLength'); + + this.assertEquals(Uint8Array.BYTES_PER_ELEMENT, 1, 'Uint8Array.BYTES_PER_ELEMENT'); + a = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 1, 'uint8Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'uint8Array.byteOffset'); + this.assertEquals(a.byteLength, 8, 'uint8Array.byteLength'); + + this.assertEquals(Int16Array.BYTES_PER_ELEMENT, 2, 'Int16Array.BYTES_PER_ELEMENT'); + a = new Int16Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 2, 'int16Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'int16Array.byteOffset'); + this.assertEquals(a.byteLength, 16, 'int16Array.byteLength'); + + this.assertEquals(Uint16Array.BYTES_PER_ELEMENT, 2, 'Uint16Array.BYTES_PER_ELEMENT'); + a = new Uint16Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 2, 'uint16Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'uint16Array.byteOffset'); + this.assertEquals(a.byteLength, 16, 'uint16Array.byteLength'); + + this.assertEquals(Int32Array.BYTES_PER_ELEMENT, 4, 'Int32Array.BYTES_PER_ELEMENT'); + a = new Int32Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 4, 'int32Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'int32Array.byteOffset'); + this.assertEquals(a.byteLength, 32, 'int32Array.byteLength'); + + this.assertEquals(Uint32Array.BYTES_PER_ELEMENT, 4, 'Uint32Array.BYTES_PER_ELEMENT'); + a = new Uint32Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 4, 'uint32Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'uint32Array.byteOffset'); + this.assertEquals(a.byteLength, 32, 'uint32Array.byteLength'); + + this.assertEquals(Float32Array.BYTES_PER_ELEMENT, 4, 'Float32Array.BYTES_PER_ELEMENT'); + a = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 4, 'float32Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'float32Array.byteOffset'); + this.assertEquals(a.byteLength, 32, 'float32Array.byteLength'); + + this.assertEquals(Float64Array.BYTES_PER_ELEMENT, 8, 'Float64Array.BYTES_PER_ELEMENT'); + a = new Float64Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 8, 'float64Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'float64Array.byteOffset'); + this.assertEquals(a.byteLength, 64, 'float64Array.byteLength'); +}); + + +test('typed array constructors', function () { + this.arrayEqual(new Int8Array({ length: 3 }), [0, 0, 0], 'array equal -1'); + var rawbuf = (new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7])).buffer; + + var int8 = new Int8Array(); + this.assertEquals(int8.length, 0, 'no args 0'); + // raises(function () { return new Int8Array(-1); }, /*Range*/Error, 'bogus length'); + // raises(function () { return new Int8Array(0x80000000); }, /*Range*/Error, 'bogus length'); + + int8 = new Int8Array(4); + this.assertEquals(int8.BYTES_PER_ELEMENT, 1); + this.assertEquals(int8.length, 4, 'length 1'); + this.assertEquals(int8.byteLength, 4, 'length 2'); + this.assertEquals(int8.byteOffset, 0, 'length 3'); + this.assertEquals(int8.get(-1), undefined, 'length, out of bounds 4'); + this.assertEquals(int8.get(4), undefined, 'length, out of bounds 5'); + + int8 = new Int8Array([1, 2, 3, 4, 5, 6]); + this.assertEquals(int8.length, 6, 'array 6'); + this.assertEquals(int8.byteLength, 6, 'array 7'); + this.assertEquals(int8.byteOffset, 0, 'array 8'); + this.assertEquals(int8.get(3), 4, 'array 9'); + this.assertEquals(int8.get(-1), undefined, 'array, out of bounds 10'); + this.assertEquals(int8.get(6), undefined, 'array, out of bounds 11'); + + int8 = new Int8Array(rawbuf); + this.assertEquals(int8.length, 8, 'buffer 12'); + this.assertEquals(int8.byteLength, 8, 'buffer 13'); + this.assertEquals(int8.byteOffset, 0, 'buffer 14'); + this.assertEquals(int8.get(7), 7, 'buffer 15'); + int8.set([111]); + this.assertEquals(int8.get(0), 111, 'buffer 16'); + this.assertEquals(int8.get(-1), undefined, 'buffer, out of bounds 17'); + this.assertEquals(int8.get(8), undefined, 'buffer, out of bounds 18'); + + int8 = new Int8Array(rawbuf, 2); + this.assertEquals(int8.length, 6, 'buffer, byteOffset 19'); + this.assertEquals(int8.byteLength, 6, 'buffer, byteOffset 20'); + this.assertEquals(int8.byteOffset, 2, 'buffer, byteOffset 21'); + this.assertEquals(int8.get(5), 7, 'buffer, byteOffset 22'); + int8.set([112]); + this.assertEquals(int8.get(0), 112, 'buffer 23'); + this.assertEquals(int8.get(-1), undefined, 'buffer, byteOffset, out of bounds 24'); + this.assertEquals(int8.get(6), undefined, 'buffer, byteOffset, out of bounds 25'); + + int8 = new Int8Array(rawbuf, 8); + this.assertEquals(int8.length, 0, 'buffer, byteOffset 26'); + + // raises(function () { return new Int8Array(rawbuf, -1); }, 'invalid byteOffset 27'); + // raises(function () { return new Int8Array(rawbuf, 9); }, 'invalid byteOffset 28'); + // raises(function () { return new Int8Array(rawbuf, -1); }, 'invalid byteOffset 29'); + // raises(function () { return new Int32Array(rawbuf, 5); }, 'invalid byteOffset 30'); + + int8 = new Int8Array(rawbuf, 2, 4); + this.assertEquals(int8.length, 4, 'buffer, byteOffset, length 31'); + this.assertEquals(int8.byteLength, 4, 'buffer, byteOffset, length 32'); + this.assertEquals(int8.byteOffset, 2, 'buffer, byteOffset, length 33'); + this.assertEquals(int8.get(3), 5, 'buffer, byteOffset, length 34'); + int8.set([113]); + this.assertEquals(int8.get(0), 113, 'buffer, byteOffset, length 35'); + this.assertEquals(int8.get(-1), undefined, 'buffer, byteOffset, length, out of bounds 36'); + this.assertEquals(int8.get(4), undefined, 'buffer, byteOffset, length, out of bounds 37'); + + // raises(function () { return new Int8Array(rawbuf, 0, 9); }, 'invalid byteOffset+length'); + // raises(function () { return new Int8Array(rawbuf, 8, 1); }, 'invalid byteOffset+length'); + // raises(function () { return new Int8Array(rawbuf, 9, -1); }, 'invalid byteOffset+length'); +}); + +test('TypedArray clone constructor', function () { + var src = new Int32Array([1, 2, 3, 4, 5, 6, 7, 8]); + var dst = new Int32Array(src); + this.arrayEqual(dst, [1, 2, 3, 4, 5, 6, 7, 8], '1'); + src.set([99]); + this.arrayEqual(src, [99, 2, 3, 4, 5, 6, 7, 8], '2'); + this.arrayEqual(dst, [1, 2, 3, 4, 5, 6, 7, 8], '3'); +}); + + +test('conversions', function () { + var uint8 = new Uint8Array([1, 2, 3, 4]), + uint16 = new Uint16Array(uint8.buffer), + uint32 = new Uint32Array(uint8.buffer); + + // Note: can't probe individual bytes without endianness awareness + this.arrayEqual(uint8, [1, 2, 3, 4]); + uint16.set([0xffff]); + this.arrayEqual(uint8, [0xff, 0xff, 3, 4]); + uint16.set([0xeeee], 1); + this.arrayEqual(uint8, [0xff, 0xff, 0xee, 0xee]); + uint32.set([0x11111111]); + this.assertEquals(uint16.get(0), 0x1111); + this.assertEquals(uint16.get(1), 0x1111); + this.arrayEqual(uint8, [0x11, 0x11, 0x11, 0x11]); +}); + + +test('signed/unsigned conversions', function () { + + var int8 = new Int8Array(1), uint8 = new Uint8Array(int8.buffer); + uint8.set([123]); + this.assertEquals(int8.get(0), 123, 'int8/uint8'); + uint8.set([161]); + this.assertEquals(int8.get(0), -95, 'int8/uint8'); + int8.set([-120]); + this.assertEquals(uint8.get(0), 136, 'uint8/int8'); + int8.set([-1]); + this.assertEquals(uint8.get(0), 0xff, 'uint8/int8'); + + var int16 = new Int16Array(1), uint16 = new Uint16Array(int16.buffer); + uint16.set([3210]); + this.assertEquals(int16.get(0), 3210, 'int16/uint16'); + uint16.set([49232]); + this.assertEquals(int16.get(0), -16304, 'int16/uint16'); + int16.set([-16384]); + this.assertEquals(uint16.get(0), 49152, 'uint16/int16'); + int16.set([-1]); + this.assertEquals(uint16.get(0), 0xffff, 'uint16/int16'); + + var int32 = new Int32Array(1), uint32 = new Uint32Array(int32.buffer); + uint32.set([0x80706050]); + this.assertEquals(int32.get(0), -2140118960, 'int32/uint32'); + int32.set([-2023406815]); + this.assertEquals(uint32.get(0), 0x87654321, 'uint32/int32'); + int32.set([-1]); + this.assertEquals(uint32.get(0), 0xffffffff, 'uint32/int32'); +}); + + +test('Int32Array round trips', function () { + var i32 = new Int32Array([0]); + var data = [ + 0, + 1, + -1, + 123, + -456, + 0x80000000 >> 0, + 0x7fffffff >> 0, + 0x12345678 >> 0, + 0x87654321 >> 0 + ]; + + for (var i = 0; i < data.length; i += 1) { + var datum = data[i]; + i32.set([datum]); + this.assertEquals(datum, i32.get(0), String(datum)); + } +}); + + +test('Int16Array round trips', function () { + var i16 = new Int16Array([0]); + var data = [ + 0, + 1, + -1, + 123, + -456, + 0xffff8000 >> 0, + 0x00007fff >> 0, + 0x00001234 >> 0, + 0xffff8765 >> 0 + ]; + + for (var i = 0; i < data.length; i += 1) { + var datum = data[i]; + i16.set([datum]); + this.assertEquals(datum, i16.get(0), String(datum)); + } +}); + + +test('Int8Array round trips', function () { + var i8 = new Int8Array([0]); + var data = [ + 0, + 1, + -1, + 123, + -45, + 0xffffff80 >> 0, + 0x0000007f >> 0, + 0x00000012 >> 0, + 0xffffff87 >> 0 + ]; + + for (var i = 0; i < data.length; i += 1) { + var datum = data[i]; + i8.set([datum]); + this.assertEquals(datum, i8.get(0), String(datum)); + } +}); + +test('TypedArray setting', function () { + + var a = new Int32Array([1, 2, 3, 4, 5]); + var b = new Int32Array(5); + b.set(a); + this.arrayEqual(b, [1, 2, 3, 4, 5], '1'); + // raises(function () { b.set(a, 1); }); + + b.set(new Int32Array([99, 98]), 2); + this.arrayEqual(b, [1, 2, 99, 98, 5], '2'); + + b.set(new Int32Array([99, 98, 97]), 2); + this.arrayEqual(b, [1, 2, 99, 98, 97], '3'); + + // raises(function () { b.set(new Int32Array([99, 98, 97, 96]), 2); }); + // raises(function () { b.set([101, 102, 103, 104], 4); }); + + // ab = [ 0, 1, 2, 3, 4, 5, 6, 7 ] + // a1 = [ ^, ^, ^, ^, ^, ^, ^, ^ ] + // a2 = [ ^, ^, ^, ^ ] + var ab = new ArrayBuffer(8); + var a1 = new Uint8Array(ab); + for (var i = 0; i < a1.length; i += 1) { a1.set([i], i); } + var a2 = new Uint8Array(ab, 4); + a1.set(a2, 2); + this.arrayEqual(a1, [0, 1, 4, 5, 6, 7, 6, 7]); + this.arrayEqual(a2, [6, 7, 6, 7]); +}); + + +test('TypedArray.subarray', function () { + + var a = new Int32Array([1, 2, 3, 4, 5]); + this.arrayEqual(a.subarray(3), [4, 5]); + this.arrayEqual(a.subarray(1, 3), [2, 3]); + this.arrayEqual(a.subarray(-3), [3, 4, 5]); + this.arrayEqual(a.subarray(-3, -1), [3, 4]); + this.arrayEqual(a.subarray(3, 2), []); + this.arrayEqual(a.subarray(-2, -3), []); + this.arrayEqual(a.subarray(4, 1), []); + this.arrayEqual(a.subarray(-1, -4), []); + this.arrayEqual(a.subarray(1).subarray(1), [3, 4, 5]); + this.arrayEqual(a.subarray(1, 4).subarray(1, 2), [3]); +}); + + +test('DataView constructors', function () { + + var d = new DataView(new ArrayBuffer(8)); + + d.setUint32(0, 0x12345678); + this.assertEquals(d.getUint32(0), 0x12345678, 'big endian/big endian'); + + d.setUint32(0, 0x12345678, true); + this.assertEquals(d.getUint32(0, true), 0x12345678, 'little endian/little endian'); + + d.setUint32(0, 0x12345678, true); + this.assertEquals(d.getUint32(0), 0x78563412, 'little endian/big endian'); + + d.setUint32(0, 0x12345678); + this.assertEquals(d.getUint32(0, true), 0x78563412, 'big endian/little endian'); + + // Chrome allows no arguments, throws if non-ArrayBuffer + //stricterEqual(new DataView().buffer.byteLength, 0, 'no arguments'); + + // Safari (iOS 5) does not + //raises(function () { return new DataView(); }, TypeError, 'no arguments'); + + // Chrome raises TypeError, Safari iOS5 raises isDOMException(INDEX_SIZE_ERR) + // raises(function () { return new DataView({}); }, 'non-ArrayBuffer argument'); + + // raises(function () { return new DataView("bogus"); }, TypeError, 'non-ArrayBuffer argument'); +}); + + + + From b9fe5b2ef1dbf03456ea582d034826224b956573 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 11 Jul 2014 16:23:19 -0700 Subject: [PATCH 18/24] Added JS exceptions + fixed up floating precision mess up --- .../script-engine/src/ArrayBufferClass.cpp | 12 +++- .../script-engine/src/ArrayBufferClass.h | 2 +- libraries/script-engine/src/DataViewClass.cpp | 2 +- .../script-engine/src/DataViewPrototype.cpp | 42 ++++++++----- .../script-engine/src/DataViewPrototype.h | 34 +++++----- libraries/script-engine/src/TypedArrays.cpp | 63 ++++++++++++++----- libraries/script-engine/src/TypedArrays.h | 2 +- 7 files changed, 105 insertions(+), 52 deletions(-) diff --git a/libraries/script-engine/src/ArrayBufferClass.cpp b/libraries/script-engine/src/ArrayBufferClass.cpp index d86e229a1a..4f85eeeae2 100644 --- a/libraries/script-engine/src/ArrayBufferClass.cpp +++ b/libraries/script-engine/src/ArrayBufferClass.cpp @@ -48,7 +48,17 @@ _scriptEngine(scriptEngine) { engine()->globalObject().setProperty(name(), _ctor); } -QScriptValue ArrayBufferClass::newInstance(quint32 size) { +QScriptValue ArrayBufferClass::newInstance(qint32 size) { + const qint32 MAX_LENGTH = 100000000; + if (size < 0) { + engine()->evaluate("throw \"ArgumentError: negative length\""); + return QScriptValue(); + } + if (size > MAX_LENGTH) { + engine()->evaluate("throw \"ArgumentError: absurd length\""); + return QScriptValue(); + } + engine()->reportAdditionalMemoryCost(size); QScriptEngine* eng = engine(); QVariant variant = QVariant::fromValue(QByteArray(size, 0)); diff --git a/libraries/script-engine/src/ArrayBufferClass.h b/libraries/script-engine/src/ArrayBufferClass.h index c56b659eff..c7b4d447dd 100644 --- a/libraries/script-engine/src/ArrayBufferClass.h +++ b/libraries/script-engine/src/ArrayBufferClass.h @@ -26,7 +26,7 @@ class ArrayBufferClass : public QObject, public QScriptClass { Q_OBJECT public: ArrayBufferClass(ScriptEngine* scriptEngine); - QScriptValue newInstance(quint32 size); + QScriptValue newInstance(qint32 size); QScriptValue newInstance(const QByteArray& ba); ScriptEngine* getScriptEngine() { return _scriptEngine; } diff --git a/libraries/script-engine/src/DataViewClass.cpp b/libraries/script-engine/src/DataViewClass.cpp index 938770ecf5..efb45daf3f 100644 --- a/libraries/script-engine/src/DataViewClass.cpp +++ b/libraries/script-engine/src/DataViewClass.cpp @@ -64,7 +64,7 @@ QScriptValue DataViewClass::construct(QScriptContext *context, QScriptEngine *en } if (byteOffsetArg.isNumber() && (byteOffsetArg.toInt32() < 0 || - byteOffsetArg.toInt32() >= arrayBuffer->size())) { + byteOffsetArg.toInt32() > arrayBuffer->size())) { engine->evaluate("throw \"RangeError: byteOffset out of range\""); return QScriptValue(); } diff --git a/libraries/script-engine/src/DataViewPrototype.cpp b/libraries/script-engine/src/DataViewPrototype.cpp index 17734e109c..95f3fdf3f6 100644 --- a/libraries/script-engine/src/DataViewPrototype.cpp +++ b/libraries/script-engine/src/DataViewPrototype.cpp @@ -26,7 +26,10 @@ QByteArray* DataViewPrototype::thisArrayBuffer() const { return qscriptvalue_cast(bufferObject.data()); } -bool DataViewPrototype::realOffset(quint32& offset, size_t size) const { +bool DataViewPrototype::realOffset(qint32& offset, size_t size) const { + if (offset < 0) { + return false; + } quint32 viewOffset = thisObject().data().property(BYTE_OFFSET_PROPERTY_NAME).toInt32(); quint32 viewLength = thisObject().data().property(BYTE_LENGTH_PROPERTY_NAME).toInt32(); //qDebug() << "View Offset: " << viewOffset << ", View Lenght: " << viewLength; @@ -38,7 +41,7 @@ bool DataViewPrototype::realOffset(quint32& offset, size_t size) const { ///////////////// GETTERS //////////////////////////// -qint32 DataViewPrototype::getInt8(quint32 byteOffset) { +qint32 DataViewPrototype::getInt8(qint32 byteOffset) { if (realOffset(byteOffset, sizeof(qint8))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -51,7 +54,7 @@ qint32 DataViewPrototype::getInt8(quint32 byteOffset) { return 0; } -quint32 DataViewPrototype::getUint8(quint32 byteOffset) { +quint32 DataViewPrototype::getUint8(qint32 byteOffset) { if (realOffset(byteOffset, sizeof(quint8))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -64,7 +67,7 @@ quint32 DataViewPrototype::getUint8(quint32 byteOffset) { return 0; } -qint32 DataViewPrototype::getInt16(quint32 byteOffset, bool littleEndian) { +qint32 DataViewPrototype::getInt16(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint16))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -78,7 +81,7 @@ qint32 DataViewPrototype::getInt16(quint32 byteOffset, bool littleEndian) { return 0; } -quint32 DataViewPrototype::getUint16(quint32 byteOffset, bool littleEndian) { +quint32 DataViewPrototype::getUint16(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint16))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -92,7 +95,7 @@ quint32 DataViewPrototype::getUint16(quint32 byteOffset, bool littleEndian) { return 0; } -qint32 DataViewPrototype::getInt32(quint32 byteOffset, bool littleEndian) { +qint32 DataViewPrototype::getInt32(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint32))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -106,7 +109,7 @@ qint32 DataViewPrototype::getInt32(quint32 byteOffset, bool littleEndian) { return 0; } -quint32 DataViewPrototype::getUint32(quint32 byteOffset, bool littleEndian) { +quint32 DataViewPrototype::getUint32(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint32))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -120,25 +123,28 @@ quint32 DataViewPrototype::getUint32(quint32 byteOffset, bool littleEndian) { return 0; } -float DataViewPrototype::getFloat32(quint32 byteOffset, bool littleEndian) { +float DataViewPrototype::getFloat32(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(float))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.setFloatingPointPrecision(QDataStream::SinglePrecision); float result; stream >> result; + qDebug() << "Get: " << result; return result; } thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); return 0; } -double DataViewPrototype::getFloat64(quint32 byteOffset, bool littleEndian) { +double DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(double))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.setFloatingPointPrecision(QDataStream::DoublePrecision); double result; stream >> result; @@ -150,7 +156,7 @@ double DataViewPrototype::getFloat64(quint32 byteOffset, bool littleEndian) { ///////////////// SETTERS //////////////////////////// -void DataViewPrototype::setInt8(quint32 byteOffset, qint32 value) { +void DataViewPrototype::setInt8(qint32 byteOffset, qint32 value) { if (realOffset(byteOffset, sizeof(qint8))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); @@ -161,7 +167,7 @@ void DataViewPrototype::setInt8(quint32 byteOffset, qint32 value) { } } -void DataViewPrototype::setUint8(quint32 byteOffset, quint32 value) { +void DataViewPrototype::setUint8(qint32 byteOffset, quint32 value) { if (realOffset(byteOffset, sizeof(quint8))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); @@ -172,7 +178,7 @@ void DataViewPrototype::setUint8(quint32 byteOffset, quint32 value) { } } -void DataViewPrototype::setInt16(quint32 byteOffset, qint32 value, bool littleEndian) { +void DataViewPrototype::setInt16(qint32 byteOffset, qint32 value, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint16))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); @@ -184,7 +190,7 @@ void DataViewPrototype::setInt16(quint32 byteOffset, qint32 value, bool littleEn } } -void DataViewPrototype::setUint16(quint32 byteOffset, quint32 value, bool littleEndian) { +void DataViewPrototype::setUint16(qint32 byteOffset, quint32 value, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint16))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); @@ -196,7 +202,7 @@ void DataViewPrototype::setUint16(quint32 byteOffset, quint32 value, bool little } } -void DataViewPrototype::setInt32(quint32 byteOffset, qint32 value, bool littleEndian) { +void DataViewPrototype::setInt32(qint32 byteOffset, qint32 value, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint32))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); @@ -208,7 +214,7 @@ void DataViewPrototype::setInt32(quint32 byteOffset, qint32 value, bool littleEn } } -void DataViewPrototype::setUint32(quint32 byteOffset, quint32 value, bool littleEndian) { +void DataViewPrototype::setUint32(qint32 byteOffset, quint32 value, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint32))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); @@ -220,11 +226,12 @@ void DataViewPrototype::setUint32(quint32 byteOffset, quint32 value, bool little } } -void DataViewPrototype::setFloat32(quint32 byteOffset, float value, bool littleEndian) { +void DataViewPrototype::setFloat32(qint32 byteOffset, float value, bool littleEndian) { if (realOffset(byteOffset, sizeof(float))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.setFloatingPointPrecision(QDataStream::SinglePrecision); stream << value; } else { @@ -232,11 +239,12 @@ void DataViewPrototype::setFloat32(quint32 byteOffset, float value, bool littleE } } -void DataViewPrototype::setFloat64(quint32 byteOffset, double value, bool littleEndian) { +void DataViewPrototype::setFloat64(qint32 byteOffset, double value, bool littleEndian) { if (realOffset(byteOffset, sizeof(double))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.setFloatingPointPrecision(QDataStream::DoublePrecision); stream << value; } else { diff --git a/libraries/script-engine/src/DataViewPrototype.h b/libraries/script-engine/src/DataViewPrototype.h index 97c94c58ce..b7061a388e 100644 --- a/libraries/script-engine/src/DataViewPrototype.h +++ b/libraries/script-engine/src/DataViewPrototype.h @@ -31,14 +31,14 @@ public slots: // // These methods raise an exception if they would read // beyond the end of the view. - qint32 getInt8(quint32 byteOffset); - quint32 getUint8(quint32 byteOffset); - qint32 getInt16(quint32 byteOffset, bool littleEndian = false); - quint32 getUint16(quint32 byteOffset, bool littleEndian = false); - qint32 getInt32(quint32 byteOffset, bool littleEndian = false); - quint32 getUint32(quint32 byteOffset, bool littleEndian = false); - float getFloat32(quint32 byteOffset, bool littleEndian = false); - double getFloat64(quint32 byteOffset, bool littleEndian = false); + qint32 getInt8(qint32 byteOffset); + quint32 getUint8(qint32 byteOffset); + qint32 getInt16(qint32 byteOffset, bool littleEndian = false); + quint32 getUint16(qint32 byteOffset, bool littleEndian = false); + qint32 getInt32(qint32 byteOffset, bool littleEndian = false); + quint32 getUint32(qint32 byteOffset, bool littleEndian = false); + float getFloat32(qint32 byteOffset, bool littleEndian = false); + double 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; @@ -51,18 +51,18 @@ public slots: // // These methods raise an exception if they would write // beyond the end of the view. - void setInt8(quint32 byteOffset, qint32 value); - void setUint8(quint32 byteOffset, quint32 value); - void setInt16(quint32 byteOffset, qint32 value, bool littleEndian = false); - void setUint16(quint32 byteOffset, quint32 value, bool littleEndian = false); - void setInt32(quint32 byteOffset, qint32 value, bool littleEndian = false); - void setUint32(quint32 byteOffset, quint32 value, bool littleEndian = false); - void setFloat32(quint32 byteOffset, float value, bool littleEndian = false); - void setFloat64(quint32 byteOffset, double value, bool littleEndian = false); + void setInt8(qint32 byteOffset, qint32 value); + void setUint8(qint32 byteOffset, quint32 value); + void setInt16(qint32 byteOffset, qint32 value, bool littleEndian = false); + void setUint16(qint32 byteOffset, quint32 value, bool littleEndian = false); + void setInt32(qint32 byteOffset, qint32 value, bool littleEndian = false); + void setUint32(qint32 byteOffset, quint32 value, bool littleEndian = false); + void setFloat32(qint32 byteOffset, float value, bool littleEndian = false); + void setFloat64(qint32 byteOffset, double value, bool littleEndian = false); private: QByteArray* thisArrayBuffer() const; - bool realOffset(quint32& offset, size_t size) const; + bool realOffset(qint32& offset, size_t size) const; }; #endif // hifi_DataViewPrototype_h \ No newline at end of file diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp index d6ffc8f5a6..102c477c04 100644 --- a/libraries/script-engine/src/TypedArrays.cpp +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -56,6 +56,7 @@ QScriptValue TypedArray::newInstance(QScriptValue array) { } return newArray; } + engine()->evaluate("throw \"ArgumentError: not an array\""); return QScriptValue(); } @@ -200,19 +201,23 @@ QByteArray* TypedArrayPrototype::thisArrayBuffer() const { return qscriptvalue_cast(bufferObject.data()); } -void TypedArrayPrototype::set(QScriptValue array, quint32 offset) { +void TypedArrayPrototype::set(QScriptValue array, qint32 offset) { TypedArray* typedArray = static_cast(parent()); - if (array.isArray()) { + if (array.isArray() || typedArray) { + if (offset < 0) { + engine()->evaluate("throw \"ArgumentError: negative offset\""); + } quint32 length = array.property("length").toInt32(); if (offset + length > thisObject().data().property(typedArray->_lengthName).toInt32()) { - // TODO throw an error maybe? + engine()->evaluate("throw \"ArgumentError: array does not fit\""); return; } for (int i = 0; i < length; ++i) { thisObject().setProperty(QString::number(offset + i), array.property(QString::number(i))); } + } else { + engine()->evaluate("throw \"ArgumentError: not an array\""); } - // TODO handle typed arrays } QScriptValue TypedArrayPrototype::subarray(qint32 begin) { @@ -411,15 +416,31 @@ Float32ArrayClass::Float32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(sc QScriptValue Float32ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); - QScriptValue result = propertyHelper(arrayBuffer, name, id); - return (result.isValid()) ? result : TypedArray::property(object, name, id); + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data());bool ok = false; + name.toArrayIndex(&ok); + + if (ok && arrayBuffer) { + QDataStream stream(*arrayBuffer); + stream.skipRawData(id); + stream.setFloatingPointPrecision(QDataStream::SinglePrecision); + + float result; + stream >> result; + return result; + } + return TypedArray::property(object, name, id); } void Float32ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value) { QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); - setPropertyHelper(ba, name, id, value); + if (ba && value.isNumber()) { + QDataStream stream(ba, QIODevice::ReadWrite); + stream.skipRawData(id); + stream.setFloatingPointPrecision(QDataStream::SinglePrecision); + + stream << (float)value.toNumber(); + } } Float64ArrayClass::Float64ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, FLOAT_64_ARRAY_CLASS_NAME) { @@ -428,16 +449,30 @@ Float64ArrayClass::Float64ArrayClass(ScriptEngine* scriptEngine) : TypedArray(sc QScriptValue Float64ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); - QScriptValue result = propertyHelper(arrayBuffer, name, id); - return (result.isValid()) ? result : TypedArray::property(object, name, id); + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data());bool ok = false; + name.toArrayIndex(&ok); + + if (ok && arrayBuffer) { + QDataStream stream(*arrayBuffer); + stream.skipRawData(id); + stream.setFloatingPointPrecision(QDataStream::DoublePrecision); + + double result; + stream >> result; + return result; + } + return TypedArray::property(object, name, id); } void Float64ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value) { QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); - setPropertyHelper(ba, name, id, value); + if (ba && value.isNumber()) { + QDataStream stream(ba, QIODevice::ReadWrite); + stream.skipRawData(id); + stream.setFloatingPointPrecision(QDataStream::DoublePrecision); + + stream << (double)value.toNumber(); + } } - - diff --git a/libraries/script-engine/src/TypedArrays.h b/libraries/script-engine/src/TypedArrays.h index 37d341b2ad..2eff3e84b9 100644 --- a/libraries/script-engine/src/TypedArrays.h +++ b/libraries/script-engine/src/TypedArrays.h @@ -69,7 +69,7 @@ public: TypedArrayPrototype(QObject* parent = NULL); public slots: - void set(QScriptValue array, quint32 offset = 0); + void set(QScriptValue array, qint32 offset = 0); QScriptValue subarray(qint32 begin); QScriptValue subarray(qint32 begin, qint32 end); From 75573a4c06a67cc17f8725296b5589dd52e0219c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 11 Jul 2014 16:23:50 -0700 Subject: [PATCH 19/24] Added exception checking to unit tests --- examples/Test.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/examples/Test.js b/examples/Test.js index 646ae5c71d..caecfc5af7 100644 --- a/examples/Test.js +++ b/examples/Test.js @@ -20,7 +20,7 @@ test = function(name, func) { unitTest.run(); print(" Success: " + unitTest.numAssertions + " assertions passed"); } catch (error) { - print(" Failure: " + error.message); + print(" Failure: " + error.name + " " + error.message); } }; @@ -30,6 +30,12 @@ AssertionException = function(expected, actual, message) { this.name = 'AssertionException'; }; +UnthrownException = function(message) { + print("Creating exception"); + this.message = message + "\n"; + this.name = 'UnthrownException'; +}; + UnitTest = function(name, func) { this.numAssertions = 0; this.func = func; @@ -77,4 +83,15 @@ UnitTest.prototype.arrayEqual = function(array1, array2, message) { throw new AssertionException(array1[i], array2[i], message); } } +} + +UnitTest.prototype.raises = function(func, message) { + this.numAssertions++; + try { + func(); + } catch (error) { + return; + } + + throw new UnthrownException(message); } \ No newline at end of file From 5a8569fcd5539d42abd345027ff52785539b10cd Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 11 Jul 2014 17:45:52 -0700 Subject: [PATCH 20/24] Handling NaN values for floats --- examples/typedArraysUnitTest.js | 339 +++++++++++++++++- .../script-engine/src/DataViewPrototype.cpp | 22 +- .../script-engine/src/DataViewPrototype.h | 4 +- libraries/script-engine/src/TypedArrays.cpp | 6 + 4 files changed, 343 insertions(+), 28 deletions(-) diff --git a/examples/typedArraysUnitTest.js b/examples/typedArraysUnitTest.js index de9b487c32..a466c9104f 100644 --- a/examples/typedArraysUnitTest.js +++ b/examples/typedArraysUnitTest.js @@ -11,6 +11,26 @@ Script.include("Test.js"); +// e.g. extractbits([0xff, 0x80, 0x00, 0x00], 23, 30); inclusive +function extractbits(bytes, lo, hi) { + var out = 0; + bytes = bytes.slice(); // make a copy + var lsb = bytes.pop(), sc = 0, sh = 0; + + for (; lo > 0; lo--, hi--) { + lsb >>= 1; + if (++sc === 8) { sc = 0; lsb = bytes.pop(); } + } + + for (; hi >= 0; hi--) { + out = out | (lsb & 0x01) << sh++; + lsb >>= 1; + if (++sc === 8) { sc = 0; lsb = bytes.pop(); } + } + + return out; +} + test('ArrayBuffer', function(finished) { this.assertEquals(new ArrayBuffer(0).byteLength, 0, 'no length'); @@ -20,8 +40,8 @@ test('ArrayBuffer', function(finished) { this.assertEquals(new ArrayBuffer(123).byteLength, 123, 'length'); - // raises(function () { return new ArrayBuffer(-1); }, RangeError, 'negative length'); - // raises(function () { return new ArrayBuffer(0x80000000); }, RangeError, 'absurd length'); + this.raises(function () { return new ArrayBuffer(-1); }, 'negative length'); + this.raises(function () { return new ArrayBuffer(0x80000000); }, 'absurd length'); }); test('DataView constructors', function (finished) { @@ -39,8 +59,8 @@ test('DataView constructors', function (finished) { d.setUint32(0, 0x12345678); this.assertEquals(d.getUint32(0, true), 0x78563412, 'big endian/little endian'); - // raises(function () { return new DataView({}); }, 'non-ArrayBuffer argument'); - // raises(function () { return new DataView("bogus"); }, TypeError, 'non-ArrayBuffer argument'); + this.raises(function () { return new DataView({}); }, 'non-ArrayBuffer argument'); + this.raises(function () { return new DataView("bogus"); }, 'non-ArrayBuffer argument'); }); @@ -118,8 +138,8 @@ test('typed array constructors', function () { var int8 = new Int8Array(); this.assertEquals(int8.length, 0, 'no args 0'); - // raises(function () { return new Int8Array(-1); }, /*Range*/Error, 'bogus length'); - // raises(function () { return new Int8Array(0x80000000); }, /*Range*/Error, 'bogus length'); + this.raises(function () { return new Int8Array(-1); }, 'bogus length'); + this.raises(function () { return new Int8Array(0x80000000); }, 'bogus length'); int8 = new Int8Array(4); this.assertEquals(int8.BYTES_PER_ELEMENT, 1); @@ -160,10 +180,10 @@ test('typed array constructors', function () { int8 = new Int8Array(rawbuf, 8); this.assertEquals(int8.length, 0, 'buffer, byteOffset 26'); - // raises(function () { return new Int8Array(rawbuf, -1); }, 'invalid byteOffset 27'); - // raises(function () { return new Int8Array(rawbuf, 9); }, 'invalid byteOffset 28'); - // raises(function () { return new Int8Array(rawbuf, -1); }, 'invalid byteOffset 29'); - // raises(function () { return new Int32Array(rawbuf, 5); }, 'invalid byteOffset 30'); + this.raises(function () { return new Int8Array(rawbuf, -1); }, 'invalid byteOffset 27'); + this.raises(function () { return new Int8Array(rawbuf, 9); }, 'invalid byteOffset 28'); + this.raises(function () { return new Int32Array(rawbuf, -1); }, 'invalid byteOffset 29'); + this.raises(function () { return new Int32Array(rawbuf, 5); }, 'invalid byteOffset 30'); int8 = new Int8Array(rawbuf, 2, 4); this.assertEquals(int8.length, 4, 'buffer, byteOffset, length 31'); @@ -175,9 +195,9 @@ test('typed array constructors', function () { this.assertEquals(int8.get(-1), undefined, 'buffer, byteOffset, length, out of bounds 36'); this.assertEquals(int8.get(4), undefined, 'buffer, byteOffset, length, out of bounds 37'); - // raises(function () { return new Int8Array(rawbuf, 0, 9); }, 'invalid byteOffset+length'); - // raises(function () { return new Int8Array(rawbuf, 8, 1); }, 'invalid byteOffset+length'); - // raises(function () { return new Int8Array(rawbuf, 9, -1); }, 'invalid byteOffset+length'); + this.raises(function () { return new Int8Array(rawbuf, 0, 9); }, 'invalid byteOffset+length'); + this.raises(function () { return new Int8Array(rawbuf, 8, 1); }, 'invalid byteOffset+length'); + this.raises(function () { return new Int8Array(rawbuf, 9, -1); }, 'invalid byteOffset+length'); }); test('TypedArray clone constructor', function () { @@ -240,6 +260,189 @@ test('signed/unsigned conversions', function () { }); +test('IEEE754 single precision unpacking', function () { + function fromBytes(bytes) { + var uint8 = new Uint8Array(bytes), + dv = new DataView(uint8.buffer); + return dv.getFloat32(0); + } + + this.assertEquals(isNaN(fromBytes([0xff, 0xff, 0xff, 0xff])), true, 'Q-NaN'); + this.assertEquals(isNaN(fromBytes([0xff, 0xc0, 0x00, 0x01])), true, 'Q-NaN'); + + this.assertEquals(isNaN(fromBytes([0xff, 0xc0, 0x00, 0x00])), true, 'Indeterminate'); + + this.assertEquals(isNaN(fromBytes([0xff, 0xbf, 0xff, 0xff])), true, 'S-NaN'); + this.assertEquals(isNaN(fromBytes([0xff, 0x80, 0x00, 0x01])), true, 'S-NaN'); + + this.assertEquals(fromBytes([0xff, 0x80, 0x00, 0x00]), -Infinity, '-Infinity'); + + this.assertEquals(fromBytes([0xff, 0x7f, 0xff, 0xff]), -3.4028234663852886E+38, '-Normalized'); + this.assertEquals(fromBytes([0x80, 0x80, 0x00, 0x00]), -1.1754943508222875E-38, '-Normalized'); + this.assertEquals(fromBytes([0xff, 0x7f, 0xff, 0xff]), -3.4028234663852886E+38, '-Normalized'); + this.assertEquals(fromBytes([0x80, 0x80, 0x00, 0x00]), -1.1754943508222875E-38, '-Normalized'); + + // TODO: Denormalized values fail on Safari on iOS/ARM + this.assertEquals(fromBytes([0x80, 0x7f, 0xff, 0xff]), -1.1754942106924411E-38, '-Denormalized'); + this.assertEquals(fromBytes([0x80, 0x00, 0x00, 0x01]), -1.4012984643248170E-45, '-Denormalized'); + + this.assertEquals(fromBytes([0x80, 0x00, 0x00, 0x00]), -0, '-0'); + this.assertEquals(fromBytes([0x00, 0x00, 0x00, 0x00]), +0, '+0'); + + // TODO: Denormalized values fail on Safari on iOS/ARM + this.assertEquals(fromBytes([0x00, 0x00, 0x00, 0x01]), 1.4012984643248170E-45, '+Denormalized'); + this.assertEquals(fromBytes([0x00, 0x7f, 0xff, 0xff]), 1.1754942106924411E-38, '+Denormalized'); + + this.assertEquals(fromBytes([0x00, 0x80, 0x00, 0x00]), 1.1754943508222875E-38, '+Normalized'); + this.assertEquals(fromBytes([0x7f, 0x7f, 0xff, 0xff]), 3.4028234663852886E+38, '+Normalized'); + + this.assertEquals(fromBytes([0x7f, 0x80, 0x00, 0x00]), +Infinity, '+Infinity'); + + this.assertEquals(isNaN(fromBytes([0x7f, 0x80, 0x00, 0x01])), true, 'S+NaN'); + this.assertEquals(isNaN(fromBytes([0x7f, 0xbf, 0xff, 0xff])), true, 'S+NaN'); + + this.assertEquals(isNaN(fromBytes([0x7f, 0xc0, 0x00, 0x00])), true, 'Q+NaN'); + this.assertEquals(isNaN(fromBytes([0x7f, 0xff, 0xff, 0xff])), true, 'Q+NaN'); +}); + +test('IEEE754 single precision packing', function () { + + function toBytes(v) { + var uint8 = new Uint8Array(4), dv = new DataView(uint8.buffer); + dv.setFloat32(0, v); + var bytes = []; + for (var i = 0; i < 4; i += 1) { + bytes.push(uint8.get(i)); + } + return bytes; + } + + this.arrayEqual(toBytes(-Infinity), [0xff, 0x80, 0x00, 0x00], '-Infinity'); + + this.arrayEqual(toBytes(-3.4028235677973366e+38), [0xff, 0x80, 0x00, 0x00], '-Overflow'); + this.arrayEqual(toBytes(-3.402824E+38), [0xff, 0x80, 0x00, 0x00], '-Overflow'); + + this.arrayEqual(toBytes(-3.4028234663852886E+38), [0xff, 0x7f, 0xff, 0xff], '-Normalized'); + this.arrayEqual(toBytes(-1.1754943508222875E-38), [0x80, 0x80, 0x00, 0x00], '-Normalized'); + + // TODO: Denormalized values fail on Safari iOS/ARM + this.arrayEqual(toBytes(-1.1754942106924411E-38), [0x80, 0x7f, 0xff, 0xff], '-Denormalized'); + this.arrayEqual(toBytes(-1.4012984643248170E-45), [0x80, 0x00, 0x00, 0x01], '-Denormalized'); + + this.arrayEqual(toBytes(-7.006492321624085e-46), [0x80, 0x00, 0x00, 0x00], '-Underflow'); + + this.arrayEqual(toBytes(-0), [0x80, 0x00, 0x00, 0x00], '-0'); + this.arrayEqual(toBytes(0), [0x00, 0x00, 0x00, 0x00], '+0'); + + this.arrayEqual(toBytes(7.006492321624085e-46), [0x00, 0x00, 0x00, 0x00], '+Underflow'); + + // TODO: Denormalized values fail on Safari iOS/ARM + this.arrayEqual(toBytes(1.4012984643248170E-45), [0x00, 0x00, 0x00, 0x01], '+Denormalized'); + this.arrayEqual(toBytes(1.1754942106924411E-38), [0x00, 0x7f, 0xff, 0xff], '+Denormalized'); + + this.arrayEqual(toBytes(1.1754943508222875E-38), [0x00, 0x80, 0x00, 0x00], '+Normalized'); + this.arrayEqual(toBytes(3.4028234663852886E+38), [0x7f, 0x7f, 0xff, 0xff], '+Normalized'); + + this.arrayEqual(toBytes(+3.402824E+38), [0x7f, 0x80, 0x00, 0x00], '+Overflow'); + this.arrayEqual(toBytes(+3.402824E+38), [0x7f, 0x80, 0x00, 0x00], '+Overflow'); + this.arrayEqual(toBytes(+Infinity), [0x7f, 0x80, 0x00, 0x00], '+Infinity'); + + // Allow any NaN pattern (exponent all 1's, fraction non-zero) + var nanbytes = toBytes(NaN), + sign = extractbits(nanbytes, 31, 31), + exponent = extractbits(nanbytes, 23, 30), + fraction = extractbits(nanbytes, 0, 22); + this.assertEquals(exponent === 255 && fraction !== 0, true, 'NaN'); +}); + + +test('IEEE754 double precision unpacking', function () { + + function fromBytes(bytes) { + var uint8 = new Uint8Array(bytes), + dv = new DataView(uint8.buffer); + return dv.getFloat64(0); + } + + this.assertEquals(isNaN(fromBytes([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])), true, 'Q-NaN'); + this.assertEquals(isNaN(fromBytes([0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])), true, 'Q-NaN'); + + this.assertEquals(isNaN(fromBytes([0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])), true, 'Indeterminate'); + + this.assertEquals(isNaN(fromBytes([0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])), true, 'S-NaN'); + this.assertEquals(isNaN(fromBytes([0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])), true, 'S-NaN'); + + this.assertEquals(fromBytes([0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), -Infinity, '-Infinity'); + + this.assertEquals(fromBytes([0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), -1.7976931348623157E+308, '-Normalized'); + this.assertEquals(fromBytes([0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), -2.2250738585072014E-308, '-Normalized'); + + // TODO: Denormalized values fail on Safari iOS/ARM + this.assertEquals(fromBytes([0x80, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), -2.2250738585072010E-308, '-Denormalized'); + this.assertEquals(fromBytes([0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]), -4.9406564584124654E-324, '-Denormalized'); + + this.assertEquals(fromBytes([0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), -0, '-0'); + this.assertEquals(fromBytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), +0, '+0'); + + // TODO: Denormalized values fail on Safari iOS/ARM + this.assertEquals(fromBytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]), 4.9406564584124654E-324, '+Denormalized'); + this.assertEquals(fromBytes([0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), 2.2250738585072010E-308, '+Denormalized'); + + this.assertEquals(fromBytes([0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), 2.2250738585072014E-308, '+Normalized'); + this.assertEquals(fromBytes([0x7f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), 1.7976931348623157E+308, '+Normalized'); + + this.assertEquals(fromBytes([0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), +Infinity, '+Infinity'); + + this.assertEquals(isNaN(fromBytes([0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])), true, 'S+NaN'); + this.assertEquals(isNaN(fromBytes([0x7f, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])), true, 'S+NaN'); + + this.assertEquals(isNaN(fromBytes([0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])), true, 'Q+NaN'); + this.assertEquals(isNaN(fromBytes([0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])), true, 'Q+NaN'); +}); + + +test('IEEE754 double precision packing', function () { + + function toBytes(v) { + var uint8 = new Uint8Array(8), + dv = new DataView(uint8.buffer); + dv.setFloat64(0, v); + var bytes = []; + for (var i = 0; i < 8; i += 1) { + bytes.push(uint8.get(i)); + } + return bytes; + } + + this.arrayEqual(toBytes(-Infinity), [0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '-Infinity'); + + this.arrayEqual(toBytes(-1.7976931348623157E+308), [0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], '-Normalized'); + this.arrayEqual(toBytes(-2.2250738585072014E-308), [0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '-Normalized'); + + // TODO: Denormalized values fail on Safari iOS/ARM + this.arrayEqual(toBytes(-2.2250738585072010E-308), [0x80, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], '-Denormalized'); + this.arrayEqual(toBytes(-4.9406564584124654E-324), [0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], '-Denormalized'); + + this.arrayEqual(toBytes(-0), [0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '-0'); + this.arrayEqual(toBytes(0), [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '+0'); + + // TODO: Denormalized values fail on Safari iOS/ARM + this.arrayEqual(toBytes(4.9406564584124654E-324), [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], '+Denormalized'); + this.arrayEqual(toBytes(2.2250738585072010E-308), [0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], '+Denormalized'); + + this.arrayEqual(toBytes(2.2250738585072014E-308), [0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '+Normalized'); + this.arrayEqual(toBytes(1.7976931348623157E+308), [0x7f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], '+Normalized'); + + this.arrayEqual(toBytes(+Infinity), [0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '+Infinity'); + + // Allow any NaN pattern (exponent all 1's, fraction non-zero) + var nanbytes = toBytes(NaN), + sign = extractbits(nanbytes, 63, 63), + exponent = extractbits(nanbytes, 52, 62), + fraction = extractbits(nanbytes, 0, 51); + this.assertEquals(exponent === 2047 && fraction !== 0, true, 'NaN'); +}); + test('Int32Array round trips', function () { var i32 = new Int32Array([0]); var data = [ @@ -319,8 +522,8 @@ test('TypedArray setting', function () { b.set(new Int32Array([99, 98, 97]), 2); this.arrayEqual(b, [1, 2, 99, 98, 97], '3'); - // raises(function () { b.set(new Int32Array([99, 98, 97, 96]), 2); }); - // raises(function () { b.set([101, 102, 103, 104], 4); }); + this.raises(function () { b.set(new Int32Array([99, 98, 97, 96]), 2); }); + this.raises(function () { b.set([101, 102, 103, 104], 4); }); // ab = [ 0, 1, 2, 3, 4, 5, 6, 7 ] // a1 = [ ^, ^, ^, ^, ^, ^, ^, ^ ] @@ -374,11 +577,113 @@ test('DataView constructors', function () { //raises(function () { return new DataView(); }, TypeError, 'no arguments'); // Chrome raises TypeError, Safari iOS5 raises isDOMException(INDEX_SIZE_ERR) - // raises(function () { return new DataView({}); }, 'non-ArrayBuffer argument'); + this.raises(function () { return new DataView({}); }, 'non-ArrayBuffer argument'); - // raises(function () { return new DataView("bogus"); }, TypeError, 'non-ArrayBuffer argument'); + this.raises(function () { return new DataView("bogus"); }, TypeError, 'non-ArrayBuffer argument'); }); +test('DataView accessors', function () { + var u = new Uint8Array(8), d = new DataView(u.buffer); + this.arrayEqual(u, [0, 0, 0, 0, 0, 0, 0, 0], '1'); + + d.setUint8(0, 255); + this.arrayEqual(u, [0xff, 0, 0, 0, 0, 0, 0, 0], '2'); + + d.setInt8(1, -1); + this.arrayEqual(u, [0xff, 0xff, 0, 0, 0, 0, 0, 0], '3'); + + d.setUint16(2, 0x1234); + this.arrayEqual(u, [0xff, 0xff, 0x12, 0x34, 0, 0, 0, 0]), '4'; + + d.setInt16(4, -1); + this.arrayEqual(u, [0xff, 0xff, 0x12, 0x34, 0xff, 0xff, 0, 0], '5'); + + d.setUint32(1, 0x12345678); + this.arrayEqual(u, [0xff, 0x12, 0x34, 0x56, 0x78, 0xff, 0, 0], '6'); + + d.setInt32(4, -2023406815); + this.arrayEqual(u, [0xff, 0x12, 0x34, 0x56, 0x87, 0x65, 0x43, 0x21], '7'); + + d.setFloat32(2, 1.2E+38); + this.arrayEqual(u, [0xff, 0x12, 0x7e, 0xb4, 0x8e, 0x52, 0x43, 0x21], '8'); + + d.setFloat64(0, -1.2345678E+301); + this.arrayEqual(u, [0xfe, 0x72, 0x6f, 0x51, 0x5f, 0x61, 0x77, 0xe5], '9'); + + u.set([0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87]); + this.assertEquals(d.getUint8(0), 128, '10'); + this.assertEquals(d.getInt8(1), -127, '11'); + this.assertEquals(d.getUint16(2), 33411, '12'); + this.assertEquals(d.getInt16(3), -31868, '13'); + this.assertEquals(d.getUint32(4), 2223343239, '14'); + this.assertEquals(d.getInt32(2), -2105310075, '15'); + this.assertEquals(d.getFloat32(2), -1.932478247535851e-37, '16'); + this.assertEquals(d.getFloat64(0), -3.116851295377095e-306, '17'); + +}); + + +test('DataView endian', function () { + var rawbuf = (new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7])).buffer; + var d; + + d = new DataView(rawbuf); + this.assertEquals(d.byteLength, 8, 'buffer'); + this.assertEquals(d.byteOffset, 0, 'buffer'); + this.raises(function () { d.getUint8(-2); }); // Chrome bug for index -, DOMException, 'bounds for buffer'? + this.raises(function () { d.getUint8(8); }, 'bounds for buffer'); + this.raises(function () { d.setUint8(-2, 0); }, 'bounds for buffer'); + this.raises(function () { d.setUint8(8, 0); }, 'bounds for buffer'); + + d = new DataView(rawbuf, 2); + this.assertEquals(d.byteLength, 6, 'buffer, byteOffset'); + this.assertEquals(d.byteOffset, 2, 'buffer, byteOffset'); + this.assertEquals(d.getUint8(5), 7, 'buffer, byteOffset'); + this.raises(function () { d.getUint8(-2); }, 'bounds for buffer, byteOffset'); + this.raises(function () { d.getUint8(6); }, 'bounds for buffer, byteOffset'); + this.raises(function () { d.setUint8(-2, 0); }, 'bounds for buffer, byteOffset'); + this.raises(function () { d.setUint8(6, 0); }, 'bounds for buffer, byteOffset'); + + d = new DataView(rawbuf, 8); + this.assertEquals(d.byteLength, 0, 'buffer, byteOffset'); + + this.raises(function () { return new DataView(rawbuf, -1); }, 'invalid byteOffset'); + this.raises(function () { return new DataView(rawbuf, 9); }, 'invalid byteOffset'); + this.raises(function () { return new DataView(rawbuf, -1); }, 'invalid byteOffset'); + + d = new DataView(rawbuf, 2, 4); + this.assertEquals(d.byteLength, 4, 'buffer, byteOffset, length'); + this.assertEquals(d.byteOffset, 2, 'buffer, byteOffset, length'); + this.assertEquals(d.getUint8(3), 5, 'buffer, byteOffset, length'); + this.raises(function () { return d.getUint8(-2); }, 'bounds for buffer, byteOffset, length'); + this.raises(function () { d.getUint8(4); }, 'bounds for buffer, byteOffset, length'); + this.raises(function () { d.setUint8(-2, 0); }, 'bounds for buffer, byteOffset, length'); + this.raises(function () { d.setUint8(4, 0); }, 'bounds for buffer, byteOffset, length'); + + this.raises(function () { return new DataView(rawbuf, 0, 9); }, 'invalid byteOffset+length'); + this.raises(function () { return new DataView(rawbuf, 8, 1); }, 'invalid byteOffset+length'); + this.raises(function () { return new DataView(rawbuf, 9, -1); }, 'invalid byteOffset+length'); +}); + + +test('Typed Array getters/setters', function () { + // Only supported if Object.defineProperty() is fully supported on non-DOM objects. + try { + var o = {}; + Object.defineProperty(o, 'x', { get: function() { return 1; } }); + if (o.x !== 1) throw Error(); + } catch (_) { + ok(true); + return; + } + + var bytes = new Uint8Array([1, 2, 3, 4]), + uint32s = new Uint32Array(bytes.buffer); + + this.assertEquals(bytes[1], 2); + uint32s[0] = 0xffffffff; + this.assertEquals(bytes[1], 0xff); +}); diff --git a/libraries/script-engine/src/DataViewPrototype.cpp b/libraries/script-engine/src/DataViewPrototype.cpp index 95f3fdf3f6..29264e9bc5 100644 --- a/libraries/script-engine/src/DataViewPrototype.cpp +++ b/libraries/script-engine/src/DataViewPrototype.cpp @@ -32,10 +32,7 @@ bool DataViewPrototype::realOffset(qint32& offset, size_t size) const { } quint32 viewOffset = thisObject().data().property(BYTE_OFFSET_PROPERTY_NAME).toInt32(); quint32 viewLength = thisObject().data().property(BYTE_LENGTH_PROPERTY_NAME).toInt32(); - //qDebug() << "View Offset: " << viewOffset << ", View Lenght: " << viewLength; - //qDebug() << "Offset: " << offset << ", Size: " << size; offset += viewOffset; - //qDebug() << "New offset: " << offset << ", bool: " << ((offset + size) <= viewOffset + viewLength); return (offset + size) <= viewOffset + viewLength; } @@ -123,7 +120,7 @@ quint32 DataViewPrototype::getUint32(qint32 byteOffset, bool littleEndian) { return 0; } -float DataViewPrototype::getFloat32(qint32 byteOffset, bool littleEndian) { +QScriptValue DataViewPrototype::getFloat32(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(float))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -132,14 +129,17 @@ float DataViewPrototype::getFloat32(qint32 byteOffset, bool littleEndian) { float result; stream >> result; - qDebug() << "Get: " << result; - return result; + if (isNaN(result)) { + return QScriptValue(NAN); + } + + return QScriptValue(result); } thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); - return 0; + return QScriptValue(); } -double DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) { +QScriptValue DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(double))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -148,10 +148,14 @@ double DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) { double result; stream >> result; + if (isNaN(result)) { + return QScriptValue(); + } + return result; } thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); - return 0; + return QScriptValue(); } ///////////////// SETTERS //////////////////////////// diff --git a/libraries/script-engine/src/DataViewPrototype.h b/libraries/script-engine/src/DataViewPrototype.h index b7061a388e..da0261dad4 100644 --- a/libraries/script-engine/src/DataViewPrototype.h +++ b/libraries/script-engine/src/DataViewPrototype.h @@ -37,8 +37,8 @@ public slots: quint32 getUint16(qint32 byteOffset, bool littleEndian = false); qint32 getInt32(qint32 byteOffset, bool littleEndian = false); quint32 getUint32(qint32 byteOffset, bool littleEndian = false); - float getFloat32(qint32 byteOffset, bool littleEndian = false); - double getFloat64(qint32 byteOffset, bool littleEndian = false); + QScriptValue getFloat32(qint32 byteOffset, bool littleEndian = false); + QScriptValue 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; diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp index 102c477c04..106b824f76 100644 --- a/libraries/script-engine/src/TypedArrays.cpp +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -426,6 +426,9 @@ QScriptValue Float32ArrayClass::property(const QScriptValue &object, const QScri float result; stream >> result; + if (isNaN(result)) { + return QScriptValue(NAN); + } return result; } return TypedArray::property(object, name, id); @@ -459,6 +462,9 @@ QScriptValue Float64ArrayClass::property(const QScriptValue &object, const QScri double result; stream >> result; + if (isNaN(result)) { + return QScriptValue(NAN); + } return result; } return TypedArray::property(object, name, id); From 6d4bb0f4fd96cc5d623b0d623058ba83aafdc862 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 11 Jul 2014 18:27:26 -0700 Subject: [PATCH 21/24] Added Uint8ClampedArray --- examples/Test.js | 2 +- examples/typedArraysUnitTest.js | 19 ++++++++++++++ libraries/script-engine/src/ScriptEngine.cpp | 2 ++ libraries/script-engine/src/TypedArrays.cpp | 27 ++++++++++++++++++++ libraries/script-engine/src/TypedArrays.h | 8 +++++- 5 files changed, 56 insertions(+), 2 deletions(-) diff --git a/examples/Test.js b/examples/Test.js index caecfc5af7..36dee7bd90 100644 --- a/examples/Test.js +++ b/examples/Test.js @@ -80,7 +80,7 @@ UnitTest.prototype.arrayEqual = function(array1, array2, message) { } for (var i = 0; i < array1.length; ++i) { if (array1[i] !== array2[i]) { - throw new AssertionException(array1[i], array2[i], message); + throw new AssertionException(array1[i], array2[i], i + " " + message); } } } diff --git a/examples/typedArraysUnitTest.js b/examples/typedArraysUnitTest.js index a466c9104f..859bc9b9b3 100644 --- a/examples/typedArraysUnitTest.js +++ b/examples/typedArraysUnitTest.js @@ -687,3 +687,22 @@ test('Typed Array getters/setters', function () { this.assertEquals(bytes[1], 0xff); }); + +test('Uint8ClampedArray', function () { + this.assertEquals(Uint8ClampedArray.BYTES_PER_ELEMENT, 1, 'Uint8ClampedArray.BYTES_PER_ELEMENT'); + var a = new Uint8ClampedArray([-Infinity, -Number.MAX_VALUE, -1, -Number.MIN_VALUE, -0, + 0, Number.MIN_VALUE, 1, 1.1, 1.9, 255, 255.1, 255.9, 256, Number.MAX_VALUE, Infinity, + NaN]); + this.assertEquals(a.BYTES_PER_ELEMENT, 1); + this.assertEquals(a.byteOffset, 0); + this.assertEquals(a.byteLength, 17); + this.arrayEqual(a, [0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0], "array test"); +}); + +test('Regression Tests', function() { + // Bug: https://github.com/inexorabletash/polyfill/issues/16 + var minFloat32 = 1.401298464324817e-45; + var truncated = new Float32Array([-minFloat32 / 2 - Math.pow(2, -202)]).get(0); + this.assertEquals(truncated, -minFloat32, 'smallest 32 bit float should not truncate to zero'); +}); + diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 4074beb88e..0279e2ea27 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -100,6 +100,7 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam new DataViewClass(this); new Int8ArrayClass(this); new Uint8ArrayClass(this); + new Uint8ClampedArrayClass(this); new Int16ArrayClass(this); new Uint16ArrayClass(this); new Int32ArrayClass(this); @@ -141,6 +142,7 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, new DataViewClass(this); new Int8ArrayClass(this); new Uint8ArrayClass(this); + new Uint8ClampedArrayClass(this); new Int16ArrayClass(this); new Uint16ArrayClass(this); new Int32ArrayClass(this); diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp index 106b824f76..6b461fc6fb 100644 --- a/libraries/script-engine/src/TypedArrays.cpp +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -342,6 +342,33 @@ void Uint8ArrayClass::setProperty(QScriptValue &object, const QScriptString &nam setPropertyHelper(ba, name, id, value); } +Uint8ClampedArrayClass::Uint8ClampedArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, UINT_8_CLAMPED_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(quint8)); +} + +QScriptValue Uint8ClampedArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(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) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + if (ba && value.isNumber()) { + QDataStream stream(ba, QIODevice::ReadWrite); + stream.skipRawData(id); + if (value.toNumber() > 255) { + stream << (quint8)255; + } else if (value.toNumber() < 0) { + stream << (quint8)0; + } else { + stream << (quint8)glm::clamp(qRound(value.toNumber()), 0, 255); + } + } +} + Int16ArrayClass::Int16ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_16_ARRAY_CLASS_NAME) { setBytesPerElement(sizeof(qint16)); } diff --git a/libraries/script-engine/src/TypedArrays.h b/libraries/script-engine/src/TypedArrays.h index 2eff3e84b9..bacb067add 100644 --- a/libraries/script-engine/src/TypedArrays.h +++ b/libraries/script-engine/src/TypedArrays.h @@ -19,6 +19,7 @@ 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"; @@ -98,7 +99,12 @@ public: }; class Uint8ClampedArrayClass : public TypedArray { - // TODO + Q_OBJECT +public: + Uint8ClampedArrayClass(ScriptEngine* scriptEngine); + + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); }; class Int16ArrayClass : public TypedArray { From aa65084ae1c8199f122e03b5d36e43e3d9c56b17 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 11 Jul 2014 23:08:41 -0700 Subject: [PATCH 22/24] Code cleanup --- .../script-engine/src/ArrayBufferClass.cpp | 15 ++++++++++- libraries/script-engine/src/ScriptEngine.cpp | 27 ++----------------- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/libraries/script-engine/src/ArrayBufferClass.cpp b/libraries/script-engine/src/ArrayBufferClass.cpp index 4f85eeeae2..f8299266fd 100644 --- a/libraries/script-engine/src/ArrayBufferClass.cpp +++ b/libraries/script-engine/src/ArrayBufferClass.cpp @@ -12,12 +12,13 @@ #include #include "ArrayBufferPrototype.h" +#include "DataViewClass.h" #include "ScriptEngine.h" +#include "TypedArrays.h" #include "ArrayBufferClass.h" static const QString CLASS_NAME = "ArrayBuffer"; -static const QString BYTE_LENGTH_PROPERTY_NAME = "byteLength"; Q_DECLARE_METATYPE(QByteArray*) @@ -46,6 +47,18 @@ _scriptEngine(scriptEngine) { _ctor.setData(engine()->toScriptValue(this)); engine()->globalObject().setProperty(name(), _ctor); + + // Registering other array types + new DataViewClass(scriptEngine); + new Int8ArrayClass(scriptEngine); + new Uint8ArrayClass(scriptEngine); + new Uint8ClampedArrayClass(scriptEngine); + new Int16ArrayClass(scriptEngine); + new Uint16ArrayClass(scriptEngine); + new Int32ArrayClass(scriptEngine); + new Uint32ArrayClass(scriptEngine); + new Float32ArrayClass(scriptEngine); + new Float64ArrayClass(scriptEngine); } QScriptValue ArrayBufferClass::newInstance(qint32 size) { diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 0279e2ea27..a224b9fa33 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -94,19 +94,8 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam _vec3Library(), _uuidLibrary(), _animationCache(this), - _arrayBufferClass(NULL) + _arrayBufferClass(new ArrayBufferClass(this)) { - _arrayBufferClass = new ArrayBufferClass(this); - new DataViewClass(this); - new Int8ArrayClass(this); - new Uint8ArrayClass(this); - new Uint8ClampedArrayClass(this); - new Int16ArrayClass(this); - new Uint16ArrayClass(this); - new Int32ArrayClass(this); - new Uint32ArrayClass(this); - new Float32ArrayClass(this); - new Float64ArrayClass(this); } ScriptEngine::ScriptEngine(const QUrl& scriptURL, @@ -131,25 +120,13 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, _vec3Library(), _uuidLibrary(), _animationCache(this), - _arrayBufferClass(NULL) + _arrayBufferClass(new ArrayBufferClass(this)) { QString scriptURLString = scriptURL.toString(); _fileNameString = scriptURLString; QUrl url(scriptURL); - _arrayBufferClass = new ArrayBufferClass(this); - new DataViewClass(this); - new Int8ArrayClass(this); - new Uint8ArrayClass(this); - new Uint8ClampedArrayClass(this); - new Int16ArrayClass(this); - new Uint16ArrayClass(this); - new Int32ArrayClass(this); - new Uint32ArrayClass(this); - new Float32ArrayClass(this); - new Float64ArrayClass(this); - // if the scheme length is one or lower, maybe they typed in a file, let's try const int WINDOWS_DRIVE_LETTER_SIZE = 1; if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) { From 30fd4e27d05d08a0a71a1a2bc515ba4868c9ed93 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 14 Jul 2014 10:43:01 -0700 Subject: [PATCH 23/24] pre-CR cleanup --- .../script-engine/src/ArrayBufferClass.cpp | 16 +- .../script-engine/src/ArrayBufferClass.h | 8 +- .../src/ArrayBufferPrototype.cpp | 1 - .../src/ArrayBufferViewClass.cpp | 60 +----- .../script-engine/src/ArrayBufferViewClass.h | 10 +- libraries/script-engine/src/DataViewClass.cpp | 5 +- .../script-engine/src/DataViewPrototype.cpp | 22 +- libraries/script-engine/src/ScriptEngine.cpp | 1 - libraries/script-engine/src/ScriptEngine.h | 2 +- .../script-engine/src/TypedArrayPrototype.cpp | 106 ++++++++++ .../script-engine/src/TypedArrayPrototype.h | 33 +++ libraries/script-engine/src/TypedArrays.cpp | 191 +++++------------- libraries/script-engine/src/TypedArrays.h | 54 ++--- 13 files changed, 241 insertions(+), 268 deletions(-) create mode 100644 libraries/script-engine/src/TypedArrayPrototype.cpp create mode 100644 libraries/script-engine/src/TypedArrayPrototype.h diff --git a/libraries/script-engine/src/ArrayBufferClass.cpp b/libraries/script-engine/src/ArrayBufferClass.cpp index f8299266fd..ab33b5ffe7 100644 --- a/libraries/script-engine/src/ArrayBufferClass.cpp +++ b/libraries/script-engine/src/ArrayBufferClass.cpp @@ -23,10 +23,9 @@ static const QString CLASS_NAME = "ArrayBuffer"; Q_DECLARE_METATYPE(QByteArray*) ArrayBufferClass::ArrayBufferClass(ScriptEngine* scriptEngine) : -QObject(scriptEngine->getScriptEngine()), -QScriptClass(scriptEngine->getScriptEngine()), +QObject(scriptEngine->getEngine()), +QScriptClass(scriptEngine->getEngine()), _scriptEngine(scriptEngine) { - qDebug() << "Created with engine: " << engine(); qScriptRegisterMetaType(engine(), toScriptValue, fromScriptValue); QScriptValue global = engine()->globalObject(); @@ -40,15 +39,16 @@ _scriptEngine(scriptEngine) { QScriptEngine::SkipMethodsInEnumeration | QScriptEngine::ExcludeSuperClassMethods | QScriptEngine::ExcludeSuperClassProperties); - _proto.setPrototype(global.property("Object").property("prototype")); + // Register constructor _ctor = engine()->newFunction(construct, _proto); _ctor.setData(engine()->toScriptValue(this)); engine()->globalObject().setProperty(name(), _ctor); // Registering other array types + // The script engine is there parent so it'll delete them with itself new DataViewClass(scriptEngine); new Int8ArrayClass(scriptEngine); new Uint8ArrayClass(scriptEngine); @@ -87,11 +87,10 @@ QScriptValue ArrayBufferClass::newInstance(const QByteArray& ba) { QScriptValue ArrayBufferClass::construct(QScriptContext* context, QScriptEngine* engine) { ArrayBufferClass* cls = qscriptvalue_cast(context->callee().data()); if (!cls) { + // return if callee (function called) is not of type ArrayBuffer return QScriptValue(); } - QScriptValue arg = context->argument(0); - if (!arg.isValid() || !arg.isNumber()) { return QScriptValue(); } @@ -100,6 +99,7 @@ QScriptValue ArrayBufferClass::construct(QScriptContext* context, QScriptEngine* QScriptValue newObject = cls->newInstance(size); if (context->isCalledAsConstructor()) { + // if called with keyword new, replace this object. context->setThisObject(newObject); return engine->undefinedValue(); } @@ -118,8 +118,8 @@ QScriptClass::QueryFlags ArrayBufferClass::queryProperty(const QScriptValue& obj return 0; // No access } -QScriptValue ArrayBufferClass::property(const QScriptValue &object, - const QScriptString &name, uint id) { +QScriptValue ArrayBufferClass::property(const QScriptValue& object, + const QScriptString& name, uint id) { QByteArray* ba = qscriptvalue_cast(object.data()); if (ba && name == _byteLength) { return ba->length(); diff --git a/libraries/script-engine/src/ArrayBufferClass.h b/libraries/script-engine/src/ArrayBufferClass.h index c7b4d447dd..f7ad8ad4bd 100644 --- a/libraries/script-engine/src/ArrayBufferClass.h +++ b/libraries/script-engine/src/ArrayBufferClass.h @@ -29,19 +29,19 @@ public: QScriptValue newInstance(qint32 size); QScriptValue newInstance(const QByteArray& ba); - ScriptEngine* getScriptEngine() { return _scriptEngine; } - QueryFlags queryProperty(const QScriptValue& object, const QScriptString& name, QueryFlags flags, uint* id); - QScriptValue property(const QScriptValue &object, - const QScriptString &name, uint id); + QScriptValue property(const QScriptValue& object, + const QScriptString& name, uint id); QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, const QScriptString& name, uint id); QString name() const; QScriptValue prototype() const; + ScriptEngine* getEngine() { return _scriptEngine; } + private: static QScriptValue construct(QScriptContext* context, QScriptEngine* engine); diff --git a/libraries/script-engine/src/ArrayBufferPrototype.cpp b/libraries/script-engine/src/ArrayBufferPrototype.cpp index 37db5f3eec..53ebebc740 100644 --- a/libraries/script-engine/src/ArrayBufferPrototype.cpp +++ b/libraries/script-engine/src/ArrayBufferPrototype.cpp @@ -12,7 +12,6 @@ #include #include "ArrayBufferClass.h" - #include "ArrayBufferPrototype.h" Q_DECLARE_METATYPE(QByteArray*) diff --git a/libraries/script-engine/src/ArrayBufferViewClass.cpp b/libraries/script-engine/src/ArrayBufferViewClass.cpp index a01f239512..aad2e6add7 100644 --- a/libraries/script-engine/src/ArrayBufferViewClass.cpp +++ b/libraries/script-engine/src/ArrayBufferViewClass.cpp @@ -14,8 +14,8 @@ Q_DECLARE_METATYPE(QByteArray*) ArrayBufferViewClass::ArrayBufferViewClass(ScriptEngine* scriptEngine) : -QObject(scriptEngine->getScriptEngine()), -QScriptClass(scriptEngine->getScriptEngine()), +QObject(scriptEngine->getEngine()), +QScriptClass(scriptEngine->getEngine()), _scriptEngine(scriptEngine) { // Save string handles for quick lookup _bufferName = engine()->toStringHandle(BUFFER_PROPERTY_NAME.toLatin1()); @@ -32,8 +32,8 @@ QScriptClass::QueryFlags ArrayBufferViewClass::queryProperty(const QScriptValue& return 0; // No access } -QScriptValue ArrayBufferViewClass::property(const QScriptValue &object, - const QScriptString &name, uint id) { +QScriptValue ArrayBufferViewClass::property(const QScriptValue& object, + const QScriptString& name, uint id) { if (name == _bufferName) { return object.data().property(_bufferName); } @@ -50,55 +50,3 @@ QScriptValue::PropertyFlags ArrayBufferViewClass::propertyFlags(const QScriptVal const QScriptString& name, uint id) { return QScriptValue::Undeletable; } - -//QScriptClass::QueryFlags DataViewClass::queryProperty(const QScriptValue& object, -// const QScriptString& name, -// QueryFlags flags, uint* id) { -// QByteArray* arrayBuffer = qscriptvalue_cast(object.property(_bufferName).data()); -// bool ok = false; -// int pos = name.toArrayIndex(&ok); -// -// // Check that name is a valid index and arrayBuffer exists -// if (ok && arrayBuffer && pos > 0 && pos < arrayBuffer->size()) { -// *id = pos; // save pos to avoid recomputation -// return HandlesReadAccess | HandlesWriteAccess; // Read/Write access -// } -// -// return ArrayBufferViewClass::queryProperty(object, name, flags, id); -//} -// -//QScriptValue DataViewClass::property(const QScriptValue &object, -// const QScriptString &name, uint id) { -// QByteArray* arrayBuffer = qscriptvalue_cast(object.property(_bufferName).data()); -// bool ok = false; -// name.toArrayIndex(&ok); -// -// if (ok && arrayBuffer) { -// return (*arrayBuffer)[id]; -// } -// -// return ArrayBufferViewClass::queryProperty(object, name, flags, id); -//} -// -//void DataViewClass::setProperty(QScriptValue &object, -// const QScriptString &name, -// uint id, const QScriptValue &value) { -// QByteArray *ba = qscriptvalue_cast(object.data()); -// if (!ba) -// return; -// if (name == length) { -// resize(*ba, value.toInt32()); -// } else { -// qint32 pos = id; -// if (pos < 0) -// return; -// if (ba->size() <= pos) -// resize(*ba, pos + 1); -// (*ba)[pos] = char(value.toInt32()); -// } -//} -// -//QScriptValue::PropertyFlags DataViewClass::propertyFlags(const QScriptValue& object, -// const QScriptString& name, uint id) { -// return QScriptValue::Undeletable; -//} diff --git a/libraries/script-engine/src/ArrayBufferViewClass.h b/libraries/script-engine/src/ArrayBufferViewClass.h index acc061c8a3..b673ebf280 100644 --- a/libraries/script-engine/src/ArrayBufferViewClass.h +++ b/libraries/script-engine/src/ArrayBufferViewClass.h @@ -34,12 +34,12 @@ public: ScriptEngine* getScriptEngine() { return _scriptEngine; } virtual QueryFlags queryProperty(const QScriptValue& object, - const QScriptString& name, - QueryFlags flags, uint* id); - virtual QScriptValue property(const QScriptValue &object, - const QScriptString &name, uint id); + const QScriptString& name, + QueryFlags flags, uint* id); + virtual QScriptValue property(const QScriptValue& object, + const QScriptString& name, uint id); virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, - const QScriptString& name, uint id); + const QScriptString& name, uint id); protected: // JS Object attributes QScriptString _bufferName; diff --git a/libraries/script-engine/src/DataViewClass.cpp b/libraries/script-engine/src/DataViewClass.cpp index efb45daf3f..a65bdff617 100644 --- a/libraries/script-engine/src/DataViewClass.cpp +++ b/libraries/script-engine/src/DataViewClass.cpp @@ -29,12 +29,11 @@ DataViewClass::DataViewClass(ScriptEngine* scriptEngine) : ArrayBufferViewClass( QScriptEngine::SkipMethodsInEnumeration | QScriptEngine::ExcludeSuperClassMethods | QScriptEngine::ExcludeSuperClassProperties); - _proto.setPrototype(global.property("Object").property("prototype")); + // Register constructor _ctor = engine()->newFunction(construct, _proto); _ctor.setData(engine()->toScriptValue(this)); - engine()->globalObject().setProperty(name(), _ctor); } @@ -47,7 +46,7 @@ QScriptValue DataViewClass::newInstance(QScriptValue buffer, quint32 byteOffset, return engine()->newObject(this, data); } -QScriptValue DataViewClass::construct(QScriptContext *context, QScriptEngine *engine) { +QScriptValue DataViewClass::construct(QScriptContext* context, QScriptEngine* engine) { DataViewClass* cls = qscriptvalue_cast(context->callee().data()); if (!cls || context->argumentCount() < 1) { return QScriptValue(); diff --git a/libraries/script-engine/src/DataViewPrototype.cpp b/libraries/script-engine/src/DataViewPrototype.cpp index 29264e9bc5..91f7e01b50 100644 --- a/libraries/script-engine/src/DataViewPrototype.cpp +++ b/libraries/script-engine/src/DataViewPrototype.cpp @@ -36,8 +36,6 @@ bool DataViewPrototype::realOffset(qint32& offset, size_t size) const { return (offset + size) <= viewOffset + viewLength; } -///////////////// GETTERS //////////////////////////// - qint32 DataViewPrototype::getInt8(qint32 byteOffset) { if (realOffset(byteOffset, sizeof(qint8))) { QDataStream stream(*thisArrayBuffer()); @@ -149,7 +147,7 @@ QScriptValue DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) double result; stream >> result; if (isNaN(result)) { - return QScriptValue(); + return QScriptValue(NAN); } return result; @@ -158,8 +156,6 @@ QScriptValue DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) return QScriptValue(); } -///////////////// SETTERS //////////////////////////// - void DataViewPrototype::setInt8(qint32 byteOffset, qint32 value) { if (realOffset(byteOffset, sizeof(qint8))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); @@ -167,7 +163,7 @@ void DataViewPrototype::setInt8(qint32 byteOffset, qint32 value) { stream << (qint8)value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } @@ -178,7 +174,7 @@ void DataViewPrototype::setUint8(qint32 byteOffset, quint32 value) { stream << (quint8)value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } @@ -190,7 +186,7 @@ void DataViewPrototype::setInt16(qint32 byteOffset, qint32 value, bool littleEnd stream << (qint16)value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } @@ -202,7 +198,7 @@ void DataViewPrototype::setUint16(qint32 byteOffset, quint32 value, bool littleE stream << (quint16)value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } @@ -214,7 +210,7 @@ void DataViewPrototype::setInt32(qint32 byteOffset, qint32 value, bool littleEnd stream << (qint32)value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } @@ -226,7 +222,7 @@ void DataViewPrototype::setUint32(qint32 byteOffset, quint32 value, bool littleE stream << (quint32)value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } @@ -239,7 +235,7 @@ void DataViewPrototype::setFloat32(qint32 byteOffset, float value, bool littleEn stream << value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } @@ -252,7 +248,7 @@ void DataViewPrototype::setFloat64(qint32 byteOffset, double value, bool littleE stream << value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index a224b9fa33..cd7c2670ec 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -143,7 +143,6 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, QTextStream in(&scriptFile); _scriptContents = in.readAll(); } else { - qDebug() << "ERROR Loading file:" << fileName; emit errorMessage("ERROR Loading file:" + fileName); } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 3dda172044..0eda74914f 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -57,7 +57,7 @@ public: /// Access the ModelsScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener static ModelsScriptingInterface* getModelsScriptingInterface() { return &_modelsScriptingInterface; } - QScriptEngine* getScriptEngine() { return &_engine; } + QScriptEngine* getEngine() { return &_engine; } ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; } /// sets the script contents, will return false if failed, will fail if script is already running diff --git a/libraries/script-engine/src/TypedArrayPrototype.cpp b/libraries/script-engine/src/TypedArrayPrototype.cpp new file mode 100644 index 0000000000..37274dd080 --- /dev/null +++ b/libraries/script-engine/src/TypedArrayPrototype.cpp @@ -0,0 +1,106 @@ +// +// TypedArrayPrototype.cpp +// +// +// Created by Clement on 7/14/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "TypedArrays.h" + +#include "TypedArrayPrototype.h" + +Q_DECLARE_METATYPE(QByteArray*) + +TypedArrayPrototype::TypedArrayPrototype(QObject* parent) : QObject(parent) { +} + +QByteArray* TypedArrayPrototype::thisArrayBuffer() const { + QScriptValue bufferObject = thisObject().data().property(BUFFER_PROPERTY_NAME); + return qscriptvalue_cast(bufferObject.data()); +} + +void TypedArrayPrototype::set(QScriptValue array, qint32 offset) { + TypedArray* typedArray = static_cast(parent()); + if (array.isArray() || typedArray) { + if (offset < 0) { + engine()->evaluate("throw \"ArgumentError: negative offset\""); + } + quint32 length = array.property("length").toInt32(); + if (offset + length > thisObject().data().property(typedArray->_lengthName).toInt32()) { + engine()->evaluate("throw \"ArgumentError: array does not fit\""); + return; + } + for (int i = 0; i < length; ++i) { + thisObject().setProperty(QString::number(offset + i), array.property(QString::number(i))); + } + } else { + engine()->evaluate("throw \"ArgumentError: not an array\""); + } +} + +QScriptValue TypedArrayPrototype::subarray(qint32 begin) { + TypedArray* typedArray = static_cast(parent()); + QScriptValue 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; + + // if indices < 0 then they start from the end of the array + begin = (begin < 0) ? length + begin : begin; + + // here we clamp the indices to fit the array + begin = glm::clamp(begin, 0, (length - 1)); + + byteOffset += begin * bytesPerElement; + return typedArray->newInstance(arrayBuffer, byteOffset, length - begin); +} + +QScriptValue TypedArrayPrototype::subarray(qint32 begin, qint32 end) { + TypedArray* typedArray = static_cast(parent()); + QScriptValue 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; + + // if indices < 0 then they start from the end of the array + begin = (begin < 0) ? length + begin : begin; + end = (end < 0) ? length + end : end; + + // here we clamp the indices to fit the array + begin = glm::clamp(begin, 0, (length - 1)); + end = glm::clamp(end, 0, (length - 1)); + + byteOffset += begin * bytesPerElement; + length = (end - begin > 0) ? end - begin : 0; + return typedArray->newInstance(arrayBuffer, byteOffset, length); +} + +QScriptValue TypedArrayPrototype::get(quint32 index) { + TypedArray* typedArray = static_cast(parent()); + QScriptString name = engine()->toStringHandle(QString::number(index)); + uint id; + QScriptClass::QueryFlags flags = typedArray->queryProperty(thisObject(), + name, + QScriptClass::HandlesReadAccess, &id); + if (QScriptClass::HandlesReadAccess & flags) { + return typedArray->property(thisObject(), name, id); + } + return QScriptValue(); +} + +void TypedArrayPrototype::set(quint32 index, QScriptValue& value) { + TypedArray* typedArray = static_cast(parent()); + QScriptValue object = thisObject(); + QScriptString name = engine()->toStringHandle(QString::number(index)); + uint id; + QScriptClass::QueryFlags flags = typedArray->queryProperty(object, + name, + QScriptClass::HandlesWriteAccess, &id); + if (QScriptClass::HandlesWriteAccess & flags) { + typedArray->setProperty(object, name, id, value); + } +} diff --git a/libraries/script-engine/src/TypedArrayPrototype.h b/libraries/script-engine/src/TypedArrayPrototype.h new file mode 100644 index 0000000000..86d578ace0 --- /dev/null +++ b/libraries/script-engine/src/TypedArrayPrototype.h @@ -0,0 +1,33 @@ +// +// TypedArrayPrototype.h +// +// +// Created by Clement on 7/14/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 +// + +#ifndef hifi_TypedArrayPrototype_h +#define hifi_TypedArrayPrototype_h + +#include "ArrayBufferViewClass.h" + +class TypedArrayPrototype : public QObject, public QScriptable { + 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); + + QScriptValue get(quint32 index); + void set(quint32 index, QScriptValue& value); +private: + QByteArray* thisArrayBuffer() const; +}; + +#endif // hifi_TypedArrayPrototype_h \ No newline at end of file diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp index 6b461fc6fb..72e84859c4 100644 --- a/libraries/script-engine/src/TypedArrays.cpp +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -12,6 +12,7 @@ #include #include "ScriptEngine.h" +#include "TypedArrayPrototype.h" #include "TypedArrays.h" @@ -30,12 +31,11 @@ TypedArray::TypedArray(ScriptEngine* scriptEngine, QString name) : ArrayBufferVi QScriptEngine::SkipMethodsInEnumeration | QScriptEngine::ExcludeSuperClassMethods | QScriptEngine::ExcludeSuperClassProperties); - _proto.setPrototype(global.property("Object").property("prototype")); + // Register constructor _ctor = engine()->newFunction(construct, _proto); _ctor.setData(engine()->toScriptValue(this)); - engine()->globalObject().setProperty(_name, _ctor); } @@ -46,8 +46,9 @@ QScriptValue TypedArray::newInstance(quint32 length) { } QScriptValue TypedArray::newInstance(QScriptValue array) { - if (array.property("length").isValid()) { - quint32 length = array.property("length").toInt32(); + 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); for (int i = 0; i < length; ++i) { QScriptValue value = array.property(QString::number(i)); @@ -70,12 +71,11 @@ QScriptValue TypedArray::newInstance(QScriptValue buffer, quint32 byteOffset, qu return engine()->newObject(this, data); } -QScriptValue TypedArray::construct(QScriptContext *context, QScriptEngine *engine) { +QScriptValue TypedArray::construct(QScriptContext* context, QScriptEngine* engine) { TypedArray* cls = qscriptvalue_cast(context->callee().data()); if (!cls) { return QScriptValue(); } - if (context->argumentCount() == 0) { return cls->newInstance(0); } @@ -84,6 +84,7 @@ QScriptValue TypedArray::construct(QScriptContext *context, QScriptEngine *engin QScriptValue bufferArg = context->argument(0); QByteArray* arrayBuffer = qscriptvalue_cast(bufferArg.data()); + // parse arguments if (arrayBuffer) { if (context->argumentCount() == 1) { // Case for entire ArrayBuffer @@ -136,6 +137,7 @@ QScriptValue TypedArray::construct(QScriptContext *context, QScriptEngine *engin } if (context->isCalledAsConstructor()) { + // if called with the new keyword, replace this object context->setThisObject(newObject); return engine->undefinedValue(); } @@ -164,8 +166,8 @@ 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) { +QScriptValue TypedArray::property(const QScriptValue& object, + const QScriptString& name, uint id) { if (name == _bytesPerElementName) { return QScriptValue(_bytesPerElement); } @@ -193,98 +195,10 @@ void TypedArray::setBytesPerElement(quint32 bytesPerElement) { _ctor.setProperty(_bytesPerElementName, _bytesPerElement); } -TypedArrayPrototype::TypedArrayPrototype(QObject* parent) : QObject(parent) { -} - -QByteArray* TypedArrayPrototype::thisArrayBuffer() const { - QScriptValue bufferObject = thisObject().data().property(BUFFER_PROPERTY_NAME); - return qscriptvalue_cast(bufferObject.data()); -} - -void TypedArrayPrototype::set(QScriptValue array, qint32 offset) { - TypedArray* typedArray = static_cast(parent()); - if (array.isArray() || typedArray) { - if (offset < 0) { - engine()->evaluate("throw \"ArgumentError: negative offset\""); - } - quint32 length = array.property("length").toInt32(); - if (offset + length > thisObject().data().property(typedArray->_lengthName).toInt32()) { - engine()->evaluate("throw \"ArgumentError: array does not fit\""); - return; - } - for (int i = 0; i < length; ++i) { - thisObject().setProperty(QString::number(offset + i), array.property(QString::number(i))); - } - } else { - engine()->evaluate("throw \"ArgumentError: not an array\""); - } -} - -QScriptValue TypedArrayPrototype::subarray(qint32 begin) { - TypedArray* typedArray = static_cast(parent()); - QScriptValue 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; - - // if indices < 0 then they start from the end of the array - begin = (begin < 0) ? length + begin : begin; - - // here we clamp the indices to fit the array - begin = glm::clamp(begin, 0, (length - 1)); - - byteOffset += begin * bytesPerElement; - return typedArray->newInstance(arrayBuffer, byteOffset, length - begin); -} - -QScriptValue TypedArrayPrototype::subarray(qint32 begin, qint32 end) { - TypedArray* typedArray = static_cast(parent()); - QScriptValue 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; - - // if indices < 0 then they start from the end of the array - begin = (begin < 0) ? length + begin : begin; - end = (end < 0) ? length + end : end; - - // here we clamp the indices to fit the array - begin = glm::clamp(begin, 0, (length - 1)); - end = glm::clamp(end, 0, (length - 1)); - - byteOffset += begin * bytesPerElement; - length = (end - begin > 0) ? end - begin : 0; - return typedArray->newInstance(arrayBuffer, byteOffset, length); -} - -QScriptValue TypedArrayPrototype::get(quint32 index) { - TypedArray* typedArray = static_cast(parent()); - QScriptString name = engine()->toStringHandle(QString::number(index)); - uint id; - QScriptClass::QueryFlags flags = typedArray->queryProperty(thisObject(), - name, - QScriptClass::HandlesReadAccess, &id); - if (QScriptClass::HandlesReadAccess & flags) { - return typedArray->property(thisObject(), name, id); - } - return QScriptValue(); -} - -void TypedArrayPrototype::set(quint32 index, QScriptValue& value) { - TypedArray* typedArray = static_cast(parent()); - QScriptValue object = thisObject(); - QScriptString name = engine()->toStringHandle(QString::number(index)); - uint id; - QScriptClass::QueryFlags flags = typedArray->queryProperty(object, - name, - QScriptClass::HandlesWriteAccess, &id); - if (QScriptClass::HandlesWriteAccess & flags) { - typedArray->setProperty(object, name, id, value); - } -} - +// templated helper functions +// don't work for floats as they require single precision settings template -QScriptValue propertyHelper(const QByteArray* arrayBuffer, const QScriptString &name, uint id) { +QScriptValue propertyHelper(const QByteArray* arrayBuffer, const QScriptString& name, uint id) { bool ok = false; name.toArrayIndex(&ok); @@ -300,7 +214,7 @@ QScriptValue propertyHelper(const QByteArray* arrayBuffer, const QScriptString & } template -void setPropertyHelper(QByteArray* arrayBuffer, const QScriptString &name, uint id, const QScriptValue &value) { +void setPropertyHelper(QByteArray* arrayBuffer, const QScriptString& name, uint id, const QScriptValue& value) { if (arrayBuffer && value.isNumber()) { QDataStream stream(arrayBuffer, QIODevice::ReadWrite); stream.skipRawData(id); @@ -313,15 +227,15 @@ Int8ArrayClass::Int8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEn setBytesPerElement(sizeof(qint8)); } -QScriptValue Int8ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { +QScriptValue Int8ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); QScriptValue result = propertyHelper(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) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); setPropertyHelper(ba, name, id, value); } @@ -329,16 +243,15 @@ Uint8ArrayClass::Uint8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(script setBytesPerElement(sizeof(quint8)); } -QScriptValue Uint8ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - +QScriptValue Uint8ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); QScriptValue result = propertyHelper(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) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Uint8ArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); setPropertyHelper(ba, name, id, value); } @@ -346,16 +259,15 @@ Uint8ClampedArrayClass::Uint8ClampedArrayClass(ScriptEngine* scriptEngine) : Typ setBytesPerElement(sizeof(quint8)); } -QScriptValue Uint8ClampedArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - +QScriptValue Uint8ClampedArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); QScriptValue result = propertyHelper(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) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Uint8ClampedArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); if (ba && value.isNumber()) { QDataStream stream(ba, QIODevice::ReadWrite); stream.skipRawData(id); @@ -373,16 +285,15 @@ Int16ArrayClass::Int16ArrayClass(ScriptEngine* scriptEngine) : TypedArray(script setBytesPerElement(sizeof(qint16)); } -QScriptValue Int16ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - +QScriptValue Int16ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); QScriptValue result = propertyHelper(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) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Int16ArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); setPropertyHelper(ba, name, id, value); } @@ -390,16 +301,15 @@ Uint16ArrayClass::Uint16ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scri setBytesPerElement(sizeof(quint16)); } -QScriptValue Uint16ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - +QScriptValue Uint16ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); QScriptValue result = propertyHelper(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) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Uint16ArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); setPropertyHelper(ba, name, id, value); } @@ -407,16 +317,15 @@ Int32ArrayClass::Int32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(script setBytesPerElement(sizeof(qint32)); } -QScriptValue Int32ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - +QScriptValue Int32ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); QScriptValue result = propertyHelper(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) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Int32ArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); setPropertyHelper(ba, name, id, value); } @@ -424,16 +333,16 @@ Uint32ArrayClass::Uint32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scri setBytesPerElement(sizeof(quint32)); } -QScriptValue Uint32ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { +QScriptValue Uint32ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); QScriptValue result = propertyHelper(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) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Uint32ArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); setPropertyHelper(ba, name, id, value); } @@ -441,8 +350,7 @@ Float32ArrayClass::Float32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(sc setBytesPerElement(sizeof(float)); } -QScriptValue Float32ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - +QScriptValue Float32ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data());bool ok = false; name.toArrayIndex(&ok); @@ -461,9 +369,9 @@ QScriptValue Float32ArrayClass::property(const QScriptValue &object, const QScri return TypedArray::property(object, name, id); } -void Float32ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, - uint id, const QScriptValue &value) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Float32ArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); if (ba && value.isNumber()) { QDataStream stream(ba, QIODevice::ReadWrite); stream.skipRawData(id); @@ -477,8 +385,7 @@ Float64ArrayClass::Float64ArrayClass(ScriptEngine* scriptEngine) : TypedArray(sc setBytesPerElement(sizeof(double)); } -QScriptValue Float64ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - +QScriptValue Float64ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data());bool ok = false; name.toArrayIndex(&ok); @@ -497,9 +404,9 @@ QScriptValue Float64ArrayClass::property(const QScriptValue &object, const QScri return TypedArray::property(object, name, id); } -void Float64ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, - uint id, const QScriptValue &value) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Float64ArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); if (ba && value.isNumber()) { QDataStream stream(ba, QIODevice::ReadWrite); stream.skipRawData(id); diff --git a/libraries/script-engine/src/TypedArrays.h b/libraries/script-engine/src/TypedArrays.h index bacb067add..a1a288c3c9 100644 --- a/libraries/script-engine/src/TypedArrays.h +++ b/libraries/script-engine/src/TypedArrays.h @@ -64,29 +64,13 @@ protected: friend class TypedArrayPrototype; }; -class TypedArrayPrototype : public QObject, public QScriptable { - 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); - - QScriptValue get(quint32 index); - void set(quint32 index, QScriptValue& value); -private: - QByteArray* thisArrayBuffer() const; -}; - class Int8ArrayClass : public TypedArray { Q_OBJECT public: Int8ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); }; class Uint8ArrayClass : public TypedArray { @@ -94,8 +78,8 @@ class Uint8ArrayClass : public TypedArray { public: Uint8ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); }; class Uint8ClampedArrayClass : public TypedArray { @@ -103,8 +87,8 @@ class Uint8ClampedArrayClass : public TypedArray { public: Uint8ClampedArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); }; class Int16ArrayClass : public TypedArray { @@ -112,8 +96,8 @@ class Int16ArrayClass : public TypedArray { public: Int16ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); }; class Uint16ArrayClass : public TypedArray { @@ -121,16 +105,17 @@ class Uint16ArrayClass : public TypedArray { public: Uint16ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value);}; + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); +}; class Int32ArrayClass : public TypedArray { Q_OBJECT public: Int32ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); }; class Uint32ArrayClass : public TypedArray { @@ -138,8 +123,8 @@ class Uint32ArrayClass : public TypedArray { public: Uint32ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); }; class Float32ArrayClass : public TypedArray { @@ -147,16 +132,17 @@ class Float32ArrayClass : public TypedArray { public: Float32ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value);}; + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); +}; class Float64ArrayClass : public TypedArray { Q_OBJECT public: Float64ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); }; #endif // hifi_TypedArrays_h \ No newline at end of file From afa43a9d7e8ce19f0f35aa708cea7b19a1c0fcba Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 14 Jul 2014 11:06:10 -0700 Subject: [PATCH 24/24] Fix win build fail --- examples/typedArraysUnitTest.js | 2 +- libraries/script-engine/src/DataViewPrototype.cpp | 4 ++-- libraries/script-engine/src/TypedArrays.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/typedArraysUnitTest.js b/examples/typedArraysUnitTest.js index 859bc9b9b3..e86a07289d 100644 --- a/examples/typedArraysUnitTest.js +++ b/examples/typedArraysUnitTest.js @@ -514,7 +514,7 @@ test('TypedArray setting', function () { var b = new Int32Array(5); b.set(a); this.arrayEqual(b, [1, 2, 3, 4, 5], '1'); - // raises(function () { b.set(a, 1); }); + this.raises(function () { b.set(a, 1); }); b.set(new Int32Array([99, 98]), 2); this.arrayEqual(b, [1, 2, 99, 98, 5], '2'); diff --git a/libraries/script-engine/src/DataViewPrototype.cpp b/libraries/script-engine/src/DataViewPrototype.cpp index 91f7e01b50..8bab574f33 100644 --- a/libraries/script-engine/src/DataViewPrototype.cpp +++ b/libraries/script-engine/src/DataViewPrototype.cpp @@ -128,7 +128,7 @@ QScriptValue DataViewPrototype::getFloat32(qint32 byteOffset, bool littleEndian) float result; stream >> result; if (isNaN(result)) { - return QScriptValue(NAN); + return QScriptValue(); } return QScriptValue(result); @@ -147,7 +147,7 @@ QScriptValue DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) double result; stream >> result; if (isNaN(result)) { - return QScriptValue(NAN); + return QScriptValue(); } return result; diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp index 72e84859c4..751d1385e5 100644 --- a/libraries/script-engine/src/TypedArrays.cpp +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -362,7 +362,7 @@ QScriptValue Float32ArrayClass::property(const QScriptValue& object, const QScri float result; stream >> result; if (isNaN(result)) { - return QScriptValue(NAN); + return QScriptValue(); } return result; } @@ -397,7 +397,7 @@ QScriptValue Float64ArrayClass::property(const QScriptValue& object, const QScri double result; stream >> result; if (isNaN(result)) { - return QScriptValue(NAN); + return QScriptValue(); } return result; }