Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qv4arraybuffer.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 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// Qt-Security score:critical reason:data-parser
4
5// Security note: ArrayBuffer can be used with arbitrary binary data stored in QBA
6// and requires some care (we e.g. don't want to create a corrupted QBA in asByteArray)
7
10#include "qv4dataview_p.h"
11#include "qv4symbol_p.h"
12
13using namespace QV4;
14
19
20void Heap::SharedArrayBufferCtor::init(QV4::ExecutionEngine *engine)
21{
22 Heap::FunctionObject::init(engine, QStringLiteral("SharedArrayBuffer"));
23}
24
25void Heap::ArrayBufferCtor::init(QV4::ExecutionEngine *engine)
26{
27 Heap::FunctionObject::init(engine, QStringLiteral("ArrayBuffer"));
28}
29
30ReturnedValue SharedArrayBufferCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
31{
32 Scope scope(f);
33 if (newTarget->isUndefined())
34 return scope.engine->throwTypeError();
35
36 const double len = argc ? argv[0].toInteger() : 0;
37 if (scope.hasException())
38 return Encode::undefined();
39 if (len < 0 || len >= std::numeric_limits<int>::max())
40 return scope.engine->throwRangeError(QStringLiteral("SharedArrayBuffer: Invalid length."));
41
42 Scoped<SharedArrayBuffer> a(
43 scope, scope.engine->memoryManager->allocate<SharedArrayBuffer>(size_t(len)));
44 if (scope.hasException())
45 return Encode::undefined();
46
47 return a->asReturnedValue();
48}
49
50ReturnedValue SharedArrayBufferCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
51{
52 return f->engine()->throwTypeError();
53}
54
55
56ReturnedValue ArrayBufferCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
57{
58 ExecutionEngine *v4 = f->engine();
59 Scope scope(v4);
60
61 ScopedValue l(scope, argc ? argv[0] : Value::undefinedValue());
62 double dl = l->toInteger();
63 if (v4->hasException)
64 return Encode::undefined();
65 uint len = (uint)qBound(0., dl, (double)UINT_MAX);
66 if (len != dl)
67 return v4->throwRangeError(QLatin1String("ArrayBuffer constructor: invalid length"));
68
69 Scoped<ArrayBuffer> a(scope, v4->newArrayBuffer(len));
70 if (newTarget->heapObject() != f->heapObject() && newTarget->isFunctionObject()) {
71 const FunctionObject *nt = static_cast<const FunctionObject *>(newTarget);
72 ScopedObject o(scope, nt->protoProperty());
73 if (o)
74 a->setPrototypeOf(o);
75 }
76 if (scope.hasException())
77 return Encode::undefined();
78
79 return a->asReturnedValue();
80}
81
82ReturnedValue ArrayBufferCtor::method_isView(const FunctionObject *, const Value *, const Value *argv, int argc)
83{
84 if (argc < 1)
85 return Encode(false);
86
87 if (argv[0].as<TypedArray>() ||
88 argv[0].as<DataView>())
89 return Encode(true);
90
91 return Encode(false);
92}
93
94
95void Heap::SharedArrayBuffer::init(size_t length)
96{
97 Object::init();
98 std::pair<QTypedArrayData<char> *, char *> pair;
99 if (length < UINT_MAX)
100 pair = QTypedArrayData<char>::allocate(length + 1);
101 if (!pair.first) {
102 new (&arrayDataPointerStorage) QArrayDataPointer<char>();
103 internalClass->engine->throwRangeError(QStringLiteral("ArrayBuffer: out of memory"));
104 return;
105 }
106 auto data = new (&arrayDataPointerStorage) QArrayDataPointer<char>{
107 pair.first, pair.second, qsizetype(length) };
108
109 // can't use appendInitialize() because we want to set the terminating '\0'
110 memset(data->data(), 0, length + 1);
111 isShared = true;
112}
113
114void Heap::SharedArrayBuffer::init(const QByteArray& array)
115{
116 Object::init();
117 new (&arrayDataPointerStorage) QArrayDataPointer<char>(*const_cast<QByteArray &>(array).data_ptr());
118 isShared = true;
119}
120
121void Heap::SharedArrayBuffer::destroy()
122{
123 arrayDataPointer().~QArrayDataPointer();
124 Object::destroy();
125}
126
127QByteArray ArrayBuffer::asByteArray() const
128{
129 return QByteArray(constArrayData(), arrayDataLength());
130}
131
132void ArrayBuffer::detach()
133{
134 detachArrayData();
135}
136
137
138void SharedArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor)
139{
140 Scope scope(engine);
141 ScopedObject o(scope);
142 ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(1));
143 ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
144 ctor->addSymbolSpecies();
145
146 defineDefaultProperty(engine->id_constructor(), (o = ctor));
147 defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr);
148 defineDefaultProperty(QStringLiteral("slice"), method_slice, 2);
149 ScopedString name(scope, engine->newString(QStringLiteral("SharedArrayBuffer")));
150 defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name);
151}
152
153ReturnedValue SharedArrayBufferPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int)
154{
155 const SharedArrayBuffer *a = thisObject->as<SharedArrayBuffer>();
156 if (!a || a->hasDetachedArrayData() || !a->isSharedArrayBuffer())
157 return b->engine()->throwTypeError();
158
159 return Encode(a->arrayDataLength());
160}
161
162ReturnedValue SharedArrayBufferPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
163{
164 return slice(b, thisObject, argv, argc, true);
165}
166
167ReturnedValue SharedArrayBufferPrototype::slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc, bool shared)
168{
169 Scope scope(b);
170 const SharedArrayBuffer *a = thisObject->as<SharedArrayBuffer>();
171 if (!a || a->hasDetachedArrayData() || (a->isSharedArrayBuffer() != shared))
172 return scope.engine->throwTypeError();
173
174 const uint aDataLength = a->arrayDataLength();
175
176 double start = argc > 0 ? argv[0].toInteger() : 0;
177 double end = (argc < 2 || argv[1].isUndefined()) ? aDataLength : argv[1].toInteger();
178 if (scope.hasException())
179 return QV4::Encode::undefined();
180
181 double first = (start < 0) ? qMax(aDataLength + start, 0.) : qMin(start, double(aDataLength));
182 double final = (end < 0) ? qMax(aDataLength + end, 0.) : qMin(end, double(aDataLength));
183
184 const FunctionObject *constructor = a->speciesConstructor(scope, shared ? scope.engine->sharedArrayBufferCtor() : scope.engine->arrayBufferCtor());
185 if (!constructor)
186 return scope.engine->throwTypeError();
187
188 double newLen = qMax(final - first, 0.);
189 ScopedValue argument(scope, QV4::Encode(newLen));
190 QV4::Scoped<SharedArrayBuffer> newBuffer(scope, constructor->callAsConstructor(argument, 1));
191 if (!newBuffer || newBuffer->arrayDataLength() < newLen ||
192 newBuffer->hasDetachedArrayData() || (newBuffer->isSharedArrayBuffer() != shared) ||
193 newBuffer->sameValue(*a) ||
194 a->hasDetachedArrayData())
195 return scope.engine->throwTypeError();
196
197 memcpy(newBuffer->arrayData(), a->constArrayData() + (uint)first, newLen);
198 return newBuffer->asReturnedValue();
199}
200
201
202void ArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor)
203{
204 Scope scope(engine);
205 ScopedObject o(scope);
206 ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(1));
207 ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
208 ctor->defineDefaultProperty(QStringLiteral("isView"), ArrayBufferCtor::method_isView, 1);
209 ctor->addSymbolSpecies();
210
211 defineDefaultProperty(engine->id_constructor(), (o = ctor));
212 defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr);
213 defineDefaultProperty(QStringLiteral("slice"), method_slice, 2);
214 defineDefaultProperty(QStringLiteral("toString"), method_toString, 0);
215 ScopedString name(scope, engine->newString(QStringLiteral("ArrayBuffer")));
216 defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name);
217}
218
219ReturnedValue ArrayBufferPrototype::method_get_byteLength(const FunctionObject *f, const Value *thisObject, const Value *, int)
220{
221 const ArrayBuffer *a = thisObject->as<ArrayBuffer>();
222 if (!a || a->isSharedArrayBuffer())
223 return f->engine()->throwTypeError();
224
225 if (a->hasDetachedArrayData())
226 return Encode(0);
227
228 return Encode(a->arrayDataLength());
229}
230
231ReturnedValue ArrayBufferPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
232{
233 return slice(b, thisObject, argv, argc, false);
234}
235
236ReturnedValue ArrayBufferPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int)
237{
238 ExecutionEngine *v4 = b->engine();
239 const ArrayBuffer *a = thisObject->as<ArrayBuffer>();
240 if (!a)
242 return Encode(v4->newString(QString::fromUtf8(a->asByteArray())));
243}
DEFINE_OBJECT_VTABLE(SharedArrayBufferCtor)
DEFINE_OBJECT_VTABLE(ArrayBufferCtor)
DEFINE_OBJECT_VTABLE(ArrayBuffer)
DEFINE_OBJECT_VTABLE(SharedArrayBuffer)
#define RETURN_UNDEFINED()