OSDN Git Service

big boom
[bytom/Bytom-JS-SDK.git] / src / wasm / go.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 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") {
10                 self.global = self;
11         } else {
12                 throw new Error("cannot export Go (neither window nor self is defined)");
13         }
14
15         let outputBuf = "";
16         global.fs = {
17                 constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
18                 writeSync(fd, buf) {
19                         outputBuf += decoder.decode(buf);
20                         const nl = outputBuf.lastIndexOf("\n");
21                         if (nl != -1) {
22                                 console.log(outputBuf.substr(0, nl));
23                                 outputBuf = outputBuf.substr(nl + 1);
24                         }
25                         return buf.length;
26                 },
27                 openSync(path, flags, mode) {
28                         const err = new Error("not implemented");
29                         err.code = "ENOSYS";
30                         throw err;
31                 },
32         };
33
34         const encoder = new TextEncoder("utf-8");
35         const decoder = new TextDecoder("utf-8");
36
37         global.Go = class {
38                 constructor() {
39                         this.argv = ["js"];
40                         this.env = {};
41                         this.exit = (code) => {
42                                 if (code !== 0) {
43                                         console.warn("exit code:", code);
44                                 }
45                                 if(typeof window.resetWasmStatus == "function") {
46                                         window.resetWasmStatus();
47                                 }
48                         };
49                         this._callbackTimeouts = new Map();
50                         this._nextCallbackTimeoutID = 1;
51
52                         const mem = () => {
53                                 // The buffer may change when requesting more memory.
54                                 return new DataView(this._inst.exports.mem.buffer);
55                         }
56
57                         const setInt64 = (addr, v) => {
58                                 mem().setUint32(addr + 0, v, true);
59                                 mem().setUint32(addr + 4, Math.floor(v / 4294967296), true);
60                         }
61
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;
66                         }
67
68                         const loadValue = (addr) => {
69                                 const f = mem().getFloat64(addr, true);
70                                 if (!isNaN(f)) {
71                                         return f;
72                                 }
73
74                                 const id = mem().getUint32(addr, true);
75                                 return this._values[id];
76                         }
77
78                         const storeValue = (addr, v) => {
79                                 const nanHead = 0x7FF80000;
80
81                                 if (typeof v === "number") {
82                                         if (isNaN(v)) {
83                                                 mem().setUint32(addr + 4, nanHead, true);
84                                                 mem().setUint32(addr, 0, true);
85                                                 return;
86                                         }
87                                         mem().setFloat64(addr, v, true);
88                                         return;
89                                 }
90
91                                 switch (v) {
92                                         case undefined:
93                                                 mem().setUint32(addr + 4, nanHead, true);
94                                                 mem().setUint32(addr, 1, true);
95                                                 return;
96                                         case null:
97                                                 mem().setUint32(addr + 4, nanHead, true);
98                                                 mem().setUint32(addr, 2, true);
99                                                 return;
100                                         case true:
101                                                 mem().setUint32(addr + 4, nanHead, true);
102                                                 mem().setUint32(addr, 3, true);
103                                                 return;
104                                         case false:
105                                                 mem().setUint32(addr + 4, nanHead, true);
106                                                 mem().setUint32(addr, 4, true);
107                                                 return;
108                                 }
109
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);
115                                 }
116                                 let typeFlag = 0;
117                                 switch (typeof v) {
118                                         case "string":
119                                                 typeFlag = 1;
120                                                 break;
121                                         case "symbol":
122                                                 typeFlag = 2;
123                                                 break;
124                                         case "function":
125                                                 typeFlag = 3;
126                                                 break;
127                                 }
128                                 mem().setUint32(addr + 4, nanHead | typeFlag, true);
129                                 mem().setUint32(addr, ref, true);
130                         }
131
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);
136                         }
137
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);
144                                 }
145                                 return a;
146                         }
147
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));
152                         }
153
154                         const timeOrigin = Date.now() - performance.now();
155                         this.importObject = {
156                                 go: {
157                                         // func wasmExit(code int32)
158                                         "runtime.wasmExit": (sp) => {
159                                                 const code = mem().getInt32(sp + 8, true);
160                                                 this.exited = true;
161                                                 delete this._inst;
162                                                 delete this._values;
163                                                 delete this._refs;
164                                                 this.exit(code);
165                                         },
166
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));
173                                         },
174
175                                         // func nanotime() int64
176                                         "runtime.nanotime": (sp) => {
177                                                 setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
178                                         },
179
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);
185                                         },
186
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
194                                                 ));
195                                                 mem().setInt32(sp + 16, id, true);
196                                         },
197
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);
203                                         },
204
205                                         // func getRandomData(r []byte)
206                                         "runtime.getRandomData": (sp) => {
207                                                 crypto.getRandomValues(loadSlice(sp + 8));
208                                         },
209
210                                         // func stringVal(value string) ref
211                                         "syscall/js.stringVal": (sp) => {
212                                                 storeValue(sp + 24, loadString(sp + 8));
213                                         },
214
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)));
218                                         },
219
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));
223                                         },
224
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)));
228                                         },
229
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));
233                                         },
234
235                                         // func valueCall(v ref, m string, args []ref) (ref, bool)
236                                         "syscall/js.valueCall": (sp) => {
237                                                 try {
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);
243                                                 } catch (err) {
244                                                         storeValue(sp + 56, err);
245                                                         mem().setUint8(sp + 64, 0);
246                                                 }
247                                         },
248
249                                         // func valueInvoke(v ref, args []ref) (ref, bool)
250                                         "syscall/js.valueInvoke": (sp) => {
251                                                 try {
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);
256                                                 } catch (err) {
257                                                         storeValue(sp + 40, err);
258                                                         mem().setUint8(sp + 48, 0);
259                                                 }
260                                         },
261
262                                         // func valueNew(v ref, args []ref) (ref, bool)
263                                         "syscall/js.valueNew": (sp) => {
264                                                 try {
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);
269                                                 } catch (err) {
270                                                         storeValue(sp + 40, err);
271                                                         mem().setUint8(sp + 48, 0);
272                                                 }
273                                         },
274
275                                         // func valueLength(v ref) int
276                                         "syscall/js.valueLength": (sp) => {
277                                                 setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
278                                         },
279
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);
285                                         },
286
287                                         // valueLoadString(v ref, b []byte)
288                                         "syscall/js.valueLoadString": (sp) => {
289                                                 const str = loadValue(sp + 8);
290                                                 loadSlice(sp + 16).set(str);
291                                         },
292
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));
296                                         },
297
298                                         "debug": (value) => {
299                                                 console.log(value);
300                                         },
301                                 }
302                         };
303                 }
304
305                 async run(instance) {
306                         this._inst = instance;
307                         this._values = [ // TODO: garbage collection
308                                 NaN,
309                                 undefined,
310                                 null,
311                                 true,
312                                 false,
313                                 global,
314                                 this._inst.exports.mem,
315                                 this,
316                         ];
317                         this._refs = new Map();
318                         this._callbackShutdown = false;
319                         this.exited = false;
320
321                         const mem = new DataView(this._inst.exports.mem.buffer)
322
323                         // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
324                         let offset = 4096;
325
326                         const strPtr = (str) => {
327                                 let ptr = offset;
328                                 new Uint8Array(mem.buffer, offset, str.length + 1).set(encoder.encode(str + "\0"));
329                                 offset += str.length + (8 - (str.length % 8));
330                                 return ptr;
331                         };
332
333                         const argc = this.argv.length;
334
335                         const argvPtrs = [];
336                         this.argv.forEach((arg) => {
337                                 argvPtrs.push(strPtr(arg));
338                         });
339
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]}`));
344                         });
345
346                         const argv = offset;
347                         argvPtrs.forEach((ptr) => {
348                                 mem.setUint32(offset, ptr, true);
349                                 mem.setUint32(offset + 4, 0, true);
350                                 offset += 8;
351                         });
352
353                         while (true) {
354                                 const callbackPromise = new Promise((resolve) => {
355                                         this._resolveCallbackPromise = () => {
356                                                 if (this.exited) {
357                                                         throw new Error("bad callback: Go program has already exited");
358                                                 }
359                                                 setTimeout(resolve, 0); // make sure it is asynchronous
360                                         };
361                                 });
362                                 this._inst.exports.run(argc, argv);
363                                 if (this.exited) {
364                                         break;
365                                 }
366                                 await callbackPromise;
367                         }
368                 }
369
370                 static _makeCallbackHelper(id, pendingCallbacks, go) {
371                         return function() {
372                                 pendingCallbacks.push({ id: id, args: arguments });
373                                 go._resolveCallbackPromise();
374                         };
375                 }
376
377                 static _makeEventCallbackHelper(preventDefault, stopPropagation, stopImmediatePropagation, fn) {
378                         return function(event) {
379                                 if (preventDefault) {
380                                         event.preventDefault();
381                                 }
382                                 if (stopPropagation) {
383                                         event.stopPropagation();
384                                 }
385                                 if (stopImmediatePropagation) {
386                                         event.stopImmediatePropagation();
387                                 }
388                                 fn(event);
389                         };
390                 }
391         }
392 };