@factoidal/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +121 -0
  2. package/LICENSE +201 -0
  3. package/README.md +514 -0
  4. package/browser-wasm.js +872 -0
  5. package/browser.d.ts +514 -0
  6. package/browser.js +2276 -0
  7. package/factoidal-npm-entry.js +32609 -0
  8. package/factoidal-npm-entry.wasm.assets/code-7ac046580f1bbdda8dc6.wasm +0 -0
  9. package/factoidal-npm-entry.wasm.js +455 -0
  10. package/factoidal.js +27560 -0
  11. package/factoidal.wasm.assets/code-bbe6099bfb5b10c4c3ab.wasm +0 -0
  12. package/factoidal.wasm.js +457 -0
  13. package/fn.d.ts +519 -0
  14. package/fn.js +916 -0
  15. package/hacl-init.js +92 -0
  16. package/hacl-wasm/FStar.wasm +0 -0
  17. package/hacl-wasm/Hacl_Bignum.wasm +0 -0
  18. package/hacl-wasm/Hacl_Bignum25519_51.wasm +0 -0
  19. package/hacl-wasm/Hacl_Bignum_Base.wasm +0 -0
  20. package/hacl-wasm/Hacl_Curve25519_51.wasm +0 -0
  21. package/hacl-wasm/Hacl_Ed25519.wasm +0 -0
  22. package/hacl-wasm/Hacl_Ed25519_PrecompTable.wasm +0 -0
  23. package/hacl-wasm/Hacl_Hash_Base.wasm +0 -0
  24. package/hacl-wasm/Hacl_Hash_SHA2.wasm +0 -0
  25. package/hacl-wasm/Hacl_IntTypes_Intrinsics.wasm +0 -0
  26. package/hacl-wasm/LowStar_Endianness.wasm +0 -0
  27. package/hacl-wasm/WasmSupport.wasm +0 -0
  28. package/hacl-wasm/api.js +775 -0
  29. package/hacl-wasm/api.json +3787 -0
  30. package/hacl-wasm/layouts.json +1 -0
  31. package/hacl-wasm/loader.js +568 -0
  32. package/hacl-wasm/shell.js +12 -0
  33. package/index.d.ts +1068 -0
  34. package/index.js +237 -0
  35. package/index.mjs +85 -0
  36. package/lib/api.js +2140 -0
  37. package/lib/engine-js.js +165 -0
  38. package/lib/engine-wasm.js +300 -0
  39. package/package.json +101 -0
  40. package/rdfjs.js +540 -0
  41. package/version.json +101 -0
  42. package/wasm.d.ts +130 -0
  43. package/wasm.js +158 -0
@@ -0,0 +1,775 @@
1
+ // jshint esversion: 6
2
+
3
+ if (typeof module !== 'undefined') {
4
+ var fs = require('fs');
5
+ var path = require('path');
6
+ var loader = require(path.resolve(__dirname, './loader.js'));
7
+ var shell = require(path.resolve(__dirname, './shell.js'));
8
+ var api_promise = Promise.resolve(require(path.resolve(__dirname, './api.json')));
9
+ var layouts_promise = Promise.resolve(require(path.resolve(__dirname, './layouts.json')));
10
+
11
+ } else {
12
+ var loader = this;
13
+ var shell = this;
14
+ var api_promise = fetch("api.json").then(r => r.json());
15
+ var layouts_promise = fetch("layouts.json").then(r => r.json());
16
+ }
17
+
18
+ // We now allow the user to pass a custom list of modules if they want to do
19
+ // their own, more lightweight packaging.
20
+ function getModulesPromise(modules=shell.my_modules) {
21
+ const readModule = async m =>
22
+ typeof module !== 'undefined'
23
+ ? new Uint8Array(await fs.promises.readFile(path.resolve(__dirname, './' + m)))
24
+ : (await fetch(m)).arrayBuffer();
25
+ return Promise.all(modules.map(async name =>
26
+ ({ buf: await readModule(name + ".wasm"), name })
27
+ ));
28
+ }
29
+
30
+ // Comment out for debug
31
+ loader.setMyPrint((x) => {});
32
+
33
+ // HELPERS FOR SIZE FIELDS
34
+ // -----------------------
35
+
36
+ // Grammar of size fields:
37
+ //
38
+ // f ::=
39
+ // var
40
+ // var<OP>n
41
+ // M.f(var)
42
+ //
43
+ // var: variable, one of the length variables, may be ghost
44
+ // n: integer constant
45
+ // <OP>: +, -, * or /
46
+ // M.f: function, referred to by its name in the high-level API, e.g.
47
+ // EverCrypt_Hash.hash_len (and not EverCrypt_Hash_Incremental_hash_len)
48
+
49
+ var parseOp = (arg, op) => {
50
+ let [ var_, const_ ] = arg.split(op);
51
+ return [ "Op", var_, op, parseInt(const_) ];
52
+ };
53
+
54
+ var parseApp = (arg) => {
55
+ let i = arg.indexOf("(");
56
+ let [ m, f ] = arg.substr(0, i).split(".");
57
+ let x = arg.substr(i+1, arg.length - 1 - i - 1);
58
+ return [ "App", m, f, parseSize(x) ];
59
+ }
60
+
61
+ var parseSize = (arg) => {
62
+ if (arg.includes("+"))
63
+ return parseOp(arg, "+");
64
+ if (arg.includes("-"))
65
+ return parseOp(arg, "-");
66
+ if (arg.includes("*"))
67
+ return parseOp(arg, "*");
68
+ if (arg.includes("/"))
69
+ return parseOp(arg, "/");
70
+
71
+ if (arg.includes("("))
72
+ return parseApp(arg);
73
+
74
+ return [ "Var", arg ];
75
+ };
76
+
77
+ var evalSize = function(parsedSize, args_int32s, api) {
78
+ let [ kind, ...args ] = parsedSize;
79
+ switch (kind) {
80
+ case "Var": {
81
+ let [ var_ ] = args;
82
+ return args_int32s[var_];
83
+ }
84
+ case "Op": {
85
+ let [ var_, op, const_ ] = args;
86
+ switch (op) {
87
+ case "+":
88
+ return args_int32s[var_] + const_;
89
+ case "-":
90
+ return args_int32s[var_] - const_;
91
+ case "*":
92
+ return args_int32s[var_] * const_;
93
+ case "/":
94
+ if (args_int32s[var_] % const_ != 0)
95
+ throw new Error("Argument whose length is "+arg+" is not a multiple of "+const_);
96
+ return args_int32s[var_] / const_;
97
+ default:
98
+ throw new Error("Illegal operator: "+op);
99
+ }
100
+ }
101
+ case "App": {
102
+ let [ m, f, x ] = args;
103
+ if (!(m in api))
104
+ throw new Error("For size "+parsedSize+", module "+m+" is unknown");
105
+ if (!(f in api[m]))
106
+ throw new Error("For size "+parsedSize+", function "+m+"."+f+" is unknown");
107
+ // console.log("RECURSIVE EVAL SIZE ENTER: ", x);
108
+ x = evalSize(x, args_int32s, api);
109
+ // console.log("RECURSIVE EVAL SIZE END: ", x);
110
+ return api[m][f](x)[0];
111
+ }
112
+ }
113
+ };
114
+
115
+
116
+ // Given a size formula `var<OP>n` = `total`, invert it, i.e. solve the
117
+ // equation where `var` is unknown.
118
+ //
119
+ // @param {String} arg The formula of the form var<OP>n
120
+ // @param {Number} total The value of the formula
121
+ // @return {[String, Number]} The variable name `var` and its value.
122
+ var invertSize = function(parsedSize, total) {
123
+ let [ kind, var_, op, const_ ] = parsedSize;
124
+ if (kind != "Var" && kind != "Op")
125
+ throw new Error("Illegal size for an input: "+parsedSize);
126
+ switch (op) {
127
+ case "+":
128
+ return [ var_, total - parseInt(const_) ];
129
+ case "-":
130
+ return [ var_, total + parseInt(const_) ];
131
+ case "*":
132
+ let x = parseInt(const_);
133
+ if (total % x != 0)
134
+ throw new Error("Argument whose length is "+parsedSize+" is not a multiple of "+x);
135
+ return [ var_, total / x ];
136
+ case "/":
137
+ return [ var_, total * parseInt(const_) ];
138
+ default:
139
+ return [var_, total];
140
+ }
141
+ };
142
+
143
+ // VALIDATION
144
+ // ----------
145
+
146
+ // The following function validates the contents of `api.json`. It is meant as
147
+ // a helper when creating new binders, it provides explicit error messages.
148
+ // It also has the side effect of filling out the parsedSize field on `arg`
149
+ // objects, so that we don't needlessly re-parse every size field all the time.
150
+ var validateJSON = function(json) {
151
+ for (let key_module in json) {
152
+ for (let key_func in json[key_module]) {
153
+ let func_obj = json[key_module][key_func];
154
+ let obj_name = key_module + "." + key_func;
155
+
156
+ if (!("module" in func_obj))
157
+ throw Error("please provide a 'module' field for " + obj_name + " in api.json");
158
+ if (!(shell.my_modules.includes(func_obj.module)))
159
+ throw Error(obj_name + ".module='" + func_obj.module + "' of api.json should be listed in shell.js");
160
+ if (!("name" in func_obj))
161
+ throw Error("please provide a 'name' field for " + obj_name + " in api.json");
162
+ if (!("args" in func_obj))
163
+ throw Error("please provide a 'args' field for " + obj_name + " in api.json");
164
+ if (!Array.isArray(func_obj.args))
165
+ throw Error("the 'args' field for " + obj_name + " should be an array");
166
+
167
+ let length_args_available = {};
168
+ func_obj.args.forEach((arg, i) => {
169
+ if (!(arg.kind === "input" || (arg.kind === "output")))
170
+ throw Error("in " + obj_name + ", argument #" + i + " should have a 'kind' that is 'output' or 'input'");
171
+ if (!(arg.type === "bool" || arg.type === "uint32" || arg.type.startsWith("buffer") || arg.type[0].toUpperCase() == arg.type[0]))
172
+ throw Error("in " + obj_name + ", argument #" + i + " should have a 'kind' that is 'int', 'bool' or 'buffer'");
173
+ if (arg.type.startsWith("buffer") && arg.size === undefined)
174
+ throw Error("in " + obj_name + ", argument #" + i + " is a buffer and should have a 'size' field");
175
+ if (arg.kind === "input" && arg.type.startsWith("buffer") && !("interface_index" in arg))
176
+ throw Error("in " + obj_name + ", argument #" + i + " is an input and should have a 'interface_index' field");
177
+ if ((arg.kind === "output" || (arg.kind === "input" && arg.interface_index !== undefined)) && arg.tests === undefined)
178
+ throw Error("please provide a 'tests' field for argument #" + i + " of " + obj_name + " in api.json");
179
+ if ((arg.kind === "output" || (arg.kind === "input" && arg.interface_index !== undefined)) && !Array.isArray(arg.tests))
180
+ throw Error("the 'tests' field for argument #" + i + " of " + obj_name + " should be an array");
181
+
182
+ if (arg.type === "uint32" && arg.kind === "input" && arg.interface_index !== undefined)
183
+ length_args_available[arg.name] = true;
184
+
185
+ if (arg.type.startsWith("buffer") && arg.kind === "input" && typeof arg.size === "string") {
186
+ arg.parsedSize = parseSize(arg.size);
187
+ let [ kind, var_ ] = arg.parsedSize;
188
+ if (kind == "Var" || kind == "Op")
189
+ length_args_available[var_] = true;
190
+ }
191
+ });
192
+ func_obj.args.forEach(function(arg, i) {
193
+ if (arg.type.startsWith("buffer") && typeof arg.size === "string" && arg.kind === "output") {
194
+ arg.parsedSize = parseSize(arg.size);
195
+ let [ kind, var_ ] = arg.parsedSize;
196
+ if ((kind == "Var" || kind == "Op") && !(var_ in length_args_available)) {
197
+ console.log(arg);
198
+ console.log(length_args_available);
199
+ throw Error("incorrect 'size' field value (" + arg.size + ") for argument #" + i + " of " + obj_name + " in api.json");
200
+ }
201
+ }
202
+ });
203
+ if (func_obj.return === undefined) {
204
+ throw Error("please provide a 'return' field for " + obj_name + " in api.json");
205
+ }
206
+ };
207
+ };
208
+ };
209
+
210
+ // The module is encapsulated inside a closure to prevent anybody from accessing
211
+ // the WebAssembly memory.
212
+ var HaclWasm = (function() {
213
+ 'use strict';
214
+ var isInitialized = false;
215
+ var Module = {};
216
+
217
+ // We defined a few WASM-specific "compile-time macros".
218
+ var my_imports = {
219
+ EverCrypt_TargetConfig: (mem) => ({
220
+ hacl_can_compile_vale: 0,
221
+ hacl_can_compile_vec128: 0,
222
+ hacl_can_compile_vec256: 0,
223
+ has_vec128_not_avx: () => false,
224
+ has_vec256_not_avx2: () => false,
225
+ }),
226
+ };
227
+
228
+ // The WebAssembly modules have to be initialized before calling any function.
229
+ // To be called only if isInitialized == false.
230
+ var loadWasm = async (modules) => {
231
+ if (!isInitialized) {
232
+ Module = await loader.link(
233
+ my_imports,
234
+ await getModulesPromise(modules)
235
+ );
236
+ isInitialized = true;
237
+ }
238
+ };
239
+
240
+ /*
241
+ Inside WebAssembly, the functions only take pointers to memory and integers.
242
+ However, we want to expose the functions of the wasm module with a nice Javascript
243
+ API that manipulates ArrayBuffers.
244
+
245
+ In order to do that, we have to describe the Javascript prototype of each function
246
+ that we expose. The functions can take and return multiple objects that can be
247
+ buffers, integers or booleans. The buffers can either have a fixed length (and
248
+ in that case we check dynamically whether they have the correct length), or
249
+ have a variable length (and we have to pass that length as an additional
250
+ parameter to WebAssembly).
251
+
252
+ In order to match the Javascript API with the actual calls to WebAssembly functions,
253
+ we have to describe the correspondence between the two in the `api.json` file.
254
+
255
+ The scheme of the JSON file is the following :
256
+ - `module`, this module name will be shown in the JS API
257
+ - `function`, this function name will be shown in the JS API
258
+ - `module`, the name of the WebAssembly file where to find the function
259
+ - 'name', the name of the WebAssembly export corresponding to the function
260
+ - 'args', the list of the WebAssembly arguments expected by the function
261
+ - 'name', the name of the argument which will be shown in the JS Doc
262
+ - 'kind', either `input` or `output` of the function
263
+ - 'type', either 'int', 'boolean', 'buffer', 'buffer(uint32)',
264
+ 'buffer(uint64)', or the name of a struct starting with an uppercase;
265
+ for the latter case, this is understood to be a pointer (the WASM API
266
+ does not take structs by value), and we assume the type is described
267
+ in layouts.json -- in that case, kind is implicitly assumed to be
268
+ input, and kind == "output" is understood to mean input-output
269
+ - 'size', see grammar of size fields above
270
+ - 'interface_index', for all `input` that should appear in JS, position
271
+ inside the argument list of the JS function
272
+ - 'tests', a list of values for this arguments, each value corresponding
273
+ to a different test case
274
+ - 'return', the return type of the WebAssembly function
275
+ - 'custom_module_name', if true, it signifies that the prefix of the name
276
+ of the WebAssembly function does not coincide with the name of the
277
+ WebAssembly module; the module name will not be used when calling it,
278
+ instead 'name' will contain the full name of the function
279
+ */
280
+
281
+ var array_type = function(type) {
282
+ switch (type) {
283
+ case "buffer":
284
+ return Uint8Array;
285
+ case "buffer(uint32)":
286
+ return Uint32Array;
287
+ case "buffer(uint64)":
288
+ return BigUint64Array;
289
+ default:
290
+ throw new Error("Unknown array type: "+type);
291
+ }
292
+ }
293
+
294
+ var cell_size = type => array_type(type).BYTES_PER_ELEMENT;
295
+
296
+ var check_array_type = function(type, candidate, length, name) {
297
+ if (!(candidate instanceof array_type(type)) || candidate.length !== length) {
298
+ throw new Error(
299
+ "name: Please ensure the argument " + name + " has length " + length + " and is a " + array_type(type)
300
+ );
301
+ }
302
+ };
303
+
304
+ var copy_array_to_stack = function(type, array, i) {
305
+ // This returns a suitably-aligned pointer.
306
+ var pointer = loader.reserve(Module.Karamel.mem, array.length*cell_size(type), cell_size(type));
307
+ (new Uint8Array(Module.Karamel.mem.buffer)).set(new Uint8Array(array.buffer), pointer);
308
+ // console.log("argument "+i, "stack pointer got", loader.p32(pointer));
309
+ // console.log(array, array.length);
310
+ // console.log("source", array.buffer);
311
+ // loader.dump(Module.Karamel.mem, 2048, 0x13000);
312
+ return pointer;
313
+ };
314
+
315
+ // len is in number of elements
316
+ var read_memory = function(type, ptr, len) {
317
+ // TODO: faster path with aligned pointers?
318
+ var result = new ArrayBuffer(len*cell_size(type));
319
+ // console.log("New memory buffer", type, len, len*cell_size(type));
320
+ (new Uint8Array(result).set(new Uint8Array(Module.Karamel.mem.buffer)
321
+ .subarray(ptr, ptr + len*cell_size(type))));
322
+ // console.log(result);
323
+ return new (array_type(type))(result);
324
+ };
325
+
326
+ // HELPERS FOR HEAP LAYOUT
327
+ // -----------------------
328
+
329
+ // Filled out via a promise.
330
+ var layouts;
331
+
332
+ var heapReadBlockSize = (ptr) => {
333
+ var m32 = new Uint32Array(Module.Karamel.mem.buffer)
334
+ return m32[ptr/4-2]-8;
335
+ };
336
+
337
+ // We adopt a uniform layout and length-tag buffers upon copying them onto the
338
+ // stack. This allows reading back layouts safely after they're modified.
339
+ var heapWriteBlockSize = (ptr, sz) => {
340
+ var m32 = new Uint32Array(Module.Karamel.mem.buffer)
341
+ return m32[ptr/4-2] = sz + 8;
342
+ };
343
+
344
+ var heapReadBuffer = (type, ptr) => {
345
+ // Pointer points to the actual data, header is 8 bytes before, length in
346
+ // header includes header. Heap base pointers are always aligned on 8 byte
347
+ // boundaries, but inner pointers (e.g. within a struct) are aligned on
348
+ // their size.
349
+ if (!((ptr % cell_size(type)) == 0))
350
+ throw new Error("malloc violation (1)");
351
+ let block_size = heapReadBlockSize(ptr);
352
+ if (!(block_size % cell_size(type) == 0))
353
+ throw new Error("malloc violation (2)");
354
+ let len = block_size / cell_size(type);
355
+ // console.log("pointer:", loader.p32(ptr), "header:", loader.p32(ptr-8), "len:", loader.p32(len));
356
+ return read_memory(type, ptr, len);
357
+ };
358
+
359
+ var heapReadInt = (typ, ptr) => {
360
+ switch (typ) {
361
+ case "A8":
362
+ var m8 = new Uint8Array(Module.Karamel.mem.buffer);
363
+ return m8[ptr];
364
+
365
+ case "A32":
366
+ if (!(ptr % 4) == 0)
367
+ throw new Error("malloc violation (3)");
368
+ var m32 = new Uint32Array(Module.Karamel.mem.buffer);
369
+ return m32[ptr/4];
370
+
371
+ case "A64":
372
+ if (!(ptr % 8) == 0)
373
+ throw new Error("malloc violation (4)");
374
+ var m64 = new BigUint64Array(Module.Karamel.mem.buffer);
375
+ return m64[ptr/8];
376
+
377
+ default:
378
+ throw new Error("Not implemented: "+typ);
379
+ }
380
+ };
381
+
382
+ var heapWriteInt = (typ, ptr, v) => {
383
+ switch (typ) {
384
+ case "A8":
385
+ var m8 = new Uint8Array(Module.Karamel.mem.buffer);
386
+ m8[ptr] = v;
387
+ break;
388
+
389
+ case "A32":
390
+ if (!(ptr % 4) == 0)
391
+ throw new Error("malloc violation (3)");
392
+ var m32 = new Uint32Array(Module.Karamel.mem.buffer);
393
+ m32[ptr/4] = v;
394
+ break;
395
+
396
+ case "A64":
397
+ if (!(ptr % 8) == 0)
398
+ throw new Error("malloc violation (4)");
399
+ var m64 = new BigUint64Array(Module.Karamel.mem.buffer);
400
+ m64[ptr/8] = v;
401
+ break;
402
+
403
+ default:
404
+ throw new Error("Not implemented: "+typ);
405
+ }
406
+ };
407
+
408
+ // Fast-path for arrays of flat integers.
409
+ var heapReadBlockFast = (int_type, ptr) => {
410
+ switch (int_type) {
411
+ case "A8":
412
+ return heapReadBuffer("buffer", ptr);
413
+ case "A32":
414
+ return heapReadBuffer("buffer(uint32)", ptr);
415
+ case "A64":
416
+ return heapReadBuffer("buffer(uint64)", ptr);
417
+ default:
418
+ throw new Error("Not implemented: "+int_type);
419
+ }
420
+ };
421
+
422
+ // Fast-path for arrays of flat integers.
423
+ var heapWriteBlockFast = (int_type, ptr, arr) => {
424
+ // console.log(arr);
425
+ (new Uint8Array(Module.Karamel.mem.buffer)).set(new Uint8Array(arr.buffer), ptr);
426
+ };
427
+
428
+ // Eventually will be mutually recursive once Layout is implemented (flat
429
+ // packed structs).
430
+ var heapReadType = (runtime_type, ptr) => {
431
+ // console.log("headReadType", runtime_type, loader.p32(ptr));
432
+ let [ type, data ] = runtime_type;
433
+ switch (type) {
434
+ case "Int":
435
+ return heapReadInt(data[0], ptr);
436
+ case "Pointer":
437
+ if (data[0] == "Int")
438
+ return heapReadBlockFast(data[1][0], heapReadInt("A32", ptr));
439
+ else if (data[0] == "Layout")
440
+ return heapReadLayout(data[1], heapReadInt("A32", ptr));
441
+ // pass-through
442
+ default:
443
+ throw new Error("Not implemented: "+type+","+data);
444
+ }
445
+ };
446
+
447
+ var heapWriteType = (runtime_type, ptr, v) => {
448
+ // console.log("heapWriteType", runtime_type, loader.p32(ptr), v);
449
+ let [ type, data ] = runtime_type;
450
+ switch (type) {
451
+ case "Int":
452
+ heapWriteInt(data[0], ptr, v);
453
+ break;
454
+ case "Pointer":
455
+ if (data[0] == "Int") {
456
+ let sz = v.buffer.byteLength;
457
+ // NB: could be more precise with alignment, I guess
458
+ let dst = loader.reserve(Module.Karamel.mem, sz + 8, 8) + 8;
459
+ heapWriteInt("A32", ptr, dst);
460
+ heapWriteBlockFast(data[1][0], dst, v);
461
+ heapWriteBlockSize(dst, sz);
462
+ break;
463
+ } else if (data[0] == "Layout") {
464
+ let dst = stackWriteLayout(data[1], v);
465
+ heapWriteInt("A32", ptr, dst);
466
+ break;
467
+ }
468
+ // pass-through
469
+ default:
470
+ throw new Error("Not implemented: "+type);
471
+ }
472
+ };
473
+
474
+ var lFlatIsTaggedUnion = data => {
475
+ if (data.fields.length == 2 && data.fields[0][0] == "tag" && data.fields[1][0] == "val") {
476
+ let [ tag_name, [ tag_ofs, [ tag_type, [ tag_width ]]]] = data.fields[0];
477
+ if (!(tag_name == "tag" && tag_ofs == "0" && tag_type == "Int" && tag_width == "A32"))
478
+ throw new Error("Inconsistent tag");
479
+ let [ val_name, [ val_ofs, [ val_type, val_cases]]] = data.fields[1];
480
+ if (!(val_name == "val" && val_ofs == "8" && val_type == "Union"))
481
+ throw new Error("Inconsistent val");
482
+ return true;
483
+ } else {
484
+ return false;
485
+ }
486
+ };
487
+
488
+ var taggedUnionGetCase = (data, tag) => {
489
+ let [ val_name, [ val_ofs, [ val_type, val_cases]]] = data.fields[1];
490
+ return val_cases[tag];
491
+ };
492
+
493
+ var heapReadLayout = (layout, ptr) => {
494
+ // console.log("heapReadLayout", layout, loader.p32(ptr));
495
+ let [ tag, data ] = layouts[layout];
496
+ switch (tag) {
497
+ case "LFlat":
498
+ if (lFlatIsTaggedUnion(data)) {
499
+ let tag = heapReadInt("A32", ptr);
500
+ return ({
501
+ tag,
502
+ val: heapReadType(taggedUnionGetCase(data, tag), ptr + 8)
503
+ });
504
+ } else {
505
+ let o = {};
506
+ data.fields.forEach(([field, [ ofs, typ ]]) =>
507
+ o[field] = heapReadType(typ, ptr + ofs)
508
+ );
509
+ return o;
510
+ }
511
+ default:
512
+ throw new Error("Not implemented: "+tag);
513
+ }
514
+ };
515
+
516
+ var heapWriteLayout = (layout, ptr, v) => {
517
+ let [ tag, data ] = layouts[layout];
518
+ // console.log(v);
519
+ switch (tag) {
520
+ case "LFlat":
521
+ if (lFlatIsTaggedUnion(data)) {
522
+ heapWriteInt("A32", ptr, v.tag);
523
+ heapWriteType(taggedUnionGetCase(data, v.tag), ptr + 8, v.val);
524
+ } else {
525
+ data.fields.forEach(([field, [ ofs, typ ]]) => {
526
+ // console.log("Writing", v[field]);
527
+ heapWriteType(typ, ptr + ofs, v[field]);
528
+ });
529
+ }
530
+ break;
531
+ default:
532
+ throw new Error("Not implemented: "+tag);
533
+ }
534
+ };
535
+
536
+ var stackWriteLayout = (layout, v) => {
537
+ // console.log("stackWriteLayout", layout, v);
538
+ let [ tag, data ] = layouts[layout];
539
+ switch (tag) {
540
+ case "LFlat":
541
+ let ptr = loader.reserve(Module.Karamel.mem, data.size, 8);
542
+ heapWriteLayout(layout, ptr, v);
543
+ return ptr;
544
+ default:
545
+ throw new Error("Not implemented: "+tag);
546
+ }
547
+ };
548
+
549
+ // END HELPERS FOR HEAP LAYOUT
550
+
551
+ // The object being filled:
552
+ // - first level of keys = modules,
553
+ // - second level of keys = functions within a module
554
+ var api_obj = {};
555
+
556
+ // This is the main logic; this function is partially applied to its
557
+ // first two arguments for each API entry. We assume JITs are working well
558
+ // enough to make this efficient.
559
+ var callWithProto = function(proto, args) {
560
+ var expected_args_number = proto.args.filter(function(arg) {
561
+ return arg.interface_index !== undefined;
562
+ }).length;
563
+ if (args.length != expected_args_number) {
564
+ throw Error("wrong number of arguments to call the F*-wasm function " + proto.name + ": expected " + expected_args_number + ", got " + args.length);
565
+ }
566
+ var memory = new Uint32Array(Module.Karamel.mem.buffer);
567
+ var sp = memory[0];
568
+
569
+ // Integer arguments are either
570
+ // - user-provided, in which case they have an interface_index
571
+ // - automatically determined, in which case they appear in the `size` field
572
+ // of another buffer argument.
573
+ // In a first pass, we need to figure out the value of all integer
574
+ // arguments to enable lookups by name.
575
+ var args_int32s = {};
576
+ proto.args.forEach(function(arg) {
577
+ if (arg.type.startsWith("buffer") && typeof arg.size === "string" && arg.interface_index !== undefined && arg.kind === "input") {
578
+ // API contains e.g.:
579
+ // { "name": "len" },
580
+ // { "name": "buf", "type": "buffer", "size": "len", "interface_index": 3, "kind": "input" }
581
+ // We need to figure out `len` automatically since it doesn't have an
582
+ // interface index, meaning it isn't one of the arguments passed to the
583
+ // high-level API. We know `buf` is the second argument passed to the
584
+ // function, and thus allows us to fill out `len`.
585
+ let [ var_, var_value ] = invertSize(arg.parsedSize, args[arg.interface_index].length);
586
+ // console.log("Determined "+var_+"="+var_value);
587
+
588
+ if (var_ in args_int32s && var_value != args_int32s[var_])
589
+ throw new Error("Inconsistency in sizes; previously, "+var_+"="+args_int32s[var_]+"; now "+var_value);
590
+ args_int32s[var_] = var_value;
591
+ } else if (arg.interface_index !== undefined) {
592
+ // API contains e.g.:
593
+ // { "name": "len", "interface_index": 3 },
594
+ // { "name": "buf", "type": "buffer", "size": "len", "kind": "output" }
595
+ // We know we will need `len` below when trying to allocate an array for
596
+ // `output` -- insert it into the table.
597
+ // Note: we are quite lax and don't require that a length be a uint32,
598
+ // it's sometimes useful to allow it to be anything, like an address.
599
+ args_int32s[arg.name] = args[arg.interface_index];
600
+ }
601
+ });
602
+
603
+ // We have determined the value of all user-provided and synthesized
604
+ // (computed) integer lengths. Now what happens with lengths is:
605
+ // - when allocating a variable-length output buffer, we compute the
606
+ // corresponding size via evalSize, passing it the args_int32s in case the
607
+ // size field of the output buffer refers to a variable
608
+ // - when computing the value of an integer argument that is not
609
+ // user-provided (i.e. has no interface_index), we look it up in
610
+ // args_int32s too
611
+
612
+ // This returns the effective arguments for the function call (all integers,
613
+ // some of which may be pointers on the stack). It has the side effect of
614
+ // growing the stack and copying the input buffers onto it.
615
+ var args = proto.args.map(function(arg, i) {
616
+ let debug = (type, x) => {
617
+ // console.log("Argument", i, type, loader.p32(x));
618
+ return x;
619
+ };
620
+ if (arg.type.startsWith("buffer")) {
621
+ var size;
622
+ if (typeof arg.size === "string") {
623
+ size = evalSize(arg.parsedSize, args_int32s, api_obj);
624
+ } else {
625
+ size = arg.size;
626
+ }
627
+ var arg_byte_buffer;
628
+ if (arg.kind === "input") {
629
+ var func_arg = args[arg.interface_index];
630
+ arg_byte_buffer = new (array_type(arg.type))(func_arg);
631
+ } else if (arg.kind === "output") {
632
+ arg_byte_buffer = new (array_type(arg.type))(size);
633
+ }
634
+ check_array_type(arg.type, arg_byte_buffer, size, arg.name);
635
+ // TODO: this copy is un-necessary in the case of output buffers.
636
+ return debug("array", copy_array_to_stack(arg.type, arg_byte_buffer, i));
637
+ }
638
+
639
+ if (arg.type === "bool" || arg.type === "uint32") {
640
+ if (arg.interface_index === undefined) {
641
+ // Variable-length argument, determined via first pass above.
642
+ return debug("int(auto)", args_int32s[arg.name]);
643
+ } else {
644
+ // Regular integer argument, passed by the user.
645
+ return debug("int", args[arg.interface_index]);
646
+ }
647
+ }
648
+
649
+ // Layout... TODO: the "kind" field is unused because we do not have the
650
+ // case where the caller allocates empty space for a layout.
651
+ if (arg.type[0].toUpperCase() == arg.type[0]) {
652
+ let func_arg = args[arg.interface_index];
653
+ return debug("layout", stackWriteLayout(arg.type, func_arg));
654
+ }
655
+
656
+ throw Error("Unimplemented ! ("+proto.name+")");
657
+ });
658
+ // console.log("Arguments laid out in WASM memory");
659
+ // args.forEach((arg, i) => console.log("argument "+i, loader.p32(arg)));
660
+ // loader.dump(Module.Karamel.mem, 2048, args[0] - (args[0] % 0x20));
661
+
662
+ // Calling the wasm function !
663
+ if (proto.custom_module_name) {
664
+ var func_name = proto.name;
665
+ } else {
666
+ var func_name = proto.module + "_" + proto.name;
667
+ }
668
+ if (!(proto.module in Module))
669
+ throw new Error(proto.module + " is not in Module");
670
+ if (!(func_name in Module[proto.module])) {
671
+ console.log(Object.keys(Module[proto.module]));
672
+ throw new Error(func_name + " is not in Module["+proto.module+"]");
673
+ }
674
+ var call_return = Module[proto.module][func_name](...args);
675
+
676
+ // console.log("After function call");
677
+ // loader.dump(Module.Karamel.mem, 256, args[0] - (args[0] % 0x20));
678
+ //loader.dump(Module.Karamel.mem, 256, call_return - (call_return % 0x20));
679
+
680
+ // Populating the JS buffers returned with their values read from Wasm memory
681
+ var return_buffers = args.map(function(pointer, i) {
682
+ let arg = proto.args[i];
683
+ if (arg.type[0].toUpperCase() == arg.type[0] && arg.kind == "output") {
684
+ // Layout
685
+ return heapReadLayout(arg.type, pointer);
686
+ } else if (arg.kind === "output") {
687
+ // Output buffer, allocated by us above, now need to read its contents
688
+ // out.
689
+ var size;
690
+ if (typeof arg.size === "string") {
691
+ size = evalSize(arg.parsedSize, args_int32s, api_obj);
692
+ } else {
693
+ size = arg.size;
694
+ }
695
+ // console.log("About to read", protoRet.type, loader.p32(pointer), size);
696
+ let r = read_memory(arg.type, pointer, size);
697
+ // console.log(r);
698
+ return r;
699
+ } else {
700
+ return null;
701
+ }
702
+ }).filter(v => v !== null);
703
+
704
+ // Resetting the stack pointer to its old value
705
+ memory[0] = sp;
706
+ if ("kind" in proto.return && proto.return.kind === "layout") {
707
+ // Heap-allocated value
708
+ let read = proto.return.type.startsWith("buffer") ? heapReadBuffer : heapReadLayout;
709
+ let r = read(proto.return.type, call_return);
710
+ memory[Module.Karamel.mem.buffer.byteLength/4-1] = 0;
711
+ return r;
712
+
713
+ // loader.dump(Module.Karamel.mem, 2048, Module.Karamel.mem.buffer.byteLength - 2048);
714
+ // console.log(loader.p32(call_return), r, JSON.stringify(layouts[proto.return.type], null, 2));
715
+
716
+ // let ptr = stackWriteLayout(proto.return.type, r);
717
+ // console.log(loader.p32(ptr));
718
+ // loader.dump(Module.Karamel.mem, 2048, 0x13000);
719
+ // throw new Error(func_name+": non-buffer return layout not implemented");
720
+ }
721
+ if (proto.return.type === "bool") {
722
+ return [call_return === 1, return_buffers].flat();
723
+ }
724
+ if (proto.return.type === "uint32") {
725
+ return [call_return >>> 0, return_buffers].flat();
726
+ }
727
+ if (proto.return.type === "uint64") {
728
+ // krml convention: uint64s are sent over as two uint32s
729
+ // console.log(call_return);
730
+ return [BigInt(call_return[0]>>>0) + (BigInt(call_return[1]>>>0) << 32n), return_buffers].flat();
731
+ }
732
+ if (proto.return.type === "void") {
733
+ return return_buffers;
734
+ }
735
+ throw new Error(func_name+": Unimplemented ! "+proto.return.type);
736
+ };
737
+
738
+ var getInitializedHaclModule = async function (modules) {
739
+ if (!isInitialized) {
740
+ // Load all WASM modules from network (web) or disk (node).
741
+ await loadWasm(modules);
742
+ // Write into the global.
743
+ layouts = await layouts_promise;
744
+
745
+ // Initial API validation (TODO: disable for release...?)
746
+ let api_json = await api_promise;
747
+ validateJSON(api_json);
748
+
749
+ // We follow the structure of api.json to expose an object whose structure
750
+ // follows the keys of api.json; each entry is a partial application of
751
+ // `callWithProto` (generic API wrapper) to its specific entry in api.json
752
+ // held in `api_json[key_module][key_func]`.
753
+ for (let key_module in api_json) {
754
+ for (let key_func in api_json[key_module]) {
755
+ if (api_obj[key_module] == null) {
756
+ api_obj[key_module] = {};
757
+ }
758
+ api_obj[key_module][key_func] = function(...args) {
759
+ return callWithProto(api_json[key_module][key_func], args);
760
+ };
761
+ };
762
+ };
763
+ }
764
+ return Promise.resolve(api_obj);
765
+ };
766
+
767
+ return {
768
+ getInitializedHaclModule: getInitializedHaclModule,
769
+ dump: (sz, ofs) => loader.dump(Module.Karamel.mem, sz, ofs)
770
+ };
771
+ })();
772
+
773
+ if (typeof module !== "undefined") {
774
+ module.exports = HaclWasm;
775
+ }