├── .gitignore ├── README.md ├── f32 ├── go_js_wasm_exec ├── main.cc ├── main.go ├── main.js ├── run.sh └── wasm_exec.js └── f64 ├── go_js_wasm_exec ├── main.cc ├── main.go ├── main.js ├── run.sh └── wasm_exec.js /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | out.* 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Result on MacBook Pro 2014 Mid (2018-10-25) 2 | 3 | ## Versions 4 | 5 | ### Go 6 | 7 | ``` 8 | go version go1.11.1 darwin/amd64 9 | ``` 10 | 11 | ### Node 12 | 13 | ``` 14 | v10.6.0 15 | ``` 16 | 17 | ### Emscripten 18 | 19 | ``` 20 | 1.38.6 21 | ``` 22 | 23 | ## f32 24 | 25 | ``` 26 | Go Native 27 | result: +5.000000e+006 28 | time [ns]: 47538000 29 | 30 | C++ Native (-Os) 31 | result: 5000000.000000 32 | time [ns]: 13612005 33 | 34 | Wasm (Go) 35 | result: +5.000000e+006 36 | time [ns]: 187528488 37 | 38 | Wasm (Go) (-Os by wasm-opt) 39 | result: +5.000000e+006 40 | time [ns]: 147503206 41 | 42 | Wasm (Go) (-O4 by wasm-opt) 43 | result: +5.000000e+006 44 | time [ns]: 133056493 45 | 46 | GopherJS 47 | result: 5000000 48 | time [ns]: 133925725 49 | 50 | JavaScript 51 | result: 5000000 52 | time [ns]: 65524751 53 | 54 | Wasm (C++) (-O3) 55 | result: 5000000.000000 56 | time [ns]: 66015871 57 | ``` 58 | 59 | ## f64 60 | 61 | ``` 62 | Go Native 63 | result: +5.000000e+006 64 | time [ns]: 47116000 65 | 66 | C++ Native (-Os) 67 | result: 5000000.000000 68 | time [ns]: 41781047 69 | 70 | Wasm (Go) 71 | result: +5.000000e+006 72 | time [ns]: 169768051 73 | 74 | Wasm (Go) (-Os by wasm-opt) 75 | result: +5.000000e+006 76 | time [ns]: 145240577 77 | 78 | Wasm (Go) (-O4 by wasm-opt) 79 | result: +5.000000e+006 80 | time [ns]: 129833968 81 | 82 | GopherJS 83 | result: 5000000 84 | time [ns]: 117594827 85 | 86 | JavaScript 87 | result: 5000000 88 | time [ns]: 63437460 89 | 90 | Wasm (C++) (-O3) 91 | result: 5000000.000000 92 | time [ns]: 64420889 93 | ``` 94 | 95 | # Result on MacBook Pro 2014 Mid (2018-07-13) 96 | 97 | ## Versions 98 | 99 | ### Go 100 | 101 | ``` 102 | go version devel +9fa988547a Thu Jul 5 07:21:50 2018 +0000 darwin/amd64 103 | ``` 104 | 105 | ### Node 106 | 107 | ``` 108 | v10.6.0 109 | ``` 110 | 111 | ### Emscripten 112 | 113 | ``` 114 | 1.38.6 115 | ``` 116 | 117 | ## f32 118 | 119 | ``` 120 | Go Native 121 | result: +5.000000e+006 122 | time [ns]: 46852896 123 | 124 | C++ Native (-Os) 125 | result: 5000000.000000 126 | time [ns]: 11827945 127 | 128 | Wasm (Go) 129 | result: +5.000000e+006 130 | time [ns]: 192059527 131 | 132 | Wasm (Go) (-Os by wasm-opt) 133 | result: +5.000000e+006 134 | time [ns]: 141913384 135 | 136 | Wasm (Go) (-O4 by wasm-opt) 137 | result: +5.000000e+006 138 | time [ns]: 142049012 139 | 140 | GopherJS 141 | result: 5000000 142 | time [ns]: 129323688 143 | 144 | JavaScript 145 | result: 5000000 146 | time [ns]: 74149606 147 | 148 | Wasm (C++) (-O3) 149 | result: 5000000.000000 150 | time [ns]: 64772805 151 | ``` 152 | 153 | ## f64 154 | 155 | ``` 156 | Go Native 157 | result: +5.000000e+006 158 | time [ns]: 46454333 159 | 160 | C++ Native (-Os) 161 | result: 5000000.000000 162 | time [ns]: 40871053 163 | 164 | Wasm (Go) 165 | result: +5.000000e+006 166 | time [ns]: 160427612 167 | 168 | Wasm (Go) (-Os by wasm-opt) 169 | result: +5.000000e+006 170 | time [ns]: 142731077 171 | 172 | Wasm (Go) (-O4 by wasm-opt) 173 | result: +5.000000e+006 174 | time [ns]: 130341177 175 | 176 | GopherJS 177 | result: 5000000 178 | time [ns]: 111787121 179 | 180 | JavaScript 181 | result: 5000000 182 | time [ns]: 63478679 183 | 184 | Wasm (C++) (-O3) 185 | result: 5000000.000000 186 | time [ns]: 64402912 187 | ``` 188 | 189 | # Result on MacBook Pro 2014 Mid (2018-07-02) 190 | 191 | ## f32 192 | 193 | ``` 194 | Go Native 195 | result: +5.000000e+006 196 | time [ns]: 46503928 197 | 198 | C++ Native (-Os) 199 | result: 5000000.000000 200 | time [ns]: 12024699 201 | 202 | Wasm (Go) 203 | result: +5.000000e+006 204 | time [ns]: 223048008 205 | 206 | Wasm (Go) (-Os by wasm-opt) 207 | result: +5.000000e+006 208 | time [ns]: 179819890 209 | 210 | Wasm (Go) (-O4 by wasm-opt) 211 | result: +5.000000e+006 212 | time [ns]: 159952173 213 | 214 | GopherJS 215 | result: 5000000 216 | time [ns]: 149482927 217 | 218 | JavaScript 219 | result: 5000000 220 | time [ns]: 73622416 221 | 222 | Wasm (C++) (-Os) 223 | result: 5000000.000000 224 | time [ns]: 67350223 225 | ``` 226 | 227 | ## f64 228 | 229 | ``` 230 | Go Native 231 | result: +5.000000e+006 232 | time [ns]: 46859944 233 | 234 | C++ Native (-Os) 235 | result: 5000000.000000 236 | time [ns]: 40685821 237 | 238 | Wasm (Go) 239 | result: +5.000000e+006 240 | time [ns]: 190058473 241 | 242 | Wasm (Go) (-Os by wasm-opt) 243 | result: +5.000000e+006 244 | time [ns]: 173105512 245 | 246 | Wasm (Go) (-O4 by wasm-opt) 247 | result: +5.000000e+006 248 | time [ns]: 154126142 249 | 250 | GopherJS 251 | result: 5000000 252 | time [ns]: 142118787 253 | 254 | JavaScript 255 | result: 5000000 256 | time [ns]: 73518466 257 | 258 | Wasm (C++) (-Os) 259 | result: 5000000.000000 260 | time [ns]: 66163768 261 | ``` 262 | 263 | # Result on MacBook Pro 2014 Mid (2018-06-29) 264 | 265 | ``` 266 | $ ./run.sh 267 | Go Native 268 | result: +5.000000e+006 269 | time [ns]: 46962513 270 | 271 | C++ Native (-Os) 272 | result: 5000000.000000 273 | time [ns]: 11941360 274 | 275 | Wasm (Go) 276 | result: +5.000000e+006 277 | time [ns]: 226033921 278 | 279 | Wasm (Go) (-Os by wasm-opt) 280 | result: +5.000000e+006 281 | time [ns]: 176618698 282 | 283 | Wasm (Go) (-O4 by wasm-opt) 284 | result: +5.000000e+006 285 | time [ns]: 155280898 286 | 287 | GopherJS 288 | result: 5000000 289 | time [ns]: 143573587 290 | 291 | JavaScript 292 | result: 5000000 293 | time [ns]: 74950863 294 | 295 | Wasm (C++) (-Os) 296 | result: 5000000.000000 297 | time [ns]: 62829009 298 | ``` 299 | 300 | # Result on MacBook Pro 2014 Mid (2018-06-25) 301 | 302 | ``` 303 | $ ./run.sh 304 | Go Native 305 | result: +5.000000e+006 306 | time [ns]: 50207750 307 | 308 | C++ Native (-Os) 309 | result: 5000000.000000 310 | time [ns]: 12605950 311 | 312 | Wasm (Go) 313 | result: +5.000000e+006 314 | time [ns]: 257062926 315 | 316 | GopherJS 317 | result: 5000000 318 | time [ns]: 154164104 319 | 320 | JavaScript 321 | result: 5000000 322 | time [ns]: 95847509 323 | 324 | Wasm (C++) (-Os) 325 | result: 5000000.000000 326 | time [ns]: 70553509 327 | ``` 328 | 329 | # Result on MacBook Pro 2014 Mid (2018-06-24) 330 | 331 | ``` 332 | $ ./run.sh 333 | Go Native 334 | result: +5.000000e+006 335 | time [ns]: 52339041 336 | 337 | C++ Native (-Os) 338 | result: 5000000.000000 339 | time [ns]: 14312885 340 | 341 | Wasm (Go) 342 | result: +5.000000e+006 343 | time [ns]: 243000000 344 | 345 | GopherJS 346 | result: 5000000 347 | time [ns]: 158000000 348 | 349 | JavaScript 350 | result: 5000000 351 | time [ns]: 89701023 352 | 353 | Wasm (C++) (-Os) 354 | result: 5000000.000000 355 | time [ns]: 78170506 356 | ``` 357 | -------------------------------------------------------------------------------- /f32/go_js_wasm_exec: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2018 The Go Authors. All rights reserved. 3 | # Use of this source code is governed by a BSD-style 4 | # license that can be found in the LICENSE file. 5 | 6 | SOURCE="${BASH_SOURCE[0]}" 7 | while [ -h "$SOURCE" ]; do 8 | DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" 9 | SOURCE="$(readlink "$SOURCE")" 10 | [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" 11 | done 12 | DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" 13 | 14 | exec node "$DIR/wasm_exec.js" "$@" 15 | -------------------------------------------------------------------------------- /f32/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | std::vector array() { 7 | return std::vector(16); 8 | } 9 | 10 | float heavyTask() { 11 | std::vector arr = std::move(array()); 12 | float result = 0.0f; 13 | for (int i = 0; i < 10000000; i++) { 14 | float u0 = 0.0f; 15 | float v0 = 0.0f; 16 | float u1 = 1.0f; 17 | float v1 = 1.0f; 18 | 19 | arr[0] = u0; 20 | arr[1] = v0; 21 | arr[2] = u1; 22 | arr[3] = u1; 23 | 24 | arr[4] = u1; 25 | arr[5] = v0; 26 | arr[6] = u0; 27 | arr[7] = u1; 28 | 29 | arr[8] = u0; 30 | arr[9] = v1; 31 | arr[10] = u1; 32 | arr[11] = u0; 33 | 34 | arr[12] = u1; 35 | arr[13] = v1; 36 | arr[14] = u0; 37 | arr[15] = u0; 38 | 39 | result += arr[i % 16]; 40 | } 41 | return result; 42 | } 43 | 44 | int main() { 45 | auto t1 = std::chrono::high_resolution_clock::now(); 46 | float x = heavyTask(); 47 | auto t2 = std::chrono::high_resolution_clock::now(); 48 | 49 | printf("result: %f\n", x); 50 | printf("time [ns]: %lld\n", std::chrono::duration_cast(t2-t1).count()); 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /f32/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Hajime Hoshi 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "time" 19 | 20 | "github.com/gopherjs/gopherwasm/js" 21 | ) 22 | 23 | func array() []float32 { 24 | return make([]float32, 16) 25 | } 26 | 27 | func heavyTask() float32 { 28 | arr := array() 29 | 30 | result := float32(0.0) 31 | 32 | for i := 0; i < 10000000; i++ { 33 | u0, v0, u1, v1 := float32(0.0), float32(0.0), float32(1.0), float32(1.0) 34 | arr[0] = u0 35 | arr[1] = v0 36 | arr[2] = u1 37 | arr[3] = u1 38 | 39 | arr[4] = u1 40 | arr[5] = v0 41 | arr[6] = u0 42 | arr[7] = u1 43 | 44 | arr[8] = u0 45 | arr[9] = v1 46 | arr[10] = u1 47 | arr[11] = u0 48 | 49 | arr[12] = u1 50 | arr[13] = v1 51 | arr[14] = u0 52 | arr[15] = u0 53 | 54 | result += arr[i % 16] 55 | } 56 | return result 57 | } 58 | 59 | var now func() int64 60 | 61 | func init() { 62 | if js.Global() != js.Null() { 63 | now = func() int64 { 64 | arr := js.Global().Get("process").Call("hrtime") 65 | return int64(arr.Index(0).Int() * 1e9 + arr.Index(1).Int()) 66 | } 67 | return 68 | } 69 | now = func() int64 { 70 | return int64(time.Now().UnixNano()) 71 | } 72 | } 73 | 74 | func main() { 75 | t1 := now() 76 | x := heavyTask() 77 | t2 := now() 78 | println("result:", x) 79 | println("time [ns]:", int32(t2-t1)) 80 | } 81 | -------------------------------------------------------------------------------- /f32/main.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Hajime Hoshi 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | function array() { 16 | return new Float32Array(16); 17 | } 18 | 19 | function heavyTask() { 20 | const arr = array(); 21 | result = 0.0; 22 | for (let i = 0; i < 10000000; i++) { 23 | const u0 = 0.0; 24 | const v0 = 0.0; 25 | const u1 = 1.0; 26 | const v1 = 1.0; 27 | 28 | arr[0] = u0 29 | arr[1] = v0 30 | arr[2] = u1 31 | arr[3] = u1 32 | 33 | arr[4] = u1 34 | arr[5] = v0 35 | arr[6] = u0 36 | arr[7] = u1 37 | 38 | arr[8] = u0 39 | arr[9] = v1 40 | arr[10] = u1 41 | arr[11] = u0 42 | 43 | arr[12] = u1 44 | arr[13] = v1 45 | arr[14] = u0 46 | arr[15] = u0 47 | 48 | result += arr[i % 16] 49 | } 50 | return result; 51 | } 52 | 53 | function main() { 54 | const t1 = process.hrtime(); 55 | const x = heavyTask(); 56 | const diff = process.hrtime(t1); 57 | console.log('result:', x); 58 | console.log('time [ns]:', diff[0] * 1e9 + diff[1]); 59 | } 60 | 61 | main(); 62 | -------------------------------------------------------------------------------- /f32/run.sh: -------------------------------------------------------------------------------- 1 | echo 'Go Native' 2 | go run main.go 3 | echo 4 | 5 | echo 'C++ Native (-Os)' 6 | g++ -std=c++14 -Os -o out.c++ main.cc 7 | ./out.c++ 8 | echo 9 | 10 | echo 'Wasm (Go)' 11 | GOOS=js GOARCH=wasm go build -o out.go.wasm . 12 | ./go_js_wasm_exec out.go.wasm 13 | echo 14 | 15 | echo 'Wasm (Go) (-Os by wasm-opt)' 16 | wasm-opt -Os out.go.wasm -o out.go.os.wasm 17 | ./go_js_wasm_exec out.go.os.wasm 18 | echo 19 | 20 | echo 'Wasm (Go) (-O4 by wasm-opt)' 21 | wasm-opt -O4 out.go.wasm -o out.go.o4.wasm 22 | ./go_js_wasm_exec out.go.o4.wasm 23 | echo 24 | 25 | echo 'GopherJS' 26 | gopherjs build -o out.gopherjs.js . 27 | node out.gopherjs.js 28 | echo 29 | 30 | echo 'JavaScript' 31 | node main.js 32 | echo 33 | 34 | echo 'Wasm (C++) (-O3)' 35 | emcc main.cc -std=c++14 -O3 -o out.emcc.js -g -s WASM=1 36 | node out.emcc.js 37 | echo 38 | -------------------------------------------------------------------------------- /f32/wasm_exec.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | (() => { 6 | // Map web browser API and Node.js API to a single common API (preferring web standards over Node.js API). 7 | const isNodeJS = typeof process !== "undefined"; 8 | if (isNodeJS) { 9 | global.require = require; 10 | global.fs = require("fs"); 11 | 12 | const nodeCrypto = require("crypto"); 13 | global.crypto = { 14 | getRandomValues(b) { 15 | nodeCrypto.randomFillSync(b); 16 | }, 17 | }; 18 | 19 | global.performance = { 20 | now() { 21 | const [sec, nsec] = process.hrtime(); 22 | return sec * 1000 + nsec / 1000000; 23 | }, 24 | }; 25 | 26 | const util = require("util"); 27 | global.TextEncoder = util.TextEncoder; 28 | global.TextDecoder = util.TextDecoder; 29 | } else { 30 | window.global = window; 31 | 32 | let outputBuf = ""; 33 | global.fs = { 34 | constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1, O_NONBLOCK: -1, O_SYNC: -1 }, // unused 35 | writeSync(fd, buf) { 36 | outputBuf += decoder.decode(buf); 37 | const nl = outputBuf.lastIndexOf("\n"); 38 | if (nl != -1) { 39 | console.log(outputBuf.substr(0, nl)); 40 | outputBuf = outputBuf.substr(nl + 1); 41 | } 42 | return buf.length; 43 | }, 44 | openSync(path, flags, mode) { 45 | const err = new Error("not implemented"); 46 | err.code = "ENOSYS"; 47 | throw err; 48 | }, 49 | }; 50 | } 51 | 52 | const encoder = new TextEncoder("utf-8"); 53 | const decoder = new TextDecoder("utf-8"); 54 | 55 | global.Go = class { 56 | constructor() { 57 | this.argv = ["js"]; 58 | this.env = {}; 59 | this.exit = (code) => { 60 | if (code !== 0) { 61 | console.warn("exit code:", code); 62 | } 63 | }; 64 | this._callbackTimeouts = new Map(); 65 | this._nextCallbackTimeoutID = 1; 66 | 67 | const mem = () => { 68 | // The buffer may change when requesting more memory. 69 | return new DataView(this._inst.exports.mem.buffer); 70 | } 71 | 72 | const setInt64 = (addr, v) => { 73 | mem().setUint32(addr + 0, v, true); 74 | mem().setUint32(addr + 4, Math.floor(v / 4294967296), true); 75 | } 76 | 77 | const getInt64 = (addr) => { 78 | const low = mem().getUint32(addr + 0, true); 79 | const high = mem().getInt32(addr + 4, true); 80 | return low + high * 4294967296; 81 | } 82 | 83 | const loadValue = (addr) => { 84 | const f = mem().getFloat64(addr, true); 85 | if (!isNaN(f)) { 86 | return f; 87 | } 88 | 89 | const id = mem().getUint32(addr, true); 90 | return this._values[id]; 91 | } 92 | 93 | const storeValue = (addr, v) => { 94 | if (typeof v === "number") { 95 | if (isNaN(v)) { 96 | mem().setUint32(addr + 4, 0x7FF80000, true); // NaN 97 | mem().setUint32(addr, 0, true); 98 | return; 99 | } 100 | mem().setFloat64(addr, v, true); 101 | return; 102 | } 103 | 104 | mem().setUint32(addr + 4, 0x7FF80000, true); // NaN 105 | 106 | switch (v) { 107 | case undefined: 108 | mem().setUint32(addr, 1, true); 109 | return; 110 | case null: 111 | mem().setUint32(addr, 2, true); 112 | return; 113 | case true: 114 | mem().setUint32(addr, 3, true); 115 | return; 116 | case false: 117 | mem().setUint32(addr, 4, true); 118 | return; 119 | } 120 | 121 | if (typeof v === "string") { 122 | let ref = this._stringRefs.get(v); 123 | if (ref === undefined) { 124 | ref = this._values.length; 125 | this._values.push(v); 126 | this._stringRefs.set(v, ref); 127 | } 128 | mem().setUint32(addr, ref, true); 129 | return; 130 | } 131 | 132 | if (typeof v === "symbol") { 133 | let ref = this._symbolRefs.get(v); 134 | if (ref === undefined) { 135 | ref = this._values.length; 136 | this._values.push(v); 137 | this._symbolRefs.set(v, ref); 138 | } 139 | mem().setUint32(addr, ref, true); 140 | return; 141 | } 142 | 143 | let ref = v[this._refProp]; 144 | if (ref === undefined) { 145 | ref = this._values.length; 146 | this._values.push(v); 147 | v[this._refProp] = ref; 148 | } 149 | mem().setUint32(addr, ref, true); 150 | } 151 | 152 | const loadSlice = (addr) => { 153 | const array = getInt64(addr + 0); 154 | const len = getInt64(addr + 8); 155 | return new Uint8Array(this._inst.exports.mem.buffer, array, len); 156 | } 157 | 158 | const loadSliceOfValues = (addr) => { 159 | const array = getInt64(addr + 0); 160 | const len = getInt64(addr + 8); 161 | const a = new Array(len); 162 | for (let i = 0; i < len; i++) { 163 | a[i] = loadValue(array + i * 8); 164 | } 165 | return a; 166 | } 167 | 168 | const loadString = (addr) => { 169 | const saddr = getInt64(addr + 0); 170 | const len = getInt64(addr + 8); 171 | return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); 172 | } 173 | 174 | const timeOrigin = Date.now() - performance.now(); 175 | this.importObject = { 176 | go: { 177 | // func wasmExit(code int32) 178 | "runtime.wasmExit": (sp) => { 179 | this.exited = true; 180 | this.exit(mem().getInt32(sp + 8, true)); 181 | }, 182 | 183 | // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) 184 | "runtime.wasmWrite": (sp) => { 185 | const fd = getInt64(sp + 8); 186 | const p = getInt64(sp + 16); 187 | const n = mem().getInt32(sp + 24, true); 188 | fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); 189 | }, 190 | 191 | // func nanotime() int64 192 | "runtime.nanotime": (sp) => { 193 | setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000); 194 | }, 195 | 196 | // func walltime() (sec int64, nsec int32) 197 | "runtime.walltime": (sp) => { 198 | const msec = (new Date).getTime(); 199 | setInt64(sp + 8, msec / 1000); 200 | mem().setInt32(sp + 16, (msec % 1000) * 1000000, true); 201 | }, 202 | 203 | // func scheduleCallback(delay int64) int32 204 | "runtime.scheduleCallback": (sp) => { 205 | const id = this._nextCallbackTimeoutID; 206 | this._nextCallbackTimeoutID++; 207 | this._callbackTimeouts.set(id, setTimeout( 208 | () => { this._resolveCallbackPromise(); }, 209 | getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early 210 | )); 211 | mem().setInt32(sp + 16, id, true); 212 | }, 213 | 214 | // func clearScheduledCallback(id int32) 215 | "runtime.clearScheduledCallback": (sp) => { 216 | const id = mem().getInt32(sp + 8, true); 217 | clearTimeout(this._callbackTimeouts.get(id)); 218 | this._callbackTimeouts.delete(id); 219 | }, 220 | 221 | // func getRandomData(r []byte) 222 | "runtime.getRandomData": (sp) => { 223 | crypto.getRandomValues(loadSlice(sp + 8)); 224 | }, 225 | 226 | // func stringVal(value string) ref 227 | "syscall/js.stringVal": (sp) => { 228 | storeValue(sp + 24, loadString(sp + 8)); 229 | }, 230 | 231 | // func valueGet(v ref, p string) ref 232 | "syscall/js.valueGet": (sp) => { 233 | storeValue(sp + 32, Reflect.get(loadValue(sp + 8), loadString(sp + 16))); 234 | }, 235 | 236 | // func valueSet(v ref, p string, x ref) 237 | "syscall/js.valueSet": (sp) => { 238 | Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); 239 | }, 240 | 241 | // func valueIndex(v ref, i int) ref 242 | "syscall/js.valueIndex": (sp) => { 243 | storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); 244 | }, 245 | 246 | // valueSetIndex(v ref, i int, x ref) 247 | "syscall/js.valueSetIndex": (sp) => { 248 | Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); 249 | }, 250 | 251 | // func valueCall(v ref, m string, args []ref) (ref, bool) 252 | "syscall/js.valueCall": (sp) => { 253 | try { 254 | const v = loadValue(sp + 8); 255 | const m = Reflect.get(v, loadString(sp + 16)); 256 | const args = loadSliceOfValues(sp + 32); 257 | storeValue(sp + 56, Reflect.apply(m, v, args)); 258 | mem().setUint8(sp + 64, 1); 259 | } catch (err) { 260 | storeValue(sp + 56, err); 261 | mem().setUint8(sp + 64, 0); 262 | } 263 | }, 264 | 265 | // func valueInvoke(v ref, args []ref) (ref, bool) 266 | "syscall/js.valueInvoke": (sp) => { 267 | try { 268 | const v = loadValue(sp + 8); 269 | const args = loadSliceOfValues(sp + 16); 270 | storeValue(sp + 40, Reflect.apply(v, undefined, args)); 271 | mem().setUint8(sp + 48, 1); 272 | } catch (err) { 273 | storeValue(sp + 40, err); 274 | mem().setUint8(sp + 48, 0); 275 | } 276 | }, 277 | 278 | // func valueNew(v ref, args []ref) (ref, bool) 279 | "syscall/js.valueNew": (sp) => { 280 | try { 281 | const v = loadValue(sp + 8); 282 | const args = loadSliceOfValues(sp + 16); 283 | storeValue(sp + 40, Reflect.construct(v, args)); 284 | mem().setUint8(sp + 48, 1); 285 | } catch (err) { 286 | storeValue(sp + 40, err); 287 | mem().setUint8(sp + 48, 0); 288 | } 289 | }, 290 | 291 | // func valueLength(v ref) int 292 | "syscall/js.valueLength": (sp) => { 293 | setInt64(sp + 16, parseInt(loadValue(sp + 8).length)); 294 | }, 295 | 296 | // valuePrepareString(v ref) (ref, int) 297 | "syscall/js.valuePrepareString": (sp) => { 298 | const str = encoder.encode(String(loadValue(sp + 8))); 299 | storeValue(sp + 16, str); 300 | setInt64(sp + 24, str.length); 301 | }, 302 | 303 | // valueLoadString(v ref, b []byte) 304 | "syscall/js.valueLoadString": (sp) => { 305 | const str = loadValue(sp + 8); 306 | loadSlice(sp + 16).set(str); 307 | }, 308 | 309 | // func valueInstanceOf(v ref, t ref) bool 310 | "syscall/js.valueInstanceOf": (sp) => { 311 | mem().setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16)); 312 | }, 313 | 314 | "debug": (value) => { 315 | console.log(value); 316 | }, 317 | } 318 | }; 319 | } 320 | 321 | async run(instance) { 322 | this._inst = instance; 323 | this._values = [ // TODO: garbage collection 324 | NaN, 325 | undefined, 326 | null, 327 | true, 328 | false, 329 | global, 330 | this._inst.exports.mem, 331 | () => { // resolveCallbackPromise 332 | if (this.exited) { 333 | throw new Error("bad callback: Go program has already exited"); 334 | } 335 | setTimeout(this._resolveCallbackPromise, 0); // make sure it is asynchronous 336 | }, 337 | ]; 338 | this._stringRefs = new Map(); 339 | this._symbolRefs = new Map(); 340 | this._refProp = Symbol(); 341 | this.exited = false; 342 | 343 | const mem = new DataView(this._inst.exports.mem.buffer) 344 | 345 | // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. 346 | let offset = 4096; 347 | 348 | const strPtr = (str) => { 349 | let ptr = offset; 350 | new Uint8Array(mem.buffer, offset, str.length + 1).set(encoder.encode(str + "\0")); 351 | offset += str.length + (8 - (str.length % 8)); 352 | return ptr; 353 | }; 354 | 355 | const argc = this.argv.length; 356 | 357 | const argvPtrs = []; 358 | this.argv.forEach((arg) => { 359 | argvPtrs.push(strPtr(arg)); 360 | }); 361 | 362 | const keys = Object.keys(this.env).sort(); 363 | argvPtrs.push(keys.length); 364 | keys.forEach((key) => { 365 | argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); 366 | }); 367 | 368 | const argv = offset; 369 | argvPtrs.forEach((ptr) => { 370 | mem.setUint32(offset, ptr, true); 371 | mem.setUint32(offset + 4, 0, true); 372 | offset += 8; 373 | }); 374 | 375 | while (true) { 376 | const callbackPromise = new Promise((resolve) => { 377 | this._resolveCallbackPromise = resolve; 378 | }); 379 | this._inst.exports.run(argc, argv); 380 | if (this.exited) { 381 | break; 382 | } 383 | await callbackPromise; 384 | } 385 | } 386 | } 387 | 388 | if (isNodeJS) { 389 | if (process.argv.length < 3) { 390 | process.stderr.write("usage: go_js_wasm_exec [wasm binary] [arguments]\n"); 391 | process.exit(1); 392 | } 393 | 394 | const go = new Go(); 395 | go.argv = process.argv.slice(2); 396 | go.env = process.env; 397 | go.exit = process.exit; 398 | WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => { 399 | process.on("exit", () => { // Node.js exits if no callback is pending 400 | if (!go.exited) { 401 | console.error("error: all goroutines asleep and no JavaScript callback pending - deadlock!"); 402 | process.exit(1); 403 | } 404 | }); 405 | return go.run(result.instance); 406 | }).catch((err) => { 407 | console.error(err); 408 | go.exited = true; 409 | process.exit(1); 410 | }); 411 | } 412 | })(); 413 | -------------------------------------------------------------------------------- /f64/go_js_wasm_exec: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2018 The Go Authors. All rights reserved. 3 | # Use of this source code is governed by a BSD-style 4 | # license that can be found in the LICENSE file. 5 | 6 | SOURCE="${BASH_SOURCE[0]}" 7 | while [ -h "$SOURCE" ]; do 8 | DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" 9 | SOURCE="$(readlink "$SOURCE")" 10 | [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" 11 | done 12 | DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" 13 | 14 | exec node "$DIR/wasm_exec.js" "$@" 15 | -------------------------------------------------------------------------------- /f64/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | std::vector array() { 7 | return std::vector(16); 8 | } 9 | 10 | double heavyTask() { 11 | std::vector arr = std::move(array()); 12 | double result = 0.0f; 13 | for (int i = 0; i < 10000000; i++) { 14 | double u0 = 0.0; 15 | double v0 = 0.0; 16 | double u1 = 1.0; 17 | double v1 = 1.0; 18 | 19 | arr[0] = u0; 20 | arr[1] = v0; 21 | arr[2] = u1; 22 | arr[3] = u1; 23 | 24 | arr[4] = u1; 25 | arr[5] = v0; 26 | arr[6] = u0; 27 | arr[7] = u1; 28 | 29 | arr[8] = u0; 30 | arr[9] = v1; 31 | arr[10] = u1; 32 | arr[11] = u0; 33 | 34 | arr[12] = u1; 35 | arr[13] = v1; 36 | arr[14] = u0; 37 | arr[15] = u0; 38 | 39 | result += arr[i % 16]; 40 | } 41 | return result; 42 | } 43 | 44 | int main() { 45 | auto t1 = std::chrono::high_resolution_clock::now(); 46 | double x = heavyTask(); 47 | auto t2 = std::chrono::high_resolution_clock::now(); 48 | 49 | printf("result: %f\n", x); 50 | printf("time [ns]: %lld\n", std::chrono::duration_cast(t2-t1).count()); 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /f64/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Hajime Hoshi 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "time" 19 | 20 | "github.com/gopherjs/gopherwasm/js" 21 | ) 22 | 23 | func array() []float64 { 24 | return make([]float64, 16) 25 | } 26 | 27 | func heavyTask() float64 { 28 | arr := array() 29 | 30 | result := float64(0.0) 31 | 32 | for i := 0; i < 10000000; i++ { 33 | u0, v0, u1, v1 := float64(0.0), float64(0.0), float64(1.0), float64(1.0) 34 | arr[0] = u0 35 | arr[1] = v0 36 | arr[2] = u1 37 | arr[3] = u1 38 | 39 | arr[4] = u1 40 | arr[5] = v0 41 | arr[6] = u0 42 | arr[7] = u1 43 | 44 | arr[8] = u0 45 | arr[9] = v1 46 | arr[10] = u1 47 | arr[11] = u0 48 | 49 | arr[12] = u1 50 | arr[13] = v1 51 | arr[14] = u0 52 | arr[15] = u0 53 | 54 | result += arr[i % 16] 55 | } 56 | return result 57 | } 58 | 59 | var now func() int64 60 | 61 | func init() { 62 | if js.Global() != js.Null() { 63 | now = func() int64 { 64 | arr := js.Global().Get("process").Call("hrtime") 65 | return int64(arr.Index(0).Int() * 1e9 + arr.Index(1).Int()) 66 | } 67 | return 68 | } 69 | now = func() int64 { 70 | return int64(time.Now().UnixNano()) 71 | } 72 | } 73 | 74 | func main() { 75 | t1 := now() 76 | x := heavyTask() 77 | t2 := now() 78 | println("result:", x) 79 | println("time [ns]:", int32(t2-t1)) 80 | } 81 | -------------------------------------------------------------------------------- /f64/main.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Hajime Hoshi 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | function array() { 16 | return new Float64Array(16); 17 | } 18 | 19 | function heavyTask() { 20 | const arr = array(); 21 | result = 0.0; 22 | for (let i = 0; i < 10000000; i++) { 23 | const u0 = 0.0; 24 | const v0 = 0.0; 25 | const u1 = 1.0; 26 | const v1 = 1.0; 27 | 28 | arr[0] = u0 29 | arr[1] = v0 30 | arr[2] = u1 31 | arr[3] = u1 32 | 33 | arr[4] = u1 34 | arr[5] = v0 35 | arr[6] = u0 36 | arr[7] = u1 37 | 38 | arr[8] = u0 39 | arr[9] = v1 40 | arr[10] = u1 41 | arr[11] = u0 42 | 43 | arr[12] = u1 44 | arr[13] = v1 45 | arr[14] = u0 46 | arr[15] = u0 47 | 48 | result += arr[i % 16] 49 | } 50 | return result; 51 | } 52 | 53 | function main() { 54 | const t1 = process.hrtime(); 55 | const x = heavyTask(); 56 | const diff = process.hrtime(t1); 57 | console.log('result:', x); 58 | console.log('time [ns]:', diff[0] * 1e9 + diff[1]); 59 | } 60 | 61 | main(); 62 | -------------------------------------------------------------------------------- /f64/run.sh: -------------------------------------------------------------------------------- 1 | echo 'Go Native' 2 | go run main.go 3 | echo 4 | 5 | echo 'C++ Native (-Os)' 6 | g++ -std=c++14 -Os -o out.c++ main.cc 7 | ./out.c++ 8 | echo 9 | 10 | echo 'Wasm (Go)' 11 | GOOS=js GOARCH=wasm go build -o out.go.wasm . 12 | ./go_js_wasm_exec out.go.wasm 13 | echo 14 | 15 | echo 'Wasm (Go) (-Os by wasm-opt)' 16 | wasm-opt -Os out.go.wasm -o out.go.os.wasm 17 | ./go_js_wasm_exec out.go.os.wasm 18 | echo 19 | 20 | echo 'Wasm (Go) (-O4 by wasm-opt)' 21 | wasm-opt -O4 out.go.wasm -o out.go.o4.wasm 22 | ./go_js_wasm_exec out.go.o4.wasm 23 | echo 24 | 25 | echo 'GopherJS' 26 | gopherjs build -o out.gopherjs.js . 27 | node out.gopherjs.js 28 | echo 29 | 30 | echo 'JavaScript' 31 | node main.js 32 | echo 33 | 34 | echo 'Wasm (C++) (-O3)' 35 | emcc main.cc -std=c++14 -O3 -o out.emcc.js -g -s WASM=1 36 | node out.emcc.js 37 | echo 38 | -------------------------------------------------------------------------------- /f64/wasm_exec.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | (() => { 6 | // Map web browser API and Node.js API to a single common API (preferring web standards over Node.js API). 7 | const isNodeJS = typeof process !== "undefined"; 8 | if (isNodeJS) { 9 | global.require = require; 10 | global.fs = require("fs"); 11 | 12 | const nodeCrypto = require("crypto"); 13 | global.crypto = { 14 | getRandomValues(b) { 15 | nodeCrypto.randomFillSync(b); 16 | }, 17 | }; 18 | 19 | global.performance = { 20 | now() { 21 | const [sec, nsec] = process.hrtime(); 22 | return sec * 1000 + nsec / 1000000; 23 | }, 24 | }; 25 | 26 | const util = require("util"); 27 | global.TextEncoder = util.TextEncoder; 28 | global.TextDecoder = util.TextDecoder; 29 | } else { 30 | window.global = window; 31 | 32 | let outputBuf = ""; 33 | global.fs = { 34 | constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1, O_NONBLOCK: -1, O_SYNC: -1 }, // unused 35 | writeSync(fd, buf) { 36 | outputBuf += decoder.decode(buf); 37 | const nl = outputBuf.lastIndexOf("\n"); 38 | if (nl != -1) { 39 | console.log(outputBuf.substr(0, nl)); 40 | outputBuf = outputBuf.substr(nl + 1); 41 | } 42 | return buf.length; 43 | }, 44 | openSync(path, flags, mode) { 45 | const err = new Error("not implemented"); 46 | err.code = "ENOSYS"; 47 | throw err; 48 | }, 49 | }; 50 | } 51 | 52 | const encoder = new TextEncoder("utf-8"); 53 | const decoder = new TextDecoder("utf-8"); 54 | 55 | global.Go = class { 56 | constructor() { 57 | this.argv = ["js"]; 58 | this.env = {}; 59 | this.exit = (code) => { 60 | if (code !== 0) { 61 | console.warn("exit code:", code); 62 | } 63 | }; 64 | this._callbackTimeouts = new Map(); 65 | this._nextCallbackTimeoutID = 1; 66 | 67 | const mem = () => { 68 | // The buffer may change when requesting more memory. 69 | return new DataView(this._inst.exports.mem.buffer); 70 | } 71 | 72 | const setInt64 = (addr, v) => { 73 | mem().setUint32(addr + 0, v, true); 74 | mem().setUint32(addr + 4, Math.floor(v / 4294967296), true); 75 | } 76 | 77 | const getInt64 = (addr) => { 78 | const low = mem().getUint32(addr + 0, true); 79 | const high = mem().getInt32(addr + 4, true); 80 | return low + high * 4294967296; 81 | } 82 | 83 | const loadValue = (addr) => { 84 | const f = mem().getFloat64(addr, true); 85 | if (!isNaN(f)) { 86 | return f; 87 | } 88 | 89 | const id = mem().getUint32(addr, true); 90 | return this._values[id]; 91 | } 92 | 93 | const storeValue = (addr, v) => { 94 | if (typeof v === "number") { 95 | if (isNaN(v)) { 96 | mem().setUint32(addr + 4, 0x7FF80000, true); // NaN 97 | mem().setUint32(addr, 0, true); 98 | return; 99 | } 100 | mem().setFloat64(addr, v, true); 101 | return; 102 | } 103 | 104 | mem().setUint32(addr + 4, 0x7FF80000, true); // NaN 105 | 106 | switch (v) { 107 | case undefined: 108 | mem().setUint32(addr, 1, true); 109 | return; 110 | case null: 111 | mem().setUint32(addr, 2, true); 112 | return; 113 | case true: 114 | mem().setUint32(addr, 3, true); 115 | return; 116 | case false: 117 | mem().setUint32(addr, 4, true); 118 | return; 119 | } 120 | 121 | if (typeof v === "string") { 122 | let ref = this._stringRefs.get(v); 123 | if (ref === undefined) { 124 | ref = this._values.length; 125 | this._values.push(v); 126 | this._stringRefs.set(v, ref); 127 | } 128 | mem().setUint32(addr, ref, true); 129 | return; 130 | } 131 | 132 | if (typeof v === "symbol") { 133 | let ref = this._symbolRefs.get(v); 134 | if (ref === undefined) { 135 | ref = this._values.length; 136 | this._values.push(v); 137 | this._symbolRefs.set(v, ref); 138 | } 139 | mem().setUint32(addr, ref, true); 140 | return; 141 | } 142 | 143 | let ref = v[this._refProp]; 144 | if (ref === undefined) { 145 | ref = this._values.length; 146 | this._values.push(v); 147 | v[this._refProp] = ref; 148 | } 149 | mem().setUint32(addr, ref, true); 150 | } 151 | 152 | const loadSlice = (addr) => { 153 | const array = getInt64(addr + 0); 154 | const len = getInt64(addr + 8); 155 | return new Uint8Array(this._inst.exports.mem.buffer, array, len); 156 | } 157 | 158 | const loadSliceOfValues = (addr) => { 159 | const array = getInt64(addr + 0); 160 | const len = getInt64(addr + 8); 161 | const a = new Array(len); 162 | for (let i = 0; i < len; i++) { 163 | a[i] = loadValue(array + i * 8); 164 | } 165 | return a; 166 | } 167 | 168 | const loadString = (addr) => { 169 | const saddr = getInt64(addr + 0); 170 | const len = getInt64(addr + 8); 171 | return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); 172 | } 173 | 174 | const timeOrigin = Date.now() - performance.now(); 175 | this.importObject = { 176 | go: { 177 | // func wasmExit(code int32) 178 | "runtime.wasmExit": (sp) => { 179 | this.exited = true; 180 | this.exit(mem().getInt32(sp + 8, true)); 181 | }, 182 | 183 | // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) 184 | "runtime.wasmWrite": (sp) => { 185 | const fd = getInt64(sp + 8); 186 | const p = getInt64(sp + 16); 187 | const n = mem().getInt32(sp + 24, true); 188 | fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); 189 | }, 190 | 191 | // func nanotime() int64 192 | "runtime.nanotime": (sp) => { 193 | setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000); 194 | }, 195 | 196 | // func walltime() (sec int64, nsec int32) 197 | "runtime.walltime": (sp) => { 198 | const msec = (new Date).getTime(); 199 | setInt64(sp + 8, msec / 1000); 200 | mem().setInt32(sp + 16, (msec % 1000) * 1000000, true); 201 | }, 202 | 203 | // func scheduleCallback(delay int64) int32 204 | "runtime.scheduleCallback": (sp) => { 205 | const id = this._nextCallbackTimeoutID; 206 | this._nextCallbackTimeoutID++; 207 | this._callbackTimeouts.set(id, setTimeout( 208 | () => { this._resolveCallbackPromise(); }, 209 | getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early 210 | )); 211 | mem().setInt32(sp + 16, id, true); 212 | }, 213 | 214 | // func clearScheduledCallback(id int32) 215 | "runtime.clearScheduledCallback": (sp) => { 216 | const id = mem().getInt32(sp + 8, true); 217 | clearTimeout(this._callbackTimeouts.get(id)); 218 | this._callbackTimeouts.delete(id); 219 | }, 220 | 221 | // func getRandomData(r []byte) 222 | "runtime.getRandomData": (sp) => { 223 | crypto.getRandomValues(loadSlice(sp + 8)); 224 | }, 225 | 226 | // func stringVal(value string) ref 227 | "syscall/js.stringVal": (sp) => { 228 | storeValue(sp + 24, loadString(sp + 8)); 229 | }, 230 | 231 | // func valueGet(v ref, p string) ref 232 | "syscall/js.valueGet": (sp) => { 233 | storeValue(sp + 32, Reflect.get(loadValue(sp + 8), loadString(sp + 16))); 234 | }, 235 | 236 | // func valueSet(v ref, p string, x ref) 237 | "syscall/js.valueSet": (sp) => { 238 | Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); 239 | }, 240 | 241 | // func valueIndex(v ref, i int) ref 242 | "syscall/js.valueIndex": (sp) => { 243 | storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); 244 | }, 245 | 246 | // valueSetIndex(v ref, i int, x ref) 247 | "syscall/js.valueSetIndex": (sp) => { 248 | Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); 249 | }, 250 | 251 | // func valueCall(v ref, m string, args []ref) (ref, bool) 252 | "syscall/js.valueCall": (sp) => { 253 | try { 254 | const v = loadValue(sp + 8); 255 | const m = Reflect.get(v, loadString(sp + 16)); 256 | const args = loadSliceOfValues(sp + 32); 257 | storeValue(sp + 56, Reflect.apply(m, v, args)); 258 | mem().setUint8(sp + 64, 1); 259 | } catch (err) { 260 | storeValue(sp + 56, err); 261 | mem().setUint8(sp + 64, 0); 262 | } 263 | }, 264 | 265 | // func valueInvoke(v ref, args []ref) (ref, bool) 266 | "syscall/js.valueInvoke": (sp) => { 267 | try { 268 | const v = loadValue(sp + 8); 269 | const args = loadSliceOfValues(sp + 16); 270 | storeValue(sp + 40, Reflect.apply(v, undefined, args)); 271 | mem().setUint8(sp + 48, 1); 272 | } catch (err) { 273 | storeValue(sp + 40, err); 274 | mem().setUint8(sp + 48, 0); 275 | } 276 | }, 277 | 278 | // func valueNew(v ref, args []ref) (ref, bool) 279 | "syscall/js.valueNew": (sp) => { 280 | try { 281 | const v = loadValue(sp + 8); 282 | const args = loadSliceOfValues(sp + 16); 283 | storeValue(sp + 40, Reflect.construct(v, args)); 284 | mem().setUint8(sp + 48, 1); 285 | } catch (err) { 286 | storeValue(sp + 40, err); 287 | mem().setUint8(sp + 48, 0); 288 | } 289 | }, 290 | 291 | // func valueLength(v ref) int 292 | "syscall/js.valueLength": (sp) => { 293 | setInt64(sp + 16, parseInt(loadValue(sp + 8).length)); 294 | }, 295 | 296 | // valuePrepareString(v ref) (ref, int) 297 | "syscall/js.valuePrepareString": (sp) => { 298 | const str = encoder.encode(String(loadValue(sp + 8))); 299 | storeValue(sp + 16, str); 300 | setInt64(sp + 24, str.length); 301 | }, 302 | 303 | // valueLoadString(v ref, b []byte) 304 | "syscall/js.valueLoadString": (sp) => { 305 | const str = loadValue(sp + 8); 306 | loadSlice(sp + 16).set(str); 307 | }, 308 | 309 | // func valueInstanceOf(v ref, t ref) bool 310 | "syscall/js.valueInstanceOf": (sp) => { 311 | mem().setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16)); 312 | }, 313 | 314 | "debug": (value) => { 315 | console.log(value); 316 | }, 317 | } 318 | }; 319 | } 320 | 321 | async run(instance) { 322 | this._inst = instance; 323 | this._values = [ // TODO: garbage collection 324 | NaN, 325 | undefined, 326 | null, 327 | true, 328 | false, 329 | global, 330 | this._inst.exports.mem, 331 | () => { // resolveCallbackPromise 332 | if (this.exited) { 333 | throw new Error("bad callback: Go program has already exited"); 334 | } 335 | setTimeout(this._resolveCallbackPromise, 0); // make sure it is asynchronous 336 | }, 337 | ]; 338 | this._stringRefs = new Map(); 339 | this._symbolRefs = new Map(); 340 | this._refProp = Symbol(); 341 | this.exited = false; 342 | 343 | const mem = new DataView(this._inst.exports.mem.buffer) 344 | 345 | // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. 346 | let offset = 4096; 347 | 348 | const strPtr = (str) => { 349 | let ptr = offset; 350 | new Uint8Array(mem.buffer, offset, str.length + 1).set(encoder.encode(str + "\0")); 351 | offset += str.length + (8 - (str.length % 8)); 352 | return ptr; 353 | }; 354 | 355 | const argc = this.argv.length; 356 | 357 | const argvPtrs = []; 358 | this.argv.forEach((arg) => { 359 | argvPtrs.push(strPtr(arg)); 360 | }); 361 | 362 | const keys = Object.keys(this.env).sort(); 363 | argvPtrs.push(keys.length); 364 | keys.forEach((key) => { 365 | argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); 366 | }); 367 | 368 | const argv = offset; 369 | argvPtrs.forEach((ptr) => { 370 | mem.setUint32(offset, ptr, true); 371 | mem.setUint32(offset + 4, 0, true); 372 | offset += 8; 373 | }); 374 | 375 | while (true) { 376 | const callbackPromise = new Promise((resolve) => { 377 | this._resolveCallbackPromise = resolve; 378 | }); 379 | this._inst.exports.run(argc, argv); 380 | if (this.exited) { 381 | break; 382 | } 383 | await callbackPromise; 384 | } 385 | } 386 | } 387 | 388 | if (isNodeJS) { 389 | if (process.argv.length < 3) { 390 | process.stderr.write("usage: go_js_wasm_exec [wasm binary] [arguments]\n"); 391 | process.exit(1); 392 | } 393 | 394 | const go = new Go(); 395 | go.argv = process.argv.slice(2); 396 | go.env = process.env; 397 | go.exit = process.exit; 398 | WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => { 399 | process.on("exit", () => { // Node.js exits if no callback is pending 400 | if (!go.exited) { 401 | console.error("error: all goroutines asleep and no JavaScript callback pending - deadlock!"); 402 | process.exit(1); 403 | } 404 | }); 405 | return go.run(result.instance); 406 | }).catch((err) => { 407 | console.error(err); 408 | go.exited = true; 409 | process.exit(1); 410 | }); 411 | } 412 | })(); 413 | --------------------------------------------------------------------------------