1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367 |
- \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}}.
- The largest missing pieces from Hoot's R7RS-small support are
- environments and evaluation (@code{environment}, @code{eval}, etc.)
- which would allow for runtime interpretation of Scheme. 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
- @node Binary installation
- @subsection Binary installation
- Currently, Hoot is available from only one GNU/Linux distribution:
- @url{https://guix.gnu.org,GNU Guix}. Guix may also be used as an
- additional package manager on top of another distribution such as
- Debian.
- If you have Guix, trying Hoot is easy:
- @example
- guix shell guile-next guile-hoot
- @end example
- This will create a temporary shell environment in which you can try
- Hoot. It's important that the @code{guile-next} package is included
- because Hoot currently relies upon features in Guile that have not yet
- made it into a stable release.
- @node Building from source
- @subsection Building from source
- The @emph{easiest} way to get everything necessary to build 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.
- @subsubsection 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 either @url{https://nodejs.org,NodeJS} or
- @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,
- Hoot includes a simple web server for development purposes. Start the
- web server like so:
- @example
- guile -c '((@ (hoot web-server) serve))'
- @end example
- Visit @url{http://localhost:8088} in your web browser. If it supports
- Wasm GC and tail calls then 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 Scheme to Wasm.
- * Reflection:: Using Hoot modules from the host environment.
- * REPL commands:: Compile and run Scheme from the REPL.
- @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 @inlinefmtifelse{html,
- @url{https://www.gnu.org/software/guile/manual/html_node/Tree_002dIL.html,
- Tree-IL}, @ref{Tree-IL,,,Guile Reference}}, which is then lowered to
- @inlinefmtifelse{html,
- @url{https://www.gnu.org/software/guile/manual/html_node/Continuation_002dPassing-Style.html,
- Continuation-passing style}, @ref{Continuation-Passing Style,,,Guile
- Reference}}(CPS), and then finally to @inlinefmtifelse{html,
- @url{https://www.gnu.org/software/guile/manual/html_node/Bytecode.html,
- Bytecode}, @ref{Bytecode,,,Guile Reference}}. Hoot adds an additional
- backend that compiles CPS to Wasm.
- In contrast to Guile's approach of compiling individual modules, Hoot
- is a whole-program compiler. The user program and all imported
- modules are part of the same compilation unit and the result is a
- single Wasm binary. Currently, Hoot uses the R6RS library system and
- does not support Guile's @code{define-module} form or R7RS style
- libraries.
- 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 --load-path=. --output=foo.wasm foo.scm
- @end example
- 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] @
- [#:imports %default-program-imports] @
- [#:include-file %include-from-path] @
- [#:extend-load-library (lambda (f) f)] @
- [#:load-library (extend-load-library (builtin-module-loader import-abi?))] @
- [#:optimization-level (default-optimization-level)] @
- [#:warning-level (default-warning-level)] @
- [#:dump-cps? #f] [#:dump-wasm? #f] [#:emit-names? #f] @
- [#:opts '()]
- Compile the Scheme expression @var{exp} to Wasm and return a Wasm
- module.
- The environment in which @var{exp} is evaluated is defined by
- @var{imports}, a list of module names such as @code{(scheme time)} or
- @code{(hoot ffi)}. If not specified, a default list of imports will
- be used.
- 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{emit-names?} is @code{#t} then human-readable names will be
- embedded in the resulting Wasm object. By default, this is turned off
- as it greatly increases binary size.
- Associating module names with source code is handled by
- @var{load-library}, a procedure that receives the module name as its
- only argument and returns the source code as an s-expression, or
- @code{#f} if there is no such module. The default loader is capable
- of loading modules from Hoot's standard library. It is generally
- recommended to leave @var{load-library} alone and use the default.
- To load additional modules, specify @var{extend-load-library} instead.
- @var{extend-load-library} is a procedure that receives one argument,
- @var{load-library}, and returns a procedure with the same signature as
- @var{load-library}. Through this extension mechanism, users can load
- their own modules.
- Most of the time, loading user modules from the file system is all
- that is needed. Hoot has a built-in
- @code{library-load-path-extension} extension procedure for this
- purpose. To demonstrate, let's first assume that the code below is
- saved to @file{example.scm} in the current directory:
- @lisp
- (library (example)
- (export double)
- (import (scheme base))
- (define (double x) (* x 2)))
- @end lisp
- The compiler can then be extended to load modules from the current
- directory like so:
- @lisp
- (compile '(double 42)
- #:imports '((scheme base) (example))
- #:extend-load-library
- (library-load-path-extension '(".")))
- @end lisp
- @inlinefmtifelse{html,
- @url{https://www.gnu.org/software/guile/manual/html_node/Compilation.html,
- See the Guile manual}, @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] @
- [#:include-file %include-from-path] @
- [#:extend-load-library (lambda (f) f)] @
- [#:load-library (extend-load-library (builtin-module-loader import-abi?))] @
- [#:optimization-level (default-optimization-level)] @
- [#:warning-level (default-warning-level)] @
- [#:dump-cps? #f] [#:dump-wasm? #f] [#:emit-names? #f] @
- [#:opts '()]
- Like @code{compile}, but read Scheme expressions from @var{port}.
- If the first expression is an @code{import} form, then only the
- bindings from those modules will be imported into the compilation
- unit. If the @code{import} form is omitted, a default set of modules
- will be imported. It is highly recommended to be explicit and use
- @code{import}.
- @end deffn
- @deffn {Procedure} compile-file input-file [#:import-abi? #f] [#:export-abi? #t] @
- [#:include-file %include-from-path] @
- [#:extend-load-library (lambda (f) f)] @
- [#:load-library (extend-load-library (builtin-module-loader import-abi?))] @
- [#:optimization-level (default-optimization-level)] @
- [#:warning-level (default-warning-level)] @
- [#:dump-cps? #f] [#:dump-wasm? #f] [#:emit-names? #f] @
- [#:opts '()]
- Like @code{read-and-compile}, but read the Scheme expression from
- @var{input-file}.
- @end deffn
- @node Reflection
- @section Reflection
- 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 %default-program-imports] @
- [load-path '()] [wasm-imports '()]
- Compile @var{exp} and return the result.
- Optionally, @var{imports} may specify a list of Scheme module names to
- import. If unspecified, a default set of modules providing a basic
- Scheme environment will be imported. @var{load-path} is a list of
- file system directories to search for additional user modules.
- Optionally, @var{wasm-imports} may contain a 2-tier association list
- structure of imported Wasm 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 ... @
- [imports %default-program-imports] @
- [load-path '()] [wasm-imports '()]
- Compile @var{proc-exp} and all @var{arg-exps}, call the procedure with
- the arguments, then return the results.
- See @code{compile-value} for an explanation of the keyword arguments.
- @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
- Below are the predicates and accessors for various Hoot heap types:
- @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 REPL commands
- @section REPL commands
- The @code{(hoot repl)} module provides some REPL meta commands to make
- it easy to compile Scheme programs and run them in Hoot's Wasm
- interpreter.
- @deffn {REPL Command} hoot-compile exp [opts ...]
- Compile @var{exp} and return a Wasm module.
- @var{opts} may specify the keyword arguments to pass to the
- @code{compile} procedure (@pxref{Invoking the compiler}).
- @end deffn
- @deffn {REPL Command} hoot-compile-file file [opts ...]
- Compile the source code in @var{file} and return a Wasm module.
- @var{opts} may specify the keyword arguments to pass to the
- @code{compile-file} procedure (@pxref{Invoking the compiler}).
- @end deffn
- @deffn {REPL Command} hoot-run exp [opts ...]
- Compile and run @var{exp} and return the results.
- @end deffn
- @deffn {REPL Command} hoot-run-file file [opts ...]
- Compile and run the source code in @var{file} and return the results.
- @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 features, Hoot includes many
- of its own extensions. This chapter documents the APIs of these
- extensions.
- @menu
- * Boxes:: Mutable cells that store a single object.
- * Atomics:: Atomic boxes.
- * Bitvectors:: Sequences of bits.
- * Bytevectors:: Sequences of bytes.
- * Control:: Delimited continuations.
- * Exceptions:: Error handling.
- * Fluids:: Dynamic state.
- * Parameters:: Dynamic variables.
- * Hashtables:: Mutable key/value data structures.
- * Records:: Extensions to standard records.
- * Pattern matching:: Object destructuring.
- * Foreign function interface:: Call host functions from Scheme.
- @end menu
- @node Boxes
- @section Boxes
- The @code{(hoot boxes)} module provides boxes, which are single-value,
- mutable cells.
- @deffn {Procedure} make-box init
- Return a new box with an initial stored value of @var{init}.
- @end deffn
- @deffn {Procedure} box-ref box
- Return the value stored within @var{box}.
- @end deffn
- @deffn {Procedure} box-set! box val
- Set the stored value of @var{box} to @var{val}.
- @end deffn
- @node Atomics
- @section Atomics
- The @code{(hoot atomics)} module provides an API compatible with
- Guile's @code{(ice-9 atomic)} module. Atomic operations allow for
- concurrent access to a resource form many threads without the need to
- use thread synchronization constructs like mutexes. Currently,
- WebAssembly assumes single-threaded execution, making atomicity
- trivial. See @inlinefmtifelse{html,
- @url{https://www.gnu.org/software/guile/manual/html_node/Atomics.html,
- the Guile manual}, @ref{Atomics,,,Guile Reference}} for more detailed
- information.
- @deffn {Procedure} make-atomic-box init
- Return a new atomic box with an initial stored value of @var{init}.
- @end deffn
- @deffn {Procedure} atomic-box-ref box
- Return the value stored within the atomic box @var{box}.
- @end deffn
- @deffn {Procedure} atomic-box-set! box val
- Store @var{val} into the atomic box @var{box}.
- @end deffn
- @deffn {Procedure} atomic-box-swap! box val
- Store @var{val} into the atomic box @var{box}, and return the value
- that was previously stored in the box.
- @end deffn
- @deffn {Procedure} atomic-box-compare-and-swap! box expected desired
- If the value of the atomic box @var{box} is the same as @var{expected}
- (in the sense of @code{eq?}), replace the contents of the box with
- @var{desired}. Otherwise, the box is not updated. Return the
- previous value of the box in either case. You can know if the swap
- worked by checking if the return value is @code{eq?} to
- @var{expected}.
- @end deffn
- @node Bitvectors
- @section Bitvectors
- The @code{(hoot bitvectors)} module provides bitvectors, which are
- tightly packed arrays of booleans.
- @deffn {Procedure} make-bitvector len [fill #f]
- Return a new bitvector of @var{len} bits with all bits initialized to
- @var{fill}.
- @end deffn
- @deffn {Procedure} bitvector? obj
- Return @code{#t} if @var{obj} is a bitvector.
- @end deffn
- @deffn {Procedure} bitvector-length bv
- Return the length of the bitvector @var{bv}.
- @end deffn
- @deffn {Procedure} bitvector-ref bv i
- Return the boolean value of bit @var{i} in the bitvector @var{bv}.
- @end deffn
- @deffn {Procedure} bitvector-set-bit! bv i
- Set the bit @var{i} in the bitvector @var{bv} to @code{#t}.
- @end deffn
- @node Bytevectors
- @section Bytevectors
- The @code{(hoot bytevectors)} module provides some of the R6RS
- bytevectors API. Bytevectors are sequences of bytes that are useful
- for low-level manipulation of binary data.
- @deffn {Procedure} make-bytevector len [init 0]
- Return a new bytevector of @var{len} bytes with all bytes initialized
- to @var{init}.
- @end deffn
- @deffn {Procedure} bytevector [byte ...]
- Return a new bytevector containing the sequence @var{byte} @dots{}.
- @end deffn
- @deffn {Procedure} bytevector? obj
- Return @code{#t} if @var{obj} is a bytevector.
- @end deffn
- @deffn {Procedure} bytevector-length bv
- Return the length of @var{bv} in bytes.
- @end deffn
- @deffn {Procedure} bytevector-copy bv [start 0] [end (bytevector-length bv)]
- Return a new bytevector that is a copy of the bytevector @var{bv} from
- byte index @var{start} to @var{end}. @var{start} must be less than or
- equal to @var{end}.
- @end deffn
- @deffn {Procedure} bytevector-copy! to at from [start 0] [end (bytevector-length from)]
- Copy the subsection of bytevector @var{from}, defined by the byte
- range [@var{start}, @var{end}), into the bytevector @var{from}.
- @end deffn
- @deffn {Procedure} bytevector-append [bv ...]
- Return a new bytevector that concatenates all the input bytevectors
- @var{bv} @dots{} in the order given.
- @end deffn
- @deffn {Procedure} bytevector-concatenate bvs
- Return a new bytevector that concatenates all of the bytevectors in
- the list @var{bvs}.
- @end deffn
- @deffn {Procedure} bytevector-concatenate-reverse bvs
- Return a new bytevector that concatenates all of the bytevectors in
- the list @var{bvs} in reverse order.
- @end deffn
- @deffn {Procedure} bytevector-u8-ref bv index
- @deffnx {Procedure} bytevector-s8-ref bv index
- @deffnx {Procedure} bytevector-u16-native-ref bv index
- @deffnx {Procedure} bytevector-s16-native-ref bv index
- @deffnx {Procedure} bytevector-u32-native-ref bv index
- @deffnx {Procedure} bytevector-s32-native-ref bv index
- @deffnx {Procedure} bytevector-u64-native-ref bv index
- @deffnx {Procedure} bytevector-s64-native-ref bv index
- Return the N-bit signed or unsigned integer from the bytevector
- @var{bv} at @var{index} using the host's native endianness.
- @end deffn
- @deffn {Procedure} bytevector-ieee-single-native-ref bv index
- @deffnx {Procedure} bytevector-ieee-double-native-ref bv index
- Return the single or double precision IEEE floating piont number from
- the bytevector @var{bv} at @var{index} using the host's native
- endianness.
- @end deffn
- @deffn {Procedure} bytevector-u8-set! bv index x
- @deffnx {Procedure} bytevector-s8-set! bv index x
- @deffnx {Procedure} bytevector-u16-native-set! bv index x
- @deffnx {Procedure} bytevector-s16-native-set! bv index x
- @deffnx {Procedure} bytevector-u32-native-set! bv index x
- @deffnx {Procedure} bytevector-s32-native-set! bv index x
- @deffnx {Procedure} bytevector-u64-native-set! bv index x
- @deffnx {Procedure} bytevector-s64-native-set! bv index x
- Store @var{x} as an N-bit signed or unsigned integer in the bytevector
- @var{bv} at @var{index} using the host's native endianness.
- @end deffn
- @deffn {Procedure} bytevector-ieee-single-native-set! bv index x
- @deffnx {Procedure} bytevector-ieee-double-native-set! bv index x
- Store @var{x} as a single or double precision IEEE floating piont
- number in the bytevector @var{bv} at @var{index} using the host's
- native endianness.
- @end deffn
- @node Control
- @section Control
- The @code{(hoot control)} module provides an interface for Guile's
- delimited continuation facility known as ``prompts'' and some of the
- @code{(ice-9 control)} API. See @inlinefmtifelse{html,
- @url{https://www.gnu.org/software/guile/manual/html_node/Prompts.html,
- the Guile manual}, @ref{Prompts,,,Guile Reference}} for more detailed
- information.
- @deffn {Procedure} make-prompt-tag [stem ``prompt'']
- Return a new prompt tag that incorporates the value of @var{stem}. A
- prompt tag is simply a unique object.
- @end deffn
- @deffn {Procedure} call-with-prompt tag body handler
- Call the procedure @var{body}, a procedure of 0 arguments or
- ``thunk'', within the context of a prompt marked with @var{tag}.
- Should this prompt be aborted via @code{abort-to-prompt}, the
- procedure @var{handler} is called. The @var{handler} procedure
- receives as arguments a continuation object and any additional
- arguments that were passed to @code{abort-to-prompt}.
- @end deffn
- @deffn {Procedure} abort-to-prompt tag [val ...]
- Unwind the dynamic and control context to the nearest prompt named
- @var{tag}, so passing the additional values @var{val} @dots{}
- @end deffn
- @deffn {Procedure} default-prompt-tag
- Return the default prompt tag.
- @end deffn
- @deffn {Procedure} default-prompt-handler
- Return the default prompt handler procedure.
- @end deffn
- Note that both @code{default-prompt-tag} and
- @code{default-prompt-handler} are parameters, so their values may be
- modified by using @code{parameterize}. @xref{Parameters} for more
- information.
- @deffn {Syntax} % expr
- @deffnx {Syntax} % expr handler
- @deffnx {Syntax} % tag expr handler
- Syntactic sugar for @code{call-with-prompt}. Evaluate @var{expr} in
- the context of a prompt. If @var{tag} is ommitted, the default prompt
- tag will be used. If @var{handler} is omitted, the default prompt
- handler will be used.
- @end deffn
- @node Exceptions
- @section Exceptions
- @node Exception types
- @subsection Exception types
- The @code{(hoot exceptions)} module implements Guile's exception API.
- See @inlinefmtifelse{html,
- @url{https://www.gnu.org/software/guile/manual/html_node/Exceptions.html,
- the Guile manual}, @ref{Exceptions,,,Guile Reference}} for more
- detailed information.
- @deffn {Procedure} make-exception exceptions @dots{}
- Return an exception object composed of @var{exceptions}.
- @end deffn
- @deffn {Procedure} exception? obj
- Return @code{#t} if @var{obj} is an exception object.
- @end deffn
- Below are the built-in exception types and their respective
- constructors, predicates, and accessors.
- @deftp {Exception Type} &exception
- @end deftp
- @deffn {Procedure} simple-exception? obj
- @end deffn
- @deftp {Exception Type} &compound-exception
- @end deftp
- @deffn {Procedure} make-compound-exception components
- @deffnx {Procedure} compound-exception? obj
- @deffnx {Procedure} compound-exception-components compound-exception
- @end deffn
- @deftp {Exception Type} &message
- @end deftp
- @deffn {Procedure} make-exception-with-message message
- @deffnx {Procedure} exception-with-message? obj
- @deffnx {Procedure} exception-message exception
- @end deffn
- @deftp {Exception Type} &warning
- @end deftp
- @deffn {Procedure} make-warning
- @deffnx {Procedure} warning? obj
- @end deffn
- @deftp {Exception Type} &serious
- @end deftp
- @deffn {Procedure} make-serious-exception
- @deffnx {Procedure} serious-exception? obj
- @end deffn
- @deftp {Exception Type} &error
- @end deftp
- @deffn {Procedure} make-error
- @deffnx {Procedure} error? obj
- @end deffn
- @deftp {Exception Type} &violation
- @end deftp
- @deffn {Procedure} make-violation
- @deffnx {Procedure} violation? obj
- @end deffn
- @deftp {Exception Type} &assertion
- @end deftp
- @deffn {Procedure} make-assertion-violation
- @deffnx {Procedure} assertion-violation? obj
- @end deffn
- @deftp {Exception Type} &arity-violation
- @end deftp
- @deffn {Procedure} make-arity-violation
- @deffnx {Procedure} arity-violation? obj
- @end deffn
- @deftp {Exception Type} &implementation-restriction
- @end deftp
- @deffn {Procedure} make-implementation-restriction-violation
- @deffnx {Procedure} implementation-restriction-violation? obj
- @end deffn
- @deftp {Exception Type} &failed-type-check
- @end deftp
- @deffn {Procedure} make-failed-type-check predicate
- @deffnx {Procedure} failed-type-check? obj
- @deffnx {Procedure} failed-type-check-predicate exception
- @end deffn
- @deftp {Exception Type} &non-continuable
- @end deftp
- @deffn {Procedure} make-non-continuable-violation
- @deffnx {Procedure} non-continuable-violation? obj
- @end deffn
- @deftp {Exception Type} &irritants
- @end deftp
- @deffn {Procedure} make-exception-with-irritants irritants
- @deffnx {Procedure} exception-with-irritants? obj
- @deffnx {Procedure} exception-irritants exception
- @end deffn
- @deftp {Exception Type} &origin
- @end deftp
- @deffn {Procedure} make-exception-with-origin origin
- @deffnx {Procedure} exception-with-origin? obj
- @deffnx {Procedure} exception-origin exception
- @end deffn
- @deftp {Exception Type} &lexical
- @end deftp
- @deffn {Procedure} make-lexical-violation
- @deffnx {Procedure} lexical-violation? obj
- @end deffn
- @deftp {Exception Type} &i/o
- @end deftp
- @deffn {Procedure} make-i/o-error
- @deffnx {Procedure} i/o-error?
- @end deffn
- @deftp {Exception Type} &i/o-line-and-column
- @end deftp
- @deffn {Procedure} make-i/o-line-and-column-error line column
- @deffnx {Procedure} i/o-line-and-column-error? obj
- @deffnx {Procedure} i/o-error-line exception
- @deffnx {Procedure} i/o-error-column exception
- @end deffn
- @deftp {Exception Type} &i/o-filename
- @end deftp
- @deffn {Procedure} make-i/o-filename-error filename
- @deffnx {Procedure} i/o-filename-error? obj
- @deffnx {Procedure} i/o-error-filename exception
- @end deffn
- @deftp {Exception Type} &i/o-not-seekable
- @end deftp
- @deffn {Procedure} make-i/o-not-seekable-error
- @deffnx {Procedure} i/o-not-seekable-error? obj
- @end deffn
- @deftp {Exception Type} &i/o-port
- @end deftp
- @deffn {Procedure} make-i/o-port-error port
- @deffnx {Procedure} i/o-port-error? obj
- @deffnx {Procedure} i/o-error-port exception
- @end deffn
- @node Raising and handling exceptions
- @subsection Raising and handling exceptions
- The @code{(hoot errors)} module provides procedures for raising and
- handling exceptions.
- @deffn {Procedure} raise exception
- Raise the non-continuable exception @var{exception} by invoking the
- current exception handler.
- @end deffn
- @deffn {Procedure} raise-continuable exception
- Raise the continuable exception @var{exception} by invoking the
- current exception handler.
- @end deffn
- @deffn {Procedure} raise-exception exception [#:continuable? #f]
- Raise the exception @var{exception} by invoking the current exception
- handler. When @var{continuable?} is @code{#t}, the raised exception
- is continuable.
- @end deffn
- @deffn {Procedure} with-exception-handler handler thunk [#:unwind? #f]
- Call @var{thunk}, a procedure of zero arguments, in a context where
- @var{handler}, a procedure of one argument, is the current exception
- handler. If an exception is raised then @var{handler} will be called
- with the exception obect.
- When @var{unwind?} is @code{#t}, the stack will be unwound before
- @var{handler} is called. The default behavior is not to unwind. When
- the stack is not unwound, it is up to @var{handler} to properly manage
- control flow. Control is allowed to fallthrough @var{handler} and
- resume from where the exception was raised only if the raised
- exception is @emph{continuable}. For non-continuable exceptions,
- @var{handler} should abort to some prompt (@xref{Control}) to escape
- the exception handling context.
- @end deffn
- @node Fluids
- @section Fluids
- A fluid is a variable whose value is associated with the dynamic
- extent of a procedure call. The @code{(hoot fluids)} module
- implements Guile's fluid API. See @inlinefmtifelse{html,
- @url{https://www.gnu.org/software/guile/manual/html_node/Fluids-and-Dynamic-States.html,
- the Guile manual}, @ref{Fluids and Dynamic States,,,Guile Reference}}
- for more detailed information.
- @deffn {Procedure} make-fluid [default #f]
- Return a new fluid whose initial value is @var{default}.
- @end deffn
- @deffn {Procedure} fluid-ref fluid
- Return the value currently stored within @var{fluid}.
- @end deffn
- @deffn {Procedure} fluid-set! fluid val
- Set the contents of @var{fluid} to @var{val}.
- @end deffn
- @deffn {Procedure} with-fluid* fluid val thunk
- Call @var{thunk}, a procedure of zero arguments, in a context where
- the @var{fluid} is set to @var{val}. When control leaves the dynamic
- extent of @var{thunk}, @var{fluid} is set back to its previous value.
- @end deffn
- @deffn {Syntax} with-fluids ((fluid value) ...) body1 body2 ...
- Evaluate @var{body1} @var{body2} @dots{} in a context where each
- @var{fluid} is set to its respective @var{value}.
- @end deffn
- @node Parameters
- @section Parameters
- Parameters are Guile's facility for dynamically bound variables.
- While parameters are part of the default Guile environment, in Hoot
- they are provided by the @code{(hoot parameters)} module. See
- @inlinefmtifelse{html,
- @url{https://www.gnu.org/software/guile/manual/html_node/Parameters.html,
- the Guile manual}, @ref{Parameters,,,Guile Reference}} for more
- detailed information.
- A parameter is a procedure. To retrieve the value of a parameter,
- call it with zero arguments. To set a new value, call it with one
- argument.
- @lisp
- (define counter (make-parameter 0))
- (counter) ; => 0
- (counter 1)
- (counter) ; => 1
- (parameterize ((counter 2))
- (counter)) ; => 2
- (counter) ; => 1
- @end lisp
- @deffn {Procedure} make-parameter init [conv (lambda (x) x)]
- Return a new parameter whose initial value is @var{(conv init)}.
- @var{conv} is a procedure of one argument that transforms an incoming
- value into the value that is actually stored within the parameter.
- The default @var{conv} is an identity function that applies no
- transformation at all.
- @end deffn
- @deffn {Syntax} parameterize ((parameter value) ...) body1 body2 ...
- Evaluate @var{body1} @var{body2} @dots{} in a context where each
- @var{parameter} is set to its respective @var{value}. When control
- leaves the dynamic extent of the body, each @var{parameter} is set
- back to its previous value.
- @end deffn
- @node Hashtables
- @section Hashtables
- 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 @code{(hoot hashtables)}
- module is R6RS-like but with some notable differences.
- @deffn {Procedure} make-hashtable [hash hash] [equiv equal?]
- Return a new, empty hashtable that uses the hash procedure @var{hash}
- and equivalence procedure @var{equiv}.
- @end deffn
- @deffn {Procedure} make-eq-hashtable
- Return a new, empty hashtable that uses @code{eq?} as the equivalence
- function and hashes keys accordingly.
- @end deffn
- @deffn {Procedure} make-eqv-hashtable
- Return a new, empty hashtable that uses @code{eqv?} as the equivalence
- function and hashes keys accordingly.
- @end deffn
- @deffn {Procedure} hashtable? obj
- Return @code{#t} if @var{obj}
- @end deffn
- @deffn {Procedure} hashtable-hash table
- Return the hash function for @var{table}.
- @end deffn
- @deffn {Procedure} hashtable-equiv table
- Return the equivalence function for @var{table}.
- @end deffn
- @deffn {Procedure} hashtable-size table
- Return the current number of key/value pairs in @var{table}.
- @end deffn
- @deffn {Procedure} hashtable-ref table key [default #f]
- Return the value associated with @var{key} in @var{table}, or
- @var{default} if there is no such association.
- @end deffn
- @deffn {Procedure} hashtable-set! table key value
- Associate @var{val} with @var{key} in @var{table}, potentially
- overwriting any previous association with @var{key}.
- @end deffn
- @deffn {Procedure} hashtable-delete! table key
- Remove the association with @var{key} in @var{table}, if one exists.
- @end deffn
- @deffn {Procedure} hashtable-clear! table
- Remove all of the key/value associations in @var{table}.
- @end deffn
- @deffn {Procedure} hashtable-contains? table key
- Return @code{#t} if @var{key} has an associated value in @var{table}.
- @end deffn
- @deffn {Procedure} hashtable-copy table
- Return a copy of @var{table}.
- @end deffn
- @deffn {Procedure} hashtable-keys table
- Return a list of keys in @var{table}.
- @end deffn
- @deffn {Procedure} hashtable-values table
- Return a list of values in @var{table}.
- @end deffn
- @deffn {Procedure} hashtable-for-each proc table
- Apply @var{proc} to each key/value association in @var{table}. Each
- call is of the form @code{(proc key value)}.
- @end deffn
- @deffn {Procedure} hashtable-fold proc init table
- Accumulate a result by applying @var{proc} with each key/value
- association in @var{table} and the result of the previous @var{proc}
- call. Each call is of the form @code{(proc key value prev)}. For the
- first call, @code{prev} is the initial value @var{init}.
- @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
- The following hash functions are available:
- @deffn {Procedure} hash key size
- @deffnx {Procedure} hashq key size
- @deffnx {Procedure} hashv key size
- Return a hash value for @var{key} suitable for using in a table
- containing @var{size} buckets. The returned hash value will be in the
- range [0, @var{size}).
- @code{hashq} is the hash function used by @code{make-eq-hashtable},
- and @code{hashv} is used by @code{make-eqv-hashtable}.
- @end deffn
- @node Records
- @section Records
- Hoot extends the R7RS @code{define-record-type} form with additional
- features such as inheritance and opaque types.
- @deffn {Syntax} define-record-type name @
- [#:printer] [#:parent] [#:uid] [#:extensible? #t] [#:opaque? #f] @
- [#:allow-duplicate-field-names? #f] @
- constructor predicate @
- (field field-ref [field-set]) ...
- Define a new record type descriptor bound to @var{name}. Define a
- constructor procedure bound to @var{constructor} and a predicate
- procedure bound to @var{predicate}. For each @var{field}, define an
- accessor procedure @var{field-ref} and, optionally, a modifier
- procedure @var{field-set}.
- The record type will inherit from the record type descriptor bound to
- @var{parent}, as long as @var{parent} is extensible. By default,
- record types are extensible. A record type may be marked as ``final''
- by passing an @var{extensible?} flag of @code{#f}.
- When @var{opaque?} is @code{#t}, instances of this record type will be
- compared for equality by identity @emph{only}. This means that
- @code{(equal? a b)} only returns @code{#t} when @code{a} and @code{b}
- reference the same instance of the record type. In other words, they
- must be @code{eq?}. The default behavior is to perform deep
- structural equality checking by comparing record fields.
- When @var{printer} is specified, that procedure will be used when
- printing instances of this record type rather than the default
- printer.
- @var{uid} should be a unique identifier that will be associated with
- the record type when specified. Record types have no unique id by
- default.
- When @var{allow-duplicate-field-names?} is @code{#t}, field names may
- appear more than once in the fields section.
- @end deffn
- @node Pattern matching
- @section Pattern matching
- Hoot provides a port of Guile's @code{(ice-9 match)} module for
- pattern matching. See @inlinefmtifelse{html,
- @url{https://www.gnu.org/software/guile/manual/html_node/Pattern-Matching.html,
- Pattern Matching} in the Guile manual, @ref{Pattern Matching,,, guile,
- GNU Guile Reference Manual}} for more information.
- @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) in the @code{(hoot ffi)} module 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 extern): a non-null external value
- @item (ref null extern): a possibly null external value
- @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 extern): a non-null external value
- @item (ref null extern): a possibly null external value
- @item (ref eq): a Scheme value
- @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
- @deffn {Procedure} external-non-null? extern
- Return @code{#t} if @var{extern} is not 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 symbolic identifiers to integer identifiers.
- * Symbolifier:: Lift integer identifiers to symbolic identifiers.
- * 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.
- * REPL commands:: Run and debug Wasm at the REPL.
- @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 |
- `-----' `-------' `---------'
- ↓ ↓ ↓
- .-----. .-----------. .-----------.
- .-------- | eq | ------------. | all funcs | | noextern |
- | `-----' | `-----------' `-----------'
- ↓ ↓ ↓ ↓
- .-----. .-------------. .---------. .---------.
- | i31 | | all arrays | | struct | | nofunc |
- `-----' `-------------' `---------' `---------'
- ↓ ↓
- .-----. .-------------.
- | any | | all structs |
- `-----' `-------------'
- @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
- Tag types specify the function signature of the tags. Since tags are
- not truly functions, their signatures must only have parameters and no
- results.
- @deffn {Procedure} tag-type? obj
- Return @code{#t} if @var{obj} is a tag type.
- @end deffn
- @deffn {Procedure} tag-type-attribute tag-type
- Return the symbolic attribute of @var{tag-type}. Currently, there is
- only one valid attribute: @code{exception}.
- @end deffn
- @deffn {Procedure} tag-type-type tag-type
- Return the type of @var{tag-type}. This is expected to be a type use
- object that refers to a function signature.
- @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{symbolified} form (@pxref{Symbolifier}).
- @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 [#:emit-names? #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{emit-names?} 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
- @node Symbolifier
- @section Symbolifier
- The @code{(wasm symbolify)} module does the opposite of @code{(wasm
- resolve)} by giving symbolic names to all objects in a Wasm module.
- Symbolified Wasm is useful for disassembling binaries (see
- @code{wasm->wat} in @ref{GWAT}).
- @deffn {Procedure} symbolify-wasm wasm
- Return a new Wasm module derived from @var{wasm} where all definitions
- and uses have been given unique symbolic identifiers.
- @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{Wasm REPL commands} 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 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 Wasm REPL commands
- @section REPL commands
- 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-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 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
|