1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qv4dataview_p.h"
41 #include "qv4arraybuffer_p.h"
42 #include "qv4string_p.h"
43 #include "qv4symbol_p.h"
44 
45 #include <QtCore/private/qnumeric_p.h>
46 #include "qendian.h"
47 
48 using namespace QV4;
49 
50 DEFINE_OBJECT_VTABLE(DataViewCtor);
51 DEFINE_OBJECT_VTABLE(DataView);
52 
init(QV4::ExecutionContext * scope)53 void Heap::DataViewCtor::init(QV4::ExecutionContext *scope)
54 {
55     Heap::FunctionObject::init(scope, QStringLiteral("DataView"));
56 }
57 
toIndex(ExecutionEngine * e,const Value & v)58 static uint toIndex(ExecutionEngine *e, const Value &v)
59 {
60     if (v.isUndefined())
61         return 0;
62     double index = v.toInteger();
63     if (index < 0) {
64         e->throwRangeError(QStringLiteral("index out of range"));
65         return 0;
66     }
67     uint idx = static_cast<uint>(index);
68     if (idx != index) {
69         e->throwRangeError(QStringLiteral("index out of range"));
70         return 0;
71     }
72     return idx;
73 }
74 
virtualCallAsConstructor(const FunctionObject * f,const Value * argv,int argc,const Value * newTarget)75 ReturnedValue DataViewCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
76 {
77     Scope scope(f->engine());
78     Scoped<SharedArrayBuffer> buffer(scope, argc ? argv[0] : Value::undefinedValue());
79     if (!newTarget || !buffer)
80         return scope.engine->throwTypeError();
81 
82     uint offset = ::toIndex(scope.engine, argc > 1 ? argv[1]: Value::undefinedValue());
83     if (scope.hasException())
84         return Encode::undefined();
85     if (buffer->isDetachedBuffer())
86         return scope.engine->throwTypeError();
87 
88     uint bufferLength = buffer->d()->data->size;
89     if (offset > bufferLength)
90         return scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range"));
91 
92     uint byteLength = (argc < 3 || argv[2].isUndefined()) ? (bufferLength - offset) : ::toIndex(scope.engine, argv[2]);
93     if (scope.hasException())
94         return Encode::undefined();
95     if (offset > bufferLength ||  byteLength > bufferLength - offset)
96         return scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range"));
97 
98     Scoped<DataView> a(scope, scope.engine->memoryManager->allocate<DataView>());
99     a->d()->buffer.set(scope.engine, buffer->d());
100     a->d()->byteLength = byteLength;
101     a->d()->byteOffset = offset;
102     return a.asReturnedValue();
103 }
104 
virtualCall(const FunctionObject * f,const Value *,const Value *,int)105 ReturnedValue DataViewCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
106 {
107     return f->engine()->throwTypeError();
108 }
109 
init(ExecutionEngine * engine,Object * ctor)110 void DataViewPrototype::init(ExecutionEngine *engine, Object *ctor)
111 {
112     Scope scope(engine);
113     ScopedObject o(scope);
114     ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(1));
115     ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
116     defineDefaultProperty(engine->id_constructor(), (o = ctor));
117     defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr);
118     defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr);
119     defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, nullptr);
120 
121     defineDefaultProperty(QStringLiteral("getInt8"), method_getChar<signed char>, 1);
122     defineDefaultProperty(QStringLiteral("getUint8"), method_getChar<unsigned char>, 1);
123     defineDefaultProperty(QStringLiteral("getInt16"), method_get<short>, 1);
124     defineDefaultProperty(QStringLiteral("getUint16"), method_get<unsigned short>, 1);
125     defineDefaultProperty(QStringLiteral("getInt32"), method_get<int>, 1);
126     defineDefaultProperty(QStringLiteral("getUint32"), method_get<unsigned int>, 1);
127     defineDefaultProperty(QStringLiteral("getFloat32"), method_getFloat<float>, 1);
128     defineDefaultProperty(QStringLiteral("getFloat64"), method_getFloat<double>, 1);
129 
130     defineDefaultProperty(QStringLiteral("setInt8"), method_setChar<signed char>, 2);
131     defineDefaultProperty(QStringLiteral("setUint8"), method_setChar<unsigned char>, 2);
132     defineDefaultProperty(QStringLiteral("setInt16"), method_set<short>, 2);
133     defineDefaultProperty(QStringLiteral("setUint16"), method_set<unsigned short>, 2);
134     defineDefaultProperty(QStringLiteral("setInt32"), method_set<int>, 2);
135     defineDefaultProperty(QStringLiteral("setUint32"), method_set<unsigned int>, 2);
136     defineDefaultProperty(QStringLiteral("setFloat32"), method_setFloat<float>, 2);
137     defineDefaultProperty(QStringLiteral("setFloat64"), method_setFloat<double>, 2);
138 
139     ScopedString name(scope, engine->newString(QStringLiteral("DataView")));
140     defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name);
141 
142     // For backword compatibility
143     defineDefaultProperty(QStringLiteral("getUInt8"), method_getChar<unsigned char>, 1);
144     defineDefaultProperty(QStringLiteral("getUInt16"), method_get<unsigned short>, 1);
145     defineDefaultProperty(QStringLiteral("getUInt32"), method_get<unsigned int>, 1);
146     defineDefaultProperty(QStringLiteral("setUInt8"), method_setChar<unsigned char>, 1);
147     defineDefaultProperty(QStringLiteral("setUInt16"), method_set<unsigned short>, 1);
148     defineDefaultProperty(QStringLiteral("setUInt32"), method_set<unsigned int>, 1);
149 }
150 
method_get_buffer(const FunctionObject * b,const Value * thisObject,const Value *,int)151 ReturnedValue DataViewPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int)
152 {
153     const DataView *v = thisObject->as<DataView>();
154     if (!v)
155         return b->engine()->throwTypeError();
156 
157     return v->d()->buffer->asReturnedValue();
158 }
159 
method_get_byteLength(const FunctionObject * b,const Value * thisObject,const Value *,int)160 ReturnedValue DataViewPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int)
161 {
162     const DataView *v = thisObject->as<DataView>();
163     if (!v)
164         return b->engine()->throwTypeError();
165 
166     if (v->d()->buffer->isDetachedBuffer())
167         return b->engine()->throwTypeError();
168 
169     return Encode(v->d()->byteLength);
170 }
171 
method_get_byteOffset(const FunctionObject * b,const Value * thisObject,const Value *,int)172 ReturnedValue DataViewPrototype::method_get_byteOffset(const FunctionObject *b, const Value *thisObject, const Value *, int)
173 {
174     const DataView *v = thisObject->as<DataView>();
175     if (!v)
176         return b->engine()->throwTypeError();
177 
178     if (v->d()->buffer->isDetachedBuffer())
179         return b->engine()->throwTypeError();
180 
181     return Encode(v->d()->byteOffset);
182 }
183 
184 template <typename T>
method_getChar(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)185 ReturnedValue DataViewPrototype::method_getChar(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
186 {
187     ExecutionEngine *e = b->engine();
188     const DataView *v = thisObject->as<DataView>();
189     if (!v)
190         return e->throwTypeError();
191     uint idx = ::toIndex(e, argc ? argv[0] : Value::undefinedValue());
192     if (e->hasException)
193         return Encode::undefined();
194     if (v->d()->buffer->isDetachedBuffer())
195         return e->throwTypeError();
196     if (idx + sizeof(T) > v->d()->byteLength)
197         return e->throwRangeError(QStringLiteral("index out of range"));
198     idx += v->d()->byteOffset;
199 
200     T t = T(v->d()->buffer->data->data()[idx]);
201 
202     return Encode((int)t);
203 }
204 
205 template <typename T>
method_get(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)206 ReturnedValue DataViewPrototype::method_get(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
207 {
208     ExecutionEngine *e = b->engine();
209     const DataView *v = thisObject->as<DataView>();
210     if (!v)
211         return e->throwTypeError();
212     uint idx = ::toIndex(e, argc ? argv[0] : Value::undefinedValue());
213     if (e->hasException)
214         return Encode::undefined();
215     if (v->d()->buffer->isDetachedBuffer())
216         return e->throwTypeError();
217     if (idx + sizeof(T) > v->d()->byteLength)
218         return e->throwRangeError(QStringLiteral("index out of range"));
219     idx += v->d()->byteOffset;
220 
221     bool littleEndian = argc < 2 ? false : argv[1].toBoolean();
222 
223     T t = littleEndian
224             ? qFromLittleEndian<T>((uchar *)v->d()->buffer->data->data() + idx)
225             : qFromBigEndian<T>((uchar *)v->d()->buffer->data->data() + idx);
226 
227     return Encode(t);
228 }
229 
230 template <typename T>
method_getFloat(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)231 ReturnedValue DataViewPrototype::method_getFloat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
232 {
233     ExecutionEngine *e = b->engine();
234     const DataView *v = thisObject->as<DataView>();
235     if (!v)
236         return e->throwTypeError();
237     uint idx = ::toIndex(e, argc ? argv[0] : Value::undefinedValue());
238     if (e->hasException)
239         return Encode::undefined();
240     if (v->d()->buffer->isDetachedBuffer())
241         return e->throwTypeError();
242     if (idx + sizeof(T) > v->d()->byteLength)
243         return e->throwRangeError(QStringLiteral("index out of range"));
244     idx += v->d()->byteOffset;
245 
246     bool littleEndian = argc < 2 ? false : argv[1].toBoolean();
247 
248     if (sizeof(T) == 4) {
249         // float
250         union {
251             uint i;
252             float f;
253         } u;
254         u.i = littleEndian
255                 ? qFromLittleEndian<uint>((uchar *)v->d()->buffer->data->data() + idx)
256                 : qFromBigEndian<uint>((uchar *)v->d()->buffer->data->data() + idx);
257         return Encode(u.f);
258     } else {
259         Q_ASSERT(sizeof(T) == 8);
260         union {
261             quint64 i;
262             double d;
263         } u;
264         u.i = littleEndian
265                 ? qFromLittleEndian<quint64>((uchar *)v->d()->buffer->data->data() + idx)
266                 : qFromBigEndian<quint64>((uchar *)v->d()->buffer->data->data() + idx);
267         return Encode(u.d);
268     }
269 }
270 
271 template <typename T>
method_setChar(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)272 ReturnedValue DataViewPrototype::method_setChar(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
273 {
274     ExecutionEngine *e = b->engine();
275     const DataView *v = thisObject->as<DataView>();
276     if (!v)
277         return e->throwTypeError();
278     uint idx = ::toIndex(e, argc ? argv[0] : Value::undefinedValue());
279     if (e->hasException)
280         return Encode::undefined();
281 
282     int val = argc >= 2 ? argv[1].toInt32() : 0;
283 
284     if (v->d()->buffer->isDetachedBuffer())
285         return e->throwTypeError();
286 
287     if (idx + sizeof(T) > v->d()->byteLength)
288         return e->throwRangeError(QStringLiteral("index out of range"));
289     idx += v->d()->byteOffset;
290 
291     v->d()->buffer->data->data()[idx] = (char)val;
292 
293     RETURN_UNDEFINED();
294 }
295 
296 template <typename T>
method_set(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)297 ReturnedValue DataViewPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
298 {
299     ExecutionEngine *e = b->engine();
300     const DataView *v = thisObject->as<DataView>();
301     if (!v)
302         return e->throwTypeError();
303     uint idx = ::toIndex(e, argc ? argv[0] : Value::undefinedValue());
304     if (e->hasException)
305         return Encode::undefined();
306 
307     int val = argc >= 2 ? argv[1].toInt32() : 0;
308     bool littleEndian = argc < 3 ? false : argv[2].toBoolean();
309 
310     if (v->d()->buffer->isDetachedBuffer())
311         return e->throwTypeError();
312 
313     if (idx + sizeof(T) > v->d()->byteLength)
314         return e->throwRangeError(QStringLiteral("index out of range"));
315     idx += v->d()->byteOffset;
316 
317 
318     if (littleEndian)
319         qToLittleEndian<T>(val, (uchar *)v->d()->buffer->data->data() + idx);
320     else
321         qToBigEndian<T>(val, (uchar *)v->d()->buffer->data->data() + idx);
322 
323     RETURN_UNDEFINED();
324 }
325 
326 template <typename T>
method_setFloat(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)327 ReturnedValue DataViewPrototype::method_setFloat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
328 {
329     ExecutionEngine *e = b->engine();
330     const DataView *v = thisObject->as<DataView>();
331     if (!v)
332         return e->throwTypeError();
333     uint idx = ::toIndex(e, argc ? argv[0] : Value::undefinedValue());
334     if (e->hasException)
335         return Encode::undefined();
336 
337     double val = argc >= 2 ? argv[1].toNumber() : qt_qnan();
338     bool littleEndian = argc < 3 ? false : argv[2].toBoolean();
339 
340     if (v->d()->buffer->isDetachedBuffer())
341         return e->throwTypeError();
342 
343     if (idx + sizeof(T) > v->d()->byteLength)
344         return e->throwRangeError(QStringLiteral("index out of range"));
345     idx += v->d()->byteOffset;
346 
347     if (sizeof(T) == 4) {
348         // float
349         union {
350             uint i;
351             float f;
352         } u;
353         u.f = val;
354         if (littleEndian)
355             qToLittleEndian(u.i, (uchar *)v->d()->buffer->data->data() + idx);
356         else
357             qToBigEndian(u.i, (uchar *)v->d()->buffer->data->data() + idx);
358     } else {
359         Q_ASSERT(sizeof(T) == 8);
360         union {
361             quint64 i;
362             double d;
363         } u;
364         u.d = val;
365         if (littleEndian)
366             qToLittleEndian(u.i, (uchar *)v->d()->buffer->data->data() + idx);
367         else
368             qToBigEndian(u.i, (uchar *)v->d()->buffer->data->data() + idx);
369     }
370     RETURN_UNDEFINED();
371 }
372