diff --git a/src/Canvas.cc b/src/Canvas.cc index 2f7563a48..bd20663d6 100644 --- a/src/Canvas.cc +++ b/src/Canvas.cc @@ -5,6 +5,7 @@ // #include "Canvas.h" +#include "PNG.h" #include "CanvasRenderingContext2d.h" #include #include @@ -44,6 +45,15 @@ Canvas::Initialize(Handle target) { proto->SetAccessor(NanSymbol("type"), GetType); proto->SetAccessor(NanSymbol("width"), GetWidth, SetWidth); proto->SetAccessor(NanSymbol("height"), GetHeight, SetHeight); + + proto->Set("PNG_NO_FILTERS", Uint32::New(PNG_NO_FILTERS)); + proto->Set("PNG_FILTER_NONE", Uint32::New(PNG_FILTER_NONE)); + proto->Set("PNG_FILTER_SUB", Uint32::New(PNG_FILTER_SUB)); + proto->Set("PNG_FILTER_UP", Uint32::New(PNG_FILTER_UP)); + proto->Set("PNG_FILTER_AVG", Uint32::New(PNG_FILTER_AVG)); + proto->Set("PNG_FILTER_PAETH", Uint32::New(PNG_FILTER_PAETH)); + proto->Set("PNG_ALL_FILTERS", Uint32::New(PNG_ALL_FILTERS)); + target->Set(NanSymbol("Canvas"), ctor->GetFunction()); } @@ -128,31 +138,31 @@ NAN_SETTER(Canvas::SetHeight) { static cairo_status_t toBuffer(void *c, const uint8_t *data, unsigned len) { closure_t *closure = (closure_t *) c; - - // Olaf: grow buffer + if (closure->len + len > closure->max_len) { uint8_t *data; - unsigned max; - - // round to the nearest multiple of 1024 bytes - max = (closure->max_len + len + 1023) & ~1023; - + unsigned max = closure->max_len; + + do { + max *= 2; + } while (closure->len + len > max); + data = (uint8_t *) realloc(closure->data, max); if (!data) return CAIRO_STATUS_NO_MEMORY; closure->data = data; closure->max_len = max; } - + memcpy(closure->data + closure->len, data, len); closure->len += len; - + return CAIRO_STATUS_SUCCESS; } /* * EIO toBuffer callback. */ - + #if NODE_VERSION_AT_LEAST(0, 6, 0) void Canvas::ToBufferAsync(uv_work_t *req) { @@ -165,11 +175,11 @@ Canvas::EIO_ToBuffer(eio_req *req) { #endif closure_t *closure = (closure_t *) req->data; - closure->status = cairo_surface_write_to_png_stream( + closure->status = canvas_write_to_png_stream( closure->canvas->surface() , toBuffer , closure); - + #if !NODE_VERSION_AT_LEAST(0, 5, 4) return 0; #endif @@ -209,20 +219,22 @@ Canvas::EIO_AfterToBuffer(eio_req *req) { delete closure->pfn; closure_destroy(closure); free(closure); - + #if !NODE_VERSION_AT_LEAST(0, 6, 0) return 0; #endif } /* - * Convert PNG data to a node::Buffer, async when a + * Convert PNG data to a node::Buffer, async when a * callback function is passed. */ NAN_METHOD(Canvas::ToBuffer) { NanScope(); cairo_status_t status; + uint32_t compression_level = 6; + uint32_t filter = PNG_ALL_FILTERS; Canvas *canvas = ObjectWrap::Unwrap(args.This()); // TODO: async / move this out @@ -234,10 +246,48 @@ NAN_METHOD(Canvas::ToBuffer) { NanReturnValue(buf); } + if (args.Length() > 1 && !(args[1]->StrictEquals(Undefined()) && args[2]->StrictEquals(Undefined()))) { + if (!args[1]->StrictEquals(Undefined())) { + bool good = true; + if (args[1]->IsNumber()) { + compression_level = args[1]->Uint32Value(); + } else if (args[1]->IsString()) { + if (args[1]->StrictEquals(String::New("0"))) { + compression_level = 0; + } else { + uint32_t tmp = args[1]->Uint32Value(); + if (tmp == 0) { + good = false; + } else { + compression_level = tmp; + } + } + } else { + good = false; + } + + if (good) { + if (compression_level > 9) { + return NanThrowRangeError("Allowed compression levels lie in the range [0, 9]."); + } + } else { + return NanThrowTypeError("Compression level must be a number."); + } + } + + if (!args[2]->StrictEquals(Undefined())) { + if (args[2]->IsUint32()) { + filter = args[1]->Uint32Value(); + } else { + return NanThrowTypeError("Invalid filter value."); + } + } + } + // Async if (args[0]->IsFunction()) { closure_t *closure = (closure_t *) malloc(sizeof(closure_t)); - status = closure_init(closure, canvas); + status = closure_init(closure, canvas, compression_level, filter); // ensure closure is ok if (status) { @@ -249,7 +299,7 @@ NAN_METHOD(Canvas::ToBuffer) { // TODO: only one callback fn in closure canvas->Ref(); closure->pfn = new NanCallback(args[0].As()); - + #if NODE_VERSION_AT_LEAST(0, 6, 0) uv_work_t* req = new uv_work_t; req->data = closure; @@ -258,12 +308,12 @@ NAN_METHOD(Canvas::ToBuffer) { eio_custom(EIO_ToBuffer, EIO_PRI_DEFAULT, EIO_AfterToBuffer, closure); ev_ref(EV_DEFAULT_UC); #endif - + NanReturnUndefined(); // Sync } else { closure_t closure; - status = closure_init(&closure, canvas); + status = closure_init(&closure, canvas, compression_level, filter); // ensure closure is ok if (status) { @@ -272,7 +322,7 @@ NAN_METHOD(Canvas::ToBuffer) { } TryCatch try_catch; - status = cairo_surface_write_to_png_stream(canvas->surface(), toBuffer, &closure); + status = canvas_write_to_png_stream(canvas->surface(), toBuffer, &closure); if (try_catch.HasCaught()) { closure_destroy(&closure); @@ -311,16 +361,60 @@ streamPNG(void *c, const uint8_t *data, unsigned len) { NAN_METHOD(Canvas::StreamPNGSync) { NanScope(); + uint32_t compression_level = 6; + uint32_t filter = PNG_ALL_FILTERS; // TODO: async as well if (!args[0]->IsFunction()) return NanThrowTypeError("callback function required"); + if (args.Length() > 1 && !(args[1]->StrictEquals(Undefined()) && args[2]->StrictEquals(Undefined()))) { + if (!args[1]->StrictEquals(Undefined())) { + bool good = true; + if (args[1]->IsNumber()) { + compression_level = args[1]->Uint32Value(); + } else if (args[1]->IsString()) { + if (args[1]->StrictEquals(String::New("0"))) { + compression_level = 0; + } else { + uint32_t tmp = args[1]->Uint32Value(); + if (tmp == 0) { + good = false; + } else { + compression_level = tmp; + } + } + } else { + good = false; + } + + if (good) { + if (compression_level > 9) { + return NanThrowRangeError("Allowed compression levels lie in the range [0, 9]."); + } + } else { + return NanThrowTypeError("Compression level must be a number."); + } + } + + if (!args[2]->StrictEquals(Undefined())) { + if (args[2]->IsUint32()) { + filter = args[1]->Uint32Value(); + } else { + return NanThrowTypeError("Invalid filter value."); + } + } + } + + Canvas *canvas = ObjectWrap::Unwrap(args.This()); closure_t closure; closure.fn = Handle::Cast(args[0]); + closure.compression_level = compression_level; + closure.filter = filter; TryCatch try_catch; - cairo_status_t status = cairo_surface_write_to_png_stream(canvas->surface(), streamPNG, &closure); + + cairo_status_t status = canvas_write_to_png_stream(canvas->surface(), streamPNG, &closure); if (try_catch.HasCaught()) { NanReturnValue(try_catch.ReThrow()); @@ -381,7 +475,7 @@ Canvas::Canvas(int w, int h, canvas_type_t t): ObjectWrap() { if (CANVAS_TYPE_PDF == t) { _closure = malloc(sizeof(closure_t)); assert(_closure); - cairo_status_t status = closure_init((closure_t *) _closure, this); + cairo_status_t status = closure_init((closure_t *) _closure, this, 0, PNG_NO_FILTERS); assert(status == CAIRO_STATUS_SUCCESS); _surface = cairo_pdf_surface_create_for_stream(toBuffer, _closure, w, h); } else { diff --git a/src/PNG.h b/src/PNG.h new file mode 100644 index 000000000..81ad1600e --- /dev/null +++ b/src/PNG.h @@ -0,0 +1,220 @@ +#ifndef _CANVAS_PNG_H +#define _CANVAS_PNG_H +#include +#include +#include +#include +#include "closure.h" + +#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) +#define likely(expr) (__builtin_expect (!!(expr), 1)) +#define unlikely(expr) (__builtin_expect (!!(expr), 0)) +#else +#define likely(expr) (expr) +#define unlikely(expr) (expr) +#endif + +static void canvas_png_flush(png_structp png_ptr) { + /* Do nothing; fflush() is said to be just a waste of energy. */ + (void) png_ptr; /* Stifle compiler warning */ +} + +/* Converts native endian xRGB => RGBx bytes */ +static void canvas_convert_data_to_bytes(png_structp png, png_row_infop row_info, png_bytep data) { + unsigned int i; + + for (i = 0; i < row_info->rowbytes; i += 4) { + uint8_t *b = &data[i]; + uint32_t pixel; + + memcpy(&pixel, b, sizeof (uint32_t)); + + b[0] = (pixel & 0xff0000) >> 16; + b[1] = (pixel & 0x00ff00) >> 8; + b[2] = (pixel & 0x0000ff) >> 0; + b[3] = 0; + } +} + +/* Unpremultiplies data and converts native endian ARGB => RGBA bytes */ +static void canvas_unpremultiply_data(png_structp png, png_row_infop row_info, png_bytep data) { + unsigned int i; + + for (i = 0; i < row_info->rowbytes; i += 4) { + uint8_t *b = &data[i]; + uint32_t pixel; + uint8_t alpha; + + memcpy(&pixel, b, sizeof (uint32_t)); + alpha = (pixel & 0xff000000) >> 24; + if (alpha == 0) { + b[0] = b[1] = b[2] = b[3] = 0; + } else { + b[0] = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha; + b[1] = (((pixel & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha; + b[2] = (((pixel & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha; + b[3] = alpha; + } + } +} + +struct canvas_png_write_closure_t { + cairo_write_func_t write_func; + void *closure; +}; + +static cairo_status_t canvas_write_png(cairo_surface_t *surface, png_rw_ptr write_func, void *closure) { + unsigned int i; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + uint8_t *data; + png_structp png; + png_infop info; + png_bytep *volatile rows = NULL; + png_color_16 white; + int png_color_type; + int bpc; + unsigned int width = cairo_image_surface_get_width(surface); + unsigned int height = cairo_image_surface_get_height(surface); + + data = cairo_image_surface_get_data(surface); + if (data == NULL) { + status = CAIRO_STATUS_SURFACE_TYPE_MISMATCH; + return status; + } + cairo_surface_flush(surface); + + if (width == 0 || height == 0) { + status = CAIRO_STATUS_WRITE_ERROR; + return status; + } + + rows = (png_bytep *) malloc(height * sizeof (png_byte*)); + if (unlikely(rows == NULL)) { + status = CAIRO_STATUS_NO_MEMORY; + return status; + } + + for (i = 0; i < height; i++) { + rows[i] = (png_byte *) data + i * cairo_image_surface_get_stride(surface); + } + +#ifdef PNG_USER_MEM_SUPPORTED + png = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, NULL, NULL, NULL); +#else + png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); +#endif + + if (unlikely(png == NULL)) { + status = CAIRO_STATUS_NO_MEMORY; + free(rows); + return status; + } + + info = png_create_info_struct (png); + if (unlikely(info == NULL)) { + status = CAIRO_STATUS_NO_MEMORY; + png_destroy_write_struct(&png, &info); + free(rows); + return status; + + } + +#ifdef PNG_SETJMP_SUPPORTED + if (setjmp (png_jmpbuf (png))) { + png_destroy_write_struct(&png, &info); + free(rows); + return status; + } +#endif + + png_set_write_fn(png, closure, write_func, canvas_png_flush); + png_set_compression_level(png, ((closure_t *) ((canvas_png_write_closure_t *) closure)->closure)->compression_level); + png_set_filter(png, 0, ((closure_t *) ((canvas_png_write_closure_t *) closure)->closure)->filter); + + switch (cairo_image_surface_get_format(surface)) { + case CAIRO_FORMAT_ARGB32: + bpc = 8; + png_color_type = PNG_COLOR_TYPE_RGB_ALPHA; + break; + case CAIRO_FORMAT_RGB30: + bpc = 10; + png_color_type = PNG_COLOR_TYPE_RGB; + break; + case CAIRO_FORMAT_RGB24: + bpc = 8; + png_color_type = PNG_COLOR_TYPE_RGB; + break; + case CAIRO_FORMAT_A8: + bpc = 8; + png_color_type = PNG_COLOR_TYPE_GRAY; + break; + case CAIRO_FORMAT_A1: + bpc = 1; + png_color_type = PNG_COLOR_TYPE_GRAY; +#ifndef WORDS_BIGENDIAN + png_set_packswap(png); +#endif + break; + case CAIRO_FORMAT_INVALID: + case CAIRO_FORMAT_RGB16_565: + default: + status = CAIRO_STATUS_INVALID_FORMAT; + png_destroy_write_struct(&png, &info); + free(rows); + return status; + } + + png_set_IHDR(png, info, width, height, bpc, png_color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + white.gray = (1 << bpc) - 1; + white.red = white.blue = white.green = white.gray; + png_set_bKGD(png, info, &white); + + /* We have to call png_write_info() before setting up the write + * transformation, since it stores data internally in 'png' + * that is needed for the write transformation functions to work. + */ + png_write_info(png, info); + if (png_color_type == PNG_COLOR_TYPE_RGB_ALPHA) { + png_set_write_user_transform_fn(png, canvas_unpremultiply_data); + } else if (png_color_type == PNG_COLOR_TYPE_RGB) { + png_set_write_user_transform_fn(png, canvas_convert_data_to_bytes); + png_set_filler(png, 0, PNG_FILLER_AFTER); + } + + png_write_image(png, rows); + png_write_end(png, info); + + png_destroy_write_struct(&png, &info); + free(rows); + return status; +} + +static void canvas_stream_write_func(png_structp png, png_bytep data, png_size_t size) { + cairo_status_t status; + struct canvas_png_write_closure_t *png_closure; + + png_closure = (struct canvas_png_write_closure_t *) png_get_io_ptr(png); + status = png_closure->write_func(png_closure->closure, data, size); + if (unlikely(status)) { + cairo_status_t *error = (cairo_status_t *) png_get_error_ptr(png); + if (*error == CAIRO_STATUS_SUCCESS) { + *error = status; + } + png_error(png, NULL); + } +} + +static cairo_status_t canvas_write_to_png_stream(cairo_surface_t *surface, cairo_write_func_t write_func, void *closure) { + struct canvas_png_write_closure_t png_closure; + + if (cairo_surface_status(surface)) { + return cairo_surface_status(surface); + } + + png_closure.write_func = write_func; + png_closure.closure = closure; + + return canvas_write_png(surface, canvas_stream_write_func, &png_closure); +} +#endif diff --git a/src/closure.h b/src/closure.h index 49ec8e267..1b3147bb6 100644 --- a/src/closure.h +++ b/src/closure.h @@ -8,6 +8,14 @@ #ifndef __NODE_CLOSURE_H__ #define __NODE_CLOSURE_H__ +#ifdef __unix__ + #include +#endif + +#ifndef PAGE_SIZE + #define PAGE_SIZE 4096 +#endif + #include "nan.h" /* @@ -22,6 +30,8 @@ typedef struct { uint8_t *data; Canvas *canvas; cairo_status_t status; + uint32_t compression_level; + uint32_t filter; } closure_t; /* @@ -29,11 +39,13 @@ typedef struct { */ cairo_status_t -closure_init(closure_t *closure, Canvas *canvas) { +closure_init(closure_t *closure, Canvas *canvas, unsigned int compression_level, unsigned int filter) { closure->len = 0; closure->canvas = canvas; - closure->data = (uint8_t *) malloc(closure->max_len = 1024); + closure->data = (uint8_t *) malloc(closure->max_len = PAGE_SIZE); if (!closure->data) return CAIRO_STATUS_NO_MEMORY; + closure->compression_level = compression_level; + closure->filter = filter; return CAIRO_STATUS_SUCCESS; } diff --git a/src/nan.h b/src/nan.h index ead7d658d..411f4c202 100644 --- a/src/nan.h +++ b/src/nan.h @@ -1,20 +1,41 @@ /********************************************************************************** * NAN - Native Abstractions for Node.js * - * Copyright (c) 2013 Rod Vagg + * Copyright (c) 2013 NAN contributors: + * - Rod Vagg + * - Benjamin Byholm + * - Trevor Norris + * * MIT +no-false-attribs License * - * Version 0.1.0 (current Node unstable: 0.11.4) + * Version 0.2.0-wip (current Node unstable: 0.11.4) + * + * ChangeLog: + * * 0.2.0 .... work in progress + * - Added NAN_PROPERTY_GETTER, NAN_PROPERTY_SETTER, NAN_PROPERTY_ENUMERATOR, + * NAN_PROPERTY_DELETER, NAN_PROPERTY_QUERY + * - Extracted _NAN_METHOD_ARGS, _NAN_GETTER_ARGS, _NAN_SETTER_ARGS, + * _NAN_PROPERTY_GETTER_ARGS, _NAN_PROPERTY_SETTER_ARGS, + * _NAN_PROPERTY_ENUMERATOR_ARGS, _NAN_PROPERTY_DELETER_ARGS, + * _NAN_PROPERTY_QUERY_ARGS + * - Added NanGetInternalFieldPointer, NanSetInternalFieldPointer + * - Added NAN_WEAK_CALLBACK, NAN_WEAK_CALLBACK_OBJECT, + * NAN_WEAK_CALLBACK_DATA, NanMakeWeak + * - Renamed THROW_ERROR to _NAN_THROW_ERROR + * - Added NanNewBufferHandle(char*, size_t, node::smalloc::FreeCallback, void*) + * - Added NanBufferUse(char*, uint32_t) + * - Added NanNewContextHandle(v8::ExtensionConfiguration*, + * v8::Handle, v8::Handle) + * - Fixed broken NanCallback#GetFunction() * - * Changelog: * * 0.1.0 Jul 21 2013 - * - Added `NAN_GETTER`, `NAN_SETTER` - * - Added `NanThrowError` with single Local argument - * - Added `NanNewBufferHandle` with single uint32_t argument - * - Added `NanHasInstance(Persistent&, Handle)` - * - Added `Local NanCallback#GetFunction()` - * - Added `NanCallback#Call(int, Local[])` - * - Deprecated `NanCallback#Run(int, Local[])` in favour of Call + * - Added `NAN_GETTER`, `NAN_SETTER` + * - Added `NanThrowError` with single Local argument + * - Added `NanNewBufferHandle` with single uint32_t argument + * - Added `NanHasInstance(Persistent&, Handle)` + * - Added `Local NanCallback#GetFunction()` + * - Added `NanCallback#Call(int, Local[])` + * - Deprecated `NanCallback#Run(int, Local[])` in favour of Call * * See https://github.com/rvagg/nan for the latest update to this file **********************************************************************************/ @@ -77,32 +98,71 @@ static inline uint32_t NanUInt32OptionValue( static v8::Isolate* nan_isolate = v8::Isolate::GetCurrent(); -# define NAN_METHOD(name) \ - void name(const v8::FunctionCallbackInfo& args) +# define _NAN_METHOD_ARGS const v8::FunctionCallbackInfo& args +# define NAN_METHOD(name) void name(_NAN_METHOD_ARGS) +# define _NAN_GETTER_ARGS const v8::PropertyCallbackInfo& args # define NAN_GETTER(name) \ - void name( \ - v8::Local property \ - , const v8::PropertyCallbackInfo& args) + void name(v8::Local property, _NAN_GETTER_ARGS) +# define _NAN_SETTER_ARGS const v8::PropertyCallbackInfo& args # define NAN_SETTER(name) \ void name( \ v8::Local property \ , v8::Local value \ - , const v8::PropertyCallbackInfo& args) + , _NAN_SETTER_ARGS) +# define _NAN_PROPERTY_GETTER_ARGS \ + const v8::PropertyCallbackInfo& args +# define NAN_PROPERTY_GETTER(name) \ + void name(v8::Local property \ + , _NAN_PROPERTY_GETTER_ARGS) +# define _NAN_PROPERTY_SETTER_ARGS \ + const v8::PropertyCallbackInfo& args +# define NAN_PROPERTY_SETTER(name) \ + void name(v8::Local property \ + , v8::Local value \ + , _NAN_PROPERTY_SETTER_ARGS) +# define _NAN_PROPERTY_ENUMERATOR_ARGS \ + const v8::PropertyCallbackInfo& args +# define NAN_PROPERTY_ENUMERATOR(name) \ + void name(_NAN_PROPERTY_ENUMERATOR_ARGS) +# define _NAN_PROPERTY_DELETER_ARGS \ + const v8::PropertyCallbackInfo& args +# define NAN_PROPERTY_DELETER(name) \ + void name( \ + v8::Local property \ + , _NAN_PROPERTY_DELETER_ARGS) +# define _NAN_PROPERTY_QUERY_ARGS \ + const v8::PropertyCallbackInfo& args +# define NAN_PROPERTY_QUERY(name) \ + void name(v8::Local property, _NAN_PROPERTY_QUERY_ARGS) +# define NanGetInternalFieldPointer(object, index) \ + object->GetAlignedPointerFromInternalField(index) +# define NanSetInternalFieldPointer(object, index, value) \ + object->SetAlignedPointerInInternalField(index, value) + +# define NAN_WEAK_CALLBACK(type, name) \ + void name( \ + v8::Isolate* isolate, \ + v8::Persistent* object, \ + type data) +# define NAN_WEAK_CALLBACK_OBJECT (*object) +# define NAN_WEAK_CALLBACK_DATA(type) ((type) data) # define NanScope() v8::HandleScope scope(nan_isolate) -# define NanReturnValue(value) return args.GetReturnValue().Set(value); -# define NanReturnUndefined() return; -# define NanAssignPersistent(type, handle, obj) handle.Reset(nan_isolate, obj); +# define NanReturnValue(value) return args.GetReturnValue().Set(value) +# define NanReturnUndefined() return +# define NanAssignPersistent(type, handle, obj) handle.Reset(nan_isolate, obj) # define NanObjectWrapHandle(obj) obj->handle() +# define NanMakeWeak(handle, parameter, callback) \ + handle.MakeWeak(nan_isolate, parameter, callback) -# define THROW_ERROR(fun, errmsg) \ +# define _NAN_THROW_ERROR(fun, errmsg) \ do { \ NanScope(); \ v8::ThrowException(fun(v8::String::New(errmsg))); \ } while (0); inline static void NanThrowError(const char* errmsg) { - THROW_ERROR(v8::Exception::Error, errmsg); + _NAN_THROW_ERROR(v8::Exception::Error, errmsg); } inline static void NanThrowError(v8::Local error) { @@ -111,17 +171,25 @@ static v8::Isolate* nan_isolate = v8::Isolate::GetCurrent(); } inline static void NanThrowTypeError(const char* errmsg) { - THROW_ERROR(v8::Exception::TypeError, errmsg); + _NAN_THROW_ERROR(v8::Exception::TypeError, errmsg); } inline static void NanThrowRangeError(const char* errmsg) { - THROW_ERROR(v8::Exception::RangeError, errmsg); + _NAN_THROW_ERROR(v8::Exception::RangeError, errmsg); } - static inline void NanDispose(v8::Persistent &handle) { + template static inline void NanDispose(v8::Persistent &handle) { handle.Dispose(nan_isolate); } + static inline v8::Local NanNewBufferHandle ( + char *data, + size_t length, + node::smalloc::FreeCallback callback, + void *hint) { + return node::Buffer::New(data, length, callback, hint); + } + static inline v8::Local NanNewBufferHandle ( char *data, uint32_t size) { return node::Buffer::New(data, size); @@ -131,6 +199,10 @@ static v8::Isolate* nan_isolate = v8::Isolate::GetCurrent(); return node::Buffer::New(size); } + static inline v8::Local NanBufferUse(char* data, uint32_t size) { + return node::Buffer::Use(data, size); + } + template inline v8::Local NanPersistentToLocal( const v8::Persistent& persistent) { @@ -148,36 +220,78 @@ static v8::Isolate* nan_isolate = v8::Isolate::GetCurrent(); return NanPersistentToLocal(function_template)->HasInstance(value); } + static inline v8::Local NanNewContextHandle( + v8::ExtensionConfiguration* extensions = NULL, + v8::Handle g_template = v8::Handle(), + v8::Handle g_object = v8::Handle()) { + return v8::Local::New(nan_isolate, v8::Context::New( + nan_isolate, extensions, g_template, g_object)); + } + #else // Node 0.8 and 0.10 -# define NAN_METHOD(name) \ - v8::Handle name(const v8::Arguments& args) +# define _NAN_METHOD_ARGS const v8::Arguments& args +# define NAN_METHOD(name) v8::Handle name(_NAN_METHOD_ARGS) +# define _NAN_GETTER_ARGS const v8::AccessorInfo &args # define NAN_GETTER(name) \ - v8::Handle name( \ - v8::Local property \ - , const v8::AccessorInfo &args) + v8::Handle name(v8::Local property, _NAN_GETTER_ARGS) +# define _NAN_SETTER_ARGS const v8::AccessorInfo &args # define NAN_SETTER(name) \ void name( \ - v8::Local property \ - , v8::Local value \ - , const v8::AccessorInfo &args) + v8::Local property \ + , v8::Local value \ + , _NAN_SETTER_ARGS) +# define _NAN_PROPERTY_GETTER_ARGS const v8::AccessorInfo& args +# define NAN_PROPERTY_GETTER(name) \ + v8::Handle name(v8::Local property \ + , _NAN_PROPERTY_GETTER_ARGS) +# define _NAN_PROPERTY_SETTER_ARGS const v8::AccessorInfo& args +# define NAN_PROPERTY_SETTER(name) \ + v8::Handle name(v8::Local property \ + , v8::Local value \ + , _NAN_PROPERTY_SETTER_ARGS) +# define _NAN_PROPERTY_ENUMERATOR_ARGS const v8::AccessorInfo& args +# define NAN_PROPERTY_ENUMERATOR(name) \ + v8::Handle name(_NAN_PROPERTY_ENUMERATOR_ARGS) +# define _NAN_PROPERTY_DELETER_ARGS const v8::AccessorInfo& args +# define NAN_PROPERTY_DELETER(name) \ + v8::Handle name( \ + v8::Local property \ + , _NAN_PROPERTY_DELETER_ARGS) +# define _NAN_PROPERTY_QUERY_ARGS const v8::AccessorInfo& args +# define NAN_PROPERTY_QUERY(name) \ + v8::Handle name( \ + v8::Local property \ + , _NAN_PROPERTY_QUERY_ARGS) + +# define NanGetInternalFieldPointer(object, index) \ + object->GetPointerFromInternalField(index) +# define NanSetInternalFieldPointer(object, index, value) \ + object->SetPointerInInternalField(index, value) +# define NAN_WEAK_CALLBACK(type, name) void name( \ + v8::Persistent object, \ + void *data) +# define NAN_WEAK_CALLBACK_OBJECT object +# define NAN_WEAK_CALLBACK_DATA(type) ((type) data) # define NanScope() v8::HandleScope scope -# define NanReturnValue(value) return scope.Close(value); -# define NanReturnUndefined() return v8::Undefined(); +# define NanReturnValue(value) return scope.Close(value) +# define NanReturnUndefined() return v8::Undefined() # define NanAssignPersistent(type, handle, obj) \ - handle = v8::Persistent::New(obj); + handle = v8::Persistent::New(obj) # define NanObjectWrapHandle(obj) obj->handle_ +# define NanMakeWeak(handle, parameters, callback) \ + handle.MakeWeak(parameters, callback) -# define THROW_ERROR(fun, errmsg) \ +# define _NAN_THROW_ERROR(fun, errmsg) \ do { \ NanScope(); \ return v8::ThrowException(fun(v8::String::New(errmsg))); \ } while (0); inline static v8::Handle NanThrowError(const char* errmsg) { - THROW_ERROR(v8::Exception::Error, errmsg); + _NAN_THROW_ERROR(v8::Exception::Error, errmsg); } inline static v8::Handle NanThrowError( @@ -187,22 +301,44 @@ static v8::Isolate* nan_isolate = v8::Isolate::GetCurrent(); } inline static v8::Handle NanThrowTypeError(const char* errmsg) { - THROW_ERROR(v8::Exception::TypeError, errmsg); + _NAN_THROW_ERROR(v8::Exception::TypeError, errmsg); } inline static v8::Handle NanThrowRangeError(const char* errmsg) { - THROW_ERROR(v8::Exception::RangeError, errmsg); + _NAN_THROW_ERROR(v8::Exception::RangeError, errmsg); } - static inline void NanDispose(v8::Persistent &handle) { + template static inline void NanDispose(v8::Persistent &handle) { handle.Dispose(); } + static inline v8::Local NanNewBufferHandle ( + char *data, + size_t length, + node::Buffer::free_callback callback, + void *hint) { + return v8::Local::New( + node::Buffer::New(data, length, callback, hint)->handle_); + } + static inline v8::Local NanNewBufferHandle ( char *data, uint32_t size) { return v8::Local::New(node::Buffer::New(data, size)->handle_); } + static inline v8::Local NanNewBufferHandle (uint32_t size) { + return v8::Local::New(node::Buffer::New(size)->handle_); + } + + static inline void FreeData(char *data, void *hint) { + delete[] data; + } + + static inline v8::Local NanBufferUse(char* data, uint32_t size) { + return v8::Local::New( + node::Buffer::New(data, size, FreeData, NULL)->handle_); + } + template inline v8::Local NanPersistentToLocal( const v8::Persistent& persistent) { @@ -220,6 +356,19 @@ static v8::Isolate* nan_isolate = v8::Isolate::GetCurrent(); return function_template->HasInstance(value); } + static inline v8::Local NanNewContextHandle( + v8::ExtensionConfiguration* extensions = NULL + , v8::Handle g_template = + v8::Handle() + , v8::Handle g_object = v8::Handle() + ) { + v8::Persistent ctx = + v8::Context::New(extensions, g_template, g_object); + v8::Local lctx = v8::Local::New(ctx); + ctx.Dispose(); + return lctx; + } + #endif // node version class NanCallback { @@ -236,9 +385,9 @@ class NanCallback { handle.Dispose(); } - v8::Local GetFunction () { - NanScope(); - return NanPersistentToLocal(handle).As(); + inline v8::Local GetFunction () { + return NanPersistentToLocal(handle)->Get(NanSymbol("callback")) + .As(); } // deprecated