@ 261105.16
current
#pragma once

#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#include "base_object.h"
#include "ffi.h"
#include "ffi/fast.h"
#include "uv.h"

#include <cstdint>
#include <memory>
#include <string>
#include <thread>
#include <unordered_map>
#include <unordered_set>
#include <vector>

// libffi only accelerates reusable call plans on x86-64 System V. Other
// targets implement the API by calling ffi_call(), which adds no benefit.
#if defined(FFI_VERSION_NUMBER) && FFI_VERSION_NUMBER >= 30700 &&              \
    defined(__x86_64__) && !defined(__ILP32__) && !defined(X86_WIN64) &&       \
    !defined(_WIN32)
#define NODE_FFI_HAS_FAST_CALL_PLAN 1
#endif

namespace node::ffi {

class DynamicLibrary;
struct FFIFunction;

struct FFIFunction {
  FFIFunction() = default;
  ~FFIFunction() {
    if (auto registry = pointer_registry.lock()) {
      registry->erase(this);
    }
  }
  FFIFunction(const FFIFunction&) = delete;
  FFIFunction& operator=(const FFIFunction&) = delete;
  FFIFunction(FFIFunction&&) = delete;
  FFIFunction& operator=(FFIFunction&&) = delete;

  bool closed = false;
  std::weak_ptr<std::unordered_set<FFIFunction*>> pointer_registry;

  void* ptr = nullptr;
  ffi_cif cif = {};
  std::vector<ffi_type*> args;
  ffi_type* return_type = nullptr;
  std::vector<std::string> arg_type_names;
  std::string return_type_name;
#if defined(NODE_FFI_HAS_FAST_CALL_PLAN)
  // The plan borrows cif, so it must remain uniquely owned by this instance.
  std::unique_ptr<ffi_call_plan, decltype(&ffi_call_plan_free)> call_plan{
      nullptr, ffi_call_plan_free};
#endif

  void Invoke(void* result, void** values);
};

class FFIFunctionInfo final : public BaseObject {
 public:
  enum InternalFields {
    kLibrary = BaseObject::kInternalFieldCount,
    kInternalFieldCount
  };

  FFIFunctionInfo(Environment* env,
                  v8::Local<v8::Object> object,
                  std::shared_ptr<FFIFunction> fn,
                  DynamicLibrary* library);

  void MemoryInfo(MemoryTracker* tracker) const override;
  SET_MEMORY_INFO_NAME(FFIFunctionInfo)
  SET_SELF_SIZE(FFIFunctionInfo)

  static BaseObjectPtr<FFIFunctionInfo> Create(Environment* env,
                                               std::shared_ptr<FFIFunction> fn,
                                               DynamicLibrary* library);
  static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
      IsolateData* isolate_data);

 private:
  std::shared_ptr<FFIFunction> fn;
  std::shared_ptr<v8::BackingStore> sb_backing;
  std::unique_ptr<FastFFIMetadata> fast_metadata;
  std::unique_ptr<FastFFIMetadata> fast_buffer_metadata;

  friend class DynamicLibrary;
};

struct FFICallback {
  // Callback pointers are plain addresses that cannot root the library, so
  // each registered callback keeps its owner alive until it is unregistered
  // or the library is closed.
  BaseObjectPtr<DynamicLibrary> owner;
  Environment* env;
  std::thread::id thread_id;
  v8::Global<v8::Function> fn;
  ffi_closure* closure;

  void* ptr;
  ffi_cif cif;
  std::vector<ffi_type*> args;
  ffi_type* return_type;

  ~FFICallback() {
    if (!fn.IsEmpty()) {
      fn.Reset();
    }

    if (closure != nullptr) {
      ffi_closure_free(closure);
      closure = nullptr;
    }
  }
};

struct ResolvedFunction {
  std::string name;
  std::shared_ptr<FFIFunction> fn;
  bool should_cache_symbol;
  bool should_cache_function;
};

class DynamicLibrary : public BaseObject {
 public:
  DynamicLibrary(Environment* env, v8::Local<v8::Object> object);
  ~DynamicLibrary() override;

  void MemoryInfo(MemoryTracker* tracker) const override;

  static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
      Environment* env);
  static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
  static void Close(const v8::FunctionCallbackInfo<v8::Value>& args);
  static void InvokeFunction(const v8::FunctionCallbackInfo<v8::Value>& args);
  static void InvokeFunctionSB(const v8::FunctionCallbackInfo<v8::Value>& args);
  static void InvokeCallback(ffi_cif* cif,
                             void* ret,
                             void** args,
                             void* user_data);

  static void GetPath(const v8::FunctionCallbackInfo<v8::Value>& args);
  static void GetFunction(const v8::FunctionCallbackInfo<v8::Value>& args);
  static void ToFunction(const v8::FunctionCallbackInfo<v8::Value>& args);
  static void GetFunctions(const v8::FunctionCallbackInfo<v8::Value>& args);
  static void GetSymbol(const v8::FunctionCallbackInfo<v8::Value>& args);
  static void GetSymbols(const v8::FunctionCallbackInfo<v8::Value>& args);

  static void RegisterCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
  static void UnregisterCallback(
      const v8::FunctionCallbackInfo<v8::Value>& args);
  static void RefCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
  static void UnrefCallback(const v8::FunctionCallbackInfo<v8::Value>& args);

  SET_MEMORY_INFO_NAME(DynamicLibrary)
  SET_SELF_SIZE(DynamicLibrary)

 private:
  void Close();
  v8::Maybe<void*> ResolveSymbol(Environment* env, const std::string& name);
  struct PreparedFunction {
    std::shared_ptr<FFIFunction> fn;
    bool should_cache_symbol;
    bool should_cache_function;
  };
  v8::Maybe<PreparedFunction> PrepareFunction(Environment* env,
                                              const std::string& name,
                                              v8::Local<v8::Object> signature,
                                              void* ptr = nullptr);
  v8::MaybeLocal<v8::Function> CreateFunction(
      Environment* env,
      const std::string& name,
      const std::shared_ptr<FFIFunction>& fn);
  v8::MaybeLocal<v8::Function> BuildFunction(
      Environment* env,
      const std::string& name,
      const std::shared_ptr<FFIFunction>& fn,
      bool optimize);
  static void CleanupHook(void* arg);
  static void CleanupFunctionInfo(
      const v8::WeakCallbackInfo<FFIFunctionInfo>& data);
  bool is_closed() const;

  uv_lib_t lib_ = {};
  std::string path_;
  std::unordered_map<std::string, void*> symbols_;
  std::unordered_map<std::string, std::shared_ptr<FFIFunction>> functions_;
  // Callables created for `functions_`, so repeated resolution of the same
  // symbol reuses one wrapper instead of emitting another trampoline. The
  // handles are weak: an entry disappears once user code drops the wrapper,
  // which keeps the map from rooting the library through the wrapper's
  // FFIFunctionInfo.
  std::unordered_map<std::string, v8::Global<v8::Function>> function_wrappers_;
  std::shared_ptr<std::unordered_set<FFIFunction*>> pointer_functions_;
  std::unordered_map<void*, std::unique_ptr<FFICallback>> callbacks_;
};

void GetInt8(const v8::FunctionCallbackInfo<v8::Value>& args);
void GetUint8(const v8::FunctionCallbackInfo<v8::Value>& args);
void GetInt16(const v8::FunctionCallbackInfo<v8::Value>& args);
void GetUint16(const v8::FunctionCallbackInfo<v8::Value>& args);
void GetInt32(const v8::FunctionCallbackInfo<v8::Value>& args);
void GetUint32(const v8::FunctionCallbackInfo<v8::Value>& args);
void GetInt64(const v8::FunctionCallbackInfo<v8::Value>& args);
void GetUint64(const v8::FunctionCallbackInfo<v8::Value>& args);
void GetFloat32(const v8::FunctionCallbackInfo<v8::Value>& args);
void GetFloat64(const v8::FunctionCallbackInfo<v8::Value>& args);

void SetInt8(const v8::FunctionCallbackInfo<v8::Value>& args);
void SetUint8(const v8::FunctionCallbackInfo<v8::Value>& args);
void SetInt16(const v8::FunctionCallbackInfo<v8::Value>& args);
void SetUint16(const v8::FunctionCallbackInfo<v8::Value>& args);
void SetInt32(const v8::FunctionCallbackInfo<v8::Value>& args);
void SetUint32(const v8::FunctionCallbackInfo<v8::Value>& args);
void SetInt64(const v8::FunctionCallbackInfo<v8::Value>& args);
void SetUint64(const v8::FunctionCallbackInfo<v8::Value>& args);
void SetFloat32(const v8::FunctionCallbackInfo<v8::Value>& args);
void SetFloat64(const v8::FunctionCallbackInfo<v8::Value>& args);

void ToString(const v8::FunctionCallbackInfo<v8::Value>& args);
void ToBuffer(const v8::FunctionCallbackInfo<v8::Value>& args);
void ToArrayBuffer(const v8::FunctionCallbackInfo<v8::Value>& args);
void ExportBytes(const v8::FunctionCallbackInfo<v8::Value>& args);
void GetRawPointer(const v8::FunctionCallbackInfo<v8::Value>& args);
void GetCurrentEventLoop(const v8::FunctionCallbackInfo<v8::Value>& args);

}  // namespace node::ffi

#endif  // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS