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// This file has been modified for use by the TinyGo compiler. 6 7(() => { 8 // Map multiple JavaScript environments to a single common API, 9 // preferring web standards over Node.js API. 10 // 11 // Environments considered: 12 // - Browsers 13 // - Node.js 14 // - Electron 15 // - Parcel 16 17 if (typeof global !== "undefined") { 18 // global already exists 19 } else if (typeof window !== "undefined") { 20 window.global = window; 21 } else if (typeof self !== "undefined") { 22 self.global = self; 23 } else { 24 throw new Error( 25 "cannot export Go (neither global, window nor self is defined)" 26 ); 27 } 28 29 if (!global.require && typeof require !== "undefined") { 30 global.require = require; 31 } 32 33 if (!global.fs && global.require) { 34 global.fs = require("fs"); 35 } 36 37 const enosys = () => { 38 const err = new Error("not implemented"); 39 err.code = "ENOSYS"; 40 return err; 41 }; 42 43 if (!global.fs) { 44 let outputBuf = ""; 45 global.fs = { 46 constants: { 47 O_WRONLY: -1, 48 O_RDWR: -1, 49 O_CREAT: -1, 50 O_TRUNC: -1, 51 O_APPEND: -1, 52 O_EXCL: -1 53 }, // unused 54 writeSync(fd, buf) { 55 outputBuf += decoder.decode(buf); 56 const nl = outputBuf.lastIndexOf("\n"); 57 if (nl != -1) { 58 console.log(outputBuf.substr(0, nl)); 59 outputBuf = outputBuf.substr(nl + 1); 60 } 61 return buf.length; 62 }, 63 write(fd, buf, offset, length, position, callback) { 64 if (offset !== 0 || length !== buf.length || position !== null) { 65 callback(enosys()); 66 return; 67 } 68 const n = this.writeSync(fd, buf); 69 callback(null, n); 70 }, 71 chmod(path, mode, callback) { 72 callback(enosys()); 73 }, 74 chown(path, uid, gid, callback) { 75 callback(enosys()); 76 }, 77 close(fd, callback) { 78 callback(enosys()); 79 }, 80 fchmod(fd, mode, callback) { 81 callback(enosys()); 82 }, 83 fchown(fd, uid, gid, callback) { 84 callback(enosys()); 85 }, 86 fstat(fd, callback) { 87 callback(enosys()); 88 }, 89 fsync(fd, callback) { 90 callback(null); 91 }, 92 ftruncate(fd, length, callback) { 93 callback(enosys()); 94 }, 95 lchown(path, uid, gid, callback) { 96 callback(enosys()); 97 }, 98 link(path, link, callback) { 99 callback(enosys()); 100 }, 101 lstat(path, callback) { 102 callback(enosys()); 103 }, 104 mkdir(path, perm, callback) { 105 callback(enosys()); 106 }, 107 open(path, flags, mode, callback) { 108 callback(enosys()); 109 }, 110 read(fd, buffer, offset, length, position, callback) { 111 callback(enosys()); 112 }, 113 readdir(path, callback) { 114 callback(enosys()); 115 }, 116 readlink(path, callback) { 117 callback(enosys()); 118 }, 119 rename(from, to, callback) { 120 callback(enosys()); 121 }, 122 rmdir(path, callback) { 123 callback(enosys()); 124 }, 125 stat(path, callback) { 126 callback(enosys()); 127 }, 128 symlink(path, link, callback) { 129 callback(enosys()); 130 }, 131 truncate(path, length, callback) { 132 callback(enosys()); 133 }, 134 unlink(path, callback) { 135 callback(enosys()); 136 }, 137 utimes(path, atime, mtime, callback) { 138 callback(enosys()); 139 } 140 }; 141 } 142 143 if (!global.process) { 144 global.process = { 145 getuid() { 146 return -1; 147 }, 148 getgid() { 149 return -1; 150 }, 151 geteuid() { 152 return -1; 153 }, 154 getegid() { 155 return -1; 156 }, 157 getgroups() { 158 throw enosys(); 159 }, 160 pid: -1, 161 ppid: -1, 162 umask() { 163 throw enosys(); 164 }, 165 cwd() { 166 throw enosys(); 167 }, 168 chdir() { 169 throw enosys(); 170 } 171 }; 172 } 173 174 if (!global.crypto) { 175 const nodeCrypto = require("crypto"); 176 global.crypto = { 177 getRandomValues(b) { 178 nodeCrypto.randomFillSync(b); 179 } 180 }; 181 } 182 183 if (!global.performance) { 184 global.performance = { 185 now() { 186 const [sec, nsec] = process.hrtime(); 187 return sec * 1000 + nsec / 1000000; 188 } 189 }; 190 } 191 192 if (!global.TextEncoder) { 193 global.TextEncoder = require("util").TextEncoder; 194 } 195 196 if (!global.TextDecoder) { 197 global.TextDecoder = require("util").TextDecoder; 198 } 199
200 // End of polyfills for common API. 201 202 const encoder = new TextEncoder("utf-8"); 203 const decoder = new TextDecoder("utf-8"); 204 var logLine = []; 205 206 global.Go = class { 207 constructor() { 208 this._callbackTimeouts = new Map(); 209 this._nextCallbackTimeoutID = 1; 210 211 const mem = () => { 212 // The buffer may change when requesting more memory. 213 return new DataView(this._inst.exports.memory.buffer); 214 }; 215 216 const setInt64 = (addr, v) => { 217 mem().setUint32(addr + 0, v, true); 218 mem().setUint32(addr + 4, Math.floor(v / 4294967296), true); 219 }; 220 221 const getInt64 = addr => { 222 const low = mem().getUint32(addr + 0, true); 223 const high = mem().getInt32(addr + 4, true); 224 return low + high * 4294967296; 225 }; 226 227 const loadValue = addr => { 228 const f = mem().getFloat64(addr, true); 229 if (f === 0) { 230 return undefined; 231 } 232 if (!isNaN(f)) { 233 return f; 234 } 235 236 const id = mem().getUint32(addr, true); 237 return this._values[id]; 238 }; 239 240 const storeValue = (addr, v) => { 241 const nanHead = 0x7ff80000; 242 243 if (typeof v === "number") { 244 if (isNaN(v)) { 245 mem().setUint32(addr + 4, nanHead, true); 246 mem().setUint32(addr, 0, true); 247 return; 248 } 249 if (v === 0) { 250 mem().setUint32(addr + 4, nanHead, true); 251 mem().setUint32(addr, 1, true); 252 return; 253 } 254 mem().setFloat64(addr, v, true); 255 return; 256 } 257 258 switch (v) { 259 case undefined: 260 mem().setFloat64(addr, 0, true); 261 return; 262 case null: 263 mem().setUint32(addr + 4, nanHead, true); 264 mem().setUint32(addr, 2, true); 265 return; 266 case true: 267 mem().setUint32(addr + 4, nanHead, true); 268 mem().setUint32(addr, 3, true); 269 return; 270 case false: 271 mem().setUint32(addr + 4, nanHead, true); 272 mem().setUint32(addr, 4, true); 273 return; 274 } 275 276 let id = this._ids.get(v); 277 if (id === undefined) { 278 id = this._idPool.pop(); 279 if (id === undefined) { 280 id = this._values.length; 281 } 282 this._values[id] = v; 283 this._goRefCounts[id] = 0; 284 this._ids.set(v, id); 285 } 286 this._goRefCounts[id]++; 287 let typeFlag = 1; 288 switch (typeof v) { 289 case "string": 290 typeFlag = 2; 291 break; 292 case "symbol": 293 typeFlag = 3; 294 break; 295 case "function": 296 typeFlag = 4; 297 break; 298 } 299 mem().setUint32(addr + 4, nanHead | typeFlag, true); 300 mem().setUint32(addr, id, true); 301 }; 302 303 const loadSlice = (array, len, cap) => { 304 return new Uint8Array(this._inst.exports.memory.buffer, array, len); 305 }; 306 307 const loadSliceOfValues = (array, len, cap) => { 308 const a = new Array(len); 309 for (let i = 0; i < len; i++) { 310 a[i] = loadValue(array + i * 8); 311 } 312 return a; 313 }; 314 315 const loadString = (ptr, len) => { 316 return decoder.decode( 317 new DataView(this._inst.exports.memory.buffer, ptr, len) 318 ); 319 }; 320 321 const timeOrigin = Date.now() - performance.now(); 322 this.importObject = { 323 wasi_unstable: { 324 // https://github.com/bytecodealliance/wasmtime/blob/master/docs/WASI-api.md#__wasi_fd_write 325 fd_write: function(fd, iovs_ptr, iovs_len, nwritten_ptr) { 326 let nwritten = 0; 327 if (fd == 1) { 328 for (let iovs_i = 0; iovs_i < iovs_len; iovs_i++) { 329 let iov_ptr = iovs_ptr + iovs_i * 8; // assuming wasm32 330 let ptr = mem().getUint32(iov_ptr + 0, true); 331 let len = mem().getUint32(iov_ptr + 4, true); 332 for (let i = 0; i < len; i++) { 333 let c = mem().getUint8(ptr + i); 334 if (c == 13) { 335 // CR 336 // ignore 337 } else if (c == 10) { 338 // LF 339 // write line 340 let line = decoder.decode(new Uint8Array(logLine)); 341 logLine = []; 342 console.log(line); 343 } else { 344 logLine.push(c); 345 } 346 } 347 } 348 } else { 349 console.error("invalid file descriptor:", fd); 350 } 351 mem().setUint32(nwritten_ptr, nwritten, true); 352 return 0; 353 } 354 }, 355 env: { 356 // func ticks() float64 357 "runtime.ticks": () => { 358 return timeOrigin + performance.now(); 359 }, 360 361 // func sleepTicks(timeout float64) 362 "runtime.sleepTicks": timeout => {
363 // Do not sleep, only reactivate scheduler after the given timeout. 364 setTimeout(this._inst.exports.go_scheduler, timeout); 365 }, 366 367 // func finalizeRef(v ref) 368 "syscall/js.finalizeRef": sp => { 369 // Note: TinyGo does not support finalizers so this should never be 370 // called. 371 console.error("syscall/js.finalizeRef not implemented"); 372 }, 373 374 // func stringVal(value string) ref 375 "syscall/js.stringVal": (ret_ptr, value_ptr, value_len) => { 376 const s = loadString(value_ptr, value_len); 377 storeValue(ret_ptr, s); 378 }, 379 380 // func valueGet(v ref, p string) ref 381 "syscall/js.valueGet": (retval, v_addr, p_ptr, p_len) => { 382 let prop = loadString(p_ptr, p_len); 383 let value = loadValue(v_addr); 384 let result = Reflect.get(value, prop); 385 storeValue(retval, result); 386 }, 387 388 // func valueSet(v ref, p string, x ref) 389 "syscall/js.valueSet": (v_addr, p_ptr, p_len, x_addr) => { 390 const v = loadValue(v_addr); 391 const p = loadString(p_ptr, p_len); 392 const x = loadValue(x_addr); 393 Reflect.set(v, p, x); 394 }, 395 396 // func valueDelete(v ref, p string) 397 "syscall/js.valueDelete": (v_addr, p_ptr, p_len) => { 398 const v = loadValue(v_addr); 399 const p = loadString(p_ptr, p_len); 400 Reflect.deleteProperty(v, p); 401 }, 402 403 // func valueIndex(v ref, i int) ref 404 "syscall/js.valueIndex": (ret_addr, v_addr, i) => { 405 storeValue(ret_addr, Reflect.get(loadValue(v_addr), i)); 406 }, 407 408 // valueSetIndex(v ref, i int, x ref) 409 "syscall/js.valueSetIndex": (v_addr, i, x_addr) => { 410 Reflect.set(loadValue(v_addr), i, loadValue(x_addr)); 411 }, 412 413 // func valueCall(v ref, m string, args []ref) (ref, bool) 414 "syscall/js.valueCall": ( 415 ret_addr, 416 v_addr, 417 m_ptr, 418 m_len, 419 args_ptr, 420 args_len, 421 args_cap 422 ) => { 423 const v = loadValue(v_addr); 424 const name = loadString(m_ptr, m_len); 425 const args = loadSliceOfValues(args_ptr, args_len, args_cap); 426 try { 427 const m = Reflect.get(v, name); 428 storeValue(ret_addr, Reflect.apply(m, v, args)); 429 mem().setUint8(ret_addr + 8, 1); 430 } catch (err) { 431 storeValue(ret_addr, err); 432 mem().setUint8(ret_addr + 8, 0); 433 } 434 }, 435 436 // func valueInvoke(v ref, args []ref) (ref, bool) 437 "syscall/js.valueInvoke": ( 438 ret_addr, 439 v_addr, 440 args_ptr, 441 args_len, 442 args_cap 443 ) => { 444 try { 445 const v = loadValue(v_addr); 446 const args = loadSliceOfValues(args_ptr, args_len, args_cap); 447 storeValue(ret_addr, Reflect.apply(v, undefined, args)); 448 mem().setUint8(ret_addr + 8, 1); 449 } catch (err) { 450 storeValue(ret_addr, err); 451 mem().setUint8(ret_addr + 8, 0); 452 } 453 }, 454 455 // func valueNew(v ref, args []ref) (ref, bool) 456 "syscall/js.valueNew": ( 457 ret_addr, 458 v_addr, 459 args_ptr, 460 args_len, 461 args_cap 462 ) => { 463 const v = loadValue(v_addr); 464 const args = loadSliceOfValues(args_ptr, args_len, args_cap); 465 try { 466 storeValue(ret_addr, Reflect.construct(v, args)); 467 mem().setUint8(ret_addr + 8, 1); 468 } catch (err) { 469 storeValue(ret_addr, err); 470 mem().setUint8(ret_addr + 8, 0); 471 } 472 }, 473 474 // func valueLength(v ref) int 475 "syscall/js.valueLength": v_addr => { 476 return loadValue(v_addr).length; 477 }, 478 479 // valuePrepareString(v ref) (ref, int) 480 "syscall/js.valuePrepareString": (ret_addr, v_addr) => { 481 const s = String(loadValue(v_addr)); 482 const str = encoder.encode(s);
483 storeValue(ret_addr, str); 484 setInt64(ret_addr + 8, str.length); 485 }, 486 487 // valueLoadString(v ref, b []byte) 488 "syscall/js.valueLoadString": ( 489 v_addr, 490 slice_ptr, 491 slice_len, 492 slice_cap 493 ) => { 494 const str = loadValue(v_addr); 495 loadSlice(slice_ptr, slice_len, slice_cap).set(str); 496 }, 497 498 // func valueInstanceOf(v ref, t ref) bool 499 //"syscall/js.valueInstanceOf": (sp) => { 500 // mem().setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16)); 501 //}, 502 503 // func copyBytesToGo(dst []byte, src ref) (int, bool) 504 "syscall/js.copyBytesToGo": ( 505 ret_addr, 506 dest_addr, 507 dest_len, 508 dest_cap, 509 source_addr 510 ) => { 511 let num_bytes_copied_addr = ret_addr; 512 let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable 513 514 const dst = loadSlice(dest_addr, dest_len); 515 const src = loadValue(source_addr); 516 if (!(src instanceof Uint8Array)) { 517 mem().setUint8(returned_status_addr, 0); // Return "not ok" status 518 return; 519 } 520 const toCopy = src.subarray(0, dst.length); 521 dst.set(toCopy); 522 setInt64(num_bytes_copied_addr, toCopy.length); 523 mem().setUint8(returned_status_addr, 1); // Return "ok" status 524 }, 525 526 // copyBytesToJS(dst ref, src []byte) (int, bool) 527 // Originally copied from upstream Go project, then modified: 528 // https://github.com/golang/go/blob/3f995c3f3b43033013013e6c7ccc93a9b1411ca9/misc/wasm/wasm_exec.js#L404-L416 529 "syscall/js.copyBytesToJS": ( 530 ret_addr, 531 dest_addr, 532 source_addr, 533 source_len, 534 source_cap 535 ) => { 536 let num_bytes_copied_addr = ret_addr; 537 let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable 538 539 const dst = loadValue(dest_addr); 540 const src = loadSlice(source_addr, source_len); 541 if (!(dst instanceof Uint8Array)) { 542 mem().setUint8(returned_status_addr, 0); // Return "not ok" status 543 return; 544 } 545 const toCopy = src.subarray(0, dst.length); 546 dst.set(toCopy); 547 setInt64(num_bytes_copied_addr, toCopy.length); 548 mem().setUint8(returned_status_addr, 1); // Return "ok" status 549 } 550 } 551 }; 552 } 553 554 async run(instance) { 555 this._inst = instance; 556 this._values = [ 557 // JS values that Go currently has references to, indexed by reference id 558 NaN, 559 0, 560 null, 561 true, 562 false, 563 global, 564 this 565 ]; 566 this._goRefCounts = []; // number of references that Go has to a JS value, indexed by reference id 567 this._ids = new Map(); // mapping from JS values to reference ids 568 this._idPool = []; // unused ids that have been garbage collected 569 this.exited = false; // whether the Go program has exited 570 571 const mem = new DataView(this._inst.exports.memory.buffer); 572 573 while (true) { 574 const callbackPromise = new Promise(resolve => { 575 this._resolveCallbackPromise = () => { 576 if (this.exited) { 577 throw new Error("bad callback: Go program has already exited"); 578 } 579 setTimeout(resolve, 0); // make sure it is asynchronous 580 }; 581 }); 582 this._inst.exports._start(); 583 if (this.exited) { 584 break; 585 } 586 await callbackPromise; 587 } 588 } 589 590 _resume() { 591 if (this.exited) { 592 throw new Error("Go program has already exited"); 593 } 594 this._inst.exports.resume(); 595 if (this.exited) { 596 this._resolveExitPromise(); 597 } 598 } 599 600 _makeFuncWrapper(id) { 601 const go = this; 602 return function() { 603 const event = { id: id, this: this, args: arguments }; 604 go._pendingEvent = event; 605 go._resume(); 606 return event.result; 607 }; 608 } 609 }; 610 611 if ( 612 global.require && 613 global.require.main === module && 614 global.process && 615 global.process.versions && 616 !global.process.versions.electron 617 ) { 618 if (process.argv.length != 3) { 619 console.error("usage: go_js_wasm_exec [wasm binary] [arguments]"); 620 process.exit(1); 621 } 622 623 const go = new Go(); 624 WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject) 625 .then(result => { 626 return go.run(result.instance); 627 }) 628 .catch(err => { 629 console.error(err); 630 process.exit(1); 631 }); 632 } 633})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.