├── .npmignore
├── .babelrc
├── .gitignore
├── example
├── hello.wasm
├── .babelrc
├── index.html
├── hello.cc
├── docker-compose.yml
├── module_loader.js
├── package-lock.json
├── Makefile
├── package.json
├── webpack.config.js
├── README.md
├── hello.js
└── hello-wasm.js
├── .eslintrc.yml
├── webpack.config.js
├── Dockerfile
├── LICENSE
├── pre.js
├── package.json
├── src
├── loader.js
└── helper.js
├── README.md
├── wasm-builder.go
└── dist
└── wasm-loader.js
/.npmignore:
--------------------------------------------------------------------------------
1 | example
2 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | { "presets": ["es2015"] }
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.mem
3 | *.wast
4 | *.map
5 |
--------------------------------------------------------------------------------
/example/hello.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/knocknote/wasm/HEAD/example/hello.wasm
--------------------------------------------------------------------------------
/example/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015"],
3 | "plugins": ["syntax-dynamic-import"]
4 | }
5 |
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/example/hello.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | static void hello()
5 | {
6 | std::cout << "Hello World" << std::endl;
7 | }
8 |
9 | EMSCRIPTEN_BINDINGS(hello) {
10 | emscripten::function("hello", &hello);
11 | };
12 |
--------------------------------------------------------------------------------
/example/docker-compose.yml:
--------------------------------------------------------------------------------
1 | hello_wasm:
2 | build: ../
3 | container_name: 'hello_wasm'
4 | command: tail -f /dev/null
5 | working_dir: /src/emscripten/example
6 | environment:
7 | COLUMNS: 1000
8 | LINES: 50
9 | volumes:
10 | - ".:/src/emscripten/example"
11 |
--------------------------------------------------------------------------------
/example/module_loader.js:
--------------------------------------------------------------------------------
1 | import WasmLoader from '../dist/wasm-loader';
2 |
3 | const loader = new WasmLoader();
4 | loader.load(
5 | () => import('./hello-wasm'),
6 | () => import('./hello')
7 | ).then(module => {
8 | console.log('loaded module', module);
9 | module.hello();
10 | });
11 |
--------------------------------------------------------------------------------
/example/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "lockfileVersion": 1,
3 | "dependencies": {
4 | "babel-plugin-syntax-dynamic-import": {
5 | "version": "6.18.0",
6 | "resolved": "http://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz",
7 | "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=",
8 | "dev": true
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/.eslintrc.yml:
--------------------------------------------------------------------------------
1 | env:
2 | browser: true
3 | es6: true
4 | extends: airbnb
5 | parserOptions:
6 | sourceType: module
7 | rules:
8 | indent:
9 | - error
10 | - 2
11 | linebreak-style:
12 | - error
13 | - unix
14 | quotes:
15 | - error
16 | - single
17 | semi:
18 | - error
19 | - always
20 | max-len:
21 | - 2
22 | - code: 150
23 | ignoreComments: true
24 | no-multi-spaces: 0
25 | no-param-reassign: 0
26 | no-underscore-dangle: 0
27 | no-return-assign: 0
28 | prefer-destructuring: 0
29 | key-spacing: 0
30 | arrow-body-style: 0
31 | import/no-cycle: 0
32 |
--------------------------------------------------------------------------------
/example/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: wasm
2 |
3 | wasm:
4 | wasm-builder --name hello -- \
5 | em++ -Oz -std=c++11 \
6 | --closure 1 \
7 | --memory-init-file 0 \
8 | -fno-exceptions \
9 | --llvm-lto 1 \
10 | -s ALLOW_MEMORY_GROWTH=1 \
11 | -s MODULARIZE=1 \
12 | -s NO_FILESYSTEM=1 \
13 | --pre-js /src/emscripten/pre.js \
14 | --bind \
15 | hello.cc -o hello.js
16 |
17 | .PHONY: asmjs
18 |
19 | asmjs:
20 | em++ -Oz -std=c++11 \
21 | --closure 1 \
22 | --memory-init-file 0 \
23 | -fno-exceptions \
24 | --llvm-lto 1 \
25 | -s MODULARIZE=1 \
26 | -s NO_FILESYSTEM=1 \
27 | -s WASM=0 \
28 | --bind \
29 | hello.cc -o hello.js
30 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "version": "",
4 | "description": "",
5 | "main": "",
6 | "scripts": {
7 | "build": "webpack",
8 | "server": "webpack-dev-server --host 0.0.0.0 --progress --port 5000"
9 | },
10 | "author": "",
11 | "dependencies": {},
12 | "devDependencies": {
13 | "babel-cli": "^6.26.0",
14 | "babel-eslint": "^8.1.2",
15 | "babel-loader": "^7.1.2",
16 | "babel-plugin-syntax-dynamic-import": "^6.18.0",
17 | "babel-polyfill": "^6.26.0",
18 | "babel-preset-es2015": "^6.24.1",
19 | "babelify": "^7.3.0",
20 | "browserify": "^14.5.0",
21 | "copy-webpack-plugin": "^4.5.2",
22 | "webpack": "^3.6.0",
23 | "webpack-dev-server": "^2.9.1"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 | const CopyWebpackPlugin = require('copy-webpack-plugin');
4 |
5 | module.exports = {
6 | devtool: 'source-map',
7 | entry: './src/loader.js',
8 | output: {
9 | publicPath: '/',
10 | path: path.resolve(__dirname, 'dist'),
11 | filename: 'wasm-loader.js',
12 | library: 'WasmLoader',
13 | libraryTarget: 'umd'
14 | },
15 | module: {
16 | rules: [
17 | {
18 | test: /\.js$/,
19 | exclude: /node_modules/,
20 | use: 'babel-loader'
21 | }
22 | ]
23 | },
24 |
25 | plugins: [
26 | new webpack.optimize.UglifyJsPlugin({
27 | sourceMap: false
28 | })
29 | ],
30 |
31 | devServer: {
32 | contentBase: 'dist',
33 | disableHostCheck: true,
34 | inline: true,
35 | historyApiFallback: true,
36 | compress: true
37 | }
38 | };
39 |
--------------------------------------------------------------------------------
/example/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 | const CopyWebpackPlugin = require('copy-webpack-plugin');
4 |
5 | module.exports = {
6 | devtool: 'source-map',
7 | entry: './module_loader.js',
8 | output: {
9 | publicPath: '/',
10 | path: path.resolve(__dirname, 'dist'),
11 | filename: 'module_loader.js'
12 | },
13 | module: {
14 | rules: [
15 | {
16 | test: /\.js$/,
17 | exclude: /node_modules/,
18 | use: 'babel-loader'
19 | }
20 | ]
21 | },
22 |
23 | plugins: [
24 | new CopyWebpackPlugin([
25 | { from: 'index.html' },
26 | { from: 'hello*.{wasm,js}' }
27 | ]),
28 | ],
29 |
30 | devServer: {
31 | contentBase: 'dist',
32 | disableHostCheck: true,
33 | inline: true,
34 | historyApiFallback: true,
35 | compress: true
36 | },
37 |
38 | node: {
39 | fs: 'empty'
40 | }
41 |
42 | };
43 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM trzeci/emscripten:sdk-tag-1.38.8-64bit
2 | LABEL maintainer "Knocknote"
3 |
4 | RUN apt-get -qq -y update && apt-get install -y --no-install-recommends \
5 | build-essential \
6 | openjdk-8-jre && apt-get clean && rm -rf /var/lib/apt/lists/*
7 |
8 | RUN wget https://dl.google.com/go/go1.11.linux-amd64.tar.gz && tar -xvf go1.11.linux-amd64.tar.gz && mv go /usr/local && rm go1.11.linux-amd64.tar.gz
9 | ENV GOROOT /usr/local/go
10 | ENV GOPATH /go
11 | ENV PATH $GOPATH/bin:$GOROOT/bin:$PATH
12 |
13 | RUN git clone --recursive https://github.com/WebAssembly/wabt /src/wabt
14 | RUN mkdir -p /src/wabt/build
15 |
16 | WORKDIR /src/wabt/build
17 | RUN cmake ../ && make && make install
18 |
19 | RUN mkdir -p /go/src/wasm-builder
20 | COPY ./wasm-builder.go /go/src/wasm-builder
21 | RUN go install wasm-builder
22 |
23 | RUN mkdir -p /src/emscripten
24 | COPY ./pre.js /src/emscripten
25 |
26 | WORKDIR /src/emscripten
27 |
28 | RUN rm -rf /src/wabt
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Knocknote, Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/pre.js:
--------------------------------------------------------------------------------
1 | Module['TOTAL_MEMORY'] = 65536;
2 | Module['TOTAL_STACK'] = 32768;
3 |
4 | Module['preInit'] = function() {
5 | ASMJS_PAGE_SIZE = 65536;
6 | MIN_TOTAL_MEMORY = 65536;
7 | };
8 |
9 | Module['instantiateWasm'] = function(imports, successCallback) {
10 | function downloadWasm(url) {
11 | return new Promise(function(resolve, reject) {
12 | var wasmXHR = new XMLHttpRequest();
13 | wasmXHR.open('GET', url, true);
14 | wasmXHR.responseType = 'arraybuffer';
15 | wasmXHR.onload = function() { resolve(wasmXHR.response); };
16 | wasmXHR.onerror = function() { reject('error ' + wasmXHR.status); };
17 | wasmXHR.send(null);
18 | });
19 | };
20 | return downloadWasm('/{{ .Name }}.wasm?v={{ .Version }}').
21 | then(function(wasmBinary) {
22 | return WebAssembly.instantiate(new Uint8Array(wasmBinary), imports).
23 | then(function(output) {
24 | successCallback(output.instance);
25 | imports.parent.then = null;
26 | return Promise.resolve(imports.parent);
27 | });
28 | }).catch(function(err) {
29 | removeRunDependency('wasm-instantiate');
30 | throw err;
31 | });
32 | };
33 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wasm-loader",
3 | "version": "0.0.1",
4 | "description": "simple loader for WebAssembly or asm.js",
5 | "main": "dist/wasm-loader.js",
6 | "scripts": {
7 | "build": "webpack",
8 | "server": "webpack-dev-server --host 0.0.0.0 --progress --port 5000",
9 | "lint": "eslint src",
10 | "doc": "jsdoc -c .jsdoc.json -R README.md"
11 | },
12 | "files": [
13 | "README.md",
14 | "LICENSE",
15 | "package.json",
16 | "dist/wasm-loader.js",
17 | "src"
18 | ],
19 | "repository": {
20 | "type": "git",
21 | "url": "https://github.com/knocknote/wasm"
22 | },
23 | "bugs": {
24 | "url": "https://github.com/knocknote/wasm/issues"
25 | },
26 | "homepage": "https://github.com/knocknote/wasm",
27 | "author": "",
28 | "license": "MIT",
29 | "dependencies": {
30 | "uupaa.useragent.js": "^1.0.2"
31 | },
32 | "devDependencies": {
33 | "babel-cli": "^6.26.0",
34 | "babel-eslint": "^8.1.2",
35 | "babel-loader": "^7.1.2",
36 | "babel-polyfill": "^6.26.0",
37 | "babel-preset-es2015": "^6.24.1",
38 | "babelify": "^7.3.0",
39 | "browserify": "^14.5.0",
40 | "copy-webpack-plugin": "^4.5.2",
41 | "eslint": "^4.14.0",
42 | "eslint-config-airbnb": "^17.0.0",
43 | "eslint-plugin-import": "^2.13.0",
44 | "eslint-plugin-jsx-a11y": "^6.1.1",
45 | "eslint-plugin-react": "^7.10.0",
46 | "jsdoc": "^3.5.5",
47 | "webpack": "^3.6.0",
48 | "webpack-dev-server": "^2.9.1"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/loader.js:
--------------------------------------------------------------------------------
1 | import WasmHelper from './helper';
2 |
3 | /**
4 | * @example
5 | * const loader = new WasmLoader();
6 | * loader.load(
7 | * () => import('./hello-wasm'),
8 | * () => import('./hello')
9 | * ).then(module => {
10 | * console.log('loaded module', module);
11 | * module.hello();
12 | * });
13 | * @class WasmLoader
14 | */
15 | export default class WasmLoader {
16 | constructor() {
17 | this.helper = new WasmHelper();
18 | }
19 |
20 | /**
21 | * Load call dynamic import statement for loading wasm module.
22 | * First, try to call dynamic import callback for **`wasm`** module.
23 | * But if your browser doesn't support to `WebAssembly`,
24 | * this try to call dynamic import callback for **`asm.js`** module.
25 | *
26 | * @memberof WasmLoader
27 | * @param {Function} - returns Promise by dynamic import for wasm
28 | * @param {Function} - returns Promise by dynamic import for asm.js
29 | * @return {Promise}
30 | */
31 | load(wasmImportCallback, asmjsImportCallback) {
32 | if (!this.helper.canUseWebAssembly()) {
33 | return this._loadFromAsmJS(asmjsImportCallback);
34 | }
35 |
36 | return this._loadFromWasm(wasmImportCallback, asmjsImportCallback);
37 | }
38 |
39 | static _loadFromAsmJS(asmjsImportCallback) {
40 | if (!asmjsImportCallback) {
41 | return Promise.reject(new Error('required callback for asm.js'));
42 | }
43 |
44 | return asmjsImportCallback().then((module) => {
45 | const loadedModule = module();
46 | loadedModule.then = null; // remove promise object
47 | return loadedModule;
48 | });
49 | }
50 |
51 | _loadFromWasm(wasmImportCallback, asmjsImportCallback) {
52 | if (!wasmImportCallback) {
53 | return Promise.reject(new Error('required callback for wasm'));
54 | }
55 |
56 | return wasmImportCallback().then(module => module().asm)
57 | .catch(() => {
58 | // cannot load wasm module. fallback to asm.js
59 | return this._loadFromAsmJS(asmjsImportCallback);
60 | });
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # wasm
2 | Supports building and loading optimized WebAssembly ( powered by emscripten toolchain )
3 |
4 | # Motivation
5 |
6 | ## 1. Supports our WebAssembly application as much as possible of all browsers
7 |
8 | We would like to use WebAssembly in our web application.
9 | But, not every browser supports WebAssembly.
10 | So, we must use it together with **not** WebAssembly module ( like `asm.js` ).
11 |
12 | Currently, some languages supports WebAssembly ( `Go`, `Rust`, `C`/`C++`, ... ).
13 | Go and Rust are famous and popular language. Also, they aggressively support WebAssembly. But, they not provide fallback plan from WebAssembly.
14 | Therefore, we cannot select them.
15 |
16 | On the other hand, `Emscripten` can compile `C`/`C++` to WebAssembly and pure javascript with `asm.js`. So we select it for WebAssembly because in this fact is very attractive for us.
17 |
18 | ## 2. Compactly use WebAssembly
19 |
20 | We know WebAssembly's binary size and memory usage is too large. But, we want to use it compactly.
21 |
22 | # Supports building optimized WebAssembly
23 |
24 | We provide `Dockerfile` and docker image (by DockerHub) for building WebAssembly.
25 | Image includes `wasm-builder` and it supports building optimized WebAssembly.
26 | If you doesn't use `wasm-builder`, compiled WebAssembly use **16MB** memory at least. But, if you compile with `wasm-builder`, it use only **64KB** !!!
27 |
28 | # Supports loading WebAssembly with fallback plan
29 |
30 | We provide `wasm-loader.js` for loading WebAssembly.
31 | `wasm-loader.js` provides `WasmLoader` class and it provides `load` method only.
32 | `load` method call dynamic import statement for loading wasm module.
33 | First, `load` method try to call dynamic import callback for **`wasm`** module. But if your browser doesn't support to `WebAssembly` , `load` to call dynamic import callback for **`asm.js`** module.
34 |
35 | ## Install
36 |
37 | ```
38 | npm install --save @knocknote/wasm-loader
39 | ```
40 |
41 | # Example
42 |
43 | See https://github.com/knocknote/wasm/blob/master/example/README.md
44 |
45 | # LICENSE
46 |
47 | MIT
--------------------------------------------------------------------------------
/src/helper.js:
--------------------------------------------------------------------------------
1 | let UserAgent;
2 | if (!global.GLOBAL) {
3 | require('uupaa.useragent.js/lib/WebModule.js');
4 | global.GLOBAL = global;
5 | }
6 | UserAgent = require('uupaa.useragent.js/lib/UserAgent');
7 |
8 | export default class WasmHelper {
9 | constructor() {
10 | this.ua = new UserAgent();
11 | }
12 |
13 | canUseWebAssembly() {
14 | return window.WebAssembly !== undefined
15 | && !this.isIgnoreBrowser()
16 | && WasmHelper.validateSafariWebAssemblyBug();
17 | }
18 |
19 | // FYI : https://github.com/brion/min-wasm-fail
20 | static validateSafariWebAssemblyBug() {
21 | const binary = new Uint8Array([
22 | 0, 97, 115, 109, 1, 0, 0, 0, 1, 6, 1, 96, 1,
23 | 127, 1, 127, 3, 2, 1, 0, 5, 3, 1, 0, 1, 7, 8,
24 | 1, 4, 116, 101, 115, 116, 0, 0, 10, 16, 1, 14,
25 | 0, 32, 0, 65, 1, 54, 2, 0, 32, 0, 40, 2, 0, 11,
26 | ]);
27 | const module = new WebAssembly.Module(binary);
28 | const instance = new WebAssembly.Instance(module, {});
29 | // test storing to and loading from a non-zero location via a parameter.
30 | // Safari on iOS 11.2.X returns 0 unexpectedly at non-zero locations
31 | return instance.exports.test(4) !== 0;
32 | }
33 |
34 | isIgnoreBrowser() {
35 | // Android Default Browser is not supported because it will be unexpected behaviour.
36 | if (this.isAndroidDefaultBrowser()) { return true; }
37 |
38 | // Google Chrome version less or 60 is not supported WebAssembly fully.
39 | if (this.ua.Chrome && this.browserMajorVersion() <= 60) {
40 | return true;
41 | }
42 | return false;
43 | }
44 |
45 | browserMajorVersion() {
46 | const semanticVersion = this.ua.BROWSER_VERSION.split('.');
47 | if (semanticVersion.length === 3) {
48 | return parseInt(semanticVersion[0], 10);
49 | }
50 | return 0;
51 | }
52 |
53 | isAndroidDefaultBrowser() {
54 | const ua = this.ua;
55 | if (!ua.Android) { return false; }
56 |
57 | return ((/Linux; U;/.test(ua) && !/Chrome/.test(ua))
58 | || (/Chrome/.test(ua) && /Version/.test(ua))
59 | || (/Chrome/.test(ua) && /SamsungBrowser/.test(ua)));
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # HOW TO WORK Example
2 |
3 | ## 1. Run Container by docker-compose
4 |
5 | ```
6 | # pwd => /path/to/wasm/example
7 | docker-compose up -d
8 | docker ps
9 |
10 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
11 | 40585001c1c2 example_hello_wasm "tail -f /dev/null" 1 minutes ago Up 1 minutes hello_wasm
12 | ```
13 |
14 | ## 2. Build hello.cc simply
15 |
16 | ```
17 | docker exec -it hello_wasm bash
18 | root@40585001c1c2:/src/emscripten/example# em++ -Oz -std=c++11 \
19 | --closure 1 \
20 | --memory-init-file 0 \
21 | -fno-exceptions \
22 | --llvm-lto 1 \
23 | -s ALLOW_MEMORY_GROWTH=1 \
24 | -s MODULARIZE=1 \
25 | -s NO_FILESYSTEM=1 \
26 | --bind \
27 | hello.cc -o hello.js
28 | ```
29 |
30 | output `hello.js` and `hello.wasm`
31 |
32 | Copy these files into your application.
33 | And load by `` . Also, can invoke `hello()` at the following code
34 |
35 | ```javascript
36 | var module = new Module();
37 | // waiting for loading of wasm file
38 | module.hello(); // Hello World
39 | ```
40 |
41 | This example works successfuly, **BUT** memory usage is **16MB** !!
42 | Also, not waiting for loading of wasm file ( This is emscripten's problem )
43 |
44 | ## 3. Build hello.cc with wasm-builder
45 |
46 | ```
47 | docker exec -it hello_wasm bash
48 | root@40585001c1c2:/src/emscripten/example# wasm-builder --name hello -- \
49 | em++ -Oz -std=c++11 \
50 | --closure 1 \
51 | --memory-init-file 0 \
52 | -fno-exceptions \
53 | --llvm-lto 1 \
54 | -s ALLOW_MEMORY_GROWTH=1 \
55 | -s MODULARIZE=1 \
56 | -s NO_FILESYSTEM=1 \
57 | --pre-js /src/emscripten/pre.js \
58 | --bind \
59 | hello.cc -o hello.js
60 | ```
61 |
62 | `wasm-builder` can optimize memory usage and support utility for loading wasm file.
63 | **This command must use with `--pre-js /src/emscripten/pre.js` .**
64 |
65 | output `hello-wasm.js` and `hello.wasm`
66 |
67 | Copy these files into your application.
68 | And load by ``. Also, invoke `hello()` at the following code.
69 |
70 | ```javascript
71 | Module().asm.then(function(module) { module.hello(); });
72 | ```
73 |
74 | This example works successfuly, also memory usage is **64KB** !! Congratulations !!
75 |
76 | ## 4. Use wasm-loader.js
77 |
78 | ### 4.1 Install dependencies
79 |
80 | ( current working directory is /path/to/wasm/example )
81 |
82 | ```
83 | $ npm install
84 | ```
85 |
86 | or
87 |
88 | ```
89 | yarn install
90 | ```
91 |
92 | ### 4.2 Start Server
93 |
94 | ```
95 | yarn server
96 | ```
97 |
98 | the above command executes `webpack-dev-server --host 0.0.0.0 --progress --port 5000`
99 |
100 | ### 4.3 Access Browser ( http://localhost:5000 )
101 |
102 | `webpack-dev-server` serve `index.html` .
103 |
104 | index.html
105 | ```html
106 |
107 |
108 |
109 |
110 |
111 |
112 | ```
113 |
114 | `module_loader.js` is the following.
115 |
116 | ```javascript
117 | import WasmLoader from '../dist/wasm-loader';
118 |
119 | const loader = new WasmLoader();
120 | loader.load(
121 | () => import('./hello-wasm'),
122 | () => import('./hello')
123 | ).then(module => {
124 | console.log('loaded module', module);
125 | module.hello();
126 | });
127 | ```
128 |
129 | This example use `wasm-loader.js` .
130 | `wasm-loader.js` provides `WasmLoader` class and it provides `load` method only.
131 | `load` method call dynamic import statement for loading wasm module.
132 | First, `load` method try to call dynamic import callback for **`wasm`** module. But if your browser doesn't support to `WebAssembly` , `load` to call dynamic import callback for **`asm.js`** module.
133 |
--------------------------------------------------------------------------------
/wasm-builder.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bufio"
5 | "bytes"
6 | "compress/gzip"
7 | "crypto/md5"
8 | "errors"
9 | "flag"
10 | "fmt"
11 | "io/ioutil"
12 | "log"
13 | "os"
14 | "os/exec"
15 | "path/filepath"
16 | "text/template"
17 | )
18 |
19 | var (
20 | fileName = flag.String("name", "module", "output filename. if '--name=output' specified, generated output-wasm.js and output.wasm")
21 | compressed = flag.Bool("compressed", false, "use gzip compress for wasm file")
22 | src = flag.String("src", ".", "generate src directory")
23 | dst = flag.String("dst", ".", "generate target directory")
24 | )
25 |
26 | func validateOption() error {
27 | if fileName == nil {
28 | flag.PrintDefaults()
29 | return errors.New("required --name option")
30 | }
31 | return nil
32 | }
33 |
34 | type wasmBuilder struct {
35 | fileName string
36 | src string
37 | dst string
38 | cwd string
39 | compressed bool
40 | }
41 |
42 | func newWasmBuilder(fileName, src, dst string, compressed bool) (*wasmBuilder, error) {
43 | cwd, err := os.Getwd()
44 | if err != nil {
45 | return nil, err
46 | }
47 | return &wasmBuilder{
48 | fileName: fileName,
49 | src: src,
50 | dst: dst,
51 | cwd: cwd,
52 | compressed: compressed,
53 | }, nil
54 | }
55 |
56 | func (b *wasmBuilder) command(args ...string) *exec.Cmd {
57 | cmd := exec.Command(args[0], args[1:]...)
58 | cmd.Dir = b.cwd
59 | return cmd
60 | }
61 |
62 | func (b *wasmBuilder) runBuildCommand(args []string) error {
63 | log.Println("- run build command")
64 | if len(args) == 0 {
65 | return errors.New("required build command")
66 | }
67 | cmd := b.command(args...)
68 | stdout, err := cmd.StdoutPipe()
69 | if err != nil {
70 | return err
71 | }
72 | cmd.Start()
73 |
74 | scanner := bufio.NewScanner(stdout)
75 | for scanner.Scan() {
76 | log.Println(scanner.Text())
77 | }
78 |
79 | cmd.Wait()
80 | return nil
81 | }
82 |
83 | func (b *wasmBuilder) wasmFilePath() string {
84 | return filepath.Join(b.cwd, b.src, b.fileName+".wasm")
85 | }
86 |
87 | func (b *wasmBuilder) wastFilePath() string {
88 | return filepath.Join(b.cwd, b.src, b.fileName+".wast")
89 | }
90 |
91 | func (b *wasmBuilder) runWasm2Wat() error {
92 | log.Println("- run wasm2wat")
93 | return b.command("wasm2wat", b.wasmFilePath(), "-o", b.wastFilePath()).Run()
94 | }
95 |
96 | func (b *wasmBuilder) runWat2Wasm() error {
97 | log.Println("- run wat2wasm")
98 | return b.command("wat2wasm", b.wastFilePath(), "-o", b.wasmFilePath()).Run()
99 | }
100 |
101 | func (b *wasmBuilder) replaceWasmInitialPageNumber() error {
102 | log.Println("- replace wasm initial page number")
103 | return b.command(
104 | "sed",
105 | "-i",
106 | "-e",
107 | `s/(import "env" "memory" (memory (;0;) 256))/(import "env" "memory" (memory (;0;) 1))/`,
108 | b.wastFilePath(),
109 | ).Run()
110 | }
111 |
112 | func (b *wasmBuilder) wasmVersion() (string, error) {
113 | wasmFilePath := filepath.Join(b.cwd, b.src, b.fileName+".wasm")
114 | bytes, err := ioutil.ReadFile(wasmFilePath)
115 | if err != nil {
116 | return "", err
117 | }
118 | return fmt.Sprintf("%x", md5.Sum(bytes)), nil
119 | }
120 |
121 | func (b *wasmBuilder) compressWasmFile() error {
122 | if !b.compressed {
123 | return nil
124 | }
125 |
126 | log.Println("- compress wasm")
127 | file, err := ioutil.ReadFile(b.wasmFilePath())
128 | if err != nil {
129 | return err
130 | }
131 | var buf bytes.Buffer
132 | zw := gzip.NewWriter(&buf)
133 | defer zw.Close()
134 | if _, err := zw.Write(file); err != nil {
135 | return err
136 | }
137 | if err := zw.Flush(); err != nil {
138 | return err
139 | }
140 |
141 | return ioutil.WriteFile(b.wasmFilePath(), buf.Bytes(), 0644)
142 | }
143 |
144 | func (b *wasmBuilder) putWasmFileToDstDirectory() error {
145 | log.Println("- move wasm file to dst directory")
146 | dstWasmPath := filepath.Join(b.cwd, b.dst, b.fileName+".wasm")
147 | return os.Rename(b.wasmFilePath(), dstWasmPath)
148 | }
149 |
150 | func (b *wasmBuilder) putWasmHelperFileToDstDirectory() error {
151 | log.Println("- output js file to dst directory")
152 | jsPath := filepath.Join(b.cwd, b.src, b.fileName+".js")
153 | jsContent, err := ioutil.ReadFile(jsPath)
154 | if err != nil {
155 | return err
156 | }
157 | tmpl, err := template.New("").Parse(string(jsContent))
158 | if err != nil {
159 | return err
160 | }
161 | version, err := b.wasmVersion()
162 | if err != nil {
163 | return err
164 | }
165 | param := struct {
166 | Version string
167 | Name string
168 | }{
169 | Version: version,
170 | Name: *fileName,
171 | }
172 | var buf bytes.Buffer
173 | if err := tmpl.Execute(&buf, ¶m); err != nil {
174 | return err
175 | }
176 |
177 | dstJsPath := filepath.Join(b.cwd, b.dst, b.fileName+"-wasm.js")
178 | return ioutil.WriteFile(dstJsPath, buf.Bytes(), 0644)
179 | }
180 |
181 | func (b *wasmBuilder) run(args []string) error {
182 | if err := b.runBuildCommand(args); err != nil {
183 | return err
184 | }
185 | if err := b.runWasm2Wat(); err != nil {
186 | return err
187 | }
188 | if err := b.replaceWasmInitialPageNumber(); err != nil {
189 | return err
190 | }
191 | if err := b.runWat2Wasm(); err != nil {
192 | return err
193 | }
194 | if err := b.compressWasmFile(); err != nil {
195 | return err
196 | }
197 | if err := b.putWasmFileToDstDirectory(); err != nil {
198 | return err
199 | }
200 | if err := b.putWasmHelperFileToDstDirectory(); err != nil {
201 | return err
202 | }
203 | return nil
204 | }
205 |
206 | func _main(args []string) error {
207 | if err := validateOption(); err != nil {
208 | return err
209 | }
210 | builder, err := newWasmBuilder(*fileName, *src, *dst, *compressed)
211 | if err != nil {
212 | return err
213 | }
214 | return builder.run(args)
215 | }
216 |
217 | func main() {
218 | flag.Parse()
219 | if err := _main(flag.Args()); err != nil {
220 | log.Printf("error: %+v\n", err)
221 | os.Exit(1)
222 | }
223 | os.Exit(0)
224 | }
225 |
--------------------------------------------------------------------------------
/dist/wasm-loader.js:
--------------------------------------------------------------------------------
1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.WasmLoader=t():e.WasmLoader=t()}("undefined"!=typeof self?self:this,function(){return function(e){function t(n){if(r[n])return r[n].exports;var i=r[n]={i:n,l:!1,exports:{}};return e[n].call(i.exports,i,i.exports,t),i.l=!0,i.exports}var r={};return t.m=e,t.c=r,t.d=function(e,r,n){t.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:n})},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="/",t(t.s=1)}([function(e,t){var r;r=function(){return this}();try{r=r||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(r=window)}e.exports=r},function(e,t,r){"use strict";function n(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t){for(var r=0;r=10&&/^iPad Pro/.test(b),g=/native/.test(Object.keys+""),C=/native/.test(String.raw+"");return{OS:h,OS_VERSION:P,PC:!v,MOBILE:v,BROWSER:E,BASE_BROWSER:O,BROWSER_VERSION:p,USER_AGENT:d,LANGUAGE:A,WEB_VIEW:y,DEVICE:b,TOUCH_3D:I,CARRIER:f,FEATURE_PHONE:!!f,ES5:g,ES6:C,ES2015:C,iOS:W,Mac:"Mac"===h,macOS:"Mac"===h,Android:w,Windows:"Windows"===h,IE:"IE"===E,Edge:"Edge"===E,Firefox:"Firefox"===E,Chrome:"Chrome"===E,Safari:"Safari"===E,Silk:"Silk"===E,AOSP:"AOSP"===E,WebKit:"WebKit"===O,Chromium:"Chromium"===O,iPod:W&&/iPod/.test(d),iPad:W&&/iPad/.test(d),iPhone:W&&/iPhone/.test(d),Kindle:"Silk"===E,has:function(e){return void 0!==this[e]},get:function(e){return this[e]}}}function n(e){var t=e.language||"en";return e.languages&&Array.isArray(e.languages)&&(t=e.languages[0]||t),t.split("-")[0]}function i(e){switch(!0){case/Android/.test(e):return"Android";case/iPhone|iPad|iPod/.test(e):return"iOS";case/Windows/.test(e):return"Windows";case/Mac OS X/.test(e):return"Mac";case/CrOS/.test(e):return"Chrome OS";case/Firefox/.test(e):return"Firefox OS"}return""}function o(e,t){switch(t){case"Android":return f(e,"Android");case"iOS":return f(e,/OS /);case"Windows":return f(e,/Phone/.test(e)?/Windows Phone (?:OS )?/:/Windows NT/);case"Mac":return f(e,/Mac OS X /)}return"0.0.0"}function s(e){var t=/Android/.test(e);switch(!0){case/CriOS/.test(e):return"Chrome for iOS";case/Edge/.test(e):return"Edge";case t&&/Silk\//.test(e):return"Silk";case/Chrome/.test(e):return"Chrome";case/Firefox/.test(e):return"Firefox";case t:return"AOSP";case/MSIE|Trident/.test(e):return"IE";case/Safari\//.test(e):return"Safari";case/AppleWebKit/.test(e):return"WebKit"}return""}function a(e,t){switch(t){case"Chrome for iOS":return f(e,"CriOS/");case"Edge":return f(e,"Edge/");case"Chrome":return f(e,"Chrome/");case"Firefox":return f(e,"Firefox/");case"Silk":return f(e,"Silk/");case"AOSP":return f(e,"Version/");case"IE":return/IEMobile/.test(e)?f(e,"IEMobile/"):/MSIE/.test(e)?f(e,"MSIE "):f(e,"rv:");case"Safari":return f(e,"Version/");case"WebKit":return f(e,"WebKit/")}return"0.0.0"}function u(e,t){return"Silk"===e&&t>=4.4?"Chromium":O[e]||""}function c(t,r,n,i,o){var s=e.screen||{},a=s.width||0,u=s.height||0,c=i.DISPLAY_DPR||e.devicePixelRatio||1,f=i.DISPLAY_LONG||Math.max(a,u),h=i.DISPLAY_SHORT||Math.min(a,u),S=c>=2,P=Math.max(f,h);switch(r){case"Android":return l(t,S);case"iOS":return d(t,S,P,n)}return o?p(t,o):""}function l(e,t){if(/Firefox/.test(e))return"";try{var r=e.split("Build/")[0].split(";").slice(-1).join().trim().replace(/^SonyEricsson/,"").replace(/^Sony/,"").replace(/ 4G$/,"");return"Nexus 7"===r?t?"Nexus 7 2nd":"Nexus 7":r}catch(e){}return""}function d(t,r,n,i){var o=e.WebModule.WebGLDetector||{};"detect"in o&&o.detect();var s=o.WEBGL_VERSION||"",a=o.WEBGL_RENDERER||"",u=/543/.test(s),c=/554/.test(s),l=/A7 /.test(s),d=/A8X/.test(s),f=/A8 /.test(s),h=/A9X/.test(s),S=/A9 /.test(s),P=/Metal/.test(s),E=/Software/.test(a);return/iPhone/.test(t)?E?"iPhone Simulator":r?n<=480?u||i>=8?"iPhone 4s":"iPhone 4":n<=568?P?"iPhone 7":S?"iPhone SE":f?"iPhone 6":l?"iPhone 5s":u?"iPhone 5":"iPhone x":n<=667?P?"iPhone 7":S?"iPhone 6s":f?"iPhone 6":"iPhone x":n<=736?P?"iPhone 7 Plus":S?"iPhone 6s Plus":f?"iPhone 6 Plus":"iPhone x":"iPhone x":"iPhone 3GS":/iPad/.test(t)?E?"iPad Simulator":r?u?"iPad 3":c?"iPad 4":l?"iPad mini 2":d?"iPad Air 2":f?"iPad mini 4":h?n<=1024?"iPad Pro 9.7":"iPad Pro":S?"iPad 5":"iPad x":"iPad 2":/iPod/.test(t)?E?"iPod Simulator":n<=480?r?"iPod touch 4":"iPod touch 3":f?"iPod touch 6":"iPod touch 5":"iPhone x"}function f(e,t){try{return h(e.split(t)[1].trim().split(/[^\w\.]/)[0])}catch(e){}return"0.0.0"}function h(e){var t=e.split(/[\._]/);return(parseInt(t[0],10)||0)+"."+(parseInt(t[1],10)||0)+"."+(parseInt(t[2],10)||0)}function S(e,t,r,n,i){switch(t+r){case"iOSSafari":return!1;case"iOSWebKit":return P(i);case"AndroidAOSP":return!1;case"AndroidChrome":return parseFloat(n)>=42?/; wv/.test(e):!!/\d{2}\.0\.0/.test(n)||E(i)}return!1}function P(t){var r=e.document||{};return"WEB_VIEW"in t?t.WEB_VIEW:!("fullscreenEnabled"in r||"webkitFullscreenEnabled"in r)}function E(t){return"WEB_VIEW"in t?t.WEB_VIEW:!("requestFileSystem"in e||"webkitRequestFileSystem"in e)}function m(e){switch(!0){case/DoCoMo/.test(e):return"DOCOMO";case/KDDI/.test(e):return"KDDI";case/SoftBank|Vodafone/.test(e):return"SOFTBANK"}return""}function p(e,t){switch(t){case"DOCOMO":return e.split("DoCoMo/2.0 ")[1].split("(")[0];case"KDDI":return e.split("KDDI-")[1].split(" ")[0];case"SOFTBANK":return/^Vodafone/.test(e)?e.split("/")[2].slice(1):e.split("/")[2]}return""}var O={Chrome:"Chromium",Firefox:"Firefox",IE:"IE",Edge:"Edge",AOSP:"WebKit",Safari:"WebKit",WebKit:"WebKit","Chrome for iOS":"WebKit",Silk:"WebKit"};return t.DISABLE_CACHE=!1,t.cache=null,t.parse=r,t.repository="https://github.com/uupaa/UserAgent.js",t})}])});
--------------------------------------------------------------------------------
/example/hello.js:
--------------------------------------------------------------------------------
1 | var Module = function(Module) {
2 | Module = Module || {};
3 |
4 | var d;d||(d=typeof Module !== 'undefined' ? Module : {});d.TOTAL_MEMORY=65536;d.TOTAL_STACK=32768;d.preInit=function(){aa=ba=65536};
5 | d.instantiateWasm=function(a,b){return function(a){return new Promise(function(b,c){var e=new XMLHttpRequest;e.open("GET",a,!0);e.responseType="arraybuffer";e.onload=function(){b(e.response)};e.onerror=function(){c("error "+e.status)};e.send(null)})}("/{{ .Name }}.wasm?v={{ .Version }}").then(function(c){return WebAssembly.instantiate(new Uint8Array(c),a).then(function(c){b(c.instance);a.parent.then=null;return Promise.resolve(a.parent)})}).catch(function(a){ca();throw a;})};var n={},r;
6 | for(r in d)d.hasOwnProperty(r)&&(n[r]=d[r]);d.arguments=[];d.thisProgram="./this.program";d.quit=function(a,b){throw b;};d.preRun=[];d.postRun=[];var t=!1,u=!1,da=!1,ea=!1;t="object"===typeof window;u="function"===typeof importScripts;da="object"===typeof process&&"function"===typeof require&&!t&&!u;ea=!t&&!da&&!u;
7 | if(da){var fa,ha;d.read=function(a,b){fa||(fa=require("fs"));ha||(ha=require("path"));a=ha.normalize(a);a=fa.readFileSync(a);return b?a:a.toString()};d.readBinary=function(a){a=d.read(a,!0);a.buffer||(a=new Uint8Array(a));assert(a.buffer);return a};1>0];c|=e;if(0==e&&!b)break;g++;if(b&&g==b)break}b||(b=g);e="";if(128>c){for(;0e?c+=String.fromCharCode(e):(e-=
13 | 65536,c+=String.fromCharCode(55296|e>>10,56320|e&1023))}}else c+=String.fromCharCode(e)}}
14 | function pa(a,b,c,e){if(0=f&&(f=65536+((f&1023)<<10)|a.charCodeAt(++g)&1023);if(127>=f){if(c>=e)break;b[c++]=f}else{if(2047>=f){if(c+1>=e)break;b[c++]=192|f>>6}else{if(65535>=f){if(c+2>=e)break;b[c++]=224|f>>12}else{if(2097151>=f){if(c+3>=e)break;b[c++]=240|f>>18}else{if(67108863>=f){if(c+4>=e)break;b[c++]=248|f>>24}else{if(c+5>=e)break;b[c++]=252|f>>30;b[c++]=128|f>>24&63}b[c++]=128|f>>18&63}b[c++]=128|f>>12&63}b[c++]=
15 | 128|f>>6&63}b[c++]=128|f&63}}b[c]=0}}function qa(a){for(var b=0,c=0;c=e&&(e=65536+((e&1023)<<10)|a.charCodeAt(++c)&1023);127>=e?++b:b=2047>=e?b+2:65535>=e?b+3:2097151>=e?b+4:67108863>=e?b+5:b+6}return b}"undefined"!==typeof TextDecoder&&new TextDecoder("utf-16le");var ba=16777216,aa=16777216;function ra(a,b){0>2]=a);return a}var K=0;function M(){K+=4;return z[K-4>>2]}var N={};
27 | function O(a,b){K=b;try{var c=M(),e=M(),g=M();a=0;O.b||(O.b=[null,[],[]],O.u=function(a,b){var c=O.b[a];assert(c);0===b||10===b?((1===a?ia:v)(na(c,0)),c.length=0):c.push(b)});for(b=0;b>2],h=z[e+(8*b+4)>>2],l=0;l=b?"_"+a:a}function Ua(a,b){a=Ta(a);return(new Function("body","return function "+a+'() {\n "use strict"; return body.apply(this, arguments);\n};\n'))(b)}
29 | function Va(a){var b=Error,c=Ua(a,function(b){this.name=a;this.message=b;b=Error(b).stack;void 0!==b&&(this.stack=this.toString()+"\n"+b.replace(/^Error(:[^\n]*)?\n/,""))});c.prototype=Object.create(b.prototype);c.prototype.constructor=c;c.prototype.toString=function(){return void 0===this.message?this.name:this.name+": "+this.message};return c}var Wa=void 0;function U(a){throw new Wa(a);}var Ya=void 0;
30 | function Za(a,b){var c=[];function e(a){a=b(a);if(a.length!==c.length)throw new Ya("Mismatched type converter count");for(var e=0;e>2])}function cb(a){if(null===a)return"null";var b=typeof a;return"object"===b||"array"===b||"function"===b?a.toString():""+a}
33 | function db(a,b){switch(b){case 2:return function(a){return this.fromWireType(ua[a>>2])};case 3:return function(a){return this.fromWireType(va[a>>3])};default:throw new TypeError("Unknown float type: "+a);}}function eb(a){var b=Function;if(!(b instanceof Function))throw new TypeError("new_ called with constructor type "+typeof b+" which is not a function");var c=Ua(b.name||"unknownFunctionName",function(){});c.prototype=b.prototype;c=new c;a=b.apply(c,a);return a instanceof Object?a:c}
34 | function gb(a){for(;a.length;){var b=a.pop();a.pop()(b)}}function hb(a,b){var c=d;if(void 0===c[a].c){var e=c[a];c[a]=function(){c[a].c.hasOwnProperty(arguments.length)||U("Function '"+b+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+c[a].c+")!");return c[a].c[arguments.length].apply(this,arguments)};c[a].c=[];c[a].c[e.A]=e}}
35 | function ib(a,b,c){d.hasOwnProperty(a)?((void 0===c||void 0!==d[a].c&&void 0!==d[a].c[c])&&U("Cannot register public name '"+a+"' twice"),hb(a,a),d.hasOwnProperty(c)&&U("Cannot register multiple overloads of a function with the same number of arguments ("+c+")!"),d[a].c[c]=b):(d[a]=b,void 0!==c&&(d[a].X=c))}function jb(a,b){for(var c=[],e=0;e>2)+e]);return c}
36 | function kb(a,b){a=Q(a);if(void 0!==d["FUNCTION_TABLE_"+a])var c=d["FUNCTION_TABLE_"+a][b];else if("undefined"!==typeof FUNCTION_TABLE)c=FUNCTION_TABLE[b];else{c=d.asm["dynCall_"+a];void 0===c&&(c=d.asm["dynCall_"+a.replace(/f/g,"d")],void 0===c&&U("No dynCall invoker for signature: "+a));for(var e=[],g=1;g>1]}:function(a){return ta[a>>1]};case 2:return c?function(a){return z[a>>2]}:function(a){return A[a>>2]};default:throw new TypeError("Unknown integer type: "+a);}}var qb={};function Y(a){if(0===a)return 0;a=ma(a);if(!qb.hasOwnProperty(a))return 0;Y.b&&X(Y.b);a=qb[a];var b=qa(a)+1,c=rb(b);c&&pa(a,y,c,b);Y.b=c;return Y.b}
39 | function Z(){Z.b||(Z.b=[]);Z.b.push(sb());return Z.b.length-1}function tb(a){return 0===a%4&&(0!==a%100||0===a%400)}function ub(a,b){for(var c=0,e=0;e<=b;c+=a[e++]);return c}var vb=[31,29,31,30,31,30,31,31,30,31,30,31],wb=[31,28,31,30,31,30,31,31,30,31,30,31];
40 | function xb(a,b){for(a=new Date(a.getTime());0e-a.getDate())b-=e-a.getDate()+1,a.setDate(1),11>c?a.setMonth(c+1):(a.setMonth(0),a.setFullYear(a.getFullYear()+1));else{a.setDate(a.getDate()+b);break}}return a}
41 | function yb(a,b,c,e){function g(a,b,c){for(a="number"===typeof a?a.toString():a||"";a.lengtha?-1:0=h(l(new Date(a.getFullYear(),0,4)),a)?0>=h(b,a)?a.getFullYear()+1:a.getFullYear():a.getFullYear()-1}var p=z[e+40>>2];e={K:z[e>>2],J:z[e+4>>2],l:z[e+8>>2],h:z[e+12>>2],g:z[e+16>>2],a:z[e+20>>2],w:z[e+24>>2],m:z[e+28>>2],Y:z[e+32>>2],I:z[e+36>>2],L:p?ma(p):""};
43 | c=ma(c);p={"%c":"%a %b %d %H:%M:%S %Y","%D":"%m/%d/%y","%F":"%Y-%m-%d","%h":"%b","%r":"%I:%M:%S %p","%R":"%H:%M","%T":"%H:%M:%S","%x":"%m/%d/%y","%X":"%H:%M:%S"};for(var m in p)c=c.replace(new RegExp(m,"g"),p[m]);var I="Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),P="January February March April May June July August September October November December".split(" ");p={"%a":function(a){return I[a.w].substring(0,3)},"%A":function(a){return I[a.w]},"%b":function(a){return P[a.g].substring(0,
44 | 3)},"%B":function(a){return P[a.g]},"%C":function(a){return f((a.a+1900)/100|0,2)},"%d":function(a){return f(a.h,2)},"%e":function(a){return g(a.h,2," ")},"%g":function(a){return q(a).toString().substring(2)},"%G":function(a){return q(a)},"%H":function(a){return f(a.l,2)},"%I":function(a){a=a.l;0==a?a=12:12a.l?"AM":"PM"},"%S":function(a){return f(a.K,2)},"%t":function(){return"\t"},"%u":function(a){return(new Date(a.a+1900,a.g+1,a.h,0,0,0,0)).getDay()||7},"%U":function(a){var b=new Date(a.a+1900,0,1),c=0===b.getDay()?b:xb(b,7-b.getDay());a=new Date(a.a+1900,a.g,a.h);return 0>h(c,a)?f(Math.ceil((31-c.getDate()+(ub(tb(a.getFullYear())?vb:wb,a.getMonth()-1)-31)+a.getDate())/7),2):0===h(c,b)?"01":"00"},"%V":function(a){var b=l(new Date(a.a+1900,0,4)),c=l(new Date(a.a+1901,0,4)),e=xb(new Date(a.a+
46 | 1900,0,1),a.m);return 0>h(e,b)?"53":0>=h(c,e)?"01":f(Math.ceil((b.getFullYear()h(c,a)?f(Math.ceil((31-c.getDate()+(ub(tb(a.getFullYear())?vb:wb,a.getMonth()-1)-31)+a.getDate())/7),2):0===h(c,b)?"01":"00"},"%y":function(a){return(a.a+1900).toString().substring(2)},
47 | "%Y":function(a){return a.a+1900},"%z":function(a){a=a.I;var b=0<=a;a=Math.abs(a)/60;return(b?"+":"-")+String("0000"+(a/60*100+a%60)).slice(-4)},"%Z":function(a){return a.L},"%%":function(){return"%"}};for(m in p)0<=c.indexOf(m)&&(c=c.replace(new RegExp(m,"g"),p[m](e)));m=zb(c);if(m.length>b)return 0;y.set(m,a);return m.length-1}for(var Ab=Array(256),Bb=0;256>Bb;++Bb)Ab[Bb]=String.fromCharCode(Bb);Ra=Ab;Wa=d.BindingError=Va("BindingError");Ya=d.InternalError=Va("InternalError");
48 | d.count_emval_handles=function(){for(var a=0,b=5;b>2]=Aa;function zb(a){var b=Array(qa(a)+1);pa(a,b,0,b.length);return b}d.wasmTableSize=479;d.wasmMaxTableSize=479;d.B={};
49 | d.C={abort:w,enlargeMemory:function(){var a=d.usingWasm?65536:ba,b=2147483648-a;if(z[D>>2]>b)return!1;var c=F;for(F=Math.max(F,aa);F>2];)536870912>=F?F=ra(2*F,a):F=Math.min(ra((3*F+2147483648)/4,a),b);a=d.reallocBuffer(F);if(!a||a.byteLength!=F)return F=c,!1;d.buffer=buffer=a;wa();return!0},getTotalMemory:function(){return F},abortOnCannotGrowMemory:function(){w("Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value "+F+", (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 ")},
50 | ___cxa_uncaught_exception:function(){return!!Db.u},___lock:function(){},___map_file:function(){Pa(1);return-1},___setErrNo:Pa,___syscall140:function(a,b){K=b;try{var c=N.v();M();var e=M(),g=M(),f=M();FS.U(c,e,f);z[g>>2]=c.position;c.D&&0===e&&0===f&&(c.D=null);return 0}catch(h){return"undefined"!==typeof FS&&h instanceof FS.i||w(h),-h.j}},___syscall145:function(a,b){K=b;try{var c=N.v(),e=M(),g=M();return N.P(c,e,g)}catch(f){return"undefined"!==typeof FS&&f instanceof FS.i||w(f),-f.j}},___syscall146:O,
51 | ___syscall54:function(a,b){K=b;return 0},___syscall6:function(a,b){K=b;try{var c=N.v();FS.close(c);return 0}catch(e){return"undefined"!==typeof FS&&e instanceof FS.i||w(e),-e.j}},___syscall91:function(a,b){K=b;try{var c=M(),e=M(),g=N.G[c];if(!g)return 0;if(e===g.T){var f=FS.R(g.fd);N.O(c,f,e,g.flags);FS.W(f);N.G[c]=null;g.N&&X(g.V)}return 0}catch(h){return"undefined"!==typeof FS&&h instanceof FS.i||w(h),-h.j}},___unlock:function(){},__embind_register_bool:function(a,b,c,e,g){var f=Qa(c);b=Q(b);V(a,
52 | {name:b,fromWireType:function(a){return!!a},toWireType:function(a,b){return b?e:g},argPackAdvance:8,readValueFromPointer:function(a){if(1===c)var e=y;else if(2===c)e=sa;else if(4===c)e=z;else throw new TypeError("Unknown boolean type size: "+b);return this.fromWireType(e[a>>f])},f:null})},__embind_register_emval:function(a,b){b=Q(b);V(a,{name:b,fromWireType:function(a){var b=W[a].value;4l&&U("argTypes array size mismatch! Must at least get return value and 'this' types!");for(var P=null!==e[1]&&!1,E=!1,k=1;k>>l}}var q=-1!=b.indexOf("unsigned");V(a,{name:b,fromWireType:f,toWireType:function(a,c){if("number"!==typeof c&&"boolean"!==typeof c)throw new TypeError('Cannot convert "'+cb(c)+'" to '+this.name);if(cg)throw new TypeError('Passing a number "'+cb(c)+'" from JS side to C/C++ side to an argument of type "'+b+'", which is outside the valid range ['+e+", "+g+"]!");return q?c>>>0:c|0},argPackAdvance:8,readValueFromPointer:pb(b,
58 | h,0!==e),f:null})},__embind_register_memory_view:function(a,b,c){function e(a){a>>=2;var b=A;return new g(b.buffer,b[a+1],b[a])}var g=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array][b];c=Q(c);V(a,{name:c,fromWireType:e,argPackAdvance:8,readValueFromPointer:e},{F:!0})},__embind_register_std_string:function(a,b){b=Q(b);V(a,{name:b,fromWireType:function(a){for(var b=A[a>>2],c=Array(b),f=0;f>2]=l;for(var p=0;p>2],f=Array(c),p=a+4>>g,m=0;m>2]=h;for(var m=p+4>>g,I=0;I>0];c|=e;if(0==e&&!b)break;g++;if(b&&g==b)break}b||(b=g);e="";if(128>c){for(;0e?c+=String.fromCharCode(e):(e-=
13 | 65536,c+=String.fromCharCode(55296|e>>10,56320|e&1023))}}else c+=String.fromCharCode(e)}}
14 | function pa(a,b,c,e){if(0=f&&(f=65536+((f&1023)<<10)|a.charCodeAt(++g)&1023);if(127>=f){if(c>=e)break;b[c++]=f}else{if(2047>=f){if(c+1>=e)break;b[c++]=192|f>>6}else{if(65535>=f){if(c+2>=e)break;b[c++]=224|f>>12}else{if(2097151>=f){if(c+3>=e)break;b[c++]=240|f>>18}else{if(67108863>=f){if(c+4>=e)break;b[c++]=248|f>>24}else{if(c+5>=e)break;b[c++]=252|f>>30;b[c++]=128|f>>24&63}b[c++]=128|f>>18&63}b[c++]=128|f>>12&63}b[c++]=
15 | 128|f>>6&63}b[c++]=128|f&63}}b[c]=0}}function qa(a){for(var b=0,c=0;c=e&&(e=65536+((e&1023)<<10)|a.charCodeAt(++c)&1023);127>=e?++b:b=2047>=e?b+2:65535>=e?b+3:2097151>=e?b+4:67108863>=e?b+5:b+6}return b}"undefined"!==typeof TextDecoder&&new TextDecoder("utf-16le");var ba=16777216,aa=16777216;function ra(a,b){0>2]=a);return a}var K=0;function M(){K+=4;return z[K-4>>2]}var N={};
27 | function O(a,b){K=b;try{var c=M(),e=M(),g=M();a=0;O.b||(O.b=[null,[],[]],O.u=function(a,b){var c=O.b[a];assert(c);0===b||10===b?((1===a?ia:v)(na(c,0)),c.length=0):c.push(b)});for(b=0;b>2],h=z[e+(8*b+4)>>2],l=0;l=b?"_"+a:a}function Ua(a,b){a=Ta(a);return(new Function("body","return function "+a+'() {\n "use strict"; return body.apply(this, arguments);\n};\n'))(b)}
29 | function Va(a){var b=Error,c=Ua(a,function(b){this.name=a;this.message=b;b=Error(b).stack;void 0!==b&&(this.stack=this.toString()+"\n"+b.replace(/^Error(:[^\n]*)?\n/,""))});c.prototype=Object.create(b.prototype);c.prototype.constructor=c;c.prototype.toString=function(){return void 0===this.message?this.name:this.name+": "+this.message};return c}var Wa=void 0;function U(a){throw new Wa(a);}var Ya=void 0;
30 | function Za(a,b){var c=[];function e(a){a=b(a);if(a.length!==c.length)throw new Ya("Mismatched type converter count");for(var e=0;e>2])}function cb(a){if(null===a)return"null";var b=typeof a;return"object"===b||"array"===b||"function"===b?a.toString():""+a}
33 | function db(a,b){switch(b){case 2:return function(a){return this.fromWireType(ua[a>>2])};case 3:return function(a){return this.fromWireType(va[a>>3])};default:throw new TypeError("Unknown float type: "+a);}}function eb(a){var b=Function;if(!(b instanceof Function))throw new TypeError("new_ called with constructor type "+typeof b+" which is not a function");var c=Ua(b.name||"unknownFunctionName",function(){});c.prototype=b.prototype;c=new c;a=b.apply(c,a);return a instanceof Object?a:c}
34 | function gb(a){for(;a.length;){var b=a.pop();a.pop()(b)}}function hb(a,b){var c=d;if(void 0===c[a].c){var e=c[a];c[a]=function(){c[a].c.hasOwnProperty(arguments.length)||U("Function '"+b+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+c[a].c+")!");return c[a].c[arguments.length].apply(this,arguments)};c[a].c=[];c[a].c[e.A]=e}}
35 | function ib(a,b,c){d.hasOwnProperty(a)?((void 0===c||void 0!==d[a].c&&void 0!==d[a].c[c])&&U("Cannot register public name '"+a+"' twice"),hb(a,a),d.hasOwnProperty(c)&&U("Cannot register multiple overloads of a function with the same number of arguments ("+c+")!"),d[a].c[c]=b):(d[a]=b,void 0!==c&&(d[a].X=c))}function jb(a,b){for(var c=[],e=0;e>2)+e]);return c}
36 | function kb(a,b){a=Q(a);if(void 0!==d["FUNCTION_TABLE_"+a])var c=d["FUNCTION_TABLE_"+a][b];else if("undefined"!==typeof FUNCTION_TABLE)c=FUNCTION_TABLE[b];else{c=d.asm["dynCall_"+a];void 0===c&&(c=d.asm["dynCall_"+a.replace(/f/g,"d")],void 0===c&&U("No dynCall invoker for signature: "+a));for(var e=[],g=1;g>1]}:function(a){return ta[a>>1]};case 2:return c?function(a){return z[a>>2]}:function(a){return A[a>>2]};default:throw new TypeError("Unknown integer type: "+a);}}var qb={};function Y(a){if(0===a)return 0;a=ma(a);if(!qb.hasOwnProperty(a))return 0;Y.b&&X(Y.b);a=qb[a];var b=qa(a)+1,c=rb(b);c&&pa(a,y,c,b);Y.b=c;return Y.b}
39 | function Z(){Z.b||(Z.b=[]);Z.b.push(sb());return Z.b.length-1}function tb(a){return 0===a%4&&(0!==a%100||0===a%400)}function ub(a,b){for(var c=0,e=0;e<=b;c+=a[e++]);return c}var vb=[31,29,31,30,31,30,31,31,30,31,30,31],wb=[31,28,31,30,31,30,31,31,30,31,30,31];
40 | function xb(a,b){for(a=new Date(a.getTime());0e-a.getDate())b-=e-a.getDate()+1,a.setDate(1),11>c?a.setMonth(c+1):(a.setMonth(0),a.setFullYear(a.getFullYear()+1));else{a.setDate(a.getDate()+b);break}}return a}
41 | function yb(a,b,c,e){function g(a,b,c){for(a="number"===typeof a?a.toString():a||"";a.lengtha?-1:0=h(l(new Date(a.getFullYear(),0,4)),a)?0>=h(b,a)?a.getFullYear()+1:a.getFullYear():a.getFullYear()-1}var p=z[e+40>>2];e={K:z[e>>2],J:z[e+4>>2],l:z[e+8>>2],h:z[e+12>>2],g:z[e+16>>2],a:z[e+20>>2],w:z[e+24>>2],m:z[e+28>>2],Y:z[e+32>>2],I:z[e+36>>2],L:p?ma(p):""};
43 | c=ma(c);p={"%c":"%a %b %d %H:%M:%S %Y","%D":"%m/%d/%y","%F":"%Y-%m-%d","%h":"%b","%r":"%I:%M:%S %p","%R":"%H:%M","%T":"%H:%M:%S","%x":"%m/%d/%y","%X":"%H:%M:%S"};for(var m in p)c=c.replace(new RegExp(m,"g"),p[m]);var I="Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),P="January February March April May June July August September October November December".split(" ");p={"%a":function(a){return I[a.w].substring(0,3)},"%A":function(a){return I[a.w]},"%b":function(a){return P[a.g].substring(0,
44 | 3)},"%B":function(a){return P[a.g]},"%C":function(a){return f((a.a+1900)/100|0,2)},"%d":function(a){return f(a.h,2)},"%e":function(a){return g(a.h,2," ")},"%g":function(a){return q(a).toString().substring(2)},"%G":function(a){return q(a)},"%H":function(a){return f(a.l,2)},"%I":function(a){a=a.l;0==a?a=12:12a.l?"AM":"PM"},"%S":function(a){return f(a.K,2)},"%t":function(){return"\t"},"%u":function(a){return(new Date(a.a+1900,a.g+1,a.h,0,0,0,0)).getDay()||7},"%U":function(a){var b=new Date(a.a+1900,0,1),c=0===b.getDay()?b:xb(b,7-b.getDay());a=new Date(a.a+1900,a.g,a.h);return 0>h(c,a)?f(Math.ceil((31-c.getDate()+(ub(tb(a.getFullYear())?vb:wb,a.getMonth()-1)-31)+a.getDate())/7),2):0===h(c,b)?"01":"00"},"%V":function(a){var b=l(new Date(a.a+1900,0,4)),c=l(new Date(a.a+1901,0,4)),e=xb(new Date(a.a+
46 | 1900,0,1),a.m);return 0>h(e,b)?"53":0>=h(c,e)?"01":f(Math.ceil((b.getFullYear()h(c,a)?f(Math.ceil((31-c.getDate()+(ub(tb(a.getFullYear())?vb:wb,a.getMonth()-1)-31)+a.getDate())/7),2):0===h(c,b)?"01":"00"},"%y":function(a){return(a.a+1900).toString().substring(2)},
47 | "%Y":function(a){return a.a+1900},"%z":function(a){a=a.I;var b=0<=a;a=Math.abs(a)/60;return(b?"+":"-")+String("0000"+(a/60*100+a%60)).slice(-4)},"%Z":function(a){return a.L},"%%":function(){return"%"}};for(m in p)0<=c.indexOf(m)&&(c=c.replace(new RegExp(m,"g"),p[m](e)));m=zb(c);if(m.length>b)return 0;y.set(m,a);return m.length-1}for(var Ab=Array(256),Bb=0;256>Bb;++Bb)Ab[Bb]=String.fromCharCode(Bb);Ra=Ab;Wa=d.BindingError=Va("BindingError");Ya=d.InternalError=Va("InternalError");
48 | d.count_emval_handles=function(){for(var a=0,b=5;b>2]=Aa;function zb(a){var b=Array(qa(a)+1);pa(a,b,0,b.length);return b}d.wasmTableSize=479;d.wasmMaxTableSize=479;d.B={};
49 | d.C={abort:w,enlargeMemory:function(){var a=d.usingWasm?65536:ba,b=2147483648-a;if(z[D>>2]>b)return!1;var c=F;for(F=Math.max(F,aa);F>2];)536870912>=F?F=ra(2*F,a):F=Math.min(ra((3*F+2147483648)/4,a),b);a=d.reallocBuffer(F);if(!a||a.byteLength!=F)return F=c,!1;d.buffer=buffer=a;wa();return!0},getTotalMemory:function(){return F},abortOnCannotGrowMemory:function(){w("Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value "+F+", (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 ")},
50 | ___cxa_uncaught_exception:function(){return!!Db.u},___lock:function(){},___map_file:function(){Pa(1);return-1},___setErrNo:Pa,___syscall140:function(a,b){K=b;try{var c=N.v();M();var e=M(),g=M(),f=M();FS.U(c,e,f);z[g>>2]=c.position;c.D&&0===e&&0===f&&(c.D=null);return 0}catch(h){return"undefined"!==typeof FS&&h instanceof FS.i||w(h),-h.j}},___syscall145:function(a,b){K=b;try{var c=N.v(),e=M(),g=M();return N.P(c,e,g)}catch(f){return"undefined"!==typeof FS&&f instanceof FS.i||w(f),-f.j}},___syscall146:O,
51 | ___syscall54:function(a,b){K=b;return 0},___syscall6:function(a,b){K=b;try{var c=N.v();FS.close(c);return 0}catch(e){return"undefined"!==typeof FS&&e instanceof FS.i||w(e),-e.j}},___syscall91:function(a,b){K=b;try{var c=M(),e=M(),g=N.G[c];if(!g)return 0;if(e===g.T){var f=FS.R(g.fd);N.O(c,f,e,g.flags);FS.W(f);N.G[c]=null;g.N&&X(g.V)}return 0}catch(h){return"undefined"!==typeof FS&&h instanceof FS.i||w(h),-h.j}},___unlock:function(){},__embind_register_bool:function(a,b,c,e,g){var f=Qa(c);b=Q(b);V(a,
52 | {name:b,fromWireType:function(a){return!!a},toWireType:function(a,b){return b?e:g},argPackAdvance:8,readValueFromPointer:function(a){if(1===c)var e=y;else if(2===c)e=sa;else if(4===c)e=z;else throw new TypeError("Unknown boolean type size: "+b);return this.fromWireType(e[a>>f])},f:null})},__embind_register_emval:function(a,b){b=Q(b);V(a,{name:b,fromWireType:function(a){var b=W[a].value;4l&&U("argTypes array size mismatch! Must at least get return value and 'this' types!");for(var P=null!==e[1]&&!1,E=!1,k=1;k>>l}}var q=-1!=b.indexOf("unsigned");V(a,{name:b,fromWireType:f,toWireType:function(a,c){if("number"!==typeof c&&"boolean"!==typeof c)throw new TypeError('Cannot convert "'+cb(c)+'" to '+this.name);if(cg)throw new TypeError('Passing a number "'+cb(c)+'" from JS side to C/C++ side to an argument of type "'+b+'", which is outside the valid range ['+e+", "+g+"]!");return q?c>>>0:c|0},argPackAdvance:8,readValueFromPointer:pb(b,
58 | h,0!==e),f:null})},__embind_register_memory_view:function(a,b,c){function e(a){a>>=2;var b=A;return new g(b.buffer,b[a+1],b[a])}var g=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array][b];c=Q(c);V(a,{name:c,fromWireType:e,argPackAdvance:8,readValueFromPointer:e},{F:!0})},__embind_register_std_string:function(a,b){b=Q(b);V(a,{name:b,fromWireType:function(a){for(var b=A[a>>2],c=Array(b),f=0;f>2]=l;for(var p=0;p>2],f=Array(c),p=a+4>>g,m=0;m>2]=h;for(var m=p+4>>g,I=0;I