Coverage Report

Created: 2025-09-04 06:34

/src/hermes/lib/VM/JSLib/ArrayBuffer.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) Meta Platforms, Inc. and affiliates.
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
8
//===----------------------------------------------------------------------===//
9
/// \file
10
/// ES6 24.1 ArrayBuffer
11
//===----------------------------------------------------------------------===//
12
13
#include "JSLibInternal.h"
14
15
#include "hermes/VM/JSArrayBuffer.h"
16
#include "hermes/VM/JSDataView.h"
17
#include "hermes/VM/JSTypedArray.h"
18
#include "hermes/VM/Operations.h"
19
#include "hermes/VM/StringPrimitive.h"
20
#pragma GCC diagnostic push
21
22
#ifdef HERMES_COMPILER_SUPPORTS_WSHORTEN_64_TO_32
23
#pragma GCC diagnostic ignored "-Wshorten-64-to-32"
24
#endif
25
namespace hermes {
26
namespace vm {
27
28
using std::max;
29
using std::min;
30
31
/// @name Implementation
32
/// @{
33
34
47
Handle<JSObject> createArrayBufferConstructor(Runtime &runtime) {
35
47
  auto arrayBufferPrototype =
36
47
      Handle<JSObject>::vmcast(&runtime.arrayBufferPrototype);
37
47
  auto cons = defineSystemConstructor<JSArrayBuffer>(
38
47
      runtime,
39
47
      Predefined::getSymbolID(Predefined::ArrayBuffer),
40
47
      arrayBufferConstructor,
41
47
      arrayBufferPrototype,
42
47
      1,
43
47
      CellKind::JSArrayBufferKind);
44
45
  // ArrayBuffer.prototype.xxx() methods.
46
47
  defineAccessor(
47
47
      runtime,
48
47
      arrayBufferPrototype,
49
47
      Predefined::getSymbolID(Predefined::byteLength),
50
47
      nullptr,
51
47
      arrayBufferPrototypeByteLength,
52
47
      nullptr,
53
47
      false,
54
47
      true);
55
47
  defineMethod(
56
47
      runtime,
57
47
      arrayBufferPrototype,
58
47
      Predefined::getSymbolID(Predefined::slice),
59
47
      nullptr,
60
47
      arrayBufferPrototypeSlice,
61
47
      2);
62
63
47
  auto dpf = DefinePropertyFlags::getDefaultNewPropertyFlags();
64
47
  dpf.writable = 0;
65
47
  dpf.enumerable = 0;
66
47
  defineProperty(
67
47
      runtime,
68
47
      arrayBufferPrototype,
69
47
      Predefined::getSymbolID(Predefined::SymbolToStringTag),
70
47
      runtime.getPredefinedStringHandle(Predefined::ArrayBuffer),
71
47
      dpf);
72
73
  // ArrayBuffer.xxx() methods.
74
47
  defineMethod(
75
47
      runtime,
76
47
      cons,
77
47
      Predefined::getSymbolID(Predefined::isView),
78
47
      nullptr,
79
47
      arrayBufferIsView,
80
47
      1);
81
82
47
  return cons;
83
47
}
84
85
CallResult<HermesValue>
86
0
arrayBufferConstructor(void *, Runtime &runtime, NativeArgs args) {
87
  // 1. If NewTarget is undefined, throw a TypeError exception.
88
0
  if (!args.isConstructorCall()) {
89
0
    return runtime.raiseTypeError(
90
0
        "ArrayBuffer() called in function context instead of constructor");
91
0
  }
92
0
  auto self = args.vmcastThis<JSArrayBuffer>();
93
0
  auto length = args.getArgHandle(0);
94
95
  // 2. Let byteLength be ToIndex(length).
96
0
  auto res = toIndex(runtime, length);
97
0
  if (res == ExecutionStatus::EXCEPTION) {
98
0
    return ExecutionStatus::EXCEPTION;
99
0
  }
100
0
  uint64_t byteLength = res->getNumberAs<uint64_t>();
101
102
  // 3. Return AllocateArrayBuffer(NewTarget, byteLength).
103
  // This object should not have been initialized yet, ensure this is true
104
0
  assert(
105
0
      !self->attached() &&
106
0
      "A new array buffer should not have an existing buffer");
107
0
  if (byteLength > std::numeric_limits<JSArrayBuffer::size_type>::max()) {
108
    // On a non-64-bit platform and requested a buffer size greater than
109
    // this platform's size type can hold
110
0
    return runtime.raiseRangeError("Too large of a byteLength requested");
111
0
  }
112
0
  if (JSArrayBuffer::createDataBlock(runtime, self, byteLength) ==
113
0
      ExecutionStatus::EXCEPTION) {
114
0
    return ExecutionStatus::EXCEPTION;
115
0
  }
116
0
  return self.getHermesValue();
117
0
}
118
119
CallResult<HermesValue>
120
0
arrayBufferIsView(void *, Runtime &runtime, NativeArgs args) {
121
  // 1. If Type(arg) is not Object, return false.
122
  // 2. If arg has a [[ViewedArrayBuffer]] internal slot, return true.
123
  // 3. Return false.
124
0
  return HermesValue::encodeBoolValue(
125
0
      vmisa<JSTypedArrayBase>(args.getArg(0)) ||
126
0
      vmisa<JSDataView>(args.getArg(0)));
127
0
}
128
129
CallResult<HermesValue>
130
0
arrayBufferPrototypeByteLength(void *, Runtime &runtime, NativeArgs args) {
131
0
  auto self = args.dyncastThis<JSArrayBuffer>();
132
0
  if (!self) {
133
0
    return runtime.raiseTypeError(
134
0
        "byteLength called on a non ArrayBuffer object");
135
0
  }
136
0
  return HermesValue::encodeUntrustedNumberValue(self->size());
137
0
}
138
139
CallResult<HermesValue>
140
0
arrayBufferPrototypeSlice(void *, Runtime &runtime, NativeArgs args) {
141
0
  auto start = args.getArgHandle(0);
142
0
  auto end = args.getArgHandle(1);
143
  // 1. Let O be the this value.
144
0
  auto self = args.dyncastThis<JSArrayBuffer>();
145
  // 2. If Type(O) is not Object, throw a TypeError exception.
146
  // 3. If O does not have an [[ArrayBufferData]] internal slot, throw a
147
  // TypeError exception. 4. If IsDetachedBuffer(O) is true, throw a TypeError
148
  // exception.
149
0
  if (!self) {
150
0
    return runtime.raiseTypeError(
151
0
        "Called ArrayBuffer.prototype.slice on a non-ArrayBuffer");
152
0
  }
153
154
  // 5. Let len be the value of O’s [[ArrayBufferByteLength]] internal slot.
155
0
  double len = self->size();
156
  // 6. Let relativeStart be ToIntegerOrInfinity(start).
157
0
  auto intRes = toIntegerOrInfinity(runtime, start);
158
0
  if (intRes == ExecutionStatus::EXCEPTION) {
159
    // 7. ReturnIfAbrupt(relativeStart).
160
0
    return ExecutionStatus::EXCEPTION;
161
0
  }
162
0
  double relativeStart = intRes->getNumber();
163
  // 8. If relativeStart < 0, let first be max((len + relativeStart),0); else
164
  // let first be min(relativeStart, len).
165
0
  double first = relativeStart < 0 ? max(len + relativeStart, 0.0)
166
0
                                   : min(relativeStart, len);
167
  // 9. If end is undefined, let relativeEnd be len; else let relativeEnd be
168
  // ToIntegerOrInfinity(end).
169
0
  double relativeEnd;
170
0
  if (end->isUndefined()) {
171
0
    relativeEnd = len;
172
0
  } else {
173
0
    intRes = toIntegerOrInfinity(runtime, end);
174
0
    if (intRes == ExecutionStatus::EXCEPTION) {
175
      // 10. ReturnIfAbrupt(relativeEnd).
176
0
      return ExecutionStatus::EXCEPTION;
177
0
    }
178
0
    relativeEnd = intRes->getNumber();
179
0
  }
180
  // 11. If relativeEnd < 0, let final be max((len + relativeEnd),0); else let
181
  // final be min(relativeEnd, len).
182
  // NOTE: final is a keyword
183
0
  double finale =
184
0
      relativeEnd < 0 ? max(len + relativeEnd, 0.0) : min(relativeEnd, len);
185
  // 12. Let newLen be max(final-first,0).
186
0
  double newLen = max(finale - first, 0.0);
187
  // Reduce the doubles to integers for addition and creation
188
0
  JSArrayBuffer::size_type first_int = first;
189
0
  JSArrayBuffer::size_type newLen_int = newLen;
190
  // 13. Let ctor be SpeciesConstructor(O, %ArrayBuffer%).
191
  // 14. ReturnIfAbrupt(ctor).
192
  // 15. Let new be Construct(ctor, «newLen»).
193
  // 16. ReturnIfAbrupt(new).
194
195
0
  auto newBuf = runtime.makeHandle(JSArrayBuffer::create(
196
0
      runtime, Handle<JSObject>::vmcast(&runtime.arrayBufferPrototype)));
197
198
0
  if (JSArrayBuffer::createDataBlock(runtime, newBuf, newLen_int) ==
199
0
      ExecutionStatus::EXCEPTION) {
200
0
    return ExecutionStatus::EXCEPTION;
201
0
  }
202
  // 17. If new does not have an [[ArrayBufferData]] internal slot, throw a
203
  // TypeError exception.
204
  // 18. If IsDetachedBuffer(new) is true, throw a TypeError exception.
205
  // 19. If SameValue(new, O) is true, throw a TypeError exception.
206
  // 20. If the value of new’s [[ArrayBufferByteLength]] internal
207
  // slot < newLen, throw a TypeError exception.
208
  // 21. NOTE: Side-effects of the above steps may have detached O.
209
  // 22. If IsDetachedBuffer(O) is true, throw a TypeError exception.
210
0
  if (!self->attached() || !newBuf->attached()) {
211
0
    return runtime.raiseTypeError("Cannot split with detached ArrayBuffers");
212
0
  }
213
214
  // 23. Let fromBuf be the value of O’s [[ArrayBufferData]] internal slot.
215
  // 24. Let toBuf be the value of new’s [[ArrayBufferData]] internal slot.
216
  // 25. Perform CopyDataBlockBytes(toBuf, 0, fromBuf, first, newLen).
217
0
  JSArrayBuffer::copyDataBlockBytes(
218
0
      runtime, *newBuf, 0, *self, first_int, newLen_int);
219
  // 26. Return new.
220
0
  return newBuf.getHermesValue();
221
0
}
222
/// @}
223
} // namespace vm
224
} // namespace hermes