porffor 0.16.0-688a50c13 → 0.16.0-7143cc59c

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/README.md CHANGED
@@ -29,7 +29,7 @@ Expect nothing to work! Only very limited JS is currently supported. See files i
29
29
  > [!WARNING]
30
30
  > Compiling to native binaries uses [2c](#2c), Porffor's own Wasm -> C compiler, which is experimental.
31
31
 
32
- **`porf native path/to/script.js out(.exe)`**. You can specify the compiler with `--compiler=clang/zig/gcc`, and which opt level to use with `--cO=O3` (`Ofast` by default). Output binaries are also stripped by default.
32
+ **`porf native path/to/script.js out(.exe)`**. You can specify the compiler with `--compiler=clang/gcc/zig` (`clang` by default), and which optimization level to use with `--cO=Ofast/O3/O2/O1/O0` (`Ofast` by default). Output binaries are also stripped by default.
33
33
 
34
34
  ### Compiling to C
35
35
  > [!WARNING]
@@ -49,7 +49,7 @@ Expect nothing to work! Only very limited JS is currently supported. See files i
49
49
 
50
50
  **`porf debug path/to/script.js`**
51
51
 
52
- ### Profiling the generated Wasm of a JS file
52
+ ### Debugging the compiled Wasm of a JS file
53
53
  > [!WARNING]
54
54
  > Very experimental WIP feature!
55
55
 
@@ -82,7 +82,6 @@ Expect nothing to work! Only very limited JS is currently supported. See files i
82
82
  - Little built-ins/prototype
83
83
  - No async/promise/await
84
84
  - No variables between scopes (except args and globals)
85
- - Literal callees only in calls (eg `print()` works, `a = print; a()` does not)
86
85
  - No `eval()` etc (since it is AOT)
87
86
 
88
87
  ## Sub-engines
@@ -112,7 +111,7 @@ These include some early (stage 1/0) and/or dead (last commit years ago) proposa
112
111
 
113
112
  - Number literals
114
113
  - Declaring functions
115
- - Calling functions *literal callees only*
114
+ - Calling functions
116
115
  - `return`
117
116
  - `let`/`const`/`var` basic declarations
118
117
  - Some basic integer operators (`+-/*%`)
package/compiler/2c.js CHANGED
@@ -2,10 +2,11 @@ import { read_ieee754_binary64, read_signedLEB128, read_unsignedLEB128 } from '.
2
2
  import { Blocktype, Opcodes, Valtype } from './wasmSpec.js';
3
3
  import { operatorOpcode } from './expression.js';
4
4
  import { log } from './log.js';
5
+ import Prefs from './prefs.js';
5
6
 
6
7
  const CValtype = {
7
- i8: 'i8',
8
- i16: 'i16',
8
+ i8: 'u8',
9
+ i16: 'u16',
9
10
  i32: 'i32',
10
11
  u32: 'u32',
11
12
  i64: 'i64',
@@ -17,8 +18,8 @@ const CValtype = {
17
18
  undefined: 'void'
18
19
  };
19
20
 
20
- const alwaysPreface = `typedef uint8_t i8;
21
- typedef uint16_t i16;
21
+ const alwaysPreface = `typedef uint8_t u8;
22
+ typedef uint16_t u16;
22
23
  typedef int32_t i32;
23
24
  typedef uint32_t u32;
24
25
  typedef int64_t i64;
@@ -33,11 +34,11 @@ struct ReturnValue {
33
34
  i32 type;
34
35
  };\n\n`;
35
36
 
36
- // todo: is memcpy/etc safe with host endianness?
37
+ // todo: review whether 2cMemcpy should be default or not
37
38
 
38
39
  // all:
39
40
  // immediates: ['align', 'offset']
40
- const CMemFuncs = {
41
+ const CMemFuncs = Prefs['2cMemcpy'] ? {
41
42
  [Opcodes.i32_store]: {
42
43
  c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
43
44
  args: ['pointer', 'value'],
@@ -47,13 +48,13 @@ const CMemFuncs = {
47
48
  [Opcodes.i32_store16]: {
48
49
  c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
49
50
  args: ['pointer', 'value'],
50
- argTypes: ['i32', 'i16'],
51
+ argTypes: ['i32', 'u16'],
51
52
  returns: false
52
53
  },
53
54
  [Opcodes.i32_store8]: {
54
55
  c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
55
56
  args: ['pointer', 'value'],
56
- argTypes: ['i32', 'i8'],
57
+ argTypes: ['i32', 'u8'],
57
58
  returns: false
58
59
  },
59
60
 
@@ -66,7 +67,7 @@ return out;`,
66
67
  returns: 'i32'
67
68
  },
68
69
  [Opcodes.i32_load16_u]: {
69
- c: `i16 out;
70
+ c: `u16 out;
70
71
  memcpy(&out, _memory + offset + pointer, sizeof(out));
71
72
  return out;`,
72
73
  args: ['pointer'],
@@ -74,7 +75,7 @@ return out;`,
74
75
  returns: 'i32'
75
76
  },
76
77
  [Opcodes.i32_load8_u]: {
77
- c: `i8 out;
78
+ c: `u8 out;
78
79
  memcpy(&out, _memory + offset + pointer, sizeof(out));
79
80
  return out;`,
80
81
  args: ['pointer'],
@@ -96,6 +97,57 @@ return out;`,
96
97
  argTypes: ['i32'],
97
98
  returns: 'f64'
98
99
  },
100
+ } : {
101
+ [Opcodes.i32_store]: {
102
+ c: `*((i32*)(_memory + offset + pointer)) = value;`,
103
+ args: ['pointer', 'value'],
104
+ argTypes: ['i32', 'i32'],
105
+ returns: false
106
+ },
107
+ [Opcodes.i32_store16]: {
108
+ c: `*((u16*)(_memory + offset + pointer)) = value;`,
109
+ args: ['pointer', 'value'],
110
+ argTypes: ['i32', 'u16'],
111
+ returns: false
112
+ },
113
+ [Opcodes.i32_store8]: {
114
+ c: `*((u8*)(_memory + offset + pointer)) = value;`,
115
+ args: ['pointer', 'value'],
116
+ argTypes: ['i32', 'u8'],
117
+ returns: false
118
+ },
119
+
120
+ [Opcodes.i32_load]: {
121
+ c: `return *((i32*)(_memory + offset + pointer));`,
122
+ args: ['pointer'],
123
+ argTypes: ['i32'],
124
+ returns: 'i32'
125
+ },
126
+ [Opcodes.i32_load16_u]: {
127
+ c: `return *((u16*)(_memory + offset + pointer));`,
128
+ args: ['pointer'],
129
+ argTypes: ['i32'],
130
+ returns: 'i32'
131
+ },
132
+ [Opcodes.i32_load8_u]: {
133
+ c: `return *((u8*)(_memory + offset + pointer));`,
134
+ args: ['pointer'],
135
+ argTypes: ['i32'],
136
+ returns: 'i32'
137
+ },
138
+
139
+ [Opcodes.f64_store]: {
140
+ c: `*((f64*)(_memory + offset + pointer)) = value;`,
141
+ args: ['pointer', 'value'],
142
+ argTypes: ['i32', 'f64'],
143
+ returns: false
144
+ },
145
+ [Opcodes.f64_load]: {
146
+ c: `return *((f64*)(_memory + offset + pointer));`,
147
+ args: ['pointer'],
148
+ argTypes: ['i32'],
149
+ returns: 'f64'
150
+ },
99
151
  };
100
152
 
101
153
  const inv = (obj, keyMap = x => x) => Object.keys(obj).reduce((acc, x) => { acc[keyMap(obj[x])] = x; return acc; }, {});
@@ -152,11 +204,16 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
152
204
 
153
205
  if (pages.size > 0) {
154
206
  prepend.set('_memory', `char _memory[${pages.size * pageSize}];\n`);
155
- includes.set('string.h', true);
207
+ if (Prefs['2cMemcpy']) includes.set('string.h', true);
156
208
  }
157
209
 
158
210
  if (data.length > 0) {
159
- prependMain.set('_data', data.map(x => `memcpy(_memory + ${x.offset}, (unsigned char[]){${x.bytes.join(',')}}, ${x.bytes.length});`).join('\n '));
211
+ if (Prefs['2cMemcpy']) {
212
+ prependMain.set('_data', data.map(x => `memcpy(_memory + ${x.offset}, (unsigned char[]){${x.bytes.join(',')}}, ${x.bytes.length});`).join('\n '));
213
+ includes.set('string.h', true);
214
+ } else {
215
+ prependMain.set('_data', data.map(x => x.bytes.reduce((acc, y, i) => acc + `_memory[${x.offset + i}]=(u8)${y};`, '')).join('\n '));
216
+ }
160
217
  }
161
218
 
162
219
  if (importFuncs.find(x => x.name === '__Porffor_readArgv')) {
@@ -362,7 +419,10 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
362
419
 
363
420
  case Opcodes.return:
364
421
  // line(`return${returns ? ` ${removeBrackets(vals.pop())}` : ''}`);
365
- line(`return${returns ? ` (struct ReturnValue){ ${removeBrackets(vals.pop())}, ${removeBrackets(vals.pop())} }` : ''}`);
422
+ const b = vals.pop();
423
+ const a = vals.pop();
424
+ line(`return${returns ? ` (struct ReturnValue){ ${removeBrackets(a)}, ${removeBrackets(b)} }` : ''}`);
425
+ // line(`return${returns ? ` (struct ReturnValue){ ${removeBrackets(vals.pop())}, ${removeBrackets(vals.pop())} }` : ''}`);
366
426
  break;
367
427
 
368
428
  case Opcodes.if: {
@@ -474,7 +534,7 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
474
534
  out[read++] = ch;
475
535
  }
476
536
 
477
- memcpy(_memory + outPtr, &read, sizeof(read));
537
+ *((i32*)(_memory + outPtr)) = (i32)read;
478
538
  return read;
479
539
  }`);
480
540
 
@@ -504,7 +564,7 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
504
564
 
505
565
  fclose(fp);
506
566
 
507
- memcpy(_memory + outPtr, &read, sizeof(read));
567
+ *((i32*)(_memory + outPtr)) = (i32)read;
508
568
  return read;
509
569
  }`);
510
570
  const outPtr = vals.pop();
@@ -0,0 +1,128 @@
1
+ import { Opcodes, PageSize, Valtype } from './wasmSpec.js';
2
+ import { number } from './embedding.js';
3
+ import Prefs from './prefs.js';
4
+
5
+ // we currently have 3 allocators:
6
+ // - static (default): a static/compile-time allocator. fast (no grow/run-time alloc needed) but can break some code
7
+ // - grow: perform a memory.grow every allocation. simple but maybe slow?
8
+ // - chunk: perform large memory.grow's in chunks when needed. needs investigation
9
+
10
+ export default name => {
11
+ switch (name) {
12
+ case 'static': return new StaticAllocator();
13
+ case 'grow': return new GrowAllocator();
14
+ case 'chunk': return new ChunkAllocator();
15
+ default: throw new Error(`unknown allocator: ${name}`);
16
+ }
17
+ };
18
+
19
+ export class StaticAllocator {
20
+ constructor() {
21
+ }
22
+
23
+ allocType(itemType) {
24
+ switch (itemType) {
25
+ case 'i8': return 'bytestring';
26
+ case 'i16': return 'string';
27
+
28
+ default: return 'array';
29
+ }
30
+ }
31
+
32
+ ptr(ind) {
33
+ if (ind === 0) return 4;
34
+ return ind * PageSize;
35
+ }
36
+
37
+ alloc({ scope, pages }, name, { itemType }) {
38
+ const reason = `${this.allocType(itemType)}: ${Prefs.scopedPageNames ? (scope.name + '/') : ''}${name}`;
39
+
40
+ if (pages.has(reason)) return number(this.ptr(pages.get(reason).ind), Valtype.i32);
41
+
42
+ if (reason.startsWith('array:')) pages.hasArray = true;
43
+ if (reason.startsWith('string:')) pages.hasString = true;
44
+ if (reason.startsWith('bytestring:')) pages.hasByteString = true;
45
+ if (reason.includes('string:')) pages.hasAnyString = true;
46
+
47
+ let ind = pages.size;
48
+ pages.set(reason, { ind, type: itemType });
49
+
50
+ scope.pages ??= new Map();
51
+ scope.pages.set(reason, { ind, type: itemType });
52
+
53
+ return number(this.ptr(ind), Valtype.i32);
54
+ }
55
+ }
56
+
57
+ export class GrowAllocator {
58
+ constructor() {
59
+ Prefs.rmUnusedTypes = false;
60
+ }
61
+
62
+ alloc() {
63
+ return [
64
+ // grow by 1 page
65
+ [ Opcodes.i32_const, 1 ],
66
+ [ Opcodes.memory_grow, 0 ], // returns old page count
67
+
68
+ // get ptr (page count * page size)
69
+ number(65536, Valtype.i32)[0],
70
+ [ Opcodes.i32_mul ]
71
+ ];
72
+ }
73
+ }
74
+
75
+ export class ChunkAllocator {
76
+ constructor(chunkSize) {
77
+ Prefs.rmUnusedTypes = false;
78
+
79
+ // 64KiB * chunk size each growth
80
+ // 16: 1MiB chunks
81
+ this.chunkSize = chunkSize ?? Prefs.chunkAllocatorSize ?? 16;
82
+ }
83
+
84
+ alloc({ asmFunc, funcIndex }) {
85
+ const func = funcIndex['#chunkallocator_alloc'] ?? asmFunc('#chunkallocator_alloc', {
86
+ wasm: [
87
+ [ Opcodes.global_get, 0 ],
88
+ [ Opcodes.global_get, 1 ],
89
+ [ Opcodes.i32_ge_s ],
90
+ [ Opcodes.if, Valtype.i32 ], // ptr >= next
91
+ // grow by chunk size pages
92
+ [ Opcodes.i32_const, this.chunkSize ],
93
+ [ Opcodes.memory_grow, 0 ],
94
+
95
+ // ptr = prev memory size * PageSize
96
+ number(65536, Valtype.i32)[0],
97
+ [ Opcodes.i32_mul ],
98
+ [ Opcodes.global_set, 0 ],
99
+
100
+ // next = ptr + ((chunkSize - 1) * PageSize)
101
+ [ Opcodes.global_get, 0 ],
102
+ number(65536 * (this.chunkSize - 1), Valtype.i32)[0],
103
+ [ Opcodes.i32_add ],
104
+ [ Opcodes.global_set, 1 ],
105
+
106
+ // return ptr
107
+ [ Opcodes.global_get, 0 ],
108
+ [ Opcodes.else ],
109
+ // return ptr = ptr + PageSize
110
+ [ Opcodes.global_get, 0 ],
111
+ number(65536, Valtype.i32)[0],
112
+ [ Opcodes.i32_add ],
113
+ [ Opcodes.global_set, 0 ],
114
+ [ Opcodes.global_get, 0 ],
115
+ [ Opcodes.end ],
116
+ ],
117
+ params: [],
118
+ locals: [],
119
+ globals: [ Valtype.i32, Valtype.i32 ],
120
+ globalNames: ['#chunkallocator_ptr', '#chunkallocator_next'],
121
+ returns: [ Valtype.i32 ],
122
+ }).index;
123
+
124
+ return [
125
+ [ Opcodes.call, func ]
126
+ ];
127
+ }
128
+ }
@@ -240,7 +240,13 @@ export default (funcs, globals, tags, pages, data, flags, noTreeshake = false) =
240
240
 
241
241
  const dataSection = data.length === 0 ? [] : createSection(
242
242
  Section.data,
243
- encodeVector(data.map(x => [ 0x00, Opcodes.i32_const, ...signedLEB128(x.offset), Opcodes.end, ...encodeVector(x.bytes) ]))
243
+ encodeVector(data.map(x => {
244
+ // type: active
245
+ if (x.offset != null) return [ 0x00, Opcodes.i32_const, ...signedLEB128(x.offset), Opcodes.end, ...encodeVector(x.bytes) ];
246
+
247
+ // type: passive
248
+ return [ 0x01, ...encodeVector(x.bytes) ];
249
+ }))
244
250
  );
245
251
 
246
252
  const dataCountSection = data.length === 0 ? [] : createSection(
@@ -171,12 +171,11 @@ export const __Array_prototype_filter = (_this: any[], callbackFn: any) => {
171
171
  };
172
172
 
173
173
  export const __Array_prototype_map = (_this: any[], callbackFn: any) => {
174
- const out: any[] = [];
175
-
176
- const len: i32 = _this.length;
177
174
  let i: i32 = 0;
175
+ const len: i32 = _this.length;
176
+ const out: any[] = new Array(len);
178
177
  while (i < len) {
179
- out.push(callbackFn(_this[i], i++, _this));
178
+ out[i] = callbackFn(_this[i], i++, _this);
180
179
  }
181
180
 
182
181
  return out;
@@ -722,10 +722,9 @@ export const __Porffor_date_allocate = (): Date => {
722
722
  const hack: bytestring = '';
723
723
 
724
724
  if (hack.length == 0) {
725
- hack.length = Porffor.wasm`memory.size 0
725
+ hack.length = Porffor.wasm`
726
726
  i32.const 1
727
727
  memory.grow 0
728
- drop
729
728
  i32.const 65536
730
729
  i32.mul
731
730
  i32.from_u`;
@@ -2,10 +2,9 @@ import type {} from './porffor.d.ts';
2
2
 
3
3
  // dark wasm magic for dealing with memory, sorry.
4
4
  export const __Porffor_allocate = (): number => {
5
- Porffor.wasm`memory.size 0
5
+ Porffor.wasm`
6
6
  i32.const 1
7
7
  memory.grow 0
8
- drop
9
8
  i32.const 65536
10
9
  i32.mul
11
10
  i32.from_u
@@ -9,7 +9,7 @@ import * as Rhemyn from '../rhemyn/compile.js';
9
9
  import parse from './parse.js';
10
10
  import { log } from './log.js';
11
11
  import Prefs from './prefs.js';
12
- import Allocator from './allocators/index.js';
12
+ import makeAllocator from './allocators.js';
13
13
 
14
14
  let globals = {};
15
15
  let tags = [];
@@ -33,11 +33,6 @@ const todo = (scope, msg, expectsValue = undefined) => {
33
33
 
34
34
  case 'runtime':
35
35
  return internalThrow(scope, 'TodoError', msg, expectsValue);
36
-
37
- // return [
38
- // ...debug(`todo! ${msg}`),
39
- // [ Opcodes.unreachable ]
40
- // ];
41
36
  }
42
37
  };
43
38
 
@@ -431,7 +426,7 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
431
426
  const leftPointer = localTmp(scope, 'concat_left_pointer', Valtype.i32);
432
427
 
433
428
  // alloc/assign array
434
- const [ , pointer ] = makeArray(scope, {
429
+ const [ out, pointer ] = makeArray(scope, {
435
430
  rawElements: new Array(0)
436
431
  }, global, name, true, 'i16', true);
437
432
 
@@ -447,7 +442,7 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
447
442
  [ Opcodes.local_set, rightPointer ],
448
443
 
449
444
  // calculate length
450
- ...pointer, // base 0 for store later
445
+ ...out,
451
446
 
452
447
  [ Opcodes.local_get, leftPointer ],
453
448
  [ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
@@ -1003,7 +998,7 @@ const asmFuncToAsm = (func, scope) => {
1003
998
  });
1004
999
  };
1005
1000
 
1006
- const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false }) => {
1001
+ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits = [], returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false }) => {
1007
1002
  const existing = funcs.find(x => x.name === name);
1008
1003
  if (existing) return existing;
1009
1004
 
@@ -1017,7 +1012,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1017
1012
 
1018
1013
  for (const x of _data) {
1019
1014
  const copy = { ...x };
1020
- copy.offset += pages.size * pageSize;
1015
+ if (copy.offset != null) copy.offset += pages.size * pageSize;
1021
1016
  data.push(copy);
1022
1017
  }
1023
1018
 
@@ -1403,10 +1398,10 @@ const countLeftover = wasm => {
1403
1398
 
1404
1399
  if (depth === 0)
1405
1400
  if ([Opcodes.throw, Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
1406
- else if ([null, Opcodes.i32_eqz, Opcodes.i64_eqz, Opcodes.f64_ceil, Opcodes.f64_floor, Opcodes.f64_trunc, Opcodes.f64_nearest, Opcodes.f64_sqrt, Opcodes.local_tee, Opcodes.i32_wrap_i64, Opcodes.i64_extend_i32_s, Opcodes.i64_extend_i32_u, Opcodes.f32_demote_f64, Opcodes.f64_promote_f32, Opcodes.f64_convert_i32_s, Opcodes.f64_convert_i32_u, Opcodes.i32_clz, Opcodes.i32_ctz, Opcodes.i32_popcnt, Opcodes.f64_neg, Opcodes.end, Opcodes.i32_trunc_sat_f64_s[0], Opcodes.i32x4_extract_lane, Opcodes.i16x8_extract_lane, Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load, Opcodes.v128_load, Opcodes.i32_load16_u, Opcodes.i32_load16_s, Opcodes.i32_load8_u, Opcodes.i32_load8_s, Opcodes.memory_grow].includes(inst[0]) && (inst[0] !== 0xfc || inst[1] < 0x0a)) {}
1401
+ else if ([null, Opcodes.i32_eqz, Opcodes.i64_eqz, Opcodes.f64_ceil, Opcodes.f64_floor, Opcodes.f64_trunc, Opcodes.f64_nearest, Opcodes.f64_sqrt, Opcodes.local_tee, Opcodes.i32_wrap_i64, Opcodes.i64_extend_i32_s, Opcodes.i64_extend_i32_u, Opcodes.f32_demote_f64, Opcodes.f64_promote_f32, Opcodes.f64_convert_i32_s, Opcodes.f64_convert_i32_u, Opcodes.i32_clz, Opcodes.i32_ctz, Opcodes.i32_popcnt, Opcodes.f64_neg, Opcodes.end, Opcodes.i32_trunc_sat_f64_s[0], Opcodes.i32x4_extract_lane, Opcodes.i16x8_extract_lane, Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load, Opcodes.v128_load, Opcodes.i32_load16_u, Opcodes.i32_load16_s, Opcodes.i32_load8_u, Opcodes.i32_load8_s, Opcodes.memory_grow].includes(inst[0]) && (inst[0] !== 0xfc || inst[1] < 0x04)) {}
1407
1402
  else if ([Opcodes.local_get, Opcodes.global_get, Opcodes.f64_const, Opcodes.i32_const, Opcodes.i64_const, Opcodes.v128_const, Opcodes.memory_size].includes(inst[0])) count++;
1408
1403
  else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
1409
- else if (Opcodes.memory_copy[0] === inst[0] && Opcodes.memory_copy[1] === inst[1]) count -= 3;
1404
+ else if (inst[0] === Opcodes.memory_copy[0] && (inst[1] === Opcodes.memory_copy[1] || inst[1] === Opcodes.memory_init[1])) count -= 3;
1410
1405
  else if (inst[0] === Opcodes.return) count = 0;
1411
1406
  else if (inst[0] === Opcodes.call) {
1412
1407
  let func = funcs.find(x => x.index === inst[1]);
@@ -3117,8 +3112,6 @@ const allocPage = (scope, reason, type) => {
3117
3112
  scope.pages ??= new Map();
3118
3113
  scope.pages.set(reason, { ind, type });
3119
3114
 
3120
- if (Prefs.allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
3121
-
3122
3115
  return ind;
3123
3116
  };
3124
3117
 
@@ -3156,6 +3149,46 @@ const compileBytes = (val, itemType) => {
3156
3149
  }
3157
3150
  };
3158
3151
 
3152
+ const makeData = (scope, elements, offset = null, itemType, initEmpty) => {
3153
+ const length = elements.length;
3154
+
3155
+ // if length is 0 memory/data will just be 0000... anyway
3156
+ if (length === 0) return false;
3157
+
3158
+ let bytes = compileBytes(length, 'i32');
3159
+
3160
+ if (!initEmpty) for (let i = 0; i < length; i++) {
3161
+ if (elements[i] == null) continue;
3162
+
3163
+ bytes.push(...compileBytes(elements[i], itemType));
3164
+ }
3165
+
3166
+ const obj = { bytes };
3167
+ if (offset != null) obj.offset = offset;
3168
+
3169
+ const idx = data.push(obj) - 1;
3170
+
3171
+ scope.data ??= [];
3172
+ scope.data.push(idx);
3173
+
3174
+ return { idx, size: bytes.length };
3175
+ };
3176
+
3177
+ const printStaticStr = str => {
3178
+ const out = [];
3179
+
3180
+ for (let i = 0; i < str.length; i++) {
3181
+ out.push(
3182
+ // ...number(str.charCodeAt(i)),
3183
+ ...number(str.charCodeAt(i), Valtype.i32),
3184
+ Opcodes.i32_from_u,
3185
+ [ Opcodes.call, importedFuncs.printChar ]
3186
+ );
3187
+ }
3188
+
3189
+ return out;
3190
+ };
3191
+
3159
3192
  const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, intOut = false, typed = false) => {
3160
3193
  if (itemType !== 'i16' && itemType !== 'i8') {
3161
3194
  pages.hasArray = true;
@@ -3175,17 +3208,50 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3175
3208
  const valtype = itemTypeToValtype[itemType];
3176
3209
  const length = elements.length;
3177
3210
 
3178
- const allocated = allocator.alloc({ scope, pages, globals }, uniqueName, { itemType });
3211
+ const allocated = allocator.alloc({ scope, pages, globals, asmFunc, funcIndex }, uniqueName, { itemType });
3179
3212
 
3180
3213
  let pointer = allocated;
3181
3214
  if (allocator.constructor.name !== 'StaticAllocator') {
3182
- const tmp = localTmp(scope, '#makearray_pointer' + uniqueName, Valtype.i32);
3215
+ // const tmp = localTmp(scope, '#makearray_pointer' + uniqueName, Valtype.i32);
3216
+ const tmp = localTmp(scope, '#makearray_pointer' + name, Valtype.i32);
3183
3217
  out.push(
3184
3218
  ...allocated,
3185
3219
  [ Opcodes.local_set, tmp ]
3186
3220
  );
3187
3221
 
3222
+ if (Prefs.runtimeAllocLog) out.push(
3223
+ ...printStaticStr(`${name}: `),
3224
+
3225
+ [ Opcodes.local_get, tmp ],
3226
+ Opcodes.i32_from_u,
3227
+ [ Opcodes.call, 0 ],
3228
+
3229
+ ...number(10),
3230
+ [ Opcodes.call, 1 ]
3231
+ );
3232
+
3188
3233
  pointer = [ [ Opcodes.local_get, tmp ] ];
3234
+
3235
+ if (Prefs.data && useRawElements) {
3236
+ const data = makeData(scope, elements, null, itemType, initEmpty);
3237
+ if (data) {
3238
+ // init data
3239
+ out.push(
3240
+ ...pointer,
3241
+ ...number(0, Valtype.i32),
3242
+ ...number(data.size, Valtype.i32),
3243
+ [ ...Opcodes.memory_init, ...unsignedLEB128(data.idx), 0 ]
3244
+ );
3245
+ }
3246
+
3247
+ // return pointer in out
3248
+ out.push(
3249
+ ...pointer,
3250
+ ...(!intOut ? [ Opcodes.i32_from_u ] : [])
3251
+ );
3252
+
3253
+ return [ out, pointer ];
3254
+ }
3189
3255
  } else {
3190
3256
  const rawPtr = read_signedLEB128(pointer[0].slice(1));
3191
3257
 
@@ -3194,24 +3260,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3194
3260
  if (firstAssign) scope.arrays.set(uniqueName, rawPtr);
3195
3261
 
3196
3262
  if (Prefs.data && firstAssign && useRawElements) {
3197
- // if length is 0 memory/data will just be 0000... anyway
3198
- if (length !== 0) {
3199
- let bytes = compileBytes(length, 'i32');
3200
-
3201
- if (!initEmpty) for (let i = 0; i < length; i++) {
3202
- if (elements[i] == null) continue;
3203
-
3204
- bytes.push(...compileBytes(elements[i], itemType));
3205
- }
3206
-
3207
- const ind = data.push({
3208
- offset: rawPtr,
3209
- bytes
3210
- }) - 1;
3211
-
3212
- scope.data ??= [];
3213
- scope.data.push(ind);
3214
- }
3263
+ makeData(scope, elements, rawPtr, itemType, initEmpty);
3215
3264
 
3216
3265
  // local value as pointer
3217
3266
  return [ number(rawPtr, intOut ? Valtype.i32 : valtypeBinary), pointer ];
@@ -3694,9 +3743,9 @@ const internalConstrs = {
3694
3743
 
3695
3744
  // new Array(n)
3696
3745
 
3697
- const [ , pointer ] = makeArray(scope, {
3746
+ const [ out, pointer ] = makeArray(scope, {
3698
3747
  rawElements: new Array(0)
3699
- }, global, name, true);
3748
+ }, global, name, true, undefined, true);
3700
3749
 
3701
3750
  const arg = decl.arguments[0] ?? DEFAULT_VALUE;
3702
3751
 
@@ -3705,7 +3754,7 @@ const internalConstrs = {
3705
3754
  if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length', true);
3706
3755
 
3707
3756
  return [
3708
- ...pointer,
3757
+ ...out,
3709
3758
  ...generate(scope, arg, global, name),
3710
3759
  Opcodes.i32_to_u,
3711
3760
  [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
@@ -3895,7 +3944,7 @@ export default program => {
3895
3944
  builtinFuncs = new BuiltinFuncs();
3896
3945
  builtinVars = new BuiltinVars();
3897
3946
  prototypeFuncs = new PrototypeFuncs();
3898
- allocator = Allocator(Prefs.allocator ?? 'static');
3947
+ allocator = makeAllocator(Prefs.allocator ?? 'static');
3899
3948
 
3900
3949
  program.id = { name: 'main' };
3901
3950