Skip to content

Commit d4b6f67

Browse files
committed
Fix assertion failure from accessors
Accessors should not attempt to unwrap the non-existent 'this' when accessed via the prototype. Fixes #803 Fixes #847 Fixes #885 Fixes nodejs/node#15099
1 parent 74a5302 commit d4b6f67

8 files changed

+85
-33
lines changed

src/Canvas.cc

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <cairo-pdf.h>
1414
#include <cairo-svg.h>
1515

16+
#include "Util.h"
1617
#include "Canvas.h"
1718
#include "PNG.h"
1819
#include "CanvasRenderingContext2d.h"
@@ -57,10 +58,10 @@ Canvas::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
5758
#ifdef HAVE_JPEG
5859
Nan::SetPrototypeMethod(ctor, "streamJPEGSync", StreamJPEGSync);
5960
#endif
60-
Nan::SetAccessor(proto, Nan::New("type").ToLocalChecked(), GetType);
61-
Nan::SetAccessor(proto, Nan::New("stride").ToLocalChecked(), GetStride);
62-
Nan::SetAccessor(proto, Nan::New("width").ToLocalChecked(), GetWidth, SetWidth);
63-
Nan::SetAccessor(proto, Nan::New("height").ToLocalChecked(), GetHeight, SetHeight);
61+
SetProtoAccessor(proto, Nan::New("type").ToLocalChecked(), GetType, NULL, ctor);
62+
SetProtoAccessor(proto, Nan::New("stride").ToLocalChecked(), GetStride, NULL, ctor);
63+
SetProtoAccessor(proto, Nan::New("width").ToLocalChecked(), GetWidth, SetWidth, ctor);
64+
SetProtoAccessor(proto, Nan::New("height").ToLocalChecked(), GetHeight, SetHeight, ctor);
6465

6566
Nan::SetTemplate(proto, "PNG_NO_FILTERS", Nan::New<Uint32>(PNG_NO_FILTERS));
6667
Nan::SetTemplate(proto, "PNG_FILTER_NONE", Nan::New<Uint32>(PNG_FILTER_NONE));

src/CanvasRenderingContext2d.cc

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <algorithm>
1313
#include <string>
1414

15+
#include "Util.h"
1516
#include "Canvas.h"
1617
#include "Point.h"
1718
#include "Image.h"
@@ -127,24 +128,24 @@ Context2d::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
127128
Nan::SetPrototypeMethod(ctor, "_setTextBaseline", SetTextBaseline);
128129
Nan::SetPrototypeMethod(ctor, "_setTextAlignment", SetTextAlignment);
129130
Nan::SetPrototypeMethod(ctor, "_getMatrix", GetMatrix);
130-
Nan::SetAccessor(proto, Nan::New("pixelFormat").ToLocalChecked(), GetFormat);
131-
Nan::SetAccessor(proto, Nan::New("patternQuality").ToLocalChecked(), GetPatternQuality, SetPatternQuality);
132-
Nan::SetAccessor(proto, Nan::New("globalCompositeOperation").ToLocalChecked(), GetGlobalCompositeOperation, SetGlobalCompositeOperation);
133-
Nan::SetAccessor(proto, Nan::New("globalAlpha").ToLocalChecked(), GetGlobalAlpha, SetGlobalAlpha);
134-
Nan::SetAccessor(proto, Nan::New("shadowColor").ToLocalChecked(), GetShadowColor, SetShadowColor);
135-
Nan::SetAccessor(proto, Nan::New("fillColor").ToLocalChecked(), GetFillColor);
136-
Nan::SetAccessor(proto, Nan::New("strokeColor").ToLocalChecked(), GetStrokeColor);
137-
Nan::SetAccessor(proto, Nan::New("miterLimit").ToLocalChecked(), GetMiterLimit, SetMiterLimit);
138-
Nan::SetAccessor(proto, Nan::New("lineWidth").ToLocalChecked(), GetLineWidth, SetLineWidth);
139-
Nan::SetAccessor(proto, Nan::New("lineCap").ToLocalChecked(), GetLineCap, SetLineCap);
140-
Nan::SetAccessor(proto, Nan::New("lineJoin").ToLocalChecked(), GetLineJoin, SetLineJoin);
141-
Nan::SetAccessor(proto, Nan::New("lineDashOffset").ToLocalChecked(), GetLineDashOffset, SetLineDashOffset);
142-
Nan::SetAccessor(proto, Nan::New("shadowOffsetX").ToLocalChecked(), GetShadowOffsetX, SetShadowOffsetX);
143-
Nan::SetAccessor(proto, Nan::New("shadowOffsetY").ToLocalChecked(), GetShadowOffsetY, SetShadowOffsetY);
144-
Nan::SetAccessor(proto, Nan::New("shadowBlur").ToLocalChecked(), GetShadowBlur, SetShadowBlur);
145-
Nan::SetAccessor(proto, Nan::New("antialias").ToLocalChecked(), GetAntiAlias, SetAntiAlias);
146-
Nan::SetAccessor(proto, Nan::New("textDrawingMode").ToLocalChecked(), GetTextDrawingMode, SetTextDrawingMode);
147-
Nan::SetAccessor(proto, Nan::New("filter").ToLocalChecked(), GetFilter, SetFilter);
131+
SetProtoAccessor(proto, Nan::New("pixelFormat").ToLocalChecked(), GetFormat, NULL, ctor);
132+
SetProtoAccessor(proto, Nan::New("patternQuality").ToLocalChecked(), GetPatternQuality, SetPatternQuality, ctor);
133+
SetProtoAccessor(proto, Nan::New("globalCompositeOperation").ToLocalChecked(), GetGlobalCompositeOperation, SetGlobalCompositeOperation, ctor);
134+
SetProtoAccessor(proto, Nan::New("globalAlpha").ToLocalChecked(), GetGlobalAlpha, SetGlobalAlpha, ctor);
135+
SetProtoAccessor(proto, Nan::New("shadowColor").ToLocalChecked(), GetShadowColor, SetShadowColor, ctor);
136+
SetProtoAccessor(proto, Nan::New("fillColor").ToLocalChecked(), GetFillColor, NULL, ctor);
137+
SetProtoAccessor(proto, Nan::New("strokeColor").ToLocalChecked(), GetStrokeColor, NULL, ctor);
138+
SetProtoAccessor(proto, Nan::New("miterLimit").ToLocalChecked(), GetMiterLimit, SetMiterLimit, ctor);
139+
SetProtoAccessor(proto, Nan::New("lineWidth").ToLocalChecked(), GetLineWidth, SetLineWidth, ctor);
140+
SetProtoAccessor(proto, Nan::New("lineCap").ToLocalChecked(), GetLineCap, SetLineCap, ctor);
141+
SetProtoAccessor(proto, Nan::New("lineJoin").ToLocalChecked(), GetLineJoin, SetLineJoin, ctor);
142+
SetProtoAccessor(proto, Nan::New("lineDashOffset").ToLocalChecked(), GetLineDashOffset, SetLineDashOffset, ctor);
143+
SetProtoAccessor(proto, Nan::New("shadowOffsetX").ToLocalChecked(), GetShadowOffsetX, SetShadowOffsetX, ctor);
144+
SetProtoAccessor(proto, Nan::New("shadowOffsetY").ToLocalChecked(), GetShadowOffsetY, SetShadowOffsetY, ctor);
145+
SetProtoAccessor(proto, Nan::New("shadowBlur").ToLocalChecked(), GetShadowBlur, SetShadowBlur, ctor);
146+
SetProtoAccessor(proto, Nan::New("antialias").ToLocalChecked(), GetAntiAlias, SetAntiAlias, ctor);
147+
SetProtoAccessor(proto, Nan::New("textDrawingMode").ToLocalChecked(), GetTextDrawingMode, SetTextDrawingMode, ctor);
148+
SetProtoAccessor(proto, Nan::New("filter").ToLocalChecked(), GetFilter, SetFilter, ctor);
148149
Nan::Set(target, Nan::New("CanvasRenderingContext2d").ToLocalChecked(), ctor->GetFunction());
149150
}
150151

src/Image.cc

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// Copyright (c) 2010 LearnBoost <[email protected]>
55
//
66

7+
#include "Util.h"
78
#include "Canvas.h"
89
#include "Image.h"
910
#include <stdlib.h>
@@ -45,16 +46,16 @@ Image::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
4546

4647
// Prototype
4748
Local<ObjectTemplate> proto = ctor->PrototypeTemplate();
48-
Nan::SetAccessor(proto, Nan::New("source").ToLocalChecked(), GetSource, SetSource);
49-
Nan::SetAccessor(proto, Nan::New("complete").ToLocalChecked(), GetComplete);
50-
Nan::SetAccessor(proto, Nan::New("width").ToLocalChecked(), GetWidth, SetWidth);
51-
Nan::SetAccessor(proto, Nan::New("height").ToLocalChecked(), GetHeight, SetHeight);
52-
Nan::SetAccessor(proto, Nan::New("naturalWidth").ToLocalChecked(), GetNaturalWidth);
53-
Nan::SetAccessor(proto, Nan::New("naturalHeight").ToLocalChecked(), GetNaturalHeight);
54-
Nan::SetAccessor(proto, Nan::New("onload").ToLocalChecked(), GetOnload, SetOnload);
55-
Nan::SetAccessor(proto, Nan::New("onerror").ToLocalChecked(), GetOnerror, SetOnerror);
49+
SetProtoAccessor(proto, Nan::New("source").ToLocalChecked(), GetSource, SetSource, ctor);
50+
SetProtoAccessor(proto, Nan::New("complete").ToLocalChecked(), GetComplete, NULL, ctor);
51+
SetProtoAccessor(proto, Nan::New("width").ToLocalChecked(), GetWidth, NULL, ctor);
52+
SetProtoAccessor(proto, Nan::New("height").ToLocalChecked(), GetHeight, NULL, ctor);
53+
SetProtoAccessor(proto, Nan::New("naturalWidth").ToLocalChecked(), GetNaturalWidth, NULL, ctor);
54+
SetProtoAccessor(proto, Nan::New("naturalHeight").ToLocalChecked(), GetNaturalHeight, NULL, ctor);
55+
SetProtoAccessor(proto, Nan::New("onload").ToLocalChecked(), GetOnload, SetOnload, ctor);
56+
SetProtoAccessor(proto, Nan::New("onerror").ToLocalChecked(), GetOnerror, SetOnerror, ctor);
5657
#if CAIRO_VERSION_MINOR >= 10
57-
Nan::SetAccessor(proto, Nan::New("dataMode").ToLocalChecked(), GetDataMode, SetDataMode);
58+
SetProtoAccessor(proto, Nan::New("dataMode").ToLocalChecked(), GetDataMode, SetDataMode, ctor);
5859
ctor->Set(Nan::New("MODE_IMAGE").ToLocalChecked(), Nan::New<Number>(DATA_IMAGE));
5960
ctor->Set(Nan::New("MODE_MIME").ToLocalChecked(), Nan::New<Number>(DATA_MIME));
6061
#endif

src/ImageData.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Copyright (c) 2010 LearnBoost <[email protected]>
66
//
77

8+
#include "Util.h"
89
#include "ImageData.h"
910

1011
Nan::Persistent<FunctionTemplate> ImageData::constructor;
@@ -25,8 +26,8 @@ ImageData::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
2526

2627
// Prototype
2728
Local<ObjectTemplate> proto = ctor->PrototypeTemplate();
28-
Nan::SetAccessor(proto, Nan::New("width").ToLocalChecked(), GetWidth);
29-
Nan::SetAccessor(proto, Nan::New("height").ToLocalChecked(), GetHeight);
29+
SetProtoAccessor(proto, Nan::New("width").ToLocalChecked(), GetWidth, NULL, ctor);
30+
SetProtoAccessor(proto, Nan::New("height").ToLocalChecked(), GetHeight, NULL, ctor);
3031
Nan::Set(target, Nan::New("ImageData").ToLocalChecked(), ctor->GetFunction());
3132
}
3233

src/Util.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include <v8.h>
2+
#include <nan.h>
3+
4+
// Wrapper around Nan::SetAccessor that makes it easier to change the last
5+
// argument (signature). Getters/setters must be accessed only when there is
6+
// actually an instance, i.e. MyClass.prototype.getter1 should not try to
7+
// unwrap the non-existent 'this'. See #803, #847, #885, nodejs/node#15099, ...
8+
inline void SetProtoAccessor(
9+
v8::Local<v8::ObjectTemplate> tpl,
10+
v8::Local<v8::String> name,
11+
Nan::GetterCallback getter,
12+
Nan::SetterCallback setter,
13+
v8::Local<v8::FunctionTemplate> ctor
14+
) {
15+
Nan::SetAccessor(
16+
tpl,
17+
name,
18+
getter,
19+
setter,
20+
v8::Local<v8::Value>(),
21+
v8::DEFAULT,
22+
v8::None,
23+
v8::AccessorSignature::New(v8::Isolate::GetCurrent(), ctor)
24+
);
25+
}

test/canvas.test.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ const os = require('os')
1717
const Readable = require('stream').Readable
1818

1919
describe('Canvas', function () {
20+
it('Prototype and ctor are well-shaped, don\'t hit asserts on accessors (GH-803)', function () {
21+
const Canvas = require('../').Canvas;
22+
var c = new Canvas(10, 10);
23+
assert.throws(function () { Canvas.prototype.width; }, /incompatible receiver/);
24+
assert(!c.hasOwnProperty('width'));
25+
assert('width' in c);
26+
assert(Canvas.prototype.hasOwnProperty('width'));
27+
});
28+
2029
it('.parseFont()', function () {
2130
var tests = [
2231
'20px Arial'

test/image.test.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88

99
const loadImage = require('../').loadImage
10+
const Image = require('../').Image;
1011

1112
const assert = require('assert')
1213
const assertRejects = require('assert-rejects')
@@ -16,6 +17,14 @@ const png_clock = `${__dirname}/fixtures/clock.png`
1617
const jpg_face = `${__dirname}/fixtures/face.jpeg`
1718

1819
describe('Image', function () {
20+
it('Prototype and ctor are well-shaped, don\'t hit asserts on accessors (GH-803)', function () {
21+
var img = new Image();
22+
assert.throws(function () { Image.prototype.width; }, /incompatible receiver/);
23+
assert(!img.hasOwnProperty('width'));
24+
assert('width' in img);
25+
assert(Image.prototype.hasOwnProperty('width'));
26+
});
27+
1928
it('loads JPEG image', function () {
2029
return loadImage(jpg_face).then((img) => {
2130
assert.strictEqual(img.onerror, null)

test/imageData.test.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@
33
'use strict'
44

55
const createImageData = require('../').createImageData
6+
const ImageData = require('../').ImageData;
67

78
const assert = require('assert')
89

910
describe('ImageData', function () {
11+
it('Prototype and ctor are well-shaped, don\'t hit asserts on accessors (GH-803)', function () {
12+
assert.throws(function () { ImageData.prototype.width; }, /incompatible receiver/);
13+
});
14+
1015
it('should throw with invalid numeric arguments', function () {
1116
assert.throws(() => { createImageData(0, 0) }, /width is zero/)
1217
assert.throws(() => { createImageData(1, 0) }, /height is zero/)

0 commit comments

Comments
 (0)