1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qv4dataview_p.h"
5#include "qv4arraybuffer_p.h"
6#include "qv4symbol_p.h"
7
8#include <QtCore/private/qnumeric_p.h>
9#include "qendian.h"
10
11using namespace QV4;
12
13DEFINE_OBJECT_VTABLE(DataViewCtor);
14DEFINE_OBJECT_VTABLE(DataView);
15
16void Heap::DataViewCtor::init(QV4::ExecutionContext *scope)
17{
18 Heap::FunctionObject::init(scope, QStringLiteral("DataView"));
19}
20
21static uint toIndex(ExecutionEngine *e, const Value &v)
22{
23 if (v.isUndefined())
24 return 0;
25 double index = v.toInteger();
26 if (index < 0) {
27 e->throwRangeError(QStringLiteral("index out of range"));
28 return 0;
29 }
30 uint idx = static_cast<uint>(index);
31 if (idx != index) {
32 e->throwRangeError(QStringLiteral("index out of range"));
33 return 0;
34 }
35 return idx;
36}
37
38ReturnedValue DataViewCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
39{
40 Scope scope(f->engine());
41 Scoped<SharedArrayBuffer> buffer(scope, argc ? argv[0] : Value::undefinedValue());
42 if (!newTarget || !buffer)
43 return scope.engine->throwTypeError();
44
45 uint offset = ::toIndex(e: scope.engine, v: argc > 1 ? argv[1] : Value::undefinedValue());
46 if (scope.hasException())
47 return Encode::undefined();
48 if (buffer->hasDetachedArrayData())
49 return scope.engine->throwTypeError();
50
51 uint bufferLength = buffer->arrayDataLength();
52 if (offset > bufferLength)
53 return scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range"));
54
55 uint byteLength = (argc < 3 || argv[2].isUndefined()) ? (bufferLength - offset) : ::toIndex(e: scope.engine, v: argv[2]);
56 if (scope.hasException())
57 return Encode::undefined();
58 if (offset > bufferLength || byteLength > bufferLength - offset)
59 return scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range"));
60
61 Scoped<DataView> a(scope, scope.engine->memoryManager->allocate<DataView>());
62 a->d()->buffer.set(e: scope.engine, newVal: buffer->d());
63 a->d()->byteLength = byteLength;
64 a->d()->byteOffset = offset;
65 return a.asReturnedValue();
66}
67
68ReturnedValue DataViewCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
69{
70 return f->engine()->throwTypeError();
71}
72
73void DataViewPrototype::init(ExecutionEngine *engine, Object *ctor)
74{
75 Scope scope(engine);
76 ScopedObject o(scope);
77 ctor->defineReadonlyConfigurableProperty(name: engine->id_length(), value: Value::fromInt32(i: 1));
78 ctor->defineReadonlyProperty(name: engine->id_prototype(), value: (o = this));
79 defineDefaultProperty(name: engine->id_constructor(), value: (o = ctor));
80 defineAccessorProperty(QStringLiteral("buffer"), getter: method_get_buffer, setter: nullptr);
81 defineAccessorProperty(QStringLiteral("byteLength"), getter: method_get_byteLength, setter: nullptr);
82 defineAccessorProperty(QStringLiteral("byteOffset"), getter: method_get_byteOffset, setter: nullptr);
83
84 defineDefaultProperty(QStringLiteral("getInt8"), code: method_getChar<signed char>, argumentCount: 1);
85 defineDefaultProperty(QStringLiteral("getUint8"), code: method_getChar<unsigned char>, argumentCount: 1);
86 defineDefaultProperty(QStringLiteral("getInt16"), code: method_get<short>, argumentCount: 1);
87 defineDefaultProperty(QStringLiteral("getUint16"), code: method_get<unsigned short>, argumentCount: 1);
88 defineDefaultProperty(QStringLiteral("getInt32"), code: method_get<int>, argumentCount: 1);
89 defineDefaultProperty(QStringLiteral("getUint32"), code: method_get<unsigned int>, argumentCount: 1);
90 defineDefaultProperty(QStringLiteral("getFloat32"), code: method_getFloat<float>, argumentCount: 1);
91 defineDefaultProperty(QStringLiteral("getFloat64"), code: method_getFloat<double>, argumentCount: 1);
92
93 defineDefaultProperty(QStringLiteral("setInt8"), code: method_setChar<signed char>, argumentCount: 2);
94 defineDefaultProperty(QStringLiteral("setUint8"), code: method_setChar<unsigned char>, argumentCount: 2);
95 defineDefaultProperty(QStringLiteral("setInt16"), code: method_set<short>, argumentCount: 2);
96 defineDefaultProperty(QStringLiteral("setUint16"), code: method_set<unsigned short>, argumentCount: 2);
97 defineDefaultProperty(QStringLiteral("setInt32"), code: method_set<int>, argumentCount: 2);
98 defineDefaultProperty(QStringLiteral("setUint32"), code: method_set<unsigned int>, argumentCount: 2);
99 defineDefaultProperty(QStringLiteral("setFloat32"), code: method_setFloat<float>, argumentCount: 2);
100 defineDefaultProperty(QStringLiteral("setFloat64"), code: method_setFloat<double>, argumentCount: 2);
101
102 ScopedString name(scope, engine->newString(QStringLiteral("DataView")));
103 defineReadonlyConfigurableProperty(name: scope.engine->symbol_toStringTag(), value: name);
104
105 // For backword compatibility
106 defineDefaultProperty(QStringLiteral("getUInt8"), code: method_getChar<unsigned char>, argumentCount: 1);
107 defineDefaultProperty(QStringLiteral("getUInt16"), code: method_get<unsigned short>, argumentCount: 1);
108 defineDefaultProperty(QStringLiteral("getUInt32"), code: method_get<unsigned int>, argumentCount: 1);
109 defineDefaultProperty(QStringLiteral("setUInt8"), code: method_setChar<unsigned char>, argumentCount: 1);
110 defineDefaultProperty(QStringLiteral("setUInt16"), code: method_set<unsigned short>, argumentCount: 1);
111 defineDefaultProperty(QStringLiteral("setUInt32"), code: method_set<unsigned int>, argumentCount: 1);
112}
113
114ReturnedValue DataViewPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int)
115{
116 const DataView *v = thisObject->as<DataView>();
117 if (!v)
118 return b->engine()->throwTypeError();
119
120 return v->d()->buffer->asReturnedValue();
121}
122
123ReturnedValue DataViewPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int)
124{
125 const DataView *v = thisObject->as<DataView>();
126 if (!v)
127 return b->engine()->throwTypeError();
128
129 if (v->d()->buffer->hasDetachedArrayData())
130 return b->engine()->throwTypeError();
131
132 return Encode(v->d()->byteLength);
133}
134
135ReturnedValue DataViewPrototype::method_get_byteOffset(const FunctionObject *b, const Value *thisObject, const Value *, int)
136{
137 const DataView *v = thisObject->as<DataView>();
138 if (!v)
139 return b->engine()->throwTypeError();
140
141 if (v->d()->buffer->hasDetachedArrayData())
142 return b->engine()->throwTypeError();
143
144 return Encode(v->d()->byteOffset);
145}
146
147template <typename T>
148ReturnedValue DataViewPrototype::method_getChar(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
149{
150 ExecutionEngine *e = b->engine();
151 const DataView *v = thisObject->as<DataView>();
152 if (!v)
153 return e->throwTypeError();
154 uint idx = ::toIndex(e, v: argc ? argv[0] : Value::undefinedValue());
155 if (e->hasException)
156 return Encode::undefined();
157 if (v->d()->buffer->hasDetachedArrayData())
158 return e->throwTypeError();
159 if (idx + sizeof(T) > v->d()->byteLength)
160 return e->throwRangeError(QStringLiteral("index out of range"));
161 idx += v->d()->byteOffset;
162
163 T t = T(v->d()->buffer->constArrayData()[idx]);
164
165 return Encode((int)t);
166}
167
168template <typename T>
169ReturnedValue DataViewPrototype::method_get(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
170{
171 ExecutionEngine *e = b->engine();
172 const DataView *v = thisObject->as<DataView>();
173 if (!v)
174 return e->throwTypeError();
175 uint idx = ::toIndex(e, v: argc ? argv[0] : Value::undefinedValue());
176 if (e->hasException)
177 return Encode::undefined();
178 if (v->d()->buffer->hasDetachedArrayData())
179 return e->throwTypeError();
180 if (idx + sizeof(T) > v->d()->byteLength)
181 return e->throwRangeError(QStringLiteral("index out of range"));
182 idx += v->d()->byteOffset;
183
184 bool littleEndian = argc < 2 ? false : argv[1].toBoolean();
185
186 T t = littleEndian
187 ? qFromLittleEndian<T>((const uchar *)v->d()->buffer->constArrayData() + idx)
188 : qFromBigEndian<T>((const uchar *)v->d()->buffer->constArrayData() + idx);
189
190 return Encode(t);
191}
192
193template <typename T>
194ReturnedValue DataViewPrototype::method_getFloat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
195{
196 ExecutionEngine *e = b->engine();
197 const DataView *v = thisObject->as<DataView>();
198 if (!v)
199 return e->throwTypeError();
200 uint idx = ::toIndex(e, v: argc ? argv[0] : Value::undefinedValue());
201 if (e->hasException)
202 return Encode::undefined();
203 if (v->d()->buffer->hasDetachedArrayData())
204 return e->throwTypeError();
205 if (idx + sizeof(T) > v->d()->byteLength)
206 return e->throwRangeError(QStringLiteral("index out of range"));
207 idx += v->d()->byteOffset;
208
209 bool littleEndian = argc < 2 ? false : argv[1].toBoolean();
210
211 if (sizeof(T) == 4) {
212 // float
213 union {
214 uint i;
215 float f;
216 } u;
217 u.i = littleEndian
218 ? qFromLittleEndian<uint>(src: (const uchar *)v->d()->buffer->constArrayData() + idx)
219 : qFromBigEndian<uint>(src: (const uchar *)v->d()->buffer->constArrayData() + idx);
220 return Encode(u.f);
221 } else {
222 Q_ASSERT(sizeof(T) == 8);
223 union {
224 quint64 i;
225 double d;
226 } u;
227 u.i = littleEndian
228 ? qFromLittleEndian<quint64>(src: (const uchar *)v->d()->buffer->constArrayData() + idx)
229 : qFromBigEndian<quint64>(src: (const uchar *)v->d()->buffer->constArrayData() + idx);
230 return Encode(u.d);
231 }
232}
233
234template <typename T>
235ReturnedValue DataViewPrototype::method_setChar(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
236{
237 ExecutionEngine *e = b->engine();
238 const DataView *v = thisObject->as<DataView>();
239 if (!v)
240 return e->throwTypeError();
241 uint idx = ::toIndex(e, v: argc ? argv[0] : Value::undefinedValue());
242 if (e->hasException)
243 return Encode::undefined();
244
245 int val = argc >= 2 ? argv[1].toInt32() : 0;
246
247 if (v->d()->buffer->hasDetachedArrayData())
248 return e->throwTypeError();
249
250 if (idx + sizeof(T) > v->d()->byteLength)
251 return e->throwRangeError(QStringLiteral("index out of range"));
252 idx += v->d()->byteOffset;
253
254 v->d()->buffer->arrayData()[idx] = (char)val;
255
256 RETURN_UNDEFINED();
257}
258
259template <typename T>
260ReturnedValue DataViewPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
261{
262 ExecutionEngine *e = b->engine();
263 const DataView *v = thisObject->as<DataView>();
264 if (!v)
265 return e->throwTypeError();
266 uint idx = ::toIndex(e, v: argc ? argv[0] : Value::undefinedValue());
267 if (e->hasException)
268 return Encode::undefined();
269
270 int val = argc >= 2 ? argv[1].toInt32() : 0;
271 bool littleEndian = argc < 3 ? false : argv[2].toBoolean();
272
273 if (v->d()->buffer->hasDetachedArrayData())
274 return e->throwTypeError();
275
276 if (idx + sizeof(T) > v->d()->byteLength)
277 return e->throwRangeError(QStringLiteral("index out of range"));
278 idx += v->d()->byteOffset;
279
280
281 if (littleEndian)
282 qToLittleEndian<T>(val, (uchar *)v->d()->buffer->arrayData() + idx);
283 else
284 qToBigEndian<T>(val, (uchar *)v->d()->buffer->arrayData() + idx);
285
286 RETURN_UNDEFINED();
287}
288
289template <typename T>
290ReturnedValue DataViewPrototype::method_setFloat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
291{
292 ExecutionEngine *e = b->engine();
293 const DataView *v = thisObject->as<DataView>();
294 if (!v)
295 return e->throwTypeError();
296 uint idx = ::toIndex(e, v: argc ? argv[0] : Value::undefinedValue());
297 if (e->hasException)
298 return Encode::undefined();
299
300 double val = argc >= 2 ? argv[1].toNumber() : qt_qnan();
301 bool littleEndian = argc < 3 ? false : argv[2].toBoolean();
302
303 if (v->d()->buffer->hasDetachedArrayData())
304 return e->throwTypeError();
305
306 if (idx + sizeof(T) > v->d()->byteLength)
307 return e->throwRangeError(QStringLiteral("index out of range"));
308 idx += v->d()->byteOffset;
309
310 if (sizeof(T) == 4) {
311 // float
312 union {
313 uint i;
314 float f;
315 } u;
316 u.f = val;
317 if (littleEndian)
318 qToLittleEndian(u.i, (uchar *)v->d()->buffer->arrayData() + idx);
319 else
320 qToBigEndian(u.i, (uchar *)v->d()->buffer->arrayData() + idx);
321 } else {
322 Q_ASSERT(sizeof(T) == 8);
323 union {
324 quint64 i;
325 double d;
326 } u;
327 u.d = val;
328 if (littleEndian)
329 qToLittleEndian(u.i, (uchar *)v->d()->buffer->arrayData() + idx);
330 else
331 qToBigEndian(u.i, (uchar *)v->d()->buffer->arrayData() + idx);
332 }
333 RETURN_UNDEFINED();
334}
335

source code of qtdeclarative/src/qml/jsruntime/qv4dataview.cpp