Hacker News new | ask | show | jobs
by pdubroy 399 days ago
The WebAssembly spec is quite approachable, but for anyone who is interested in learning Wasm and doesn't want to read the spec —

WebAssembly from the Ground Up (https://wasmgroundup.com/) an online book to learn Wasm by building a simple compiler in JavaScript. It starts with handcrafting bytecodes in JS, and then slowly builds up a simple programming language that compiles to Wasm.

There's a free sample available: https://wasmgroundup.com/book/contents-sample/

(Disclaimer: I'm one of the authors)

2 comments

Side-note:

  const MIN_U32 = 0;
  const MAX_U32 = 2 ** 32 - 1;
  
  function u32(v) {
    if (v < MIN_U32 || v > MAX_U32) {
      throw Error(`Value out of range for u32: ${v}`);
    }
  
    return leb128(v);
  }
I love Ada, because you can do this:

  subtype U32 is Interfaces.Unsigned_64 range 0 .. 2 ** 32 - 1;
or alternatively:

  type U32 is mod 2 ** 32;
and then you can use attributes such as:

  First  : constant U32 := U32'First; -- = 0
  Last   : constant U32 := U32'Last;  -- = 2 ** 32 - 1
  Range_ : constant U32 := U32'Range; -- Range 0 .. 2**32 - 1
That's kinda cool. I bet you could take that to the next step and allow arbitrary code for validation of types/arguments at compile time.
If you're interested to learn more in whatever language, the relevant search term is "refinement type": https://en.wikipedia.org/wiki/Refinement_type
Ada keeps being used as example for subranges, however they exist already in Pascal and all Modula variants.
I know. That said, I keep mentioning Ada because it is widely used in mission critical systems, and because it supports contracts (yes, I know, so does Eiffel), and you can do formal verification using Ada / SPARK, meaning that it could be used in place of Rust, whereas Pascal probably not.
Is it possible to "instrument" the WASM code to enable in-process debugging? In other words, would it be possible to generate WASM based off some input string (my custom language) on-the-fly, and then run it with breakpoints and memory inspection, all within the same Javascript script hosted on, say, a web page?
Wizard has engine support for instrumentation, but Whamm (https://github.com/ejrgilbert/whamm) can also do instrumentation through bytecode rewriting.
That still requires the usage of dev tools and linear source code mapping between the original and the generated WASM, correct? Would it be possible to avoid dev tools, and implement the debugger in Javascript? Or the WASM technology doesn't provide such an opportunity? I'd like to break on breakpoints, jump back into Javascript, unroll it into the original location in the source code, and display it all in an IDE-like window all within a browser page, and without involvement of dev tools (that can't handle non-linear conversions between source code and generated JS/WASM).
Yes, if you use bytecode rewriting then all the offsets are changed and you need a mapping. This is one of the advantages of engine-side instrumentation; bytecode offsets don't change. It'll be some time before we can get engines to agree on a standard interface for instrumentation, but there have been some discussions.

Whamm can inject arbitrary instrumentation logic, so you could, e.g. inject calls to imports that are implemented in JS. You'll have some heavy lifting to do on the JS side.

Visual Studio supports debugging C# compiled to WASM when your page is made with Blazor.

Granted, you're debugging in another window that isn't a browser; but overall the debugger is about 80% of what you get when debugging a .net process running outside of the debugger.

I'm not sure I totally understand what you mean by "in-process" here. But you could have some JavaScript that compiles some code in your custom language to WebAssembly and then execute it, and you can use the browser dev tools to set breakpoints the Wasm, inspect the memory, etc.

In the book, we don't cover source maps, but it would also be possible to generate source maps so that you can set breakpoints in (and step through) the original source code in your custom language, rather than debugging at the Wasm instruction level.

Does that answer your question?

Sadly, no, I'd like to write a ~Prolog interpreter (compiler into WASM that would dynamically replace parts of the implementation as source code evolves), and have the debugger and WASM memory inspector as part of the web page written in Javascript, which was used to compiled the code in the first place. That is, would it be possible to implement a debugger and memory inspector in Javascript without resorting to dev tools? Prolog doesn't map 1:1 to WASM/Javscript via source maps, making it nearly impossible to properly debug it in dev tools.
Ah, I see! Yeah that's significantly trickier.

re: "dynamically replace parts of the implementation as source code evolves" — there is a technique for this, I have a short write-up on it here: https://github.com/pdubroy/til/blob/main/wasm/2024-02-22-Run...

About the debugging and inspecting —

Inspecting Wasm memory is easy from JS, but to be able to do the debugging, you'd probably either need to rewrite the bytecode (e.g., inserting a call out to JS between every "real" instruction) or a self-hosted interpreter like wasm3 (https://github.com/wasm3/wasm3).

(Or maybe there are better solutions that I'm not thinking of.)