1 |
|
|
// Copyright Joyent, Inc. and other Node contributors. |
2 |
|
|
// |
3 |
|
|
// Permission is hereby granted, free of charge, to any person obtaining a |
4 |
|
|
// copy of this software and associated documentation files (the |
5 |
|
|
// "Software"), to deal in the Software without restriction, including |
6 |
|
|
// without limitation the rights to use, copy, modify, merge, publish, |
7 |
|
|
// distribute, sublicense, and/or sell copies of the Software, and to permit |
8 |
|
|
// persons to whom the Software is furnished to do so, subject to the |
9 |
|
|
// following conditions: |
10 |
|
|
// |
11 |
|
|
// The above copyright notice and this permission notice shall be included |
12 |
|
|
// in all copies or substantial portions of the Software. |
13 |
|
|
// |
14 |
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
15 |
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
16 |
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN |
17 |
|
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
18 |
|
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
19 |
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
20 |
|
|
// USE OR OTHER DEALINGS IN THE SOFTWARE. |
21 |
|
|
|
22 |
|
|
#include "stream_wrap.h" |
23 |
|
|
#include "stream_base-inl.h" |
24 |
|
|
|
25 |
|
|
#include "env-inl.h" |
26 |
|
|
#include "handle_wrap.h" |
27 |
|
|
#include "node_buffer.h" |
28 |
|
|
#include "node_external_reference.h" |
29 |
|
|
#include "pipe_wrap.h" |
30 |
|
|
#include "req_wrap-inl.h" |
31 |
|
|
#include "tcp_wrap.h" |
32 |
|
|
#include "udp_wrap.h" |
33 |
|
|
#include "util-inl.h" |
34 |
|
|
|
35 |
|
|
#include <cstring> // memcpy() |
36 |
|
|
#include <climits> // INT_MAX |
37 |
|
|
|
38 |
|
|
|
39 |
|
|
namespace node { |
40 |
|
|
|
41 |
|
|
using v8::Context; |
42 |
|
|
using v8::DontDelete; |
43 |
|
|
using v8::EscapableHandleScope; |
44 |
|
|
using v8::FunctionCallbackInfo; |
45 |
|
|
using v8::FunctionTemplate; |
46 |
|
|
using v8::HandleScope; |
47 |
|
|
using v8::Local; |
48 |
|
|
using v8::MaybeLocal; |
49 |
|
|
using v8::Object; |
50 |
|
|
using v8::PropertyAttribute; |
51 |
|
|
using v8::ReadOnly; |
52 |
|
|
using v8::Signature; |
53 |
|
|
using v8::Value; |
54 |
|
|
|
55 |
|
125041 |
void IsConstructCallCallback(const FunctionCallbackInfo<Value>& args) { |
56 |
✗✓ |
125041 |
CHECK(args.IsConstructCall()); |
57 |
|
125041 |
StreamReq::ResetObject(args.This()); |
58 |
|
125041 |
} |
59 |
|
|
|
60 |
|
1269 |
void LibuvStreamWrap::Initialize(Local<Object> target, |
61 |
|
|
Local<Value> unused, |
62 |
|
|
Local<Context> context, |
63 |
|
|
void* priv) { |
64 |
|
1269 |
Environment* env = Environment::GetCurrent(context); |
65 |
|
|
|
66 |
|
|
Local<FunctionTemplate> sw = |
67 |
|
1269 |
FunctionTemplate::New(env->isolate(), IsConstructCallCallback); |
68 |
|
2538 |
sw->InstanceTemplate()->SetInternalFieldCount(StreamReq::kInternalFieldCount); |
69 |
|
|
|
70 |
|
|
// we need to set handle and callback to null, |
71 |
|
|
// so that those fields are created and functions |
72 |
|
|
// do not become megamorphic |
73 |
|
|
// Fields: |
74 |
|
|
// - oncomplete |
75 |
|
|
// - callback |
76 |
|
|
// - handle |
77 |
|
5076 |
sw->InstanceTemplate()->Set( |
78 |
|
|
env->oncomplete_string(), |
79 |
|
|
v8::Null(env->isolate())); |
80 |
|
5076 |
sw->InstanceTemplate()->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "callback"), |
81 |
|
|
v8::Null(env->isolate())); |
82 |
|
5076 |
sw->InstanceTemplate()->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "handle"), |
83 |
|
|
v8::Null(env->isolate())); |
84 |
|
|
|
85 |
|
1269 |
sw->Inherit(AsyncWrap::GetConstructorTemplate(env)); |
86 |
|
|
|
87 |
|
1269 |
env->SetConstructorFunction(target, "ShutdownWrap", sw); |
88 |
|
1269 |
env->set_shutdown_wrap_template(sw->InstanceTemplate()); |
89 |
|
|
|
90 |
|
|
Local<FunctionTemplate> ww = |
91 |
|
1269 |
FunctionTemplate::New(env->isolate(), IsConstructCallCallback); |
92 |
|
2538 |
ww->InstanceTemplate()->SetInternalFieldCount( |
93 |
|
|
StreamReq::kInternalFieldCount); |
94 |
|
1269 |
ww->Inherit(AsyncWrap::GetConstructorTemplate(env)); |
95 |
|
1269 |
env->SetConstructorFunction(target, "WriteWrap", ww); |
96 |
|
1269 |
env->set_write_wrap_template(ww->InstanceTemplate()); |
97 |
|
|
|
98 |
|
3807 |
NODE_DEFINE_CONSTANT(target, kReadBytesOrError); |
99 |
|
3807 |
NODE_DEFINE_CONSTANT(target, kArrayBufferOffset); |
100 |
|
3807 |
NODE_DEFINE_CONSTANT(target, kBytesWritten); |
101 |
|
3807 |
NODE_DEFINE_CONSTANT(target, kLastWriteWasAsync); |
102 |
|
1269 |
target->Set(context, FIXED_ONE_BYTE_STRING(env->isolate(), "streamBaseState"), |
103 |
|
3807 |
env->stream_base_state().GetJSArray()).Check(); |
104 |
|
1269 |
} |
105 |
|
|
|
106 |
|
5213 |
void LibuvStreamWrap::RegisterExternalReferences( |
107 |
|
|
ExternalReferenceRegistry* registry) { |
108 |
|
5213 |
registry->Register(IsConstructCallCallback); |
109 |
|
5213 |
registry->Register(GetWriteQueueSize); |
110 |
|
5213 |
registry->Register(SetBlocking); |
111 |
|
5213 |
StreamBase::RegisterExternalReferences(registry); |
112 |
|
5213 |
} |
113 |
|
|
|
114 |
|
18936 |
LibuvStreamWrap::LibuvStreamWrap(Environment* env, |
115 |
|
|
Local<Object> object, |
116 |
|
|
uv_stream_t* stream, |
117 |
|
18936 |
AsyncWrap::ProviderType provider) |
118 |
|
|
: HandleWrap(env, |
119 |
|
|
object, |
120 |
|
|
reinterpret_cast<uv_handle_t*>(stream), |
121 |
|
|
provider), |
122 |
|
|
StreamBase(env), |
123 |
|
18936 |
stream_(stream) { |
124 |
|
18936 |
StreamBase::AttachToObject(object); |
125 |
|
18936 |
} |
126 |
|
|
|
127 |
|
|
|
128 |
|
18060 |
Local<FunctionTemplate> LibuvStreamWrap::GetConstructorTemplate( |
129 |
|
|
Environment* env) { |
130 |
|
18060 |
Local<FunctionTemplate> tmpl = env->libuv_stream_wrap_ctor_template(); |
131 |
✓✓ |
18060 |
if (tmpl.IsEmpty()) { |
132 |
|
6441 |
tmpl = env->NewFunctionTemplate(nullptr); |
133 |
|
6441 |
tmpl->SetClassName( |
134 |
|
|
FIXED_ONE_BYTE_STRING(env->isolate(), "LibuvStreamWrap")); |
135 |
|
6441 |
tmpl->Inherit(HandleWrap::GetConstructorTemplate(env)); |
136 |
|
12882 |
tmpl->InstanceTemplate()->SetInternalFieldCount( |
137 |
|
|
StreamBase::kInternalFieldCount); |
138 |
|
|
Local<FunctionTemplate> get_write_queue_size = |
139 |
|
|
FunctionTemplate::New(env->isolate(), |
140 |
|
|
GetWriteQueueSize, |
141 |
|
|
Local<Value>(), |
142 |
|
6441 |
Signature::New(env->isolate(), tmpl)); |
143 |
|
25764 |
tmpl->PrototypeTemplate()->SetAccessorProperty( |
144 |
|
|
env->write_queue_size_string(), |
145 |
|
|
get_write_queue_size, |
146 |
|
|
Local<FunctionTemplate>(), |
147 |
|
|
static_cast<PropertyAttribute>(ReadOnly | DontDelete)); |
148 |
|
6441 |
env->SetProtoMethod(tmpl, "setBlocking", SetBlocking); |
149 |
|
6441 |
StreamBase::AddMethods(env, tmpl); |
150 |
|
6441 |
env->set_libuv_stream_wrap_ctor_template(tmpl); |
151 |
|
|
} |
152 |
|
18060 |
return tmpl; |
153 |
|
|
} |
154 |
|
|
|
155 |
|
|
|
156 |
|
3924 |
LibuvStreamWrap* LibuvStreamWrap::From(Environment* env, Local<Object> object) { |
157 |
|
3924 |
Local<FunctionTemplate> sw = env->libuv_stream_wrap_ctor_template(); |
158 |
✓✗✗✓ ✗✓ |
7848 |
CHECK(!sw.IsEmpty() && sw->HasInstance(object)); |
159 |
|
3924 |
return Unwrap<LibuvStreamWrap>(object); |
160 |
|
|
} |
161 |
|
|
|
162 |
|
|
|
163 |
|
120 |
int LibuvStreamWrap::GetFD() { |
164 |
|
|
#ifdef _WIN32 |
165 |
|
|
return fd_; |
166 |
|
|
#else |
167 |
|
120 |
int fd = -1; |
168 |
✓✗ |
120 |
if (stream() != nullptr) |
169 |
|
120 |
uv_fileno(reinterpret_cast<uv_handle_t*>(stream()), &fd); |
170 |
|
120 |
return fd; |
171 |
|
|
#endif |
172 |
|
|
} |
173 |
|
|
|
174 |
|
|
|
175 |
|
123251 |
bool LibuvStreamWrap::IsAlive() { |
176 |
|
123251 |
return HandleWrap::IsAlive(this); |
177 |
|
|
} |
178 |
|
|
|
179 |
|
|
|
180 |
|
3 |
bool LibuvStreamWrap::IsClosing() { |
181 |
|
3 |
return uv_is_closing(reinterpret_cast<uv_handle_t*>(stream())); |
182 |
|
|
} |
183 |
|
|
|
184 |
|
|
|
185 |
|
171698 |
AsyncWrap* LibuvStreamWrap::GetAsyncWrap() { |
186 |
|
171698 |
return static_cast<AsyncWrap*>(this); |
187 |
|
|
} |
188 |
|
|
|
189 |
|
|
|
190 |
|
27324 |
bool LibuvStreamWrap::IsIPCPipe() { |
191 |
|
27324 |
return is_named_pipe_ipc(); |
192 |
|
|
} |
193 |
|
|
|
194 |
|
|
|
195 |
|
21988 |
int LibuvStreamWrap::ReadStart() { |
196 |
|
43976 |
return uv_read_start(stream(), [](uv_handle_t* handle, |
197 |
|
|
size_t suggested_size, |
198 |
|
73105 |
uv_buf_t* buf) { |
199 |
|
73105 |
static_cast<LibuvStreamWrap*>(handle->data)->OnUvAlloc(suggested_size, buf); |
200 |
|
146433 |
}, [](uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { |
201 |
|
73328 |
static_cast<LibuvStreamWrap*>(stream->data)->OnUvRead(nread, buf); |
202 |
|
21988 |
}); |
203 |
|
|
} |
204 |
|
|
|
205 |
|
|
|
206 |
|
9190 |
int LibuvStreamWrap::ReadStop() { |
207 |
|
9190 |
return uv_read_stop(stream()); |
208 |
|
|
} |
209 |
|
|
|
210 |
|
|
|
211 |
|
73105 |
void LibuvStreamWrap::OnUvAlloc(size_t suggested_size, uv_buf_t* buf) { |
212 |
|
146210 |
HandleScope scope(env()->isolate()); |
213 |
|
73105 |
Context::Scope context_scope(env()->context()); |
214 |
|
|
|
215 |
|
73105 |
*buf = EmitAlloc(suggested_size); |
216 |
|
73105 |
} |
217 |
|
|
|
218 |
|
|
template <class WrapType> |
219 |
|
320 |
static MaybeLocal<Object> AcceptHandle(Environment* env, |
220 |
|
|
LibuvStreamWrap* parent) { |
221 |
|
|
static_assert(std::is_base_of<LibuvStreamWrap, WrapType>::value || |
222 |
|
|
std::is_base_of<UDPWrap, WrapType>::value, |
223 |
|
|
"Can only accept stream handles"); |
224 |
|
|
|
225 |
|
320 |
EscapableHandleScope scope(env->isolate()); |
226 |
|
|
Local<Object> wrap_obj; |
227 |
|
|
|
228 |
✗✓ |
640 |
if (!WrapType::Instantiate(env, parent, WrapType::SOCKET).ToLocal(&wrap_obj)) |
229 |
|
|
return Local<Object>(); |
230 |
|
|
|
231 |
|
320 |
HandleWrap* wrap = Unwrap<HandleWrap>(wrap_obj); |
232 |
✗✓ |
320 |
CHECK_NOT_NULL(wrap); |
233 |
|
320 |
uv_stream_t* stream = reinterpret_cast<uv_stream_t*>(wrap->GetHandle()); |
234 |
✗✓ |
320 |
CHECK_NOT_NULL(stream); |
235 |
|
|
|
236 |
✗✓ |
320 |
if (uv_accept(parent->stream(), stream)) |
237 |
|
|
ABORT(); |
238 |
|
|
|
239 |
|
320 |
return scope.Escape(wrap_obj); |
240 |
|
|
} |
241 |
|
|
|
242 |
|
|
|
243 |
|
73328 |
void LibuvStreamWrap::OnUvRead(ssize_t nread, const uv_buf_t* buf) { |
244 |
|
73328 |
HandleScope scope(env()->isolate()); |
245 |
|
73328 |
Context::Scope context_scope(env()->context()); |
246 |
|
73328 |
uv_handle_type type = UV_UNKNOWN_HANDLE; |
247 |
|
|
|
248 |
✓✓✓✓ ✓✓ |
77294 |
if (is_named_pipe_ipc() && |
249 |
|
3966 |
uv_pipe_pending_count(reinterpret_cast<uv_pipe_t*>(stream())) > 0) { |
250 |
|
160 |
type = uv_pipe_pending_type(reinterpret_cast<uv_pipe_t*>(stream())); |
251 |
|
|
} |
252 |
|
|
|
253 |
|
|
// We should not be getting this callback if someone has already called |
254 |
|
|
// uv_close() on the handle. |
255 |
✗✓ |
73328 |
CHECK_EQ(persistent().IsEmpty(), false); |
256 |
|
|
|
257 |
✓✓ |
73328 |
if (nread > 0) { |
258 |
|
|
MaybeLocal<Object> pending_obj; |
259 |
|
|
|
260 |
✓✓ |
66408 |
if (type == UV_TCP) { |
261 |
|
129 |
pending_obj = AcceptHandle<TCPWrap>(env(), this); |
262 |
✓✓ |
66279 |
} else if (type == UV_NAMED_PIPE) { |
263 |
|
1 |
pending_obj = AcceptHandle<PipeWrap>(env(), this); |
264 |
✓✓ |
66278 |
} else if (type == UV_UDP) { |
265 |
|
30 |
pending_obj = AcceptHandle<UDPWrap>(env(), this); |
266 |
|
|
} else { |
267 |
✗✓ |
66248 |
CHECK_EQ(type, UV_UNKNOWN_HANDLE); |
268 |
|
|
} |
269 |
|
|
|
270 |
|
|
Local<Object> local_pending_obj; |
271 |
✓✓ |
66568 |
if (pending_obj.ToLocal(&local_pending_obj) && |
272 |
|
160 |
object()->Set(env()->context(), |
273 |
|
|
env()->pending_handle_string(), |
274 |
✗✓✗✓
|
66888 |
local_pending_obj).IsNothing()) { |
275 |
|
|
return; |
276 |
|
|
} |
277 |
|
|
} |
278 |
|
|
|
279 |
|
73328 |
EmitRead(nread, *buf); |
280 |
|
|
} |
281 |
|
|
|
282 |
|
|
|
283 |
|
5409 |
void LibuvStreamWrap::GetWriteQueueSize( |
284 |
|
|
const FunctionCallbackInfo<Value>& info) { |
285 |
|
|
LibuvStreamWrap* wrap; |
286 |
✗✓ |
5409 |
ASSIGN_OR_RETURN_UNWRAP(&wrap, info.This()); |
287 |
|
|
|
288 |
✗✓ |
5409 |
if (wrap->stream() == nullptr) { |
289 |
|
|
info.GetReturnValue().Set(0); |
290 |
|
|
return; |
291 |
|
|
} |
292 |
|
|
|
293 |
|
5409 |
uint32_t write_queue_size = wrap->stream()->write_queue_size; |
294 |
✓✗ |
10818 |
info.GetReturnValue().Set(write_queue_size); |
295 |
|
|
} |
296 |
|
|
|
297 |
|
|
|
298 |
|
32 |
void LibuvStreamWrap::SetBlocking(const FunctionCallbackInfo<Value>& args) { |
299 |
|
|
LibuvStreamWrap* wrap; |
300 |
✗✓ |
32 |
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); |
301 |
|
|
|
302 |
✗✓ |
32 |
CHECK_GT(args.Length(), 0); |
303 |
✗✓ |
32 |
if (!wrap->IsAlive()) |
304 |
|
|
return args.GetReturnValue().Set(UV_EINVAL); |
305 |
|
|
|
306 |
|
32 |
bool enable = args[0]->IsTrue(); |
307 |
|
64 |
args.GetReturnValue().Set(uv_stream_set_blocking(wrap->stream(), enable)); |
308 |
|
|
} |
309 |
|
|
|
310 |
|
|
typedef SimpleShutdownWrap<ReqWrap<uv_shutdown_t>> LibuvShutdownWrap; |
311 |
|
|
typedef SimpleWriteWrap<ReqWrap<uv_write_t>> LibuvWriteWrap; |
312 |
|
|
|
313 |
|
10015 |
ShutdownWrap* LibuvStreamWrap::CreateShutdownWrap(Local<Object> object) { |
314 |
|
10015 |
return new LibuvShutdownWrap(this, object); |
315 |
|
|
} |
316 |
|
|
|
317 |
|
266 |
WriteWrap* LibuvStreamWrap::CreateWriteWrap(Local<Object> object) { |
318 |
|
266 |
return new LibuvWriteWrap(this, object); |
319 |
|
|
} |
320 |
|
|
|
321 |
|
|
|
322 |
|
10015 |
int LibuvStreamWrap::DoShutdown(ShutdownWrap* req_wrap_) { |
323 |
|
10015 |
LibuvShutdownWrap* req_wrap = static_cast<LibuvShutdownWrap*>(req_wrap_); |
324 |
|
10015 |
return req_wrap->Dispatch(uv_shutdown, stream(), AfterUvShutdown); |
325 |
|
|
} |
326 |
|
|
|
327 |
|
|
|
328 |
|
10011 |
void LibuvStreamWrap::AfterUvShutdown(uv_shutdown_t* req, int status) { |
329 |
✓✗ |
10011 |
LibuvShutdownWrap* req_wrap = static_cast<LibuvShutdownWrap*>( |
330 |
|
10011 |
LibuvShutdownWrap::from_req(req)); |
331 |
✗✓ |
10011 |
CHECK_NOT_NULL(req_wrap); |
332 |
|
20022 |
HandleScope scope(req_wrap->env()->isolate()); |
333 |
|
10011 |
Context::Scope context_scope(req_wrap->env()->context()); |
334 |
|
10011 |
req_wrap->Done(status); |
335 |
|
10011 |
} |
336 |
|
|
|
337 |
|
|
|
338 |
|
|
// NOTE: Call to this function could change both `buf`'s and `count`'s |
339 |
|
|
// values, shifting their base and decrementing their length. This is |
340 |
|
|
// required in order to skip the data that was successfully written via |
341 |
|
|
// uv_try_write(). |
342 |
|
120855 |
int LibuvStreamWrap::DoTryWrite(uv_buf_t** bufs, size_t* count) { |
343 |
|
|
int err; |
344 |
|
|
size_t written; |
345 |
|
120855 |
uv_buf_t* vbufs = *bufs; |
346 |
|
120855 |
size_t vcount = *count; |
347 |
|
|
|
348 |
|
120855 |
err = uv_try_write(stream(), vbufs, vcount); |
349 |
✓✗✓✓
|
120855 |
if (err == UV_ENOSYS || err == UV_EAGAIN) |
350 |
|
27 |
return 0; |
351 |
✓✓ |
120828 |
if (err < 0) |
352 |
|
2510 |
return err; |
353 |
|
|
|
354 |
|
|
// Slice off the buffers: skip all written buffers and slice the one that |
355 |
|
|
// was partially written. |
356 |
|
118318 |
written = err; |
357 |
✓✓ |
314867 |
for (; vcount > 0; vbufs++, vcount--) { |
358 |
|
|
// Slice |
359 |
✓✓ |
196682 |
if (vbufs[0].len > written) { |
360 |
|
133 |
vbufs[0].base += written; |
361 |
|
133 |
vbufs[0].len -= written; |
362 |
|
133 |
written = 0; |
363 |
|
133 |
break; |
364 |
|
|
|
365 |
|
|
// Discard |
366 |
|
|
} else { |
367 |
|
196549 |
written -= vbufs[0].len; |
368 |
|
|
} |
369 |
|
|
} |
370 |
|
|
|
371 |
|
118318 |
*bufs = vbufs; |
372 |
|
118318 |
*count = vcount; |
373 |
|
|
|
374 |
|
118318 |
return 0; |
375 |
|
|
} |
376 |
|
|
|
377 |
|
|
|
378 |
|
266 |
int LibuvStreamWrap::DoWrite(WriteWrap* req_wrap, |
379 |
|
|
uv_buf_t* bufs, |
380 |
|
|
size_t count, |
381 |
|
|
uv_stream_t* send_handle) { |
382 |
|
266 |
LibuvWriteWrap* w = static_cast<LibuvWriteWrap*>(req_wrap); |
383 |
|
266 |
return w->Dispatch(uv_write2, |
384 |
|
|
stream(), |
385 |
|
|
bufs, |
386 |
|
|
count, |
387 |
|
|
send_handle, |
388 |
|
266 |
AfterUvWrite); |
389 |
|
|
} |
390 |
|
|
|
391 |
|
|
|
392 |
|
|
|
393 |
|
265 |
void LibuvStreamWrap::AfterUvWrite(uv_write_t* req, int status) { |
394 |
✓✗ |
265 |
LibuvWriteWrap* req_wrap = static_cast<LibuvWriteWrap*>( |
395 |
|
265 |
LibuvWriteWrap::from_req(req)); |
396 |
✗✓ |
265 |
CHECK_NOT_NULL(req_wrap); |
397 |
|
529 |
HandleScope scope(req_wrap->env()->isolate()); |
398 |
|
265 |
Context::Scope context_scope(req_wrap->env()->context()); |
399 |
|
265 |
req_wrap->Done(status); |
400 |
|
264 |
} |
401 |
|
|
|
402 |
|
|
} // namespace node |
403 |
|
|
|
404 |
|
5281 |
NODE_MODULE_CONTEXT_AWARE_INTERNAL(stream_wrap, |
405 |
|
|
node::LibuvStreamWrap::Initialize) |
406 |
|
5213 |
NODE_MODULE_EXTERNAL_REFERENCE( |
407 |
|
|
stream_wrap, node::LibuvStreamWrap::RegisterExternalReferences) |