Wrapping Event in a C++ class so that every where you pass an event it works.
in JavaScript I can do this
class MyEvent extends Event {
constructor(type, msg) {
super(type);
this.message = msg;
}
}
const target = new EventTarget();
const e = await new Promise(resolve => {
target.addEventListener('custom', resolve);
target.dispatchEvent(new MyEvent('custom', 'hello'));
});
assert.ok(e.message === 'hello');
Is it possible to have MyEvent be a C++ class?
In my current attempt, when I call target.dispatchEvent I get
node:internal/event_target:220
throw new ERR_INVALID_THIS('Event');
^
TypeError [ERR_INVALID_THIS]: Value of "this" must be of type Event
at get type [as type] (node:internal/event_target:220:13)
at EventTarget.dispatchEvent (node:internal/event_target:755:40)
I tried setting the prototype chain in C++ and this passes
assert.ok(new MyEvent('custom', 'hello') instanceof Event);
but of course it's not actually an Event, it's a non-event who's prototype chain contains Event which is what node is complaining about.
wrt, I'm trying to verify if this is possible or not. My impression is it's not possible and that I have to do something like create class MyEvent extends Event in JavaScript and then use it from my addon.
Just to make it clear what my goal is here. I'm trying to get dawn.node to be spec compliant.
Dawn is Chromium's implementation of the WebGPU Spec and dawn.node is trying to bring the same API to node.js
That spec has a few requirements of extending existing classes
For example GPUDevice is supposed to extend EventTarget
[Exposed=(Window, Worker), SecureContext interface GPUDevice : EventTarget { ... }
This means, for example, if you patch dispatchEvent on EventTarget and call dispatchEvent on a GPUDevice it must go through the patched function
EventTarget.prototype.dispatchEvent = (function(origFn) {
return function(event) {
console.log('type:', event.type);
return origFn.call(this, event);
};
})(EventTarget.prototype.dispatchEvent);
someGPUDevice.dispatchEvent(new Event('foobar')); // should see type: foobar in console
Similarly, as mentioned above, a custom event WebGPU event, implemented by dawn.node, needs to act like an Event. In particular GPUUncapturedErrorEvent extends Event
Exposed=(Window, Worker), SecureContext]] interface GPUUncapturedErrorEvent : Event { constructor( DOMString type, GPUUncapturedErrorEventInit gpuUncapturedErrorEventInitDict ); [SameObject] readonly attribute GPUError error; }; dictionary GPUUncapturedErrorEventInit : EventInit { required GPUError error; };
This means for example, it's valid to dispatchEvent an GPUncaptureErrorEvent through an EventTarget
const target = new EventTarget();
target.dispatchEvent(new GPUUncapturedErrorEvent('uncapturederror', {
error: new GPUValidationError('msg'),
});
Which is the issue above.
Several other requirements
GPUDevice.prototype instanceof EventTarget // should be true
someGPUDevice instanceof EventTarget // should be true
someGPUDevice instanceof GPUDevice // should be true
I feel like I'm reading around the net and in issues in this repo that NAPI doesn't support what I'm trying to do and basically I'm trying to verify that.
Node-API does not currently provide a mechanism to create a C++ class that extends a JavaScript class. There's some technical limitations (as a class in V8 is a FunctionTemplate, which does not have a corresponding Node-API counterpart).
Have you tried prototype manipulation, eg. with Object.setPrototypeOf()? I am not sure if this would work, but if you have time to try it out... 👍 But it may not succeed in your requirement of someGPUDevice instanceof GPUDevice === true 🤷
Perhaps look at https://github.com/nodejs/node-addon-api/issues/229 for some other discussions regarding same.
Let us know how it turns out!
So I did a bunch of hacking to get things to kind of work. The frustrating part is this works in the browsers (chrome/firefox/safari) that implement WebGPU but apparently with the current Napi design, and because limitations in v8 if I understand correctly, it's not possible in node addons.
My workarounds involved using a JavaScript object instead of C++ object for GPUUncapturedErrorEvent which works because GPUUncapturedErrorEvent is basically just a vehicle for some data. No functionality. I had to do similar things for GPUPipelineError as it's supposed to extend DOMException.
On the other hand, I could not workaround the fact that GPUDevice is supposed to extend EventTarget. That said, I know of no apis that take an EventTarget so it's unlikely to mater.
@greggman I just realized that we can utilize a trick in JS to allow extending a JS class in node-addon-api, working around the limitation.
This should be already doable in C node-api. https://github.com/nodejs/node-addon-api/pull/1670 is a work to allow it in node-addon-api.
This issue is stale because it has been open many days with no activity. It will be closed soon unless the stale label is removed or a comment is made.
class MyEvent extends Event { constructor(type, msg) { super(type); this.message = msg; } } const target = new EventTarget(); const e = await new Promise(resolve => { target.addEventListener('custom', resolve); target.dispatchEvent(new MyEvent('custom', 'hello')); }); assert.ok(e.message === 'hello');
@jackdenied , not sure the point of your reply. This post is about C++, not JavaScript