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 "qv4atomics_p.h"
6#include "qv4symbol_p.h"
7
8using namespace QV4;
9
10DEFINE_OBJECT_VTABLE(Atomics);
11
12void Heap::Atomics::init()
13{
14 Object::init();
15 Scope scope(internalClass->engine);
16 ScopedObject m(scope, this);
17
18 m->defineDefaultProperty(QStringLiteral("add"), code: QV4::Atomics::method_add, argumentCount: 3);
19 m->defineDefaultProperty(QStringLiteral("and"), code: QV4::Atomics::method_and, argumentCount: 3);
20 m->defineDefaultProperty(QStringLiteral("compareExchange"), code: QV4::Atomics::method_compareExchange, argumentCount: 4);
21 m->defineDefaultProperty(QStringLiteral("exchange"), code: QV4::Atomics::method_exchange, argumentCount: 3);
22 m->defineDefaultProperty(QStringLiteral("isLockFree"), code: QV4::Atomics::method_isLockFree, argumentCount: 1);
23 m->defineDefaultProperty(QStringLiteral("load"), code: QV4::Atomics::method_load, argumentCount: 2);
24 m->defineDefaultProperty(QStringLiteral("or"), code: QV4::Atomics::method_or, argumentCount: 3);
25 m->defineDefaultProperty(QStringLiteral("store"), code: QV4::Atomics::method_store, argumentCount: 3);
26 m->defineDefaultProperty(QStringLiteral("sub"), code: QV4::Atomics::method_sub, argumentCount: 3);
27 m->defineDefaultProperty(QStringLiteral("wait"), code: QV4::Atomics::method_wait, argumentCount: 4);
28 m->defineDefaultProperty(QStringLiteral("wake"), code: QV4::Atomics::method_wake, argumentCount: 3);
29 m->defineDefaultProperty(QStringLiteral("xor"), code: QV4::Atomics::method_xor, argumentCount: 3);
30
31 ScopedString name(scope, scope.engine->newString(QStringLiteral("Atomics")));
32 m->defineReadonlyConfigurableProperty(name: scope.engine->symbol_toStringTag(), value: name);
33}
34
35static SharedArrayBuffer *validateSharedIntegerTypedArray(Scope &scope, const Value &typedArray, bool onlyInt32 = false)
36{
37 const TypedArray *a = typedArray.as<TypedArray>();
38 if (!a) {
39 scope.engine->throwTypeError();
40 return nullptr;
41 }
42
43 TypedArrayType t(a->arrayType());
44 if (!a->d()->type->atomicLoad || (onlyInt32 && t != TypedArrayType::Int32Array)) {
45 scope.engine->throwTypeError();
46 return nullptr;
47 }
48
49 Scoped<SharedArrayBuffer> buffer(scope, a->d()->buffer);
50 if (!buffer->isSharedArrayBuffer()) {
51 scope.engine->throwTypeError();
52 return nullptr;
53 }
54 Q_ASSERT(!buffer->hasDetachedArrayData());
55 return buffer;
56}
57
58static int validateAtomicAccess(Scope &scope, const TypedArray &typedArray, const Value &index)
59{
60 const TypedArray &a = static_cast<const TypedArray &>(typedArray);
61 qint64 idx = index.toIndex();
62 if (scope.hasException())
63 return -1;
64 if (idx < 0 || idx >= a.length()) {
65 scope.engine->throwRangeError(QStringLiteral("index out of range."));
66 return -1;
67 }
68 return static_cast<int>(idx);
69}
70
71ReturnedValue atomicReadModifyWrite(const FunctionObject *f, const Value *argv, int argc, AtomicModifyOps modify)
72{
73 Scope scope(f);
74 if (!argc)
75 return scope.engine->throwTypeError();
76
77 SharedArrayBuffer *buffer = validateSharedIntegerTypedArray(scope, typedArray: argv[0]);
78 if (!buffer)
79 return Encode::undefined();
80 const TypedArray &a = static_cast<const TypedArray &>(argv[0]);
81 int index = validateAtomicAccess(scope, typedArray: a, index: argc > 1 ? argv[1] : Value::undefinedValue());
82 if (index < 0)
83 return Encode::undefined();
84
85 Value v = Value::fromReturnedValue(val: (argc > 2 ? argv[2] : Value::undefinedValue()).convertedToNumber());
86 if (scope.hasException())
87 return Encode::undefined();
88
89 int bytesPerElement = a.d()->type->bytesPerElement;
90 int byteOffset = a.d()->byteOffset + index * bytesPerElement;
91
92 return a.d()->type->atomicModifyOps[modify](buffer->arrayData() + byteOffset, v);
93}
94
95ReturnedValue Atomics::method_add(const FunctionObject *f, const Value *, const Value *argv, int argc)
96{
97 return atomicReadModifyWrite(f, argv, argc, modify: AtomicAdd);
98}
99
100ReturnedValue Atomics::method_and(const FunctionObject *f, const Value *, const Value *argv, int argc)
101{
102 return atomicReadModifyWrite(f, argv, argc, modify: AtomicAnd);
103}
104
105ReturnedValue Atomics::method_compareExchange(const FunctionObject *f, const Value *, const Value *argv, int argc)
106{
107 Scope scope(f);
108 if (!argc)
109 return scope.engine->throwTypeError();
110
111 SharedArrayBuffer *buffer = validateSharedIntegerTypedArray(scope, typedArray: argv[0]);
112 if (!buffer)
113 return Encode::undefined();
114 const TypedArray &a = static_cast<const TypedArray &>(argv[0]);
115 int index = validateAtomicAccess(scope, typedArray: a, index: argc > 1 ? argv[1] : Value::undefinedValue());
116 if (index < 0)
117 return Encode::undefined();
118
119 Value expected = Value::fromReturnedValue(val: (argc > 2 ? argv[2] : Value::undefinedValue()).convertedToNumber());
120 if (scope.hasException())
121 return Encode::undefined();
122 Value v = Value::fromReturnedValue(val: (argc > 3 ? argv[3] : Value::undefinedValue()).convertedToNumber());
123 if (scope.hasException())
124 return Encode::undefined();
125
126 int bytesPerElement = a.d()->type->bytesPerElement;
127 int byteOffset = a.d()->byteOffset + index * bytesPerElement;
128
129 return a.d()->type->atomicCompareExchange(buffer->arrayData() + byteOffset, expected, v);
130}
131
132ReturnedValue Atomics::method_exchange(const FunctionObject *f, const Value *, const Value *argv, int argc)
133{
134 return atomicReadModifyWrite(f, argv, argc, modify: AtomicExchange);
135}
136
137ReturnedValue Atomics::method_isLockFree(const FunctionObject *, const Value *, const Value *argv, int argc)
138{
139 if (!argc)
140 return Encode(false);
141 double n = argv[0].toInteger();
142 if (n == 4.)
143 return Encode(true);
144 if (n == 2.)
145 return Encode(QAtomicOps<unsigned short>::isTestAndSetNative());
146#ifdef Q_ATOMIC_INT8_IS_SUPPORTED
147 if (n == 1.)
148 return Encode(QAtomicOps<unsigned char>::isTestAndSetNative());
149#endif
150 return Encode(false);
151}
152
153ReturnedValue Atomics::method_load(const FunctionObject *f, const Value *, const Value *argv, int argc)
154{
155 Scope scope(f);
156 if (!argc)
157 return scope.engine->throwTypeError();
158
159 SharedArrayBuffer *buffer = validateSharedIntegerTypedArray(scope, typedArray: argv[0]);
160 if (!buffer)
161 return Encode::undefined();
162 const TypedArray &a = static_cast<const TypedArray &>(argv[0]);
163 int index = validateAtomicAccess(scope, typedArray: a, index: argc > 1 ? argv[1] : Value::undefinedValue());
164 if (index < 0)
165 return Encode::undefined();
166
167 int bytesPerElement = a.d()->type->bytesPerElement;
168 int byteOffset = a.d()->byteOffset + index * bytesPerElement;
169
170 return a.d()->type->atomicLoad(buffer->arrayData() + byteOffset);
171}
172
173ReturnedValue Atomics::method_or(const FunctionObject *f, const Value *, const Value *argv, int argc)
174{
175 return atomicReadModifyWrite(f, argv, argc, modify: AtomicOr);
176}
177
178ReturnedValue Atomics::method_store(const FunctionObject *f, const Value *, const Value *argv, int argc)
179{
180 Scope scope(f);
181 if (!argc)
182 return scope.engine->throwTypeError();
183
184 SharedArrayBuffer *buffer = validateSharedIntegerTypedArray(scope, typedArray: argv[0]);
185 if (!buffer)
186 return Encode::undefined();
187 const TypedArray &a = static_cast<const TypedArray &>(argv[0]);
188 int index = validateAtomicAccess(scope, typedArray: a, index: argc > 1 ? argv[1] : Value::undefinedValue());
189 if (index < 0)
190 return Encode::undefined();
191
192 Value v = Value::fromReturnedValue(val: (argc > 2 ? argv[2] : Value::undefinedValue()).convertedToNumber());
193 if (scope.hasException())
194 return Encode::undefined();
195
196 int bytesPerElement = a.d()->type->bytesPerElement;
197 int byteOffset = a.d()->byteOffset + index * bytesPerElement;
198
199 return a.d()->type->atomicStore(buffer->arrayData() + byteOffset, v);
200}
201
202ReturnedValue Atomics::method_sub(const FunctionObject *f, const Value *, const Value *argv, int argc)
203{
204 return atomicReadModifyWrite(f, argv, argc, modify: AtomicSub);
205}
206
207ReturnedValue Atomics::method_wait(const FunctionObject *f, const Value *, const Value *, int)
208{
209 return f->engine()->throwTypeError();
210}
211
212ReturnedValue Atomics::method_wake(const FunctionObject *f, const Value *, const Value *, int)
213{
214 return f->engine()->throwTypeError();
215}
216
217ReturnedValue Atomics::method_xor(const FunctionObject *f, const Value *, const Value *argv, int argc)
218{
219 return atomicReadModifyWrite(f, argv, argc, modify: AtomicXor);
220
221}
222

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