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 19namespace v8 { 20namespace 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 26inline 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 43BUILTIN(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 52V8_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 ) 91V8_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 112namespace { 113 114inline size_t GetAddress64(size_t index, size_t byte_offset) { 115 return (index << 3) + byte_offset; 116} 117 118inline 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 ) 126BUILTIN(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 181Object 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 ) 253BUILTIN(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 264BUILTIN(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