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.
5 export function LoadWasm() {
6 // Map web browser API and Node.js API to a single common API (preferring web standards over Node.js API).
7 if (typeof window !== "undefined") {
8 window.global = window;
9 } else if (typeof self !== "undefined") {
12 throw new Error("cannot export Go (neither window nor self is defined)");
17 constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
19 outputBuf += decoder.decode(buf);
20 const nl = outputBuf.lastIndexOf("\n");
22 console.log(outputBuf.substr(0, nl));
23 outputBuf = outputBuf.substr(nl + 1);
27 openSync(path, flags, mode) {
28 const err = new Error("not implemented");
34 const encoder = new TextEncoder("utf-8");
35 const decoder = new TextDecoder("utf-8");
41 this.exit = (code) => {
43 console.warn("exit code:", code);
45 if(typeof window.resetWasmStatus == "function") {
46 window.resetWasmStatus();
49 this._callbackTimeouts = new Map();
50 this._nextCallbackTimeoutID = 1;
53 // The buffer may change when requesting more memory.
54 return new DataView(this._inst.exports.mem.buffer);
57 const setInt64 = (addr, v) => {
58 mem().setUint32(addr + 0, v, true);
59 mem().setUint32(addr + 4, Math.floor(v / 4294967296), true);
62 const getInt64 = (addr) => {
63 const low = mem().getUint32(addr + 0, true);
64 const high = mem().getInt32(addr + 4, true);
65 return low + high * 4294967296;
68 const loadValue = (addr) => {
69 const f = mem().getFloat64(addr, true);
74 const id = mem().getUint32(addr, true);
75 return this._values[id];
78 const storeValue = (addr, v) => {
79 const nanHead = 0x7FF80000;
81 if (typeof v === "number") {
83 mem().setUint32(addr + 4, nanHead, true);
84 mem().setUint32(addr, 0, true);
87 mem().setFloat64(addr, v, true);
93 mem().setUint32(addr + 4, nanHead, true);
94 mem().setUint32(addr, 1, true);
97 mem().setUint32(addr + 4, nanHead, true);
98 mem().setUint32(addr, 2, true);
101 mem().setUint32(addr + 4, nanHead, true);
102 mem().setUint32(addr, 3, true);
105 mem().setUint32(addr + 4, nanHead, true);
106 mem().setUint32(addr, 4, true);
110 let ref = this._refs.get(v);
111 if (ref === undefined) {
112 ref = this._values.length;
113 this._values.push(v);
114 this._refs.set(v, ref);
128 mem().setUint32(addr + 4, nanHead | typeFlag, true);
129 mem().setUint32(addr, ref, true);
132 const loadSlice = (addr) => {
133 const array = getInt64(addr + 0);
134 const len = getInt64(addr + 8);
135 return new Uint8Array(this._inst.exports.mem.buffer, array, len);
138 const loadSliceOfValues = (addr) => {
139 const array = getInt64(addr + 0);
140 const len = getInt64(addr + 8);
141 const a = new Array(len);
142 for (let i = 0; i < len; i++) {
143 a[i] = loadValue(array + i * 8);
148 const loadString = (addr) => {
149 const saddr = getInt64(addr + 0);
150 const len = getInt64(addr + 8);
151 return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
154 const timeOrigin = Date.now() - performance.now();
155 this.importObject = {
157 // func wasmExit(code int32)
158 "runtime.wasmExit": (sp) => {
159 const code = mem().getInt32(sp + 8, true);
167 // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
168 "runtime.wasmWrite": (sp) => {
169 const fd = getInt64(sp + 8);
170 const p = getInt64(sp + 16);
171 const n = mem().getInt32(sp + 24, true);
172 fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
175 // func nanotime() int64
176 "runtime.nanotime": (sp) => {
177 setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
180 // func walltime() (sec int64, nsec int32)
181 "runtime.walltime": (sp) => {
182 const msec = (new Date).getTime();
183 setInt64(sp + 8, msec / 1000);
184 mem().setInt32(sp + 16, (msec % 1000) * 1000000, true);
187 // func scheduleCallback(delay int64) int32
188 "runtime.scheduleCallback": (sp) => {
189 const id = this._nextCallbackTimeoutID;
190 this._nextCallbackTimeoutID++;
191 this._callbackTimeouts.set(id, setTimeout(
192 () => { this._resolveCallbackPromise(); },
193 getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early
195 mem().setInt32(sp + 16, id, true);
198 // func clearScheduledCallback(id int32)
199 "runtime.clearScheduledCallback": (sp) => {
200 const id = mem().getInt32(sp + 8, true);
201 clearTimeout(this._callbackTimeouts.get(id));
202 this._callbackTimeouts.delete(id);
205 // func getRandomData(r []byte)
206 "runtime.getRandomData": (sp) => {
207 crypto.getRandomValues(loadSlice(sp + 8));
210 // func stringVal(value string) ref
211 "syscall/js.stringVal": (sp) => {
212 storeValue(sp + 24, loadString(sp + 8));
215 // func valueGet(v ref, p string) ref
216 "syscall/js.valueGet": (sp) => {
217 storeValue(sp + 32, Reflect.get(loadValue(sp + 8), loadString(sp + 16)));
220 // func valueSet(v ref, p string, x ref)
221 "syscall/js.valueSet": (sp) => {
222 Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
225 // func valueIndex(v ref, i int) ref
226 "syscall/js.valueIndex": (sp) => {
227 storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
230 // valueSetIndex(v ref, i int, x ref)
231 "syscall/js.valueSetIndex": (sp) => {
232 Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
235 // func valueCall(v ref, m string, args []ref) (ref, bool)
236 "syscall/js.valueCall": (sp) => {
238 const v = loadValue(sp + 8);
239 const m = Reflect.get(v, loadString(sp + 16));
240 const args = loadSliceOfValues(sp + 32);
241 storeValue(sp + 56, Reflect.apply(m, v, args));
242 mem().setUint8(sp + 64, 1);
244 storeValue(sp + 56, err);
245 mem().setUint8(sp + 64, 0);
249 // func valueInvoke(v ref, args []ref) (ref, bool)
250 "syscall/js.valueInvoke": (sp) => {
252 const v = loadValue(sp + 8);
253 const args = loadSliceOfValues(sp + 16);
254 storeValue(sp + 40, Reflect.apply(v, undefined, args));
255 mem().setUint8(sp + 48, 1);
257 storeValue(sp + 40, err);
258 mem().setUint8(sp + 48, 0);
262 // func valueNew(v ref, args []ref) (ref, bool)
263 "syscall/js.valueNew": (sp) => {
265 const v = loadValue(sp + 8);
266 const args = loadSliceOfValues(sp + 16);
267 storeValue(sp + 40, Reflect.construct(v, args));
268 mem().setUint8(sp + 48, 1);
270 storeValue(sp + 40, err);
271 mem().setUint8(sp + 48, 0);
275 // func valueLength(v ref) int
276 "syscall/js.valueLength": (sp) => {
277 setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
280 // valuePrepareString(v ref) (ref, int)
281 "syscall/js.valuePrepareString": (sp) => {
282 const str = encoder.encode(String(loadValue(sp + 8)));
283 storeValue(sp + 16, str);
284 setInt64(sp + 24, str.length);
287 // valueLoadString(v ref, b []byte)
288 "syscall/js.valueLoadString": (sp) => {
289 const str = loadValue(sp + 8);
290 loadSlice(sp + 16).set(str);
293 // func valueInstanceOf(v ref, t ref) bool
294 "syscall/js.valueInstanceOf": (sp) => {
295 mem().setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16));
298 "debug": (value) => {
305 async run(instance) {
306 this._inst = instance;
307 this._values = [ // TODO: garbage collection
314 this._inst.exports.mem,
317 this._refs = new Map();
318 this._callbackShutdown = false;
321 const mem = new DataView(this._inst.exports.mem.buffer)
323 // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
326 const strPtr = (str) => {
328 new Uint8Array(mem.buffer, offset, str.length + 1).set(encoder.encode(str + "\0"));
329 offset += str.length + (8 - (str.length % 8));
333 const argc = this.argv.length;
336 this.argv.forEach((arg) => {
337 argvPtrs.push(strPtr(arg));
340 const keys = Object.keys(this.env).sort();
341 argvPtrs.push(keys.length);
342 keys.forEach((key) => {
343 argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
347 argvPtrs.forEach((ptr) => {
348 mem.setUint32(offset, ptr, true);
349 mem.setUint32(offset + 4, 0, true);
354 const callbackPromise = new Promise((resolve) => {
355 this._resolveCallbackPromise = () => {
357 throw new Error("bad callback: Go program has already exited");
359 setTimeout(resolve, 0); // make sure it is asynchronous
362 this._inst.exports.run(argc, argv);
366 await callbackPromise;
370 static _makeCallbackHelper(id, pendingCallbacks, go) {
372 pendingCallbacks.push({ id: id, args: arguments });
373 go._resolveCallbackPromise();
377 static _makeEventCallbackHelper(preventDefault, stopPropagation, stopImmediatePropagation, fn) {
378 return function(event) {
379 if (preventDefault) {
380 event.preventDefault();
382 if (stopPropagation) {
383 event.stopPropagation();
385 if (stopImmediatePropagation) {
386 event.stopImmediatePropagation();