@wasmgroundup/emit 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 WebAssembly from the Ground Up
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1 @@
1
+ # emit
@@ -0,0 +1,40 @@
1
+ type BytecodeFragment = (number | BytecodeFragment)[];
2
+ declare enum valtype {
3
+ i32 = 127,
4
+ i64 = 126,
5
+ f32 = 125,
6
+ f64 = 124
7
+ }
8
+ declare function vec<T extends BytecodeFragment>(elements: T): BytecodeFragment;
9
+ declare function section(id: number, contents: BytecodeFragment): (number | BytecodeFragment)[];
10
+ declare function functype(paramTypes: valtype[], resultTypes: valtype[]): BytecodeFragment;
11
+ declare function typesec(functypes: BytecodeFragment): BytecodeFragment;
12
+ declare const typeidx: typeof u32;
13
+ declare function funcsec(typeidxs: BytecodeFragment): BytecodeFragment;
14
+ declare function code(func: BytecodeFragment): BytecodeFragment;
15
+ declare function func(locals: BytecodeFragment, body: BytecodeFragment): BytecodeFragment;
16
+ declare function codesec(codes: BytecodeFragment): BytecodeFragment;
17
+ declare function name(s: string): BytecodeFragment;
18
+ declare function export_(nm: string, exportdesc: BytecodeFragment): BytecodeFragment;
19
+ declare function exportsec(exports: BytecodeFragment): BytecodeFragment;
20
+ declare const funcidx: typeof u32;
21
+ declare const exportdesc: {
22
+ func(idx: number): BytecodeFragment;
23
+ };
24
+ declare function module(sections: any): BytecodeFragment;
25
+ declare const instr: {
26
+ nop: number;
27
+ end: number;
28
+ i32: {
29
+ const: number;
30
+ add: number;
31
+ sub: number;
32
+ };
33
+ i64: {
34
+ const: number;
35
+ };
36
+ };
37
+ declare function u32(v: number | bigint): number[];
38
+ declare function i32(v: number | bigint): number[];
39
+ declare function locals(n: number, type: valtype): BytecodeFragment;
40
+ export { code, codesec, export_, exportdesc, exportsec, func, funcidx, funcsec, functype, i32, instr, locals, module, name, section, typeidx, typesec, valtype, vec, };
package/dist/index.js ADDED
@@ -0,0 +1,136 @@
1
+ const SECTION_ID_TYPE = 1;
2
+ const SECTION_ID_FUNCTION = 3;
3
+ const SECTION_ID_EXPORT = 7;
4
+ const SECTION_ID_CODE = 10;
5
+ const TYPE_FUNCTION = 0x60;
6
+ function stringToBytes(s) {
7
+ const bytes = new TextEncoder().encode(s);
8
+ return Array.from(bytes);
9
+ }
10
+ function int32ToBytes(v) {
11
+ // prettier-ignore
12
+ return [
13
+ v & 0xff,
14
+ (v >> 8) & 0xff,
15
+ (v >> 16) & 0xff,
16
+ (v >> 24) & 0xff,
17
+ ];
18
+ }
19
+ function magic() {
20
+ // [0x00, 0x61, 0x73, 0x6d]
21
+ return stringToBytes("\0asm");
22
+ }
23
+ function version() {
24
+ // [0x01, 0x00, 0x00, 0x00]
25
+ return int32ToBytes(1);
26
+ }
27
+ var valtype;
28
+ (function (valtype) {
29
+ valtype[valtype["i32"] = 127] = "i32";
30
+ valtype[valtype["i64"] = 126] = "i64";
31
+ valtype[valtype["f32"] = 125] = "f32";
32
+ valtype[valtype["f64"] = 124] = "f64";
33
+ })(valtype || (valtype = {}));
34
+ function vec(elements) {
35
+ return [u32(elements.length), ...elements];
36
+ }
37
+ function section(id, contents) {
38
+ const sizeInBytes = contents.flat(Infinity).length;
39
+ return [id, u32(sizeInBytes), contents];
40
+ }
41
+ function functype(paramTypes, resultTypes) {
42
+ return [TYPE_FUNCTION, vec(paramTypes), vec(resultTypes)];
43
+ }
44
+ function typesec(functypes) {
45
+ return section(SECTION_ID_TYPE, vec(functypes));
46
+ }
47
+ const typeidx = u32;
48
+ function funcsec(typeidxs) {
49
+ return section(SECTION_ID_FUNCTION, vec(typeidxs));
50
+ }
51
+ function code(func) {
52
+ const sizeInBytes = func.flat(Infinity).length;
53
+ return [u32(sizeInBytes), func];
54
+ }
55
+ function func(locals, body) {
56
+ return [vec(locals), body];
57
+ }
58
+ function codesec(codes) {
59
+ return section(SECTION_ID_CODE, vec(codes));
60
+ }
61
+ function name(s) {
62
+ return vec(stringToBytes(s));
63
+ }
64
+ function export_(nm, exportdesc) {
65
+ return [name(nm), exportdesc];
66
+ }
67
+ function exportsec(exports) {
68
+ return section(SECTION_ID_EXPORT, vec(exports));
69
+ }
70
+ const funcidx = u32;
71
+ const exportdesc = {
72
+ func(idx) {
73
+ return [0x00, funcidx(idx)];
74
+ },
75
+ };
76
+ function module(sections) {
77
+ return [magic(), version(), sections];
78
+ }
79
+ var numtype;
80
+ (function (numtype) {
81
+ numtype[numtype["i32"] = 127] = "i32";
82
+ })(numtype || (numtype = {}));
83
+ const instr = {
84
+ nop: 0x01,
85
+ end: 0x0b,
86
+ i32: {
87
+ const: 0x41,
88
+ add: 0x6a,
89
+ sub: 0x6b,
90
+ },
91
+ i64: {
92
+ const: 0x42,
93
+ },
94
+ };
95
+ const SEVEN_BIT_MASK_BIG_INT = 127n;
96
+ const CONTINUATION_BIT = 0b10000000;
97
+ function u32(v) {
98
+ let val = BigInt(v);
99
+ let more = true;
100
+ const r = [];
101
+ while (more) {
102
+ const b = Number(val & SEVEN_BIT_MASK_BIG_INT);
103
+ val = val >> 7n;
104
+ more = val !== 0n;
105
+ if (more) {
106
+ r.push(b | CONTINUATION_BIT);
107
+ }
108
+ else {
109
+ r.push(b);
110
+ }
111
+ }
112
+ return r;
113
+ }
114
+ ///! START i32-v1 #priv #api #dedent
115
+ function i32(v) {
116
+ let val = BigInt(v);
117
+ const r = [];
118
+ let more = true;
119
+ while (more) {
120
+ const b = Number(val & 127n);
121
+ const signBitSet = !!(b & 0x40);
122
+ val = val >> 7n;
123
+ if ((val === 0n && !signBitSet) || (val === -1n && signBitSet)) {
124
+ more = false;
125
+ r.push(b);
126
+ }
127
+ else {
128
+ r.push(b | CONTINUATION_BIT);
129
+ }
130
+ }
131
+ return r;
132
+ }
133
+ function locals(n, type) {
134
+ return [u32(n), type];
135
+ }
136
+ export { code, codesec, export_, exportdesc, exportsec, func, funcidx, funcsec, functype, i32, instr, locals, module, name, section, typeidx, typesec, valtype, vec, };
package/index.test.ts ADDED
@@ -0,0 +1,42 @@
1
+ import { expect, test } from "bun:test";
2
+
3
+ import {
4
+ module,
5
+ typesec,
6
+ functype,
7
+ valtype,
8
+ funcsec,
9
+ typeidx,
10
+ exportsec,
11
+ export_,
12
+ exportdesc,
13
+ codesec,
14
+ code,
15
+ func,
16
+ locals,
17
+ instr,
18
+ } from "./index";
19
+
20
+ test("simple modules", async () => {
21
+ const makeModule = (paramTypes, resultTypes, body) => {
22
+ const mod = module([
23
+ typesec([functype(paramTypes, resultTypes)]),
24
+ funcsec([typeidx(0)]),
25
+ exportsec([export_("main", exportdesc.func(0))]),
26
+ codesec([code(func([], body))]),
27
+ ]);
28
+ return Uint8Array.from(mod.flat(Infinity));
29
+ };
30
+ const runMain = async (bytes, args) => {
31
+ const { instance } = await WebAssembly.instantiate(bytes);
32
+ return instance.exports.main(...args);
33
+ };
34
+
35
+ expect(await runMain(makeModule([], [], [instr.end]), [])).toBe(undefined);
36
+ expect(
37
+ await runMain(
38
+ makeModule([], [valtype.i32], [instr.i32.const, 1, instr.end]),
39
+ [],
40
+ ),
41
+ ).toBe(1);
42
+ });
package/index.ts ADDED
@@ -0,0 +1,194 @@
1
+ const SECTION_ID_TYPE = 1;
2
+ const SECTION_ID_FUNCTION = 3;
3
+ const SECTION_ID_EXPORT = 7;
4
+ const SECTION_ID_CODE = 10;
5
+
6
+ const TYPE_FUNCTION = 0x60;
7
+
8
+ type BytecodeFragment = (number | BytecodeFragment)[];
9
+
10
+ function stringToBytes(s: string): number[] {
11
+ const bytes = new TextEncoder().encode(s);
12
+ return Array.from(bytes);
13
+ }
14
+
15
+ function int32ToBytes(v: number): number[] {
16
+ // prettier-ignore
17
+ return [
18
+ v & 0xff,
19
+ (v >> 8) & 0xff,
20
+ (v >> 16) & 0xff,
21
+ (v >> 24) & 0xff,
22
+ ];
23
+ }
24
+
25
+ function magic(): number[] {
26
+ // [0x00, 0x61, 0x73, 0x6d]
27
+ return stringToBytes("\0asm");
28
+ }
29
+
30
+ function version(): number[] {
31
+ // [0x01, 0x00, 0x00, 0x00]
32
+ return int32ToBytes(1);
33
+ }
34
+
35
+ enum valtype {
36
+ i32 = 0x7f,
37
+ i64 = 0x7e,
38
+ f32 = 0x7d,
39
+ f64 = 0x7c,
40
+ }
41
+
42
+ function vec<T extends BytecodeFragment>(elements: T): BytecodeFragment {
43
+ return [u32(elements.length), ...elements];
44
+ }
45
+
46
+ function section(id: number, contents: BytecodeFragment) {
47
+ const sizeInBytes = (contents as any[]).flat(Infinity).length;
48
+ return [id, u32(sizeInBytes), contents];
49
+ }
50
+
51
+ function functype(
52
+ paramTypes: valtype[],
53
+ resultTypes: valtype[],
54
+ ): BytecodeFragment {
55
+ return [TYPE_FUNCTION, vec(paramTypes), vec(resultTypes)];
56
+ }
57
+
58
+ function typesec(functypes: BytecodeFragment): BytecodeFragment {
59
+ return section(SECTION_ID_TYPE, vec(functypes));
60
+ }
61
+
62
+ const typeidx = u32;
63
+
64
+ function funcsec(typeidxs: BytecodeFragment): BytecodeFragment {
65
+ return section(SECTION_ID_FUNCTION, vec(typeidxs));
66
+ }
67
+
68
+ function code(func: BytecodeFragment): BytecodeFragment {
69
+ const sizeInBytes = (func as any[]).flat(Infinity).length;
70
+ return [u32(sizeInBytes), func];
71
+ }
72
+
73
+ function func(
74
+ locals: BytecodeFragment,
75
+ body: BytecodeFragment,
76
+ ): BytecodeFragment {
77
+ return [vec(locals), body];
78
+ }
79
+
80
+ function codesec(codes: BytecodeFragment): BytecodeFragment {
81
+ return section(SECTION_ID_CODE, vec(codes));
82
+ }
83
+
84
+ function name(s: string): BytecodeFragment {
85
+ return vec(stringToBytes(s));
86
+ }
87
+
88
+ function export_(nm: string, exportdesc: BytecodeFragment): BytecodeFragment {
89
+ return [name(nm), exportdesc];
90
+ }
91
+
92
+ function exportsec(exports: BytecodeFragment): BytecodeFragment {
93
+ return section(SECTION_ID_EXPORT, vec(exports));
94
+ }
95
+
96
+ const funcidx = u32;
97
+
98
+ const exportdesc = {
99
+ func(idx: number): BytecodeFragment {
100
+ return [0x00, funcidx(idx)];
101
+ },
102
+ };
103
+
104
+ function module(sections): BytecodeFragment {
105
+ return [magic(), version(), sections];
106
+ }
107
+
108
+ enum numtype {
109
+ i32 = 0x7f,
110
+ }
111
+
112
+ const instr = {
113
+ nop: 0x01,
114
+ end: 0x0b,
115
+
116
+ i32: {
117
+ const: 0x41,
118
+ add: 0x6a,
119
+ sub: 0x6b,
120
+ },
121
+ i64: {
122
+ const: 0x42,
123
+ },
124
+ };
125
+
126
+ const SEVEN_BIT_MASK_BIG_INT = 0b01111111n;
127
+ const CONTINUATION_BIT = 0b10000000;
128
+
129
+ function u32(v: number | bigint): number[] {
130
+ let val = BigInt(v);
131
+ let more = true;
132
+ const r = [];
133
+
134
+ while (more) {
135
+ const b = Number(val & SEVEN_BIT_MASK_BIG_INT);
136
+ val = val >> 7n;
137
+ more = val !== 0n;
138
+ if (more) {
139
+ r.push(b | CONTINUATION_BIT);
140
+ } else {
141
+ r.push(b);
142
+ }
143
+ }
144
+ return r;
145
+ }
146
+
147
+ ///! START i32-v1 #priv #api #dedent
148
+ function i32(v: number | bigint): number[] {
149
+ let val = BigInt(v);
150
+ const r = [];
151
+
152
+ let more = true;
153
+ while (more) {
154
+ const b = Number(val & 0b01111111n);
155
+ const signBitSet = !!(b & 0x40);
156
+
157
+ val = val >> 7n;
158
+
159
+ if ((val === 0n && !signBitSet) || (val === -1n && signBitSet)) {
160
+ more = false;
161
+ r.push(b);
162
+ } else {
163
+ r.push(b | CONTINUATION_BIT);
164
+ }
165
+ }
166
+
167
+ return r;
168
+ }
169
+
170
+ function locals(n: number, type: valtype): BytecodeFragment {
171
+ return [u32(n), type];
172
+ }
173
+
174
+ export {
175
+ code,
176
+ codesec,
177
+ export_,
178
+ exportdesc,
179
+ exportsec,
180
+ func,
181
+ funcidx,
182
+ funcsec,
183
+ functype,
184
+ i32,
185
+ instr,
186
+ locals,
187
+ module,
188
+ name,
189
+ section,
190
+ typeidx,
191
+ typesec,
192
+ valtype,
193
+ vec,
194
+ };
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@wasmgroundup/emit",
3
+ "description": "A library for creating binary-encoded WebAssembly modules",
4
+ "version": "0.1.0",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/wasmgroundup/emit.git"
10
+ },
11
+ "keywords": [
12
+ "wasm",
13
+ "webassembly"
14
+ ],
15
+ "author": "Mariano Guerra & Patrick Dubroy <hello@wasmgroundup.com>",
16
+ "license": "MIT",
17
+ "bugs": {
18
+ "url": "https://github.com/wasmgroundup/emit/issues"
19
+ },
20
+ "homepage": "https://github.com/wasmgroundup/emit#readme",
21
+ "scripts": {
22
+ "check": "tsc --noEmit",
23
+ "format": "prettier --write .",
24
+ "prepublish": "tsc"
25
+ },
26
+ "dependencies": {
27
+ "prettier": "^3.0.3"
28
+ },
29
+ "devDependencies": {
30
+ "typescript": "^5.2.2"
31
+ }
32
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "files": ["index.ts"],
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "target": "es2020",
6
+ "declaration": true
7
+ }
8
+ }
Binary file