AtomicsObject.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. /*
  2. * Copyright (c) 2021, Tim Flynn <trflynn89@pm.me>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Atomic.h>
  7. #include <AK/ByteBuffer.h>
  8. #include <AK/Endian.h>
  9. #include <LibJS/Runtime/AtomicsObject.h>
  10. #include <LibJS/Runtime/GlobalObject.h>
  11. #include <LibJS/Runtime/TypedArray.h>
  12. #include <LibJS/Runtime/Value.h>
  13. namespace JS {
  14. // 25.4.2.1 ValidateIntegerTypedArray ( typedArray [ , waitable ] ), https://tc39.es/ecma262/#sec-validateintegertypedarray
  15. static void validate_integer_typed_array(GlobalObject& global_object, TypedArrayBase& typed_array, bool waitable = false)
  16. {
  17. auto& vm = global_object.vm();
  18. validate_typed_array(global_object, typed_array);
  19. if (vm.exception())
  20. return;
  21. auto type_name = typed_array.element_name();
  22. if (waitable) {
  23. if ((type_name != "Int32Array"sv) && (type_name != "BigInt64Array"sv))
  24. vm.throw_exception<TypeError>(global_object, ErrorType::TypedArrayTypeIsNot, type_name, "Int32 or BigInt64"sv);
  25. } else {
  26. if (!typed_array.is_unclamped_integer_element_type() && !typed_array.is_bigint_element_type())
  27. vm.throw_exception<TypeError>(global_object, ErrorType::TypedArrayTypeIsNot, type_name, "an unclamped integer or BigInt"sv);
  28. }
  29. }
  30. // 25.4.2.2 ValidateAtomicAccess ( typedArray, requestIndex ), https://tc39.es/ecma262/#sec-validateatomicaccess
  31. static Optional<size_t> validate_atomic_access(GlobalObject& global_object, TypedArrayBase& typed_array, Value request_index)
  32. {
  33. auto& vm = global_object.vm();
  34. auto access_index = request_index.to_index(global_object);
  35. if (vm.exception())
  36. return {};
  37. if (access_index >= typed_array.array_length()) {
  38. vm.throw_exception<RangeError>(global_object, ErrorType::IndexOutOfRange, access_index, typed_array.array_length());
  39. return {};
  40. }
  41. return access_index * typed_array.element_size() + typed_array.byte_offset();
  42. }
  43. // 25.4.2.11 AtomicReadModifyWrite ( typedArray, index, value, op ), https://tc39.es/ecma262/#sec-atomicreadmodifywrite
  44. static Value atomic_read_modify_write(GlobalObject& global_object, TypedArrayBase& typed_array, Value index, Value value, ReadWriteModifyFunction operation)
  45. {
  46. auto& vm = global_object.vm();
  47. validate_integer_typed_array(global_object, typed_array);
  48. if (vm.exception())
  49. return {};
  50. auto byte_index = validate_atomic_access(global_object, typed_array, index);
  51. if (!byte_index.has_value())
  52. return {};
  53. Value value_to_set;
  54. if (typed_array.content_type() == TypedArrayBase::ContentType::BigInt) {
  55. value_to_set = value.to_bigint(global_object);
  56. if (vm.exception())
  57. return {};
  58. } else {
  59. value_to_set = Value(value.to_integer_or_infinity(global_object));
  60. if (vm.exception())
  61. return {};
  62. }
  63. if (typed_array.viewed_array_buffer()->is_detached()) {
  64. vm.throw_exception<TypeError>(global_object, ErrorType::DetachedArrayBuffer);
  65. return {};
  66. }
  67. return typed_array.get_modify_set_value_in_buffer(*byte_index, value_to_set, move(operation));
  68. }
  69. template<typename T, typename AtomicFunction>
  70. static Value perform_atomic_operation(GlobalObject& global_object, TypedArrayBase& typed_array, AtomicFunction&& operation)
  71. {
  72. auto& vm = global_object.vm();
  73. auto index = vm.argument(1);
  74. auto value = vm.argument(2);
  75. auto operation_wrapper = [&, operation = forward<AtomicFunction>(operation)](ByteBuffer x_bytes, ByteBuffer y_bytes) -> ByteBuffer {
  76. if constexpr (IsFloatingPoint<T>) {
  77. VERIFY_NOT_REACHED();
  78. } else {
  79. using U = Conditional<IsSame<ClampedU8, T>, u8, T>;
  80. auto* x = reinterpret_cast<U*>(x_bytes.data());
  81. auto* y = reinterpret_cast<U*>(y_bytes.data());
  82. operation(x, *y);
  83. return x_bytes;
  84. }
  85. };
  86. return atomic_read_modify_write(global_object, typed_array, index, value, move(operation_wrapper));
  87. }
  88. AtomicsObject::AtomicsObject(GlobalObject& global_object)
  89. : Object(*global_object.object_prototype())
  90. {
  91. }
  92. void AtomicsObject::initialize(GlobalObject& global_object)
  93. {
  94. Object::initialize(global_object);
  95. auto& vm = this->vm();
  96. u8 attr = Attribute::Writable | Attribute::Configurable;
  97. define_native_function(vm.names.add, add, 3, attr);
  98. define_native_function(vm.names.and_, and_, 3, attr);
  99. define_native_function(vm.names.compareExchange, compare_exchange, 4, attr);
  100. define_native_function(vm.names.exchange, exchange, 3, attr);
  101. define_native_function(vm.names.isLockFree, is_lock_free, 1, attr);
  102. define_native_function(vm.names.load, load, 2, attr);
  103. define_native_function(vm.names.or_, or_, 3, attr);
  104. define_native_function(vm.names.store, store, 3, attr);
  105. define_native_function(vm.names.sub, sub, 3, attr);
  106. define_native_function(vm.names.xor_, xor_, 3, attr);
  107. // 25.4.15 Atomics [ @@toStringTag ], https://tc39.es/ecma262/#sec-atomics-@@tostringtag
  108. define_direct_property(*vm.well_known_symbol_to_string_tag(), js_string(global_object.heap(), "Atomics"), Attribute::Configurable);
  109. }
  110. // 25.4.3 Atomics.add ( typedArray, index, value ), https://tc39.es/ecma262/#sec-atomics.add
  111. JS_DEFINE_NATIVE_FUNCTION(AtomicsObject::add)
  112. {
  113. auto* typed_array = typed_array_from(global_object, vm.argument(0));
  114. if (!typed_array)
  115. return {};
  116. auto atomic_add = [](auto* storage, auto value) { return AK::atomic_fetch_add(storage, value); };
  117. #define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
  118. if (is<ClassName>(typed_array)) \
  119. return perform_atomic_operation<Type>(global_object, *typed_array, move(atomic_add));
  120. JS_ENUMERATE_TYPED_ARRAYS
  121. #undef __JS_ENUMERATE
  122. VERIFY_NOT_REACHED();
  123. }
  124. // 25.4.4 Atomics.and ( typedArray, index, value ), https://tc39.es/ecma262/#sec-atomics.and
  125. JS_DEFINE_NATIVE_FUNCTION(AtomicsObject::and_)
  126. {
  127. auto* typed_array = typed_array_from(global_object, vm.argument(0));
  128. if (!typed_array)
  129. return {};
  130. auto atomic_and = [](auto* storage, auto value) { return AK::atomic_fetch_and(storage, value); };
  131. #define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
  132. if (is<ClassName>(typed_array)) \
  133. return perform_atomic_operation<Type>(global_object, *typed_array, move(atomic_and));
  134. JS_ENUMERATE_TYPED_ARRAYS
  135. #undef __JS_ENUMERATE
  136. VERIFY_NOT_REACHED();
  137. }
  138. template<typename T>
  139. static Value atomic_compare_exchange_impl(GlobalObject& global_object, TypedArrayBase& typed_array)
  140. {
  141. auto& vm = global_object.vm();
  142. validate_integer_typed_array(global_object, typed_array);
  143. if (vm.exception())
  144. return {};
  145. auto indexed_position = validate_atomic_access(global_object, typed_array, vm.argument(1));
  146. if (!indexed_position.has_value())
  147. return {};
  148. Value expected;
  149. Value replacement;
  150. if (typed_array.content_type() == TypedArrayBase::ContentType::BigInt) {
  151. expected = vm.argument(2).to_bigint(global_object);
  152. if (vm.exception())
  153. return {};
  154. replacement = vm.argument(3).to_bigint(global_object);
  155. if (vm.exception())
  156. return {};
  157. } else {
  158. expected = Value(vm.argument(2).to_integer_or_infinity(global_object));
  159. if (vm.exception())
  160. return {};
  161. replacement = Value(vm.argument(3).to_integer_or_infinity(global_object));
  162. if (vm.exception())
  163. return {};
  164. }
  165. if (typed_array.viewed_array_buffer()->is_detached()) {
  166. vm.throw_exception<TypeError>(global_object, ErrorType::DetachedArrayBuffer);
  167. return {};
  168. }
  169. constexpr bool is_little_endian = __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__;
  170. auto& block = typed_array.viewed_array_buffer()->buffer();
  171. auto expected_bytes = numeric_to_raw_bytes<T>(global_object, expected, is_little_endian);
  172. auto replacement_bytes = numeric_to_raw_bytes<T>(global_object, replacement, is_little_endian);
  173. // FIXME: Implement SharedArrayBuffer case.
  174. auto raw_bytes_read = block.slice(*indexed_position, sizeof(T));
  175. if constexpr (IsFloatingPoint<T>) {
  176. VERIFY_NOT_REACHED();
  177. } else {
  178. using U = Conditional<IsSame<ClampedU8, T>, u8, T>;
  179. auto* v = reinterpret_cast<U*>(block.span().slice(*indexed_position).data());
  180. auto* e = reinterpret_cast<U*>(expected_bytes.data());
  181. auto* r = reinterpret_cast<U*>(replacement_bytes.data());
  182. (void)AK::atomic_compare_exchange_strong(v, *e, *r);
  183. }
  184. return raw_bytes_to_numeric<T>(global_object, raw_bytes_read, is_little_endian);
  185. }
  186. // 25.4.5 Atomics.compareExchange ( typedArray, index, expectedValue, replacementValue ), https://tc39.es/ecma262/#sec-atomics.compareexchange
  187. JS_DEFINE_NATIVE_FUNCTION(AtomicsObject::compare_exchange)
  188. {
  189. auto* typed_array = typed_array_from(global_object, vm.argument(0));
  190. if (!typed_array)
  191. return {};
  192. #define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
  193. if (is<ClassName>(typed_array)) \
  194. return atomic_compare_exchange_impl<Type>(global_object, *typed_array);
  195. JS_ENUMERATE_TYPED_ARRAYS
  196. #undef __JS_ENUMERATE
  197. VERIFY_NOT_REACHED();
  198. }
  199. // 25.4.6 Atomics.exchange ( typedArray, index, value ), https://tc39.es/ecma262/#sec-atomics.exchange
  200. JS_DEFINE_NATIVE_FUNCTION(AtomicsObject::exchange)
  201. {
  202. auto* typed_array = typed_array_from(global_object, vm.argument(0));
  203. if (!typed_array)
  204. return {};
  205. auto atomic_exchange = [](auto* storage, auto value) { return AK::atomic_exchange(storage, value); };
  206. #define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
  207. if (is<ClassName>(typed_array)) \
  208. return perform_atomic_operation<Type>(global_object, *typed_array, move(atomic_exchange));
  209. JS_ENUMERATE_TYPED_ARRAYS
  210. #undef __JS_ENUMERATE
  211. VERIFY_NOT_REACHED();
  212. }
  213. // 25.4.7 Atomics.isLockFree ( size ), https://tc39.es/ecma262/#sec-atomics.islockfree
  214. JS_DEFINE_NATIVE_FUNCTION(AtomicsObject::is_lock_free)
  215. {
  216. auto size = vm.argument(0).to_integer_or_infinity(global_object);
  217. if (vm.exception())
  218. return {};
  219. if (size == 1)
  220. return Value(AK::atomic_is_lock_free<u8>());
  221. if (size == 2)
  222. return Value(AK::atomic_is_lock_free<u16>());
  223. if (size == 4)
  224. return Value(true);
  225. if (size == 8)
  226. return Value(AK::atomic_is_lock_free<u64>());
  227. return Value(false);
  228. }
  229. // 25.4.8 Atomics.load ( typedArray, index ), https://tc39.es/ecma262/#sec-atomics.load
  230. JS_DEFINE_NATIVE_FUNCTION(AtomicsObject::load)
  231. {
  232. auto* typed_array = typed_array_from(global_object, vm.argument(0));
  233. if (!typed_array)
  234. return {};
  235. validate_integer_typed_array(global_object, *typed_array);
  236. if (vm.exception())
  237. return {};
  238. auto indexed_position = validate_atomic_access(global_object, *typed_array, vm.argument(1));
  239. if (!indexed_position.has_value())
  240. return {};
  241. if (typed_array->viewed_array_buffer()->is_detached()) {
  242. vm.throw_exception<TypeError>(global_object, ErrorType::DetachedArrayBuffer);
  243. return {};
  244. }
  245. return typed_array->get_value_from_buffer(*indexed_position, ArrayBuffer::Order::SeqCst, true);
  246. }
  247. // 25.4.9 Atomics.or ( typedArray, index, value ), https://tc39.es/ecma262/#sec-atomics.or
  248. JS_DEFINE_NATIVE_FUNCTION(AtomicsObject::or_)
  249. {
  250. auto* typed_array = typed_array_from(global_object, vm.argument(0));
  251. if (!typed_array)
  252. return {};
  253. auto atomic_or = [](auto* storage, auto value) { return AK::atomic_fetch_or(storage, value); };
  254. #define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
  255. if (is<ClassName>(typed_array)) \
  256. return perform_atomic_operation<Type>(global_object, *typed_array, move(atomic_or));
  257. JS_ENUMERATE_TYPED_ARRAYS
  258. #undef __JS_ENUMERATE
  259. VERIFY_NOT_REACHED();
  260. }
  261. // 25.4.10 Atomics.store ( typedArray, index, value ), https://tc39.es/ecma262/#sec-atomics.store
  262. JS_DEFINE_NATIVE_FUNCTION(AtomicsObject::store)
  263. {
  264. auto* typed_array = typed_array_from(global_object, vm.argument(0));
  265. if (!typed_array)
  266. return {};
  267. validate_integer_typed_array(global_object, *typed_array);
  268. if (vm.exception())
  269. return {};
  270. auto indexed_position = validate_atomic_access(global_object, *typed_array, vm.argument(1));
  271. if (!indexed_position.has_value())
  272. return {};
  273. auto value = vm.argument(2);
  274. Value value_to_set;
  275. if (typed_array->content_type() == TypedArrayBase::ContentType::BigInt) {
  276. value_to_set = value.to_bigint(global_object);
  277. if (vm.exception())
  278. return {};
  279. } else {
  280. value_to_set = Value(value.to_integer_or_infinity(global_object));
  281. if (vm.exception())
  282. return {};
  283. }
  284. if (typed_array->viewed_array_buffer()->is_detached()) {
  285. vm.throw_exception<TypeError>(global_object, ErrorType::DetachedArrayBuffer);
  286. return {};
  287. }
  288. typed_array->set_value_in_buffer(*indexed_position, value_to_set, ArrayBuffer::Order::SeqCst, true);
  289. return value_to_set;
  290. }
  291. // 25.4.11 Atomics.sub ( typedArray, index, value ), https://tc39.es/ecma262/#sec-atomics.sub
  292. JS_DEFINE_NATIVE_FUNCTION(AtomicsObject::sub)
  293. {
  294. auto* typed_array = typed_array_from(global_object, vm.argument(0));
  295. if (!typed_array)
  296. return {};
  297. auto atomic_sub = [](auto* storage, auto value) { return AK::atomic_fetch_sub(storage, value); };
  298. #define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
  299. if (is<ClassName>(typed_array)) \
  300. return perform_atomic_operation<Type>(global_object, *typed_array, move(atomic_sub));
  301. JS_ENUMERATE_TYPED_ARRAYS
  302. #undef __JS_ENUMERATE
  303. VERIFY_NOT_REACHED();
  304. }
  305. // 25.4.14 Atomics.xor ( typedArray, index, value ), https://tc39.es/ecma262/#sec-atomics.xor
  306. JS_DEFINE_NATIVE_FUNCTION(AtomicsObject::xor_)
  307. {
  308. auto* typed_array = typed_array_from(global_object, vm.argument(0));
  309. if (!typed_array)
  310. return {};
  311. auto atomic_xor = [](auto* storage, auto value) { return AK::atomic_fetch_xor(storage, value); };
  312. #define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
  313. if (is<ClassName>(typed_array)) \
  314. return perform_atomic_operation<Type>(global_object, *typed_array, move(atomic_xor));
  315. JS_ENUMERATE_TYPED_ARRAYS
  316. #undef __JS_ENUMERATE
  317. VERIFY_NOT_REACHED();
  318. }
  319. }