GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/api/environment.cc Lines: 244 294 83.0 %
Date: 2020-02-19 22:14:06 Branches: 87 162 53.7 %

Line Branch Exec Source
1
#include "node.h"
2
#include "node_context_data.h"
3
#include "node_errors.h"
4
#include "node_internals.h"
5
#include "node_native_module_env.h"
6
#include "node_platform.h"
7
#include "node_v8_platform-inl.h"
8
#include "uv.h"
9
10
namespace node {
11
using errors::TryCatchScope;
12
using v8::Array;
13
using v8::Context;
14
using v8::EscapableHandleScope;
15
using v8::FinalizationGroup;
16
using v8::Function;
17
using v8::HandleScope;
18
using v8::Isolate;
19
using v8::Local;
20
using v8::MaybeLocal;
21
using v8::MicrotasksPolicy;
22
using v8::Null;
23
using v8::Object;
24
using v8::ObjectTemplate;
25
using v8::Private;
26
using v8::String;
27
using v8::Value;
28
29
47
static bool AllowWasmCodeGenerationCallback(Local<Context> context,
30
                                            Local<String>) {
31
  Local<Value> wasm_code_gen =
32
94
      context->GetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration);
33

141
  return wasm_code_gen->IsUndefined() || wasm_code_gen->IsTrue();
34
}
35
36
32
static bool ShouldAbortOnUncaughtException(Isolate* isolate) {
37
32
  DebugSealHandleScope scope(isolate);
38
32
  Environment* env = Environment::GetCurrent(isolate);
39
32
  return env != nullptr &&
40

64
         (env->is_main_thread() || !env->is_stopping()) &&
41

96
         env->should_abort_on_uncaught_toggle()[0] &&
42
65
         !env->inside_should_not_abort_on_uncaught_scope();
43
}
44
45
71639
static MaybeLocal<Value> PrepareStackTraceCallback(Local<Context> context,
46
                                      Local<Value> exception,
47
                                      Local<Array> trace) {
48
71639
  Environment* env = Environment::GetCurrent(context);
49
71639
  if (env == nullptr) {
50
    MaybeLocal<String> s = exception->ToString(context);
51
    return s.IsEmpty() ?
52
      MaybeLocal<Value>() :
53
      MaybeLocal<Value>(s.ToLocalChecked());
54
  }
55
71639
  Local<Function> prepare = env->prepare_stack_trace_callback();
56
71639
  if (prepare.IsEmpty()) {
57
    MaybeLocal<String> s = exception->ToString(context);
58
    return s.IsEmpty() ?
59
      MaybeLocal<Value>() :
60
      MaybeLocal<Value>(s.ToLocalChecked());
61
  }
62
  Local<Value> args[] = {
63
      context->Global(),
64
      exception,
65
      trace,
66
286556
  };
67
  // This TryCatch + Rethrow is required by V8 due to details around exception
68
  // handling there. For C++ callbacks, V8 expects a scheduled exception (which
69
  // is what ReThrow gives us). Just returning the empty MaybeLocal would leave
70
  // us with a pending exception.
71
143278
  TryCatchScope try_catch(env);
72
  MaybeLocal<Value> result = prepare->Call(
73
143278
      context, Undefined(env->isolate()), arraysize(args), args);
74

71639
  if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
75
2
    try_catch.ReThrow();
76
  }
77
71639
  return result;
78
}
79
80
3
static void HostCleanupFinalizationGroupCallback(
81
    Local<Context> context, Local<FinalizationGroup> group) {
82
3
  Environment* env = Environment::GetCurrent(context);
83
3
  if (env == nullptr) {
84
    return;
85
  }
86
3
  env->RegisterFinalizationGroupForCleanup(group);
87
}
88
89
92137
void* NodeArrayBufferAllocator::Allocate(size_t size) {
90
  void* ret;
91

92137
  if (zero_fill_field_ || per_process::cli_options->zero_fill_all_buffers)
92
57483
    ret = UncheckedCalloc(size);
93
  else
94
34654
    ret = UncheckedMalloc(size);
95
92137
  if (LIKELY(ret != nullptr))
96
92137
    total_mem_usage_.fetch_add(size, std::memory_order_relaxed);
97
92137
  return ret;
98
}
99
100
367000
void* NodeArrayBufferAllocator::AllocateUninitialized(size_t size) {
101
367000
  void* ret = node::UncheckedMalloc(size);
102
367000
  if (LIKELY(ret != nullptr))
103
367000
    total_mem_usage_.fetch_add(size, std::memory_order_relaxed);
104
367000
  return ret;
105
}
106
107
293145
void* NodeArrayBufferAllocator::Reallocate(
108
    void* data, size_t old_size, size_t size) {
109
293145
  void* ret = UncheckedRealloc<char>(static_cast<char*>(data), size);
110

293145
  if (LIKELY(ret != nullptr) || UNLIKELY(size == 0))
111
293145
    total_mem_usage_.fetch_add(size - old_size, std::memory_order_relaxed);
112
293145
  return ret;
113
}
114
115
452597
void NodeArrayBufferAllocator::Free(void* data, size_t size) {
116
452597
  total_mem_usage_.fetch_sub(size, std::memory_order_relaxed);
117
452597
  free(data);
118
452597
}
119
120
9
DebuggingArrayBufferAllocator::~DebuggingArrayBufferAllocator() {
121
3
  CHECK(allocations_.empty());
122
6
}
123
124
42
void* DebuggingArrayBufferAllocator::Allocate(size_t size) {
125
84
  Mutex::ScopedLock lock(mutex_);
126
42
  void* data = NodeArrayBufferAllocator::Allocate(size);
127
42
  RegisterPointerInternal(data, size);
128
84
  return data;
129
}
130
131
8
void* DebuggingArrayBufferAllocator::AllocateUninitialized(size_t size) {
132
16
  Mutex::ScopedLock lock(mutex_);
133
8
  void* data = NodeArrayBufferAllocator::AllocateUninitialized(size);
134
8
  RegisterPointerInternal(data, size);
135
16
  return data;
136
}
137
138
50
void DebuggingArrayBufferAllocator::Free(void* data, size_t size) {
139
100
  Mutex::ScopedLock lock(mutex_);
140
50
  UnregisterPointerInternal(data, size);
141
50
  NodeArrayBufferAllocator::Free(data, size);
142
50
}
143
144
void* DebuggingArrayBufferAllocator::Reallocate(void* data,
145
                                                size_t old_size,
146
                                                size_t size) {
147
  Mutex::ScopedLock lock(mutex_);
148
  void* ret = NodeArrayBufferAllocator::Reallocate(data, old_size, size);
149
  if (ret == nullptr) {
150
    if (size == 0)  // i.e. equivalent to free().
151
      UnregisterPointerInternal(data, old_size);
152
    return nullptr;
153
  }
154
155
  if (data != nullptr) {
156
    auto it = allocations_.find(data);
157
    CHECK_NE(it, allocations_.end());
158
    allocations_.erase(it);
159
  }
160
161
  RegisterPointerInternal(ret, size);
162
  return ret;
163
}
164
165
void DebuggingArrayBufferAllocator::RegisterPointer(void* data, size_t size) {
166
  Mutex::ScopedLock lock(mutex_);
167
  NodeArrayBufferAllocator::RegisterPointer(data, size);
168
  RegisterPointerInternal(data, size);
169
}
170
171
void DebuggingArrayBufferAllocator::UnregisterPointer(void* data, size_t size) {
172
  Mutex::ScopedLock lock(mutex_);
173
  NodeArrayBufferAllocator::UnregisterPointer(data, size);
174
  UnregisterPointerInternal(data, size);
175
}
176
177
50
void DebuggingArrayBufferAllocator::UnregisterPointerInternal(void* data,
178
                                                              size_t size) {
179
50
  if (data == nullptr) return;
180
50
  auto it = allocations_.find(data);
181
50
  CHECK_NE(it, allocations_.end());
182
50
  if (size > 0) {
183
    // We allow allocations with size 1 for 0-length buffers to avoid having
184
    // to deal with nullptr values.
185
50
    CHECK_EQ(it->second, size);
186
  }
187
50
  allocations_.erase(it);
188
}
189
190
50
void DebuggingArrayBufferAllocator::RegisterPointerInternal(void* data,
191
                                                            size_t size) {
192
50
  if (data == nullptr) return;
193
50
  CHECK_EQ(allocations_.count(data), 0);
194
50
  allocations_[data] = size;
195
}
196
197
4395
std::unique_ptr<ArrayBufferAllocator> ArrayBufferAllocator::Create(bool debug) {
198

4395
  if (debug || per_process::cli_options->debug_arraybuffer_allocations)
199
3
    return std::make_unique<DebuggingArrayBufferAllocator>();
200
  else
201
4392
    return std::make_unique<NodeArrayBufferAllocator>();
202
}
203
204
42
ArrayBufferAllocator* CreateArrayBufferAllocator() {
205
42
  return ArrayBufferAllocator::Create().release();
206
}
207
208
42
void FreeArrayBufferAllocator(ArrayBufferAllocator* allocator) {
209
42
  delete allocator;
210
42
}
211
212
4394
void SetIsolateCreateParamsForNode(Isolate::CreateParams* params) {
213
4394
  const uint64_t constrained_memory = uv_get_constrained_memory();
214
4394
  const uint64_t total_memory = constrained_memory > 0 ?
215
4394
      std::min(uv_get_total_memory(), constrained_memory) :
216
4394
      uv_get_total_memory();
217
4394
  if (total_memory > 0) {
218
    // V8 defaults to 700MB or 1.4GB on 32 and 64 bit platforms respectively.
219
    // This default is based on browser use-cases. Tell V8 to configure the
220
    // heap based on the actual physical memory.
221
4394
    params->constraints.ConfigureDefaults(total_memory, 0);
222
  }
223
4394
}
224
225
4394
void SetIsolateErrorHandlers(v8::Isolate* isolate, const IsolateSettings& s) {
226
4394
  if (s.flags & MESSAGE_LISTENER_WITH_ERROR_LEVEL)
227
4394
    isolate->AddMessageListenerWithErrorLevel(
228
            errors::PerIsolateMessageListener,
229
            Isolate::MessageErrorLevel::kMessageError |
230
4394
                Isolate::MessageErrorLevel::kMessageWarning);
231
232
4394
  auto* abort_callback = s.should_abort_on_uncaught_exception_callback ?
233
      s.should_abort_on_uncaught_exception_callback :
234
4394
      ShouldAbortOnUncaughtException;
235
4394
  isolate->SetAbortOnUncaughtExceptionCallback(abort_callback);
236
237
4394
  auto* fatal_error_cb = s.fatal_error_callback ?
238
4394
      s.fatal_error_callback : OnFatalError;
239
4394
  isolate->SetFatalErrorHandler(fatal_error_cb);
240
241
4394
  auto* prepare_stack_trace_cb = s.prepare_stack_trace_callback ?
242
4394
      s.prepare_stack_trace_callback : PrepareStackTraceCallback;
243
4394
  isolate->SetPrepareStackTraceCallback(prepare_stack_trace_cb);
244
4394
}
245
246
4395
void SetIsolateMiscHandlers(v8::Isolate* isolate, const IsolateSettings& s) {
247
4395
  isolate->SetMicrotasksPolicy(s.policy);
248
249
4395
  auto* allow_wasm_codegen_cb = s.allow_wasm_code_generation_callback ?
250
4395
    s.allow_wasm_code_generation_callback : AllowWasmCodeGenerationCallback;
251
4395
  isolate->SetAllowWasmCodeGenerationCallback(allow_wasm_codegen_cb);
252
253
4395
  auto* promise_reject_cb = s.promise_reject_callback ?
254
4395
    s.promise_reject_callback : task_queue::PromiseRejectCallback;
255
4395
  isolate->SetPromiseRejectCallback(promise_reject_cb);
256
257
4395
  auto* host_cleanup_cb = s.host_cleanup_finalization_group_callback ?
258
    s.host_cleanup_finalization_group_callback :
259
4395
    HostCleanupFinalizationGroupCallback;
260
4395
  isolate->SetHostCleanupFinalizationGroupCallback(host_cleanup_cb);
261
262
4395
  if (s.flags & DETAILED_SOURCE_POSITIONS_FOR_PROFILING)
263
4395
    v8::CpuProfiler::UseDetailedSourcePositionsForProfiling(isolate);
264
4395
}
265
266
270
void SetIsolateUpForNode(v8::Isolate* isolate,
267
                         const IsolateSettings& settings) {
268
270
  SetIsolateErrorHandlers(isolate, settings);
269
270
  SetIsolateMiscHandlers(isolate, settings);
270
270
}
271
272
270
void SetIsolateUpForNode(v8::Isolate* isolate) {
273
270
  IsolateSettings settings;
274
270
  SetIsolateUpForNode(isolate, settings);
275
270
}
276
277
Isolate* NewIsolate(ArrayBufferAllocator* allocator, uv_loop_t* event_loop) {
278
  return NewIsolate(allocator, event_loop, GetMainThreadMultiIsolatePlatform());
279
}
280
281
// TODO(joyeecheung): we may want to expose this, but then we need to be
282
// careful about what we override in the params.
283
41
Isolate* NewIsolate(Isolate::CreateParams* params,
284
                    uv_loop_t* event_loop,
285
                    MultiIsolatePlatform* platform) {
286
41
  Isolate* isolate = Isolate::Allocate();
287
41
  if (isolate == nullptr) return nullptr;
288
289
  // Register the isolate on the platform before the isolate gets initialized,
290
  // so that the isolate can access the platform during initialization.
291
41
  platform->RegisterIsolate(isolate, event_loop);
292
293
41
  SetIsolateCreateParamsForNode(params);
294
41
  Isolate::Initialize(isolate, *params);
295
41
  SetIsolateUpForNode(isolate);
296
297
41
  return isolate;
298
}
299
300
41
Isolate* NewIsolate(ArrayBufferAllocator* allocator,
301
                    uv_loop_t* event_loop,
302
                    MultiIsolatePlatform* platform) {
303
82
  Isolate::CreateParams params;
304
41
  if (allocator != nullptr) params.array_buffer_allocator = allocator;
305
82
  return NewIsolate(&params, event_loop, platform);
306
}
307
308
Isolate* NewIsolate(std::shared_ptr<ArrayBufferAllocator> allocator,
309
                    uv_loop_t* event_loop,
310
                    MultiIsolatePlatform* platform) {
311
  Isolate::CreateParams params;
312
  if (allocator) params.array_buffer_allocator_shared = allocator;
313
  return NewIsolate(&params, event_loop, platform);
314
}
315
316
255
IsolateData* CreateIsolateData(Isolate* isolate,
317
                               uv_loop_t* loop,
318
                               MultiIsolatePlatform* platform,
319
                               ArrayBufferAllocator* allocator) {
320
255
  return new IsolateData(isolate, loop, platform, allocator);
321
}
322
323
254
void FreeIsolateData(IsolateData* isolate_data) {
324
254
  delete isolate_data;
325
255
}
326
327
26
Environment* CreateEnvironment(IsolateData* isolate_data,
328
                               Local<Context> context,
329
                               int argc,
330
                               const char* const* argv,
331
                               int exec_argc,
332
                               const char* const* exec_argv) {
333
26
  Isolate* isolate = context->GetIsolate();
334
52
  HandleScope handle_scope(isolate);
335
  Context::Scope context_scope(context);
336
  // TODO(addaleax): This is a much better place for parsing per-Environment
337
  // options than the global parse call.
338
52
  std::vector<std::string> args(argv, argv + argc);
339
52
  std::vector<std::string> exec_args(exec_argv, exec_argv + exec_argc);
340
  // TODO(addaleax): Provide more sensible flags, in an embedder-accessible way.
341
  Environment* env = new Environment(
342
      isolate_data,
343
      context,
344
      args,
345
      exec_args,
346
      static_cast<Environment::Flags>(Environment::kIsMainThread |
347
                                      Environment::kOwnsProcessState |
348
26
                                      Environment::kOwnsInspector));
349
26
  env->InitializeLibuv(per_process::v8_is_profiling);
350
52
  if (env->RunBootstrapping().IsEmpty()) {
351
    return nullptr;
352
  }
353
354
  std::vector<Local<String>> parameters = {
355
      env->require_string(),
356
52
      FIXED_ONE_BYTE_STRING(env->isolate(), "markBootstrapComplete")};
357
  std::vector<Local<Value>> arguments = {
358
      env->native_module_require(),
359
52
      env->NewFunctionTemplate(MarkBootstrapComplete)
360
78
          ->GetFunction(env->context())
361
78
          .ToLocalChecked()};
362
52
  if (ExecuteBootstrapper(
363
          env, "internal/bootstrap/environment", &parameters, &arguments)
364
          .IsEmpty()) {
365
    return nullptr;
366
  }
367
26
  return env;
368
}
369
370
252
void FreeEnvironment(Environment* env) {
371
252
  env->RunCleanup();
372
253
  delete env;
373
254
}
374
375
5
Environment* GetCurrentEnvironment(Local<Context> context) {
376
5
  return Environment::GetCurrent(context);
377
}
378
379
MultiIsolatePlatform* GetMainThreadMultiIsolatePlatform() {
380
  return per_process::v8_platform.Platform();
381
}
382
383
MultiIsolatePlatform* CreatePlatform(
384
    int thread_pool_size,
385
    node::tracing::TracingController* tracing_controller) {
386
  return new NodePlatform(thread_pool_size, tracing_controller);
387
}
388
389
void FreePlatform(MultiIsolatePlatform* platform) {
390
  delete platform;
391
}
392
393
39801
MaybeLocal<Object> GetPerContextExports(Local<Context> context) {
394
39801
  Isolate* isolate = context->GetIsolate();
395
39803
  EscapableHandleScope handle_scope(isolate);
396
397
39803
  Local<Object> global = context->Global();
398
  Local<Private> key = Private::ForApi(isolate,
399
39803
      FIXED_ONE_BYTE_STRING(isolate, "node:per_context_binding_exports"));
400
401
  Local<Value> existing_value;
402
79603
  if (!global->GetPrivate(context, key).ToLocal(&existing_value))
403
    return MaybeLocal<Object>();
404
39801
  if (existing_value->IsObject())
405
39546
    return handle_scope.Escape(existing_value.As<Object>());
406
407
255
  Local<Object> exports = Object::New(isolate);
408

1028
  if (context->Global()->SetPrivate(context, key, exports).IsNothing() ||
409
257
      !InitializePrimordials(context))
410
    return MaybeLocal<Object>();
411
257
  return handle_scope.Escape(exports);
412
}
413
414
// Any initialization logic should be performed in
415
// InitializeContext, because embedders don't necessarily
416
// call NewContext and so they will experience breakages.
417
256
Local<Context> NewContext(Isolate* isolate,
418
                          Local<ObjectTemplate> object_template) {
419
511
  auto context = Context::New(isolate, nullptr, object_template);
420
255
  if (context.IsEmpty()) return context;
421
422
255
  if (!InitializeContext(context)) {
423
    return Local<Context>();
424
  }
425
426
256
  return context;
427
}
428
429
// This runs at runtime, regardless of whether the context
430
// is created from a snapshot.
431
4937
void InitializeContextRuntime(Local<Context> context) {
432
4937
  Isolate* isolate = context->GetIsolate();
433
9874
  HandleScope handle_scope(isolate);
434
435
  // Delete `Intl.v8BreakIterator`
436
  // https://github.com/nodejs/node/issues/14909
437
4937
  Local<String> intl_string = FIXED_ONE_BYTE_STRING(isolate, "Intl");
438
  Local<String> break_iter_string =
439
4937
    FIXED_ONE_BYTE_STRING(isolate, "v8BreakIterator");
440
  Local<Value> intl_v;
441

19748
  if (context->Global()->Get(context, intl_string).ToLocal(&intl_v) &&
442
4937
      intl_v->IsObject()) {
443
4937
    Local<Object> intl = intl_v.As<Object>();
444
9874
    intl->Delete(context, break_iter_string).FromJust();
445
  }
446
447
  // Delete `Atomics.wake`
448
  // https://github.com/nodejs/node/issues/21219
449
4937
  Local<String> atomics_string = FIXED_ONE_BYTE_STRING(isolate, "Atomics");
450
4937
  Local<String> wake_string = FIXED_ONE_BYTE_STRING(isolate, "wake");
451
  Local<Value> atomics_v;
452

19748
  if (context->Global()->Get(context, atomics_string).ToLocal(&atomics_v) &&
453
4937
      atomics_v->IsObject()) {
454
4936
    Local<Object> atomics = atomics_v.As<Object>();
455
9872
    atomics->Delete(context, wake_string).FromJust();
456
  }
457
4937
}
458
459
255
bool InitializeContextForSnapshot(Local<Context> context) {
460
255
  Isolate* isolate = context->GetIsolate();
461
511
  HandleScope handle_scope(isolate);
462
463
512
  context->SetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration,
464
256
                           True(isolate));
465
512
  return InitializePrimordials(context);
466
}
467
468
513
bool InitializePrimordials(Local<Context> context) {
469
  // Run per-context JS files.
470
513
  Isolate* isolate = context->GetIsolate();
471
  Context::Scope context_scope(context);
472
  Local<Object> exports;
473
474
  Local<String> primordials_string =
475
511
      FIXED_ONE_BYTE_STRING(isolate, "primordials");
476
512
  Local<String> global_string = FIXED_ONE_BYTE_STRING(isolate, "global");
477
513
  Local<String> exports_string = FIXED_ONE_BYTE_STRING(isolate, "exports");
478
479
  // Create primordials first and make it available to per-context scripts.
480
512
  Local<Object> primordials = Object::New(isolate);
481

2556
  if (!primordials->SetPrototype(context, Null(isolate)).FromJust() ||
482

2044
      !GetPerContextExports(context).ToLocal(&exports) ||
483
1533
      !exports->Set(context, primordials_string, primordials).FromJust()) {
484
    return false;
485
  }
486
487
  static const char* context_files[] = {"internal/per_context/primordials",
488
                                        "internal/per_context/domexception",
489
                                        "internal/per_context/messageport",
490
                                        nullptr};
491
492
2049
  for (const char** module = context_files; *module != nullptr; module++) {
493
    std::vector<Local<String>> parameters = {
494
3075
        global_string, exports_string, primordials_string};
495
7693
    Local<Value> arguments[] = {context->Global(), exports, primordials};
496
    MaybeLocal<Function> maybe_fn =
497
        native_module::NativeModuleEnv::LookupAndCompile(
498
1539
            context, *module, &parameters, nullptr);
499
1539
    if (maybe_fn.IsEmpty()) {
500
      return false;
501
    }
502
1539
    Local<Function> fn = maybe_fn.ToLocalChecked();
503
    MaybeLocal<Value> result =
504
3078
        fn->Call(context, Undefined(isolate), arraysize(arguments), arguments);
505
    // Execution failed during context creation.
506
    // TODO(joyeecheung): deprecate this signature and return a MaybeLocal.
507
1538
    if (result.IsEmpty()) {
508
      return false;
509
    }
510
  }
511
512
513
  return true;
513
}
514
515
255
bool InitializeContext(Local<Context> context) {
516
255
  if (!InitializeContextForSnapshot(context)) {
517
    return false;
518
  }
519
520
256
  InitializeContextRuntime(context);
521
256
  return true;
522
}
523
524
8
uv_loop_t* GetCurrentEventLoop(Isolate* isolate) {
525
16
  HandleScope handle_scope(isolate);
526
8
  Local<Context> context = isolate->GetCurrentContext();
527
8
  if (context.IsEmpty()) return nullptr;
528
8
  Environment* env = Environment::GetCurrent(context);
529
8
  if (env == nullptr) return nullptr;
530
8
  return env->event_loop();
531
}
532
533
1
void AddLinkedBinding(Environment* env, const node_module& mod) {
534
1
  CHECK_NOT_NULL(env);
535
2
  Mutex::ScopedLock lock(env->extra_linked_bindings_mutex());
536
537
1
  node_module* prev_head = env->extra_linked_bindings_head();
538
1
  env->extra_linked_bindings()->push_back(mod);
539
1
  if (prev_head != nullptr)
540
    prev_head->nm_link = &env->extra_linked_bindings()->back();
541
1
}
542
543
1
void AddLinkedBinding(Environment* env,
544
                      const char* name,
545
                      addon_context_register_func fn,
546
                      void* priv) {
547
  node_module mod = {
548
    NODE_MODULE_VERSION,
549
    NM_F_LINKED,
550
    nullptr,  // nm_dso_handle
551
    nullptr,  // nm_filename
552
    nullptr,  // nm_register_func
553
    fn,
554
    name,
555
    priv,
556
    nullptr   // nm_link
557
1
  };
558
1
  AddLinkedBinding(env, mod);
559
1
}
560
561
}  // namespace node