-
-
Notifications
You must be signed in to change notification settings - Fork 30
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
Create async iterators and iterator result objects in the correct realm #247
Conversation
This looks good in general! We have a very small set of behavior tests for the utils file: https://github.com/jsdom/webidl2js/blob/master/test/utils.test.js . It might be worth trying to add another, but it seems hard given that everything is tied to file I/O right now. (#160 discusses this.) The |
These _should_ be created in the current realm, but I don't see how to do that yet.
I added Also added (very) basic tests for the new utilities; not sure if they're valuable, but they're there. |
The main remaining question is what to call the new API and how to document it. Should all the existing "well-known keys" be documented or just |
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.
Just curious, can we possibly get the object through globalObject.eval("(async function* () {})")
?
Either way, even if eval works, I think this is a good approach, just in case folks have CSP or something similar enabled that prevents eval from executing.
lib/output/utils.js
Outdated
ctorRegistry["%IteratorPrototype%"] = Object.getPrototypeOf( | ||
Object.getPrototypeOf(new ctorRegistry["%Array%"]()[Symbol.iterator]()) | ||
); | ||
ctorRegistry["%AsyncIteratorPrototype%"] = AsyncIteratorPrototype; |
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.
Add a comment here saying that this is not actually correct, and integrators should use registerConstructor to do the right thing.
lib/output/utils.js
Outdated
|
||
globalObject[ctorRegistrySymbol] = ctorRegistry; | ||
return ctorRegistry; | ||
} | ||
|
||
// Associates a constructor with the given global object. Intended as a public | ||
// utility for the purpose of registering "%AsyncIteratorPrototype%". | ||
function registerConstructor(globalObject, key, constructor) { |
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.
The name is a bit imprecise, since we allow registering any intrinsic, not just constructors.
Edit: I see you noticed this as well. We can do registerIntrinsic maybe?
That's brilliant! |
Hmm so if Regarding relevant vs. current, yeah, we're not going to be able to get current unless we do the work in jsdom/jsdom#2727 (roughly, eval the entire webidl2js bundle inside the jsdom realm). In general I don't go for the philosophy of introducing unstable APIs that don't benefit from semver. New major version bumps are relatively cheap. |
Added a commit switching to using Possible downsides:
What do people think? |
Given that this is mostly a Node-focused project, and we already use vm.runInContext() a lot which is equivalent to eval(), I think it's fine. @TimothyGu? |
My main concern here is that this is webidl2js, not jsdom. Webidl2js is used for some more "isomorphic" projects like whatwg-url, which we do expect to run in browsers. In the past, we've seen some issues with using webidl2js in browsers, such as with Safari and SharedArrayBuffer; but it was reasonably easy to work around that issue by polyfilling I think I'd be fine with throwing a try-catch around the |
lib/output/utils.js
Outdated
function newObjectInRealm(globalObject, object) { | ||
const ctorRegistry = initCtorRegistry(globalObject); | ||
return Object.defineProperties( | ||
Object.create(ctorRegistry["%Object%"].prototype), |
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.
nit: could we additionally capture %Object.prototype%
in ctorRegistry, and use that here? One can imagine some weird client code overwriting Object.prototype
.
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.
Good catch!
lib/output/utils.js
Outdated
const IteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())); | ||
const AsyncIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf(async function* () {}).prototype); |
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.
These exports could be removed now; should they be?
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 think removing them would technically be a major bump. Maybe add a comment here saying we should remove them when we bump?
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.
Makes sense. It's worth noting though that there's already a bit of incompatibility between the main branch and the last release; interfaces generated after #234 expect initCtorRegistry
to be the only way of initializing the ctorRegistry
, while code generated before #234 naturally doesn't call initCtorRegistry
, as it didn't exist. Installing pre-#234 interfaces before post-#234 interfaces causes the latter to throw, because the ctorRegistry
was created but %IteratorPrototype%
was not captured. There's a workaround (calling initCtorRegistry
manually before installing interfaces), which I've been using in local testing, but it's not documented.
I sort of assumed that the next release would be major for this reason, though admittedly that would probably be more of a pain for jsdom than just using the workaround. I'm not terribly clear on what the semver guarantees mean for codegen projects, much less for domexception
and other projects whose public interface is only part codegen.
This PR is ready for review/merge as far as I'm aware. I've updated the title & header to reflect the current implementation. |
This PR intends to allow webidl2js to use the correct realm's
AsyncIteratorPrototype
. It uses a structure similar to #234, registering anAsyncIteratorPrototype
in each global object'sctorRegistry
and inheriting from that prototype ininstall()
.#234 obtained the
IteratorPrototype
fromglobalObject.Array
; unfortunately, afaik there are no ecmascript globals that link to theAsyncIteratorPrototype
, so that approach is not possible here.This PR exposes a utility,registerConstructor(globalObject, key, constructor)
(bikeshedding appreciated), which sets a binding in theglobalObject
'sctorRegistry
. Consumers can use this to register the correctAsyncIteratorPrototype
prior to installing interfaces on their global object, using the key"%AsyncIteratorPrototype%"
.Updated: This PR uses
globalObject.eval("(async function* () {})")
to find the correct realm'sAsyncIteratorPrototype
. It also creates the iterator result objects ({value, done}
) in the correct realm, though it does not use the correct realm'sPromise
s.As a fallback, if
eval()
doesn't work,initCtorRegistry()
initializes"%AsyncIteratorPrototype%"
to the generated class's realm'sAsyncIteratorPrototype
.Helps with jsdom/jsdom#3200.
I'm not sure how to test this. Does
webidl2js
currently have any form of behavior testing, or just generated code snapshots? Currently, I'm testing by linking this into a local fork of jsdom.Remaining issues:
{value, done}
objects returned by the async iterator are still created in the wrong realm, causingreadable-streams/async-iterator.any.js
to still fail...