blob: da1b91e69ab169dd43e29dc3210794800bcc0663 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** 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 The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qv4dataview_p.h"
#include "qv4arraybuffer_p.h"
#include "qv4string_p.h"
#include "qv4symbol_p.h"
#include <QtCore/private/qnumeric_p.h>
#include "qendian.h"
using namespace QV4;
DEFINE_OBJECT_VTABLE(DataViewCtor);
DEFINE_OBJECT_VTABLE(DataView);
void Heap::DataViewCtor::init(QV4::ExecutionContext *scope)
{
Heap::FunctionObject::init(scope, QStringLiteral("DataView"));
}
static uint toIndex(ExecutionEngine *e, const Value &v)
{
if (v.isUndefined())
return 0;
double index = v.toInteger();
if (index < 0) {
e->throwRangeError(QStringLiteral("index out of range"));
return 0;
}
uint idx = static_cast<uint>(index);
if (idx != index) {
e->throwRangeError(QStringLiteral("index out of range"));
return 0;
}
return idx;
}
ReturnedValue DataViewCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
{
Scope scope(f->engine());
Scoped<SharedArrayBuffer> buffer(scope, argc ? argv[0] : Value::undefinedValue());
if (!newTarget || !buffer)
return scope.engine->throwTypeError();
uint offset = ::toIndex(scope.engine, argc > 1 ? argv[1]: Value::undefinedValue());
if (scope.hasException())
return Encode::undefined();
if (buffer->isDetachedBuffer())
return scope.engine->throwTypeError();
uint bufferLength = buffer->d()->data->size;
if (offset > bufferLength)
return scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range"));
uint byteLength = (argc < 3 || argv[2].isUndefined()) ? (bufferLength - offset) : ::toIndex(scope.engine, argv[2]);
if (scope.hasException())
return Encode::undefined();
if (offset > bufferLength || byteLength > bufferLength - offset)
return scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range"));
Scoped<DataView> a(scope, scope.engine->memoryManager->allocate<DataView>());
a->d()->buffer.set(scope.engine, buffer->d());
a->d()->byteLength = byteLength;
a->d()->byteOffset = offset;
return a.asReturnedValue();
}
ReturnedValue DataViewCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
{
return f->engine()->throwTypeError();
}
void DataViewPrototype::init(ExecutionEngine *engine, Object *ctor)
{
Scope scope(engine);
ScopedObject o(scope);
ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(1));
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
defineDefaultProperty(engine->id_constructor(), (o = ctor));
defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr);
defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr);
defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, nullptr);
defineDefaultProperty(QStringLiteral("getInt8"), method_getChar<signed char>, 1);
defineDefaultProperty(QStringLiteral("getUint8"), method_getChar<unsigned char>, 1);
defineDefaultProperty(QStringLiteral("getInt16"), method_get<short>, 1);
defineDefaultProperty(QStringLiteral("getUint16"), method_get<unsigned short>, 1);
defineDefaultProperty(QStringLiteral("getInt32"), method_get<int>, 1);
defineDefaultProperty(QStringLiteral("getUint32"), method_get<unsigned int>, 1);
defineDefaultProperty(QStringLiteral("getFloat32"), method_getFloat<float>, 1);
defineDefaultProperty(QStringLiteral("getFloat64"), method_getFloat<double>, 1);
defineDefaultProperty(QStringLiteral("setInt8"), method_setChar<signed char>, 2);
defineDefaultProperty(QStringLiteral("setUint8"), method_setChar<unsigned char>, 2);
defineDefaultProperty(QStringLiteral("setInt16"), method_set<short>, 2);
defineDefaultProperty(QStringLiteral("setUint16"), method_set<unsigned short>, 2);
defineDefaultProperty(QStringLiteral("setInt32"), method_set<int>, 2);
defineDefaultProperty(QStringLiteral("setUint32"), method_set<unsigned int>, 2);
defineDefaultProperty(QStringLiteral("setFloat32"), method_setFloat<float>, 2);
defineDefaultProperty(QStringLiteral("setFloat64"), method_setFloat<double>, 2);
ScopedString name(scope, engine->newString(QStringLiteral("DataView")));
defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name);
// For backword compatibility
defineDefaultProperty(QStringLiteral("getUInt8"), method_getChar<unsigned char>, 1);
defineDefaultProperty(QStringLiteral("getUInt16"), method_get<unsigned short>, 1);
defineDefaultProperty(QStringLiteral("getUInt32"), method_get<unsigned int>, 1);
defineDefaultProperty(QStringLiteral("setUInt8"), method_setChar<unsigned char>, 1);
defineDefaultProperty(QStringLiteral("setUInt16"), method_set<unsigned short>, 1);
defineDefaultProperty(QStringLiteral("setUInt32"), method_set<unsigned int>, 1);
}
ReturnedValue DataViewPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
const DataView *v = thisObject->as<DataView>();
if (!v)
return b->engine()->throwTypeError();
return v->d()->buffer->asReturnedValue();
}
ReturnedValue DataViewPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
const DataView *v = thisObject->as<DataView>();
if (!v)
return b->engine()->throwTypeError();
if (v->d()->buffer->isDetachedBuffer())
return b->engine()->throwTypeError();
return Encode(v->d()->byteLength);
}
ReturnedValue DataViewPrototype::method_get_byteOffset(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
const DataView *v = thisObject->as<DataView>();
if (!v)
return b->engine()->throwTypeError();
if (v->d()->buffer->isDetachedBuffer())
return b->engine()->throwTypeError();
return Encode(v->d()->byteOffset);
}
template <typename T>
ReturnedValue DataViewPrototype::method_getChar(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
ExecutionEngine *e = b->engine();
const DataView *v = thisObject->as<DataView>();
if (!v)
return e->throwTypeError();
uint idx = ::toIndex(e, argc ? argv[0] : Value::undefinedValue());
if (e->hasException)
return Encode::undefined();
if (v->d()->buffer->isDetachedBuffer())
return e->throwTypeError();
if (idx + sizeof(T) > v->d()->byteLength)
return e->throwRangeError(QStringLiteral("index out of range"));
idx += v->d()->byteOffset;
T t = T(v->d()->buffer->data->data()[idx]);
return Encode((int)t);
}
template <typename T>
ReturnedValue DataViewPrototype::method_get(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
ExecutionEngine *e = b->engine();
const DataView *v = thisObject->as<DataView>();
if (!v)
return e->throwTypeError();
uint idx = ::toIndex(e, argc ? argv[0] : Value::undefinedValue());
if (e->hasException)
return Encode::undefined();
if (v->d()->buffer->isDetachedBuffer())
return e->throwTypeError();
if (idx + sizeof(T) > v->d()->byteLength)
return e->throwRangeError(QStringLiteral("index out of range"));
idx += v->d()->byteOffset;
bool littleEndian = argc < 2 ? false : argv[1].toBoolean();
T t = littleEndian
? qFromLittleEndian<T>((uchar *)v->d()->buffer->data->data() + idx)
: qFromBigEndian<T>((uchar *)v->d()->buffer->data->data() + idx);
return Encode(t);
}
template <typename T>
ReturnedValue DataViewPrototype::method_getFloat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
ExecutionEngine *e = b->engine();
const DataView *v = thisObject->as<DataView>();
if (!v)
return e->throwTypeError();
uint idx = ::toIndex(e, argc ? argv[0] : Value::undefinedValue());
if (e->hasException)
return Encode::undefined();
if (v->d()->buffer->isDetachedBuffer())
return e->throwTypeError();
if (idx + sizeof(T) > v->d()->byteLength)
return e->throwRangeError(QStringLiteral("index out of range"));
idx += v->d()->byteOffset;
bool littleEndian = argc < 2 ? false : argv[1].toBoolean();
if (sizeof(T) == 4) {
// float
union {
uint i;
float f;
} u;
u.i = littleEndian
? qFromLittleEndian<uint>((uchar *)v->d()->buffer->data->data() + idx)
: qFromBigEndian<uint>((uchar *)v->d()->buffer->data->data() + idx);
return Encode(u.f);
} else {
Q_ASSERT(sizeof(T) == 8);
union {
quint64 i;
double d;
} u;
u.i = littleEndian
? qFromLittleEndian<quint64>((uchar *)v->d()->buffer->data->data() + idx)
: qFromBigEndian<quint64>((uchar *)v->d()->buffer->data->data() + idx);
return Encode(u.d);
}
}
template <typename T>
ReturnedValue DataViewPrototype::method_setChar(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
ExecutionEngine *e = b->engine();
const DataView *v = thisObject->as<DataView>();
if (!v)
return e->throwTypeError();
uint idx = ::toIndex(e, argc ? argv[0] : Value::undefinedValue());
if (e->hasException)
return Encode::undefined();
int val = argc >= 2 ? argv[1].toInt32() : 0;
if (v->d()->buffer->isDetachedBuffer())
return e->throwTypeError();
if (idx + sizeof(T) > v->d()->byteLength)
return e->throwRangeError(QStringLiteral("index out of range"));
idx += v->d()->byteOffset;
v->d()->buffer->data->data()[idx] = (char)val;
RETURN_UNDEFINED();
}
template <typename T>
ReturnedValue DataViewPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
ExecutionEngine *e = b->engine();
const DataView *v = thisObject->as<DataView>();
if (!v)
return e->throwTypeError();
uint idx = ::toIndex(e, argc ? argv[0] : Value::undefinedValue());
if (e->hasException)
return Encode::undefined();
int val = argc >= 2 ? argv[1].toInt32() : 0;
bool littleEndian = argc < 3 ? false : argv[2].toBoolean();
if (v->d()->buffer->isDetachedBuffer())
return e->throwTypeError();
if (idx + sizeof(T) > v->d()->byteLength)
return e->throwRangeError(QStringLiteral("index out of range"));
idx += v->d()->byteOffset;
if (littleEndian)
qToLittleEndian<T>(val, (uchar *)v->d()->buffer->data->data() + idx);
else
qToBigEndian<T>(val, (uchar *)v->d()->buffer->data->data() + idx);
RETURN_UNDEFINED();
}
template <typename T>
ReturnedValue DataViewPrototype::method_setFloat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
ExecutionEngine *e = b->engine();
const DataView *v = thisObject->as<DataView>();
if (!v)
return e->throwTypeError();
uint idx = ::toIndex(e, argc ? argv[0] : Value::undefinedValue());
if (e->hasException)
return Encode::undefined();
double val = argc >= 2 ? argv[1].toNumber() : qt_qnan();
bool littleEndian = argc < 3 ? false : argv[2].toBoolean();
if (v->d()->buffer->isDetachedBuffer())
return e->throwTypeError();
if (idx + sizeof(T) > v->d()->byteLength)
return e->throwRangeError(QStringLiteral("index out of range"));
idx += v->d()->byteOffset;
if (sizeof(T) == 4) {
// float
union {
uint i;
float f;
} u;
u.f = val;
if (littleEndian)
qToLittleEndian(u.i, (uchar *)v->d()->buffer->data->data() + idx);
else
qToBigEndian(u.i, (uchar *)v->d()->buffer->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->data->data() + idx);
else
qToBigEndian(u.i, (uchar *)v->d()->buffer->data->data() + idx);
}
RETURN_UNDEFINED();
}