-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Fix accessing prototypes of Canvas, Image, Context2d #808
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Urg, no real desire to debug node 0.10 and 0.8. The Canvas 2.0 proposal drops 0.8, at least... |
50ef012
to
fb60ca1
Compare
@LinusU I just made a change to allow node v0.8 and v0.10 to skip the tests that I added in this PR. None of the existing tests fail in v0.10 so this seems harmless to 0.10 and beneficial to 0.12+. What do you think of merging this? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Um... I think this fix is not work. Though it is not throw a error anymore, it will let Node exit at present.
Try the code in REPL: require('canvas').prototype
Then REPL exits.
I can reproduce that, but the access violation is in libcairo-2.dll, not node-canvas. I can't see enough of the call stack to tell if it's a bad usage of libcairo or an actual libcairo bug. @chearon does your msys2 build use a newer version of cairo? I don't have the msys2 build chain setup, but it might be informative to test this branch with newer cairo. |
I think the GTK.zip on here is from 2010 so the MSYS version should almost certainly be much newer. If it helps I made a wiki documenting how I got it working, although I need to update it for Pango |
Although the error message seems to disappear, nodejs still exits.
|
@basvanmeurs what OS are you running? gles2 doesn't link to Cairo, does it? |
@zbjornson Would you mind rebasing this on latest master as soon as I have merged #915? Also, I think it's better to fix |
Done!
Sure, dropped that change. |
Hmm 🤔 seems like something isn't quite right... |
Boo. Gonna take me a bit longer to look into that, won't get to it today... |
No problem, I'll try and have a look if I have time as well, but the clock just turned midnight here and I need to get up to work tomorrow :) |
It doesn't work for me from this branch. I still get a crash when inspecting. |
I need to work on this more sometime. I think this PR is a correct change to make based on some feedback from a core node.js dev, but another change is definitely needed to fully fix the error. I came across this node.js core issue, which looks similar and might provide some hints: nodejs/node-v0.x-archive#9028 |
a8f4289
to
bdec7a1
Compare
I think this is finally fixed, but I posed a small question to the node.js devs that would be good to know before landing. |
src/Canvas.cc
Outdated
@@ -51,16 +52,17 @@ Canvas::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) { | |||
|
|||
// Prototype | |||
Local<ObjectTemplate> proto = ctor->PrototypeTemplate(); | |||
proto->SetInternalFieldCount(1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @zbjornson, thanks for linking to this PR.
While this is just fine as a Band-Aid for the problem, I'd like to note several potential problems that would stem from this implementation. First, philosophically, the prototype object should not be an instance of the class -- that is, it shouldn't have internal slots like instances do. JavaScript got this wrong with Array.prototype
and Function.prototype
(Array.isArray(Array.prototype)
and typeof Function.prototype === 'function'
both return true), but they have rectified this problem with future additions to the language with a note in the spec:
The Array prototype object is specified to be an Array exotic object to ensure compatibility with ECMAScript code that was created prior to the ECMAScript 2015 specification.
More concretely, although accessing Canvas.prototype.type
will not throw, other code fragments e.g. Object.create(Canvas.prototype).type
will still throw, I believe. Even worse (but more unlikely), Object.setPrototypeOf(anotherObjectWithInternalFields, Canvas.prototype).type
may cause memory issues, as the type of the object pointer wrapped in V8 object is untyped.
If a Band-Aid is all that is sought here, then please disregard my suggestions below to make this more robust. Either suggestion will unfortunately incur quite some work, so it's totally up to you regarding what's best for this project.
-
Write a pure JS wrapper around the JS object created in C++, and implement the property getters/setters as ordinary functions (e.g.
getLength()
,setLength()
). This means that you would do all necessary type checking in JS, so in C++ you can feel free to assert() on predicates. The C++ wrapper would also be hidden in a symbol, so we can tell any user that get curious and call the prototype methods w/o context, well, don't try to access things you are not supposed to like hidden properties. This is generally what we do in Node.js. -
Check if
info.Holder()
is an instance of theFunctionTemplate
, and throw an error if it is not. This is what Chrome does for DOM, but it would require holding a reference to the FunctionTemplate, always. If you were to go down this route, I would put the Persistent reference to the FunctionTemplate in astd::unordered_map
keyed onIsolate*
.
A lot of this is based on speculation, so please let me know if you found a better solution to all of this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks so much for the detailed and helpful info!
the prototype object should not be an instance of the class -- that is, it shouldn't have internal slots like instances do
Oops, proto->SetInternalFieldCount(1)
was leftover from before I added the signatures to the SetAccessor
s, and no value is set for the slot anyway. Will remove in a moment.
As far as the proto being an instance of the class: my understanding is that internal slots are usable for anything and have no effect on the behavior of the object (they're just C++-accessible storage). Canvas.prototype instanceof Canvas === false
, so I think we're okay?
Object.create(Canvas.prototype).type
will still throw, I believe. ...Object.setPrototypeOf(anotherObjectWithInternalFields, Canvas.prototype).type
may cause memory issues
Both of these throw the "incompatible receiver" error (as desired; same as Canvas.prototype.type
), and neither have the assertion failure.
Check if info.Holder() is an instance of the FunctionTemplate
I think this is what v8 does actually, when SetAccessor
is used with an AccessorSignature
:
The signature describes valid receivers for the accessor and is used to perform implicit instance checks against them. If the receiver is incompatible (i.e. is not an instance of the constructor as defined by FunctionTemplate::HasInstance()), an implicit TypeError is thrown and no callback is invoked.
Am I reading that right? If so, then I think this PR is actually a sturdy route forward.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't familiar with how AccessorSignature worked... and it looks like that's it! This looks good to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you very much for reviewing!
bdec7a1
to
e16303e
Compare
@LinusU this should be ready to go 🚀. bnoordhuis answered my question in the linked thread, no change needed. |
Awesome!! @zbjornson mind rebasing and fixing the conflicts? ❤️ |
Accessors should not attempt to unwrap the non-existent 'this' when accessed via the prototype. Fixes Automattic#803 Fixes Automattic#847 Fixes Automattic#885 Fixes nodejs/node#15099
e16303e
to
d4b6f67
Compare
Done! :) |
Super! |
Wait for 1 year! 🤣🤣 |
...and one day 😆 |
Fixes #803
Fixes #847
Fixes #76
Fixes #855
Fixes nodejs/node#15099
Fixes #568