diff --git a/binding.gyp b/binding.gyp index eb85075..017cb51 100644 --- a/binding.gyp +++ b/binding.gyp @@ -30,10 +30,11 @@ }, "targets": [ { - "target_name": "node-cpython-2X", + "target_name": "node-cpython", "sources": [ "src/addon.cc", "src/node-cpython-2X.cc", + "src/node-cpython-3X.cc", "src/ffi.cc" ], "include_dirs": [ diff --git a/lib/bindings.js b/lib/bindings.js index 568d66e..2524a74 100644 --- a/lib/bindings.js +++ b/lib/bindings.js @@ -1,6 +1,7 @@ -var addon = require('bindings')('node-cpython-2X') +var addon = require('bindings')('node-cpython') module.exports = { NodeCPython2X: addon.NodeCPython2X, + NodeCPython3X: addon.NodeCPython3X, FFI: new addon.FFI() } diff --git a/lib/node-cpython.js b/lib/node-cpython.js index e3e7f08..74ea95d 100644 --- a/lib/node-cpython.js +++ b/lib/node-cpython.js @@ -3,7 +3,10 @@ const EventEmitter = require('events') const util = require('util') const glob = require('glob') const NCPY2 = require('./bindings.js').NodeCPython2X +const NCPY3 = require('./bindings.js').NodeCPython3X var NCPy2 = new NCPY2() +var NCPy3 = new NCPY2() +var NCPy = NCPy2 // use 2.7 as default, overwrite in constructor const FFI = require('./ffi.js') const path = require('path') @@ -25,6 +28,10 @@ function Ncpy (options) { } } } + // override cpp dep if so desired + if (this.opts.version === '3.x') { + NCPy = NCPy3 + } this.programs = [] } @@ -56,9 +63,9 @@ Ncpy.prototype.init = function (options) { */ Ncpy.prototype.repl = function () { // http://stackoverflow.com/a/9541411/3580261 for below - NCPy2.initialize() - NCPy2.addModule() - NCPy2.getDict() + NCPy.initialize() + NCPy.addModule() + NCPy.getDict() process.stdin.setEncoding('utf8') @@ -67,19 +74,19 @@ Ncpy.prototype.repl = function () { process.stdout.write('> ') if (chunk !== null) { // TODO: implement pass-down to interactiveLoop - NCPy2.runString(chunk) + NCPy.runString(chunk) } }) process.stdin.on('end', function () { process.stdout.write('-------\nPyREPL connection end.\n') - NCPy2.finalize() + NCPy.finalize() }) process.on('SIGINT', function () { process.stdout.write('\n-------\nPyREPL closed.\n') - NCPy2.finalize() + NCPy.finalize() process.exit(0) }) } @@ -170,7 +177,7 @@ Ncpy.prototype.runSync = function (glob, argv, cb) { Ncpy.prototype.runString = function () { let args = Array.prototype.slice.call(arguments) // http://stackoverflow.com/a/9541411/3580261 for above repl implementation - return NCPy2.runString(args[0]) + return NCPy.runString(args[0]) } /** @@ -201,9 +208,9 @@ Ncpy.prototype.simpleString = function (str, cb) { return cb(err) } - NCPy2.initialize() - NCPy2.simpleString(args[0]) - NCPy2.finalize() + NCPy.initialize() + NCPy.simpleString(args[0]) + NCPy.finalize() cb = (typeof cb === 'function') ? cb : function () {} @@ -267,7 +274,7 @@ Ncpy.prototype.ffi = function (file, functionname, argList, cb) { return } - NCPy2.setPath(path.join(process.cwd(), path.dirname(file))) + NCPy.setPath(path.join(process.cwd(), path.dirname(file))) cb = (typeof cb === 'function') ? cb : function () {} @@ -291,11 +298,11 @@ Ncpy.prototype.ffi.require = function (file, options) { Ncpy.prototype.ffi._setPath = function () { // go directly down to cc-land - return NCPy2.setPath(this.fileParam[1]) + return NCPy.setPath(this.fileParam[1]) } Ncpy.prototype.ffi.init = function (stream) { - NCPy2.initialize() + NCPy.initialize() this._setPath() @@ -305,7 +312,7 @@ Ncpy.prototype.ffi.init = function (stream) { // listen for SIGINT (and sorts) // REVIEW: for memory leaks FFI._init(stream, function () { - NCPy2.finalize() + NCPy.finalize() }) return this } @@ -337,7 +344,7 @@ Ncpy.prototype.eval = function () { * */ Ncpy.prototype.initialize = function () { - return NCPy2.initialize() + return NCPy.initialize() } /** @@ -345,7 +352,7 @@ Ncpy.prototype.initialize = function () { * @return {Boolean} returns true if Py_isInitialized is ecplictely not 0 */ Ncpy.prototype.isInitialized = function () { - return NCPy2.isInitialized() + return NCPy.isInitialized() } /** @@ -353,7 +360,7 @@ Ncpy.prototype.isInitialized = function () { * @param {callback} callback for completion of py context */ Ncpy.prototype.finalize = function (ref) { - return NCPy2.finalize(ref) + return NCPy.finalize(ref) } /** @@ -361,7 +368,7 @@ Ncpy.prototype.finalize = function (ref) { * @return {Boolean} return true if Py_isInitialized explictely is 0 */ Ncpy.prototype.isFinalized = function () { - return NCPy2.isFinalized() + return NCPy.isFinalized() } /** diff --git a/src/addon.cc b/src/addon.cc index 45ade4c..6bb7923 100644 --- a/src/addon.cc +++ b/src/addon.cc @@ -1,9 +1,11 @@ #include #include "node-cpython-2X.h" +#include "node-cpython-3X.h" #include "ffi.h" void InitAll(v8::Local exports) { NodeCPython2X::Init(exports); + NodeCPython3X::Init(exports); FFI::Init(exports); } diff --git a/src/node-cpython-3X.cc b/src/node-cpython-3X.cc index 2d8dda0..229dc12 100644 --- a/src/node-cpython-3X.cc +++ b/src/node-cpython-3X.cc @@ -1,174 +1,143 @@ -#include +#include "node-cpython-3X.h" +extern "C" { + #include +} +Nan::Persistent NodeCPython3X::constructor; -using namespace v8; -using namespace std; +NodeCPython3X::NodeCPython3X() { + Py_Initialize(); +} + +NodeCPython3X::~NodeCPython3X() { + Py_Finalize(); +} + +void NodeCPython3X::Init(v8::Local exports) { + Nan::HandleScope scope; + + // Prepare constructor template + v8::Local tpl = Nan::New(New); + tpl->SetClassName(Nan::New("NodeCPython3X").ToLocalChecked()); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + + // Prototype + Nan::SetPrototypeMethod(tpl, "value", GetValue); + + Nan::SetPrototypeMethod(tpl, "preInit", PreInit); + Nan::SetPrototypeMethod(tpl, "initialize", Initialize); + Nan::SetPrototypeMethod(tpl, "isInitialized", Initialize); + Nan::SetPrototypeMethod(tpl, "finalize", Finalize); + Nan::SetPrototypeMethod(tpl, "isFinalized", Finalize); + Nan::SetPrototypeMethod(tpl, "setPath", SetPath); + + Nan::SetPrototypeMethod(tpl, "addModule", AddModule); + Nan::SetPrototypeMethod(tpl, "getDict", GetDict); + + Nan::SetPrototypeMethod(tpl, "simpleString", SimpleString); + Nan::SetPrototypeMethod(tpl, "runString", RunString); + + constructor.Reset(tpl->GetFunction()); + exports->Set(Nan::New("NodeCPython3X").ToLocalChecked(), tpl->GetFunction()); +} + +void NodeCPython3X::New(const Nan::FunctionCallbackInfo& info) { + if (info.IsConstructCall()) { + // Invoked as constructor: `new NodeCPython3X(...)` + NodeCPython3X* obj = new NodeCPython3X(); + obj->Wrap(info.This()); + info.GetReturnValue().Set(info.This()); + } else { + // Invoked as plain function `NodeCPython3X(...)`, turn into construct call. + const int argc = 1; + v8::Local argv[argc] = { info[0] }; + v8::Local cons = Nan::New(constructor); + info.GetReturnValue().Set(cons->NewInstance(argc, argv)); + } +} + +void NodeCPython3X::GetValue(const Nan::FunctionCallbackInfo& info) { + // NodeCPython3X* obj = ObjectWrap::Unwrap(info.Holder()); + // info.GetReturnValue().Set(Nan::New(obj->value_)); +} -extern "C" { - #include +void NodeCPython3X::PreInit(const Nan::FunctionCallbackInfo& info) { + // NodeCPython3X* obj = ObjectWrap::Unwrap(info.Holder()); + // obj->value_ += 1; + // program = Py_DecodeLocale(info[0], NULL); + // if (program == NULL) { + // fprintf(stderr, "Fatal error: cannot decode argv[0]\n"); + // exit(1); + // } + // obj->value_ = program; + // info.GetReturnValue().Set(Nan::New(obj->program)); + // info.GetReturnValue().Set(Nan::New(obj->value_)); +} + +void NodeCPython3X::Initialize(const Nan::FunctionCallbackInfo& info) { + Py_Initialize(); + info.GetReturnValue().Set(Nan::True()); +} + +void NodeCPython3X::IsInitialized(const Nan::FunctionCallbackInfo& info) { + if (Py_IsInitialized()) { + info.GetReturnValue().Set(Nan::True()); + } else { + info.GetReturnValue().Set(Nan::False()); + } +} + +void NodeCPython3X::Finalize(const Nan::FunctionCallbackInfo& info) { + Py_Finalize(); + info.GetReturnValue().Set(Nan::True()); +} + +void NodeCPython3X::IsFinalized(const Nan::FunctionCallbackInfo& info) { + if (!Py_IsInitialized()) { + info.GetReturnValue().Set(Nan::True()); + } else { + info.GetReturnValue().Set(Nan::False()); + } +} + +void NodeCPython3X::SetPath(const Nan::FunctionCallbackInfo& info) { + std::string argStr = std::string(*Nan::Utf8String(info[0]->ToString())); + const char *str = argStr.c_str(); + // Lorenzo did some magic below + PySys_SetPath(const_cast(str)); +} + +void NodeCPython3X::AddModule(const Nan::FunctionCallbackInfo& info) { + NodeCPython3X* obj = ObjectWrap::Unwrap(info.Holder()); + + obj->main = PyImport_AddModule("__main__"); + info.GetReturnValue().Set(Nan::New(obj->main)); +} + +void NodeCPython3X::GetDict(const Nan::FunctionCallbackInfo& info) { + NodeCPython3X* obj = ObjectWrap::Unwrap(info.Holder()); + obj->d = PyModule_GetDict(obj->main); + info.GetReturnValue().Set(Nan::New(obj->d)); } +// ============================================================================= +// ================================== High Level =============================== +// ============================================================================= -// NAN_METHOD(simpleString) { -// NanScope(); -// -// // TODO: Check whether this check is necessary and -// // if not redundant to JS check -// if (args.Length() != 1) { -// NanThrowTypeError("Function expects one argument"); -// NanReturnUndefined(); -// } -// -// /* -// * This method takes the string from arguments and goes to -// * great length to convert from arguments to v8::String.. to -// * std::string to c-string, to eventually pass into cpython methods -// */ -// v8::String::Utf8Value py_string_param(args[0]->ToString()); -// std::string param = std::string(*py_string_param); -// const char *py_cstr = param.c_str(); -// -// simple_String(py_cstr); -// -// // TODO: Clean-up -// NanReturnValue(NanNew(0)); -// } -// -// NAN_METHOD(simpleFile) { -// NanScope(); -// -// // TODO: Check whether this check is necessary and -// // if not redundant to JS check -// // if (args.Length() != 1) { -// // NanThrowTypeError("Function expects one argument"); -// // NanReturnUndefined(); -// // } -// -// // v8::String::Utf8Value py_filepath_string_param(args[0]->ToString()); -// // std::string param0 = std::string(*py_filepath_string_param); -// // const char *py_filepath_cstr = param0.c_str(); -// // -// // v8::String::Utf8Value py_filename_string_param(args[1]->ToString()); -// // std::string param1 = std::string(*py_filename_string_param); -// // const char *py_filename_cstr = param1.c_str(); -// -// FILE * fp; -// fp = fopen(py_filepath_cstr, "r"); -// -// simple_File(fp, py_filename_cstr); -// -// // TODO: Clean-up -// NanReturnValue(NanNew(0)); -// } -// -// NAN_METHOD(run) { -// NanScope(); -// -// if (args.Length() != 3) { -// NanThrowTypeError("Function expects one argument"); -// NanReturnUndefined(); -// } -// -// -// -// v8::String::Utf8Value py_program_path_string(args[0]->ToString()); -// std::string param0 = std::string(*py_program_path_string); -// const char *path = param0.c_str(); -// -// char *program_path; -// strcpy(program_path, path); -// -// printf("hello %s\n", program_path); -// -// int arrc = args[1]->NumberValue(); -// -// Local arr= Local::Cast(args[2]); -// char * arrv[] = {}; -// -// for (size_t i = 0; i < arrc; i++) { -// -// Local item = arr->Get(i); -// v8::String::Utf8Value array_string(item->ToString()); -// std::string param = std::string(*array_string); -// -// // first cast const char to char and then -// arrv[i] = strdup((char *) param.c_str()); -// } -// // last statement: http://stackoverflow.com/a/1788749/3580261 -// -// -// -// run(program_path , arrc , arrv); -// -// // TODO: Clean-up -// NanReturnValue(NanNew(0)); -// } -// -// // -// // -// NAN_METHOD(initialize) { -// NanScope(); -// -// initialize(); -// -// // TODO: Clean-up -// NanReturnValue(NanNew(0)); -// } -// -// // -// // -// NAN_METHOD(finalize) { -// NanScope(); -// -// finalize(); -// -// // TODO: Clean-up -// NanReturnValue(NanNew(0)); -// } -// -// // -// // -// NAN_METHOD(setArgv) { -// NanScope(); -// -// // v8::String::Utf8Value py_filename_string_param(args[0]->ToString()); -// // std::string param1 = std::string(*py_filename_string_param); -// // const char *py_filename_cstr = param1.c_str(); -// -// char *arrv[] = {"program name", "arg1", "arg2"}; -// int arrc = sizeof(arrv) / sizeof(char*) - 1; -// -// pysetargv(arrc,arrv); -// -// // TODO: Clean-up -// NanReturnValue(NanNew(0)); -// } -// -// NAN_METHOD(setProgramName) { -// NanScope(); -// -// v8::String::Utf8Value py_program_name_string(args[0]->ToString()); -// std::string param0 = std::string(*py_program_name_string); -// const char *py_program_name_cstr = param0.c_str(); -// -// setprogramname(py_program_name_cstr); -// -// // TODO: Clean-up -// NanReturnValue(NanNew(0)); -// } -// -void Init(Handle exports) { - // exports->Set(NanNew("simpleString"), NanNew(simpleString)->GetFunction()); - // exports->Set(NanNew("simpleFile"), NanNew(simpleFile)->GetFunction()); - // exports->Set(NanNew("runRun"), NanNew(run)->GetFunction()); - // exports->Set(NanNew("initialize"), NanNew(initialize)->GetFunction()); - // exports->Set(NanNew("finalize"), NanNew(finalize)->GetFunction()); - // exports->Set(NanNew("setargv"), NanNew(setArgv)->GetFunction()); - // exports->Set(NanNew("setprogramname"), NanNew(setProgramName)->GetFunction()); -} - - -NODE_MODULE(cpython, Init) +void NodeCPython3X::SimpleString(const Nan::FunctionCallbackInfo& info) { + std::string pyStr = std::string(*Nan::Utf8String(info[0]->ToString())); + const char *str = pyStr.c_str(); + PyRun_SimpleString(str); +} + +void NodeCPython3X::RunString(const Nan::FunctionCallbackInfo& info) { + NodeCPython3X* obj = ObjectWrap::Unwrap(info.Holder()); + + std::string pyStr = std::string(*Nan::Utf8String(info[0]->ToString())); + const char *str = pyStr.c_str(); + + PyRun_String(str, Py_single_input, obj->d, obj->d); +} diff --git a/src/node-cpython-3X.h b/src/node-cpython-3X.h new file mode 100644 index 0000000..4ed814b --- /dev/null +++ b/src/node-cpython-3X.h @@ -0,0 +1,39 @@ +#ifndef NODECPYTHON3X_H +#define NODECPYTHON3X_H + +#include +extern "C" { + #include +} + +class NodeCPython3X : public Nan::ObjectWrap { + public: + static void Init(v8::Local exports); + + private: + NodeCPython3X(); + ~NodeCPython3X(); + + static void New(const Nan::FunctionCallbackInfo& info); + static void GetValue(const Nan::FunctionCallbackInfo& info); + + static void PreInit(const Nan::FunctionCallbackInfo& info); + static void Initialize(const Nan::FunctionCallbackInfo& info); + static void IsInitialized(const Nan::FunctionCallbackInfo& info); + static void Finalize(const Nan::FunctionCallbackInfo& info); + static void IsFinalized(const Nan::FunctionCallbackInfo& info); + static void SetPath(const Nan::FunctionCallbackInfo& info); + + static void SimpleString(const Nan::FunctionCallbackInfo& info); + static void RunString(const Nan::FunctionCallbackInfo& info); + + static void AddModule(const Nan::FunctionCallbackInfo& info); + static void GetDict(const Nan::FunctionCallbackInfo& info); + + static Nan::Persistent constructor; + static wchar_t *program; + PyObject *main; + PyObject *d; +}; + +#endif