From 868478e92afaa9d0823f3a65ff3d7b44216087ea Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 10 Sep 2014 16:39:23 +0200 Subject: [PATCH] Implement DataView The second class that is required for typed array support. Change-Id: Idc2dcec7c1eee541f76dc5ab1aea6057ba03cb93 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/jsruntime.pri | 6 +- src/qml/jsruntime/qv4dataview.cpp | 316 +++++++++++++++++++++++++++++ src/qml/jsruntime/qv4dataview_p.h | 96 +++++++++ src/qml/jsruntime/qv4engine.cpp | 14 +- src/qml/jsruntime/qv4engine_p.h | 4 + tests/auto/qml/qjsengine/tst_qjsengine.cpp | 1 + 6 files changed, 434 insertions(+), 3 deletions(-) create mode 100644 src/qml/jsruntime/qv4dataview.cpp create mode 100644 src/qml/jsruntime/qv4dataview_p.h diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index 566d256..3914537 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -43,7 +43,8 @@ SOURCES += \ $$PWD/qv4qmlextensions.cpp \ $$PWD/qv4vme_moth.cpp \ $$PWD/qv4profiling.cpp \ - $$PWD/qv4arraybuffer.cpp + $$PWD/qv4arraybuffer.cpp \ + $$PWD/qv4dataview.cpp HEADERS += \ $$PWD/qv4global_p.h \ @@ -91,7 +92,8 @@ HEADERS += \ $$PWD/qv4qmlextensions_p.h \ $$PWD/qv4vme_moth_p.h \ $$PWD/qv4profiling_p.h \ - $$PWD/qv4arraybuffer_p.h + $$PWD/qv4arraybuffer_p.h \ + $$PWD/qv4dataview_p.h } diff --git a/src/qml/jsruntime/qv4dataview.cpp b/src/qml/jsruntime/qv4dataview.cpp new file mode 100644 index 0000000..2750b2c --- /dev/null +++ b/src/qml/jsruntime/qv4dataview.cpp @@ -0,0 +1,316 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4dataview_p.h" +#include "qv4arraybuffer_p.h" + +#include "qendian.h" + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(DataViewCtor); +DEFINE_OBJECT_VTABLE(DataView); + +DataViewCtor::Data::Data(ExecutionContext *scope) + : FunctionObject::Data(scope, QStringLiteral("DataView")) +{ + setVTable(staticVTable()); +} + +ReturnedValue DataViewCtor::construct(Managed *m, CallData *callData) +{ + Scope scope(m->engine()); + Scoped buffer(scope, callData->argument(0)); + if (!buffer) + return scope.engine->currentContext()->throwTypeError(); + + double bo = callData->argc > 1 ? callData->args[1].toNumber() : 0; + uint byteOffset = (uint)bo; + uint bufferLength = buffer->d()->data->size; + double bl = callData->argc < 3 || callData->args[2].isUndefined() ? (bufferLength - bo) : callData->args[2].toNumber(); + uint byteLength = (uint)bl; + if (bo != byteOffset || bl != byteLength || byteOffset + byteLength > bufferLength) + return scope.engine->currentContext()->throwRangeError(QStringLiteral("DataView: constructor arguments out of range")); + + Scoped a(scope, scope.engine->memoryManager->alloc(scope.engine)); + a->d()->buffer = buffer; + a->d()->byteLength = byteLength; + a->d()->byteOffset = byteOffset; + return a.asReturnedValue(); + +} + +ReturnedValue DataViewCtor::call(Managed *that, CallData *callData) +{ + return construct(that, callData); +} + + +DataView::Data::Data(ExecutionEngine *e) + : Object::Data(e->dataViewClass), + buffer(0), + byteLength(0), + byteOffset(0) +{ +} + + +void DataView::markObjects(Managed *that, ExecutionEngine *e) +{ + DataView *v = static_cast(that); + v->d()->buffer->mark(e); +} + +void DataViewPrototype::init(ExecutionEngine *engine, Object *ctor) +{ + Scope scope(engine); + ScopedObject o(scope); + ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(3)); + ctor->defineReadonlyProperty(engine->id_prototype, (o = this)); + defineDefaultProperty(engine->id_constructor, (o = ctor)); + defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, 0); + defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, 0); + defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, 0); + + defineDefaultProperty(QStringLiteral("getInt8"), method_getChar, 0); + defineDefaultProperty(QStringLiteral("getUInt8"), method_getChar, 0); + defineDefaultProperty(QStringLiteral("getInt16"), method_get, 0); + defineDefaultProperty(QStringLiteral("getUInt16"), method_get, 0); + defineDefaultProperty(QStringLiteral("getInt32"), method_get, 0); + defineDefaultProperty(QStringLiteral("getUInt32"), method_get, 0); + defineDefaultProperty(QStringLiteral("getFloat32"), method_getFloat, 0); + defineDefaultProperty(QStringLiteral("getFloat64"), method_getFloat, 0); + + defineDefaultProperty(QStringLiteral("setInt8"), method_setChar, 0); + defineDefaultProperty(QStringLiteral("setUInt8"), method_setChar, 0); + defineDefaultProperty(QStringLiteral("setInt16"), method_set, 0); + defineDefaultProperty(QStringLiteral("setUInt16"), method_set, 0); + defineDefaultProperty(QStringLiteral("setInt32"), method_set, 0); + defineDefaultProperty(QStringLiteral("setUInt32"), method_set, 0); + defineDefaultProperty(QStringLiteral("setFloat32"), method_setFloat, 0); + defineDefaultProperty(QStringLiteral("setFloat64"), method_setFloat, 0); +} + +ReturnedValue DataViewPrototype::method_get_buffer(CallContext *ctx) +{ + Scope scope(ctx); + Scoped v(scope, ctx->d()->callData->thisObject); + if (!v) + return ctx->throwTypeError(); + + return Encode(v->d()->buffer->asReturnedValue()); +} + +ReturnedValue DataViewPrototype::method_get_byteLength(CallContext *ctx) +{ + Scope scope(ctx); + Scoped v(scope, ctx->d()->callData->thisObject); + if (!v) + return ctx->throwTypeError(); + + return Encode(v->d()->byteLength); +} + +ReturnedValue DataViewPrototype::method_get_byteOffset(CallContext *ctx) +{ + Scope scope(ctx); + Scoped v(scope, ctx->d()->callData->thisObject); + if (!v) + return ctx->throwTypeError(); + + return Encode(v->d()->byteOffset); +} + +template +ReturnedValue DataViewPrototype::method_getChar(CallContext *ctx) +{ + Scope scope(ctx); + Scoped v(scope, ctx->d()->callData->thisObject); + if (!v || ctx->d()->callData->argc < 1) + return ctx->throwTypeError(); + double l = ctx->d()->callData->args[0].toNumber(); + uint idx = (uint)l; + if (l != idx || idx + sizeof(T) > v->d()->byteLength) + return ctx->throwTypeError(); + idx += v->d()->byteOffset; + + T t = T(v->d()->buffer->d()->data->data()[idx]); + + return Encode((int)t); +} + +template +ReturnedValue DataViewPrototype::method_get(CallContext *ctx) +{ + Scope scope(ctx); + Scoped v(scope, ctx->d()->callData->thisObject); + if (!v || ctx->d()->callData->argc < 1) + return ctx->throwTypeError(); + double l = ctx->d()->callData->args[0].toNumber(); + uint idx = (uint)l; + if (l != idx || idx + sizeof(T) > v->d()->byteLength) + return ctx->throwTypeError(); + idx += v->d()->byteOffset; + + bool littleEndian = ctx->d()->callData->argc < 2 ? false : ctx->d()->callData->args[1].toBoolean(); + + T t = littleEndian + ? qFromLittleEndian((uchar *)v->d()->buffer->d()->data->data() + idx) + : qFromBigEndian((uchar *)v->d()->buffer->d()->data->data() + idx); + + return Encode(t); +} + +template +ReturnedValue DataViewPrototype::method_getFloat(CallContext *ctx) +{ + Scope scope(ctx); + Scoped v(scope, ctx->d()->callData->thisObject); + if (!v || ctx->d()->callData->argc < 1) + return ctx->throwTypeError(); + double l = ctx->d()->callData->args[0].toNumber(); + uint idx = (uint)l; + if (l != idx || idx + sizeof(T) > v->d()->byteLength) + return ctx->throwTypeError(); + idx += v->d()->byteOffset; + + bool littleEndian = ctx->d()->callData->argc < 2 ? false : ctx->d()->callData->args[1].toBoolean(); + + if (sizeof(T) == 4) { + // float + union { + uint i; + float f; + } u; + u.i = littleEndian + ? qFromLittleEndian((uchar *)v->d()->buffer->d()->data->data() + idx) + : qFromBigEndian((uchar *)v->d()->buffer->d()->data->data() + idx); + return Encode(u.f); + } else { + Q_ASSERT(sizeof(T) == 8); + union { + quint64 i; + double d; + } u; + u.i = littleEndian + ? qFromLittleEndian((uchar *)v->d()->buffer->d()->data->data() + idx) + : qFromBigEndian((uchar *)v->d()->buffer->d()->data->data() + idx); + return Encode(u.d); + } +} + +template +ReturnedValue DataViewPrototype::method_setChar(CallContext *ctx) +{ + Scope scope(ctx); + Scoped v(scope, ctx->d()->callData->thisObject); + if (!v || ctx->d()->callData->argc < 1) + return ctx->throwTypeError(); + double l = ctx->d()->callData->args[0].toNumber(); + uint idx = (uint)l; + if (l != idx || idx + sizeof(T) > v->d()->byteLength) + return ctx->throwTypeError(); + idx += v->d()->byteOffset; + + int val = ctx->d()->callData->argc >= 2 ? ctx->d()->callData->args[1].toInt32() : 0; + v->d()->buffer->d()->data->data()[idx] = (char)val; + + return Encode::undefined(); +} + +template +ReturnedValue DataViewPrototype::method_set(CallContext *ctx) +{ + Scope scope(ctx); + Scoped v(scope, ctx->d()->callData->thisObject); + if (!v || ctx->d()->callData->argc < 1) + return ctx->throwTypeError(); + double l = ctx->d()->callData->args[0].toNumber(); + uint idx = (uint)l; + if (l != idx || idx + sizeof(T) > v->d()->byteLength) + return ctx->throwTypeError(); + idx += v->d()->byteOffset; + + int val = ctx->d()->callData->argc >= 2 ? ctx->d()->callData->args[1].toInt32() : 0; + + bool littleEndian = ctx->d()->callData->argc < 3 ? false : ctx->d()->callData->args[2].toBoolean(); + + if (littleEndian) + qToLittleEndian(val, (uchar *)v->d()->buffer->d()->data->data() + idx); + else + qToBigEndian(val, (uchar *)v->d()->buffer->d()->data->data() + idx); + + return Encode::undefined(); +} + +template +ReturnedValue DataViewPrototype::method_setFloat(CallContext *ctx) +{ + Scope scope(ctx); + Scoped v(scope, ctx->d()->callData->thisObject); + if (!v || ctx->d()->callData->argc < 1) + return ctx->throwTypeError(); + double l = ctx->d()->callData->args[0].toNumber(); + uint idx = (uint)l; + if (l != idx || idx + sizeof(T) > v->d()->byteLength) + return ctx->throwTypeError(); + idx += v->d()->byteOffset; + + double val = ctx->d()->callData->argc >= 2 ? ctx->d()->callData->args[1].toNumber() : qSNaN(); + bool littleEndian = ctx->d()->callData->argc < 3 ? false : ctx->d()->callData->args[2].toBoolean(); + + if (sizeof(T) == 4) { + // float + union { + uint i; + float f; + } u; + u.f = val; + if (littleEndian) + qToLittleEndian(u.i, (uchar *)v->d()->buffer->d()->data->data() + idx); + else + qToBigEndian(u.i, (uchar *)v->d()->buffer->d()->data->data() + idx); + } else { + Q_ASSERT(sizeof(T) == 8); + union { + quint64 i; + double d; + } u; + u.d = val; + if (littleEndian) + qToLittleEndian(u.i, (uchar *)v->d()->buffer->d()->data->data() + idx); + else + qToBigEndian(u.i, (uchar *)v->d()->buffer->d()->data->data() + idx); + } + return Encode::undefined(); +} diff --git a/src/qml/jsruntime/qv4dataview_p.h b/src/qml/jsruntime/qv4dataview_p.h new file mode 100644 index 0000000..aa8a020 --- /dev/null +++ b/src/qml/jsruntime/qv4dataview_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4DATAVIEW_H +#define QV4DATAVIEW_H + +#include "qv4object_p.h" +#include "qv4functionobject_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct ArrayBuffer; + +struct DataViewCtor: FunctionObject +{ + struct Data : FunctionObject::Data { + Data(ExecutionContext *scope); + }; + + V4_OBJECT(FunctionObject) + + static ReturnedValue construct(Managed *m, CallData *callData); + static ReturnedValue call(Managed *that, CallData *callData); +}; + +struct DataView : Object +{ + struct Data : Object::Data { + Data(ExecutionEngine *e); + ArrayBuffer *buffer; + uint byteLength; + uint byteOffset; + }; + V4_OBJECT(Object) + + static void markObjects(Managed *that, ExecutionEngine *e); +}; + +struct DataViewPrototype: Object +{ + void init(ExecutionEngine *engine, Object *ctor); + + static ReturnedValue method_get_buffer(CallContext *ctx); + static ReturnedValue method_get_byteLength(CallContext *ctx); + static ReturnedValue method_get_byteOffset(CallContext *ctx); + template + static ReturnedValue method_getChar(CallContext *ctx); + template + static ReturnedValue method_get(CallContext *ctx); + template + static ReturnedValue method_getFloat(CallContext *ctx); + template + static ReturnedValue method_setChar(CallContext *ctx); + template + static ReturnedValue method_set(CallContext *ctx); + template + static ReturnedValue method_setFloat(CallContext *ctx); +}; + + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 239bb85..4549282 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -62,6 +62,7 @@ #include "qv4qmlextensions_p.h" #include "qv4memberdata_p.h" #include "qv4arraybuffer_p.h" +#include "qv4dataview_p.h" #include #include @@ -254,6 +255,8 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) id_destroy = newIdentifier(QStringLiteral("destroy")); id_valueOf = newIdentifier(QStringLiteral("valueOf")); id_byteLength = newIdentifier(QStringLiteral("byteLength")); + id_byteOffset = newIdentifier(QStringLiteral("byteOffset")); + id_buffer = newIdentifier(QStringLiteral("buffer")); memberDataClass = InternalClass::create(this, MemberData::staticVTable(), 0); @@ -369,10 +372,15 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) // typed arrays arrayBufferCtor = memoryManager->alloc(rootContext); - Scoped arrayBufferPrototype(scope, memoryManager->alloc(arrayBufferClass)); + Scoped arrayBufferPrototype(scope, memoryManager->alloc(objectClass)); arrayBufferPrototype->init(this, arrayBufferCtor.asObject()); arrayBufferClass = InternalClass::create(this, ArrayBuffer::staticVTable(), arrayBufferPrototype); + dataViewCtor = memoryManager->alloc(rootContext); + Scoped dataViewPrototype(scope, memoryManager->alloc(objectClass)); + dataViewPrototype->init(this, dataViewCtor.asObject()); + dataViewClass = InternalClass::create(this, DataView::staticVTable(), dataViewPrototype); + // // set up the global object // @@ -397,6 +405,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) globalObject->defineDefaultProperty(QStringLiteral("TypeError"), typeErrorCtor); globalObject->defineDefaultProperty(QStringLiteral("URIError"), uRIErrorCtor); globalObject->defineDefaultProperty(QStringLiteral("ArrayBuffer"), arrayBufferCtor); + globalObject->defineDefaultProperty(QStringLiteral("DataView"), dataViewCtor); ScopedObject o(scope); globalObject->defineDefaultProperty(QStringLiteral("Math"), (o = memoryManager->alloc(QV4::InternalClass::create(this, MathObject::staticVTable(), objectPrototype)))); globalObject->defineDefaultProperty(QStringLiteral("JSON"), (o = memoryManager->alloc(QV4::InternalClass::create(this, JsonObject::staticVTable(), objectPrototype)))); @@ -899,6 +908,8 @@ void ExecutionEngine::markObjects() id_destroy->mark(this); id_valueOf->mark(this); id_byteLength->mark(this); + id_byteOffset->mark(this); + id_buffer->mark(this); objectCtor.mark(this); stringCtor.mark(this); @@ -916,6 +927,7 @@ void ExecutionEngine::markObjects() typeErrorCtor.mark(this); uRIErrorCtor.mark(this); arrayBufferCtor.mark(this); + dataViewCtor.mark(this); sequencePrototype.mark(this); exceptionValue.mark(this); diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index d2d36e9..ba96649 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -190,6 +190,7 @@ public: Value uRIErrorCtor; Value sequencePrototype; Value arrayBufferCtor; + Value dataViewCtor; InternalClassPool *classPool; InternalClass *emptyClass; @@ -226,6 +227,7 @@ public: InternalClass *memberDataClass; InternalClass *arrayBufferClass; + InternalClass *dataViewClass; EvalFunction *evalFunction; FunctionObject *thrower; @@ -266,6 +268,8 @@ public: StringValue id_destroy; StringValue id_valueOf; StringValue id_byteLength; + StringValue id_byteOffset; + StringValue id_buffer; QSet compilationUnits; diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index fcfb95a..7410416 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -804,6 +804,7 @@ void tst_QJSEngine::globalObjectProperties_enumerate() << "undefined" << "JSON" << "ArrayBuffer" + << "DataView" ; QSet actualNames; { -- 2.7.4