diff options
author | Lars Knoll <[email protected]> | 2014-09-10 14:50:28 +0200 |
---|---|---|
committer | Simon Hausmann <[email protected]> | 2014-10-29 09:06:56 +0100 |
commit | a2c97406cad22a73a4c68303ef54128cf756f577 (patch) | |
tree | 274a68bea63c6934be4e2b82147adec74759da41 /src | |
parent | d9e70d1a49af347f79db7e64bdd8e2e8083a77b5 (diff) |
Implement ArrayBuffer
This is the first class required to support typed
arrays in our JS engine.
Change-Id: I0fe1e1ca430769c171912dda207cfae772e9b9db
Reviewed-by: Simon Hausmann <[email protected]>
Diffstat (limited to 'src')
-rw-r--r-- | src/qml/jsruntime/jsruntime.pri | 6 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4arraybuffer.cpp | 164 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4arraybuffer_p.h | 93 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 13 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4engine_p.h | 4 |
5 files changed, 278 insertions, 2 deletions
diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index c27aaa90d8..566d2563cc 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -42,7 +42,8 @@ SOURCES += \ $$PWD/qv4qobjectwrapper.cpp \ $$PWD/qv4qmlextensions.cpp \ $$PWD/qv4vme_moth.cpp \ - $$PWD/qv4profiling.cpp + $$PWD/qv4profiling.cpp \ + $$PWD/qv4arraybuffer.cpp HEADERS += \ $$PWD/qv4global_p.h \ @@ -89,7 +90,8 @@ HEADERS += \ $$PWD/qv4qobjectwrapper_p.h \ $$PWD/qv4qmlextensions_p.h \ $$PWD/qv4vme_moth_p.h \ - $$PWD/qv4profiling_p.h + $$PWD/qv4profiling_p.h \ + $$PWD/qv4arraybuffer_p.h } diff --git a/src/qml/jsruntime/qv4arraybuffer.cpp b/src/qml/jsruntime/qv4arraybuffer.cpp new file mode 100644 index 0000000000..9d24044d7c --- /dev/null +++ b/src/qml/jsruntime/qv4arraybuffer.cpp @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** 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 "qv4arraybuffer_p.h" +#include "qv4typedarray_p.h" +#include "qv4dataview_p.h" + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(ArrayBufferCtor); +DEFINE_OBJECT_VTABLE(ArrayBuffer); + +ArrayBufferCtor::Data::Data(ExecutionContext *scope) + : FunctionObject::Data(scope, QStringLiteral("ArrayBuffer")) +{ + setVTable(staticVTable()); +} + +ReturnedValue ArrayBufferCtor::construct(Managed *m, CallData *callData) +{ + ExecutionEngine *v4 = m->engine(); + + Scope scope(v4); + ScopedValue l(scope, callData->argument(0)); + double dl = l->toInteger(); + if (v4->hasException) + return Encode::undefined(); + uint len = (uint)qBound(0., dl, (double)UINT_MAX); + if (len != dl) + return v4->currentContext()->throwRangeError(QLatin1String("ArrayBuffer constructor: invalid length")); + + Scoped<ArrayBuffer> a(scope, v4->memoryManager->alloc<ArrayBuffer>(v4, len)); + return a.asReturnedValue(); +} + + +ReturnedValue ArrayBufferCtor::call(Managed *that, CallData *callData) +{ + return construct(that, callData); +} + +ReturnedValue ArrayBufferCtor::method_isView(CallContext *ctx) +{ + QV4::Scope scope(ctx); + QV4::Scoped<TypedArray> a(scope, ctx->argument(0)); + if (!!a) + return Encode(true); + QV4::Scoped<DataView> v(scope, ctx->argument(0)); + if (!!v) + return Encode(true); + return Encode(true); +} + + +ArrayBuffer::Data::Data(ExecutionEngine *e, int length) + : Object::Data(e->arrayBufferClass) +{ + data = QTypedArrayData<char>::allocate(length + 1); + if (!data) { + data = 0; + e->currentContext()->throwRangeError(QStringLiteral("ArrayBuffer: out of memory")); + return; + } + data->size = length; + memset(data->data(), 0, length + 1); +} + +QByteArray ArrayBuffer::asByteArray() const +{ + QByteArrayDataPtr ba = { d()->data }; + ba.ptr->ref.ref(); + return QByteArray(ba); +} + +void ArrayBuffer::destroy(Managed *m) +{ + ArrayBuffer *b = static_cast<ArrayBuffer *>(m); + if (!b->d()->data->ref.deref()) + QTypedArrayData<char>::deallocate(b->d()->data); +} + + +void ArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor) +{ + Scope scope(engine); + ScopedObject o(scope); + ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(1)); + ctor->defineReadonlyProperty(engine->id_prototype, (o = this)); + ctor->defineDefaultProperty(QStringLiteral("isView"), ArrayBufferCtor::method_isView, 1); + defineDefaultProperty(engine->id_constructor, (o = ctor)); + defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, 0); + defineDefaultProperty(QStringLiteral("slice"), method_slice, 2); +} + +ReturnedValue ArrayBufferPrototype::method_get_byteLength(CallContext *ctx) +{ + Scope scope(ctx); + Scoped<ArrayBuffer> v(scope, ctx->d()->callData->thisObject); + if (!v) + return ctx->throwTypeError(); + + return Encode(v->d()->data->size); +} + +ReturnedValue ArrayBufferPrototype::method_slice(CallContext *ctx) +{ + Scope scope(ctx); + Scoped<ArrayBuffer> a(scope, ctx->d()->callData->thisObject); + if (!a) + return ctx->throwTypeError(); + + double start = ctx->d()->callData->argc > 0 ? ctx->d()->callData->args[0].toInteger() : 0; + double end = (ctx->d()->callData->argc < 2 || ctx->d()->callData->args[1].isUndefined()) ? + a->d()->data->size : ctx->d()->callData->args[1].toInteger(); + if (scope.engine->hasException) + return Encode::undefined(); + + double first = (start < 0) ? qMax(a->d()->data->size + start, 0.) : qMin(start, (double)a->d()->data->size); + double final = (end < 0) ? qMax(a->d()->data->size + end, 0.) : qMin(end, (double)a->d()->data->size); + + Scoped<FunctionObject> constructor(scope, a->get(scope.engine->id_constructor)); + if (!constructor) + return ctx->throwTypeError(); + + ScopedCallData callData(scope, 1); + double newLen = qMax(final - first, 0.); + callData->args[0] = QV4::Encode(newLen); + QV4::Scoped<ArrayBuffer> newBuffer(scope, constructor->construct(callData)); + if (!newBuffer || newBuffer->d()->data->size < (int)newLen) + return scope.engine->currentContext()->throwTypeError(); + + memcpy(newBuffer->d()->data->data(), a->d()->data->data() + (uint)first, newLen); + + return newBuffer.asReturnedValue(); +} diff --git a/src/qml/jsruntime/qv4arraybuffer_p.h b/src/qml/jsruntime/qv4arraybuffer_p.h new file mode 100644 index 0000000000..57ee34e570 --- /dev/null +++ b/src/qml/jsruntime/qv4arraybuffer_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** 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 QV4ARRAYBUFFER_H +#define QV4ARRAYBUFFER_H + +#include "qv4object_p.h" +#include "qv4functionobject_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct ArrayBufferCtor: 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); + + static ReturnedValue method_isView(CallContext *ctx); + +}; + +struct ArrayBuffer : Object +{ + struct Data : Object::Data { + Data(ExecutionEngine *e, int length); + QTypedArrayData<char> *data; + }; + V4_OBJECT(Object) + + QByteArray asByteArray() const; + uint byteLength() const { return d()->data->size; } + char *data() { + // ### detach if refcount > 1 + return d()->data->data(); + } + const char *constData() { + // ### detach if refcount > 1 + return d()->data->data(); + } + + static void destroy(Managed *m); +}; + +struct ArrayBufferPrototype: Object +{ + void init(ExecutionEngine *engine, Object *ctor); + + static ReturnedValue method_get_byteLength(CallContext *ctx); + static ReturnedValue method_slice(CallContext *ctx); +}; + + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index ea075f9cbd..239bb85167 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -61,6 +61,7 @@ #include "qv4qobjectwrapper_p.h" #include "qv4qmlextensions_p.h" #include "qv4memberdata_p.h" +#include "qv4arraybuffer_p.h" #include <QtCore/QTextStream> #include <QDateTime> @@ -252,6 +253,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) id_toString = newIdentifier(QStringLiteral("toString")); id_destroy = newIdentifier(QStringLiteral("destroy")); id_valueOf = newIdentifier(QStringLiteral("valueOf")); + id_byteLength = newIdentifier(QStringLiteral("byteLength")); memberDataClass = InternalClass::create(this, MemberData::staticVTable(), 0); @@ -363,6 +365,14 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) static_cast<VariantPrototype *>(variantPrototype.getPointer())->init(); static_cast<SequencePrototype *>(sequencePrototype.managed())->init(); + + // typed arrays + + arrayBufferCtor = memoryManager->alloc<ArrayBufferCtor>(rootContext); + Scoped<ArrayBufferPrototype> arrayBufferPrototype(scope, memoryManager->alloc<ArrayBufferPrototype>(arrayBufferClass)); + arrayBufferPrototype->init(this, arrayBufferCtor.asObject()); + arrayBufferClass = InternalClass::create(this, ArrayBuffer::staticVTable(), arrayBufferPrototype); + // // set up the global object // @@ -386,6 +396,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) globalObject->defineDefaultProperty(QStringLiteral("SyntaxError"), syntaxErrorCtor); globalObject->defineDefaultProperty(QStringLiteral("TypeError"), typeErrorCtor); globalObject->defineDefaultProperty(QStringLiteral("URIError"), uRIErrorCtor); + globalObject->defineDefaultProperty(QStringLiteral("ArrayBuffer"), arrayBufferCtor); ScopedObject o(scope); globalObject->defineDefaultProperty(QStringLiteral("Math"), (o = memoryManager->alloc<MathObject>(QV4::InternalClass::create(this, MathObject::staticVTable(), objectPrototype)))); globalObject->defineDefaultProperty(QStringLiteral("JSON"), (o = memoryManager->alloc<JsonObject>(QV4::InternalClass::create(this, JsonObject::staticVTable(), objectPrototype)))); @@ -887,6 +898,7 @@ void ExecutionEngine::markObjects() id_toString->mark(this); id_destroy->mark(this); id_valueOf->mark(this); + id_byteLength->mark(this); objectCtor.mark(this); stringCtor.mark(this); @@ -903,6 +915,7 @@ void ExecutionEngine::markObjects() syntaxErrorCtor.mark(this); typeErrorCtor.mark(this); uRIErrorCtor.mark(this); + arrayBufferCtor.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 a4a40c2f41..d2d36e9e19 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -189,6 +189,7 @@ public: Value typeErrorCtor; Value uRIErrorCtor; Value sequencePrototype; + Value arrayBufferCtor; InternalClassPool *classPool; InternalClass *emptyClass; @@ -224,6 +225,8 @@ public: InternalClass *variantClass; InternalClass *memberDataClass; + InternalClass *arrayBufferClass; + EvalFunction *evalFunction; FunctionObject *thrower; @@ -262,6 +265,7 @@ public: StringValue id_toString; StringValue id_destroy; StringValue id_valueOf; + StringValue id_byteLength; QSet<CompiledData::CompilationUnit*> compilationUnits; |