内容纲要

基本类型

  • v8::Isolate*
  • v8::HandleScope
  • v8::Local<>
  • v8::Local<v8::Object>
  • v8::Local<v8::Value>
  • v8::Local<v8::Context>
  • v8::MaybeLocal<>
  • v8::MaybeLocal<v8::Object>
  • v8::Locker
  • v8::HandleScope
  • gin::Arguments*
  • gin::StringToV8(isolate, "xxxxx") 字符串传递
  • gin::ConvertFromV8()
  • gin::ConvertToV8()

v8::Isolate

v8::HandleScope

生成一个虚拟的作用域,监视(绑定)从v8的GC分配的对象

v8::Local<>

v8有GC,但c++没有GC
相反,它通过RAII (Resource Acquisition Is Initialization)分配和释放资源
在C++中有一个称为析构函数的函数,当分配到stack上的类超出作用域并被丢弃时,该函数被调用。
通过 v8::Local,当超出了类的作用域时,会自动调用析构函数,析构函数会自动释放资源,实现一种所谓的智能指针功能。
v8::Local 是一个包装类,用于监视在 c++ 中分配给堆的对象,并在调用析构函数时与当前的 HandleScope 一起删除。

例如:

void test() {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::HandleScope handle_scope;

  v8::Local<v8::Array> array = v8::Array::New(isolate, 3);
  ...
}

创建 v8::HandleScope 后,所有 v8::Local 都将分配给该 v8::HandleScope
因此,当在测试函数结束时调用 handle_scope 析构函数时,也会删除与 v8::HandleScope 相关的所有 v8::Local

v8::Handle

v8::Local 包装的类,但实际上链接到 v8::HandleScope
有些api会返回这个 v8::Handle,但基本上它就像 v8::Local 一样使用。

chromium 中的 gin

converter.h

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef GIN_CONVERTER_H_
#define GIN_CONVERTER_H_

#include <stdint.h>

#include <string>
#include <type_traits>
#include <vector>

#include "base/logging.h"
#include "base/strings/string16.h"
#include "base/strings/string_piece.h"
#include "gin/gin_export.h"
#include "v8/include/v8.h"

namespace gin {

template<typename KeyType>
bool SetProperty(v8::Isolate* isolate,
                 v8::Local<v8::Object> object,
                 KeyType key,
                 v8::Local<v8::Value> value) {
  auto maybe =
      object->DefineOwnProperty(isolate->GetCurrentContext(), key, value);
  return !maybe.IsNothing() && maybe.FromJust();
}

template <typename T, typename Enable = void>
struct ToV8ReturnsMaybe {
  static const bool value = false;
};

template<typename T, typename Enable = void>
struct Converter {};

template<>
struct GIN_EXPORT Converter<bool> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                    bool val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     bool* out);
};

template<>
struct GIN_EXPORT Converter<int32_t> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                    int32_t val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     int32_t* out);
};

template<>
struct GIN_EXPORT Converter<uint32_t> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                    uint32_t val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     uint32_t* out);
};

template<>
struct GIN_EXPORT Converter<int64_t> {
  // Warning: JavaScript cannot represent 64 integers precisely.
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                    int64_t val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     int64_t* out);
};

template<>
struct GIN_EXPORT Converter<uint64_t> {
  // Warning: JavaScript cannot represent 64 integers precisely.
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                    uint64_t val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     uint64_t* out);
};

template<>
struct GIN_EXPORT Converter<float> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                    float val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     float* out);
};

template<>
struct GIN_EXPORT Converter<double> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                    double val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     double* out);
};

template<>
struct GIN_EXPORT Converter<base::StringPiece> {
  // This crashes when val.size() > v8::String::kMaxLength.
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                    const base::StringPiece& val);
  // No conversion out is possible because StringPiece does not contain storage.
};

template<>
struct GIN_EXPORT Converter<std::string> {
  // This crashes when val.size() > v8::String::kMaxLength.
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                    const std::string& val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     std::string* out);
};

template <>
struct GIN_EXPORT Converter<base::string16> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                   const base::string16& val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     base::string16* out);
};

template <>
struct GIN_EXPORT Converter<v8::Local<v8::Function>> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                   v8::Local<v8::Function> val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     v8::Local<v8::Function>* out);
};

template<>
struct GIN_EXPORT Converter<v8::Local<v8::Object> > {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                    v8::Local<v8::Object> val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     v8::Local<v8::Object>* out);
};

template <>
struct GIN_EXPORT Converter<v8::Local<v8::Promise>> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                   v8::Local<v8::Promise> val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     v8::Local<v8::Promise>* out);
};

template<>
struct GIN_EXPORT Converter<v8::Local<v8::ArrayBuffer> > {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                    v8::Local<v8::ArrayBuffer> val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     v8::Local<v8::ArrayBuffer>* out);
};

template<>
struct GIN_EXPORT Converter<v8::Local<v8::External> > {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                    v8::Local<v8::External> val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     v8::Local<v8::External>* out);
};

template<>
struct GIN_EXPORT Converter<v8::Local<v8::Value> > {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                    v8::Local<v8::Value> val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     v8::Local<v8::Value>* out);
};

template<typename T>
struct Converter<std::vector<T> > {
  static std::conditional_t<ToV8ReturnsMaybe<T>::value,
                            v8::MaybeLocal<v8::Value>,
                            v8::Local<v8::Value>>
  ToV8(v8::Isolate* isolate, const std::vector<T>& val) {
    v8::Local<v8::Context> context = isolate->GetCurrentContext();
    v8::Local<v8::Array> result(
        v8::Array::New(isolate, static_cast<int>(val.size())));
    for (uint32_t i = 0; i < val.size(); ++i) {
      v8::MaybeLocal<v8::Value> maybe = Converter<T>::ToV8(isolate, val[i]);
      v8::Local<v8::Value> element;
      if (!maybe.ToLocal(&element))
        return {};
      bool property_created;
      if (!result->CreateDataProperty(context, i, element)
               .To(&property_created) ||
          !property_created) {
        NOTREACHED() << "CreateDataProperty should always succeed here.";
      }
    }
    return result;
  }

  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     std::vector<T>* out) {
    if (!val->IsArray())
      return false;

    std::vector<T> result;
    v8::Local<v8::Array> array(v8::Local<v8::Array>::Cast(val));
    uint32_t length = array->Length();
    for (uint32_t i = 0; i < length; ++i) {
      v8::Local<v8::Value> v8_item;
      if (!array->Get(isolate->GetCurrentContext(), i).ToLocal(&v8_item))
        return false;
      T item;
      if (!Converter<T>::FromV8(isolate, v8_item, &item))
        return false;
      result.push_back(item);
    }

    out->swap(result);
    return true;
  }
};

template<typename T>
struct ToV8ReturnsMaybe<std::vector<T>> {
  static const bool value = ToV8ReturnsMaybe<T>::value;
};

// Convenience functions that deduce T.
template <typename T>
std::conditional_t<ToV8ReturnsMaybe<T>::value,
                   v8::MaybeLocal<v8::Value>,
                   v8::Local<v8::Value>>
ConvertToV8(v8::Isolate* isolate, const T& input) {
  return Converter<T>::ToV8(isolate, input);
}

template <typename T>
std::enable_if_t<ToV8ReturnsMaybe<T>::value, bool> TryConvertToV8(
    v8::Isolate* isolate,
    const T& input,
    v8::Local<v8::Value>* output) {
  return gin::ConvertToV8(isolate, input).ToLocal(output);
}

template <typename T>
std::enable_if_t<!ToV8ReturnsMaybe<T>::value, bool> TryConvertToV8(
    v8::Isolate* isolate,
    const T& input,
    v8::Local<v8::Value>* output) {
  *output = gin::ConvertToV8(isolate, input);
  return true;
}

// This crashes when input.size() > v8::String::kMaxLength.
GIN_EXPORT inline v8::Local<v8::String> StringToV8(
    v8::Isolate* isolate,
    const base::StringPiece& input) {
  return ConvertToV8(isolate, input).As<v8::String>();
}

// This crashes when input.size() > v8::String::kMaxLength.
GIN_EXPORT v8::Local<v8::String> StringToSymbol(v8::Isolate* isolate,
                                                 const base::StringPiece& val);

// This crashes when input.size() > v8::String::kMaxLength.
GIN_EXPORT v8::Local<v8::String> StringToSymbol(v8::Isolate* isolate,
                                                const base::StringPiece16& val);

template<typename T>
bool ConvertFromV8(v8::Isolate* isolate, v8::Local<v8::Value> input,
                   T* result) {
  DCHECK(isolate);
  return Converter<T>::FromV8(isolate, input, result);
}

GIN_EXPORT std::string V8ToString(v8::Isolate* isolate,
                                  v8::Local<v8::Value> value);

}  // namespace gin

#endif  // GIN_CONVERTER_H_

converter.cc

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "gin/converter.h"

#include <stdint.h>

#include "base/strings/string_util.h"
#include "v8/include/v8.h"

using v8::ArrayBuffer;
using v8::Boolean;
using v8::External;
using v8::Function;
using v8::Int32;
using v8::Integer;
using v8::Isolate;
using v8::Local;
using v8::Maybe;
using v8::MaybeLocal;
using v8::Number;
using v8::Object;
using v8::Promise;
using v8::String;
using v8::Uint32;
using v8::Value;

namespace {

template <typename T, typename U>
bool FromMaybe(Maybe<T> maybe, U* out) {
  if (maybe.IsNothing())
    return false;
  *out = static_cast<U>(maybe.FromJust());
  return true;
}

}  // namespace

namespace gin {

Local<Value> Converter<bool>::ToV8(Isolate* isolate, bool val) {
  return Boolean::New(isolate, val).As<Value>();
}

bool Converter<bool>::FromV8(Isolate* isolate, Local<Value> val, bool* out) {
  *out = val->BooleanValue(isolate);
  // BooleanValue cannot throw.
  return true;
}

Local<Value> Converter<int32_t>::ToV8(Isolate* isolate, int32_t val) {
  return Integer::New(isolate, val).As<Value>();
}

bool Converter<int32_t>::FromV8(Isolate* isolate,
                                Local<Value> val,
                                int32_t* out) {
  if (!val->IsInt32())
    return false;
  *out = val.As<Int32>()->Value();
  return true;
}

Local<Value> Converter<uint32_t>::ToV8(Isolate* isolate, uint32_t val) {
  return Integer::NewFromUnsigned(isolate, val).As<Value>();
}

bool Converter<uint32_t>::FromV8(Isolate* isolate,
                                 Local<Value> val,
                                 uint32_t* out) {
  if (!val->IsUint32())
    return false;
  *out = val.As<Uint32>()->Value();
  return true;
}

Local<Value> Converter<int64_t>::ToV8(Isolate* isolate, int64_t val) {
  return Number::New(isolate, static_cast<double>(val)).As<Value>();
}

bool Converter<int64_t>::FromV8(Isolate* isolate,
                                Local<Value> val,
                                int64_t* out) {
  if (!val->IsNumber())
    return false;
  // Even though IntegerValue returns int64_t, JavaScript cannot represent
  // the full precision of int64_t, which means some rounding might occur.
  return FromMaybe(val->IntegerValue(isolate->GetCurrentContext()), out);
}

Local<Value> Converter<uint64_t>::ToV8(Isolate* isolate, uint64_t val) {
  return Number::New(isolate, static_cast<double>(val)).As<Value>();
}

bool Converter<uint64_t>::FromV8(Isolate* isolate,
                                 Local<Value> val,
                                 uint64_t* out) {
  if (!val->IsNumber())
    return false;
  return FromMaybe(val->IntegerValue(isolate->GetCurrentContext()), out);
}

Local<Value> Converter<float>::ToV8(Isolate* isolate, float val) {
  return Number::New(isolate, val).As<Value>();
}

bool Converter<float>::FromV8(Isolate* isolate, Local<Value> val, float* out) {
  if (!val->IsNumber())
    return false;
  *out = static_cast<float>(val.As<Number>()->Value());
  return true;
}

Local<Value> Converter<double>::ToV8(Isolate* isolate, double val) {
  return Number::New(isolate, val).As<Value>();
}

bool Converter<double>::FromV8(Isolate* isolate,
                               Local<Value> val,
                               double* out) {
  if (!val->IsNumber())
    return false;
  *out = val.As<Number>()->Value();
  return true;
}

Local<Value> Converter<base::StringPiece>::ToV8(Isolate* isolate,
                                                const base::StringPiece& val) {
  return String::NewFromUtf8(isolate, val.data(),
                             v8::NewStringType::kNormal,
                             static_cast<uint32_t>(val.length()))
      .ToLocalChecked();
}

Local<Value> Converter<std::string>::ToV8(Isolate* isolate,
                                          const std::string& val) {
  return Converter<base::StringPiece>::ToV8(isolate, val);
}

bool Converter<std::string>::FromV8(Isolate* isolate,
                                    Local<Value> val,
                                    std::string* out) {
  if (!val->IsString())
    return false;
  Local<String> str = Local<String>::Cast(val);
  int length = str->Utf8Length(isolate);
  out->resize(length);
  str->WriteUtf8(isolate, &(*out)[0], length, NULL,
                 String::NO_NULL_TERMINATION);
  return true;
}

Local<Value> Converter<base::string16>::ToV8(Isolate* isolate,
                                             const base::string16& val) {
  return String::NewFromTwoByte(isolate,
                                reinterpret_cast<const uint16_t*>(val.data()),
                                v8::NewStringType::kNormal, val.size())
      .ToLocalChecked();
}

bool Converter<base::string16>::FromV8(Isolate* isolate,
                                       Local<Value> val,
                                       base::string16* out) {
  if (!val->IsString())
    return false;
  Local<String> str = Local<String>::Cast(val);
  int length = str->Length();
  // Note that the reinterpret cast is because on Windows string16 is an alias
  // to wstring, and hence has character type wchar_t not uint16_t.
  str->Write(isolate,
             reinterpret_cast<uint16_t*>(base::WriteInto(out, length + 1)), 0,
             length);
  return true;
}

Local<Value> Converter<Local<Function>>::ToV8(Isolate* isolate,
                                              Local<Function> val) {
  return val.As<Value>();
}

bool Converter<Local<Function>>::FromV8(Isolate* isolate,
                                        Local<Value> val,
                                        Local<Function>* out) {
  if (!val->IsFunction())
    return false;
  *out = Local<Function>::Cast(val);
  return true;
}

Local<Value> Converter<Local<Object>>::ToV8(Isolate* isolate,
                                            Local<Object> val) {
  return val.As<Value>();
}

bool Converter<Local<Object>>::FromV8(Isolate* isolate,
                                      Local<Value> val,
                                      Local<Object>* out) {
  if (!val->IsObject())
    return false;
  *out = Local<Object>::Cast(val);
  return true;
}

Local<Value> Converter<Local<Promise>>::ToV8(Isolate* isolate,
                                             Local<Promise> val) {
  return val.As<Value>();
}

bool Converter<Local<Promise>>::FromV8(Isolate* isolate,
                                       Local<Value> val,
                                       Local<Promise>* out) {
  if (!val->IsPromise())
    return false;
  *out = Local<Promise>::Cast(val);
  return true;
}

Local<Value> Converter<Local<ArrayBuffer>>::ToV8(Isolate* isolate,
                                                 Local<ArrayBuffer> val) {
  return val.As<Value>();
}

bool Converter<Local<ArrayBuffer>>::FromV8(Isolate* isolate,
                                           Local<Value> val,
                                           Local<ArrayBuffer>* out) {
  if (!val->IsArrayBuffer())
    return false;
  *out = Local<ArrayBuffer>::Cast(val);
  return true;
}

Local<Value> Converter<Local<External>>::ToV8(Isolate* isolate,
                                              Local<External> val) {
  return val.As<Value>();
}

bool Converter<Local<External>>::FromV8(Isolate* isolate,
                                        v8::Local<Value> val,
                                        Local<External>* out) {
  if (!val->IsExternal())
    return false;
  *out = Local<External>::Cast(val);
  return true;
}

Local<Value> Converter<Local<Value>>::ToV8(Isolate* isolate, Local<Value> val) {
  return val;
}

bool Converter<Local<Value>>::FromV8(Isolate* isolate,
                                     Local<Value> val,
                                     Local<Value>* out) {
  *out = val;
  return true;
}

v8::Local<v8::String> StringToSymbol(v8::Isolate* isolate,
                                      const base::StringPiece& val) {
  return String::NewFromUtf8(isolate, val.data(),
                             v8::NewStringType::kInternalized,
                             static_cast<uint32_t>(val.length()))
      .ToLocalChecked();
}

v8::Local<v8::String> StringToSymbol(v8::Isolate* isolate,
                                     const base::StringPiece16& val) {
  return String::NewFromTwoByte(isolate,
                                reinterpret_cast<const uint16_t*>(val.data()),
                                v8::NewStringType::kInternalized, val.length())
      .ToLocalChecked();
}

std::string V8ToString(v8::Isolate* isolate, v8::Local<v8::Value> value) {
  if (value.IsEmpty())
    return std::string();
  std::string result;
  if (!ConvertFromV8(isolate, value, &result))
    return std::string();
  return result;
}

}  // namespace gin

electron\native_mate\native_mate\converter.h

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE.chromium file.

#ifndef NATIVE_MATE_NATIVE_MATE_CONVERTER_H_
#define NATIVE_MATE_NATIVE_MATE_CONVERTER_H_

#include <map>
#include <set>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>

#include "base/strings/string_piece.h"
#include "gin/converter.h"
#include "v8/include/v8.h"

namespace mate {

template <typename KeyType>
bool SetProperty(v8::Isolate* isolate,
                 v8::Local<v8::Object> object,
                 KeyType key,
                 v8::Local<v8::Value> value) {
  auto maybe = object->Set(isolate->GetCurrentContext(), key, value);
  return !maybe.IsNothing() && maybe.FromJust();
}

template <typename T>
struct ToV8ReturnsMaybe {
  static const bool value = false;
};

template <typename T, typename Enable = void>
struct Converter {};

template <>
struct Converter<void*> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, void* val) {
    return v8::Undefined(isolate);
  }
};

template <>
struct Converter<std::nullptr_t> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, std::nullptr_t val) {
    return v8::Null(isolate);
  }
};

template <>
struct Converter<bool> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, bool val);
  static bool FromV8(v8::Isolate* isolate, v8::Local<v8::Value> val, bool* out);
};

#if !defined(OS_LINUX) && !defined(OS_FREEBSD)
template <>
struct Converter<unsigned long> {  // NOLINT(runtime/int)
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                   unsigned long val);  // NOLINT(runtime/int)
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     unsigned long* out);  // NOLINT(runtime/int)
};
#endif

template <>
struct Converter<int32_t> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, int32_t val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     int32_t* out);
};

template <>
struct Converter<uint32_t> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, uint32_t val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     uint32_t* out);
};

template <>
struct Converter<int64_t> {
  // Warning: JavaScript cannot represent 64 integers precisely.
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, int64_t val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     int64_t* out);
};

template <>
struct Converter<uint64_t> {
  // Warning: JavaScript cannot represent 64 integers precisely.
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, uint64_t val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     uint64_t* out);
};

template <>
struct Converter<float> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, float val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     float* out);
};

template <>
struct Converter<double> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, double val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     double* out);
};

template <>
struct Converter<const char*> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, const char* val);
};

template <>
struct Converter<base::StringPiece> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, base::StringPiece val);
  // No conversion out is possible because StringPiece does not contain storage.
};

template <>
struct Converter<std::string> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                   const std::string& val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     std::string* out);
};

v8::Local<v8::String> StringToSymbol(v8::Isolate* isolate,
                                     base::StringPiece input);

template <>
struct Converter<v8::Local<v8::Function>> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                   v8::Local<v8::Function> val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     v8::Local<v8::Function>* out);
};

template <>
struct Converter<v8::Local<v8::Object>> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                   v8::Local<v8::Object> val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     v8::Local<v8::Object>* out);
};

template <>
struct Converter<v8::Local<v8::String>> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                   v8::Local<v8::String> val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     v8::Local<v8::String>* out);
};

template <>
struct Converter<v8::Local<v8::External>> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                   v8::Local<v8::External> val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     v8::Local<v8::External>* out);
};

template <>
struct Converter<v8::Local<v8::Array>> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                   v8::Local<v8::Array> val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     v8::Local<v8::Array>* out);
};

template <>
struct Converter<v8::Local<v8::Value>> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                   v8::Local<v8::Value> val);
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     v8::Local<v8::Value>* out);
};

template <>
struct Converter<v8::Local<v8::Promise>> {
  static v8::Local<v8::Promise> ToV8(v8::Isolate* isolate,
                                     v8::Local<v8::Promise> val);
  // static bool FromV8(v8::Isolate* isolate,
  //                    v8::Local<v8::Value> val,
  //                    v8::Local<v8::Value>* out);
};

template <typename T>
struct Converter<std::vector<T>> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                   const std::vector<T>& val) {
    v8::Local<v8::Array> result(
        v8::Array::New(isolate, static_cast<int>(val.size())));
    auto context = isolate->GetCurrentContext();
    for (size_t i = 0; i < val.size(); ++i) {
      result
          ->Set(context, static_cast<int>(i),
                Converter<T>::ToV8(isolate, val[i]))
          .Check();
    }
    return result;
  }

  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     std::vector<T>* out) {
    if (!val->IsArray())
      return false;

    auto context = isolate->GetCurrentContext();
    std::vector<T> result;
    v8::Local<v8::Array> array(v8::Local<v8::Array>::Cast(val));
    uint32_t length = array->Length();
    for (uint32_t i = 0; i < length; ++i) {
      T item;
      if (!Converter<T>::FromV8(isolate,
                                array->Get(context, i).ToLocalChecked(), &item))
        return false;
      result.push_back(item);
    }

    out->swap(result);
    return true;
  }
};

template <typename T>
struct Converter<std::set<T>> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                   const std::set<T>& val) {
    v8::Local<v8::Array> result(
        v8::Array::New(isolate, static_cast<int>(val.size())));
    auto context = isolate->GetCurrentContext();
    typename std::set<T>::const_iterator it;
    int i;
    for (i = 0, it = val.begin(); it != val.end(); ++it, ++i)
      result->Set(context, i, Converter<T>::ToV8(isolate, *it)).Check();
    return result;
  }

  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     std::set<T>* out) {
    if (!val->IsArray())
      return false;

    auto context = isolate->GetCurrentContext();
    std::set<T> result;
    v8::Local<v8::Array> array(v8::Local<v8::Array>::Cast(val));
    uint32_t length = array->Length();
    for (uint32_t i = 0; i < length; ++i) {
      T item;
      if (!Converter<T>::FromV8(isolate,
                                array->Get(context, i).ToLocalChecked(), &item))
        return false;
      result.insert(item);
    }

    out->swap(result);
    return true;
  }
};

// Convenience functions that deduce T.
template <typename T>
v8::Local<v8::Value> ConvertToV8(v8::Isolate* isolate, const T& input) {
  return Converter<T>::ToV8(isolate, input);
}

template <typename T>
v8::Local<v8::Value> ConvertToV8(v8::Isolate* isolate, T&& input) {
  return Converter<typename std::remove_reference<T>::type>::ToV8(
      isolate, std::move(input));
}

inline v8::Local<v8::Value> ConvertToV8(v8::Isolate* isolate,
                                        const char* input) {
  return Converter<const char*>::ToV8(isolate, input);
}

template <typename T>
v8::MaybeLocal<v8::Value> ConvertToV8(v8::Local<v8::Context> context,
                                      const T& input) {
  return Converter<T>::ToV8(context, input);
}

template <typename T, bool = ToV8ReturnsMaybe<T>::value>
struct ToV8Traits;

template <typename T>
struct ToV8Traits<T, true> {
  static bool TryConvertToV8(v8::Isolate* isolate,
                             const T& input,
                             v8::Local<v8::Value>* output) {
    auto maybe = ConvertToV8(isolate->GetCurrentContext(), input);
    if (maybe.IsEmpty())
      return false;
    *output = maybe.ToLocalChecked();
    return true;
  }
};

template <typename T>
struct ToV8Traits<T, false> {
  static bool TryConvertToV8(v8::Isolate* isolate,
                             const T& input,
                             v8::Local<v8::Value>* output) {
    *output = ConvertToV8(isolate, input);
    return true;
  }
};

template <typename T>
bool TryConvertToV8(v8::Isolate* isolate,
                    const T& input,
                    v8::Local<v8::Value>* output) {
  return ToV8Traits<T>::TryConvertToV8(isolate, input, output);
}

template <typename T>
bool ConvertFromV8(v8::Isolate* isolate,
                   v8::Local<v8::Value> input,
                   T* result) {
  return Converter<T>::FromV8(isolate, input, result);
}

inline v8::Local<v8::String> StringToV8(v8::Isolate* isolate,
                                        base::StringPiece input) {
  return ConvertToV8(isolate, input).As<v8::String>();
}

}  // namespace mate

#endif  // NATIVE_MATE_NATIVE_MATE_CONVERTER_H_

electron\native_mate\native_mate\converter.cc

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE.chromium file.

#include "native_mate/converter.h"

#include "v8/include/v8.h"

namespace mate {

namespace {

template <typename T, typename U>
bool FromMaybe(v8::Maybe<T> maybe, U* out) {
  if (maybe.IsNothing())
    return false;
  *out = static_cast<U>(maybe.FromJust());
  return true;
}

}  // namespace

v8::Local<v8::Value> Converter<bool>::ToV8(v8::Isolate* isolate, bool val) {
  return v8::Boolean::New(isolate, val);
}

bool Converter<bool>::FromV8(v8::Isolate* isolate,
                             v8::Local<v8::Value> val,
                             bool* out) {
  if (!val->IsBoolean())
    return false;
  *out = val.As<v8::Boolean>()->Value();
  return true;
}

#if !defined(OS_LINUX) && !defined(OS_FREEBSD)
v8::Local<v8::Value> Converter<unsigned long>::ToV8(  // NOLINT(runtime/int)
    v8::Isolate* isolate,
    unsigned long val) {  // NOLINT(runtime/int)
  return v8::Integer::New(isolate, val);
}

bool Converter<unsigned long>::FromV8(  // NOLINT(runtime/int)
    v8::Isolate* isolate,
    v8::Local<v8::Value> val,
    unsigned long* out) {  // NOLINT(runtime/int)
  if (!val->IsNumber())
    return false;
  return FromMaybe(val->IntegerValue(isolate->GetCurrentContext()), out);
}
#endif

v8::Local<v8::Value> Converter<int32_t>::ToV8(v8::Isolate* isolate,
                                              int32_t val) {
  return v8::Integer::New(isolate, val);
}

bool Converter<int32_t>::FromV8(v8::Isolate* isolate,
                                v8::Local<v8::Value> val,
                                int32_t* out) {
  if (!val->IsInt32())
    return false;
  *out = val.As<v8::Int32>()->Value();
  return true;
}

v8::Local<v8::Value> Converter<uint32_t>::ToV8(v8::Isolate* isolate,
                                               uint32_t val) {
  return v8::Integer::NewFromUnsigned(isolate, val);
}

bool Converter<uint32_t>::FromV8(v8::Isolate* isolate,
                                 v8::Local<v8::Value> val,
                                 uint32_t* out) {
  if (!val->IsUint32())
    return false;
  *out = val.As<v8::Uint32>()->Value();
  return true;
}

v8::Local<v8::Value> Converter<int64_t>::ToV8(v8::Isolate* isolate,
                                              int64_t val) {
  return v8::Number::New(isolate, static_cast<double>(val));
}

bool Converter<int64_t>::FromV8(v8::Isolate* isolate,
                                v8::Local<v8::Value> val,
                                int64_t* out) {
  if (!val->IsNumber())
    return false;
  // Even though IntegerValue returns int64_t, JavaScript cannot represent
  // the full precision of int64_t, which means some rounding might occur.
  return FromMaybe(val->IntegerValue(isolate->GetCurrentContext()), out);
}

v8::Local<v8::Value> Converter<uint64_t>::ToV8(v8::Isolate* isolate,
                                               uint64_t val) {
  return v8::Number::New(isolate, static_cast<double>(val));
}

bool Converter<uint64_t>::FromV8(v8::Isolate* isolate,
                                 v8::Local<v8::Value> val,
                                 uint64_t* out) {
  if (!val->IsNumber())
    return false;
  return FromMaybe(val->IntegerValue(isolate->GetCurrentContext()), out);
}

v8::Local<v8::Value> Converter<float>::ToV8(v8::Isolate* isolate, float val) {
  return v8::Number::New(isolate, val);
}

bool Converter<float>::FromV8(v8::Isolate* isolate,
                              v8::Local<v8::Value> val,
                              float* out) {
  if (!val->IsNumber())
    return false;
  *out = static_cast<float>(val.As<v8::Number>()->Value());
  return true;
}

v8::Local<v8::Value> Converter<double>::ToV8(v8::Isolate* isolate, double val) {
  return v8::Number::New(isolate, val);
}

bool Converter<double>::FromV8(v8::Isolate* isolate,
                               v8::Local<v8::Value> val,
                               double* out) {
  if (!val->IsNumber())
    return false;
  *out = val.As<v8::Number>()->Value();
  return true;
}

v8::Local<v8::Value> Converter<const char*>::ToV8(v8::Isolate* isolate,
                                                  const char* val) {
  return v8::String::NewFromUtf8(isolate, val, v8::NewStringType::kNormal)
      .ToLocalChecked();
}

v8::Local<v8::Value> Converter<base::StringPiece>::ToV8(v8::Isolate* isolate,
                                                        base::StringPiece val) {
  return v8::String::NewFromUtf8(isolate, val.data(),
                                 v8::NewStringType::kNormal,
                                 static_cast<uint32_t>(val.length()))
      .ToLocalChecked();
}

v8::Local<v8::Value> Converter<std::string>::ToV8(v8::Isolate* isolate,
                                                  const std::string& val) {
  return Converter<base::StringPiece>::ToV8(isolate, val);
}

bool Converter<std::string>::FromV8(v8::Isolate* isolate,
                                    v8::Local<v8::Value> val,
                                    std::string* out) {
  if (!val->IsString())
    return false;
  v8::Local<v8::String> str = v8::Local<v8::String>::Cast(val);
  int length = str->Utf8Length(isolate);
  out->resize(length);
  str->WriteUtf8(isolate, &(*out)[0], length, nullptr,
                 v8::String::NO_NULL_TERMINATION);
  return true;
}

v8::Local<v8::Value> Converter<v8::Local<v8::Function>>::ToV8(
    v8::Isolate* isolate,
    v8::Local<v8::Function> val) {
  return val;
}

bool Converter<v8::Local<v8::Function>>::FromV8(v8::Isolate* isolate,
                                                v8::Local<v8::Value> val,
                                                v8::Local<v8::Function>* out) {
  if (!val->IsFunction())
    return false;
  *out = v8::Local<v8::Function>::Cast(val);
  return true;
}

v8::Local<v8::Value> Converter<v8::Local<v8::Object>>::ToV8(
    v8::Isolate* isolate,
    v8::Local<v8::Object> val) {
  return val;
}

bool Converter<v8::Local<v8::Object>>::FromV8(v8::Isolate* isolate,
                                              v8::Local<v8::Value> val,
                                              v8::Local<v8::Object>* out) {
  if (!val->IsObject())
    return false;
  *out = v8::Local<v8::Object>::Cast(val);
  return true;
}

v8::Local<v8::Value> Converter<v8::Local<v8::String>>::ToV8(
    v8::Isolate* isolate,
    v8::Local<v8::String> val) {
  return val;
}

bool Converter<v8::Local<v8::String>>::FromV8(v8::Isolate* isolate,
                                              v8::Local<v8::Value> val,
                                              v8::Local<v8::String>* out) {
  if (!val->IsString())
    return false;
  *out = v8::Local<v8::String>::Cast(val);
  return true;
}

v8::Local<v8::Value> Converter<v8::Local<v8::External>>::ToV8(
    v8::Isolate* isolate,
    v8::Local<v8::External> val) {
  return val;
}

bool Converter<v8::Local<v8::External>>::FromV8(v8::Isolate* isolate,
                                                v8::Local<v8::Value> val,
                                                v8::Local<v8::External>* out) {
  if (!val->IsExternal())
    return false;
  *out = v8::Local<v8::External>::Cast(val);
  return true;
}

v8::Local<v8::Value> Converter<v8::Local<v8::Array>>::ToV8(
    v8::Isolate* isolate,
    v8::Local<v8::Array> val) {
  return val;
}

bool Converter<v8::Local<v8::Array>>::FromV8(v8::Isolate* isolate,
                                             v8::Local<v8::Value> val,
                                             v8::Local<v8::Array>* out) {
  if (!val->IsArray())
    return false;
  *out = v8::Local<v8::Array>::Cast(val);
  return true;
}

v8::Local<v8::Value> Converter<v8::Local<v8::Value>>::ToV8(
    v8::Isolate* isolate,
    v8::Local<v8::Value> val) {
  return val;
}

v8::Local<v8::Promise> Converter<v8::Local<v8::Promise>>::ToV8(
    v8::Isolate* isolate,
    v8::Local<v8::Promise> val) {
  return val;
}

bool Converter<v8::Local<v8::Value>>::FromV8(v8::Isolate* isolate,
                                             v8::Local<v8::Value> val,
                                             v8::Local<v8::Value>* out) {
  *out = val;
  return true;
}

v8::Local<v8::String> StringToSymbol(v8::Isolate* isolate,
                                     base::StringPiece val) {
  return v8::String::NewFromUtf8(isolate, val.data(),
                                 v8::NewStringType::kInternalized,
                                 static_cast<uint32_t>(val.length()))
      .ToLocalChecked();
}

}  // namespace mate

std_converter.h

// Copyright (c) 2019 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#ifndef SHELL_COMMON_GIN_CONVERTERS_STD_CONVERTER_H_
#define SHELL_COMMON_GIN_CONVERTERS_STD_CONVERTER_H_

#include <map>
#include <set>
#include <utility>

#include "gin/converter.h"

namespace gin {

// Make it possible to convert move-only types.
template <typename T>
v8::Local<v8::Value> ConvertToV8(v8::Isolate* isolate, T&& input) {
  return Converter<typename std::remove_reference<T>::type>::ToV8(
      isolate, std::move(input));
}

#if !defined(OS_LINUX) && !defined(OS_FREEBSD)
template <>
struct Converter<unsigned long> {  // NOLINT(runtime/int)
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                   unsigned long val) {  // NOLINT(runtime/int)
    return v8::Integer::New(isolate, val);
  }
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     unsigned long* out) {  // NOLINT(runtime/int)
    auto maybe = val->IntegerValue(isolate->GetCurrentContext());
    if (maybe.IsNothing())
      return false;
    *out = maybe.FromJust();
    return true;
  }
};
#endif

template <>
struct Converter<std::nullptr_t> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, std::nullptr_t val) {
    return v8::Null(isolate);
  }
};

template <>
struct Converter<const char*> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, const char* val) {
    return v8::String::NewFromUtf8(isolate, val, v8::NewStringType::kNormal)
        .ToLocalChecked();
  }
};

template <>
struct Converter<char[]> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, const char* val) {
    return v8::String::NewFromUtf8(isolate, val, v8::NewStringType::kNormal)
        .ToLocalChecked();
  }
};

template <size_t n>
struct Converter<char[n]> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, const char* val) {
    return v8::String::NewFromUtf8(isolate, val, v8::NewStringType::kNormal,
                                   n - 1)
        .ToLocalChecked();
  }
};

template <>
struct Converter<v8::Local<v8::Array>> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                   v8::Local<v8::Array> val) {
    return val;
  }
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     v8::Local<v8::Array>* out) {
    if (!val->IsArray())
      return false;
    *out = v8::Local<v8::Array>::Cast(val);
    return true;
  }
};

template <>
struct Converter<v8::Local<v8::String>> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                   v8::Local<v8::String> val) {
    return val;
  }
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     v8::Local<v8::String>* out) {
    if (!val->IsString())
      return false;
    *out = v8::Local<v8::String>::Cast(val);
    return true;
  }
};

template <typename T>
struct Converter<std::set<T>> {
  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                   const std::set<T>& val) {
    v8::Local<v8::Array> result(
        v8::Array::New(isolate, static_cast<int>(val.size())));
    auto context = isolate->GetCurrentContext();
    typename std::set<T>::const_iterator it;
    int i;
    for (i = 0, it = val.begin(); it != val.end(); ++it, ++i)
      result->Set(context, i, Converter<T>::ToV8(isolate, *it)).Check();
    return result;
  }
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> val,
                     std::set<T>* out) {
    if (!val->IsArray())
      return false;

    auto context = isolate->GetCurrentContext();
    std::set<T> result;
    v8::Local<v8::Array> array(v8::Local<v8::Array>::Cast(val));
    uint32_t length = array->Length();
    for (uint32_t i = 0; i < length; ++i) {
      T item;
      if (!Converter<T>::FromV8(isolate,
                                array->Get(context, i).ToLocalChecked(), &item))
        return false;
      result.insert(item);
    }

    out->swap(result);
    return true;
  }
};

template <typename K, typename V>
struct Converter<std::map<K, V>> {
  static bool FromV8(v8::Isolate* isolate,
                     v8::Local<v8::Value> value,
                     std::map<K, V>* out) {
    if (!value->IsObject())
      return false;
    out->clear();
    v8::Local<v8::Context> context = isolate->GetCurrentContext();
    v8::Local<v8::Object> obj = value.As<v8::Object>();
    v8::Local<v8::Array> keys = obj->GetPropertyNames(context).ToLocalChecked();
    for (uint32_t i = 0; i < keys->Length(); ++i) {
      v8::MaybeLocal<v8::Value> maybe_v8key = keys->Get(context, i);
      if (maybe_v8key.IsEmpty())
        return false;
      v8::Local<v8::Value> v8key = maybe_v8key.ToLocalChecked();
      v8::MaybeLocal<v8::Value> maybe_v8value = obj->Get(context, v8key);
      if (maybe_v8value.IsEmpty())
        return false;
      K key;
      V value;
      if (!ConvertFromV8(isolate, v8key, &key) ||
          !ConvertFromV8(isolate, maybe_v8value.ToLocalChecked(), &value))
        return false;
      (*out)[key] = std::move(value);
    }
    return true;
  }

  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
                                   const std::map<K, V>& dict) {
    v8::Local<v8::Object> obj = v8::Object::New(isolate);
    v8::Local<v8::Context> context = isolate->GetCurrentContext();
    for (const auto& it : dict) {
      if (obj->Set(context, ConvertToV8(isolate, it.first),
                   ConvertToV8(isolate, it.second))
              .IsNothing())
        break;
    }
    return obj;
  }
};

}  // namespace gin

#endif  // SHELL_COMMON_GIN_CONVERTERS_STD_CONVERTER_H_

Node.js C++ codebase

Hi! 👋 You’ve found the C++ code backing Node.js. This README aims to help you
get started working on it and document some idioms you may encounter while
doing so.

Coding style

Node.js has a document detailing its C++ coding style
that can be helpful as a reference for stylistic issues.

V8 API documentation

A lot of the Node.js codebase is around what the underlying JavaScript engine,
V8, provides through its API for embedders. Knowledge of this API can also be
useful when working with native addons for Node.js written in C++, although for
new projects N-API is typically the better alternative.

V8 does not provide much public API documentation beyond what is
available in its C++ header files, most importantly v8.h, which can be
accessed online in the following locations:

V8 also provides an introduction for V8 embedders,
which can be useful for understanding some of the concepts it uses in its
embedder API.

Important concepts when using V8 are the ones of Isolates and
JavaScript value handles.

libuv API documentation

The other major dependency of Node.js is libuv, providing
the event loop and other operation system abstractions to Node.js.

There is a reference documentation for the libuv API.

Helpful concepts

A number of concepts are involved in putting together Node.js on top of V8 and
libuv. This section aims to explain some of them and how they work together.

Isolate

The v8::Isolate class represents a single JavaScript engine instance, in
particular a set of JavaScript objects that can refer to each other
(the “heap”).

The v8::Isolate is often passed to other V8 API functions, and provides some
APIs for managing the behaviour of the JavaScript engine or querying about its
current state or statistics such as memory usage.

V8 APIs are not thread-safe unless explicitly specified. In a typical Node.js
application, the main thread and any Worker threads each have one Isolate,
and JavaScript objects from one Isolate cannot refer to objects from
another Isolate.

Garbage collection, as well as other operations that affect the entire heap,
happen on a per-Isolate basis.

Typical ways of accessing the current Isolate in the Node.js code are:

V8 JavaScript values

V8 provides classes that mostly correspond to JavaScript types; for example,
v8::Value is a class representing any kind of JavaScript type, with
subclasses such as v8::Number (which in turn has subclasses like v8::Int32),
v8::Boolean or v8::Object. Most types are represented by subclasses
of v8::Object, e.g. v8::Uint8Array or v8::Date.

Internal fields

V8 provides the ability to store data in so-called “internal fields” inside
v8::Objects that were created as instances of C++-backed classes. The number
of fields needs to be defined when creating that class.

Both JavaScript values and void* pointers may be stored in such fields.
In most native Node.js objects, the first internal field is used to store a
pointer to a BaseObject subclass, which then contains all relevant
information associated with the JavaScript object.

The most typical way of working internal fields are:

  • obj->InternalFieldCount() to look up the number of internal fields for an
    object (0 for regular JavaScript objects).
  • obj->GetInternalField(i) to get a JavaScript value from an internal field.
  • obj->SetInternalField(i, v) to store a JavaScript value in an
    internal field.
  • obj->GetAlignedPointerFromInternalField(i) to get a void* pointer from an
    internal field.
  • obj->SetAlignedPointerInInternalField(i, p) to store a void* pointer in an
    internal field.

Contexts provide the same feature under the name “embedder data”.

JavaScript value handles

All JavaScript values are accessed through the V8 API through so-called handles,
of which there are two types: Locals and Globals.

Local handles

A v8::Local handle is a temporary pointer to a JavaScript object, where
“temporary” usually means that is no longer needed after the current function
is done executing. Local handles can only be allocated on the C++ stack.

Most of the V8 API uses Local handles to work with JavaScript values or return
them from functions.

Whenever a Local handle is created, a v8::HandleScope or
v8::EscapableHandleScope object must exist on the stack. The Local is then
added to that scope and deleted along with it.

When inside a binding function, a HandleScope already exists outside of
it, so there is no need to explicitly create one.

EscapableHandleScopes can be used to allow a single Local handle to be
passed to the outer scope. This is useful when a function returns a Local.

The following JavaScript and C++ functions are mostly equivalent:

function getFoo(obj) {
  return obj.foo;
}
v8::Local<v8::Value> GetFoo(v8::Local<v8::Context> context,
                            v8::Local<v8::Object> obj) {
  v8::Isolate* isolate = context->GetIsolate();
  v8::EscapableHandleScope handle_scope(isolate);

  // The 'foo_string' handle cannot be returned from this function because
  // it is not “escaped” with `.Escape()`.
  v8::Local<v8::String> foo_string =
      v8::String::NewFromUtf8(isolate,
                              "foo",
                              v8::NewStringType::kNormal).ToLocalChecked();

  v8::Local<v8::Value> return_value;
  if (obj->Get(context, foo_string).ToLocal(&return_value)) {
    return handle_scope.Escape(return_value);
  } else {
    // There was a JS exception! Handle it somehow.
    return v8::Local<v8::Value>();
  }
}

See exception handling for more information about the usage of .To(),
.ToLocalChecked(), v8::Maybe and v8::MaybeLocal usage.

Casting local handles

If it is known that a Local<Value> refers to a more specific type, it can
be cast to that type using .As<...>():

v8::Local<v8::Value> some_value;
// CHECK() is a Node.js utilitity that works similar to assert().
CHECK(some_value->IsUint8Array());
v8::Local<v8::Uint8Array> as_uint8 = some_value.As<v8::Uint8Array>();

Generally, using val.As<v8::X>() is only valid if val->IsX() is true, and
failing to follow that rule may lead to crashes.

Detecting handle leaks

If it is expected that no Local handles should be created within a given
scope unless explicitly within a HandleScope, a SealHandleScope can be used.

For example, there is a SealHandleScope around the event loop, forcing
any functions that are called from the event loop and want to run or access
JavaScript code to create HandleScopes.

Global handles

A v8::Global handle (sometimes also referred to by the name of its parent
class Persistent, although use of that is discouraged in Node.js) is a
reference to a JavaScript object that can remain active as long as the engine
instance is active.

Global handles can be either strong or weak. Strong global handles are so-called
“GC roots”, meaning that they will keep the JavaScript object they refer to
alive even if no other objects refer to them. Weak global handles do not do
that, and instead optionally call a callback when the object they refer to
is garbage-collected.

v8::Global<v8::Object> reference;

void StoreReference(v8::Isolate* isolate, v8::Local<v8::Object> obj) {
  // Create a strong reference to `obj`.
  reference.Reset(isolate, obj);
}

// Must be called with a HandleScope around it.
v8::Local<v8::Object> LoadReference(v8::Isolate* isolate) {
  return reference.Get(isolate);
}
Eternal handles

v8::Eternal handles are a special kind of handles similar to v8::Global
handles, with the exception that the values they point to are never
garbage-collected while the JavaScript Engine instance is alive, even if
the v8::Eternal itself is destroyed at some point. This type of handle
is rarely used.

Context

JavaScript allows multiple global objects and sets of built-in JavaScript
objects (like the Object or Array functions) to coexist inside the same
heap. Node.js exposes this ability through the vm module.

V8 refers to each of these global objects and their associated builtins as a
Context.

Currently, in Node.js there is one main Context associated with an
Environment instance, and most Node.js features will only work inside
that context. (The only exception at the time of writing are
MessagePort objects.) This restriction is not inherent to the design of
Node.js, and a sufficiently committed person could restructure Node.js to
provide built-in modules inside of vm.Contexts.

Often, the Context is passed around for exception handling.
Typical ways of accessing the current Environment in the Node.js code are:

  • Given an Isolate, using isolate->GetCurrentContext().
  • Given an Environment, using env->context() to get the Environment’s
    main context.

Event loop

The main abstraction for an event loop inside Node.js is the uv_loop_t struct.
Typically, there is one event loop per thread. This includes not only the main
thread and Workers, but also helper threads that may occasionally be spawned
in the course of running a Node.js program.

The current event loop can be accessed using env->event_loop() given an
Environment instance. The restriction of using a single event loop
is not inherent to the design of Node.js, and a sufficiently committed person
could restructure Node.js to provide e.g. the ability to run parts of Node.js
inside an event loop separate from the active thread’s event loop.

Environment

Node.js instances are represented by the Environment class.

Currently, every Environment class is associated with:

The Environment class contains a large number of different fields for
different Node.js modules, for example a libuv timer for setTimeout() or
the memory for a Float64Array that the fs module uses for storing data
returned from a fs.stat() call.

It also provides cleanup hooks and maintains a list of BaseObject
instances.

Typical ways of accessing the current Environment in the Node.js code are:

  • Given a FunctionCallbackInfo for a binding function,
    using Environment::GetCurrent(args).
  • Given a BaseObject, using env() or self->env().
  • Given a Context, using Environment::GetCurrent(context).
    This requires that context has been associated with the Environment
    instance, e.g. is the main Context for the Environment or one of its
    vm.Contexts.
  • Given an Isolate, using Environment::GetCurrent(isolate). This looks
    up the current Context and then uses that.

IsolateData

Every Node.js instance (Environment) is associated with one IsolateData
instance that contains information about or associated with a given
Isolate.

String table

IsolateData contains a list of strings that can be quickly accessed
inside Node.js code, e.g. given an Environment instance env the JavaScript
string “name” can be accessed through env->name_string() without actually
creating a new JavaScript string.

Platform

Every process that uses V8 has a v8::Platform instance that provides some
functionalities to V8, most importantly the ability to schedule work on
background threads.

Node.js provides a NodePlatform class that implements the v8::Platform
interface and uses libuv for providing background threading abilities.

The platform can be accessed through isolate_data->platform() given an
IsolateData instance, although that only works when:

  • The current Node.js instance was not started by an embedder; or
  • The current Node.js instance was started by an embedder whose v8::Platform
    implementation also implement’s the node::MultiIsolatePlatform interface
    and who passed this to Node.js.

Binding functions

C++ functions exposed to JS follow a specific signature. The following example
is from node_util.cc:

void ArrayBufferViewHasBuffer(const FunctionCallbackInfo<Value>& args) {
  CHECK(args[0]->IsArrayBufferView());
  args.GetReturnValue().Set(args[0].As<ArrayBufferView>()->HasBuffer());
}

(Namespaces are usually omitted through the use of using statements in the
Node.js source code.)

args[n] is a Local<Value> that represents the n-th argument passed to the
function. args.This() is the this value inside this function call.
args.Holder() is equivalent to args.This() in all use cases inside of
Node.js.

args.GetReturnValue() is a placeholder for the return value of the function,
and provides a .Set() method that can be called with a boolean, integer,
floating-point number or a Local<Value> to set the return value.

Node.js provides various helpers for building JS classes in C++ and/or attaching
C++ functions to the exports of a built-in module:

void Initialize(Local<Object> target,
                Local<Value> unused,
                Local<Context> context,
                void* priv) {
  Environment* env = Environment::GetCurrent(context);

  env->SetMethod(target, "getaddrinfo", GetAddrInfo);
  env->SetMethod(target, "getnameinfo", GetNameInfo);

  // 'SetMethodNoSideEffect' means that debuggers can safely execute this
  // function for e.g. previews.
  env->SetMethodNoSideEffect(target, "canonicalizeIP", CanonicalizeIP);

  // ... more code ...

  // Building the `ChannelWrap` class for JS:
  Local<FunctionTemplate> channel_wrap =
      env->NewFunctionTemplate(ChannelWrap::New);
  // Allow for 1 internal field, see `BaseObject` for details on this:
  channel_wrap->InstanceTemplate()->SetInternalFieldCount(1);
  channel_wrap->Inherit(AsyncWrap::GetConstructorTemplate(env));

  // Set various methods on the class (i.e. on the prototype):
  env->SetProtoMethod(channel_wrap, "queryAny", Query<QueryAnyWrap>);
  env->SetProtoMethod(channel_wrap, "queryA", Query<QueryAWrap>);
  // ...
  env->SetProtoMethod(channel_wrap, "querySoa", Query<QuerySoaWrap>);
  env->SetProtoMethod(channel_wrap, "getHostByAddr", Query<GetHostByAddrWrap>);

  env->SetProtoMethodNoSideEffect(channel_wrap, "getServers", GetServers);

  Local<String> channel_wrap_string =
      FIXED_ONE_BYTE_STRING(env->isolate(), "ChannelWrap");
  channel_wrap->SetClassName(channel_wrap_string);
  target->Set(env->context(), channel_wrap_string,
              channel_wrap->GetFunction(context).ToLocalChecked()).Check();
}

// Run the `Initialize` function when loading this module through
// `internalBinding('cares_wrap')` in Node.js’s built-in JavaScript code:
NODE_MODULE_CONTEXT_AWARE_INTERNAL(cares_wrap, Initialize)

Exception handling

The V8 engine provides multiple features to work with JavaScript exceptions,
as C++ exceptions are disabled inside of Node.js:

Maybe types

V8 provides the v8::Maybe<T> and v8::MaybeLocal<T> types, typically used
as return values from API functions that can run JavaScript code and therefore
can throw exceptions.

Conceptually, the idea is that every v8::Maybe<T> is either empty (checked
through .IsNothing()) or holds a value of type T (checked through
.IsJust()). If the Maybe is empty, then a JavaScript exception is pending.
A typical way of accessing the value is using the .To() function, which
returns a boolean indicating success of the operation (i.e. the Maybe not
being empty) and taking a pointer to a T to store the value if there is one.

Checked conversion

maybe.Check() can be used to assert that the maybe is not empty, i.e. crash
the process otherwise. maybe.FromJust() (aka maybe.ToChecked()) can be used
to access the value and crash the process if it is not set.

This should only be performed if it is actually sure that the operation has
not failed. A lot of Node.js’s source code does not follow this rule, and
can be brought to crash through this.

In particular, it is often not safe to assume that an operation does not throw
an exception, even if it seems like it would not do that.
The most common reasons for this are:

  • Calls to functions like object->Get(...) or object->Set(...) may fail on
    most objects, if the Object.prototype object has been modified from userland
    code that added getters or setters.
  • Calls that invoke any JavaScript code, including JavaScript code that is
    provided from Node.js internals or V8 internals, will fail when JavaScript
    execution is being terminated. This typically happens inside Workers when
    worker.terminate() is called, but it can also affect the main thread when
    e.g. Node.js is used as an embedded library. These exceptions can happen at
    any point.
    It is not always obvious whether a V8 call will enter JavaScript. In addition
    to unexpected getters and setters, accessing some types of built-in objects
    like Maps and Sets can also run V8-internal JavaScript code.
MaybeLocal

v8::MaybeLocal<T> is a variant of v8::Maybe<T> that is either empty or
holds a value of type Local<T>. It has methods that perform the same
operations as the methods of v8::Maybe, but with different names:

Maybe MaybeLocal
maybe.IsNothing() maybe_local.IsEmpty()
maybe.IsJust() !maybe_local.IsEmpty()
maybe.To(&value) maybe_local.ToLocal(&local)
maybe.ToChecked() maybe_local.ToLocalChecked()
maybe.FromJust() maybe_local.ToLocalChecked()
maybe.Check()
v8::Nothing<T>() v8::MaybeLocal<T>()
v8::Just<T>(value) v8::MaybeLocal<T>(value)
Handling empty Maybes

Usually, the best approach to encountering an empty Maybe is to just return
from the current function as soon as possible, and let execution in JavaScript
land resume. If the empty Maybe is encountered inside a nested function,
is may be a good idea to use a Maybe or MaybeLocal for the return type
of that function and pass information about pending JavaScript exceptions along
that way.

Generally, when an empty Maybe is encountered, it is not valid to attempt
to perform further calls to APIs that return Maybes.

A typical pattern for dealing with APIs that return Maybe and MaybeLocal is
using .ToLocal() and .To() and returning early in case there is an error:

// This could also return a v8::MaybeLocal<v8::Number>, for example.
v8::Maybe<double> SumNumbers(v8::Local<v8::Context> context,
                             v8::Local<v8::Array> array_of_integers) {
  v8::Isolate* isolate = context->GetIsolate();
  v8::HandleScope handle_scope(isolate);

  double sum = 0;

  for (uint32_t i = 0; i < array_of_integers->Length(); i++) {
    v8::Local<v8::Value> entry;
    if (array_of_integers->Get(context, i).ToLocal(&entry)) {
      // Oops, we might have hit a getter that throws an exception!
      // It’s better to not continue return an empty (“nothing”) Maybe.
      return v8::Nothing<double>();
    }

    if (!entry->IsNumber()) {
      // Let’s just skip any non-numbers. It would also be reasonable to throw
      // an exception here, e.g. using the error system in src/node_errors.h,
      // and then to return an empty Maybe again.
      continue;
    }

    // This cast is valid, because we’ve made sure it’s really a number.
    v8::Local<v8::Number> entry_as_number = entry.As<v8::Number>();

    sum += entry_as_number->Value();
  }

  return v8::Just(sum);
}

// Function that is exposed to JS:
void SumNumbers(const v8::FunctionCallbackInfo<v8::Value>& args) {
  // This will crash if the first argument is not an array. Let’s assume we
  // have performed type checking in a JavaScript wrapper function.
  CHECK(args[0]->IsArray());

  double sum;
  if (!SumNumbers(args.GetIsolate()->GetCurrentContext(),
                  args[0].As<v8::Array>()).To(&sum)) {
    // Nothing to do, we can just return directly to JavaScript.
    return;
  }

  args.GetReturnValue().Set(sum);
}

TryCatch

If there is a need to catch JavaScript exceptions in C++, V8 provides the
v8::TryCatch type for doing so, which we wrap into our own
node::errors::TryCatchScope in Node.js. The latter has the additional feature
of providing the ability to shut down the program in the typical Node.js way
(printing the exception + stack trace) if an exception is caught.

A TryCatch will catch regular JavaScript exceptions, as well as termination
exceptions such as the ones thrown by worker.terminate() calls.
In the latter case, the try_catch.HasTerminated() function will return true,
and the exception object will not be a meaningful JavaScript value.
try_catch.ReThrow() should not be used in this case.

libuv handles and requests

Two central concepts when working with libuv are handles and requests.

Handles are subclasses of the uv_handle_t “class”, and generally refer to
long-lived objects that can emit events multiple times, such as network sockets
or file system watchers.

In Node.js, handles are often managed through a HandleWrap subclass.

Requests are one-time asynchronous function calls on the event loop, such as
file system requests or network write operations, that either succeed or fail.

In Node.js, requests are often managed through a ReqWrap subclass.

Environment cleanup

When a Node.js Environment is destroyed, it generally needs to clean up
any resources owned by it, e.g. memory or libuv requests/handles.

Cleanup hooks

Cleanup hooks are provided that run before the Environment
is destroyed. They can be added and removed through by using
env->AddCleanupHook(callback, hint); and
env->RemoveCleanupHook(callback, hint);, where callback takes a void* hint
argument.

Inside these cleanup hooks, new asynchronous operations may be started on the
event loop, although ideally that is avoided as much as possible.

Every BaseObject has its own cleanup hook that deletes it. For
ReqWrap and HandleWrap instances, cleanup of the associated libuv
objects is performed automatically, i.e. handles are closed and requests
are cancelled if possible.

Closing libuv handles

If a libuv handle is not managed through a HandleWrap instance,
it needs to be closed explicitly. Do not use uv_close() for that, but rather
env->CloseHandle(), which works the same way but keeps track of the number
of handles that are still closing.

Closing libuv requests

There is no way to abort libuv requests in general. If a libuv request is not
managed through a ReqWrap instance, the
env->IncreaseWaitingRequestCounter() and
env->DecreaseWaitingRequestCounter() functions need to be used to keep track
of the number of active libuv requests.

Calling into JavaScript

Calling into JavaScript is not allowed during cleanup. Worker threads explicitly
forbid this during their shutdown sequence, but the main thread does not for
backwards compatibility reasons.

When calling into JavaScript without using MakeCallback(), check the
env->can_call_into_js() flag and do not proceed if it is set to false.

Classes associated with JavaScript objects

MemoryRetainer

A large number of classes in the Node.js C++ codebase refer to other objects.
The MemoryRetainer class is a helper for annotating C++ classes with
information that can be used by the heap snapshot builder in V8, so that
memory retained by C++ can be tracked in V8 heap snapshots captured in
Node.js applications.

Inheriting from the MemoryRetainer class enables objects (both from JavaScript
and C++) to refer to instances of that class, and in turn enables that class
to point to other objects as well, including native C++ types
such as std::string and track their memory usage.

This can be useful for debugging memory leaks.

The memory_tracker.h header file explains how to use this class.

BaseObject

A frequently recurring situation is that a JavaScript object and a C++ object
need to be tied together. BaseObject is the main abstraction for that in
Node.js, and most classes that are associated with JavaScript objects are
subclasses of it. It is defined in base_object.h.

Every BaseObject is associated with one Environment and one
v8::Object. The v8::Object needs to have at least one internal field
that is used for storing the pointer to the C++ object. In order to ensure this,
the V8 SetInternalFieldCount() function is usually used when setting up the
class from C++.

The JavaScript object can be accessed as a v8::Local<v8::Object> by using
self->object(), given a BaseObject named self.

Accessing a BaseObject from a v8::Local<v8::Object> (frequently that is
args.This() or args.Holder() in a binding function) can be done using
the Unwrap<T>(obj) function, where T is a subclass of BaseObject.
A helper for this is the ASSIGN_OR_RETURN_UNWRAP macro that returns from the
current function if unwrapping fails (typically that means that the BaseObject
has been deleted earlier).

void Http2Session::Request(const FunctionCallbackInfo<Value>& args) {
  Http2Session* session;
  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
  Environment* env = session->env();
  Local<Context> context = env->context();
  Isolate* isolate = env->isolate();

  // ...
  // The actual function body, which can now use the `session` object.
  // ...
}

Lifetime management

The BaseObject class comes with a set of features that allow managing the
lifetime of its instances, either associating it with the lifetime of the
corresponding JavaScript object or untying the two.

The BaseObject::MakeWeak() method turns the underlying Global handle
into a weak one, and makes it so that the BaseObject::OnGCCollect() virtual
method is called when the JavaScript object is garbage collected. By default,
that methods deletes the BaseObject instance.

BaseObject::ClearWeak() undoes this effect.

It generally makes sense to call MakeWeak() in the constructor of a
BaseObject subclass, unless that subclass is referred to by e.g. the event
loop, as is the case for the HandleWrap and ReqWrap classes.

In addition, there are two kinds of smart pointers that can be used to refer
to BaseObjects.

BaseObjectWeakPtr<T> is similar to std::weak_ptr<T>, but holds on to
an object of a BaseObject subclass T and integrates with the lifetime
management of the former. When the BaseObject no longer exists, e.g. when
it was garbage collected, accessing it through weak_ptr.get() will return
nullptr.

BaseObjectPtr<T> is similar to std::shared_ptr<T>, but also holds on to
objects of a BaseObject subclass T. While there are BaseObjectPtrs
pointing to a given object, the BaseObject will always maintain a strong
reference to its associated JavaScript object. This can be useful when one
BaseObject refers to another BaseObject and wants to make sure it stays
alive during the lifetime of that reference.

A BaseObject can be “detached” throught the BaseObject::Detach() method.
In this case, it will be deleted once the last BaseObjectPtr referring to
it is destroyed. There must be at least one such pointer when Detach() is
called. This can be useful when one BaseObject fully owns another
BaseObject.

AsyncWrap

AsyncWrap is a subclass of BaseObject that additionally provides tracking
functions for asynchronous calls. It is commonly used for classes whose methods
make calls into JavaScript without any JavaScript stack below, i.e. more or less
directly from the event loop. It is defined in async_wrap.h.

Every AsyncWrap subclass has a “provider type”. A list of provider types is
maintained in src/async_wrap.h.

Every AsyncWrap instance is associated with two numbers, the “async id”
and the “async trigger id”. The “async id” is generally unique per AsyncWrap
instance, and only changes when the object is re-used in some way.

See the async_hooks module documentation for more information about how
this information is provided to async tracking tools.

MakeCallback

The AsyncWrap class has a set of methods called MakeCallback(), with the
intention of the naming being that it is used to “make calls back into
JavaScript” from the event loop, rather than making callbacks in some way.
(As the naming has made its way into Node.js’s public API, it’s not worth
the breakage of fixing it).

MakeCallback() generally calls a method on the JavaScript object associated
with the current AsyncWrap, and informs async tracking code about these calls
as well as takes care of running the process.nextTick() and Promise task
queues once it returns.

Before calling MakeCallback(), it is typically necessary to enter both a
HandleScope and a Context::Scope.

void StatWatcher::Callback(uv_fs_poll_t* handle,
                           int status,
                           const uv_stat_t* prev,
                           const uv_stat_t* curr) {
  // Get the StatWatcher instance associated with this call from libuv,
  // StatWatcher is a subclass of AsyncWrap.
  StatWatcher* wrap = ContainerOf(&StatWatcher::watcher_, handle);
  Environment* env = wrap->env();
  HandleScope handle_scope(env->isolate());
  Context::Scope context_scope(env->context());

  // Transform 'prev' and 'curr' into an array:
  Local<Value> arr = ...;

  Local<Value> argv[] = { Integer::New(env->isolate(), status), arr };
  wrap->MakeCallback(env->onchange_string(), arraysize(argv), argv);
}

See Callback scopes for more information.

HandleWrap

HandleWrap is a subclass of AsyncWrap specifically designed to make working
with libuv handles easier. It provides the .ref(), .unref() and
.hasRef() methods as well as .close() to enable easier lifetime management
from JavaScript. It is defined in handle_wrap.h.

HandleWrap instances are cleaned up automatically when the
current Node.js Environment is destroyed, e.g. when a Worker thread stops.

HandleWrap also provides facilities for diagnostic tooling to get an
overview over libuv handles managed by Node.js.

ReqWrap

ReqWrap is a subclass of AsyncWrap specifically designed to make working
with libuv requests easier. It is defined in req_wrap.h.

In particular, its Dispatch() method is designed to avoid the need to keep
track of the current count of active libuv requests.

ReqWrap also provides facilities for diagnostic tooling to get an
overview over libuv handles managed by Node.js.

Callback scopes

The public CallbackScope and the internally used InternalCallbackScope
classes provide the same facilities as MakeCallback(), namely:

  • Emitting the 'before' event for async tracking when entering the scope
  • Setting the current async IDs to the ones passed to the constructor
  • Emitting the 'after' event for async tracking when leaving the scope
  • Running the process.nextTick() queue
  • Running microtasks, in particular Promise callbacks and async/await
    functions

Usually, using AsyncWrap::MakeCallback() or using the constructor taking
an AsyncWrap* argument (i.e. used as
InternalCallbackScope callback_scope(this);) suffices inside of Node.js’s
C++ codebase.

C++ utilities

Node.js uses a few custom C++ utilities, mostly defined in util.h.

Memory allocation

Node.js provides Malloc(), Realloc() and Calloc() functions that work
like their C stdlib counterparts, but crash if memory cannot be allocated.
(As V8 does not handle out-of-memory situations gracefully, it does not make
sense for Node.js to attempt to do so in all cases.)

The UncheckedMalloc(), UncheckedRealloc() and UncheckedCalloc() functions
return nullptr in these cases (or when size == 0).

Optional stack-based memory allocation

The MaybeStackBuffer class provides a way to allocate memory on the stack
if it is smaller than a given limit, and falls back to allocating it on the
heap if it is larger. This can be useful for performantly allocating temporary
data if it is typically expected to be small (e.g. file paths).

The Utf8Value, TwoByteValue (i.e. UTF-16 value) and BufferValue
(Utf8Value but copy data from a Buffer if one is passed) helpers
inherit from this class and allow accessing the characters in a JavaScript
string this way.

static void Chdir(const FunctionCallbackInfo<Value>& args) {
  Environment* env = Environment::GetCurrent(args);
  // ...
  CHECK(args[0]->IsString());
  Utf8Value path(env->isolate(), args[0]);
  int err = uv_chdir(*path);
  if (err) {
    // ... error handling ...
  }
}

Assertions

Node.js provides a few macros that behave similar to assert():

  • CHECK(expression) aborts the process with a stack trace
    if expression is false.
  • CHECK_EQ(a, b) checks for a == b
  • CHECK_GE(a, b) checks for a >= b
  • CHECK_GT(a, b) checks for a > b
  • CHECK_LE(a, b) checks for a <= b
  • CHECK_LT(a, b) checks for a < b
  • CHECK_NE(a, b) checks for a != b
  • CHECK_NULL(val) checks for a == nullptr
  • CHECK_NOT_NULL(val) checks for a != nullptr
  • CHECK_IMPLIES(a, b) checks that b is true if a is true.
  • UNREACHABLE([message]) aborts the process if it is reached.

CHECKs are always enabled. For checks that should only run in debug mode, use
DCHECK(), DCHECK_EQ(), etc.

Scope-based cleanup

The OnScopeLeave() function can be used to run a piece of code when leaving
the current C++ scope.

static void GetUserInfo(const FunctionCallbackInfo<Value>& args) {
  Environment* env = Environment::GetCurrent(args);
  uv_passwd_t pwd;
  // ...

  const int err = uv_os_get_passwd(&pwd);

  if (err) {
    // ... error handling, return early ...
  }

  auto free_passwd = OnScopeLeave([&]() { uv_os_free_passwd(&pwd); });

  // ...
  // Turn `pwd` into a JavaScript object now; whenever we return from this
  // function, `uv_os_free_passwd()` will be called.
  // ...
}

参考

发表评论

电子邮件地址不会被公开。 必填项已用*标注