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