v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
builtins-sharedarraybuffer.cc
Go to the documentation of this file.
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"
10#include "src/heap/factory.h"
15
16namespace v8 {
17namespace internal {
18
19// See builtins-arraybuffer.cc for implementations of
20// SharedArrayBuffer.prototype.byteLength and SharedArrayBuffer.prototype.slice
21
22// https://tc39.es/ecma262/#sec-atomics.islockfree
23inline bool AtomicIsLockFree(double size) {
24 // According to the standard, 1, 2, and 4 byte atomics are supposed to be
25 // 'lock free' on every platform. 'Lock free' means that all possible uses of
26 // those atomics guarantee forward progress for the agent cluster (i.e. all
27 // threads in contrast with a single thread).
28 //
29 // This property is often, but not always, aligned with whether atomic
30 // accesses are implemented with software locks such as mutexes.
31 //
32 // V8 has lock free atomics for all sizes on all supported first-class
33 // architectures: ia32, x64, ARM32 variants, and ARM64. Further, this property
34 // is depended upon by WebAssembly, which prescribes that all atomic accesses
35 // are always lock free.
36 return size == 1 || size == 2 || size == 4 || size == 8;
37}
38
39// https://tc39.es/ecma262/#sec-atomics.islockfree
40BUILTIN(AtomicsIsLockFree) {
41 HandleScope scope(isolate);
42 Handle<Object> size = args.atOrUndefined(isolate, 1);
44 Object::ToNumber(isolate, size));
45 return *isolate->factory()->ToBoolean(
47}
48
49// https://tc39.es/ecma262/#sec-validatesharedintegertypedarray
51 Isolate* isolate, Handle<Object> object, const char* method_name,
52 bool only_int32_and_big_int64 = false) {
53 if (IsJSTypedArray(*object)) {
55
56 if (typed_array->IsDetachedOrOutOfBounds()) {
58 isolate, NewTypeError(MessageTemplate::kDetachedOperation,
59 isolate->factory()->NewStringFromAsciiChecked(
60 method_name)));
61 }
62
63 if (only_int32_and_big_int64) {
64 if (typed_array->type() == kExternalInt32Array ||
65 typed_array->type() == kExternalBigInt64Array) {
66 return typed_array;
67 }
68 } else {
69 if (typed_array->type() != kExternalFloat32Array &&
70 typed_array->type() != kExternalFloat64Array &&
71 typed_array->type() != kExternalUint8ClampedArray)
72 return typed_array;
73 }
74 }
75
77 isolate, NewTypeError(only_int32_and_big_int64
78 ? MessageTemplate::kNotInt32OrBigInt64TypedArray
79 : MessageTemplate::kNotIntegerTypedArray,
80 object));
81}
82
83// https://tc39.es/ecma262/#sec-validateatomicaccess
84// ValidateAtomicAccess( typedArray, requestIndex )
86 Isolate* isolate, DirectHandle<JSTypedArray> typed_array,
87 Handle<Object> request_index) {
88 DirectHandle<Object> access_index_obj;
90 isolate, access_index_obj,
91 Object::ToIndex(isolate, request_index,
92 MessageTemplate::kInvalidAtomicAccessIndex),
94
95 size_t access_index;
96 size_t typed_array_length = typed_array->GetLength();
97 if (!TryNumberToSize(*access_index_obj, &access_index) ||
98 access_index >= typed_array_length) {
99 isolate->Throw(*isolate->factory()->NewRangeError(
100 MessageTemplate::kInvalidAtomicAccessIndex));
101 return Nothing<size_t>();
102 }
103 return Just<size_t>(access_index);
104}
105
106namespace {
107
108inline size_t GetAddress64(size_t index, size_t byte_offset) {
109 return (index << 3) + byte_offset;
110}
111
112inline size_t GetAddress32(size_t index, size_t byte_offset) {
113 return (index << 2) + byte_offset;
114}
115
116} // namespace
117
118// ES #sec-atomics.notify
119// Atomics.notify( typedArray, index, count )
120BUILTIN(AtomicsNotify) {
121 // TODO(clemensb): This builtin only allocates (an exception) in the case of
122 // an error; we could try to avoid allocating the HandleScope in the non-error
123 // case.
124 HandleScope scope(isolate);
125 Handle<Object> array = args.atOrUndefined(isolate, 1);
126 Handle<Object> index = args.atOrUndefined(isolate, 2);
127 Handle<Object> count = args.atOrUndefined(isolate, 3);
128
131 isolate, sta,
132 ValidateIntegerTypedArray(isolate, array, "Atomics.notify", true));
133
134 // 2. Let i be ? ValidateAtomicAccess(typedArray, index).
135 Maybe<size_t> maybe_index = ValidateAtomicAccess(isolate, sta, index);
136 if (maybe_index.IsNothing()) return ReadOnlyRoots(isolate).exception();
137 size_t i = maybe_index.FromJust();
138
139 // 3. If count is undefined, let c be +∞.
140 // 4. Else,
141 // a. Let intCount be ? ToInteger(count).
142 // b. Let c be max(intCount, 0).
143 uint32_t c;
144 if (IsUndefined(*count, isolate)) {
145 c = kMaxUInt32;
146 } else {
147 double count_double;
149 isolate, count_double, Object::IntegerValue(isolate, count));
150 if (count_double < 0) {
151 count_double = 0;
152 } else if (count_double > kMaxUInt32) {
153 count_double = kMaxUInt32;
154 }
155 c = static_cast<uint32_t>(count_double);
156 }
157
158 // Steps 5-9 performed in FutexEmulation::Wake.
159
160 // 10. If IsSharedArrayBuffer(buffer) is false, return 0.
161 DirectHandle<JSArrayBuffer> array_buffer = sta->GetBuffer();
162
163 if (V8_UNLIKELY(!array_buffer->is_shared())) {
164 return Smi::zero();
165 }
166
167 // Steps 11-17 performed in FutexEmulation::Wake.
168 size_t wake_addr;
169 if (sta->type() == kExternalBigInt64Array) {
170 wake_addr = GetAddress64(i, sta->byte_offset());
171 } else {
172 DCHECK(sta->type() == kExternalInt32Array);
173 wake_addr = GetAddress32(i, sta->byte_offset());
174 }
175 int num_waiters_woken = FutexEmulation::Wake(*array_buffer, wake_addr, c);
176 return Smi::FromInt(num_waiters_woken);
177}
178
180 Handle<Object> array, Handle<Object> index,
181 Handle<Object> value, Handle<Object> timeout) {
182 // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray, true).
185 isolate, sta,
186 ValidateIntegerTypedArray(isolate, array, "Atomics.wait", true));
187
188 // 2. If IsSharedArrayBuffer(buffer) is false, throw a TypeError exception.
189 if (V8_UNLIKELY(!sta->GetBuffer()->is_shared())) {
191 isolate, NewTypeError(MessageTemplate::kNotSharedTypedArray, array));
192 }
193
194 // 3. Let i be ? ValidateAtomicAccess(typedArray, index).
195 Maybe<size_t> maybe_index = ValidateAtomicAccess(isolate, sta, index);
196 if (maybe_index.IsNothing()) return ReadOnlyRoots(isolate).exception();
197 size_t i = maybe_index.FromJust();
198
199 // 4. Let arrayTypeName be typedArray.[[TypedArrayName]].
200 // 5. If arrayTypeName is "BigInt64Array", let v be ? ToBigInt64(value).
201 // 6. Otherwise, let v be ? ToInt32(value).
202 if (sta->type() == kExternalBigInt64Array) {
204 BigInt::FromObject(isolate, value));
205 } else {
206 DCHECK(sta->type() == kExternalInt32Array);
208 Object::ToInt32(isolate, value));
209 }
210
211 // 7. Let q be ? ToNumber(timeout).
212 // 8. If q is NaN, let t be +∞, else let t be max(q, 0).
213 double timeout_number;
214 if (IsUndefined(*timeout, isolate)) {
215 timeout_number =
216 Object::NumberValue(ReadOnlyRoots(isolate).infinity_value());
217 } else {
218 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, timeout,
219 Object::ToNumber(isolate, timeout));
220 timeout_number = Object::NumberValue(*timeout);
221 if (std::isnan(timeout_number))
222 timeout_number =
223 Object::NumberValue(ReadOnlyRoots(isolate).infinity_value());
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.
232 !isolate->allow_atomics_wait()) {
234 isolate, NewTypeError(MessageTemplate::kAtomicsOperationNotAllowed,
235 isolate->factory()->NewStringFromAsciiChecked(
236 "Atomics.wait")));
237 }
238
239 DirectHandle<JSArrayBuffer> array_buffer = sta->GetBuffer();
240
241 if (sta->type() == kExternalBigInt64Array) {
243 isolate, mode, array_buffer, GetAddress64(i, sta->byte_offset()),
244 Cast<BigInt>(value)->AsInt64(), timeout_number);
245 } else {
246 DCHECK(sta->type() == kExternalInt32Array);
247 return FutexEmulation::WaitJs32(isolate, mode, array_buffer,
248 GetAddress32(i, sta->byte_offset()),
249 NumberToInt32(*value), timeout_number);
250 }
251}
252
253// https://tc39.es/ecma262/#sec-atomics.wait
254// Atomics.wait( typedArray, index, value, timeout )
255BUILTIN(AtomicsWait) {
256 HandleScope scope(isolate);
257 Handle<Object> array = args.atOrUndefined(isolate, 1);
258 Handle<Object> index = args.atOrUndefined(isolate, 2);
259 Handle<Object> value = args.atOrUndefined(isolate, 3);
260 Handle<Object> timeout = args.atOrUndefined(isolate, 4);
261
262 return DoWait(isolate, FutexEmulation::WaitMode::kSync, array, index, value,
263 timeout);
264}
265
266BUILTIN(AtomicsWaitAsync) {
267 HandleScope scope(isolate);
268 Handle<Object> array = args.atOrUndefined(isolate, 1);
269 Handle<Object> index = args.atOrUndefined(isolate, 2);
270 Handle<Object> value = args.atOrUndefined(isolate, 3);
271 Handle<Object> timeout = args.atOrUndefined(isolate, 4);
272 isolate->CountUsage(v8::Isolate::kAtomicsWaitAsync);
273
274 return DoWait(isolate, FutexEmulation::WaitMode::kAsync, array, index, value,
275 timeout);
276}
277
278namespace {
279V8_NOINLINE Maybe<bool> CheckAtomicsPauseIterationNumber(
280 Isolate* isolate, DirectHandle<Object> iteration_number) {
281 constexpr char method_name[] = "Atomics.pause";
282
283 // 1. If N is neither undefined nor an integral Number, throw a TypeError
284 // exception.
285 if (IsNumber(*iteration_number)) {
286 double iter = Object::NumberValue(*iteration_number);
287 if (std::isfinite(iter) && nearbyint(iter) == iter) {
288 return Just(true);
289 }
290 }
291
293 isolate,
294 NewError(isolate->type_error_function(),
295 MessageTemplate::kArgumentIsNotUndefinedOrInteger,
296 isolate->factory()->NewStringFromAsciiChecked(method_name)),
297 Nothing<bool>());
298}
299} // namespace
300
301// https://tc39.es/proposal-atomics-microwait/
302BUILTIN(AtomicsPause) {
303 HandleScope scope(isolate);
304 DirectHandle<Object> iteration_number = args.atOrUndefined(isolate, 1);
305
306 // 1. If N is neither undefined nor an integral Number, throw a TypeError
307 // exception.
308 if (V8_UNLIKELY(!IsUndefined(*iteration_number, isolate) &&
309 !IsSmi(*iteration_number))) {
311 isolate, CheckAtomicsPauseIterationNumber(isolate, iteration_number),
312 ReadOnlyRoots(isolate).exception());
313 }
314
315 // 2. If the execution environment of the ECMAScript implementation supports
316 // signaling to the operating system or CPU that the current executing code
317 // is in a spin-wait loop, such as executing a pause CPU instruction, send
318 // that signal. When N is not undefined, it determines the number of times
319 // that signal is sent. The number of times the signal is sent for an
320 // integral Number N is less than or equal to the number times it is sent
321 // for N + 1 if both N and N + 1 have the same sign.
322 //
323 // In the non-inlined version, JS call overhead is sufficiently expensive that
324 // iterationNumber is not used to determine how many times YIELD_PROCESSOR is
325 // performed.
326 //
327 // TODO(352359899): Try to estimate the call overhead and adjust the yield
328 // count while taking iterationNumber into account.
330
331 // 3. Return undefined.
332 return ReadOnlyRoots(isolate).undefined_value();
333}
334
335} // namespace internal
336} // namespace v8
#define BUILTIN(name)
V8_INLINE T FromJust() const &
Definition v8-maybe.h:64
V8_INLINE bool IsNothing() const
Definition v8-maybe.h:35
static Tagged< Object > WaitJs64(Isolate *isolate, WaitMode mode, DirectHandle< JSArrayBuffer > array_buffer, size_t addr, int64_t value, double rel_timeout_ms)
static Tagged< Object > WaitJs32(Isolate *isolate, WaitMode mode, DirectHandle< JSArrayBuffer > array_buffer, size_t addr, int32_t value, double rel_timeout_ms)
static V8_EXPORT_PRIVATE int Wake(Tagged< JSArrayBuffer > array_buffer, size_t addr, uint32_t num_waiters_to_wake)
static V8_WARN_UNUSED_RESULT HandleType< Number >::MaybeType ToNumber(Isolate *isolate, HandleType< T > input)
static V8_WARN_UNUSED_RESULT HandleType< Object >::MaybeType ToIndex(Isolate *isolate, HandleType< T > input, MessageTemplate error_index)
static double NumberValue(Tagged< Number > obj)
static V8_WARN_UNUSED_RESULT Maybe< double > IntegerValue(Isolate *isolate, HandleType< T > input)
static V8_EXPORT_PRIVATE bool ToInt32(Tagged< Object > obj, int32_t *value)
Definition objects.cc:1438
static constexpr Tagged< Smi > FromInt(int value)
Definition smi.h:38
static constexpr Tagged< Smi > zero()
Definition smi.h:99
#define THROW_NEW_ERROR_RETURN_VALUE(isolate, call, value)
Definition isolate.h:300
#define ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, dst, call)
Definition isolate.h:284
#define THROW_NEW_ERROR(isolate, call)
Definition isolate.h:307
#define ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, dst, call, value)
Definition isolate.h:276
#define THROW_NEW_ERROR_RETURN_FAILURE(isolate, call)
Definition isolate.h:294
#define MAYBE_RETURN_ON_EXCEPTION_VALUE(isolate, call, value)
Definition isolate.h:238
#define MAYBE_ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, dst, call)
Definition isolate.h:448
base::Vector< const DirectHandle< Object > > args
Definition execution.cc:74
V8_WARN_UNUSED_RESULT MaybeDirectHandle< JSTypedArray > ValidateIntegerTypedArray(Isolate *isolate, Handle< Object > object, const char *method_name, bool only_int32_and_big_int64=false)
bool IsNumber(Tagged< Object > obj)
V8_INLINE constexpr bool IsSmi(TaggedImpl< kRefType, StorageType > obj)
Definition objects.h:665
bool AtomicIsLockFree(double size)
int32_t NumberToInt32(Tagged< Object > number)
V8_WARN_UNUSED_RESULT Maybe< size_t > ValidateAtomicAccess(Isolate *isolate, DirectHandle< JSTypedArray > typed_array, Handle< Object > request_index)
Tagged< Object > DoWait(Isolate *isolate, FutexEmulation::WaitMode mode, Handle< Object > array, Handle< Object > index, Handle< Object > value, Handle< Object > timeout)
@ kExternalFloat64Array
Definition globals.h:2461
@ kExternalBigInt64Array
Definition globals.h:2463
@ kExternalInt32Array
Definition globals.h:2457
@ kExternalUint8ClampedArray
Definition globals.h:2462
@ kExternalFloat32Array
Definition globals.h:2460
bool TryNumberToSize(Tagged< Object > number, size_t *result)
constexpr uint32_t kMaxUInt32
Definition globals.h:387
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
Maybe< T > Nothing()
Definition v8-maybe.h:112
Maybe< T > Just(const T &t)
Definition v8-maybe.h:117
#define DCHECK(condition)
Definition logging.h:482
#define V8_WARN_UNUSED_RESULT
Definition v8config.h:671
#define V8_UNLIKELY(condition)
Definition v8config.h:660
#define V8_NOINLINE
Definition v8config.h:586
#define YIELD_PROCESSOR