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/code-factory.h" 11 #include "src/conversions-inl.h" 12 #include "src/counters.h" 13 #include "src/futex-emulation.h" 14 #include "src/globals.h" 15 #include "src/heap/factory.h" 16 #include "src/objects-inl.h" 17 #include "src/objects/js-array-buffer-inl.h" 18 19 namespace v8 { 20 namespace internal { 21 22 // See builtins-arraybuffer.cc for implementations of 23 // SharedArrayBuffer.prototye.byteLength and SharedArrayBuffer.prototype.slice 24 25 inline bool AtomicIsLockFree(uint32_t size) { 26 return size == 1 || size == 2 || size == 4; 27 } 28 29 // ES #sec-atomics.islockfree 30 BUILTIN(AtomicsIsLockFree) { 31 HandleScope scope(isolate); 32 Handle<Object> size = args.atOrUndefined(isolate, 1); 33 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, size, 34 Object::ToNumber(isolate, size)); 35 return *isolate->factory()->ToBoolean(AtomicIsLockFree(size->Number())); 36 } 37 38 // ES #sec-validatesharedintegertypedarray 39 V8_WARN_UNUSED_RESULT MaybeHandle<JSTypedArray> ValidateSharedIntegerTypedArray( 40 Isolate* isolate, Handle<Object> object, bool only_int32 = false) { 41 if (object->IsJSTypedArray()) { 42 Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(object); 43 if (typed_array->GetBuffer()->is_shared()) { 44 if (only_int32) { 45 if (typed_array->type() == kExternalInt32Array) return typed_array; 46 } else { 47 if (typed_array->type() != kExternalFloat32Array && 48 typed_array->type() != kExternalFloat64Array && 49 typed_array->type() != kExternalUint8ClampedArray) 50 return typed_array; 51 } 52 } 53 } 54 55 THROW_NEW_ERROR( 56 isolate, 57 NewTypeError(only_int32 ? MessageTemplate::kNotInt32SharedTypedArray 58 : MessageTemplate::kNotIntegerSharedTypedArray, 59 object), 60 JSTypedArray); 61 } 62 63 // ES #sec-validateatomicaccess 64 // ValidateAtomicAccess( typedArray, requestIndex ) 65 V8_WARN_UNUSED_RESULT Maybe<size_t> ValidateAtomicAccess( 66 Isolate* isolate, Handle<JSTypedArray> typed_array, 67 Handle<Object> request_index) { 68 Handle<Object> access_index_obj; 69 ASSIGN_RETURN_ON_EXCEPTION_VALUE( 70 isolate, access_index_obj, 71 Object::ToIndex(isolate, request_index, 72 MessageTemplate::kInvalidAtomicAccessIndex), 73 Nothing<size_t>()); 74 75 size_t access_index; 76 if (!TryNumberToSize(*access_index_obj, &access_index) || 77 access_index >= typed_array->length_value()) { 78 isolate->Throw(*isolate->factory()->NewRangeError( 79 MessageTemplate::kInvalidAtomicAccessIndex)); 80 return Nothing<size_t>(); 81 } 82 return Just<size_t>(access_index); 83 } 84 85 // ES #sec-atomics.wake 86 // Atomics.wake( typedArray, index, count ) 87 BUILTIN(AtomicsWake) { 88 HandleScope scope(isolate); 89 Handle<Object> array = args.atOrUndefined(isolate, 1); 90 Handle<Object> index = args.atOrUndefined(isolate, 2); 91 Handle<Object> count = args.atOrUndefined(isolate, 3); 92 93 Handle<JSTypedArray> sta; 94 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 95 isolate, sta, ValidateSharedIntegerTypedArray(isolate, array, true)); 96 97 Maybe<size_t> maybe_index = ValidateAtomicAccess(isolate, sta, index); 98 if (maybe_index.IsNothing()) return ReadOnlyRoots(isolate).exception(); 99 size_t i = maybe_index.FromJust(); 100 101 uint32_t c; 102 if (count->IsUndefined(isolate)) { 103 c = kMaxUInt32; 104 } else { 105 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, count, 106 Object::ToInteger(isolate, count)); 107 double count_double = count->Number(); 108 if (count_double < 0) 109 count_double = 0; 110 else if (count_double > kMaxUInt32) 111 count_double = kMaxUInt32; 112 c = static_cast<uint32_t>(count_double); 113 } 114 115 Handle<JSArrayBuffer> array_buffer = sta->GetBuffer(); 116 size_t addr = (i << 2) + NumberToSize(sta->byte_offset()); 117 118 return FutexEmulation::Wake(array_buffer, addr, c); 119 } 120 121 // ES #sec-atomics.wait 122 // Atomics.wait( typedArray, index, value, timeout ) 123 BUILTIN(AtomicsWait) { 124 HandleScope scope(isolate); 125 Handle<Object> array = args.atOrUndefined(isolate, 1); 126 Handle<Object> index = args.atOrUndefined(isolate, 2); 127 Handle<Object> value = args.atOrUndefined(isolate, 3); 128 Handle<Object> timeout = args.atOrUndefined(isolate, 4); 129 130 Handle<JSTypedArray> sta; 131 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 132 isolate, sta, ValidateSharedIntegerTypedArray(isolate, array, true)); 133 134 Maybe<size_t> maybe_index = ValidateAtomicAccess(isolate, sta, index); 135 if (maybe_index.IsNothing()) return ReadOnlyRoots(isolate).exception(); 136 size_t i = maybe_index.FromJust(); 137 138 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value, 139 Object::ToInt32(isolate, value)); 140 int32_t value_int32 = NumberToInt32(*value); 141 142 double timeout_number; 143 if (timeout->IsUndefined(isolate)) { 144 timeout_number = ReadOnlyRoots(isolate).infinity_value()->Number(); 145 } else { 146 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, timeout, 147 Object::ToNumber(isolate, timeout)); 148 timeout_number = timeout->Number(); 149 if (std::isnan(timeout_number)) 150 timeout_number = ReadOnlyRoots(isolate).infinity_value()->Number(); 151 else if (timeout_number < 0) 152 timeout_number = 0; 153 } 154 155 if (!isolate->allow_atomics_wait()) { 156 THROW_NEW_ERROR_RETURN_FAILURE( 157 isolate, NewTypeError(MessageTemplate::kAtomicsWaitNotAllowed)); 158 } 159 160 Handle<JSArrayBuffer> array_buffer = sta->GetBuffer(); 161 size_t addr = (i << 2) + NumberToSize(sta->byte_offset()); 162 163 return FutexEmulation::Wait(isolate, array_buffer, addr, value_int32, 164 timeout_number); 165 } 166 167 } // namespace internal 168 } // namespace v8 169