learn webassembly: build web applications with native … · production coordinator: nilesh mohite...

501

Upload: others

Post on 20-May-2020

2 views

Category:

Documents


0 download

TRANSCRIPT

LearnWebAssembly

BuildwebapplicationswithnativeperformanceusingWasmandC/C++

MikeRourke

BIRMINGHAM-MUMBAI

LearnWebAssembly

Copyright©2018PacktPublishingAllrightsreserved.Nopartofthisbookmaybereproduced,storedinaretrievalsystem,ortransmittedinanyformorbyanymeans,withoutthepriorwrittenpermissionofthepublisher,exceptinthecaseofbriefquotationsembeddedincriticalarticlesorreviews.

Everyefforthasbeenmadeinthepreparationofthisbooktoensuretheaccuracyoftheinformationpresented.However,theinformationcontainedinthisbookissoldwithoutwarranty,eitherexpressorimplied.Neithertheauthor,norPacktPublishingoritsdealersanddistributors,willbeheldliableforanydamagescausedorallegedtohavebeencauseddirectlyorindirectlybythisbook.

PacktPublishinghasendeavoredtoprovidetrademarkinformationaboutallofthecompaniesandproductsmentionedinthisbookbytheappropriateuseofcapitals.However,PacktPublishingcannotguaranteetheaccuracyofthisinformation.

CommissioningEditor:KunalChaudhariAcquisitionEditor:TrushaShriyanContentDevelopmentEditor:AishwaryaGawankarTechnicalEditor:SurabhiKulkarniCopyEditor:SafisEditingProjectCoordinator:SheejalShahProofreader:SafisEditingIndexer:PriyankaDhadkeGraphics:AlishonMendonsaProductionCoordinator:NileshMohite

Firstpublished:September2018

Productionreference:1240918

PublishedbyPacktPublishingLtd.LiveryPlace35LiveryStreetBirminghamB32PB,UK.

ISBN978-1-78899-737-9

www.packtpub.com

Tomybeautifulandinfinitelypatientwife,Elisabeth.Icouldn'thavedonethiswithoutyourloveandsupport.Toallthemembersofmywolfpack,howl.

mapt.io

Maptisanonlinedigitallibrarythatgivesyoufullaccesstoover5,000booksandvideos,aswellasindustryleadingtoolstohelpyouplanyourpersonaldevelopmentandadvanceyourcareer.Formoreinformation,pleasevisitourwebsite.

Whysubscribe?SpendlesstimelearningandmoretimecodingwithpracticaleBooksandVideosfromover4,000industryprofessionals

ImproveyourlearningwithSkillPlansbuiltespeciallyforyou

GetafreeeBookorvideoeverymonth

Maptisfullysearchable

Copyandpaste,print,andbookmarkcontent

PacktPub.comDidyouknowthatPacktofferseBookversionsofeverybookpublished,withPDFandePubfilesavailable?YoucanupgradetotheeBookversionatwww.PacktPub.comandasaprintbookcustomer,youareentitledtoadiscountontheeBookcopy.Getintouchwithusatservice@packtpub.comformoredetails.

Atwww.PacktPub.com,youcanalsoreadacollectionoffreetechnicalarticles,signupforarangeoffreenewsletters,andreceiveexclusivediscountsandoffersonPacktbooksandeBooks.

Contributors

AbouttheauthorMikeRourkehasbeenwritingcodeforoveradecade.HegothisstartcreatingMicrosoftAccessapplicationsusingVBAanddecidedhewantedtoworkwithJavaScriptfull-timeafterbuildingaMozillaFirefoxextension.HehasaB.S.inmechanicalengineeringtechnologyandworkedprimarilyinproductdesign/manufacturingengineeringrolesbeforestartingacareerasasoftwareengineerin2017.Currently,heworksforaChicago-basedconsultingcompanyandisfocusedprimarilyonfrontendJavaScriptdevelopment.When'shenotwritingcode,he'soutinthewoodscampingwithhiswolfbrothers.

Iwouldliketothankmywife,Elisabeth,forherloveandsupport.IwouldalsoliketothankmycolleaguesatPanderaLabsfortheirenthusiasm,support,andvaluablesuggestions.

Aboutthereviewers

DanRutaisafreshgraduate,abouttostartanMScincomputervision.HegotstartedwithWebAssemblybyimplementingasmallweb-baseddeeplearninglibrary,andmessingaroundwithWebAssemblyandGPGPU.

OtherpublicationshehasworkedonincludeoccasionaltechnicalblogsonMedium,andateamresearchpapercombiningAI,AR,andWebGLshaderstoassistthevisuallyimpaired,whichhepresentedataconference.

HisprojectscanbefollowedonGitHubandMedium(DanRuta),oronhiswebsiteandtweets(Dan_Ruta).

MaximShaydoakaMoreasMaxGraeyisanindependentdeveloper,consultant,systemarchitectfromUkraine,hehasworkedwithatLaSoftasaCTOandisabigfanofopensourcecommunity.

HecontinuestobeanenduringcontributorforopensourceprojectsdedicatedtoWebAssembly,suchasAssemblyScriptlanguagethathasbeengainingalotofattentionlately.HehappenstobeveryinterestedindevelopmentofWebGL,WebVRtechnologies,andFlowBasedProgrammingaswell.

ThisprojectcouldnothavebeencompletedwithoutbeingreviewedbyAlonZakai(kripken)knownforhisworkonemscriptenandbinaryen.SpecialthankstoDanielWirtz(dcodeIO)whoisthemaincontributorofAssemplyScriptandanincrediblyproductivemate.Lastbutnottheleast,Iwouldliketothankmyparents—Mr.andMrsShaydo,withoutthemnoneofthiswouldindeedbepossible.

PacktissearchingforauthorslikeyouIfyou'reinterestedinbecominganauthorforPackt,pleasevisitauthors.packtpub.comandapplytoday.Wehaveworkedwiththousandsofdevelopersandtechprofessionals,justlikeyou,tohelpthemsharetheirinsightwiththeglobaltechcommunity.Youcanmakeageneralapplication,applyforaspecifichottopicthatwearerecruitinganauthorfor,orsubmityourownidea.

TableofContentsTitlePage

CopyrightandCredits

LearnWebAssembly

Dedication

PacktPub.com

Whysubscribe?

PacktPub.com

Contributors

Abouttheauthor

Aboutthereviewers

Packtissearchingforauthorslikeyou

Preface

Whothisbookisfor

Whatthisbookcovers

Togetthemostoutofthisbook

Downloadtheexamplecodefiles

Downloadthecolorimages

Conventionsused

Getintouch

Reviews

1. WhatisWebAssembly?

TheroadtoWebAssembly

TheevolutionofJavaScript

GoogleandNativeClient

Mozillaandasm.js

WebAssemblyisborn

WhatexactlyisWebAssemblyandwherecanIuseit?

Officialdefinition

Binaryinstructionformat

Portabletargetforcompilation

Thecorespecification

Languageconcepts

Semanticphases

TheJavaScriptandWebAPIs

SowillitreplaceJavaScript?

WherecanIuseit?

Whatlanguagesaresupported?

CandC++

Rust

Otherlanguages

Whatarethelimitations?

Nogarbage collection

NodirectDOMaccess

Nosupportinolderbrowsers

HowdoesitrelatetoEmscripten?

Emscripten'srole

TheEMSDKandBinaryen

Summary

Questions

Furtherreading

2. ElementsofWebAssembly-Wat,Wasm,andtheJavaScriptAPI

Commonstructureandabstractsyntax

Wat

DefinitionsandS-expressions

Values,types,andinstructions

Roleinthedevelopmentprocess

Binaryformatandthemodulefile(Wasm)

Definitionandmoduleoverview

Modulesections

TheJavaScriptandWebAPIs

WebAssemblystoreandobjectcaches

LoadingamoduleandtheWebAssemblynamespacemethods

WebAssemblyobjects

WebAssembly.Module

WebAssembly.Instance

WebAssembly.Memory

WebAssembly.Table

WebAssemblyerrors(CompileError,LinkError,RuntimeError)

ConnectingthedotswithWasmFiddle

WhatisWasmFiddle?

CcodetoWat

WasmtoJavaScript

Summary

Questions

Furtherreading

3. SettingUpaDevelopmentEnvironment

Installingthedevelopmenttooling

Operatingsystemsandhardware

macOS 

Ubuntu

Windows

Packagemanagers

HomebrewformacOS

AptforUbuntu

ChocolateyforWindows

Git

InstallingGitonmacOS

InstallingGitonUbuntu

InstallingGitonWindows

Node.js

nvm

InstallingnvmonmacOS

InstallnvmonUbuntu

InstallingnvmonWindows

InstallingNode.jsusingnvm

GNUmakeandrimraf

GNUMakeonmacOSandUbuntu

InstallingGNUMakeonmacOS

InstallingGNUMakeonUbuntu

InstallingGNUmakeonWindows

Installingrimraf

VSCode

InstallingVisualStudioCodeonmacOS

InstallingVisualStudioCodeonUbuntu

InstallingVSCodeonWindows

ConfiguringVSCode

Managingsettingsandcustomization

Extensionsoverview

ConfigurationforC/C++andWebAssembly

InstallingC/C++forVSCode

ConfiguringC/C++forVSCode

WebAssemblyToolkitforVSCode

Otherusefulextensions

Autorenametag

Bracketpaircolorizer

MaterialIconthemeandAtomOneLighttheme

Settingupfortheweb

Cloningthebookexamplesrepository

Installingalocalserver

Validatingyourbrowser

ValidatingGoogleChrome

ValidatingMozillaFirefox

Validatingotherbrowsers

Othertools

iTerm2formacOS

TerminatorforUbuntu

cmderforWindows

ZshandOh-My-Zsh

Summary

Questions

Furtherreading

4. InstallingtheRequiredDependencies

Thedevelopmentworkflow

Stepsintheworkflow

IntegratingToolingintotheworkflow

EmscriptenandtheEMSDK

Emscriptenoverview

WheredoestheEMSDKfitin?

Installingtheprerequisites

Commonprerequisites

InstallingtheprerequisitesonmacOS

InstallingtheprerequisitesonUbuntu

InstallingtheprerequisitesonWindows

InstallingandconfiguringtheEMSDK

Installationprocessacrossallplatforms

InstallationonmacOSandUbuntu

InstallationandconfigurationonWindows

ConfigurationinVSCode

Testingthecompiler

TheCcode

CompilingtheCcode

Summary

Questions

Furtherreading

5. CreatingandLoadingaWebAssemblyModule

CompilingCwithEmscriptengluecode

WritingtheexampleCcode

CompilingtheexampleCcode

OutputtingHTMLwithgluecode

OutputtinggluecodewithnoHTML

LoadingtheEmscriptenmodule

Pre-generatedloadingcode

Writingcustomloadingcode

CompilingCwithoutthegluecode

CcodeforWebAssembly

CompilingwithaBuildTaskinVSCode

FetchingandinstantiatingaWasmfile

CommonJavaScriptloadingcode

TheHTMLpage

Servingitallup

Summary

Questions

Furtherreading

6. InteractingwithJavaScriptandDebugging

TheEmscriptenmoduleversustheWebAssemblyobject

WhatistheEmscriptenmodule?

Defaultmethodsinthegluecode

DifferenceswiththeWebAssemblyobject

CallingcompiledC/C++functionsfromJavaScript

CallingfunctionsfromaModule 

Module.ccall()

Module.cwrap()

C++andnamemangling

CallingfunctionsfromaWebAssemblyinstance

CallingJavaScriptfunctionsfromC/C++

InteractingwithJavaScriptusinggluecode

Executingstringswithemscripten_run_script()

ExecutinginlineJavaScriptwithEM_ASM()

ReusinginlineJavaScriptwithEM_JS()

Examplesofusinggluecode

TheCcode

TheHTMLcode

Compilingandservingtheresult

InteractingwithJavaScriptwithoutgluecode

PassingJavaScripttoC/C++usingtheimportobject

CallingimportedfunctionsinC/C++

Anexamplewithoutgluecode

TheC++code

TheHTMLcode

Compilingandservingtheresult

AdvancedEmscriptenfeatures

Embind

FileSystemAPI

FetchAPI

Debugginginthebrowser

High-leveloverview

Usingsourcemaps

Summary

Questions

Furtherreading

7. CreatinganApplicationfromScratch

CooktheBooks– makingWebAssemblyaccountable

Overviewandfunctionality

JavaScriptlibrariesused

Vue

UIkit

Lodash

Data-drivendocuments

Otherlibraries

Candthebuildprocess

Settinguptheproject

ConfiguringforNode.js

Addingfilesandfolders

Configuringthebuildstep

SettingupamockAPI

DownloadingtheCstdlibWasm

Thefinalresult

BuildingtheCportion

Overview

Anoteregardingworkflow

Cfilecontents

Declarations

Linkedlistoperations

transactionsoperations

transactionscalculations

Categorycalculations

CompilingtoWasm

BuildingtheJavaScriptportion

Overview

Anoteaboutbrowsercompatibility

CreatingaWasminstanceininitializeWasm.js

InteractingwithWasminWasmTransactions.js

UtilizingtheAPIinapi.js

Managingglobalstateinstore.js

Theimportandstoredeclarations

Transactionsoperations

TransactionModalmanagement

Balancescalculation

Storeinitialization

Loadingtheapplicationinmain.js

Addingthewebassets

CreatingtheVuecomponents

ThestructureofaVuecomponent

TheAppcomponent

TheBalancesBar

TheTransactionsTab

TheChartsTab

Runningtheapplication

Validatingthe/srcfolder

Startitup!

Testingitout

Changinginitialbalances

Creatinganewtransaction

Deletinganexistingtransaction

Editinganexistingtransaction

TestingtheChartstab

Wrapup

Summary

Questions

Furtherreading

8. PortingaGamewithEmscripten

Overviewofthegame

WhatisTetris?

Thesourceofthesource

Anoteaboutporting

Gettingthecode

Buildingthenativeproject

Thegameinaction

Thecodebaseindepth

Breakingthecodeintoobjects

Theconstantsfile

Thepiececlass

Theconstructoranddraw()function

Themove(),rotate(),andisBlock()functions

ThegetColumn()andgetRow()functions

TheBoardclass

Theconstructoranddraw()function

TheisCollision()function

Theunite()function

ThedisplayScore()function

TheGameclass

Theconstructoranddestructor

Theloop()function

Themainfile

PortingtoEmscripten

Preparingforporting

What'schanging?

Addingthewebassets

Portingtheexistingcode

Updatingtheconstantsfile

Buildingandrunningthegame

BuildingwithVSCodetasks

BuildingwithaMakefile

Runningthegame

Summary

Questions

Furtherreading

9. IntegratingwithNode.js

WhyNode.js?

Seamlessintegration

Complementarytechnologies

Developmentwithnpm

Server-sideWebAssemblywithExpress

Overviewoftheproject

Expressconfiguration

InstantiatingaWasmmodulewithNode.js

Creatingamockdatabase

InteractingwiththeWebAssemblymodule

WrappinginteractioninTransaction.js

Transactionoperationsinassign-routes.js

Buildingandrunningtheapplication

Buildingtheapplication

Startingandtestingouttheapplication

Client-sideWebAssemblywithWebpack

Overviewoftheproject

WhatisWebpack?

InstallingandconfiguringWebpack

Dependenciesoverview

Configuringloadersandpluginsinwebpack.config.js

TheCcode

Definitionsanddeclarations

Thestart()function

TheupdateRectLocation()function

TheJavaScriptcode

Theimportstatements

Componentstate

Wasminitialization

Componentmounting

Componentrendering

Buildingandrunningtheapplication

Testingthebuild

Runningthestartscript

TestingWebAssemblymoduleswithJest

Thecodebeingtested

Testingconfiguration

Testsfilereview

Runningthetests

Summary

Questions

Furtherreading

10. AdvancedToolsandUpcomingFeatures

WABTandBinaryen

WABT–theWebAssemblybinarytoolkit

Binaryen

CompilingwithLLVM

Theinstallationprocess

Theexamplecode

TheC++file

TheHTMLfile

Compilingandrunningtheexample

Onlinetooling

WasmFiddle

WebAssemblyExplorer

WebAssemblyStudio

ParallelWasmwithWebWorkers

WebWorkersandWebAssembly

Creatingaworker

TheWebAssemblyworkflow

LimitationsinGoogleChrome

Overviewofthecode

TheCcode

TheJavaScriptcode

Definingthreadexecutioninworker.js

InteractingwithWasminWasmWorker.js

Loadingtheapplicationinindex.js

Thewebassets

Buildingandrunningtheapplication

CompilingtheCfiles

Interactingwiththeapplication

DebuggingWebWorkers

Upcomingfeatures

Thestandardizationprocess

Threads

Hostbindings

Garbagecollection

Referencetypes

Summary

Questions

Furtherreading

OtherBooksYouMayEnjoy

Leaveareview-letotherreadersknowwhatyouthink

PrefaceThisbookintroducesreaderstoWebAssembly,anewandexcitingtechnologycapableofexecutinglanguagesotherthanJavaScriptinthebrowser.ThebookdescribeshowtobuildaC/JavaScriptapplicationfromscratchthatusesWebAssemblyandtheprocessforportinganexistingC++codebasetoruninthebrowserwiththehelpofEmscripten.

WebAssemblyrepresentsanimportantshiftforthewebplatform.AsacompilationtargetforlanguagessuchasC,C++,andRust,itprovidestheabilitytobuildanewclassofapplication.WebAssemblyissupportedbyallofthemajorbrowservendorsandrepresentsacollaborativeeffort.

Inthisbook,we'lldescribetheelementsthatmakeupWebAssembly,aswellasitsorigins.We'llwalkthroughtheprocessofinstallingtherequiredtools,settingupadevelopmentenvironment,andinteractingwithWebAssembly.We'llworkthroughsimpleexamplesandprogressthroughincreasinglyadvancedusecases.Bytheendofthisbook,you'llbewell-equippedtouseWebAssemblyinyourC,C++,orJavaScriptproject.

WhothisbookisforIfyouareaC/C++programmerwhowishestobuildapplicationsfortheweb,orawebdeveloperwhowishestoimprovetheperformanceoftheirJavaScriptapplications,thenthisbookisforyou.ThebookisintendedfordevelopersfamiliarwithJavaScriptwhowouldn'tmindlearningsomeCandC++(andviceversa).ThisbookaccommodatesforC/C++programmersandJavaScriptprogrammersalikebyprovidingtwoexampleapplications.

WhatthisbookcoversChapter1,WhatisWebAssembly?,describestheoriginsofWebAssemblyandprovidesahigh-leveloverviewofthetechnology.ItcovershowWebAssemblycanbeused,whichprogramminglanguagesaresupported,anditscurrentlimitations.

Chapter2,ElementsofWebAssembly–Wat,Wasm,andtheJavaScriptAPI,outlinestheelementsthatcompriseWebAssembly.Itprovidesadetailedexplanationofthetextandbinaryformats,aswellasthecorrespondingJavaScriptandWebAPIs.

Chapter3,SettingUpaDevelopmentEnvironment,walksthroughthetoolingusedtodevelopwithWebAssembly.Itprovidestheinstallationinstructionsforeachplatformandprovidesrecommendationsforimprovingthedevelopmentexperience.

Chapter4,InstallingtheRequiredDependencies,providesinstructionsforinstallingthetoolchainrequirementsforeachplatform.Bytheendofthischapter,you'llbeabletocompileCandC++toWebAssemblymodules.

Chapter5,CreatingandLoadingaWebAssemblyModule,explainshowtogenerateaWebAssemblymoduleusingEmscriptenandhowflagsarepassedtothecompileraffecttheresultingoutput.ItdescribesthetechniquesforloadingaWebAssemblymoduleinthebrowser.

Chapter6,InteractingwithJavaScriptandDebugging,elaboratesonthedifferencesbetweenEmscripten'sModuleobjectandthebrowser'sglobalWebAssemblyobject.ThischapterdescribesthefeaturesEmscriptenprovidesaswellasinstructionsforgeneratingsourcemaps.

Chapter7,CreatinganApplicationfromScratch,walksthroughthecreationofaJavaScriptaccountingapplicationthatinteractswithaWebAssemblymodule.WewillwriteCcodetocalculatevaluesfromaccountingtransactionsandpassthedatabetweenJavaScriptandthecompiledWebAssemblymodule.

Chapter8,PortingaGamewithEmscripten,takesastep-by-stepapproachtoportinganexistingC++gametoWebAssemblyusingEmscripten.AfterreviewingtheexistingC++codebase,changesaremadetotheappropriatefilestoenablethegametoruninthebrowser.

Chapter9,IntegratingwithNode.js,demonstrateshowNode.jsandnpmcanbeusedwithWebAssemblyontheserverandclientside.ThechaptercoverstheuseofWebAssemblyinanExpressapplication,integratingWebAssemblywithwebpack,andtestingaWebAssemblymoduleusingJest.

Chapter10,AdvancedToolsandUpcomingFeatures,coversadvancedtools,usecases,andnewWebAssemblyfeaturescurrentlyintheprocessofstandardization.ThischapterdescribesWABT,Binaryen,andthetoolingavailableonline.Inthischapter,you'lllearnhowtocompileWebAssemblymodulesusingLLVMandhowWebAssemblymodulescanbeusedwithWebWorkers.Thechapterwrapsupwithadescriptionofthestandardizationprocessandareviewofsomeoftheexcitingfeaturesintheprocessofbeingaddedtothespecification.

TogetthemostoutofthisbookYoushouldhavesomeprogrammingexperienceandunderstandconceptssuchasvariables,andfunctions.Ifyou'veneverseenalineofJavaScriptorC/C++code,youmaywanttodosomepreliminaryresearchbeforeworkingthroughtheexamplesinthisbook.I'vechosentouseJavaScriptES6/7featuressuchasdestructuringandarrowfunctions,soifyouhaven'tworkedwithJavaScriptinthelast3-4years,thesyntaxmaylookslightlydifferent.

DownloadtheexamplecodefilesYoucandownloadtheexamplecodefilesforthisbookfromyouraccountatwww.packtpub.com.Ifyoupurchasedthisbookelsewhere,youcanvisitwww.packtpub.com/supportandregistertohavethefilesemaileddirectlytoyou.

Youcandownloadthecodefilesbyfollowingthesesteps:

1. Loginorregisteratwww.packtpub.com.2. SelecttheSUPPORTtab.3. ClickonCodeDownloads&Errata.4. EnterthenameofthebookintheSearchboxandfollowtheonscreen

instructions.

Oncethefileisdownloaded,pleasemakesurethatyouunziporextractthefolderusingthelatestversionof:

WinRAR/7-ZipforWindowsZipeg/iZip/UnRarXforMac7-Zip/PeaZipforLinux

ThecodebundleforthebookisalsohostedonGitHubathttps://github.com/PacktPublishing/Learn-WebAssembly.Incasethere'sanupdatetothecode,itwillbeupdatedontheexistingGitHubrepository.

Wealsohaveothercodebundlesfromourrichcatalogofbooksandvideosavailableathttps://github.com/PacktPublishing/.Checkthemout!

DownloadthecolorimagesWealsoprovideaPDFfilethathascolorimagesofthescreenshots/diagramsusedinthisbook.Youcandownloadithere:https://www.packtpub.com/sites/default/files/downloads/9781788997379_ColorImages.pdf.

ConventionsusedThereareanumberoftextconventionsusedthroughoutthisbook.

CodeInText:Indicatescodewordsintext,databasetablenames,foldernames,filenames,fileextensions,pathnames,dummyURLs,userinput,andTwitterhandles.Hereisanexample:"instantiate()istheprimaryAPIforcompilingandinstantiatingWebAssemblycode."

Ablockofcodeissetasfollows:

intaddTwo(intnum){

returnnum+2;

}

Whenwewishtodrawyourattentiontoaparticularpartofacodeblock,therelevantlinesoritemsaresetinbold:

intcalculate(intfirstVal,intsecondVal){

returnfirstVal-secondVal;

}

Anycommand-lineinputoroutputiswrittenasfollows:

npminstall-gwebassembly

Bold:Indicatesanewterm,animportantword,orwordsthatyouseeonscreen.Forexample,wordsinmenusordialogboxesappearinthetextlikethis.Hereisanexample:"YoucandothisbypressingtheStartmenubutton,andright-clickingontheCommandPromptapplicationandselectingRunasadministrator."

Warningsorimportantnotesappearlikethis.

Tipsandtricksappearlikethis.

GetintouchFeedbackfromourreadersisalwayswelcome.

Generalfeedback:Emailfeedback@packtpub.comandmentionthebooktitleinthesubjectofyourmessage.Ifyouhavequestionsaboutanyaspectofthisbook,[email protected].

Errata:Althoughwehavetakeneverycaretoensuretheaccuracyofourcontent,mistakesdohappen.Ifyouhavefoundamistakeinthisbook,wewouldbegratefulifyouwouldreportthistous.Pleasevisitwww.packtpub.com/submit-errata,selectingyourbook,clickingontheErrataSubmissionFormlink,andenteringthedetails.

Piracy:IfyoucomeacrossanyillegalcopiesofourworksinanyformontheInternet,wewouldbegratefulifyouwouldprovideuswiththelocationaddressorwebsitename.Pleasecontactusatcopyright@packtpub.comwithalinktothematerial.

Ifyouareinterestedinbecominganauthor:Ifthereisatopicthatyouhaveexpertiseinandyouareinterestedineitherwritingorcontributingtoabook,pleasevisitauthors.packtpub.com.

ReviewsPleaseleaveareview.Onceyouhavereadandusedthisbook,whynotleaveareviewonthesitethatyoupurchaseditfrom?Potentialreaderscanthenseeanduseyourunbiasedopiniontomakepurchasedecisions,weatPacktcanunderstandwhatyouthinkaboutourproducts,andourauthorscanseeyourfeedbackontheirbook.Thankyou!

FormoreinformationaboutPackt,pleasevisitpacktpub.com.

WhatisWebAssembly?WebAssembly(Wasm)representsanimportantsteppingstoneforthewebplatform.Enablingadevelopertoruncompiledcodeonthewebwithoutapluginorbrowserlock-inpresentsmanynewopportunities.SomeconfusionexistsaboutwhatWebAssemblyis,asdoessomeskepticismaboutitsstayingpower.

Inthischapter,wewilldiscusshowWebAssemblycametobe,whatWebAssemblyiswithregardtotheofficialdefinition,andthetechnologiesitencompasses.Thepotentialusecases,supportedlanguages,andlimitationswillbecovered,aswellaswheretofindadditionalinformation.

Ourgoalforthischapteristounderstandthefollowing:

ThetechnologiesthatledthewayforWebAssemblyWhatWebAssemblyisandsomeofitspotentialusecasesWhichprogramminglanguagescanbeusedwithWebAssemblyThecurrentlimitationsofWebAssemblyHowWebAssemblyrelatestoEmscriptenandasm.js

TheroadtoWebAssemblyWebdevelopmenthashadaninterestinghistory,tosaytheleast.Several(failed)attemptshavebeenmadetoexpandtheplatformtosupportdifferentlanguages.Clunkysolutionssuchaspluginsfailedtostandthetestoftime,andlimitingausertoasinglebrowserisarecipefordisaster.

WebAssemblywasdevelopedasanelegantsolutiontoaproblemthathasexistedsincebrowserswerefirstabletoexecutecode:Ifyouwanttodevelopfortheweb,youhavetouseJavaScript.Fortunately,usingJavaScriptdoesn'thavethesamenegativeconnotationsithadbackintheearly2000s,butitcontinuestohavecertainlimitationsasaprogramminglanguage.Inthissection,we'regoingtodiscussthetechnologiesthatledtoWebAssemblytogetabettergraspofwhythisnewtechnologyisneeded.

TheevolutionofJavaScriptJavaScriptwascreatedbyBrendanEichinjust10daysbackin1995.Originallyseenasatoylanguagebyprogrammers,itwasusedprimarilytomakebuttonsflashorbannersappearonawebpage.ThelastdecadehasseenJavaScriptevolvefromatoytoaplatformwithprofoundcapabilitiesandamassivefollowing.

In2008heavycompetitioninthebrowsermarketresultedintheadditionofjust-in-time(JIT)compilers,whichincreasedtheexecutionspeedofJavaScriptbyafactorof10.Node.jsdebutedin2009andrepresentedaparadigmshiftinwebdevelopment.RyanDahlcombinedGoogle'sV8JavaScriptengine,aneventloop,andalow-levelI/OAPItobuildaplatformthatallowedfortheuseofJavaScriptacrosstheserverandclientside.Node.jsledtonpm,apackagemanagerthatallowedforthedevelopmentoflibrariestobeusedwithintheNode.jsecosystem.Asofthetimeofwriting,thereareover600,000packagesavailablewithhundredsbeingaddedeveryday:

Packagecountgrowthonnpmsince2012,takenfromModulecounts

It'snotjusttheNode.jsecosystemthatisgrowing;JavaScriptitselfisbeingactivelydeveloped.TheECMATechnicalCommittee39(TC39),whichdictatesthestandardsforJavaScriptandoverseestheadditionofnewlanguagefeatures,releasesyearlyupdatestoJavaScriptwithacommunity-drivenproposalprocess.Betweenitswealthoflibrariesandtooling,constantimprovementstothelanguage,andpossessingoneofthelargestcommunitiesofprogrammers,JavaScripthasbecomeaforcetobereckonedwith.

Butthelanguagedoeshavesomeshortcomings:

Upuntilrecently,JavaScriptonlyincluded64-bitfloatingpointnumbers.Thiscancauseissueswithverylargeorverysmallnumbers.BigInt,anewnumericprimitivethatcanalleviatesomeoftheseissues,isinthetheprocessofbeingaddedtotheECMAScriptspecification,butitmaytakesometimeuntilit'sfullysupportedinbrowsers.JavaScriptisweaklytyped,whichaddstoitsflexibility,butcancauseconfusionandbugs.Itessentiallygivesyouenoughropetohangyourself.JavaScriptisn'tasperformantascompiledlanguagesdespitethebesteffortsofthebrowservendors.Ifadeveloperwantstocreateawebapplication,theyneedtolearnJavaScript—whethertheylikeitornot.

ToavoidhavingtowritemorethanafewlinesofJavaScript,somedevelopersbuilttranspilerstoconvertotherlanguagestoJavaScript.Transpilers(orsource-to-sourcecompilers)aretypesofcompilersthatconvertsourcecodeinoneprogramminglanguagetoequivalentsourcecodeinanotherprogramminglanguage.TypeScript,whichisapopulartoolforfrontendJavaScriptdevelopment,transpilesTypeScripttovalidJavaScripttargetedforbrowsersorNode.js.Pickanyprogramminglanguageandthere'sagoodchancethatsomeonecreatedaJavaScripttranspilerforit.Forexample,ifyouprefertowritePython,youhaveabout15differenttoolsthatyoucanusetogenerateJavaScript.Intheend,though,it'sstillJavaScript,soyou'restillsubjecttotheidiosyncrasiesofthelanguage.

Asthewebevolvedintoavalidplatformforbuildinganddistributingapplications,moreandmorecomplexandresource-intensiveapplicationswerecreated.Inordertomeetthedemandsoftheseapplications,browservendorsbeganworkingonnewtechnologiestointegrateintotheirsoftwarewithout

disruptingthenormalcourseofwebdevelopment.GoogleandMozilla,creatorsofChromeandFirefox,respectively,tooktwodifferentpathstoachievethisgoal,culminatinginthecreationofWebAssembly.

GoogleandNativeClientGoogledevelopedNativeClient(NaCl)withtheintenttosafelyrunnativecodewithinawebbrowser.Theexecutablecodewouldruninasandboxandofferedtheperformanceadvantagesofnativecodeexecution.

Inthecontextofsoftwaredevelopment,asandboxisanenvironmentthatpreventsexecutablecodefrominteractingwithotherpartsofyoursystem.Itisintendedtopreventthespreadofmaliciouscodeandplacerestrictionsonwhatsoftwarecando.

NaClwastiedtoaspecificarchitecture,whilePortableNativeClient(PNaCl)wasanarchitecture-independentversionofNaCldevelopedtorunonanyplatform.Thetechnologyconsistedoftwoelements:

ToolchainswhichcouldtransformC/C++codetoNaClmodulesRuntimecomponentswhichwerecomponentsembeddedinthebrowserthatallowedexecutionofNaClmodules:

TheNativeClienttoolchainsandtheiroutputs

NaCl'sarchitecture-specificexecutable(nexe)waslimitedtoapplicationsandextensionsthatwereinstalledfromGoogle'sChromeWebStore,butPNaCl

executables(pexe)canbefreelydistributedonthewebandembeddedinwebapplications.PortabilitywasmadepossiblewithPepper,anopensourceAPIforcreatingNaClmodules,anditscorrespondingpluginAPI(PPAPI).PepperenabledcommunicationbetweenNaClmodulesandthehostingbrowser,andallowedforaccesstosystem-levelfunctionsinasafeandportableway.Applicationscouldbeeasilydistributedbyincludingamanifestfileandacompiledmodule(pexe)withthecorrespondingHTML,CSS,andJavaScript:

Pepper'sroleinaNativeClientapplication

NaClofferedpromisingopportunitiestoovercometheperformancelimitationsoftheweb,butithadsomedrawbacks.AlthoughChromehadbuilt-insupportforPNaClexecutablesandPepper,othermajorbrowserdidnot.Detractorsofthetechnologytookissuewiththeblack-boxnatureoftheapplicationsaswellasthepotentialsecurityrisksandcomplexity.

MozillafocuseditseffortsonimprovingtheperformanceofJavaScriptwithasm.js.Theywouldn'taddsupportforPeppertoFirefoxduetotheincompletenessofitsAPIspecificationandlimiteddocumentation.Intheend,NaClwasdeprecatedinMay,2017,infavorofWebAssembly.

Mozillaandasm.jsMozilladebutedasm.jsin2013andprovidedawayfordeveloperstotranslatetheirCandC++sourcecodetoJavaScript.Theofficialspecificationforasm.jsdefinesitasastrictsubsetofJavaScriptthatcanbeusedasalow-level,efficienttargetlanguageforcompilers.It'sstillvalidJavaScript,butthelanguagefeaturesarelimitedtothosethatareamenabletoahead-of-time(AOT)optimization.AOTisatechniquethatthebrowser'sJavaScriptengineusestoexecutecodemoreefficientlybycompilingitdowntonativemachinecode.asm.jsachievestheseperformancegainsbyhaving100%typeconsistencyandmanualmemorymanagement.

UsingatoolsuchasEmscripten,C/C++codecanbetranspileddowntoasm.jsandeasilydistributedusingthesamemeansasnormalJavaScript.Accessingthefunctionsinanasm.jsmodulerequireslinking,whichinvolvescallingitsfunctiontoobtainanobjectwiththemodule'sexports.

asm.jsisincrediblyflexible,however,certaininteractionswiththemodulecancausealossofperformance.Forexample,ifanasm.jsmoduleisgivenaccesstoacustomJavaScriptfunctionthatfailsdynamicorstaticvalidation,thecodecan'ttakeadvantageofAOTandfallsbacktotheinterpreter:

Theasm.jsAOTcompilationworkflow

asm.jsisn'tjustasteppingstone.ItformsthebasisforWebAssembly'sMinimumViableProduct(MVP).TheofficialWebAssemblysiteexplicitlymentionsasm.jsinthesectionentitledWebAssemblyHigh-LevelGoals.

SowhycreateWebAssemblywhenyoucoulduseasm.js?Asidefromthepotentialperformanceloss,anasm.jsmoduleisatextfilethatmustbetransferredoverthenetworkbeforeanycompilationcantakeplace.AWebAssemblymoduleisinabinaryformat,whichmakesitmuchmoreefficienttotransferduetoitssmallersize.

WebAssemblymodulesuseapromise-basedapproachtoinstantiation,whichtakesadvantageofmodernJavaScriptandeliminatestheneedforanyisthisloadedyet?code.

WebAssemblyisbornTheWorldWideWebConsortium(W3C),aninternationalcommunitybuilttodevelopwebstandards,formedtheWebAssemblyWorkingGroupinApril,2015,tostandardizeWebAssemblyandoverseethespecificationandproposalprocess.Sincethen,theCoreSpecificationandcorrespondingJavaScriptAPIandWebAPIhavebeenreleased.TheinitialimplementationofWebAssemblysupportinbrowserswasbasedonthefeaturesetofasm.js.WebAssembly'sbinaryformatandcorresponding.wasmfilecombinedfacetsofasm.jsoutputwithPNaCl'sconceptofadistributedexecutable.

SohowwillWebAssemblysucceedwhereNaClfailed?AccordingtoDr.AxelRauschmayer,therearethreereasonsdetailedathttp://2ality.com/2015/06/web-assembly.html#what-is-different-this-time:

"First,thisisacollaborativeeffort,nosinglecompanygoesitalone.Atthemoment,thefollowingprojectsareinvolved:Firefox,Chromium,EdgeandWebKit.

Second,theinteroperabilitywiththewebplatformandJavaScriptisexcellent.UsingWebAssemblycodefromJavaScriptwillbeassimpleasimportingamodule.

Third,thisisnotaboutreplacingJavaScriptengines,itismoreaboutaddinganewfeaturetothem.ThatgreatlyreducestheamountofworktoimplementWebAssemblyandshouldhelpwithgettingthesupportofthewebdevelopmentcommunity."

-Dr.AxelRauschmayer

WhatexactlyisWebAssemblyandwherecanIuseit?WebAssemblyhasasuccinctanddescriptivedefinitionontheofficialsite,butit'sonlyapieceofthepuzzle.ThereareseveralothercomponentsthatfallundertheumbrellaofWebAssembly.Understandingtheroleeachcomponentplayswillgiveyouabetterunderstandingofthetechnologyasawhole.Inthissection,wewillprovideadetailedbreakdownofWebAssembly'sdefinitionanddescribepotentialusecases.

OfficialdefinitionTheofficialWebAssemblywebsite(https://webassembly.org)offersthisdefinition:Wasmisabinaryinstructionformatforastack-basedvirtualmachine.Wasmisdesignedasaportabletargetforcompilationofhigh-levellanguageslikeC/C++/Rust,enablingdeploymentonthewebforclientandserverapplications.

Let'sbreakthatdefinitiondownintopartstoaddsomeclarification.

BinaryinstructionformatWebAssemblyactuallyencompassesseveralelements—abinaryformatandtextformat,whicharedocumentedintheCoreSpecification,thecorrespondingAPIs(JavaScriptandweb),andacompilationtarget.Thebinaryandtextformatbothmaptoacommonstructureintheformofanabstractsyntax.Tobetterunderstandabstractsyntax,itcanbeexplainedinthecontextofanabstractsyntaxtree(AST).AnASTisatreerepresentationofthestructureofsourcecodeforaprogramminglanguage.ToolssuchasESLintuseJavaScript'sASTtofindlintingerrors.ThefollowingexamplecontainsafunctionandthecorrespondingASTforJavaScript(takenfromhttps://astexplorer.net).

AsimpleJavaScriptfunctionfollows:

functiondoStuff(thingToDo){

console.log(thingToDo);

}

ThecorrespondingASTisasfollows:

{

"type":"Program",

"start":0,

"end":57,

"body":[

{

"type":"FunctionDeclaration",

"start":9,

"end":16,

"id":{

"type":"Identifier",

"start":17,

"end":26,

"name":"doStuff"

},

"generator":false,

"expression":false,

"params":[

{

"type":"Identifier",

"start":28,

"end":57,

"name":"thingToDo"

}

],

"body":{

"type":"BlockStatement",

"start":32,

"end":55,

"body":[

{

"type":"ExpressionStatement",

"start":32,

"end":55,

"expression":{

"type":"CallExpression",

"start":32,

"end":54,

"callee":{

"type":"MemberExpression",

"start":32,

"end":43,

"object":{

"type":"Identifier",

"start":32,

"end":39,

"name":"console"

},

"property":{

"type":"Identifier",

"start":40,

"end":43,

"name":"log"

},

"computed":false

},

"arguments":[

{

"type":"Identifier",

"start":44,

"end":53,

"name":"thingToDo"

}

]

}

}

]

}

}

],

"sourceType":"module"

}

AnASTmaybeverbose,butitdoesanexcellentjobatdescribingthecomponentsofaprogram.RepresentingsourcecodeinanASTmakesverificationandcompilationsimpleandefficient.WebAssemblycodeintextformatisserializedintoanASTandcompiledtothebinaryformat(asa.wasmfile),whichisfetched,loaded,andutilizedbyawebpage.Whenthemoduleisloaded,thebrowser'sJavaScriptengineutilizesadecodingstacktodecodethe.wasmfileintoanAST,performtypechecking,andinterpretittoexecutefunctions.WebAssemblystartedasabinaryinstructionformatforanAST.DuetotheperformanceimplicationsofverifyingWasmexpressionsthatreturnvoid,thebinaryinstructionformatwasupdatedtotargetastackmachine.

Astackmachineconsistsoftwoelements:astackandinstructions.Astackisadatastructurewithtwooperations:pushandpop.Itemsarepushedontothestackandsubsequentlypoppedfromthestackinlastin,firstout(LIFO)order.Astackalsoincludesapointer,whichpointstotheitematthetopofthestack.Instructionsrepresentactionstoperformontheitemsinthestack.Forexample,anADDinstructionmightpopthetoptwoitemsfromthestack(thevalues100and10),andpushasingleitemwiththesumbackontothestack(thevalue110):

Asimplestackmachine

WebAssembly'sstackmachineoperatesinthesameway.Aprogramcounter(pointer)maintainstheexecutionpositionwithinthecodeandavirtualcontrolstackkeepstrackofblocksandifconstructsastheyareentered(pushed)andexited(popped).TheinstructionsareexecutedwithnoreferencetoanAST.Thus,thebinaryinstructionformatportionofthedefinitionreferstoabinaryrepresentationofinstructionsthatareinaformatreadablebythedecodingstackinthebrowser.

PortabletargetforcompilationWebAssemblywasdesignedfromthebeginningwithportabilityinmind.PortabilityinthiscontextmeansthatWebAssembly'sbinaryformatcanbeexecutedefficientlyonavarietyofoperatingsystemsandinstructionsetarchitectures,onandofftheweb.ThespecificationforWebAssemblydefinesportabilityinthecontextofanexecutionenvironment.WebAssemblywasdesignedtorunefficientlyinenvironmentsthatmeetcertaincharacteristics,mostofwhicharerelatedtomemory.WebAssembly'sportabilitycanalsobeattributedtotheabsenceofaspecificAPIaroundthecoretechnologies.Instead,itdefinesanimportmechanismwherethesetofavailableimportsisdefinedbythehostenvironment.

Inanutshell,thismeansthatWebAssemblyisn'ttiedtoaspecificenvironment,suchasthewebordesktop.TheWebAssemblyWorkingGrouphasdefinedaWebAPI,butthat'sseparatefromtheCoreSpecification.TheWebAPIcaterstoWebAssembly,nottheotherwayaround.

ThecompilationaspectofthedefinitionindicatesthatWebAssemblywillbesimpletocompiledowntoitsbinaryformatfromsourcecodewritteninhigh-levellanguages.TheMVPfocusesontwolanguages,CandC++,butRustcanalsobeusedgivenitssimilaritiestoC++.CompilationwillbeachievedthroughtheuseofaClang/LLVMbackend,althoughwe'llbeusingEmscripteninthisbooktogenerateourWasmmodules.Theplanistoeventuallyaddsupportforotherlanguagesandcompilers(suchasGCC),buttheMVPisfocusedonLLVM.

ThecorespecificationTheofficialdefinitiongivessomehigh-levelinsightintotheoveralltechnology,butforthesakeofcompleteness,it'sworthdiggingalittledeeper.WebAssembly'sCoreSpecificationistheofficialdocumenttoreferenceifyouwanttounderstandWebAssemblyataverygranularlevel.Ifyou'reinterestedinlearningaboutthecharacteristicsoftheruntimestructurewithregardtotheexecutionenvironment,checkoutsection4:Execution.Wewon'tcoverthathere,butunderstandingwheretheCoreSpecificationfitsinwillhelpinestablishingacompletedefinitionofWebAssembly.

LanguageconceptsTheCoreSpecificationstatesWebAssemblyencodesalow-level,assembly-likeprogramminglanguage.Thespecificationdefinesthestructure,execution,andvalidationofthislanguageaswellasthedetailsofthebinaryandtextformats.Thelanguageitselfisstructuredaroundthefollowingconcepts:

Values,orrathervaluetypesthatWebAssemblyprovidesInstructionsthatareexecutedwithinthestackmachineTrapsproducedundererrorconditionsandabortexecutionFunctionsintowhichcodeisorganized,eachofwhichtakesasequenceofvaluesasparametersandreturnsasequenceofvaluesasaresultTables,whicharearraysofvaluesofaparticularelementtype(suchasfunctionreferences)thatareselectablebytheexecutingprogramLinearMemory,whichisanarrayofrawbytesthatcanbeusedtostoreandloadvaluesModules,WebAssemblybinary(.wasmfile)thatcontainsfunction,tables,andlinearmemoriesEmbedder,themechanismbywhichWebAssemblycanbeexecutedinahostenvironment,suchasawebbrowser

Functions,tables,memory,andmoduleshavedirectcorrelationswiththeJavaScriptAPIandareimportanttobeawareof.TheseconceptsdescribetheunderlyingstructureofthelanguageitselfandhowtowriteorencodeWebAssembly.Withregardtousage,understandingthecorrespondingsemanticphasesofWebAssemblyprovidesacompletedefinitionofthetechnology:

Languageconceptsandtheirrelationship

SemanticphasesTheCoreSpecificationdescribesthedifferentphasesanencodedmodule(.wasmfile)undergoeswhenitisbeingutilizedinahostenvironment(suchasawebbrowser).Thisaspectofthespecificationrepresentshowtheoutputishandledandexecuted:

Decoding:ThebinaryformatisconvertedintoamoduleValidation:Thedecodedmoduleundergoesvalidationchecks(suchastypechecking)toensurethemoduleiswellformedandsafeExecution,Part1:Instantiation:Amoduleinstance,whichisthedynamicrepresentationofthemodule,isinstantiatedbyinitializingtheGlobals,Memories,andTables,andinvokesthemodule'sstart()functionExecution,Part2:Invocation:Exportedfunctionsarecalledfromthemoduleinstance:

Thefollowingdiagramprovidesavisualrepresentationofthesemanticphases:

Semanticphasesofmoduleuse

TheJavaScriptandWebAPIsTheWebAssemblyWorkingGroupalsoreleasedAPIspecificationsforinteractingwithJavaScriptandtheweb,whichqualifiesthemforinclusionintheWebAssemblytechnologyspace.TheJavaScriptAPIisscopedtotheJavaScriptlanguageitself,withoutbeingspecificallytiedtoanenvironment(forexample,webbrowsersorNode.js).Itdefinesclasses,methods,andobjectsforinteractingwithWebAssemblyandmanagingthecompilationandinstantiationprocesses.TheWebAPIisanextensionoftheJavaScriptAPIthatdefinesfunctionalityspecifictowebbrowsers.TheWebAPIspecificationcurrentlyonlydefinestwomethods,compileStreamingandinstantiateStreaming,whichareconveniencemethodsthatsimplifytheuseofWasmmodulesinthebrowser.ThesewillbecoveredingreaterdetailinChapter2,ElementsofWebAssembly-Wat,Wasm,andtheJavaScriptAPI.

SowillitreplaceJavaScript?WebAssembly'sultimategoalisnottoreplaceJavaScript,butrathertocomplementit.JavaScript'srichecosystemandflexibilitystillmakesittheideallanguagefortheweb.WebAssembly'sJavaScriptAPImakesinteroperabilitybetweenthetwotechnologiesrelativelysimple.SowillyoubeabletobuildawebapplicationusingjustWebAssembly?OneoftheexplicitgoalsofWebAssemblyisportability,andreplicatingallofJavaScript'sfunctionalitycouldinhibitthatgoal.However,theofficialsiteincludesagoaltoexecuteandintegratewellwiththeexistingwebplatform,soonlytimewilltell.ItmaynotbepracticaltowritetheentirecodebaseinalanguagethatcompilesdowntoWebAssembly,butmovingsomeoftheapplicationlogictoWasmmodulescouldbebeneficialintermsofperformanceandloadtimes.

WherecanIuseit?WebAssembly'sofficialsitehasanextensivelistofpotentialusecases.I'mnotgoingtocoverthemallhere,butthereareseveralthatrepresentsignificantenhancementstothecapabilitiesofthewebplatform:

Image/videoeditingGamesMusicapplications(streaming,caching)ImagerecognitionLivevideoaugmentationVRandaugmentedreality

AlthoughsomeoftheseusecasesaretechnicallyfeasiblewithJavaScript,HTML,andCSS,usingWebAssemblycanoffersignificantperformancegains.Servingupabinaryfile(insteadofasingleJavaScriptfile)cangreatlyreducethebundlesize,andinstantiatingtheWasmmoduleonpageloadspeedsupcodeexecution.

WebAssemblyisn'tjustlimitedtothebrowser.Outsidethebrowser,youcoulduseittobuildhybridnativeappsonmobiledevicesorperformserver-sidecomputationsofuntrustedcode.UsingWasmmodulesforphoneappscouldbeincrediblybeneficialintermsofpowerusageandperformance.

WebAssemblyalsooffersflexibilitywithregardtohowitcanbeused.YoucanwriteyourentirecodebaseinWebAssembly,althoughthismaynotbepracticalinitscurrentformorinthecontextofawebapplication.GivenWebAssembly'srobustJavaScriptAPI,youcouldwritetheUIinJavaScript/HTMLanduseWasmmodulesforfunctionalitythatdoesn'tdirectlyaccesstheDOM.Onceadditionallanguagesaresupported,objectscanbeeasilypassedbetweentheWasmmoduleandJavaScriptcode,whichwillgreatlysimplifyintegrationandincreasedeveloperadoption.

Whatlanguagesaresupported?WebAssembly'shigh-levelgoalsfortheirMVPwastoprovideroughlythesamefunctionalityasasm.js.Thetwotechnologiesareverycloselyrelated.C,C++,andRustareverypopularlanguagesthatsupportmanualmemoryallocation,whichmadethemidealcandidatesfortheinitialimplementation.Inthissection,we'regoingtoprovideabriefoverviewofeachprogramminglanguage.

CandC++CandC++arelow-levelprogramminglanguagesthathavebeenaroundforover30years.Cisproceduralanddoesn'tinherentlysupportobject-orientedprogrammingconceptssuchasclassesandinheritance,butit'sfast,portable,andwidelyused.

C++wasbuilttofillthegapsinCbyaddingfeaturessuchasoperatoroverloadingandimprovedtypechecking.Bothlanguagesconsistentlyrankinthetop10mostpopularprogramminglanguages,whichmakethemideallysuitedfortheMVP:

TIOBEVeryLongTermHistoryofthetop10programminglanguagesCandC++supportisalsobakedintoEmscripten,soinadditiontosimplifyingthecompilationprocess,itallowsyoutotakeadvantageofWebAssembly'sfullcapabilities.ItisalsopossibletocompileC/C++codedowntoa.wasmfileusingLLVM.LLVMisacollectionofmodularandreusablecompilerandtoolchain

technologies.Inanutshell,it'saframeworkthatsimplifiestheconfigurationofacompilationprocessfromsourcecodetomachinecode.Ifyoumadeyourownprogramminglanguageandwouldliketobuildacompiler,LLVMhastoolstosimplifytheprocess.I'll

coverhowtocompileC/C++into.wasmfilesusingLLVMinChapter10,AdvancedToolsandUpcomingFeatures.

ThefollowingsnippetdemonstrateshowtoprintHelloWorld!totheconsoleusingC++:#include<iostream>

intmain(){std::cout<<"Hello,World!\n";return0;}

fnmain(){<br/>println!("HelloWorld!");<br/>}

OtherlanguagesVarioustoolingexiststoenabletheuseofWebAssemblywithsomeoftheotherpopularprogramminglanguages,althoughtheyaremostlyexperimental:

C#viaBlazorHaxeviaWebIDLJavaviaTeaVMorBytecoderKotlinviaTeaVMTypeScriptviaAssemblyScript

ItisalsotechnicallypossibletotranspilealanguagetoCandconsequentlycompilethattoaWasmmodule,butthesuccessofcompilationiscontingentontheoutputofthetranspiler.Morethanlikely,you'dhavetomakesignificantchangestothecodetogetittowork.

Whatarethelimitations?Admittedly,WebAssemblyisnotwithoutitslimitations.Newfeaturesarebeingactivelydevelopedandthetechnologyisconstantlyevolving,buttheMVPfunctionalityrepresentsonlyaportionofWebAssembly'scapabilities.Inthissection,we'llcoversomeoftheselimitationsandhowtheyimpactthedevelopmentprocess.

NogarbagecollectionWebAssemblysupportsaflatlinearmemory,whichisn'talimitationperse,butrequiressomeunderstandingofhowtoexplicitlyallocatememorytoexecutecode.CandC++werelogicalchoicesfortheMVPbecausememorymanagementisbuiltintothelanguage.Thereasonwhysomeofthemorepopularhigh-levellanguagessuchasJavaweren'tincludedinitiallyisduetosomethingcalledgarbagecollection(GC).

GCisaformofautomatedmemorymanagementwhereinmemoryoccupiedbyobjectsthatarenolongerinusebytheprogramisreclaimedautomatically.GCisanalogoustoanautomatictransmissiononacar.Ithasbeenheavilyoptimizedbyskilledengineerstooperateasefficientlyaspossible,butlimitstheamountofcontrolthedriverhas.Manuallyallocatingmemoryislikedrivingacarwithamanualtransmission.Itaffordsgreatercontroloverspeedandtorque,butmisuseorlackofexperiencecanleaveyoustrandedwithaseverelydamagedcar.PartofCandC++'sexcellentperformanceandspeedcanbeattributedtothemanualallocationofmemory.

GClanguagesallowyoutoprogramwithouthavingtoworryaboutmemoryavailabilityorallocation.JavaScriptisanexampleofaGClanguage.Thebrowserengineemployssomethingcalledamark-and-sweepalgorithmtocollectunreachableobjectsandfreeupthecorrespondingmemory.SupportforGClanguagesiscurrentlybeingworkedoninWebAssembly,butit'shardtosayexactlywhenitwillbecompleted.

NodirectDOMaccessWebAssemblyisunabletoaccesstheDOM,soanyDOMmanipulationneedstobedoneindirectlythroughJavaScriptorusingatoolsuchasEmscripten.ThereareplanstoaddtheabilitytoreferenceDOMandotherWebAPIobjectsdirectly,butthat'sstillintheproposalphase.DOMmanipulationwilllikelygohandinhandwithGClanguages,sinceitwillallowtheseamlesspassingofobjectsbetweenWebAssemblyandJavaScriptcode.

NosupportinolderbrowsersOlderbrowsersdon'thavetheglobalWebAssemblyobjectavailabletoinstantiateandloadWasmmodules.Thereareexperimentalpolyfillsthatutilizeasm.jsiftheobjectisn'tfound,buttheWebAssemblyWorkingGroupcurrentlyhasnoplanstocreateone.Sinceasm.jsandWebAssemblyarecloselyrelated,simplyservingupanasm.jsfileiftheWebAssemblyobjectisunavailablewillstillofferperformancegainswhileaccommodatingforbackwardcompatibility.YoucanseewhichbrowserscurrentlysupportWebAssemblyathttps://caniuse.com/#feat=wasm.

HowdoesitrelatetoEmscripten?Emscriptenisthesource-to-sourcecompilerthatcangenerateasm.jsfromCandC++sourcecode.We'lluseitasabuildtooltogeneratetheWasmmodules.Inthissection,we'llquicklyreviewhowEmscriptenrelatestoWebAssembly.

Emscripten'sroleEmscriptenisanLLVM-to-JavaScriptcompiler,whichmeansittakesLLVMbitcodeoutputofacompilersuchasClang(forCandC++),andconvertsthattoJavaScript.Itisn'tonespecifictechnology,butratheracombinationoftechnologiesthatworktogethertobuild,compile,andrunasm.js.TogenerateWasmmodules,we'llusetheEmscriptenSDK(EMSDK)Manager:

WasmmodulegenerationwiththeEMSDK

TheEMSDKandBinaryenInChapter4,InstallingtheRequiredDependencies,we'llinstalltheEMSDKanduseittomanagethedependenciesrequiredtocompileCandC++toWasmmodules.EmscriptenusesBinaryen'sasm2wasmtooltocompiletheasm.jsoutputbyEmscriptentoa.wasmfile.BinaryenisacompilerandtoolchaininfrastructurelibrarythatincludestoolstocompilevariousformatstoWebAssemblymodulesandviceversa.UnderstandingtheinnerworkingsofBinaryenisn'trequiredtouseWebAssembly,butitisimportanttobeawareoftheunderlyingtechnologiesandhowtheyworktogether.BypassingcertainflagsintothecompilecommandforEmscripten(emcc),wecanpipetheresultantasm.jscodetoBinaryentooutputour.wasmfile.

SummaryInthischapter,wediscussedthehistoryofWebAssemblywithregardtothetechnologiesthatledtoitscreation.AdetailedoverviewofthedefinitionofWebAssemblywasprovidedtoallowforagreaterunderstandingoftheunderlyingtechnologiesinvolved.

TheCoreSpecification,JavaScriptAPI,andWebAPIwerepresentedasimportantelementsofWebAssemblyanddemonstratehowthetechnologywillevolve.Wealsoreviewedpotentialsusecases,currentlysupportedlanguages,andtoolsthatenabletheuseofnon-supportedlanguages.

ThelimitationsofWebAssemblyaretheabsenceofGC,theinabilitytocommunicatedirectlywiththeDOM,andthelackofsupportforolderbrowsers.Thesewerediscussedtoconveythenewnessofthetechnologyandshedlightonsomeofitsshortcomings.Finally,wediscussedEmscripten'sroleinthedevelopmentprocessandwhereitfitsintotheWebAssemblydevelopmentworkflow.

InChapter2,ElementsofWebAssembly-Wat,Wasm,andtheJavaScriptAPI,we'llbedivingdeeperintotheelementsthatmakeupWebAssembly:theWebAssemblytextformat(Wat),binaryformat(Wasm),JavaScript,andWebAPIs.

Questions1. WhichtwotechnologiesinfluencedthecreationofWebAssembly?2. WhatisastackmachineandhowdoesitrelatetoWebAssembly?3. InwhatwaysdoesWebAssemblycomplementJavaScript?4. WhichthreeprogramminglanguagescanbecompiledtoWasmmodules?5. WhatroledoesLLVMplaywithregardtoWebAssembly?6. WhatarethreepotentialusecasesforWebAssembly?7. HowareDOMaccessandGCrelated?8. WhattooldoesEmscriptenusetogenerateWasmmodules?

FurtherreadingOfficialWebAssemblysite:https://webassembly.orgNativeClienttechnicaloverview:https://developer.chrome.com/native-client/overview

TheLLVMCompilerInfrastructureProject:https://llvm.orgAboutEmscripten:http://kripken.github.io/emscripten-site/docs/introducing_emscripten/about_emscripten.html

asm.jsspecification:http://asmjs.org/spec/latest

ElementsofWebAssembly-Wat,Wasm,andtheJavaScriptAPIChapter1,WhatisWebAssembly?,describedthehistoryofWebAssemblyandprovidedahigh-leveloverviewofthetechnologyaswellasthepotentialusecasesandlimitations.WebAssemblywasdescribedasbeingcomposedofmultipleelements,notjustthebinaryinstructionformatspecifiedintheofficialdefinition.

Inthischapter,wewilldigintotheelementsthatcorrespondtotheofficialspecificationscreatedbytheWebAssemblyWorkingGroup.WewillexaminetheWatandthebinaryformatingreaterdetailtogainabetterunderstandingofhowtheyrelatetomodules.WewillreviewtheJavaScriptAPIandWebAPItoensureyou'reabletoutilizetheWebAssemblyeffectivelyinthebrowser.

Ourgoalforthischapteristounderstandthefollowing:

HowthetextandbinaryformatsarerelatedWhatWatisandwhereitfitsintothedevelopmentprocessThebinaryformatandmodule(Wasm)fileThecomponentsoftheJavaScriptandWebAPIandhowtheyrelatetotheWasmmoduleHowtoutilizeWasmFiddletoevaluatethephasesofWebAssembly(C/C++>Wat>Wasm)

CommonstructureandabstractsyntaxInChapter1,WhatisWebAssembly?,wetalkedabouthowthebinaryandtextformatsofWebAssemblybothmaptoacommonstructureintheformofanabstractsyntax.Beforegettingintothenutsandboltsoftheseformats,it'sworthmentioninghowthesearerelatedwithintheCoreSpecification.Thefollowingdiagramisavisualrepresentationofthetableofcontents(withsomesectionsexcludedforclarity):

CoreSpecificationtableofcontents

Asyoucansee,theTextFormatandBinaryFormatsectionscontainsubsectionsforValues,Types,Instructions,andModulesthatcorrelatewiththeStructuresection.Consequently,muchofwhatwecoverinthenextsectionforthetextformathavedirectcorollarieswiththebinaryformat.Withthatinmind,let'sdiveintothetextformat.

WatTheTextFormatsectionoftheCoreSpecificationprovidestechnicaldescriptionsforcommonlanguageconceptssuchasvalues,types,andinstructions.Theseareimportantconceptstoknowandunderstandifyou'replanningonbuildingtoolingforWebAssembly,butnotnecessaryifyoujustplanonusingitinyourapplications.Thatbeingsaid,thetextformatisanimportantpartofWebAssembly,sothereareconceptsyoushouldbeawareof.Inthissection,wewilldigintosomeofthedetailsofthetextformatandhighlightimportantpointsfromtheCoreSpecification.

DefinitionsandS-expressionsTounderstandWat,let'sstartwiththefirstsentenceofthedescriptiontakendirectlyfromtheWebAssemblyCoreSpecification:

"ThetextualformatforWebAssemblymodulesisarenderingoftheirabstractsyntaxintoS-expressions."

Sowhataresymbolicexpressions(S-expressions)?S-expressionsarenotationsfornestedlist(tree-structured)data.Essentially,theyprovideasimpleandelegantwaytorepresentlist-baseddataintextualform.Tounderstandhowtextualrepresentationsofnestedlistsmaptoatreestructure,let'sextrapolatethetreestructurefromanHTMLpage.ThefollowingexamplecontainsasimpleHTMLpageandthecorrespondingtreestructurediagram.

AsimpleHTMLpage:

<html>

<head>

<linkrel="icon"href="favicon.ico">

<title>PageTitle</title>

</head>

<body>

<div>

<h1>Header</h1>

<p>Thisisaparagraph.</p>

</div>

<div>Somecontent</div>

<nav>

<ul>

<li>Item1</li>

<li>Item2</li>

<li>Item3</li>

</ul>

</nav>

</body>

</html>

Thecorrespondingtreestructureis:

AtreestructurediagramforanHTMLpage

Evenifyou'veneverseenatreestructurebefore,it'sstillcleartoseehowtheHTMLmapstothetreeintermsofstructureandhierarchy.MappingHTMLelementsisrelativelysimplebecauseit'samarkuplanguagewithwell-definedtagsandnoactuallogic.

Watrepresentsmodulesthatcanhavemultiplefunctionswithvaryingparameters.Todemonstratetherelationshipbetweensourcecode,Wat,andthecorrespondingtreestructure,let'sstartwithasimpleCfunctionthatadds2tothenumberthatispassedinasaparameter:

HereisaCfunctionthatadds2tothenumargumentpassedinandreturnstheresult:

intaddTwo(intnum){

returnnum+2;

}

ConvertingtheaddTwofunctiontovalidWatproducesthisresult:

(module

(table0anyfunc)

(memory$01)

(export"memory"(memory$0))

(export"addTwo"(func$addTwo))

(func$addTwo(;0;)(param$0i32)(resulti32)

(i32.add

(get_local$0)

(i32.const2)

)

)

)

InChapter1,WhatisWebAssembly?,wetalkedaboutlanguageconceptsassociatedwiththeCoreSpecification(Functions,LinearMemory,Tables,andsoon).Withinthatspecification,theStructuresectiondefineseachoftheseconceptsinthecontextofanabstractsyntax.TheTextFormatsectionofthespecificationcorrespondswiththeseconceptsaswell,andyoucanseethemdefinedbytheirkeywordsintheprecedingsnippet(func,memory,table).

TreeStructure:

AtreestructurediagramforWat

Theentiretreewouldbetoolargetofitonapage,sothisdiagramislimitedto

thefirstfivelinesoftheWatsourcetext.Eachfilled-indotrepresentsalistnode(orthecontentsofasetofparentheses).Asyoucansee,codewrittenins-expressionscanbeclearlyandconciselyexpressedinatreestructure,whichiswhys-expressionswerechosenforWebAssembly'stextformat.

Values,types,andinstructionsAlthoughdetailedcoverageoftheTextFormatsectionoftheCoreSpecificationisoutofthescopeofthistext,it'sworthdemonstratinghowsomeofthelanguageconceptsmaptothecorrespondingWat.ThefollowingdiagramdemonstratesthesemappingsinasampleWatsnippet.TheCcodethatthiswascompiledfromrepresentsafunctionthattakesawordasaparameterandreturnsthesquarerootofthecharactercount:

Watexamplewithlanguageconceptdetails

IfyouintendonwritingoreditingWat,notethatitsupportsblockandlinecomments.Theinstructionsaresplitupintoblocksandconsistofsettingandgettingmemoryassociatedwithvariableswithvalidtypes.Youareabletocontroltheflowoflogicusingifstatementsandloopsaresupportedusingtheloopkeyword.

RoleinthedevelopmentprocessThetextformatallowsfortherepresentationofabinaryWasmmoduleintextualform.Thishassomeprofoundimplicationswithregardtotheeaseofdevelopmentanddebugging.HavingatextualrepresentationofaWebAssemblymoduleallowsdeveloperstoviewthesourceofaloadedmoduleinabrowser,whicheliminatestheblack-boxissuesthatinhibitedtheadoptionofNaCl.Italsoallowsfortoolingtobebuiltaroundtroubleshootingmodules.Theofficialwebsitedescribestheusecasesthatdrovethedesignofthetextformat:•ViewSourceonaWebAssemblymodule,thusfittingintotheWeb(whereeverysourcecanbeviewed)inanaturalway.

•Presentationinbrowserdevelopmenttoolswhensourcemapsaren'tpresent(whichisnecessarilythecasewiththeMinimumViableProduct(MVP)).

•WritingWebAssemblycodedirectlyforreasonsincludingpedagogical,experimental,debugging,optimization,andtestingofthespecitself.

Thelastiteminthelistreflectsthatthetextformatisn'tintendedtobewrittenbyhandinthecourseofnormaldevelopment,butrathergeneratedfromatoollikeEmscripten.Youprobablywon'tseeormanipulateany.watfileswhenyou'regeneratingmodules,butyoumaybeviewingtheminadebuggingcontext.

Notonlyisthetextformatvaluablewithregardstodebugging,buthavingthisintermediateformatreducestheamountofrelianceonasingletoolforcompilation.Severaldifferenttoolscurrentlyexisttoconsumeandemitthiss-expressionsyntax,someofwhichareusedbyEmscriptentocompileyourcodedowntoa.wasmfile.

Binaryformatandthemodulefile(Wasm)TheBinaryFormatsectionoftheCoreSpecificationprovidesthesamelevelofdetailwithregardtolanguageconceptsastheTextformatsection.Inthissection,wewillbrieflycoversomehigh-leveldetailsaboutthebinaryformatanddiscussthevarioussectionsthatmakeupaWasmmodule.

DefinitionandmoduleoverviewThebinaryformatisdefinedasadenselinearencodingoftheabstractsyntax.Withoutgettingtootechnical,thatessentiallymeansit'sanefficientformofbinarythatallowsforfastdecoding,smallfilesize,andreducedmemoryusage.Thefilerepresentationofthebinaryformatisa.wasmfile,whichwillbethecompilationoutputfromEmscriptenthatwe'lluseforourexamples.

TheValues,Types,andInstructionssubsectionsoftheCoreSpecificationforthebinaryformatcorrelatedirectlytotheTextFormatsection.Eachoftheseconceptsiscoveredinthecontextofencoding.Forexample,accordingtothespecification,theIntegertypesareencodedusingtheLEB128variable-lengthintegerencoding,ineitherunsignedorsignedvariant.TheseareimportantdetailstoknowifyouwishtodeveloptoolingforWebAssembly,butnotnecessaryifyoujustplanonusingitonyourwebsite.

TheStructure,BinaryFormat,andTextFormat(wat)sectionsoftheCoreSpecificationhaveaModulesubsection.Wedidn'tcoveraspectsofthemoduleintheprevioussectionbecauseit'smoreprudenttodescribetheminthecontextofabinary.TheofficialWebAssemblysiteoffersthefollowingdescriptionforamodule:

"Thedistributable,loadable,andexecutableunitofcodeinWebAssemblyiscalledamodule.Atruntime,amodulecanbeinstantiatedwithasetofimportvaluestoproduceaninstance,whichisanimmutabletuplereferencingallthestateaccessibletotherunningmodule."

WewilldiscusshowtointeractwiththemoduleusingtheJavaScriptandWebAPIslaterinthischapter,solet'sestablishsomecontexttounderstandhowthemoduleelementsmaptotheAPImethods.

ModulesectionsAmoduleismadeupofseveralsections,someofwhichyou'llbeinteractingwiththroughtheJavaScriptAPI:

Imports(import)areelementsthatcanbeaccessedwithinthemoduleandcanbeoneofthefollowing:

Function,whichcanbecalledinsidethemoduleusingthecalloperatorGlobal,whichcanbeaccessedinsidethemoduleviatheglobaloperatorsLinearMemory,whichcanbeaccessedinsidethemoduleviathememoryoperatorsTable,whichcanbeaccessedinsidethemoduleusingcall_indirect

Exports(export)areelementsthatcanbeaccessedbytheconsumingAPI(thatis,calledbyaJavaScriptfunction)Modulestartfunction(start)iscalledafterthemoduleinstanceisinitializedGlobal(global)containstheinternaldefinitionofglobalvariablesLinearmemory(memory)containstheinternaldefinitionoflinearmemorywithaninitialmemorysizeandoptionalmaximumsizeData(data)containsanarrayofdatasegmentswhichspecifytheinitialcontentsoffixedrangesofagivenmemoryTable(table)isalinearmemorywhoseelementsareopaquevaluesofaparticulartableelementtype:

IntheMVP,itsprimarypurposeistoimplementindirectfunctioncallsinC/C++

Elements(elements)isasectionthatallowsamoduletoinitializetheelementsofanyimportorinternallydefinedtablewithanyotherdefinitioninthemoduleFunctionandcode:

ThefunctionsectiondeclaresthesignaturesofeachinternalfunctiondefinedinthemoduleThecodesectioncontainsthefunctionbodyofeachfunctiondeclaredbythefunctionsection

Someofthekeywords(import,export,andsoon)shouldlookfamiliar;they'representinthecontentsoftheWatfileintheprevioussection.WebAssembly'scomponentsfollowalogicalmappingthatdirectlycorrespondtotheAPIs(forexample,youpassamemoryandtableinstanceintoJavaScript'sWebAssembly.instantiate()function).YourprimaryinteractionwithamoduleinbinaryformatwillbethroughtheseAPIs.

TheJavaScriptandWebAPIsInadditiontotheWebAssemblyCoreSpecification,therearetwoAPIspecificationsforinteractingwithWebAssemblymodules:theWebAssemblyJavaScriptInterface(JavaScriptAPI)andtheWebAssemblyWebAPI.Intheprevioussections,wecoveredpertinentaspectsoftheCoreSpecificationtobecomefamiliarwiththeunderlyingtechnology.IfyouneverreadtheCoreSpecification(orifyouskippedthefirstfewsectionsofthischapter),itwouldn'tinhibittheuseofWebAssemblyinyourapplication.ThatisnotthecasefortheAPIs,astheydescribethemethodsandinterfacerequiredtoinstantiateandinteractwithyourcompiledWasmmodule.Inthissection,wewillreviewtheWebandJavaScriptAPIsanddescribehowtoloadandcommunicatewithaWasmmoduleusingJavaScript.

WebAssemblystoreandobjectcachesBeforediggingintointeractions,let'sdiscusstherelationshipbetweenJavaScriptandWebAssemblyinthecontextofexecution.TheCoreSpecificationcontainsthefollowingdescriptionintheExecutionsection:"WebAssemblycodeisexecutedwheninstantiatingamoduleorinvokinganexportedfunctionontheresultingmoduleinstance.

Executionbehaviorisdefinedintermsofanabstractmachinethatmodelstheprogramstate.Itincludesastack,whichrecordsoperandvaluesandcontrolconstructs,andanabstractstorecontainingglobalstate."

Underthehood,JavaScriptusessomethingcalledagentstomanageexecution.Thestorebeingreferredtointhedefinitioniscontainedwithinanagent.ThefollowingdiagramrepresentsaJavaScriptagent:

JavaScriptagentelements

Thestorerepresentsthestateoftheabstractmachine.WebAssemblyoperationstakeastoreandreturnanupdatedstore.EachagentisassociatedwithcachesthatmapJavaScriptobjectstoWebAssemblyaddresses.Sowhyisthisimportant?ItrepresentstheunderlyingmethodofinteractionbetweenWebAssemblymodulesandJavaScript.TheJavaScriptobjectscorrespondtothe

WebAssemblynamespacewithintheJavaScriptAPI.Withthatinmind,let'sdigintotheinterface.

LoadingamoduleandtheWebAssemblynamespacemethodsTheJavaScriptAPIcoversthevariousobjectsavailableontheglobalWebAssemblyobjectinthebrowser.Beforewediscussthose,we'llstartwiththemethodsavailableontheWebAssemblyobject,withabriefoverviewoftheirintendedpurposes:

instantiate()istheprimaryAPIforcompilingandinstantiatingWebAssemblycodeinstantiateStreaming()performsthesamefunctionalityasinstantiate(),butitusesstreamingtocompileandinstantiatethemodule,whicheliminatesanintermediatestepcompile()onlycompilesaWebAssemblymodule,butdoesn'tinstantiateitcompileStreaming()alsoonlycompilesaWebAssemblymodule,butitusesstreamingsimilartoinstantiateStreaming()validate()checkstheWebAssemblybinarycodetoensurethebytesarevalidandreturnstrueifvalidorfalseifnotvalid

TheinstantiateStreaming()andcompileStreaming()methodsarecurrentlyonlypresentintheWebAPI.Infact,thesetwomethodscomprisetheentirespecification.ThemethodsavailableontheWebAssemblyobjectarefocusedprimarilyoncompilingandinstantiatingmodules.Withthatinmind,let'sdiscusshowtofetchandinstantiateaWasmmodule.

Whenyouperformafetchcalltogetamodule,itreturnsaPromisethatresolveswiththerawbytesofthatmodule,whichneedtobeloadedintoanArrayBufferandinstantiated.Goingforward,wewillrefertothisprocessasloadingamodule.

Thefollowingdiagramdemonstratesthisprocess:

FetchingandloadingaWebAssemblymodule

ThisprocessisactuallyquitesimpleusingPromises.Thefollowingcodedemonstrateshowamoduleisloaded.TheimportObjargumentpassesanydataorfunctionstotheWasmmodule.Youcandisregarditfornow,aswe'llbe

discussingitingreaterdetailinChapter5,CreatingandLoadingaWebAssemblyModule:

fetch('example.wasm')

.then(response=>response.arrayBuffer())

.then(buffer=>WebAssembly.instantiate(buffer,importObj))

.then(({module,instance})=>{

//Dosomethingwithmoduleorinstance

});

Theprecedingexampledictatesthemethodforloadingthemoduleusingtheinstantiate()method.TheinstantiateStreaming()methodisalittledifferentandsimplifiestheprocessevenmorebyfetching,compiling,andinstantiatingamoduleinasinglestep.Thefollowingcodeachievesthesamegoal(loadingamodule)usingthismethod:

WebAssembly.instantiateStreaming(fetch('example.wasm'),importObj)

.then(({module,instance})=>{

//Dosomethingwithmoduleorinstance

});

TheinstantiationmethodsreturnaPromisethatresolveswithanobjectcontainingacompiledWebAssembly.Module(module)andWebAssembly.Instance(instance),bothofwhichwillbecoveredlaterinthissection.Inmostcases,youwilluseoneofthesemethodstoloadaWasmmoduleonyoursite.TheinstancecontainsalloftheexportedWebAssemblyfunctionsthatyoucancallfromyourJavaScriptcode.

Thecompile()andcompileStreaming()methodsreturnaPromisethatonlyresolveswithacompiledWebAssembly.Module.Thisisusefulifyouwanttocompileamoduleandinstantiateitatalatertime.MozillaDeveloperNetwork(MDN),thewebdocssitemanagedbyMozilla,providesanexampleinwhichthecompiledmoduleispassedtoaWebWorker.

Asfarasthevalidate()methodisconcerned,itsonlypurposeistotestwhetherthetypedarrayorArrayBufferpassedinasaparameterisvalid.ThiswouldbecalledaftertherawbytesoftheresponseareloadedintoanArrayBuffer.Thismethodwasn'tincludedinthecodeexamplesbecauseattemptingtoinstantiateorcompileaninvalidWasmmodulewillthroweitheraTypeErrororoneoftheErrorobjectspresentontheWebAssemblyobject.WewillcovertheseErrorobjectslaterinthissection.

WebAssemblyobjectsInadditiontothemethodscoveredintheLoadingamoduleandtheWebAssemblynamespacemethodssection,theglobalWebAssemblyobjecthaschildobjectsthatareusedtointeractwithandtroubleshootWebAssembly.TheseobjectscorrelatedirectlytotheconceptswediscussedinthesectionsontheWebAssemblybinaryandtextformats.ThefollowinglistcontainstheseobjectsaswellastheirdefinitionstakenfromMDN:

TheWebAssembly.ModuleobjectcontainsstatelessWebAssemblycodethathasalreadybeencompiledbythebrowserandcanbeefficientlysharedwithworkers,cachedinIndexedDB,andinstantiatedmultipletimesTheWebAssembly.Instanceobjectisastateful,executableinstanceofaWebAssembly.ModulewhichcontainsalloftheexportedWebAssemblyfunctionsthatallowcallingintoWebAssemblycodefromJavaScriptWebAssembly.Memory,whencalledwiththeconstructor,createsanewMemoryobjectwhichisaresizableArrayBufferthatholdstherawbytesofmemoryaccessedbyaWebAssemblyInstanceWebAssembly.Table,whencalledwiththeconstructor,createsanewTableobjectofthegivensizeandelementtypethatrepresentsaWebAssemblyTable(whichstoresfunctionreferences)WebAssembly.CompileError,whencalledwiththeconstructor,createsanerrorwhichindicatesthatanissueoccurredduringWebAssemblydecoding,orvalidationWebAssembly.LinkError,whencalledwiththeconstructor,createsanerrorwhichindicatesthatanissueoccurredduringmoduleinstantiationWebAssembly.RuntimeError,whencalledwiththeconstructor,createsanerrorwhichindicatesthatWebAssemblyspecifiedatrap(forexample,stackoverflowoccurred)

Let'sdigintoeachoneindividually,startingwiththeWebAssembly.Moduleobject.

WebAssembly.ModuleTheWebAssembly.ModuleobjectistheintermediatestepbetweentheArrayBufferandtheinstantiatedmodule.Thecompile()andinstantiate()methods(andtheirstreamingcounterparts)returnaPromisethatresolveswithamodule(moduleinlowercaserepresentsthecompiledModule).AmodulecanalsobecreatedsynchronouslybypassingatypedarrayorArrayBufferdirectlyintotheconstructor,butthisisdiscouragedforlargemodules.

TheModuleobjectalsohasthreestaticmethods:exports(),imports(),andcustomSections().Allthreetakeamoduleasaparameter,butcustomSections()takesastringrepresentingthesectionnameasitssecondparameter.CustomsectionsaredescribedintheBinaryFormatsectionoftheCoreSpecificationandareintendedtobeusedfordebugginginformationorthird-partyextensions.Inmostcases,youwon'tneedtodefinethese.Theexports()functionisusefulifyou'reusingaWasmmodulethatyoudidn'tcreate,althoughyou'llonlybeabletoseethenameandkind(forexample,function)ofeachexport.

Forsimpleusecases,youwon'tbedealingdirectlywiththeModuleobjectorcompiledmodule.MostoftheinteractionwilltakeplacewithanInstance.

WebAssembly.InstanceTheWebAssembly.InstanceobjectistheinstantiatedWebAssemblymodule,whichmeansyoucancallexportedWebAssemblyfunctionsfromit.Callinginstantiate()orinstantiateStreaming()returnsaPromisethatresolveswithanobjectcontaininganinstance.YoucallWebAssemblyfunctionsbyreferencingthenameofthefunctionontheinstance'sexportproperty.Forexample,ifamodulecontainedanexportedfunctionnamedsayHello(),you'dcallthefunctionusinginstance.exports.sayHello().

WebAssembly.MemoryTheWebAssembly.MemoryobjectholdsthememoryaccessedbyaWebAssemblyInstance.ThismemorycanbeaccessedandchangedfrombothJavaScriptandWebAssembly.TocreateanewinstanceofMemory,youneedtopassanobjectwithaninitialand(optional)maximumvaluetotheWebAssembly.Memory()constructor.ThesevaluesareinunitsofWebAssemblypages,whereonepageis64KB.Youincreasethesizeofthememoryinstancebycallingthegrow()functionwithasingleparameterthatrepresentsthenumberofWebAssemblypagestogrowby.Youcanalsoaccessthecurrentbuffercontainedinthememoryinstancethroughitsbufferproperty.

MDNdescribestwowaystogettoaWebAssembly.Memoryobject.ThefirstwayistoconstructitfromJavaScript(varmemory=newWebAssembly.Memory(...)),whilethesecondwayistohaveitexportedbyaWebAssemblymodule.TheimportanttakeawayisthatmemorycanbepassedeasilybetweenJavaScriptandWebAssembly.

WebAssembly.TableTheWebAssembly.Tableobjectisanarray-likestructurethatisusedtostorefunctionreferences.JustaswithWebAssembly.Memory,aTablecanbeaccessedandchangedfrombothJavaScriptandWebAssembly.Asofthetimeofwriting,tablescanonlystorefunctionreferences,butit'slikelythat,asthetechnologyevolves,additionalentitieswillbeabletobestoredintablesaswell.

TocreateanewTableinstance,youneedtopassanobjectwithanelement,initial,and(optional)maximumvalue.Theelementmemberisastringthatrepresentsthetypeofvaluestoredinthetable;currentlytheonlyvalidvalueis"anyfunc"(forfunctions).TheinitialandmaximumvaluesrepresentthenumberofelementsintheWebAssemblyTable.

YoucanaccessthenumberofelementsintheTableinstanceusingthelengthproperty.Theinstancealsoincludesmethodstomanipulateandqueryelementsinthetable.Theget()methodallowsyoutoaccesstheelementatthegivenindex,whichispassedinasaparameter.Theset()methodallowsyoutosetanelementattheindexspecifiedasthefirstparametertothevaluespecifiedasthesecondparameter(pertheprecedingnote,onlyfunctionsaresupported).Finally,grow()allowsyoutoincreasethesizeoftheTableinstance(numberofelements)bythenumberpassedinasaparameter.

WebAssemblyerrors(CompileError,LinkError,RuntimeError)TheJavaScriptAPIprovidesconstructorstocreateinstancesoftheErrorobjectsspecifictoWebAssembly,butwewon'tspendtoomuchtimecoveringtheseobjects.Theobjectdefinitionlistatthebeginningofthissectiondescribesthenatureofeacherror,whichmayberaisedifthespecifiedconditionismet.Allthreeerrorscanbeconstructedwithamessage,filename,andlinenumberparameter(allofwhichareoptional),andhasthesamepropertiesandmethodsasthestandardJavaScriptErrorobject.

ConnectingthedotswithWasmFiddleWespentthischapterreviewingthevariouselementsofWebAssemblyandthecorrespondingJavaScriptandWebAPIs,butunderstandinghowthepiecesfittogethercanstillbeconfusing.Asweprogressthroughtheexamplesinthisbookandyou'reabletoseehowC/C++,WebAssembly,andJavaScriptinteract,theseconceptswillbecomeclearer.

Thatbeingsaid,ademonstrationofthisinteractionmayhelpinclearingupsomeoftheconfusion.Inthissection,we'regoingtouseanonlinetoolcalledWasmFiddletodemonstratetherelationshipbetweentheseelementssoyoucanseeWebAssemblyinactionandgetahigh-leveloverviewofthedevelopmentworkflow.

WhatisWasmFiddle?WasmFiddle,locatedathttps://wasdk.github.io/WasmFiddle/,isanonlinecodeeditingtoolthatallowsyoutowritesomeCorC++codeandconvertittoWat,compileittoWasm,orinteractwithitdirectlyusingJavaScript.TheC/C++andJavaScripteditorsareminimalandaren'tintendedtobeusedasyourprimarydevelopmentenvironment,butitoffersavaluableserviceintheWasmcompiler.InChapter3,SettingUpADevelopmentEnvironment,you'lldiscoverthatgoingfromsquareonetogeneratingWasmfilesrequiresalittlebitofwork—beingabletopasteyourCcodeintothebrowserandhittingacoupleofbuttonsmakesthingsmuchmoreconvenient.Thefollowingdiagramgivesaquickoverviewoftheinterface:

ComponentsoftheWasmFiddleuserinterfaceAsyoucansee,theinterfaceisrelativelysimple.Let'stryoutsomecode!

CcodetoWatTheupper-leftpaneinthefollowingscreenshotcontainsasimpleCfunctionthatadds2tothenumberspecifiedasaparameter.Thelower-leftpanecontainsthecorrespondingWat:

CfunctionandthecorrespondingWat

Ifthislooksfamiliar,it'sbecausethissamecodewasusedfortheexplanationofWat'ss-expressionsinthebeginningofthischapter.Diggingalittledeeper,youcanseehowtheCcodecorrespondstotheWatoutput.TheaddTwo()functionisexportedfromthemoduleasastringonline5.Line5alsocontains(func$addTwo),whichreferencesthe$addTwofunctiononline6.Line6specifiesthatasingleparameteroftypei32(aninteger)canbepassedinandtheresultreturnedisalsoani32.PressingtheBuildbuttonintheupper-rightcorner(orabovetheC/C++editor)willcompiletheCcodeintoaWasmfile.TheWasmwillbeavailablefordownloadorinteractionwithJavaScriptoncethebuildiscompleted.

WasmtoJavaScriptTheupper-rightpaneinthefollowingscreenshotcontainssomeJavaScriptcodetocompiletheWasmthatwasgeneratedinthepreviousstep.ThewasmCodewasgeneratedwhenthebuildfinished,soitshouldbeavailableautomatically.Ratherthanusetheinstantiate()method,WasmFiddlecreatesacompiledWebAssembly.ModuleinstanceandpassesthatintotheconstructorofanewWebAssembly.Instance.ThewasmImportsobjectiscurrentlyempty,althoughwecouldpassinaWebAssembly.MemoryandWebAssembly.Tableinstanceifdesired:

JavaScriptcodecallingtheCfunctionfromthecompiledWasmmodule

ThefinallineofJavaScriptprintstheresultofaddTwo()totheoutputinthelower-rightpanewhenpassedthenumber2.Thelog()methodisacustomfunctionthatensurestheoutputisprintedtothelower-rightpane(thenumber4).NotehowtheJavaScriptcodeinteractswithwasmInstance.TheaddTwo()functioniscalledfromtheinstance'sexportsobject.Althoughthiswasacontrivedexample,itdemonstratesthestepsCorC++codegoesthroughbeforeitcanbeusedbyJavaScriptasaWasmmodule.

SummaryInthischapter,wediscussedtheelementsofWebAssemblyandtheirrelationship.ThestructureoftheCoreSpecificationwasusedtodescribethemappingofthetextandbinaryformatstoacommonabstractsyntax.Wehighlightedaspectsofthetextformat(Wat)thatcanbeusefulinthecontextofdebugginganddevelopment,aswellaswhys-expressionsareanexcellentfitforthetextualrepresentationoftheabstractsyntax.Wealsorevieweddetailspertainingtothebinaryformatandthevariouselementsthatmakeupamodule.ThemethodsandobjectswithintheJavaScriptandWebAPIsweredefinedwithdescriptionsoftheirroleswithregardtoWebAssemblyinteraction.Finally,asimpleexampleoftherelationshipbetweensourcecode,Wat,andJavaScriptwaspresentedusingtheWasmFiddletool.

InChapter3,SettingUpaDevelopmentEnvironment,we'llinstallthedevelopmenttoolingwe'llusetoworkeffectivelywithWebAssembly.

Questions1. Whatkindofdataares-expressionsgoodatrepresenting?2. Whatarethefourlanguageconceptsthataresharedbetweenthebinaryand

textformats?3. Whatisoneoftheusecasesforthetextformat?4. WhatistheonlyelementtypethatcanbestoredinaWebAssemblyTable?5. WhatdoestheJavaScriptengineusetomanageexecution?6. Whichmethodrequireslesscodetoinstantiateamodule,instantiate()or

instantiateStreaming()?7. WhaterrorobjectsareavailableontheWebAssemblyJavaScriptobjectand

whateventcauseseachone?

FurtherreadingWebAssemblyonMDN:https://developer.mozilla.org/en-US/docs/WebAssemblyWasmFiddle:https://wasdk.github.io/WasmFiddleS-expressionsonWikipedia:https://en.wikipedia.org/wiki/S-expressionExamplesofTrees:http://interactivepython.org/runestone/static/pythonds/Trees/ExamplesofTrees.html

SettingUpaDevelopmentEnvironment

Nowthatyou'refamiliarwiththeelementsofWebAssembly,it'stimetosetupasuitabledevelopmentenvironment.DevelopingwithWebAssemblyistantamounttodevelopinginCorC++.Thedifferenceliesinthebuildprocessandtheoutput.Inthischapter,wewillcoverthedevelopmenttooling,andhowtoinstallandconfigureitonyoursystem.

Ourgoalforthischapteristounderstandthefollowing:

Howtoinstalltherequireddevelopmenttooling(Git,Node.js,andVisualStudioCode)HowtoconfigureVisualStudioCodeforusewithC/C++andWebAssemblyusingextensionsHowtosetupalocalHTTPservertoserveuptheHTML,JavaScript,and.wasmfilesCheckingyourbrowserforWebAssemblysupportWhathelpfultoolsareavailabletosimplifyandimprovethedevelopmentprocess

InstallingthedevelopmenttoolingYou'llneedtoinstallsomeapplicationsandtoolingtostartdevelopingWebAssembly.WewilluseVisualStudioCode,atexteditor,towriteourC/C++,JavaScript,HTML,andWat.We'llalsouseNode.jsforservingupthefilesandGittomanageourcode.Wewillusepackagemanagerstoinstallthesetools,whichmakestheinstallationprocessmuchsimplerthandownloadingandinstallingthemmanually.Inthissection,wewillcovertheoperatingsystems,aswellasthepackagemanagersforeachplatform.We'llalsorevieweachoftheapplications,withabriefoverviewoftheirroleinthedevelopmentprocess.

OperatingsystemsandhardwareToensurethattheinstallationandconfigurationprocessgoessmoothly,it'simportanttobeawareoftheoperatingsystemsIwillusefortheexamplesinthisbook.Ifyouencounteranissue,itmaybeduetoanincompatibilitybetweentheplatformyou'reusingandtheoneI'musing.Inmostcases,youshouldn'thaveanissue.ForthesakeofeliminatingtheOSversionasapotentialproblemcauser,I'veprovideddetailsfortheoperatingsystemsI'musinginthefollowinglist:

macOSHighSierra,version10.13.x2.2GHzInteli7processor16GBofRAM

UbuntuUbuntu16.04LTSrunninginVMwareFusion2.2GHzInteli7Processor4GBofRAM

WindowsWindows10ProrunninginVMwareFusion2.2GHzInteli7Processor8GBofRAM

PackagemanagersPackagemanagersaretoolsthatsimplifytheinstallationprocessforsoftware.Theyallowustoupgrade,configure,uninstall,andsearchforavailablesoftwarefromthecommandlinewithouthavingtogotoawebsitetodownloadandruntheinstaller.Theyalsosimplifytheinstallationprocessforsoftwarethatmayhavemultipledependenciesorrequiremanualconfigurationbeforeuse.Inthissection,I'llcoverthepackagemanagerforeachplatform.

HomebrewformacOSHomebrewisanexcellentpackagemanagerformacOSthatallowsustoinstallmostofthetoolswewilluseoutofthebox.HomebrewisassimpleaspastingthefollowingcommandinTerminalandrunningit:

/usr/bin/ruby-e"$(curl-fsSLhttps://raw.githubusercontent.com/Homebrew/install/master/install)"

You'llseemessagesinTerminalthatwillwalkyouthroughtheinstallationprocess.Oncethat'scomplete,you'llneedtoinstallanextensionforHomebrewcalledHomebrew-CaskthatallowsyoutoinstallmacOSapplicationswithouthavingtodownloadtheinstaller,mountit,anddragtheapplicationintotheApplicationsfolder.Youcaninstallthisbyrunningthefollowingcommand:

brewtapcaskroom/cask

That'sit!You'renowabletoinstallapplicationsbyrunningeitherofthesecommands:

#Forcommandlinetools:

brewinstall<ToolName>

#Fordesktopapplications:

brewcaskinstall<ApplicationName>

AptforUbuntuAptisthepackagemanagerprovidedwithUbuntu;there'snoneedtoinstallit.Itallowsyoutoinstallbothcommand-linetoolsandapplicationsoutofthebox.Ifanapplicationisn'tavailablefromApt'srepository,youcanaddarepositoryusingthefollowingcommand:

add-apt-repository

ChocolateyforWindowsChocolateyisapackagemanagerforWindows.It'ssimilartoAptinthatitletsyouinstallbothcommand-linetoolsandapplications.ToinstallChocolatey,youneedtorunthecommandprompt(cmd.exe)asanadministrator.YoucandothisbypressingtheStartmenubutton,typingcmd,andright-clickingontheCommandPromptapplicationandselectingRunasadministrator:

RunningtheCommandPromptasanadministratorThenjustrunthefollowingcommand:

@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe"-NoProfile-InputFormatNone-ExecutionPolicyBypass-Command"iex((New-ObjectSystem.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))"&amp;&amp;SET"PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"

TheeasiestwaytogetthecommandtextisthroughChocolatey'sinstallationpageathttps://chocolatey.org/install.There'sabuttontocopythetexttoyourclipboardundertheInstallwithcmd.exesection.YoucouldalsoinstalltheapplicationusingPowerShellifyoufollowthestepsontheInstallationpage.

GitGitisaversioncontrolsystem(VCS)thatallowsyoutotrackchangestofilesandmanageworkbetweenmultipledeveloperscontributingtothesamecodebase.GitistheVCSpoweringGitHubandGitLab,andisalsoavailableonBitbucket(theyalsoofferMercurial,whichisanotherVCS).GitwillallowustoclonerepositoriesfromGitHub,andisaprerequisitefortheEMSDK,whichwe'llcoverinthenextchapter.Inthissection,wewillcovertheinstallationprocessforGit.

InstallingGitonmacOSGitisprobablyalreadyavailableifyou'reusingmacOS.macOScomesbundledwithAppleGit,whichwillprobablybeafewversionsbehindthemostrecentversion.Forthepurposesofthisbook,theversionyoualreadyhaveinstalledshouldbesufficient.Ifyouwishtoupgrade,youcaninstallthemostrecentversionofGitusingHomebrewbyrunningthefollowingcommandsinTerminal:

#InstallGittotheHomebrewinstallationfolder(/usr/local/bin/git):

brewinstallgit

#EnsurethedefaultGitispointingtotheHomebrewinstallation:

sudomv/usr/bin/git/usr/bin/git-apple

Ifyourunthiscommand,youshouldsee/usr/local/bin/git:

whichgit

Youcanchecktoensurethattheinstallationwassuccessfulbyrunningthiscommand:

git--version

InstallingGitonUbuntuYoucanuseapttoinstallGit;justrunthefollowingcommandinTerminal:

sudoaptinstallgit

Youcanchecktoensurethattheinstallationwassuccessfulbyrunningthiscommand:

git--version

InstallingGitonWindowsYoucaninstallGitusingChocolatey.OpenupCommandPromptorPowerShellandrunthiscommand:

chocoinstallgit

Youcanchecktoensurethattheinstallationwassuccessfulbyrunningthiscommand:

git--version

Youcanbypasstheconfirmationmessagesbyaddinga-ytotheendoftheinstallcommand(forexample,chocoinstallgit-y).Youcanalsoopttoalwaysskiptheconfirmationbyenteringthechocofeatureenable-nallowGlobalConfirmationcommand.

Node.jsTheofficialwebsiteforNode.jsdescribesitasanasynchronousevent-drivenJavaScriptruntime.Nodeisdesignedtobuildscalablenetworkapplications.Wewilluseitinthisbooktoserveupourfilesandworkwiththeminabrowser.Node.jscomespackagedwithnpm,apackagemanagerforJavaScript,whichwillallowustoinstallpackagesgloballyandaccessthemthroughthecommandline.Inthissection,we'llcovertheinstallationprocessforeachplatformusingtheNodeVersionManager(nvm).

nvmWewillusethelong-termstable(LTS)releaseofNode.js(Version8)toensurethatwe'reusingthemoststableversionoftheplatform.WewillusenvmtomanageNode.jsversions.Thiswillpreventconflictsifyoualreadyhaveahigher(orlower)versionofNode.jsinstalledonyourcomputer.nvmallowsyoutohavemultipleversionsofNode.jsinstalledthatyoucanquicklyswitchtoandisolateinthecontextofasingleterminalwindow.

InstallingnvmonmacOSRunthefollowingcommandinTerminal:

brewinstallnvm

Followthepost-installationstepsHomebrewspecifiestoensurethatyoucanstartusingit(youmayhavetorestartyourTerminalsession).IfyouclearedyourTerminalcontentsbeforeperformingthesteps,youcanrunthiscommandtoseetheinstallationstepsagain:

brewinfonvm

Youcanchecktoensurethattheinstallationwassuccessfulbyrunningthiscommand:

nvm--version

InstallnvmonUbuntu

Ubuntucomesbundledwithwget,whichcanretrievefilesusingHTTP/SandFTP/Sprotocols.TheGitHubpagefornvm(https://github.com/creationix/nvm)containsthefollowingcommandtoinstallitusingwget:wget-qO-https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh|bash

Onceinstalled,restartTerminaltocompletetheinstallation.Youcanchecktoensurethattheinstallationwassuccessfulbyrunningthefollowingcommand:nvm--version

InstallingnvmonWindows

nvmdoesn'tcurrentlysupportWindows,soyou'reactuallyinstallingadifferentapplicationnamednvm-windows.TheGitHubpagefornvm-windowscanbefoundathttps://github.com/coreybutler/nvm-windows.Someofthecommandsareslightlydifferent,buttheinstallationcommandwerunwillbethesame.Toinstallnvm-windows,openupCommandPromptorPowerShellandrunthiscommand:chocoinstallnvm

Youcanchecktoensurethattheinstallationwassuccessfulbyrunningthefollowingcommand:nvm--version

InstallingNode.jsusingnvmAfterinstallingnvm,youneedtoinstalltheversionofNode.jswewilluseinthisbook:version8.11.1.Toinstallit,runthiscommand:nvminstall8.11.1

Ifyoudidn'thaveNode.jsornvmpreviouslyinstalled,itwillautomaticallysetthistoyourdefaultNode.jsinstallation,sotheoutputofthiscommandshouldbev8.11.1:node--version

IfyouhaveexistingNode.jsversionsinstalled,youcaneitherusev8.11.1asadefault,orensurethatyourunthiscommandtousev8.11.1whenworkingthroughtheexamplesinthisbook:nvmuse8.11.1

Youcancreateafilenamed.nvmrcinthefolderwithyourcodeandpopulateitwiththecontentsv8.11.1.Youcanrunnvmusewithinthisdirectoryanditwillsettheversionto8.11.1withouthavingtospecifyit.

GNUmakeandrimrafInthelearn-webassemblyrepository,thecodeexamplesuseGNUMakeandVSCode'sTasksfeature(whichwe'llcoverinChapter5,CreatingandLoadingaWebAssemblyModule)toperformthebuildtasksdefinedthroughoutthebook.GNUMakeisanexcellentcross-platformtoolforautomatingbuildprocesses.YoucanreadmoreaboutGNUMakeathttps://www.gnu.org/software/make.Let'sreviewtheinstallationstepsforeachplatform.

<strong>make-v</strong>

Ifyouseeversioninformation,you'rereadytogo.SkipaheadtotheInstallingrimrafsection.Otherwise,followtheGNUMakeinstallationinstructionsforyourplatform.

InstallingGNUMakeonmacOSToinstallGNUMakeonmacOS,runthefollowingcommandfromTerminal:

brewinstallmake

Youcanchecktoensurethattheinstallationwassuccessfulbyrunningthiscommand:

make-v

Ifyouseeversioninformation,skiptotheInstallingrimrafsection.

InstallingGNUMakeonUbuntuToinstallGNUMakeonUbuntu,runthefollowingcommandfromTerminal:

sudoapt-getinstallmake

Youcanchecktoensurethattheinstallationwassuccessfulbyrunningthiscommand:

make-v

Ifyouseeversioninformation,skiptotheInstallingrimrafsection.

InstallingGNUmakeonWindowsYoucaninstallGNUmakeonWindowsusingChocolatey.OpenupCommandPromptorPowerShellandrunthefollowingcommand:chocoinstallmake

YoumayneedtorestarttheCLItousethemakecommand.Oncerestarted,runthefollowingcommandtovalidatetheinstallation:make-v

Ifyouseeversioninformation,continuetothenextsection.Ifyouencounterissues,youmayneedtodownloadandinstallthesetuppackageathttp://gnuwin32.sourceforge.net/packages/make.htm.

InstallingrimrafSomeofthebuildstepsdefinedintheMakefilesorVSCodeTasksdeletefilesordirectories.Thecommandsrequiredtodeleteafileorfolderdifferbasedonyourplatformandshell.Toaddressthisissuewe'llusetherimrafnpmpackage(https://www.npmjs.com/package/rimraf).Installingthepackagegloballyprovidesarimrafcommandthatperformsthecorrectdeletionoperationfortheoperatingsystemandshell.

Toinstallrimraf,ensurethatNode.jsisinstalledandrunthefollowingcommandfromaCLI:

npminstall-grimraf

Toensurethattheinstallationwassuccessful,runthefollowingcommand:

rimraf--help

Youshouldseeusageinstructionsandalistofcommandlineflags.Let'smoveontotheVSCodeinstallation.

VSCodeVSCodeisacross-platformtexteditorwithmultiple-languagesupportandarichextensionsecosystem.IntegrateddebuggingandGitsupportarebuiltin,andnewfeaturesarebeingaddedallthetime.We'reabletouseitfortheentireWebAssemblydevelopmentprocessthroughoutthecourseofthisbook.Inthissection,wewillcovertheinstallationstepsforeachplatform:

ScreenshotfromVisualStudioCode'swebsite

InstallingVisualStudioCodeonmacOSUseHomebrew-CasktoinstallVSCode.RunthefollowingcommandinTerminaltoinstall:

brewcaskinstallvisual-studio-code

Onceit'scomplete,youshouldbeabletolaunchitfromtheApplicationsfolderortheLaunchpad.

InstallingVisualStudioCodeonUbuntuTheprocessforinstallingVSCodeonUbuntuhasafewextrasteps,butisstillrelativelysimple.First,downloadthe.debfilefromVSCode'sdownloadpage(https://code.visualstudio.com/Download).Oncethedownloadcompletes,runthefollowingcommandstocompletetheinstallation:

#ChangedirectoriestotheDownloadsfolder

cd~/Downloads

#Replace<file>withthenameofthedownloadedfile

sudodpkg-i<file>.deb

#Completeinstallation

sudoapt-getinstall-f

Ifyougetamissingdependencyerror,youcanfixitbyrunningthefollowingcommandbeforesudodpkg:

sudoapt-getinstalllibgconf-2-4

sudoapt--fix-brokeninstall

YoushouldnowbeabletoopenVSCodefromtheLauncher.

InstallingVSCodeonWindowsYoucaninstallVSCodeusingChocolatey.RunthiscommandfromCommandPromptorPowerShell:

chocoinstallvisualstudiocode

Onceinstalled,youcanaccessitfromtheStartmenu.

YoucanopenVSCodewiththecurrentworkingdirectoryastheprojectbyrunningcode.intheCLI.

ConfiguringVSCodeOutofthebox,VSCodeisapowerfultexteditorwithalotofgreatfunctionality.Inadditiontobeinghighlyconfigurableandcustomizable,itpossessesanincrediblyrichextensionsecosystem.We'llneedtoinstallsomeoftheseextensionssowewon'tneedtousedifferenteditorsfordifferentprogramminglanguages.Inthissection,wewillcoverhowtoconfigureVSCodeandwhichextensionstoinstalltosimplifytheWebAssemblydevelopmentprocess.

ManagingsettingsandcustomizationCustomizingandconfiguringVSCodeissimpleandintuitive.YoucanmanagecustomsettingssuchaseditorfontandtabsizesbyselectingCode|Preferences|SettingsonmacOSorFile|Preferences|SettingsonWindows.UserandworkspacesettingsaremanagedseparatelyinJSONfilesandautocompletionisprovidedincaseyoucan'tremembertheexactnameofasetting.YoucanalsochangethethemesorkeyboardshortcutsbyselectingtheappropriateoptioninthePreferencesmenu.Thesettingsfileisalsowhereyoucansetcustomsettingsforanyextensionsyouinstall.Somesettingsareaddedbydefaultwhenyouinstallanextension,sochangingthemisassimpleasupdatingandsavingthisfile.

ExtensionsoverviewWe'llneedtoinstallsomeextensionsaspartoftheconfigurationprocess.TherearemultiplewaystofindandinstallextensionsinVSCode.IprefertoclickontheExtensionsbutton(fourthbuttonfromthetopintheActivitybarontheleft-handsideoftheeditor),enterwhatI'mlookingforintheSearchbox,andpressthegreenInstallbuttonfortheextensionI'dliketoinstall.YoucouldalsovisittheVSCodeMarketplaceathttps://marketplace.visualstudio.com/vscode,searchforandselectanextensionyou'dliketoinstall,andpressthegreenInstallbuttonontheextension'spage.Youcanmanageextensionsthroughthecommandlineaswell.Formoreinformation,visithttps://code.visualstudio.com/docs/editor/extension-gallery:

InstallingextensionsinVSCode

ConfigurationforC/C++andWebAssemblyVSCodedoesn'tsupportCandC++outofthebox,butthereisanexcellentextensionthatallowsyoutoworkwiththeselanguages.Italsodoesn'tsupportsyntaxhighlightingfortheWebAssemblytextformat,butthereisanextensionthataddsthatfunctionalityaswell.Inthissection,wewillcovertheinstallationandconfigurationoftheC/C++forVSCodeandWebAssemblyToolkitforVSCodeextensions.

InstallingC/C++forVSCodeTheC/C++extensionforVSCodeincludesseveralfeaturesforwritinganddebuggingCandC++code,suchasautocompletion,symbolsearching,class/methodnavigation,line-by-linecodestepping,andmanyothers.Toinstalltheextension,searchforC/C++intheExtensionsandinstalltheextensiontitledC/C++(it'screatedbyMicrosoft)ornavigatetotheextension'sofficialpageathttps://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptoolsandpressthegreenInstallbutton.

Onceinstalled,youcanviewconfigurationdetailsfortheextensionbyselectingtheextensionfromtheExtensionslistinVSCodeandselectingtheContributionstab.Thistabcontainsthevarioussettings,commands,anddebuggerdetails:

ContributionstabfortheC/C++extension

ConfiguringC/C++forVSCodeMicrosofthasanofficialpagefortheextension,whichyoucanviewathttps://code.visualstudio.com/docs/languages/cpp.Thispagedescribes,amongotherthings,howtoconfigurethroughtheuseofJSONfiles.Let'sstartbycreatinganewconfigurationfiletomanageourC/C++environment.YoucangenerateanewconfigurationfilebypressingtheF1key,typingC/C,andselectingC/Cpp:EditConfigurations…:

CommandPalettewithC/C++extensionoptions

Thiswillgenerateanewc_cpp_properties.jsonina.vscodefolderwithinyourcurrentproject.ThefilewillcontainconfigurationoptionsforyourC/C++compilerbasedonyourplatform,theCandC++standardstouse,andtheincludepathsforheaderfiles.Youcanclosethisfileonceit'sgenerated.WewillrevisititwhenweconfiguretheEMSDK.

WebAssemblyToolkitforVSCodeThereareafewdifferentWebAssemblyextensionsforVSCodecurrentlyavailable.I'musingtheWebAssemblyToolkitforVSCodeextensionbecauseitallowsyoutoright-clickona.wasmfileandselectShowWebAssembly,whichdisplaystheWatrepresentationofthefile.YoucaninstallthisextensionthroughtheExtensionspanel(searchforWebAssembly),orfromtheofficialextensionpageintheVSCodeMarketplace(https://marketplace.visualstudio.com/items?itemName=dtsvet.vscode-wasm):

ViewingtheWatfora.wasmfileusingtheWebAssemblyToolkitfortheVSCodeextension

Onceinstalled,you'rereadytogo!Nowthatyou'vegotalloftherequired

extensions,let'sevaluatesomeoptionalextensionsthatcansimplifycommontasks.

OtherusefulextensionsVSCodehassomegreatextensionstoimproveefficiencyandcustomizetheinterface.Inthissection,IwillcoversomeoftheextensionsIhaveinstalledthatsimplifycommontasksaswellastheuserinterface/iconthemes.Youdon'tneedtoinstallanyoftheseextensionsfortheexamplesinthisbook,butyoumayfindsomeofthemuseful.

AutorenametagThisextensionisincrediblyhelpfulwhenworkingwithHTML.Itautomaticallychangesthenameoftheclosingtagifyouchangethetagtype.Forexample,ifyouhavea<div>elementandyouwanttomakeita<span>,changingthetextoftheopeningelementtospanwillupdatetheclosingelementtext(</div>to</span>):

AutorenamingtagrenamingHTMLtag

BracketpaircolorizerThisextensioncolorizesthebrackets,braces,andparenthesesinyourcodesoyoucanquicklyidentifytheopeningandclosingbrackets.WebAssembly'stextformatusesparenthesesextensively,sobeingabletodeterminewhichelementsareenclosedinwhichlistmakesdebuggingandevaluationmuchsimpler:

BracketpaircolorizercolormatchingparenthesesinaWatfile

MaterialIconthemeandAtomOneLightthemeThereareover1,000iconandinterfacethemesavailableontheVSCodeMarketplace.I'mincludingtheMaterialIconthemeandAtomOneLightthemeinthissectionbecausethey'rebeingusedinthescreenshotsinthisbook.TheMaterialIconthemeisincrediblypopular,withover2milliondownloads,whiletheAtomOneLightthemehasover70,000downloads:

IconsintheMaterialIconstheme

SettingupforthewebInteractingwithanddebuggingWasmmoduleswillbedoneinthebrowser,whichmeanswe'llneedawaytoserveupafoldercontainingourexamplefiles.AswediscussedinChapter2,ElementsofWebAssembly-Wat,Wasm,andtheJavaScriptAPI,WebAssemblyisintegratedintothebrowser'sJavaScriptengine,butyou'llneedtomakesureyou'reusingabrowserthatsupportsit.Inthissection,wewillprovideinstructionsforcloningthebookexamplesrepository.Wewillalsoreviewhowtoquicklysetupalocalwebserverfortestingandevaluatingbrowseroptionstoensurethatyou'reabletodeveloplocally.

CloningthebookexamplesrepositoryYoumaywanttoclonetheGitHubrepositorynowwithalloftheexamplescontainedinthisbook.You'lldefinitelyneedtohavethecodeavailableforChapter7,CreatinganApplicationfromScratch,becausetheapplication'scodebaseistoolargetofitintoasinglechapter.Selectafolderonyourharddriveandrunthefollowingcommandtoclonetherepository:gitclonehttps://github.com/mikerourke/learn-webassembly

Oncethecloneprocessiscomplete,you'llfindthattheexamplesareorganizedbychapter.Ifthereareseveralexamplesinachapter,they'rebrokendownbysubfolderswithinthechapterfolder.

Ifyou'reusingWindows,donotclonetherepositoryintothe\Windowsfolderoranyotherfolderwithlimitedpermissions.Otherwise,youwillrunintoissueswhenattemptingtocompiletheexamples.

InstallingalocalserverWewilluseannpmpackage,serve,forservingupthefiles.Toinstall,simplyrunthiscommand:npminstall-gserve

Onceinstallationiscompleted,youcanserveupthefilesinanyfolder.Toensurethatit'sworking,let'stryservingupalocalfolder.Thecodeforthissectionislocatedinthe/chapter-03-dev-envfolderofthelearn-webassemblyrepository.Followtheseinstructionstovalidateyourserverinstallation:

1. First,let'screateafolderthatwillcontainthecodesampleswe'llbeworkingthroughfortheremainderofthebook(theexamplesusethenamebook-examples).

2. LaunchVSCodeandselectFile|Open...fromthemenubarformacOS/Linux,andFile|OpenFolder...forWindows.

3. Next,selectthefolder,book-examples,andpresstheOpen(orSelectFolder)button.

4. OnceVSCodefinishesloading,right-clickwithintheVSCodefileexplorerandselectNewFolderfromthemenuandnamethefolderchapter-03-dev-env.

5. Selectthechapter-03-dev-envfolderandpresstheNewFilebutton(orCmd/Ctrl+N)tocreateanewfile.Namethefileindex.htmlandpopulateitwiththefollowingcontents:

<!doctypehtml>

<htmllang="en-us">

<title>TestServer</title>

</head>

<body>

<h1>Test</h1>

<div>

Thisissometextonthemainpage.Click<ahref="stuff.html">here</a>

tocheckoutthestuffpage.

</div>

</body>

</html>

6. Createanotherfileinthechapter-03-dev-envfoldernamedstuff.htmlandpopulateitwiththefollowingcontents:

<!doctypehtml>

<htmllang="en-us">

<head>

<title>TestServer</title>

</head>

<body>

<h1>Stuff</h1>

<div>

Thisissometextonthestuffpage.Click<ahref="index.html">here</a>

togobacktotheindexpage.

</div>

</body>

</html>

7. WewilluseVSCode'sintegratedterminaltoserveupthefiles.YoucanaccessthisbyselectingView|IntegratedTerminal,orusingthekeyboardshortcutCtrl+`(the`isthebacktickkeyundertheEsckey).Onceloaded,runthiscommandtoserveuptheworkingfolder:

serve-l8080chapter-03-dev-env

Youshouldseethefollowing:

ResultsofrunningtheservecommandinterminalThe-l8080flagtellsservetoservethefolderonport8080.Thefirstlink(http://127.0.0.1:8080)isonlyaccessibleonyourcomputer.Anylinksbelowthatcanbeusedtoaccessthepagefromanother

computeronyourlocalnetwork.Ifyounavigatetothefirstlink(http://127.0.0.1:8080/index.html)inyourbrowser,youshouldsee

this:TestpageservedupinGoogleChromeClickingontheherelinkshouldbringyoutotheStuffpage(theaddressbarwillshow

127.0.0.1:8080/stuff.html.Ifeverythingisworkingcorrectly,it'stimetovalidateyourbrowser.

ValidatingyourbrowserToensurethatyou'reabletotestouttheexamplesinabrowser,youneedtomakesurethatthere'saglobalWebAssemblyobjectavailable.Topreventanyissuesrelatedtobrowsercompatibility,IrecommendthatyouhaveeitherGoogleChromeorMozillaFirefoxinstalledfordevelopment.Ifyouhadeitherofthesebrowsersinstalledbeforehand,there'saverygoodchancethatyourbrowserisalreadyvalid.Forthesakeofbeingthorough,wewillstillcoverthevalidationprocess.Inthissection,IwillreviewthestepsyoucantaketoensurethatyourbrowsersupportsWebAssembly.

ValidatingGoogleChromeTheprocessforvalidatingChromeprettystraightforward.Selectthebuttonthatlookslikethreeverticaldots(nexttotheaddressbar)andselectMoreTools|DeveloperToolsorusethekeyboardshortcutCmd/Ctrl+Shift+I:

AccessingDeveloperToolsinGoogleChromeOncetheDeveloperToolswindowappears,selecttheConsoletab,typeWebAssembly,andpressEnter.Ifyouseethis,yourbrowserisvalid:

ResultsofWebAssemblyvalidationinGoogleChrome'sDeveloperToolsconsole

ValidatingMozillaFirefoxTheprocessforvalidatingFirefoxisalmostidenticaltothatforGoogleChrome.SelectTools|WebDeveloper|ToggleToolsfromthemenubarorusethekeyboardshortcutCmd/Ctrl+Shift+I:

AccessingDeveloperToolsinMozillaFirefoxSelecttheConsoletab,clickinsidethecommandinputbox,typeWebAssembly,andpressEnter.You'llseethisifyourversionofFirefoxisvalid:

ResultsofWebAssemblyvalidationinMozillaFirefox'sDeveloperToolsconsole

ValidatingotherbrowsersThevalidationprocessforotherbrowsersisessentiallythesame;theonlyaspectofvalidationthatdiffersacrossbrowsersishowtoaccessthedevelopertools.IfaWebAssemblyobjectisavailablethroughtheconsoleofthebrowseryou'reusing,youcanusethatbrowserforWebAssemblydevelopment.

OthertoolsInadditiontotheapplicationsandtoolswecoveredintheprevioussections,therearesomegreattoolsthatarefreetouseandrichinfunctionalitythatcangreatlyimproveyourdevelopmentprocess.Iwon'thavetimetocoverthemall,butI'dliketohighlighttheonesIuseregularly.Inthissection,Iwillbrieflyreviewsomeofthepopulartoolingandapplicationsthatareavailableforeachplatform.

iTerm2formacOSThedefaultmacOSinstallationincludesTerminalapplication,Terminal,thatissufficientforuseinthisbook.Ifyouwantamorefull-featuredTerminal,iTerm2isanexcellentoption.Itoffersfeaturessuchassplittingwindows,extensivecustomization,multipleprofiles,andaToolbeltfeaturethatcandisplaynotes,runningjobs,commandhistory,andsoon.Youcandownloadtheimagefilefromtheofficialwebsite(https://www.iterm2.com/)andinstallitmanually,orinstalliTermwithHomebrew-Caskusingthiscommand:

brewcaskinstalliterm2

HereisiTerm2runningwiththeToolbeltopenandmultipleeditorwindows:

ITerminstancewithmultiplepanesandToolbelt

TerminatorforUbuntuTerminatoristheiTermandcmderofUbuntu,Terminalemulatorthatallowsformultipletabsandpaneswithinasinglewindow.Terminatoralsoprovidesfeaturessuchasdraganddrop,findfunctionality,andawidearrayofpluginsandthemes.YoucaninstallTerminatorthroughapt.Toensurethatyou'reusingthemostrecentversion,runthefollowingcommandsinTerminal:

sudoadd-apt-repositoryppa:gnome-terminator

sudoapt-getupdate

sudoapt-getinstallterminator

Referthescreenshot:

Terminatorscreenshottakenfromhttp://technicalworldforyou.blogspot.comB09984_03_17

cmderforWindowscmderisaconsoleemulatorforWindowsthataddsalotoffunctionalityandfeaturestothestandardCommandPromptorPowerShell.Itoffersfeaturessuchasmultipletabsandcustomizability.Itallowsyoutoopenupinstancesofdifferentshellswithinthesameprogram.Youcandownloadandinstallitfromtheofficialwebsite(cmder.net)orinstallitwithChocolateyusingthiscommand:chocoinstallcmder

Thisishowitlooks:

cmderscreenshotfromtheofficialwebsite

ZshandOh-My-ZshZshisaninteractiveshellthatimprovesuponBash.Oh-My-ZshisaconfigurationmanagerforZshthathasawidearrayofusefulplugins.Youcanseethewholelistontheirwebsite(https://github.com/robbyrussell/oh-my-zsh).Forexample,ifyouwantpowerfulautocompleteandsyntaxhighlightingfunctionalityinyourCLI,therearepluginssuchaszsh-autosuggestionandzsh-syntax-highlighting.YoucaninstallandconfigureZshandOh-My-ZshonmacOS,Linux,andWindows.TheOh-My-Zshpagehasinstallationinstructionsaswellasalistofthemesandplugins.

SummaryInthischapter,wecoveredtheinstallationandconfigurationprocessforthedevelopmenttoolingwewillusetostartworkingwithWebAssembly.WediscussedhowtoinstallGit,Node.js,andVSCodequicklyandeasilyusingapackagemanagerforyouroperatingsystems(forexample,HomebrewformacOS).ThestepstoconfigureVSCodewerepresentedaswellastherequiredandoptionalextensionsyoucanaddtoenhancethedevelopmentexperience.WediscussedhowtoinstallalocalwebserverfortestingandhowtovalidateyourbrowsertoensurethatWebAssemblyissupported.Finally,webrieflyreviewedsomeadditionaltoolsyoucaninstallforyourplatformtoaidindevelopment.

InChapter4,InstallingtheRequiredDependencies,we'llinstalltherequireddependenciesandtestoutthetoolchain.

Questions1. Whatisthenameofthepackagemanageryoushoulduseforyour

operatingsystem?2. DoesBitBucketsupportGit?3. Whyareweusingversion8ofNode.jsinsteadofthemostrecentversion?4. HowdoyouchangethecolorthemeinVisualStudioCode?5. HowdoyouaccesstheCommandPaletteinVisualStudioCode?6. HowdoyoucheckifyourbrowsersupportsWebAssembly?7. WhichofthetoolsintheOthertoolssectionissupportedonallthree

operatingsystems?

FurtherreadingHomebrew:https://brew.shaptdocumentation:https://help.ubuntu.com/lts/serverguide/apt.html.enChocolatey:https://chocolatey.orgGit:https://git-scm.comNode.js:https://nodejs.org/enGNUMake:https://www.gnu.org/software/makeVSCode:https://code.visualstudio.com

InstallingtheRequiredDependenciesNowthatyouhaveyourdevelopmentenvironmentsetupandyou'rereadytostartwritingC,C++,andJavaScript,it'stimetoaddthefinalpieceofthepuzzle.Inordertogenerate.wasmfilesfromourC/C++code,weneedtoinstallandconfiguretheEmscriptenSDK(EMSDK).

Inthischapter,we'lldiscussthedevelopmentworkflowandtalkabouthowtheEMSDKfitsintothedevelopmentprocess.DetailedinstructionswillbeprovidedonhowtoinstallandconfiguretheEMSDKoneachplatform,aswellasanyprerequisites.Oncetheinstallationandconfigurationprocessiscomplete,you'lltestitoutbywritingandcompilingsomeCcode.

Ourgoalforthischapteristounderstandthefollowing:

TheoveralldevelopmentworkflowwhenworkingwithWebAssemblyHowtheEMSDKrelatestoEmscriptenandWebAssemblyandwhyit'sneededHowtoinstalltheprerequisitesfortheEMSDKHowtoinstallandconfiguretheEMSDKHowtotesttheEMSDKtoensureit'sworkingcorrectly

ThedevelopmentworkflowThedevelopmentworkflowforWebAssemblyiscomparabletomostotherlanguagesthatrequirecompilationandabuildprocess.Beforegettingintothetoolingsetup,wewillcoverthedevelopmentcycle.Inthissection,wewillestablishsomecontextforthetoolingwewillinstallandconfigureintherestofthischapter.

StepsintheworkflowForthisbook,wewillwriteCandC++codeandcompileitdowntoaWasmmodule,buttheworkflowwillbeapplicabletoanyprogramminglanguagethatcompilesdowntoa.wasmfile.Thefollowingdiagramgivesanoverviewofthe

process:

Stepsinthedevelopmentworkflow

Thisprocesswillbeusedthroughoutthebookforourexamples,soyou'llgetanideaofhowtheprojectstructurecorrespondstotheworkflow.We'llusesomeofthetoolingavailabletoexpediteandsimplifytheprocess,butthestepswillstillbethesame.

IntegratingToolingintotheworkflowTherearemanyeditorsandtoolsavailabletosimplifythedevelopmentprocess.Fortunately,C/C++andJavaScripthavebeenaroundforquitesometime,soyoucantakeadvantageoftheoptionsthatsuityoubest.ThelistoftoolsforWebAssemblyisconsiderablyshorter,giventheshorterdurationofwhichthetechnologyhasexisted,buttheyareoutthere.

Theprimarytoolwe'lluse,VSCode,offerssomeexcellentandusefulfeaturesforsimplifyingthebuildanddevelopmentprocess.Inadditiontousingitforwritingourcode,we'llutilizeVSCode'sbuilt-inTasksfeaturetobuildthe.wasmfilefromC/C++.Bycreatinga.vscode/tasks.jsonfileintheprojectrootfolder,we'reabletospecifyalloftheparametersassociatedwiththebuildstepandrunitquicklyusingakeyboardshortcut.Inadditiontoperformingabuild,wecanstartandstoparunningNode.jsprocess(thatis,thelocalserverintheworkflowdiagram).We'llcoverhowtoaddandconfigurethesefeaturesinthenextchapter.

EmscriptenandtheEMSDKWe'lluseEmscriptentocompileourC/C++codedownto.wasmfiles.Uptothispoint,Emscriptenhasonlybrieflybeenmentionedinageneralcontext.Sincewe'llusethistoolandthecorrespondingEmscriptenSDK(EMSDK)inthebuildprocess,it'simportanttounderstandwhateachtechnologyisandthepartitplaysinthedevelopmentworkflow.Inthissection,we'lldescribeEmscripten'spurposeanddiscussitsrelationshiptotheEMSDK.

EmscriptenoverviewSowhatisEmscripten?Wikipediaprovidesthefollowingdefinition:

"Emscriptenisasource-to-sourcecompilerthatrunsasabackendtotheLLVMcompilerandproducesasubsetofJavaScriptknownasasm.js.ItcanalsoproduceWebAssembly."

Wediscussedsource-to-sourcecompilers(ortranspilers)inthefirstchapterandusedTypeScriptasanexample.Transpilersconvertsourcecodeinoneprogramminglanguagetoequivalentsourcecodeinanotherprogramminglanguage.ToelaborateonEmscriptenrunningasabackendtotheLLVMcompiler,weneedtoprovidesomeadditionaldetailsaboutLLVM.

TheofficialwebsiteforLLVM(https://llvm.org)definestheLLVMasacollectionofmodularandreusablecompilerandtoolchaintechnologies.Thereareseveralsub-projectsthatmakeupLLVM,butwe'llbefocusingonthetwothatEmscriptenutilizes:ClangandtheLLVMCorelibraries.Tounderstandhowthesepiecesfittogether,let'sreviewthedesignofathree-stagecompiler:

Designofageneralthree-stagecompiler

Theprocessisrelativelystraightforward:threeseparatestagesorendshandlethecompilationprocess.Thisdesignallowsfordifferentfrontendsandbackendsforvariousprogramminglanguagesandtargetarchitecturesandcompletelydecouplesthemachinecodefromthesourcecodebyusinganintermediaterepresentation.Nowlet'sassociateeachcompilationstagewithacomponentofthetoolchainwe'llusetogenerateWebAssembly:

Three-stagecompilationusingtheLLVM,Clang,andEmscripten

ClangisusedtocompileC/C++downtoLLVM'sIntermediateRepresentation(IR),whichEmscriptencompilestoaWasmmodule(binaryformat).ThetwodiagramsalsodemonstratetherelationshipbetweenWasmandmachinecode.YoucanthinkofWebAssemblyasaCPUinthebrowser,withWasmbeingthemachinecodeonwhichitruns.

WheredoestheEMSDKfitin?EmscriptenreferstothetoolchainusedtocompileCandC++downtoasm.jsorWebAssembly.TheEMSDKisusedtomanagethetoolsinthetoolchainandthecorrespondingconfiguration.Thiseliminatestheneedforcomplexenvironmentsetupandpreventsissueswithincompatibleversionsoftooling.ByinstallingtheEMSDK,wehaveallofthetoolingweneed(withtheexceptionoftheprerequisites)tousetheEmscriptencompiler.ThefollowingdiagramisavisualrepresentationoftheEmscriptentoolchain(withtheEMSDKshownindarkgray):

EmscriptenToolchain(modifiedslightlyfromemscripten.org)

NowthatyouhaveabetterunderstandingofEmscriptenandtheEMSDK,let'smoveontotheinstallationprocessfortheprerequisites.

InstallingtheprerequisitesBeforeinstallingandconfiguringtheEMSDK,we'llneedtoinstallsomeprerequisites.YouinstalledtwooftheprerequisitesinChapter3,SettingUpaDevelopmentEnvironment:Node.jsandGit.Eachplatformhasslightlydifferentinstallationprocessesandtoolingrequirements.Inthissection,wecovertheinstallationprocessfortheprerequisitetoolingforeachplatform.

CommonprerequisitesIt'spossiblethatyoualreadyhavealloftheprerequisitesinstalled.Herearethethreethatyou'llneedregardlessoftheplatform:

GitNode.jsPython2.7

NotethePythonversion;thisisimportantbecauseinstallingthewrongversioncouldcausetheinstallationprocesstofail.IfyoufollowedalonginChapter2,ElementsofWebAssembly-Wat,Wasm,andtheJavaScriptAPI,andinstalledNode.jsandGit,allthat'sleftistoinstallPython2.7andanyadditionalprerequisitesspecifiedforyourplatform.ThePythoninstallationprocessforeachplatformwillbespecifiedinthefollowingsubsections.

Pythonisahigh-levelprogramminglanguageusedforgeneral-purposeprogramming.Ifyou'dliketolearnmore,checkouttheofficialwebsiteathttps://www.python.org/.

InstallingtheprerequisitesonmacOSTherearethreeadditionaltoolsyou'llneedtoinstallpriortoinstallingtheEMSDK:

XcodeXcodeCommandLineToolsCMake

YoucaninstallXcodefromthemacOSAppStore.IfyoualreadyhadXcodeinstalled,youcancheckiftheCommandLineToolsareinstalledbygoingtoXcode|Preferences|LocationsandcheckingiftheCommandLineToolsoptionhasavalue.TheCommandLineToolsshouldhavealreadybeeninstalledifyouinstalledtheHomebrewpackagemanager:

CheckingthecurrentversionoftheXcodeCommandLineTools

Ifyoudon'tseethat,openupTerminalandrunthiscommand:

xcode-select--install

Oncecomplete,youcaninstallCMakebyrunningthiscommand:

brewinstallcmake

PriortoinstallingPython,runthiscommand:

python--version

IfyouseePython2.7.xx(wherexxisthepatchversionandcanbeanynumber),you'rereadytoinstalltheEMSDK.IfyougetanerrorsayingthePythoncommandwasn'tfoundoryouseePython3.x.xx,Irecommendyouinstallpyenv,aPythonVersionmanager.Toinstallpyenv,runthiscommand:

brewinstallpyenv

You'llneedtotakesomeadditionalconfigurationstepstofinalizetheinstallation.FollowtheinstallationinstructionsforHomebrewathttps://github.com/pyenv/pyenv#homebrew-on-mac-os-x.Afterinstallingandconfiguringpyenv,runthiscommandtoinstallPython2.7:

pyenvinstall2.7.15

Aftertheinstallationiscomplete,runthiscommand:

pyenvglobal2.7.15

Toensureyou'reusingthecorrectversionofPython,runthiscommand:

python--version

YoushouldseePython2.7.xx,wherexxisthepatchversion(Iwasseeing2.7.10,whichwillworkfine).

InstallingtheprerequisitesonUbuntuUbuntushouldalreadyhavePython2.7installed.Youcanconfirmthisbyrunningthiscommand:

python--version

IfyouseePython2.7.xx(wherexxisthepatchversionandcanbeanynumber),you'rereadytoinstalltheEMSDK.Ifyougetanerrorsayingthepythoncommandwasn'tfoundoryouseePython3.x.xx,Irecommendyouinstallpyenv,aPythonversionmanager.Beforeinstallingpyenv,checkifyouhavecurlinstalled.Youcandothisbyrunningthefollowingcommand:

curl--version

Ifyouseeaversionnumberandotherinformation,curlisinstalled.Ifnot,youcaninstallcurlbyrunningthefollowingcommand:

sudoapt-getinstallcurl

Oncethecurlinstallationiscomplete,runthiscommandtoinstallpyenv:

curl-Lhttps://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer|bash

Afterinstallingandconfiguringpyenv,runthiscommandtoinstallPython2.7:

pyenvinstall2.7.15

Ifyouencounterbuildissues,navigatetotheCommonbuildproblemspageathttps://github.com/pyenv/pyenv/wiki/common-build-problems.Aftertheinstallationiscomplete,runthiscommand:

pyenvglobal2.7.15

Toensureyou'reusingthecorrectversionofPython,runthiscommand:

python--version

YoushouldseePython2.7.xx,wherexxisthepatchversion(Iwasseeing2.7.10,whichwillworkfine).

InstallingtheprerequisitesonWindowsTheonlyadditionalprerequisiteforWindowsisPython2.7.Beforeattemptingtheinstallation,runthiscommand:python--version

IfyouseePython2.7.xx(wherexxisthepatchversionandcanbeanynumber),you'rereadytoinstalltheEMSDK.IfyougetanerrorsayingthePythoncommandwasn'tfound,oryouseePython3.x.xxandPython2.7isn'tinstalledonyoursystem,runthiscommandtoinstallPython2.7:chocoinstallpython2-y

IfyousawPython3.x.xxpriortoinstallingPython2.7,youshouldbeabletochangethecurrentPythonversionbyupdatingyourpath.BeforeattemptingtheEMSDKinstallation,runthiscommandtosetPythonto2.7:SETPATH=C:\Python27\python.exe

InstallingandconfiguringtheEMSDKIfyouhavealloftheprerequisitesinstalled,you'rereadytoinstalltheEMSDK.TheprocessforgettingtheEMSDKupandrunningisrelativelystraightforward.Inthissection,wecovertheinstallationprocessfortheEMSDKanddemonstratehowtoupdateyourVSCodeC/C++configurationtoaccommodateforEmscripten.

InstallationprocessacrossallplatformsFirst,selectafoldertoinstalltheEMSDK.Icreatedafolderat~/Tooling(orC:\Users\Mike\ToolingonWindows).Inaterminal,cdintothefolderyoujustcreatedandrunthiscommand:

gitclonehttps://github.com/juj/emsdk.git

Oncethecloneprocessiscomplete,followtheinstructionstocompletetheinstallationfromthesectionbelowthatcorrespondstoyourplatform.

InstallationonmacOSandUbuntuOncethecloneprocessiscomplete,runeachofthecommandsfromthefollowingcodesnippet.Ifyouseeamessagerecommendingthatyourungitpullinsteadof./emsdkupdate,usethegitpullcommandpriortorunningthe./emsdkinstalllatestcommand:#ChangedirectoryintotheEMSDKinstallationfoldercdemsdk

#Fetchthelatestregistryofavailabletools./emsdkupdate

#DownloadandinstallthelatestSDKtools./emsdkinstalllatest

#MakethelatestSDKactiveforthecurrentuser(writes~/.emscriptenfile)./emsdkactivatelatest

#ActivatePATHandotherenvironmentvariablesinthecurrentTerminalsource./emsdk_env.sh

Thesource./emsdk_env.shcommandwillactivatetheenvironmentvariablesinthecurrentTerminal,whichmeanseverytimeyoucreateanewTerminalinstance,you'dhavetore-runit.Topreventhavingtotakethisstep,youcanaddthefollowinglinetoyourBashorZshconfigurationfile(thatis,~/.bash_profileor~/.zshrc):source~/Tooling/emsdk/emsdk_env.sh>/dev/null

IfyouinstalledtheEMSDKinadifferentlocation,makesurethatyouupdatethepathtoreflectthis.AddingthislinetoyourconfigurationfilewillrunthatenvironmentupdatecommandautomaticallysoyoucanstartusingtheEMSDKimmediately.ToensureyoucanusetheEmscriptencompiler,runthiscommand:emcc--version

Ifyouseeamessagewithversioninformation,thesetupwassuccessful.Ifyouseeanerrormessagestatingthatthecommandwasnotfound,double-checkyourconfiguration.Youmayhavespecifiedaninvalidpathfortheemsdk_env.shin

yourBashorZshconfigurationfile.

InstallationandconfigurationonWindowsBeforecompletingtheinstallation,IrecommendyouusePowerShellgoingforward.TheexamplesinthisbookwillbeusingPowerShellinsidecmder.Oncethecloneprocessiscomplete,runeachofthecommandsgiveninthefollowingcodesnippet.Ifyouseeamessagerecommendingthatyourungitpullinsteadof./emsdkupdate,usethegitpullcommandpriortorunningthe./emsdkinstalllatestcommand:

#ChangedirectoryintotheEMSDKinstallationfolder

cdemsdk

#Fetchthelatestregistryofavailabletools

.\emsdkupdate

#DownloadandinstallthelatestSDKtools

.\emsdkinstalllatest

#MakethelatestSDKactiveforthecurrentuser(writes~/.emscriptenfile)

.\emsdkactivate--globallatest

The--globalflaginthe.\emsdkactivatecommandallowsyoutorunemccwithouthavingtorunascripttosettheenvironmentvariableseachsession.ToensureyoucanusetheEmscriptencompiler,restartyourCLIandrunthiscommand:

emcc--version

Ifyouseeamessagewithversioninformation,thesetupwassuccessful.

ConfigurationinVSCodeIfyouhaven'talreadydoneso,createafolderthatwillcontainthecodesampleswe'llbeworkingthrough(theexamplesusethenamebook-examples).OpenthisfolderinVSCode,presstheF1key,andselectC/Cpp:EditConfigurations…tocreatea.vscode/c_cpp_properties.jsonfileintherootofyourproject.Itshouldopenthefileautomatically.Addthefollowinglinetothebrowse.patharray:"${env:EMSCRIPTEN}/system/include".Thiswillpreventerrorsbeingthrownifyouincludetheemscripten.hheader.Youmayneedtomanuallycreatethebrowseobjectwithapathentryifitdidn'tgenerateoneautomatically.ThefollowingsnippetrepresentstheupdatedconfigurationfileonUbuntu:

{

"name":"Linux",

"includePath":[

"/usr/include",

"/usr/local/include",

"${workspaceFolder}",

"${env:EMSCRIPTEN}/system/include"

],

"defines":[],

"intelliSenseMode":"clang-x64",

"browse":{

"path":[

"/usr/include",

"/usr/local/include",

"${workspaceFolder}"

],

"limitSymbolsToIncludedHeaders":true,

"databaseFilename":""

}

}

TestingthecompilerAfterinstallingandconfiguringtheEMSDK,you'llneedtotestittoensureyou'reabletogenerateWasmmodulesfromC/C++code.Theeasiestwaytotestitistocompilesomecodeusingtheemcccommandandtryrunningitinabrowser.Inthissection,we'llvalidatetheEMSDKinstallationbywritingandcompilingsomesimpleCcodeandevaluatingtheWatassociatedwiththe.wasmoutput.

TheCcode

We'llusesomeverysimpleCcodetotestourcompilerinstallation.Wewon'tneedtoimportanyheadersorexternallibraries.Wewon'tuseC++forthistestbecauseweneedtoperformanextrastepwithC++topreventnamemangling,whichwe'lldescribeingreaterdetailinChapter6,InteractingwithJavaScriptandDebugging.Thecodeforthissectionislocatedinthe/chapter-04-installing-depsfolderofthelearn-webassemblyrepository.FollowtheinstructionslistedheretotestouttheEMSDK.

Createasubfoldernamed/chapter-04-installing-depsinyour/book-examplesfolder.Next,createanewfileinthisfoldernamedmain.candpopulateitwiththefollowingcontents:

intaddTwoNumbers(intleftValue,intrightValue){

returnleftValue+rightValue;

}

CompilingtheCcodeInordertocompileaC/C++filewithEmscripten,we'llusetheemcccommand.Weneedtopasssomeargumentstothecompilertoensurewegetavalidoutputthatwecanutilizeinthebrowser.TogenerateaWasmfilefromaC/C++file,thecommandfollowsthisformat:

emcc<file.c>-Os-sWASM=1-sSIDE_MODULE=1-sBINARYEN_ASYNC_COMPILATION=0-o<file.wasm>

Here'sabreakdownofeachoftheargumentsfortheemcccommand:

Argument Description

<file.c>

PathoftheCorC++inputfilethatwillbecompileddowntoaWasmmodule;we'llreplacethiswiththeactualfilepathwhenwerunthecommand.

-Os

Compileroptimizationlevel.ThisoptimizationflagallowsformoduleinstantiationwithoutrequiringEmscripten'sgluecode.

-sWASM=1TellsthecompilertocompilecodetoWebAssembly.

-sSIDE_MODULE=1EnsuresonlyaWebAssemblymoduleisoutput(nogluecode).

-s

BINARYEN_ASYNC_COMPILATION=0

Fromofficialdocs:

Whethertocompilethewasmasynchronously,whichismoreefficientanddoesnotblockthemainthread.ThisiscurrentlyrequiredforallbutthesmallestmodulestoruninV8.

-o<file.wasm>

Pathofoutputfile.wasmfile.We'llreplacethiswiththedesiredoutputpathwhenwerunthecommand.

TotestifEmscriptenisworkingcorrectly,opentheintegratedterminalinVSCodeandrunthefollowingcommands:

#Ensureyou'reinthe/chapter-04-installing-depsfolder:

cdchapter-04-installing-deps

#Compilethemain.cfiletomain.wasm:

emccmain.c-Os-sWASM=1-sSIDE_MODULE=1-sBINARYEN_ASYNC_COMPILATION=0-omain.wasm

Itmaytakeaminutetocompilethefilethefirsttime,butsubsequentbuildswillbemuchfaster.Ifthecompilationwassuccessful,youshouldseeamain.wasmfileinthe/chapter-04-installing-depsfolder.Ifyouencounteranerror,Emscripten'serrormessageshouldbedescriptiveenoughtohelpyoucorrecttheissue.

Ifeverythingcompletedsuccessfully,youcanviewtheWatassociatedwiththemain.wasmfilebyright-clickingmain.wasminVSCode'sfileexplorerandselectingShowWebAssemblyfromthecontextmenu.Theoutputshouldlooklikethis:

(module

(type$t0(func(parami32)))

(type$t1(func(parami32i32)(resulti32)))

(type$t2(func))

(type$t3(func(resultf64)))

(import"env""table"(table$env.table2anyfunc))

(import"env""memoryBase"(global$env.memoryBasei32))

(import"env""tableBase"(global$env.tableBasei32))

(import"env""abort"(func$env.abort(type$t0)))

(func$_addTwoNumbers(type$t1)(param$p0i32)(param$p1i32)(resulti32)

get_local$p1

get_local$p0

i32.add)

(func$runPostSets(type$t2)

nop)

(func$__post_instantiate(type$t2)

get_global$env.memoryBase

set_global$g2

get_global$g2

i32.const5242880

i32.add

set_global$g3)

(func$f4(type$t3)(resultf64)

i32.const0

call$env.abort

f64.const0x0p+0(;=0;))

(global$g2(muti32)(i32.const0))

(global$g3(muti32)(i32.const0))

(global$fp$_addTwoNumbersi32(i32.const1))

(export"__post_instantiate"(func$__post_instantiate))

(export"_addTwoNumbers"(func$_addTwoNumbers))

(export"runPostSets"(func$runPostSets))

(export"fp$_addTwoNumbers"(global4))

(elem(get_global$env.tableBase)$f4$_addTwoNumbers))

Ifthecompilerransuccessfully,you'rereadytomoveontothenextstepandwriteJavaScriptcodetointeractwiththemodule,whichwe'llcoverinthenextchapter.

SummaryInthischapter,wecoveredtheoveralldevelopmentworkflowwhenworkingwithWebAssembly.Inordertogenerateour.wasmfiles,we'reusingEmscripten,whichrequirestheinstallationoftheEMSDK.Priortoreviewinganyinstallationdetails,wediscussedthetechnologiesunderthehoodanddescribedhowtheyrelatetoeachotherandtoWebAssembly.WecoveredeachofthestepsrequiredtogetEMDSKworkinglocallyonyourcomputer.TheinstallationprocessfortheEMSDKoneachplatformwaspresented,aswellastheinstallationandconfigurationinstructionsfortheEMSDK.AfterinstallingtheEMSDK,wetestedthecompiler(noto).Thatwastheemcccommandweranintheprevioussection.UsingtheemcccommandonasimpleCcodefiletoensureEmscriptenwasworkingcorrectly.Inthenextchapter,we'llwalkthroughtheprocessofcreatingandloadingyourfirstmodule!

Questions1. Whatarethefivestepsinthedevelopmentworkflow?2. WhichstageorenddoesEmscriptenrepresentinthecompilationprocess?3. WhatdoesIRstandfor(LLVM'soutput)?4. WhatroledoestheEMSDKplaywithregardtoEmscripten?5. WhichEMSDKprerequisitesarerequiredonallthreeplatforms(macOS,

Windows,andLinux)?6. Whydoyouneedtoruntheemsdk_envscriptbeforeyoucanusethe

Emscriptencompiler?7. Whydoyouneedtoaddthe"${env:EMSCRIPTEN}/system/include"pathtothe

C/Cppconfigurationfile?8. WhatisthecommandusedtocompileC/C++downtoWasmmodules?9. Whatdoesthe-Oscompilerflagrepresent?

FurtherreadingEmscripten:http://emscripten.orgTheLLVMCompilerInfrastructureProject:https://llvm.orgC++programmingwithVisualStudioCode:https://code.visualstudio.com/docs/languages/cpp

CreatingandLoadingaWebAssemblyModuleTheflagswepassedtotheemcccommandinChapter4,InstallingtheRequiredDependencies,producedasingle.wasmfilethatcouldbeloadedandinstantiatedinthebrowserusingthenativeWebAssemblyobject.TheCcodewasaverysimpleexampleintendedtotestthecompilerwithouthavingtoaccommodateforincludedlibrariesorWebAssembly'slimitations.WecanovercomesomeofthelimitationsofWebAssemblyinourC/C++codewithminimalperformancelossbyutilizingsomeofEmscripten'scapabilities.

Inthischapter,we'llcoverthecompilationandloadingstepsthatcorrespondwiththeuseofEmscripten'sgluecode.We'llalsodescribetheprocessforcompiling/outputtingstrictly.wasmfilesandloadingthemusingthebrowser'sWebAssemblyobject.

Ourgoalforthischapteristounderstandthefollowing:

ThecompilationprocessforCcodethatutilizesEmscripten'sJavaScript"glue"codeHowtoloadanEmscriptenmoduleinthebrowserThecompilationprocessforCcodethatoutputsonly.wasmfiles(no"glue"code)HowtoconfigurebuildtasksinVSCodeHowtocompileandloadaWasmmoduleinthebrowserusingtheglobalWebAssemblyobject

CompilingCwithEmscriptengluecodeInChapter4,InstallingtheRequiredDependencies,youwroteandcompiledasimplethree-lineprogramtoensureyourEmscripteninstallationwasvalid.Wepassedseveralflagstotheemcccommandthatwererequiredtoonlyoutputasingle.wasmfile.Bypassingotherflagstotheemcccommand,wecanoutputJavaScriptgluecodealongsidethe.wasmfileaswellasanHTMLfiletohandletheloadingprocess.Inthissection,we'regoingtowriteamorecomplexCprogramandcompileitwiththeoutputoptionsthatEmscriptenoffers.

WritingtheexampleCcodeWedidn'tincludeanyheaderfilesorpassinanyfunctionsintheexamplewecoveredinChapter4,InstallingtheRequiredDependencies.Sincetheintentionofthecodewassolelytotestifthecompilerinstallationwasvalid,therewasn'tmuchneed.EmscriptenoffersalotofextrafunctionalitythatenablesustointeractwithourCandC++codewithJavaScriptandviceversa.SomeofthesecapabilitiesareEmscripten-specificanddon'tcorrespondtotheCoreSpecificationoritsAPIs.Inourfirstexample,we'lltakeadvantageofoneofEmscripten'sportedlibrariesandafunctionprovidedbyEmscripten'sAPI.

ThefollowingprogramusesaSimpleDirectMediaLayer(SDL2)tomovearectanglediagonallyacrossacanvasinaninfiniteloop.Itwastakenfromhttps://github.com/timhutton/sdl-canvas-wasm,butIconverteditfromC++toCandmodifiedthecodeslightly.Thecodeforthissectionislocatedinthe/chapter-05-create-load-modulefolderofthelearn-webassemblyrepository.FollowthefollowinginstructionstocompileCwithEmscripten.

Createafolderinyour/book-examplesfoldernamed/chapter-05-create-load-module.Createanewfileinthisfoldernamedwith-glue.candpopulateitwiththefollowingcontents:

/*

*ConvertedtoCcodetakenfrom:

*https://github.com/timhutton/sdl-canvas-wasm

*Someofthevariablenamesandcommentswerealso

*slightlyupdated.

*/

#include<SDL2/SDL.h>

#include<emscripten.h>

#include<stdlib.h>

//Thisenablesustohaveasinglepointofreference

//forthecurrentiterationandrenderer,ratherthan

//havetorefertothemseparately.

typedefstructContext{

SDL_Renderer*renderer;

intiteration;

}Context;

/*

*Loopingfunctionthatdrawsabluesquareonared

*backgroundandmovesitacrossthe<canvas>.

*/

voidmainloop(void*arg){

Context*ctx=(Context*)arg;

SDL_Renderer*renderer=ctx->renderer;

intiteration=ctx->iteration;

//Thissetsthebackgroundcolortored:

SDL_SetRenderDrawColor(renderer,255,0,0,255);

SDL_RenderClear(renderer);

//Thiscreatesthemovingbluesquare,therect.x

//andrect.yvaluesupdatewitheachiterationtomove

//1pxatatime,sothesquarewillmovedownand

//totherightinfinitely:

SDL_Rectrect;

rect.x=iteration;

rect.y=iteration;

rect.w=50;

rect.h=50;

SDL_SetRenderDrawColor(renderer,0,0,255,255);

SDL_RenderFillRect(renderer,&rect);

SDL_RenderPresent(renderer);

//Thisresetsthecounterto0assoonastheiteration

//hitsthemaximumcanvasdimension(otherwiseyou'd

//neverseethebluesquareafterittravelledacross

//thecanvasonce).

if(iteration==255){

ctx->iteration=0;

}else{

ctx->iteration++;

}

}

intmain(){

SDL_Init(SDL_INIT_VIDEO);

SDL_Window*window;

SDL_Renderer*renderer;

//Thefirsttwo255valuesrepresentthesizeofthe<canvas>

//elementinpixels.

SDL_CreateWindowAndRenderer(255,255,0,&window,&renderer);

Contextctx;

ctx.renderer=renderer;

ctx.iteration=0;

//Callthefunctionrepeatedly:

intinfinite_loop=1;

//Callthefunctionasfastasthebrowserwantstorender

//(typically60fps):

intfps=-1;

//Thisisafunctionfromemscripten.h,itsetsaCfunction

//asthemaineventloopforthecallingthread:

emscripten_set_main_loop_arg(mainloop,&ctx,fps,infinite_loop);

SDL_DestroyRenderer(renderer);

SDL_DestroyWindow(window);

SDL_Quit();

returnEXIT_SUCCESS;

}

Theemscripten_set_main_loop_arg()towardtheendofthemain()functionisavailablebecauseweincludedemscripten.hatthetopofthefile.ThevariablesandfunctionsprefixedwithSDL_areavailablebecauseofthe#include<SDL2/SDL.h>atthetopofthefile.Ifyou'reseeingasquigglyrederrorlineunderthe<SDL2/SDL.h>statement,youcandisregardit.It'sduetoSDL'sincludepathnotbeingpresentinyourc_cpp_properties.jsonfile.

CompilingtheexampleCcodeNowthatwehaveourCcodewritten,we'llneedtocompileit.Oneoftherequiredflagsyoumustpasstotheemcccommandis-o<target>,where<target>isthepathtothedesiredoutputfile.Theextensionofthatfilewilldomorethanjustoutputthatfile;itimpactssomeofthedecisionsthecompilermakes.Thefollowingtable,takenfromEmscripten'semccdocumentationathttp://kripken.github.io/emscripten-site/docs/tools_reference/emcc.html#emcc-o-target,definesthegeneratedoutputtypesbasedonthefileextensionspecified:

Extension Output

<name>.js JavaScriptgluecode(and.wasmifthesWASM=1flagisspecified).

<name>.htmlHTMLandseparateJavaScriptfile(<name>.js).HavingtheseparateJavaScriptfileimprovespageloadtime.

<name>.bc LLVMbitcode(default).

<name>.o LLVMbitcode(sameas.bc).

<name>.wasmWasmfileonly(withflagsspecifiedfromChapter4,InstallingtheRequiredDependencies).

Youcandisregardthe.bcand.ofileextensions—wewon'tneedtooutputLLVMbitcode.The.wasmextensionisn'tontheemccToolsReferencepage,butitisavalidoptionifyoupassthecorrectcompilerflags.TheseoutputoptionsfactorintotheC/C++codewewrite.

OutputtingHTMLwithgluecodeIfyouspecifyanHTMLfileextension(forexample,-owith-glue.html)fortheoutput,you'llendupwithawith-glue.html,with-glue.js,andwith-glue.wasmfile(assumingyoualsospecified-sWASM=1).Ifyouhaveamain()functioninthesourceC/C++file,it'llexecutethatfunctionassoonastheHTMLloads.Let'scompileourexampleCcodetoseethisinaction.TocompileitwiththeHTMLfileandJavaScriptgluecode,cdintothe/chapter-05-create-load-modulefolderandrunthefollowingcommand:

emccwith-glue.c-O3-sWASM=1-sUSE_SDL=2-owith-glue.html

Thefirsttimeyourunthiscommand,EmscriptenisgoingtodownloadandbuildtheSDL2library.Itcouldtakeseveralminutestocompletethis,butyou'llonlyneedtowaitonce.Emscriptencachesthelibrarysosubsequentbuildswillbemuchfaster.Oncethebuildiscomplete,you'llseethreenewfilesinthefolder:HTML,JavaScript,andWasmfiles.Runthefollowingcommandtoservethefilelocally:

serve-l8080

Ifyouopenyourbrowseruptohttp://127.0.0.1:8080/with-glue.html,youshouldseethefollowing:

Emscriptenloadingcoderunninginthebrowser

Thebluerectangleshouldbemovingdiagonallyfromtheupper-leftcorneroftheredrectangletothelower-right.Sinceyouspecifiedamain()functionintheCfile,Emscriptenknowsitshouldexecuteitrightaway.Ifyouopenupthewith-glue.htmlfileinVScodeandscrolltothebottomofthefile,youwillseetheloadingcode.Youwon'tseeanyreferencestotheWebAssemblyobject;that'sbeinghandledintheJavaScriptgluecodefile.

OutputtinggluecodewithnoHTMLTheloadingcodethatEmscriptengeneratesintheHTMLfilecontainserrorhandlingandotherhelpfulfunctionstoensurethemoduleisloadingbeforeexecutingthemain()function.Ifyouspecify.jsfortheextensionoftheoutputfile,you'llhavetocreateanHTMLfileandwritetheloadingcodeyourself.Inthenextsection,we'regoingtodigintotheloadingcodeinmoredetail.

LoadingtheEmscriptenmoduleLoadingandinteractingwithamodulethatutilizesEmscripten'sgluecodeisconsiderablydifferentfromWebAssembly'sJavaScriptAPI.ThisisbecauseEmscriptenprovidesadditionalfunctionalityforinteractingwiththeJavaScriptcode.Inthissection,we'regoingtodiscusstheloadingcodethatEmscriptenprovideswhenoutputtinganHTMLfileandreviewtheprocessforloadinganEmscriptenmoduleinthebrowser.

Pre-generatedloadingcodeIfyouspecify-o<target>.htmlwhenrunningtheemcccommand,EmscriptengeneratesanHTMLfileandautomaticallyaddscodetoloadthemoduletotheendofthefile.Here'swhattheloadingcodeintheHTMLfilelookslikewiththecontentsofeachModulefunctionexcluded:

varstatusElement=document.getElementById('status');

varprogressElement=document.getElementById('progress');

varspinnerElement=document.getElementById('spinner');

varModule={

preRun:[],

postRun:[],

print:(function(){...})(),

printErr:function(text){...},

canvas:(function(){...})(),

setStatus:function(text){...},

totalDependencies:0,

monitorRunDependencies:function(left){...}

};

Module.setStatus('Downloading...');

window.onerror=function(event){

Module.setStatus('Exceptionthrown,seeJavaScriptconsole');

spinnerElement.style.display='none';

Module.setStatus=function(text){

if(text)Module.printErr('[post-exceptionstatus]'+text);

};

};

ThefunctionswithintheModuleobjectarepresenttodetectandaddresserrors,monitortheloadingstatusoftheModule,andoptionallyexecutesomefunctionsbeforeoraftertherun()methodfromthecorrespondinggluecodefileexecutes.Thecanvasfunction,showninthefollowingsnippet,returnsthe<canvas>elementfromtheDOMthatwasspecifiedintheHTMLfilebeforetheloadingcode:

canvas:(function(){

varcanvas=document.getElementById('canvas');

canvas.addEventListener(

'webglcontextlost',

function(e){

alert('WebGLcontextlost.Youwillneedtoreloadthepage.');

e.preventDefault();

},

false

);

returncanvas;

})(),

ThiscodeisconvenientfordetectingerrorsandensuringtheModuleisloaded,butforourpurposes,wewon'tneedtobeasverbose.

WritingcustomloadingcodeEmscripten'sgeneratedloadingcodeprovideshelpfulerrorhandling.Ifyou'reusingEmscripten'soutputinproduction,Iwouldrecommendthatyouincludeittoensureyou'rehandlingerrorscorrectly.However,wedon'tactuallyneedallthecodetoutilizeourModule.Let'swritesomemuchsimplercodeandtestitout.First,let'scompileourCfiledowntogluecodewithnoHTMLoutput.Todothat,runthefollowingcommand:emccwith-glue.c-O3-sWASM=1-sUSE_SDL=2-sMODULARIZE=1-ocustom-loading.js

The-sMODULARIZE=1compilerflagallowsustouseaPromise-likeAPItoloadourModule.Oncethecompilationiscomplete,createafileinthe/chapter-05-create-load-modulefoldernamedcustom-loading.htmlandpopulateitwiththefollowingcontents:<!doctypehtml><htmllang="en-us"><head><title>CustomLoadingCode</title></head><body><h1>UsingCustomLoadingCode</h1><canvasid="canvas"></canvas><scripttype="application/javascript"src="custom-loading.js"></script><scripttype="application/javascript">Module({canvas:(()=>document.getElementById('canvas'))(),}).then(()=>{console.log('Loaded!');});</script></body></html>

TheloadingcodeisnowusingES6'sarrowfunctionsyntaxforthecanvasloadingfunction,whichreducesthelinesofcoderequired.Startyourlocal

serverbyrunningtheservecommandwithinthe/chapter-05-create-load-modulefolder:serve-l8080

Whenyounavigatetohttp://127.0.0.1:8080/custom-loading.htmlinyourbrowser,youshouldseethis:

Customloadingcoderunninginthebrowser

Ofcourse,thefunctionwe'rerunningisn'tverycomplex,butitdemonstratesthebare-bonesrequirementsforloadingEmscripten'sModule.WewillexaminetheModuleobjectinmuchgreaterdetailinChapter6,InteractingwithJavaScriptandDebugging,butfornowjustbeawarethattheloadingprocessisdifferentfromWebAssembly,whichwe'llcoverinthenextsection.

CompilingCwithoutthegluecodeIfwewanttouseWebAssemblyaccordingtotheofficialspecification,withouttheextrafeaturesthatEmscriptenprovides,weneedtopasssomeflagstotheemcccommandandensurewe'rewritingcodethatcanbeusedbyWebAssemblywithrelativeease.IntheWritingtheexampleCcodesection,wewroteaprogramthatrenderedabluerectanglethatmoveddiagonallyacrossaredcanvas.ItutilizedoneofEmscripten'sportedlibraries,SDL2.Inthissection,we'regoingtowriteandcompilesomeCcodethatdoesn'trelyonEmscripten'shelpermethodsandportedlibraries.

CcodeforWebAssemblyBeforewegettotheCcodewe'lluseforourWebAssemblymodule,let'stryanexperiment.OpentheCLIinthe/chapter-05-create-load-modulefolder,andtryrunningthiscommand:

emccwith-glue.c-Os-sWASM=1-sUSE_SDL=2-sSIDE_MODULE=1-sBINARYEN_ASYNC_COMPILATION=0-otry-with-glue.wasm

Youshouldseeatry-with-glue.wasmfileappearinVSCode'sfileexplorerpanelafterthecompilationiscomplete.Right-clickonthefileandselectShowWebAssembly.ThebeginningofthecorrespondingWatrepresentationshouldresemblethefollowingcode:

(module

(type$t0(func(parami32)))

(type$t1(func(parami32i32i32i32i32)(resulti32)))

(type$t2(func(parami32)(resulti32)))

(type$t3(func))

(type$t4(func(parami32i32)(resulti32)))

(type$t5(func(parami32i32i32i32)))

(type$t6(func(resulti32)))

(type$t7(func(resultf64)))

(import"env""memory"(memory$env.memory256))

(import"env""table"(table$env.table4anyfunc))

(import"env""memoryBase"(global$env.memoryBasei32))

(import"env""tableBase"(global$env.tableBasei32))

(import"env""abort"(func$env.abort(type$t0)))

(import"env""_SDL_CreateWindowAndRenderer"(func$env._SDL_CreateWindowAndRenderer(type$t1)))

(import"env""_SDL_DestroyRenderer"(func$env._SDL_DestroyRenderer(type$t0)))

(import"env""_SDL_DestroyWindow"(func$env._SDL_DestroyWindow(type$t0)))

(import"env""_SDL_Init"(func$env._SDL_Init(type$t2)))

(import"env""_SDL_Quit"(func$env._SDL_Quit(type$t3)))

(import"env""_SDL_RenderClear"(func$env._SDL_RenderClear(type$t2)))

(import"env""_SDL_RenderFillRect"(func$env._SDL_RenderFillRect(type$t4)))

(import"env""_SDL_RenderPresent"(func$env._SDL_RenderPresent(type$t0)))

(import"env""_SDL_SetRenderDrawColor"(func$env._SDL_SetRenderDrawColor(type$t1)))

(import"env""_emscripten_set_main_loop_arg"(func$env._emscripten_set_main_loop_arg(type$t5)))

...

Ifyouwantedtoloadthisinabrowserandexecuteit,you'dhavetopassinanimportObjobjecttoWebAssembly'sinstantiate()orcompile()functionwithanenvobjectcontainingeachofthoseimport"env"functions.Emscriptenhandlesallofthisforusbehindthesceneswiththegluecode,whichmakesitanincrediblyvaluabletool.However,wecanreplacetheSDL2functionalitybyusingthe

DOMwhilestilltrackingtherectangle'slocationinC.

WewillwritetheCcodedifferentlytoensureweonlyhavetopassafewfunctionsintotheimportObj.envobjecttoexecutethecode.Createafilenamedwithout-glue.cinthe/chapter-05-create-load-modulefolderandpopulateitwiththefollowingcontents:

/*

*Thisfileinteractswiththecanvasthroughimportedfunctions.

*Itmovesabluerectanglediagonallyacrossthecanvas

*(mimicstheSDLexample).

*/

#include<stdbool.h>

#defineBOUNDS255

#defineRECT_SIDE50

#defineBOUNCE_POINT(BOUNDS-RECT_SIDE)

//ThesefunctionsarepassedinthroughtheimportObj.envobject

//andupdatetherectangleonthe<canvas>:

externintjsClearRect();

externintjsFillRect(intx,inty,intwidth,intheight);

boolisRunning=true;

typedefstructRect{

intx;

inty;

chardirection;

}Rect;

structRectrect;

/*

*Updatestherectanglelocationby1pxinthexandyina

*directionbasedonitscurrentposition.

*/

voidupdateRectLocation(){

//Sincewewanttherectangleto"bump"intotheedgeofthe

//canvas,weneedtodeterminewhentherightedgeofthe

//rectangleencounterstheboundsofthecanvas,whichiswhy

//we'reusingthecanvaswidth-rectanglewidth:

if(rect.x==BOUNCE_POINT)rect.direction='L';

//Assoonastherectangle"bumps"intotheleftsideofthe

//canvas,itshouldchangedirectionagain.

if(rect.x==0)rect.direction='R';

//Ifthedirectionhaschangedbasedonthexandy

//coordinates,ensurethexandypointsupdate

//accordingly:

intincrementer=1;

if(rect.direction=='L')incrementer=-1;

rect.x=rect.x+incrementer;

rect.y=rect.y+incrementer;

}

/*

*Cleartheexistingrectangleelementfromthecanvasanddrawa

*newoneintheupdatedlocation.

*/

voidmoveRect(){

jsClearRect();

updateRectLocation();

jsFillRect(rect.x,rect.y,RECT_SIDE,RECT_SIDE);

}

boolgetIsRunning(){

returnisRunning;

}

voidsetIsRunning(boolnewIsRunning){

isRunning=newIsRunning;

}

voidinit(){

rect.x=0;

rect.y=0;

rect.direction='R';

setIsRunning(true);

}

WewillcallthefunctionsfromtheCcodetodeterminethexandycoordinates.ThesetIsRunning()functioncanbeusedtopausetherectangle'smovement.NowthatourCcodeisready,let'scompileit.IntheVSCodeterminal,cdintothe/chapter-05-create-load-modulefolder,andrunthefollowingcommand:

emccwithout-glue.c-Os-sWASM=1-sSIDE_MODULE=1-sBINARYEN_ASYNC_COMPILATION=0-owithout-glue.wasm

Oncethecompilationiscomplete,youcanright-clickontheresultantwithout-glue.wasmfileandselectShowWebAssemblytoseetheWatrepresentation.Youshouldseethefollowingatthetopofthefilefortheimport"env"items:

(module

(type$t0(func(parami32)))

(type$t1(func(resulti32)))

(type$t2(func(parami32i32i32i32)(resulti32)))

(type$t3(func))

(type$t4(func(resultf64)))

(import"env""memory"(memory$env.memory256))

(import"env""table"(table$env.table8anyfunc))

(import"env""memoryBase"(global$env.memoryBasei32))

(import"env""tableBase"(global$env.tableBasei32))

(import"env""abort"(func$env.abort(type$t0)))

(import"env""_jsClearRect"(func$env._jsClearRect(type$t1)))

(import"env""_jsFillRect"(func$env._jsFillRect(type$t2)))

...

Weneedtopassinthe_jsClearRectand_jsFillRectfunctionswithintheimportObjobject.We'llcoverhowtodothatinthesectionontheHTMLfilewith

JavaScriptinteractioncode.

CompilingwithaBuildTaskinVSCodeTheemcccommandisalittleverbose,andhavingtomanuallyrunthisonthecommandlinefordifferentfilescangetcumbersome.Toexpeditethecompilationprocess,wecanuseVSCode'sTasksfeaturetocreateabuildtaskforthefileswe'lluse.Tocreateabuildtask,selectTasks|ConfigureDefaultBuildTask…,selecttheCreatetasks.jsonfromtemplateoption,andselectOtherstogenerateasimpletasks.jsonfileinthe.vscodefolder.Updatethecontentsofthefiletocontainthefollowing:{//Seehttps://go.microsoft.com/fwlink/?LinkId=733558//forthedocumentationaboutthetasks.jsonformat"version":"2.0.0","tasks":[{"label":"Build","type":"shell","command":"emcc","args":["${file}","-Os","-s","WASM=1","-s","SIDE_MODULE=1","-s","BINARYEN_ASYNC_COMPILATION=0","-o","${fileDirname}/${fileBasenameNoExtension}.wasm"],"group":{"kind":"build","isDefault":true},"presentation":{"panel":"new"}}

]}

Thelabelvalueissimplyanametorefertowhenrunningatask.Thetypeandcommandvaluesindicatethatitshouldruntheemcccommandinashell(terminal).Theargsvalueisanarrayofargumentstobepassedtotheemcccommand(basedonspaceseparation).The"${file}"argumenttellsVSCodetocompilethecurrentlyopenfile.The"${fileDirname}/${fileBasenameNoExtension}.wasm"argumentindicatesthatthe.wasmoutputwillhavethesamenameasthecurrentlyopenfile(witha.wasmextension),anditshouldbeplacedintheactivefolderofthecurrentlyopenfile.Ifyoudon'tspecify${fileDirname},theoutputfilewillbeplacedintherootfolder(ratherthan/chapter-05-create-load-moduleinthiscase).

Thegroupobjectindicatesthatthistaskisthedefaultbuildstep,soifyouusethekeyboardshortcutCmd/Ctrl+Shift+B,thisisthetaskthatwillberun.Thepresentation.panelvalueof"new"tellsVSCodetoopenupanewCLIinstancewhenthebuildstepruns.Thisisapersonalpreferenceandcanbeomitted.

Youcansaveandclosethetasks.jsonfileonceit'sfullypopulated.Totestitout,firstdeletethewithout-glue.wasmfilethatyougeneratedwiththeemcccommandintheprevioussection.Next,ensureyouhavewithout-glue.copenwiththecursorinthefileandrunthebuildtaskbyeitherselectingTasks|RunBuildTask…orusingthekeyboardshortcutCmd/Ctrl+Shift+B.Anewpanelintheintegratedterminalwillperformthecompilationandawithout-glue.wasmfileshouldappearafterasecondortwo.

FetchingandinstantiatingaWasmfileNowthatwehaveaWasmfile,we'llneedsomeJavaScriptcodetocompileandexecuteit.There'safewstepswe'llhavetofollowtoensurethecodecanbesuccessfullyutilizedinthebrowser.Inthissection,wewillwritesomecommonJavaScriptloadingcodethatwecanreuseforotherexamples,createanHTMLfilethatdemonstratestheuseoftheWasmmodule,andtesttheresultsinthebrowser.

CommonJavaScriptloadingcodeWewillfetchandinstantiatea.wasmfileinseveraloftheexamples,soitmakessensetomovetheJavaScriptloadingcodetoacommonfile.Theactualfetchandinstantiationcodeisonlyafewlines,buthavingtorepeatedlyredefinetheimportObjobjectthatEmscriptenexpectsisawasteoftime.We'llmakethiscodeavailableinacommonlyaccessiblefiletoexpeditethecode-writingprocess.Createanewfoldernamed/commoninthe/book-examplesfolderandaddafilenamedload-wasm.jswiththefollowingcontents:/***ReturnsavalidimportObj.envobjectwithdefaultvaluestopass*intotheWebAssembly.InstanceconstructorforEmscripten's*Wasmmodule.*/constgetDefaultEnv=()=>({memoryBase:0,tableBase:0,memory:newWebAssembly.Memory({initial:256}),table:newWebAssembly.Table({initial:2,element:'anyfunc'}),abort:console.log});

/***ReturnsaWebAssembly.Instanceinstancecompiledfromthespecified*.wasmfile.*/functionloadWasm(fileName,importObj={env:{}}){//OverrideanydefaultenvvalueswiththepassedinimportObj.env//values:constallEnv=Object.assign({},getDefaultEnv(),importObj.env);

//EnsuretheimportObjobjectincludesthevalidenvvalue:constallImports=Object.assign({},importObj,{env:allEnv});

//Returntheresultofinstantiatingthemodule(instanceandmodule):returnfetch(fileName)

.then(response=>{if(response.ok)returnresponse.arrayBuffer();thrownewError(`UnabletofetchWebAssemblyfile${fileName}`);}).then(bytes=>WebAssembly.instantiate(bytes,allImports));}

ThegetDefaultEnv()functionprovidestherequiredimportObj.envcontentsforEmscripten'sWasmmodule.Wewanttheabilitytopassinanyadditionalimports,whichiswhytheObject.assign()statementisused.WiththeadditionofanyotherimportstheWasmmoduleexpects,Emscripten'sWasmoutputwillalwaysrequirethesefiveimportstatementsforthe"env"object:

(import"env""memory"(memory$env.memory256))

(import"env""table"(table$env.table8anyfunc))

(import"env""memoryBase"(global$env.memoryBasei32))

(import"env""tableBase"(global$env.tableBasei32))

(import"env""abort"(func$env.abort(type$t0)))

Weneedtopassthoseintotheinstantiate()functiontoensuretheWasmmoduleloadssuccessfully,otherwisethebrowserwillthrowanerror.Nowthatwehaveourloadingcodeready,let'smoveontotheHTMLandrectangle-renderingcode.

<!doctypehtml><br/><htmllang="en-us"><br/><head><br/><title>NoGlueCode</title><br/><scripttype="application/javascript"src="../common/load-wasm.js"></script><br/></head><br/><body><br/><h1>NoGlueCode</h1><br/><canvasid="myCanvas"width="255"height="255"></canvas><br/><divstyle="margin-top:16px;"><br/><buttonid="actionButton"style="width:100px;height:24px;"><br/>Pause<br/></button><br/></div><br/><scripttype="application/javascript"><br/>constcanvas=document.querySelector('#myCanvas');<br/>constctx=canvas.getContext('2d');<br/><br/>constenv={<br/>table:newWebAssembly.Table({initial:8,element:'anyfunc'}),<br/>_jsFillRect:function(x,y,w,h){<br/>ctx.fillStyle='#0000ff';<br/>ctx.fillRect(x,y,w,h);<br/>},<br/>_jsClearRect:function(){<br/>ctx.fillStyle='#ff0000';<br/>ctx.fillRect(0,0,255,255);<br/>},<br/>};<br/><br/>loadWasm('without-glue.wasm',{env}).then(({instance})=>{<br/>constm=instance.exports;<br/>m._init();<br/><br/>//Movetherectangleby1pxinthexandyevery20milliseconds:<br/>constloopRectMotion=()=>{<br/>setTimeout(()=>{<br/>m._moveRect();<br/>if(m._getIsRunning())loopRectMotion();<br/>},20)<br/>};<br/><br/>//Enableyoutopauseandresumetherectanglemovement:<br/>document.querySelector('#actionButton')<br/>.addEventListener('click',event=>{<br/>constnewIsRunning=!m._getIsRunning();<br/>m._setIsRunning(newIsRunning);<br/>event.target.innerHTML=newIsRunning?'Pause':'Start';<br/>if(newIsRunning)loopRectMotion();<br/>});<br/><br/>loopRectMotion();<br/>});<br/></script><br/></body><br/></html>

ThiscodewillreplicatetheSDLexamplewecreatedintheprevioussectionswithsomeaddedfunctionality.Whentherectanglebumpsintothelower-righthandcorner,itchangesdirection.You'realsoabletopauseandresumetherectangle'smovementusingabuttonunderthe<canvas>element.Youcanseehowwepassedthe_jsFillRectand_jsClearRectfunctionsintotheimportObj.envobjectsotheycanbereferencedbytheWasmmodule.

ServingitallupLet'stestourcodeoutinthebrowser.FromtheVSCodeterminal,makesureyou'reinthe/book-examplesfolderandrunthecommandtostartupalocalserver:

serve-l8080

It'simportantthatyou'reinthe/book-examplesfolder.Ifyoutryservingupthecodeinthe/chapter-05-create-load-modulefolderonly,youwon'tbeabletousetheloadWasm()function.Ifyouopenupyourbrowsertohttp://127.0.0.1:8080/chapter-05-create-load-module/without-glue.html,youshouldseethis:

Withoutgluecodeexamplerunninginthebrowser

TrypressingthePausebutton;thecaptionshouldchangetoStartandtherectangleshouldstopmoving.Clickingitagainshouldcausetherectangletostartmovingagain.

SummaryInthischapter,wecoveredthecompilationandloadingprocessesformodulesthatutilizetheEmscriptengluecodealongsidetheWasmmodules.ByutilizingsomeofEmscripten'sbuilt-infeatures,suchasportedlibrariesandhelpermethods,wewereabletodemonstratetheadvantagesEmscriptenoffers.Wediscussedsomeofthecompilerflagsthatyoucanpasstotheemcccommandandhowthatwillaffectyouroutput.ByutilizingVSCode'sTasksfeature,wewereabletosetupabuildcommandtoexpeditethebuildprocessgoingforward.WealsoreviewedtheprocessforcompilingandloadingaWasmmodulewithoutthegluecode.WewrotesomereusableJavaScriptcodetoloadthemoduleaswellascodetointeractwithourcompiledWasmmodule.

InChapter6,InteractingwithJavaScriptandDebugging,we'regoingtocoverinteractingwithJavaScriptanddebuggingtechniquesinthebrowser.

Questions1. WhatdoesSDLstandfor?2. InadditiontoJavaScript,HTML,andWasm,whatotheroutputtypecan

yougeneratewiththe-oflagfortheemcccommand?3. WhatadvantagesdoesusingEmscripten'spre-generatedloadingcodeoffer?4. WhatmustyounameyourfunctionintheC/C++filetoensureit

automaticallyexecutesthecompiledoutputinthebrowser?5. Whycan'tweusejusttheWasmfileoutputwithoutthe"glue"codewhen

usingportedlibraries?6. WhatisthekeyboardshortcutinVSCodeforrunningyourdefaultbuild

task?7. WhydoweneedthegetDefaultEnv()methodintheWasmloadingcode?8. WhichfiveitemsarerequiredfortheimportObj.envobjectpassedintothe

WasminstantiationcodeforaWasmmodulecreatedwithEmscripten?

FurtherreadingAboutSDL:https://www.libsdl.org/index.phpEmscriptenCompilerFrontend(emcc):http://kripken.github.io/emscripten-site/docs/tools_reference/emcc.html

IntegratewithExternalToolsviaTasks:https://code.visualstudio.com/docs/editor/tasks

LoadingandrunningWebAssemblycode:https://developer.mozilla.org/en-US/docs/WebAssembly/Loading_and_running

InteractingwithJavaScriptandDebuggingThere'sagreatdealofexcitingfeaturesandproposalsintheworksforWebAssembly.However,atthetimeofwritingthisbook,thefeaturesetisratherlimited.Asitstands,youcanbenefitgreatlyfromusingsomeofthefeaturesEmscriptenprovides.TheprocessforinteractingwithC/C++fromJavaScript(andviceversa)willdifferdependingonwhetheryoudecidetouseEmscripten.

Inthischapter,wewillcoverhowtoutilizeJavaScriptfunctionswithC/C++codeaswellashowtointeractwiththecompiledoutputofyourC/C++codefromJavaScript.We'llalsodescribehowEmscripten'sgluecodeaffectsthewaysaWasminstanceisutilizedandhowtodebugcompiledcodeinthebrowser.

Ourgoalforthischapteristounderstandthefollowing:

ThedifferencesbetweenEmscripten'sModuleandthebrowser'sWebAssemblyobjectHowtocallcompiledC/C++functionsfromyourJavaScriptcodeHowtocallJavaScriptfunctionsfromyourC/C++codeSpecialconsiderationstobeawareofwhenworkingwithC++Techniquesfordebuggingcompiledoutputinthebrowser

TheEmscriptenmoduleversustheWebAssemblyobjectInthepreviouschapter,webrieflycoveredEmscripten'sModuleobjectandhowtoloaditinthebrowser.TheModuleobjectprovidesseveralconvenientmethodsanddifferssignificantlyfromthebrowser'sWebAssemblyobject.Inthissection,we'regoingtoreviewEmscripten'sModuleobjectingreaterdetail.We'llalsodiscussthedifferencebetweenEmscripten'sModuleandtheobjectsdescribedinWebAssembly'sJavaScriptAPI.

WhatistheEmscriptenmodule?Emscripten'sofficialsiteprovidesthefollowingdefinitionfortheModuleobject:

"ModuleisaglobalJavaScriptobjectwithattributesthatEmscripten-generatedcodecallsatvariouspointsinitsexecution."

NotonlyistheloadingproceduredifferentfromWebAssembly'scompileandinstantiatefunctions,buttheModuleprovidessomehelpfulfunctionalityoutoftheboxthatwouldotherwiserequireacustomimplementationinWebAssembly.TheModuleisavailableinaglobalscope(window.Module)afterfetchingandloadingEmscripten'sJavaScriptgluecode.

DefaultmethodsinthegluecodeEmscripten'sModuleobjectprovidessomedefaultmethodsandpropertiestoaidindebuggingandensuringthesuccessfulexecutionofyourcompiledcode.YoucanutilizethepreRunandpostRunpropertiestoexecuteJavaScriptcodebeforeoraftertheModule'srun()functioniscalled,orpipetheoutputoftheprint()andprintErr()functionstoanHTMLelementonthepage.We'llutilizesomeofthesemethodslaterinthisbook.Youcanreadmoreaboutthemathttps://kripken.github.io/emscripten-site/docs/api_reference/module.html.

DifferenceswiththeWebAssemblyobjectWecoveredthebrowser'sWebAssemblyobjectandthecorrespondingloadingproceduresinChapter5,CreatingandLoadingaWebAssemblyModule.WebAssembly'sJavaScriptandWebAPIsdefinetheobjectsandmethodsavailableinthebrowser'swindow.WebAssemblyobject.Emscripten'sModulecanbeseenasacombinationofWebAssembly'sModuleandInstanceobjects,whicharepresentintheresultobjectthatWebAssembly'sinstantiationfunctionreturns.Bypassingthe-sMODULARIZE=1flagtotheemcccommand,we'reabletoreplicateWebAssembly'sinstantiationmethod(toadegree).WewillexaminethedifferencesbetweenEmscripten'sModuleandthebrowser'sWebAssemblyobjectingreaterdetailasweevaluatethemethodsofintegratingJavaScriptandC/C++intheupcomingsections.

CallingcompiledC/C++functionsfromJavaScriptCallingfunctionsfromaWasminstanceisarelativelystraightforwardprocesswithorwithoutEmscripten'sgluecode.UtilizingEmscripten'sAPIaffordsawiderrangeoffunctionalityandintegrationattheexpenseofincludingthegluecodealongsidethe.wasmfile.Inthissection,wewillreviewthemeansofinteractingwiththecompiledWasminstancethroughJavaScriptandtheaddedtoolingEmscriptenprovides.

CallingfunctionsfromaModuleEmscriptenprovidestwofunctionsforcallingcompiledC/C++functionsfromJavaScript:ccall()andcwrap().BothofthesefunctionsarepresentintheModuleobject.Decidingwhichonetouseiscontingentonwhetherthefunctionwillbecalledmorethanonce.ThecontentinthefollowingsectionswastakenfromEmscripten'sAPIreferencedocumentationforpreamble.js,whichcanbeviewedathttp://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html.

Youdon'tneedtoprefixfunctioncallswith_whenusingccall()orcwrap()–justusethenamespecifiedintheC/C++file.

Module.ccall()Module.ccall()callsacompiledCfunctionfromJavaScriptandreturnstheresultofthatfunction.ThefunctionsignatureforModule.ccall()isasfollows:ccall(ident,returnType,argTypes,args,opts)

YoumustspecifyatypenameforthereturnTypeandargTypesparameters.Thepossibletypesare"number","string","array",and"boolean",whichcorrespondtotheappropriateJavaScripttypes.Youcannotspecify"array"forthereturnTypeparameterbecausethereisnowaytoknowthelengthofthearray.Ifthefunctiondoesn'treturnanything,youcanspecifynullforthereturnType(notetheabsenceofquotationmarks).

TheoptsparameterisanoptionaloptionsobjectthatcancontainaBooleanpropertynamedasync.Specifyingavalueoftrueforthispropertyimpliesthatthecallwillperformanasyncoperation.Wewon'tusethisparameterforanyofourexamples,butifyouwishtolearnmoreaboutit,thedocumentationisavailableathttp://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#calling-compiled-c-functions-from-javascript.

Let'slookatanexampleofccall().Thefollowingcode,takenfromtheEmscriptensite,demonstrateshowtocallafunctionnamedc_add()fromthecompiledoutputofaCfile://CallCfromJavaScriptvarresult=Module.ccall('c_add',//nameofCfunction'number',//returntype['number','number'],//argumenttypes[10,20]//arguments);

//resultis30

Module.cwrap()Module.cwrap()issimilartoccall()inthatitcallsacompiledCfunction.However,ratherthanreturningavalue,itreturnsaJavaScriptfunctionthatcanbereusedasmanytimesasneeded.ThefunctionsignatureforModule.cwrap()isasfollows:

cwrap(ident,returnType,argTypes)

Justaswithccall(),youcanspecifystringvaluesthatrepresenttypesforthereturnTypeandargTypesparameters.Youcannotusethe"array"typeinargTypesbecausethereisnowaytoknowthelengthofthearraywhenthefunctioniscalled.Forafunctionthatdoesn'treturnavalue,usenull(withnoquotationmarks)forthereturnTypeparameter.

Thefollowingcode,takenfromtheEmscriptensite,demonstratestheuseofcwrap()tocreateareusablefunction:

//CallCfromJavaScript

varc_javascript_add=Module.cwrap(

'c_add',//nameofCfunction

'number',//returntype

['number','number']//argumenttypes

);

//Callc_javascript_addnormally

console.log(c_javascript_add(10,20));//30

console.log(c_javascript_add(20,30));//50

intaddNumbers(intnum1,intnum2){<br/>returnnum1+num2;<br/>}<br/><br/>intaddNumbers(intnum1,intnum2,intnum3){<br/>returnnum1+num2+num3;<br/>}<br/><br/>intaddNumbers(intnum1,intnum2,intnum3,intnum4){<br/>returnnum1+num2+num3+num4;<br/>}<br/><br/>//Thefunctionwillreturnavaluebasedonhowmany<br/>//argumentsyoupassit:<br/>intgetSumOfTwoNumbers=addNumbers(1,2);<br/>//returns3<br/><br/>intgetSumOfThreeNumbers=addNumbers(1,2,3);<br/>//returns6<br/><br/>intgetSumOfFourNumbers=addNumbers(1,2,3,4);<br/>//returns10

extern"C"{<br/>intaddTwoNumbers(intnum1,intnum2){<br/>returnnum1+num2;<br/>}<br/><br/>intaddThreeNumbers(intnum1,intnum2,intnum3){<br/>returnnum1+num2+num3;<br/>}<br/><br/>intaddFourNumbers(intnum1,intnum2,intnum3,intnum4){<br/>returnnum1+num2+num3+num4;<br/>}<br/>}

CallingfunctionsfromaWebAssemblyinstanceWedemonstratedhowtocallafunctioninaWasminstancefromJavaScriptinthepreviouschapter,butthatwasassumingyouinstantiatedamoduleinthebrowserwithnogluecode.EmscriptenprovidestheabilitytocallfunctionsfromtheWasminstanceaswell.Afteramoduleisinstantiated,youcallfunctionsbyinvokingthemfromtheinstance.exportsobject,whichisaccessiblefromtheresultoftheresolvedPromise.MDN'sdocumentationprovidesthefollowingfunctionsignatureforWebAssembly.instantiateStreaming:Promise<ResultObject>WebAssembly.instantiateStreaming(source,importObject);

YoumayneedtousetheWebAssembly.instantiate()method,dependingonyourbrowser.ChromecurrentlysupportsWebAssembly.instantiateStreaming(),butifyouencounteranerrorwhenattemptingtoloadyourmodule,usetheWebAssembly.instantiate()methodinstead.

TheResultObjectcontainstheinstanceobjectthatweneedtoreferencetocallexportedfunctionsfromthemodule.Here'ssomecodethatcallsafunctionnamed_addTwoNumbersfromthecompiledWasminstance:

//AssumetheimportObjisalreadydefined.

WebAssembly.instantiateStreaming(

fetch('simple.wasm'),

importObj

)

.then(result=>{

constaddedNumbers=result.instance.exports._addTwoNumbers(1,2);

//resultis3

});

Emscriptenprovidesawaytoperformfunctioncallsinmuchthesameway,albeitinaslightlydifferentimplementation.IfyouusethePromise-likeAPI,youcanaccessthefunctionfromanasmobjectthatthepromiseoftheModule()resolveswith.Thefollowingexampledemonstrateshowtoutilizethisfunctionality:

//UsingEmscripten'sModule

Module()

.then(result=>{

//"asm"isessentially"instance"

constexports=result.asm;

constaddedNumbers=exports._addTwoNumbers(1,2);

//resultis3

});

ReplicatingtheWebAssembly'sWebAPIsyntaxwithEmscriptensimplifiesanyfuturerefactoring.YoucaneasilyreplaceModule()withWebAssembly'sinstantiateStreaming()methodandresult.asmwithresult.instanceinthefutureifyoudecidetouseWebAssembly'sWebAPI.

CallingJavaScriptfunctionsfromC/C++AccessingJavaScript'sfunctionalityfromC/C++codeallowsforaddedflexibilitywhenworkingwithWebAssembly.ThemethodologiesandmeansofutilizingJavaScriptdifferconsiderablybetweenEmscripten'sgluecodeandWasm-onlyimplementations.Inthissection,wewillcoverthevariouswaysyoucanintegrateJavaScriptintoyourC/C++codewithandwithoutEmscripten.

InteractingwithJavaScriptusinggluecodeEmscriptenprovidesseveraltechniquesforintegratingJavaScriptwithyourC/C++code.Thetechniquesavailabledifferinimplementationandcomplexity,andsomeonlyapplytospecificexecutionenvironments(forexample,thebrowser).Decidingwhichonetouseiscontingentonyourspecificusecase.We'llfocusontheemscripten_run_script()functionandinliningJavaScriptwithEM_*wrappers.ThecontentinthefollowingsectionswastakenfromtheInteractingwithCodesectionofEmscripten'ssite,whichcanbeviewedathttps://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.ht

ml#interacting-with-code.

Executingstringswithemscripten_run_script()TheEmscriptensitedescribestheemscripten_run_script()functionasthemostdirect,butslightlyslowerapproachforcallingJavaScriptforC/C++.It'satechniquethatiswellsuitedforasinglelineofJavaScriptcodeandcanbeusefulfordebugging.Thedocumentationstatesthatiteffectivelyrunsthecodeusingeval(),whichisaJavaScriptfunctionthatexecutesastringascode.ThefollowingcodetakenfromtheEmscriptensitedemonstratestheuseofemscripten_run_script()tocallthebrowser'salert()functionwiththetext'hi':emscripten_run_script("alert('hi')");

Formorecomplexusecaseswhereperformanceisafactor,usinginlineJavaScriptprovidesabettersolution.

#include<emscripten.h><br/><br/>intmain(){<br/>EM_ASM(<br/>console.log('ThisissomeJScode.');<br/>);<br/>return0;<br/>}

EM_ASM({<br/>console.log('Ireceived:'+[$0,$1]);<br/>},100,35.5);

ReusinginlineJavaScriptwithEM_JS()IfyouneedareusablefunctionwithinyourC/C++file,youcanwrapJavaScriptcodewithinanEM_JS()blockandexecuteitlikeanormalC/C++function.ThedefinitionforEM_JS()isdescribedinthefollowingcodesnippet:

EM_JS(return_type,function_name,arguments,code)

Thereturn_typeparameterrepresentstheCtypethatcorrespondswiththeJavaScriptcode'soutput(forexample,intorfloat).IfnothingisreturnedfromtheJavaScriptcode,specifyvoidforthereturn_type.Thenextparameter,function_name,representsthenametousewhencallingtheJavaScriptcodefromotherlocationsintheC/C++file.TheargumentsparameterisusedtodefineargumentsthatcanbepassedintotheJavaScriptcodefromtheCcallingfunction.ThecodeparameteristheJavaScriptcodethat'swrappedincurlybraces.Thefollowingcodesnippet,takenfromtheEmscriptensite,demonstratestheuseofEM_JS()inaCfile:

#include<emscripten.h>

EM_JS(void,take_args,(intx,floaty),{

console.log(`Ireceived${x}and${y}`);

});

intmain(){

take_args(100,35.5);

return0;

}

ExamplesofusinggluecodeLet'swritesomecodethatutilizesallofthesefeatures.Inthissection,wewillmodifythecodeweusedintheCompilingCwithoutthegluecodeandFetchingandinstantiatingaWasmfilesectionsofChapter5,CreatingandLoadingaWebAssemblyModule.Thiswasthecodethatdisplayedamovingbluerectangleonaredcanvasandcouldbepausedandrestartedwiththeclickofabutton.Thecodeforthissectionislocatedinthe/chapter-06-interact-with-jsfolderinthelearn-webassemblyrepository.Let'sstartbyupdatingtheCcode.

TheCcodeCreateanewfolderinyour/book-examplesfoldernamed/chapter-06-interact-with-js.Createanewfileinthe/chapter-06-interact-with-jsfoldernamedjs-with-glue.candpopulateitwiththefollowingcontents:

/*

*Thisfileinteractswiththecanvasthroughimportedfunctions.

*Itmovesabluerectanglediagonallyacrossthecanvas

*(mimicstheSDLexample).

*/

#include<emscripten.h>

#include<stdbool.h>

#defineBOUNDS255

#defineRECT_SIDE50

#defineBOUNCE_POINT(BOUNDS-RECT_SIDE)

boolisRunning=true;

typedefstructRect{

intx;

inty;

chardirection;

}Rect;

structRectrect;

/*

*Updatestherectanglelocationby1pxinthexandyina

*directionbasedonitscurrentposition.

*/

voidupdateRectLocation(){

//Sincewewanttherectangleto"bump"intotheedgeofthe

//canvas,weneedtodeterminewhentherightedgeofthe

//rectangleencounterstheboundsofthecanvas,whichiswhy

//we'reusingthecanvaswidth-rectanglewidth:

if(rect.x==BOUNCE_POINT)rect.direction='L';

//Assoonastherectangle"bumps"intotheleftsideofthe

//canvas,itshouldchangedirectionagain.

if(rect.x==0)rect.direction='R';

//Ifthedirectionhaschangedbasedonthexandy

//coordinates,ensurethexandypointsupdate

//accordingly:

intincrementer=1;

if(rect.direction=='L')incrementer=-1;

rect.x=rect.x+incrementer;

rect.y=rect.y+incrementer;

}

EM_JS(void,js_clear_rect,(),{

//Cleartherectangletoensurethere'snocolorwhereit

//wasbefore:

varcanvas=document.querySelector('#myCanvas');

varctx=canvas.getContext('2d');

ctx.fillStyle='#ff0000';

ctx.fillRect(0,0,255,255);

});

EM_JS(void,js_fill_rect,(intx,inty,intwidth,intheight),{

//Filltherectanglewithblueinthespecifiedcoordinates:

varcanvas=document.querySelector('#myCanvas');

varctx=canvas.getContext('2d');

ctx.fillStyle='#0000ff';

ctx.fillRect(x,y,width,height);

});

/*

*Cleartheexistingrectangleelementfromthecanvasanddrawa

*newoneintheupdatedlocation.

*/

EMSCRIPTEN_KEEPALIVE

voidmoveRect(){

//Eventthoughthejs_clear_rectdoesn'thaveany

//parameters,wepass0intopreventacompilerwarning:

js_clear_rect(0);

updateRectLocation();

js_fill_rect(rect.x,rect.y,RECT_SIDE,RECT_SIDE);

}

EMSCRIPTEN_KEEPALIVE

boolgetIsRunning(){

returnisRunning;

}

EMSCRIPTEN_KEEPALIVE

voidsetIsRunning(boolnewIsRunning){

isRunning=newIsRunning;

EM_ASM({

//isRunningiseither0or1,butinJavaScript,0

//is"falsy",sowecansetthestatustextbased

//withoutexplicitlycheckingifthevalueis0or1:

varnewStatus=$0?'Running':'Paused';

document.querySelector('#runStatus').innerHTML=newStatus;

},isRunning);

}

EMSCRIPTEN_KEEPALIVE

voidinit(){

emscripten_run_script("console.log('Initializingrectangle...')");

rect.x=0;

rect.y=0;

rect.direction='R';

setIsRunning(true);

emscripten_run_script("console.log('Rectangleshouldbemoving!')");

}

YoucanseethatweusedallthreeoftheJavaScriptintegrationsthatEmscriptenprovides.Therearetwofunctions,js_clear_rect()andjs_fill_rect(),thataredefinedinEM_JS()blocksthattaketheplaceoftheimportedfunctionsfromtheoriginalexample.TheEM_ASM()blockwithinthesetIsRunning()functionupdatesthe

textofanewstatuselementwe'lladdtotheHTMLcode.Theemscripten_run_script()functionssimplylogoutsomestatusmessages.WeneedtospecifyEMSCRIPTEN_KEEPALIVEabovethefunctionswe'replanningtoutilizeoutsideofthemodule.Ifyoudon'tspecifythis,thecompilerwilltreatthefunctionsasdeadcodeandremovethem.

<!doctypehtml><br/><htmllang="en-us"><br/><head><br/><title>InteractwithJSusingGlueCode</title><br/></head><br/><body><br/><h1>InteractwithJSusingGlueCode</h1><br/><canvasid="myCanvas"width="255"height="255"></canvas><br/><divstyle="margin-top:16px;"><br/><buttonid="actionButton"style="width:100px;height:24px;">Pause</button><br/><spanstyle="width:100px;margin-left:8px;">Status:</span><br/><spanid="runStatus"style="width:100px;"></span><br/></div><br/><scripttype="application/javascript"src="js-with-glue.js"></script><br/><scripttype="application/javascript"><br/>Module()<br/>.then(result=>{<br/>constm=result.asm;<br/>m._init();<br/><br/>//Movetherectangleby1pxinthexandyevery20milliseconds:<br/>constloopRectMotion=()=>{<br/>setTimeout(()=>{<br/>m._moveRect();<br/>if(m._getIsRunning())loopRectMotion();<br/>},20)<br/>};<br/><br/>//Enableyoutopauseandresumetherectanglemovement:<br/>document.querySelector('#actionButton')<br/>.addEventListener('click',event=>{<br/>constnewIsRunning=!m._getIsRunning();<br/>m._setIsRunning(newIsRunning);<br/>event.target.innerHTML=newIsRunning?'Pause':'Start';<br/>if(newIsRunning)loopRectMotion();<br/>});<br/><br/>loopRectMotion();<br/>});<br/></script><br/></body><br/></html>

Weaddedtwo<span>elementstodisplaythestatusoftherectangle'smovement,alongwithacorrespondinglabel.We'reusingEmscripten'sPromise-likeAPItoloadthemoduleandreferencethefunctionsfromthecompiledcode.We'renolongerpassinginthe_jsFillRectand_jsClearRectfunctionstothemodulebecausewe'rehandlingthatwithinthejs-with-glue.cfile.

CompilingandservingtheresultTocompilethecode,ensurethatyou'reinthe/chapter-06-interact-with-jsfolderandrunthefollowingcommand:

emccjs-with-glue.c-O3-sWASM=1-sMODULARIZE=1-ojs-with-glue.js

Oncecomplete,runthefollowingcommandtostartyourlocalserver:

serve-l8080

Openupabrowserandnavigatetohttp://127.0.0.1:8080/js-with-glue.html.Youshouldseesomethinglikethis:

Gluecoderunninginthebrowser

IfyoupressthePausebutton,thecaptiononthebuttonshouldchangetoStart,thetextnexttoStatusshouldchangetoPaused,andtherectangleshouldstopmoving.

InteractingwithJavaScriptwithoutgluecodeUtilizingJavaScriptcodeinC/C++filesfollowsadifferentparadigmthanthetechniquesusedforEmscripten.RatherthanwritingJavaScriptwithintheC/C++files,youpassthefunctionsintoyourWebAssemblyinstantiationcode.Inthissection,wewilldescribethisprocessingreaterdetail.

//Youcandefinethefunctioninsideoftheenvobject:<br/>constenv={<br/>//Makesureyouprefixthefunctionnamewith"_"!<br/>_logValueToConsole:value=>{<br/>console.log(`'Thevalueis${value}'`);<br/>}<br/>};<br/><br/>//Ordefineitoutsideofenvandreferenceitwithinenv:<br/>constlogValueToConsole=value=>{<br/>console.log(`'Thevalueis${value}'`);<br/>};<br/><br/>constenv={<br/>_logValueToConsole:logValueToConsole<br/>};

GiventhemanualmemorymanagementandstricttypingrequirementsofC,C++,andRust,you'relimitedinwhatcanbepassedinandutilizedinaWasmmodule.JavaScriptallowsyoutoeasilyadd,remove,andchangethevaluesofpropertiesonanobjectoverthecourseofcodeexecution.Youcanevenextendthelanguagebyaddingfunctionstotheprototypeofabuilt-inlanguagefeature.C,C++,andRustaremuchmorerestrictive,anditcanbedifficulttotakefulladvantageofWebAssemblyifyou'renotfamiliarwiththeselanguages.

CallingimportedfunctionsinC/C++YouneedtodefinetheJavaScriptfunctionyoupassedintoimportObj.envwithintheC/C++codethatutilizesit.Thefunctionsignaturemustmatchwhatyoupassedin.Thefollowingexampledemonstratesthisingreaterdetail.Here'stheJavaScriptcodethatinteractswiththecompiledCfile(index.html)://index.html<script>contentsconstenv={_logAndMultiplyTwoNums:(num1,num2)=>{constresult=num1*num2;console.log(result);returnresult;},};

loadWasm('main.wasm',{env}).then(({instance})=>{constresult=instance.exports._callMultiply(5.5,10);console.log(result);//55isloggedtotheconsoletwice});

Thisisthecontentsofmain.c,whichiscompiledtomain.wasmandusedwithinindex.html:

//main.c(compiledtomain.wasm)

externfloatlogAndMultiplyTwoNums(floatnum1,floatnum2);

floatcallMultiply(floatnum1,floatnum2){

returnlogAndMultiplyTwoNums(num1,num2);

}

YoucalltheJavaScriptfunctioninyourC/C++thesamewayyou'dcallanormalC/C++function.Althoughyouprefixyourfunctionwitha_whenyoupassitintotheimportObj.env,youdon'tneedtoincludetheprefixwhendefiningitintheC/C++file.

AnexamplewithoutgluecodeTheexamplecodefromtheCompilingCwithoutthegluecodeandFetchingandinstantiatingaWasmfilesectionsofChapter5,CreatingandLoadingaWebAssemblyModule,demonstratedhowtointegrateJavaScriptinourCfilewithoutusingEmscripten'sgluecode.Inthissection,wewillmodifytheexamplecodeslightlyandchangethefiletypetoC++.

/*<br/>*Thisfileinteractswiththecanvasthroughimportedfunctions.<br/>*Itmovesacirclediagonallyacrossthecanvas.<br/>*/<br/>#defineBOUNDS255<br/>#defineCIRCLE_RADIUS50<br/>#defineBOUNCE_POINT(BOUNDS-CIRCLE_RADIUS)<br/><br/>boolisRunning=true;<br/><br/>typedefstructCircle{<br/>intx;<br/>inty;<br/>chardirection;<br/>}Circle;<br/><br/>structCirclecircle;<br/><br/>/*<br/>*Updatesthecirclelocationby1pxinthexandyina<br/>*directionbasedonitscurrentposition.<br/>*/<br/>voidupdateCircleLocation(){<br/>//Sincewewantthecircleto"bump"intotheedgeofthecanvas,<br/>//weneedtodeterminewhentherightedgeofthecircle<br/>//encounterstheboundsofthecanvas,whichiswhywe'reusing<br/>//thecanvaswidth-circlewidth:<br/>if(circle.x==BOUNCE_POINT)circle.direction='L';<br/><br/>//Assoonasthecircle"bumps"intotheleftsideofthe<br/>//canvas,itshouldchangedirectionagain.<br/>if(circle.x==CIRCLE_RADIUS)circle.direction='R';<br/><br/>//Ifthedirectionhaschangedbasedonthexandy<br/>//coordinates,ensurethexandypointsupdateaccordingly:<br/>intincrementer=1;<br/>if(circle.direction=='L')incrementer=-1;<br/>circle.x=circle.x+incrementer;<br/>circle.y=circle.y-incrementer;<br/>}<br/><br/>//Weneedtowrapanyimportedorexportedfunctionsinan<br/>//externblock,otherwisethefunctionnameswillbemangled.<br/>extern"C"{<br/>//ThesefunctionsarepassedinthroughtheimportObj.envobject<br/>//andupdatethecircleonthe<canvas>:<br/>externintjsClearCircle();<br/>externintjsFillCircle(intx,inty,intradius);<br/><br/>/*<br/>*Cleartheexistingcircleelementfromthecanvasanddrawa<br/>*newoneintheupdatedlocation.<br/>*/<br/>voidmoveCircle(){<br/>jsClearCircle();<br/>updateCircleLocation();<br/>jsFillCircle(circle.x,circle.y,CIRCLE_RADIUS);<br/>}<br/><br/>boolgetIsRunning(){<br/>returnisRunning;<br/>}<br/><br/>voidsetIsRunning(boolnewIsRunning){<br/>isRunning=newIsRunning;<br/>}<br/><br/>voidinit(){<br/>circle.x=0;<br/>circle.y=255;<br/>circle.direction='R';<br/>setIsRunning(true);<br/>}<br/>}

Thiscodeissimilartothepreviousexample,buttheshapeanddirectionoftheelementonthecanvashaschanged.Now,theelementisacirclethatstartsinthelower-leftcornerofthecanvasandmovesdiagonallytowardtheupper-right.

<!doctypehtml><br/><htmllang="en-us"><br/><head><br/><title>InteractwithJSwithoutGlueCode</title><br/><script<br/>type="application/javascript"<br/>src="../common/load-wasm.js"><br/></script><br/><style><br/>#myCanvas{<br/>border:2pxsolidblack;<br/>}<br/>#actionButtonWrapper{<br/>margin-top:16px;<br/>}<br/>#actionButton{<br/>width:100px;<br/>height:24px;<br/>}<br/></style><br/></head><br/><body><br/><h1>InteractwithJSwithoutGlueCode</h1><br/><canvasid="myCanvas"width="255"height="255"></canvas><br/><divid="actionButtonWrapper"><br/><buttonid="actionButton">Pause</button><br/></div><br/><scripttype="application/javascript"><br/>constcanvas=document.querySelector('#myCanvas');<br/>constctx=canvas.getContext('2d');<br/><br/>constfillCircle=(x,y,radius)=>{<br/>ctx.fillStyle='#fed530';<br/>//Faceoutline:<br/>ctx.beginPath();<br/>ctx.arc(x,y,radius,0,2*Math.PI);<br/>ctx.fill();<br/>ctx.stroke();<br/>ctx.closePath();<br/><br/>//Eyes:<br/>ctx.fillStyle='#000000';<br/>ctx.beginPath();<br/>ctx.arc(x-15,y-15,6,0,2*Math.PI);<br/>ctx.arc(x+15,y-15,6,0,2*Math.PI);<br/>ctx.fill();<br/>ctx.closePath();<br/><br/>//Mouth:<br/>ctx.beginPath();<br/>ctx.moveTo(x-20,y+10);<br/>ctx.quadraticCurveTo(x,y+30,x+20,y+10);<br/>ctx.lineWidth=4;<br/>ctx.stroke();<br/>ctx.closePath();<br/>};<br/><br/>constenv={<br/>table:newWebAssembly.Table({initial:8,element:'anyfunc'}),<br/>_jsFillCircle:fillCircle,<br/>_jsClearCircle:function(){<br/>ctx.fillStyle='#fff';<br/>ctx.fillRect(0,0,255,255);<br/>},<br/>};<br/><br/>loadWasm('js-without-glue.wasm',{env}).then(({instance})=>{<br/>constm=instance.exports;<br/>m._init();<br/><br/>//Movethecircleby1pxinthexandyevery20milliseconds:<br/>constloopCircleMotion=()=>{<br/>setTimeout(()=>{<br/>m._moveCircle();<br/>if(m._getIsRunning())loopCircleMotion();<br/>},20)<br/>};<br/><br/>//Enableyoutopauseandresumethecirclemovement:<br/>document.querySelector('#actionButton')<br/>.addEventListener('click',event=>{<br/>constnewIsRunning=!m._getIsRunning();<br/>m._setIsRunning(newIsRunning);<br/>event.target.innerHTML=newIsRunning?'Pause':'Start';<br/>if(newIsRunning)loopCircleMotion();<br/>});<br/><br/>loopCircleMotion();<br/>});<br/></script><br/></body><br/></html>

Insteadofusingtherect()element,wecanmanuallydrawpathsusingthefunctionsavailableonthecanvaselement's2Dcontext.

CompilingandservingtheresultWe'reonlygeneratingaWasmmodule,sowecanusethebuildtaskwesetupinthepreviouschaptertocompileourcode.SelectTasks|RunBuildTask…orusethekeyboardshortcutCtrl/Cmd+Shift+Btocompilethecode.Ifyou'renotusingVSCode,openaCLIinstanceinthe/chapter-06-interact-with-jsfolderandrunthefollowingcommand:

emccjs-without-glue.cpp-Os-sWASM=1-sSIDE_MODULE=1-sBINARYEN_ASYNC_COMPILATION=0-ojs-without-glue.wasm

Oncecomplete,openaterminalinthe/book-examplesfolder,andrunthefollowingcommandtostartyourlocalserver:

serve-l8080

Openupabrowserandnavigatetohttp://127.0.0.1:8080/chapter-06-interact-with-js/js-without-glue.html.Youshouldseesomethinglikethis:

TheWasmmodulerunninginthebrowserwithoutgluecode

Justaswiththepreviousexamples,ifyoupressthePausebutton,thecaptiononthebuttonshouldchangetoStartandthecircleshouldstopmoving.

AdvancedEmscriptenfeaturesWecoveredtheEmscriptenfeatureswe'llbeusingmostfrequentlyforcommunicatingbetweenJavaScriptandC/C++intheprevioussections,butthosearen'ttheonlycapabilitiesEmscriptenprovides.ThereareadvancedfeaturesandadditionalAPIsthatyouneedtobeawareof,especiallyifyouplanonaddingmorecomplexfunctionalitytoyourapplication.Inthissection,we'llbrieflyreviewsomeoftheseadvancedfeaturesandprovidedetailsaboutwhereyoucanlearnmore.

EmbindEmbindisanadditionalfeaturethatEmscriptenoffersforconnectingJavaScriptandC++.Emscripten'ssiteprovidesthefollowingdescription:

"EmbindisusedtobindC++functionsandclassestoJavaScript,sothatthecompiledcodecanbeusedinanaturalwayby'normal'JavaScript.EmbindalsosupportscallingJavaScriptclassesfromC++."

EmbindisapowerfulfeaturethatallowsfortightintegrationbetweenJavaScriptandC++.YoucanwrapsomeC++codeinanEMSCRIPTEN_BINDINGS()blockandreferenceitthroughtheModuleobjectinyourbrowser.Let'slookatanexamplefromEmscripten'ssite.Thefollowingfile,example.cpp,iscompiledwiththe--bindflagofemcc:

//example.cpp

#include<emscripten/bind.h>

usingnamespaceemscripten;

floatlerp(floata,floatb,floatt){

return(1-t)*a+t*b;

}

EMSCRIPTEN_BINDINGS(my_module){

function("lerp",&lerp);

}

Theresultantmoduleisloadedinexample.htmlandthelerp()functioniscalled:

<!--example.html-->

<!doctypehtml>

<html>

<scriptsrc="example.js"></script>

<script>

//example.jswasgeneratedbyrunningthiscommand:

//emcc--bind-oexample.jsexample.cpp

console.log('lerpresult:'+Module.lerp(1,2,0.5));

</script>

</html>

TheprecedingexamplerepresentsasmallportionofEmbind'scapabilities.YoucanlearnmoreaboutEmbindathttps://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/embind.html.

FileSystemAPIEmscriptenprovidessupportforfileoperationsbyusingtheFSlibraryandexposesanAPIforworkingwiththefilesystem.However,it'snotincludedbydefaultwhenyoucompileyourprojectbecauseitcouldincreasethefile'ssizesignificantly.IfyourC/C++codeusesfiles,thelibrarywillbeaddedautomatically.Thefilesystemtypesvarybasedontheexecutionenvironment.Forexample,ifyou'rerunningcodeinsideaworker,theWORKERFSfilesystemcanbeused.Bydefault,MEMFSisused,whichstoresthedatainmemory,andanydatawrittentomemoryislostwhenthepageisreloaded.YoucanreadmoreabouttheFileSystemAPIathttps://kripken.github.io/emscripten-site/docs/api_reference/Filesystem-API.html#filesystem-api.

FetchAPIEmscriptenprovidesaFetchAPIaswell.Thefollowingistakenfromthedocumentation:

"TheEmscriptenFetchAPIallowsnativecodetotransferfilesviaXHR(HTTPGET,PUT,POST)fromremoteservers,andtopersistthedownloadedfileslocallyinbrowser'sIndexedDBstorage,sothattheycanbere-accessedlocallyonsubsequentpagevisits.TheFetchAPIiscallablefrommultiplethreads,andthenetworkrequestscanberuneithersynchronouslyorasynchronouslyasdesired."

TheFetchAPIcanbeusedtointegratewithEmscripten'sotherfeatures.Ifyouneedtofetchdatathatisn'tutilizedbyEmscripten,youshouldusethebrowser'sFetchAPI(https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).YoucanreadmoreabouttheFetchAPIathttps://kripken.github.io/emscripten-site/docs/api_reference/fetch.html.

DebugginginthebrowserEffectivelydebuggingJavaScriptcodeinthebrowserhasnotalwaysbeeneasy.However,developmenttoolinghasmarkedlyimprovedinthebrowserandineditors/IDEswithbuilt-indebuggingcapabilities.Unfortunately,addingWebAssemblytoawebapplicationaddsanadditionallevelofcomplexitytothedebuggingprocess.Inthissection,wewillreviewsometechniquesfordebuggingJavaScriptthatutilizesWasmaswellassomeoftheadditionalcapabilitiesEmscriptenoffers.

High-leveloverviewDebuggingEmscripten'sModuleisrelativelystraightforward.Emscripten'serrormessagesarewellformedanddescriptive,soyou'llusuallydiscoverwhat'scausingtheissuerightaway.Youcanviewthesemessagesinyourbrowser'sdevelopmenttoolsconsole.

Ifyouspecifieda.htmloutputwhenrunningtheemcccommand,somedebuggingcodewillalreadybebuiltin(Module.printandModule.printErr).WithintheHTMLfile,theloadingcodesetsthewindow.onerroreventtocalltheModule.printErrevent,soyoucanseedetailsabouttheerrorthatoccurredwhenloading.

Onecommonerroryoumayencounteriscallingthewrongfunctionname.Ifyou'reusingEmscripten'sPromise-likeAPI,youcanprintouttheavailablefunctionsbyrunningthefollowingcodeinyourbrowser'sconsole:

console.log(Module().asm);

Thefollowingscreenshotshowstheoutputforthejs-with-glue.jsexampleweusedintheCallingJavaScriptfunctionsfromC/C++sectionofthischapter:

LoggingthecontentsofModule().asminthebrowserconsole

Yourfunctions,aswellassomefunctionsthatEmscriptengenerates,willbeprefixedwitha_.Theadvantageofwritingcodethatgetscompiledisthatthecompilerwillcatchmosterrorsupfront.GiventheextensivetoolingavailableforlanguagessuchasCandC++,youshouldbeabletounderstandandaddresstheseerrorsquickly.

Ifyou'renotusinganygluecodeandinstantiatingaWasmfileusingWebAssembly'sJavaScriptandWebAPIs,debuggingcangetalittlemorecomplex.Aspreviouslystated,youhavetheadvantageofcatchingmosterrorsatcompiletimeinyourCorC++code.JustaswithEmscripten,theerrormessagesprintedoutinyourbrowser'sdevelopmenttoolsconsoleprovideastacktraceandarelativelycleardescriptionoftheissue.However,loggingtotheconsolemaybecomecumbersomeanddifficulttomanageifyou'retroubleshootingaparticularlydifficultbug.Fortunately,youcanusesourcemapstoimproveyourdebuggingcapabilities.

UsingsourcemapsEmscriptenhastheabilitytogeneratesourcemapsbypassingsomeadditionalflagstothecompiler.Sourcemapsallowyourbrowsertomapthesourceofafiletothefilebeingutilizedinanapplication.Forexample,youcanuseaJavaScriptbuildtoolsuchWebpacktominifythecodeaspartofyourbuildprocess.However,it'sincrediblydifficulttonavigateandtroubleshoottheminifiedcodeifyou'retryingtofindabug.Bygeneratingasourcemap,youcanviewthecodeinitsoriginalformwithinthebrowser'sdevelopmenttoolsandsetbreakpointsfordebugging.Let'sgenerateasourcemapforour/chapter-06-interact-with-js/js-without-glue.cppfile.Withinthe/book-examplesfolder,runthefollowingcommandinaterminal:

emccchapter-06-interact-with-js/js-without-glue.cpp-O1-g4-sWASM=1-sSIDE_MODULE=1-sBINARYEN_ASYNC_COMPILATION=0-ochapter-06-interact-with-js/js-without-glue.wasm--source-map-basehttp://localhost:8080/chapter-06-interact-with-js/

The-g4argumentenablessourcemaps,whilethe--source-map-baseargumenttellsthebrowserwheretofindthesourcemapfile.Oncecompiled,startyourlocalserverupfromthe/book-examplesfolderbyrunningthefollowingcommand:

serve-l8080

Navigatetohttp://127.0.0.1:8080/chapter-06-interact-with-js/js-without-glue.html,opentheDeveloperTools,andselecttheSourcestab(inChrome)orDebuggertab(inFirefox).Ifyou'reusingChrome,youshouldseethefollowing:

WasmsourcemapsinChromeDeveloperTools

Asyoucansee,thefilenamesaren'tveryhelpful.Eachfileshouldincludethefunctionnameatthetop,althoughsomeofthenamesmayhavebeenmangled.Youcansetbreakpointsifyouencountererrors,andChrome'sdebuggingfunctionalityallowsyoutonavigatethecallstack.Firefoxhandlestheirsourcemapsdifferently.ThefollowingscreenshotshowstheDebuggerviewinFirefox'sDeveloperTools:

WasmsourcemapinFirefoxDeveloperTools

ThesourcemapisasinglefilethatcontainstheWatrepresentationoftheWasmfile.Youcansetbreakpointsanddebugcodehereaswell.AsWebAssemblyevolves,more(andbetter)toolingwillbecomeavailable.Inthemeantime,loggingtotheconsoleandutilizingsourcemapsarethecurrentdebuggingmethodsyoucanuse.

SummaryInthischapter,wefocusedontheintercommunicationofJavaScriptandC/C++,someofthefeaturesEmscriptenoffers,andhowtoeffectivelydebugwebapplicationsthatutilizeWasminthebrowser.WereviewedthevariousmeansofcallingcompiledC/C++functionsfromJavaScript,andhowtointegrateJavaScriptwithyourC/C++code.Emscripten'sAPIswerepresentedasawaytounderstandhowyoucanovercomesomeofWebAssembly'scurrentlimitationsbyincludinggluecodewithyourcompiledWasmfiles.EventhoughthecapabilitiesEmscriptenprovidesarenotpresentintheofficialWebAssemblyCoreSpecification(andmayneverbe),thatshouldn'tdeteryoufromtakingadvantageofthem.Finally,webrieflycoveredhowtodebugWasmfilesinthebrowserinthecontextofanEmscriptenmoduleoraWebAssemblyinstance.

Inthenextchapter,we'llbuildareal-worldWebAssemblyapplicationfromscratch.

Questions1. WhatarethenamesofthetwofunctionsavailableontheModuleobjectthat

youusetointeractwiththecompiledcodefromthebrowser?2. WhatdoyouneedtowrapyourC++codeintoensurethefunctionnames

don'tgetmangled?3. What'sthedifferencebetweenEM_ASM()andEM_JS()?4. Whichismoreperformant,emscripten_run_script()orEM_ASM()/EM_JS()?5. Whatdoyouneedtoincludeinthelineaboveyourfunctionifyouwantto

useitoutsideofyourC/C++code(hint:itstartswithEMSCRIPTEN)?6. Wherecanyoudefineafunctionthatneedstobepassedintothe

importObj.envobjectwheninstantiatingamodule?7. WhatadditionalAPIsdoesEmscriptenprovide?8. Whatisthepurposeofsourcemaps?

FurtherreadingEmscriptenAPIReference:http://kripken.github.io/emscripten-site/docs/api_reference/index.html

AnIntroductiontoSourceMaps:http://blog.teamtreehouse.com/introduction-source-maps

UsingBrowserstoDebugWebAssembly:http://webassemblycode.com/using-browsers-debug-webassembly

CreatinganApplicationfromScratch

Nowit'stimetoapplyyourknowledge!SinceoneofWebAssembly'sprimarydesigngoalsistoexecutewithinandintegratewellwiththeexistingwebplatform,itmakessensetobuildawebapplicationtotestitout.EventhoughWebAssembly'scurrentfeaturesetisratherlimited,wecanutilizethetechnologyatabasiclevel.Inthischapter,wewillbuildasingle-pageapplicationfromscratchthatutilizesWasmmoduleswithinthecontextoftheCoreSpecification.

Bytheendofthischapter,you'llknowhowto:

WritefunctionsthatperformsimplecomputationswithCBuildabasicJavaScriptapplicationwithVueIntegrateWasmintoyourJavaScriptapplicationIdentifythecapabilitiesandlimitationsofWebAssemblyinitscurrentformRunandtestaJavaScriptapplicationusingbrowser-sync

CooktheBooks–makingWebAssemblyaccountableAsmentionedbefore,WebAssembly'scurrentfeaturesetisratherlimited.WecanuseEmscriptentogreatlyextendthecapabilitiesofawebapplication,butthatcarriesthecostofnoncompliancewiththeofficialspecificationandtheadditionofgluecode.WecanstilluseWebAssemblyeffectivelytoday,whichbringsustotheapplicationwe'llbuildinthischapter.Inthissection,wewillreviewthelibrariesandtoolswe'llusetobuildtheapplication,aswellasabriefoverviewofitsfunctionality.

OverviewandfunctionalityInWebAssembly'scurrentform,wecanpassnumbersbetweenaWasmmoduleandJavaScriptcodewithrelativeease.Anaccountingapplicationseemslikealogicalchoiceintermsofreal-worldapplicability.TheonlycontentionIhavewithaccountingsoftwareisthatit'salittleboring(nooffense).We'regoingtospiceitupabitbybuildinginsomeunethicalaccountingpractices.TheapplicationisnamedCooktheBooks,atermassociatedwithaccountingfraud.InvestopediaprovidesthefollowingdefinitionofCooktheBooks:

"CooktheBooksisanidiomdescribingfraudulentactivitiesperformedbycorporationsinordertofalsifytheirfinancialstatements.Typically,cookingthebooksinvolvesaugmentingfinancialdatatoyieldpreviouslynonexistentearnings.Examplesoftechniquesusedtocookthebooksinvolveacceleratingrevenues,delayingexpenses,manipulatingpensionplans,andimplementingsyntheticleases."

TheInvestopediapageathttps://www.investopedia.com/terms/c/cookthebooks.aspoffersdetailedexamplesofwhatconstitutescookingthebooks.We'lltakeasimpleapproachforourapplication.Wewillallowtheusertoenteratransactionwitharawandcookedamount.Therawamountrepresentstheactualamountofmoneythatwaseitherdepositedorwithdrawn,whilethecookedamountiswhateveryoneelsewillsee.Theapplicationwillgeneratepiechartsthatdisplayexpensesandincomebycategoryforeithertheraworcookedtransactions.Theuserwillbeabletoeasilytogglebetweenthetwoviews.Theapplicationconsistsofthefollowingcomponents:

TabsforswitchingbetweentransactionsandchartsTablethatdisplaystransactionsButtonsthatallowausertoadd,edit,orremoveatransactionModaldialogforadding/updatingatransactionPiechartstodisplaytheincome/expensesbycategory

JavaScriptlibrariesusedTheJavaScriptportionoftheapplicationwilluseseverallibrariesservedfromaCDN.Itwillalsouseonelocallyinstalledlibrarytowatchforchangesinthecode.Thefollowingsectionswilldescribeeachlibraryanditspurposeintheapplication.

VueVueisaJavaScriptframeworkthatallowsyoutosplitanapplicationintoindividualcomponentsforeaseofdevelopmentanddebugging.We'reusingittoavoidhavingonemonolithicJavaScriptfilewithallofourapplicationlogicandanothermonolithicHTMLfilewiththeentireUI.Vuewaschosenbecauseitdoesn'trequiretheaddedcomplexityofabuildsystemandallowsustouseHTML,CSS,andJavaScriptwithouthavingtodoanytranspiling.Theofficialwebsiteishttps://vuejs.org.

UIkitUIkitisthefrontendframeworkwewillusetoaddstylingandlayouttoourapplication.Therearedozensofalternatives,likeBootstraporBulma,thatoffercomparablecomponentsandfunctionality.ButIchoseUIkitbecauseofthehelpfulutilityclassesandaddedJavaScriptfunctionality.Youcanviewthedocumentationathttps://getuikit.com.

LodashLodashisanexcellentutilitylibrarythatprovidesmethodsforperformingcommonactionsinJavaScriptthataren'talreadybuiltintothelanguage.Wewilluseittoperformcalculationsandmanipulatethetransactionsdata.Documentationandinstallationinstructionscanbefoundathttps://lodash.com.

Data-drivendocumentsData-drivendocuments(D3)isamulti-facetedlibrarythatallowsyoutotranslatedataintoimpressivevisualizations.D3'sAPIconsistsofseveralmodulesthatrangefromarraymanipulationtochartingandtransitions.WewilluseD3primarilytocreatethepiecharts,butwe'llalsotakeadvantageofsomeoftheutilitymethodsitprovides.Youcanfindmoreinformationathttps://d3js.org.

OtherlibrariesInordertodisplaycurrencyvaluesinthecorrectformatandensuretheuserentersavaliddollaramount,wewillutilizetheaccounting.js(http://openexchangerates.github.io/accounting.js)andvue-numeric(https://kevinongko.github.io/vue-numeric)libraries.Tosimplifydevelopment,we'llsetupabasicnpmprojectandusebrowser-sync(https://www.browsersync.io)toimmediatelyseecodechangesreflectedintherunningapplication.

CandthebuildprocessTheapplicationusesCsincewe'reperformingsimplecalculationswithbasicalgebra.Itwouldn'tmakesensetouseC++inthiscase.ThatwouldintroducetheaddedstepofensuringthefunctionsweneedtocallfromJavaScriptarewrappedinanexternblock.We'llwritethecalculationfunctionsinasingleCfileandcompileitdowntoasingleWasmmodule.WecancontinuetouseVSCode'sTasksfunctionalitytoperformthebuild,buttheargumentswillneedtobeupdatedsincewe'llonlycompileasinglefile.Let'smoveontoprojectconfiguration.

SettinguptheprojectWebAssemblyhasn'tbeenaroundlongenoughtohaveestablishedbestpracticeswithregardtofolderstructure,filenamingconventions,andsoon.IfyouweretosearchforbestpracticesforC/C++orJavaScriptprojects,you'dencounteragreatdealofconflictingadviceandstronglyheldopinions.Withthatinmind,let'sspendthissectionsettingupourprojectwiththerequiredconfigurationfiles.

Thecodeforthisprojectislocatedinthe/chapter-07-cook-the-booksfolderinthelearn-webassemblyrepository.YoumusthavethiscodeavailablewhenwegettotheJavaScriptportionoftheapplication.Iwon'tbeprovidingthesourcecodeforalloftheVuecomponentsinthebook,soyouneedtocopythemfromtherepository.

ConfiguringforNode.jsIntheinterestofkeepingtheapplicationassimpleaspossible,we'llavoidabuild/bundlingtoollikeWebpackorRollup.js.Thisallowsustocutdownonthenumberofrequireddependenciesandensuresthatanyissuesyourunintoaren'tcausedbyabreakingchangeinabuilddependency.

We'llcreateaNode.jsprojectbecauseitallowsustorunscriptsandinstalladependencylocallyfordevelopmentpurposes.We'veusedthe/book-examplesfolderuptothispoint,butwe'llcreateanewprojectfolderoutsideof/book-examplestoconfigureadifferentdefaultbuildtaskinVSCode.Openaterminal,cdintothedesiredfolder,andenterthefollowingcommands:

//Createanewdirectoryandcdintoit:

mkdircook-the-books

cdcook-the-books

//Createapackage.jsonfilewithdefaultvalues

npminit-y

The-ycommandforgoesthepromptsandpopulatesthepackage.jsonfilewithsensibledefaults.Oncecompleted,runthefollowingcommandtoinstallbrowser-sync:

npminstall-Dbrowser-sync@^2.24.4

The-Disoptionalandindicatesthatthelibraryisadevelopmentdependency.Youwouldusethe-Dflagifyouwerebuildinganddistributingtheapplication,soIincludedittoadheretocommonpractice.I'drecommendinstallingthatspecificversiontoensurethestartscriptrunswithoutanyissues.Afterbrowser-syncinstalls,addthefollowingentrytothescriptsentryinthepackage.jsonfile:

...

"scripts":{

...

"start":"browser-syncstart--server\"src\"--files\"src/**\"--single--no-open--port4000"

},

Ifyourunnpminitwiththe-yflag,thereshouldbeanexistingscriptnamedtest,whichIomittedforclarity.Ifyoudidn'trunitwiththe-yflag,youmayneedtocreatethescriptsentry.

Youcanpopulatethe"description"and"author"keysifdesired.Thefileshouldend

uplookingsimilartothis:

{

"name":"cook-the-books",

"version":"1.0.0",

"description":"ExampleapplicationforLearnWebAssembly",

"main":"src/index.js",

"scripts":{

"start":"browser-syncstart--server\"src\"--files\"src/**\"--single--no-open--port4000",

"test":"echo\"Error:notestspecified\"&&exit1"

},

"keywords":[],

"author":"MikeRourke",

"license":"MIT",

"devDependencies":{

"browser-sync":"^2.24.4"

}

}

Ifyouomitthe--no-openflagfromthestartscript,thebrowserwillopenautomatically.Theflagwasincludedtopreventissueswithusersrunninginaheadlessenvironment.

AddingfilesandfoldersCreatetwonewfolderswithintherootfolder:/liband/src.TheJavaScript,HTML,CSS,andWasmfileswillbelocatedinthe/srcfolderwhiletheCfilewillbein/lib.Ionlywanttoincludefilesthatareusedbythewebapplicationin/src.We'llneverusetheCfiledirectlyfromtheapplication,onlythecompiledoutput.

Copythe/.vscodefolderfromyour/book-examplesprojectintotherootfolder.Thiswillensureyou'reusingtheexistingC/C++settingsandgiveyouagoodstartingpointforthebuildtask.

Ifyou'reusingmacOSorLinux,you'llhavetousetheterminaltocopythefolder;youcanaccomplishthisbyrunningthecp-rcommand.

ConfiguringthebuildstepWeneedtomodifythedefaultbuildstepinthe/.vscode/tasks.jsonfiletoaccommodateourupdatedworkflow.Theargumentsforthebuildstepweusedinour/book-examplesprojectallowedustocompilewhicheverfilewascurrentlyactiveintheeditor.Italsooutputthe.wasmfileintothesamefolderasthesourceCfile.However,thisconfigurationdoesn'tmakesenseforthisproject.We'llalwayscompilethesameCfilethatisoutputtothecompiled.wasmfileinaspecificfolder.Toaccomplishthis,updatetheargsarrayintheBuildtaskin/.vscode/tasks.jsonwiththefollowingcontents:"args":["${workspaceFolder}/lib/main.c","-Os","-s","WASM=1","-s","SIDE_MODULE=1","-s","BINARYEN_ASYNC_COMPILATION=0","-o","${workspaceFolder}/src/assets/main.wasm"],

Wechangedtheinputandoutputpaths,whicharethefirstandlastelementsintheargsarray.Nowbotharestaticpathsthatalwayscompileandoutputthesamefilesregardlessofwhichfileisopenintheactiveeditor.

SettingupamockAPIWeneedsomemockdataandameansofpersistinganyupdates.IfyoustorethedatalocallyinaJSONfile,anychangesyoumaketothetransactionswillbelostassoonasyourefreshthepage.WecouldsetupalocalserverwithalibrarylikeExpress,mockadatabase,writeroutes,andsoon.Butinsteadwe'regoingtotakeadvantageoftheexcellentdevelopmenttoolingavailableonline.Theonlinetoojsonstore.ioisallowsyoutostoreJSONdataforsmallprojectsandprovidesendpointsoutofthebox.TakethefollowingstepstogetyourmockAPIupandrunning:

1. Navigatetohttps://www.jsonstore.io/andpresstheCopybuttontocopytheendpointtoyourclipboard;thisistheendpointyou'llbemakingHTTPrequeststo.

2. GototheJSFiddleathttps://jsfiddle.net/mikerourke/cta0km6d,pasteyourjsonstore.ioendpointintotheinput,andpressthePopulateDatabutton.

3. Openupanewtabandpasteyourjsonstore.ioendpointintheaddressbarandadd/transactionstotheendoftheURLandpressEnter.IfyouseethecontentsoftheJSONfileinyourbrowser,theAPIsetupwassuccessful.

Keepthatjsonstore.ioendpointhandy—you'llneeditwhenwebuildtheJavaScriptportionoftheapp.

DownloadingtheCstdlibWasmWeneedthemalloc()andfree()functionsfromC'sstandardlibraryforthefunctionalityinourCcode.WebAssemblydoesn'thavethesefunctionsbuiltin,soweneedtoprovideourownimplementation.

Fortunately,someonehasalreadybuiltthatforus;wejustneedtodownloadthemoduleandincludeitintheinstantiationstep.ThemodulecanbedownloadedfromGuyBedford'swasm-stdlib-hackGitHubrepositoryathttps://github.com/guybedford/wasm-stdlib-hack.Youneedthememory.wasmfilefromthe/distfolder.Oncethefileisdownloaded,createafoldernamed/assetsinthe/srcfolderofyourprojectandcopythememory.wasmfilethere.

Youcancopythememory.wasmfilefromthe/chapter-07-cook-the-books/src/assetsfolderofthelearn-webassemblyrepositoryinsteadofdownloadingitfromGitHub.

ThefinalresultAfterperformingthesesteps,yourprojectshouldlooklikethis:

├──/.vscode

│├──tasks.json

│└──c_cpp_properties.json

├──/lib

├──/src

│└──/assets

│└──memory.wasm

├──package.json

└──package-lock.json

BuildingtheCportionTheCportionoftheapplicationwillaggregatetransactionandcategoryamounts.ThecalculationsweperforminCcouldbedonejustaseasilyinJavaScript,butWebAssemblyisidealforcomputation.We'lldivedeeperintomorecomplexusageofC/C++inChapter8,PortingaGamewithEmscripten,butfornowwe'retryingtolimitourscopetowhatcanbedonewithintheconfinesoftheCoreSpecification.Inthissection,we'llwritesomeCcodetodemonstratehowtointegrateWebAssemblywithawebapplicationwithouttheuseofEmscripten.

OverviewWewillwritesomeCfunctionsthatcalculatethegrandtotalsaswellastheendingbalancesforrawandcookedtransactions.Inadditiontocalculatingthegrandtotals,weneedtocalculatethetotalsforeachcategoryfordisplayinthepiecharts.AllofthesecalculationswillbeperformedinasingleCfileandcompileddowntoasingleWasmfilethatwillbeinstantiatedwhentheapplicationloads.Ccanbealittledauntingfortheuninitiated,soourcodewillbesacrificingsomeefficiencyforthesakeofclarity.I'dliketotakeamomenttoapologizetotheC/C++programmersreadingthisbook;you'renotgoingtolikewhatyouC.

Inordertoperformcalculationsdynamically,weneedtoallocateanddeallocatememoryastransactionsareaddedanddeleted.Toaccomplishthis,we'lluseadoublylinkedlist.Adoublylinkedlistisadatastructurethatallowsustoremoveitemsornodesinsidealistandaddandeditnodesasneeded.Nodesareaddedusingmalloc()andremovedusingfree(),bothofwhichareprovidedbythememory.wasmmoduleyoudownloadedintheprevioussection.

AnoteregardingworkflowTheorderofoperationsintermsofdevelopmentdoesn'treflecthowyouwouldnormallybuildanapplicationthatusesWebAssembly.TheworkflowwouldconsistofjumpingbetweenC/C++andJavaScripttoachievethedesiredresults.Inthiscase,thefunctionalitythatwe'reoffloadingfromJavaScriptintoWebAssemblyisalreadyknown,sowe'llwritetheCcodeupfront.

CfilecontentsLet'swalkthrougheachsectionoftheCfile.Createafileinthe/libfoldernamedmain.candpopulateitwiththefollowingcontentsineachsection.It'llbeeasiertocomprehendwhat'shappeningintheCfileifwebreakitintosmallerchunks.Let'sstartwiththeDeclarationssection.

DeclarationsThefirstsectioncontainsdeclarationswewillusetocreateandtraversethedoublylinkedlist,asfollows:

#include<stdlib.h>

structNode{

intid;

intcategoryId;

floatrawAmount;

floatcookedAmount;

structNode*next;

structNode*prev;

};

typedefenum{

RAW=1,

COOKED=2

}AmountType;

structNode*transactionsHead=NULL;

structNode*categoriesHead=NULL;

TheNodestructisusedtorepresentatransactionorcategory.ThetransactionsHeadandcategoriesHeadnodeinstancesrepresentthefirstnodeineachlinkedlistwe'lluse(onefortransactionsandoneforcategories).TheAmountTypetheenumisn'trequired,butwe'lldiscusshowit'susefulwhenwegettothesectionofcodethatutilizesit.

LinkedlistoperationsThesecondsectioncontainsthetwofunctionsusedtoaddanddeletenodesfromthelinkedlist:

voiddeleteNode(structNode**headNode,structNode*delNode){

//Basecase:

if(*headNode==NULL||delNode==NULL)return;

//Ifnodetobedeletedisheadnode:

if(*headNode==delNode)*headNode=delNode->next;

//ChangenextonlyifnodetobedeletedisNOTthelastnode:

if(delNode->next!=NULL)delNode->next->prev=delNode->prev;

//ChangeprevonlyifnodetobedeletedisNOTthefirstnode:

if(delNode->prev!=NULL)delNode->prev->next=delNode->next;

//Finally,freethememoryoccupiedbydelNode:

free(delNode);

}

voidappendNode(structNode**headNode,intid,intcategoryId,

floatrawAmount,floatcookedAmount){

//1.Allocatenode:

structNode*newNode=(structNode*)malloc(sizeof(structNode));

structNode*last=*headNode;//UsedinStep5

//2.Populatewithdata:

newNode->id=id;

newNode->categoryId=categoryId;

newNode->rawAmount=rawAmount;

newNode->cookedAmount=cookedAmount;

//3.Thisnewnodeisgoingtobethelastnode,somakenextNULL:

newNode->next=NULL;

//4.Ifthelinkedlistisempty,thenmakethenewnodeashead:

if(*headNode==NULL){

newNode->prev=NULL;

*headNode=newNode;

return;

}

//5.Otherwise,traversetillthelastnode:

while(last->next!=NULL){

last=last->next;

}

//6.Changethenextoflastnode:

last->next=newNode;

//7.Makelastnodeaspreviousofnewnode:

newNode->prev=last;

}

Thecommentswithinthecodedescribewhat'shappeningateachstep.WhenweneedtoaddaNodetothelist,wehavetoallocatethememorytakenupbythestructNodeusingmalloc()andappendittothelastnodeinthelinkedlist.Ifweneedtodeleteanode,wehavetoremoveitfromthelinkedlistanddeallocatethememorythatthenodewasusingbycallingthefree()function.

transactionsoperationsThethirdsectioncontainsfunctionstoadd,edit,andremovetransactionsfromthetransactionslinkedlist,asfollows:

structNode*findNodeById(intid,structNode*withinNode){

structNode*node=withinNode;

while(node!=NULL){

if(node->id==id)returnnode;

node=node->next;

}

returnNULL;

}

voidaddTransaction(intid,intcategoryId,floatrawAmount,

floatcookedAmount){

appendNode(&transactionsHead,id,categoryId,rawAmount,cookedAmount);

}

voideditTransaction(intid,intcategoryId,floatrawAmount,

floatcookedAmount){

structNode*foundNode=findNodeById(id,transactionsHead);

if(foundNode!=NULL){

foundNode->categoryId=categoryId;

foundNode->rawAmount=rawAmount;

foundNode->cookedAmount=cookedAmount;

}

}

voidremoveTransaction(intid){

structNode*foundNode=findNodeById(id,transactionsHead);

if(foundNode!=NULL)deleteNode(&transactionsHead,foundNode);

}

TheappendNode()anddeleteNode()functionswereviewedintheprevioussectionaren'tintendedtobecalledfromtheJavaScriptcode.Instead,callstoaddTransaction(),editTransaction(),andremoveTransaction()areusedtoupdatethelocallinkedlist.TheaddTransaction()functioncallstheappendNode()functiontoaddthedatapassedinasargumentstoanewnodeinthelocallinkedlist.TheremoveTransaction()callsthedeleteNode()functiontodeletethecorrespondingtransactionnode.ThefindNodeById()functionisusedtodeterminewhichnodeneedstobeupdatedordeletedwithinthelinkedlistbasedonthespecifiedID.

transactionscalculationsThefourthsectioncontainsfunctionstocalculatethegrandtotalsandfinalbalancesforrawandcookedtransactions,asfollows:

voidcalculateGrandTotals(float*totalRaw,float*totalCooked){

structNode*node=transactionsHead;

while(node!=NULL){

*totalRaw+=node->rawAmount;

*totalCooked+=node->cookedAmount;

node=node->next;

}

}

floatgetGrandTotalForType(AmountTypetype){

floattotalRaw=0;

floattotalCooked=0;

calculateGrandTotals(&totalRaw,&totalCooked);

if(type==RAW)returntotalRaw;

if(type==COOKED)returntotalCooked;

return0;

}

floatgetFinalBalanceForType(AmountTypetype,floatinitialBalance){

floattotalForType=getGrandTotalForType(type);

returninitialBalance+totalForType;

}

TheAmountTypeenumwedeclaredinthedeclarationssectionisusedheretoavoidmagicnumbers.Itmakesiteasytorememberthat1representsrawtransactionsand2representscookedtransactions.ThegrandtotalsforbothrawandcookedtransactionsarecalculatedinthecalculateGrandTotals()function,eventhoughwe'reonlyaskingforonetypeingetGrandTotalForType().SincewecanonlyreturnasinglevaluefromaWasmfunction,weenduploopingthroughallofthetransactionstwicewhenwecallgetGrandTotalForType()forbothrawandcookedtransactions.Witharelativelysmallamountoftransactionsandthesimplicityofthecalculation,thisdoesn'tpresentanyissues.ThegetFinalBalanceForType()returnsthegrandtotalplusthespecifiedinitialBalance.You'llseethisinactionwhenweaddtheabilitytochangeinitialbalancesinthewebapplication.

CategorycalculationsThefifthandfinalsectioncontainsfunctionstocalculatetotalsbycategory,whichwe'llutilizeinthepiecharts,asfollows:

voidupsertCategoryNode(intcategoryId,floattransactionRaw,

floattransactionCooked){

structNode*foundNode=findNodeById(categoryId,categoriesHead);

if(foundNode!=NULL){

foundNode->rawAmount+=transactionRaw;

foundNode->cookedAmount+=transactionCooked;

}else{

appendNode(&categoriesHead,categoryId,categoryId,transactionRaw,

transactionCooked);

}

}

voidbuildValuesByCategoryList(){

structNode*node=transactionsHead;

while(node!=NULL){

upsertCategoryNode(node->categoryId,node->rawAmount,

node->cookedAmount);

node=node->next;

}

}

voidrecalculateForCategories(){

categoriesHead=NULL;

buildValuesByCategoryList();

}

floatgetCategoryTotal(AmountTypetype,intcategoryId){

//Ensurethecategorytotalshavebeencalculated:

if(categoriesHead==NULL)buildValuesByCategoryList();

structNode*categoryNode=findNodeById(categoryId,categoriesHead);

if(categoryNode==NULL)return0;

if(type==RAW)returncategoryNode->rawAmount;

if(type==COOKED)returncategoryNode->cookedAmount;

return0;

}

ThebuildValuesByCategoryList()functioniscalledwhenevertherecalculateForCategories()orgetCategoryTotal()functionsarecalled.Thefunctionloopsthroughallofthetransactionsinthetransactionslinkedlistandcreatesanodeinaseparatelinkedlistforeachcorrespondingcategorywiththeaggregatedrawandtotalamounts.TheupsertCategoryNode()functionlooksforanodethatcorrespondstothecategoryIdinthecategorieslinkedlist.Ifitfindsit,therawandcookedtransactionamountsareaddedtotheexistingamountsonthatnode,otherwiseanewnodeiscreatedforsaidcategory.The

recalculateForCategories()functioniscalledtoensurethecategorytotalsareuptodatewithanytransactionschanges.

CompilingtoWasmAfterpopulatingthefile,weneedtocompileitdowntoWasmforuseintheJavaScriptportionoftheapplication.RunthebuildtaskbyselectingTasks|RunBuildTask...fromthemenuorusingthekeyboardshortcutCmd/Ctrl+Shift+B.Ifthebuildwassuccessful,you'llseeafilenamedmain.wasminthe/src/assetsfolder.Ifanerroroccurred,theterminalshouldprovidedetailsonhowtoresolveit.

Ifyou'renotusingVSCode,openaterminalinstanceinthe/cook-the-booksfolderandrunthefollowingcommand:emcclib/main.c-Os-sWASM=1-sSIDE_MODULE=1-sBINARYEN_ASYNC_COMPILATION=0-osrc/assets/main.wasm

That'sitfortheCcode.Let'smoveontotheJavaScriptportion.

BuildingtheJavaScriptportionTheJavaScriptportionoftheapplicationpresentsthetransactionsdatatotheuserandallowsthemtoeasilyadd,edit,andremovetransactions.TheapplicationissplitacrossseveralfilestosimplifythedevelopmentprocessandusesthelibrariesdescribedintheJavaScriptlibrariesusedsectionofthischapter.Inthissection,wewillbuildtheapplicationstepbystep,startingwiththeAPIandglobalstateinteractionlayer.We'llwritefunctionstoinstantiateandinteractwithourWasmmoduleandreviewtheVuecomponentsrequiredtobuildtheuserinterface.

OverviewTheapplicationisbrokendownintocontextstosimplifythedevelopmentprocess.We'llbuildtheapplicationfromthebottomuptoensurewedon'thavetobouncebackandforthbetweenthedifferentcontextswhenwritingcode.We'llstartwiththeWasminteractioncode,thenmoveontotheglobalstoreandAPIinteraction.I'lldescribethepurposeofeachVuecomponent,butthesourcecodewillonlybeprovidedforaselectfew.Ifyou'refollowingalongandwishtoruntheapplicationlocally,you'llneedtocopythe/src/componentsfolderfromthe/chapter-07-cook-the-booksfolderinthelearn-webassemblyrepositoryintothe/srcfolderofyourproject.

AnoteaboutbrowsercompatibilityBeforewestartwritinganycode,youmustensureyourbrowsersupportsthenewerJavaScriptfeatureswe'lluseintheapplication.YourbrowserhastosupportESModules(importandexport),theFetchAPI,andasync/await.YouneedatleastVersion61ofGoogleChromeorVersion60ofFirefox.Youcancheckwhichversionyou'recurrentlyusingbyselectingAboutChromeorAboutFirefoxfromthemenubar.I'mcurrentlyrunningtheapplicationwithChromeVersion67andFirefoxVersion61withoutanyissues.

CreatingaWasminstanceininitializeWasm.jsYoushouldhavetwocompiledWasmfilesinthe/src/assetsfolderofyourproject:main.wasmandmemory.wasm.Sinceweneedtoutilizethemalloc()andfree()functionsexportedfrommemory.wasminthemain.wasmcode,ourloadingcodeisgoingtolookdifferentfromtheearlierexamples.Createafileinthe/src/storefoldernamedinitializeWasm.jsandpopulateitwiththefollowingcontents:

/**

*Returnsanarrayofcompiled(notinstantiated!)Wasmmodules.

*Weneedthemain.wasmfilewecreated,aswellasthememory.wasmfile

*thatallowsustouseCfunctionslikemalloc()andfree().

*/

constfetchAndCompileModules=()=>

Promise.all(

['../assets/main.wasm','../assets/memory.wasm'].map(fileName=>

fetch(fileName)

.then(response=>{

if(response.ok)returnresponse.arrayBuffer();

thrownewError(`UnabletofetchWebAssemblyfile:${fileName}`);

})

.then(bytes=>WebAssembly.compile(bytes))

)

);

/**

*Returnsaninstanceofthecompiled"main.wasm"file.

*/

constinstantiateMain=(compiledMain,memoryInstance,wasmMemory)=>{

constmemoryMethods=memoryInstance.exports;

returnWebAssembly.instantiate(compiledMain,{

env:{

memoryBase:0,

tableBase:0,

memory:wasmMemory,

table:newWebAssembly.Table({initial:16,element:'anyfunc'}),

abort:console.log,

_consoleLog:value=>console.log(value),

_malloc:memoryMethods.malloc,

_free:memoryMethods.free

}

});

};

/**

*Compilesandinstantiatesthe"memory.wasm"and"main.wasm"filesand

*returnsthe`exports`propertyfrommain's`instance`.

*/

exportdefaultasyncfunctioninitializeWasm(){

constwasmMemory=newWebAssembly.Memory({initial:1024});

const[compiledMain,compiledMemory]=awaitfetchAndCompileModules();

constmemoryInstance=awaitWebAssembly.instantiate(compiledMemory,{

env:{

memory:wasmMemory

}

});

constmainInstance=awaitinstantiateMain(

compiledMain,

memoryInstance,

wasmMemory

);

returnmainInstance.exports;

}

Thefile'sdefaultexportfunction,initializeWasm(),performsthefollowingsteps:

1. CreateanewWebAssembly.Memoryinstance(wasmMemory).2. CallthefetchAndCompileModules()functiontogetaWebAssembly.Moduleinstancefor

memory.wasm(compiledMemory)andmain.wasm(compiledMain).3. InstantiatecompiledMemory(memoryInstance)andpassthewasmMemoryintothe

importObj.4. PasscompiledMain,memoryInstance,andwasmMemoryintotheinstantiateMain()

function.5. InstantiatecompiledMainandpasstheexportedmalloc()andfree()functions

frommemoryInstancealongwithwasmMemoryintotheimportObj.6. ReturntheexportspropertyoftheInstancereturnedfrominstantiateMain

(mainInstance).

Asyoucansee,theprocessismorecomplexwhenyouhavedependencieswithinWasmmodules.

YoumayhavenoticedthatthemallocandfreemethodsonthememoryInstanceexportspropertyweren'tprefixedwithanunderscore.Thisisbecausethememory.wasmfilewascompiledusingLLVMwithoutEmscripten,whichdoesn'taddthe_.

importinitializeWasmfrom'./initializeWasm.js';<br/><br/>/**<br/>*ClassusedtowrapthefunctionalityfromtheWasmmodule(rather<br/>*thanaccessitdirectlyfromtheVuecomponentsorstore).<br/>*@class<br/>*/<br/>exportdefaultclassWasmTransactions{<br/>constructor(){<br/>this.instance=null;<br/>this.categories=[];<br/>}<br/><br/>asyncinitialize(){<br/>this.instance=awaitinitializeWasm();<br/>returnthis;<br/>}<br/><br/>getCategoryId(category){<br/>returnthis.categories.indexOf(category);<br/>}<br/><br/>//Ensurestherawandcookedamountshavethepropersign(withdrawals<br/>//arenegativeanddepositsarepositive).<br/>getValidAmounts(transaction){<br/>const{rawAmount,cookedAmount,type}=transaction;<br/>constgetAmount=amount=><br/>type==='Withdrawal'?-Math.abs(amount):amount;<br/>return{<br/>validRaw:getAmount(rawAmount),<br/>validCooked:getAmount(cookedAmount)<br/>};<br/>}<br/><br/>//AddsthespecifiedtransactiontothelinkedlistintheWasmmodule.<br/>addToWasm(transaction){<br/>const{id,category}=transaction;<br/>const{validRaw,validCooked}=this.getValidAmounts(transaction);<br/>constcategoryId=this.getCategoryId(category);<br/>this.instance._addTransaction(id,categoryId,validRaw,validCooked);<br/>}<br/><br/>//UpdatesthetransactionnodeintheWasmmodule:<br/>editInWasm(transaction){<br/>const{id,category}=transaction;<br/>const{validRaw,validCooked}=this.getValidAmounts(transaction);<br/>constcategoryId=this.getCategoryId(category);<br/>this.instance._editTransaction(id,categoryId,validRaw,validCooked);<br/>}<br/><br/>//RemovesthetransactionnodefromthelinkedlistintheWasmmodule:<br/>removeFromWasm(transactionId){<br/>this.instance._removeTransaction(transactionId);<br/>}<br/><br/>//PopulatesthelinkedlistintheWasmmodule.Thecategoriesare<br/>//neededtosetthecategoryIdintheWasmmodule.<br/>populateInWasm(transactions,categories){<br/>this.categories=categories;<br/>transactions.forEach(transaction=>this.addToWasm(transaction));<br/>}<br/><br/>//Returnsthebalanceforrawandcookedtransactionsbasedonthe<br/>//specifiedinitialbalances.<br/>getCurrentBalances(initialRaw,initialCooked){<br/>constcurrentRaw=this.instance._getFinalBalanceForType(<br/>AMOUNT_TYPE.raw,<br/>initialRaw<br/>);<br/>constcurrentCooked=this.instance._getFinalBalanceForType(<br/>AMOUNT_TYPE.cooked,<br/>initialCooked<br/>);<br/>return{currentRaw,currentCooked};<br/>}<br/><br/>//Returnsanobjectthathascategorytotalsforallincome(deposit)<br/>//andexpense(withdrawal)transactions.<br/>getCategoryTotals(){<br/>//This

isdonetoensurethetotalsreflectthemostrecent<br/>//transactions:<br/>this.instance._recalculateForCategories();<br/>constcategoryTotals=this.categories.map((category,idx)=>({<br/>category,<br/>id:idx,<br/>rawTotal:this.instance._getCategoryTotal(AMOUNT_TYPE.raw,idx),<br/>cookedTotal:this.instance._getCategoryTotal(AMOUNT_TYPE.cooked,idx)<br/>}));<br/><br/>consttotalsByGroup={income:[],expenses:[]};<br/>categoryTotals.forEach(categoryTotal=>{<br/>if(categoryTotal.rawTotal<0){<br/>totalsByGroup.expenses.push(categoryTotal);<br/>}else{<br/>totalsByGroup.income.push(categoryTotal);<br/>}<br/>});<br/>returntotalsByGroup;<br/>}<br/>}

Whentheinitialize()functioniscalledonaninstanceoftheclass,thereturnvalueoftheinitializeWasm()functionisassignedtotheinstancepropertyoftheclass.Theclassmethodscallfunctionsfromthis.instanceand,ifapplicable,returnthedesiredresults.NotetheAMOUNT_TYPEobjectreferencedinthegetCurrentBalances()andgetCategoryTotals()functions.ThiscorrespondstotheAmountTypeenuminourCfile.TheAMOUNT_TYPEobjectisdeclaredgloballyinthe/src/main.jsfilewheretheapplicationisloaded.NowthatwehaveourWasminteractioncodewritten,let'smoveontoAPIinteractioncode.

//Pasteyourjsonstore.ioendpointhere(noendingslash):<br/>constAPI_URL='[JSONSTORE.IOENDPOINT]';<br/><br/>/**<br/>*WrapperforperformingAPIcalls.Wedon'twanttocallresponse.json()<br/>*eachtimewemakeafetchcall.<br/>*@param{string}endpointEndpoint(e.g."/transactions"tomakeAPIcallto<br/>*@param{Object}initFetchoptionsobjectcontaininganycustomsettings<br/>*@returns{Promise<*>}<br/>*@seehttps://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch<br/>*/<br/>constperformApiFetch=(endpoint='',init={})=><br/>fetch(`${API_URL}${endpoint}`,{<br/>headers:{<br/>'Content-type':'application/json'<br/>},<br/>...init<br/>}).then(response=>response.json());<br/><br/>exportconstapiFetchTransactions=()=><br/>performApiFetch('/transactions').then(({result})=><br/>/*<br/>*Theresponseobjectlookslikethis:<br/>*{<br/>*"result":{<br/>*"1":{<br/>*"category":"SalesRevenue",<br/>*...<br/>*},<br/>*"2":{<br/>*"category":"Hotels",<br/>*...<br/>*},<br/>*...<br/>*}<br/>*}<br/>*Weneedthe"1"and"2"valuesfordeletingoreditingexisting<br/>*records,sowestorethatinthetransactionrecordas"apiId".<br/>*/<br/>Object.keys(result).map(apiId=>({<br/>...result[apiId],<br/>apiId<br/>}))<br/>);<br/><br/>exportconstapiEditTransaction=transaction=><br/>performApiFetch(`/transactions/${transaction.apiId}`,{<br/>method:'POST',<br/>body:JSON.stringify(transaction)<br/>});<br/><br/>exportconstapiRemoveTransaction=transaction=><br/>performApiFetch(`/transactions/${transaction.apiId}`,{<br/>method:'DELETE'<br/>});<br/><br/>exportconstapiAddTransaction=transaction=><br/>performApiFetch(`/transactions/${transaction.apiId}`,{<br/>method:'POST',<br/>body:JSON.stringify(transaction)<br/>});

You'llneedthejsonstore.ioendpointyoucreatedintheSettinguptheprojectsectioninordertointeractwiththeAPI.Replace[JSONSTORE.IOENDPOINT]withyourjsonstore.ioendpoint.Ensuretheendpointdoesn'tendwithaforwardslashorthewordtransactions.

Managingglobalstateinstore.jsThefilethatmanagesglobalstateintheapplicationhasalotofmovingparts.Consequently,wewillbreakthecodedownintosmallerchunksandwalkthrougheachsectionindividually.Createafileinthe/src/storefoldernamedstore.jsandpopulateitwiththecontentsfromeachofthefollowingsections.

TheimportandstoredeclarationsThefirstsectioncontainsimportstatementsandthewasmandstatepropertiesontheexportedstoreobject,asfollows:

import{

apiFetchTransactions,

apiAddTransaction,

apiEditTransaction,

apiRemoveTransaction

}from'./api.js';

importWasmTransactionsfrom'./WasmTransactions.js';

exportconststore={

wasm:null,

state:{

transactions:[],

activeTransactionId:0,

balances:{

initialRaw:0,

currentRaw:0,

initialCooked:0,

currentCooked:0

}

},

...

AllAPIinteractionislimitedtothestore.jsfile.Sinceweneedtomanipulate,add,andsearchtransactions,alloftheexportedfunctionsfromapi.jsareimported.ThestoreobjectholdstheWasmTransactionsinstanceinthewasmpropertyandinitialstateinthestateproperty.Thevaluesinstatearereferencedinmultiplelocationsthroughouttheapplication.Thestoreobjectwillbeaddedtotheglobalwindowobjectwhentheapplicationloads,soallcomponentshaveaccesstotheglobalstate.

TransactionsoperationsThesecondsectioncontainsfunctionsthatmanagetransactionsintheWasminstance(throughtheWasmTransactionsinstance)andtheAPI,asfollows:

...

getCategories(){

constcategories=this.state.transactions.map(

({category})=>category

);

//Removeduplicatecategoriesandsortthenamesinascendingorder:

return_.uniq(categories).sort();

},

//PopulateglobalstatewiththetransactionsfromtheAPIresponse:

populateTransactions(transactions){

constsortedTransactions=_.sortBy(transactions,[

'transactionDate',

'id'

]);

this.state.transactions=sortedTransactions;

store.wasm.populateInWasm(sortedTransactions,this.getCategories());

this.recalculateBalances();

},

addTransaction(newTransaction){

//WeneedtoassignanewIDtothetransaction,sothisjustadds

//1tothecurrentmaximumtransactionID:

newTransaction.id=_.maxBy(this.state.transactions,'id').id+1;

store.wasm.addToWasm(newTransaction);

apiAddTransaction(newTransaction).then(()=>{

this.state.transactions.push(newTransaction);

this.hideTransactionModal();

});

},

editTransaction(editedTransaction){

store.wasm.editInWasm(editedTransaction);

apiEditTransaction(editedTransaction).then(()=>{

this.state.transactions=this.state.transactions.map(

transaction=>{

if(transaction.id===editedTransaction.id){

returneditedTransaction;

}

returntransaction;

}

);

this.hideTransactionModal();

});

},

removeTransaction(transaction){

consttransactionId=transaction.id;

store.wasm.removeFromWasm(transactionId);

//We'repassingthewholetransactionrecordintotheAPIcall

//forthesakeofconsistency:

apiRemoveTransaction(transaction).then(()=>{

this.state.transactions=this.state.transactions.filter(

({id})=>id!==transactionId

);

this.hideTransactionModal();

});

},

...

ThepopulateTransactions()functionfetchesallofthetransactionsfromtheAPIandloadsthemintotheglobalstateandtheWasminstance.ThecategorynamesareextrapolatedfromthetransactionsarrayinthegetCategories()function.TheresultsarepassedtotheWasmTransactionsinstancewhenstore.wasm.populateInWasm()iscalled.

TheaddTransaction(),editTransaction(),andremoveTransaction()functionsperformtheactionsthatcorrespondwiththeirnames.AllthreefunctionsmanipulatetheWasminstanceandupdatethedataontheAPIthroughafetchcall.Eachofthefunctionscallthis.hideTransactionModal()becausechangestoatransactioncanonlybemadethroughtheTransactionModalcomponent.Oncethechangeissuccessfullymade,themodalshouldclose.Let'slookattheTransactionModalmanagementcodenext.

...<br/>showTransactionModal(transactionId){<br/>this.state.activeTransactionId=transactionId||0;<br/>consttransactModal=document.querySelector('#transactionModal');<br/>UIkit.modal(transactModal).show();<br/>},<br/><br/>hideTransactionModal(){<br/>this.state.activeTransactionId=0;<br/>consttransactModal=document.querySelector('#transactionModal');<br/>UIkit.modal(transactModal).hide();<br/>},<br/><br/>getActiveTransaction(){<br/>const{transactions,activeTransactionId}=this.state;<br/>constfoundTransaction=transactions.find(transaction=><br/>transaction.id===activeTransactionId);<br/>returnfoundTransaction||{id:0};<br/>},<br/>...

TheshowTransactionModal()andhideTransactionModal()functionsshouldbeself-explanatory.Thehide()orshow()methodofUIkit.modal()iscalledontheDOMelementrepresentingtheTransactionModal.ThegetActiveTransaction()functionreturnsthetransactionrecordassociatedwiththeactiveTransactionIdvalueinglobalstate.

...<br/>updateInitialBalance(amount,fieldName){<br/>this.state.balances[fieldName]=amount;<br/>},<br/><br/>//Updatethe"balances"objectinglobalstatebasedonthecurrent<br/>//initialbalances:<br/>recalculateBalances(){<br/>const{initialRaw,initialCooked}=this.state.balances;<br/>const{currentRaw,currentCooked}=this.wasm.getCurrentBalances(<br/>initialRaw,<br/>initialCooked<br/>);<br/>this.state.balances={<br/>initialRaw,<br/>currentRaw,<br/>initialCooked,<br/>currentCooked<br/>};<br/>}<br/>};

TheupdateInitialBalance()functionsetsthepropertyvalueinthebalancesobjectinglobalstatebasedontheamountandfieldNamearguments.TherecalculateBalances()functionupdatesallofthefieldsonthebalancesobjecttoreflectanychangesmadetotheinitialbalancesortransactions.

StoreinitializationThefinalsectionofcodeinthefileinitializesthestore:

/**

*ThisfunctioninstantiatestheWasmmodule,fetchesthetransactions

*fromtheAPIendpoint,andloadsthemintostateandtheWasm

*instance.

*/

exportconstinitializeStore=async()=>{

constwasmTransactions=newWasmTransactions();

store.wasm=awaitwasmTransactions.initialize();

consttransactions=awaitapiFetchTransactions();

store.populateTransactions(transactions);

};

TheinitializeStore()functioninstantiatestheWasmmodule,fetchesalltransactionsfromtheAPI,andpopulatesthecontentsofstate.Thisfunctioniscalledfromtheapplicationloadingcodein/src/main.js,whichwe'llcoverinthenextsection.

Loadingtheapplicationinmain.jsWeneedanentrypointtoloadourapplication.Createafileinthe/srcfoldernamedmain.jsandpopulateitwiththefollowingcontents:

importAppfrom'./components/App.js';

import{store,initializeStore}from'./store/store.js';

//Thisallowsustousethe<vue-numeric>componentglobally:

Vue.use(VueNumeric.default);

//Createagloballyaccessiblestore(withouthavingtopassitdown

//asprops):

window.$store=store;

//SincewecanonlypassnumbersintoaWasmfunction,theseflags

//representtheamounttypewe'retryingtocalculate:

window.AMOUNT_TYPE={

raw:1,

cooked:2

};

//AfterfetchingthetransactionsandinitializingtheWasmmodule,

//rendertheapp.

initializeStore()

.then(()=>{

newVue({render:h=>h(App),el:'#app'});

})

.catch(err=>{

console.error(err);

});

ThisfileisloadedafterthelibrariesarefetchedandloadedfromCDNsin/src/index.html.WeusetheglobalVueobjecttospecifythatwewanttousetheVueNumericcomponent.Weaddthestoreobjectexportedfrom/store/store.jstowindowas$store.Thisisn'tthemostrobustsolution,butwillbesufficientgiventhescopeoftheapplication.Ifyouwerecreatingaproductionapplication,you'dusealibrarylikeVuexorReduxforglobalstatemanagement.We'llforegothisapproachintheinterestofkeepingthingssimple.

WealsoaddedAMOUNT_TYPEtothewindowobject.ThiswasdonetoensuretheentireapplicationcanreferencetheAMOUNT_TYPEvalue,ratherthanspecifyamagicnumber.Aftervaluesareassignedtowindow,theinitializeStore()functioniscalled.IftheinitializeStore()functionfiredsuccessfully,anewVueinstanceiscreatedtorendertheapplication.Let'saddthewebassetsnext,thenmoveontotheVuecomponents.

AddingthewebassetsBeforewestartaddingVuecomponentstotheapplication,let'screatetheHTMLandCSSfilesthathouseourmarkupandstyles.Createafileinthe/srcfoldernamedindex.htmlandpopulateitwiththefollowingcontents:<!doctypehtml><htmllang="en-us"><head><title>CooktheBooks</title><linkrel="stylesheet"type="text/css"href="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-rc.6/css/uikit.min.css"/><linkrel="stylesheet"type="text/css"href="styles.css"/><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-rc.6/js/uikit.min.js"></script><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-rc.6/js/uikit-icons.min.js"></script><scriptsrc="https://unpkg.com/[email protected]/dist/accounting.umd.js"></script><scriptsrc="https://unpkg.com/[email protected]/lodash.min.js"></script><scriptsrc="https://unpkg.com/[email protected]/dist/d3.min.js"></script><scriptsrc="https://unpkg.com/[email protected]/dist/vue.min.js"></script><scriptsrc="https://unpkg.com/[email protected]/dist/vue-numeric.min.js"></script><scriptsrc="main.js"type="module"></script></head><body><divid="app"></div></body></html>

We'reonlyusingtheHTMLfiletofetchlibrariesfromCDNs,specifya<div>thatVuecanrenderto,andloadmain.jstostarttheapplication.Notethetype="module"attributeonthefinal<script>element.ThisallowsustouseESmodules

throughoutourapplication.Nowlet'saddtheCSSfile.Createafileinthe/srcfoldernamedstyles.cssandpopulateitwiththefollowingcontents:@importurl("https://fonts.googleapis.com/css?family=Quicksand");

:root{--blue:#2889ed;}

*{font-family:"Quicksand",Helvetica,Arial,sans-serif!important;}

#app{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;}

.addTransactionButton{color:white;height:64px;width:64px;background:var(--blue);position:fixed;bottom:24px;right:24px;}

.addTransactionButton:hover{color:white;background-color:var(--blue);opacity:.6;}

.errorText{color:white;font-size:36px;}

.appHeader{height:80px;margin:0;}

.balanceEntry{font-size:2rem;}

.tableAmount{white-space:pre;}

Thisfilehasonlyafewclassesbecausemostofthestylingwillbehandledatthecomponentlevel.Inthenextsection,we'llreviewtheVuecomponentsthatmakeupourapplication.

CreatingtheVuecomponentsWithVue,wecancreateseparatecomponentsthatencapsulatetheirownfunctionality,thencomposethesecomponentstobuildanapplication.Thismakesdebugging,extensibility,andchangemanagementmucheasierthanstoringtheapplicationinasinglemonolithicfile.

Theapplicationusesasingle-component-per-filedevelopmentmethodology.Beforewestartreviewingthecomponentfiles,let'slookatthefinishedproduct.ThefollowingscreenshotisoftheapplicationwiththeTRANSACTIONStabselected:

RunningtheapplicationwithTRANSACTIONStabvisible

Here'sascreenshotoftheapplicationwiththeCHARTStabselected:

RunningtheapplicationwiththeCHARTStabvisible

importSomeComponentfrom'./SomeComponent.js';<br/><br/>exportdefault{<br/>name:'dummy-component',<br/><br/>//Propspassedfromothercomponents:<br/>props:{<br/>label:String,<br/>},<br/><br/>//OtherVuecomponentstorenderwithinthetemplate:<br/>components:{<br/>SomeComponent<br/>},<br/><br/>//Usedtostorelocaldata/state:<br/>data(){<br/>return{<br/>amount:0<br/>}<br/>},<br/><br/>//Usedtostorecomplexlogicthatoutsideofthe`template`:<br/>computed:{<br/>negativeClass(){<br/>return{<br/>'negative':this.amount<0<br/>};<br/>}<br/>},<br/><br/>//Methodsthatcanbeperformedwithinthecomponent:<br/>methods:{<br/>addOne(){<br/>this.amount+=1;<br/>}<br/>},<br/><br/>//Performactionsifthelocaldatachanges:<br/>watch:{<br/>amount(val,oldVal){<br/>console.log(`New:${val}|Old:${oldVal}`);<br/>}<br/>},<br/><br/>//ContainstheHTMLtorenderthecomponent:<br/>template:`<br/><div><br/><some-component></some-component><br/><labelfor="someAmount">{{label}}</label><br/><input<br/>id="someAmount"<br/>:class="negativeClass"<br/>v-model="amount"<br/>type="number"<br/>/><br/><button@click="addOne">AddOne</button><br/></div><br/>`<br/>};

Thecommentsaboveeachpropertydescribeitspurpose,albeitataveryhighlevel.Let'sseeVueinactionbyreviewingtheAppcomponent.

TheAppcomponentTheAppcomponentisthebasecomponentthatrendersallofthechildcomponentsintheapplication.We'llbrieflyreviewtheAppcomponent'scodetogainabetterunderstandingofVue.Goingforward,we'lldescribetheroleeachremainingcomponentplays,butonlyreviewsectionsofthecorrespondingcode.ThecontentsoftheAppcomponentfile,locatedat/src/components/App.js,areshownasfollows:

importBalancesBarfrom'./BalancesBar/BalancesBar.js';

importChartsTabfrom'./ChartsTab/ChartsTab.js';

importTransactionsTabfrom'./TransactionsTab/TransactionsTab.js';

/**

*Thiscomponentistheentrypointfortheapplication.Itcontainsthe

*header,tabs,andcontent.

*/

exportdefault{

name:'app',

components:{

BalancesBar,

ChartsTab,

TransactionsTab

},

data(){

return{

balances:$store.state.balances,

activeTab:0

};

},

methods:{

//Anytimeatransactionisadded,edited,orremoved,weneedto

//ensurethebalanceisupdated:

onTransactionChange(){

$store.recalculateBalances();

this.balances=$store.state.balances;

},

//Whenthe"Charts"tabisactivated,thisensuresthatthecharts

//getautomaticallyupdated:

onTabClick(event){

this.activeTab=+event.target.dataset.tab;

}

},

template:`

<div>

<divclass="appHeaderuk-background-primaryuk-flexuk-flex-middle">

<h2class="uk-lightuk-margin-remove-bottomuk-margin-left">

CooktheBooks

</h2>

</div>

<divclass="uk-position-relative">

<uluk-tabclass="uk-margin-small-bottomuk-margin-top">

<liclass="uk-margin-small-left">

<ahref="#"data-tab="0"@click="onTabClick">Transactions</a>

</li>

<li>

<ahref="#"data-tab="1"@click="onTabClick">Charts</a>

</li>

</ul>

<balances-bar

:balances="balances"

:onTransactionChange="onTransactionChange">

</balances-bar>

<ulclass="uk-switcher">

<li>

<transactions-tab:onTransactionChange="onTransactionChange">

</transactions-tab>

</li>

<li>

<charts-tab:isActive="this.activeTab===1"></charts-tab>

</li>

</ul>

</div>

</div>

`

};

WeusethecomponentspropertytospecifytheotherVuecomponentswe'llrenderinthetemplatefortheAppcomponent.Thedata()function,whichreturnsthelocalstate,isusedtokeeptrackofbalancesandwhichtabisactive(TRANSACTIONSorCHARTS).Themethodspropertycontainstwofunctions:onTransactionChange()andonTabClick().TheonTransactionChange()functioncalls$store.recalculateBalances()andupdatesbalancesinlocalstateifachangeismadetoatransactionrecord.TheonTabClick()functionchangesthevalueofactiveTabinthelocalstatetothedata-tabattributeoftheclickedtab.Finally,thetemplatepropertycontainsthemarkupusedtorenderthecomponent.

Ifyou'renotusingsinglefilecomponentsinVue(.vueextension),youneedtoconvertthecomponentnametokebabcaseinthetemplateproperty.Forexample,intheAppcomponentshownearlier,BalancesBarwaschangedto<balances-bar>inthetemplate.

TheBalancesBarThe/components/BalancesBarfoldercontainstwocomponentfiles:BalanceCard.jsandBalancesBar.js.TheBalancesBarcomponentpersistsacrosstheTRANSACTIONSandCHARTStabsandislocateddirectlyunderthetabcontrol.ItcontainsfouroftheBalanceCardcomponents,oneforeachbalancetype:initialraw,currentraw,initialcooked,andcurrentcooked.Thefirstandthirdcardsrepresentingtheinitialbalancescontaininputssothebalancecanbechanged.ThesecondandfourthcardsrepresentingthecurrentbalancesarecalculateddynamicallyintheWasmmodule(usingthegetFinalBalanceForType()function).Thefollowingsnippet,takenfromtheBalancesBarcomponent,demonstratesVue'sbindingsyntax:

<balance-card

title="InitialRawBalance"

:value="balances.initialRaw"

:onChange="amount=>onBalanceChange(amount,'initialRaw')">

</balance-card>

The:precedingthevalueandonChangeattributesindicatethatthesepropertiesareboundtotheVuecomponent.Ifthevalueofbalances.initialRawchanges,thevaluedisplayedintheBalanceCardwillupdateaswell.TheonBalanceChange()functionforthiscardupdatesthevalueofbalances.initialRawinglobalstate.

getFormattedTransactions(){<br/>constgetDisplayAmount=(type,amount)=>{<br/>if(amount===0)returnaccounting.formatMoney(amount);<br/>returnaccounting.formatMoney(amount,{<br/>format:{pos:'%s%v',neg:'%s(%v)'}<br/>});<br/>};<br/><br/>constgetDisplayDate=transactionDate=>{<br/>if(!transactionDate)return'';<br/>constparsedTime=d3.timeParse('%Y-%m-%d')(transactionDate);<br/>returnd3.timeFormat('%m/%d/%Y')(parsedTime);<br/>};<br/><br/>return$store.state.transactions.map(<br/>({<br/>type,<br/>rawAmount,<br/>cookedAmount,<br/>transactionDate,<br/>...transaction<br/>})=>({<br/>...transaction,<br/>type,<br/>rawAmount:getDisplayAmount(type,rawAmount),<br/>cookedAmount:getDisplayAmount(type,cookedAmount),<br/>transactionDate:getDisplayDate(transactionDate)<br/>})<br/>);<br/>}

TheprecedinggetFormattedTransactions()functionshownappliesformattingtotherawAmount,cookedAmount,andtransactionDatefieldswithineachtransactionrecord.Thisisdonetoensurethevaluebeingdisplayedincludesadollarsign(foramounts)andispresentedinauser-friendlyformat.

TheChartsTabThe/components/ChartsTabfoldercontainstwocomponentfiles:ChartsTab.jsandPieChart.js.TheChartsTabcomponentcontainstwoinstancesofthePieChartcomponent,oneforincomeandoneforexpenses.EachPieChartcomponentdisplayseithertheraworcookedpercentagesbycategory.Theusercanswitchbetweenraworcookedviewsviabuttonsdirectlyabovethechart.ThedrawChart()methodinPieChart.jsusesD3torenderthepiechartandlegend.ItusesD3'sbuilt-inanimationstoanimateeachpieceofthepiewhenloading:arc.append('path').attr('fill',d=>colorScale(d.data.category)).transition().delay((d,i)=>i*100).duration(500).attrTween('d',d=>{consti=d3.interpolate(d.startAngle+0.1,d.endAngle);returnt=>{d.endAngle=i(t);returnarcPath(d);};});

Theprecedingsnippet,takenfromdrawChart()inPieChart.js,definestheanimationforthepiepieceinonlyafewlinesofcode.Ifyou'reinterestedinlearningmoreaboutD3'scapabilities,checkoutsometheexamplesathttps://bl.ocks.org.That'sitforthecomponentsreview;let'stryrunningtheapplication.

RunningtheapplicationYou'vewrittenandcompiledtheCcodeandaddedthefrontendlogic.It'stimetostarttheapplicationandinteractwithit.Inthissection,wewillvalidateyourapplication's/srcfolder,runtheapplication,andtestoutthefeaturestoensureeverythingisworkingcorrectly.

├──/assets<br/>│├──main.wasm<br/>│└──memory.wasm<br/>├──/components<br/>│├──/BalancesBar<br/>││├──BalanceCard.js<br/>││└──BalancesBar.js<br/>│├──/ChartsTab<br/>││├──ChartsTab.js<br/>││└──PieChart.js<br/>│├──/TransactionsTab<br/>││├──ConfirmationModal.js<br/>│|├──TransactionModal.js<br/>│|├──TransactionsTab.js<br/>│|└──TransactionsTable.js<br/>│└──App.js<br/>├──/store<br/>│├──api.js<br/>│├──initializeWasm.js<br/>│├──store.js<br/>│└──WasmTransactions.js<br/>├──index.html<br/>├──main.js<br/>└──styles.css

Ifeverythingmatchesup,you'rereadytoproceed.

Startitup!Tostarttheapplication,openupaterminalinthe/cook-the-booksfolderandrunthefollowingcommand:

npmstart

browser-syncthedevelopmentdependencyweinstalledinthefirstsectionofthischapter,actsasalocalserver(liketheservelibrary).Itmakestheapplicationaccessibleinthebrowserfromtheportspecifiedinthepackage.jsonfile(inthiscase,4000).Ifyounavigatetohttp://localhost:4000/index.htmlinyourbrowser,youshouldseethis:

ApplicationoninitialloadWe'reusingbrowser-syncinsteadofservebecauseitwatchesforchangesinyourfilesandautomaticallyreloadstheapplicationifyoumakeachange.Toseethisinaction,trychangingthecontentsofthetitlebarinApp.jsfromCooktheBookstoBroiltheBooks.Thebrowserwillrefreshandyou'llseetheupdatedtextinthetitlebar.

TestingitoutToensureeverythingisworkingcorrectly,let'stestouttheapplication.Eachofthefollowingsectionsdescribesanactionandexpectedbehaviorforaparticularfunctionoftheapplication.Followalongtoseeifyou'regettingtheexpectedresults.Ifyourunintoanissue,youcanalwaysreferbacktothe/chapter-07-cook-the-booksfolderinthelearn-webassemblyrepository.

ChanginginitialbalancesTrychangingtheinputvaluesontheINITIALRAWBALANCEandINITIALCOOKEDBALANCEBalanceCardcomponents.TheCURRENTRAWBALANCEandCURRENTCOOKEDBALANCEcardvaluesshouldupdatetoreflectyourchanges.

CreatinganewtransactionMakeanoteofthecurrentrawandcookedbalances,thenpresstheblueAddbuttonatthebottom-rightcornerofthewindow.ItshouldloadtheTransactionModalcomponent.Populatetheinputs,makeanoteoftheType,RawAmount,andCookedAmountyouentered,thenpresstheSavebutton.

Thebalancesshouldhaveupdatedtoreflectthenewamounts.IfyoupickedWithdrawalfortheType,thebalancesshoulddecrease,otherwise,theyincrease(forDeposit)asshowninthefollowingscreenshot:

TransactionModalwhenaddinganewtransaction

DeletinganexistingtransactionPickarowwithintheTransactionsTablecomponent,notetheamounts,andpressthebuttonthatlookslikeatrashcanforthatrecord.TheConfirmationModalcomponentshouldappear.WhenyoupresstheYesbutton,thetransactionrecordshouldnolongerbepresentinthetableandthecurrentbalancesshouldupdatetoreflecttheamountsassociatedwiththedeletedtransactionasshowninthefollowingscreenshot:

Confirmationmodalshownafterdeletebuttonispressed

EditinganexistingtransactionFollowthesameprocedureasyoudidforcreatinganewtransaction,exceptchangetheexistingamounts.Checkthecurrentbalancestoensuretheyreflecttheupdatedtransactionamounts.

TestingtheChartstabSelecttheChartstabtoloadtheChartsTabcomponent.PressthebuttonsineachPieChartcomponenttoswitchbetweentherawandcookedviews.Thepiechartsshouldre-renderwiththeupdatedvalues:

ContentsofCHARTStabwithdifferentamounttypesselected

WrapupCongratulations,youjustbuiltanapplicationthatusesWebAssembly!Tellyourfriends!NowthatyouunderstandthecapabilitiesandlimitationsofWebAssembly,it'stimetoexpandourhorizonsandusesomeoftheexcellentfeaturesEmscriptenprovides.

SummaryInthischapter,webuiltanaccountingapplicationfromscratchthatusesWebAssemblywithoutanyoftheextrafeaturesEmscriptenprovides.ByadheringtotheCoreSpecification,wedemonstratedthelimitationsofWebAssemblyinitscurrentform.However,wewereabletoperformcomputationquicklythroughtheuseofWasmmodules,whichiswellsuitedforaccounting.WeusedVuetosplitourapplicationintocomponents,UIkitforthedesignandlayout,andD3tocreatepiechartsfromourtransactionsdata.InChapter8,PortingaGamewithEmscripten,we'lltakefulladvantageofEmscriptentoportanexistingC++codebasetoWebAssembly.

Questions1. WhydidweuseVueforthisapplication(insteadofReactorAngular)?2. WhydidweuseCinsteadofC++forthisproject?3. WhydidweneedtosetupamockAPIusingjsonstore.ioinsteadofstoring

thedatalocallyinaJSONfile?4. Whatisthenameofthedatastructureweusedformanagingtransactionsin

theCfile?5. Whichfunctionsdidweneedfromthememory.wasmfileandwhatarethey

usedfor?6. WhydidwecreateawrapperclassaroundtheWasmmodule?7. Whydidwemakethe$storeobjectglobal?8. Whichlibrariescouldyouuseinaproductionapplicationformanaging

globalstate?9. Whyareweusingbrowser-sync,insteadofserve,toruntheapplication?

FurtherreadingVue:https://vuejs.org

PortingaGamewithEmscriptenAsdemonstratedinChapter7,CreatinganApplicationfromScratch,WebAssemblyisstillrelativelylimitedinitscurrentform.EmscriptenprovidespowerfulAPIsforextendingWebAssembly'scapabilitiestoaddfunctionalitytoyourapplication.CompilingtoaWebAssemblymoduleandJavaScriptgluecode(insteadofanexecutable)can,insomecases,onlyrequireminorchangestotheexistingCorC++source.

Inthischapter,we'regoingtotakeacodebasewritteninC++thatgetscompiledtoatraditionalexecutable,andupdatethecodesothatitcanbecompiledtoWasm/JavaScript.We'llalsoaddsomeadditionalfeaturesfortighterintegrationwiththebrowser.

Bytheendofthischapter,you'llknowhowtodothefollowing:

UpdateaC++codebasetocompiletoaWasmmodule/JavaScriptgluecode(insteadofanativeexecutable)UseEmscripten'sAPIstoaddbrowserintegrationtoaC++applicationBuildamulti-fileC++projectwiththeproperemccflagsRunandtestaC++applicationinthebrowserusingemrun

OverviewofthegameInthischapter,we'retakingaTetrisclonewritteninC++andupdatingthecodetointegrateEmscriptenandcompiletoWasm/JS.ThecodebaseinitsoriginalformcompiledtoanexecutableutilizesSDL2andcanbeloadedfromthecommandline.Inthissection,we'regoingtobrieflyreviewwhatTetrisis,howtogetthecode(withouthavingtowriteitfromscratch),andhowtogetitrunning.

WhatisTetris?InTetris,themainobjectiveofthegameistorotateandmovepieces(Tetriminos)ofvariousshapeswithinaplayingfield(wellormatrix)tocreatearowofblockswithoutgaps.Whenafullrowiscreated,itisdeletedfromtheplayingfieldandyourscoreisincreasedbyone.Inourversionofthegame,therewon'tbeawincondition(althoughitwouldbesimpletoaddit).

It'simportanttounderstandtherulesandmechanicsofthegamebecausethecodeusesalgorithmsforconceptssuchascollisiondetectionandscoring.Understandingthegoalofafunctionhelpsyouunderstandthecodewithin.IrecommendyougiveitatryonlineifyouneedtobrushuponyourTetrisskills.Youcanplayitathttps://emulatoronline.com/nes-games/classic-tetris/withouthavingtoinstallAdobeFlash.ItlooksjustliketheoriginalNintendoVersion:

ClassicTetrisatEmulatorOnline.com

Theversionwe'llbeworkingwithwon'tcontainthepiececounters,levels,orpoints(we'restickingtolinecounts),butitwilloperateinthesameway.

ThesourceofthesourceItturnsoutthatasearchforTetrisC++providesamultitudeoftutorialsandexamplerepositoriestochoosefrom.IntheinterestofstickingtotheformattingandnamingconventionsthatI'vebeenusinguptothispoint,Icombinedtheseresourcestocreatemyownversionofthegame.TheFurtherreadingsectionattheendofthischapterhaslinkstotheseresourcesifyou'reinterestedinlearningmore.Theconceptsandprocessforportingacodebaseareapplicable,regardlessofthesource.Onthatnote,let'stakeabriefstep-asidetodiscussportingingeneral.

AnoteaboutportingPortinganexistingcodebasetoEmscriptenisnotalwaysasimpletask.ThereareseveralvariablestotakeintoaccountwhenevaluatingwhetheraC,C++,orRustapplicationisamenabletoconversion.Forexample,gamesthatmakeuseofseveralthird-partylibrariesorevenafewthird-partylibrariesthatareofconsiderablecomplexitymayrequireasignificantamountofeffort.Emscriptenprovidesthefollowingcommonlyusedlibrariesoutofthebox:

asio:Anetworkandlow-levelI/OprogramminglibraryBullet:Areal-timecollisiondetectionandmulti-physicssimulationlibraryCocos2d:Asuiteofopensource,cross-platform,gamedevelopmenttoolsFreeType:AlibraryusedtorenderfontsHarfBuzz:AnOpenTypetextshapingenginelibpng:TheofficialPNGreferencelibraryOgg:AmultimediacontainerformatSDL2:Alibrarydesignedtoprovidelow-levelaccesstoaudio,akeyboard,amouse,ajoystick,andgraphicshardwareSDL2_image:AnimagefileloadinglibrarySDL2_mixer:Asamplemulti-channelaudiomixerlibrarySDL2_net:Asmallsamplecross-platformnetworkinglibrarySDL2_ttf:AsamplelibrarythatallowsyoutouseTrueTypefontsinyourSDLapplicationsVorbis:Ageneralpurposeaudioandmusicencodingformatzlib:Alosslessdatacompressionlibrary

Ifthelibraryisn'talreadyported,youwillneedtodoityourself.Thiswouldbenefitthecommunity,butrequiresasignificantinvestmentoftimeandresources.OurTetrisexampleonlyusesSDL2,whichmakestheportingprocessrelativelysimple.

GettingthecodeThecodeforthischapterislocatedinthe/chapter-08-tetrisfolderofthelearn-webassemblyrepository.Therearetwodirectorieswithin/chapter-08-tetris:the/output-nativefolder,whichcontainstheoriginal(pre-ported)codeandthe/output-wasmfolder,whichcontainstheportedcode.

IfyouwanttouseVSCode'sTaskfeatureforthenativebuildstep,you'llneedtoopenthe/chapter-08-tetris/output-nativefolderinVSCode,notthetop-level/learn-webassemblyfolder.

BuildingthenativeprojectThe/cmakefolderandCMakeLists.txtfilewithinthe/output-nativefolderarerequiredtobuildtheproject.TheREADME.mdfilecontainsinstructionstogetthecodeupandrunningoneachplatform.Buildingtheprojectisn'tnecessarytoworkthroughtheportingprocess.Theprocessforinstallingtherequireddependenciesandgettingtheprojecttobuildsuccessfullyonyourplatformcanbetime-consumingandcomplex.Ifyoustillwishtoproceed,youcanbuildtheexecutablethroughVSCode'sTaskfeaturebyselectingTasks|RunTask...fromthemenuandselectingBuildExecutablefromthelistafterfollowingtheinstructionsintheREADME.mdfile.

ThegameinactionIfyouweresuccessfulinbuildingtheproject,youshouldbeabletorunitbyselectingTasks|RunTask...fromtheVSCodemenuandselectingtheStartExecutabletaskfromthelist.Ifeverythingwassuccessful,youshouldsee

somethinglikethis:

Compiledgamerunningnatively

Ourversionofthegamedoesn'thavealosingcondition;itjustincrementstheROWScountbyoneforeachrowyouclear.IfoneoftheTetriminostouchesthetopoftheboard,thegameisoverandtheboardresets.It'sarudimentaryimplementationofthegame,butadditionalfeaturesincreasethecomplexityandamountofcoderequired.Let'sreviewthecodebaseinmoredetail.

ThecodebaseindepthNowthatyouhavethecodeavailable,you'llneedtofamiliarizeyourselfwiththecodebase.Withouthavingagoodunderstandingofthecodeyouwanttoport,you'llhaveamuchhardertimeportingitsuccessfully.Inthischapter,we'regoingtowalkthrougheachoftheC++classandheaderfilesanddescribetheirrolesintheapplication.

BreakingthecodeintoobjectsC++wasdesignedaroundanobject-orientedparadigm,whichiswhattheTetriscodebaseusestosimplifymanagementoftheapplication.ThecodebaseconsistsofC++classfiles

(.cpp)andheaderfiles(.h)thatrepresentobjectswithinthecontextofthegame.IusedthegameplaysummaryfromtheWhatisTetris?sectiontoextrapolatewhichobjectsIneeded.

Thegamepieces(Tetriminos)andplayingfield(referredtoasawellormatrix)aregoodcandidatesforclasses.Maybelessintuitively,butstilljustasvalid,isthegameitself.Classesdon'tnecessarilyneedtobeasconcreteasactualobjects—they'reexcellentforstoringsharedcode.I'mabigfanoflesstyping,soIoptedtousePiecetorepresentaTetriminoandBoardfortheplayingfield(althoughthewordwellisshorter,itjustdoesn'tquitefit).Icreatedaheaderfiletostoreglobalvariables(constants.h),aGameclasstomanagegameplay,andamain.cppfile,whichactsastheentrypointforthegame.Here'sthecontentsofthe/srcfolder:

├──board.cpp

├──board.h

├──constants.h

├──game.cpp

├──game.h

├──main.cpp

├──piece.cpp

└──piece.h

Eachfile(withtheexceptionofmain.cppandconstants.h)hasaclass(.cpp)andheader(.h)file.Headerfilesallowyoutoreusecodeacrossmultiplefilesandpreventcodeduplication.TheFurtherreadingsectioncontainsresourcesforyoutolearnmoreaboutheaderfilesifyou'reinterested.Theconstants.hfileisusedinalmostalloftheotherfileswithintheapplication,solet'sreviewthatfirst.

TheconstantsfileRatherthanhaveconfusingmagicnumberssprinkledthroughoutthecodebase,Ioptedforaheaderfilecontainingtheconstantswe'llbeusing(constants.h).Thecontentsofthisfileareshownhere:#ifndefTETRIS_CONSTANTS_H#defineTETRIS_CONSTANTS_H

namespaceConstants{constintBoardColumns=10;constintBoardHeight=720;constintBoardRows=20;constintBoardWidth=360;constintOffset=BoardWidth/BoardColumns;constintPieceSize=4;constintScreenHeight=BoardHeight+50;}

#endif//TETRIS_CONSTANTS_H

The#ifndefstatementinthefirstlineofthefileisan#includeguard,whichpreventstheheaderfilefrombeingincludedmultipletimesduringcompilation.Theseguardsareusedinalloftheapplication'sheaderfiles.Thepurposeofeachoftheseconstantswillbecomeclearwhenwestepthrougheachoftheclasses.Iincludeditfirsttoprovidecontextaroundthevariouselementsizesandhowtheyrelatetoeachother.

Let'smoveontothevariousclassesthatrepresentaspectsofthegame.ThePiececlassrepresentsanobjectatthelowestlevel,sowe'llstartthereandworkourwayuptotheBoardandGameclasses.

ThepiececlassThepiece,orTetrimino,istheelementthatcanbemovedandrotatedontheboard.TherearesevenkindsofTetriminos—eachisrepresentedbyaletterandhasacorrespondingcolor:

Tetriminocolors,takenfromWikipedia

Weneedawaytodefineeachpieceintermsofshape,color,andcurrentorientation.Eachpiecehasfourdifferentorientations(at90degreeincrements),whichresultsin28totalvariationsforallpieces.Thecolordoesn'tchange,sothatonlyneedstobeassignedonce.Withthatinmind,let'sfirsttakealookattheheaderfile(piece.h):

#ifndefTETRIS_PIECE_H

#defineTETRIS_PIECE_H

#include<SDL2/SDL.h>

#include"constants.h"

classPiece{

public:

enumKind{I=0,J,L,O,S,T,Z};

explicitPiece(Kindkind);

voiddraw(SDL_Renderer*renderer);

voidmove(intcolumnDelta,introwDelta);

voidrotate();

boolisBlock(intcolumn,introw)const;

intgetColumn()const;

intgetRow()const;

private:

Kindkind_;

intcolumn_;

introw_;

intangle_;

};

#endif//TETRIS_PIECE_H

ThegameusesSDL2torenderthevariousgraphicalelementsandhandlekeyboardinput,whichiswhywe'repassingaSDL_Rendererintothedraw()function.You'llseehowSDL2isusedintheGameclass,butfornowjustbeawareofitsinclusion.TheheaderfiledefinestheinterfaceforthePiececlass;let'sreviewtheimplementationinpiece.cpp.We'llwalkthrougheachsectionofcodeanddescribethefunctionality.

Theconstructoranddraw()functionThefirstsectionofcodedefinestheconstructorofthePiececlassandthedraw()function:

#include"piece.h"

usingnamespaceConstants;

Piece::Piece(Piece::Kindkind):

kind_(kind),

column_(BoardColumns/2-PieceSize/2),

row_(0),

angle_(0){

}

voidPiece::draw(SDL_Renderer*renderer){

switch(kind_){

caseI:

SDL_SetRenderDrawColor(renderer,

/*Cyan:*/45,254,254,255);

break;

caseJ:

SDL_SetRenderDrawColor(renderer,

/*Blue:*/11,36,251,255);

break;

caseL:

SDL_SetRenderDrawColor(renderer,

/*Orange:*/253,164,41,255);

break;

caseO:

SDL_SetRenderDrawColor(renderer,

/*Yellow:*/255,253,56,255);

break;

caseS:

SDL_SetRenderDrawColor(renderer,

/*Green:*/41,253,47,255);

break;

caseT:

SDL_SetRenderDrawColor(renderer,

/*Purple:*/126,15,126,255);

break;

caseZ:

SDL_SetRenderDrawColor(renderer,

/*Red:*/252,13,28,255);

break;

}

for(intcolumn=0;column<PieceSize;++column){

for(introw=0;row<PieceSize;++row){

if(isBlock(column,row)){

SDL_Rectrect{

(column+column_)*Offset+1,

(row+row_)*Offset+1,

Offset-2,

Offset-2

};

SDL_RenderFillRect(renderer,&rect);

}

}

}

}

Theconstructorinitializestheclasswithdefaultvalues.TheBoardColumnsandPieceSizevaluesareconstantsfromtheconstants.hfile.BoardColumnsrepresentstheamountofcolumnsthatcanfitonaboard,whichis10inthiscase.ThePieceSizeconstantrepresentstheareaorblockthatapiecetakesupincolumns,whichis4.Theinitialvalueassignedtotheprivatecolumns_variablerepresentsthecenteroftheboard.

Thedraw()functionloopsthroughallofthepossiblerowsandcolumnsontheboardandfillsinanycellsthatarepopulatedbyapiecewiththecolorthatcorrespondstoitskind.ThedeterminationforwhetheracellispopulatedbyapieceisperformedintheisBlock()function,whichwe'lldiscussnext.

Themove(),rotate(),andisBlock()functionsThesecondsectioncontainsthelogictomoveorrotatethepieceanddetermineitscurrentlocation:

voidPiece::move(intcolumnDelta,introwDelta){

column_+=columnDelta;

row_+=rowDelta;

}

voidPiece::rotate(){

angle_+=3;

angle_%=4;

}

boolPiece::isBlock(intcolumn,introw)const{

staticconstchar*Shapes[][4]={

//I

{

"*"

"*"

"*"

"*",

""

"****"

""

"",

"*"

"*"

"*"

"*",

""

"****"

""

"",

},

//J

{

"*"

"*"

"**"

"",

""

"*"

"***"

"",

"**"

"*"

"*"

"",

""

""

"***"

"*",

},

...

};

returnShapes[kind_][angle_][column+row*PieceSize]=='*';

}

intPiece::getColumn()const{

returncolumn_;

}

intPiece::getRow()const{

returnrow_;

}

Themove()functionupdatesthevaluesoftheprivatecolumn_androw_variables,whichdictatesthepiece'slocationontheboard.Therotate()functionsetsthevalueoftheprivateangle_variabletoeither0,1,2,or3(whichiswhy%=4isused).

Determinationforwhichkindofpieceisshown,itslocation,androtationisperformedintheisBlock()function.IomittedallbutthefirsttwoelementsoftheShapesmulti-dimensionalarraytoavoidclutteringupthefile,buttheremainingfivepiecekindsarepresentintheactualcode.Iwilladmitthatthisisn'tthemostelegantimplementation,butitsuitsourpurposesjustfine.

Theprivatekind_andangle_valuesarespecifiedasdimensionsintheShapesarraytopickthefourcorrespondingchar*elements.Thesefourelementsrepresentthefourpossibleorientationsofthepiece.Iftheindexofcolumn+row*PieceSizeinthestringisanasterisk,thepieceispresentinthespecifiedrowandcolumn.IfyoudecidetoworkthroughoneoftheTetristutorialsavailableontheweb(orlookatoneofthemanyTetrisrepositoriesonGitHub),you'llfindthatthereareseveraldifferentwaystocalculatewhetheracellispopulatedbyapiece.Ichosethismethodbecauseit'seasiertovisualizethepieces.

ThegetColumn()andgetRow()functionsThefinalsectionofcodecontainsfunctionstogettherowandcolumnofthepiece:

intPiece::getColumn()const{

returncolumn_;

}

intPiece::getRow()const{

returnrow_;

}

Thesefunctionssimplyreturnthevalueoftheprivatecolumn_orrow_variable.NowthatyouhaveabetterunderstandingofthePiececlass,let'smoveontotheBoard.

TheBoardclassTheBoardcontainsinstancesofthePiececlassandneedstodetectcollisionsamongthepieces,whenrowsarefilled,andwhenthegameisover.Let'sstartwiththecontentsoftheheaderfile(board.h):

#ifndefTETRIS_BOARD_H

#defineTETRIS_BOARD_H

#include<SDL2/SDL.h>

#include<SDL2/SDL2_ttf.h>

#include"constants.h"

#include"piece.h"

usingnamespaceConstants;

classBoard{

public:

Board();

voiddraw(SDL_Renderer*renderer,TTF_Font*font);

boolisCollision(constPiece&piece)const;

voidunite(constPiece&piece);

private:

boolisRowFull(introw);

boolareFullRowsPresent();

voidupdateOffsetRow(intfullRow);

voiddisplayScore(SDL_Renderer*renderer,TTF_Font*font);

boolcells_[BoardColumns][BoardRows];

intcurrentScore_;

};

#endif//TETRIS_BOARD_H

TheBoardhasadraw()functionlikethePiececlassaswellasseveralotherfunctionsformanagingrowsandkeepingtrackofwhichcellsarepopulatedontheboard.TheSDL2_ttflibraryisusedtorendertheROWS:textatthebottomofthewindowwiththecurrentscore(countofrowscleared).Now,let'stakealookateachsectionoftheimplementationfile(board.cpp).

Theconstructoranddraw()functionThefirstsectionofcodedefinestheconstructoroftheBoardclassandthedraw()function:

#include<sstream>

#include"board.h"

usingnamespaceConstants;

Board::Board():cells_{{false}},currentScore_(0){}

voidBoard::draw(SDL_Renderer*renderer,TTF_Font*font){

displayScore(renderer,font);

SDL_SetRenderDrawColor(

renderer,

/*LightGray:*/140,140,140,255);

for(intcolumn=0;column<BoardColumns;++column){

for(introw=0;row<BoardRows;++row){

if(cells_[column][row]){

SDL_Rectrect{

column*Offset+1,

row*Offset+1,

Offset-2,

Offset-2

};

SDL_RenderFillRect(renderer,&rect);

}

}

}

}

TheBoardconstructorinitializesthevaluesoftheprivatecells_andcurrentScore_variablestodefaultvalues.Thecells_variableisatwo-dimensionalarrayofBooleans,withthefirstdimensionrepresentingcolumnsandthesecondrows.Ifapieceoccupiesaspecificcolumnandrow,thecorrespondingvalueinthearrayistrue.Thedraw()functionbehavessimilarlytothedraw()functionofPieceinthatitfillscellsthatcontainpieceswithcolor.However,thisfunctiononlyfillsincellsthatareoccupiedbypiecesthathavereachedthebottomoftheboardwithalightgraycolor,regardlessofwhatkindofpieceitis.

TheisCollision()functionThesecondsectionofcodecontainslogictodetectcollisions:

boolBoard::isCollision(constPiece&piece)const{

for(intcolumn=0;column<PieceSize;++column){

for(introw=0;row<PieceSize;++row){

if(piece.isBlock(column,row)){

intcolumnTarget=piece.getColumn()+column;

introwTarget=piece.getRow()+row;

if(

columnTarget<0

||columnTarget>=BoardColumns

||rowTarget<0

||rowTarget>=BoardRows

){

returntrue;

}

if(cells_[columnTarget][rowTarget])returntrue;

}

}

}

returnfalse;

}

TheisCollision()functionloopsthrougheachcellontheboarduntilitreachesonepopulatedbythe&piecepassedasanargument.Ifthepieceisabouttocollidewitheithersideoftheboardorithasreachedthebottom,thefunctionreturnstrue,otherwiseitreturnsfalse.

Theunite()functionThethirdsectionofcodecontainslogictouniteapiecewiththetoprowwhenitcomestorest:

voidBoard::unite(constPiece&piece){

for(intcolumn=0;column<PieceSize;++column){

for(introw=0;row<PieceSize;++row){

if(piece.isBlock(column,row)){

intcolumnTarget=piece.getColumn()+column;

introwTarget=piece.getRow()+row;

cells_[columnTarget][rowTarget]=true;

}

}

}

//Continuouslyloopsthrougheachoftherowsuntilnofullrowsare

//detectedandensuresthefullrowsarecollapsedandnon-fullrows

//areshiftedaccordingly:

while(areFullRowsPresent()){

for(introw=BoardRows-1;row>=0;--row){

if(isRowFull(row)){

updateOffsetRow(row);

currentScore_+=1;

for(intcolumn=0;column<BoardColumns;++column){

cells_[column][0]=false;

}

}

}

}

}

boolBoard::isRowFull(introw){

for(intcolumn=0;column<BoardColumns;++column){

if(!cells_[column][row])returnfalse;

}

returntrue;

}

boolBoard::areFullRowsPresent(){

for(introw=BoardRows-1;row>=0;--row){

if(isRowFull(row))returntrue;

}

returnfalse;

}

voidBoard::updateOffsetRow(intfullRow){

for(intcolumn=0;column<BoardColumns;++column){

for(introwOffset=fullRow-1;rowOffset>=0;--rowOffset){

cells_[column][rowOffset+1]=

cells_[column][rowOffset];

}

}

}

Theunite()functionandthecorrespondingisRowFull(),areFullRowsPresent(),and

updateOffsetRow()functionsperformseveraloperations.Itupdatestheprivatecells_variablewiththerowsandcolumnsthatthespecified&pieceargumentoccupiesbysettingtheappropriatearraylocationtotrue.Italsoclearsanyfullrows(allcolumnsfilled)fromtheboardbysettingthecorrespondingcells_arraylocationstofalseandincrementsthecurrentScore_.Aftertherowiscleared,thecells_arrayisupdatedtoshifttherowabovetheclearedrowdownby1.

ThedisplayScore()functionThefinalsectionofcodedisplaysthescoreatthebottomofthegamewindow:

voidBoard::displayScore(SDL_Renderer*renderer,TTF_Font*font){

std::stringstreammessage;

message<<"ROWS:"<<currentScore_;

SDL_Colorwhite={255,255,255};

SDL_Surface*surface=TTF_RenderText_Blended(

font,

message.str().c_str(),

white);

SDL_Texture*texture=SDL_CreateTextureFromSurface(

renderer,

surface);

SDL_RectmessageRect{20,BoardHeight+15,surface->w,surface->h};

SDL_FreeSurface(surface);

SDL_RenderCopy(renderer,texture,nullptr,&messageRect);

SDL_DestroyTexture(texture);

}

ThedisplayScore()functionusestheSDL2_ttflibrarytodisplaythecurrentscoreatthebottomofthewindow(underneaththeboard).TheTTF_Font*fontargumentispassedinfromtheGameclasstoavoidinitializingthefonteverytimethescoreisupdated.ThestringstreammessagevariableisusedtocreatethetextvalueandsetittoaCchar*withintheTTF_RenderText_Blended()function.TherestofthecodedrawsthetextonaSDL_Recttoensurethatit'sproperlydisplayed.

That'sitfortheBoardclass;let'smoveontotheGametoseehowitallfitstogether.

TheGameclassTheGameclasscontainstheloopingfunctionthatenablesyoutomovepiecesaroundtheboardwithkeypresses.Here'sthecontentsoftheheaderfile(game.h):

#ifndefTETRIS_GAME_H

#defineTETRIS_GAME_H

#include<SDL2/SDL.h>

#include<SDL2/SDL2_ttf.h>

#include"constants.h"

#include"board.h"

#include"piece.h"

classGame{

public:

Game();

~Game();

boolloop();

private:

Game(constGame&);

Game&operator=(constGame&);

voidcheckForCollision(constPiece&newPiece);

voidhandleKeyEvents(SDL_Event&event);

SDL_Window*window_;

SDL_Renderer*renderer_;

TTF_Font*font_;

Boardboard_;

Piecepiece_;

uint32_tmoveTime_;

};

#endif//TETRIS_GAME_H

Theloop()functioncontainsthegamelogicandmanagesstatebasedonevents.Thefirsttwolinesundertheprivate:headerpreventmorethanoneinstanceofthegamefrombeingcreated,whichcouldcauseamemoryleak.Theprivatemethodsreducetheamountofcodelinesintheloop()function,whichsimplifiesmaintenanceanddebugging.Let'smoveontotheimplementationingame.cpp.

TheconstructoranddestructorThefirstsectionofcodedefinestheactionstoperformwhentheclassinstanceisloaded(constructor)andunloaded(destructor):

#include<cstdlib>

#include<iostream>

#include<stdexcept>

#include"game.h"

usingnamespacestd;

usingnamespaceConstants;

Game::Game():

//Createanewrandompiece:

piece_{static_cast<Piece::Kind>(rand()%7)},

moveTime_(SDL_GetTicks())

{

if(SDL_Init(SDL_INIT_VIDEO)!=0){

throwruntime_error(

"SDL_Init(SDL_INIT_VIDEO):"+string(SDL_GetError()));

}

SDL_CreateWindowAndRenderer(

BoardWidth,

ScreenHeight,

SDL_WINDOW_OPENGL,

&window_,

&renderer_);

SDL_SetWindowPosition(

window_,

SDL_WINDOWPOS_CENTERED,

SDL_WINDOWPOS_CENTERED);

SDL_SetWindowTitle(window_,"Tetris");

if(TTF_Init()!=0){

throwruntime_error("TTF_Init():"+string(TTF_GetError()));

}

font_=TTF_OpenFont("PressStart2P.ttf",18);

if(font_==nullptr){

throwruntime_error("TTF_OpenFont:"+string(TTF_GetError()));

}

}

Game::~Game(){

TTF_CloseFont(font_);

TTF_Quit();

SDL_DestroyRenderer(renderer_);

SDL_DestroyWindow(window_);

SDL_Quit();

}

Theconstructorrepresentstheentrypointfortheapplication,soalloftherequiredresourcesareallocatedandinitializedwithinit.TheTTF_OpenFont()functionisreferencingaTrueTypefontfiledownloadedfromGoogleFonts

namedPressStart2P.Youcanviewthefontathttps://fonts.google.com/specimen/Press+Start+2P.It'spresentinthe/resourcesfolderoftherepositoryandgetscopiedintothesamefolderastheexecutablewhentheprojectisbuilt.IfatanypointanerroroccurswheninitializingtheSDL2resources,aruntime_erroristhrownwithdetailsoftheerror.Thedestructor(~Game())freesuptheresourcesweallocatedforSDL2andSDL2_ttfbeforetheapplicationexits.Thisisdonetoavoidamemoryleak.

Theloop()functionThefinalsectionofcoderepresentstheGame::loop:

boolGame::loop(){

SDL_Eventevent;

while(SDL_PollEvent(&event)){

switch(event.type){

caseSDL_KEYDOWN:

handleKeyEvents(event);

break;

caseSDL_QUIT:

returnfalse;

default:

returntrue;

}

}

SDL_SetRenderDrawColor(renderer_,/*DarkGray:*/58,58,58,255);

SDL_RenderClear(renderer_);

board_.draw(renderer_,font_);

piece_.draw(renderer_);

if(SDL_GetTicks()>moveTime_){

moveTime_+=1000;

PiecenewPiece=piece_;

newPiece.move(0,1);

checkForCollision(newPiece);

}

SDL_RenderPresent(renderer_);

returntrue;

}

voidGame::checkForCollision(constPiece&newPiece){

if(board_.isCollision(newPiece)){

board_.unite(piece_);

piece_=Piece{static_cast<Piece::Kind>(rand()%7)};

if(board_.isCollision(piece_))board_=Board();

}else{

piece_=newPiece;

}

}

voidGame::handleKeyEvents(SDL_Event&event){

PiecenewPiece=piece_;

switch(event.key.keysym.sym){

caseSDLK_DOWN:

newPiece.move(0,1);

break;

caseSDLK_RIGHT:

newPiece.move(1,0);

break;

caseSDLK_LEFT:

newPiece.move(-1,0);

break;

caseSDLK_UP:

newPiece.rotate();

break;

default:

break;

}

if(!board_.isCollision(newPiece))piece_=newPiece;

}

Theloop()functionreturnsaBooleanaslongastheSDL_QUITeventhasn'tfired.Every1second,thedraw()functionsforthePieceandBoardinstancesareexecuted,andthepiecelocationsontheboardareupdatedaccordingly.Theleft,right,anddownarrowkeyscontrolthepiece'smovementwhiletheuparrowkeyrotatesthepieceby90degrees.AppropriateresponsestokeypressesarehandledinthehandleKeyEvents()function.ThecheckForCollision()functiondeterminesifanewinstanceoftheactivepiececollidedwitheithersideoftheboardorcametorestontopoftheotherpieces.Ifitdid,anewpieceiscreated.Thelogicforclearingtherows(viatheunite()functionofBoard)isalsohandledinthisfunction.We'realmostdone!Let'smoveontothemain.cppfile.

ThemainfileThere'snoheaderfileassociatedwithmain.cppbecauseitsonlypurposeistoactasanentrypointtotheapplication.Infact,thefileisonlysevenlineslong:

#include"game.h"

intmain(){

Gamegame;

while(game.loop());

return0;

}

Thewhilestatementisexitedwhentheloop()functionreturnsfalse,whichoccurswhentheSDL_QUITeventfires.AllthisfileisdoingiscreatinganewinstanceofGameandstartingtheloop.That'sitforthecodebase;let'sstartporting!

PortingtoEmscriptenYouhaveagoodunderstandingofthecodebase,sonowit'stimetostartportingitoverwithEmscripten.Fortunately,we'reabletoleveragesomeofthebrowser'sfeaturestosimplifythecodeandcompletelyremoveathird-partylibrary.Inthissection,we'regoingtoupdatethecodetocompiletoaWasmmoduleandJavaScriptgluefileandupdatesomeofthefunctionalitytoutilizethebrowser.

PreparingforportingThe/output-wasmfoldercontainstheendresult,butIrecommendthatyoucreateacopyofthe/output-nativefoldersothatyoucanfollowalongwiththeportingprocess.ThereareVSCodeTaskssetupforbothnativecompilationandEmscriptencompilation.Ifyougetstuck,youcanalwaysreferencethe/output-wasmcontents.MakesureyouopenyourcopiedfolderinVSCode(File|Openandselectyourcopiedfolder),otherwiseyouwon'tbeabletousetheTasksfeature.

What'schanging?ThisgameisanidealcandidateforportingbecauseitusesSDL2,awidelyusedlibrarywithanexistingEmscriptenport.IncludingSDL2inthecompilationsteprequiresonlyoneadditionalargumentpassedtotheemcccommand.AnEmscriptenportoftheSDL2_ttflibraryalsoexists,butkeepingitinthecodebasedoesn'tmakemuchsense.Itssolepurposeistorenderthescore(amountofrowscleared)astext.WewouldneedtoincludetheTTFfilewiththeapplicationandcomplicatethebuildprocess.EmscriptenprovidesthemeansforusingJavaScriptcodewithinourC++,sowe'regoingtotakeamuchsimplerroute:showthescoreintheDOM.

Inadditiontochangingtheexistingcode,we'llneedtocreateanHTMLandCSSfilefordisplayingandstylingthegameinthebrowser.TheJavaScriptcodewewritewillbeminimal—wejustneedtoloadtheEmscriptenmoduleandallourfunctionalityishandledintheC++codebase.We'llalsoneedtoaddafew<div>elementsandlaythemoutaccordinglytodisplaythescore.Let'sstartporting!

AddingthewebassetsCreateafolderinyourprojectfoldernamed/public.Addanewfilenamedindex.htmltothe/publicfolderandpopulateitwiththefollowingcontents:

<!doctypehtml>

<htmllang="en-us">

<head>

<title>Tetris</title>

<linkrel="stylesheet"type="text/css"href="styles.css"/>

</head>

<body>

<divclass="wrapper">

<h1>Tetris</h1>

<div>

<canvasid="canvas"></canvas>

<divclass="scoreWrapper">

<span>ROWS:</span><spanid="score"></span>

</div>

</div>

</div>

<scripttype="application/javascript"src="index.js"></script>

<scripttype="application/javascript">

Module({canvas:(()=>document.getElementById('canvas'))()})

</script>

</body>

</html>

Theindex.jsfilebeingloadedinthefirst<script>tagdoesn'texistyet;that'llbegeneratedinthecompilationstep.Let'saddsomestylestotheelements.Createastyles.cssfileinthe/publicfolderandpopulateitwiththefollowingcontents:

@importurl("https://fonts.googleapis.com/css?family=Press+Start+2P");

*{

font-family:"PressStart2P",sans-serif;

}

body{

margin:24px;

}

h1{

font-size:36px;

}

span{

color:white;

font-size:24px;

}

.wrapper{

display:flex;

align-items:center;

flex-direction:column;

}

.titleWrapper{

display:flex;

align-items:center;

justify-content:center;

}

.header{

font-size:24px;

margin-left:16px;

}

.scoreWrapper{

background-color:#3A3A3A;

border-top:1pxsolidwhite;

padding:16px0;

width:360px;

}

span:first-child{

margin-left:16px;

margin-right:8px;

}

SincethePressStart2Pfontwe'reusingishostedonGoogleFonts,wecanimportitforuseonthesite.TheCSSrulesinthisfilehandlesimplelayoutandstyling.That'sitfortheweb-relatedfilesweneededtocreate.Now,it'stimetoupdatetheC++code.

PortingtheexistingcodeWeonlyneedtoeditafewfilestogetEmscriptenworkingcorrectly.Forthesakeofsimplicityandcompactness,onlytheaffectedsectionsofcodewillbeincluded(ratherthantheentirefile).Let'sworkthroughthefilesinthesameorderastheprevioussectionandstartwithconstants.h.

UpdatingtheconstantsfileWe'lldisplaytherowsclearedcountontheDOMinsteadofinthegamewindowitself,soyoucandeletetheScreenHeightconstantfromthefile.Wenolongerneedadditionalspacetoaccommodateforthescoretext:

namespaceConstants{

constintBoardColumns=10;

constintBoardHeight=720;

constintBoardRows=20;

constintBoardWidth=360;

constintOffset=BoardWidth/BoardColumns;

constintPieceSize=4;

//constintScreenHeight=BoardHeight+50;<-----Deletethisline

}

NochangesneedtobemadetothePiececlassfiles(piece.cpp/piece.h).However,wewillneedtoupdatetheBoardclass.Let'sstartwiththeheaderfile(board.h).Startingwiththebottomandworkingourwayup,let'supdatethedisplayScore()function.Inthe<body>sectionoftheindex.htmlfile,there'sa<span>elementwithid="score".We'regoingtoupdatethiselementusingtheemscripten_run_scriptcommandtodisplaythecurrentscore.Asaresult,thedisplayScore()functionbecomesmuchshorter.Thebeforeandafterisshownasfollows.

HereistheoriginalversionoftheBoardclass'sdisplayScore()function:

voidBoard::displayScore(SDL_Renderer*renderer,TTF_Font*font){

std::stringstreammessage;

message<<"ROWS:"<<currentScore_;

SDL_Colorwhite={255,255,255};

SDL_Surface*surface=TTF_RenderText_Blended(

font,

message.str().c_str(),

white);

SDL_Texture*texture=SDL_CreateTextureFromSurface(

renderer,

surface);

SDL_RectmessageRect{20,BoardHeight+15,surface->w,surface->h};

SDL_FreeSurface(surface);

SDL_RenderCopy(renderer,texture,nullptr,&messageRect);

SDL_DestroyTexture(texture);

}

HereistheportedversionofthedisplayScore()function:

voidBoard::displayScore(intnewScore){

std::stringstreamaction;

action<<"document.getElementById('score').innerHTML="<<newScore;

emscripten_run_script(action.str().c_str());

}

Theemscripten_run_scriptactionsimplyfindsthe<span>elementontheDOMandsetstheinnerHTMLtothecurrentscore.Wecan'tusetheEM_ASM()functionherebecauseEmscriptendoesn'trecognizethedocumentobject.SincewehaveaccesstotheprivatecurrentScore_variableintheclass,we'regoingtomovethedisplayScore()callinthedraw()functionintotheunite()function.ThislimitstheamountofcallstodisplayScore()toensurethatthefunctioniscalledonlywhenthescorehasactuallychanged.Weonlyneedtoaddonelineofcodetoaccomplishthis.Here'swhattheunite()functionlookslikenow:

voidBoard::unite(constPiece&piece){

for(intcolumn=0;column<PieceSize;++column){

for(introw=0;row<PieceSize;++row){

if(piece.isBlock(column,row)){

intcolumnTarget=piece.getColumn()+column;

introwTarget=piece.getRow()+row;

cells_[columnTarget][rowTarget]=true;

}

}

}

//Continuouslyloopsthrougheachoftherowsuntilnofullrowsare

//detectedandensuresthefullrowsarecollapsedandnon-fullrows

//areshiftedaccordingly:

while(areFullRowsPresent()){

for(introw=BoardRows-1;row>=0;--row){

if(isRowFull(row)){

updateOffsetRow(row);

currentScore_+=1;

for(intcolumn=0;column<BoardColumns;++column){

cells_[column][0]=false;

}

}

}

displayScore(currentScore_);//<-----Addthisline

}

}

Sincewe'renolongerusingtheSDL2_ttflibrary,wecanupdatethedraw()functionsignatureandremovethedisplayScore()functioncall.Here'stheupdateddraw()function:

voidBoard::draw(SDL_Renderer*renderer/*,TTF_Font*font*/){

//^^^^^^^^^^^^^^<--Removethisargument

//displayScore(renderer,font);<-----Deletethisline

SDL_SetRenderDrawColor(

renderer,

/*LightGray:*/140,140,140,255);

for(intcolumn=0;column<BoardColumns;++column){

for(introw=0;row<BoardRows;++row){

if(cells_[column][row]){

SDL_Rectrect{

column*Offset+1,

row*Offset+1,

Offset-2,

Offset-2

};

SDL_RenderFillRect(renderer,&rect);

}

}

}

}

ThedisplayScore()functioncallwasremovedfromthefirstlineofthefunctionandtheTTF_Font*fontargumentwasremovedaswell.Let'saddacalltodisplayScore()intheconstructortoensurethattheinitialvalueissetto0whenthegameendsandanewonebegins:

Board::Board():cells_{{false}},currentScore_(0){

displayScore(0);//<-----Addthisline

}

That'sitfortheclassfile.SincewechangedthesignaturesforthedisplayScore()anddraw()functions,andremovedthedependencyforSDL2_ttf,we'llneedtoupdatetheheaderfile.Removethefollowinglinesfromboard.h:

#ifndefTETRIS_BOARD_H

#defineTETRIS_BOARD_H

#include<SDL2/SDL.h>

//#include<SDL2/SDL2_ttf.h><-----Deletethisline

#include"constants.h"

#include"piece.h"

usingnamespaceConstants;

classBoard{

public:

Board();

voiddraw(SDL_Renderer*renderer/*,TTF_Font*font*/);

//^^^^^^^^^^^^^^<--Removethis

boolisCollision(constPiece&piece)const;

voidunite(constPiece&piece);

private:

boolisRowFull(introw);

boolareFullRowsPresent();

voidupdateOffsetRow(intfullRow);

voiddisplayScore(SDL_Renderer*renderer,TTF_Font*font);

//^^^^^^^^^^^^^^<--Removethis

boolcells_[BoardColumns][BoardRows];

intcurrentScore_;

};

#endif//TETRIS_BOARD_H

We'removingrightalong!Thefinalchangeweneedtomakeisthealsothebiggestone.TheexistingcodebasehasaGameclassthatmanagestheapplicationlogicandamain.cppfilethatcallstheGame.loop()functioninthemain()function.TheloopingmechanismisawhileloopthatcontinuestorunaslongastheSDL_QUITeventhasn'tfired.WeneedtochangeourapproachtoaccommodateforEmscripten.

Emscriptenprovidesanemscripten_set_main_loopfunctionthatacceptsanem_callback_funcloopingfunction,fps,andasimulate_infinite_loopflag.Wecan'tincludetheGameclassandpassGame.loop()astheem_callback_funcargument,becausethebuildwillfail.Instead,we'regoingtoeliminatetheGameclasscompletelyandmovethelogicintothemain.cppfile.Copythecontentsofgame.cppintomain.cpp(overwritingtheexistingcontents)anddeletetheGameclassfiles(game.cpp/game.h).Sincewe'renotdeclaringaclassforGame,removetheGame::prefixesfromthefunctions.Theconstructoranddestructorarenolongervalid(they'renolongerpartofaclass),soweneedtomovethatlogictoadifferentlocation.Wealsoneedtoreorderthefiletoensurethatourcalledfunctionscomebeforethecallingfunctions.Thefinalresultlookslikethis:

#include<emscripten/emscripten.h>

#include<SDL2/SDL.h>

#include<stdexcept>

#include"constants.h"

#include"board.h"

#include"piece.h"

usingnamespacestd;

usingnamespaceConstants;

staticSDL_Window*window=nullptr;

staticSDL_Renderer*renderer=nullptr;

staticPiececurrentPiece{static_cast<Piece::Kind>(rand()%7)};

staticBoardboard;

staticintmoveTime;

voidcheckForCollision(constPiece&newPiece){

if(board.isCollision(newPiece)){

board.unite(currentPiece);

currentPiece=Piece{static_cast<Piece::Kind>(rand()%7)};

if(board.isCollision(currentPiece))board=Board();

}else{

currentPiece=newPiece;

}

}

voidhandleKeyEvents(SDL_Event&event){

PiecenewPiece=currentPiece;

switch(event.key.keysym.sym){

caseSDLK_DOWN:

newPiece.move(0,1);

break;

caseSDLK_RIGHT:

newPiece.move(1,0);

break;

caseSDLK_LEFT:

newPiece.move(-1,0);

break;

caseSDLK_UP:

newPiece.rotate();

break;

default:

break;

}

if(!board.isCollision(newPiece))currentPiece=newPiece;

}

voidloop(){

SDL_Eventevent;

while(SDL_PollEvent(&event)){

switch(event.type){

caseSDL_KEYDOWN:

handleKeyEvents(event);

break;

caseSDL_QUIT:

break;

default:

break;

}

}

SDL_SetRenderDrawColor(renderer,/*DarkGray:*/58,58,58,255);

SDL_RenderClear(renderer);

board.draw(renderer);

currentPiece.draw(renderer);

if(SDL_GetTicks()>moveTime){

moveTime+=1000;

PiecenewPiece=currentPiece;

newPiece.move(0,1);

checkForCollision(newPiece);

}

SDL_RenderPresent(renderer);

}

intmain(){

moveTime=SDL_GetTicks();

if(SDL_Init(SDL_INIT_VIDEO)!=0){

throwstd::runtime_error("SDL_Init(SDL_INIT_VIDEO)");

}

SDL_CreateWindowAndRenderer(

BoardWidth,

BoardHeight,

SDL_WINDOW_OPENGL,

&window,

&renderer);

emscripten_set_main_loop(loop,0,1);

SDL_DestroyRenderer(renderer);

renderer=nullptr;

SDL_DestroyWindow(window);

window=nullptr;

SDL_Quit();

return0;

}

ThehandleKeyEvents()andcheckForCollision()functionshaven'tchanged;wesimplymovedthemtothetopofthefile.Theloop()functionreturntypewaschangedfrombooltovoidasrequiredbyemscripten_set_main_loop.Finally,thecodefromtheconstructoranddestructorwasmovedintothemain()functionandanyreferencestoSDL2_ttfwereremoved.Insteadofthewhilestatementthatcalledtheloop()functionofGame,wehavetheemscripten_set_main_loop(loop,0,1)call.Wechangedthe#includestatementsatthetopofthefiletoaccommodateforEmscripten,SDL2,andourBoardandPiececlasses.That'sitforchanges—nowit'stimetoconfigurethebuildandtestoutthegame.

BuildingandrunningthegameWiththecodeupdatedandtherequiredwebassetspresent,it'stimetobuildandtestoutthegame.Thecompilationstepissimilartothepreviousexamplesinthisbook,butwe'regoingtouseadifferenttechniquetorunthegame.Inthissection,we'regoingtoconfigurethebuildtasktoaccommodatefortheC++filesandruntheapplicationusingafeatureprovidedbyEmscripten.

BuildingwithVSCodetasksWe'regoingtoconfigurethebuildintwoways:withVSCodetasksandaMakefile.MakefilesareniceifyouprefertouseadifferenteditorthanVSCode.The/.vscode/tasks.jsonfilealreadycontainsthetasksyou'llneedtobuildtheproject.TheEmscriptenbuildstepisthedefault(asetofnativebuildtasksisalsopresent).Let'swalkthrougheachtaskinthetasksarrayandreviewwhat'stakingplace.Thefirsttaskdeletesanyexistingcompiledoutputfilespriortobuilding:{"label":"RemoveExistingWebFiles","type":"shell","command":"rimraf","options":{"cwd":"${workspaceRoot}/public"},"args":["index.js","index.wasm"]}

Thesecondtaskperformsthebuildwiththeemcccommand:

{

"label":"BuildWebAssembly",

"type":"shell",

"command":"emcc",

"args":[

"--bind","src/board.cpp","src/piece.cpp","src/main.cpp",

"-std=c++14",

"-O3",

"-s","WASM=1",

"-s","USE_SDL=2",

"-s","MODULARIZE=1",

"-o","public/index.js"

],

"group":{

"kind":"build",

"isDefault":true

},

"problemMatcher":[],

"dependsOn":["RemoveExistingWebFiles"]

}

Therelatedargumentsareplacedonthesameline.Theonlynewandunfamiliaradditiontotheargsarrayisthe--bindargumentwiththecorresponding.cppfiles.ThistellsEmscriptenthatallthefilesafter--bindarerequiredtobuildtheproject.TestoutthebuildbyselectingTasks|RunBuildTask...fromthemenuorusingthekeyboardshortcutCmd/Ctrl+Shift+B.Ittakesafewsecondstobuild,buttheterminalwillletyouknowwhenthecompilationprocessiscomplete.Ifsuccessful,youshouldseeanindex.jsandindex.wasmfileinthe/publicfolder.

BuildingwithaMakefileIfyouprefernottouseVSCode,youcanuseaMakefiletoaccomplishthesamegoalastheVSCodetasks.CreateafilenamedMakefileinyourprojectfolderandpopulateitwiththefollowingcontents(makesurethatthefileisusingtabs,notspaces):

#Thisallowsyoutojustrunthe"make"commandwithoutspecifying

#arguments:

.DEFAULT_GOAL:=build

#Specifieswhichfilestocompileaspartoftheproject:

CPP_FILES=$(wildcardsrc/*.cpp)

#FlagstouseforEmscriptenemcccompilecommand:

FLAGS=-std=c++14-O3-sWASM=1-sUSE_SDL=2-sMODULARIZE=1\

--bind$(CPP_FILES)

#Nameofoutput(the.wasmfileiscreatedautomatically):

OUTPUT_FILE=public/index.js

#Thisisthetargetthatcompilesourexecutable

compile:$(CPP_FILES)

emcc$(FLAGS)-o$(OUTPUT_FILE)

#Removestheexistingindex.jsandindex.wasmfiles:

clean:

rimraf$(OUTPUT_FILE)

rimrafpublic/index.wasm

#Removestheexistingfilesandbuildstheproject:

build:cleancompile

@echo"BuildComplete!"

TheoperationsbeingperformedareidenticaltotheVSCodetasks,justinadifferentformatusingmoreuniversaltooling.Thedefaultbuildstepissetinthefile,soyoucanrunthefollowingcommandwithinyourprojectfoldertocompiletheproject:

make

NowthatyouhaveacompiledWasmfileandJavaScriptgluecode,let'stryrunningthegame.

RunningthegameInsteadofusingserveorbrowser-sync,we'regoingtouseabuilt-infeatureofEmscripten'stoolchain,emrun.Itprovidestheaddedbenefitofcapturingstdoutandstderr(ifyoupassthe--emrunlinkerflagtotheemcccommand)andprintingthemtotheterminalifdesired.We'renotgoingtousethe--emrunflag,buthavingalocalwebserveravailablewithouthavingtoinstallanyadditionaldependenciesisaniceaddedfeaturetobeawareof.Openupaterminalinstancewithinyourprojectfolderandrunthefollowingcommandtostartthegame:emrun--browserchrome--no_emrun_detectpublic/index.html

Youcanspecifyfirefoxforthebrowserifthat'swhatyou'reusingfordevelopment.The--no_emrun_detectflaghidesamessageintheterminalstatingthattheHTMLpageisnotemruncapable.Ifyounavigatetohttp://localhost:6931/index.html,youshouldseethefollowing:

Tetrisrunninginthebrowser

Tryrotatingandmovingthepiecestoensurethateverythingisworkingcorrectly.TheROWScountshouldincrementbyonewhenyou'vesuccessfullyclearedarow.Youmayalsonoticethatifyou'retooclosetotheedgeoftheboard,youwon'tbeabletorotatesomeofthepieces.Congratulations,you'vesuccessfullyportedaC++gameovertoEmscripten!

SummaryInthischapter,weportedaTetrisclonewritteninC++thatusedSDL2toEmscriptensoitcouldberuninthebrowserwithWebAssembly.WecoveredtherulesofTetrisandhowtheymaptothelogicwithintheexistingcodebase.WealsoreviewedeachfileintheexistingcodebaseindividuallyandwhichchangeshadtobemadetosuccessfullycompiletoaWasmfileandJavaScriptgluecode.Afterupdatingtheexistingcode,wecreatedtherequiredHTMLandCSSfiles,thenconfiguredabuildstepwiththeappropriateemccflags.Oncebuilt,thegamewasrunusingEmscripten'semruncommand.

InChapter9,IntegratingwithNode.js,we'regoingtodiscusshowtointegrateWebAssemblyintoNode.jsandthebenefitsthisintegrationprovides.

Questions1. WhatarethepiecescalledinTetris?2. WhatisonereasonforchoosingnottoportanexistingC++codebaseto

Emscripten?3. Whattooldidweusetocompilethegamenatively(forexample,toan

executable)?4. Whatisthepurposeoftheconstants.hfile?5. WhywereweabletoeliminatetheSDL2_ttflibrary?6. WhichEmscriptenfunctiondidweusetostartrunningthegame?7. Whichargumentdidweaddtotheemcccommandtobuildthegameand

whatpurposedoesitserve?8. WhatadvantagedoesemrunofferoveratoollikeserveandBrowsersync?

FurtherreadingHeaderFilesinC++:https://www.sitesbay.com/cpp/cpp-header-filesSDL2TetrisonGitHub:https://github.com/andwn/sdl2-tetrisTetrisonGitHub:https://github.com/abesary/tetrisTetris-LinuxonGitHub:https://github.com/abesary/tetris-linux

IntegratingwithNode.js

ThemodernwebleansheavilyonNode.jsforbothdevelopmentandserver-sidemanagement.Withtheadventofincreasinglycomplexbrowserapplicationsthatperformcomputationallyexpensiveoperations,performanceincreasescanbeincrediblybeneficial.Inthischapter,we'regoingtodescribethevariouswaysyoucanintegrateWebAssemblywithNode.jsthroughtheuseofvariousexamples.

Ourgoalforthischapteristounderstandthefollowing:

TheadvantagesofintegratingWebAssemblywithNode.jsHowtointeractwiththeNode.jsWebAssemblyAPIHowtoutilizeWasmmodulesinaprojectthatusesWebpackHowtowriteunittestsforWebAssemblymodulesusingnpmlibraries

WhyNode.js?InChapter3,SettingUpaDevelopmentEnvironment,Node.jswasdescribedasanasynchronousevent-drivenJavaScriptruntime,whichisthedefinitiontakenfromtheofficialwebsite.WhatNode.jsrepresents,however,isaprofoundshiftinthewaywebuildandmanagewebapplications.Inthissection,wewilldiscusstherelationshipbetweenWebAssemblyandNode.js,andwhythetwotechnologiescomplementeachothersowell.

SeamlessintegrationNode.jsrunsonGoogle'sV8JavaScriptengine,whichpowersGoogleChrome.SinceV8'sWebAssemblyimplementationadherestotheCoreSpecification,youcaninteractwithaWebAssemblymoduleusingthesameAPIasthebrowser.Insteadofperformingafetchcallfora.wasmfile,youcanuseNode.js'sfsmoduletoreadthecontentsintoabuffer,thencallinstantiate()ontheresult.

ComplementarytechnologiesJavaScripthaslimitationsontheserversideaswell.ExpensivecomputationorworkingwithlargenumberscanbeoptimizedwithWebAssembly'ssuperiorperformance.Asascriptinglanguage,JavaScriptexcelsatautomatingsimpletasks.YoucouldwriteascripttocompileC/C++toaWasmfile,copyittoabuildfolder,andseethechangesreflectedinthebrowserifyou'reusingatoollikeBrowsersync.

DevelopmentwithnpmNode.jshasanextensiveecosystemoftoolsandlibrariesintheformofnpm.SvenSauleauandothermembersoftheopensourcecommunityhavecreatedwebassemblyjs,anextensivesuiteoftoolingforWebAssemblybuiltwithNode.js.Thewebassemblyjssiteathttps://webassembly.js.orgincludesthetaglineToolchainforWebAssembly.Therearecurrentlyover20npmpackagestoperformvarioustasksandaidindevelopment,suchasanESLintplugin,anASTvalidator,andaformatter.AssemblyScript,aTypeScripttoWebAssemblycompiler,allowsyoutowriteperformantcodethatcompilestoaWasmmodulewithouthavingtolearnCorC++.TheNode.jscommunityisclearlyvestedinWebAssembly'ssuccess.

Server-sideWebAssemblywithExpressNode.jscanbeusedinseveralwaystoaddvaluetoaWebAssemblyproject.Inthissection,we'regoingtowalkthroughanexampleNode.jsapplicationthatintegratesWebAssembly.TheapplicationusesExpresswithsomesimpleroutestocallfunctionsfromacompiledWasmmodule.

OverviewoftheprojectTheprojectreusessomeofthecodefromtheapplicationwebuiltinChapter7,CreatinganApplicationfromScratch(CooktheBooks)todemonstratehowNode.jscanbeusedwithWebAssembly.Thecodeforthissectionislocatedinthe/chapter-09-node/server-examplefolderinthelearn-webassemblyrepository.We'regoingtoreviewportionsoftheapplicationdirectlyapplicabletoNode.js.Thefollowingstructurerepresentsthefilestructurefortheproject:├──/lib│└──main.c├──/src|├──Transaction.js|├──/assets|│├──db.json|│├──main.wasm|│└──memory.wasm|├──assign-routes.js|├──index.js|└──load-assets.js├──package.json├──package-lock.json└──requests.js

Withregardtodependencies,theapplicationusestheexpressandbody-parserlibrariestosetuproutesandparseJSONfromthebodyofrequests.Fordatamanagement,ituseslowdb,alibrarythatprovidesmethodsforreadingandupdatingaJSONfile.TheJSONfileislocatedin/src/assets/db.jsonandcontainsdatathatwasslightlymodifiedfromtheCooktheBooksdataset.We'reusingnodemontowatchforchangesinthe/srcfolderandreloadtheapplicationautomatically.We'reusingrimraftomanagefiledeletion.Thelibraryisincludedasadependencyintheeventthatyoudidn'tinstallitgloballyinChapter3,SettingUpaDevelopmentEnvironment.Finally,thenode-fetchlibraryallowsustousethefetchAPItomakeHTTPrequestswhentestingtheapplication.

TosimplifyfunctionalityinboththeJavaScriptandCfiles,therawAmountandcookedAmountfieldswerereplacedwithasingleamountfield,andthecategoryfieldisnowcategoryId,whichmapstoacategoriesarrayindb.json.

ExpressconfigurationTheapplicationisloadedin/src/index.js.Thecontentsofthisfileareshownasfollows:

constexpress=require('express');

constbodyParser=require('body-parser');

constloadAssets=require('./load-assets');

constassignRoutes=require('./assign-routes');

//IfyouprefacethenpmstartcommandwithPORT=[YourPort]on

//macOS/UbuntuorsetPORT=[YourPort]onWindows,itwillchangetheport

//thattheserverisrunningon,soPORT=3001willruntheappon

//port3001:

constPORT=process.env.PORT||3000;

conststartApp=async()=>{

constapp=express();

//Usebody-parserforparsingJSONinthebodyofarequest:

app.use(bodyParser.urlencoded({extended:true}));

app.use(bodyParser.json());

//InstantiatetheWasmmoduleandlocaldatabase:

constassets=awaitloadAssets();

//SetuproutesthatcaninteractwithWasmandthedatabase:

assignRoutes(app,assets);

//Starttheserverwiththespecifiedport:

app.listen(PORT,(err)=>{

if(err)returnPromise.reject(err);

returnPromise.resolve();

});

};

startApp()

.then(()=>console.log(`Serverisrunningonport${PORT}`))

.catch(err=>console.error(`Anerroroccurred:${err}`));

ThisfilesetsupanewExpressapp,addsthebody-parsermiddleware,loadsthemockdatabaseandWasminstance,andassignsroutes.Let'smoveontodiscussingthedifferencebetweeninstantiatingaWasmmoduleinthebrowserandNode.js.

InstantiatingaWasmmodulewithNode.jsTheWasmfilesareinstantiatedin/src/load-assets.js.We'reusingthememory.wasmfilefromCooktheBooks,butthe/assets/main.wasmfileiscompiledfromaslightlydifferentversionofmain.c,whichislocatedinthe/libfolder.TheloadWasm()functionperformsthesameoperationastheWasminitializationcodefromCooktheBooks,butthemethodforpassinginthebufferSourcetoWebAssembly.instantiate()isdifferent.Let'sexaminethisfurtherbyreviewingaportionofthecodeintheloadWasm()functionoftheload-assets.jsfile:constfs=require('fs');constpath=require('path');

constassetsPath=path.resolve(__dirname,'assets');

constgetBufferSource=fileName=>{constfilePath=path.resolve(assetsPath,fileName);returnfs.readFileSync(filePath);//<-Replacesthefetch()and.arrayBuffer()};

//We'reusingasync/awaitbecauseitsimplifiesthePromisesyntaxconstloadWasm=async()=>{constwasmMemory=newWebAssembly.Memory({initial:1024});constmemoryBuffer=getBufferSource('memory.wasm');constmemoryInstance=awaitWebAssembly.instantiate(memoryBuffer,{env:{memory:wasmMemory}});...

Toelaborateonthedifferences,here'ssomecodethatinstantiatesamoduleusingfetch:

fetch('main.wasm')

.then(response=>{

if(response.ok)returnresponse.arrayBuffer();

thrownewError('UnabletofetchWebAssemblyfile');

})

.then(bytes=>WebAssembly.instantiate(bytes,importObj));

WhenusingNode.js,thefetchcallisreplacedbythefs.readFileSync()functionandthearrayBuffer()functionisnolongerrequiredbecausefs.readFileSync()returnsabufferthatcanbepasseddirectlyintotheinstantiate()function.OncetheWasmmoduleisinstantiated,wecanstartinteractingwiththeinstance.

CreatingamockdatabaseTheload-assets.jsfilealsocontainsamethodforcreatingamockdatabaseinstance:

constloadDb=()=>{

constdbPath=path.resolve(assetsPath,'db.json');

constadapter=newFileSync(dbPath);

returnlow(adapter);

};

TheloadDb()functionloadsthecontentsof/assets/db.jsonintoaninstanceoflowdb.Thedefaultfunctionexportedfromload-assets.jscallstheloadWasm()andloadDb()functionsandreturnsanobjectcontainingthemockdatabaseandWasminstance:

module.exports=asyncfunctionloadAssets(){

constdb=loadDb();

constwasmInstance=awaitloadWasm();

return{

db,

wasmInstance

};

};

Goingforward,I'llusethetermdatabasetorefertothelowdbinstancethataccessesthedb.jsonfile.Nowthattheassetsareloaded,let'sreviewhowtheapplicationinteractswiththem.

InteractingwiththeWebAssemblymoduleInteractionwiththedatabaseandWasminstancetakesplaceacrosstwofilesinthe/srcfolder:Transaction.jsandassign-routes.js.Inourexampleapplication,allcommunicationwiththeAPIisperformedviaHTTPrequests.Sendingarequesttoaspecificendpointwilltriggersomeinteractionwiththedatabase/Wasminstanceontheserver.Let'sstartbyreviewingTransaction.js,whichinteractsdirectlywiththedatabaseandWasminstance.

WrappinginteractioninTransaction.jsJustaswithCooktheBooks,there'saclassthatwrapstheWasminteractioncodeandprovidesacleaninterface.ThecontentsofTransaction.jsareverysimilartothecontentsof/src/store/WasmTransactions.jsfromCooktheBooks.MostofthechangesaccommodateforthecategoryIdbeingpresentinatransactionrecordandasingleamountfield(nomorerawandcookedamounts).Additionalfunctionalitywasaddedtointeractwiththedatabase.Forexample,here'safunctionthateditsanexistingtransaction,bothinthedatabaseandthelinkedlistfromtheWasminstance:

getValidAmount(transaction){

const{amount,type}=transaction;

returntype==='Withdrawal'?-Math.abs(amount):amount;

}

edit(transactionId,contents){

constupdatedTransaction=this.db.get('transactions')

.find({id:transactionId})

.assign(contents)

.write();

const{categoryId,...transaction}=updatedTransaction;

constamount=this.getValidAmount(transaction);

this.wasmInstance._editTransaction(transactionId,categoryId,amount);

returnupdatedTransaction;

}

Theedit()functionupdatesthedatabaserecordthatcorrespondstothetransactionIdargumentwiththevaluesinthecontentsargument.this.dbisthedatabaseinstancethatwascreatedintheload-assets.jsfile.SincethecategoryIdfieldisavailableontheupdatedTransactionrecord,wecanpassitdirectlytothis.wasmInstance._editTransaction().ItgetspassedintotheconstructorwhenanewinstanceofTransactioniscreated.

Transactionoperationsinassign-routes.jsTheassign-routes.jsfiledefinesroutesandaddsthemtotheexpressinstance(app)createdinindex.js.InExpress,routescanbedefineddirectlyonapp(forexample,app.get()),orthroughtheuseofaRouter.Inthiscase,aRouterwasusedtoaddmultiplemethodstothesameroutepath.Thefollowingcode,takenfromtheassign-routes.jsfile,createsaRouterinstanceandaddstworoutes:aGETroutethatreturnsalltransactions,andaPOSTroutethatcreatesanewtransaction:

module.exports=functionassignRoutes(app,assets){

const{db,wasmInstance}=assets;

consttransaction=newTransaction(db,wasmInstance);

consttransactionsRouter=express.Router();

transactionsRouter

.route('/')

.get((req,res)=>{

consttransactions=transaction.findAll();

res.status(200).send(transactions);

})

.post((req,res)=>{

const{body}=req;

if(!body){

returnres.status(400).send('Bodyofrequestisempty');

}

constnewRecord=transaction.add(body);

res.status(200).send(newRecord);

});

...

//SetthebasepathforallroutesontransactionsRouter:

app.use('/api/transactions',transactionsRouter);

}

Theapp.use()functionattheendofthesnippetspecifiesthatallroutesdefinedonthetransactionsRouterinstanceareprefixedwith/api/transactions.Ifyouwererunningtheapplicationlocallyonport3000,youcouldnavigatetohttp://localhost:3000/api/transactionsinyourbrowserandseeanarrayofallthetransactionsinJSONformat.

Asyoucanseefromthebodyoftheget()andpost()functions,interactionswith

anytransactionrecordsarebeingdelegatedtotheTransactioninstancecreatedinline3.Thatcompletesourreviewofpertinentsectionsofthecodebase.Eachofthefilescontaincommentsdescribingthefile'sfunctionalityandpurpose,soyoumaywanttoreviewthosebeforemovingontothenextsection.Inthenextsection,we'llbuild,run,andinteractwiththeapplication.

BuildingandrunningtheapplicationBeforewebuildandtestouttheproject,you'llneedtoinstallthenpmdependencies.Openaterminalwithinthe/server-examplefolderandrunthefollowingcommand:

npminstall

Oncethat'scomplete,you'rereadytomoveontothebuildstep.

BuildingtheapplicationInthecaseofthisapplication,buildingreferstocompilingthelib/main.ctoa.wasmfileusingtheemcccommand.SincethisisaNode.jsproject,wecanusethescriptskeyinourpackage.jsonfiletodefineTasks.YoucanstilluseVSCode'sTasksfeaturebecauseitautomaticallydetectsthescriptsfromyourpackage.jsonfileandpresentstheminthelistoftaskswhenyouselectTasks|RunTask...fromthemenu.Thefollowingcodecontainsthecontentsofthescriptssectioninthisproject'spackage.jsonfile:

"scripts":{

"prebuild":"rimrafsrc/assets/main.wasm",

"build":"emcclib/main.c-Os-sWASM=1-sSIDE_MODULE=1

-sBINARYEN_ASYNC_COMPILATION=0-sALLOW_MEMORY_GROWTH=1

-osrc/assets/main.wasm",

"start":"nodesrc/index.js",

"watch":"nodemonsrc/*--exec'npmstart'"

},

Thebuildscriptwassplitacrossmultiplelinesfordisplaypurposes,soyou'dhavetocombinethoselinesforvalidJSON.TheprebuildscriptremovestheexistingWasmfile,andthebuildscriptrunstheemcccommandwiththerequiredflagstocompilelib/main.candoutputtheresulttosrc/assets/main.wasm.Torunthescript,openaterminalwithinthe/server-examplefolderandrunthefollowingcommand:

npmrunbuild

Ifthe/src/assetsfoldercontainsafilenamedmain.wasm,thebuildcompletedsuccessfully.Ifanerrorhasoccurred,theterminalshouldprovideadescriptionoftheerror,aswellasastacktrace.

Youcancreatenpmscriptsthatrunbeforeorafteraspecificscriptbycreatinganentrywiththesamenameandprefixingitwithpreorpost.Forexample,ifyouwantedtorunascriptafterthebuildscripthascompleted,youcancreateascriptnamed"postbuild"andspecifythecommandyouwanttorun.

StartingandtestingouttheapplicationIfyou'remakingchangestotheapplicationortryingtofixabug,youcouldusethewatchscripttowatchforanychangestothecontentsofthe/srcfolderandautomaticallyrestarttheapplicationifachangewasmade.Sincewe'rejustrunningandtestingouttheapplication,wecanusethestartcommandinstead.Intheterminal,ensureyou'reinthe/server-examplefolderandrunthefollowingcommand:

npmstart

YoushouldseeamessagethatsaysServerisrunningonport3000.You'renowabletosendHTTPrequeststotheserver.Totesttheapplication,openanewterminalinstancewithintheserver-exampledirectoryandrunthefollowingcommand:

node./requests.js1

ThisshouldlogouttheresponsebodyoftheGETcalltothe/api/transactionsendpoint.Therequests.jsfilecontainsfunctionalitythatallowsyoutomakerequeststoalloftheavailableroutes.ThegetFetchActionForId()functionreturnsanobjectwithanendpointandoptionsvalue,whichcorrespondstoarouteintheassign-routes.jsfile.TheactionIdisanarbitrarynumbertosimplifytestingandreducetheamountoftypingforrunningcommands.Forexample,youcouldrunthefollowingcommand:

node./requests.js5

ItwilllogoutthesumofalltransactionsfortheComputer&Internetcategory.Youcanpassanadditionalargumenttothenodecommandifyouwantthetotalforadifferentcategory.TogetthesumofalltransactionsintheInsurancecategory,runthiscommand:

node./requests.js53

Trygoingthrougheachoftherequests(thereareeightintotal).Ifyoumakearequestthatadds,removes,oreditsatransaction,youshouldseethechangesinthe/src/assets/db.jsonfile.That'sitfortheNode.jsexampleproject.Inthenextsection,we'llutilizeWebpacktoloadandinteractwithaWasmmodule.

Client-sideWebAssemblywithWebpackWebapplicationscontinuetogrowincomplexityandsize.SimplyservingupafewhandwrittenHTML,CSS,andJavaScriptfilesisnotfeasibleforlargeapplications.Tomanagethiscomplexity,webdevelopersusebundlerstoallowformodularization,ensurebrowsercompatibility,andreducethesizeofJavaScriptfiles.Inthissection,we'regoingtobeusingapopularbundler,Webpack,toutilizeWasmwithoutusingemcc.

OverviewoftheprojectTheexampleWebpackapplicationextendsthefunctionalityoftheCcodewewroteintheCompilingCwithoutthegluecodesectionofChapter5,CreatingandLoadingaWebAssemblyModule.Insteadofshowingabluerectanglebouncingaroundaredbackground,we'llshowanalieninaspaceshipbouncingaroundtheHorseheadNebula.Thecollisiondetectionfunctionalityhasbeenmodifiedtoaccommodateforbouncingwithinarectangle,sothemovementofthespaceshipwillberandom.Thecodeforthissectionislocatedinthe/chapter-09-node/webpack-examplefolderinthelearn-webassemblyrepository.Thefilestructurefortheprojectisshowninthefollowingcode:

├──/src

│├──/assets

││├──background.jpg

││└──spaceship.svg

│├──App.js

│├──index.html

│├──index.js

│├──main.c

│└──styles.css

├──package.json

├──package-lock.json

└──webpack.config.js

We'llreviewtheWebpackconfigurationfileinalatersection.Fornow,let'stakeamomenttodiscussWebpackinmoredetail.

WhatisWebpack?TheJavaScriptecosystemhasbeenrapidlyevolvingoverthepastseveralyears,resultinginnewframeworksandlibrariespoppingupconstantly.BundlerscameaboutasawaytoenabledeveloperstosplitaJavaScriptapplicationintoseveralfileswithouthavingtoworryaboutmanagingglobalnamespaces,scriptloadingorder,oranincrediblylonglistof<script>tagsintheHTMLfile.Abundlercombinesallofthefilesintooneandresolvesanynamingcollisions.

Webpackis,atthetimeofwriting,oneofthemostpopularbundlersforfrontenddevelopment.ItdoesmuchmorethancombineJavaScriptfiles,however.Italsoperformscomplextaskssuchascode-splittingandtreeshaking(dead-codeelimination).Webpackwasdesignedwithapluginarchitecture,whichresultedinamassiveamountofcommunity-developedplugins.AsearchforWebpackonnpmcurrentlyreturnsover12,000packages!Thisexhaustivelistofplugins,alongwithitspowerfulbuilt-infeatureset,makesWebpackafull-fledgedbuildtool.

InstallingandconfiguringWebpackBeforewebegintheapplicationwalk-through,openupaterminalwithinthe/webpack-examplefolderandrunthefollowingcommand:

npminstall

DependenciesoverviewTheapplicationusesVersion4ofWebpack(themostrecentversionasofwritingthis)tobuildourapplication.WeneedtouseWebpackpluginstoloadthevariousfiletypesusedintheapplicationandBabeltoutilizenewerJavaScriptfeatures.ThefollowingsnippetliststhedevDependencieswe'reusingintheproject(takenfrompackage.json):

...

"devDependencies":{

"@babel/core":"^7.0.0-rc.1",

"@babel/preset-env":"^7.0.0-rc.1",

"babel-loader":"^8.0.0-beta.4",

"cpp-wasm-loader":"0.7.7",

"css-loader":"1.0.0",

"file-loader":"1.1.11",

"html-loader":"0.5.5",

"html-webpack-plugin":"3.2.0",

"mini-css-extract-plugin":"0.4.1",

"rimraf":"2.6.2",

"webpack":"4.16.5",

"webpack-cli":"3.1.0",

"webpack-dev-server":"3.1.5"

},

...

Ispecifiedexactversionsforsomeofthelibrariestoensuretheapplicationbuildsandrunssuccessfully.Anylibrarieswithanameendingin-loaderor-pluginareusedinconjunctionwithWebpack.Thecpp-wasm-loaderlibraryallowsustoimportaCorC++filedirectly,withouthavingtocompileittoWasmfirst.Webpack4hasbuilt-insupportforimporting.wasmfiles,butyoucan'tspecifyanimportObjargument,whichisrequiredformodulesgeneratedwithEmscripten.

Configuringloadersandpluginsinwebpack.config.jsWe'reusingseveraldifferentfiletypesinadditiontoJavaScriptfortheapplication:CSS,SVG,HTML,andsoon.Installingthe-loaderdependenciesisonlypartoftheequation—youalsoneedtotellWebpackhowtoloadthem.Youalsoneedtospecifyconfigurationdetailsforanypluginsyouhaveinstalled.Youcanspecifytheloadingandconfigurationdetailsinawebpack.config.jsfileintherootfolderofyourproject.Thefollowingsnippetcontainsthecontentsof/webpack-example/webpack.config.js:

constHtmlWebpackPlugin=require('html-webpack-plugin');

constMiniCssExtractPlugin=require('mini-css-extract-plugin');

module.exports={

module:{

rules:[

{

test:/\.js$/,

exclude:/node_modules/,

use:{

loader:'babel-loader',

options:{

//Weneedthistouseasync/await:

presets:[

[

'@babel/preset-env',{

targets:{node:'10'}

}

]

]

}

}

},

{

test:/\.html$/,

use:{

loader:'html-loader',

options:{minimize:true}

}

},

{

test:/\.css$/,

use:[MiniCssExtractPlugin.loader,'css-loader']

},

{

test:/\.(c|cpp)$/,

use:{

loader:'cpp-wasm-loader',

options:{

emitWasm:true

}

}

},

{

test:/\.(png|jpg|gif|svg)$/,

use:{

loader:'file-loader',

options:{

name:'assets/[name].[ext]'

}

}

}

]

},

plugins:[

newHtmlWebpackPlugin({

template:'./src/index.html',

filename:'./index.html'

}),

//Thisisusedforbundling(buildingforproduction):

newMiniCssExtractPlugin({

filename:'[name].css',

chunkFilename:'[id].css'

})

]

};

TherulessectiontellsWebpackwhichloadertouseforafileextension.ThefourthiteminthearrayhandlesC/C++files(notethetestfieldvaluecontainingc|cpp).TheHtmlWebpackPlugintakesthecontentsof/src/index.html,addsanyrequired<script>tags,minifiesit,andcreatesanindex.htmlinthebuildfolder,whichdefaultsto/dist.TheMiniCssExtractPlugincopiesanyimportedCSSintoasingleCSSfileinthe/distfolder.We'llreviewhowtobuildtheprojectinalatersection,solet'smoveontotheapplicationcode,startingwiththeCfile.

TheCcodeSincewe'reallowedtoimportCandC++filesdirectly,theCfileislocatedwithinthe/srcfolder.Thisfile,main.c,containslogictomanagecollisiondetectionandmovethespaceshiparoundthe<canvas>.Thecodeisbasedonthewithout-glue.cfilewecreatedinChapter5,CreatingandLoadingaWebAssemblyModule.We'renotgoingtoreviewtheentirefile,onlythesectionsthathavechangedandmeritexplanation.Let'sbeginwiththedefinitionsanddeclarationssection,whichincludesanewstruct:Bounds.

DefinitionsanddeclarationsThecodecontainingthedefinitionsanddeclarationssectionsisshownasfollows:

typedefstructBounds{

intwidth;

intheight;

}Bounds;

//We'reusingtheterm"Rect"torepresenttherectanglethe

//imageoccupies:

typedefstructRect{

intx;

inty;

intwidth;

intheight;

//Horizontaldirectionoftravel(L/R):

charhorizDir;

//Verticaldirectionoftravel(U/D):

charvertDir;

}Rect;

structBoundsbounds;

structRectrect;

NewpropertieswereaddedtotheexistingRectdefinitiontoaccommodateforflexiblesizingandtrackingmovementinthexandydirections.Wedefinedanewstruct,Bounds,andremovedtheexisting#definestatementsbecausethe<canvas>elementisnolongerasquarewithstaticdimensions.Anewinstanceofbothelementsisdeclaredwhenthemoduleloads.Thedimensionalpropertiesoftheseinstancesareassignedinthestart()function,whichwe'llcovernext.

Thestart()functionTheupdatedstart()function,whichactsastheentrypointtothemodule,isshownasfollows:

EMSCRIPTEN_KEEPALIVE

voidstart(intboundsWidth,intboundsHeight,intrectWidth,

intrectHeight){

rect.x=0;

rect.y=0;

rect.horizDir='R';

rect.vertDir='D';

rect.width=rectWidth;

rect.height=rectHeight;

bounds.width=boundsWidth;

bounds.height=boundsHeight;

setIsRunning(true);

}

AnyfunctionsthatarecalledfromJavaScriptareprependedwiththeEMSCRIPTEN_KEEPALIVEstatement.We'renowpassingthewidthandheightofboththeBoundsandRectelementsasargumentstothestart()function,whichweassigntothelocalboundsandrectvariables.Thisallowsustoeasilychangethedimensionsofeitheronewithouthavingtomakeanychangestothecollisiondetectionlogic.Inthecontextofthisapplication,therectrepresentstherectangleinwhichthespaceshipimageresides.Wesetthedefaulthorizontalandverticaldirectionfortherectsotheimageinitiallymovestotherightanddown.Let'smoveontotherectmovement/collisiondetectioncode.

TheupdateRectLocation()functionThecoderelatedtocollisiondetectionandtheRectmovementishandledintheupdateRectLocation()function,whichisshownasfollows:

/**

*Updatestherectanglelocationby+/-1pxinthexorybasedon

*thecurrentlocation.

*/

voidupdateRectLocation(){

//Determineiftheboundingrectanglehas"bumped"intoeither

//theleft/rightsideortop/bottomside.Dependingonwhichside,

//flipthedirection:

intxBouncePoint=bounds.width-rect.width;

if(rect.x==xBouncePoint)rect.horizDir='L';

if(rect.x==0)rect.horizDir='R';

intyBouncePoint=bounds.height-rect.height;

if(rect.y==yBouncePoint)rect.vertDir='U';

if(rect.y==0)rect.vertDir='D';

//Ifthedirectionhaschangedbasedonthexandy

//coordinates,ensurethexandypointsupdate

//accordingly:

inthorizIncrement=1;

if(rect.horizDir=='L')horizIncrement=-1;

rect.x=rect.x+horizIncrement;

intvertIncrement=1;

if(rect.vertDir=='U')vertIncrement=-1;

rect.y=rect.y+vertIncrement;

}

TheprimarydifferencebetweenthiscodeandthecodewewroteinChapter5,CreatingandLoadingaWebAssemblyModule,isthecollisiondetectionlogic.Insteadofsimplytrackingthelocationoftherectinstancehorizontallyandchangingdirectionwhenithitstherightboundary,thefunctionnowtracksthehorizontalandverticaldirectionsandmanageseachindependently.Althoughthisisn'tthemostperformantalgorithm,itdoesachievethegoalofensuringthespaceshipchangesdirectionwhenitencounterstheedgeofthe<canvas>.

TheJavaScriptcodeTheonlyproductiondependencywe'reusingfortheapplicationisVue.Althoughtheapplicationconsistsofasinglecomponent,Vuemakesmanagingdata,functions,andthecomponentlife-cyclemuchsimplerthantryingtodoitmanually.Theindex.jsfilecontainstheVueinitializationcode,whiletherenderingandapplicationlogicisin/src/App.js.Thisfilehasalotofmovingparts,sowe'regoingtoreviewthecodeinchunks,aswedidintheprevioussection.Let'sstartwiththeimportstatements.

TheimportstatementsThefollowingcodedemonstratestheWebpackloadersinaction:

//Thisisloadedusingthecss-loaderdependency:

import'./styles.css';

//Thisisloadedusingthecpp-wasm-loaderdependency:

importwasmfrom'./main.c';

//Theseareloadedusingthefile-loaderdependency:

importbackgroundImagefrom'./assets/background.jpg';

importspaceshipImagefrom'./assets/spaceship.svg';

Theloadersweconfiguredinthewebpack.config.jsfileunderstandhowtohandleCSS,C,andimagefiles.Nowthatwehavetherequiredresourcesavailable,wecanstartdefiningourcomponentstate.

ComponentstateThefollowingcodeinitializesthelocalstateinthedata()functionforourcomponent:

exportdefault{

data(){

return{

instance:null,

bounds:{width:800,height:592},

rect:{width:200,height:120},

speed:5

};

},

...

Althoughtheboundsandrectpropertiesneverchange,wedefinedtheminthelocalstatetokeepallthedatausedbythecomponentinasinglelocation.Thespeedpropertydictateshowquicklythespaceshipmovesacrossthe<canvas>andhasarangeof1to10.Theinstancepropertyisinitializedtonull,butwillbeusedtoaccessthecompiledWasmmodule'sexportedfunctions.Let'smoveontotheWasminitializationcodethatcompilestheWasmfileandpopulatesthe<canvas>.

WasminitializationThecodetocompiletheWasmfileandpopulatethe<canvas>elementisshownasfollows:

methods:{

//CreateanewImageinstancetopassintothedrawImagefunction

//forthe<canvas>element'scontext:

loadImage(imageSrc){

constloadedImage=newImage();

loadedImage.src=imageSrc;

returnnewPromise((resolve,reject)=>{

loadedImage.onload=()=>resolve(loadedImage);

loadedImage.onerror=()=>reject();

});

},

//Compile/loadthecontentsofmain.candassigntheresulting

//Wasmmoduleinstancetothecomponentsthis.instanceproperty:

asyncinitializeWasm(){

constctx=this.$refs.canvas.getContext('2d');

//CreateImageinstancesofthebackgroundandspaceship.

//Thesearerequiredtopassintothectx.drawImage()function:

const[bouncer,background]=awaitPromise.all([

this.loadImage(spaceshipImage),

this.loadImage(backgroundImage)

]);

//CompiletheCcodetoWasmandassigntheresulting

//module.exportstothis.instance:

const{width,height}=this.bounds;

returnwasm

.init(imports=>({

...imports,

_jsFillRect(x,y,w,h){

ctx.drawImage(bouncer,x,y,w,h);

},

_jsClearRect(){

ctx.drawImage(background,0,0,width,height);

}

}))

.then(module=>{

this.instance=module.exports;

returnPromise.resolve();

});

},

...

Thereareadditionalfunctionsdefinedinthemethodskeyofthecomponent,butfornowwe'llfocusonthecodethatcompilestheimportedCfiletoWasm.AfterImageinstancesarecreatedforthespaceshipandbackgroundimages,themain.cfile(importedas.wasm)iscompiledtoaWasmmoduleandtheresultingexportsis

assignedtothis.instance.Oncetheseoperationscomplete,thestart()functioncanbecalledfromtheexportedWasmmodule.SincetheinitializeWasm()functioncallsthe<canvas>element'sgetContext()function,thecomponentneedstobemountedbeforethisfunctioncanbecalled.Let'sreviewtherestofthemethodsdefinitionsandthemounted()eventhandler.

ComponentmountingTheremainingmethodsdefinitionsandmounted()eventhandlerfunctionareshownasfollows:

...

//Loopingfunctiontomovethespaceshipacrossthecanvas.

loopRectMotion(){

setTimeout(()=>{

this.instance.moveRect();

if(this.instance.getIsRunning())this.loopRectMotion();

},15-this.speed);

},

//Pauses/resumesthespaceship'smovementwhenthebuttonis

//clicked:

onActionClick(event){

constnewIsRunning=!this.instance.getIsRunning();

this.instance.setIsRunning(newIsRunning);

event.target.innerHTML=newIsRunning?'Pause':'Resume';

if(newIsRunning)this.loopRectMotion();

}

},

mounted(){

this.initializeWasm().then(()=>{

this.instance.start(

this.bounds.width,

this.bounds.height,

this.rect.width,

this.rect.height

);

this.loopRectMotion();

});

},

OncetheWasmmoduleiscompiled,thestart()functionisaccessibleonthis.instance.Theboundsandrectdimensionsarepassedintothestart()function,andthentheloopRectFunction()iscalledtostartmovingthespaceship.TheonActionClick()eventhandlerfunctionpausesorresumesthemovementofthespaceshipbasedonwhetherornotit'scurrentlyinmotion.

TheloopRectMotion()functionsinthesamewayastheexamplecodefromChapter5,CreatingandLoadingaWebAssemblyModule,exceptthespeedisnowadjustable.The15-this.speedcalculation,whichdictatesthetimeoutlength,maylookalittlestrange.Sincethemovementspeedoftheimageisbasedontheamountoftimethatelapsesbetweenfunctioncalls,increasingthisnumber

wouldactuallyslowdownthespaceship.Consequently,this.speedissubtractedfrom15,whichwaschosenbecauseit'sslightlygreaterthan10butwon'tturnthespaceshipintoablurifthis.speedisincreasedtothemaximum.That'sitforthecomponentlogic;let'smoveontotherenderingsectionofthecodewherethetemplateisdefined.

ComponentrenderingThecontentsofthetemplateproperty,whichdictateswhattorender,areshownasfollows:

template:`

<divclass="flexcolumn">

<h1>SPACEWASM!</h1>

<canvas

ref="canvas"

:height="bounds.height"

:width="bounds.width">

</canvas>

<divclass="flexcontrols">

<div>

<buttonclass="defaultText"@click="onActionClick">

Pause

</button>

</div>

<divclass="flexcolumn">

<labelclass="defaultText"for="speed">Speed:{{speed}}</label>

<input

v-model="speed"

id="speed"

type="range"

min="1"

max="10"

step="1">

</div>

</div>

</div>

Sincewe'reusingVue,wecanbindtheattributesandeventhandlersofHTMLelementstopropertiesandmethodsdefinedinourcomponent.InadditiontoaPAUSE/RESUMEbutton,there'sarange<input>thatallowsyoutochangethespeed.Byslidingittotheleftorright,you'reabletoslowdownorspeedupthespaceshipandseethechangesreflectedimmediately.Thatconcludesourreview;let'sseehowWebpackcanbeusedtobuildorruntheapplication.

<strong>npmrunbuild</strong>

Itmaytakeaminutetobuildtheprojectthefirsttimeyourunit.ThiscanbeattributedtotheWasmcompilationstep.However,subsequentbuildsshouldbemuchfaster.Ifthebuildwassuccessful,youshouldseeanewlycreated/distfolderwiththesecontents:├──/assets│├──background.jpg│└──spaceship.svg├──index.html├──main.css├──main.js└──main.wasm

TestingthebuildLet'stryoutthebuildtoensureeverythingisworkingcorrectly.Runthefollowingcommandinyourterminalinstancetostarttheapplication:serve-l8080dist

Ifyounavigatetohttp://127.0.0.1:8080/index.htmlinyourbrowser,youshouldseethis:

Webpackapplicationrunninginthebrowser

Thespaceshipimage(takenfromhttps://commons.wikimedia.org/wiki/File:Alien_Spaceship_-_SVG_Vector.svg)bouncesaroundwithintheboundsoftheHorseheadNebulabackgroundimage(takenfromhttps://commons.wikimedia.org/wiki/File:Horsehead_Nebula_Christmas_2017_Deography.jpg).WhenthePAUSEbuttonispressed,thebutton'scaptionchangestoRESUMEandtheshipstopsmoving.PressingthebuttonagainwillchangethecaptionbacktoPAUSEandtheshipwillstartmovingagain.AdjustingtheSPEEDsliderincreasesordecreasesthespeedoftheship.

RunningthestartscriptTheapplicationhasthewebpack-dev-serverlibraryinstalled,whichoperateslikeBrowsersync.ThelibraryusesLiveReloading,whichautomaticallyupdatestheapplicationwhenyoumakeanychangestothefilesin/src.Sincewe'reusingaWebpackloaderforCandC++files,theautomaticupdateeventwilltriggerifyouchangetheCfileaswell.Runthefollowingthecommandtostarttheapplicationandwatchforchanges:npmstart

Abrowserwindowshouldopenautomaticallywhenthebuildcompletes,andthendirectyoutotherunningapplication.Toseethelive-reloadingfeatureinaction,trysettingthevalueoftheisRunningvariableinthesetIsRunning()functioninmain.ctofalseinsteadofnewIsRunning:

EMSCRIPTEN_KEEPALIVE

voidsetIsRunning(boolnewIsRunning){

//isRunning=newIsRunning;

//Setthevaluetoalwaysfalse:

isRunning=false;

}

Thespaceshipshouldbestuckintheupper-leftcorner.Ifyouchangeitback,thespaceshipstartsmovingagain.Inthenextsection,wewillwriteunittestsinJavaScripttotestWebAssemblymodules.

TestingWebAssemblymoduleswithJestWell-testedcodepreventsregressionbugs,simplifiesrefactoring,andalleviatessomeofthefrustrationsthatgoalongwithaddingnewfeatures.Onceyou'vecompiledaWasmmodule,youshouldwriteteststoensureit'sfunctioningasexpected,evenifyou'vewrittentestsforC,C++,orRustcodeyoucompileditfrom.Inthissection,we'lluseJest,aJavaScripttestingframework,totestthefunctionsinacompiledWasmmodule.

ThecodebeingtestedAllofthecodeusedinthisexampleislocatedinthe/chapter-09-node/testing-examplefolder.Thecodeandcorrespondingtestsareverysimpleandarenotrepresentativeofreal-worldapplications,butthey'reintendedtodemonstratehowtouseJestfortesting.Thefollowingcoderepresentsthefilestructureofthe/testing-examplefolder:├──/src|├──/__tests__|│└──main.test.js|└──main.c├──package.json└──package-lock.json

ThecontentsoftheCfilethatwe'lltest,/src/main.c,isshownasfollows:

intaddTwoNumbers(intleftValue,intrightValue){

returnleftValue+rightValue;

}

floatdivideTwoNumbers(floatleftValue,floatrightValue){

returnleftValue/rightValue;

}

doublefindFactorial(floatvalue){

inti;

doublefactorial=1;

for(i=1;i<=value;i++){

factorial=factorial*i;

}

returnfactorial;

}

Allthreefunctionsinthefileareperformingsimplemathematicaloperations.Thepackage.jsonfileincludesascripttocompiletheCfiletoaWasmfilefortesting.RunthefollowingcommandtocompiletheCfile:npmrunbuild

Thereshouldbeafilenamedmain.wasminthe/srcdirectory.Let'smoveontodescribingthetestingconfigurationstep.

TestingconfigurationTheonlydependencywe'lluseforthisexampleisJest,aJavaScripttestingframeworkbuiltbyFacebook.Jestisanexcellentchoicefortestingbecauseitincludesmostofthefeaturesyou'llneedoutofthebox,suchascoverage,assertions,andmocking.Inmostcases,youcanuseitwithzeroconfiguration,dependingonthecomplexityofyourapplication.Ifyou'reinterestedinlearningmore,checkoutJest'swebsiteathttps://jestjs.io.Openaterminalinstanceinthe/chapter-09-node/testing-examplefolderandrunthefollowingcommandtoinstallJest:

npminstall

Inthepackage.jsonfile,therearethreeentriesinthescriptssection:build,pretest,andtest.Thebuildscriptexecutestheemcccommandwiththerequiredflagstocompile/src/main.cto/src/main.wasm.Thetestscriptexecutesthejestcommandwiththe--verboseflag,whichprovidesadditionaldetailsforeachofthetestsuites.Thepretestscriptsimplyrunsthebuildscripttoensure/src/main.wasmexistspriortorunninganytests.

TestsfilereviewLet'swalkthroughthetestfile,locatedat/src/__tests__/main.test.js,andreviewthepurposeofeachsectionofcode.Thefirstsectionofthetestfileinstantiatesthemain.wasmfileandassignstheresulttothelocalwasmInstancevariable:

constfs=require('fs');

constpath=require('path');

describe('main.wasmTests',()=>{

letwasmInstance;

beforeAll(async()=>{

constwasmPath=path.resolve(__dirname,'..','main.wasm');

constbuffer=fs.readFileSync(wasmPath);

constresults=awaitWebAssembly.instantiate(buffer,{

env:{

memoryBase:0,

tableBase:0,

memory:newWebAssembly.Memory({initial:1024}),

table:newWebAssembly.Table({initial:16,element:'anyfunc'}),

abort:console.log

}

});

wasmInstance=results.instance.exports;

});

...

Jestprovideslife-cyclemethodstoperformanysetuporteardownactionspriortorunningtests.Youcanspecifyfunctionstorunbeforeorafterallofthetests(beforeAll()/afterAll()),orbeforeoraftereachtest(beforeEach()/afterEach()).WeneedacompiledinstanceoftheWasmmodulefromwhichwecancallexportedfunctions,soweputtheinstantiationcodeinthebeforeAll()function.

We'rewrappingtheentiretestsuiteinadescribe()blockforthefile.Jestusesadescribe()functiontoencapsulatesuitesofrelatedtestsandtest()orit()torepresentasingletest.Here'sasimpleexampleofthisconcept:

constadd=(a,b)=>a+b;

describe('theaddfunction',()=>{

test('returns6when4and2arepassedin',()=>{

constresult=add(4,2);

expect(result).toEqual(6);

});

test('returns20when12and8arepassedin',()=>{

constresult=add(12,8);

expect(result).toEqual(20);

});

});

Thenextsectionofcodecontainsallthetestsuitesandtestsforeachexportedfunction:

...

describe('the_addTwoNumbersfunction',()=>{

test('returns300when100and200arepassedin',()=>{

constresult=wasmInstance._addTwoNumbers(100,200);

expect(result).toEqual(300);

});

test('returns-20when-10and-10arepassedin',()=>{

constresult=wasmInstance._addTwoNumbers(-10,-10);

expect(result).toEqual(-20);

});

});

describe('the_divideTwoNumbersfunction',()=>{

test.each([

[10,100,10],

[-2,-10,5],

])('returns%fwhen%fand%farepassedin',(expected,a,b)=>{

constresult=wasmInstance._divideTwoNumbers(a,b);

expect(result).toEqual(expected);

});

test('returns~3.77when20.75and5.5arepassedin',()=>{

constresult=wasmInstance._divideTwoNumbers(20.75,5.5);

expect(result).toBeCloseTo(3.77,2);

});

});

describe('the_findFactorialfunction',()=>{

test.each([

[120,5],

[362880,9.2],

])('returns%pwhen%pispassedin',(expected,input)=>{

constresult=wasmInstance._findFactorial(input);

expect(result).toEqual(expected);

});

});

});

Thefirstdescribe()block,forthe_addTwoNumbers()function,hastwotest()instancestoensurethatthefunctionreturnsthesumofthetwonumberspassedinasarguments.Thenexttwodescribe()blocks,forthe_divideTwoNumbers()and_findFactorial()functions,useJest's.eachfeature,whichallowsyoutorunthesametestwithdifferentdata.Theexpect()functionallowsyoutomakeassertionsonthevaluepassedinasanargument.The.toBeCloseTo()assertioninthelast_divideTwoNumbers()testcheckswhethertheresultiswithintwodecimalplacesof3.77.Therestusethe.toEqual()assertiontocheckforequality.

WritingtestswithJestisrelativelysimple,andrunningthemiseveneasier!Let'stryrunningourtestsandreviewingsomeoftheCLIflagsthatJestprovides.

RunningthetestsTorunthetests,openaterminalinstanceinthe/chapter-09-node/testing-examplefolderandrunthefollowingcommand:

npmtest

Youshouldseethefollowingoutputinyourterminal:

main.wasmTests

the_addTwoNumbersfunction

✓returns300when100and200arepassedin(4ms)✓returns-20when-10and-10arepassedinthe_divideTwoNumbersfunction

✓returns10when100and10arepassedin✓returns-2when-10and5arepassedin(1ms)✓returns~3.77when20.75and5.5arepassedinthe_findFactorialfunction

✓returns120when5ispassedin(1ms)✓returns362880when9.2ispassedin

TestSuites:1passed,1total

Tests:7passed,7total

Snapshots:0total

Time:1.008s

Ranalltestsuites.

Ifyouhavealargenumberoftests,youcouldremovethe--verboseflagfromthetestscriptinpackage.jsonandonlypasstheflagtothenpmtestcommandifneeded.ThereareseveralotherCLIflagsyoucanpasstothejestcommand.Thefollowinglistcontainssomeofthemorecommonlyusedflags:

--bail:Exitsthetestsuiteimmediatelyuponthefirstfailingtestsuite--coverage:Collectstestcoverageanddisplaysitintheterminalafterthetestshaverun--watch:Watchesfilesforchangesandrerunstestsrelatedtochangedfiles

Youcanpasstheseflagstothenpmtestcommandbyaddingthemaftera--.Forexample,ifyouwantedtousethe--bailflag,you'drunthiscommand:

npmtest----bail

YoucanviewtheentirelistofCLIoptionsontheofficialsiteathttps://jestjs.io/docs/en/cli.

SummaryInthischapter,wediscussedtheadvantagesofintegratingWebAssemblywithNode.jsanddemonstratedhowNode.jscouldbeusedontheserverandclientside.WeevaluatedanExpressapplicationthatusesaWasmmoduletoperformcalculationsonaccountingtransactions.Wethenreviewedabrowser-basedapplicationthatutilizesWebpacktoimportandcallfunctionsfromaCfilewithouthavingtowriteanyWasminstantiationcode.Finally,wesawhowtheJesttestingframeworkcanbeleveragedtotestacompiledmoduleandensureit'sfunctioningcorrectly.InChapter10,AdvancedToolsandUpcomingFeatures,we'llcoveradvancedtoolsanddiscussthefeaturesthatareonthehorizonforWebAssembly.

Questions1. WhatisoneoftheadvantagesofintegratingWebAssemblywithNode.js?2. WhatlibrarydoestheExpressapplicationusetoreadandwritedatatoa

JSONfile?3. Whatisthedifferencebetweenloadingamoduleinthebrowserandin

Node.js?4. Whattechniquecanyouusetorunannpmscriptbeforeorafteranexisting

npmscript?5. WhatisthenameofthetaskWebpackperformstoeliminatedeadcode?6. WhatisthepurposeofaloaderinWebpack?7. Whatisthedifferencebetweenthedescribe()andtest()functionsinJest?8. HowdoyoupassadditionalCLIflagstothenpmtestcommand?

FurtherreadingExpress:https://expressjs.comWebpack:https://webpack.js.orgJestAPI:https://jestjs.io/docs/en/api

AdvancedToolsandUpcomingFeaturesWebAssembly'secosystemisconstantlygrowingandevolving.DevelopershaveseenthepotentialforWebAssembly.TheybuildtoolstoimprovethedevelopmentexperienceoroutputWasmmodulesfromtheirlanguageofchoice(albeitwithsomelimitations).

Inthischapter,we'llevaluatetheunderlyingtechnologiesthatmakeWebAssemblytick.We'llalsoreviewtoolsyoucanuseinthebrowserandcoveranadvancedusecasethatutilizesWebWorkers.Finally,we'llquicklyreviewupcomingfeaturesandproposalsthatareontheroadmapforWebAssembly.

Ourgoalforthischapteristounderstandthefollowing:

HowWABTandBinaryenfitintothebuildprocessandwhattheycanbeusedforHowtocompileaWebAssemblymoduleusingLLVM(ratherthanEmscripten)OnlinetoolssuchasWasmFiddleandotherusefultoolingonlineHowtoutilizeWebWorkerstorunWebAssemblyinparallelUpcomingfeatures(proposedandinprogress)thatwillbeintegratedintoWebAssemblyinthefuture

WABTandBinaryenWABTandBinaryenallowdeveloperstoworkwithsourcefilesanddeveloptoolingforWebAssembly.Ifyou'reinterestedinworkingwithWebAssemblyatalowerlevel,thesetoolsprovidethemeansforaccomplishingsuchagoal.Inthissection,we'llevaluatethesetoolsingreaterdetailandreviewthepurposeandcapabilitiesofeachone.

WABT–theWebAssemblybinarytoolkitWABT'sfocusisonthemanipulationofWebAssemblybinary(.wasm)filesandtext(.wat)files,aswellasconversionbetweenthetwoformats.WABTprovidestoolstotranslateWattoWasm(wat2wasm)andviceversa(wasm2wat),aswellasatooltoconvertaWasmfiletoaCsourceandheaderfile(wasm2c).YoucanviewtheentirelistoftoolsintheREADMEfileoftheWABTGitHubrepositoryathttps://github.com/WebAssembly/wabt.

OneexampleusecaseofWABTistheWebAssemblyToolkitforVSCodeextensionweinstalledinChapter3,SettingUpaDevelopmentEnvironment.TheextensiondependsonWABTtoviewthetextformatassociatedwitha.wasmfile.Therepositoryprovideslinkstowat2wasmandwasm2watdemos,whichyoucanusetotestthevalidityofaWatprogramorinteractwithacompiledbinaryusingJavaScript.ThefollowingscreenshotcontainstheWatandJavaScriptinstantiationcodefromthewat2wasmdemo:

WatandJavaScriptloadingcodefromwat2wasm's"simple"example

Inline3oftheJSpanel,youmayhavenoticedthattheaddTwo()functionfromwasmInstance.exportsisn'tprefixedwitha_.Emscriptenaddsthe_automaticallyinthecompilationprocess.Youcouldomitthe_byconvertingthe.wasmfiletoa.wat,updatingthefunctionnames,andconvertingitbackto.wasmusingtheWABT,althoughthiswouldn'tbeverypractical.TheWABTsimplifiestheprocessoftransformingtextformattobinaryformatandviceversa.IfyouwanttobuildcompilationtoolingforWebAssembly,you'duseBinaryen,whichwewillcovernext.

BinaryenBinaryen'sGitHubpageathttps://github.com/WebAssembly/binaryendescribesBinaryenasacompilerandtoolchaininfrastructurelibraryforWebAssembly,writteninC++.ItaimstomakecompilingtoWebAssemblyeasy,fast,andeffective.ItachievestheseaimsbyprovidingasimpleCAPI,aninternalIR,andanoptimizer.JustaswiththeWABT,BinaryenprovidesanextensivesuiteoftoolsfordevelopingWebAssemblytooling.ThefollowinglistdescribesasubsetofthetoolsthatBinaryenprovides:

wasm-shell:ToolcapableofloadingandinterpretingWebAssemblyasm2wasm:Compilesasm.jscodetoaWasmmodulewasm2js:CompilesaWasmmoduletoJavaScriptwasm-merge:CombinesmultipleWasmfilesintoonewasm.js:JavaScriptlibrarythatincludestheBinaryeninterpreter,asm2wasm,theWatparser,andotherBinaryentoolsbinaryen.js:JavaScriptlibrarythatprovidesaJavaScriptinterfacefortheBinaryentoolchain

Thewasm.jsandbinaryen.jstoolsareofparticularinterestforJavaScriptdevelopersinterestedinbuildingWebAssemblytooling.Thebinaryen.jslibraryisavailableasannpmpackage(https://www.npmjs.com/package/binaryen).

Anexcellentexampleofbinaryen.jsusageisAssemblyScript(https://github.com/AssemblyScript/assemblyscript).AssemblyScriptisastrictlytypedsubsetofTypeScriptthatgeneratesWebAssemblymodules.ThelibrarycomespackagedwithaCLItoquicklyscaffoldnewprojectsandmanagethebuildstep.IntheCompilingwithLLVMsection,we'llcoverhowtocompileWasmmodulesusingLLVM.

CompilingwithLLVMInChapter1,WhatisWebAssembly?,wediscussedtherelationshipbetweenEmscripten'sEMSDKandLLVM.EmscriptenusesLLVMandClangtocompileC/C++downtoLLVMbitcode.TheEmscriptencompiler(emcc)compilesthatbitcodetoasm.js,whichispassedtoBinaryentogenerateaWasmfile.Ifyou'reinterestedinusingLLVM,youcancompileC/C++toWasmwithoutinstallingtheEMSDK.Inthissection,wewillreviewtheprocessforenablingWasmcompilationusingLLVM.AftercompilingsomeexampleC++codetoaWasmfile,we'lltryitoutinthebrowser.

TheinstallationprocessIfyouwanttocompileWebAssemblymodulesusingLLVM,severaltoolsneedtobeinstalledandconfigured.Gettingthesetoolsworkingtogethercorrectlycanbeanarduousandtime-consumingprocess.Fortunately,someonewentthroughthetroubleofmakingthisprocessmuchsimpler.DanielWirtzcreatedannpmpackagenamedwebassembly(https://www.npmjs.com/package/webassembly)thatcanperformthefollowingoperations(withthecorrespondingCLIcommands):

CompileC/C++codetoaWebAssemblymodule(wacompile)LinkmultipleWebAssemblymodulestoone(walink)DecompileaWebAssemblymoduletotextformat(wadisassemble)AssembleWebAssemblytextformattoamodule(waassemble)

ThelibraryisusingBinaryen,Clang,LLVM,andadditionalLLVMtoolsbehindthescenes.We'llinstallthispackagegloballytoensurewehaveaccesstothewacommand.Toinstall,openaterminalinstanceandrunthefollowingcommand:

npminstall-gwebassembly

Itmaytakeafewminutestoinstallanyrequireddependencies.Oncecomplete,runthefollowingcommandtovalidatetheinstallation:

wa

Youshouldseethefollowinginterminal:

Outputofthewacommand

YoushouldbereadytostartcompilingWasmmodules.Let'smoveontotheexamplecode.

TheexamplecodeTotestoutthecompiler,we'regoingtouseaslightlymodifiedversionofthewithout-glue.cfilefromtheInteractingwithJavaScriptwithoutgluecodesectionofChapter5,CreatingandLoadingaWebAssemblyModule.Thecodeforthissectionislocatedinthe/chapter-10-advanced-tools/compile-with-llvmdirectoryofthelearn-webassemblyrepository.Followthefollowinginstructionstocreatethefilesnecessaryforthecompilertest.Let'sstartwiththeC++file.

TheC++fileCreateanewdirectoryinyour/book-examplesdirectorynamed/compile-with-llvm.Createanewfileinthe/compile-with-llvmdirectorynamedmain.cppandpopulateitwiththefollowingcontents:

#include<stdbool.h>

#defineBOUNDS255

#defineRECT_SIDE50

#defineBOUNCE_POINT(BOUNDS-RECT_SIDE)

boolisRunning=true;

typedefstructRect{

intx;

inty;

chardirection;

}Rect;

structRectrect;

voidupdateRectLocation(){

if(rect.x==BOUNCE_POINT)rect.direction='L';

if(rect.x==0)rect.direction='R';

intincrementer=1;

if(rect.direction=='L')incrementer=-1;

rect.x=rect.x+incrementer;

rect.y=rect.y+incrementer;

}

extern"C"{

externintjsClearRect();

externintjsFillRect(intx,inty,intwidth,intheight);

__attribute__((visibility("default")))

voidmoveRect(){

jsClearRect();

updateRectLocation();

jsFillRect(rect.x,rect.y,RECT_SIDE,RECT_SIDE);

}

__attribute__((visibility("default")))

boolgetIsRunning(){

returnisRunning;

}

__attribute__((visibility("default")))

voidsetIsRunning(boolnewIsRunning){

isRunning=newIsRunning;

}

__attribute__((visibility("default")))

voidinit(){

rect.x=0;

rect.y=0;

rect.direction='R';

setIsRunning(true);

}

}

Thecodeinthisfileisalmostidenticaltothecontentsofwithout-glue.cfromChapter5,CreatingandLoadingaWebAssemblyModule.Thecommentshavebeenremovedfromthefileandtheimported/exportedfunctionsarewrappedinanextern"C"block.The__attribute__((visibility("default")))linesaremacrostatements(similartoEMSCRIPTEN_KEEPALIVE)thatensurethefunctionsaren'tremovedfromthecompiledoutputduringthedead-codeeliminationstep.Justaswithpriorexamples,we'llinteractwiththecompiledWasmmodulethroughanHTMLfile.Let'screatethatnext.

TheHTMLfileCreateafilenamedindex.htmlinthe/compile-with-llvmdirectoryandpopulateitwiththefollowingcontents:

<!doctypehtml>

<htmllang="en-us">

<head>

<title>LLVMTest</title>

</head>

<body>

<h1>LLVMTest</h1>

<canvasid="myCanvas"width="255"height="255"></canvas>

<divstyle="margin-top:16px;">

<buttonid="actionButton"style="width:100px;height:24px;">

Pause

</button>

</div>

<scripttype="application/javascript">

constcanvas=document.querySelector('#myCanvas');

constctx=canvas.getContext('2d');

constimportObj={

env:{

memoryBase:0,

tableBase:0,

memory:newWebAssembly.Memory({initial:256}),

table:newWebAssembly.Table({initial:8,element:'anyfunc'}),

abort:console.log,

jsFillRect:function(x,y,w,h){

ctx.fillStyle='#0000ff';

ctx.fillRect(x,y,w,h);

},

jsClearRect:function(){

ctx.fillStyle='#ff0000';

ctx.fillRect(0,0,255,255);

}

}

};

WebAssembly.instantiateStreaming(fetch('main.wasm'),importObj)

.then(({instance})=>{

constm=instance.exports;

m.init();

constloopRectMotion=()=>{

setTimeout(()=>{

m.moveRect();

if(m.getIsRunning())loopRectMotion();

},20)

};

document.querySelector('#actionButton')

.addEventListener('click',event=>{

constnewIsRunning=!m.getIsRunning();

m.setIsRunning(newIsRunning);

event.target.innerHTML=newIsRunning?'Pause':'Start';

if(newIsRunning)loopRectMotion();

});

loopRectMotion();

});

</script>

</body>

</html>

Thecontentsofthisfileareverysimilartothewithout-glue.htmlfilefromChapter5,CreatingandLoadingaWebAssemblyModule.InsteadofusingtheloadWasm()functionfromthe/common/load-wasm.jsfile,we'reusingtheWebAssembly.instantiateStreaming()function.Thisallowsustoomitanadditional<script>elementandservethefilesdirectlyfromthe/compile-with-llvmdirectory.

The_isomittedfromthejsFillRectandjsClearRectfunctionspassedintotheimportObj.Wecanomitthe_forthefunctionspresentontheinstance.exportsobjectaswell.LLVMdoesn'tprefixanyofthedata/functionspassedinoroutofthemodulewitha_.Inthenextsection,we'llcompilemain.cppandinteractwiththeresultantWasmfileinthebrowser.

CompilingandrunningtheexampleWeinstalledthewebassemblynpmpackagewiththe-gflag,sothewacommandshouldbeavailableintheterminal.Openaterminalinstanceinthe/compile-with-llvmdirectoryandrunthefollowingcommand:

wacompilemain.cpp-omain.wasm

Youshouldseeafilenamedmain.wasmappearinthecompile-with-llvmfolderofVSCode'sfileexplorer.ToensuretheWasmmodulecompiledcorrectly,runthefollowingcommandwithinthe/compile-with-llvmdirectory:

serve-l8080

Ifyounavigatetohttp://127.0.0.1:8080/index.htmlinyourbrowser,youshouldseethefollowing:

LLVMcompiledmodulerunninginthebrowser

OnlinetoolingTheinstallationandconfigurationprocessforcompilingWebAssemblymoduleslocallyis,admittedly,alittlecumbersome.Fortunately,thereareseveralonlinetoolsavailablethatallowyoutodevelopandinteractwithWebAssemblyinthebrowser.Inthissection,we'llreviewthosetoolsanddiscussthefunctionalityeachoneprovides.

WasmFiddleIntheConnectingthedotswithWasmFiddlesectioninChapter2,ElementsofWebAssembly-Wat,Wasm,andtheJavaScriptAPI,weusedWasmFiddletocompileasimpleCfunctiontoWasmandinteractwithitusingJavaScript.WasmFiddleprovidesaC/C++editor,JavaScripteditor,Wat/x86viewer,andJavaScriptoutputpanel.Youcanalsointeractwiththe<canvas>ifdesired.WasmFiddleusesLLVMtogeneratetheWasmmodules,whichiswhytheimportsandexportsaren'tprefixedwitha_.YoucaninteractwithWasmFiddleathttps://wasdk.github.io/WasmFiddle.

WebAssemblyExplorerWebAssemblyExplorer,locatedathttps://mbebenita.github.io/WasmExplorer,providessimilarfunctionalitytoWasmFiddle.ItallowsyoutocompileCorC++toaWasmmoduleandviewthecorrespondingWat.However,WebAssemblyExplorerprovidesadditionalfunctionalitynotpresentinWasmFiddle.Forexample,youcancompileCorC++toWasmandviewthecorrespondingFirefoxx86andLLVMx86code.Youcanselectfromalistofcodeexamplesandspecifytheoptimizationlevel(-Oflaginemcc).ItalsoprovidesabuttonthatallowsyoutoimportthecodeintoWasmFiddle:

ScreenshotofWebAssemblyExplorer

WebAssemblyStudioWebAssemblyStudio,locatedathttps://webassembly.studio,isafeature-richeditoranddevelopmentenvironment.YoucancreateC,Rust,andAssemblyScriptprojects.ItprovidesthecapabilitiestobuildandruncodewithinthebrowserandintegrateswellwithGitHub.WebAssemblyStudioenablesyoutobuildawebapplicationwithouthavingtoinstallandconfiguretherequiredWebAssemblytoolinglocally:

ScreenshotofWebAssemblyStudio

Inthenextsection,we'lldemonstratehowtoaddparallelismtoyourWebAssemblyapplicationwithWebWorkers.

ParallelWasmwithWebWorkersTheprocessofbuildingacomplexapplicationthatperformsheavycomputationorotherresource-intensiveworkcanbenefitgreatlyfromusingthreads.Threadsallowyoutoperformoperationsinparallelbydividingfunctionalityamongtasksthatrunindependently.Atofwritingthis,supportforthreadsinWebAssemblyisintheFeatureProposalphase.Inthisphase,thespecificationhasn'tbeenwrittenandthefeatureisn'timplemented.Fortunately,JavaScriptprovidesthreadingcapabilitiesintheformofWebWorkers.Inthissection,we'lldemonstratehowtouseJavaScript'sWebWorkersAPItointeractwithWasmmodulesinseparatethreads.

WebWorkersandWebAssemblyWebWorkersallowyoutoutilizethreadsinthebrowser,whichcanimprovetheperformanceofyourapplicationbyoffloadingsomeofthelogicfromthemain(UI)thread.WorkerthreadsarealsocapableofperformingI/OusingXMLHttpRequest.Workerthreadscommunicatewiththemainthreadbypostingmessagestoaneventhandler.

WebWorkersallowustoloadWasmmodulesintoseparatethreadsandperformoperationsthatdon'thindertheperformanceoftheUI.WebWorkersdohavesomelimitations.They'reunabletodirectlymanipulatetheDOMoraccesssomeofthemethodsandpropertiesonthewindowobject.Themessagespassedbetweenthreadsmustbeserializedobjects,whichmeansyoucan'tpassfunctions.Nowthatyouknowwhataworkeris,let'sdiscusshowtocreateone.

CreatingaworkerBeforeyoucancreateaworker,youneedaJavaScriptfilewithcodethatrunsintheworkerthread.Youcanseeasimpleexampleofaworkerdefinitionfileathttps://github.com/mdn/simple-web-worker/blob/gh-pages/worker.js.Thefileshouldcontainamessageeventlistenerthatperformsoperationswhenmessagesarereceivedfromotherthreadsandrespondsaccordingly.

Oncethatfileiscreated,you'rereadytouseitwithaworker.AworkeriscreatedbypassingaURLargumenttotheWorker()constructor.TheURLcanbeastringrepresentingthenameofthefilewithyourworkerdefinitioncode,orconstructedusingaBlob.TheBlobtechniquecanbeusefulifyou'refetchingtheworkerdefinitioncodefromaserver.Theexampleapplicationdemonstrateshowtousebothapproaches.Let'smoveontotheprocessofintegratingWebAssemblywithWebWorkers.

TheWebAssemblyworkflowInordertoutilizeWasmmodulesinseparatethreads,theWasmfilemustbecompiledinthemainthreadandinstantiatedinaWebWorker.Let'sreviewthisprocessinmoredetail:

1. AnewWebWorker(we'llrefertoitaswasmWorker)iscreatedusingtheWorker()constructor.

2. Afetchcallismadetoretrievea.wasmfileandthearrayBuffer()functioniscalledontheresponse.

3. TheresolvedvalueofthearrayBuffer()functionispassedtotheWebAssembly.compile()function.

4. TheWebAssembly.compile()functionresolveswithaWebAssembly.Moduleinstance,whichisincludedinthebodyofamessagepostedtothewasmWorkerusingthepostMessage()function.

5. WithinwasmWorker,theWebAssembly.ModuleinstancefromthemessagebodyispassedtotheWebAssembly.instantiate()function,whichresolveswithaWebAssembly.Instance.

6. TheWebAssembly.InstanceexportsobjectisassignedtoalocalvariableinwasmWorkerandisusedtocallWasmfunctions.

TocallafunctionfromthewasmWorkerWasminstance,youpostamessagetotheworkerthreadwithanyargumentstopasstotheWasmfunction.Then,wasmWorkerexecutesthefunctionandpassestheresultsbacktothemainthread.That'sthecruxofhowthreadsareutilizedinthecontextofWebWorkers.Beforewemoveontotheexampleapplication,youmayneedtoaddressalimitationthatGoogleChromeimposes.FollowtheinstructionsintheLimitationsinGoogleChromesectiontoensuretheexampleapplicationworkssuccessfully.

LimitationsinGoogleChromeGoogleChromeplacesarestrictiononwhatcanbeincludedinthebodyofaWebWorker'spostMessage()function.IfyoutriedtosendacompiledWebAssembly.Moduletoaworker,you'dgetanerrorandtheoperationwouldbeunsuccessful.Youcanoverridethisbysettingaflag.Toenablethisfunctionality,openGoogleChromeandenterchrome://flagsintheaddressbar.Typecloninginthesearchboxatthetopofthepage.YoushouldseealistitemtitledWebAssemblystructuredcloningsupport.SelecttheEnabledoptionfromthedropdownnexttothelistitemandpresstheRELAUNCHNOWbuttonwhenprompted:

UpdatingtheWebAssemblyflaginGoogleChrome

AfterChromerestarts,youcanruntheexampleapplicationwithoutissue.Ifyou'reusingMozillaFirefox,noactionisrequired.Itsupportsthisfeaturebydefault.Let'smoveontotheexampleapplicationthatdemonstratestheuseofWebAssemblyinthreads.

OverviewofthecodeTheexampleapplicationisn'tmuchofanapplication.It'sasimpleformthatacceptstwoinputvaluesandreturnsthesumordifferenceofthesetwovalues.TheaddandsubtractoperationsareeachexportedfromtheirownWasmmoduleinstantiatedinaworkerthread.Theexamplemaybecontrived,butiteffectivelydemonstrateshowtointegrateWebAssemblyintoWebWorkers.

Thecodeforthissectionislocatedinthe/chapter-10-advanced-tools/parallel-wasmdirectoryofthelearn-webassemblyrepository.Thefollowingsectionswalkthrougheachsectionofthecodebaseanddescribehowtobuildtheapplicationfromscratch.Ifyouwishtofollowalong,createafolderinyour/book-examplesdirectorynamed/parallel-wasm.

TheCcodeTheexampleusestwoworkerthreads:oneforadditionandanotherforsubtraction.Consequently,we'llneedtwoseparateWasmmodules.Createafoldernamed/libinyour/parallel-wasmdirectory.Withinthe/libdirectory,createafilenamedadd.candpopulateitwiththefollowingcontents:

intcalculate(intfirstVal,intsecondVal){

returnfirstVal+secondVal;

}

Createanotherfilein/libnamedsubtract.candpopulateitwiththefollowingcontents:

intcalculate(intfirstVal,intsecondVal){

returnfirstVal-secondVal;

}

Notethatthefunctionnameinbothfilesiscalculate.Thiswasdonesowedon'thavetowriteanyconditionallogicwithintheworkercodetodeterminetheWasmfunctiontocall.Thealgebraicoperationistiedtoaworker,sowhenweneedtoaddtwonumbers,the_calculate()functionwillbecalledintheaddWorker.ThiswillbecomeclearerwhenwereviewtheJavaScriptportionofthecode,whichwe'llcovernext.

TheJavaScriptcodeBeforewedigintotheJavaScriptcode,createafoldernamed/srcinyour/parallel-wasmdirectory.Let'sstartwiththefilecontainingthecodethatrunsintheworkerthread.

Definingthreadexecutioninworker.jsCreateanewfileinthe/srcdirectorynamedworker.jsandpopulateitwiththefollowingcontents:

varwasmInstance=null;

self.addEventListener('message',event=>{

/**

*OncetheWebAssemblycompilationiscomplete,thispostsamessage

*backwithwhetherornottheinstantiationwassuccessful.Ifthe

*payloadisnull,thecompilationsucceeded.

*/

constsendCompilationMessage=(error=null)=>{

self.postMessage({

type:'COMPILE_WASM_RESPONSE',

payload:error

});

};

const{type,payload}=event.data;

switch(type){

//InstantiatesthecompiledWasmmoduleandpostsamessagebackto

//themainthreadindicatingiftheinstantiationwassuccessful:

case'COMPILE_WASM_REQUEST':

constimportObj={

env:{

memoryBase:0,

tableBase:0,

memory:newWebAssembly.Memory({initial:256}),

table:newWebAssembly.Table({initial:2,element:'anyfunc'}),

abort:console.log

}

};

WebAssembly.instantiate(payload,importObj)

.then(instance=>{

wasmInstance=instance.exports;

sendCompilationMessage();

})

.catch(error=>{

sendCompilationMessage(error);

});

break;

//Callsthe`calculate`methodassociatedwiththeinstance(addor

//subtract,andpoststheresultbacktothemainthread:

case'CALC_REQUEST':

const{firstVal,secondVal}=payload;

constresult=wasmInstance._calculate(firstVal,secondVal);

self.postMessage({

type:'CALC_RESPONSE',

payload:result

});

break;

default:

break;

}

},false);

Thecodeisencapsulatedwithintheeventlistenerforthemessageevent(self.addEventListener(...)),whichisraisedwhenthepostMessage()functioniscalledonthecorrespondingworker.Theeventparameterintheeventlistener'scallbackfunctioncontainsadatapropertywiththecontentsofthemessage.AllofthemessagespassedbetweenthreadsintheapplicationfollowtheFluxStandardAction(FSA)convention.Objectsthatadheretothisconventionhaveatypeandpayloadproperty,wheretypeisastringandpayloadcanbeofanytype.YoucanreadmoreabouttheFSAathttps://github.com/redux-utilities/flux-standard-action.

YoucanuseanyformatorstructureforthedatayoupassusingthepostMessage()function,aslongasthedataisserializable.

Theswitchstatementexecutesanactionbasedonthemessage'stypevalue,whichisastring.Ifthetypeis'COMPILE_WASM_REQUEST',theWebAssembly.instantiate()functioniscalledwiththepayloadfromthemessageandimportObj.TheexportsobjectoftheresultisassignedtothelocalwasmInstancevariableforlateruse.Ifthetypeis'CALC_REQUEST',thewasmInstance._calculate()functioniscalledwiththefirstValandsecondValvaluesfromthepayloadobject.Thecalculationcodeshouldshedsomelightonwhythefunctionwasnamed_calculate()insteadof_add()or_subtract().Byusingageneralname,theworkerdoesn'tcarewhatoperationit'sperforming,itjustcallsthefunctiontogettheresult.

Inbothcases,theworkerpostsamessagebacktothemainthreadusingthepostMessage()function.IusedaREQUEST/RESPONSEconventionforthetypepropertyvalue.Thisallowsyoutoquicklyidentifywhichthreadthemessagesareoriginatingfrom.Messagessentfromthemainthreadendwith_REQUESTinthetypewhileresponsescomingfromtheworkerthreadsendwith_RESPONSE.Let'smoveontotheWebAssemblyinteractioncode.

InteractingwithWasminWasmWorker.jsCreateanewfileinthe/srcdirectorynamedWasmWorker.jsandpopulateitwiththefollowingcontents:/***WebWorkerassociatedwithaninstantiatedWasmmodule.*@class*/exportdefaultclassWasmWorker{constructor(workerUrl){this.worker=newWorker(workerUrl);this.listenersByType={};this.addListeners();}

//Addalistenerassociatedwiththe`type`valuefromthe//Workermessage:addListenerForType(type,listener){this.listenersByType[type]=listener;}

//Addeventlistenersforerrorandmessagehandling.addListeners(){this.worker.addEventListener('error',event=>{console.error(`%cError:${event.message}`,'color:red;');},false);

//Ifahandlerwasspecifiedusingthe`addListener`method,//firethatmethodifthe`type`matches:this.worker.addEventListener('message',event=>{if(event.datainstanceofObject&&event.data.hasOwnProperty('type')&&event.data.hasOwnProperty('payload')

){const{type,payload}=event.data;if(this.listenersByType[type]){this.listenersByType[type](payload);}}else{console.log(event.data);}},false);}

//FetchestheWasmfile,compilesit,andpassesthecompiledresult//tothecorrespondingworker.Thecompiledmoduleisinstantiated//intheworker.initialize(name){returnfetch(`calc-${name}.wasm`).then(response=>response.arrayBuffer()).then(bytes=>WebAssembly.compile(bytes)).then(wasmModule=>{this.worker.postMessage({type:'COMPILE_WASM_REQUEST',payload:wasmModule});returnPromise.resolve();});}

//Postsamessagetotheworkerthreadtocallthe`calculate`//methodfromtheWasminstance:calculate(firstVal,secondVal){this.worker.postMessage({type:'CALC_REQUEST',payload:{firstVal,secondVal}});

}}

TheWasmWorkerclassmanagesaworkerthreadassociatedwithaWasmfile.IntheWasmWorkerconstructor,anewWorkeriscreatedanddefaulteventlistenersareaddedfortheerrorandmessageevents.Theinitialize()functionfetchesthe.wasmfileassociatedwiththenameargument,compilesit,andsendstheresultantWebAssembly.Moduleinstancetotheworkerthreadtobeinstantiated.

TheaddListenerForType()functionisusedtospecifyacallbackfunction(listener)toexecutewhenthetypefieldinthemessageresponsematchesthetypeargumentpassedtothefunction.Thisisrequiredtocapturetheresultofthe_calculate()functionfromtheworkerthread.

Finally,thecalculate()functioninWasmWorkerpostsamessagetotheworkerthreadwiththefirstValandsecondValargumentspassedinfromthe<input>elementsonthe<form>.Let'smoveontotheapplicationloadingcodetoseehowWasmWorkerinteractswiththeUI.

Loadingtheapplicationinindex.jsCreateanewfileinthe/srcdirectorynamedindex.jsandpopulateitwiththefollowingcontents:

importWasmWorkerfrom'./WasmWorker.js';

/**

*Ifyouadd?blob=truetotheendoftheURL(e.g.

*http://localhost:8080/index.html?blob=true),theworkerwillbe

*createdfromaBlobratherthanaURL.Thisreturnsthe

*URLtousefortheWorkereitherasastringorcreatedfromaBlob.

*/

constgetWorkerUrl=async()=>{

consturl=newURL(window.location);

constisBlob=url.searchParams.get('blob');

varworkerUrl='worker.js';

document.title='WasmWorker(StringURL)';

//CreateaBlobinstancefromthetextcontentsof`worker.js`:

if(isBlob==='true'){

constresponse=awaitfetch('worker.js');

constresults=awaitresponse.text();

constworkerBlob=newBlob([results]);

workerUrl=window.URL.createObjectURL(workerBlob);

document.title='WasmWorker(BlobURL)';

}

returnPromise.resolve(workerUrl);

};

/**

*InstantiatestheWasmmoduleassociatedwiththespecifiedworker

*andaddseventlistenerstothe"Add"and"Subtract"buttons.

*/

constinitializeWorker=async(wasmWorker,name)=>{

awaitwasmWorker.initialize(name);

wasmWorker.addListenerForType('CALC_RESPONSE',payload=>{

document.querySelector('#result').value=payload;

});

document.querySelector(`#${name}`).addEventListener('click',()=>{

constinputs=document.querySelectorAll('input');

var[firstInput,secondInput]=inputs.values();

wasmWorker.calculate(+firstInput.value,+secondInput.value);

});

};

/**

*Spawns(2)workers:oneassociatedwithcalc-add.wasmandanother

*withcalc-subtract.wasm.Addsaneventlistenertothe"Reset"

*buttontoclearalltheinputvalues.

*/

constloadPage=async()=>{

document.querySelector('#reset').addEventListener('click',()=>{

constinputs=document.querySelectorAll('input');

inputs.forEach(input=>(input.value=0));

});

constworkerUrl=awaitgetWorkerUrl();

constaddWorker=newWasmWorker(workerUrl);

awaitinitializeWorker(addWorker,'add');

constsubtractWorker=newWasmWorker(workerUrl);

awaitinitializeWorker(subtractWorker,'subtract');

};

loadPage()

.then(()=>console.log('%cPageloaded!','color:green;'))

.catch(error=>console.error(error));

TheapplicationentrypointistheloadPage()function.Beforewedigintotheworkerinitializationcode,let'sdiscussthegetWorkerUrl()function.Earlierinthissection,welearnedthatyoucanpassastringrepresentingafilenameoraURLcreatedfromaBlobtotheWorker()constructor.Thefollowingexamplecodedemonstratesthefirsttechnique:

varworker=newWorker('worker.js');

Thesecondtechniqueisdemonstratedintheif(isBlob==='true')blockofthegetWorkerUrl()function.Ifthecurrentwindow.locationvalueendswith?blob=true,theURLpassedtotheWorker()constructoriscreatedfromaBlob.Theonlynoticeabledifferenceisthedocument.titlevalue,whichupdatestoreflecttheURLtype.Let'sjumpbacktotheloadPage()functiontodiscusstheinitializationcode.

AfteraneventlistenerisaddedtotheResetbuttonintheloadPage()function,twoWasmWorkerinstancesarecreated:addWorkerandsubtractWorker.EachworkerispassedtotheinitializeWorker()functionasthewasmWorkerargument.IninitializeWorker(),thewasmWorker.initialize()functioniscalledtoinstantiatetheWasmmodule.ThewasmWorker.addListenerForType()functioniscalledtosetthevalueoftheResult<input>tothevaluereturnedfromthe_calculate()functioninthecorrespondingworker.Finally,aneventlistenerisaddedtotheclickeventofthe<button>thateitheraddsorsubtractsthefirstValandsecondVal<input>values(basedonthenameargument).That'sitfortheJavaScriptcode.Let'screateanHTMLandCSSfile,thenmoveontothebuildstep.

ThewebassetsWeneedanHTMLfiletoactastheentrypointtotheapplication.Createafileinthe/srcdirectorynamedindex.htmlandpopulateitwiththefollowingcontents:

<!DOCTYPEhtml>

<html>

<head>

<metacharset="utf-8">

<title>WasmWorkers</title>

<linkrel="stylesheet"type="text/css"href="styles.css"/>

</head>

<body>

<formclass="valueForm">

<divclass="valueForm">

<labelfor="firstVal">FirstValue:</label>

<inputid="firstVal"type="number"value="0"/>

</div>

<divclass="valueForm">

<labelfor="secondVal">SecondValue:</label>

<inputid="secondVal"type="number"value="0"/>

</div>

<divclass="valueForm">

<labelfor="result">Result:</label>

<inputid="result"type="number"value="0"readonly/>

</div>

</form>

<div>

<buttonid="add">Add</button>

<buttonid="subtract">Subtract</button>

<buttonid="reset">Reset</button>

</div>

<scripttype="module"src="index.js"></script>

</body>

</html>

Theapplicationconsistsofa<form>withthree<input>elementsandablockofthree<button>elements.Thefirsttwo<input>elementscorrespondtothefirstValandsecondValpropertiesincludedinthepayloadsenttoeitherworkerthread.Thefinal<input>isread-onlyanddisplaystheresultofeitheroperation.

Theblockof<button>elementsbelowthe<form>performoperationsonthe<input>values.Thefirsttwo<button>elementssendthe<input>valuestoeithertheaddWorkerorsubtractWorkerthread(dependingonwhichbuttonwaspressed).Thefinal<button>setsallofthe<input>valuesto0.

Theapplicationisinitializedinthe<script>taginthelastlinebeforethe</body>closingtag.JustaswithCooktheBooks,thetype="module"attributeallowsusto

usetheimport/exportsyntaxavailableinnewerbrowsers.Finally,weneedtoaddsomestylestotheapplication.Createafileinthe/srcdirectorynamedstyles.cssandpopulateitwiththefollowingcontents:

*{

font-family:sans-serif;

font-size:14px;

}

body{

margin:16px;

}

form.valueForm{

display:table;

}

div.valueForm{

display:table-row;

}

label,input{

display:table-cell;

margin-bottom:16px;

}

label{

font-weight:bold;

padding-right:16px;

}

button{

border:1pxsolidblack;

border-radius:4px;

cursor:pointer;

font-weight:bold;

height:24px;

margin-right:4px;

width:80px;

}

button:hover{

background:lightgray;

}

That'sthelastfileweneedtocreate,butnotthelastonerequiredtoruntheapplication.WestillneedtogenerateWasmfilesfromtheCfilesinthe/libdirectory.Let'smoveontothebuildstep.

BuildingandrunningtheapplicationWiththecodewritten,it'stimetobuildandtesttheapplication.Aftercompletingthebuildstep,we'llinteractwiththerunningapplicationandreviewhowtotroubleshootWebWorkersusingthebrowser'sdevelopmenttools.

CompilingtheCfilesWeneedtocompileeachCfiletoaseparate.wasmfile,whichmeansthecommandneededtoperformthecompilationstepisverbose.Toperformthebuild,openaterminalinstanceinyour/parallel-wasmdirectoryandrunthefollowingcommands:

#First,compiletheadd.cfile:

emcc-Os-sWASM=1-sSIDE_MODULE=1-sBINARYEN_ASYNC_COMPILATION=0lib/add.c-osrc/calc-add.wasm

#Next,compilethesubtract.cfile

emcc-Os-sWASM=1-sSIDE_MODULE=1-sBINARYEN_ASYNC_COMPILATION=0lib/subtract.c-osrc/calc-subtract.wasm

Youshouldseetwonewfilesinthe/srcdirectory:calc-add.wasmandcalc-subtract.wasm.Withtherequiredfilesinplace,it'stimetotestouttheapplication.

InteractingwiththeapplicationOpenaterminalinstanceinthe/parallel-wasmdirectoryandrunthefollowingcommand:serve-l8080src

Ifyounavigatetohttp://127.0.0.1:8080/index.htmlinyourbrowser,youshouldsee

this:

WasmWorkersapplicationrunninginthebrowserTrychangingthevaluesintheFirstValueandSecondValueinputsandpressingtheAddandSubtractbuttons.TheResultinputshouldupdatewiththecalculatedresult.Ifyounavigateto

http://127.0.0.1:8080/index.html?blob=true,theURLargumentpassedtotheWorker()constructorwilluseaBlobinsteadofthefilename.ThetabshouldchangetoreflectthattheBlobtechniqueisusedtoconstructtheURL:

TabtitleupdatedtoreflecttheBlobURLtechnique

DebuggingWebWorkersYoucansetbreakpointsandinteractwithworkerthreadsusingthebrowser'sdevelopmenttools.InGoogleChrome,openDeveloperToolsandselecttheSourcestab.Thefilelistpanelshouldcontaintwoinstancesofworker.js.ThedebuggerpanelcontainsaThreadssectionwiththemainthreadandtwoworker.jsthreads.ThefollowingscreenshotindicatesthethreaddebuggingelementswithintheChromeDeveloperToolspanelfortherunningapplication:

ThreaddebuggingtoolsintheChromeDeveloperToolspanel

InFirefox,workerdebuggingisdoneinseparateDeveloperToolswindows.Toseethisinaction,openDeveloperToolsinFirefoxandselecttheDebuggerpanel.Clickononeoftheworker.jslistitemsintheWorkerspanel.AnewDeveloperToolswindowshouldappearthatcorrespondswiththeselectedworker.ThefollowingscreenshotshowsaseparateDeveloperToolswindowforoneoftheworker.jsinstancesselectedfromtheWorkerspanel:

ThreaddebuggingtoolsintheFirefoxDeveloperToolspanelInthenextsection,we'lldiscusssomeoftheupcomingfeaturesofWebAssembly.

UpcomingfeaturesThereareseveralupcomingWebAssemblyfeaturesinvariousphasesofthestandardizationprocess.Someofthemaremoreimpactfulthanothers,butallofthemarevaluableimprovements.Inthissection,we'lldescribethestandardizationprocessandreviewasubsetofthefeaturesthatrepresentasignificantshiftinWebAssembly'scapabilities.MostofthecontentinthissectionwasreferencedfromColinEberhardt'sblogposttitledThefutureofWebAssembly-Alookatupcomingfeaturesandproposals.Thepostcanbefoundathttps://blog.scottlogic.com/2018/07/20/wasm-future.html.

ThestandardizationprocessTheWebAssemblyW3CProcessdocumentationathttps://github.com/WebAssembly/meetings/blob/master/process/phases.mddescribesthesixphases(from0to5)ofthestandardizationprocess.Thefollowinglistprovidesbriefdescriptionsofeachofthesephases:

Phase0.Pre-Proposal:AWebAssemblyCommunityGroup(CG)memberhasanidea,andtheCGvotesonwhethertomoveittoPhase1.Phase1.FeatureProposal:Thepre-proposalprocesshassucceededandarepositoryiscreatedintheWebAssemblyorganizationonGitHubtodocumentthefeature.Phase2.ProposedSpecTextAvailable:Thefullproposedspectextisavailable,possibleimplementationsareprototyped,andatestsuiteisadded.Phase3.ImplementationPhase:Embeddersimplementthefeature,therepositoryisupdatedtoincluderevisionstotheformalization,andthespecisupdatedtoincludeimplementationofthefeatureinthereferenceinterpreter.Phase4.StandardizetheFeature:IftwoormoreWebVMsandatleastonetoolchainimplementthefeature,thefeatureisfullyhandedofftotheWebAssemblyWorkingGroup(WG).Phase5.TheFeatureisStandardized:TheWGmembershavereachedconsensusthatthefeatureiscomplete.

Nowthatyou'refamiliarwiththephasesassociatedwiththestandardizationprocess,let'smoveontothethreadsproposal.

ThreadsIntheprevioussection,weusedWebWorkerstomoveWasmmodulesintoworkerthreads,whichallowedustocallWasmfunctionswithoutblockingthemainthread.However,passingmessagesbetweenworkerthreadshasperformancelimitations.Inanefforttoaddressthisissue,athreadsfeaturewasproposedforWebAssembly.

Theproposal,currentlyinPhase1,isdescribedindetailathttps://github.com/WebAssembly/threads/blob/master/proposals/threads/Overview.md.Pertheproposaldocumentation,thethreadsfeatureaddsanewsharedlinearmemorytypeandsomenewoperationsforatomicmemoryaccess.Thisproposalisrelativelylimitedinscope.Eberhardtprovidesthefollowingelaborationinhisblogpost:

"Notably,thisproposaldoesnotintroduceamechanismforcreatingthreads(whichhascausedalotofdebate)insteadthisfunctionalityissuppliedbythehost.WithinthecontextofwasmexecutedbythebrowserthiswillbethefamiliarWebWorkers."

Althoughthefeaturewouldn'tallowforthecreationofthreads,itprovidesasimplerwayofsharingdatabetweentheworkerthreadswecreateinJavaScript.

HostbindingsThehostbindingsproposal,whichisalsoinPhase1,wouldaddressasignificantlimitationofWebAssemblywhenusedinthebrowser:DOMmanipulation.Theproposaldocumentationathttps://github.com/WebAssembly/host-bindings/blob/master/proposals/host-bindings/Overview.mdprovidesthefollowinglistofgoalsforthisfeature:

Ergonomics:AllowWebAssemblymodulestocreate,passaround,call,andmanipulateJavaScript+DOMobjectsSpeed:AllowJS/DOMorotherhostcallstobewelloptimizedPlatformconsistency:AllowWebIDLtobeusedtoannotateWasmimports/exports(viaatool)Incrementalism:Provideastrategythatispolyfillable

ImprovingWebAssembly'sinteroperabilitywithJavaScriptandWebAPIswouldsimplifythedevelopmentprocessconsiderably.Itwouldalsoeliminatetheneedforthe"glue"codethattoolssuchasEmscriptenprovide.

GarbagecollectionThegarbagecollection(GC)proposaliscurrentlyinPhase1.WediscussedgarbagecollectionintheWhataretheLimitations?sectionofChapter1,WhatisWebAssembly?Theproposaldocumentationathttps://github.com/WebAssembly/gc/blob/master/proposals/gc/Overview.mdprovidesanextensiveoverviewofthefeatureanddescribestheelementsthatneedtobeaddedtothespecification.Eberhardtprovidesthefollowingdescriptionoftheproposalinhisblogpost:"ThisproposaladdsGCcapabilitiestoWebAssembly.Interestingly,itwillnothaveitsownGC,insteaditwillintegratewiththeGCprovidedbythehostenvironment.Thismakesalotofsenseasthis,andvariousotherproposals(hostbindings,referencetypes),aredesignedtoimprovetheinteropwiththehost,makingiteasiertosharestateandcallAPIs.HavingasingleGCtomanagememorymakesthismucheasier."

Thisfeaturewillrequireagreatdealofefforttoimplement,butaddingittoWebAssemblywillbeworththeeffort.Let'swrapupthissectionwithafeaturecurrentlyintheimplementationphase:referencetypes.

ReferencetypesReferencetypes,currentlyinPhase3,formthebasisforthehostbindingsandGCfeatures.Theproposaldocumentationathttps://github.com/WebAssembly/reference-types/blob/master/proposals/reference-types/Overview.mddescribestheadditionofanewtype,anyref,whichcanbeusedasbothavaluetypeandatableelementtype.TheanyreftypeallowsyoutopassaJavaScriptobjecttoaWasmmodule.Eberhardtdescribestheimplicationsofthisfeatureinhisblogpost:"Thewasmmodulecan'treallydomuchwiththeobjectviatheanyreftype.What'smoreimportantisthatthemoduleisholdingareferencetoagarbagecollectedobjectontheJSheap,meaningtheyneedtobetracedduringwasmexecution.Thisproposalisseenasastepping-stonetowardsthemoresignificantgarbagecollectionproposal."

ThereareseveralotherexcitingfeaturesinthepipelineforWebAssembly.TheWebAssemblyCGandWGaredevotingtheirtimeandresourcestomakingthesefeaturesareality.YoucanviewalloftheproposalsattheWebAssemblyorganizationpageonGitHub,locatedathttps://github.com/WebAssembly.

SummaryInthischapter,wereviewedadvancedtoolsandanalternatecompilationmethodforWebAssembly.WelearnedaboutWABTandBinaryen'sroleintheWebAssemblydevelopmentprocessandthefunctionalitytheyprovide.WecompiledaWasmmodulewithLLVMthroughtheuseoftheWebAssemblynpmpackageandinteractedwiththeresultinthebrowser.WereviewedsomeoftheWebAssemblytoolingavailableonlineandcreatedasimpleapplicationthatusesWebWorkerstostoreWasmmodulesinseparatethreads.Finally,wediscussedtheupcomingfeaturesofWebAssemblyandthestandardizationprocess.Nowthatyou'vegainedagreaterunderstandingofWebAssembly,gooutthereandbuildsomething!

QuestionsWhatdoesWABTstandfor?WhatthreeelementsdoesBinaryenprovidetomakecompilingtoWebAssemblyeasy,fast,andeffective?WhatisthemaindifferencebetweenmodulescompiledusingEmscriptenversusLLVMwithregardtotheimportObj/exports?WhichonlinetoolallowsyoutouseAssemblyScript?WhatarethetwotypesofargumentsyoucanpasstotheWorker()constructor?Whatconventionwasusedforpassingmessagesbetweenthemainthreadandworkerthreads?HowmanyphasesareintheWebAssemblystandardizationprocess?Whatisthenameofthenewtypedefinedinthereferencetypesfeature?

FurtherreadingAcrashcourseinmemorymanagement:https://hacks.mozilla.org/2017/06/a-crash-course-in-memory-management

MDNWebWorkersAPI:https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API

WebAssembly-WebWorkers:https://medium.com/@c.gerard.gallant/webassembly-web-workers-f2ba637c3e4a

OtherBooksYouMayEnjoyIfyouenjoyedthisbook,youmaybeinterestedintheseotherbooksbyPackt:

Angular6forEnterprise-ReadyWebApplicationsDoguhanUluca

ISBN:9781786462909

Createfull-stackwebapplicationsusingAngularandRESTfulAPIsMasterAngularfundamentals,RxJS,CLItools,unittesting,GitHub,andDockerDesignandarchitectresponsive,secureandscalableappstodeployonAWSAdoptaminimalist,value-firstapproachtodeliveringyourappwithKanbanGetintroducedtoautomatedtestingwithcontinuousintegrationonCircleCIOptimizeNginxandNode.jswebserverswithloadtestingtools

MasteringTheFasterWebwithPHP,MySQL,andJavaScript

AndrewCaya

ISBN:9781788392211

Install,confgure,anduseproflingandbenchmarkingtestingtoolsUnderstandhowtorecognizeoptimizabledatastructuresandfunctionstoeffectivelyoptimizeaPHP7applicationDiagnosebadSQLqueryperformanceanddiscoverwaystooptimizeitGraspmodernSQLtechniquestooptimizecomplexSQLqueriesIdentifyandsimplifyoverlycomplexJavaScriptcodeExploreandimplementUIdesignprinciplesthateffectivelyenhancetheperformanceCombinewebtechnologiestoboostwebserverperformance

Leaveareview-letotherreadersknowwhatyouthinkPleaseshareyourthoughtsonthisbookwithothersbyleavingareviewonthesitethatyouboughtitfrom.IfyoupurchasedthebookfromAmazon,pleaseleaveusanhonestreviewonthisbook'sAmazonpage.Thisisvitalsothatotherpotentialreaderscanseeanduseyourunbiasedopiniontomakepurchasingdecisions,wecanunderstandwhatourcustomersthinkaboutourproducts,andourauthorscanseeyourfeedbackonthetitlethattheyhaveworkedwithPackttocreate.Itwillonlytakeafewminutesofyourtime,butisvaluabletootherpotentialcustomers,ourauthors,andPackt.Thankyou!