1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/base/macros.h"
6 #include "src/base/platform/mutex.h"
7 #include "src/base/platform/time.h"
8 #include "src/builtins/builtins-utils-inl.h"
9 #include "src/builtins/builtins.h"
10 #include "src/codegen/code-factory.h"
11 #include "src/common/globals.h"
12 #include "src/execution/futex-emulation.h"
13 #include "src/heap/factory.h"
14 #include "src/logging/counters.h"
15 #include "src/numbers/conversions-inl.h"
16 #include "src/objects/js-array-buffer-inl.h"
17 #include "src/objects/objects-inl.h"
18 
19 namespace v8 {
20 namespace internal {
21 
22 // See builtins-arraybuffer.cc for implementations of
23 // SharedArrayBuffer.prototype.byteLength and SharedArrayBuffer.prototype.slice
24 
25 // https://tc39.es/ecma262/#sec-atomics.islockfree
AtomicIsLockFree(double size)26 inline bool AtomicIsLockFree(double size) {
27   // According to the standard, 1, 2, and 4 byte atomics are supposed to be
28   // 'lock free' on every platform. 'Lock free' means that all possible uses of
29   // those atomics guarantee forward progress for the agent cluster (i.e. all
30   // threads in contrast with a single thread).
31   //
32   // This property is often, but not always, aligned with whether atomic
33   // accesses are implemented with software locks such as mutexes.
34   //
35   // V8 has lock free atomics for all sizes on all supported first-class
36   // architectures: ia32, x64, ARM32 variants, and ARM64. Further, this property
37   // is depended upon by WebAssembly, which prescribes that all atomic accesses
38   // are always lock free.
39   return size == 1 || size == 2 || size == 4 || size == 8;
40 }
41 
42 // https://tc39.es/ecma262/#sec-atomics.islockfree
BUILTIN(AtomicsIsLockFree)43 BUILTIN(AtomicsIsLockFree) {
44   HandleScope scope(isolate);
45   Handle<Object> size = args.atOrUndefined(isolate, 1);
46   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, size,
47                                      Object::ToNumber(isolate, size));
48   return *isolate->factory()->ToBoolean(AtomicIsLockFree(size->Number()));
49 }
50 
51 // https://tc39.es/ecma262/#sec-validatesharedintegertypedarray
ValidateIntegerTypedArray( Isolate* isolate, Handle<Object> object, const char* method_name, bool only_int32_and_big_int64 = false)52 V8_WARN_UNUSED_RESULT MaybeHandle<JSTypedArray> ValidateIntegerTypedArray(
53     Isolate* isolate, Handle<Object> object, const char* method_name,
54     bool only_int32_and_big_int64 = false) {
55   if (object->IsJSTypedArray()) {
56     Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(object);
57 
58     if (typed_array->WasDetached()) {
59       THROW_NEW_ERROR(
60           isolate,
61           NewTypeError(
62               MessageTemplate::kDetachedOperation,
63               isolate->factory()->NewStringFromAsciiChecked(method_name)),
64           JSTypedArray);
65     }
66 
67     if (only_int32_and_big_int64) {
68       if (typed_array->type() == kExternalInt32Array ||
69           typed_array->type() == kExternalBigInt64Array) {
70         return typed_array;
71       }
72     } else {
73       if (typed_array->type() != kExternalFloat32Array &&
74           typed_array->type() != kExternalFloat64Array &&
75           typed_array->type() != kExternalUint8ClampedArray)
76         return typed_array;
77     }
78   }
79 
80   THROW_NEW_ERROR(
81       isolate,
82       NewTypeError(only_int32_and_big_int64
83                        ? MessageTemplate::kNotInt32OrBigInt64TypedArray
84                        : MessageTemplate::kNotIntegerTypedArray,
85                    object),
86       JSTypedArray);
87 }
88 
89 // https://tc39.es/ecma262/#sec-validateatomicaccess
90 // ValidateAtomicAccess( typedArray, requestIndex )
ValidateAtomicAccess( Isolate* isolate, Handle<JSTypedArray> typed_array, Handle<Object> request_index)91 V8_WARN_UNUSED_RESULT Maybe<size_t> ValidateAtomicAccess(
92     Isolate* isolate, Handle<JSTypedArray> typed_array,
93     Handle<Object> request_index) {
94   Handle<Object> access_index_obj;
95   ASSIGN_RETURN_ON_EXCEPTION_VALUE(
96       isolate, access_index_obj,
97       Object::ToIndex(isolate, request_index,
98                       MessageTemplate::kInvalidAtomicAccessIndex),
99       Nothing<size_t>());
100 
101   size_t access_index;
102   size_t typed_array_length = typed_array->length();
103   if (!TryNumberToSize(*access_index_obj, &access_index) ||
104       access_index >= typed_array_length) {
105     isolate->Throw(*isolate->factory()->NewRangeError(
106         MessageTemplate::kInvalidAtomicAccessIndex));
107     return Nothing<size_t>();
108   }
109   return Just<size_t>(access_index);
110 }
111 
112 namespace {
113 
GetAddress64(size_t index, size_t byte_offset)114 inline size_t GetAddress64(size_t index, size_t byte_offset) {
115   return (index << 3) + byte_offset;
116 }
117 
GetAddress32(size_t index, size_t byte_offset)118 inline size_t GetAddress32(size_t index, size_t byte_offset) {
119   return (index << 2) + byte_offset;
120 }
121 
122 }  // namespace
123 
124 // ES #sec-atomics.notify
125 // Atomics.notify( typedArray, index, count )
BUILTIN(AtomicsNotify)126 BUILTIN(AtomicsNotify) {
127   HandleScope scope(isolate);
128   Handle<Object> array = args.atOrUndefined(isolate, 1);
129   Handle<Object> index = args.atOrUndefined(isolate, 2);
130   Handle<Object> count = args.atOrUndefined(isolate, 3);
131 
132   Handle<JSTypedArray> sta;
133   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
134       isolate, sta,
135       ValidateIntegerTypedArray(isolate, array, "Atomics.notify", true));
136 
137   // 2. Let i be ? ValidateAtomicAccess(typedArray, index).
138   Maybe<size_t> maybe_index = ValidateAtomicAccess(isolate, sta, index);
139   if (maybe_index.IsNothing()) return ReadOnlyRoots(isolate).exception();
140   size_t i = maybe_index.FromJust();
141 
142   // 3. If count is undefined, let c be +∞.
143   // 4. Else,
144   //   a. Let intCount be ? ToInteger(count).
145   //   b. Let c be max(intCount, 0).
146   uint32_t c;
147   if (count->IsUndefined(isolate)) {
148     c = kMaxUInt32;
149   } else {
150     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, count,
151                                        Object::ToInteger(isolate, count));
152     double count_double = count->Number();
153     if (count_double < 0) {
154       count_double = 0;
155     } else if (count_double > kMaxUInt32) {
156       count_double = kMaxUInt32;
157     }
158     c = static_cast<uint32_t>(count_double);
159   }
160 
161   // Steps 5-9 performed in FutexEmulation::Wake.
162 
163   // 10. If IsSharedArrayBuffer(buffer) is false, return 0.
164   Handle<JSArrayBuffer> array_buffer = sta->GetBuffer();
165   size_t wake_addr;
166 
167   if (V8_UNLIKELY(!sta->GetBuffer()->is_shared())) {
168     return Smi::FromInt(0);
169   }
170 
171   // Steps 11-17 performed in FutexEmulation::Wake.
172   if (sta->type() == kExternalBigInt64Array) {
173     wake_addr = GetAddress64(i, sta->byte_offset());
174   } else {
175     DCHECK(sta->type() == kExternalInt32Array);
176     wake_addr = GetAddress32(i, sta->byte_offset());
177   }
178   return FutexEmulation::Wake(array_buffer, wake_addr, c);
179 }
180 
DoWait(Isolate* isolate, FutexEmulation::WaitMode mode, Handle<Object> array, Handle<Object> index, Handle<Object> value, Handle<Object> timeout)181 Object DoWait(Isolate* isolate, FutexEmulation::WaitMode mode,
182               Handle<Object> array, Handle<Object> index, Handle<Object> value,
183               Handle<Object> timeout) {
184   // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray, true).
185   Handle<JSTypedArray> sta;
186   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
187       isolate, sta,
188       ValidateIntegerTypedArray(isolate, array, "Atomics.wait", true));
189 
190   // 2. If IsSharedArrayBuffer(buffer) is false, throw a TypeError exception.
191   if (V8_UNLIKELY(!sta->GetBuffer()->is_shared())) {
192     THROW_NEW_ERROR_RETURN_FAILURE(
193         isolate, NewTypeError(MessageTemplate::kNotSharedTypedArray, array));
194   }
195 
196   // 3. Let i be ? ValidateAtomicAccess(typedArray, index).
197   Maybe<size_t> maybe_index = ValidateAtomicAccess(isolate, sta, index);
198   if (maybe_index.IsNothing()) return ReadOnlyRoots(isolate).exception();
199   size_t i = maybe_index.FromJust();
200 
201   // 4. Let arrayTypeName be typedArray.[[TypedArrayName]].
202   // 5. If arrayTypeName is "BigInt64Array", let v be ? ToBigInt64(value).
203   // 6. Otherwise, let v be ? ToInt32(value).
204   if (sta->type() == kExternalBigInt64Array) {
205     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value,
206                                        BigInt::FromObject(isolate, value));
207   } else {
208     DCHECK(sta->type() == kExternalInt32Array);
209     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value,
210                                        Object::ToInt32(isolate, value));
211   }
212 
213   // 7. Let q be ? ToNumber(timeout).
214   // 8. If q is NaN, let t be +∞, else let t be max(q, 0).
215   double timeout_number;
216   if (timeout->IsUndefined(isolate)) {
217     timeout_number = ReadOnlyRoots(isolate).infinity_value().Number();
218   } else {
219     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, timeout,
220                                        Object::ToNumber(isolate, timeout));
221     timeout_number = timeout->Number();
222     if (std::isnan(timeout_number))
223       timeout_number = ReadOnlyRoots(isolate).infinity_value().Number();
224     else if (timeout_number < 0)
225       timeout_number = 0;
226   }
227 
228   // 9. If mode is sync, then
229   //   a. Let B be AgentCanSuspend().
230   //   b. If B is false, throw a TypeError exception.
231   if (mode == FutexEmulation::WaitMode::kSync &&
232       !isolate->allow_atomics_wait()) {
233     THROW_NEW_ERROR_RETURN_FAILURE(
234         isolate, NewTypeError(MessageTemplate::kAtomicsWaitNotAllowed));
235   }
236 
237   Handle<JSArrayBuffer> array_buffer = sta->GetBuffer();
238 
239   if (sta->type() == kExternalBigInt64Array) {
240     return FutexEmulation::WaitJs64(
241         isolate, mode, array_buffer, GetAddress64(i, sta->byte_offset()),
242         Handle<BigInt>::cast(value)->AsInt64(), timeout_number);
243   } else {
244     DCHECK(sta->type() == kExternalInt32Array);
245     return FutexEmulation::WaitJs32(isolate, mode, array_buffer,
246                                     GetAddress32(i, sta->byte_offset()),
247                                     NumberToInt32(*value), timeout_number);
248   }
249 }
250 
251 // https://tc39.es/ecma262/#sec-atomics.wait
252 // Atomics.wait( typedArray, index, value, timeout )
BUILTIN(AtomicsWait)253 BUILTIN(AtomicsWait) {
254   HandleScope scope(isolate);
255   Handle<Object> array = args.atOrUndefined(isolate, 1);
256   Handle<Object> index = args.atOrUndefined(isolate, 2);
257   Handle<Object> value = args.atOrUndefined(isolate, 3);
258   Handle<Object> timeout = args.atOrUndefined(isolate, 4);
259 
260   return DoWait(isolate, FutexEmulation::WaitMode::kSync, array, index, value,
261                 timeout);
262 }
263 
BUILTIN(AtomicsWaitAsync)264 BUILTIN(AtomicsWaitAsync) {
265   HandleScope scope(isolate);
266   Handle<Object> array = args.atOrUndefined(isolate, 1);
267   Handle<Object> index = args.atOrUndefined(isolate, 2);
268   Handle<Object> value = args.atOrUndefined(isolate, 3);
269   Handle<Object> timeout = args.atOrUndefined(isolate, 4);
270 
271   return DoWait(isolate, FutexEmulation::WaitMode::kAsync, array, index, value,
272                 timeout);
273 }
274 
275 }  // namespace internal
276 }  // namespace v8
277