12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690 |
- \input texinfo @c -*- texinfo -*-
- @c %**start of header
- @setfilename hoot.info
- @settitle Guile Hoot
- @documentencoding UTF-8
- @documentlanguage en
- @syncodeindex pg cp
- @c %**end of header
- @dircategory The Algorithmic Language Scheme
- @direntry
- * Hoot: (hoot). Scheme to Wasm compiler backend for Guile and Wasm toolchain.
- @end direntry
- @finalout
- @titlepage
- @title Guile Hoot
- @author David Thompson (@email{dave@@spritely.institute})
- @author The Spritely Institute
- @end titlepage
- @contents
- @ifnottex
- @node Top
- @top Guile Hoot
- This is the manual for Guile Hoot, a Scheme to WebAssembly compiler
- backend for @url{https://gnu.org/software/guile,GNU Guile} and general
- purpose Wasm toolchain.
- Both this manual and Guile Hoot itself are released under Apache v2.
- See @ref{License} for more information.
- @end ifnottex
- @menu
- * Introduction:: What's the deal with Wasm, anyway?
- * Compiling to Wasm:: Using the compiler and development tools.
- * Web deployment:: Scheme in the browser!
- * Scheme reference:: Hoot-specific Scheme extensions.
- * Toolchain reference:: General purpose Wasm tools.
- * Contributing:: Lend a hand!
- * License:: Copying, distributing, and using this text.
- * Index::
- @end menu
- @node Introduction
- @chapter Introduction
- Guile Hoot is a Scheme to WebAssembly (henceforth referred to as
- @emph{Wasm}) compiler backend for
- @url{https://gnu.org/software/guile,GNU Guile} and a general purpose
- Wasm toolchain. Wasm is an abstract but low-level binary compilation
- target that can run on all major web browsers, and increasingly in
- other, more ``native'' contexts, as well. For over two decades,
- JavaScript has been @emph{the} official language of the web, and while
- the language has improved a lot over the years, its design leaves much
- to be desired. Thus web developers looked for ways to bring their
- preferred language to the browser to use instead. In the past, the
- only option available was to @emph{compile that language to
- JavaScript!} This approach worked in some sense but it was unideal
- because many languages do not map cleanly to JavaScript. In the case
- of Scheme, for example, the lack of a tail call facility makes
- compiling tail-recursive Scheme code unpleasant. At long last, thanks
- to Wasm, it has become possible to use an alternative language with
- fewer compromises and better performance. Hoot aims to bring Guile's
- ``whole self'' to the web, as well as to other Wasm GC runtimes.
- Hoot is being developed by the
- @url{https://spritely.institute,Spritely Institute} in collaboration
- with @url{https://www.igalia.com/,Igalia} to advance Spritely's
- mission to build the infrastructure for a completely decentralized
- social Internet. And for that mission, what better platform to target
- than the web browser?
- @url{https://spritely.institute/goblins/,Goblins}, Spritely's
- distributed object programming environment, is primarily written in
- Guile. So, to meet users where they are at @emph{and} not use
- JavaScript at the same time, Spritely needs a Guile to Wasm compiler!
- A secondary goal of Hoot is to advocate for all dynamic programming
- languages' (Python, Ruby, etc.) rightful place on the client-side web.
- The Wasm 1.0 specification was not a habitable environment for
- languages that require a garbage collector. The Wasm GC proposal,
- among others, has made it possible for dynamic languages to target
- Wasm in a real way. However, such advances are not without their
- detractors. Without the necessary support, a useful proposal will
- never make it into the core specification. For example, strings are a
- particularly controversial subject in the WebAssembly Community Group
- and proposals that would greatly benefit Hoot and other languages have
- not reached consensus. Implementing and targeting emergent and useful
- Wasm proposals helps those proposals find their way into the core
- specification. A rising tide lifts all boats, as they say, and while
- we may be little schemers, we want our work to help advance the Wasm
- standard for all dynamic languages.
- @menu
- * Status:: What works. What doesn't.
- * Installation:: Setting up Hoot.
- * Tutorial:: Compiling your first Scheme program to Wasm.
- @end menu
- @node Status
- @section Status
- Hoot's Wasm output is compatible with Google Chrome starting with
- version 119 and Mozilla Firefox starting with version 121. As of
- writing, WebKit/Apple Safari is not yet compatible.
- Hoot is still in an early phase of active development and its API
- should be considered unstable and subject to change in future
- releases. Hoot currently supports a subset of the R7RS-small Scheme
- specification, along with a small set of Guile-specific functionality
- such as @inlinefmtifelse{html,
- @url{https://www.gnu.org/software/guile/manual/html_node/Prompts.html,
- prompts}, @ref{Prompts,,,Guile Reference}}.
- Missing R7RS-small features include:
- @enumerate
- @item Libraries (@code{define-library}, @code{import}, etc.)
- @item Complex numbers (constants are supported, math procedures are not)
- @item Environments and evaluation (@code{environment}, @code{eval}, etc.)
- @end enumerate
- Future releases will add support for all of R7RS-small and eventually
- full Guile-flavored Scheme.
- To compile Scheme to Wasm, Hoot takes advantage of several new Wasm
- proposals. The most important of these new features are tail calls
- and GC reference types. The @code{return_call} family of instructions
- has made the implementation of Scheme's tail recursive procedure call
- semantics relatively straightforward. GC reference type instructions
- allow for heap allocated objects (and immediates via the @code{i31}
- type) that are managed by the Wasm runtime. This allows Hoot to take
- advantage of production garbage collectors already present in web
- browsers, obviating the need to implement and ship our own which would
- be both inferior to the host's and a major source of binary bloat.
- There's an additional Wasm proposal that Hoot has been built on that
- has, unfortunately, not found its way into the core Wasm
- specification: stringref. We still emit stringref, but it is reduced
- to being an intermediate form. A lowering pass replaces stringref
- instructions with something resembling the JS String Builtins
- proposal.
- @node Installation
- @section Installation
- The @emph{easiest} way to get up and running with Hoot is by using the
- @url{https://guix.gnu.org,GNU Guix} package manager for which we
- provide a @file{guix.scm} file ready for use with @command{guix
- shell}:
- @example
- cd guile-hoot/
- guix shell
- @end example
- @command{guix shell} will download/compile all required dependencies
- and start an interactive shell that is ready to use for building Hoot.
- To use Hoot without Guix requires building Guile from source. Hoot is
- currently undergoing a lot of development and requires a bleeding-edge
- Guile built against the @code{main} branch. Eventually Hoot will just
- require a stable release of Guile.
- With a sufficiently fresh Guile, via Guix or otherwise, the build can
- begin. If you are building from a Git checkout rather than an
- official release tarball, the first step is to bootstrap the build
- system:
- @example
- ./bootstrap.sh
- @end example
- Release tarballs have a pre-bootstrapped build system and do not
- require the above step.
- Now, build Hoot:
- @example
- ./configure
- make
- @end example
- If you'd like to install Hoot onto your system, run:
- @example
- sudo make install
- @end example
- The GNU build system defaults to @file{/usr/local} as the installation
- prefix. This can be changed by re-running the configure script:
- @example
- ./configure --prefix=/some/where/else
- sudo make install
- @end example
- To try out Hoot without installing it, use the @file{pre-inst-env}
- wrapper to launch Guile in the context of the Hoot build directory:
- @example
- ./pre-inst-env guile
- @end example
- If you installed Guile to your system, simply run @command{guile}.
- If everything went well, you will be greeted with a Guile REPL prompt.
- Regardless of installation status, to verify that Guile can find the
- Hoot modules, run:
- @lisp
- scheme@@(guile-user)> ,use (hoot compile)
- @end lisp
- If there is no error then congratulations! Your setup is correct.
- Proceed to the tutorial for a crash course in how to use Hoot, or see
- later chapters for an API reference.
- @subsection Running the test suite
- This is entirely optional, but if you'd like further verification that
- your build is good (or perhaps you're packaging Hoot for
- distribution), the test suite can be run via @command{make check}. By
- default, the tests are run against two Wasm runtimes: Hoot's own Wasm
- interpreter and @url{https://v8.dev/,V8} via the @command{d8} tool.
- Getting V8 can be tricky, and will most likely require you to
- @url{https://v8.dev/docs/build,compile it from source.} It's a pain!
- To skip all of that trouble and just run the tests against the
- built-in interpreter, run:
- @example
- make check Wasm_HOST=hoot
- @end example
- @node Tutorial
- @section Tutorial
- Let's compile some simple Scheme programs and learn how to work with
- their compiled Wasm forms.
- As we all know, the answer to everything is simply 42. So, we should
- make sure that we can compile 42 to Wasm. To do so, import the
- @code{(hoot compile)} module and call the @code{compile} procedure.
- @lisp
- @verbatim
- scheme@(guile-user)> ,use (hoot compile)
- scheme@(guile-user)> (define the-answer (compile 42))
- @end verbatim
- @end lisp
- The result is a Wasm module. There is a lot of stuff inside, but
- we're not going to focus on that right now. We should load and run
- the module to verify that it outputs 42 like we expect. We can do so
- from the comfort of our Guile REPL because Hoot includes a Wasm
- interpreter. There's no need to use a web browser or other Wasm
- runtime to try out small programs.
- First, import the @code{(hoot reflect)} module. Then, instantiate
- @code{the-answer} to load it into the Wasm interpreter:
- @lisp
- @verbatim
- scheme@(guile-user)> ,use (hoot reflect)
- scheme@(guile-user)> (define instance (hoot-instantiate the-answer))
- @end verbatim
- @end lisp
- All that's left to do now is execute the program with
- @code{hoot-load}:
- @lisp
- @verbatim
- scheme@(guile-user)> (hoot-load instance)
- $5 = 42
- @end verbatim
- @end lisp
- Ta-da! It feels kind of funny to compile a Scheme program to Wasm
- only to load it back into Scheme, but it's a quick and easy way to
- test things out.
- For cases when you simply want to compile an expression and see the
- result immediately, there is a faster method. Just use the
- @code{compile-value} procedure instead:
- @lisp
- @verbatim
- scheme@(guile-user)> (compile-value '(list 1 2 3))
- $6 = #<hoot (1 2 3)>
- @end verbatim
- @end lisp
- With @code{compile-value}, the compiled Wasm module is thrown away,
- which is just fine for testing throwaway code.
- Lists are cool and 42 is ultimately the answer to everything, but it
- would be a shame if we didn't talk about compiling something a little
- more complicated. Let's compile a simple, tail-recursive procedure!
- How about good ol' factorial?
- @lisp
- @verbatim
- scheme@(guile-user)> (define hoot-factorial
- (compile-value
- '(let ()
- (define (factorial x result)
- (if (= x 1)
- result
- (factorial (- x 1)
- (* result x))))
- factorial)))
- @end verbatim
- @end lisp
- A Hoot procedure can be called just like a regular procedure:
- @lisp
- @verbatim
- scheme@(guile-user)> (hoot-factorial 5 1)
- $7 = 120
- @end verbatim
- @end lisp
- The Hoot reflection in Guile is great for quickly iterating on code,
- but what we really want is to get our programs running in a web
- browser. We've compiled a couple of things to Wasm now, but the
- resulting modules have stayed within the confines of the Guile
- process. To make something that can be loaded by a web browser, we
- need to use the assembler to create a Wasm binary:
- @lisp
- @verbatim
- scheme@(guile-user)> (define hello (compile "Hello, world!"))
- scheme@(guile-user)> ,use (wasm assemble)
- scheme@(guile-user)> (define bin (assemble-wasm hello))
- @end verbatim
- @end lisp
- Now, create a new directory for this tutorial:
- @example
- mkdir hoot-tutorial
- @end example
- Write the binary to disk in that directory:
- @lisp
- @verbatim
- scheme@(guile-user)> ,use (ice-9 binary-ports)
- scheme@(guile-user)> (call-with-output-file "/path/to/hoot-tutorial/hello.wasm"
- (lambda (port)
- (put-bytevector port bin)))
- @end verbatim
- @end lisp
- To inspect Scheme values from JavaScript, Hoot provides the
- @file{js-runtime/reflect.js} library. Copy that file and its
- associated Wasm helper modules, @file{js-runtime/reflect.wasm} and
- @file{js-runtime/wtf8.wasm}, to the @file{hoot-tutorial} directory:
- @example
- cd /path/to/hoot-tutorial
- cp /path/to/guile-hoot/js-runtime/reflect.js .
- mkdir js-runtime
- cp /path/to/guile-hoot/js-runtime/reflect.wasm js-runtime/
- cp /path/to/guile-hoot/js-runtime/wtf8.wasm js-runtime/
- @end example
- To run @file{hello.wasm}, we need a little JavaScript glue code.
- Let's call this @file{hello.js}:
- @example
- @verbatim
- async function load() {
- const [message] = await Scheme.load_main("hello.wasm", {});
- console.log(message);
- }
- window.addEventListener("load", load);
- @end verbatim
- @end example
- We also need a minimal @file{index.html} web page to bring it all
- together:
- @example
- <!DOCTYPE html>
- <html>
- <head>
- <script type="text/javascript" src="reflect.js"></script>
- <script type="text/javascript" src="hello.js"></script>
- </head>
- <body>
- Guile is a hoot!
- </body>
- </html>
- @end example
- The file tree in @file{hoot-tutorial} should look like this:
- @example
- ./js-runtime
- ./js-runtime/wtf8.wasm
- ./js-runtime/reflect.wasm
- ./reflect.js
- ./hello.js
- ./index.html
- ./hello.wasm
- @end example
- Finally, we need a local web server to serve the files. Fortunately,
- Guile includes all the building blocks we need to make a minimal one
- for the purposes of this tutorial. Save the following to
- @file{web-server.scm}:
- @lisp
- (use-modules (ice-9 binary-ports) (ice-9 format) (ice-9 match)
- (web server) (web request) (web response) (web uri))
- (define (extension file)
- (match (string-split file #\.)
- (() #f)
- ((_ ... ext) ext)))
- (define (mime-type file-name)
- (or (assoc-ref '(("js" . application/javascript)
- ("html" . text/html)
- ("wasm" . application/wasm))
- (extension file-name))
- 'text/plain))
- (define (render-file file-name)
- (values `((content-type . (,(mime-type file-name))))
- (call-with-input-file file-name get-bytevector-all)))
- (define (not-found path)
- (values (build-response #:code 404) (string-append "Not found: " path)))
- (define (directory? file-name)
- (eq? (stat:type (stat file-name)) 'directory))
- (define (serve-file path)
- (let ((f (string-append (getcwd) (uri-decode path))))
- (if (and (file-exists? f) (not (directory? f)))
- (render-file f)
- (not-found path))))
- (define (handle-request request body)
- (let ((method (request-method request))
- (path (uri-path (request-uri request))))
- (format #t "~a ~a\n" method path)
- (serve-file path)))
- (run-server handle-request 'http '(#:port 8080))
- @end lisp
- Start the web server like so:
- @example
- guile web-server.scm
- @end example
- Visit @url{http://localhost:8080/index.html} in your web browser, and
- if it is new enough to have Wasm GC enabled, you should see the text
- ``Hello, world!'' printed in the developer console.
- We hope this tutorial has helped you get started with Hoot! Read on
- for full API documentation.
- @node Compiling to Wasm
- @chapter Compiling to Wasm
- @menu
- * Invoking the compiler:: Compiling a Hoot module.
- * High-level development tools:: Using Hoot modules from the host environment.
- * Low-level development tools:: Inspecting and debugging Wasm.
- @end menu
- @node Invoking the compiler
- @section Invoking the compiler
- In Guile's compiler tower, Scheme code goes through several
- transformations before being compiled to VM bytecode. Scheme is
- lowered to @ref{Tree-IL,,,Guile Reference}, which is then lowered to
- @ref{Continuation Passing Style,,,Guile Reference}(CPS), and then
- finally to @ref{Bytecode,,,Guile Reference}. Hoot adds an additional
- backend that compiles CPS to Wasm.
- Currently, Hoot does not use Guile's module system and instead peforms
- whole program compilation using a prelude that provides R7RS-small
- features (and also some Guile ones, like prompts.) Support for
- modules will be added in a future release.
- For hooking the Hoot compiler up to a build system such as GNU Make,
- invoke the @command{guild compile-wasm} tool:
- @example
- guild compile-wasm -o foo.wasm foo.scm
- @end example
- When writing Scheme intended to be compiled to the Wasm target, a
- special form called @code{%inline-wasm} is available for implementing
- a section of code in WAT rather than Scheme. For example, here's how
- @code{port?} is implemented:
- @lisp
- (define (port? x)
- (%inline-wasm '(func (param $obj (ref eq))
- (result (ref eq))
- (if (ref eq)
- (ref.test $port (local.get $obj))
- (then (ref.i31 (i32.const 17)))
- (else (ref.i31 (i32.const 1)))))
- x))
- @end lisp
- An inline Wasm form specifies a single function using WAT expressions
- whose parameters and result types are all @code{(ref eq)}, as well as
- the Scheme variables that map to the parameters. The compiler then
- transforms and splices the function body into the procedure.
- Like Guile's built-in compiler, the Hoot compiler can also be invoked
- within Scheme. The @code{(hoot compile)} module provides the
- interface to the Wasm compiler backend.
- @deffn {Procedure} compile exp [#:import-abi? #f] [#:export-abi? #t] @
- [#:from (current-language)] @
- [#:env (default-hoot-environment)] @
- [#:optimization-level (default-optimization-level)] @
- [#:warning-level (default-warning-level)] @
- [#:dump-cps? #f] [#:dump-wasm? #f] [#:debug? #f] @
- [#:opts '()]
- Compile the Scheme expression @var{exp} to Wasm and return a Wasm
- module. When @var{import-abi?} is @code{#t}, the Wasm module will be
- built such that it needs to import its ABI from another module. When
- @var{export-abi?} is @code{#t}, the Wasm module will be built such
- that it exports its ABI functions. A typical use of these flags is to
- export the ABI from one ``main'' module and then import that ABI into
- any additional modules that are being used.
- When @var{debug?} is @code{#t} then debugging information will be
- generated. By default, this is turned off as it greatly increases
- binary size.
- @xref{Compiling Scheme Code,,,Guile Reference} for more information
- about invoking Guile's compiler.
- @end deffn
- @deffn {Procedure} read-and-compile port [#:import-abi? #f] [#:export-abi? #t] @
- [#:from (current-language)] @
- [#:env (default-hoot-environment)] @
- [#:optimization-level (default-optimization-level)] @
- [#:warning-level (default-warning-level)] @
- [#:dump-cps? #f] [#:dump-wasm? #f] [#:debug? #f] @
- [#:opts '()]
- Like @code{compile}, but read the Scheme expression from @var{port}.
- @end deffn
- @deffn {Procedure} compile-file input-file [#:import-abi? #f] @
- [#:export-abi? #t] @
- [#:from (current-language)] @
- [#:env (default-hoot-environment)] @
- [#:optimization-level (default-optimization-level)] @
- [#:warning-level (default-warning-level)] @
- [#:dump-cps? #f] [#:dump-wasm? #f] [#:debug? #f] @
- [#:opts '()]
- Like @code{compile}, but read the Scheme expression from
- @var{input-file}.
- @end deffn
- @node High-level development tools
- @section High-level development tools
- The @code{(hoot reflect)} module provides an interface for inspecting
- and manipulating Scheme values that live within Wasm modules. This is
- the primary interface for testing compiler output directly from Guile.
- @deffn {Procedure} hoot-instantiate scheme-wasm [imports '()] [reflector]
- Instantiate and return a new Hoot module using the compiled Scheme
- Wasm module @var{scheme-wasm} and the reflection module
- @var{reflector}. If @var{reflector} is not specified, a new reflector
- instance will be created.
- Optionally, @var{imports} may contain a 2-tier association list
- structure of imported functions, globals, tables, and memories:
- @lisp
- `(("math" . (("random" . ,(lambda (x) (random x))))))
- @end lisp
- @end deffn
- @deffn {Procedure} hoot-load module
- Invoke the load thunk of @var{module} and return the reflected
- result values.
- @end deffn
- @deffn {Procedure} compile-value exp [imports '()]
- Compile @var{exp} and return the result.
- Optionally, @var{imports} may contain a 2-tier association list
- structure of imported functions, globals, tables, and memories. See
- @code{hoot-instantiate} for an example of such a structure.
- @end deffn
- @deffn {Procedure} compile-call proc-exp arg-exps ...
- Compile @var{proc-exp} and all @var{arg-exps}, call the procedure with
- the arguments, then return the results.
- @end deffn
- @deffn {Procedure} hoot-module? obj
- Return @code{#t} if @var{obj} is a Hoot module.
- @end deffn
- @deffn {Procedure} hoot-module-reflector module
- Return the reflection module for @var{module}.
- @end deffn
- @deffn {Procedure} hoot-module-instance module
- Return the Wasm instance for @var{module}.
- @end deffn
- @deffn {Procedure} reflector? obj
- Return @code{#t} if @var{obj} is a reflector.
- @end deffn
- @deffn {Procedure} reflector-instance reflector
- Return the Wasm instance of @var{reflector}.
- @end deffn
- @deffn {Procedure} reflector-abi reflector
- Return the association list of ABI imports for @var{reflector}.
- @end deffn
- @deffn {Procedure} hoot-object? obj
- Return @code{#t} if @var{obj} is a Hoot object.
- @end deffn
- @deffn {Procedure} hoot-complex? obj
- Return @code{#t} if @var{obj} is a Hoot complex number.
- @end deffn
- @deffn {Procedure} hoot-complex-real complex
- Return the real part of @var{complex}.
- @end deffn
- @deffn {Procedure} hoot-complex-imag complex
- Return the imaginary part of @var{complex}.
- @end deffn
- @deffn {Procedure} hoot-fraction? obj
- Return @code{#t} if @var{obj} is a Hoot fraction.
- @end deffn
- @deffn {Procedure} hoot-fraction-num fraction
- Return the numerator of @var{fraction}
- @end deffn
- @deffn {Procedure} hoot-fraction-denom fraction
- Return the denominator of @var{fraction}.
- @end deffn
- @deffn {Procedure} hoot-pair? obj
- Return @code{#t} if @var{obj} is a Hoot pair.
- @end deffn
- @deffn {Procedure} mutable-hoot-pair? obj
- Return @code{#t} if @var{obj} is a mutable Hoot pair.
- @end deffn
- @deffn {Procedure} hoot-pair-car pair
- Return the first element of @var{pair}.
- @end deffn
- @deffn {Procedure} hoot-pair-cdr pair
- Return the second element of @var{pair}.
- @end deffn
- @deffn {Procedure} hoot-vector? obj
- Return @code{#t} if @var{obj} is a Hoot vector.
- @end deffn
- @deffn {Procedure} mutable-hoot-vector? obj
- Return @code{#t} if @var{obj} is a mutable Hoot vector.
- @end deffn
- @deffn {Procedure} hoot-vector-length vec
- Return the length of @var{vec}.
- @end deffn
- @deffn {Procedure} hoot-vector-ref vec i
- Return the @var{i}th element of @var{vec}.
- @end deffn
- @deffn {Procedure} hoot-bytevector? obj
- Return @code{#t} if @var{obj} is a Hoot bytevector.
- @end deffn
- @deffn {Procedure} mutable-hoot-bytevector? obj
- Return @code{#t} if @var{obj} is a mutable Hoot bytevector.
- @end deffn
- @deffn {Procedure} hoot-bytevector-length bv
- Return the length of @var{bv}.
- @end deffn
- @deffn {Procedure} hoot-bytevector-ref bv i
- Return the @var{i}th byte of @var{bv}.
- @end deffn
- @deffn {Procedure} hoot-bitvector? obj
- Return @code{#t} if @var{obj} is a Hoot bitvector.
- @end deffn
- @deffn {Procedure} mutable-hoot-bitvector? obj
- Return @code{#t} if @var{obj} is a mutable Hoot bitvector.
- @end deffn
- @deffn {Procedure} hoot-bitvector-length bv
- Return the length of @var{bv}.
- @end deffn
- @deffn {Procedure} hoot-bitvector-ref bv i
- Return the @var{i}th bit of @var{bv}.
- @end deffn
- @deffn {Procedure} hoot-symbol? obj
- Return @code{#t} if @var{obj} is a Hoot symbol.
- @end deffn
- @deffn {Procedure} hoot-symbol-name sym
- Return the string name of @var{sym}.
- @end deffn
- @deffn {Procedure} hoot-keyword? obj
- Return @code{#t} if @var{obj} is a Hoot keyword.
- @end deffn
- @deffn {Procedure} hoot-keyword-name keyword
- Return the name string of @var{keyword}.
- @end deffn
- @deffn {Procedure} mutable-hoot-string? obj
- Return @code{#t} if @var{obj} is a mutable Hoot string.
- @end deffn
- @deffn {Procedure} mutable-hoot-string->string str
- Return the underlying string for @var{str}.
- @end deffn
- @deffn {Procedure} hoot-procedure? obj
- Return @code{#t} if @var{obj} is a Hoot procedure.
- @end deffn
- @deffn {Procedure} hoot-variable? obj
- Return @code{#t} if @var{obj} is a Hoot variable.
- @end deffn
- @deffn {Procedure} hoot-atomic-box? obj
- Return @code{#t} if @var{obj} is a Hoot atomic box.
- @end deffn
- @deffn {Procedure} hoot-hash-table? obj
- Return @code{#t} if @var{obj} is a Hoot hash table.
- @end deffn
- @deffn {Procedure} hoot-weak-table? obj
- Return @code{#t} if @var{obj} is a Hoot weak table.
- @end deffn
- @deffn {Procedure} hoot-fluid? obj
- Return @code{#t} if @var{obj} is a Hoot fluid.
- @end deffn
- @deffn {Procedure} hoot-dynamic-state? obj
- Return @code{#t} if @var{obj} is a Hoot dynamic state.
- @end deffn
- @deffn {Procedure} hoot-syntax? obj
- Return @code{#t} if @var{obj} is a Hoot syntax object.
- @end deffn
- @deffn {Procedure} hoot-port? obj
- Return @code{#t} if @var{obj} is a Hoot port.
- @end deffn
- @deffn {Procedure} hoot-struct? obj
- Return @code{#t} if @var{obj} is a Hoot struct.
- @end deffn
- @node Low-level development tools
- @section Low-level development tools
- The @code{(hoot repl)} module provides a set of REPL commands to
- assist with inspecting and debugging Wasm modules. As a matter of
- course, Hoot's Scheme compiler @emph{should not} cause low-level Wasm
- runtime errors, but when it does, or when working with the Wasm
- toolchain directly, these REPL tools may provide some assistance.
- To install the REPL commands, simply import the module:
- @lisp
- scheme@@(guile-user)> ,use (hoot repl)
- @end lisp
- To see a list of all the Wasm commands, run:
- @lisp
- scheme@@(guile-user)> ,help wasm
- @end lisp
- To demonstrate the debugging features, let's create a trivial module
- with a buggy function:
- @lisp
- @verbatim
- scheme@(guile-user)> (define src
- '(module
- (func (export "main") (param $x i32) (result i32)
- (i32.add (local.get $x)
- (unreachable)))))
- @end verbatim
- @end lisp
- When called, this function will hit the @code{unreachable} instruction
- and throw a runtime error. Let's compile the WAT source, load it into
- the VM, and get a reference to the @code{main} function:
- @lisp
- @verbatim
- scheme@(guile-user)> ,use (wasm resolve) (wasm vm) (wasm wat)
- scheme@(guile-user)> (define wasm (validate-wasm (resolve-wasm (wat->wasm src))))
- scheme@(guile-user)> (define instance (instantiate-wasm wasm))
- scheme@(guile-user)> (define main (wasm-instance-export-ref instance "main"))
- @end verbatim
- @end lisp
- To trap the Wasm runtime error and open a Wasm debugging REPL, the
- @command{wasm-catch} REPL command can be prefixed before an
- expression:
- @lisp
- @verbatim
- scheme@(guile-user)> ,wasm-catch (main 7)
- ice-9/boot-9.scm:1674:22: In procedure raise-exception:
- ERROR:
- 1. &wasm-runtime-error:
- instruction: (unreachable)
- position: (func 0 1)
- instance: #<wasm-instance 140506559041920>
- stack: #<<wasm-stack> items: (7)>
- blocks: ((wasm-block))
- locals: #(7)
- 2. &message: "Wasm runtime error: unreachable"
- 3. &irritants: ()
- Entering Wasm debug prompt. Type `,help wasm' for info or `,q' to continue.
- scheme@(guile-user) [1]>
- @end verbatim
- @end lisp
- Once in a Wasm debug context, many of the other REPL commands become
- usable. To highlight the instruction where execution has paused, use
- @command{wasm-pos}:
- @lisp
- @verbatim
- scheme@(guile-user) [1]> ,wasm-pos
- (func 0 (param $x i32) (result i32)
- (local.get 0)
- <<< (unreachable) >>>
- (i32.add))
- @end verbatim
- @end lisp
- To print the contents of the values stack, use @command{wasm-stack}:
- @lisp
- @verbatim
- scheme@(guile-user) [1]> ,wasm-stack
- Value stack:
- 0: 7
- @end verbatim
- @end lisp
- To print the contents of the function locals, use @command{wasm-locals}:
- @lisp
- @verbatim
- scheme@(guile-user) [1]> ,wasm-locals
- Locals:
- 0: 7
- @end verbatim
- @end lisp
- To evaluate arbitary Wasm instructions in the current context, either
- in an attempt to repair interpreter state or just for fun, use
- @command{wasm-eval}:
- @lisp
- @verbatim
- scheme@(guile-user) [1]> ,wasm-eval '(local.get 0)
- scheme@(guile-user) [1]> ,wasm-stack
- Value stack:
- 0: 7
- 1: 7
- @end verbatim
- @end lisp
- There are now two i32 values on the stack. If we were to proceed with
- execution, the next instruction, @code{i32.add}, should add them
- together and return a result of 14. To resume execution, use
- @command{wasm-continue}:
- @lisp
- @verbatim
- scheme@(guile-user) [1]> ,wasm-continue
- $5 = 14
- @end verbatim
- @end lisp
- Evaluating arbitrary Wasm commands in a debugging context is very
- helpful when trying to understand the nature of a bug, but bear in
- mind that cursed things may happen during the process as there is no
- validation applied. This goes especially for when you try to resume
- execution.
- See @ref{Interpreter} for detailed information on running Wasm within
- Guile and @ref{Toolchain reference} in general for working with Wasm
- directly.
- @deffn {REPL Command} wasm-trace exp
- Evaluate @var{exp} with verbose Wasm tracing enabled. This will print
- out every instruction along with the state of the value stack and
- function locals at the time of evaluation.
- @end deffn
- @deffn {REPL Command} wasm-freq exp
- Evaluate @var{exp} and print out a table showing how many times each
- kind of Wasm instruction was executed as well as a total instruction
- count.
- @end deffn
- @deffn {REPL Command} wasm-catch exp
- Catch and debug Wasm runtime errors that are raised by evaluating
- @var{exp}.
- @end deffn
- The following commands are usable only in the context of a Wasm debug
- REPL:
- @deffn {REPL Command} wasm-stack
- Print the state of the Wasm stack.
- @end deffn
- @deffn {REPL Command} wasm-locals
- Print the state of the Wasm function locals.
- @end deffn
- @deffn {REPL Command} wasm-pos
- Print the current function disassembly and highlight the instruction
- where Wasm execution has paused.
- @end deffn
- @deffn {REPL Command} wasm-eval instr
- Evaluate the Wasm instruction @var{instr} in the current debug
- context. Use this when attempting to fix the state of the Wasm stack
- or locals before attempting to resume with @code{,wasm-continue}.
- @end deffn
- The following commands behave differently depending on if they are run
- within a Wasm debug REPL or not.
- @deffn {REPL Command} wasm-dump [wasm]
- Display information about @var{wasm}, or the current Wasm instance
- when debugging.
- @end deffn
- @deffn {REPL Command} wasm-disassemble [wasm]
- Display the disassembly of @var{wasm}, or the current Wasm instance
- when debugging, in WAT form.
- @end deffn
- @deffn {REPL Command} wasm-continue
- When in a debugger, exit and resume Wasm execution. In the event that
- this is run after trapping a runtime error, your warranty is void and
- all bets are off! While it may be dangerous, this does allow one to
- manually fix the Wasm interpreter state manually with
- @code{,wasm-eval} and attempt to proceed, which can come in handy
- sometimes.
- When not in a debugger, set the Wasm execution mode to continue
- without interruption. In other words, deactive the instruction
- stepper if it is active.
- @end deffn
- @deffn {REPL Command} wasm-step
- When in a debugger, resume Wasm execution but pause before the next
- instruction is evaluated.
- When not in a debugger, set Wasm execution to pause before each
- instruction is evaluated.
- @end deffn
- @node Web deployment
- @chapter Web deployment
- On the client-side web, JavaScript is the host environment for Wasm
- modules and the
- @url{https://developer.mozilla.org/en-US/docs/WebAssembly,WebAssembly}
- API is used to load and run them. Hoot includes a JavaScript library,
- @file{reflect.js} that wraps the @code{WebAssembly} API and
- furthermore can inspect Scheme values and call Scheme procedures.
- This chapter documents deploying Hoot artifacts and using the
- reflection API to run Scheme in the browser.
- @menu
- * Web server setup:: Prepare a server to run Hoot programs.
- * JavaScript API reference:: JavaScript reflection interface.
- @end menu
- @node Web server setup
- @section Web server setup
- In order to run Hoot binaries in the browser, a web server needs to
- host a copy of the Hoot JavaScript runtime.
- The runtime files can be found in the
- @file{$prefix/share/guile-hoot/js-runtime} directory, where
- @code{$prefix} is the directory where Hoot was installed on your
- system. This is typically @file{/usr} or @file{/usr/local} on Linux
- distributions such as Debian, Ubuntu, Fedora, etc.
- Don't forget to upload the Wasm files for the Scheme programs, too!
- A bit of JavaScript code is needed to bootstrap a Scheme program using
- the @file{js-runtime/reflect.js} library. For example, here's an
- example @file{boot.js} file that runs the Scheme program
- @file{hello.wasm} and prints the return values:
- @example
- @verbatim
- window.addEventListener("load", async () => {
- const results = await Scheme.load_main("/hello.wasm", {});
- console.log(results);
- });
- @end verbatim
- @end example
- The @code{Scheme} namespace is defined in @file{reflect.js}.
- @xref{JavaScript API reference} for more information.
- To run @file{boot.js} on a web page, add @code{<script>} tags for it
- and @file{reflect.js}:
- @example
- <!DOCTYPE html>
- <html>
- <head>
- <script type="text/javascript" src="/js-runtime/reflect.js"></script>
- <script type="text/javascript" src="/boot.js"></script>
- </head>
- <body>
- <h1>Hello, Hoot!</h1>
- </body>
- </html>
- @end example
- @node JavaScript API reference
- @section JavaScript API reference
- The @code{Scheme} class is used to load a Hoot binary, start the
- program, and initialize reflection.
- @deftp {Class} Scheme
- A Scheme runtime environment.
- @end deftp
- @defop {Static method} Scheme load_main path abi [user_imports @code{@{@}}]
- Fetch and execute the Hoot Wasm binary at the URL @var{path} and
- return an array of Scheme values produced by the program.
- The @var{abi} parameter is for more advanced usage where multiple Hoot
- binaries share a single application binary interface (ABI). This
- should be set to @code{@{@}} when loading the first Scheme binary. It
- is better to use the @code{load_extension} method for subsequent
- binaries, though.
- The @var{user_imports} parameter is for providing concrete
- implementations of functions declared using the @ref{Foreign function
- interface}. It uses a two-tier nested object structure to map import
- names to the functions that implement them.
- For example, this Scheme code:
- @lisp
- (define-foreign make-text-node
- "document" "createTextNode"
- (ref string) -> (ref null extern))
- @end lisp
- Could be instantiated like so:
- @example
- @verbatim
- Scheme.load_main("hello.wasm", {}, {
- document: {
- createTextNode: Document.prototype.createTextNode.bind(document)
- }
- });
- @end verbatim
- @end example
- @end defop
- @defmethod Scheme load_extension path [user_imports @code{@{@}}]
- Fetch and load an additional Hoot binary at the URL @var{path} that
- shares the ABI of @code{this}. Optionally, a set of user-defined
- imported functions can be specified with the @var{user_imports}
- parameter.
- @end defmethod
- All of the fundamental Scheme types have an associated JavaScript
- class that can reflect their values. Calling the @code{repr} function
- on an instance of a reflected Scheme object will return a Scheme-like
- printing of the object.
- @example
- repr(pair) // => "(1 . 2)"
- @end example
- @deftp {Class} Char
- A Unicode character.
- @end deftp
- @deftp {Class} Eof
- End-of-file object.
- @end deftp
- @deftp {Class} Null
- The empty list.
- @end deftp
- @deftp {Class} Unspecified
- The unspecified value.
- @end deftp
- @deftp {Class} Complex real imag
- Complex number with real part @var{real} and imaginary part
- @var{imag}.
- @end deftp
- @deftp {Class} Fraction num denom
- An exact fraction with numerator @var{num} and denominator
- @var{denom}.
- @end deftp
- The @code{HeapObject} class is the parent class of all of the
- remaining Scheme types.
- @deftp {Class} HeapObject
- A Scheme heap object.
- @end deftp
- @defivar HeapObject reflector
- The reflector for @code{this}, an instance of the @code{Scheme} class.
- @end defivar
- The @code{reflector} property can be used in conjuction with the
- @code{load_extension} method to load additional Hoot binaries that
- share the same ABI.
- @example
- heapObject.reflector.load_extension("/helper.wasm")
- @end example
- @deftp {Class} Procedure
- A Scheme procedure.
- @end deftp
- Procedure instances can be invoked with the @code{call} method to
- perform a Javascript to Scheme function call.
- @defmethod Procedure call args@dots{}
- Call procedure with @var{args} and return an array of result values.
- @end defmethod
- @deftp {Class} Pair
- An immutable cons cell.
- @end deftp
- @deftp {Class} MutablePair
- A mutable cons cell.
- @end deftp
- @deftp {Class} Vector
- An immutable vector.
- @end deftp
- @deftp {Class} MutableVector
- A mutable vector.
- @end deftp
- @deftp {Class} Bytevector
- An immutable bytevector.
- @end deftp
- @deftp {Class} MutableBytevector
- A mutable bytevector.
- @end deftp
- @deftp {Class} Bitvector
- An immutable bitvector.
- @end deftp
- @deftp {Class} MutableBitvector
- A mutable bitvector.
- @end deftp
- @deftp {Class} MutableString
- A mutable string.
- @end deftp
- @deftp {Class} Sym
- A symbol.
- @end deftp
- @deftp {Class} Keyword
- A keyword.
- @end deftp
- @deftp {Class} Variable
- A mutable variable.
- @end deftp
- @deftp {Class} AtomicBox
- A mutable box with atomic updates.
- @end deftp
- @deftp {Class} HashTable
- A hash table.
- @end deftp
- @deftp {Class} WeakTable
- A weak key hash table.
- @end deftp
- @deftp {Class} Fluid
- A dynamic variable.
- @end deftp
- @deftp {Class} DynamicState
- A set of fluids.
- @end deftp
- @deftp {Class} Syntax
- A syntax object.
- @end deftp
- @deftp {Class} Port
- An I/O port.
- @end deftp
- @deftp {Class} Struct
- A user-defined structure.
- @end deftp
- @node Scheme reference
- @chapter Scheme reference
- In addition to supporting standard Scheme and Guile-specific features,
- Hoot includes some of its own Scheme extensions. This chapter
- documents the APIs of these extensions.
- @menu
- * Hash tables:: Mutable key/value data structures.
- * Foreign function interface:: Call host functions from Scheme.
- @end menu
- @node Hash tables
- @section Hash tables
- There are many mutable hashtable APIs amongst all the various Scheme
- implementations, standards, and SRFIs. From our point of view, there
- is no clear ``best'' hashtable API that has emerged, but we think the
- R6RS interface is OK. Guile's own hashtable API has design issues
- that are best left in the past. So, the Hoot hashtable API is
- R6RS-like.
- Currently, this interface supports keys hashed by object identity
- (@code{eq?}) only. The object equivalence (@code{eqv?}) and deep
- object equality (@code{equal?}) hashing strategies are not yet
- implemented.
- @deffn {Procedure} make-eq-hashtable
- Return a new, empty hash table.
- @end deffn
- @deffn {Procedure} hashtable? obj
- Return @code{#t} if @var{obj}
- @end deffn
- @deffn {Procedure} hashtable-size hashtable
- Return the number of key/value pairs in @var{hashtable}.
- @end deffn
- @deffn {Procedure} hashtable-ref hashtable key default
- Return the value associated with @var{key} in @var{hashtable} or
- @var{default} if there is no such association.
- @end deffn
- @deffn {Procedure} hashtable-set! hashtable key value
- Modify @var{hashtable} to associate @var{key} with @var{value},
- overwriting any previous association that may have existed.
- @end deffn
- @deffn {Procedure} hashtable-delete! hashtable key
- Remove the association with @var{key} in @var{hashtable}, if one
- exists.
- @end deffn
- @deffn {Procedure} hashtable-clear! hashtable
- Remove all of the key/value associations in @var{hashtable}.
- @end deffn
- @deffn {Procedure} hashtable-contains? hashtable key
- Return @code{#t} if @var{key} has an associated value in
- @var{hashtable}.
- @end deffn
- @deffn {Procedure} hashtable-copy hashtable
- Return a copy of @var{hashtable}.
- @end deffn
- @deffn {Procedure} hashtable-keys hashtable
- Return a vector of keys in @var{hashtable}.
- @end deffn
- @deffn {Procedure} hashtable-entries hashtable
- Return a vector of values in @var{hashtable}.
- @end deffn
- @deffn {Procedure} hashtable-for-each proc hashtable
- For each key/value pair in @var{hashtable}, call @var{proc} with two
- arguments: the key and the value.
- @end deffn
- Hoot also includes weak key hash tables that wrap those of the Wasm
- host platform, such as the @code{WeakMap} JavaScript class on the web.
- @deffn {Procedure} make-weak-key-hashtable
- Return a new weak key hashtable.
- @end deffn
- @deffn {Procedure} weak-key-hashtable? obj
- Return @code{#t} if @var{obj} is a weak key hashtable.
- @end deffn
- @deffn {Procedure} weak-key-hashtable-ref hashtable key [default #f]
- Return the value associated with @var{key} in @var{hashtable} or
- @var{default} if there is no such association.
- @end deffn
- @deffn {Procedure} weak-key-hashtable-set! hashtable key value
- Modify @var{hashtable} to associate @var{key} with @var{value},
- overwriting any previous association that may have existed.
- @end deffn
- @deffn {Procedure} weak-key-hashtable-delete! hashtable key
- Remove the association with @var{key} in @var{hashtable}, if one
- exists.
- @end deffn
- @node Foreign function interface
- @section Foreign function interface
- WebAssembly follows the capability security model, which means that
- modules cannot do much on their own. Wasm modules are guests within a
- host. They must be given capabilities by the host in order to
- interact with the outside world. Modules request capabilities by
- declaring imports, which the host then fills out with concrete
- implementations at instantiation time. Hoot provides a foreign
- function interface (FFI) to embed these import declarations within
- Scheme code.
- The @code{define-foreign} form declares an import with a given type
- signature (Wasm is statically typed) and defines a procedure for
- calling it. The FFI takes care of converting Scheme values to Wasm
- values and vice versa. For example, declaring an import for creating
- text nodes in a web browser could look like this:
- @lisp
- (define-foreign make-text-node
- "document" "createTextNode"
- (ref string) -> (ref null extern))
- @end lisp
- In the above example, the procedure is bound to the variable
- @code{make-text-node}. In the Wasm binary, this import is named
- ``createTextNode'' and resides in the ``document'' namespace of the
- import table. A Wasm host is expected to satisfy this import by
- providing a function that accepts one argument, a string, and returns
- an arbitary host value which may be null.
- Note that declaring an import @emph{does not} do anything to bind that
- import to an implementation on the host. The Wasm guest cannot grant
- capabilities unto itself. Furthermore, the host could be any Wasm
- runtime, so the actual implementation will vary. In the context of a
- web browser, the JavaScript code that instantiates a module with this
- import could look like this:
- @example
- @verbatim
- Scheme.load_main("hello.wasm", {}, {
- document: {
- createTextNode: Document.prototype.createTextNode.bind(document)
- }
- });
- @end verbatim
- @end example
- And here's what it might look like when using the Hoot interpreter:
- @lisp
- (use-modules (hoot reflect))
- (hoot-instantiate (call-with-input-file "hello.wasm" parse-wasm)
- `(("document" .
- (("createTextNode" . ,(lambda (str) `(text ,str)))))))
- @end lisp
- Once defined, @code{make-text-node} can be called like any other
- procedure:
- @lisp
- (define text-node (make-text-node "Hello, world!"))
- @end lisp
- Since the return type of @code{make-text-node} is @code{(ref null
- extern}), the value of @code{text-node} is an @emph{external
- reference}. To check if a value is an external reference, use the
- @code{external?} predicate:
- @lisp
- (external? text-node) ; => #t
- @end lisp
- External references may be null, which could indicate failure, a cache
- miss, etc. To check if an external value is null, use the
- @code{external-null?} predicate:
- @lisp
- (if (external-null? text-node) 'yay 'uh-oh)
- @end lisp
- @deffn {Syntax} define-foreign scheme-name namespace import-name param-types ... -> result-type
- Define @var{scheme-name}, a procedure wrapping the Wasm import
- @var{import-name} in the namespace @var{namespace}.
- The signature of the function is specified by @var{param-types} and
- @var{result-type}, which are all Wasm types expressed in WAT form.
- Valid parameter types are:
- @itemize
- @item i32: 32-bit integer
- @item i64: 64-bit integer
- @item f32: 32-bit float
- @item f64: 64-bit float
- @item (ref string): a string
- @item (ref null extern): an external reference
- @item (ref eq): any Scheme value
- @end itemize
- Valid result types are:
- @itemize
- @item none: no return value
- @item i32: 32-bit integer
- @item i64: 64-bit integer
- @item f32: 32-bit float
- @item f64: 64-bit float
- @item (ref string): a string
- @item (ref null extern): an external reference
- @end itemize
- @end deffn
- @deffn {Procedure} external? obj
- Return @code{#t} if @var{obj} is an external reference.
- @end deffn
- @deffn {Procedure} external-null? extern
- Return @code{#t} if @var{extern} is null.
- @end deffn
- @node Toolchain reference
- @chapter Toolchain reference
- Hoot is not just a Scheme to Wasm compiler. It's also a
- self-contained and general purpose Wasm toolchain. Hoot does not use
- binaryen, wabt, emscripten, etc. in order to assemble and disassemble
- Wasm. The entire toolchain is implemented as a set of Scheme modules
- that can be used to automate other Wasm targeted build workflows.
- Since everything is implemented in one place, in a single language,
- and because Guile encourages a REPL-driven development workflow, Hoot
- makes a great platform for learning Wasm in a hands-on, interactive
- way!
- @menu
- * Data types:: Core Wasm module data types.
- * GWAT:: Guile-flavored WebAssembly Text format.
- * Resolver:: Lower human-readable identifiers to index values.
- * Linker:: Add a standard library to a Wasm module.
- * Assembler:: Create Wasm binaries.
- * Binary Parser:: Parse Wasm binaries.
- * Printer:: Print the contents of a Wasm module.
- * Interpreter:: Execute Wasm within Guile.
- @end menu
- @node Data types
- @section Data types
- The @code{(wasm types)} module contains all the core data types that
- comprise a Wasm module.
- @subsection Modules
- The Wasm module type is the top type, incorporating values of all the
- types that are to follow.
- @deffn {Procedure} wasm? obj
- Return @code{#t} if @var{obj} is a Wasm module.
- @end deffn
- @deffn {Procedure} wasm-id wasm
- Return the symbolic ID of @var{wasm}.
- @end deffn
- @deffn {Procedure} wasm-types wasm
- Return the list of types in @var{wasm}.
- @end deffn
- @deffn {Procedure} wasm-imports wasm
- Return the list of imports in @var{wasm}.
- @end deffn
- @deffn {Procedure} wasm-funcs wasm
- Return the list of functions in @var{wasm}.
- @end deffn
- @deffn {Procedure} wasm-tables wasm
- Return the list of tables in @var{wasm}.
- @end deffn
- @deffn {Procedure} wasm-memories wasm
- Return the list of memories in @var{wasm}.
- @end deffn
- @deffn {Procedure} wasm-globals wasm
- Return the list of globals in @var{wasm}.
- @end deffn
- @deffn {Procedure} wasm-exports wasm
- Return the list of exports in @var{wasm}.
- @end deffn
- @deffn {Procedure} wasm-elems wasm
- Return the list of element segments in @var{wasm}.
- @end deffn
- @deffn {Procedure} wasm-datas wasm
- Return the list of data segments in @var{wasm}.
- @end deffn
- @deffn {Procedure} wasm-tags wasm
- Return the list of tags in @var{wasm}.
- @end deffn
- @deffn {Procedure} wasm-strings wasm
- Return the list of strings in @var{wasm}.
- @end deffn
- @deffn {Procedure} wasm-custom wasm
- Return the list of custom segments in @var{wasm}.
- @end deffn
- @deffn {Procedure} wasm-start wasm
- Return the start function index for @var{wasm}.
- @end deffn
- @subsection Types
- Wasm has four numeric types:
- @enumerate
- @item @code{i32}:
- 32-bit integer (signed or unsigned)
- @item @code{i64}:
- 64-bit integer (signed or unsigned)
- @item @code{f32}:
- 32-bit single precision IEEE floating point number.
- @item @code{f64}:
- 64-bit double precision IEEE floating point number.
- @end enumerate
- There is also the @code{v128} vector type, but it is currently
- unsupported.
- Then there are a number of reference types that fall into 3
- categories: function, external, and internal.
- Function reference types:
- @enumerate
- @item @code{func}:
- Function reference.
- @item @code{nofunc}:
- Bottom type for functions. No function is of type @code{nofunc}.
- @end enumerate
- External reference types:
- @enumerate
- @item @code{extern}:
- External reference introduced by the host.
- @item @code{noextern}:
- Bottom type for external references. No external reference is of type
- @code{noextern}.
- @end enumerate
- Internal reference types:
- @enumerate
- @item @code{any}:
- The top type of all internal reference types.
- @item @code{eq}:
- Structural equivalence type. Subtype of @code{all}.
- @item @code{i31}:
- Used for immediate references (such as the empty list or fixnums in
- Scheme.) Subtype of @code{eq}.
- @item @code{array}:
- Super type of all array types. Subtype of @code{eq}.
- @item @code{struct}:
- Super type of all struct types. Subtype of @code{eq}.
- @item @code{none}:
- The bottom type for internal references. No internal reference is of
- type @code{none}.
- @end enumerate
- Of course, modules may specify their own compound types assembled from
- these primitives.
- The type hierarchy looks like this:
- @verbatim
- .-----. .-------. .---------.
- .--------- | any | ------------. | func | | extern |
- | `-----' | `-------' `---------'
- ↓ ↓ ↓ ↓ ↓
- .-----. .-----. .---------. .-----------. .-----------.
- | i31 | | eq | | struct | | all funcs | | noextern |
- `-----' `-----' `---------' `-----------' `-----------'
- ↓ ↓ ↓
- .-------------. .-------------. .---------.
- | all arrays | | all structs | | nofunc |
- `-------------' `-------------' `---------'
- ↓
- .-----.
- | any |
- `-----'
- @end verbatim
- A collection of type descriptor objects form a type table that
- describes all non-primitive types used within a module. Type objects
- associate an identifier with a function signature or reference type
- descriptor.
- @deffn {Procedure} type? obj
- Return @code{#t} if @var{obj} is a type.
- @end deffn
- @deffn {Procedure} type-id type
- Return the symbolic ID of @var{type}.
- @end deffn
- @deffn {Procedure} type-val type
- Return the type descriptor of @var{type}.
- @end deffn
- Types may also be nested within recursive type groups that allow for
- circular and self references to the types within the group. Types
- @emph{not} within a group can be thought of as belonging to a group of
- one.
- @deffn {Procedure} rec-group? obj
- Return @code{#t} if @var{obj} is a recursive type group.
- @end deffn
- @deffn {Procedure} rec-group-types rec-group
- Return the types within @var{rec-group}.
- @end deffn
- Note that while each Wasm module contains a full inventory of its
- types, structurally identical type groups across Wasm modules are
- canonicalized at runtime and are considered to be identical
- (@code{eq?} in Scheme terms.) This allows for passing references
- between modules.
- Type uses refer to function signatures and are used for specifying the
- type of a @code{block}, @code{loop}, or @code{if} expression.
- @deffn {Procedure} type-use? obj
- Return @code{#t} if @var{obj} is a type use.
- @end deffn
- @deffn {Procedure} type-use-idx type-use
- Return the type index of @var{type-use}.
- @end deffn
- @deffn {Procedure} type-use-sig type-use
- Return the function signature of @var{type-use}.
- @end deffn
- @deffn {Procedure} ref-type? obj
- Return @code{#t} if @var{obj} is a reference type.
- @end deffn
- @deffn {Procedure} ref-type-nullable? ref-type
- Return @var{#t} if @var{ref-type} is nullable.
- @end deffn
- @deffn {Procedure} ref-type-heap-type ref-type
- Return the heap type of @var{ref-type}.
- @end deffn
- As mentioned above, reference types support structural subtyping.
- @deffn {Procedure} sub-type? obj
- Return @code{#t} if @var{obj} is a sub type.
- @end deffn
- @deffn {Procedure} sub-type-final? sub-type
- Return @code{#t} if @var{sub-type} is marked as final.
- @end deffn
- @deffn {Procedure} sub-type-supers sub-type
- Return super types of @var{sub-type}.
- @end deffn
- @deffn {Procedure} sub-type-type sub-type
- Return the concrete type descriptor of @var{sub-type}.
- @end deffn
- Compound types take the form of arrays and structs.
- @deffn {Procedure} array-type? obj
- Return @code{#t} if @var{obj} is an array type.
- @end deffn
- @deffn {Procedure} array-type-mutable? array-type
- Return @code{#t} if @var{array-type} is mutable.
- @end deffn
- @deffn {Procedure} array-type-type array-type
- Retun the element type descriptor of @var{array-type}.
- @end deffn
- @deffn {Procedure} struct-type? obj
- Return @code{#t} if @var{obj} is a struct type.
- @end deffn
- @deffn {Procedure} struct-type-fields struct-type
- Return the field descriptors of @var{struct-type}.
- @end deffn
- Struct types are composed of several fields.
- @deffn {Procedure} field? obj
- Return @code{#t} if @var{obj} is a struct field.
- @end deffn
- @deffn {Procedure} field-id field
- Return the symbolic ID of @var{field}.
- @end deffn
- @deffn {Procedure} field-mutable? field
- Return @code{#t} if @var{field} is mutable.
- @end deffn
- @deffn {Procedure} field-type field
- Return the type descriptor of @var{field}.
- @end deffn
- Both arrays and struct fields allow for packed data using the special
- @code{i8} and @code{i16} data types.
- @subsection Globals
- Wasm supports both mutable and immutable global variables.
- @deffn {Procedure} global? obj
- Return @code{#t} if @var{obj} is a global.
- @end deffn
- @deffn {Procedure} global-id global
- Return the symbloc ID of @var{global}.
- @end deffn
- @deffn {Procedure} global-type global
- Return the type of @var{global}.
- @end deffn
- @deffn {Procedure} global-init global
- Return the initialization instructions of @var{global}. Only constant
- instructions are allowed.
- @end deffn
- @deffn {Procedure} global-type? obj
- Return @code{#t} if @var{obj} is a global type.
- @end deffn
- @deffn {Procedure} global-type-mutable? global-type
- Return @code{#t} if @var{global-type} is mutable.
- @end deffn
- @deffn {Procedure} global-type-type global-type
- Return the type descriptor of @var{global-type}.
- @end deffn
- @subsection Functions
- @deffn {Procedure} func? obj
- Return @code{#t} if @var{obj} is a function.
- @end deffn
- @deffn {Procedure} func-id func
- Return the symbolic ID of @var{func}.
- @end deffn
- @deffn {Procedure} func-type func
- Return the signature of @var{func}.
- @end deffn
- @deffn {Procedure} func-locals func
- Return the locals of @var{func}.
- @end deffn
- @deffn {Procedure} func-body func
- Return the body instructions of @var{func}.
- @end deffn
- The type of a function is its signature. Notably, Wasm supports
- multiple return values, just like Scheme.
- @deffn {Procedure} func-sig? obj
- Return @code{#t} if @var{obj} is a function signature.
- @end deffn
- @deffn {Procedure} func-sig-params func
- Return the parameters of @var{func}.
- @end deffn
- @deffn {Procedure} func-sig-results func
- Return the result types of @var{func}.
- @end deffn
- Function parameters pair a local identifier with its type.
- @deffn {Procedure} param? obj
- Return @code{#t} if @var{obj} is a param.
- @end deffn
- @deffn {Procedure} param-id param
- Return the symbolic ID of @var{param}.
- @end deffn
- @deffn {Procedure} param-type param
- Return the type descriptor of @var{param}.
- @end deffn
- Locals provide additional mutable variables scoped to the body of a
- function.
- @deffn {Procedure} local? obj
- Return @code{#t} if @var{obj} is a function local.
- @end deffn
- @deffn {Procedure} local-id local
- Return the symbolic ID of @var{local}.
- @end deffn
- @deffn {Procedure} local-type local
- Return the type descriptor of @var{local}.
- @end deffn
- @subsection Imports/exports
- Functions, globals, memories, and tables can be imported from the host
- or another Wasm module. They are organized into a two layer
- hierarchy. An import module groups many imports under an umbrella
- name, and then the individual item names distinguish imported data
- within a module.
- @deffn {Procedure} import? obj
- Return @code{#t} if @var{obj} is an import.
- @end deffn
- @deffn {Procedure} import-mod import
- Return the module name string of @var{import}.
- @end deffn
- @deffn {Procedure} import-name import
- Return the name string of @var{import}.
- @end deffn
- @deffn {Procedure} import-kind import
- Return the kind of @var{import}. Either @code{func}, @code{global},
- @code{memory}, or @code{table}.
- @end deffn
- @deffn {Procedure} import-id import
- Return the symbolic ID of @var{import}.
- @end deffn
- @deffn {Procedure} import-type import
- Return the type descriptor of @var{import}.
- @end deffn
- Likewise, functions, globals, memories, and tables can be exported
- from a module to be used by the host or by other modules.
- @deffn {Procedure} export? obj
- Return @code{#t} if @var{obj} is an export.
- @end deffn
- @deffn {Procedure} export-name export
- Return the name string of @var{export}.
- @end deffn
- @deffn {Procedure} export-kind export
- Return the kind of @var{export}. Either @code{func}, @code{global},
- @code{memory}, or @code{table}.
- @end deffn
- @deffn {Procedure} export-idx export
- Return the index of @var{export}.
- @end deffn
- @subsection Linear memory
- Memory objects specify linear chunks of bytes that a module can write
- to/read from at runtime. The size of a memory is specified in terms
- of 64KiB pages. While many memory objects coud be included in a
- module, the Wasm specification currently only allows the use of a
- single memory at index 0.
- @deffn {Procedure} memory? obj
- Return @code{#t} if @var{obj} is a memory.
- @end deffn
- @deffn {Procedure} memory-id memory
- Return the symbolic ID of @var{memory}.
- @end deffn
- The type of a memory currently just specifies the size limitations.
- @deffn {Procedure} memory-type memory
- Return the type of @var{memory}.
- @end deffn
- @deffn {Procedure} mem-type? obj
- Return @code{#t} if @var{obj} is a memory type.
- @end deffn
- @deffn {Procedure} mem-type-limits mem-type
- Return the limits of @var{mem-type}.
- @end deffn
- Instructions that manipulate linear memory use the memory argument
- type to point to a specific offset within a memory.
- @deffn {Procedure} mem-arg? obj
- Return @code{#t} if @var{obj} is a memory argument.
- @end deffn
- @deffn {Procedure} mem-arg-id mem-arg
- Return the symbolic ID of @var{mem-arg}.
- @end deffn
- @deffn {Procedure} mem-arg-offset mem-arg
- Return the offset of @var{mem-arg}.
- @end deffn
- @deffn {Procedure} mem-arg-align mem-arg
- Return the alignment of @var{mem-arg}.
- @end deffn
- @subsection Data segments
- Data segments are static chunks of data used to initialize regions of
- memory. They have two possible modes of use:
- @enumerate
- @item @strong{Active:}
- The data segment is copied into memory during instantiation.
- @item @strong{Passive:}
- The data segment is copied into memory using the @code{memory.init}
- instruction.
- @end enumerate
- @deffn {Procedure} data? obj
- Return @code{#t} if @var{obj} is a data segment.
- @end deffn
- @deffn {Procedure} data-id data
- Return the symbolic ID of @var{data}.
- @end deffn
- @deffn {Procedure} data-mode data
- Return the mode of @var{data}. Either @code{passive} or
- @code{active}.
- @end deffn
- @deffn {Procedure} data-mem data
- Return the memory associated with @var{data}.
- @end deffn
- @deffn {Procedure} data-offset data
- Return the instructions that compute the offset of @var{data}. Only
- constant instructions are allowed.
- @end deffn
- @deffn {Procedure} data-init data
- Return a bytevector containing the initialization data of @var{data}.
- @end deffn
- @subsection Tables
- Tables specify a vector of heap object references of a particular
- reference type.
- @deffn {Procedure} table? obj
- Return @code{#t} if @var{obj} is a reference table.
- @end deffn
- @deffn {Procedure} table-id table
- Return the symbolic ID of @var{table}.
- @end deffn
- @deffn {Procedure} table-type table
- Return the type of @var{table}.
- @end deffn
- Table types specify the reference type of the elements as well as the
- size limitations.
- @deffn {Procedure} table-type? obj
- Return @code{#t} if @var{obj} is a table type.
- @end deffn
- @deffn {Procedure} table-type-limits table-type
- Return the limts of @var{table-type}.
- @end deffn
- @deffn {Procedure} table-type-elem-type table-type
- Return the element type of @var{table-type}.
- @end deffn
- @subsection Element segments
- Element segments are static vectors of references used to initialize
- regions of tables (well, mostly.) They have three possible modes of
- use:
- @enumerate
- @item @strong{Active:}
- The element segment is copied into its associated table during
- instantiation.
- @item @strong{Passive:}
- The element segment is copied into its associated table using the
- @code{table.init} instruction.
- @item @strong{Declarative:}
- The element segment is unavailable at runtime and is instead used for
- forward declarations of types that are used elsewhere in the code.
- @end enumerate
- @deffn {Procedure} elem? obj
- Return @code{#t} if @var{obj} is an element segment.
- @end deffn
- @deffn {Procedure} elem-id elem
- Return the symoblic ID of @var{elem}.
- @end deffn
- @deffn {Procedure} elem-mode elem
- Return the mode of @var{elem}.
- @end deffn
- @deffn {Procedure} elem-table elem
- Return the table associated with @var{elem}.
- @end deffn
- @deffn {Procedure} elem-type elem
- Return the type of @var{elem}.
- @end deffn
- @deffn {Procedure} elem-offset elem
- Return the instructions that compute the offset of @var{elem}. Only
- constant instructions are allowed.
- @end deffn
- @deffn {Procedure} elem-inits elem
- Return a list of initializer instructions for the items of @var{elem}.
- Only constant instructions are allowed.
- @end deffn
- @subsection Limits
- Both memories and tables use limits to constrain their minimum and
- maximum size. A valid limit must have a minimum of at least 1, but
- the maximum may be @code{#f} if unbounded growth is allowed.
- @deffn {Procedure} limits? obj
- Return @code{#t} if @var{obj} is a limits.
- @end deffn
- @deffn {Procedure} limits-min limits
- Return the minimum value of @var{limits}.
- @end deffn
- @deffn {Procedure} limits-max limits
- Return the maximum value of @var{limits} or @code{#f} if there is no
- maximum.
- @end deffn
- @subsection Tags
- Tag segments specify types of runtime errors that may be raised.
- @deffn {Procedure} tag? obj
- Return @code{#t} if @var{obj} is a tag.
- @end deffn
- @deffn {Procedure} tag-id tag
- Return the symbolic ID of @var{tag}.
- @end deffn
- @deffn {Procedure} tag-type tag
- Return the type of @var{tag}.
- @end deffn
- @subsection Custom sections
- Custom sections specify arbitrary data that is not covered by the Wasm
- specification.
- @deffn {Procedure} custom? obj
- Return @code{#t} if @var{obj} is a custom segment.
- @end deffn
- @deffn {Procedure} custom-name custom
- Return the name of @var{custom}.
- @end deffn
- @deffn {Procedure} custom-bytes custom
- Return the bytevector of @var{custom}.
- @end deffn
- There is, however, one custom section that @emph{is} specified: the
- name section. This section contains various ``name maps'' that can be
- used to translate integer identifiers to (hopefully) human-readable
- names for the purposes of debugging.
- Hoot supports the name subsections described in the Wasm core
- specification, the Wasm GC specification, and the extended names
- proposal:
- @enumerate
- @item Module name
- @item Function name map
- @item Function local indirect name map
- @item Block label indirect name map
- @item Type name map
- @item Table name map
- @item Memory name map
- @item Global name map
- @item Element name map
- @item Data name map
- @item Struct field indirect name map
- @item Tag name map
- @end enumerate
- Name maps are represented as association lists mapping integers to
- strings. Indirect name maps are represented as association lists
- mapping integers to name maps.
- @deffn {Procedure} names? obj
- Return @code{#t} if @var{obj} is a name section object.
- @end deffn
- @deffn {Procedure} names-module names
- Return the module name of @var{names}.
- @end deffn
- @deffn {Procedure} names-func names
- Return the function name map of @var{names}.
- @end deffn
- @deffn {Procedure} names-local names
- Return the function local indirect name map of @var{names}.
- @end deffn
- @deffn {Procedure} names-label names
- Return the block label indirect name map of @var{names}.
- @end deffn
- @deffn {Procedure} names-type names
- Return the type name map of @var{names}.
- @end deffn
- @deffn {Procedure} names-table names
- Return the table name map of @var{names}.
- @end deffn
- @deffn {Procedure} names-memory names
- Return the memory name map of @var{names}.
- @end deffn
- @deffn {Procedure} names-global names
- Return the global name map of @var{names}.
- @end deffn
- @deffn {Procedure} names-elem names
- Return the element name map of @var{names}.
- @end deffn
- @deffn {Procedure} names-data names
- Return the data name map of @var{names}.
- @end deffn
- @deffn {Procedure} names-fields names
- Return the struct field indirect name map of @var{names}.
- @end deffn
- @deffn {Procedure} names-tag names
- Return the tag name map of @var{names}.
- @end deffn
- @node GWAT
- @section GWAT
- The @code{(wasm wat)} module provides a parser for a variant of
- WebAssembly Text (WAT) format. Since the WAT uses an s-expression
- syntax that resembles but is distinct from Scheme syntax, Hoot opts to
- represent WAT code as Scheme expressions. This allows for embedding
- WAT directly into Scheme code and programmatically generating WAT code
- via quasiquote templating or other means. We call this variant GWAT
- where the ``G'' stands for ``Guile'', of course.
- The GWAT form has some additional expressive power such as allowing
- string constants, bytevectors for data segments, and i32/i64 constants
- in either the signed or unsigned range.
- WAT has two variants: unfolded and folded. In the unfolded form,
- instruction sequences are linear, as they would be in the resulting
- binary:
- @lisp
- '(module
- (func (export "add") (param $a i32) (param $b i32) (result i32)
- (local.get $a)
- (local.get $b)
- (i32.add)))
- @end lisp
- The folded form allows instructions to be nested within each other:
- @lisp
- '(module
- (func (export "add") (param $a i32) (param $b i32) (result i32)
- (i32.add (local.get $a)
- (local.get $b))))
- @end lisp
- This form looks more like Scheme procedure calls and is generally
- easier to write and reason about.
- @deffn {Procedure} wat->wasm expr
- Parse @var{expr}, a Wasm module expressed as WAT code, and return a
- Wasm module.
- @lisp
- (parse-wat
- '(module
- (func (export "add") (param $a i32) (param $b i32) (result i32)
- (i32.add (local.get $a)
- (local.get $b)))))
- @end lisp
- The returned Wasm module preserves named references, among other
- things, and is thus unsuitable as input to the assembler or
- interpreter. To lower the module into a usable form, see
- @code{resolve-wasm} in @ref{Resolver}.
- @end deffn
- @deffn {Procedure} wasm->wat wasm
- Disassemble @var{wasm} and return its symbolic WAT form. @var{wasm}
- is assumed to be in @emph{unresolved} form, like the output of
- @code{wat->wasm}. To lift a resolved Wasm module into a form usable
- by @code{wasm->wat}, see @code{unresolve-wasm} in @ref{Resolver}.
- @end deffn
- @node Resolver
- @section Resolver
- The @code{(wasm resolve)} module provides the @code{resolve-wasm}
- procedure which lowers Wasm modules into a form that can be used by
- the assembler or interpreter. The resolver replaces named references
- with their respective integer identifiers, fills out the type table,
- and adjusts i32 and i64 constants into their canonical form.
- @deffn {Procedure} resolve-wasm mod [#:name-section? #f]
- Lower the Wasm module @var{mod} into a form that can be assembled or
- interpreted. Returns a new Wasm module and does not modify @var{mod}.
- When @var{name-section?} is @code{#t}, the returned Wasm module will
- include a name map that maps the original, human-readable names to the
- resolved integer identifiers.
- @end deffn
- There is also the @code{unresolve-wasm} procedure which performs the
- reverse translation (though not all of what @code{resolve-wasm} does
- is reversible.)
- @deffn {Procedure} unresolve-wasm mod
- Lift the Wasm module @var{mod}, assumed to be in lowered form via
- @code{resolve-wasm}, into a form where integer identifiers are
- replaced with human-readable names. If a name map is present, the
- original names are restored, otherwise unique names are
- auto-generated.
- @end deffn
- @node Linker
- @section Linker
- The @code{(wasm link)} module provides a means for extending a Wasm
- module with the standard library that it needs at runtime. Hoot uses
- the linker to add the Scheme runtime to the compiled form of user
- code. The linker uses a form of tree-shaking to remove anything that
- is not used by the base module.
- @deffn {Procedure} add-stdlib wasm stdlib
- Return a new Wasm module that is the combination of the Wasm module
- @var{wasm} with the Wasm module @var{stdlib}.
- @end deffn
- @node Assembler
- @section Assembler
- The @code{(wasm assemble)} module is used to lower Wasm modules into
- the Wasm binary format.
- @deffn {Procedure} assemble-wasm wasm
- Return a bytevector containing the assembled binary form of the Wasm
- module @var{wasm}.
- @end deffn
- @node Binary Parser
- @section Binary Parser
- The @code{(wasm parse)} module parses the Wasm binary format.
- @deffn {Procedure} parse-wasm port
- Parse the Wasm binary data from @var{port} and return a Wasm module.
- @end deffn
- @node Printer
- @section Printer
- The @code{(wasm dump)} module provides the @code{dump-wasm} procedure
- for generating a detailed print-out of a Wasm module's contents. See
- also @ref{Low-level development tools} for the @code{wasm-dump} REPL
- command.
- @deffn {Procedure} dump-wasm mod [#:port] [#:dump-func-defs? #t]
- Write a detailed inventory of the Wasm module @var{mod} to @var{port}
- or the current output port if @var{port} is not specified. If
- @var{dump-func-defs?} is @code{#t}, which is the default, all function
- definitions are printed, including the instructions in the body of
- each. Depending on the size of the module, this may be an
- overwhelming amount of data, thus it is made optional.
- @end deffn
- @node Interpreter
- @section Interpreter
- The @code{(wasm vm)} module provides a virtual machine for
- interpreting Wasm functions. To use the interpreter, a Wasm module is
- first validated for type safety (among other things) and then
- instantiated, at which point exported functions become callable from
- Scheme.
- The interpreter only accepts validated Wasm. The @code{validate-wasm}
- procedure validates and wraps a Wasm module to indicate successful
- validation:
- @lisp
- (use-modules (wasm vm) (wasm resolve))
- (define validated-wasm
- (validate-wasm
- (wat->wasm
- '(module
- (func (export "main") (result i32)
- (i32.const 42))))))
- @end lisp
- When starting with a Wasm binary, the convenient
- @code{load-and-validate-wasm} procedure parses the binary and then
- performs validation:
- @lisp
- (call-with-input-file "hello.wasm" load-and-validate-wasm)
- @end lisp
- Once the Wasm module has been validated, the runtime data needed for
- interpretation can be created by instantiating the module:
- @lisp
- (define instance (instantiate-wasm validated-wasm))
- @end lisp
- Exported Wasm functions then become usable as Scheme procedures:
- @lisp
- (define wasm-main (wasm-instance-export-ref instance "main"))
- (wasm-main) ;; => 42
- @end lisp
- Wasm functions are statically typed, which means that calls from
- Scheme to Wasm require runtime type checking for each call.
- @subsection Validation
- @deffn {Procedure} validate-wasm wasm
- Validate the Wasm module @var{wasm} and return a validated Wasm
- object.
- @end deffn
- @deffn {Procedure} load-and-validate-wasm obj
- Load and validate the Wasm module within @var{obj} then return a
- validated Wasm object. @var{obj} may be a @code{<wasm>} record as
- produced by @code{resolve-wasm}(@pxref{Resolver}), a bytevector
- containing a Wasm binary, or an input port from which to read a Wasm
- binary.
- @end deffn
- @deffn {Procedure} validated-wasm? obj
- Return @code{#t} if @var{obj} is a validated Wasm object.
- @end deffn
- @deffn {Procedure} validated-wasm-ref validated-wasm
- Unbox and return the Wasm module within @var{validated-wasm}.
- @end deffn
- @subsection Instantiation
- @deffn {Procedure} instantiate-wasm wasm [#:imports '()]
- Return a new Wasm instance for the validated Wasm module @var{wasm}.
- @var{imports} is a nested association list of imported functions,
- globals, memories, and tables. Wasm imports are identified by a
- module name and an object name. Consider the following Wasm module
- that computes 2D polar coordinates and prints them to a log:
- @lisp
- (use-modules (wasm resolve) (wasm vm) (wasm wat))
- (define the-module
- (resolve-wasm
- (wat->wasm
- '(module
- (func $logf64 (import "debug" "logf64") (param f64))
- (func $cos (import "math" "cos") (param f64) (result f64))
- (func $sin (import "math" "sin") (param f64) (result f64))
- (func (export "polar") (param $r f64) (param $theta f64)
- (call $logf64 (f64.mul (local.get $r)
- (call $cos (local.get $theta))))
- (call $logf64 (f64.mul (local.get $r)
- (call $sin (local.get $theta)))))))))
- @end lisp
- This module requires three imported functions from two modules. Thus
- the module instantiation code would look like this:
- @lisp
- (define (logf64 x)
- (format #t "f64: ~a\n" x))
- (define the-instance
- (instantiate-wasm (validate-wasm the-module)
- #:imports `(("debug" . (("logf64" . ,logf64)))
- ("math" . (("cos" . ,cos)
- ("sin" . ,sin))))))
- @end lisp
- @end deffn
- @subsection Globals
- @deffn {Procedure} make-wasm-global value mutable?
- Return a new Wasm global containing @var{value}. When @var{mutable?}
- is @code{#f}, the value cannot be modified later.
- @end deffn
- @deffn {Procedure} wasm-global? obj
- Return @code{#t} if @var{obj} is a Wasm global.
- @end deffn
- @deffn {Procedure} wasm-global-ref global
- Return the current value within @var{global}.
- @end deffn
- @deffn {Procedure} wasm-global-set! global val
- Set the value within @var{global} to @var{val}. An exception is
- raised if @var{global} is immutable.
- @end deffn
- @deffn {Procedure} wasm-global-mutable? global
- Return @code{#t} if @var{global} is mutable.
- @end deffn
- @subsection Memories
- @deffn {Procedure} make-wasm-memory size [#:limits (make-limits 1 #f)]
- Return a new Wasm linear memory containing @var{size} 64KiB pages.
- @var{limits} determines the lower and upper bounds of how many pages
- this memory can store. The default limits are a minimum of 1 page and
- no maximum page limit. @xref{Data types} for more information on
- limit objects.
- @end deffn
- @deffn {Procedure} wasm-memory? obj
- Return @code{#t} if @var{obj} is a Wasm memory.
- @end deffn
- @deffn {Procedure} wasm-memory-bytes memory
- Return the current bytevector representing the pages of @var{memory}.
- @end deffn
- @deffn {Procedure} wasm-memory-size memory
- Return the size of @var{memory} in 64KiB pages.
- @end deffn
- @deffn {Procedure} wasm-memory-limits memory
- Return the limits of @var{memory}
- @end deffn
- @deffn {Procedure} wasm-memory-grow! memory n
- Increase the size of @var{memory} by @var{n} pages. An exception is
- raised if growing by @var{n} exceeds the limits of @var{memory}.
- @end deffn
- @subsection Tables
- @deffn {Procedure} make-wasm-table size [#:limits (make-limits 1 #f)]
- Return a new Wasm reference table containing @var{size} element slots.
- @var{limits} determines the lower and upper bounds of how many
- elements this table can store. The default limits are a minimum of 1
- element and no maximum element limit. @xref{Data types} for more
- information on limit objects.
- @end deffn
- @deffn {Procedure} wasm-table?
- Return @code{#t} if @var{obj} is a Wasm table.
- @end deffn
- @deffn {Procedure} wasm-table-size table
- Return the size of @var{table}.
- @end deffn
- @deffn {Procedure} wasm-table-ref table i
- Return the reference at the @var{i}th index in @var{table}.
- @end deffn
- @deffn {Procedure} wasm-table-set! table i x
- Set the @var{i}th element of @var{table} to @var{x}, a Wasm reference
- type.
- @end deffn
- @deffn {Procedure} wasm-table-fill! table start fill length
- Fill the elements of @var{table} from @var{start} to @var{start} +
- @var{length}, exclusive, with the value @var{fill}.
- @end deffn
- @deffn {Procedure} wasm-table-copy! table at elems start length
- Copy the block of elements from vector @var{elems}, from @var{start}
- to @var{start} + @var{length}, exclusive, to @var{table}, starting at
- @var{at}.
- @end deffn
- @deffn {Procedure} wasm-table-grow! table n init
- Increase the size of @var{table} by @var{n} elements. An exception is
- raised if growing by @var{n} exceeds the limits of @var{table}.
- @end deffn
- @subsection Observation
- Every Wasm instruction evaluated by interpreter can be observed via
- the @code{current-instruction-listener} parameter. Use this hook to
- instrument Wasm modules.
- The following instruction listener would print every instruction's
- name on a separate line:
- @lisp
- (define (log-instr instr path instance stack blocks locals)
- (display (car instr))
- (newline))
- (parameterize ((current-instruction-listener log-instr))
- ...)
- @end lisp
- @defvar {Variable} current-instruction-listener
- The current instruction observation hook which is invoked
- @emph{before} each instruction evaluation. Must be a procedure that
- accepts the following arguments:
- @enumerate
- @item @strong{Instruction:}
- The symbolic Wasm instruction to be evaluated.
- @item @strong{Path:}
- The symbolic location of the instruction within the Wasm module.
- @item @strong{Instance:}
- The instance that is evaluating the instruction.
- @item @strong{Stack:}
- The Wasm value stack.
- @item @strong{Blocks:}
- The Wasm block stack, which is just a list of prompt tags.
- @item @strong{Locals:}
- The Wasm function locals.
- @end enumerate
- @end defvar
- The Wasm value stack is a special data type with the following API:
- @deffn {Procedure} wasm-stack? obj
- Return @code{#t} if @var{obj} is a Wasm value stack.
- @end deffn
- @deffn {Procedure} wasm-stack-items stack
- Return the values on @var{stack} as a list.
- @end deffn
- @node Contributing
- @chapter Contributing
- Found a bug? Let us know! Made an improvement? Show us! Issues can
- be filed and pull requests can be submitted on
- @url{https://gitlab.com/spritely/guile-hoot,GitLab}.
- @node License
- @chapter License
- @emph{(C) 2023 David Thompson}
- @emph{Both Guile Hoot and this manual are released under the terms of
- the following license:}
- @include apache-2.0.texi
- @node Index
- @unnumbered Index
- @printindex fn
- @bye
|