Change to node-addon-api
authorNick Downing <nick@ndcode.org>
Mon, 3 Jan 2022 02:26:46 +0000 (13:26 +1100)
committerNick Downing <nick@ndcode.org>
Mon, 3 Jan 2022 02:30:12 +0000 (13:30 +1100)
binding.gyp
index.js
package.json
test.js
zettair.cpp

index 9dfa2f8..136e15b 100644 (file)
@@ -2,9 +2,18 @@
   "targets": [
     {
       "target_name": "zettair",
+      "cflags!": ["-fno-exceptions"],
+      "cflags_cc!": ["-fno-exceptions"],
       "sources": ["zettair.cpp"],
-      "include_dirs": ["/home/nick/include"],
-      "libraries": ["-L/home/nick/lib -lzet"]
+      "defines": ["NAPI_DISABLE_CPP_EXCEPTIONS"],
+      "include_dirs": [
+        "<!@(node -p \"require('node-addon-api').include\")",
+        "<!@(echo \"$HOME/include\")"
+      ],
+      "libraries": [
+        "<!@(echo \"-L$HOME/lib\")",
+        "-lzet"
+      ]
     }
   ]
 }
index 57b8a3f..4d4bfba 100644 (file)
--- a/index.js
+++ b/index.js
@@ -1,17 +1,17 @@
 /*
- * Copyright (C) 2018 Nick Downing <nick@ndcode.org>
+ * Copyright (C) 2022 Nick Downing <nick@ndcode.org>
  * SPDX-License-Identifier: MIT
- * 
+ *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
  * deal in the Software without restriction, including without limitation the
  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  * sell copies of the Software, and to permit persons to whom the Software is
  * furnished to do so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in
  * all copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -21,4 +21,5 @@
  * IN THE SOFTWARE.
  */
 
-module.exports = require('./build/Release/zettair.node');
+let bindings = require('bindings')
+module.exports = bindings('zettair')
index f2f65fe..e2687bf 100644 (file)
     "email": "nick@ndcode.org"
   },
   "main": "index.js",
-  "directories": {},
-  "dependencies": {
-    "node-gyp": "^3.6.2"
-  },
-  "devDependencies": {},
-  "scripts": {
-    "install": "node-gyp rebuild"
-  },
   "author": "Nick Downing <nick@ndcode.org>",
   "license": "MIT",
+  "dependencies": {
+    "bindings": "^1.5.0",
+    "node-addon-api": "^4.2.0"
+  },
   "gypfile": true
 }
diff --git a/test.js b/test.js
index ae19228..a7b458e 100755 (executable)
--- a/test.js
+++ b/test.js
@@ -23,7 +23,8 @@
  * IN THE SOFTWARE.
  */
 
-const zettair = require('./build/Release/zettair.node');
+let bindings = require('bindings')
+let zettair = bindings('zettair')
 
-const obj = new zettair.Index();
-console.log(obj.search('moby', 0, 10));
+let index = new zettair.Index('site')
+console.log(index.search('checkout', 0, 10))
index 38e8812..1c67960 100644 (file)
@@ -1,17 +1,17 @@
 /*
- * Copyright (C) 2018 Nick Downing <nick@ndcode.org>
+ * Copyright (C) 2022 Nick Downing <nick@ndcode.org>
  * SPDX-License-Identifier: MIT
- * 
+ *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
  * deal in the Software without restriction, including without limitation the
  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  * sell copies of the Software, and to permit persons to whom the Software is
  * furnished to do so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in
  * all copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -21,8 +21,7 @@
  * IN THE SOFTWARE.
  */
 
-#include <node.h>
-#include <node_object_wrap.h>
+#include <napi.h>
 #include <stdlib.h>
 #include <string.h>
 #include <zettair/def.h>
 
 namespace zettair {
 
-static v8::Local<v8::Object> index_result_to_object(
-  v8::Isolate* isolate,
+static Napi::Object result_to_object(
+  Napi::Env env,
   struct index_result* result
 );
-static v8::Local<v8::Object> index_results_to_object(
-  v8::Isolate* isolate,
+static Napi::Object results_to_object(
+  Napi::Env env,
   struct index_result* results,
-  unsigned int num_results,
+  unsigned int n_results,
   double/*unsigned long int*/ total_results
 );
 
-class IndexObject : public node::ObjectWrap {
+class Index : public Napi::ObjectWrap<Index> {
 public:
-  static void Init(v8::Local<v8::Object> exports);
+  Index(const Napi::CallbackInfo& info);
+  Napi::Value search(const Napi::CallbackInfo& info);
 
-private:
   struct index* idx;
-
-  explicit IndexObject(struct index* idx0);
-  ~IndexObject();
-
-  static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
-  static void Search(const v8::FunctionCallbackInfo<v8::Value>& args);
-  static v8::Persistent<v8::Function> constructor;
 };
 
-v8::Persistent<v8::Function> IndexObject::constructor;
+Index::Index(const Napi::CallbackInfo& info) : Napi::ObjectWrap<Index>(info) {
+  Napi::Env env = info.Env();
+  Napi::String prefix = info[0].As<Napi::String>();
 
-IndexObject::IndexObject(struct index* idx0) : idx(idx0) {
-}
+  int lopts = INDEX_LOAD_NOOPT;
+  struct index_load_opt lopt;
+  memset(&lopt, 0, sizeof(struct index_load_opt));
 
-IndexObject::~IndexObject() {
+  lopts |= INDEX_LOAD_IGNORE_VERSION;  /* quick hack */
+  idx = index_load(prefix.Utf8Value().c_str(), MEMORY_DEFAULT, lopts, &lopt);
+  if (idx == NULL)
+    Napi::Error::New(
+      env,
+      "Unable to load index"
+    ).ThrowAsJavaScriptException();
 }
 
-void IndexObject::Init(v8::Local<v8::Object> exports) {
-  v8::Isolate* isolate = exports->GetIsolate();
-
-  // Prepare constructor template
-  v8::Local<v8::FunctionTemplate> tpl =
-    v8::FunctionTemplate::New(isolate, New);
-  tpl->SetClassName(v8::String::NewFromUtf8(isolate, "Index"));
-  tpl->InstanceTemplate()->SetInternalFieldCount(1);
+Napi::Value Index::search(const Napi::CallbackInfo& info){
+  Napi::Env env = info.Env();
+  std::string query = info[0].As<Napi::String>().Utf8Value();
+  uint32_t startdoc = info[1].As<Napi::Number>().Uint32Value();
+  uint32_t len = info[2].As<Napi::Number>().Uint32Value();
+  std::string opt_type =
+    info[3] == env.Undefined() ?
+      std::string() :
+      info[3].As<Napi::String>().Utf8Value();
+  Napi::Array opt_args_tuple =
+    info[4] == env.Undefined() ?
+      Napi::Array::New(env) :
+      info[4].As<Napi::Array>();
+  uint32_t accumulator_limit =
+    info[5] == env.Undefined() ?
+      0 :
+      info[5].As<Napi::Number>().Uint32Value();
 
-  // Prototype
-  NODE_SET_PROTOTYPE_METHOD(tpl, "search", Search);
-
-  constructor.Reset(isolate, tpl->GetFunction());
-  exports->Set(
-    v8::String::NewFromUtf8(isolate, "Index"),
-    tpl->GetFunction()
+  struct index_result* results = (struct index_result*)malloc(
+    len * sizeof(struct index_result)
   );
-}
-
-void IndexObject::New(const v8::FunctionCallbackInfo<v8::Value>& args) {
-  v8::Isolate* isolate = args.GetIsolate();
-
-  if (args.IsConstructCall()) {
-    // Invoked as constructor: `new Index(...)`
-    v8::String::Utf8Value prefix(args[0]);
-    struct index* idx = NULL;;
-    int lopts = INDEX_LOAD_NOOPT;
-    struct index_load_opt lopt;
-
-    lopts |= INDEX_LOAD_IGNORE_VERSION;  /* quick hack */
-    if (
-      (
-        idx = index_load(
-          args[0]->IsString() ? *prefix : "index",
-          MEMORY_DEFAULT,
-          lopts,
-          &lopt
-        )
-      ) == NULL
-    ) {
-      isolate->ThrowException(
-        v8::String::NewFromUtf8(isolate, "Unable to load index")
-      );
-      return;
-    }
-    IndexObject* obj = new IndexObject(idx);
-    obj->Wrap(args.This());
-    args.GetReturnValue().Set(args.This());
-  } else {
-    // Invoked as plain function `Index(...)`, turn into construct call.
-    const int argc = 1;
-    v8::Local<v8::Value> argv[argc] = { args[0] };
-    v8::Local<v8::Context> context = isolate->GetCurrentContext();
-    v8::Local<v8::Function> cons =
-      v8::Local<v8::Function>::New(isolate, constructor);
-    v8::Local<v8::Object> result =
-      cons->NewInstance(context, argc, argv).ToLocalChecked();
-    args.GetReturnValue().Set(result);
+  if (results == NULL) {
+    Napi::Error::New(
+      env,
+      "Unable to allocate results"
+    ).ThrowAsJavaScriptException();
+    return env.Null();
   }
-}
-
-void IndexObject::Search(const v8::FunctionCallbackInfo<v8::Value>& args) {
-  v8::Isolate* isolate = args.GetIsolate();
 
-  v8::String::Utf8Value query(args[0]);
-  v8::String::Utf8Value optType(args[3]);
-  v8::Local<v8::Array> optArgsTuple;
-  if (args[4]->IsArray())
-    optArgsTuple = args[4].As<v8::Array>();
-  unsigned long startdoc = args[1]->IntegerValue();
-  unsigned long len = args[2]->IntegerValue();
-  IndexObject* Index = node::ObjectWrap::Unwrap<IndexObject>(args.Holder());
-  struct index_result* result;
-  unsigned int results;
+  unsigned int n_results;
   double/*unsigned long int*/ total_results;
   int est;
-  unsigned int accumulator_limit = args[5]->Uint32Value();
   int opts = INDEX_SEARCH_NOOPT;
   struct index_search_opt opt;
+  memset(&opt, 0, sizeof(struct index_search_opt));
 
   opt.u.okapi_k3.k1 = 1.2;
   opt.u.okapi_k3.k3 = 1e10;
   opt.u.okapi_k3.b = 0.75;
 
-  if (
-    (result = (struct index_result*)malloc(sizeof(*result) * len)) == NULL
-  ) {
-    isolate->ThrowException(
-      v8::String::NewFromUtf8(isolate, "Unable to allocate results")
-    );
-    return;
-  }
-  if (args[3]->IsString()) {
-    if (strcmp(*optType, "COSINE") == 0) {
+  if (info[3] != env.Undefined()) {
+    if (opt_type == "COSINE")
       opts = INDEX_SEARCH_COSINE_RANK;
-    } else if (strcmp(*optType, "OKAPI") == 0) {
+    else if (opt_type == "OKAPI")
       opts = INDEX_SEARCH_OKAPI_RANK;
-    } else if (strcmp(*optType, "OKAPI_K3") == 0) {
-      if (optArgsTuple.IsEmpty()) {
-        isolate->ThrowException(
-          v8::String::NewFromUtf8(isolate, "Must supply args to search type")
-        );
-        free(result);
-        return;
+    else if (opt_type == "OKAPI_K3") {
+      if (opt_args_tuple.Length() < 3U) {
+        Napi::Error::New(
+          env,
+          "Must supply args to search type"
+        ).ThrowAsJavaScriptException();
+        free(results);
+        return env.Null();
       }
       opts = INDEX_SEARCH_OKAPI_RANK;
-      opt.u.okapi_k3.k1 = optArgsTuple->Get(0)->IntegerValue();
-      opt.u.okapi_k3.k3 = optArgsTuple->Get(1)->IntegerValue();
-      opt.u.okapi_k3.b = optArgsTuple->Get(2)->IntegerValue();
-    } else if (strcmp(*optType, "HAWKAPI") == 0) {
-      if (optArgsTuple.IsEmpty()) {
-        isolate->ThrowException(
-          v8::String::NewFromUtf8(isolate, "Must supply args to search type")
-        );
-        free(result);
-        return;
+      opt.u.okapi_k3.k1 = opt_args_tuple.Get(0U).As<Napi::Number>().FloatValue();
+      opt.u.okapi_k3.k3 = opt_args_tuple.Get(1U).As<Napi::Number>().FloatValue();
+      opt.u.okapi_k3.b = opt_args_tuple.Get(2U).As<Napi::Number>().FloatValue();
+    }
+    else if (opt_type == "HAWKAPI") {
+      if (opt_args_tuple.Length() < 2U) {
+        Napi::Error::New(
+          env,
+          "Must supply args to search type"
+        ).ThrowAsJavaScriptException();
+        free(results);
+        return env.Null();
       }
       opts = INDEX_SEARCH_HAWKAPI_RANK;
-      opt.u.hawkapi.alpha = optArgsTuple->Get(0)->IntegerValue();
-      opt.u.hawkapi.k3 = optArgsTuple->Get(1)->IntegerValue();
-    } else if (strcmp(*optType, "DIRICHLET") == 0) {
+      opt.u.hawkapi.alpha = opt_args_tuple.Get(0U).As<Napi::Number>().FloatValue();
+      opt.u.hawkapi.k3 = opt_args_tuple.Get(1U).As<Napi::Number>().FloatValue();
+    }
+    else if (opt_type == "DIRICHLET") {
       opts = INDEX_SEARCH_DIRICHLET_RANK;
-      if (optArgsTuple.IsEmpty() || optArgsTuple->Length() == 0)
-        opt.u.dirichlet.mu = 2500.0;
-      else
-        opt.u.dirichlet.mu = optArgsTuple->Get(0)->NumberValue();
-    } else {
-      isolate->ThrowException(
-        v8::String::NewFromUtf8(isolate, "Unknown search type")
-      );
-      free(result);
-      return;
+      opt.u.dirichlet.mu =
+        opt_args_tuple.Length() < 1U ?
+          2500.0 :
+          opt_args_tuple.Get(0U).As<Napi::Number>().FloatValue();
+    }
+    else {
+      Napi::Error::New(
+        env,
+        "Unknown search type"
+      ).ThrowAsJavaScriptException();
+      free(results);
+      return env.Null();
     }
   }
   if (accumulator_limit != 0) {
@@ -206,79 +161,106 @@ void IndexObject::Search(const v8::FunctionCallbackInfo<v8::Value>& args) {
  opts |= INDEX_SEARCH_SUMMARY_TYPE;
  opt.summary_type = INDEX_SUMMARISE_TAG;
 #endif
-  if (!index_search(Index->idx, *query, startdoc, len,
-      result, &results, &total_results, &est, opts, &opt)) {
+  if (
+    !index_search(
+      idx,
+      query.c_str(),
+      startdoc,
+      len,
+      results,
+      &n_results,
+      &total_results,
+      &est,
+      opts,
+      &opt
+    )
+  ) {
     char err_buf[1024];
-    snprintf(err_buf, 1024, "Unable to perform search for query '%s'; "
-       "system error is '%s'\n", *query, strerror(errno));
-    isolate->ThrowException(
-      v8::String::NewFromUtf8(isolate, err_buf)
+    snprintf(
+      err_buf,
+      sizeof(err_buf),
+      "Unable to perform search for query '%s'; system error is '%s'\n",
+      query.c_str(),
+      strerror(errno)
     );
-    free(result);
-    return;
+    Napi::Error::New(env, err_buf).ThrowAsJavaScriptException();
+    free(results);
+    return env.Null();
   }
-  args.GetReturnValue().Set(
-    index_results_to_object(isolate, result, results, total_results)
-  );
-  free(result);
-  return;
+  Napi::Object object =
+    results_to_object(env, results, n_results, total_results);
+  free(results);
+  return object;
 }
 
-static v8::Local<v8::Object> index_result_to_object(
-  v8::Isolate* isolate,
+static Napi::Object result_to_object(
+  Napi::Env env,
   struct index_result* result
 ) {
-  v8::EscapableHandleScope handle_scope(isolate);
-  v8::Local<v8::Object> result_object = v8::Object::New(isolate);
-  result_object->Set(
-    v8::String::NewFromUtf8(isolate, "docno"),
-    v8::Integer::New(isolate, result->docno)
+  Napi::Object object = Napi::Object::New(env);
+  object.Set(
+    Napi::String::New(env, "docno"),
+    Napi::Number::New(env, result->docno)
   );
-  result_object->Set(
-    v8::String::NewFromUtf8(isolate, "score"),
-    v8::Number::New(isolate, result->score)
+  object.Set(
+    Napi::String::New(env, "score"),
+    Napi::Number::New(env, result->score)
   );
-  result_object->Set(
-    v8::String::NewFromUtf8(isolate, "summary"),
-    v8::String::NewFromUtf8(isolate, result->summary)
+  object.Set(
+    Napi::String::New(env, "summary"),
+    Napi::String::New(env, result->summary)
   );
-  result_object->Set(
-    v8::String::NewFromUtf8(isolate, "title"),
-    v8::String::NewFromUtf8(isolate, result->title)
+  object.Set(
+    Napi::String::New(env, "title"),
+    Napi::String::New(env, result->title)
   );
-  result_object->Set(
-    v8::String::NewFromUtf8(isolate, "auxiliary"),
-    v8::String::NewFromUtf8(isolate, result->auxilliary)
+  object.Set(
+    Napi::String::New(env, "auxiliary"),
+    Napi::String::New(env, result->auxilliary)
   );
-  return handle_scope.Escape(result_object);
+  return object;
 }
 
-static v8::Local<v8::Object> index_results_to_object(
-  v8::Isolate* isolate,
+static Napi::Object results_to_object(
+  Napi::Env env,
   struct index_result* results,
-  unsigned int num_results,
+  unsigned int n_results,
   double/*unsigned long int*/ total_results
 ) {
-  v8::EscapableHandleScope handle_scope(isolate);
-  v8::Local<v8::Object> results_object = v8::Object::New(isolate);
-  v8::Local<v8::Array> results_array = v8::Array::New(isolate, num_results);
-  for (unsigned int i = 0; i < num_results; ++i)
-    results_array->Set(i, index_result_to_object(isolate, results + i));
-  results_object->Set(
-    v8::String::NewFromUtf8(isolate, "results"),
-    results_array
+  Napi::Object object = Napi::Object::New(env);
+  Napi::Array array = Napi::Array::New(env, n_results);
+  for (unsigned int i = 0; i < n_results; ++i)
+    array.Set(i, result_to_object(env, results + i));
+  object.Set(
+    Napi::String::New(env, "results"),
+    array
   );
-  results_object->Set(
-    v8::String::NewFromUtf8(isolate, "total_results"),
-    v8::Number::New(isolate, total_results)
+  object.Set(
+    Napi::String::New(env, "total_results"),
+    Napi::Number::New(env, total_results)
   );
-  return handle_scope.Escape(results_object);
+  return object;
 }
 
-void InitAll(v8::Local<v8::Object> exports) {
-  IndexObject::Init(exports);
+Napi::Object Init(Napi::Env env, Napi::Object exports) {
+  exports.Set(
+    "Index",
+    Napi::ObjectWrap<Index>::DefineClass(
+      env,
+      "Index",
+      {
+        Napi::ObjectWrap<Index>::InstanceMethod<&Index::search>(
+          "search",
+          static_cast<napi_property_attributes>(
+            napi_writable | napi_configurable
+          )
+        )
+      }
+    )
+  );
+  return exports;
 }
 
-NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)
+NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)
 
 }