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

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