Skip to content

Add new walkthrough-style explainer - #745

Open
lukewagner wants to merge 7 commits into
rename-explainerfrom
new-explainer
Open

lukewagner wants to merge 7 commits into
rename-explainerfrom
new-explainer

Conversation

@lukewagner

Copy link
Copy Markdown
Member

This commit adds a new Explainer.md that attempts to provide a better introductory experience to the CM for folks already thinking at the WAT level by walking through the CM's (pre-0.3) features, starting with a "hello, world" WAT component and then progressively introducing CM features through new examples. The target audience here is CG folks already familiar with WAT and evaluating this as a new spec proposal and perhaps thinking about runtime/toolchain implementations... not general audiences (for that, component-model.bytecodealliance.org is still linked to as the better starting point).

@eqrion pointed out that a good explainer should also start with a nice crisp updated-for-2026 Problem Statement, but that's something I think we can iterate on separately (perhaps as part of the first one or two CM SG meetings) so for now there's just a short placeholder section at the beginning with a "TODO" and link to design/high-level/ (which have historically sorta served this purpose).

There are also JS API links left as "TODO" that are intended to be resolved by the new Markdown files added in #686, which I'd like to focus on getting merged next, before this PR (at which point I'll rebase and update this PR to link to the new docs); I'm mostly just posting this now to parallelize feedback. Also, in the last CG meeting, @tlively asked if there was any good CM SG "pre-reading" for anyone so motivated and I think perhaps this PR might serve that purpose.

@vados-cosmonic

Copy link
Copy Markdown
Collaborator

This is fantastic -- I did have one ask though, could the new walkthrough style explainer be multiple files rather than one massive one?

Also I do wonder if renaming Explainer.md is a good idea given all the links out there to it...

@lukewagner

Copy link
Copy Markdown
Member Author

@vados-cosmonic Thanks! Personally, I think single-page documents are convenient, because you can ctrl-F skip around the page, see your progress as you go down the page, and see the automatic ToC. Also, the CM repo already uses this single-page-explainer convention. If page-load times were bad, that might be a reason to break it up, but it seems to load quite fast. As for renaming old Explainer.md to Text.md, I think it might not be too bad; I think mostly folks were linking to the top-level repo or README.md or just https://component-model.bytecodealliance.org.

@tlively tlively left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the nice explainer. These comments are mostly about ways to improve the exposition, but a few design questions slipped in there, too. I have plenty more design questions that I'll reserve for a better forum :)

Comment thread design/mvp/Explainer.md
example.

Given the ABI options supplied to `canon`, the Component Model specifies how the
given component-level type (`(func (result string))`) is **flattened** into a

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, why "flattened" instead of "lowered?"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question: the overall process (involving reading/writing memory and at what offsets, etc) is called lifting/lowering, but if we're just talking about the mapping from a component-level function type to a core-level function type, we call the core type the "flattened" function type (since the mapping "flattens" compound types like records/variants into scalar i32s etc). It's a good question of whether we could avoid introducing a new term and instead call it the "lowered function type", but IMO that might be more confusing (both at this point in the walkthrough and in general) since we're otherwise talking about lifting a core function into a component-level function.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you could talk about the inverse of flattening, which would map a sequence of core types to a set of possible component types. That would be a type-level lifting, so I think using lifting and lowering terminology for types as well as functions would be reasonable.

Comment thread design/mvp/Explainer.md Outdated
Comment thread design/mvp/Explainer.md
(memory.copy (local.get $ptr) (i32.const 16 (; = "hello " ;)) (i32.const 6))
(memory.copy (i32.add (local.get $ptr) (i32.const 6)) (local.get $namePtr) (local.get $nameLen))
(call $libc-free (local.get $namePtr))
(local.set $retp (i32.const 0))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, this $retp value will later be passed to $libc-free, so it is probably intended to be initialized via a call to $libc-malloc.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite: as the comment and second paragraph attempt to explain: it's $retp->$ptr that is passed to $libc-free; the actual return-address-space is effectively statically allocated.

Comment thread design/mvp/Explainer.md Outdated
Comment thread design/mvp/Explainer.md Outdated
Comment on lines +336 to +337
following type which, due to running everything during a module `start`
function, has no exports:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is running everything in the start function a common pattern? If it's not common, it would be helpful to point that out.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although occasionally we have to work around temporary toolchain/CM limitations (including in the Web Preview, due to lack of a CM-level callback type), it is already and increasingly normal. In particular, the CM provides a solution to the core-memory-not-visible-to-imports issue (multiple modules) and thus start functions can serve as ESM top-level scripts (literally when using component ESM-integration).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And toolchains know how to emit the multiple modules necessary to work around the cyclic dependency problem?

Comment thread design/mvp/Explainer.md
Comment on lines +608 to +613
first parameter. Beyond `[method]`, other bindings hints include `[static]`,
`[constructor]`, `[get]`, and `[set]` ([📡]). When any of these annotations is
present, to avoid weird corner cases in language bindings, the Component Model
specifies additional validation conditions on the `func` type (e.g., in the case
of `[method]`, there must be a `borrow`ed `self` parameter of the correct named
resource type).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems very strange to me that not only are we depending on name mangling for ergonomics, but we are letting name mangling affect validation rules. Why not put this information in a separate grammar production the way (external-id "...") and (implements "...") attributes are?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great question and indeed a decent amount of discussion and iteration has gone into the current choice. It's still open for more discussion though, but probably best described live in one of the upcoming CM SG meetings. I added a bullet to a new gdoc I just created to collect such topics; feel free to add any more.

Comment thread design/mvp/Explainer.md
* For `read` and `drop`, the `i32` parameter must refer to a valid handle table
element of the right type or else there is a trap.
* For `drop`, after the checks, the referred-to resource is destroyed and the
`i32` index is marked invalid but available for subsequent reuse.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this safe? Doesn't this allow forging handles to resources?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The key is that resource handle tables are per-component-instance, which is, in cap-safety terms, the "unit of isolation". Thus, a component instance can't forge anything that wasn't already given to that component instance; intra-component-instance handle use safety is up to the guest language and guest code.

Comment thread design/mvp/Explainer.md Outdated
Comment on lines +643 to +644
resource types could be type-imported by modules and then the untyped `i32`
indices could be replaced by references to the imported type.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't they already be replaced by externref or anyref, even without type imports? Surely those are no worse than i32 table indices.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That does seem like a much more appropriate type for resources.

Also, has anyone thought of the following: if they are externref, can we get rid of explicit drops for resources? I expect instead the engine's management of the externref could do it on its own (through ref counting or more elaborate GC). If yes, then that could simplify a lot of things, and solve (one of) the biggest difficulties in getting a WasmGC language to target CM (with or without #525). For example, do we even need the own/borrow distinction then? It seems like they are there only for subtle decisions about dropping.

(Of course, we would still need the destructor side of things, i.e., the drop notification on the resource exporting side. But resource users would not need to worry about the whole thing.)

If this hasn't been fully explored before, should I open an issue with a fleshed-out proposal?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great point; I forgot to mention externref as an intermediate step; added!.

Based on our experience, if you have a standard linear-memory language, i32 is still rather simpler and more efficient than externref: because support for manually passing around externrefs is still rather limited (and requires source-language support), linear memory bindings are going to immediately table.{get,set}ing the externref anyways to get an i32 that will flow into the wild core wasm, but now there's this extra bit of overhead of (un)boxing up the original host value into an externref and then storing the externref in a table, and also now the GC might be involved.

But yeah, externref is totally being discussed in the context of GC languages in #525. If anyone wants to discuss further, do feel free to add to this gdoc I just created.

Comment thread design/mvp/Explainer.md
Comment thread design/mvp/Explainer.md Outdated
Comment on lines +1017 to +1018
implementation of `file`. In particular, there is no global runtime namespace or
registry in which different local implementations of `file` might clash.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This statement seems contradicted by the existence of external-id and implements.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, that is a subtle point that bears explanation; added a bit more of an explanation, but happy to discuss further in the CM SG.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants