123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831 |
- \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} (version 22+)
- 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 the Great and Ultimate Question, of
- Life, the Universe, and 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{reflect-js/reflect.js} library. Copy that file and its
- associated Wasm helper modules, @file{reflect-wasm/reflect.wasm} and
- @file{reflect-wasm/wtf8.wasm}, to the @file{hoot-tutorial} directory:
- @example
- cd /path/to/hoot-tutorial
- cp /path/to/guile-hoot/reflect-js/reflect.js .
- cp /path/to/guile-hoot/reflect-wasm/reflect.wasm .
- cp /path/to/guile-hoot/reflect-wasm/wtf8.wasm .
- @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
- ./reflect.js
- ./hello.js
- ./index.html
- ./hello.wasm
- ./reflect.wasm
- ./wtf8.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
- The following procedures, @code{compile-value} and
- @code{compile-call}, are convenience procedures for when you just want
- to quickly compile something and see the result and you don't care
- about the intermediary Wasm binary.
- @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-apply proc . args
- Apply the Hoot procedure @var{proc} with @var{args}.
- @end deffn
- @deffn {Procedure} hoot-apply-async proc . args
- Apply the Hoot procedure @var{proc} in an asynchronous context.
- @var{proc} should be a procedure that accepts two additional arguments
- (arguments 0 and 1) in addition to @var{args}:
- @itemize
- @item @code{resolved}:
- An opaque external value representing the successful completion of the
- async operation.
- @item @code{rejected}:
- An opaque external value representing the failure of the async
- operation.
- @end itemize
- You almost certainly want to be using this procedure with
- @code{call-with-async-result} in the @code{(fibers promises)} module.
- @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/}
- 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{reflect-js/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="/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] [reflect_wasm_dir "."] [user_imports @{@}]
- Fetch and execute the Hoot Wasm binary at the URL @var{path} and
- return an array of Scheme values produced by the program.
- The reflection library requires the assistance of some Wasm helper
- modules, which are looked up in @var{reflect_wasm_dir}.
- 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", {
- user_imports: {
- 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
- @defmethod Procedure call_async args@dots{}
- Call procedure with @var{args} in an async context and return a
- promise to an array of result values. Before the procedure is called,
- @var{args} is prepended with two additional arguments: a function that
- resolves the promise, and a function that rejects the promise. It is
- up to the underlying Scheme procedure to use these values
- appropriately to settle the promise.
- @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.
- * Evaluation:: Interpret Scheme programs at runtime.
- * Fibers:: Lightweight concurrency.
- * Finalization:: Finalization registries.
- @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? obj
- Return @code{#t} if @var{obj} is a fluid.
- @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 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: (text) => document.createTextNode(text)
- }
- });
- @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 hello (make-text-node "Hello, world!"))
- @end lisp
- Since the return type of @code{make-text-node} is @code{(ref extern}),
- the value of @code{hello} is an @emph{external reference}. To check
- if a value is an external reference, use the @code{external?}
- predicate:
- @lisp
- (external? hello) ; => #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
- (external-null? hello) ; => #f
- @end lisp
- Note that we defined the return type of @code{make-text-node} to be
- @code{(ref extern)}, not @code{(ref null extern)}, so @code{hello}
- would never be null in this example.
- A large application will likely need to manipulate many different
- kinds of foreign values. This introduces an opportunity for errors
- because @code{external?} cannot distinguish between them. The
- solution is to wrap external values using disjoint types. To define
- such wrapper types, use @code{define-external-type}:
- @lisp
- (define-external-type <text-node>
- text-node? wrap-text-node unwrap-text-node)
- @end lisp
- @code{make-text-node} could then be reimplemented like this:
- @lisp
- (define-foreign %make-text-node
- "document" "createTextNode"
- (ref string) -> (ref extern))
- (define (make-text-node str)
- (wrap-text-node (%make-text-node str)))
- (define hello (make-text-node "Hello, world!"))
- (external? hello) ; => #f
- (text-node? hello) ; => #t
- (external? (unwrap-text-node hello)) ; => #t
- @end lisp
- We've now explained the basics of using the FFI. Read below for
- detailed API documentation.
- @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 null string): a possibly null 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
- @deffn {Syntax} define-external-type name predicate wrap unwrap
- @deffnx {Syntax} define-external-type name predicate wrap unwrap print
- Define a new record type named @var{name} for the purposes of wrapping
- external values. @var{predicate} is the name of the record type
- predicate. @var{wrap} is the name of the record type constructor.
- @var{unwrap} is the name of the record field accessor that returns the
- wrapped value. Optionally, @var{print} is a procedure that accepts
- two arguments (@code{obj} and @code{port}) and prints a textual
- representation of the wrapped value.
- @end deffn
- @node Evaluation
- @section Evaluation
- Hoot is an ahead-of-time, whole-program compiler, but what if we want
- to evaluate arbitrary Scheme code at runtime? Fortunately, the
- @code{(hoot eval}) module provides a Scheme interpreter for this
- purpose!
- Evaluation happens in the context of an environment. Here's a simple
- example:
- @lisp
- (use-modules (hoot eval) (hoot interaction-environment))
- (eval '(let ((x 1) (y 2)) (+ x y)) (interaction-environment))
- @end lisp
- @deffn {Procedure} eval exp toplevel-env
- Evaluate @code{exp} in the context of the environment
- @var{toplevel-env}.
- @end deffn
- @deffn {Syntax} current-environment
- Expands to code that creates an evaluation environment containing all
- of the available top-level bindings at the location where
- @code{(current-environment)} is used.
- @end deffn
- @deffn {Procedure} make-empty-environment
- Return an empty evaluation environment.
- @end deffn
- The @code{(hoot interaction-environment)} module provides a simple
- environment containing all of Guile's default bindings.
- @deffn {Procedure} interaction-environment
- Return a basic evaluation environment with all of Guile's default
- top-level bindings.
- @end deffn
- @node Fibers
- @section Fibers
- Fibers is a facility for lightweight concurrency in Guile. Hoot's
- built-in fibers API is based on the third-party
- @url{https://github.com/wingo/fibers,fibers library} available for the
- Guile VM.
- @menu
- * Fibers example:: A simple example program.
- * Operations:: Asynchronous events.
- * Channels:: Communication between fibers.
- * Timers:: Sleepy fibers.
- * Conditions:: Send signals between fibers.
- * Promises:: Integrating fibers with the Wasm host.
- @end menu
- @node Fibers example
- @subsection Fibers example
- Running a fibers program is a little different than running a regular
- Scheme program. Fibers programs need to be invoked in an asychronous
- context from the host (usually JavaScript) and need to communicate the
- overall success or failure of the program back to the host.
- To demonstrate, let's make a @emph{very} simple program that sleeps
- for one second before returning the value @code{42}.
- @lisp
- (use-modules (fibers promises)
- (fibers timers))
- (lambda (resolved rejected)
- (call-with-async-result
- resolved rejected
- (lambda ()
- (display "Waiting... ")
- (force-output)
- (sleep 1)
- (display "done!\n")
- (force-output)
- 42)))
- @end lisp
- Note that we've wrapped our program in a @code{lambda} that receives
- @code{resolved} and @code{rejected} arguments. When the Wasm host
- calls this procedure in an asynchronous manner, these arguments will
- be special host values that are used to resolve or reject the promise
- (@pxref{Promises}) that represents the result of our program. The
- @code{call-with-async-result} procedure receives these host values and
- takes care of all the promise plumbing for us.
- To try this out, save the above code to @file{fibers-example.scm} and
- run the following command:
- @example
- guild compile-wasm --run --async fibers-test.scm
- @end example
- The expected output is:
- @example
- Waiting... done!
- (42)
- @end example
- There's just one value returned but since Scheme supports multiple
- return values, the result of the program is a @emph{list} of values.
- To invoke an asynchronous procedure from JavaScript, use the
- @code{call_async} method on a @code{Procedure} object:
- @example
- const [proc] = await Scheme.load_main("fibers-test.wasm");
- proc.call_async();
- @end example
- Read on for the detailed fibers API reference.
- @node Operations
- @subsection Operations
- Operations are first-class abstractions for asynchronous events.
- There are primitive operation types, such as waiting for a timer
- (@pxref{Timers}) or waiting for a message on a channel
- (@pxref{Channels}). Operations can also be combined and transformed
- using the @code{choice-operation} and @code{wrap-operation} from the
- @code{(fibers operations)} module.
- @deffn {Procedure} wrap-operation op f
- Given the operation @var{op}, return a new operation that, if and when
- it succeeds, will apply @var{f} to the values yielded by performing
- @var{op}, and yield the result as the values of the wrapped operation.
- @end deffn
- @deffn {Procedure} choice-operation . ops
- Given the operations @var{ops}, return a new operation that if it
- succeeds, will succeed with one and only one of the sub-operations
- @var{ops}.
- @end deffn
- Finally, once you have an operation, you can perform it using
- @code{perform-operation}.
- @deffn {Procedure} perform-operation op
- Perform the operation @var{op} and return the resulting values. If the
- operation cannot complete directly, block until it can complete.
- @end deffn
- There is also a low-level constructor for other modules that implement
- primitive operation types:
- @deffn {Procedure} make-base-operation wrap-fn try-fn block-fn
- Make a fresh base operation.
- @end deffn
- @node Channels
- @subsection Channels
- Channels are the way to communicate between fibers. To use them, load
- the @code{(fibers channels)} module.
- @deffn {Procedure} make-channel
- Make a fresh channel.
- @end deffn
- @deffn {Procedure} channel? obj
- Return @code{#t} if @var{obj} is a channel, or @code{#f} otherwise.
- @end deffn
- @deffn {Procedure} put-operation channel message
- Make an operation that if and when it completes will rendezvous with a
- receiving operation to send @var{message} over @var{channel}.
- @end deffn
- @deffn {Procedure} get-operation channel
- Make an operation that if and when it completes will rendezvous with a
- sending operation to receive one value from @var{channel}.
- @end deffn
- @deffn {Procedure} put-message channel message
- Send @var{message} on @var{channel}, and return zero values. If there
- is already a receiver waiting to receive a message on this channel,
- give it our message and continue. Otherwise, block until a receiver
- becomes available.
- Equivalent to:
- @lisp
- (perform-operation (put-operation channel message))
- @end lisp
- @end deffn
- @deffn {Procedure} get-message channel
- Receive a message from @var{channel} and return it. If there is
- already a sender waiting to send a message on this channel, take its
- message directly. Otherwise, block until a sender becomes available.
- Equivalent to:
- @lisp
- (perform-operation (get-operation channel))
- @end lisp
- @end deffn
- @node Timers
- @subsection Timers
- Timers are a kind of operation that, you guessed it, let you sleep
- until a certain time. The timer API can be found in the @code{(fibers
- timers)} module.
- @deffn {Procedure} sleep-operation seconds
- Make an operation that will succeed with no values when @var{seconds}
- have elapsed.
- @end deffn
- @deffn {Procedure} timer-operation expiry
- Make an operation that will succeed when the current time is greater
- than or equal to @var{expiry}, expressed in internal time units. The
- operation will succeed with no values.
- @end deffn
- @deffn {Procedure} sleep seconds
- Block the calling fiber until @var{seconds} have elapsed.
- @end deffn
- @node Conditions
- @subsection Conditions
- Condition variables are a simple one-bit form of concurrent
- communication. A condition variable has two states: it starts in the
- @dfn{unsignalled} state and later may transition to the
- @dfn{signalled} state. When a condition becomes signalled, any
- associated waiting operations complete. The following API can be
- found in the @code{(fibers conditions)} module.
- @deffn {Procedure} make-condition
- Make a new condition variable.
- @end deffn
- @deffn {Procedure} condition? obj
- Return @code{#t} if @var{obj} is a condition variable, or @code{#f}
- otherwise.
- @end deffn
- @deffn {Procedure} signal-condition! cvar
- Signal @var{cvar}, notifying all waiting fibers and preventing
- blocking of future fibers waiting on this condition.
- @end deffn
- @deffn {Procedure} wait-operation cvar
- Make an operation that will succeed with no values when @var{cvar}
- becomes signalled.
- @end deffn
- @deffn {Procedure} wait cvar
- Block the calling fiber until @var{cvar} becomes signalled.
- Equivalent to @code{(perform-operation (wait-operation cvar))}.
- @end deffn
- @node Promises
- @subsection Promises
- Promises represent the result of a computation that may or may not
- complete at some later point in time. They are the primitive upon
- which asynchronous concurrency is based in
- @url{https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise,JavaScript},
- whether using promises directly or through the
- @url{https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function,async}/@url{https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await,await}
- syntax.
- Hoot fibers use these host promises under the hood and the
- @code{(fibers promises)} module provides the API for doing so. This
- is @emph{mostly} transparent to the programmer, except at program
- startup. Programs that use fibers must be called in an async context
- in which they receive special values used to resolve or reject the
- promise representing the result of the entire program. @xref{Fibers
- example} for a walkthrough of this process.
- @deffn {Procedure} await-promise-operation promise
- Make an operation that will complete when @var{promise} is resolved.
- Performing the operation produces one value: a thunk which when called
- will either return the value or throw an exception.
- @end deffn
- @deffn {Procedure} await promise
- Suspend the current fiber until @var{promise} is resolved. If the
- promise resolves successfully, one value is returned. Otherwise, an
- exception is thrown.
- @end deffn
- @deffn {Procedure} call-with-async-result resolved rejected thunk
- Call @var{thunk} and resolve the promise with @var{resolved} and the
- returned values. If an exception is thrown, the promise is rejected
- with @var{rejected} and the exception.
- @var{resolved} and @var{rejected} are external host values that are
- obtained by calling a Scheme procedure asynchronously from the host.
- See the @code{Procedure.call_async} method in @ref{JavaScript API
- reference} and the @code{hoot-apply-async} procedure in
- @ref{Reflection} for more information on asynchronous procedure calls.
- @end deffn
- @node Finalization
- @section Finalization
- The @code{(hoot finalization)} module provides an interface for the
- JavaScript
- @url{https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry,FinalizationRegistry}
- class, which notifies user code when a registered object has been
- garbage collected. Finalization registries are quite different from
- Guile's @inlinefmtifelse{html,
- @url{https://www.gnu.org/software/guile/manual/html_node/Guardians.html,
- guardians}, @ref{Guardians,,,Guile Reference}}:
- @itemize
- @item
- Guardians return references to the objects they have protected from
- garbage collection. Finalization registries @emph{do not} protect
- objects from garbage collection at all. Instead, when a registered
- object is garbage collected, the finalization registry passes along a
- user-specified ``held value''. A held value @emph{cannot} be a
- reference to the object being registered for finalization.
- @item
- Guardians are polled, whereas finalization registries use an
- asynchronous callback function.
- @item
- Finalization registries allow objects to be unregistered, whereas
- objects cannot be removed from a guardian once they have been added.
- @end itemize
- The following contrived example will print ``hey'' when the registered
- object is garbage collected:
- @lisp
- (define (cleanup x)
- (display x)
- (newline))
- (define registry (make-finalization-registry cleanup))
- (finalization-registry-register! registry (list 'garbage) 'hey)
- @end lisp
- @deffn {Procedure} make-finalization-registry cleanup
- Return a new finalization registry that will call @var{cleanup} (a
- procedure of one argument) whenever a registered object is garbage
- collected.
- @end deffn
- @deffn {Procedure} finalization-registry? obj
- Return @var{#t} if @var{obj} is a finalization registry.
- @end deffn
- @deffn {Procedure} finalization-registry-register! registry target held-value [unregister-token]
- Register @var{target} with @var{registry}. When @var{target} is
- garbage collected, the cleanup callback will receive @var{held-value}.
- @var{held-value} @emph{cannot} be @code{eq?} to @var{target}.
- If @var{unregister-token} is specified and not @code{#f} then this
- token can be used to unregister @var{target} later.
- @end deffn
- @deffn {Procedure} finalization-registry-unregister! registry unregister-token
- Unregister the objects associated with @var{unregister-token} from
- @var{registry}. Return @var{#t} if at least one object was
- unregistered this way.
- @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
|