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

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