@@ -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,112 @@ 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 (Environment* env,
217+ Local<Value> value,
218+ uint64_t * result) {
219+ int64_t integer;
220+ if (!value->IntegerValue (env->context ()).To (&integer)) {
221+ return false ;
222+ }
223+ if (integer < 0 ) {
224+ return false ;
225+ }
226+ *result = static_cast <uint64_t >(integer);
227+ return true ;
228+ }
229+
230+ // Returns true iff bytes can be safely copied between the buffers given the
231+ // requested offsets and count. Matches lib/internal/webstreams/util.js:
232+ // toBuffer !== fromBuffer &&
233+ // !toBuffer.detached &&
234+ // !fromBuffer.detached &&
235+ // toIndex + count <= toBuffer.byteLength &&
236+ // fromIndex + count <= fromBuffer.byteLength
237+ void CanCopyArrayBuffer (const FunctionCallbackInfo<Value>& args) {
238+ Environment* env = Environment::GetCurrent (args);
239+ CHECK (args[0 ]->IsArrayBuffer () || args[0 ]->IsSharedArrayBuffer ());
240+ CHECK (args[1 ]->IsNumber ());
241+ CHECK (args[2 ]->IsArrayBuffer () || args[2 ]->IsSharedArrayBuffer ());
242+ CHECK (args[3 ]->IsNumber ());
243+ CHECK (args[4 ]->IsNumber ());
244+
245+ // SharedArrayBuffer handles are interoperable with ArrayBuffer handles in
246+ // V8, so we can use the ArrayBuffer accessors uniformly. WasDetached()
247+ // always returns false on a SAB.
248+ Local<ArrayBuffer> to_buffer = args[0 ].As <ArrayBuffer>();
249+ Local<ArrayBuffer> from_buffer = args[2 ].As <ArrayBuffer>();
250+
251+ if (to_buffer->StrictEquals (from_buffer)) {
252+ args.GetReturnValue ().Set (false );
253+ return ;
254+ }
255+ if (to_buffer->WasDetached () || from_buffer->WasDetached ()) {
256+ args.GetReturnValue ().Set (false );
257+ return ;
258+ }
259+
260+ uint64_t to_index;
261+ uint64_t from_index;
262+ uint64_t count;
263+ if (!ReadNonNegativeInteger (env, args[1 ], &to_index) ||
264+ !ReadNonNegativeInteger (env, args[3 ], &from_index) ||
265+ !ReadNonNegativeInteger (env, args[4 ], &count)) {
266+ args.GetReturnValue ().Set (false );
267+ return ;
268+ }
269+
270+ const uint64_t to_byte_length = to_buffer->ByteLength ();
271+ const uint64_t from_byte_length = from_buffer->ByteLength ();
272+
273+ bool ok = to_index <= to_byte_length &&
274+ count <= to_byte_length - to_index &&
275+ from_index <= from_byte_length &&
276+ count <= from_byte_length - from_index;
277+ args.GetReturnValue ().Set (ok);
278+ }
279+
280+ // Equivalent to:
281+ // new Uint8Array(view.buffer.slice(view.byteOffset,
282+ // view.byteOffset + view.byteLength))
283+ // Allocates a fresh ArrayBuffer with the view's bytes copied into it, then
284+ // returns a Uint8Array over the full new buffer. Avoids the JS-side
285+ // Reflect.get + slice round-trip.
286+ void CloneAsUint8Array (const FunctionCallbackInfo<Value>& args) {
287+ Environment* env = Environment::GetCurrent (args);
288+ Isolate* isolate = env->isolate ();
289+ CHECK (args[0 ]->IsArrayBufferView ());
290+ Local<ArrayBufferView> view = args[0 ].As <ArrayBufferView>();
291+ size_t byte_length = view->ByteLength ();
292+ Local<ArrayBuffer> new_buffer;
293+ if (!ArrayBuffer::MaybeNew (isolate, byte_length).ToLocal (&new_buffer)) {
294+ // MaybeNew does not schedule an exception on allocation failure.
295+ THROW_ERR_MEMORY_ALLOCATION_FAILED (isolate);
296+ return ;
297+ }
298+ if (byte_length > 0 ) {
299+ size_t copied = view->CopyContents (new_buffer->Data (), byte_length);
300+ CHECK_EQ (copied, byte_length);
301+ }
302+ args.GetReturnValue ().Set (Uint8Array::New (new_buffer, 0 , byte_length));
303+ }
304+
197305static uint32_t GetUVHandleTypeCode (const uv_handle_type type) {
198306 // TODO(anonrig): We can use an enum here and then create the array in the
199307 // binding, which will remove the hard-coding in C++ and JS land.
@@ -480,6 +588,9 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
480588 registry->Register (GetExternalValue);
481589 registry->Register (Sleep);
482590 registry->Register (ArrayBufferViewHasBuffer);
591+ registry->Register (GetArrayBufferView);
592+ registry->Register (CanCopyArrayBuffer);
593+ registry->Register (CloneAsUint8Array);
483594 registry->Register (GuessHandleType);
484595 registry->Register (fast_guess_handle_type_);
485596 registry->Register (ParseEnv);
@@ -589,6 +700,11 @@ void Initialize(Local<Object> target,
589700 SetMethod (context, target, " parseEnv" , ParseEnv);
590701 SetMethod (
591702 context, target, " arrayBufferViewHasBuffer" , ArrayBufferViewHasBuffer);
703+ SetMethodNoSideEffect (
704+ context, target, " getArrayBufferView" , GetArrayBufferView);
705+ SetMethodNoSideEffect (
706+ context, target, " canCopyArrayBuffer" , CanCopyArrayBuffer);
707+ SetMethod (context, target, " cloneAsUint8Array" , CloneAsUint8Array);
592708 SetMethod (context,
593709 target,
594710 " constructSharedArrayBuffer" ,
0 commit comments