Skip to content

Commit

Permalink
implement c++ passthrough using n-api
Browse files Browse the repository at this point in the history
  • Loading branch information
Fishrock123 committed Feb 16, 2018
1 parent 33e8c22 commit b4fafa6
Show file tree
Hide file tree
Showing 6 changed files with 364 additions and 1 deletion.
10 changes: 10 additions & 0 deletions addon/addon.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <node_api.h>
#include "js-passthrough.h"

napi_value Init(napi_env env, napi_value exports) {
exports = PassThrough::CreateClass(env);

return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
8 changes: 8 additions & 0 deletions addon/binding.gyp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"targets": [
{
"target_name": "addon",
"sources": [ "addon.cc", "js-passthrough.cc" ]
}
]
}
271 changes: 271 additions & 0 deletions addon/js-passthrough.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
#include <node_api.h>
#include <assert.h>
#include <stdio.h>
#include "js-passthrough.h"
#include "utils-inl.h"

napi_ref PassThrough::constructor;

PassThrough::PassThrough()
: source_(nullptr), sink_(nullptr), env_(nullptr), wrapper_(nullptr) {}

PassThrough::~PassThrough() { napi_delete_reference(env_, wrapper_); }

void PassThrough::Destructor(napi_env env, void* nativeObject, void* /*finalize_hint*/) {
reinterpret_cast<PassThrough*>(nativeObject)->~PassThrough();
}

#define DECLARE_NAPI_METHOD(name, func) \
{ name, 0, func, 0, 0, 0, napi_default, 0 }

napi_value PassThrough::CreateClass(napi_env env) {
napi_status status;
napi_property_descriptor properties[] = {
// { "value", 0, 0, GetValue, SetValue, 0, napi_default, 0 },
DECLARE_NAPI_METHOD("bindSource", BindSource),
DECLARE_NAPI_METHOD("bindSink", BindSink),
DECLARE_NAPI_METHOD("next", Next),
DECLARE_NAPI_METHOD("pull", Pull),
};

napi_value cons;
status =
napi_define_class(env, "PassThrough", NAPI_AUTO_LENGTH, New, nullptr, 4, properties, &cons);
if (status != napi_ok) return nullptr;

status = napi_create_reference(env, cons, 1, &constructor);
if (status != napi_ok) return nullptr;

assert(status == napi_ok);
return cons;
}

napi_value PassThrough::New(napi_env env, napi_callback_info info) {
napi_status status;

napi_value target;
status = napi_get_new_target(env, info, &target);
assert(status == napi_ok);
bool is_constructor = target != nullptr;

if (is_constructor) {
// Invoked as constructor: `new PassThrough(...)`
napi_value jsthis;
status = napi_get_cb_info(env, info, nullptr, nullptr, &jsthis, nullptr);
assert(status == napi_ok);
//
// double value = 0;
//
// napi_valuetype valuetype;
// status = napi_typeof(env, args[0], &valuetype);
// assert(status == napi_ok);
//
// if (valuetype != napi_undefined) {
// status = napi_get_value_double(env, args[0], &value);
// assert(status == napi_ok);
// }

PassThrough* obj = new PassThrough();

obj->env_ = env;
status = napi_wrap(env,
jsthis,
reinterpret_cast<void*>(obj),
PassThrough::Destructor,
nullptr, // finalize_hint
&obj->wrapper_);
assert(status == napi_ok);

return jsthis;
} else {
// Invoked as plain function `PassThrough(...)`, turn into construct call.
size_t argc_ = 1;
napi_value args[1];
status = napi_get_cb_info(env, info, &argc_, args, nullptr, nullptr);
assert(status == napi_ok);

const size_t argc = 1;
napi_value argv[argc] = {args[0]};

napi_value cons;
status = napi_get_reference_value(env, constructor, &cons);
assert(status == napi_ok);

napi_value instance;
status = napi_new_instance(env, cons, argc, argv, &instance);
assert(status == napi_ok);

return instance;
}
}

napi_value PassThrough::BindSource(napi_env env, napi_callback_info info) {
napi_status status;

size_t argc = 1;
napi_value argv[argc];
napi_value jsthis;
status = napi_get_cb_info(env, info, &argc, argv, &jsthis, nullptr);
assert(status == napi_ok);

napi_value source = argv[0];

napi_value global;
status = napi_get_global(env, &global);
assert(status == napi_ok);
PRINT_NAPI_TYPE(global, "0");

napi_value console;
status = napi_get_named_property(env,
global,
"console",
&console);
assert(status == napi_ok);
PRINT_NAPI_TYPE(console, "1");

napi_value log_;
status = napi_get_named_property(env,
console,
"log",
&log_);
assert(status == napi_ok);
PRINT_NAPI_TYPE(log_, "2");

napi_value console_args[1] = { source };
status = napi_call_function(env,
console,
log_,
1,
console_args,
nullptr);
assert(status == napi_ok);


napi_value source_bindsink;
status = napi_get_named_property(env,
source,
"bindSink",
&source_bindsink);
assert(status == napi_ok);
PRINT_NAPI_TYPE(source_bindsink, "4");

napi_value argv_[1] = { jsthis };
status = napi_call_function(env,
source,
source_bindsink,
1,
argv_,
nullptr);
PRINT_NAPI_ERROR_MESSAGE(status, "source.bindSink()");
assert(status == napi_ok);

PassThrough* obj;
status = napi_unwrap(env, jsthis, reinterpret_cast<void**>(&obj));
assert(status == napi_ok);

napi_ref source_ref;
status = napi_create_reference(env, source, 1, &source_ref);
assert(status == napi_ok);

obj->source_ = source_ref;

return jsthis;
}

napi_value PassThrough::BindSink(napi_env env, napi_callback_info info) {
napi_status status;

size_t argc = 1;
napi_value argv[argc];
napi_value jsthis;
status = napi_get_cb_info(env, info, &argc, argv, &jsthis, nullptr);
assert(status == napi_ok);

PassThrough* obj;
status = napi_unwrap(env, jsthis, reinterpret_cast<void**>(&obj));
assert(status == napi_ok);

napi_ref sink_ref;
status = napi_create_reference(env, argv[0], 1, &sink_ref);
assert(status == napi_ok);

obj->sink_ = sink_ref;

return nullptr;
}

napi_value PassThrough::Next(napi_env env, napi_callback_info info) {
napi_status status;

size_t argc = 4;
napi_value argv[argc];
napi_value jsthis;
status = napi_get_cb_info(env, info, &argc, argv, &jsthis, nullptr);
assert(status == napi_ok);

PassThrough* obj;
status = napi_unwrap(env, jsthis, reinterpret_cast<void**>(&obj));
assert(status == napi_ok);

napi_value sink;
status = napi_get_reference_value(env, obj->sink_, &sink);
assert(status == napi_ok);

napi_value sink_next;
status = napi_get_named_property(env,
sink,
"next",
&sink_next);
assert(status == napi_ok);

status = napi_call_function(env,
sink,
sink_next,
4,
argv,
nullptr);
assert(status == napi_ok);

return nullptr;
}

napi_value PassThrough::Pull(napi_env env, napi_callback_info info) {
napi_status status;

size_t argc = 2;
napi_value argv[argc];
napi_value jsthis;
status = napi_get_cb_info(env, info, &argc, argv, &jsthis, nullptr);
assert(status == napi_ok);

PassThrough* obj;
status = napi_unwrap(env, jsthis, reinterpret_cast<void**>(&obj));
assert(status == napi_ok);

napi_value source;
status = napi_get_reference_value(env, obj->source_, &source);
assert(status == napi_ok);

PRINT_NAPI_TYPE(source, "5");

napi_value source_pull;
status = napi_get_named_property(env,
source,
"pull",
&source_pull);
assert(status == napi_ok);

PRINT_NAPI_TYPE(source_pull, "5");

status = napi_call_function(env,
source,
source_pull,
2,
argv,
nullptr);
PRINT_NAPI_ERROR_MESSAGE(status, "10");
PRINT_NAPI_STATUS(status, "20");
// assert(status == napi_ok);

return nullptr;
}
29 changes: 29 additions & 0 deletions addon/js-passthrough.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#ifndef JS_PASSTHROUGH_H_
#define JS_PASSTHROUGH_H_

#include <node_api.h>

class PassThrough {
public:
PassThrough();
~PassThrough();

static napi_value CreateClass(napi_env env);

static void Destructor(napi_env env, void* nativeObject, void* finalize_hint);

private:
static napi_value New(napi_env env, napi_callback_info info);
static napi_value BindSource(napi_env env, napi_callback_info info);
static napi_value BindSink(napi_env env, napi_callback_info info);
static napi_value Next(napi_env env, napi_callback_info info);
static napi_value Pull(napi_env env, napi_callback_info info);

static napi_ref constructor;
napi_ref source_;
napi_ref sink_;
napi_env env_;
napi_ref wrapper_;
};

#endif // JS_PASSTHROUGH_H_
45 changes: 45 additions & 0 deletions addon/utils-inl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include <node_api.h>

#define PRINT_NAPI_TYPE(value, str) \
{ \
napi_valuetype type; \
napi_status status = napi_typeof(env, value, &type); \
assert(status == napi_ok); \
\
if (type == napi_undefined) printf("%s - undefined\n", str); \
if (type == napi_null) printf("%s - null\n", str); \
if (type == napi_boolean) printf("%s - boolean\n", str); \
if (type == napi_number) printf("%s - number\n", str); \
if (type == napi_string) printf("%s - string\n", str); \
if (type == napi_symbol) printf("%s - symbol\n", str); \
if (type == napi_object) printf("%s - Object\n", str); \
if (type == napi_function) printf("%s - Function\n", str); \
if (type == napi_external) printf("%s - External\n", str); \
}

#define PRINT_NAPI_STATUS(status, str) \
{ \
if (status == napi_ok) printf("%s - ok\n", str); \
if (status == napi_invalid_arg) printf("%s - invalid_arg\n", str); \
if (status == napi_object_expected) printf("%s - object_expected\n", str); \
if (status == napi_string_expected) printf("%s - string_expected\n", str); \
if (status == napi_name_expected) printf("%s - name_expected\n", str); \
if (status == napi_function_expected) printf("%s - function_expecte\n", str); \
if (status == napi_number_expected) printf("%s - number_expected\n", str); \
if (status == napi_boolean_expected) printf("%s - boolean_expected\n", str); \
if (status == napi_array_expected) printf("%s - array_expected\n", str); \
if (status == napi_generic_failure) printf("%s - generic_failure\n", str); \
if (status == napi_pending_exception) printf("%s - pending_exception\n", str); \
if (status == napi_cancelled) printf("%s - cancelled\n", str); \
}
// if (status == napi_status_last) printf("%s - status_last\n", str); \ // ????

#define PRINT_NAPI_ERROR_MESSAGE(status, str) \
{ \
if (status != napi_ok) { \
const napi_extended_error_info* result; \
\
napi_get_last_error_info(env, &result); \
printf("%s - %s\n", str, result->error_message); \
} \
}
2 changes: 1 addition & 1 deletion tests/file-to-file-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

const FileSource = require('../fs/file-source')
const FileSink = require('../fs/file-sink')
const PassThrough = require('../data-passthrough')
const PassThrough = require('../addon/build/Release/addon')

const fileSource = new FileSource(process.argv[2])
const fileSink = new FileSink(process.argv[2] + '_')
Expand Down

0 comments on commit b4fafa6

Please sign in to comment.