diff --git a/src/Canvas.cc b/src/Canvas.cc index 02601cf4d..db3321ff1 100644 --- a/src/Canvas.cc +++ b/src/Canvas.cc @@ -13,6 +13,7 @@ #include #include +#include "Util.h" #include "Canvas.h" #include "PNG.h" #include "CanvasRenderingContext2d.h" @@ -57,10 +58,10 @@ Canvas::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) { #ifdef HAVE_JPEG Nan::SetPrototypeMethod(ctor, "streamJPEGSync", StreamJPEGSync); #endif - Nan::SetAccessor(proto, Nan::New("type").ToLocalChecked(), GetType); - Nan::SetAccessor(proto, Nan::New("stride").ToLocalChecked(), GetStride); - Nan::SetAccessor(proto, Nan::New("width").ToLocalChecked(), GetWidth, SetWidth); - Nan::SetAccessor(proto, Nan::New("height").ToLocalChecked(), GetHeight, SetHeight); + SetProtoAccessor(proto, Nan::New("type").ToLocalChecked(), GetType, NULL, ctor); + SetProtoAccessor(proto, Nan::New("stride").ToLocalChecked(), GetStride, NULL, ctor); + SetProtoAccessor(proto, Nan::New("width").ToLocalChecked(), GetWidth, SetWidth, ctor); + SetProtoAccessor(proto, Nan::New("height").ToLocalChecked(), GetHeight, SetHeight, ctor); Nan::SetTemplate(proto, "PNG_NO_FILTERS", Nan::New(PNG_NO_FILTERS)); Nan::SetTemplate(proto, "PNG_FILTER_NONE", Nan::New(PNG_FILTER_NONE)); diff --git a/src/CanvasRenderingContext2d.cc b/src/CanvasRenderingContext2d.cc index ecfb6f5f9..20a3aa6f4 100644 --- a/src/CanvasRenderingContext2d.cc +++ b/src/CanvasRenderingContext2d.cc @@ -12,6 +12,7 @@ #include #include +#include "Util.h" #include "Canvas.h" #include "Point.h" #include "Image.h" @@ -127,24 +128,24 @@ Context2d::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) { Nan::SetPrototypeMethod(ctor, "_setTextBaseline", SetTextBaseline); Nan::SetPrototypeMethod(ctor, "_setTextAlignment", SetTextAlignment); Nan::SetPrototypeMethod(ctor, "_getMatrix", GetMatrix); - Nan::SetAccessor(proto, Nan::New("pixelFormat").ToLocalChecked(), GetFormat); - Nan::SetAccessor(proto, Nan::New("patternQuality").ToLocalChecked(), GetPatternQuality, SetPatternQuality); - Nan::SetAccessor(proto, Nan::New("globalCompositeOperation").ToLocalChecked(), GetGlobalCompositeOperation, SetGlobalCompositeOperation); - Nan::SetAccessor(proto, Nan::New("globalAlpha").ToLocalChecked(), GetGlobalAlpha, SetGlobalAlpha); - Nan::SetAccessor(proto, Nan::New("shadowColor").ToLocalChecked(), GetShadowColor, SetShadowColor); - Nan::SetAccessor(proto, Nan::New("fillColor").ToLocalChecked(), GetFillColor); - Nan::SetAccessor(proto, Nan::New("strokeColor").ToLocalChecked(), GetStrokeColor); - Nan::SetAccessor(proto, Nan::New("miterLimit").ToLocalChecked(), GetMiterLimit, SetMiterLimit); - Nan::SetAccessor(proto, Nan::New("lineWidth").ToLocalChecked(), GetLineWidth, SetLineWidth); - Nan::SetAccessor(proto, Nan::New("lineCap").ToLocalChecked(), GetLineCap, SetLineCap); - Nan::SetAccessor(proto, Nan::New("lineJoin").ToLocalChecked(), GetLineJoin, SetLineJoin); - Nan::SetAccessor(proto, Nan::New("lineDashOffset").ToLocalChecked(), GetLineDashOffset, SetLineDashOffset); - Nan::SetAccessor(proto, Nan::New("shadowOffsetX").ToLocalChecked(), GetShadowOffsetX, SetShadowOffsetX); - Nan::SetAccessor(proto, Nan::New("shadowOffsetY").ToLocalChecked(), GetShadowOffsetY, SetShadowOffsetY); - Nan::SetAccessor(proto, Nan::New("shadowBlur").ToLocalChecked(), GetShadowBlur, SetShadowBlur); - Nan::SetAccessor(proto, Nan::New("antialias").ToLocalChecked(), GetAntiAlias, SetAntiAlias); - Nan::SetAccessor(proto, Nan::New("textDrawingMode").ToLocalChecked(), GetTextDrawingMode, SetTextDrawingMode); - Nan::SetAccessor(proto, Nan::New("filter").ToLocalChecked(), GetFilter, SetFilter); + SetProtoAccessor(proto, Nan::New("pixelFormat").ToLocalChecked(), GetFormat, NULL, ctor); + SetProtoAccessor(proto, Nan::New("patternQuality").ToLocalChecked(), GetPatternQuality, SetPatternQuality, ctor); + SetProtoAccessor(proto, Nan::New("globalCompositeOperation").ToLocalChecked(), GetGlobalCompositeOperation, SetGlobalCompositeOperation, ctor); + SetProtoAccessor(proto, Nan::New("globalAlpha").ToLocalChecked(), GetGlobalAlpha, SetGlobalAlpha, ctor); + SetProtoAccessor(proto, Nan::New("shadowColor").ToLocalChecked(), GetShadowColor, SetShadowColor, ctor); + SetProtoAccessor(proto, Nan::New("fillColor").ToLocalChecked(), GetFillColor, NULL, ctor); + SetProtoAccessor(proto, Nan::New("strokeColor").ToLocalChecked(), GetStrokeColor, NULL, ctor); + SetProtoAccessor(proto, Nan::New("miterLimit").ToLocalChecked(), GetMiterLimit, SetMiterLimit, ctor); + SetProtoAccessor(proto, Nan::New("lineWidth").ToLocalChecked(), GetLineWidth, SetLineWidth, ctor); + SetProtoAccessor(proto, Nan::New("lineCap").ToLocalChecked(), GetLineCap, SetLineCap, ctor); + SetProtoAccessor(proto, Nan::New("lineJoin").ToLocalChecked(), GetLineJoin, SetLineJoin, ctor); + SetProtoAccessor(proto, Nan::New("lineDashOffset").ToLocalChecked(), GetLineDashOffset, SetLineDashOffset, ctor); + SetProtoAccessor(proto, Nan::New("shadowOffsetX").ToLocalChecked(), GetShadowOffsetX, SetShadowOffsetX, ctor); + SetProtoAccessor(proto, Nan::New("shadowOffsetY").ToLocalChecked(), GetShadowOffsetY, SetShadowOffsetY, ctor); + SetProtoAccessor(proto, Nan::New("shadowBlur").ToLocalChecked(), GetShadowBlur, SetShadowBlur, ctor); + SetProtoAccessor(proto, Nan::New("antialias").ToLocalChecked(), GetAntiAlias, SetAntiAlias, ctor); + SetProtoAccessor(proto, Nan::New("textDrawingMode").ToLocalChecked(), GetTextDrawingMode, SetTextDrawingMode, ctor); + SetProtoAccessor(proto, Nan::New("filter").ToLocalChecked(), GetFilter, SetFilter, ctor); Nan::Set(target, Nan::New("CanvasRenderingContext2d").ToLocalChecked(), ctor->GetFunction()); } diff --git a/src/Image.cc b/src/Image.cc index 73bfcc77e..8e0dbff19 100644 --- a/src/Image.cc +++ b/src/Image.cc @@ -4,6 +4,7 @@ // Copyright (c) 2010 LearnBoost // +#include "Util.h" #include "Canvas.h" #include "Image.h" #include @@ -45,16 +46,16 @@ Image::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) { // Prototype Local proto = ctor->PrototypeTemplate(); - Nan::SetAccessor(proto, Nan::New("source").ToLocalChecked(), GetSource, SetSource); - Nan::SetAccessor(proto, Nan::New("complete").ToLocalChecked(), GetComplete); - Nan::SetAccessor(proto, Nan::New("width").ToLocalChecked(), GetWidth, SetWidth); - Nan::SetAccessor(proto, Nan::New("height").ToLocalChecked(), GetHeight, SetHeight); - Nan::SetAccessor(proto, Nan::New("naturalWidth").ToLocalChecked(), GetNaturalWidth); - Nan::SetAccessor(proto, Nan::New("naturalHeight").ToLocalChecked(), GetNaturalHeight); - Nan::SetAccessor(proto, Nan::New("onload").ToLocalChecked(), GetOnload, SetOnload); - Nan::SetAccessor(proto, Nan::New("onerror").ToLocalChecked(), GetOnerror, SetOnerror); + SetProtoAccessor(proto, Nan::New("source").ToLocalChecked(), GetSource, SetSource, ctor); + SetProtoAccessor(proto, Nan::New("complete").ToLocalChecked(), GetComplete, NULL, ctor); + SetProtoAccessor(proto, Nan::New("width").ToLocalChecked(), GetWidth, NULL, ctor); + SetProtoAccessor(proto, Nan::New("height").ToLocalChecked(), GetHeight, NULL, ctor); + SetProtoAccessor(proto, Nan::New("naturalWidth").ToLocalChecked(), GetNaturalWidth, NULL, ctor); + SetProtoAccessor(proto, Nan::New("naturalHeight").ToLocalChecked(), GetNaturalHeight, NULL, ctor); + SetProtoAccessor(proto, Nan::New("onload").ToLocalChecked(), GetOnload, SetOnload, ctor); + SetProtoAccessor(proto, Nan::New("onerror").ToLocalChecked(), GetOnerror, SetOnerror, ctor); #if CAIRO_VERSION_MINOR >= 10 - Nan::SetAccessor(proto, Nan::New("dataMode").ToLocalChecked(), GetDataMode, SetDataMode); + SetProtoAccessor(proto, Nan::New("dataMode").ToLocalChecked(), GetDataMode, SetDataMode, ctor); ctor->Set(Nan::New("MODE_IMAGE").ToLocalChecked(), Nan::New(DATA_IMAGE)); ctor->Set(Nan::New("MODE_MIME").ToLocalChecked(), Nan::New(DATA_MIME)); #endif diff --git a/src/ImageData.cc b/src/ImageData.cc index 1d47a01ed..e8e030ab5 100644 --- a/src/ImageData.cc +++ b/src/ImageData.cc @@ -5,6 +5,7 @@ // Copyright (c) 2010 LearnBoost // +#include "Util.h" #include "ImageData.h" Nan::Persistent ImageData::constructor; @@ -25,8 +26,8 @@ ImageData::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) { // Prototype Local proto = ctor->PrototypeTemplate(); - Nan::SetAccessor(proto, Nan::New("width").ToLocalChecked(), GetWidth); - Nan::SetAccessor(proto, Nan::New("height").ToLocalChecked(), GetHeight); + SetProtoAccessor(proto, Nan::New("width").ToLocalChecked(), GetWidth, NULL, ctor); + SetProtoAccessor(proto, Nan::New("height").ToLocalChecked(), GetHeight, NULL, ctor); Nan::Set(target, Nan::New("ImageData").ToLocalChecked(), ctor->GetFunction()); } diff --git a/src/Util.h b/src/Util.h new file mode 100644 index 000000000..f0c703516 --- /dev/null +++ b/src/Util.h @@ -0,0 +1,25 @@ +#include +#include + +// Wrapper around Nan::SetAccessor that makes it easier to change the last +// argument (signature). Getters/setters must be accessed only when there is +// actually an instance, i.e. MyClass.prototype.getter1 should not try to +// unwrap the non-existent 'this'. See #803, #847, #885, nodejs/node#15099, ... +inline void SetProtoAccessor( + v8::Local tpl, + v8::Local name, + Nan::GetterCallback getter, + Nan::SetterCallback setter, + v8::Local ctor + ) { + Nan::SetAccessor( + tpl, + name, + getter, + setter, + v8::Local(), + v8::DEFAULT, + v8::None, + v8::AccessorSignature::New(v8::Isolate::GetCurrent(), ctor) + ); +} diff --git a/test/canvas.test.js b/test/canvas.test.js index 1500822fb..2b2fc58f8 100644 --- a/test/canvas.test.js +++ b/test/canvas.test.js @@ -17,6 +17,15 @@ const os = require('os') const Readable = require('stream').Readable describe('Canvas', function () { + it('Prototype and ctor are well-shaped, don\'t hit asserts on accessors (GH-803)', function () { + const Canvas = require('../').Canvas; + var c = new Canvas(10, 10); + assert.throws(function () { Canvas.prototype.width; }, /incompatible receiver/); + assert(!c.hasOwnProperty('width')); + assert('width' in c); + assert(Canvas.prototype.hasOwnProperty('width')); + }); + it('.parseFont()', function () { var tests = [ '20px Arial' diff --git a/test/image.test.js b/test/image.test.js index ae0fd5eac..875b8eb83 100644 --- a/test/image.test.js +++ b/test/image.test.js @@ -7,6 +7,7 @@ */ const loadImage = require('../').loadImage +const Image = require('../').Image; const assert = require('assert') const assertRejects = require('assert-rejects') @@ -16,6 +17,14 @@ const png_clock = `${__dirname}/fixtures/clock.png` const jpg_face = `${__dirname}/fixtures/face.jpeg` describe('Image', function () { + it('Prototype and ctor are well-shaped, don\'t hit asserts on accessors (GH-803)', function () { + var img = new Image(); + assert.throws(function () { Image.prototype.width; }, /incompatible receiver/); + assert(!img.hasOwnProperty('width')); + assert('width' in img); + assert(Image.prototype.hasOwnProperty('width')); + }); + it('loads JPEG image', function () { return loadImage(jpg_face).then((img) => { assert.strictEqual(img.onerror, null) diff --git a/test/imageData.test.js b/test/imageData.test.js index 30e60bbcd..cfffe9aaf 100644 --- a/test/imageData.test.js +++ b/test/imageData.test.js @@ -3,10 +3,15 @@ 'use strict' const createImageData = require('../').createImageData +const ImageData = require('../').ImageData; const assert = require('assert') describe('ImageData', function () { + it('Prototype and ctor are well-shaped, don\'t hit asserts on accessors (GH-803)', function () { + assert.throws(function () { ImageData.prototype.width; }, /incompatible receiver/); + }); + it('should throw with invalid numeric arguments', function () { assert.throws(() => { createImageData(0, 0) }, /width is zero/) assert.throws(() => { createImageData(1, 0) }, /height is zero/)