345 wiersze
8.2 KiB
C++
345 wiersze
8.2 KiB
C++
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include "v8.h"
|
|
#include "node.h"
|
|
#include "node_buffer.h"
|
|
#include <nan.h>
|
|
|
|
#ifdef WIN32
|
|
#include <process.h>
|
|
#else
|
|
#include <pthread.h>
|
|
#endif // WIN32
|
|
|
|
using namespace v8;
|
|
using namespace node;
|
|
|
|
/*
|
|
* Exported function with C naming and calling conventions.
|
|
* Used by dynamic_library.js to test symbol lookup.
|
|
* Never actually called.
|
|
*/
|
|
|
|
extern "C"
|
|
int
|
|
NODE_MODULE_EXPORT
|
|
ExportedFunction(int value)
|
|
{
|
|
return value * 2;
|
|
}
|
|
|
|
namespace {
|
|
|
|
/*
|
|
* Test struct definition used in the test harness functions below.
|
|
*/
|
|
|
|
typedef struct box {
|
|
int width;
|
|
int height;
|
|
} _box;
|
|
|
|
/*
|
|
* Accepts a struct by value, and returns a struct by value.
|
|
*/
|
|
|
|
box double_box(box input) {
|
|
box rtn;
|
|
// modify the input box, ensure on the JS side that it's not altered
|
|
input.width *= 2;
|
|
input.height *= 2;
|
|
rtn.width = input.width;
|
|
rtn.height = input.height;
|
|
return rtn;
|
|
}
|
|
|
|
/*
|
|
* Accepts a box struct pointer, and returns a struct by value.
|
|
*/
|
|
|
|
box double_box_ptr(box *input) {
|
|
box rtn;
|
|
// modify the input box, ensure on the JS side that IT IS altered
|
|
input->width *= 2;
|
|
input->height *= 2;
|
|
rtn.width = input->width;
|
|
rtn.height = input->height;
|
|
return rtn;
|
|
}
|
|
|
|
/*
|
|
* Accepts a struct by value, and returns an int.
|
|
*/
|
|
|
|
int area_box(box input) {
|
|
return input.width * input.height;
|
|
}
|
|
|
|
/*
|
|
* Accepts a box pointer and returns an int.
|
|
*/
|
|
|
|
int area_box_ptr(box *input) {
|
|
return input->width * input->height;
|
|
}
|
|
|
|
/*
|
|
* Creates a box and returns it by value.
|
|
*/
|
|
|
|
box create_box(int width, int height) {
|
|
box rtn = { width, height };
|
|
return rtn;
|
|
}
|
|
|
|
/*
|
|
* Creates a box that has the sum of the width and height for its own values.
|
|
*/
|
|
|
|
box add_boxes(box boxes[], int num) {
|
|
box rtn = { 0, 0 };
|
|
box cur;
|
|
for (int i = 0; i < num; i++) {
|
|
cur = boxes[i];
|
|
rtn.width += cur.width;
|
|
rtn.height += cur.height;
|
|
}
|
|
return rtn;
|
|
}
|
|
|
|
/*
|
|
* Reads "ints" from the "input" array until -1 is found.
|
|
* Returns the number of elements in the array.
|
|
*/
|
|
|
|
int *int_array(int *input) {
|
|
int *array = input;
|
|
while (*array != -1){
|
|
*array = *array * 2;
|
|
array++;
|
|
}
|
|
return input;
|
|
}
|
|
|
|
/*
|
|
* Tests for passing a Struct that contains Arrays inside of it.
|
|
*/
|
|
|
|
struct arst {
|
|
int num;
|
|
double array[20];
|
|
};
|
|
|
|
struct arst array_in_struct (struct arst input) {
|
|
struct arst rtn;
|
|
rtn.num = input.num * 2;
|
|
for (int i = 0; i < 20; i++) {
|
|
rtn.array[i] = input.array[i] * 3.14;
|
|
}
|
|
return rtn;
|
|
}
|
|
|
|
/*
|
|
* Tests for C function pointers.
|
|
*/
|
|
|
|
typedef int (*my_callback)(int);
|
|
|
|
my_callback callback_func (my_callback cb) {
|
|
return cb;
|
|
}
|
|
|
|
/*
|
|
* Hard-coded `strtoul` binding, for the benchmarks.
|
|
*
|
|
* args[0] - the string number to convert to a real Number
|
|
* args[1] - a "buffer" instance to write into (the "endptr")
|
|
* args[2] - the base (0 means autodetect)
|
|
*/
|
|
|
|
NAN_METHOD(Strtoul) {
|
|
Nan::HandleScope();
|
|
int base;
|
|
char **endptr;
|
|
|
|
Nan::Utf8String buf(info[0]);
|
|
|
|
Local<Value> endptr_arg = info[1];
|
|
endptr = (char **)Buffer::Data(endptr_arg.As<Object>());
|
|
|
|
base = info[2]->Int32Value();
|
|
|
|
unsigned long val = strtoul(*buf, endptr, base);
|
|
|
|
info.GetReturnValue().Set(Nan::New<Integer>((uint32_t)val));
|
|
}
|
|
|
|
|
|
// experiments for #72
|
|
typedef void (*cb)(void);
|
|
|
|
static cb callback = NULL;
|
|
|
|
NAN_METHOD(SetCb) {
|
|
Nan::HandleScope();
|
|
char *buf = Buffer::Data(info[0].As<Object>());
|
|
callback = (cb)buf;
|
|
info.GetReturnValue().SetUndefined();
|
|
}
|
|
|
|
NAN_METHOD(CallCb) {
|
|
Nan::HandleScope();
|
|
if (callback == NULL) {
|
|
return Nan::ThrowError("you must call \"set_cb()\" first");
|
|
} else {
|
|
callback();
|
|
}
|
|
info.GetReturnValue().SetUndefined();
|
|
}
|
|
|
|
// Invoke callback from a native (non libuv) thread:
|
|
#ifdef WIN32
|
|
void invoke_callback(void* args) {
|
|
#else
|
|
void* invoke_callback(void* args) {
|
|
#endif // WIN32
|
|
cb c = callback;
|
|
if (c != NULL) {
|
|
c();
|
|
}
|
|
#ifndef WIN32
|
|
return NULL;
|
|
#endif // WIN32
|
|
}
|
|
|
|
NAN_METHOD(CallCbFromThread) {
|
|
Nan::HandleScope();
|
|
if (callback == NULL) {
|
|
return Nan::ThrowError("you must call \"set_cb()\" first");
|
|
}
|
|
else {
|
|
#ifdef WIN32
|
|
_beginthread(&invoke_callback, 0, NULL);
|
|
#else
|
|
pthread_t thread;
|
|
pthread_create(&thread, NULL, &invoke_callback, NULL);
|
|
#endif // WIN32
|
|
}
|
|
info.GetReturnValue().SetUndefined();
|
|
}
|
|
|
|
void AsyncCbCall(uv_work_t *req) {
|
|
cb c = (cb)req->data;
|
|
c();
|
|
}
|
|
|
|
void FinishAsyncCbCall(uv_work_t *req) {
|
|
// nothing
|
|
delete req;
|
|
}
|
|
|
|
NAN_METHOD(CallCbAsync) {
|
|
Nan::HandleScope();
|
|
if (callback == NULL) {
|
|
return Nan::ThrowError("you must call \"set_cb()\" first");
|
|
} else {
|
|
uv_work_t *req = new uv_work_t;
|
|
req->data = (void *)callback;
|
|
uv_queue_work(uv_default_loop(), req, AsyncCbCall, (uv_after_work_cb)FinishAsyncCbCall);
|
|
}
|
|
info.GetReturnValue().SetUndefined();
|
|
}
|
|
|
|
|
|
// Race condition in threaded callback invocation testing
|
|
// https://github.com/node-ffi/node-ffi/issues/153
|
|
void play_ping_pong (const char* (*callback) (const char*)) {
|
|
const char * response;
|
|
do {
|
|
response = callback("ping");
|
|
} while (strcmp(response, "pong") == 0);
|
|
}
|
|
|
|
|
|
// https://github.com/node-ffi/node-ffi/issues/169
|
|
int test_169(char* dst, int len) {
|
|
const char src[] = "sample str\0";
|
|
strncpy(dst, src, len);
|
|
return fmin(len, strlen(src));
|
|
}
|
|
|
|
|
|
// https://github.com/TooTallNate/ref/issues/56
|
|
struct Obj56 {
|
|
bool traceMode;
|
|
};
|
|
int test_ref_56(struct Obj56 *obj) {
|
|
return obj->traceMode ? 1 : 0;
|
|
}
|
|
|
|
|
|
void wrap_pointer_cb(char *data, void *hint) {
|
|
}
|
|
|
|
inline Local<Value> WrapPointer(char *ptr, size_t length) {
|
|
Nan::EscapableHandleScope scope;
|
|
return scope.Escape(Nan::NewBuffer(ptr, length, wrap_pointer_cb, NULL).ToLocalChecked());
|
|
}
|
|
|
|
inline Local<Value> WrapPointer(char *ptr) {
|
|
return WrapPointer(ptr, 0);
|
|
}
|
|
|
|
void Initialize(Handle<Object> target) {
|
|
Nan::HandleScope();
|
|
|
|
#if WIN32
|
|
// initialize "floating point support" on Windows?!?!
|
|
// (this is some serious bullshit...)
|
|
// http://support.microsoft.com/kb/37507
|
|
float x = 2.3f;
|
|
#endif
|
|
|
|
// atoi and abs here for testing purposes
|
|
target->Set(Nan::New<String>("atoi").ToLocalChecked(), WrapPointer((char *)atoi));
|
|
|
|
// Windows has multiple `abs` signatures, so we need to manually disambiguate
|
|
int (*absPtr)(int)(abs);
|
|
target->Set(Nan::New<String>("abs").ToLocalChecked(), WrapPointer((char *)absPtr));
|
|
|
|
// sprintf pointer; used in the varadic tests
|
|
target->Set(Nan::New<String>("sprintf").ToLocalChecked(), WrapPointer((char *)sprintf));
|
|
|
|
// hard-coded `strtoul` binding, for the benchmarks
|
|
Nan::Set(target, Nan::New<String>("strtoul").ToLocalChecked(),
|
|
Nan::New<FunctionTemplate>(Strtoul)->GetFunction());
|
|
|
|
Nan::Set(target, Nan::New<String>("set_cb").ToLocalChecked(),
|
|
Nan::New<FunctionTemplate>(SetCb)->GetFunction());
|
|
Nan::Set(target, Nan::New<String>("call_cb").ToLocalChecked(),
|
|
Nan::New<FunctionTemplate>(CallCb)->GetFunction());
|
|
Nan::Set(target, Nan::New<String>("call_cb_from_thread").ToLocalChecked(),
|
|
Nan::New<FunctionTemplate>(CallCbFromThread)->GetFunction());
|
|
Nan::Set(target, Nan::New<String>("call_cb_async").ToLocalChecked(),
|
|
Nan::New<FunctionTemplate>(CallCbAsync)->GetFunction());
|
|
|
|
// also need to test these custom functions
|
|
target->Set(Nan::New<String>("double_box").ToLocalChecked(), WrapPointer((char *)double_box));
|
|
target->Set(Nan::New<String>("double_box_ptr").ToLocalChecked(), WrapPointer((char *)double_box_ptr));
|
|
target->Set(Nan::New<String>("area_box").ToLocalChecked(), WrapPointer((char *)area_box));
|
|
target->Set(Nan::New<String>("area_box_ptr").ToLocalChecked(), WrapPointer((char *)area_box_ptr));
|
|
target->Set(Nan::New<String>("create_box").ToLocalChecked(), WrapPointer((char *)create_box));
|
|
target->Set(Nan::New<String>("add_boxes").ToLocalChecked(), WrapPointer((char *)add_boxes));
|
|
target->Set(Nan::New<String>("int_array").ToLocalChecked(), WrapPointer((char *)int_array));
|
|
target->Set(Nan::New<String>("array_in_struct").ToLocalChecked(), WrapPointer((char *)array_in_struct));
|
|
target->Set(Nan::New<String>("callback_func").ToLocalChecked(), WrapPointer((char *)callback_func));
|
|
target->Set(Nan::New<String>("play_ping_pong").ToLocalChecked(), WrapPointer((char *)play_ping_pong));
|
|
target->Set(Nan::New<String>("test_169").ToLocalChecked(), WrapPointer((char *)test_169));
|
|
target->Set(Nan::New<String>("test_ref_56").ToLocalChecked(), WrapPointer((char *)test_ref_56));
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
NODE_MODULE(ffi_tests, Initialize);
|