builtins-sharedarraybuffer.cc 5.83 KB
Newer Older
1 2 3 4
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6 7
#include "src/base/macros.h"
#include "src/base/platform/mutex.h"
#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 12 13 14
#include "src/conversions-inl.h"
#include "src/counters.h"
#include "src/futex-emulation.h"
#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 20 21

namespace v8 {
namespace internal {

22 23
// See builtins-arraybuffer.cc for implementations of
// SharedArrayBuffer.prototye.byteLength and SharedArrayBuffer.prototype.slice
24

25 26 27 28 29 30 31 32
inline bool AtomicIsLockFree(uint32_t size) {
  return size == 1 || size == 2 || size == 4;
}

// ES #sec-atomics.islockfree
BUILTIN(AtomicsIsLockFree) {
  HandleScope scope(isolate);
  Handle<Object> size = args.atOrUndefined(isolate, 1);
33 34
  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, size,
                                     Object::ToNumber(isolate, size));
35 36 37 38
  return *isolate->factory()->ToBoolean(AtomicIsLockFree(size->Number()));
}

// ES #sec-validatesharedintegertypedarray
39
V8_WARN_UNUSED_RESULT MaybeHandle<JSTypedArray> ValidateSharedIntegerTypedArray(
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
    Isolate* isolate, Handle<Object> object, bool only_int32 = false) {
  if (object->IsJSTypedArray()) {
    Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(object);
    if (typed_array->GetBuffer()->is_shared()) {
      if (only_int32) {
        if (typed_array->type() == kExternalInt32Array) return typed_array;
      } else {
        if (typed_array->type() != kExternalFloat32Array &&
            typed_array->type() != kExternalFloat64Array &&
            typed_array->type() != kExternalUint8ClampedArray)
          return typed_array;
      }
    }
  }

  THROW_NEW_ERROR(
      isolate,
      NewTypeError(only_int32 ? MessageTemplate::kNotInt32SharedTypedArray
                              : MessageTemplate::kNotIntegerSharedTypedArray,
                   object),
      JSTypedArray);
}

// ES #sec-validateatomicaccess
// ValidateAtomicAccess( typedArray, requestIndex )
65
V8_WARN_UNUSED_RESULT Maybe<size_t> ValidateAtomicAccess(
66 67
    Isolate* isolate, Handle<JSTypedArray> typed_array,
    Handle<Object> request_index) {
68 69 70 71 72 73 74
  Handle<Object> access_index_obj;
  ASSIGN_RETURN_ON_EXCEPTION_VALUE(
      isolate, access_index_obj,
      Object::ToIndex(isolate, request_index,
                      MessageTemplate::kInvalidAtomicAccessIndex),
      Nothing<size_t>());

75 76 77
  size_t access_index;
  if (!TryNumberToSize(*access_index_obj, &access_index) ||
      access_index >= typed_array->length_value()) {
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
    isolate->Throw(*isolate->factory()->NewRangeError(
        MessageTemplate::kInvalidAtomicAccessIndex));
    return Nothing<size_t>();
  }
  return Just<size_t>(access_index);
}

// ES #sec-atomics.wake
// Atomics.wake( typedArray, index, count )
BUILTIN(AtomicsWake) {
  HandleScope scope(isolate);
  Handle<Object> array = args.atOrUndefined(isolate, 1);
  Handle<Object> index = args.atOrUndefined(isolate, 2);
  Handle<Object> count = args.atOrUndefined(isolate, 3);

  Handle<JSTypedArray> sta;
  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
      isolate, sta, ValidateSharedIntegerTypedArray(isolate, array, true));

  Maybe<size_t> maybe_index = ValidateAtomicAccess(isolate, sta, index);
98
  if (maybe_index.IsNothing()) return ReadOnlyRoots(isolate).exception();
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
  size_t i = maybe_index.FromJust();

  uint32_t c;
  if (count->IsUndefined(isolate)) {
    c = kMaxUInt32;
  } else {
    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, count,
                                       Object::ToInteger(isolate, count));
    double count_double = count->Number();
    if (count_double < 0)
      count_double = 0;
    else if (count_double > kMaxUInt32)
      count_double = kMaxUInt32;
    c = static_cast<uint32_t>(count_double);
  }

  Handle<JSArrayBuffer> array_buffer = sta->GetBuffer();
  size_t addr = (i << 2) + NumberToSize(sta->byte_offset());

118
  return FutexEmulation::Wake(array_buffer, addr, c);
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
}

// ES #sec-atomics.wait
// Atomics.wait( typedArray, index, value, timeout )
BUILTIN(AtomicsWait) {
  HandleScope scope(isolate);
  Handle<Object> array = args.atOrUndefined(isolate, 1);
  Handle<Object> index = args.atOrUndefined(isolate, 2);
  Handle<Object> value = args.atOrUndefined(isolate, 3);
  Handle<Object> timeout = args.atOrUndefined(isolate, 4);

  Handle<JSTypedArray> sta;
  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
      isolate, sta, ValidateSharedIntegerTypedArray(isolate, array, true));

  Maybe<size_t> maybe_index = ValidateAtomicAccess(isolate, sta, index);
135
  if (maybe_index.IsNothing()) return ReadOnlyRoots(isolate).exception();
136 137 138 139 140 141 142 143
  size_t i = maybe_index.FromJust();

  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value,
                                     Object::ToInt32(isolate, value));
  int32_t value_int32 = NumberToInt32(*value);

  double timeout_number;
  if (timeout->IsUndefined(isolate)) {
144
    timeout_number = ReadOnlyRoots(isolate).infinity_value()->Number();
145 146
  } else {
    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, timeout,
147
                                       Object::ToNumber(isolate, timeout));
148 149
    timeout_number = timeout->Number();
    if (std::isnan(timeout_number))
150
      timeout_number = ReadOnlyRoots(isolate).infinity_value()->Number();
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
    else if (timeout_number < 0)
      timeout_number = 0;
  }

  if (!isolate->allow_atomics_wait()) {
    THROW_NEW_ERROR_RETURN_FAILURE(
        isolate, NewTypeError(MessageTemplate::kAtomicsWaitNotAllowed));
  }

  Handle<JSArrayBuffer> array_buffer = sta->GetBuffer();
  size_t addr = (i << 2) + NumberToSize(sta->byte_offset());

  return FutexEmulation::Wait(isolate, array_buffer, addr, value_int32,
                              timeout_number);
}

167 168
}  // namespace internal
}  // namespace v8