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
44 using namespace QV4;
45
46 DEFINE_OBJECT_VTABLE(Atomics);
47
init()48 void Heap::Atomics::init()
49 {
50 Object::init();
51 Scope scope(internalClass->engine);
52 ScopedObject m(scope, this);
53
54 m->defineDefaultProperty(QStringLiteral("add"), QV4::Atomics::method_add, 3);
55 m->defineDefaultProperty(QStringLiteral("and"), QV4::Atomics::method_and, 3);
56 m->defineDefaultProperty(QStringLiteral("compareExchange"), QV4::Atomics::method_compareExchange, 4);
57 m->defineDefaultProperty(QStringLiteral("exchange"), QV4::Atomics::method_exchange, 3);
58 m->defineDefaultProperty(QStringLiteral("isLockFree"), QV4::Atomics::method_isLockFree, 1);
59 m->defineDefaultProperty(QStringLiteral("load"), QV4::Atomics::method_load, 2);
60 m->defineDefaultProperty(QStringLiteral("or"), QV4::Atomics::method_or, 3);
61 m->defineDefaultProperty(QStringLiteral("store"), QV4::Atomics::method_store, 3);
62 m->defineDefaultProperty(QStringLiteral("sub"), QV4::Atomics::method_sub, 3);
63 m->defineDefaultProperty(QStringLiteral("wait"), QV4::Atomics::method_wait, 4);
64 m->defineDefaultProperty(QStringLiteral("wake"), QV4::Atomics::method_wake, 3);
65 m->defineDefaultProperty(QStringLiteral("xor"), QV4::Atomics::method_xor, 3);
66
67 ScopedString name(scope, scope.engine->newString(QStringLiteral("Atomics")));
68 m->defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name);
69 }
70
validateSharedIntegerTypedArray(Scope & scope,const Value & typedArray,bool onlyInt32=false)71 static 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
validateAtomicAccess(Scope & scope,const TypedArray & typedArray,const Value & index)94 static 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
atomicReadModifyWrite(const FunctionObject * f,const Value * argv,int argc,AtomicModifyOps modify)107 ReturnedValue 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, argv[0]);
114 if (!buffer)
115 return Encode::undefined();
116 const TypedArray &a = static_cast<const TypedArray &>(argv[0]);
117 int index = validateAtomicAccess(scope, a, argc > 1 ? argv[1] : Value::undefinedValue());
118 if (index < 0)
119 return Encode::undefined();
120
121 Value v = Value::fromReturnedValue((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
method_add(const FunctionObject * f,const Value *,const Value * argv,int argc)131 ReturnedValue Atomics::method_add(const FunctionObject *f, const Value *, const Value *argv, int argc)
132 {
133 return atomicReadModifyWrite(f, argv, argc, AtomicAdd);
134 }
135
method_and(const FunctionObject * f,const Value *,const Value * argv,int argc)136 ReturnedValue Atomics::method_and(const FunctionObject *f, const Value *, const Value *argv, int argc)
137 {
138 return atomicReadModifyWrite(f, argv, argc, AtomicAnd);
139 }
140
method_compareExchange(const FunctionObject * f,const Value *,const Value * argv,int argc)141 ReturnedValue 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, argv[0]);
148 if (!buffer)
149 return Encode::undefined();
150 const TypedArray &a = static_cast<const TypedArray &>(argv[0]);
151 int index = validateAtomicAccess(scope, a, argc > 1 ? argv[1] : Value::undefinedValue());
152 if (index < 0)
153 return Encode::undefined();
154
155 Value expected = Value::fromReturnedValue((argc > 2 ? argv[2] : Value::undefinedValue()).convertedToNumber());
156 if (scope.hasException())
157 return Encode::undefined();
158 Value v = Value::fromReturnedValue((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
method_exchange(const FunctionObject * f,const Value *,const Value * argv,int argc)168 ReturnedValue Atomics::method_exchange(const FunctionObject *f, const Value *, const Value *argv, int argc)
169 {
170 return atomicReadModifyWrite(f, argv, argc, AtomicExchange);
171 }
172
method_isLockFree(const FunctionObject *,const Value *,const Value * argv,int argc)173 ReturnedValue 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
method_load(const FunctionObject * f,const Value *,const Value * argv,int argc)189 ReturnedValue 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, argv[0]);
196 if (!buffer)
197 return Encode::undefined();
198 const TypedArray &a = static_cast<const TypedArray &>(argv[0]);
199 int index = validateAtomicAccess(scope, a, 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
method_or(const FunctionObject * f,const Value *,const Value * argv,int argc)209 ReturnedValue Atomics::method_or(const FunctionObject *f, const Value *, const Value *argv, int argc)
210 {
211 return atomicReadModifyWrite(f, argv, argc, AtomicOr);
212 }
213
method_store(const FunctionObject * f,const Value *,const Value * argv,int argc)214 ReturnedValue 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, argv[0]);
221 if (!buffer)
222 return Encode::undefined();
223 const TypedArray &a = static_cast<const TypedArray &>(argv[0]);
224 int index = validateAtomicAccess(scope, a, argc > 1 ? argv[1] : Value::undefinedValue());
225 if (index < 0)
226 return Encode::undefined();
227
228 Value v = Value::fromReturnedValue((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
method_sub(const FunctionObject * f,const Value *,const Value * argv,int argc)238 ReturnedValue Atomics::method_sub(const FunctionObject *f, const Value *, const Value *argv, int argc)
239 {
240 return atomicReadModifyWrite(f, argv, argc, AtomicSub);
241 }
242
method_wait(const FunctionObject * f,const Value *,const Value *,int)243 ReturnedValue Atomics::method_wait(const FunctionObject *f, const Value *, const Value *, int)
244 {
245 return f->engine()->throwTypeError();
246 }
247
method_wake(const FunctionObject * f,const Value *,const Value *,int)248 ReturnedValue Atomics::method_wake(const FunctionObject *f, const Value *, const Value *, int)
249 {
250 return f->engine()->throwTypeError();
251 }
252
method_xor(const FunctionObject * f,const Value *,const Value * argv,int argc)253 ReturnedValue Atomics::method_xor(const FunctionObject *f, const Value *, const Value *argv, int argc)
254 {
255 return atomicReadModifyWrite(f, argv, argc, AtomicXor);
256
257 }
258