tc39/proposal-module-expressions

How can i synchronously determine if something is a module block?

ljharb opened this issue · 18 comments

Before i await import(x), or similar, i need to know if x is something importable (a module block, or a string), or if instead the user passed me the wrong kind of object, so i can throw a synchronous error.

How can i brand-check a module block x without evaluating the module block and without having to yield to the event loop?

surma commented

Yes, that is possible, we just haven't quite figured out what exactly the type of x should be when you write let x = module { ... }.

Working on that in #1. Most likely Object.getPrototypeOf(x) will be something intrinsic.

I'd want a cross-realm way, which doesn't work with instanceof or checking the prototype - there'd need to be a static method, or a .call-able prototype method to satisfy that (like there is on everything else that's built in), or it'd have to have a unique typeof value.

@ljharb I encourage you to pursue a general Type.isType proposal in TC39. It's quite awkward to have brand detection as a requirement on a case-by-case basis but with a general clean API excluded. I don't think the committee currently contains any of the previous objectors to that proposal, and we've successfully moved other things forward that were blocked in the past. To me, it seems quite ugly to add Type.isType predicates just for the objects that don't happen to have any other way of doing brand-checking--we should make a general up/down decision, I think.

Module blocks may fall into this category. Importing a module block obviously may have side effects. For module blocks, we didn't have any particular methods for module blocks in mind, though @kriskowal proposed that we implement the StaticModuleRecords interface, which defines the staticImports method which would presumably fit the bill. At the same time, I want to give the Compartments proposal some time to 'bake' as a whole, and I would prefer that we add that method to module blocks' prototype later, as part of that broader proposal.

That’s a great idea, and I’ll try to find time to do that, but in the meantime it doesn’t change the constraints on new proposals. I’m not aware of anything that’s moved forward without satisfying this constraint.

These objects surely have another way of doing it - that you can pass them into import, and they’re not simply stringified as they would be now, implies there’s an internal slot being checked, and I’m asking for a way to synchronously test for it.

import.isModule(expr) meta property? 🤔

I’d prefer something non-syntactic, but i suppose that’s one possibility.

surma commented

Just to double-check: Is something (modulo bikeshed) like Object.getPrototypeOf(myModuleBlock) == ModuleBlock sufficient? If not, why?

Because unless module blocks are primitive types, that check returns false for module blocks coming from a different realm (since they have the other realms's prototype).

Yes, I do like the Type.isType pattern. I would be happy to help with a general proposal for this. From a process perspective, I am not very happy with the work of designing brand checking being pushed off to each proposal champion to solve individually; I would prefer that we could find a single solution. I don't think "I don't have time to propose that, just to ask others to do it one by one" really forms the ideal design process. Let's see if we can discuss this issue in general at the next TC39 meeting, separated from this particular proposal.

Some of the objectors to the concept are still in the committee, so i don’t think that it’s being pushed off due to mere unavailability. Happy to discuss it in plenary tho!

surma commented

Ah okay, thanks for clarifying. I think I understand your concern now.

(since they have the other realms's prototype).

I don’t think this is necessarily true. I envision Module Blocks to be structured cloneable, but AFAICT that doesn’t mean the prototype must be retained from the original realm. WebAssembly.Module, Set, Map all take on the prototype of the target realm — any reason why a Module Block would need to be different?

I agree with others on this thread that we can't use instanceof for the type check. This question isn't about postMessage across agents, but rather about when multiple realms are used together within an agent, in which case instanceof becomes unreliable. I added an agenda item to discuss the general subject of type checking in tc39/agendas#902 .

surma commented

Oooh, okay. I did not consider the Realm use-case all that deeply. Makes sense!

(Note, this isn't about the Realm proposal, but about what can happen with multiple iframes, or test frameworks like Jest)

I put the Type.isType discussion topic on the agenda for the next TC39 meeting, but also, in #24, you can use ModuleBlock.prototype.toString as a brand check.

I'm marking this as Stage 3, as I think it's OK to iterate on details of the solution until then, but I think ModuleBlock.prototype.toString will provide a good solution already.

#24 has landed, so closing this issue for now, due to the brand check available in https://tc39.es/proposal-js-module-blocks/#sec-moduleblock.prototype.tostring , but if you have concerns with this solution, please post a comment and we can consider reopening.