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 multiple JavaScript environments to a single common API,
|
---|
7 | // preferring web standards over Node.js API.
|
---|
8 | //
|
---|
9 | // Environments considered:
|
---|
10 | // - Browsers
|
---|
11 | // - Node.js
|
---|
12 | // - Electron
|
---|
13 | // - Parcel
|
---|
14 | // - Webpack
|
---|
15 |
|
---|
16 | if (typeof global !== "undefined") {
|
---|
17 | // global already exists
|
---|
18 | } else if (typeof window !== "undefined") {
|
---|
19 | window.global = window;
|
---|
20 | } else if (typeof self !== "undefined") {
|
---|
21 | self.global = self;
|
---|
22 | } else {
|
---|
23 | throw new Error("cannot export Go (neither global, window nor self is defined)");
|
---|
24 | }
|
---|
25 |
|
---|
26 | if (!global.require && typeof require !== "undefined") {
|
---|
27 | global.require = require;
|
---|
28 | }
|
---|
29 |
|
---|
30 | if (!global.fs && global.require) {
|
---|
31 | const fs = require("fs");
|
---|
32 | if (typeof fs === "object" && fs !== null && Object.keys(fs).length !== 0) {
|
---|
33 |
|
---|
34 | global.fs = Object.assign({}, fs, {
|
---|
35 | // Hack around a Unicode bug in node: https://github.com/nodejs/node/issues/24550
|
---|
36 | write(fd, buf, offset, length, position, callback) {
|
---|
37 | if (offset === 0 && length === buf.length && position === null) {
|
---|
38 | if (fd === process.stdout.fd) {
|
---|
39 | try {
|
---|
40 | process.stdout.write(buf, err => err ? callback(err, 0, null) : callback(null, length, buf));
|
---|
41 | } catch (err) {
|
---|
42 | callback(err, 0, null);
|
---|
43 | }
|
---|
44 | return;
|
---|
45 | }
|
---|
46 | if (fd === process.stderr.fd) {
|
---|
47 | try {
|
---|
48 | process.stderr.write(buf, err => err ? callback(err, 0, null) : callback(null, length, buf));
|
---|
49 | } catch (err) {
|
---|
50 | callback(err, 0, null);
|
---|
51 | }
|
---|
52 | return;
|
---|
53 | }
|
---|
54 | }
|
---|
55 | fs.write(fd, buf, offset, length, position, callback);
|
---|
56 | },
|
---|
57 | });
|
---|
58 |
|
---|
59 | }
|
---|
60 | }
|
---|
61 |
|
---|
62 | const enosys = () => {
|
---|
63 | const err = new Error("not implemented");
|
---|
64 | err.code = "ENOSYS";
|
---|
65 | return err;
|
---|
66 | };
|
---|
67 |
|
---|
68 | if (!global.fs) {
|
---|
69 | let outputBuf = "";
|
---|
70 | global.fs = {
|
---|
71 | constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
|
---|
72 | writeSync(fd, buf) {
|
---|
73 | outputBuf += decoder.decode(buf);
|
---|
74 | const nl = outputBuf.lastIndexOf("\n");
|
---|
75 | if (nl != -1) {
|
---|
76 | console.log(outputBuf.substr(0, nl));
|
---|
77 | outputBuf = outputBuf.substr(nl + 1);
|
---|
78 | }
|
---|
79 | return buf.length;
|
---|
80 | },
|
---|
81 | write(fd, buf, offset, length, position, callback) {
|
---|
82 | if (offset !== 0 || length !== buf.length || position !== null) {
|
---|
83 | callback(enosys());
|
---|
84 | return;
|
---|
85 | }
|
---|
86 | const n = this.writeSync(fd, buf);
|
---|
87 | callback(null, n);
|
---|
88 | },
|
---|
89 | chmod(path, mode, callback) { callback(enosys()); },
|
---|
90 | chown(path, uid, gid, callback) { callback(enosys()); },
|
---|
91 | close(fd, callback) { callback(enosys()); },
|
---|
92 | fchmod(fd, mode, callback) { callback(enosys()); },
|
---|
93 | fchown(fd, uid, gid, callback) { callback(enosys()); },
|
---|
94 | fstat(fd, callback) { callback(enosys()); },
|
---|
95 | fsync(fd, callback) { callback(null); },
|
---|
96 | ftruncate(fd, length, callback) { callback(enosys()); },
|
---|
97 | lchown(path, uid, gid, callback) { callback(enosys()); },
|
---|
98 | link(path, link, callback) { callback(enosys()); },
|
---|
99 | lstat(path, callback) { callback(enosys()); },
|
---|
100 | mkdir(path, perm, callback) { callback(enosys()); },
|
---|
101 | open(path, flags, mode, callback) { callback(enosys()); },
|
---|
102 | read(fd, buffer, offset, length, position, callback) { callback(enosys()); },
|
---|
103 | readdir(path, callback) { callback(enosys()); },
|
---|
104 | readlink(path, callback) { callback(enosys()); },
|
---|
105 | rename(from, to, callback) { callback(enosys()); },
|
---|
106 | rmdir(path, callback) { callback(enosys()); },
|
---|
107 | stat(path, callback) { callback(enosys()); },
|
---|
108 | symlink(path, link, callback) { callback(enosys()); },
|
---|
109 | truncate(path, length, callback) { callback(enosys()); },
|
---|
110 | unlink(path, callback) { callback(enosys()); },
|
---|
111 | utimes(path, atime, mtime, callback) { callback(enosys()); },
|
---|
112 | };
|
---|
113 | }
|
---|
114 |
|
---|
115 | if (!global.process) {
|
---|
116 | global.process = {
|
---|
117 | getuid() { return -1; },
|
---|
118 | getgid() { return -1; },
|
---|
119 | geteuid() { return -1; },
|
---|
120 | getegid() { return -1; },
|
---|
121 | getgroups() { throw enosys(); },
|
---|
122 | pid: -1,
|
---|
123 | ppid: -1,
|
---|
124 | umask() { throw enosys(); },
|
---|
125 | cwd() { throw enosys(); },
|
---|
126 | chdir() { throw enosys(); },
|
---|
127 | }
|
---|
128 | }
|
---|
129 |
|
---|
130 | if (!global.crypto && global.require) {
|
---|
131 | const nodeCrypto = require("crypto");
|
---|
132 | global.crypto = {
|
---|
133 | getRandomValues(b) {
|
---|
134 | nodeCrypto.randomFillSync(b);
|
---|
135 | },
|
---|
136 | };
|
---|
137 | }
|
---|
138 | if (!global.crypto) {
|
---|
139 | throw new Error("global.crypto is not available, polyfill required (getRandomValues only)");
|
---|
140 | }
|
---|
141 |
|
---|
142 | if (!global.performance) {
|
---|
143 | global.performance = {
|
---|
144 | now() {
|
---|
145 | const [sec, nsec] = process.hrtime();
|
---|
146 | return sec * 1000 + nsec / 1000000;
|
---|
147 | },
|
---|
148 | };
|
---|
149 | }
|
---|
150 |
|
---|
151 | if (!global.TextEncoder && global.require) {
|
---|
152 | global.TextEncoder = require("util").TextEncoder;
|
---|
153 | }
|
---|
154 | if (!global.TextEncoder) {
|
---|
155 | throw new Error("global.TextEncoder is not available, polyfill required");
|
---|
156 | }
|
---|
157 |
|
---|
158 | if (!global.TextDecoder && global.require) {
|
---|
159 | global.TextDecoder = require("util").TextDecoder;
|
---|
160 | }
|
---|
161 | if (!global.TextDecoder) {
|
---|
162 | throw new Error("global.TextDecoder is not available, polyfill required");
|
---|
163 | }
|
---|
164 |
|
---|
165 | // End of polyfills for common API.
|
---|
166 |
|
---|
167 | const encoder = new TextEncoder("utf-8");
|
---|
168 | const decoder = new TextDecoder("utf-8");
|
---|
169 |
|
---|
170 | global.Go = class {
|
---|
171 | constructor() {
|
---|
172 | this.argv = ["js"];
|
---|
173 | this.env = {};
|
---|
174 | this.exit = (code) => {
|
---|
175 | if (code !== 0) {
|
---|
176 | console.warn("exit code:", code);
|
---|
177 | }
|
---|
178 | };
|
---|
179 | this._exitPromise = new Promise((resolve) => {
|
---|
180 | this._resolveExitPromise = resolve;
|
---|
181 | });
|
---|
182 | this._pendingEvent = null;
|
---|
183 | this._scheduledTimeouts = new Map();
|
---|
184 | this._nextCallbackTimeoutID = 1;
|
---|
185 |
|
---|
186 | const setInt64 = (addr, v) => {
|
---|
187 | this.mem.setUint32(addr + 0, v, true);
|
---|
188 | this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true);
|
---|
189 | }
|
---|
190 |
|
---|
191 | const getInt64 = (addr) => {
|
---|
192 | const low = this.mem.getUint32(addr + 0, true);
|
---|
193 | const high = this.mem.getInt32(addr + 4, true);
|
---|
194 | return low + high * 4294967296;
|
---|
195 | }
|
---|
196 |
|
---|
197 | const loadValue = (addr) => {
|
---|
198 | const f = this.mem.getFloat64(addr, true);
|
---|
199 | if (f === 0) {
|
---|
200 | return undefined;
|
---|
201 | }
|
---|
202 | if (!isNaN(f)) {
|
---|
203 | return f;
|
---|
204 | }
|
---|
205 |
|
---|
206 | const id = this.mem.getUint32(addr, true);
|
---|
207 | return this._values[id];
|
---|
208 | }
|
---|
209 |
|
---|
210 | const storeValue = (addr, v) => {
|
---|
211 | const nanHead = 0x7FF80000;
|
---|
212 |
|
---|
213 | if (typeof v === "number" && v !== 0) {
|
---|
214 | if (isNaN(v)) {
|
---|
215 | this.mem.setUint32(addr + 4, nanHead, true);
|
---|
216 | this.mem.setUint32(addr, 0, true);
|
---|
217 | return;
|
---|
218 | }
|
---|
219 | this.mem.setFloat64(addr, v, true);
|
---|
220 | return;
|
---|
221 | }
|
---|
222 |
|
---|
223 | if (v === undefined) {
|
---|
224 | this.mem.setFloat64(addr, 0, true);
|
---|
225 | return;
|
---|
226 | }
|
---|
227 |
|
---|
228 | let id = this._ids.get(v);
|
---|
229 | if (id === undefined) {
|
---|
230 | id = this._idPool.pop();
|
---|
231 | if (id === undefined) {
|
---|
232 | id = this._values.length;
|
---|
233 | }
|
---|
234 | this._values[id] = v;
|
---|
235 | this._goRefCounts[id] = 0;
|
---|
236 | this._ids.set(v, id);
|
---|
237 | }
|
---|
238 | this._goRefCounts[id]++;
|
---|
239 | let typeFlag = 0;
|
---|
240 | switch (typeof v) {
|
---|
241 | case "object":
|
---|
242 | if (v !== null) {
|
---|
243 | typeFlag = 1;
|
---|
244 | }
|
---|
245 | break;
|
---|
246 | case "string":
|
---|
247 | typeFlag = 2;
|
---|
248 | break;
|
---|
249 | case "symbol":
|
---|
250 | typeFlag = 3;
|
---|
251 | break;
|
---|
252 | case "function":
|
---|
253 | typeFlag = 4;
|
---|
254 | break;
|
---|
255 | }
|
---|
256 | this.mem.setUint32(addr + 4, nanHead | typeFlag, true);
|
---|
257 | this.mem.setUint32(addr, id, true);
|
---|
258 | }
|
---|
259 |
|
---|
260 | const loadSlice = (addr) => {
|
---|
261 | const array = getInt64(addr + 0);
|
---|
262 | const len = getInt64(addr + 8);
|
---|
263 | return new Uint8Array(this._inst.exports.mem.buffer, array, len);
|
---|
264 | }
|
---|
265 |
|
---|
266 | const loadSliceOfValues = (addr) => {
|
---|
267 | const array = getInt64(addr + 0);
|
---|
268 | const len = getInt64(addr + 8);
|
---|
269 | const a = new Array(len);
|
---|
270 | for (let i = 0; i < len; i++) {
|
---|
271 | a[i] = loadValue(array + i * 8);
|
---|
272 | }
|
---|
273 | return a;
|
---|
274 | }
|
---|
275 |
|
---|
276 | const loadString = (addr) => {
|
---|
277 | const saddr = getInt64(addr + 0);
|
---|
278 | const len = getInt64(addr + 8);
|
---|
279 | return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
|
---|
280 | }
|
---|
281 |
|
---|
282 | const timeOrigin = Date.now() - performance.now();
|
---|
283 | this.importObject = {
|
---|
284 | go: {
|
---|
285 | // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)
|
---|
286 | // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported
|
---|
287 | // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function).
|
---|
288 | // This changes the SP, thus we have to update the SP used by the imported function.
|
---|
289 |
|
---|
290 | // func wasmExit(code int32)
|
---|
291 | "runtime.wasmExit": (sp) => {
|
---|
292 | sp >>>= 0;
|
---|
293 | const code = this.mem.getInt32(sp + 8, true);
|
---|
294 | this.exited = true;
|
---|
295 | delete this._inst;
|
---|
296 | delete this._values;
|
---|
297 | delete this._goRefCounts;
|
---|
298 | delete this._ids;
|
---|
299 | delete this._idPool;
|
---|
300 | this.exit(code);
|
---|
301 | },
|
---|
302 |
|
---|
303 | // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
|
---|
304 | "runtime.wasmWrite": (sp) => {
|
---|
305 | sp >>>= 0;
|
---|
306 | const fd = getInt64(sp + 8);
|
---|
307 | const p = getInt64(sp + 16);
|
---|
308 | const n = this.mem.getInt32(sp + 24, true);
|
---|
309 | fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
|
---|
310 | },
|
---|
311 |
|
---|
312 | // func resetMemoryDataView()
|
---|
313 | "runtime.resetMemoryDataView": (sp) => {
|
---|
314 | sp >>>= 0;
|
---|
315 | this.mem = new DataView(this._inst.exports.mem.buffer);
|
---|
316 | },
|
---|
317 |
|
---|
318 | // func nanotime1() int64
|
---|
319 | "runtime.nanotime1": (sp) => {
|
---|
320 | sp >>>= 0;
|
---|
321 | setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
|
---|
322 | },
|
---|
323 |
|
---|
324 | // func walltime() (sec int64, nsec int32)
|
---|
325 | "runtime.walltime": (sp) => {
|
---|
326 | sp >>>= 0;
|
---|
327 | const msec = (new Date).getTime();
|
---|
328 | setInt64(sp + 8, msec / 1000);
|
---|
329 | this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true);
|
---|
330 | },
|
---|
331 |
|
---|
332 | // func scheduleTimeoutEvent(delay int64) int32
|
---|
333 | "runtime.scheduleTimeoutEvent": (sp) => {
|
---|
334 | sp >>>= 0;
|
---|
335 | const id = this._nextCallbackTimeoutID;
|
---|
336 | this._nextCallbackTimeoutID++;
|
---|
337 | this._scheduledTimeouts.set(id, setTimeout(
|
---|
338 | () => {
|
---|
339 | this._resume();
|
---|
340 | while (this._scheduledTimeouts.has(id)) {
|
---|
341 | // for some reason Go failed to register the timeout event, log and try again
|
---|
342 | // (temporary workaround for https://github.com/golang/go/issues/28975)
|
---|
343 | console.warn("scheduleTimeoutEvent: missed timeout event");
|
---|
344 | this._resume();
|
---|
345 | }
|
---|
346 | },
|
---|
347 | getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early
|
---|
348 | ));
|
---|
349 | this.mem.setInt32(sp + 16, id, true);
|
---|
350 | },
|
---|
351 |
|
---|
352 | // func clearTimeoutEvent(id int32)
|
---|
353 | "runtime.clearTimeoutEvent": (sp) => {
|
---|
354 | sp >>>= 0;
|
---|
355 | const id = this.mem.getInt32(sp + 8, true);
|
---|
356 | clearTimeout(this._scheduledTimeouts.get(id));
|
---|
357 | this._scheduledTimeouts.delete(id);
|
---|
358 | },
|
---|
359 |
|
---|
360 | // func getRandomData(r []byte)
|
---|
361 | "runtime.getRandomData": (sp) => {
|
---|
362 | sp >>>= 0;
|
---|
363 | crypto.getRandomValues(loadSlice(sp + 8));
|
---|
364 | },
|
---|
365 |
|
---|
366 | // func finalizeRef(v ref)
|
---|
367 | "syscall/js.finalizeRef": (sp) => {
|
---|
368 | sp >>>= 0;
|
---|
369 | const id = this.mem.getUint32(sp + 8, true);
|
---|
370 | this._goRefCounts[id]--;
|
---|
371 | if (this._goRefCounts[id] === 0) {
|
---|
372 | const v = this._values[id];
|
---|
373 | this._values[id] = null;
|
---|
374 | this._ids.delete(v);
|
---|
375 | this._idPool.push(id);
|
---|
376 | }
|
---|
377 | },
|
---|
378 |
|
---|
379 | // func stringVal(value string) ref
|
---|
380 | "syscall/js.stringVal": (sp) => {
|
---|
381 | sp >>>= 0;
|
---|
382 | storeValue(sp + 24, loadString(sp + 8));
|
---|
383 | },
|
---|
384 |
|
---|
385 | // func valueGet(v ref, p string) ref
|
---|
386 | "syscall/js.valueGet": (sp) => {
|
---|
387 | sp >>>= 0;
|
---|
388 | const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16));
|
---|
389 | sp = this._inst.exports.getsp() >>> 0; // see comment above
|
---|
390 | storeValue(sp + 32, result);
|
---|
391 | },
|
---|
392 |
|
---|
393 | // func valueSet(v ref, p string, x ref)
|
---|
394 | "syscall/js.valueSet": (sp) => {
|
---|
395 | sp >>>= 0;
|
---|
396 | Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
|
---|
397 | },
|
---|
398 |
|
---|
399 | // func valueDelete(v ref, p string)
|
---|
400 | "syscall/js.valueDelete": (sp) => {
|
---|
401 | sp >>>= 0;
|
---|
402 | Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16));
|
---|
403 | },
|
---|
404 |
|
---|
405 | // func valueIndex(v ref, i int) ref
|
---|
406 | "syscall/js.valueIndex": (sp) => {
|
---|
407 | sp >>>= 0;
|
---|
408 | storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
|
---|
409 | },
|
---|
410 |
|
---|
411 | // valueSetIndex(v ref, i int, x ref)
|
---|
412 | "syscall/js.valueSetIndex": (sp) => {
|
---|
413 | sp >>>= 0;
|
---|
414 | Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
|
---|
415 | },
|
---|
416 |
|
---|
417 | // func valueCall(v ref, m string, args []ref) (ref, bool)
|
---|
418 | "syscall/js.valueCall": (sp) => {
|
---|
419 | sp >>>= 0;
|
---|
420 | try {
|
---|
421 | const v = loadValue(sp + 8);
|
---|
422 | const m = Reflect.get(v, loadString(sp + 16));
|
---|
423 | const args = loadSliceOfValues(sp + 32);
|
---|
424 | const result = Reflect.apply(m, v, args);
|
---|
425 | sp = this._inst.exports.getsp() >>> 0; // see comment above
|
---|
426 | storeValue(sp + 56, result);
|
---|
427 | this.mem.setUint8(sp + 64, 1);
|
---|
428 | } catch (err) {
|
---|
429 | sp = this._inst.exports.getsp() >>> 0; // see comment above
|
---|
430 | storeValue(sp + 56, err);
|
---|
431 | this.mem.setUint8(sp + 64, 0);
|
---|
432 | }
|
---|
433 | },
|
---|
434 |
|
---|
435 | // func valueInvoke(v ref, args []ref) (ref, bool)
|
---|
436 | "syscall/js.valueInvoke": (sp) => {
|
---|
437 | sp >>>= 0;
|
---|
438 | try {
|
---|
439 | const v = loadValue(sp + 8);
|
---|
440 | const args = loadSliceOfValues(sp + 16);
|
---|
441 | const result = Reflect.apply(v, undefined, args);
|
---|
442 | sp = this._inst.exports.getsp() >>> 0; // see comment above
|
---|
443 | storeValue(sp + 40, result);
|
---|
444 | this.mem.setUint8(sp + 48, 1);
|
---|
445 | } catch (err) {
|
---|
446 | sp = this._inst.exports.getsp() >>> 0; // see comment above
|
---|
447 | storeValue(sp + 40, err);
|
---|
448 | this.mem.setUint8(sp + 48, 0);
|
---|
449 | }
|
---|
450 | },
|
---|
451 |
|
---|
452 | // func valueNew(v ref, args []ref) (ref, bool)
|
---|
453 | "syscall/js.valueNew": (sp) => {
|
---|
454 | sp >>>= 0;
|
---|
455 | try {
|
---|
456 | const v = loadValue(sp + 8);
|
---|
457 | const args = loadSliceOfValues(sp + 16);
|
---|
458 | const result = Reflect.construct(v, args);
|
---|
459 | sp = this._inst.exports.getsp() >>> 0; // see comment above
|
---|
460 | storeValue(sp + 40, result);
|
---|
461 | this.mem.setUint8(sp + 48, 1);
|
---|
462 | } catch (err) {
|
---|
463 | sp = this._inst.exports.getsp() >>> 0; // see comment above
|
---|
464 | storeValue(sp + 40, err);
|
---|
465 | this.mem.setUint8(sp + 48, 0);
|
---|
466 | }
|
---|
467 | },
|
---|
468 |
|
---|
469 | // func valueLength(v ref) int
|
---|
470 | "syscall/js.valueLength": (sp) => {
|
---|
471 | sp >>>= 0;
|
---|
472 | setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
|
---|
473 | },
|
---|
474 |
|
---|
475 | // valuePrepareString(v ref) (ref, int)
|
---|
476 | "syscall/js.valuePrepareString": (sp) => {
|
---|
477 | sp >>>= 0;
|
---|
478 | const str = encoder.encode(String(loadValue(sp + 8)));
|
---|
479 | storeValue(sp + 16, str);
|
---|
480 | setInt64(sp + 24, str.length);
|
---|
481 | },
|
---|
482 |
|
---|
483 | // valueLoadString(v ref, b []byte)
|
---|
484 | "syscall/js.valueLoadString": (sp) => {
|
---|
485 | sp >>>= 0;
|
---|
486 | const str = loadValue(sp + 8);
|
---|
487 | loadSlice(sp + 16).set(str);
|
---|
488 | },
|
---|
489 |
|
---|
490 | // func valueInstanceOf(v ref, t ref) bool
|
---|
491 | "syscall/js.valueInstanceOf": (sp) => {
|
---|
492 | sp >>>= 0;
|
---|
493 | this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0);
|
---|
494 | },
|
---|
495 |
|
---|
496 | // func copyBytesToGo(dst []byte, src ref) (int, bool)
|
---|
497 | "syscall/js.copyBytesToGo": (sp) => {
|
---|
498 | sp >>>= 0;
|
---|
499 | const dst = loadSlice(sp + 8);
|
---|
500 | const src = loadValue(sp + 32);
|
---|
501 | if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) {
|
---|
502 | this.mem.setUint8(sp + 48, 0);
|
---|
503 | return;
|
---|
504 | }
|
---|
505 | const toCopy = src.subarray(0, dst.length);
|
---|
506 | dst.set(toCopy);
|
---|
507 | setInt64(sp + 40, toCopy.length);
|
---|
508 | this.mem.setUint8(sp + 48, 1);
|
---|
509 | },
|
---|
510 |
|
---|
511 | // func copyBytesToJS(dst ref, src []byte) (int, bool)
|
---|
512 | "syscall/js.copyBytesToJS": (sp) => {
|
---|
513 | sp >>>= 0;
|
---|
514 | const dst = loadValue(sp + 8);
|
---|
515 | const src = loadSlice(sp + 16);
|
---|
516 | if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) {
|
---|
517 | this.mem.setUint8(sp + 48, 0);
|
---|
518 | return;
|
---|
519 | }
|
---|
520 | const toCopy = src.subarray(0, dst.length);
|
---|
521 | dst.set(toCopy);
|
---|
522 | setInt64(sp + 40, toCopy.length);
|
---|
523 | this.mem.setUint8(sp + 48, 1);
|
---|
524 | },
|
---|
525 |
|
---|
526 | "debug": (value) => {
|
---|
527 | console.log(value);
|
---|
528 | },
|
---|
529 | }
|
---|
530 | };
|
---|
531 | }
|
---|
532 |
|
---|
533 | async run(instance) {
|
---|
534 | if (!(instance instanceof WebAssembly.Instance)) {
|
---|
535 | throw new Error("Go.run: WebAssembly.Instance expected");
|
---|
536 | }
|
---|
537 | this._inst = instance;
|
---|
538 | this.mem = new DataView(this._inst.exports.mem.buffer);
|
---|
539 | this._values = [ // JS values that Go currently has references to, indexed by reference id
|
---|
540 | NaN,
|
---|
541 | 0,
|
---|
542 | null,
|
---|
543 | true,
|
---|
544 | false,
|
---|
545 | global,
|
---|
546 | this,
|
---|
547 | ];
|
---|
548 | this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id
|
---|
549 | this._ids = new Map([ // mapping from JS values to reference ids
|
---|
550 | [0, 1],
|
---|
551 | [null, 2],
|
---|
552 | [true, 3],
|
---|
553 | [false, 4],
|
---|
554 | [global, 5],
|
---|
555 | [this, 6],
|
---|
556 | ]);
|
---|
557 | this._idPool = []; // unused ids that have been garbage collected
|
---|
558 | this.exited = false; // whether the Go program has exited
|
---|
559 |
|
---|
560 | // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
|
---|
561 | let offset = 4096;
|
---|
562 |
|
---|
563 | const strPtr = (str) => {
|
---|
564 | const ptr = offset;
|
---|
565 | const bytes = encoder.encode(str + "\0");
|
---|
566 | new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes);
|
---|
567 | offset += bytes.length;
|
---|
568 | if (offset % 8 !== 0) {
|
---|
569 | offset += 8 - (offset % 8);
|
---|
570 | }
|
---|
571 | return ptr;
|
---|
572 | };
|
---|
573 |
|
---|
574 | const argc = this.argv.length;
|
---|
575 |
|
---|
576 | const argvPtrs = [];
|
---|
577 | this.argv.forEach((arg) => {
|
---|
578 | argvPtrs.push(strPtr(arg));
|
---|
579 | });
|
---|
580 | argvPtrs.push(0);
|
---|
581 |
|
---|
582 | const keys = Object.keys(this.env).sort();
|
---|
583 | keys.forEach((key) => {
|
---|
584 | argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
|
---|
585 | });
|
---|
586 | argvPtrs.push(0);
|
---|
587 |
|
---|
588 | const argv = offset;
|
---|
589 | argvPtrs.forEach((ptr) => {
|
---|
590 | this.mem.setUint32(offset, ptr, true);
|
---|
591 | this.mem.setUint32(offset + 4, 0, true);
|
---|
592 | offset += 8;
|
---|
593 | });
|
---|
594 |
|
---|
595 | this._inst.exports.run(argc, argv);
|
---|
596 | if (this.exited) {
|
---|
597 | this._resolveExitPromise();
|
---|
598 | }
|
---|
599 | await this._exitPromise;
|
---|
600 | }
|
---|
601 |
|
---|
602 | _resume() {
|
---|
603 | if (this.exited) {
|
---|
604 | throw new Error("Go program has already exited");
|
---|
605 | }
|
---|
606 | this._inst.exports.resume();
|
---|
607 | if (this.exited) {
|
---|
608 | this._resolveExitPromise();
|
---|
609 | }
|
---|
610 | }
|
---|
611 |
|
---|
612 | _makeFuncWrapper(id) {
|
---|
613 | const go = this;
|
---|
614 | return function () {
|
---|
615 | const event = { id: id, this: this, args: arguments };
|
---|
616 | go._pendingEvent = event;
|
---|
617 | go._resume();
|
---|
618 | return event.result;
|
---|
619 | };
|
---|
620 | }
|
---|
621 | }
|
---|
622 |
|
---|
623 | if (
|
---|
624 | typeof module !== "undefined" &&
|
---|
625 | global.require &&
|
---|
626 | global.require.main === module &&
|
---|
627 | global.process &&
|
---|
628 | global.process.versions &&
|
---|
629 | !global.process.versions.electron
|
---|
630 | ) {
|
---|
631 | if (process.argv.length < 3) {
|
---|
632 | console.error("usage: go_js_wasm_exec [wasm binary] [arguments]");
|
---|
633 | process.exit(1);
|
---|
634 | }
|
---|
635 |
|
---|
636 | const go = new Go();
|
---|
637 | go.argv = process.argv.slice(2);
|
---|
638 | go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env);
|
---|
639 | go.exit = process.exit;
|
---|
640 | WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
|
---|
641 | process.on("exit", (code) => { // Node.js exits if no event handler is pending
|
---|
642 | if (code === 0 && !go.exited) {
|
---|
643 | // deadlock, make Go print error and stack traces
|
---|
644 | go._pendingEvent = { id: 0 };
|
---|
645 | go._resume();
|
---|
646 | }
|
---|
647 | });
|
---|
648 | return go.run(result.instance);
|
---|
649 | }).catch((err) => {
|
---|
650 | console.error(err);
|
---|
651 | process.exit(1);
|
---|
652 | });
|
---|
653 | }
|
---|
654 | })();
|
---|