Table of Contents for
Learn WebAssembly

Version ebook / Retour

Cover image for bash Cookbook, 2nd Edition Learn WebAssembly by Mike Rourke Published by Packt Publishing, 2018
  1. Learn WebAssembly
  2. Title Page
  3. Copyright and Credits
  4. Learn WebAssembly
  5. Dedication
  6. PacktPub.com
  7. Why subscribe?
  8. PacktPub.com
  9. Contributors
  10. About the author
  11. About the reviewers
  12. Packt is searching for authors like you
  13. Table of Contents
  14. Preface
  15. Who this book is for
  16. What this book covers
  17. To get the most out of this book
  18. Download the example code files
  19. Download the color images
  20. Conventions used
  21. Get in touch
  22. Reviews
  23. What is WebAssembly?
  24. The road to WebAssembly
  25. The evolution of JavaScript
  26. Google and Native Client
  27. Mozilla and asm.js
  28. WebAssembly is born
  29. What exactly is WebAssembly and where can I use it?
  30. Official definition
  31. Binary instruction format
  32. Portable target for compilation
  33. The core specification
  34. Language concepts
  35. Semantic phases
  36. The JavaScript and Web APIs
  37. So will it replace JavaScript?
  38. Where can I use it?
  39. What languages are supported?
  40. C and C++
  41. Rust
  42. Other languages
  43. What are the limitations?
  44. No garbage collection
  45. No direct DOM access
  46. No support in older browsers
  47. How does it relate to Emscripten?
  48. Emscripten's role
  49. The EMSDK and Binaryen
  50. Summary
  51. Questions
  52. Further reading
  53. Elements of WebAssembly - Wat, Wasm, and the JavaScript API
  54. Common structure and abstract syntax
  55. Wat
  56. Definitions and S-expressions
  57. Values, types, and instructions
  58. Role in the development process
  59. Binary format and the module file (Wasm)
  60. Definition and module overview
  61. Module sections
  62. The JavaScript and Web APIs
  63. WebAssembly store and object caches
  64. Loading a module and the WebAssembly namespace methods
  65. WebAssembly objects
  66. WebAssembly.Module
  67. WebAssembly.Instance
  68. WebAssembly.Memory
  69. WebAssembly.Table
  70. WebAssembly errors (CompileError, LinkError, RuntimeError)
  71. Connecting the dots with WasmFiddle
  72. What is WasmFiddle?
  73. C code to Wat
  74. Wasm to JavaScript
  75. Summary
  76. Questions
  77. Further reading
  78. Setting Up a Development Environment
  79. Installing the development tooling
  80. Operating systems and hardware
  81. macOS 
  82. Ubuntu
  83. Windows
  84. Package managers
  85. Homebrew for macOS
  86. Apt for Ubuntu
  87. Chocolatey for Windows
  88. Git
  89. Installing Git on macOS
  90. Installing Git on Ubuntu
  91. Installing Git on Windows
  92. Node.js
  93. nvm
  94. Installing nvm on macOS
  95. Install nvm on Ubuntu
  96. Installing nvm on Windows
  97. Installing Node.js using nvm
  98. GNU make and rimraf
  99. GNU Make on macOS and Ubuntu
  100. Installing GNU Make on macOS
  101. Installing GNU Make on Ubuntu
  102. Installing GNU make on Windows
  103. Installing rimraf
  104. VS Code
  105. Installing Visual Studio Code on macOS
  106. Installing Visual Studio Code on Ubuntu
  107. Installing VS Code on Windows
  108. Configuring VS Code
  109. Managing settings and customization
  110. Extensions overview
  111. Configuration for C/C++ and WebAssembly
  112. Installing C/C++ for VS Code
  113. Configuring C/C++ for VS Code
  114. WebAssembly Toolkit for VSCode
  115. Other useful extensions
  116. Auto rename tag
  117. Bracket pair colorizer
  118. Material Icon theme and Atom One Light theme
  119. Setting up for the web
  120. Cloning the book examples repository
  121. Installing a local server
  122. Validating your browser
  123. Validating Google Chrome
  124. Validating Mozilla Firefox
  125. Validating other browsers
  126. Other tools
  127. iTerm2 for macOS
  128. Terminator for Ubuntu
  129. cmder for Windows
  130. Zsh and Oh-My-Zsh
  131. Summary
  132. Questions
  133. Further reading
  134. Installing the Required Dependencies
  135. The development workflow
  136. Steps in the workflow
  137. Integrating Tooling into the workflow
  138. Emscripten and the EMSDK
  139. Emscripten overview
  140. Where does the EMSDK fit in?
  141. Installing the prerequisites
  142. Common prerequisites
  143. Installing the prerequisites on macOS
  144. Installing the prerequisites on Ubuntu
  145. Installing the prerequisites on Windows
  146. Installing and configuring the EMSDK
  147. Installation process across all platforms
  148. Installation on macOS and Ubuntu
  149. Installation and configuration on Windows
  150. Configuration in VS Code
  151. Testing the compiler
  152. The C code
  153. Compiling the C code
  154. Summary
  155. Questions
  156. Further reading
  157. Creating and Loading a WebAssembly Module
  158. Compiling C with Emscripten glue code
  159. Writing the example C code
  160. Compiling the example C code
  161. Outputting HTML with glue code
  162. Outputting glue code with no HTML
  163. Loading the Emscripten module
  164. Pre-generated loading code
  165. Writing custom loading code
  166. Compiling C without the glue code
  167. C code for WebAssembly
  168. Compiling with a Build Task in VS Code
  169. Fetching and instantiating a Wasm file
  170. Common JavaScript loading code
  171. The HTML page
  172. Serving it all up
  173. Summary
  174. Questions
  175. Further reading
  176. Interacting with JavaScript and Debugging
  177. The Emscripten module versus the WebAssembly object
  178. What is the Emscripten module?
  179. Default methods in the glue code
  180. Differences with the WebAssembly object
  181. Calling compiled C/C++ functions from JavaScript
  182. Calling functions from a Module 
  183. Module.ccall()
  184. Module.cwrap()
  185. C++ and name mangling
  186. Calling functions from a WebAssembly instance
  187. Calling JavaScript functions from C/C++
  188. Interacting with JavaScript using glue code
  189. Executing strings with emscripten_run_script()
  190. Executing inline JavaScript with EM_ASM()
  191. Reusing inline JavaScript with EM_JS()
  192. Examples of using glue code
  193. The C code
  194. The HTML code
  195. Compiling and serving the result
  196. Interacting with JavaScript without glue code
  197. Passing JavaScript to C/C++ using the import object
  198. Calling imported functions in C/C++
  199. An example without glue code
  200. The C++ code
  201. The HTML code
  202. Compiling and serving the result
  203. Advanced Emscripten features
  204. Embind
  205. File System API
  206. Fetch API
  207. Debugging in the browser
  208. High-level overview
  209. Using source maps
  210. Summary
  211. Questions
  212. Further reading
  213. Creating an Application from Scratch
  214. Cook the Books – making WebAssembly accountable
  215. Overview and functionality
  216. JavaScript libraries used
  217. Vue
  218. UIkit
  219. Lodash
  220. Data-driven documents
  221. Other libraries
  222. C and the build process
  223. Setting up the project
  224. Configuring for Node.js
  225. Adding files and folders
  226. Configuring the build step
  227. Setting up a mock API
  228. Downloading the C stdlib Wasm
  229. The final result
  230. Building the C portion
  231. Overview
  232. A note regarding workflow
  233. C file contents
  234. Declarations
  235. Linked list operations
  236. transactions operations
  237. transactions calculations
  238. Category calculations
  239. Compiling to Wasm
  240. Building the JavaScript portion
  241. Overview
  242. A note about browser compatibility
  243. Creating a Wasm instance in initializeWasm.js
  244. Interacting with Wasm in WasmTransactions.js
  245. Utilizing the API in api.js
  246. Managing global state in store.js
  247. The import and store declarations
  248. Transactions operations
  249. TransactionModal management
  250. Balances calculation
  251. Store initialization
  252. Loading the application in main.js
  253. Adding the web assets
  254. Creating the Vue components
  255. The structure of a Vue component
  256. The App component
  257. The BalancesBar
  258. The TransactionsTab
  259. The ChartsTab
  260. Running the application
  261. Validating the /src folder
  262. Start it up!
  263. Testing it out
  264. Changing initial balances
  265. Creating a new transaction
  266. Deleting an existing transaction
  267. Editing an existing transaction
  268. Testing the Charts tab
  269. Wrap up
  270. Summary
  271. Questions
  272. Further reading
  273. Porting a Game with Emscripten
  274. Overview of the game
  275. What is Tetris?
  276. The source of the source
  277. A note about porting
  278. Getting the code
  279. Building the native project
  280. The game in action
  281. The code base in depth
  282. Breaking the code into objects
  283. The constants file
  284. The piece class
  285. The constructor and draw() function
  286. The move(), rotate(), and isBlock() functions
  287. The getColumn() and getRow() functions
  288. The Board class
  289. The constructor and draw() function
  290. The isCollision() function
  291. The unite() function
  292. The displayScore() function
  293. The Game class
  294. The constructor and destructor
  295. The loop() function
  296. The main file
  297. Porting to Emscripten
  298. Preparing for porting
  299. What's changing?
  300. Adding the web assets
  301. Porting the existing code
  302. Updating the constants file
  303. Building and running the game
  304. Building with VS Code tasks
  305. Building with a Makefile
  306. Running the game
  307. Summary
  308. Questions
  309. Further reading
  310. Integrating with Node.js
  311. Why Node.js?
  312. Seamless integration
  313. Complementary technologies
  314. Development with npm
  315. Server-side WebAssembly with Express
  316. Overview of the project
  317. Express configuration
  318. Instantiating a Wasm module with Node.js
  319. Creating a mock database
  320. Interacting with the WebAssembly module
  321. Wrapping interaction in Transaction.js
  322. Transaction operations in assign-routes.js
  323. Building and running the application
  324. Building the application
  325. Starting and testing out the application
  326. Client-side WebAssembly with Webpack
  327. Overview of the project
  328. What is Webpack?
  329. Installing and configuring Webpack
  330. Dependencies overview
  331. Configuring loaders and plugins in webpack.config.js
  332. The C code
  333. Definitions and declarations
  334. The start() function
  335. The updateRectLocation() function
  336. The JavaScript code
  337. The import statements
  338. Component state
  339. Wasm initialization
  340. Component mounting
  341. Component rendering
  342. Building and running the application
  343. Testing the build
  344. Running the start script
  345. Testing WebAssembly modules with Jest
  346. The code being tested
  347. Testing configuration
  348. Tests file review
  349. Running the tests
  350. Summary
  351. Questions
  352. Further reading
  353. Advanced Tools and Upcoming Features
  354. WABT and Binaryen
  355. WABT – the WebAssembly binary toolkit
  356. Binaryen
  357. Compiling with LLVM
  358. The installation process
  359. The example code
  360. The C++ file
  361. The HTML file
  362. Compiling and running the example
  363. Online tooling
  364. WasmFiddle
  365. WebAssembly Explorer
  366. WebAssembly Studio
  367. Parallel Wasm with Web Workers
  368. Web Workers and WebAssembly
  369. Creating a worker
  370. The WebAssembly workflow
  371. Limitations in Google Chrome
  372. Overview of the code
  373. The C code
  374. The JavaScript code
  375. Defining thread execution in worker.js
  376. Interacting with Wasm in WasmWorker.js
  377. Loading the application in index.js
  378. The web assets
  379. Building and running the application
  380. Compiling the C files
  381. Interacting with the application
  382. Debugging Web Workers
  383. Upcoming features
  384. The standardization process
  385. Threads
  386. Host bindings
  387. Garbage collection
  388. Reference types
  389. Summary
  390. Questions
  391. Further reading
  392. Other Books You May Enjoy
  393. Leave a review - let other readers know what you think

Updating the constants file

We'll display the rows cleared count on the DOM instead of in the game window itself, so you can delete the ScreenHeight constant from the file. We no longer need additional space to accommodate for the score text:

namespace Constants {
const int BoardColumns = 10;
const int BoardHeight = 720;
const int BoardRows = 20;
const int BoardWidth = 360;
const int Offset = BoardWidth / BoardColumns;
const int PieceSize = 4;
// const int ScreenHeight = BoardHeight + 50; <----- Delete this line
}

No changes need to be made to the Piece class files (piece.cpp/piece.h). However, we will need to update the Board class. Let's start with the header file (board.h). Starting with the bottom and working our way up, let's update the displayScore() function. In the <body> section of the index.html file, there's a <span> element with id="score". We're going to update this element using the emscripten_run_script command to display the current score. As a result, the displayScore() function becomes much shorter. The before and after is shown as follows.

Here is the original version of the Board class's displayScore() function:

void Board::displayScore(SDL_Renderer *renderer, TTF_Font *font) {
std::stringstream message;
message << "ROWS: " << currentScore_;
SDL_Color white = { 255, 255, 255 };
SDL_Surface *surface = TTF_RenderText_Blended(
font,
message.str().c_str(),
white);
SDL_Texture *texture = SDL_CreateTextureFromSurface(
renderer,
surface);
SDL_Rect messageRect{ 20, BoardHeight + 15, surface->w, surface->h };
SDL_FreeSurface(surface);
SDL_RenderCopy(renderer, texture, nullptr, &messageRect);
SDL_DestroyTexture(texture);
}

Here is the ported version of the displayScore() function:

void Board::displayScore(int newScore) {
std::stringstream action;
action << "document.getElementById('score').innerHTML =" << newScore;
emscripten_run_script(action.str().c_str());
}

The emscripten_run_script action simply finds the <span> element on the DOM and sets the innerHTML to the current score. We can't use the EM_ASM() function here because Emscripten doesn't recognize the document object. Since we have access to the private currentScore_ variable in the class, we're going to move the displayScore() call in the draw() function into the unite() function. This limits the amount of calls to displayScore() to ensure that the function is called only when the score has actually changed. We only need to add one line of code to accomplish this. Here's what the unite() function looks like now:

void Board::unite(const Piece &piece) {
for (int column = 0; column < PieceSize; ++column) {
for (int row = 0; row < PieceSize; ++row) {
if (piece.isBlock(column, row)) {
int columnTarget = piece.getColumn() + column;
int rowTarget = piece.getRow() + row;
cells_[columnTarget][rowTarget] = true;
}
}
}

// Continuously loops through each of the rows until no full rows are
// detected and ensures the full rows are collapsed and non-full rows
// are shifted accordingly:
while (areFullRowsPresent()) {
for (int row = BoardRows - 1; row >= 0; --row) {
if (isRowFull(row)) {
updateOffsetRow(row);
currentScore_ += 1;
for (int column = 0; column < BoardColumns; ++column) {
cells_[column][0] = false;
}
}
}
displayScore(currentScore_); // <----- Add this line
}
}

Since we're no longer using the SDL2_ttf library, we can update the draw() function signature and remove the displayScore() function call. Here's the updated draw() function:

void Board::draw(SDL_Renderer *renderer/*, TTF_Font *font */) {
// ^^^^^^^^^^^^^^ <-- Remove this argument
// displayScore(renderer, font); <----- Delete this line
SDL_SetRenderDrawColor(
renderer,
/* Light Gray: */ 140, 140, 140, 255);
for (int column = 0; column < BoardColumns; ++column) {
for (int row = 0; row < BoardRows; ++row) {
if (cells_[column][row]) {
SDL_Rect rect{
column * Offset + 1,
row * Offset + 1,
Offset - 2,
Offset - 2
};
SDL_RenderFillRect(renderer, &rect);
}
}
}
}

The displayScore() function call was removed from the first line of the function and the TTF_Font *font argument was removed as well. Let's add a call to displayScore() in the constructor to ensure that the initial value is set to 0 when the game ends and a new one begins:

Board::Board() : cells_{{ false }}, currentScore_(0) {
displayScore(0); // <----- Add this line
}

That's it for the class file. Since we changed the signatures for the displayScore() and draw() functions, and removed the dependency for SDL2_ttf, we'll need to update the header file. Remove the following lines from board.h:

#ifndef TETRIS_BOARD_H
#define TETRIS_BOARD_H

#include <SDL2/SDL.h>
// #include <SDL2/SDL2_ttf.h> <----- Delete this line
#include "constants.h"
#include "piece.h"

using namespace Constants;

class Board {
public:
Board();
void draw(SDL_Renderer *renderer /*, TTF_Font *font */);
// ^^^^^^^^^^^^^^ <-- Remove this
bool isCollision(const Piece &piece) const;
void unite(const Piece &piece);

private:
bool isRowFull(int row);
bool areFullRowsPresent();
void updateOffsetRow(int fullRow);
void displayScore(SDL_Renderer *renderer, TTF_Font *font);
// ^^^^^^^^^^^^^^ <-- Remove this
bool cells_[BoardColumns][BoardRows];
int currentScore_;
};

#endif // TETRIS_BOARD_H

We're moving right along! The final change we need to make is the also the biggest one. The existing code base has a Game class that manages the application logic and a main.cpp file that calls the Game.loop() function in the main() function. The looping mechanism is a while loop that continues to run as long as the SDL_QUIT event hasn't fired. We need to change our approach to accommodate for Emscripten.

Emscripten provides an emscripten_set_main_loop function that accepts an em_callback_func looping function, fps, and a simulate_infinite_loop flag. We can't include the Game class and pass Game.loop() as the em_callback_func argument, because the build will fail. Instead, we're going to eliminate the Game class completely and move the logic into the main.cpp file. Copy the contents of game.cpp into main.cpp (overwriting the existing contents) and delete the Game class files (game.cpp/game.h). Since we're not declaring a class for Game, remove the Game:: prefixes from the functions. The constructor and destructor are no longer valid (they're no longer part of a class), so we need to move that logic to a different location. We also need to reorder the file to ensure that our called functions come before the calling functions. The final result looks like this:

#include <emscripten/emscripten.h>
#include <SDL2/SDL.h>
#include <stdexcept>
#include "constants.h"
#include "board.h"
#include "piece.h"

using namespace std;
using namespace Constants;

static SDL_Window *window = nullptr;
static SDL_Renderer *renderer = nullptr;
static Piece currentPiece{ static_cast<Piece::Kind>(rand() % 7) };
static Board board;
static int moveTime;

void checkForCollision(const Piece &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;
}
}

void handleKeyEvents(SDL_Event &event) {
Piece newPiece = currentPiece;
switch (event.key.keysym.sym) {
case SDLK_DOWN:
newPiece.move(0, 1);
break;
case SDLK_RIGHT:
newPiece.move(1, 0);
break;
case SDLK_LEFT:
newPiece.move(-1, 0);
break;
case SDLK_UP:
newPiece.rotate();
break;
default:
break;
}
if (!board.isCollision(newPiece)) currentPiece = newPiece;
}

void loop() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_KEYDOWN:
handleKeyEvents(event);
break;
case SDL_QUIT:
break;
default:
break;
}
}

SDL_SetRenderDrawColor(renderer, /* Dark Gray: */ 58, 58, 58, 255);
SDL_RenderClear(renderer);
board.draw(renderer);
currentPiece.draw(renderer);

if (SDL_GetTicks() > moveTime) {
moveTime += 1000;
Piece newPiece = currentPiece;
newPiece.move(0, 1);
checkForCollision(newPiece);
}
SDL_RenderPresent(renderer);
}

int main() {
moveTime = SDL_GetTicks();
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
throw std::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();
return 0;
}

The handleKeyEvents() and checkForCollision() functions haven't changed; we simply moved them to the top of the file. The loop() function return type was changed from bool to void as required by emscripten_set_main_loop. Finally, the code from the constructor and destructor was moved into the main() function and any references to SDL2_ttf were removed. Instead of the while statement that called the loop() function of Game, we have the emscripten_set_main_loop(loop, 0, 1) call. We changed the #include statements at the top of the file to accommodate for Emscripten, SDL2, and our Board and Piece classes. That's it for changes — now it's time to configure the build and test out the game.