GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/stream_base.cc Lines: 264 280 94.3 %
Date: 2020-02-19 22:14:06 Branches: 194 316 61.4 %

Line Branch Exec Source
1
#include "stream_base.h"  // NOLINT(build/include_inline)
2
#include "stream_base-inl.h"
3
#include "stream_wrap.h"
4
5
#include "node.h"
6
#include "node_buffer.h"
7
#include "node_errors.h"
8
#include "env-inl.h"
9
#include "js_stream.h"
10
#include "string_bytes.h"
11
#include "util-inl.h"
12
#include "v8.h"
13
14
#include <climits>  // INT_MAX
15
16
namespace node {
17
18
using v8::Array;
19
using v8::ArrayBuffer;
20
using v8::Context;
21
using v8::DontDelete;
22
using v8::DontEnum;
23
using v8::External;
24
using v8::Function;
25
using v8::FunctionCallbackInfo;
26
using v8::HandleScope;
27
using v8::Integer;
28
using v8::Local;
29
using v8::MaybeLocal;
30
using v8::Object;
31
using v8::ReadOnly;
32
using v8::String;
33
using v8::Value;
34
35
template int StreamBase::WriteString<ASCII>(
36
    const FunctionCallbackInfo<Value>& args);
37
template int StreamBase::WriteString<UTF8>(
38
    const FunctionCallbackInfo<Value>& args);
39
template int StreamBase::WriteString<UCS2>(
40
    const FunctionCallbackInfo<Value>& args);
41
template int StreamBase::WriteString<LATIN1>(
42
    const FunctionCallbackInfo<Value>& args);
43
44
45
46194
int StreamBase::ReadStartJS(const FunctionCallbackInfo<Value>& args) {
46
46194
  return ReadStart();
47
}
48
49
50
1199
int StreamBase::ReadStopJS(const FunctionCallbackInfo<Value>& args) {
51
1199
  return ReadStop();
52
}
53
54
6
int StreamBase::UseUserBuffer(const FunctionCallbackInfo<Value>& args) {
55
6
  CHECK(Buffer::HasInstance(args[0]));
56
57
12
  uv_buf_t buf = uv_buf_init(Buffer::Data(args[0]), Buffer::Length(args[0]));
58
6
  PushStreamListener(new CustomBufferJSListener(buf));
59
6
  return 0;
60
}
61
62
28254
int StreamBase::Shutdown(const FunctionCallbackInfo<Value>& args) {
63
56508
  CHECK(args[0]->IsObject());
64
56508
  Local<Object> req_wrap_obj = args[0].As<Object>();
65
66
28254
  return Shutdown(req_wrap_obj);
67
}
68
69
327712
void StreamBase::SetWriteResult(const StreamWriteResult& res) {
70
327712
  env_->stream_base_state()[kBytesWritten] = res.bytes;
71
327712
  env_->stream_base_state()[kLastWriteWasAsync] = res.async;
72
327712
}
73
74
12657
int StreamBase::Writev(const FunctionCallbackInfo<Value>& args) {
75
12657
  Environment* env = Environment::GetCurrent(args);
76
77
25314
  CHECK(args[0]->IsObject());
78
25314
  CHECK(args[1]->IsArray());
79
80
25314
  Local<Object> req_wrap_obj = args[0].As<Object>();
81
25314
  Local<Array> chunks = args[1].As<Array>();
82
25314
  bool all_buffers = args[2]->IsTrue();
83
84
  size_t count;
85
12657
  if (all_buffers)
86
314
    count = chunks->Length();
87
  else
88
12343
    count = chunks->Length() >> 1;
89
90
25314
  MaybeStackBuffer<uv_buf_t, 16> bufs(count);
91
92
12657
  size_t storage_size = 0;
93
  size_t offset;
94
95
12657
  if (!all_buffers) {
96
    // Determine storage size first
97
81115
    for (size_t i = 0; i < count; i++) {
98
206316
      Local<Value> chunk = chunks->Get(env->context(), i * 2).ToLocalChecked();
99
100
68772
      if (Buffer::HasInstance(chunk))
101
29349
        continue;
102
        // Buffer chunk, no additional storage required
103
104
      // String chunk
105
118269
      Local<String> string = chunk->ToString(env->context()).ToLocalChecked();
106
39423
      enum encoding encoding = ParseEncoding(env->isolate(),
107
118269
          chunks->Get(env->context(), i * 2 + 1).ToLocalChecked());
108
      size_t chunk_size;
109


86299
      if (encoding == UTF8 && string->Length() > 65535 &&
110
39429
          !StringBytes::Size(env->isolate(), string, encoding).To(&chunk_size))
111
        return 0;
112
78846
      else if (!StringBytes::StorageSize(env->isolate(), string, encoding)
113
39423
                    .To(&chunk_size))
114
        return 0;
115
39423
      storage_size += chunk_size;
116
    }
117
118
12343
    if (storage_size > INT_MAX)
119
      return UV_ENOBUFS;
120
  } else {
121
27277
    for (size_t i = 0; i < count; i++) {
122
80889
      Local<Value> chunk = chunks->Get(env->context(), i).ToLocalChecked();
123
26963
      bufs[i].base = Buffer::Data(chunk);
124
26963
      bufs[i].len = Buffer::Length(chunk);
125
    }
126
  }
127
128
25314
  AllocatedBuffer storage;
129
12657
  if (storage_size > 0)
130
12297
    storage = env->AllocateManaged(storage_size);
131
132
12657
  offset = 0;
133
12657
  if (!all_buffers) {
134
81115
    for (size_t i = 0; i < count; i++) {
135
206316
      Local<Value> chunk = chunks->Get(env->context(), i * 2).ToLocalChecked();
136
137
      // Write buffer
138
68772
      if (Buffer::HasInstance(chunk)) {
139
29349
        bufs[i].base = Buffer::Data(chunk);
140
29349
        bufs[i].len = Buffer::Length(chunk);
141
29349
        continue;
142
      }
143
144
      // Write string
145
39423
      CHECK_LE(offset, storage_size);
146
39423
      char* str_storage = storage.data() + offset;
147
39423
      size_t str_size = storage.size() - offset;
148
149
118269
      Local<String> string = chunk->ToString(env->context()).ToLocalChecked();
150
39423
      enum encoding encoding = ParseEncoding(env->isolate(),
151
118269
          chunks->Get(env->context(), i * 2 + 1).ToLocalChecked());
152
78846
      str_size = StringBytes::Write(env->isolate(),
153
                                    str_storage,
154
                                    str_size,
155
                                    string,
156
39423
                                    encoding);
157
39423
      bufs[i].base = str_storage;
158
39423
      bufs[i].len = str_size;
159
39423
      offset += str_size;
160
    }
161
  }
162
163
12657
  StreamWriteResult res = Write(*bufs, count, nullptr, req_wrap_obj);
164
12657
  SetWriteResult(res);
165

12657
  if (res.wrap != nullptr && storage_size > 0) {
166
342
    res.wrap->SetAllocatedStorage(std::move(storage));
167
  }
168
12657
  return res.err;
169
}
170
171
172
43079
int StreamBase::WriteBuffer(const FunctionCallbackInfo<Value>& args) {
173
86158
  CHECK(args[0]->IsObject());
174
175
43079
  Environment* env = Environment::GetCurrent(args);
176
177
86158
  if (!args[1]->IsUint8Array()) {
178
1
    node::THROW_ERR_INVALID_ARG_TYPE(env, "Second argument must be a buffer");
179
1
    return 0;
180
  }
181
182
86156
  Local<Object> req_wrap_obj = args[0].As<Object>();
183
  uv_buf_t buf;
184
43078
  buf.base = Buffer::Data(args[1]);
185
43078
  buf.len = Buffer::Length(args[1]);
186
187
43078
  uv_stream_t* send_handle = nullptr;
188
189

86156
  if (args[2]->IsObject() && IsIPCPipe()) {
190
    Local<Object> send_handle_obj = args[2].As<Object>();
191
192
    HandleWrap* wrap;
193
    ASSIGN_OR_RETURN_UNWRAP(&wrap, send_handle_obj, UV_EINVAL);
194
    send_handle = reinterpret_cast<uv_stream_t*>(wrap->GetHandle());
195
    // Reference LibuvStreamWrap instance to prevent it from being garbage
196
    // collected before `AfterWrite` is called.
197
    req_wrap_obj->Set(env->context(),
198
                      env->handle_string(),
199
                      send_handle_obj).Check();
200
  }
201
202
43078
  StreamWriteResult res = Write(&buf, 1, send_handle, req_wrap_obj);
203
43078
  SetWriteResult(res);
204
205
43078
  return res.err;
206
}
207
208
209
template <enum encoding enc>
210
271977
int StreamBase::WriteString(const FunctionCallbackInfo<Value>& args) {
211
271977
  Environment* env = Environment::GetCurrent(args);
212


543954
  CHECK(args[0]->IsObject());
213


815931
  CHECK(args[1]->IsString());
214
215
543954
  Local<Object> req_wrap_obj = args[0].As<Object>();
216
543954
  Local<String> string = args[1].As<String>();
217
  Local<Object> send_handle_obj;
218


543954
  if (args[2]->IsObject())
219
196
    send_handle_obj = args[2].As<Object>();
220
221
  // Compute the size of the storage that the string will be flattened into.
222
  // For UTF8 strings that are very long, go ahead and take the hit for
223
  // computing their actual size, rather than tripling the storage.
224
  size_t storage_size;
225



541472
  if (enc == UTF8 && string->Length() > 65535 &&
226
269566
      !StringBytes::Size(env->isolate(), string, enc).To(&storage_size))
227
    return 0;
228


543954
  else if (!StringBytes::StorageSize(env->isolate(), string, enc)
229
271977
                .To(&storage_size))
230
    return 0;
231
232


271977
  if (storage_size > INT_MAX)
233
    return UV_ENOBUFS;
234
235
  // Try writing immediately if write size isn't too big
236
  char stack_storage[16384];  // 16kb
237
  size_t data_size;
238
271977
  size_t synchronously_written = 0;
239
  uv_buf_t buf;
240
241




813637
  bool try_write = storage_size <= sizeof(stack_storage) &&
242


544008
                   (!IsIPCPipe() || send_handle_obj.IsEmpty());
243


271977
  if (try_write) {
244
270782
    data_size = StringBytes::Write(env->isolate(),
245
                                   stack_storage,
246
                                   storage_size,
247
                                   string,
248
                                   enc);
249
270782
    buf = uv_buf_init(stack_storage, data_size);
250
251
270782
    uv_buf_t* bufs = &buf;
252
270782
    size_t count = 1;
253
270782
    const int err = DoTryWrite(&bufs, &count);
254
    // Keep track of the bytes written here, because we're taking a shortcut
255
    // by using `DoTryWrite()` directly instead of using the utilities
256
    // provided by `Write()`.
257


270782
    synchronously_written = count == 0 ? data_size : data_size - buf.len;
258
270782
    bytes_written_ += synchronously_written;
259
260
    // Immediate failure or success
261




270782
    if (err != 0 || count == 0) {
262
269075
      SetWriteResult(StreamWriteResult { false, err, nullptr, data_size });
263
269075
      return err;
264
    }
265
266
    // Partial write
267


1707
    CHECK_EQ(count, 1);
268
  }
269
270
5804
  AllocatedBuffer data;
271
272


2902
  if (try_write) {
273
    // Copy partial data
274
1707
    data = env->AllocateManaged(buf.len);
275
1707
    memcpy(data.data(), buf.base, buf.len);
276
1707
    data_size = buf.len;
277
  } else {
278
    // Write it
279
1195
    data = env->AllocateManaged(storage_size);
280
1195
    data_size = StringBytes::Write(env->isolate(),
281
                                   data.data(),
282
                                   storage_size,
283
                                   string,
284
                                   enc);
285
  }
286
287


2902
  CHECK_LE(data_size, storage_size);
288
289
2902
  buf = uv_buf_init(data.data(), data_size);
290
291
2902
  uv_stream_t* send_handle = nullptr;
292
293






3001
  if (IsIPCPipe() && !send_handle_obj.IsEmpty()) {
294
    HandleWrap* wrap;
295


98
    ASSIGN_OR_RETURN_UNWRAP(&wrap, send_handle_obj, UV_EINVAL);
296
98
    send_handle = reinterpret_cast<uv_stream_t*>(wrap->GetHandle());
297
    // Reference LibuvStreamWrap instance to prevent it from being garbage
298
    // collected before `AfterWrite` is called.
299
392
    req_wrap_obj->Set(env->context(),
300
                      env->handle_string(),
301
                      send_handle_obj).Check();
302
  }
303
304
2902
  StreamWriteResult res = Write(&buf, 1, send_handle, req_wrap_obj);
305
2902
  res.bytes += synchronously_written;
306
307
2902
  SetWriteResult(res);
308


2902
  if (res.wrap != nullptr) {
309
2882
    res.wrap->SetAllocatedStorage(std::move(data));
310
  }
311
312
2902
  return res.err;
313
}
314
315
316
307122
MaybeLocal<Value> StreamBase::CallJSOnreadMethod(ssize_t nread,
317
                                                 Local<ArrayBuffer> ab,
318
                                                 size_t offset,
319
                                                 StreamBaseJSChecks checks) {
320
307122
  Environment* env = env_;
321
322
  DCHECK_EQ(static_cast<int32_t>(nread), nread);
323
  DCHECK_LE(offset, INT32_MAX);
324
325
307122
  if (checks == DONT_SKIP_NREAD_CHECKS) {
326
307096
    if (ab.IsEmpty()) {
327
      DCHECK_EQ(offset, 0);
328
      DCHECK_LE(nread, 0);
329
    } else {
330
      DCHECK_GE(nread, 0);
331
    }
332
  }
333
334
307122
  env->stream_base_state()[kReadBytesOrError] = nread;
335
307122
  env->stream_base_state()[kArrayBufferOffset] = offset;
336
337
  Local<Value> argv[] = {
338
652446
    ab.IsEmpty() ? Undefined(env->isolate()).As<Value>() : ab.As<Value>()
339
1228488
  };
340
341
307122
  AsyncWrap* wrap = GetAsyncWrap();
342
307122
  CHECK_NOT_NULL(wrap);
343
921366
  Local<Value> onread = wrap->object()->GetInternalField(kOnReadFunctionField);
344
307122
  CHECK(onread->IsFunction());
345
614244
  return wrap->MakeCallback(onread.As<Function>(), arraysize(argv), argv);
346
}
347
348
349
3335
bool StreamBase::IsIPCPipe() {
350
3335
  return false;
351
}
352
353
354
int StreamBase::GetFD() {
355
  return -1;
356
}
357
358
359
46237
Local<Object> StreamBase::GetObject() {
360
46237
  return GetAsyncWrap()->object();
361
}
362
363
38128
void StreamBase::AddMethod(Environment* env,
364
                           Local<Signature> signature,
365
                           enum PropertyAttribute attributes,
366
                           Local<FunctionTemplate> t,
367
                           JSMethodFunction* stream_method,
368
                           Local<String> string) {
369
  Local<FunctionTemplate> templ =
370
      env->NewFunctionTemplate(stream_method,
371
                               signature,
372
                               v8::ConstructorBehavior::kThrow,
373
38128
                               v8::SideEffectType::kHasNoSideEffect);
374
114384
  t->PrototypeTemplate()->SetAccessorProperty(
375
38128
      string, templ, Local<FunctionTemplate>(), attributes);
376
38128
}
377
378
9532
void StreamBase::AddMethods(Environment* env, Local<FunctionTemplate> t) {
379
19064
  HandleScope scope(env->isolate());
380
381
  enum PropertyAttribute attributes =
382
9532
      static_cast<PropertyAttribute>(ReadOnly | DontDelete | DontEnum);
383
9532
  Local<Signature> sig = Signature::New(env->isolate(), t);
384
385
9532
  AddMethod(env, sig, attributes, t, GetFD, env->fd_string());
386
9532
  AddMethod(
387
9532
      env, sig, attributes, t, GetExternal, env->external_stream_string());
388
9532
  AddMethod(env, sig, attributes, t, GetBytesRead, env->bytes_read_string());
389
9532
  AddMethod(
390
9532
      env, sig, attributes, t, GetBytesWritten, env->bytes_written_string());
391
9532
  env->SetProtoMethod(t, "readStart", JSMethod<&StreamBase::ReadStartJS>);
392
9532
  env->SetProtoMethod(t, "readStop", JSMethod<&StreamBase::ReadStopJS>);
393
9532
  env->SetProtoMethod(t, "shutdown", JSMethod<&StreamBase::Shutdown>);
394
  env->SetProtoMethod(t,
395
                      "useUserBuffer",
396
9532
                      JSMethod<&StreamBase::UseUserBuffer>);
397
9532
  env->SetProtoMethod(t, "writev", JSMethod<&StreamBase::Writev>);
398
9532
  env->SetProtoMethod(t, "writeBuffer", JSMethod<&StreamBase::WriteBuffer>);
399
  env->SetProtoMethod(
400
9532
      t, "writeAsciiString", JSMethod<&StreamBase::WriteString<ASCII>>);
401
  env->SetProtoMethod(
402
9532
      t, "writeUtf8String", JSMethod<&StreamBase::WriteString<UTF8>>);
403
  env->SetProtoMethod(
404
9532
      t, "writeUcs2String", JSMethod<&StreamBase::WriteString<UCS2>>);
405
  env->SetProtoMethod(
406
9532
      t, "writeLatin1String", JSMethod<&StreamBase::WriteString<LATIN1>>);
407
47660
  t->PrototypeTemplate()->Set(FIXED_ONE_BYTE_STRING(env->isolate(),
408
                                                    "isStreamBase"),
409
9532
                              True(env->isolate()));
410
38128
  t->PrototypeTemplate()->SetAccessor(
411
      FIXED_ONE_BYTE_STRING(env->isolate(), "onread"),
412
      BaseObject::InternalFieldGet<kOnReadFunctionField>,
413
9532
      BaseObject::InternalFieldSet<kOnReadFunctionField, &Value::IsFunction>);
414
9532
}
415
416
389
void StreamBase::GetFD(const FunctionCallbackInfo<Value>& args) {
417
  // Mimic implementation of StreamBase::GetFD() and UDPWrap::GetFD().
418
778
  StreamBase* wrap = StreamBase::FromObject(args.This().As<Object>());
419
389
  if (wrap == nullptr) return args.GetReturnValue().Set(UV_EINVAL);
420
421
389
  if (!wrap->IsAlive()) return args.GetReturnValue().Set(UV_EINVAL);
422
423
1167
  args.GetReturnValue().Set(wrap->GetFD());
424
}
425
426
34209
void StreamBase::GetBytesRead(const FunctionCallbackInfo<Value>& args) {
427
68418
  StreamBase* wrap = StreamBase::FromObject(args.This().As<Object>());
428
34211
  if (wrap == nullptr) return args.GetReturnValue().Set(0);
429
430
  // uint64_t -> double. 53bits is enough for all real cases.
431
102624
  args.GetReturnValue().Set(static_cast<double>(wrap->bytes_read_));
432
}
433
434
34209
void StreamBase::GetBytesWritten(const FunctionCallbackInfo<Value>& args) {
435
68418
  StreamBase* wrap = StreamBase::FromObject(args.This().As<Object>());
436
34209
  if (wrap == nullptr) return args.GetReturnValue().Set(0);
437
438
  // uint64_t -> double. 53bits is enough for all real cases.
439
102627
  args.GetReturnValue().Set(static_cast<double>(wrap->bytes_written_));
440
}
441
442
2
void StreamBase::GetExternal(const FunctionCallbackInfo<Value>& args) {
443
4
  StreamBase* wrap = StreamBase::FromObject(args.This().As<Object>());
444
2
  if (wrap == nullptr) return;
445
446
2
  Local<External> ext = External::New(args.GetIsolate(), wrap);
447
4
  args.GetReturnValue().Set(ext);
448
}
449
450
template <int (StreamBase::*Method)(const FunctionCallbackInfo<Value>& args)>
451
403367
void StreamBase::JSMethod(const FunctionCallbackInfo<Value>& args) {
452
806734
  StreamBase* wrap = StreamBase::FromObject(args.Holder().As<Object>());
453





403368
  if (wrap == nullptr) return;
454
455





403369
  if (!wrap->IsAlive()) return args.GetReturnValue().Set(UV_EINVAL);
456
457
806732
  AsyncHooks::DefaultTriggerAsyncIdScope trigger_scope(wrap->GetAsyncWrap());
458
1210098
  args.GetReturnValue().Set((wrap->*Method)(args));
459
}
460
461
8611
int StreamResource::DoTryWrite(uv_buf_t** bufs, size_t* count) {
462
  // No TryWrite by default
463
8611
  return 0;
464
}
465
466
467
41342
const char* StreamResource::Error() const {
468
41342
  return nullptr;
469
}
470
471
472
void StreamResource::ClearError() {
473
  // No-op
474
}
475
476
477
279084
uv_buf_t EmitToJSStreamListener::OnStreamAlloc(size_t suggested_size) {
478
279084
  CHECK_NOT_NULL(stream_);
479
279084
  Environment* env = static_cast<StreamBase*>(stream_)->stream_env();
480
279084
  return env->AllocateManaged(suggested_size).release();
481
}
482
483
293411
void EmitToJSStreamListener::OnStreamRead(ssize_t nread, const uv_buf_t& buf_) {
484
293411
  CHECK_NOT_NULL(stream_);
485
293411
  StreamBase* stream = static_cast<StreamBase*>(stream_);
486
293411
  Environment* env = stream->stream_env();
487
567688
  HandleScope handle_scope(env->isolate());
488
567688
  Context::Scope context_scope(env->context());
489
567688
  AllocatedBuffer buf(env, buf_);
490
491
293411
  if (nread <= 0)  {
492
19092
    if (nread < 0)
493
19075
      stream->CallJSOnreadMethod(nread, Local<ArrayBuffer>());
494
19063
    return;
495
  }
496
497
274319
  CHECK_LE(static_cast<size_t>(nread), buf.size());
498
274319
  buf.Resize(nread);
499
500
274319
  stream->CallJSOnreadMethod(nread, buf.ToArrayBuffer());
501
}
502
503
504
26
uv_buf_t CustomBufferJSListener::OnStreamAlloc(size_t suggested_size) {
505
26
  return buffer_;
506
}
507
508
509
26
void CustomBufferJSListener::OnStreamRead(ssize_t nread, const uv_buf_t& buf) {
510
26
  CHECK_NOT_NULL(stream_);
511
26
  CHECK_EQ(buf.base, buffer_.base);
512
513
26
  StreamBase* stream = static_cast<StreamBase*>(stream_);
514
26
  Environment* env = stream->stream_env();
515
52
  HandleScope handle_scope(env->isolate());
516
26
  Context::Scope context_scope(env->context());
517
518
  MaybeLocal<Value> ret = stream->CallJSOnreadMethod(nread,
519
                             Local<ArrayBuffer>(),
520
                             0,
521
26
                             StreamBase::SKIP_NREAD_CHECKS);
522
  Local<Value> next_buf_v;
523

78
  if (ret.ToLocal(&next_buf_v) && !next_buf_v->IsUndefined()) {
524
12
    buffer_.base = Buffer::Data(next_buf_v);
525
12
    buffer_.len = Buffer::Length(next_buf_v);
526
  }
527
26
}
528
529
530
10025
void ReportWritesToJSStreamListener::OnStreamAfterReqFinished(
531
    StreamReq* req_wrap, int status) {
532
10025
  StreamBase* stream = static_cast<StreamBase*>(stream_);
533
10025
  Environment* env = stream->stream_env();
534
10025
  AsyncWrap* async_wrap = req_wrap->GetAsyncWrap();
535
20049
  HandleScope handle_scope(env->isolate());
536
10025
  Context::Scope context_scope(env->context());
537
20050
  CHECK(!async_wrap->persistent().IsEmpty());
538
10025
  Local<Object> req_wrap_obj = async_wrap->object();
539
540
  Local<Value> argv[] = {
541
    Integer::New(env->isolate(), status),
542
10025
    stream->GetObject(),
543
    Undefined(env->isolate())
544
50125
  };
545
546
10025
  const char* msg = stream->Error();
547
10025
  if (msg != nullptr) {
548
    argv[2] = OneByteString(env->isolate(), msg);
549
    stream->ClearError();
550
  }
551
552
40100
  if (req_wrap_obj->Has(env->context(), env->oncomplete_string()).FromJust())
553
10024
    async_wrap->MakeCallback(env->oncomplete_string(), arraysize(argv), argv);
554
10024
}
555
556
5145
void ReportWritesToJSStreamListener::OnStreamAfterWrite(
557
    WriteWrap* req_wrap, int status) {
558
5145
  OnStreamAfterReqFinished(req_wrap, status);
559
5144
}
560
561
4880
void ReportWritesToJSStreamListener::OnStreamAfterShutdown(
562
    ShutdownWrap* req_wrap, int status) {
563
4880
  OnStreamAfterReqFinished(req_wrap, status);
564
4880
}
565
566
567
}  // namespace node