@@ -27,6 +27,7 @@ using v8::Local;
2727using v8::LocalVector;
2828using v8::MaybeLocal;
2929using v8::Name;
30+ using v8::Number;
3031using v8::Object;
3132using v8::ObjectTemplate;
3233using v8::ONLY_CONFIGURABLE ;
@@ -42,6 +43,7 @@ using v8::StackFrame;
4243using v8::StackTrace;
4344using v8::String;
4445using v8::Uint32;
46+ using v8::Uint8Array;
4547using v8::Value;
4648
4749// If a UTF-16 character is a low/trailing surrogate.
@@ -194,6 +196,106 @@ void ArrayBufferViewHasBuffer(const FunctionCallbackInfo<Value>& args) {
194196 args.GetReturnValue ().Set (args[0 ].As <ArrayBufferView>()->HasBuffer ());
195197}
196198
199+ // Returns [buffer, byteOffset, byteLength] in a single binding crossing,
200+ // equivalent to reading the three properties via
201+ // Reflect.get(view.constructor.prototype, ..., view). Uses the V8 API
202+ // directly so it is immune to prototype tampering and avoids the JS-side
203+ // overhead of the defensive accessors in lib/internal/.
204+ void GetArrayBufferView (const FunctionCallbackInfo<Value>& args) {
205+ Isolate* isolate = args.GetIsolate ();
206+ CHECK (args[0 ]->IsArrayBufferView ());
207+ Local<ArrayBufferView> view = args[0 ].As <ArrayBufferView>();
208+ Local<Value> values[] = {
209+ view->Buffer (),
210+ Number::New (isolate, static_cast <double >(view->ByteOffset ())),
211+ Number::New (isolate, static_cast <double >(view->ByteLength ())),
212+ };
213+ args.GetReturnValue ().Set (Array::New (isolate, values, arraysize (values)));
214+ }
215+
216+ static bool ReadNonNegativeInteger (Local<Value> value, uint64_t * result) {
217+ constexpr double kMaxSafeInteger = 9007199254740991.0 ;
218+ double number = value.As <Number>()->Value ();
219+ if (number < 0 || number > kMaxSafeInteger ) {
220+ return false ;
221+ }
222+ *result = static_cast <uint64_t >(number);
223+ return static_cast <double >(*result) == number;
224+ }
225+
226+ // Returns true iff bytes can be safely copied between the buffers given the
227+ // requested offsets and count. Matches lib/internal/webstreams/util.js:
228+ // toBuffer !== fromBuffer &&
229+ // !toBuffer.detached &&
230+ // !fromBuffer.detached &&
231+ // toIndex + count <= toBuffer.byteLength &&
232+ // fromIndex + count <= fromBuffer.byteLength
233+ void CanCopyArrayBuffer (const FunctionCallbackInfo<Value>& args) {
234+ CHECK (args[0 ]->IsArrayBuffer () || args[0 ]->IsSharedArrayBuffer ());
235+ CHECK (args[1 ]->IsNumber ());
236+ CHECK (args[2 ]->IsArrayBuffer () || args[2 ]->IsSharedArrayBuffer ());
237+ CHECK (args[3 ]->IsNumber ());
238+ CHECK (args[4 ]->IsNumber ());
239+
240+ // SharedArrayBuffer handles are interoperable with ArrayBuffer handles in
241+ // V8, so we can use the ArrayBuffer accessors uniformly. WasDetached()
242+ // always returns false on a SAB.
243+ Local<ArrayBuffer> to_buffer = args[0 ].As <ArrayBuffer>();
244+ Local<ArrayBuffer> from_buffer = args[2 ].As <ArrayBuffer>();
245+
246+ if (to_buffer->StrictEquals (from_buffer)) {
247+ args.GetReturnValue ().Set (false );
248+ return ;
249+ }
250+ if (to_buffer->WasDetached () || from_buffer->WasDetached ()) {
251+ args.GetReturnValue ().Set (false );
252+ return ;
253+ }
254+
255+ uint64_t to_index;
256+ uint64_t from_index;
257+ uint64_t count;
258+ if (!ReadNonNegativeInteger (args[1 ], &to_index) ||
259+ !ReadNonNegativeInteger (args[3 ], &from_index) ||
260+ !ReadNonNegativeInteger (args[4 ], &count)) {
261+ args.GetReturnValue ().Set (false );
262+ return ;
263+ }
264+
265+ const uint64_t to_byte_length = to_buffer->ByteLength ();
266+ const uint64_t from_byte_length = from_buffer->ByteLength ();
267+
268+ bool ok = to_index <= to_byte_length && count <= to_byte_length - to_index &&
269+ from_index <= from_byte_length &&
270+ count <= from_byte_length - from_index;
271+ args.GetReturnValue ().Set (ok);
272+ }
273+
274+ // Equivalent to:
275+ // new Uint8Array(view.buffer.slice(view.byteOffset,
276+ // view.byteOffset + view.byteLength))
277+ // Allocates a fresh ArrayBuffer with the view's bytes copied into it, then
278+ // returns a Uint8Array over the full new buffer. Avoids the JS-side
279+ // Reflect.get + slice round-trip.
280+ void CloneAsUint8Array (const FunctionCallbackInfo<Value>& args) {
281+ Environment* env = Environment::GetCurrent (args);
282+ Isolate* isolate = env->isolate ();
283+ CHECK (args[0 ]->IsArrayBufferView ());
284+ Local<ArrayBufferView> view = args[0 ].As <ArrayBufferView>();
285+ size_t byte_length = view->ByteLength ();
286+ Local<ArrayBuffer> new_buffer;
287+ if (!ArrayBuffer::MaybeNew (isolate, byte_length).ToLocal (&new_buffer)) {
288+ // MaybeNew does not schedule an exception on allocation failure.
289+ THROW_ERR_MEMORY_ALLOCATION_FAILED (isolate);
290+ return ;
291+ }
292+ if (byte_length > 0 ) {
293+ size_t copied = view->CopyContents (new_buffer->Data (), byte_length);
294+ CHECK_EQ (copied, byte_length);
295+ }
296+ args.GetReturnValue ().Set (Uint8Array::New (new_buffer, 0 , byte_length));
297+ }
298+
197299static uint32_t GetUVHandleTypeCode (const uv_handle_type type) {
198300 // TODO(anonrig): We can use an enum here and then create the array in the
199301 // binding, which will remove the hard-coding in C++ and JS land.
@@ -480,6 +582,9 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
480582 registry->Register (GetExternalValue);
481583 registry->Register (Sleep);
482584 registry->Register (ArrayBufferViewHasBuffer);
585+ registry->Register (GetArrayBufferView);
586+ registry->Register (CanCopyArrayBuffer);
587+ registry->Register (CloneAsUint8Array);
483588 registry->Register (GuessHandleType);
484589 registry->Register (fast_guess_handle_type_);
485590 registry->Register (ParseEnv);
@@ -589,6 +694,11 @@ void Initialize(Local<Object> target,
589694 SetMethod (context, target, " parseEnv" , ParseEnv);
590695 SetMethod (
591696 context, target, " arrayBufferViewHasBuffer" , ArrayBufferViewHasBuffer);
697+ SetMethodNoSideEffect (
698+ context, target, " getArrayBufferView" , GetArrayBufferView);
699+ SetMethodNoSideEffect (
700+ context, target, " canCopyArrayBuffer" , CanCopyArrayBuffer);
701+ SetMethod (context, target, " cloneAsUint8Array" , CloneAsUint8Array);
592702 SetMethod (context,
593703 target,
594704 " constructSharedArrayBuffer" ,
0 commit comments