porffor 0.16.0-f9dde1759 → 0.16.0-f9e625159
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 +5 -17
- package/compiler/2c.js +75 -15
- package/compiler/allocators.js +128 -0
- package/compiler/assemble.js +7 -1
- package/compiler/builtins/array.ts +72 -5
- package/compiler/builtins/date.ts +3 -30
- package/compiler/builtins/number.ts +10 -21
- package/compiler/builtins/porffor.d.ts +7 -0
- package/compiler/builtins/set.ts +2 -5
- package/compiler/builtins/z_ecma262.ts +62 -0
- package/compiler/codegen.js +276 -327
- package/compiler/cyclone.js +11 -11
- package/compiler/generated_builtins.js +113 -75
- package/compiler/index.js +15 -1
- package/compiler/opt.js +8 -6
- package/compiler/parse.js +0 -6
- package/compiler/pgo.js +5 -0
- package/compiler/precompile.js +9 -6
- package/compiler/prefs.js +2 -3
- package/compiler/prototype.js +34 -43
- package/compiler/wasmSpec.js +2 -2
- package/compiler/wrap.js +2 -1
- package/package.json +1 -1
- package/runner/index.js +5 -0
- package/no_pgo.txt +0 -923
- package/pgo.txt +0 -916
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
|
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
|
-
###
|
52
|
+
### Debugging the compiled Wasm of a JS file
|
53
53
|
> [!WARNING]
|
54
54
|
> Very experimental WIP feature!
|
55
55
|
|
@@ -63,26 +63,14 @@ Expect nothing to work! Only very limited JS is currently supported. See files i
|
|
63
63
|
- `--valtype=i32|i64|f64` (default: `f64`) to set valtype
|
64
64
|
- `-O0` to disable opt
|
65
65
|
- `-O1` (default) to enable basic opt (simplify insts, treeshake wasm imports)
|
66
|
-
- `-O2` to enable advanced opt (inlining). unstable
|
67
|
-
- `-O3` to enable advanceder opt (precompute const math). unstable
|
68
|
-
- `--no-run` to not run wasm output, just compile
|
69
|
-
- `--opt-log` to log some opts
|
70
|
-
- `--code-log` to log some codegen (you probably want `-funcs`)
|
71
|
-
- `--regex-log` to log some regex
|
72
|
-
- `--funcs` to log funcs
|
73
|
-
- `--ast-log` to log AST
|
74
|
-
- `--opt-funcs` to log funcs after opt
|
75
|
-
- `--sections` to log sections as hex
|
76
|
-
- `--opt-no-inline` to not inline any funcs
|
77
|
-
- `--tail-call` to enable tail calls (experimental + not widely implemented)
|
78
|
-
- `--compile-hints` to enable V8 compilation hints (experimental + doesn't seem to do much?)
|
66
|
+
- `-O2` to enable advanced opt (inlining). unstable!
|
67
|
+
- `-O3` to enable advanceder opt (precompute const math). unstable!
|
79
68
|
|
80
69
|
## Limitations
|
81
70
|
- No full object support yet
|
82
71
|
- Little built-ins/prototype
|
83
72
|
- No async/promise/await
|
84
73
|
- No variables between scopes (except args and globals)
|
85
|
-
- Literal callees only in calls (eg `print()` works, `a = print; a()` does not)
|
86
74
|
- No `eval()` etc (since it is AOT)
|
87
75
|
|
88
76
|
## Sub-engines
|
@@ -112,7 +100,7 @@ These include some early (stage 1/0) and/or dead (last commit years ago) proposa
|
|
112
100
|
|
113
101
|
- Number literals
|
114
102
|
- Declaring functions
|
115
|
-
- Calling functions
|
103
|
+
- Calling functions
|
116
104
|
- `return`
|
117
105
|
- `let`/`const`/`var` basic declarations
|
118
106
|
- 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: '
|
8
|
-
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
|
21
|
-
typedef uint16_t
|
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:
|
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', '
|
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', '
|
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: `
|
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: `
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
+
}
|
package/compiler/assemble.js
CHANGED
@@ -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 =>
|
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(
|
@@ -54,7 +54,7 @@ export const __Array_prototype_indexOf = (_this: any[], searchElement: any, posi
|
|
54
54
|
} else position = 0;
|
55
55
|
|
56
56
|
for (let i: i32 = position; i < len; i++) {
|
57
|
-
if (_this[i]
|
57
|
+
if (_this[i] === searchElement) return i;
|
58
58
|
}
|
59
59
|
|
60
60
|
return -1;
|
@@ -68,7 +68,7 @@ export const __Array_prototype_lastIndexOf = (_this: any[], searchElement: any,
|
|
68
68
|
} else position = 0;
|
69
69
|
|
70
70
|
for (let i: i32 = len - 1; i >= position; i--) {
|
71
|
-
if (_this[i]
|
71
|
+
if (_this[i] === searchElement) return i;
|
72
72
|
}
|
73
73
|
|
74
74
|
return -1;
|
@@ -82,7 +82,7 @@ export const __Array_prototype_includes = (_this: any[], searchElement: any, pos
|
|
82
82
|
} else position = 0;
|
83
83
|
|
84
84
|
for (let i: i32 = position; i < len; i++) {
|
85
|
-
if (_this[i]
|
85
|
+
if (_this[i] === searchElement) return true;
|
86
86
|
}
|
87
87
|
|
88
88
|
return false;
|
@@ -171,12 +171,13 @@ 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 len: i32 = _this.length;
|
174
175
|
const out: any[] = [];
|
176
|
+
out.length = len;
|
175
177
|
|
176
|
-
const len: i32 = _this.length;
|
177
178
|
let i: i32 = 0;
|
178
179
|
while (i < len) {
|
179
|
-
out
|
180
|
+
out[i] = callbackFn(_this[i], i++, _this);
|
180
181
|
}
|
181
182
|
|
182
183
|
return out;
|
@@ -222,4 +223,70 @@ export const __Array_prototype_every = (_this: any[], callbackFn: any) => {
|
|
222
223
|
}
|
223
224
|
|
224
225
|
return true;
|
226
|
+
};
|
227
|
+
|
228
|
+
export const __Array_prototype_reduce = (_this: any[], callbackFn: any, initialValue: any) => {
|
229
|
+
let acc: any = initialValue ?? _this[0];
|
230
|
+
|
231
|
+
const len: i32 = _this.length;
|
232
|
+
let i: i32 = 0;
|
233
|
+
while (i < len) {
|
234
|
+
acc = callbackFn(acc, _this[i], i++, _this);
|
235
|
+
}
|
236
|
+
|
237
|
+
return acc;
|
238
|
+
};
|
239
|
+
|
240
|
+
export const __Array_prototype_toString = (_this: any[]) => {
|
241
|
+
// todo: this is bytestring only!
|
242
|
+
|
243
|
+
let out: bytestring = '';
|
244
|
+
out.length = 0;
|
245
|
+
|
246
|
+
const len: i32 = _this.length;
|
247
|
+
let i: i32 = 0;
|
248
|
+
while (i < len) {
|
249
|
+
if (i > 0) Porffor.bytestring.appendChar(out, 44);
|
250
|
+
|
251
|
+
const element: any = _this[i++];
|
252
|
+
const type: i32 = Porffor.rawType(element);
|
253
|
+
if (element != 0 || Porffor.fastAnd(
|
254
|
+
type != Porffor.TYPES.undefined, // undefined
|
255
|
+
type != Porffor.TYPES.object // null
|
256
|
+
)) {
|
257
|
+
Porffor.bytestring.appendStr(out, ecma262.ToString(element));
|
258
|
+
}
|
259
|
+
}
|
260
|
+
|
261
|
+
return out;
|
262
|
+
};
|
263
|
+
|
264
|
+
export const __Array_prototype_join = (_this: any[], _separator: any) => {
|
265
|
+
// todo: this is bytestring only!
|
266
|
+
// todo/perf: optimize single char separators
|
267
|
+
// todo/perf: optimize default separator (?)
|
268
|
+
|
269
|
+
let separator: bytestring = ',';
|
270
|
+
if (Porffor.rawType(_separator) != Porffor.TYPES.undefined)
|
271
|
+
separator = ecma262.ToString(_separator);
|
272
|
+
|
273
|
+
let out: bytestring = '';
|
274
|
+
out.length = 0;
|
275
|
+
|
276
|
+
const len: i32 = _this.length;
|
277
|
+
let i: i32 = 0;
|
278
|
+
while (i < len) {
|
279
|
+
if (i > 0) Porffor.bytestring.appendStr(out, separator);
|
280
|
+
|
281
|
+
const element: any = _this[i++];
|
282
|
+
const type: i32 = Porffor.rawType(element);
|
283
|
+
if (element != 0 || Porffor.fastAnd(
|
284
|
+
type != Porffor.TYPES.undefined, // undefined
|
285
|
+
type != Porffor.TYPES.object // null
|
286
|
+
)) {
|
287
|
+
Porffor.bytestring.appendStr(out, ecma262.ToString(element));
|
288
|
+
}
|
289
|
+
}
|
290
|
+
|
291
|
+
return out;
|
225
292
|
};
|
@@ -253,29 +253,6 @@ export const __ecma262_UTC = (t: number): number => {
|
|
253
253
|
return t;
|
254
254
|
};
|
255
255
|
|
256
|
-
|
257
|
-
// todo: move this somewhere generic?
|
258
|
-
// 7.1.5 ToIntegerOrInfinity (argument)
|
259
|
-
// https://tc39.es/ecma262/multipage/abstract-operations.html#sec-tointegerorinfinity
|
260
|
-
export const __ecma262_ToIntegerOrInfinity = (argument: unknown): number => {
|
261
|
-
// 1. Let number be ? ToNumber(argument).
|
262
|
-
let number: number = Number(argument);
|
263
|
-
|
264
|
-
// 2. If number is one of NaN, +0𝔽, or -0𝔽, return 0.
|
265
|
-
if (Number.isNaN(number)) return 0;
|
266
|
-
|
267
|
-
// 3. If number is +∞𝔽, return +∞.
|
268
|
-
// 4. If number is -∞𝔽, return -∞.
|
269
|
-
if (!Number.isFinite(number)) return number;
|
270
|
-
|
271
|
-
// 5. Return truncate(ℝ(number)).
|
272
|
-
number = Math.trunc(number);
|
273
|
-
|
274
|
-
// return 0 for -0
|
275
|
-
if (number == 0) return 0;
|
276
|
-
return number;
|
277
|
-
};
|
278
|
-
|
279
256
|
// 21.4.1.27 MakeTime (hour, min, sec, ms)
|
280
257
|
// https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-maketime
|
281
258
|
export const __ecma262_MakeTime = (hour: number, min: number, sec: number, ms: number): number => {
|
@@ -722,12 +699,9 @@ export const __Porffor_date_allocate = (): Date => {
|
|
722
699
|
const hack: bytestring = '';
|
723
700
|
|
724
701
|
if (hack.length == 0) {
|
725
|
-
hack.length = Porffor.wasm`
|
726
|
-
memory.grow 0
|
727
|
-
drop
|
728
|
-
memory.size 0
|
702
|
+
hack.length = Porffor.wasm`
|
729
703
|
i32.const 1
|
730
|
-
|
704
|
+
memory.grow 0
|
731
705
|
i32.const 65536
|
732
706
|
i32.mul
|
733
707
|
i32.from_u`;
|
@@ -1894,8 +1868,7 @@ export const __ecma262_ToDateString = (tv: number) => {
|
|
1894
1868
|
|
1895
1869
|
// 1. If tv is NaN, return "Invalid Date".
|
1896
1870
|
if (Number.isNaN(tv)) {
|
1897
|
-
out = 'Invalid Date';
|
1898
|
-
return out;
|
1871
|
+
return out = 'Invalid Date';
|
1899
1872
|
}
|
1900
1873
|
|
1901
1874
|
// 2. Let t be LocalTime(tv).
|
@@ -6,11 +6,9 @@ export const __Number_prototype_toString = (_this: number, radix: number|any) =>
|
|
6
6
|
let outPtr: i32 = Porffor.wasm`local.get ${out}`;
|
7
7
|
|
8
8
|
if (!Number.isFinite(_this)) {
|
9
|
-
if (Number.isNaN(_this)) out = 'NaN';
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
return out;
|
9
|
+
if (Number.isNaN(_this)) return out = 'NaN';
|
10
|
+
if (_this == Infinity) return out = 'Infinity';
|
11
|
+
return out = '-Infinity';
|
14
12
|
}
|
15
13
|
|
16
14
|
if (Porffor.rawType(radix) != Porffor.TYPES.number) {
|
@@ -24,8 +22,7 @@ export const __Number_prototype_toString = (_this: number, radix: number|any) =>
|
|
24
22
|
}
|
25
23
|
|
26
24
|
if (_this == 0) {
|
27
|
-
out = '0';
|
28
|
-
return out;
|
25
|
+
return out = '0';
|
29
26
|
}
|
30
27
|
|
31
28
|
// if negative value
|
@@ -99,7 +96,6 @@ export const __Number_prototype_toString = (_this: number, radix: number|any) =>
|
|
99
96
|
}
|
100
97
|
|
101
98
|
out.length = outPtr - Porffor.wasm`local.get ${out}`;
|
102
|
-
|
103
99
|
return out;
|
104
100
|
}
|
105
101
|
|
@@ -235,7 +231,6 @@ export const __Number_prototype_toString = (_this: number, radix: number|any) =>
|
|
235
231
|
}
|
236
232
|
|
237
233
|
out.length = outPtr - Porffor.wasm`local.get ${out}`;
|
238
|
-
|
239
234
|
return out;
|
240
235
|
};
|
241
236
|
|
@@ -244,11 +239,9 @@ export const __Number_prototype_toFixed = (_this: number, fractionDigits: number
|
|
244
239
|
let outPtr: i32 = Porffor.wasm`local.get ${out}`;
|
245
240
|
|
246
241
|
if (!Number.isFinite(_this)) {
|
247
|
-
if (Number.isNaN(_this)) out = 'NaN';
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
return out;
|
242
|
+
if (Number.isNaN(_this)) return out = 'NaN';
|
243
|
+
if (_this == Infinity) return out = 'Infinity';
|
244
|
+
return out = '-Infinity';
|
252
245
|
}
|
253
246
|
|
254
247
|
fractionDigits |= 0;
|
@@ -324,7 +317,6 @@ export const __Number_prototype_toFixed = (_this: number, fractionDigits: number
|
|
324
317
|
}
|
325
318
|
|
326
319
|
out.length = outPtr - Porffor.wasm`local.get ${out}`;
|
327
|
-
|
328
320
|
return out;
|
329
321
|
};
|
330
322
|
|
@@ -334,11 +326,9 @@ export const __Number_prototype_toExponential = (_this: number, fractionDigits:
|
|
334
326
|
let outPtr: i32 = Porffor.wasm`local.get ${out}`;
|
335
327
|
|
336
328
|
if (!Number.isFinite(_this)) {
|
337
|
-
if (Number.isNaN(_this)) out = 'NaN';
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
return out;
|
329
|
+
if (Number.isNaN(_this)) return out = 'NaN';
|
330
|
+
if (_this == Infinity) return out = 'Infinity';
|
331
|
+
return out = '-Infinity';
|
342
332
|
}
|
343
333
|
|
344
334
|
if (Porffor.rawType(fractionDigits) != Porffor.TYPES.number) {
|
@@ -519,7 +509,6 @@ export const __Number_prototype_toExponential = (_this: number, fractionDigits:
|
|
519
509
|
}
|
520
510
|
|
521
511
|
out.length = outPtr - Porffor.wasm`local.get ${out}`;
|
522
|
-
|
523
512
|
return out;
|
524
513
|
};
|
525
514
|
|
@@ -28,6 +28,13 @@ type PorfforGlobal = {
|
|
28
28
|
write(_this: any, index: number, value: any): boolean;
|
29
29
|
}
|
30
30
|
|
31
|
+
bytestring: {
|
32
|
+
// defined in date.ts
|
33
|
+
appendStr(str: bytestring, appendage: bytestring): i32;
|
34
|
+
appendChar(str: bytestring, char: i32): i32;
|
35
|
+
appendPadNum(str: bytestring, num: number, len: number): i32;
|
36
|
+
}
|
37
|
+
|
31
38
|
print(x: any): i32;
|
32
39
|
|
33
40
|
randomByte(): i32;
|
package/compiler/builtins/set.ts
CHANGED
@@ -2,12 +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`
|
6
|
-
memory.grow 0
|
7
|
-
drop
|
8
|
-
memory.size 0
|
5
|
+
Porffor.wasm`
|
9
6
|
i32.const 1
|
10
|
-
|
7
|
+
memory.grow 0
|
11
8
|
i32.const 65536
|
12
9
|
i32.mul
|
13
10
|
i32.from_u
|