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 | |
8 | using namespace QV4; |
9 | |
10 | DEFINE_OBJECT_VTABLE(SharedArrayBufferCtor); |
11 | DEFINE_OBJECT_VTABLE(ArrayBufferCtor); |
12 | DEFINE_OBJECT_VTABLE(SharedArrayBuffer); |
13 | DEFINE_OBJECT_VTABLE(ArrayBuffer); |
14 | |
15 | void Heap::SharedArrayBufferCtor::init(QV4::ExecutionContext *scope) |
16 | { |
17 | Heap::FunctionObject::init(scope, QStringLiteral("SharedArrayBuffer" )); |
18 | } |
19 | |
20 | void Heap::ArrayBufferCtor::init(QV4::ExecutionContext *scope) |
21 | { |
22 | Heap::FunctionObject::init(scope, QStringLiteral("ArrayBuffer" )); |
23 | } |
24 | |
25 | ReturnedValue 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 | |
45 | ReturnedValue SharedArrayBufferCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int) |
46 | { |
47 | return f->engine()->throwTypeError(); |
48 | } |
49 | |
50 | |
51 | ReturnedValue 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 | |
77 | ReturnedValue 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 | |
90 | void 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 | |
109 | void 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 | |
116 | void Heap::SharedArrayBuffer::destroy() |
117 | { |
118 | arrayDataPointer().~QArrayDataPointer(); |
119 | Object::destroy(); |
120 | } |
121 | |
122 | QByteArray ArrayBuffer::asByteArray() const |
123 | { |
124 | return QByteArray(constArrayData(), arrayDataLength()); |
125 | } |
126 | |
127 | void ArrayBuffer::detach() |
128 | { |
129 | detachArrayData(); |
130 | } |
131 | |
132 | |
133 | void 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 | |
148 | ReturnedValue 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 | |
157 | ReturnedValue 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 | |
162 | ReturnedValue 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 | |
197 | void 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 | |
214 | ReturnedValue 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 | |
226 | ReturnedValue 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 | |
231 | ReturnedValue 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 | |