porffor 0.2.0-c6c8c81 → 0.2.0-cb647c8

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.
package/compiler/2c.js CHANGED
@@ -1,24 +1,106 @@
1
- import { read_ieee754_binary64, read_signedLEB128 } from './encoding.js';
1
+ import { read_ieee754_binary64, read_signedLEB128, read_unsignedLEB128 } from './encoding.js';
2
2
  import { Blocktype, Opcodes, Valtype } from './wasmSpec.js';
3
3
  import { operatorOpcode } from './expression.js';
4
4
  import { log } from "./log.js";
5
5
 
6
6
  const CValtype = {
7
- i8: 'char',
8
- i16: 'unsigned short', // presume all i16 stuff is unsigned
9
- i32: 'long',
10
- i32_u: 'unsigned long',
11
- i64: 'long long',
12
- i64_u: 'unsigned long long',
7
+ i8: 'i8',
8
+ i16: 'i16',
9
+ i32: 'i32',
10
+ u32: 'u32',
11
+ i64: 'i64',
12
+ u64: 'u64',
13
13
 
14
- f32: 'float',
15
- f64: 'double',
14
+ f32: 'f32',
15
+ f64: 'f64',
16
16
 
17
17
  undefined: 'void'
18
18
  };
19
19
 
20
+ const alwaysPreface = `typedef uint8_t i8;
21
+ typedef uint16_t i16;
22
+ typedef int32_t i32;
23
+ typedef uint32_t u32;
24
+ typedef int64_t i64;
25
+ typedef uint64_t u64;
26
+ typedef float f32;
27
+ typedef double f64;
28
+
29
+ f64 NAN = 0e+0/0e+0;
30
+
31
+ struct ReturnValue {
32
+ ${CValtype.f64} value;
33
+ ${CValtype.i32} type;
34
+ };\n\n`;
35
+
36
+ // todo: is memcpy/etc safe with host endianness?
37
+
38
+ // all:
39
+ // immediates: ['align', 'offset']
40
+ const CMemFuncs = {
41
+ [Opcodes.i32_store]: {
42
+ c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
43
+ args: ['pointer', 'value'],
44
+ argTypes: [CValtype.i32, CValtype.i32],
45
+ returns: false
46
+ },
47
+ [Opcodes.i32_store16]: {
48
+ c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
49
+ args: ['pointer', 'value'],
50
+ argTypes: [CValtype.i32, CValtype.i16],
51
+ returns: false
52
+ },
53
+ [Opcodes.i32_store8]: {
54
+ c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
55
+ args: ['pointer', 'value'],
56
+ argTypes: [CValtype.i32, CValtype.i8],
57
+ returns: false
58
+ },
59
+
60
+ [Opcodes.i32_load]: {
61
+ c: `${CValtype.i32} out;
62
+ memcpy(&out, _memory + offset + pointer, sizeof(out));
63
+ return out;`,
64
+ args: ['pointer'],
65
+ argTypes: [CValtype.i32],
66
+ returns: CValtype.i32
67
+ },
68
+ [Opcodes.i32_load16_u]: {
69
+ c: `${CValtype.i16} out;
70
+ memcpy(&out, _memory + offset + pointer, sizeof(out));
71
+ return out;`,
72
+ args: ['pointer'],
73
+ argTypes: [CValtype.i32],
74
+ returns: CValtype.i32
75
+ },
76
+ [Opcodes.i32_load8_u]: {
77
+ c: `${CValtype.i8} out;
78
+ memcpy(&out, _memory + offset + pointer, sizeof(out));
79
+ return out;`,
80
+ args: ['pointer'],
81
+ argTypes: [CValtype.i32],
82
+ returns: CValtype.i32
83
+ },
84
+
85
+ [Opcodes.f64_store]: {
86
+ c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
87
+ args: ['pointer', 'value'],
88
+ argTypes: [CValtype.i32, CValtype.f64],
89
+ returns: false
90
+ },
91
+ [Opcodes.f64_load]: {
92
+ c: `${CValtype.f64} out;
93
+ memcpy(&out, _memory + offset + pointer, sizeof(out));
94
+ return out;`,
95
+ args: ['pointer'],
96
+ argTypes: [CValtype.i32],
97
+ returns: CValtype.f64
98
+ },
99
+ };
100
+
20
101
  const inv = (obj, keyMap = x => x) => Object.keys(obj).reduce((acc, x) => { acc[keyMap(obj[x])] = x; return acc; }, {});
21
102
  const invOpcodes = inv(Opcodes);
103
+ const invValtype = inv(Valtype);
22
104
 
23
105
  for (const x in CValtype) {
24
106
  if (Valtype[x]) CValtype[Valtype[x]] = CValtype[x];
@@ -35,9 +117,19 @@ const todo = msg => {
35
117
  throw new TodoError(`todo: ${msg}`);
36
118
  };
37
119
 
38
- const removeBrackets = str => str.startsWith('(') && str.endsWith(')') ? str.slice(1, -1) : str;
120
+ const removeBrackets = str => {
121
+ // return str;
122
+ // if (str.startsWith(`(${CValtype.i32})(${CValtype.u32})`)) return `(${CValtype.i32})(${CValtype.u32})(` + removeBrackets(str.slice(22, -1)) + ')';
123
+
124
+ for (const x in CValtype) {
125
+ const p = `(${x})`;
126
+ if (str.startsWith(p)) return p + removeBrackets(str.slice(p.length));
127
+ }
128
+
129
+ return str.startsWith('(') && str.endsWith(')') ? str.slice(1, -1) : str;
130
+ };
39
131
 
40
- export default ({ funcs, globals, tags, exceptions, pages }) => {
132
+ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
41
133
  const invOperatorOpcode = Object.values(operatorOpcode).reduce((acc, x) => {
42
134
  for (const k in x) {
43
135
  acc[x[k]] = k;
@@ -53,12 +145,11 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
53
145
  }
54
146
 
55
147
  const includes = new Map(), unixIncludes = new Map(), winIncludes = new Map();
148
+ const prepend = new Map(), prependMain = new Map();
56
149
 
57
- // TODO: make type i16
58
- let out = `struct ReturnValue {
59
- ${CValtype.f64} value;
60
- ${CValtype.i32} type;
61
- };\n\n`;
150
+ includes.set('stdint.h', true);
151
+
152
+ let out = ``;
62
153
 
63
154
  for (const x in globals) {
64
155
  const g = globals[x];
@@ -67,6 +158,15 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
67
158
  out += ';\n';
68
159
  }
69
160
 
161
+ if (pages.size > 0) {
162
+ prepend.set('_memory', `char _memory[${pages.size * pageSize}];\n`);
163
+ includes.set('string.h', true);
164
+ }
165
+
166
+ if (data.length > 0) {
167
+ prependMain.set('_data', data.map(x => `memcpy(_memory + ${x.offset}, (unsigned char[]){${x.bytes.join(',')}}, ${x.bytes.length});`).join('\n'));
168
+ }
169
+
70
170
  // for (const [ x, p ] of pages) {
71
171
  // out += `${CValtype[p.type]} ${x.replace(': ', '_').replace(/[^0-9a-zA-Z_]/g, '')}[100]`;
72
172
  // out += ';\n';
@@ -75,7 +175,8 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
75
175
  if (out) out += '\n';
76
176
 
77
177
  let depth = 1;
78
- const line = (str, semi = true) => out += `${' '.repeat(depth * 2)}${str}${semi ? ';' : ''}\n`;
178
+ let brDepth = 0;
179
+ const line = (str, semi = true) => out += `${' '.repeat(depth * 2 + brDepth * 2)}${str}${semi ? ';' : ''}\n`;
79
180
  const lines = lines => {
80
181
  for (const x of lines) {
81
182
  out += `${' '.repeat(depth * 2)}${x}\n`;
@@ -108,6 +209,8 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
108
209
  return tmp;
109
210
  };
110
211
 
212
+ let brId = 0;
213
+
111
214
  for (const f of funcs) {
112
215
  depth = 1;
113
216
 
@@ -121,7 +224,12 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
121
224
  const returns = f.returns.length > 0;
122
225
 
123
226
  const shouldInline = f.internal;
124
- out += `${f.name === 'main' ? 'int' : (f.internal ? 'double' : 'struct ReturnValue')} ${shouldInline ? 'inline ' : ''}${sanitize(f.name)}(${f.params.map((x, i) => `${CValtype[x]} ${invLocals[i]}`).join(', ')}) {\n`;
227
+ out += `${f.name === 'main' ? 'int' : (f.internal ? (returns ? CValtype.f64 : 'void') : 'struct ReturnValue')} ${shouldInline ? 'inline ' : ''}${sanitize(f.name)}(${f.params.map((x, i) => `${CValtype[x]} ${invLocals[i]}`).join(', ')}) {\n`;
228
+
229
+ if (f.name === 'main') {
230
+ out += [...prependMain.values()].join('\n');
231
+ if (prependMain.size > 0) out += '\n\n';
232
+ }
125
233
 
126
234
  const localKeys = Object.keys(f.locals).sort((a, b) => f.locals[a].idx - f.locals[b].idx).slice(f.params.length).sort((a, b) => f.locals[a].idx - f.locals[b].idx);
127
235
  for (const x of localKeys) {
@@ -131,11 +239,58 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
131
239
 
132
240
  if (localKeys.length !== 0) out += '\n';
133
241
 
242
+ const rets = [];
243
+ const runOnEnd = [];
244
+
134
245
  let vals = [];
135
- const endNeedsCurly = [], ignoreEnd = [];
136
- let beginLoop = false, lastCond = false, ifTernary = false;
246
+ const endNeedsCurly = [];
247
+ const brs = [];
248
+ let lastCond = false;
249
+
250
+ // let brDepth = 0;
251
+
252
+ const blockStart = (i, loop) => {
253
+ // reset "stack"
254
+ // vals = [];
255
+
256
+ rets.push(i[1]);
257
+
258
+ const br = brId++;
259
+ brs.push(br);
260
+ if (loop) {
261
+ line(`j${br}:;`, false);
262
+ runOnEnd.push(null);
263
+ } else {
264
+ runOnEnd.push(() => line(`j${br}:;`, false));
265
+ }
266
+
267
+ if (i[1] !== Blocktype.void) line(`${CValtype[i[1]]} _r${br}`);
268
+
269
+ brDepth++;
270
+ };
271
+
272
+ const highlight = i => {
273
+ const surrounding = 6;
274
+
275
+ const decomp = decompile(f.wasm.slice(i - surrounding, i + surrounding + 1), '', 0, f.locals, f.params, f.returns, funcs, globals, exceptions).slice(0, -1).split('\n');
276
+
277
+ const noAnsi = s => s.replace(/\u001b\[[0-9]+m/g, '');
278
+ let longest = 0;
279
+ for (let j = 0; j < decomp.length; j++) {
280
+ longest = Math.max(longest, noAnsi(decomp[j]).length);
281
+ }
282
+
283
+ const middle = Math.floor(decomp.length / 2);
284
+ decomp[middle] = `\x1B[47m\x1B[30m${noAnsi(decomp[middle])}${'\u00a0'.repeat(longest - noAnsi(decomp[middle]).length)}\x1B[0m`;
285
+
286
+ console.log('\x1B[90m...\x1B[0m');
287
+ console.log(decomp.join('\n'));
288
+ console.log('\x1B[90m...\x1B[0m\n');
289
+ };
290
+
137
291
  for (let _ = 0; _ < f.wasm.length; _++) {
138
292
  const i = f.wasm[_];
293
+ if (!i || !i[0]) continue;
139
294
 
140
295
  if (invOperatorOpcode[i[0]]) {
141
296
  const b = vals.pop();
@@ -157,12 +312,12 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
157
312
  switch (i[1]) {
158
313
  // i32_trunc_sat_f64_s
159
314
  case 0x02:
160
- vals.push(`(${CValtype.i32})${vals.pop()}`);
315
+ vals.push(`(${CValtype.i32})(${vals.pop()})`);
161
316
  break;
162
317
 
163
318
  // i32_trunc_sat_f64_u
164
319
  case 0x03:
165
- vals.push(`(${CValtype.i32})(${CValtype.i32_u})${vals.pop()}`);
320
+ vals.push(`(${CValtype.u32})(${vals.pop()})`);
166
321
  break;
167
322
  }
168
323
 
@@ -173,12 +328,19 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
173
328
  switch (i[0]) {
174
329
  case Opcodes.i32_const:
175
330
  case Opcodes.i64_const:
176
- vals.push(read_signedLEB128(i.slice(1)).toString());
331
+ // vals.push(read_signedLEB128(i.slice(1)).toString());
332
+ vals.push(new String(read_signedLEB128(i.slice(1)).toString()));
333
+ vals.at(-1).offset = _;
177
334
  break;
178
335
 
179
- case Opcodes.f64_const:
180
- vals.push(read_ieee754_binary64(i.slice(1)).toExponential());
336
+ case Opcodes.f64_const: {
337
+ // const val = read_ieee754_binary64(i.slice(1)).toExponential();
338
+ const val = new String(read_ieee754_binary64(i.slice(1)).toExponential());
339
+ // vals.push(val == 'NaN' ? 'NAN' : val);
340
+ vals.push(val == 'NaN' ? new String('NAN') : val);
341
+ vals.at(-1).offset = _;
181
342
  break;
343
+ }
182
344
 
183
345
  case Opcodes.local_get:
184
346
  vals.push(`${invLocals[i[1]]}`);
@@ -189,9 +351,9 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
189
351
  break;
190
352
 
191
353
  case Opcodes.local_tee:
192
- // line(`${invLocals[i[1]]} = ${removeBrackets(vals.pop())}`);
193
- // vals.push(`${invLocals[i[1]]}`);
194
- vals.push(`((${invLocals[i[1]]} = ${vals.pop()}))`);
354
+ line(`${invLocals[i[1]]} = ${removeBrackets(vals.pop())}`);
355
+ vals.push(`${invLocals[i[1]]}`);
356
+ // vals.push(`((${invLocals[i[1]]} = ${vals.pop()}))`);
195
357
  break;
196
358
 
197
359
  case Opcodes.global_get:
@@ -204,78 +366,102 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
204
366
 
205
367
  case Opcodes.f64_trunc:
206
368
  // vals.push(`trunc(${vals.pop()})`);
207
- vals.push(`(int)(${removeBrackets(vals.pop())})`); // this is ~10x faster with clang??
369
+ vals.push(`(${CValtype.i32})(${removeBrackets(vals.pop())})`); // this is ~10x faster with clang??
208
370
  break;
209
371
 
210
372
  case Opcodes.f64_convert_i32_u:
211
373
  case Opcodes.f64_convert_i32_s:
212
374
  case Opcodes.f64_convert_i64_u:
213
375
  case Opcodes.f64_convert_i64_s:
214
- // int to double
215
- vals.push(`(double)${vals.pop()}`);
376
+ // int to f64
377
+ vals.push(`(${CValtype.f64})(${removeBrackets(vals.pop())})`);
216
378
  break;
217
379
 
380
+ case Opcodes.i32_eqz:
381
+ if (lastCond) {
382
+ vals.push(`!(${removeBrackets(vals.pop())})`);
383
+ } else {
384
+ let cond = '(' + removeBrackets(vals.pop());
385
+ if (cond.startsWith(`(${CValtype.i32})`)) cond = `${cond.slice(`(${CValtype.i32})`.length)}) == 0e+0`;
386
+ else cond += ') == 0';
387
+ vals.push(cond);
388
+ }
389
+ lastCond = true;
390
+ continue;
391
+
218
392
  case Opcodes.return:
219
393
  // line(`return${returns ? ` ${removeBrackets(vals.pop())}` : ''}`);
220
394
  line(`return${returns ? ` (struct ReturnValue){ ${removeBrackets(vals.pop())}, ${removeBrackets(vals.pop())} }` : ''}`);
221
395
  break;
222
396
 
223
- case Opcodes.if:
397
+ case Opcodes.if: {
224
398
  let cond = removeBrackets(vals.pop());
225
399
  if (!lastCond) {
226
- if (cond.startsWith('(long)')) cond = `${cond.slice(6)} == 1e+0`;
227
- else cond += ' == 1';
400
+ if (cond.startsWith(`(${CValtype.i32})`)) cond = `(${cond.slice(`(${CValtype.i32})`.length)}) != 0e+0`;
401
+ else cond = `(${cond}) != 0`;
228
402
  }
229
403
 
230
- ifTernary = i[1] !== Blocktype.void;
231
- if (ifTernary) {
232
- ifTernary = cond;
233
- break;
234
- }
235
-
236
- if (beginLoop) {
237
- beginLoop = false;
238
- line(`while (${cond}) {`, false);
239
-
240
- depth++;
241
- endNeedsCurly.push(true);
242
- ignoreEnd.push(false, true);
243
- break;
244
- }
404
+ line(`// if ${invValtype[i[1]] ?? ''}`, false);
405
+ blockStart(i, false);
245
406
 
246
407
  line(`if (${cond}) {`, false);
247
408
 
248
409
  depth++;
249
410
  endNeedsCurly.push(true);
250
- ignoreEnd.push(false);
251
411
  break;
412
+ }
252
413
 
253
- case Opcodes.else:
254
- if (ifTernary) break;
414
+ case Opcodes.else: {
415
+ const br = brs.at(-1);
416
+ const ret = rets.at(-1);
417
+ if (ret && ret !== Blocktype.void) {
418
+ // console.log(vals, ret);
419
+ // console.log(decompile(f.wasm.slice(_ - 5, _ + 1)));
420
+ if (vals.length > 0) line(`_r${br} = ${removeBrackets(vals.pop())}`);
421
+ // vals.push(`_r${br}`);
422
+ }
255
423
 
256
424
  depth--;
257
425
  line(`} else {`, false);
258
426
  depth++;
427
+
428
+ // reset "stack"
429
+ // vals = [];
259
430
  break;
431
+ }
260
432
 
261
- case Opcodes.loop:
262
- // not doing properly, fake a while loop
263
- beginLoop = true;
433
+ case Opcodes.loop: {
434
+ line(`// loop ${invValtype[i[1]] ?? ''}`, false);
435
+ blockStart(i, true);
436
+ endNeedsCurly.push(false);
264
437
  break;
438
+ }
265
439
 
266
- case Opcodes.end:
267
- if (ignoreEnd.pop()) break;
440
+ case Opcodes.end: {
441
+ const br = brs.pop();
442
+ const ret = rets.pop();
443
+ if (ret && ret !== Blocktype.void) {
444
+ // console.log(vals, ret);
445
+ // console.log(decompile(f.wasm.slice(_ - 5, _ + 1)));
446
+ if (vals.length > 0) line(`_r${br} = ${removeBrackets(vals.pop())}`);
447
+ vals.push(`_r${br}`);
448
+ }
268
449
 
269
- if (ifTernary) {
270
- const b = vals.pop();
271
- const a = vals.pop();
272
- vals.push(`${ifTernary} ? ${a} : ${b}`);
273
- break;
450
+ const enc = endNeedsCurly.pop() === true;
451
+ if (enc) {
452
+ depth--;
453
+ line('}', false);
274
454
  }
275
455
 
276
- depth--;
277
- if (endNeedsCurly.pop() === true) line('}', false);
456
+ brDepth--;
457
+
458
+ line(`// end`, false);
459
+
460
+ const roe = runOnEnd.pop();
461
+ if (roe) roe();
462
+
278
463
  break;
464
+ }
279
465
 
280
466
  case Opcodes.call:
281
467
  let func = funcs.find(x => x.index === i[1]);
@@ -283,7 +469,8 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
283
469
  const importFunc = importFuncs[i[1]];
284
470
  switch (importFunc.name) {
285
471
  case 'print':
286
- line(`printf("%f\\n", ${vals.pop()})`);
472
+ // line(`printf("%f\\n", ${vals.pop()})`);
473
+ line(`printf("${valtype === 'f64' ? '%g' : '%i'}\\n", ${vals.pop()})`);
287
474
  includes.set('stdio.h', true);
288
475
  break;
289
476
  case 'printChar':
@@ -346,13 +533,74 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
346
533
  vals.pop();
347
534
  break;
348
535
 
349
- case Opcodes.br:
350
- // ignore
351
- // reset "stack"
352
- vals = [];
536
+ case Opcodes.block:
537
+ line(`// block ${invValtype[i[1]] ?? ''}`, false);
538
+ blockStart(i, false);
539
+ endNeedsCurly.push(false);
540
+ break;
541
+
542
+ case Opcodes.br: {
543
+ const ret = rets[brDepth - i[1] - 1];
544
+ // console.log(rets, brDepth, i[1], brDepth - i[1] - 1, ret, vals);
545
+ if (ret !== Blocktype.void) line(`_r${brs[brDepth - i[1] - 1]} = ${removeBrackets(vals.pop())}`);
546
+ line(`goto j${brs[brDepth - i[1] - 1]}`);
547
+
548
+ // // reset "stack"
549
+ // vals = [];
353
550
  break;
551
+ }
552
+
553
+ case Opcodes.br_if: {
554
+ const ret = rets[brDepth - i[1] - 1];
555
+ // console.log(rets, brDepth, i[1], brDepth - i[1] - 1, ret, vals);
556
+
557
+ let cond = removeBrackets(vals.pop());
558
+ if (!lastCond) {
559
+ if (cond.startsWith(`(${CValtype.i32})`)) cond = `(${cond.slice(`(${CValtype.i32})`.length)}) != 0e+0`;
560
+ else cond = `(${cond}) != 0`;
561
+ }
562
+
563
+ line(`if (${cond}) {`, false);
564
+ depth++;
565
+ if (ret !== Blocktype.void) line(`_r${brs[brDepth - i[1] - 1]} = ${removeBrackets(vals.at(-1))}`);
566
+ line(`goto j${brs[brDepth - i[1] - 1]}`);
567
+ depth--;
568
+ line(`}`, false);
569
+
570
+ break;
571
+ }
572
+
573
+ case Opcodes.throw: {
574
+ const id = vals.pop();
575
+
576
+ line(`printf("Uncaught ${exceptions[id].constructor}: ${exceptions[id].message}\\n")`);
577
+ line(`exit(1)`);
578
+
579
+ includes.set('stdlib.h', true);
580
+
581
+ break;
582
+ }
354
583
 
355
584
  default:
585
+ if (CMemFuncs[i[0]]) {
586
+ const name = invOpcodes[i[0]];
587
+ const func = CMemFuncs[i[0]];
588
+ if (!prepend.has(name)) {
589
+ prepend.set(name, `${func.returns || 'void'} ${name}(${CValtype.i32} align, ${CValtype.i32} offset, ${func.args.map((x, i) => `${func.argTypes[i]} ${x}`).join(', ')}) {\n ${func.c.replaceAll('\n', '\n ')}\n}\n`);
590
+ // generate func c and prepend
591
+ }
592
+
593
+ const immediates = [ i[1], read_unsignedLEB128(i.slice(2)) ];
594
+
595
+ let args = [];
596
+ for (let j = 0; j < func.args.length; j++) args.unshift(removeBrackets(vals.pop()));
597
+
598
+ if (func.returns !== false) {
599
+ vals.push(`${name}(${immediates[0]}, ${immediates[1]}, ${args.join(', ')})`);
600
+ } else line(`${name}(${immediates[0]}, ${immediates[1]}, ${args.join(', ')})`);
601
+ break;
602
+ }
603
+
356
604
  log.warning('2c', `unimplemented op: ${invOpcodes[i[0]]}`);
357
605
  // todo(`unimplemented op: ${invOpcodes[i[0]]}`);
358
606
  }
@@ -364,6 +612,11 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
364
612
  line(`return ${vals.pop()}`);
365
613
  }
366
614
 
615
+ if (f.name === 'main') {
616
+ out += '\n';
617
+ line(`return 0`);
618
+ }
619
+
367
620
  out += '}\n\n';
368
621
  }
369
622
 
@@ -371,7 +624,7 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
371
624
 
372
625
  const makeIncludes = includes => [...includes.keys()].map(x => `#include <${x}>\n`).join('');
373
626
 
374
- out = platformSpecific(makeIncludes(winIncludes), makeIncludes(unixIncludes), false) + '\n' + makeIncludes(includes) + '\n' + out;
627
+ out = platformSpecific(makeIncludes(winIncludes), makeIncludes(unixIncludes), false) + '\n' + makeIncludes(includes) + '\n' + alwaysPreface + [...prepend.values()].join('\n') + '\n\n' + out;
375
628
 
376
- return out;
629
+ return out.trim();
377
630
  };
@@ -0,0 +1,68 @@
1
+ // @porf -funsafe-no-unlikely-proto-checks
2
+
3
+ import type { i32, bytestring } from './porffor.d.ts';
4
+
5
+ const keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
6
+
7
+ const btoa = (input: bytestring): bytestring => {
8
+ // todo: throw invalid character for unicode
9
+
10
+ let output: bytestring = "";
11
+ let i: i32 = 0;
12
+
13
+ while (i < input.length) {
14
+ const chr1: i32 = input.charCodeAt(i++);
15
+ const chr2: i32 = input.charCodeAt(i++);
16
+ const chr3: i32 = input.charCodeAt(i++);
17
+
18
+ const enc1: i32 = chr1 >> 2;
19
+ const enc2: i32 = ((chr1 & 3) << 4) | (chr2 >> 4);
20
+ let enc3: i32 = ((chr2 & 15) << 2) | (chr3 >> 6);
21
+ let enc4: i32 = chr3 & 63;
22
+
23
+ if (isNaN(chr2)) {
24
+ enc3 = 64;
25
+ enc4 = 64;
26
+ } else if (isNaN(chr3)) {
27
+ enc4 = 64;
28
+ }
29
+
30
+ output += keyStr.charAt(enc1);
31
+ output += keyStr.charAt(enc2);
32
+ output += keyStr.charAt(enc3);
33
+ output += keyStr.charAt(enc4);
34
+ }
35
+
36
+ return output;
37
+ };
38
+
39
+ /* var atob = function (input) {
40
+ const keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
41
+
42
+ let output = "";
43
+ let chr1, chr2, chr3;
44
+ let enc1, enc2, enc3, enc4;
45
+ let i = 0;
46
+
47
+ while (i < input.length) {
48
+ enc1 = keyStr.indexOf(input.charAt(i++));
49
+ enc2 = keyStr.indexOf(input.charAt(i++));
50
+ enc3 = keyStr.indexOf(input.charAt(i++));
51
+ enc4 = keyStr.indexOf(input.charAt(i++));
52
+
53
+ chr1 = (enc1 << 2) | (enc2 >> 4);
54
+ chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
55
+ chr3 = ((enc3 & 3) << 6) | enc4;
56
+
57
+ output += String.fromCharCode(chr1);
58
+
59
+ if (enc3 != 64) {
60
+ output += String.fromCharCode(chr2);
61
+ }
62
+ if (enc4 != 64) {
63
+ output += String.fromCharCode(chr3);
64
+ }
65
+ }
66
+
67
+ return output;
68
+ }; */
@@ -0,0 +1,2 @@
1
+ export type i32 = number;
2
+ export type bytestring = string;