diff --git a/compat-inl.h b/compat-inl.h new file mode 100644 index 0000000..e9aa89f --- /dev/null +++ b/compat-inl.h @@ -0,0 +1,251 @@ +// Copyright (c) 2014, StrongLoop Inc. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +#ifndef COMPAT_INL_H_ // NOLINT(build/header_guard) +#define COMPAT_INL_H_ // NOLINT(build/header_guard) + +#include "compat.h" + +namespace compat { + +#if COMPAT_NODE_VERSION == 10 +#define COMPAT_ISOLATE +#define COMPAT_ISOLATE_ +#else +#define COMPAT_ISOLATE isolate +#define COMPAT_ISOLATE_ isolate, +#endif + +namespace I { + +template +inline void Use(const T&) {} + +template +inline v8::Local ToLocal(v8::Local handle) { + return handle; +} + +#if COMPAT_NODE_VERSION == 10 +template +inline v8::Local ToLocal(v8::Handle handle) { + return v8::Local::New(handle); +} +#endif + +} // namespace I + +v8::Local True(v8::Isolate* isolate) { + I::Use(isolate); + return I::ToLocal(v8::True(COMPAT_ISOLATE)); +} + +v8::Local False(v8::Isolate* isolate) { + I::Use(isolate); + return I::ToLocal(v8::False(COMPAT_ISOLATE)); +} + +v8::Local Null(v8::Isolate* isolate) { + I::Use(isolate); + return I::ToLocal(v8::Null(COMPAT_ISOLATE)); +} + +v8::Local Undefined(v8::Isolate* isolate) { + I::Use(isolate); + return I::ToLocal(v8::Undefined(COMPAT_ISOLATE)); +} + +v8::Local Array::New(v8::Isolate* isolate, int length) { + I::Use(isolate); + return v8::Array::New(COMPAT_ISOLATE_ length); +} + +v8::Local Boolean::New(v8::Isolate* isolate, bool value) { + I::Use(isolate); + return I::ToLocal(v8::Boolean::New(COMPAT_ISOLATE_ value)); +} + +v8::Local FunctionTemplate::New( + v8::Isolate* isolate, FunctionCallback callback) { + I::Use(isolate); + return v8::FunctionTemplate::New(COMPAT_ISOLATE_ callback); +} + +v8::Local Integer::New(v8::Isolate* isolate, int32_t value) { + I::Use(isolate); + return v8::Integer::New(COMPAT_ISOLATE_ value); +} + +v8::Local Integer::NewFromUnsigned(v8::Isolate* isolate, + uint32_t value) { + I::Use(isolate); + return v8::Integer::NewFromUnsigned(COMPAT_ISOLATE_ value); +} + +v8::Local Number::New(v8::Isolate* isolate, double value) { + I::Use(isolate); + return v8::Number::New(COMPAT_ISOLATE_ value); +} + +v8::Local Object::New(v8::Isolate* isolate) { + I::Use(isolate); + return v8::Object::New(COMPAT_ISOLATE); +} + +template +Persistent::~Persistent() { + // Trying to dispose the handle when the VM has been destroyed + // (e.g. at program exit) will segfault. + if (v8::V8::IsDead()) return; + Reset(); +} + +template +bool Persistent::IsEmpty() const { + return handle_.IsEmpty(); +} + +ReturnType ReturnableHandleScope::Return(bool value) { + return Return(Boolean::New(isolate(), value)); +} + +ReturnType ReturnableHandleScope::Return(intptr_t value) { + return Return(Integer::New(isolate(), value)); +} + +ReturnType ReturnableHandleScope::Return(int value) { + return Return(Integer::New(isolate(), value)); +} + +ReturnType ReturnableHandleScope::Return(double value) { + return Return(Number::New(isolate(), value)); +} + +ReturnType ReturnableHandleScope::Return(const char* value) { + return Return(String::NewFromUtf8(isolate(), value)); +} + +v8::Isolate* ReturnableHandleScope::isolate() const { + return args_.GetIsolate(); +} + +#if COMPAT_NODE_VERSION == 10 + +const v8::HeapSnapshot* HeapProfiler::TakeHeapSnapshot( + v8::Isolate* isolate, v8::Local title) { + if (title.IsEmpty()) title = v8::String::Empty(isolate); + return v8::HeapProfiler::TakeSnapshot(title); +} + +void HeapProfiler::DeleteAllHeapSnapshots(v8::Isolate* isolate) { + I::Use(isolate); + v8::HeapProfiler::DeleteAllSnapshots(); +} + +v8::Local String::NewFromUtf8(v8::Isolate* isolate, + const char* data, NewStringType type, + int length) { + I::Use(isolate); + if (type == kInternalizedString) { + return v8::String::NewSymbol(data, length); + } + if (type == kUndetectableString) { + return v8::String::NewUndetectable(data, length); + } + return v8::String::New(data, length); +} + +HandleScope::HandleScope(v8::Isolate*) {} + +template +v8::Local Persistent::ToLocal(v8::Isolate*) const { + return *handle_; +} + +template +void Persistent::Reset() { + handle_.Dispose(); + handle_.Clear(); +} + +template +void Persistent::Reset(v8::Isolate*, v8::Local value) { + Reset(); + handle_ = v8::Persistent::New(value); +} + +ReturnableHandleScope::ReturnableHandleScope(const ArgumentType& args) + : args_(args) {} + +ReturnType ReturnableHandleScope::Return() { return v8::Undefined(); } + +ReturnType ReturnableHandleScope::Return(v8::Local value) { + return handle_scope_.Close(value); +} + +#elif COMPAT_NODE_VERSION == 12 + +const v8::HeapSnapshot* HeapProfiler::TakeHeapSnapshot( + v8::Isolate* isolate, v8::Local title) { + if (title.IsEmpty()) title = v8::String::Empty(isolate); + return isolate->GetHeapProfiler()->TakeHeapSnapshot(title); +} + +void HeapProfiler::DeleteAllHeapSnapshots(v8::Isolate* isolate) { + return isolate->GetHeapProfiler()->DeleteAllHeapSnapshots(); +} + +v8::Local String::NewFromUtf8(v8::Isolate* isolate, + const char* data, NewStringType type, + int length) { + return v8::String::NewFromUtf8( + isolate, data, static_cast(type), length); +} + +HandleScope::HandleScope(v8::Isolate* isolate) : handle_scope_(isolate) {} + +template +v8::Local Persistent::ToLocal(v8::Isolate* isolate) const { + return v8::Local::New(isolate, handle_); +} + +template +void Persistent::Reset() { + handle_.Reset(); +} + +template +void Persistent::Reset(v8::Isolate* isolate, v8::Local value) { + handle_.Reset(isolate, value); +} + +ReturnableHandleScope::ReturnableHandleScope(const ArgumentType& args) + : args_(args), handle_scope_(args.GetIsolate()) {} + +ReturnType ReturnableHandleScope::Return() {} + +ReturnType ReturnableHandleScope::Return(v8::Local value) { + // TODO(bnoordhuis) Creating handles for primitive values (int, double) + // is less efficient than passing them to ReturnValue::Set() directly. + return args_.GetReturnValue().Set(value); +} + +#endif + +#undef COMPAT_ISOLATE_ +#undef COMPAT_ISOLATE + +} // namespace compat + +#endif // COMPAT_INL_H_ // NOLINT(build/header_guard) diff --git a/compat.h b/compat.h new file mode 100644 index 0000000..8277958 --- /dev/null +++ b/compat.h @@ -0,0 +1,140 @@ +// Copyright (c) 2014, StrongLoop Inc. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +#ifndef COMPAT_H_ // NOLINT(build/header_guard) +#define COMPAT_H_ // NOLINT(build/header_guard) + +#include "node_version.h" +#include "v8.h" +#include "v8-profiler.h" + +#if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION >= 11 +#define COMPAT_NODE_VERSION 12 // v0.12 +#elif NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION == 10 +#define COMPAT_NODE_VERSION 10 // v0.10 +#else +#error "Unsupported node.js version." +#endif + +namespace compat { + +#if COMPAT_NODE_VERSION == 10 +typedef v8::Arguments ArgumentType; +typedef v8::Handle ReturnType; +typedef v8::InvocationCallback FunctionCallback; +#elif COMPAT_NODE_VERSION == 12 +typedef v8::FunctionCallbackInfo ArgumentType; +typedef void ReturnType; +typedef v8::FunctionCallback FunctionCallback; +#endif + +inline v8::Local True(v8::Isolate* isolate); +inline v8::Local False(v8::Isolate* isolate); +inline v8::Local Null(v8::Isolate* isolate); +inline v8::Local Undefined(v8::Isolate* isolate); + +class AllStatic { + private: + AllStatic(); +}; + +struct Array : public AllStatic { + inline static v8::Local New(v8::Isolate* isolate, int length = 0); +}; + +struct Boolean : public AllStatic { + inline static v8::Local New(v8::Isolate* isolate, bool value); +}; + +struct FunctionTemplate : public AllStatic { + inline static v8::Local New(v8::Isolate* isolate, + FunctionCallback callback = + 0); +}; + +struct HeapProfiler : public AllStatic { + inline static const v8::HeapSnapshot* TakeHeapSnapshot( + v8::Isolate* isolate, + v8::Local title = v8::Local()); + inline static void DeleteAllHeapSnapshots(v8::Isolate* isolate); +}; + +struct Integer : public AllStatic { + inline static v8::Local New(v8::Isolate* isolate, int32_t value); + inline static v8::Local NewFromUnsigned(v8::Isolate* isolate, + uint32_t value); +}; + +struct Number : public AllStatic { + inline static v8::Local New(v8::Isolate* isolate, double value); +}; + +struct Object : public AllStatic { + inline static v8::Local New(v8::Isolate* isolate); +}; + +struct String : public AllStatic { + enum NewStringType { + kNormalString, + kInternalizedString, + kUndetectableString + }; + inline static v8::Local NewFromUtf8(v8::Isolate* isolate, + const char* data, + NewStringType type = + kNormalString, + int length = -1); +}; + +class HandleScope { + public: + inline explicit HandleScope(v8::Isolate* isolate); + + private: + v8::HandleScope handle_scope_; +}; + +template +class Persistent { + public: + inline ~Persistent(); + inline v8::Local ToLocal(v8::Isolate* isolate) const; + inline void Reset(); + inline void Reset(v8::Isolate* isolate, v8::Local value); + inline bool IsEmpty() const; + + private: + v8::Persistent handle_; +}; + +class ReturnableHandleScope { + public: + inline explicit ReturnableHandleScope(const ArgumentType& args); + inline ReturnType Return(); + inline ReturnType Return(bool value); + inline ReturnType Return(intptr_t value); + inline ReturnType Return(int value); + inline ReturnType Return(double value); + inline ReturnType Return(const char* value); + inline ReturnType Return(v8::Local value); + + private: + inline v8::Isolate* isolate() const; + const ArgumentType& args_; + v8::HandleScope handle_scope_; +}; + +} // namespace compat + +#endif // COMPAT_H_ // NOLINT(build/header_guard) diff --git a/node-syslog.h b/node-syslog.h index 8a00593..50eea93 100644 --- a/node-syslog.h +++ b/node-syslog.h @@ -10,32 +10,26 @@ #include #include -namespace node { +#include "compat.h" +namespace C = ::compat; + +class Syslog { -class Syslog : ObjectWrap { public: static void Initialize ( v8::Handle target); protected: - static v8::Persistent constructor_template; - static v8::Handle init (const v8::Arguments& args); - static v8::Handle log (const v8::Arguments& args); - static v8::Handle setMask (const v8::Arguments& args); - static v8::Handle destroy (const v8::Arguments& args); - - Syslog () : ObjectWrap() { - }; + static C::ReturnType init (const C::ArgumentType& args); + static C::ReturnType log (const C::ArgumentType& args); + static C::ReturnType setMask (const C::ArgumentType& args); + static C::ReturnType destroy (const C::ArgumentType& args); - ~Syslog (){}; - - private: static void open(int, int); static void close(); static bool connected_; - static char name[1024]; + static char name[1024]; }; -} // namespace node #endif // syslog_h diff --git a/node-syslog.js b/node-syslog.js index 7b02ca6..750744b 100644 --- a/node-syslog.js +++ b/node-syslog.js @@ -1,6 +1,4 @@ -(function(){ - -var SyslogWrapper = require('./build/Release/syslog').Syslog; +var SyslogWrapper = require('./build/Release/syslog'); /* * export Syslog as module @@ -58,10 +56,9 @@ LOG_DEBUG : 7 /* * Attach destroy handling + * + * XXX(sam) consider using AtExit: joyent/node#e4a8d261 */ process.on('exit', function() { SyslogWrapper.close(); }); - - -})(); diff --git a/package.json b/package.json index 84d191a..c8d6d8f 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,9 @@ "engines": { "node" : ">=0.8.0" }, + "scripts": { + "test": "node test.js" + }, "description": "Node module to support sending messages to syslog daemon", "author": "Nazar Kulyk ", "homepage": "http://github.com/schamane/node-syslog", @@ -21,5 +24,5 @@ "type": "Simplified BSD License", "url": "http://en.wikipedia.org/wiki/BSD_licenses" }], - "tags": ["syslog", "system", "log", "logging"] + "tags": ["syslog", "system", "log", "logging" ] } diff --git a/syslog.cc b/syslog.cc index f733a48..f743cc1 100644 --- a/syslog.cc +++ b/syslog.cc @@ -1,117 +1,114 @@ #include "node-syslog.h" +#include "compat-inl.h" using namespace v8; -using namespace node; +using compat::ReturnType; +using compat::ArgumentType; +using compat::ReturnableHandleScope; + +#if COMPAT_NODE_VERSION < 12 +static ReturnType ThrowException(const ArgumentType& args, const char* m) { + return v8::ThrowException(v8::Exception::Error( + v8::String::New(m) + )); +} +#else +static ReturnType ThrowException(const ArgumentType& args, const char* m) { + args.GetIsolate()->ThrowException(v8::Exception::Error( + v8::String::NewFromUtf8(args.GetIsolate(), m) + )); +#endif -Persistent Syslog::constructor_template; bool Syslog::connected_ = false; char Syslog::name[1024]; void Syslog::Initialize ( Handle target) { - Local t = FunctionTemplate::New(); - constructor_template = Persistent::New(t); - constructor_template->InstanceTemplate()->SetInternalFieldCount(1); - constructor_template->SetClassName(String::NewSymbol("Syslog")); - - - NODE_SET_METHOD(constructor_template, "init", Syslog::init); - NODE_SET_METHOD(constructor_template, "log", Syslog::log); - NODE_SET_METHOD(constructor_template, "setMask", Syslog::setMask); - NODE_SET_METHOD(constructor_template, "close", Syslog::destroy); - - target->Set(String::NewSymbol("Syslog"), constructor_template->GetFunction()); + NODE_SET_METHOD(target, "init", Syslog::init); + NODE_SET_METHOD(target, "log", Syslog::log); + NODE_SET_METHOD(target, "setMask", Syslog::setMask); + NODE_SET_METHOD(target, "close", Syslog::destroy); } -Handle -Syslog::init ( const Arguments& args) +ReturnType +Syslog::init ( const ArgumentType& args) { - HandleScope scope; + ReturnableHandleScope scope(args); if (args.Length() == 0 || !args[0]->IsString()) { - return ThrowException(Exception::Error( - String::New("Must give daemonname string as argument"))); + return ThrowException(args, "Must give daemonname string as argument"); } if (args.Length() < 3 ) { - return ThrowException(Exception::Error( - String::New("Must have atleast 3 params as argument"))); + return ThrowException(args, "Must have at least 3 params as argument"); } if(connected_) close(); //open syslog - args[0]->ToString()->WriteAscii((char*) &name); + args[0]->ToString()->WriteUtf8(name); int options = args[1]->ToInt32()->Value(); int facility = args[2]->ToInt32()->Value(); open( options , facility ); - - return scope.Close(Undefined()); + + return scope.Return(); } struct log_request { - Persistent cb; - char *msg; + uv_work_t work; uint32_t log_level; + char msg[1]; // variable length msg buffer }; -static void UV_AfterLog(uv_work_t *req) { - struct log_request *log_req = (struct log_request *)(req->data); - - log_req->cb.Dispose(); // is this necessary? - free(log_req->msg); - free(log_req); - delete req; +static void UV_AfterLog(uv_work_t *req, int) { + free(req->data); } static void UV_Log(uv_work_t *req) { struct log_request *log_req = (struct log_request *)(req->data); - char *msg = log_req->msg; - - syslog(log_req->log_level, "%s", msg); + syslog(log_req->log_level, "%s", log_req->msg); return; } -Handle -Syslog::log ( const Arguments& args) +ReturnType +Syslog::log ( const ArgumentType& args) { - HandleScope scope; - Local cb = Local::Cast(args[3]); - - struct log_request * log_req = (struct log_request *) - calloc(1, sizeof(struct log_request)); + ReturnableHandleScope scope(args); + uint32_t log_level = args[0]->Int32Value(); + String::Utf8Value msg(args[1]); + + if(!connected_) + return ThrowException(args, "Init method has to be called before syslog"); + if(!args[1]->IsString()) { + return ThrowException(args, "Log message must be a string"); + } + + struct log_request * log_req = (struct log_request *) malloc( + sizeof(*log_req) + msg.length()); + if(!log_req) { - V8::LowMemoryNotification(); - return ThrowException(Exception::Error( - String::New("Could not allocate enought memory"))); + return ThrowException(args, "Could not allocate enough memory"); } - - if(!connected_) - return ThrowException(Exception::Error( - String::New("init method has to be called befor syslog"))); - - String::AsciiValue msg(args[1]); - uint32_t log_level = args[0]->Int32Value(); - - log_req->cb = Persistent::New(cb); - log_req->msg = strdup(*msg); + + log_req->work.data = log_req; log_req->log_level = log_level; + strcpy(&log_req->msg[0], *msg); - uv_work_t *work_req = new uv_work_t(); - work_req->data = log_req; - int status = uv_queue_work(uv_default_loop(), work_req, UV_Log,(uv_after_work_cb) UV_AfterLog); + int status = uv_queue_work(uv_default_loop(), &log_req->work, + UV_Log, UV_AfterLog); assert(status == 0); - return scope.Close(Undefined()); + return scope.Return(); } -Handle -Syslog::destroy ( const Arguments& args) +ReturnType +Syslog::destroy ( const ArgumentType& args) { + ReturnableHandleScope scope(args); close(); - return Undefined(); + return scope.Return(); } void @@ -121,23 +118,23 @@ Syslog::open ( int option, int facility) connected_ = true; } -Handle -Syslog::setMask ( const Arguments& args) +ReturnType +Syslog::setMask ( const ArgumentType& args) { bool upTo = false; int mask, value; - HandleScope scope; + ReturnableHandleScope scope(args); if (args.Length() < 1) { - return ThrowException(Exception::Error(String::New("You must provide an mask"))); + return ThrowException(args, "You must provide an mask"); } if (!args[0]->IsNumber()) { - return ThrowException(Exception::Error(String::New("First parameter (mask) should be numeric"))); + return ThrowException(args, "First parameter (mask) should be numeric"); } if (args.Length() == 2 && !args[1]->IsBoolean()) { - return ThrowException(Exception::Error(String::New("Second parameter (upTo) should be boolean"))); + return ThrowException(args, "Second parameter (upTo) should be boolean"); } if (args.Length() == 2 && args[1]->IsBoolean()) { @@ -150,8 +147,8 @@ Syslog::setMask ( const Arguments& args) } else { mask = LOG_MASK(value); } - - return scope.Close(Integer::New( setlogmask(mask) )); + + return scope.Return(setlogmask(mask)); } void diff --git a/test.js b/test.js index 5007007..c218bd3 100644 --- a/test.js +++ b/test.js @@ -1,8 +1,14 @@ +var assert = require('assert'); var Syslog = require('./node-syslog'); +assert.throws(function() { + Syslog.init(); +}, Error); + Syslog.init("node-syslog-test", Syslog.LOG_PID | Syslog.LOG_ODELAY, Syslog.LOG_LOCAL0); Syslog.log(Syslog.LOG_INFO, "news info log test"); Syslog.log(Syslog.LOG_ERR, "news log error test"); Syslog.log(Syslog.LOG_DEBUG, "Last log message as debug: " + new Date()); Syslog.close(); +console.log('PASS');