wat4wasm 1.0.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 +21 -0
- package/README.md +30 -0
- package/examples/01-text/module-output.wat +228 -0
- package/examples/01-text/module.wat +9 -0
- package/examples/02-include/module-output.wat +22 -0
- package/examples/02-include/module.wat +3 -0
- package/examples/02-include/used-folder/included-file.wat +4 -0
- package/examples/03-ref.extern/module-output.wat +1537 -0
- package/examples/03-ref.extern/module.wat +33 -0
- package/examples/04-ref.func/module-output.wat +25 -0
- package/examples/04-ref.func/module.wat +8 -0
- package/examples/05-global.get/module-output.wat +991 -0
- package/examples/05-global.get/module.wat +26 -0
- package/examples/06-async/module-output.wat +661 -0
- package/examples/06-async/module.wat +15 -0
- package/examples/07-data/module-output.wasm +0 -0
- package/examples/07-data/module.wat +29 -0
- package/examples/07-data/used-folder/clear-text.txt +1 -0
- package/examples/07-data/used-folder/compile-this.wat +8 -0
- package/examples/08-reflectors/how-to/README.md +0 -0
- package/examples/08-reflectors/how-to/output-01-command.sh +0 -0
- package/examples/08-reflectors/how-to/output-02-command.sh +0 -0
- package/examples/08-reflectors/how-to/output-03-command.sh +0 -0
- package/examples/08-reflectors/how-to/output-04-command.sh +0 -0
- package/examples/08-reflectors/how-to/wat4wasm-outputs/01-module.wat +3 -0
- package/examples/08-reflectors/how-to/wat4wasm-outputs/02-module.wasm +3 -0
- package/examples/08-reflectors/how-to/wat4wasm-outputs/03-module.js +0 -0
- package/examples/08-reflectors/how-to/wat4wasm-outputs/04-module.html +0 -0
- package/examples/08-reflectors/module-output.wat +995 -0
- package/examples/08-reflectors/module.wat +108 -0
- package/examples/09-replaceAll/module-output.wat +347 -0
- package/examples/09-replaceAll/module.wat +68 -0
- package/examples/99-complex/module.wat +8 -0
- package/examples/99-complex/output.html +1 -0
- package/examples/99-complex/sub/worker.wat +2 -0
- package/examples/shell-usages.sh +60 -0
- package/lib/build +33 -0
- package/lib/clean.js +91 -0
- package/lib/cli.js +273 -0
- package/lib/helpers.js +567 -0
- package/lib/index.js +95 -0
- package/lib/processors/async.js +53 -0
- package/lib/processors/data.js +188 -0
- package/lib/processors/import.js +178 -0
- package/lib/processors/include.js +17 -0
- package/lib/processors/new.js +21 -0
- package/lib/processors/ref_extern.js +64 -0
- package/lib/processors/ref_func.js +44 -0
- package/lib/processors/replace_all.js +56 -0
- package/lib/processors/start.js +42 -0
- package/lib/processors/string.js +57 -0
- package/lib/processors/text.js +115 -0
- package/lib/processors/wat4wasm.js +285 -0
- package/lib/wat4beauty.js +320 -0
- package/package.json +30 -0
- package/ss-console.png +0 -0
- package/ss-terminal.png +0 -0
- package/test/boot.wat +5 -0
- package/test/test-output.html +1 -0
- package/test/test-output.js +27 -0
- package/test/test-output.wasm +0 -0
- package/test/test-sub.wat +4 -0
- package/test/test.wat +73 -0
- package/test/test_worker.js +1 -0
- package/wat4wasm +1998 -0
package/wat4wasm
ADDED
|
@@ -0,0 +1,1998 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import cp, { spawnSync } from "child_process";
|
|
4
|
+
|
|
5
|
+
function wat4beauty(watContent, alignIndentsWith = "\t", exportPadStart = 90) {
|
|
6
|
+
|
|
7
|
+
function alignIndents(watContent) {
|
|
8
|
+
let depth = 0;
|
|
9
|
+
const TAB = alignIndentsWith || "\t";
|
|
10
|
+
return watContent
|
|
11
|
+
.split("\n")
|
|
12
|
+
.map(line => {
|
|
13
|
+
const trimmed = line.trim();
|
|
14
|
+
if (!trimmed) return "";
|
|
15
|
+
|
|
16
|
+
// --- 1. Adım: Hesaplama için "Temiz" Satırı Oluştur ---
|
|
17
|
+
// Yorumları (;;) ve tırnak içindeki stringleri ("...") geçici olarak siliyoruz.
|
|
18
|
+
// Bu sayede yorumdaki veya stringdeki parantezler sayımı bozmaz.
|
|
19
|
+
let cleanLine = trimmed
|
|
20
|
+
.replace(/;;.*/g, "") // Yorumları sil
|
|
21
|
+
.replace(/"[^"]*"/g, '""'); // String içlerini boşalt
|
|
22
|
+
|
|
23
|
+
// --- 2. Adım: Parantez Bakiyesini Hesapla ---
|
|
24
|
+
const openCount = (cleanLine.match(/\(/g) || []).length;
|
|
25
|
+
const closeCount = (cleanLine.match(/\)/g) || []).length;
|
|
26
|
+
|
|
27
|
+
// Satırın BAŞINDAKİ kapanış parantezlerini say (Örn: "))" ile başlıyorsa)
|
|
28
|
+
// Bu, o satırın kendisini geri çekmek için lazım.
|
|
29
|
+
let leadingCloseCount = 0;
|
|
30
|
+
for (let char of cleanLine) {
|
|
31
|
+
if (char === ')') leadingCloseCount++;
|
|
32
|
+
else if (char === '(') break; // Açılış gelirse dur
|
|
33
|
+
else if (char === ' ') continue; // Boşlukları atla
|
|
34
|
+
else break; // Başka karakter gelirse dur
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// --- 3. Adım: O anki satırı bas ---
|
|
38
|
+
// Satırın kendi seviyesi = Mevcut Derinlik - Başlangıçtaki Kapanışlar
|
|
39
|
+
let printDepth = Math.max(0, depth - leadingCloseCount);
|
|
40
|
+
const indentation = TAB.repeat(printDepth);
|
|
41
|
+
|
|
42
|
+
// --- 4. Adım: Gelecek satır için derinliği güncelle ---
|
|
43
|
+
// Net değişim = Açılan - Kapanan
|
|
44
|
+
// NOT: Senin istediğin "aynı satırda açılıp kapananlar" burada otomatik olarak
|
|
45
|
+
// birbirini götürür (1 - 1 = 0) ve derinliği değiştirmez.
|
|
46
|
+
depth += (openCount - closeCount);
|
|
47
|
+
|
|
48
|
+
return `${indentation}${trimmed}`;
|
|
49
|
+
})
|
|
50
|
+
.join("\n");
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
function alignGlobals(watContent) {
|
|
54
|
+
const lines = watContent.split("\n");
|
|
55
|
+
const globalUpdates = []; // Hangi satırların global olduğunu tutacağız
|
|
56
|
+
let maxNameLength = 0;
|
|
57
|
+
|
|
58
|
+
// --- 1. TUR: Keşif ---
|
|
59
|
+
// Tüm satırları gez, globalleri bul ve en uzun ismi ölç.
|
|
60
|
+
lines.forEach((line, index) => {
|
|
61
|
+
// Regex Açıklaması:
|
|
62
|
+
// ^(\s*\(global\s+) -> Grup 1: Girinti + "(global " kelimesi
|
|
63
|
+
// (\S+) -> Grup 2: Global ismi (boşluk olmayan her şey)
|
|
64
|
+
// \s+ -> İsimden sonraki (atılacak) boşluklar
|
|
65
|
+
// (.*)$ -> Grup 3: Satırın geri kalanı (tip tanımları vs.)
|
|
66
|
+
const match = line.match(/^(\s*\(global\s+)(\S+)\s+(.*)$/);
|
|
67
|
+
|
|
68
|
+
if (match) {
|
|
69
|
+
const name = match[2];
|
|
70
|
+
if (name.length > maxNameLength) {
|
|
71
|
+
maxNameLength = name.length;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Bu satırı ve parçalarını daha sonra kullanmak üzere sakla
|
|
75
|
+
globalUpdates.push({
|
|
76
|
+
index: index,
|
|
77
|
+
prefix: match[1], // " (global " kısmı
|
|
78
|
+
name: name, // "$self.Object" kısmı
|
|
79
|
+
rest: match[3] // "(mut externref)..." kısmı
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Kural 2: En uzun isme 1 ekle (Minimum boşluk garantisi)
|
|
85
|
+
const alignLength = maxNameLength + 1;
|
|
86
|
+
|
|
87
|
+
// --- 2. TUR: Uygulama ---
|
|
88
|
+
// Sadece global satırlarını güncelle
|
|
89
|
+
globalUpdates.forEach(update => {
|
|
90
|
+
// Kural 3: İsim uzunluğunu bul ve aradaki boşluğu hesapla
|
|
91
|
+
const currentNameLength = update.name.length;
|
|
92
|
+
|
|
93
|
+
// Hedef uzunluktan ismin uzunluğunu çıkar
|
|
94
|
+
const spacesNeeded = alignLength - currentNameLength;
|
|
95
|
+
|
|
96
|
+
// Boşluk stringini oluştur
|
|
97
|
+
const padding = " ".repeat(Math.max(1, spacesNeeded));
|
|
98
|
+
|
|
99
|
+
// Parçaları birleştir: Prefix + İsim + Yeni Boşluklar + Geri Kalan
|
|
100
|
+
// Not: update.rest'i trim() yapıyoruz ki, eski düzensiz boşluklar gitsin.
|
|
101
|
+
const newLine = `${update.prefix}${update.name}${padding}${update.rest.trim()}`;
|
|
102
|
+
|
|
103
|
+
// Orijinal satırı güncelle
|
|
104
|
+
lines[update.index] = newLine;
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
return lines.join("\n");
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
function alignImports(watContent) {
|
|
111
|
+
const lines = watContent.split("\n");
|
|
112
|
+
const importUpdates = [];
|
|
113
|
+
let maxDefLength = 0;
|
|
114
|
+
|
|
115
|
+
lines.forEach((line, index) => {
|
|
116
|
+
// Regex Açıklaması:
|
|
117
|
+
// ^(\s*\(import\s+) -> Grup 1: Girinti + "(import "
|
|
118
|
+
// ("[^"]*"\s+"[^"]*") -> Grup 2: İki string bloğu (Modül ve İsim) - Burası ölçülecek
|
|
119
|
+
// (.*)$ -> Grup 3: Satırın geri kalanı (imza, tip vb.)
|
|
120
|
+
|
|
121
|
+
// Not: Bu regex tam senin dediğin gibi 1. tırnak ile 4. tırnak arasını (içindekilerle beraber) yakalar.
|
|
122
|
+
const match = line.match(/^(\s*\(import\s+)("[^"]*"\s+"[^"]*")(.*)$/);
|
|
123
|
+
|
|
124
|
+
if (match) {
|
|
125
|
+
const definitionPart = match[2]; // Örn: "env" "log"
|
|
126
|
+
|
|
127
|
+
if (definitionPart.length > maxDefLength) {
|
|
128
|
+
maxDefLength = definitionPart.length;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
importUpdates.push({
|
|
132
|
+
index: index,
|
|
133
|
+
prefix: match[1], // " (import "
|
|
134
|
+
definition: definitionPart,
|
|
135
|
+
rest: match[3] // " (func $log...))"
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// En uzun tanıma +1 ekleyerek hizalama sınırını belirle
|
|
141
|
+
const alignTarget = maxDefLength + 1;
|
|
142
|
+
|
|
143
|
+
// Hesaplanan boşluklarla satırları yeniden ör
|
|
144
|
+
importUpdates.forEach(update => {
|
|
145
|
+
const currentLen = update.definition.length;
|
|
146
|
+
const paddingNeeded = alignTarget - currentLen;
|
|
147
|
+
const padding = " ".repeat(Math.max(1, paddingNeeded));
|
|
148
|
+
|
|
149
|
+
// Parçaları birleştir
|
|
150
|
+
// update.rest.trim() yaparak imza kısmının başındaki düzensiz boşluğu atıyoruz.
|
|
151
|
+
lines[update.index] = `${update.prefix}${update.definition}${padding}${update.rest.trim()}`;
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
return lines.join("\n");
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
function alignImportItemsPerfectly(watContent) {
|
|
158
|
+
const lines = watContent.split("\n");
|
|
159
|
+
const updates = [];
|
|
160
|
+
let maxHeaderLength = 0; // "tip + isim" toplam uzunluğu için
|
|
161
|
+
|
|
162
|
+
lines.forEach((line, index) => {
|
|
163
|
+
// Regex Açıklaması:
|
|
164
|
+
// ^(\s*\(import\s+.*\() -> Grup 1: Prefix (parantez açılışına kadar)
|
|
165
|
+
// (func|global|table|memory) -> Grup 2: TİP (keyword)
|
|
166
|
+
// \s+ -> Aradaki boşluk
|
|
167
|
+
// (\$[^\s)]+) -> Grup 3: İSİM ($variable)
|
|
168
|
+
// (.*)$ -> Grup 4: Geri kalan (Rest)
|
|
169
|
+
const match = line.match(/^(\s*\(import\s+.*\()((?:func|global|table|memory))\s+(\$[^\s)]+)(.*)$/);
|
|
170
|
+
|
|
171
|
+
if (match) {
|
|
172
|
+
const type = match[2]; // örn: "func" veya "global"
|
|
173
|
+
const name = match[3]; // örn: "$add"
|
|
174
|
+
|
|
175
|
+
// Kritik Hesaplama: Tipin uzunluğu + 1 boşluk + İsmin uzunluğu
|
|
176
|
+
// Bu bize "func $add" bloğunun toplam kapladığı yeri verir.
|
|
177
|
+
const currentHeaderLength = type.length + 1 + name.length;
|
|
178
|
+
|
|
179
|
+
if (currentHeaderLength > maxHeaderLength) {
|
|
180
|
+
maxHeaderLength = currentHeaderLength;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
updates.push({
|
|
184
|
+
index: index,
|
|
185
|
+
prefix: match[1], // " (import "mod" "item" ("
|
|
186
|
+
type: type, // "func"
|
|
187
|
+
name: name, // "$add"
|
|
188
|
+
headerLen: currentHeaderLength,
|
|
189
|
+
rest: match[4] // " (param i32)..."
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// En uzun başlığa 1 karakter güvenli boşluk ekle
|
|
195
|
+
const alignTarget = maxHeaderLength + 1;
|
|
196
|
+
|
|
197
|
+
updates.forEach(update => {
|
|
198
|
+
// Ne kadar dolgu boşluğu lazım?
|
|
199
|
+
// Hedef - Şu anki toplam uzunluk
|
|
200
|
+
const paddingNeeded = alignTarget - update.headerLen;
|
|
201
|
+
const padding = " ".repeat(Math.max(1, paddingNeeded));
|
|
202
|
+
|
|
203
|
+
// Yeniden montaj:
|
|
204
|
+
// Prefix + Tip + " " + İsim + HESAPLANAN_DOLGU + Geri Kalan
|
|
205
|
+
lines[update.index] = `${update.prefix}${update.type} ${update.name}${padding}${update.rest.trim()}`;
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
return lines.join("\n");
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
function formatWatNearPerfectRestored(watContent) {
|
|
212
|
+
|
|
213
|
+
const namableMatches = Array.from(
|
|
214
|
+
watContent.matchAll(
|
|
215
|
+
/\((param|local|type)\s+\$(.[^\s]*)\s+(.[^\s]*)\)/g
|
|
216
|
+
)
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
const unNamedMatches = Array
|
|
220
|
+
.from(
|
|
221
|
+
watContent.matchAll(
|
|
222
|
+
/\((param|local|type|result)((\s(i32|f32|i64|f64|externref|funcref|v128))+)\)/g
|
|
223
|
+
)
|
|
224
|
+
)
|
|
225
|
+
.filter(m => !namableMatches.some(a => a.index === m.index))
|
|
226
|
+
.map(m => Object.defineProperties(m, { 3: { value: m[2] }, 2: { value: "" } }))
|
|
227
|
+
;
|
|
228
|
+
|
|
229
|
+
const matches = namableMatches
|
|
230
|
+
.concat(unNamedMatches)
|
|
231
|
+
.sort((a, b) => b.index - a.index)
|
|
232
|
+
;
|
|
233
|
+
|
|
234
|
+
const replace = new Array();
|
|
235
|
+
let maxLineLength = -Infinity;
|
|
236
|
+
|
|
237
|
+
matches
|
|
238
|
+
.filter(m => {
|
|
239
|
+
const wrapperFuncBegin = watContent.lastIndexOf("\n", watContent.lastIndexOf("(func", m.index));
|
|
240
|
+
const wrapperNextEOL = watContent.indexOf("\n", wrapperFuncBegin);
|
|
241
|
+
const wrapperFuncLine = watContent.substring(wrapperFuncBegin, wrapperNextEOL);
|
|
242
|
+
|
|
243
|
+
return wrapperFuncLine.split("(").length !== wrapperFuncLine.split(")").length;
|
|
244
|
+
})
|
|
245
|
+
.forEach(match => {
|
|
246
|
+
let [line, tag, name, kind] = match;
|
|
247
|
+
|
|
248
|
+
const length = String(line).length;
|
|
249
|
+
const begin = match.index;
|
|
250
|
+
const end = begin + length;
|
|
251
|
+
|
|
252
|
+
if (name.length > 0) {
|
|
253
|
+
name = `$${name} `
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const lineLeft = `(${tag} ${name}`;
|
|
257
|
+
const lineRight = ` ${kind})`;
|
|
258
|
+
|
|
259
|
+
maxLineLength = Math.max(maxLineLength, length);
|
|
260
|
+
replace.push({ begin, end, lineLeft, lineRight });
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
replace
|
|
264
|
+
.forEach(({ begin, end, lineLeft, lineRight }) => {
|
|
265
|
+
const padding = maxLineLength - (lineLeft.length + lineRight.length) + 1;
|
|
266
|
+
const newLine = lineLeft.concat(" ".repeat(padding)).concat(lineRight);
|
|
267
|
+
|
|
268
|
+
watContent = ""
|
|
269
|
+
.concat(watContent.substring(0, begin))
|
|
270
|
+
.concat(newLine)
|
|
271
|
+
.concat(watContent.substring(end))
|
|
272
|
+
;
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
return watContent;
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
function alignExportsRight(watContent) {
|
|
279
|
+
|
|
280
|
+
return watContent.split("\n").map(line => {
|
|
281
|
+
// 1. Export tanımını yakala: (export "...")
|
|
282
|
+
// Bu regex, tırnak içindeki kaçış karakterlerini (\") de güvenle atlar.
|
|
283
|
+
const exportRegex = /\(export\s+"(?:[^"\\]|\\.)*"\)/;
|
|
284
|
+
const match = line.match(exportRegex);
|
|
285
|
+
|
|
286
|
+
// Eğer satırda export yoksa olduğu gibi bırak
|
|
287
|
+
if (!match) return line;
|
|
288
|
+
|
|
289
|
+
const exportPart = match[0];
|
|
290
|
+
const targetColumn = exportPadStart || 90;
|
|
291
|
+
|
|
292
|
+
// 2. Export'u satırdan geçici olarak söküp al
|
|
293
|
+
// replace sadece ilk eşleşmeyi siler, bu tam istediğimiz şey.
|
|
294
|
+
let leftSide = line.replace(exportPart, "");
|
|
295
|
+
|
|
296
|
+
// Sildikten sonra sonda kalan gereksiz boşlukları temizle
|
|
297
|
+
// ÖNEMLİ: trim() değil trimEnd() kullanıyoruz ki baştaki girinti (indent) bozulmasın.
|
|
298
|
+
leftSide = leftSide.trimEnd();
|
|
299
|
+
|
|
300
|
+
// 3. Mesafe Hesabı (Hedef: 70. Karakter)
|
|
301
|
+
const currentLength = leftSide.length;
|
|
302
|
+
|
|
303
|
+
// Hedefe ulaşmak için kaç boşluk lazım?
|
|
304
|
+
let paddingCount = targetColumn - currentLength - exportPart.length;
|
|
305
|
+
|
|
306
|
+
// Eğer satır zaten 70'i geçtiyse veya çok yakınsa (çakışmaması için) en az 1 boşluk bırak
|
|
307
|
+
if (paddingCount < 1) {
|
|
308
|
+
paddingCount = 1;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// 4. Birleştir: Sol Taraf + Boşluklar + Export
|
|
312
|
+
return `${leftSide}${" ".repeat(paddingCount)}${exportPart}`;
|
|
313
|
+
}).join("\n");
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
watContent = alignIndents(watContent);
|
|
317
|
+
watContent = alignGlobals(watContent);
|
|
318
|
+
watContent = alignImports(watContent);
|
|
319
|
+
watContent = alignImportItemsPerfectly(watContent);
|
|
320
|
+
watContent = formatWatNearPerfectRestored(watContent);
|
|
321
|
+
watContent = alignExportsRight(watContent);
|
|
322
|
+
|
|
323
|
+
return watContent;
|
|
324
|
+
}const helpers = {
|
|
325
|
+
hasProtocol(str) {
|
|
326
|
+
return str?.includes("://");
|
|
327
|
+
},
|
|
328
|
+
parseProtoPath(path) {
|
|
329
|
+
const [protocol, fullpath, filename, basename, extension] = path.match(/([a-z0-9]+\:\/\/)((?:(?:.*)\/)*((.[^\/]*)\.(.[^\.]*)))/).slice(1);
|
|
330
|
+
const directory = fullpath.substring(0, fullpath.length - filename.length)
|
|
331
|
+
return { protocol, fullpath, directory, filename, basename, extension };
|
|
332
|
+
},
|
|
333
|
+
readFileAsText(fullpath) {
|
|
334
|
+
return fs.readFileSync(fullpath, "utf8");
|
|
335
|
+
},
|
|
336
|
+
readFileAsHex(fullpath) {
|
|
337
|
+
const data = fs.readFileSync(fullpath, "hex").replaceAll(/(..)/g, `\\$1`);
|
|
338
|
+
const size = data.length / 3;
|
|
339
|
+
return { data, size };
|
|
340
|
+
},
|
|
341
|
+
unlinkFile(path) {
|
|
342
|
+
return fs.unlinkSync(path);
|
|
343
|
+
},
|
|
344
|
+
copyFile(path, topath) {
|
|
345
|
+
return fs.cpSync(path, topath);
|
|
346
|
+
},
|
|
347
|
+
spawnSync(command, argv) {
|
|
348
|
+
return cp.spawnSync(command, argv, { stdio: "inherit" });
|
|
349
|
+
},
|
|
350
|
+
blockAt(raw, begin) {
|
|
351
|
+
raw = raw.toString();
|
|
352
|
+
if ((begin === -1) || !(raw = raw.substring(begin))) {
|
|
353
|
+
return "";
|
|
354
|
+
}
|
|
355
|
+
let maskUsed = raw.includes("\\)");
|
|
356
|
+
if (maskUsed) {
|
|
357
|
+
maskUsed = `__RAND${Math.random()}__`;
|
|
358
|
+
raw = raw.replaceAll("\\)", maskUsed);
|
|
359
|
+
}
|
|
360
|
+
let end = begin = 0, block = raw;
|
|
361
|
+
end = raw.indexOf(")", end);
|
|
362
|
+
block = raw.substring(begin, ++end);
|
|
363
|
+
while (block && (
|
|
364
|
+
block.split("(").length !==
|
|
365
|
+
block.split(")").length)
|
|
366
|
+
) {
|
|
367
|
+
end = raw.indexOf(")", end);
|
|
368
|
+
block = raw.substring(begin, ++end);
|
|
369
|
+
}
|
|
370
|
+
if (maskUsed && block) {
|
|
371
|
+
block = block.replaceAll(maskUsed, "\\)");
|
|
372
|
+
}
|
|
373
|
+
return block;
|
|
374
|
+
},
|
|
375
|
+
rawContent(block) {
|
|
376
|
+
let raw = block.toString().trim();
|
|
377
|
+
let begin;
|
|
378
|
+
if (raw.startsWith("(")) {
|
|
379
|
+
raw = raw.substring(1);
|
|
380
|
+
while (raw && raw.at(0).match(/[a-z0-9\.\_\(\)]/)) {
|
|
381
|
+
raw = raw.substring(1);
|
|
382
|
+
}
|
|
383
|
+
raw = raw.trim();
|
|
384
|
+
if (raw.startsWith("$")) {
|
|
385
|
+
while (raw && raw.at(0).match(/[a-z0-9A-Z\:\.\<\>\/\_\+\-\`\[\]\$\=\#\!]/)) {
|
|
386
|
+
raw = raw.substring(1)
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
raw = raw.trim();
|
|
390
|
+
}
|
|
391
|
+
raw = raw.trim();
|
|
392
|
+
if (raw.endsWith(")")) {
|
|
393
|
+
raw = raw.substring(0, raw.length - 1)
|
|
394
|
+
while (raw && !raw.at(-1).trim()) {
|
|
395
|
+
raw = raw.substring(0, raw.length - 1)
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
raw = raw.trim();
|
|
399
|
+
return raw;
|
|
400
|
+
},
|
|
401
|
+
blockContent(block) {
|
|
402
|
+
let raw = this.rawContent(block);
|
|
403
|
+
while (raw.startsWith("(type")) { raw = raw.substring(raw.indexOf(")") + 1).trim(); }
|
|
404
|
+
while (raw.startsWith("(param")) { raw = raw.substring(raw.indexOf(")") + 1).trim(); }
|
|
405
|
+
while (raw.startsWith("(result")) { raw = raw.substring(raw.indexOf(")") + 1).trim(); }
|
|
406
|
+
while (raw.startsWith("(local")) { raw = raw.substring(raw.indexOf(")") + 1).trim(); }
|
|
407
|
+
raw = raw.trim();
|
|
408
|
+
return raw;
|
|
409
|
+
},
|
|
410
|
+
containsMemoryOperation(raw) {
|
|
411
|
+
return raw.toString().split(/(memory|i32|f32|i64|f64|v128)(\.)(init|load|store|atomic|fill|drop)/).length > 1;
|
|
412
|
+
},
|
|
413
|
+
prepend(raw, block) {
|
|
414
|
+
raw = raw.toString();
|
|
415
|
+
block = block.toString().trim().concat("\n\n");
|
|
416
|
+
if (raw.startsWith("(module")) {
|
|
417
|
+
return String(`(module\n${block}\n`).concat(
|
|
418
|
+
raw.substring("(module".length).trimStart()
|
|
419
|
+
)
|
|
420
|
+
}
|
|
421
|
+
if (raw.replaceAll(/\s+/g, '').match(/\(([a-z0-9\.\_]+)\)/)) {
|
|
422
|
+
return raw.substring(0, raw.length - 1).concat(block).concat(`)`);
|
|
423
|
+
}
|
|
424
|
+
let begin;
|
|
425
|
+
begin = raw.indexOf("(", 1);
|
|
426
|
+
let headmatch = true;
|
|
427
|
+
while (headmatch) {
|
|
428
|
+
if (headmatch = raw.substring(begin).startsWith("(param")) { begin = raw.indexOf("(", ++begin); continue; }
|
|
429
|
+
if (headmatch = raw.substring(begin).startsWith("(result")) { begin = raw.indexOf("(", ++begin); continue; }
|
|
430
|
+
if (headmatch = raw.substring(begin).startsWith("(local")) { begin = raw.indexOf("(", ++begin); continue; }
|
|
431
|
+
if (headmatch = raw.substring(begin).startsWith("(type")) { begin = raw.indexOf("(", ++begin); continue; }
|
|
432
|
+
}
|
|
433
|
+
if (begin !== -1) {
|
|
434
|
+
return raw.substring(0, begin).concat(block).concat(raw.substring(begin))
|
|
435
|
+
}
|
|
436
|
+
const blockparts = raw.split(/\s+/).filter(Boolean);
|
|
437
|
+
if (blockparts.length === 1) {
|
|
438
|
+
return this.append(raw, block);
|
|
439
|
+
}
|
|
440
|
+
const maybe$name = blockparts.at(1);
|
|
441
|
+
if (maybe$name.startsWith("$")) {
|
|
442
|
+
if (maybe$name.endsWith(")")) {
|
|
443
|
+
return this.append(raw, block);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
const [firstLine, ...restLines] = raw.split(/\n/g);
|
|
447
|
+
return [firstLine, block, ...restLines].join("\n");
|
|
448
|
+
},
|
|
449
|
+
append(raw, block) {
|
|
450
|
+
raw = raw.toString();
|
|
451
|
+
return raw.substring(0, raw.lastIndexOf(")")).concat(`\n${block || ''}\n)`);
|
|
452
|
+
},
|
|
453
|
+
findQuotedTexts(rawBlock, maxcount = -1) {
|
|
454
|
+
let maskUsed = rawBlock.includes('\\"');
|
|
455
|
+
if (maskUsed) {
|
|
456
|
+
maskUsed = `__RAND${Math.random()}__`;
|
|
457
|
+
rawBlock = rawBlock.replaceAll('\\"', maskUsed);
|
|
458
|
+
}
|
|
459
|
+
let texts = [];
|
|
460
|
+
let begin = rawBlock.indexOf(`"`);
|
|
461
|
+
let end = rawBlock.indexOf(`"`, begin + 1);
|
|
462
|
+
while (maxcount-- && begin !== -1) {
|
|
463
|
+
texts.push(rawBlock.substring(begin + 1, end));
|
|
464
|
+
begin = rawBlock.indexOf('"', end + 1);
|
|
465
|
+
end = rawBlock.indexOf(`"`, begin + 1);
|
|
466
|
+
}
|
|
467
|
+
if (maskUsed) {
|
|
468
|
+
texts = texts.map(t => t.replaceAll(maskUsed, "\\)"));
|
|
469
|
+
}
|
|
470
|
+
return texts;
|
|
471
|
+
},
|
|
472
|
+
findQuotedText(rawBlock) {
|
|
473
|
+
return this.findQuotedTexts(rawBlock, 1).at(0);
|
|
474
|
+
},
|
|
475
|
+
encodeText: TextEncoder.prototype.encode.bind(new TextEncoder),
|
|
476
|
+
encodeString: str => Array.from(str || '').map(c => c.charCodeAt()),
|
|
477
|
+
fix$Name(keyword, self = false) {
|
|
478
|
+
if (keyword.startsWith("$") === false) {
|
|
479
|
+
return `$${keyword}`;
|
|
480
|
+
}
|
|
481
|
+
if (self && keyword.startsWith("$self") === false) {
|
|
482
|
+
return `$self.${keyword.substring(1)}`;
|
|
483
|
+
}
|
|
484
|
+
return keyword;
|
|
485
|
+
},
|
|
486
|
+
fixBlockKeyword(keyword, filter = {}) {
|
|
487
|
+
if (keyword.split(/\s/).length > 1) {
|
|
488
|
+
throw new Error(`Given keyword is wrong: ${keyword}`)
|
|
489
|
+
}
|
|
490
|
+
if (filter.$name) {
|
|
491
|
+
keyword = `${keyword} ${filter.$name}`;
|
|
492
|
+
}
|
|
493
|
+
if (filter.name) {
|
|
494
|
+
keyword = `${keyword} $${filter.name}`;
|
|
495
|
+
}
|
|
496
|
+
if (keyword.startsWith("(") === false) {
|
|
497
|
+
return `(${keyword}`;
|
|
498
|
+
}
|
|
499
|
+
return keyword;
|
|
500
|
+
},
|
|
501
|
+
getBlockKeyword(block) {
|
|
502
|
+
let keyword = block;
|
|
503
|
+
if (keyword.startsWith("(") === true) {
|
|
504
|
+
keyword = keyword.substring(1);
|
|
505
|
+
}
|
|
506
|
+
let i = 0;
|
|
507
|
+
while (keyword.at(i++).match(/[a-z0-9\_\.]/));;
|
|
508
|
+
return keyword.substring(0, i);
|
|
509
|
+
},
|
|
510
|
+
getBlockRootTag(block) {
|
|
511
|
+
return this.getBlockKeyword(block).split(".").at(0);
|
|
512
|
+
},
|
|
513
|
+
getBlockRootTagType(block) {
|
|
514
|
+
return this.getBlockRootTag(block).match(/(i32|f32|i64|f64)/)?.at(0) || "ext";
|
|
515
|
+
},
|
|
516
|
+
getTableOperator(block) {
|
|
517
|
+
let [match, $name = "", initial = "", maximum = "", kindof = "externref"] = block.toString().match(/\(table(?:\s*(.[^\s]*)?)\s+(\d+)(?:\s*(\d+)?)\s+(externref|funcref)\)/) ?? [];
|
|
518
|
+
initial = parseInt(initial);
|
|
519
|
+
maximum = parseInt(maximum);
|
|
520
|
+
return {
|
|
521
|
+
$name, initial, maximum, kindof,
|
|
522
|
+
grow: function (count = 1) {
|
|
523
|
+
return {
|
|
524
|
+
newTableBlock: `(table ${[$name, initial + count, maximum, kindof].filter(Boolean).join(" ").trim()})`,
|
|
525
|
+
getTableBlock: `(table.get ${[$name, `(i32.const ${initial})`].join(" ").trim()})`
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
},
|
|
530
|
+
clearExceptKnown(raw) {
|
|
531
|
+
return `${raw || ''}`.trim().split(/\n/)
|
|
532
|
+
.map(l => l.replaceAll(/\s+/g, " "))
|
|
533
|
+
.map(l => l.replace(/;;.*/g, ""))
|
|
534
|
+
.map(l => l.replaceAll(/\(;(.*);\)/g, ""))
|
|
535
|
+
.filter(l => l.replaceAll(/\s+/g, "").trim())
|
|
536
|
+
.join(" ").replaceAll(/\s+(\)|\()/g, `$1`)
|
|
537
|
+
;
|
|
538
|
+
},
|
|
539
|
+
generateId(raw) {
|
|
540
|
+
let sum = 0;
|
|
541
|
+
this.clearExceptKnown(raw)
|
|
542
|
+
.split("").map((c, i) => sum += c.charCodeAt() * i);
|
|
543
|
+
return sum;
|
|
544
|
+
},
|
|
545
|
+
abstract(str, max = 15) {
|
|
546
|
+
str = `${str || ''}`.replaceAll(/\s+/g, ' ').replaceAll(/\s+\)/g, ")");
|
|
547
|
+
if (str.length < max) return str;
|
|
548
|
+
return `${str.substring(0, max / 3)} ... ${str.substring(str.length - max / 3)}`
|
|
549
|
+
},
|
|
550
|
+
createTableGetter(index, kindof = "extern") {
|
|
551
|
+
return `(ref.null (;${index};) ${kindof})`;
|
|
552
|
+
},
|
|
553
|
+
referenceId() {
|
|
554
|
+
return "0x" + crypto.randomUUID().replace(/\-/g, "");
|
|
555
|
+
},
|
|
556
|
+
hasBlock(raw, keyword, filter) {
|
|
557
|
+
if (!keyword) { return raw.indexOf("(", 1) !== -1; };
|
|
558
|
+
keyword = this.fixBlockKeyword(keyword, filter);
|
|
559
|
+
return raw.includes(keyword);
|
|
560
|
+
},
|
|
561
|
+
hasAnyBlock(raw) {
|
|
562
|
+
return this.hasBlock(raw);
|
|
563
|
+
},
|
|
564
|
+
MaskSet: class MaskSet extends Map {
|
|
565
|
+
constructor(raw) {
|
|
566
|
+
raw = (raw || '').toString().trim();
|
|
567
|
+
Reflect.defineProperty(super(), "input", { value: raw, writable: true })
|
|
568
|
+
}
|
|
569
|
+
remove(block) {
|
|
570
|
+
if (!block || !block?.uuid) { return this.input; };
|
|
571
|
+
if (this.has(block.uuid) !== false) {
|
|
572
|
+
this.delete(block.uuid);
|
|
573
|
+
}
|
|
574
|
+
return this.mask(block, "");
|
|
575
|
+
}
|
|
576
|
+
mask(block, maskWith = block.uuid) {
|
|
577
|
+
if (!block.uuid) {
|
|
578
|
+
throw new Error(`Raw block needs uuid: ${block}`);
|
|
579
|
+
};
|
|
580
|
+
if (this.has(block.uuid) === false) {
|
|
581
|
+
this.set(block.uuid, block);
|
|
582
|
+
}
|
|
583
|
+
const rawRange = this.input.substring(
|
|
584
|
+
block.begin, block.end
|
|
585
|
+
);
|
|
586
|
+
if (block.toString() !== rawRange) {
|
|
587
|
+
console.error({ block, rawRange })
|
|
588
|
+
throw new Error(`Raw block pointer range is not matched!`);
|
|
589
|
+
};
|
|
590
|
+
this.input = this.input
|
|
591
|
+
.substring(0, block.begin)
|
|
592
|
+
.concat(block.maskWith = maskWith)
|
|
593
|
+
.concat(this.input.substring(block.end))
|
|
594
|
+
;
|
|
595
|
+
}
|
|
596
|
+
unmask(block) {
|
|
597
|
+
this.input = this.input.replaceAll(block.uuid, block.toString());
|
|
598
|
+
return this;
|
|
599
|
+
}
|
|
600
|
+
lastBlockOf(keyword, filter) {
|
|
601
|
+
return helpers.lastBlockOf(this.input, keyword, filter);
|
|
602
|
+
}
|
|
603
|
+
hasBlock(keyword, filter) {
|
|
604
|
+
return helpers.hasBlock(this.input, keyword, filter);
|
|
605
|
+
}
|
|
606
|
+
update(oldBlock, newBlock) {
|
|
607
|
+
this.set(oldBlock.uuid, newBlock);
|
|
608
|
+
}
|
|
609
|
+
get hasAnyBlock() {
|
|
610
|
+
return this.input.trim().indexOf("(", 1) !== -1;
|
|
611
|
+
}
|
|
612
|
+
parseFirstBlock() {
|
|
613
|
+
return helpers.parseFirstBlock(this.input);
|
|
614
|
+
}
|
|
615
|
+
refresh() {
|
|
616
|
+
this.input = this.restoreInto(this.input);
|
|
617
|
+
return this;
|
|
618
|
+
}
|
|
619
|
+
restore() {
|
|
620
|
+
return this.refresh().restoreInto();
|
|
621
|
+
}
|
|
622
|
+
toString() {
|
|
623
|
+
return this.input;
|
|
624
|
+
}
|
|
625
|
+
[Symbol.toPrimitive]() {
|
|
626
|
+
return this.toString();
|
|
627
|
+
}
|
|
628
|
+
get rawContent() {
|
|
629
|
+
return helpers.rawContent(this.restore())
|
|
630
|
+
}
|
|
631
|
+
get blockContent() {
|
|
632
|
+
return helpers.blockContent(this.restore())
|
|
633
|
+
}
|
|
634
|
+
restoreInto(raw = this.input) {
|
|
635
|
+
const masks = Array.from(this.keys());
|
|
636
|
+
const uuid = masks.find(uuid => raw.includes(uuid));
|
|
637
|
+
if (uuid) {
|
|
638
|
+
const block = this.get(uuid);
|
|
639
|
+
raw = raw.replaceAll(uuid, block.toString());
|
|
640
|
+
return this.restoreInto(raw);
|
|
641
|
+
}
|
|
642
|
+
return raw;
|
|
643
|
+
}
|
|
644
|
+
generateId() {
|
|
645
|
+
return helpers.generateId(this.restore())
|
|
646
|
+
}
|
|
647
|
+
},
|
|
648
|
+
nameSignatureofGlobal($name) {
|
|
649
|
+
return String($name || '')
|
|
650
|
+
.match(/\$(.[^<]*)(?:\<(.[^>]*)\>)?/)?.slice(1) || [];
|
|
651
|
+
},
|
|
652
|
+
assignBlockProperties(raw, block, begin) {
|
|
653
|
+
if (begin === -1) return null;
|
|
654
|
+
let $name = block.split(/[^a-z0-9A-Z\:\.\<\>\/\_\+\-\`\[\]\$\=\#\!\*]/g).filter(Boolean).at(1) || "";
|
|
655
|
+
let isGetter = false;
|
|
656
|
+
let isSetter = false;
|
|
657
|
+
let descriptorKey = "value";
|
|
658
|
+
if ($name.startsWith("$") === false) {
|
|
659
|
+
$name = "";
|
|
660
|
+
}
|
|
661
|
+
if ($name.includes("[")) {
|
|
662
|
+
descriptorKey = $name.substring(
|
|
663
|
+
$name.indexOf("[") + 1,
|
|
664
|
+
$name.indexOf("]")
|
|
665
|
+
);
|
|
666
|
+
$name = $name.substring(0, $name.indexOf("[")).concat("/").concat(descriptorKey);
|
|
667
|
+
}
|
|
668
|
+
isGetter = descriptorKey === "get";
|
|
669
|
+
isSetter = descriptorKey === "set";
|
|
670
|
+
$name = $name
|
|
671
|
+
.replaceAll(":", ".prototype.")
|
|
672
|
+
.replaceAll(".TypedArray", ".Uint8Array.__proto__")
|
|
673
|
+
;
|
|
674
|
+
return block && Object.defineProperties({
|
|
675
|
+
block: `${block}`,
|
|
676
|
+
begin,
|
|
677
|
+
uuid: crypto.randomUUID(),
|
|
678
|
+
end: begin + block.length,
|
|
679
|
+
$name: $name || "",
|
|
680
|
+
toString: function () { return this.block; },
|
|
681
|
+
wrappedRaws: function () {
|
|
682
|
+
return {
|
|
683
|
+
before: this.input.substring(0, this.begin),
|
|
684
|
+
after: this.input.substring(this.end)
|
|
685
|
+
}
|
|
686
|
+
},
|
|
687
|
+
maskedRaw: function () { return this.replacedRaw(this.uuid); },
|
|
688
|
+
removedRaw: function () { return this.replacedRaw(""); },
|
|
689
|
+
replacedRaw: function (str) {
|
|
690
|
+
const { before, after } = this.wrappedRaws();
|
|
691
|
+
return before.concat(str.toString()).concat(after);
|
|
692
|
+
},
|
|
693
|
+
}, {
|
|
694
|
+
isGetter: { value: isGetter },
|
|
695
|
+
isSetter: { value: isSetter },
|
|
696
|
+
descriptorKey: { value: descriptorKey },
|
|
697
|
+
input: { value: raw },
|
|
698
|
+
hasBlock: { value: function (keyword, filter) { return helpers.hasBlock(this.toString(), keyword, filter); } },
|
|
699
|
+
blockName: {
|
|
700
|
+
get: function () {
|
|
701
|
+
let rawContent = this.toString().trim();
|
|
702
|
+
let begin;
|
|
703
|
+
let blockName = ``;
|
|
704
|
+
if (rawContent.startsWith("(")) {
|
|
705
|
+
rawContent = rawContent.substring(1);
|
|
706
|
+
while (rawContent && rawContent.at(0).match(/[a-z0-9\.\_]/)) {
|
|
707
|
+
blockName = blockName + rawContent.at(0)
|
|
708
|
+
rawContent = rawContent.substring(1);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
return blockName;
|
|
712
|
+
}
|
|
713
|
+
},
|
|
714
|
+
generateId: { value: function () { return helpers.generateId(this.toString()) } },
|
|
715
|
+
name: { get: function () { return `${this.$name}`.substring(1); } },
|
|
716
|
+
rawContent: { get: function () { return helpers.rawContent(this.toString()) } },
|
|
717
|
+
blockContent: { get: function () { return helpers.blockContent(this.toString()) } },
|
|
718
|
+
hasAnyBlock: { get: function () { return helpers.hasAnyBlock(this.toString()) } },
|
|
719
|
+
indexOf: { value: function () { return this.block.indexOf(...arguments) } },
|
|
720
|
+
includes: { value: function () { return this.block.includes(...arguments) } },
|
|
721
|
+
lastIndexOf: { value: function () { return this.block.lastIndexOf(...arguments) } },
|
|
722
|
+
split: { value: function () { return this.block.split(...arguments) } },
|
|
723
|
+
at: { value: function () { return this.block.at(...arguments) } },
|
|
724
|
+
length: { get: function () { return this.block.length } },
|
|
725
|
+
charCodeAt: { value: function () { return this.block.charCodeAt(...arguments) } },
|
|
726
|
+
concat: { value: function () { return this.block.concat(...arguments) } },
|
|
727
|
+
startsWith: { value: function () { return this.block.startsWith(...arguments) } },
|
|
728
|
+
endsWith: { value: function () { return this.block.endsWith(...arguments) } },
|
|
729
|
+
substring: { value: function () { return this.block.substring(...arguments) } },
|
|
730
|
+
replace: { value: function () { return this.block.replace(...arguments) } },
|
|
731
|
+
replaceAll: { value: function () { return this.block.replaceAll(...arguments) } },
|
|
732
|
+
lastBlockOf: { value: function () { return helpers.lastBlockOf(this.block, ...arguments) } },
|
|
733
|
+
[Symbol.toPrimitive]: { value: function () { return this.block; } },
|
|
734
|
+
})
|
|
735
|
+
},
|
|
736
|
+
lastBlockOf(raw, keyword, filter) {
|
|
737
|
+
if (!raw) throw new Error(`no raw for block: ${keyword}`);
|
|
738
|
+
keyword = this.fixBlockKeyword(keyword, filter);
|
|
739
|
+
raw = raw.toString();
|
|
740
|
+
let begin = raw.lastIndexOf(keyword);
|
|
741
|
+
const block = this.parseBlockAt(raw, begin);
|
|
742
|
+
return block;
|
|
743
|
+
},
|
|
744
|
+
firstBlockOf(raw, keyword, filter) {
|
|
745
|
+
if (!raw) throw new Error(`no raw for block: ${keyword}`);
|
|
746
|
+
keyword = this.fixBlockKeyword(keyword, filter);
|
|
747
|
+
raw = raw.toString();
|
|
748
|
+
let begin = raw.indexOf(keyword);
|
|
749
|
+
const block = this.parseBlockAt(raw, begin);
|
|
750
|
+
return block;
|
|
751
|
+
},
|
|
752
|
+
parseBlockAt(raw, begin) {
|
|
753
|
+
return this.assignBlockProperties(raw, this.blockAt(raw, begin), begin);
|
|
754
|
+
},
|
|
755
|
+
parseBlock(raw) {
|
|
756
|
+
return this.parseFirstBlock(raw);
|
|
757
|
+
},
|
|
758
|
+
parseFirstBlock(raw) {
|
|
759
|
+
raw = raw.toString();
|
|
760
|
+
return this.parseBlockAt(raw, raw.indexOf("(", 1));
|
|
761
|
+
}
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
const ASYNC_BLOCK_NAME = "async";
|
|
765
|
+
function ASYNC (wat, WAT4WASM) {
|
|
766
|
+
const maskSet = new helpers.MaskSet(wat);
|
|
767
|
+
const inlineFunctions = new Array();
|
|
768
|
+
while (maskSet.hasBlock(ASYNC_BLOCK_NAME)) {
|
|
769
|
+
const block = maskSet.lastBlockOf(ASYNC_BLOCK_NAME);
|
|
770
|
+
const result = block.blockName.split(".").at(1) || "";
|
|
771
|
+
let chain, step, $exit, $name, $prop, $func,
|
|
772
|
+
steps = new helpers.MaskSet(block);
|
|
773
|
+
steps.mask(chain = steps.parseFirstBlock());
|
|
774
|
+
maskSet.mask(block);
|
|
775
|
+
while (steps.hasAnyBlock) {
|
|
776
|
+
steps.mask(step = steps.parseFirstBlock());
|
|
777
|
+
$prop = step.blockName;
|
|
778
|
+
$func = step.rawContent;
|
|
779
|
+
$exit = steps.hasAnyBlock && "ext" || result;
|
|
780
|
+
if ($func.startsWith("ref.func")) {
|
|
781
|
+
$name = `$${$func.split("ref.func").pop().trim()}`;
|
|
782
|
+
} else {
|
|
783
|
+
$name = `$${$prop}_${block.begin}_${step.begin}`;
|
|
784
|
+
inlineFunctions.push(`(func ${$name}\n${$func}\n)`);
|
|
785
|
+
}
|
|
786
|
+
chain = String(`
|
|
787
|
+
(call $self.Reflect.apply<ext.ext.ext>${$exit}
|
|
788
|
+
(ref.extern $self.Promise.prototype.${$prop}<ext>)
|
|
789
|
+
${chain.toString()}
|
|
790
|
+
(call $self.Array.of<fun>ext (ref.func ${$name}))
|
|
791
|
+
)`);
|
|
792
|
+
}
|
|
793
|
+
maskSet.update(block, chain);
|
|
794
|
+
}
|
|
795
|
+
wat = maskSet.restore();
|
|
796
|
+
if (inlineFunctions.length) {
|
|
797
|
+
wat = helpers.append(wat, inlineFunctions.join("\n\n"));
|
|
798
|
+
}
|
|
799
|
+
return wat;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
const DATA_BLOCK_NAME = "data";
|
|
803
|
+
const SIZE_BLOCK_NAME = "data.size";
|
|
804
|
+
const VIEW_BLOCK_NAME = "data.view";
|
|
805
|
+
const GENERATE_DATA_VIEWER = (size, $name) => {
|
|
806
|
+
return `
|
|
807
|
+
(block ${$name}>
|
|
808
|
+
(result externref)
|
|
809
|
+
(global.set $wat4wasm (call $self.Array<>ext))
|
|
810
|
+
|
|
811
|
+
(call $self.Reflect.set<ext.i32.i32>
|
|
812
|
+
(global.get $wat4wasm) (i32.const 0) (i32.const ${size})
|
|
813
|
+
)
|
|
814
|
+
(global.set $wat4wasm
|
|
815
|
+
(call $self.Reflect.construct<ext.ext>ext
|
|
816
|
+
(ref.extern $self.Uint8Array<ext>)
|
|
817
|
+
(global.get $wat4wasm)
|
|
818
|
+
)
|
|
819
|
+
)
|
|
820
|
+
|
|
821
|
+
(if (i32.const ${size})
|
|
822
|
+
(then
|
|
823
|
+
(i32.const 0)
|
|
824
|
+
(i64.load (i32.const 0))
|
|
825
|
+
(block $copy
|
|
826
|
+
(i32.store (i32.const 0) (i32.const ${size}))
|
|
827
|
+
(loop $i--
|
|
828
|
+
(if (i32.load (i32.const 0))
|
|
829
|
+
(then
|
|
830
|
+
(i32.store
|
|
831
|
+
(i32.const 0)
|
|
832
|
+
(i32.sub (i32.load (i32.const 0)) (i32.const 1))
|
|
833
|
+
)
|
|
834
|
+
(memory.init ${$name}
|
|
835
|
+
(i32.const 4)
|
|
836
|
+
(i32.load (i32.const 0))
|
|
837
|
+
(i32.const 1)
|
|
838
|
+
)
|
|
839
|
+
(call $self.Reflect.set<ext.i32.i32>
|
|
840
|
+
(global.get $wat4wasm)
|
|
841
|
+
(i32.load (i32.const 0))
|
|
842
|
+
(i32.load8_u (i32.const 4))
|
|
843
|
+
)
|
|
844
|
+
(br $i--)
|
|
845
|
+
)
|
|
846
|
+
)
|
|
847
|
+
)
|
|
848
|
+
)
|
|
849
|
+
(i64.store (; stack stack ;))
|
|
850
|
+
)
|
|
851
|
+
)
|
|
852
|
+
(global.get $wat4wasm)
|
|
853
|
+
(global.set $wat4wasm (null))
|
|
854
|
+
)
|
|
855
|
+
`;
|
|
856
|
+
};
|
|
857
|
+
function DATA (wat, WAT4WASM) {
|
|
858
|
+
const maskSet = new helpers.MaskSet(wat);
|
|
859
|
+
const externgetter = new Map();
|
|
860
|
+
const segmentSizes = new Map();
|
|
861
|
+
const sizeRequests = new Set();
|
|
862
|
+
const viewRequests = new Set();
|
|
863
|
+
while (maskSet.hasBlock(VIEW_BLOCK_NAME)) {
|
|
864
|
+
const block = maskSet.lastBlockOf(VIEW_BLOCK_NAME);
|
|
865
|
+
viewRequests.add(block);
|
|
866
|
+
maskSet.mask(block);
|
|
867
|
+
}
|
|
868
|
+
while (maskSet.hasBlock(SIZE_BLOCK_NAME)) {
|
|
869
|
+
const block = maskSet.lastBlockOf(SIZE_BLOCK_NAME);
|
|
870
|
+
sizeRequests.add(block);
|
|
871
|
+
maskSet.mask(block);
|
|
872
|
+
}
|
|
873
|
+
while (maskSet.hasBlock(DATA_BLOCK_NAME)) {
|
|
874
|
+
const block = maskSet.lastBlockOf(DATA_BLOCK_NAME);
|
|
875
|
+
const content = helpers.findQuotedText(block);
|
|
876
|
+
maskSet.mask(block);
|
|
877
|
+
if (helpers.hasProtocol(content) === false) {
|
|
878
|
+
continue;
|
|
879
|
+
}
|
|
880
|
+
let {
|
|
881
|
+
protocol, fullpath, directory,
|
|
882
|
+
filename, basename, extension
|
|
883
|
+
} = helpers.parseProtoPath(content);
|
|
884
|
+
if (protocol === "wasm://") {
|
|
885
|
+
const module_wat = `wat4wasm-${basename}.wat`;
|
|
886
|
+
const wasm_output = `wat4wasm-${basename}.wasm`;
|
|
887
|
+
const wat4wasm_out = `wat4wasm-${basename}.wasm.wat`;
|
|
888
|
+
const params = process.argv
|
|
889
|
+
.filter(a => a.startsWith("--"))
|
|
890
|
+
.filter(a => !a.startsWith("--input="))
|
|
891
|
+
.filter(a => !a.startsWith("--output="))
|
|
892
|
+
;
|
|
893
|
+
helpers.copyFile(fullpath, module_wat)
|
|
894
|
+
const wat4wasm = process.argv[1];
|
|
895
|
+
const nodejs = process.argv[0];
|
|
896
|
+
const argv = Array.of(
|
|
897
|
+
wat4wasm,
|
|
898
|
+
`--input=${module_wat}`,
|
|
899
|
+
`--output=${wasm_output}`,
|
|
900
|
+
`--no-unlink`
|
|
901
|
+
).concat(params);
|
|
902
|
+
helpers.spawnSync(nodejs, argv);
|
|
903
|
+
const { data, size } = helpers.readFileAsHex(wasm_output);
|
|
904
|
+
segmentSizes.set(block.$name, size);
|
|
905
|
+
maskSet.update(block, block.replace(content, data));
|
|
906
|
+
helpers.unlinkFile(module_wat);
|
|
907
|
+
helpers.unlinkFile(wasm_output);
|
|
908
|
+
helpers.unlinkFile(wat4wasm_out);
|
|
909
|
+
}
|
|
910
|
+
else if (protocol === "file://") {
|
|
911
|
+
const { data, size } = helpers.readFileAsHex(fullpath);
|
|
912
|
+
segmentSizes.set(block.$name, size);
|
|
913
|
+
maskSet.update(block, block.replace(content, data));
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
sizeRequests.forEach(block => {
|
|
917
|
+
const size = segmentSizes.get(block.$name);
|
|
918
|
+
const code = `(i32.const ${size})`;
|
|
919
|
+
maskSet.update(block, code);
|
|
920
|
+
});
|
|
921
|
+
viewRequests.forEach(block => {
|
|
922
|
+
block.id = helpers.referenceId();
|
|
923
|
+
maskSet.update(block, block.id);
|
|
924
|
+
});
|
|
925
|
+
wat = maskSet.restore();
|
|
926
|
+
let oninit = String();
|
|
927
|
+
viewRequests.forEach(block => {
|
|
928
|
+
if (externgetter.has(block.$name) === false) {
|
|
929
|
+
const size = segmentSizes.get(block.$name);
|
|
930
|
+
const code = GENERATE_DATA_VIEWER(size, block.$name);
|
|
931
|
+
const growRequest = WAT4WASM_GROW_EXTERN_TABLE(wat);
|
|
932
|
+
block.tableSetter = growRequest.generateSetter(code);
|
|
933
|
+
wat = growRequest.modifiedRaw;
|
|
934
|
+
oninit = `${oninit}\n\n${block.tableSetter}\n\n`;
|
|
935
|
+
externgetter.set(
|
|
936
|
+
block.$name,
|
|
937
|
+
growRequest.getter.concat(` ;; ${block.$name}\n`)
|
|
938
|
+
);
|
|
939
|
+
}
|
|
940
|
+
});
|
|
941
|
+
viewRequests.forEach(block => {
|
|
942
|
+
wat = wat.replaceAll(
|
|
943
|
+
block.id, externgetter.get(block.$name)
|
|
944
|
+
);
|
|
945
|
+
});
|
|
946
|
+
wat = APPEND_ON_EXTERN_READY(wat, oninit);
|
|
947
|
+
return wat;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
const IMPORT_BLOCK_NAME = "import";
|
|
951
|
+
const RAWBLOCK_IMPORT_CODE = (pathName, rawBlock) => `(import "${pathName.split(".").at(-2) || 'self'}" "${pathName.split(".").at(-1)}" \t${rawBlock})`.replaceAll(" )", ")");
|
|
952
|
+
const FUNCTION_IMPORT_CODE = ($name) => {
|
|
953
|
+
const namesig = $name.match(/\$(.[^<]*)<(.[^>]*)?>(.[^\s]*)?/).slice(1);
|
|
954
|
+
const [pathName, inputs = "", outputs = ""] = namesig;
|
|
955
|
+
const tagName = `func`;
|
|
956
|
+
const longType = t => ({ ext: 'externref', fun: 'funcref' })[t] || t;
|
|
957
|
+
const paramBlock = `(param${`.${inputs}`.split('.').map(longType).join(' ')})`;
|
|
958
|
+
const resultBlock = `(result${`.${outputs}`.split('.').map(longType).join(' ')})`;
|
|
959
|
+
return RAWBLOCK_IMPORT_CODE(pathName, `(${tagName} ${$name} ${paramBlock} ${resultBlock})`);
|
|
960
|
+
}
|
|
961
|
+
const GLOBAL_IMPORT_CODE = ($name) => {
|
|
962
|
+
const namesig = $name.match(/\$(.[^<]*)(?:\<(.[^>]*)\>)?/)?.slice(1) || [];
|
|
963
|
+
const [pathName = "self", type = "ext"] = namesig;
|
|
964
|
+
const tagName = "global";
|
|
965
|
+
const typeName = type.replace(`ext`, `externref`);
|
|
966
|
+
return RAWBLOCK_IMPORT_CODE(pathName, `(${tagName} ${$name} ${typeName})`);
|
|
967
|
+
}
|
|
968
|
+
const GLOBAL_DEFINE_CODE = ($name) => {
|
|
969
|
+
const [pathName = "self", type = "ext"] = helpers.nameSignatureofGlobal($name);
|
|
970
|
+
const tagName = "global";
|
|
971
|
+
const mutValue = t => `(mut ${({ ext: 'externref', fun: 'funcref' })[t] || t})`;
|
|
972
|
+
const nilValue = t => `(${({ ext: 'ref.null extern', fun: 'ref.null func' })[t] || `${t}.const 0`})`;
|
|
973
|
+
return `(${tagName} ${$name} ${mutValue(type)} ${nilValue(type)})`;
|
|
974
|
+
}
|
|
975
|
+
const PATH_WALKER_CODE = ($name) => {
|
|
976
|
+
let descriptorKey;
|
|
977
|
+
[$name, descriptorKey = "value"] = $name.split("/")
|
|
978
|
+
const nameparts = $name.split("<").at(0).split("$").pop().split(".");
|
|
979
|
+
const stepType = new Array(nameparts.length - 1).fill("ext");
|
|
980
|
+
const type = $name.split(">").at(0).split("<").pop() || "ext";
|
|
981
|
+
let pathWalker = `(global.get $${nameparts[0]})`;
|
|
982
|
+
let currentKey;
|
|
983
|
+
stepType.push(type);
|
|
984
|
+
stepType.reverse().pop();
|
|
985
|
+
nameparts.reverse().pop();
|
|
986
|
+
let stepCount = nameparts.length;
|
|
987
|
+
if (descriptorKey !== "value") {
|
|
988
|
+
stepCount = stepCount - 1;
|
|
989
|
+
}
|
|
990
|
+
while (stepCount--) {
|
|
991
|
+
currentKey = nameparts.pop();
|
|
992
|
+
pathWalker = String(`
|
|
993
|
+
(call $self.Reflect.get<ext.ext>${stepType.pop()}
|
|
994
|
+
${pathWalker}
|
|
995
|
+
(text "${currentKey}") ;; ${currentKey}
|
|
996
|
+
)`).trim();
|
|
997
|
+
}
|
|
998
|
+
if (descriptorKey !== "value") {
|
|
999
|
+
currentKey = nameparts.pop();
|
|
1000
|
+
pathWalker = String(`
|
|
1001
|
+
(call $self.Reflect.get<ext.ext>ext
|
|
1002
|
+
(call $self.Reflect.getOwnPropertyDescriptor<ext.ext>ext
|
|
1003
|
+
${pathWalker}
|
|
1004
|
+
(text "${currentKey}") ;; ${currentKey}
|
|
1005
|
+
)
|
|
1006
|
+
(text "${descriptorKey}") ;; ${descriptorKey}
|
|
1007
|
+
)`).trim();
|
|
1008
|
+
}
|
|
1009
|
+
return String(`
|
|
1010
|
+
${pathWalker}
|
|
1011
|
+
`);
|
|
1012
|
+
}
|
|
1013
|
+
function IMPORT (wat, WAT4WASM) {
|
|
1014
|
+
const maskSet = new helpers.MaskSet(wat);
|
|
1015
|
+
const selfSet = new Set();
|
|
1016
|
+
const imports = new Array();
|
|
1017
|
+
const globals = new Array();
|
|
1018
|
+
const oninits = new Array();
|
|
1019
|
+
while (maskSet.hasBlock(IMPORT_BLOCK_NAME)) {
|
|
1020
|
+
const wrapper = maskSet.lastBlockOf(IMPORT_BLOCK_NAME);
|
|
1021
|
+
const $name = helpers.parseFirstBlock(wrapper).$name;
|
|
1022
|
+
if ($name && $name.startsWith("$self")) {
|
|
1023
|
+
if (selfSet.has($name) === false) {
|
|
1024
|
+
selfSet.add($name);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
maskSet.mask(wrapper);
|
|
1028
|
+
}
|
|
1029
|
+
while (maskSet.hasBlock("global.get")) {
|
|
1030
|
+
const global = maskSet.lastBlockOf("global.get");
|
|
1031
|
+
const $name = global.$name;
|
|
1032
|
+
maskSet.mask(global);
|
|
1033
|
+
maskSet.update(global, `(global.get ${$name})`);
|
|
1034
|
+
if ($name && $name.startsWith("$self")) {
|
|
1035
|
+
if (selfSet.has($name) === false) {
|
|
1036
|
+
selfSet.add($name);
|
|
1037
|
+
const [pathName = "self", type = "ext"
|
|
1038
|
+
] = helpers.nameSignatureofGlobal($name);
|
|
1039
|
+
const nameparts = pathName.split(".");
|
|
1040
|
+
/**
|
|
1041
|
+
* previous logic is based on direct import for
|
|
1042
|
+
* short paths but this is unnecessary.. just set
|
|
1043
|
+
* global value in the start function to gathering
|
|
1044
|
+
* more simple import segment.
|
|
1045
|
+
*/
|
|
1046
|
+
//if (nameparts.length <= 3) {
|
|
1047
|
+
if (["$self", "$self.String.fromCharCode"].includes($name)) {
|
|
1048
|
+
imports.push(GLOBAL_IMPORT_CODE($name));
|
|
1049
|
+
}
|
|
1050
|
+
else if (globals.includes($name) === false) {
|
|
1051
|
+
globals.push(GLOBAL_DEFINE_CODE($name));
|
|
1052
|
+
const walker = PATH_WALKER_CODE($name, global.descriptorKey);
|
|
1053
|
+
const setter = `(global.set ${$name} ${walker})`;
|
|
1054
|
+
oninits.push({ setter, name: $name.substring(1), type: 'global' });
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
while (maskSet.hasBlock("call")) {
|
|
1060
|
+
const caller = maskSet.lastBlockOf("call");
|
|
1061
|
+
const $name = caller.$name;
|
|
1062
|
+
if ($name && $name.startsWith("$self")) {
|
|
1063
|
+
if (selfSet.has($name) === false) {
|
|
1064
|
+
selfSet.add($name);
|
|
1065
|
+
imports.push(FUNCTION_IMPORT_CODE($name));
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
maskSet.mask(caller);
|
|
1069
|
+
}
|
|
1070
|
+
wat = maskSet.restore();
|
|
1071
|
+
imports.forEach(code => {
|
|
1072
|
+
if (wat.includes(code) === false) {
|
|
1073
|
+
wat = helpers.prepend(wat, code);
|
|
1074
|
+
}
|
|
1075
|
+
});
|
|
1076
|
+
globals.forEach(code => {
|
|
1077
|
+
if (wat.includes(code) === false) {
|
|
1078
|
+
wat = helpers.append(wat, code);
|
|
1079
|
+
}
|
|
1080
|
+
});
|
|
1081
|
+
oninits.forEach(init => {
|
|
1082
|
+
const header = `block $${init.type}/${init.name}`;
|
|
1083
|
+
if (wat.includes(header) === false) {
|
|
1084
|
+
const code = String(`(${header}\n${init.setter}\n)`);
|
|
1085
|
+
wat = APPEND_ON_TEXT_READY(wat, code);
|
|
1086
|
+
}
|
|
1087
|
+
});
|
|
1088
|
+
return wat;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
const INCLUDE_BLOCK_NAME = "include";
|
|
1092
|
+
function INCLUDE (wat, WAT4WASM) {
|
|
1093
|
+
while (helpers.hasBlock(wat, INCLUDE_BLOCK_NAME)) {
|
|
1094
|
+
const block = helpers.lastBlockOf(wat, INCLUDE_BLOCK_NAME);
|
|
1095
|
+
const path = helpers.findQuotedText(block);
|
|
1096
|
+
wat = block.replacedRaw(
|
|
1097
|
+
helpers.readFileAsText(path)
|
|
1098
|
+
);
|
|
1099
|
+
}
|
|
1100
|
+
return wat;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
const NEW_BLOCK_NAME = "new";
|
|
1104
|
+
function NEW (wat) {
|
|
1105
|
+
while (helpers.hasBlock(wat, NEW_BLOCK_NAME)) {
|
|
1106
|
+
const block = helpers.lastBlockOf(wat, NEW_BLOCK_NAME);
|
|
1107
|
+
const $name = block.$name;
|
|
1108
|
+
const param = $name.split(">").at(0).split("<").at(1) || "";
|
|
1109
|
+
const $constructor = `(ref.extern $self.${$name.substring(1)}<ext>)`;
|
|
1110
|
+
const $arguments = block.replace(`(new ${$name}`, `(array $of<${param}>ext`);
|
|
1111
|
+
const $reflect = `(reflect $construct<ext.ext>ext\n${$constructor}\n${$arguments}\n)`;
|
|
1112
|
+
wat = block.replacedRaw($reflect);
|
|
1113
|
+
}
|
|
1114
|
+
return wat;
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
const REF_EXTERN_BLOCK_NAME = "ref.extern";
|
|
1118
|
+
const FIXNAME_SELF_PATH = ($name, descriptorKey) => {
|
|
1119
|
+
if ($name.startsWith("$self") === false) {
|
|
1120
|
+
$name = `$self.${$name.substring(1)}`;
|
|
1121
|
+
}
|
|
1122
|
+
if ($name.endsWith(">") === false) {
|
|
1123
|
+
if (descriptorKey === "value") {
|
|
1124
|
+
$name = `${$name}<ext>`;
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
return $name;
|
|
1128
|
+
};
|
|
1129
|
+
function REF_EXTERN (wat, WAT4WASM) {
|
|
1130
|
+
const externs = new Map();
|
|
1131
|
+
const getters = new Map();
|
|
1132
|
+
const maskSet = new helpers.MaskSet(wat);
|
|
1133
|
+
while (maskSet.hasBlock(REF_EXTERN_BLOCK_NAME)) {
|
|
1134
|
+
const block = maskSet.lastBlockOf(REF_EXTERN_BLOCK_NAME);
|
|
1135
|
+
const $name = FIXNAME_SELF_PATH(block.$name, block.descriptorKey);
|
|
1136
|
+
if (externs.has($name) === false) {
|
|
1137
|
+
externs.set($name, block.descriptorKey);
|
|
1138
|
+
}
|
|
1139
|
+
maskSet.mask(block);
|
|
1140
|
+
}
|
|
1141
|
+
wat = maskSet.restore();
|
|
1142
|
+
let oninit = String();
|
|
1143
|
+
externs.forEach((dKey, $name) => {
|
|
1144
|
+
const pathWalker = PATH_WALKER_CODE($name);
|
|
1145
|
+
const growExtern = WAT4WASM_GROW_EXTERN_TABLE(wat);
|
|
1146
|
+
const __getter__ = growExtern.getter.concat(` ;; ${$name}\n`);
|
|
1147
|
+
const __setter__ = growExtern.generateSetter(pathWalker);
|
|
1148
|
+
wat = growExtern.modifiedRaw;
|
|
1149
|
+
oninit = `${oninit}\n\n(block ${$name}\n${__setter__})\n`;
|
|
1150
|
+
getters.set($name, __getter__);
|
|
1151
|
+
});
|
|
1152
|
+
while (helpers.hasBlock(wat, REF_EXTERN_BLOCK_NAME)) {
|
|
1153
|
+
const block = helpers.lastBlockOf(wat, REF_EXTERN_BLOCK_NAME);
|
|
1154
|
+
const $name = FIXNAME_SELF_PATH(block.$name, block.descriptorKey);
|
|
1155
|
+
wat = block.replacedRaw(getters.get($name));
|
|
1156
|
+
}
|
|
1157
|
+
if (oninit.trim()) {
|
|
1158
|
+
wat = APPEND_ON_TEXT_READY(wat, oninit);
|
|
1159
|
+
}
|
|
1160
|
+
return wat;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
const REF_FUNC_BLOCK_NAME = "ref.func";
|
|
1164
|
+
function REF_FUNC (wat, WAT4WASM) {
|
|
1165
|
+
const maskSetElem = new helpers.MaskSet(wat);
|
|
1166
|
+
const elemSegments = new Array();
|
|
1167
|
+
const needReference = new Set();
|
|
1168
|
+
while (maskSetElem.hasBlock("elem")) {
|
|
1169
|
+
const block = maskSetElem.lastBlockOf("elem");
|
|
1170
|
+
const $name = block.$name;
|
|
1171
|
+
if (WAT4WASM.WAT4WASM_$NAME !== $name) {
|
|
1172
|
+
elemSegments.push(block.toString());
|
|
1173
|
+
}
|
|
1174
|
+
maskSetElem.mask(block);
|
|
1175
|
+
}
|
|
1176
|
+
const maskSetRef = new helpers.MaskSet(wat);
|
|
1177
|
+
while (maskSetRef.hasBlock(REF_FUNC_BLOCK_NAME)) {
|
|
1178
|
+
const block = maskSetRef.lastBlockOf(REF_FUNC_BLOCK_NAME);
|
|
1179
|
+
const $name = block.$name
|
|
1180
|
+
if (WAT4WASM.WAT4WASM_$NAME !== $name) {
|
|
1181
|
+
if (elemSegments.some(seg => seg.includes($name)) === false) {
|
|
1182
|
+
if (needReference.has($name) === false) {
|
|
1183
|
+
needReference.add($name);
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
maskSetRef.mask(block);
|
|
1188
|
+
}
|
|
1189
|
+
wat = maskSetRef.restore();
|
|
1190
|
+
needReference.forEach($name => {
|
|
1191
|
+
wat = WAT4WASM_REFERENCE_FUNC_ELEMENT(wat, $name);
|
|
1192
|
+
});
|
|
1193
|
+
return wat;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
|
|
1197
|
+
function REPLACE_ALL (wat) {
|
|
1198
|
+
return wat
|
|
1199
|
+
.replaceAll("(self)", "(global.get $self)")
|
|
1200
|
+
.replaceAll("(null)", "(ref.null extern)")
|
|
1201
|
+
.replaceAll("(func)", "(ref.null func)")
|
|
1202
|
+
.replaceAll("(this)", "(local.get 0)")
|
|
1203
|
+
.replaceAll("(array)", "(call $self.Array<>ext)")
|
|
1204
|
+
.replaceAll("(NaN)", "(ref.extern $self.NaN<ext>)")
|
|
1205
|
+
.replaceAll("(nan)", "(f32.const nan)")
|
|
1206
|
+
.replaceAll("(true)", "(i32.const 1)")
|
|
1207
|
+
.replaceAll("(false)", "(i32.const 0)")
|
|
1208
|
+
.replaceAll("(undefined)", "(ref.extern $self.undefined<ext>)")
|
|
1209
|
+
.replaceAll("(object)", "(call $self.Object<>ext)")
|
|
1210
|
+
.replaceAll("(string)", "(call $self.String<>ext)")
|
|
1211
|
+
.replaceAll("(console $", "(call $self.console.")
|
|
1212
|
+
.replaceAll("(reflect $", "(call $self.Reflect.")
|
|
1213
|
+
.replaceAll("(bigint $", "(call $self.BigInt.")
|
|
1214
|
+
.replaceAll("(number $", "(call $self.Number.")
|
|
1215
|
+
.replaceAll("(math $", "(call $self.Math.")
|
|
1216
|
+
.replaceAll("(string $", "(call $self.String.")
|
|
1217
|
+
.replaceAll("(object $", "(call $self.Object.")
|
|
1218
|
+
.replaceAll("(array $", "(call $self.Array.")
|
|
1219
|
+
.replaceAll("(grow $", "(table.grow $")
|
|
1220
|
+
.replaceAll("(url $", "(call $self.URL.")
|
|
1221
|
+
.replaceAll("(self $", "(ref.extern $self.")
|
|
1222
|
+
.replaceAll(" mut ext)", " (mut externref) (null))")
|
|
1223
|
+
.replaceAll(" mut fun)", " (mut funcref) (func))")
|
|
1224
|
+
.replaceAll(" mut vec)", " (mut v128) (v128.const i32x4 0 0 0 0))")
|
|
1225
|
+
.replaceAll(" fun)", " funcref)")
|
|
1226
|
+
.replaceAll(" ext)", " externref)")
|
|
1227
|
+
.replaceAll(" != null)", ") (ref.is_null) (i32.eqz)")
|
|
1228
|
+
.replaceAll(" == null)", ") (ref.is_null)")
|
|
1229
|
+
.replaceAll(" == 0)", ") (i32.eqz)")
|
|
1230
|
+
.replaceAll(" != 0)", ") (i32.const 0) (i32.ne)")
|
|
1231
|
+
.replaceAll(" == 1)", ") (i32.const 1) (i32.eq)")
|
|
1232
|
+
.replaceAll(" != 1)", ") (i32.const 1) (i32.ne)")
|
|
1233
|
+
.replaceAll(" == true)", ") (i32.const 1) (i32.eq)")
|
|
1234
|
+
.replaceAll(" == false)", ") (i32.eqz)")
|
|
1235
|
+
.replaceAll(" == nan)", ") (f32.const nan) (f32.eq)")
|
|
1236
|
+
.replaceAll(" != nan)", ") (f32.const nan) (f32.ne)")
|
|
1237
|
+
.replaceAll(" != NaN)", ") (NaN) (object $is<ext.ext>i32) (i32.eqz)")
|
|
1238
|
+
.replaceAll(" == NaN)", ") (NaN) (object $is<ext.ext>i32)")
|
|
1239
|
+
.replaceAll(" != undefined)", ") (undefined) (object $is<ext.ext>i32) (i32.eqz)")
|
|
1240
|
+
.replaceAll(" == undefined)", ") (undefined) (object $is<ext.ext>i32)")
|
|
1241
|
+
.replaceAll(/ mut\s+(i32|f32|i64|f64)\)/g, ` (mut $1) ($1.const 0))`)
|
|
1242
|
+
.replaceAll(/\<[A-Z](.[^>]*)\>/g, "externref")
|
|
1243
|
+
.replaceAll(/\(l(get|set|tee)(\s)/g, `(local.$1$2`)
|
|
1244
|
+
.replaceAll(/\(g(get|set)(\s)/g, `(global.$1$2`)
|
|
1245
|
+
.replaceAll(/\(t(get|set)(\s)/g, `(table.$1$2`)
|
|
1246
|
+
.replaceAll(/(i32|f32|i64|f64|fun|ext)\((\+|\-|)\s*([0-9\.]+)\)/g, `($1.const $2$3)`)
|
|
1247
|
+
.replaceAll(/\((.*)\.(set|tee)\s+([\+|\-])+\s+(\$.*)\s*\)/g, "($1.$2 $4 (i32.add ($1.get $4) (i32.const $31)))")
|
|
1248
|
+
.replaceAll(/\(apply(?:\.*)(i32|f32|i64|f64|fun|ext|)(\s*)/g, `(call $self.Reflect.apply<ext.ext.ext>$1 $2`)
|
|
1249
|
+
.replaceAll(/\(main(\s+)(\$.[^\s]*)(\s)/g, `(start$1$2)\n\n(func$1$2$3`)
|
|
1250
|
+
;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
const START_BLOCK_NAME = "start";
|
|
1254
|
+
function START (wat, WAT4WASM) {
|
|
1255
|
+
let startCalls = [];
|
|
1256
|
+
let removedWat = wat;
|
|
1257
|
+
while (helpers.hasBlock(removedWat, START_BLOCK_NAME)) {
|
|
1258
|
+
let block = helpers.lastBlockOf(removedWat, START_BLOCK_NAME);
|
|
1259
|
+
removedWat = block.removedRaw();
|
|
1260
|
+
if (block.includes(WAT4WASM.WAT4WASM_$NAME) === false) {
|
|
1261
|
+
startCalls.push(block);
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
if (startCalls.length > 0) {
|
|
1265
|
+
wat = removedWat;
|
|
1266
|
+
let $wat4func = helpers.lastBlockOf(wat, "func", { $name: WAT4WASM.WAT4WASM_$NAME });
|
|
1267
|
+
let funcblock = $wat4func.toString();
|
|
1268
|
+
const appends = startCalls.filter(start => {
|
|
1269
|
+
let $name = `${start.$name}`;
|
|
1270
|
+
let $call = `(call ${$name})`;
|
|
1271
|
+
if (helpers.hasBlock(funcblock, "call", { $name }) === false) {
|
|
1272
|
+
funcblock = helpers.append(funcblock, $call);
|
|
1273
|
+
return true;
|
|
1274
|
+
}
|
|
1275
|
+
});
|
|
1276
|
+
if (appends.length) {
|
|
1277
|
+
wat = $wat4func.replacedRaw(funcblock);
|
|
1278
|
+
}
|
|
1279
|
+
wat = helpers.append(wat, WAT4WASM.WAT4WASM_START);
|
|
1280
|
+
}
|
|
1281
|
+
return wat;
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
const STRING_BLOCK_NAME = "string";
|
|
1285
|
+
function STRING (wat) {
|
|
1286
|
+
while (helpers.hasBlock(wat, STRING_BLOCK_NAME)) {
|
|
1287
|
+
let oldBlock = helpers.lastBlockOf(wat, STRING_BLOCK_NAME);
|
|
1288
|
+
const string = helpers.findQuotedText(oldBlock);
|
|
1289
|
+
const ccodes = helpers.encodeString(string);
|
|
1290
|
+
if (ccodes.length === 0) {
|
|
1291
|
+
wat = oldBlock.replacedRaw(`
|
|
1292
|
+
(reflect $apply<ext.ext.ext>ext
|
|
1293
|
+
(global.get $self.String.fromCharCode)
|
|
1294
|
+
(self)
|
|
1295
|
+
(self)
|
|
1296
|
+
)
|
|
1297
|
+
`);
|
|
1298
|
+
}
|
|
1299
|
+
else if (ccodes.length === 1) {
|
|
1300
|
+
wat = oldBlock.replacedRaw(`
|
|
1301
|
+
(reflect $apply<ext.ext.ext>ext
|
|
1302
|
+
(global.get $self.String.fromCharCode)
|
|
1303
|
+
(self)
|
|
1304
|
+
(array $of<i32>ext (i32.const ${string.charCodeAt(0)})) ;; "${string}"
|
|
1305
|
+
)
|
|
1306
|
+
`);
|
|
1307
|
+
}
|
|
1308
|
+
else {
|
|
1309
|
+
wat = oldBlock.replacedRaw(`
|
|
1310
|
+
(block (; "${helpers.abstract(string)}" ;)
|
|
1311
|
+
(result externref)
|
|
1312
|
+
(global.set $wat4wasm (call $self.Array<>ext))
|
|
1313
|
+
|
|
1314
|
+
${ccodes
|
|
1315
|
+
.map((c, i) => `(global.get $wat4wasm) (i32.const ${i}) (i32.const ${c})`)
|
|
1316
|
+
.map((args) => `(call $self.Reflect.set<ext.i32.i32> ${args})`)
|
|
1317
|
+
.join("\n")}
|
|
1318
|
+
|
|
1319
|
+
(call $self.Reflect.apply<ext.ext.ext>ext
|
|
1320
|
+
(global.get $self.String.fromCharCode)
|
|
1321
|
+
(ref.null extern)
|
|
1322
|
+
(global.get $wat4wasm)
|
|
1323
|
+
)
|
|
1324
|
+
;; stacked
|
|
1325
|
+
|
|
1326
|
+
(global.set $wat4wasm (null))
|
|
1327
|
+
;; cleared
|
|
1328
|
+
)
|
|
1329
|
+
`);
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
return wat;
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
const TEXT_BLOCK_NAME = "text";
|
|
1336
|
+
const TEXT_ONINIT_BLOCK = (offset, length, setter) => String(
|
|
1337
|
+
`
|
|
1338
|
+
(block $decodeText/${offset}:${length}
|
|
1339
|
+
(local.set $viewAt (i32.const 0))
|
|
1340
|
+
(local.set $offset (i32.const ${offset}))
|
|
1341
|
+
(local.set $length (i32.const ${length}))
|
|
1342
|
+
(local.set $arguments (call $self.Array<>ext))
|
|
1343
|
+
|
|
1344
|
+
(call $self.Reflect.set<ext.i32.i32>
|
|
1345
|
+
(local.get $arguments) (i32.const 0) (local.get $length)
|
|
1346
|
+
)
|
|
1347
|
+
(local.set $arrayBufferView
|
|
1348
|
+
(call $self.Reflect.construct<ext.ext>ext
|
|
1349
|
+
(local.get $Uint8Array)
|
|
1350
|
+
(local.get $arguments)
|
|
1351
|
+
)
|
|
1352
|
+
)
|
|
1353
|
+
(loop $length--
|
|
1354
|
+
(if (local.get $length)
|
|
1355
|
+
(then
|
|
1356
|
+
(memory.init $wat4wasm
|
|
1357
|
+
(i32.const 0)
|
|
1358
|
+
(local.get $offset)
|
|
1359
|
+
(i32.const 1)
|
|
1360
|
+
)
|
|
1361
|
+
(call $self.Reflect.set<ext.i32.i32>
|
|
1362
|
+
(local.get $arrayBufferView)
|
|
1363
|
+
(local.get $viewAt)
|
|
1364
|
+
(i32.load8_u (i32.const 0))
|
|
1365
|
+
)
|
|
1366
|
+
(local.set $viewAt (i32.add (local.get $viewAt) (i32.const 1)))
|
|
1367
|
+
(local.set $offset (i32.add (local.get $offset) (i32.const 1)))
|
|
1368
|
+
(local.set $length (i32.sub (local.get $length) (i32.const 1)))
|
|
1369
|
+
(br $length--)
|
|
1370
|
+
)
|
|
1371
|
+
)
|
|
1372
|
+
)
|
|
1373
|
+
(local.set $arguments (call $self.Array<>ext))
|
|
1374
|
+
(call $self.Reflect.set<ext.i32.ext>
|
|
1375
|
+
(local.get $arguments)
|
|
1376
|
+
(i32.const 0)
|
|
1377
|
+
(local.get $arrayBufferView)
|
|
1378
|
+
)
|
|
1379
|
+
${setter}
|
|
1380
|
+
)
|
|
1381
|
+
`).trim();
|
|
1382
|
+
function TEXT (wat, WAT4WASM) {
|
|
1383
|
+
const maskSet = new helpers.MaskSet(wat);
|
|
1384
|
+
const textBlocks = new Array();
|
|
1385
|
+
while (maskSet.hasBlock(TEXT_BLOCK_NAME)) {
|
|
1386
|
+
let block = maskSet.lastBlockOf(TEXT_BLOCK_NAME);
|
|
1387
|
+
maskSet.mask(block);
|
|
1388
|
+
textBlocks.push(block);
|
|
1389
|
+
}
|
|
1390
|
+
wat = maskSet.restore();
|
|
1391
|
+
textBlocks.forEach(block => {
|
|
1392
|
+
const text = helpers.findQuotedText(block);
|
|
1393
|
+
const view = helpers.encodeText(text);
|
|
1394
|
+
const dataRequest = WAT4WASM_ALLOC_DATA_BUFFER(wat, view.buffer);
|
|
1395
|
+
block.dataOffset = dataRequest.offset;
|
|
1396
|
+
block.viewLength = view.length;
|
|
1397
|
+
block.strPreview = helpers.abstract(text);
|
|
1398
|
+
wat = dataRequest.modifiedRaw;
|
|
1399
|
+
});
|
|
1400
|
+
textBlocks.forEach(block => {
|
|
1401
|
+
const growRequest = WAT4WASM_GROW_EXTERN_TABLE(wat);
|
|
1402
|
+
block.tableGetter = growRequest.getter.concat(`;; ${block.strPreview} \n`);
|
|
1403
|
+
block.tableSetter = growRequest.generateSetter(`
|
|
1404
|
+
(call $self.Reflect.apply<ext.ext.ext>ext
|
|
1405
|
+
(local.get $textDecoder.decode)
|
|
1406
|
+
(local.get $textDecoder)
|
|
1407
|
+
(local.get $arguments) ;; ${block.strPreview}
|
|
1408
|
+
)`).trim();
|
|
1409
|
+
wat = growRequest.modifiedRaw;
|
|
1410
|
+
});
|
|
1411
|
+
textBlocks.forEach(block => {
|
|
1412
|
+
wat = wat.replaceAll(block.toString(), block.tableGetter)
|
|
1413
|
+
});
|
|
1414
|
+
const oninit = textBlocks.map(block =>
|
|
1415
|
+
TEXT_ONINIT_BLOCK(
|
|
1416
|
+
block.dataOffset,
|
|
1417
|
+
block.viewLength, block.tableSetter
|
|
1418
|
+
)
|
|
1419
|
+
).join("\n");
|
|
1420
|
+
wat = APPEND_ON_INIT(wat, oninit);
|
|
1421
|
+
return wat;
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
let WAT4WASM_$NAME = `$wat4wasm`;
|
|
1425
|
+
let WAT4WASM_FUNC = String(`
|
|
1426
|
+
(func $wat4wasm
|
|
1427
|
+
(local $textDecoder externref)
|
|
1428
|
+
(local $textDecoder.decode externref)
|
|
1429
|
+
(local $Uint8Array externref)
|
|
1430
|
+
(local $arguments externref)
|
|
1431
|
+
(local $arrayBufferView externref)
|
|
1432
|
+
(local $viewAt i32)
|
|
1433
|
+
(local $offset i32)
|
|
1434
|
+
(local $length i32)
|
|
1435
|
+
(block $prepare
|
|
1436
|
+
(local.set $textDecoder
|
|
1437
|
+
(call $self.Reflect.construct<ext.ext>ext
|
|
1438
|
+
(call $self.Reflect.get<ext.ext>ext
|
|
1439
|
+
(self)
|
|
1440
|
+
(string "TextDecoder")
|
|
1441
|
+
)
|
|
1442
|
+
(self)
|
|
1443
|
+
)
|
|
1444
|
+
)
|
|
1445
|
+
(local.set $textDecoder.decode
|
|
1446
|
+
(call $self.Reflect.get<ext.ext>ext
|
|
1447
|
+
(local.get $textDecoder)
|
|
1448
|
+
(string "decode")
|
|
1449
|
+
)
|
|
1450
|
+
)
|
|
1451
|
+
(local.set $Uint8Array
|
|
1452
|
+
(call $self.Reflect.get<ext.ext>ext
|
|
1453
|
+
(self)
|
|
1454
|
+
(string "Uint8Array")
|
|
1455
|
+
)
|
|
1456
|
+
)
|
|
1457
|
+
)
|
|
1458
|
+
;;secure zero heap for memory.init
|
|
1459
|
+
(i32.const 0)
|
|
1460
|
+
(i32.load (i32.const 0))
|
|
1461
|
+
;; offset and value stacked now
|
|
1462
|
+
(block $oninit)
|
|
1463
|
+
(block $ontextready)
|
|
1464
|
+
(block $onexternready)
|
|
1465
|
+
|
|
1466
|
+
;; restore zero heap value
|
|
1467
|
+
(i32.store (; stack stack ;))
|
|
1468
|
+
(nop)
|
|
1469
|
+
)
|
|
1470
|
+
`);
|
|
1471
|
+
let WAT4WASM_GLOBAL = String(
|
|
1472
|
+
`(global $wat4wasm (mut externref) (ref.null extern))`
|
|
1473
|
+
);
|
|
1474
|
+
let WAT4WASM_TABLE = (size = 1) => String(
|
|
1475
|
+
`(table $wat4wasm ${size} externref)`
|
|
1476
|
+
);
|
|
1477
|
+
let WAT4WASM_DATA = (buff = "0000") => String(
|
|
1478
|
+
`(data $wat4wasm "${Buffer.from(buff).toString("hex").replaceAll(/(..)/g, `\\$1`)}")`
|
|
1479
|
+
);
|
|
1480
|
+
let WAT4WASM_ELEM = String(
|
|
1481
|
+
`(elem $wat4wasm declare func)`
|
|
1482
|
+
);
|
|
1483
|
+
let WAT4WASM_MEMORY = String(
|
|
1484
|
+
`(memory $wat4wasm 1)`
|
|
1485
|
+
);
|
|
1486
|
+
let WAT4WASM_START = String(
|
|
1487
|
+
`(start $wat4wasm)`
|
|
1488
|
+
);
|
|
1489
|
+
const WAT4WASM_BLOCKS = {
|
|
1490
|
+
global: WAT4WASM_GLOBAL,
|
|
1491
|
+
table: WAT4WASM_TABLE(1),
|
|
1492
|
+
elem: WAT4WASM_ELEM,
|
|
1493
|
+
func: WAT4WASM_FUNC,
|
|
1494
|
+
data: WAT4WASM_DATA(),
|
|
1495
|
+
};
|
|
1496
|
+
function FUNC_WAT4WASM(wat) {
|
|
1497
|
+
return helpers.lastBlockOf(wat, "func", { $name: WAT4WASM_$NAME });
|
|
1498
|
+
}
|
|
1499
|
+
function ELEM_WAT4WASM(wat) {
|
|
1500
|
+
const block = helpers.lastBlockOf(wat, "elem", { $name: WAT4WASM_$NAME });
|
|
1501
|
+
return block && Object.assign(block, {
|
|
1502
|
+
isInitial: helpers.generateId(block) === helpers.generateId(WAT4WASM_ELEM)
|
|
1503
|
+
});
|
|
1504
|
+
}
|
|
1505
|
+
function MEMORY_WAT4WASM(wat) {
|
|
1506
|
+
return helpers.lastBlockOf(wat, "memory", { $name: WAT4WASM_$NAME });
|
|
1507
|
+
}
|
|
1508
|
+
function GLOBAL_WAT4WASM(wat) {
|
|
1509
|
+
return helpers.lastBlockOf(wat, "global", { $name: WAT4WASM_$NAME });
|
|
1510
|
+
}
|
|
1511
|
+
function START_WAT4WASM(wat) {
|
|
1512
|
+
return helpers.lastBlockOf(wat, "start", { $name: WAT4WASM_$NAME });
|
|
1513
|
+
}
|
|
1514
|
+
function FUNC_WAT4WASM_NOBLOCKS(wat) {
|
|
1515
|
+
const maskSet = new helpers.MaskSet(FUNC_WAT4WASM(wat));
|
|
1516
|
+
maskSet.remove(maskSet.lastBlockOf("block", { name: "onexternready" }));
|
|
1517
|
+
maskSet.remove(maskSet.lastBlockOf("block", { name: "ontextready" }));
|
|
1518
|
+
maskSet.remove(maskSet.lastBlockOf("block", { name: "oninit" }));
|
|
1519
|
+
maskSet.remove(maskSet.lastBlockOf("block", { name: "prepare" }));
|
|
1520
|
+
return maskSet.restore();
|
|
1521
|
+
}
|
|
1522
|
+
function FUNC_WAT4WASM_BLOCK_ONINIT(wat) {
|
|
1523
|
+
const wat4func = FUNC_WAT4WASM(wat);
|
|
1524
|
+
const blockoninit = helpers.lastBlockOf(wat4func, "block", { name: "oninit" });
|
|
1525
|
+
const $blockoninit = helpers.parseBlockAt(wat, wat4func.begin + blockoninit.begin);
|
|
1526
|
+
return $blockoninit;
|
|
1527
|
+
}
|
|
1528
|
+
function FUNC_WAT4WASM_BLOCK_ONTEXTREADY(wat) {
|
|
1529
|
+
const wat4func = FUNC_WAT4WASM(wat);
|
|
1530
|
+
const ontextready = helpers.lastBlockOf(wat4func, "block", { name: "ontextready" });
|
|
1531
|
+
const $ontextready = helpers.parseBlockAt(wat, wat4func.begin + ontextready.begin);
|
|
1532
|
+
return $ontextready;
|
|
1533
|
+
}
|
|
1534
|
+
function FUNC_WAT4WASM_BLOCK_ONEXTERNREADY(wat) {
|
|
1535
|
+
const wat4func = FUNC_WAT4WASM(wat);
|
|
1536
|
+
const onexternready = helpers.lastBlockOf(wat4func, "block", { name: "onexternready" });
|
|
1537
|
+
const $onexternready = helpers.parseBlockAt(wat, wat4func.begin + onexternready.begin);
|
|
1538
|
+
return $onexternready;
|
|
1539
|
+
}
|
|
1540
|
+
function APPEND_ON_INIT(wat, block) {
|
|
1541
|
+
const oninitblock = FUNC_WAT4WASM_BLOCK_ONINIT(wat);
|
|
1542
|
+
if (oninitblock.includes(block.toString())) {
|
|
1543
|
+
return wat;
|
|
1544
|
+
}
|
|
1545
|
+
wat = oninitblock.replacedRaw(
|
|
1546
|
+
helpers.append(oninitblock, block)
|
|
1547
|
+
);
|
|
1548
|
+
return wat;
|
|
1549
|
+
}
|
|
1550
|
+
function APPEND_ON_TEXT_READY(wat, block) {
|
|
1551
|
+
const ontextready = FUNC_WAT4WASM_BLOCK_ONTEXTREADY(wat);
|
|
1552
|
+
if (ontextready.hasBlock("block",
|
|
1553
|
+
{ $name: helpers.firstBlockOf(block, "block")?.$name }
|
|
1554
|
+
) === false && !ontextready.includes(block.trim())) {
|
|
1555
|
+
return ontextready.replacedRaw(
|
|
1556
|
+
helpers.append(ontextready, block)
|
|
1557
|
+
);
|
|
1558
|
+
}
|
|
1559
|
+
return wat;
|
|
1560
|
+
}
|
|
1561
|
+
function APPEND_ON_EXTERN_READY(wat, block) {
|
|
1562
|
+
const onexternready = FUNC_WAT4WASM_BLOCK_ONEXTERNREADY(wat);
|
|
1563
|
+
if (onexternready.includes(block.toString())) {
|
|
1564
|
+
return wat;
|
|
1565
|
+
}
|
|
1566
|
+
wat = onexternready.replacedRaw(
|
|
1567
|
+
helpers.append(onexternready, block)
|
|
1568
|
+
);
|
|
1569
|
+
return wat;
|
|
1570
|
+
}
|
|
1571
|
+
function TABLE_WAT4WASM(wat) {
|
|
1572
|
+
const wat4table = helpers.lastBlockOf(wat, "table", { $name: WAT4WASM_$NAME });
|
|
1573
|
+
const lastIndex = parseInt(wat4table.toString().match(/\s(\d+)/).pop());
|
|
1574
|
+
return wat4table && Object.assign(wat4table, {
|
|
1575
|
+
lastIndex, isInitial: lastIndex === 1
|
|
1576
|
+
})
|
|
1577
|
+
}
|
|
1578
|
+
function WAT4WASM_GROW_EXTERN_TABLE(wat) {
|
|
1579
|
+
const wat4table = TABLE_WAT4WASM(wat);
|
|
1580
|
+
const lastIndex = wat4table.lastIndex;
|
|
1581
|
+
return {
|
|
1582
|
+
index: lastIndex,
|
|
1583
|
+
getter: `(table.get ${WAT4WASM_$NAME} (i32.const ${lastIndex}))`,
|
|
1584
|
+
generateSetter: value => `(table.set ${WAT4WASM_$NAME} (i32.const ${lastIndex}) ${value.toString()})`,
|
|
1585
|
+
modifiedRaw: wat4table.replacedRaw(WAT4WASM_TABLE(lastIndex + 1))
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1588
|
+
function WAT4WASM_REFERENCE_FUNC_ELEMENT(wat, $name) {
|
|
1589
|
+
const $wat4elem = ELEM_WAT4WASM(wat);
|
|
1590
|
+
const _wat4elem = $wat4elem.toString();
|
|
1591
|
+
if (_wat4elem.includes($name) === false) {
|
|
1592
|
+
wat = $wat4elem.replacedRaw(_wat4elem
|
|
1593
|
+
.substring(0, _wat4elem.lastIndexOf(")"))
|
|
1594
|
+
.concat(` ${$name}`)
|
|
1595
|
+
.concat(")")
|
|
1596
|
+
);
|
|
1597
|
+
}
|
|
1598
|
+
return wat;
|
|
1599
|
+
}
|
|
1600
|
+
function DATA_WAT4WASM(wat) {
|
|
1601
|
+
const block = helpers.lastBlockOf(wat, "data", { $name: WAT4WASM_$NAME });
|
|
1602
|
+
const strhex = helpers.findQuotedText(block);
|
|
1603
|
+
const buffer = Buffer.from(strhex.replaceAll(/[^a-f0-9A-F]/g, ""), "hex");
|
|
1604
|
+
return Object.assign(block, {
|
|
1605
|
+
buffer: buffer,
|
|
1606
|
+
offset: buffer.byteLength,
|
|
1607
|
+
isInitial: buffer.byteLength === 4
|
|
1608
|
+
});
|
|
1609
|
+
}
|
|
1610
|
+
function WAT4WASM_ALLOC_DATA_BUFFER(wat, buffer) {
|
|
1611
|
+
const new_data = Buffer.from(buffer);
|
|
1612
|
+
const all_data = DATA_WAT4WASM(wat);
|
|
1613
|
+
let offset = all_data.buffer.indexOf(new_data);
|
|
1614
|
+
let modifiedRaw = wat;
|
|
1615
|
+
if (offset < 1) {
|
|
1616
|
+
offset = all_data.offset;
|
|
1617
|
+
all_data.buffer.writeUint32LE(offset + new_data.byteLength);
|
|
1618
|
+
modifiedRaw = all_data.replacedRaw(WAT4WASM_DATA(Buffer.concat([all_data.buffer, new_data])));
|
|
1619
|
+
}
|
|
1620
|
+
return {
|
|
1621
|
+
offset,
|
|
1622
|
+
modifiedRaw,
|
|
1623
|
+
isRawModified: modifiedRaw.toString() !== wat.toString()
|
|
1624
|
+
};
|
|
1625
|
+
}
|
|
1626
|
+
function W4W (wat) {
|
|
1627
|
+
let i = 31, raw;
|
|
1628
|
+
if (helpers.hasBlock(wat, "memory") === false) {
|
|
1629
|
+
WAT4WASM_BLOCKS.memory = WAT4WASM_MEMORY;
|
|
1630
|
+
}
|
|
1631
|
+
for (const BLOCK_NAME in WAT4WASM_BLOCKS) {
|
|
1632
|
+
if (wat.includes(`(${BLOCK_NAME} ${WAT4WASM_$NAME}`)) {
|
|
1633
|
+
continue;
|
|
1634
|
+
}
|
|
1635
|
+
if (i === 31) console.log("")
|
|
1636
|
+
console.log(`🦋 appending element --> \x1b[${i++}m(${BLOCK_NAME} ${WAT4WASM_$NAME}) ...)\x1b[0m`);
|
|
1637
|
+
raw = WAT4WASM_BLOCKS[BLOCK_NAME].replaceAll("$wat4wasm", WAT4WASM_$NAME);
|
|
1638
|
+
wat = helpers.append(wat, raw);
|
|
1639
|
+
}
|
|
1640
|
+
if (i !== 31) console.log("")
|
|
1641
|
+
return wat;
|
|
1642
|
+
}function FUNC_WAT4WASM_START_CALLS(wat) {
|
|
1643
|
+
let $call;
|
|
1644
|
+
const calls = [];
|
|
1645
|
+
const maskSet = new helpers.MaskSet(
|
|
1646
|
+
FUNC_WAT4WASM_NOBLOCKS(wat)
|
|
1647
|
+
);
|
|
1648
|
+
while ($call = maskSet.lastBlockOf("call")) {
|
|
1649
|
+
if (calls.includes($call.$name) === false) {
|
|
1650
|
+
calls.push($call.$name);
|
|
1651
|
+
}
|
|
1652
|
+
maskSet.mask($call);
|
|
1653
|
+
}
|
|
1654
|
+
return calls;
|
|
1655
|
+
}
|
|
1656
|
+
function clean (wat) {
|
|
1657
|
+
let block;
|
|
1658
|
+
block = FUNC_WAT4WASM_BLOCK_ONEXTERNREADY(wat);
|
|
1659
|
+
if (!block.hasAnyBlock) { wat = block.removedRaw(); }
|
|
1660
|
+
block = FUNC_WAT4WASM_BLOCK_ONTEXTREADY(wat);
|
|
1661
|
+
if (!block.hasAnyBlock) { wat = block.removedRaw(); }
|
|
1662
|
+
block = FUNC_WAT4WASM_BLOCK_ONINIT(wat);
|
|
1663
|
+
if (!block.hasAnyBlock) {
|
|
1664
|
+
wat = block.removedRaw();
|
|
1665
|
+
let $starts = FUNC_WAT4WASM_START_CALLS(wat);
|
|
1666
|
+
if ($starts.length === 1) {
|
|
1667
|
+
wat = wat.replace(`(start $wat4wasm)`, `(start ${$starts.pop()})`);
|
|
1668
|
+
}
|
|
1669
|
+
let $func = FUNC_WAT4WASM(wat);
|
|
1670
|
+
if ($func) {
|
|
1671
|
+
wat = $func.removedRaw();
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
block = ELEM_WAT4WASM(wat);
|
|
1675
|
+
if (block.isInitial) { wat = block.removedRaw(); }
|
|
1676
|
+
block = TABLE_WAT4WASM(wat);
|
|
1677
|
+
if (block.isInitial) { wat = block.removedRaw(); }
|
|
1678
|
+
block = DATA_WAT4WASM(wat);
|
|
1679
|
+
if (block.isInitial) { wat = block.removedRaw(); }
|
|
1680
|
+
if (false === helpers.hasBlock(wat, "global.get", { $name: WAT4WASM_$NAME }) &&
|
|
1681
|
+
false === helpers.hasBlock(wat, "global.set", { $name: WAT4WASM_$NAME })) {
|
|
1682
|
+
wat = GLOBAL_WAT4WASM(wat).removedRaw();
|
|
1683
|
+
}
|
|
1684
|
+
block = MEMORY_WAT4WASM(wat);
|
|
1685
|
+
if (block && (helpers.containsMemoryOperation(wat) === false)) {
|
|
1686
|
+
wat = block.removedRaw();
|
|
1687
|
+
}
|
|
1688
|
+
const maskSet = new helpers.MaskSet(wat);
|
|
1689
|
+
const imports = new Array();
|
|
1690
|
+
while (block = maskSet.lastBlockOf("import")) {
|
|
1691
|
+
block.$name = helpers.parseFirstBlock(block).$name;
|
|
1692
|
+
imports.push(block);
|
|
1693
|
+
maskSet.remove(block);
|
|
1694
|
+
}
|
|
1695
|
+
wat = maskSet.restore();
|
|
1696
|
+
wat = helpers.prepend(wat,
|
|
1697
|
+
imports
|
|
1698
|
+
.filter(b => new RegExp(`\\${b.$name}\(\\s|\\)\)`).test(wat))
|
|
1699
|
+
.sort((a, b) => helpers.generateId(a) - helpers.generateId(b))
|
|
1700
|
+
.map(b => b.toString())
|
|
1701
|
+
.join("\n")
|
|
1702
|
+
);
|
|
1703
|
+
return wat;
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
|
|
1707
|
+
async function processCLI(compileCallback, PROCESS = process) {
|
|
1708
|
+
console.log("\x1b[31m")
|
|
1709
|
+
// Argument Parsing Logic
|
|
1710
|
+
const args = PROCESS.argv.slice(2);
|
|
1711
|
+
const config = {
|
|
1712
|
+
inputFile: null,
|
|
1713
|
+
outputFile: null,
|
|
1714
|
+
wat2wasmPath: null,
|
|
1715
|
+
unlinkWat4FileIfCompilationSuccess: true,
|
|
1716
|
+
windowtagName: "",
|
|
1717
|
+
defaultTagName: "🦋",
|
|
1718
|
+
keepChromeGlobal: false,
|
|
1719
|
+
keepHTMLDocument: false,
|
|
1720
|
+
keepWindowTagName: false,
|
|
1721
|
+
keepWindowObjects: false,
|
|
1722
|
+
keepFaviconRequest: false,
|
|
1723
|
+
generateWASMFromHEXString: true,
|
|
1724
|
+
generateWASMFromNumberArray: false,
|
|
1725
|
+
consoleLogInstance: false,
|
|
1726
|
+
faviconLinkHref: "data:null",
|
|
1727
|
+
printOnly: false,
|
|
1728
|
+
passthroughArgs: [],
|
|
1729
|
+
};
|
|
1730
|
+
for (let i = 0; i < args.length; i++) {
|
|
1731
|
+
const arg = args[i];
|
|
1732
|
+
if (arg.startsWith("--output=")) {
|
|
1733
|
+
config.outputFile = arg.split("=")[1];
|
|
1734
|
+
} else if (arg.startsWith("--input=")) {
|
|
1735
|
+
config.inputFile = arg.split("=")[1];
|
|
1736
|
+
} else if (arg.startsWith("--wat2wasm=")) {
|
|
1737
|
+
config.wat2wasmPath = arg.split("=")[1];
|
|
1738
|
+
} else if (arg.startsWith("--tag=")) {
|
|
1739
|
+
config.windowtagName = arg.split("=")[1];
|
|
1740
|
+
} else if (arg === "--print-only") {
|
|
1741
|
+
config.printOnly = true;
|
|
1742
|
+
} else if (arg === "--wasm-from-numbers-array") {
|
|
1743
|
+
config.generateWASMFromNumberArray = true;
|
|
1744
|
+
config.generateWASMFromHEXString = false;
|
|
1745
|
+
} else if (arg === "--wasm-from-hex-string") {
|
|
1746
|
+
config.generateWASMFromHEXString = true;
|
|
1747
|
+
config.generateWASMFromNumberArray = false;
|
|
1748
|
+
} else if (arg === "--untouched-window") {
|
|
1749
|
+
config.keepHTMLDocument = true;
|
|
1750
|
+
config.keepChromeGlobal = true;
|
|
1751
|
+
config.keepWindowObjects = true;
|
|
1752
|
+
config.keepFaviconRequest = true;
|
|
1753
|
+
config.windowtagName &&= "";
|
|
1754
|
+
} else if (arg === "--log-instance") {
|
|
1755
|
+
config.consoleLogInstance = true;
|
|
1756
|
+
} else if (arg === "--keep-window") {
|
|
1757
|
+
config.keepWindowObjects = true;
|
|
1758
|
+
} else if (arg === "--keep-chrome-global") {
|
|
1759
|
+
config.keepChromeGlobal = true;
|
|
1760
|
+
} else if (arg === "--keep-document") {
|
|
1761
|
+
config.keepHTMLDocument = true;
|
|
1762
|
+
} else if (arg === "--keep-favicon") {
|
|
1763
|
+
config.keepFaviconRequest = true;
|
|
1764
|
+
} else if (arg === "--no-unlink") {
|
|
1765
|
+
config.unlinkWat4FileIfCompilationSuccess = false;
|
|
1766
|
+
} else if (arg.startsWith("-")) {
|
|
1767
|
+
// Collect flags to pass to wat2wasm
|
|
1768
|
+
config.passthroughArgs.push(arg);
|
|
1769
|
+
} else {
|
|
1770
|
+
if (!config.inputFile) {
|
|
1771
|
+
config.inputFile = arg;
|
|
1772
|
+
} else {
|
|
1773
|
+
console.warn(`Warning: Ignoring extra argument '${arg}'`);
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
if (!config.inputFile) {
|
|
1778
|
+
console.error("Error: Input file required! Usage: wat4wasm <input.wat> [options]");
|
|
1779
|
+
PROCESS.exit(1);
|
|
1780
|
+
}
|
|
1781
|
+
const isJSTarget = config.outputFile?.endsWith(".js");
|
|
1782
|
+
const isHTMLTarget = config.outputFile?.endsWith(".html");
|
|
1783
|
+
if (isHTMLTarget || isJSTarget) {
|
|
1784
|
+
config.outputFile = config.outputFile.substring(
|
|
1785
|
+
0, config.outputFile.lastIndexOf(".")
|
|
1786
|
+
).concat(".wat");
|
|
1787
|
+
}
|
|
1788
|
+
// Default output file if not specified (and not printing only)
|
|
1789
|
+
if (!config.outputFile && !config.printOnly) {
|
|
1790
|
+
config.outputFile = config.inputFile.replace(/\.wat$/, "-output.wat");
|
|
1791
|
+
}
|
|
1792
|
+
try {
|
|
1793
|
+
console.log(`\x1b[0m\x1b[33m🚀 Wat4Wasm: Processing ${config.inputFile}...\x1b[0m`);
|
|
1794
|
+
if (!fs.existsSync(config.inputFile)) {
|
|
1795
|
+
throw new Error(`Input file not found: ${config.inputFile}`);
|
|
1796
|
+
}
|
|
1797
|
+
const rawCode = fs.readFileSync(config.inputFile, "utf8");
|
|
1798
|
+
// 2. Call the provided compile function
|
|
1799
|
+
const compiled = await compileCallback(rawCode);
|
|
1800
|
+
console.log("\x1b[0m")
|
|
1801
|
+
// 3. Handle Output
|
|
1802
|
+
if (config.printOnly) {
|
|
1803
|
+
console.log("\x1b[0m\x1b[33m--- Compiled Output ---");
|
|
1804
|
+
console.log(compiled);
|
|
1805
|
+
console.log("-----------------------\x1b[0m");
|
|
1806
|
+
return;
|
|
1807
|
+
}
|
|
1808
|
+
// Write compiled WAT (or temp WAT for WASM target)
|
|
1809
|
+
let watFile = config.outputFile;
|
|
1810
|
+
let wasmFile = config.outputFile.substring(
|
|
1811
|
+
0, config.outputFile.lastIndexOf(".")
|
|
1812
|
+
).concat(".wasm");
|
|
1813
|
+
const isWasmTarget = isJSTarget || isHTMLTarget || config.outputFile.endsWith(".wasm") || config.wat2wasmPath;
|
|
1814
|
+
if (isWasmTarget && config.outputFile.endsWith(".wasm")) {
|
|
1815
|
+
// If target is WASM, write WAT to a temp file
|
|
1816
|
+
watFile = config.outputFile + ".wat";
|
|
1817
|
+
}
|
|
1818
|
+
fs.writeFileSync(watFile, compiled);
|
|
1819
|
+
console.log(`\x1b[0m\x1b[36m✅ WAT Written to: ${watFile}\n`);
|
|
1820
|
+
// Run wat2wasm if requested
|
|
1821
|
+
if (config.wat2wasmPath || isHTMLTarget || isJSTarget) {
|
|
1822
|
+
console.log(`\x1b[0m\x1b[32m🔨 Running wat2wasm...`);
|
|
1823
|
+
// If user explicitly set .wasm output but output file was .wat?
|
|
1824
|
+
// The logic above sets wasmFile = config.outputFile.
|
|
1825
|
+
// If config.outputFile was "out.wat", then we overwrite it with "out.wasm"?
|
|
1826
|
+
// Standard wat2wasm behavior takes -o <file>.
|
|
1827
|
+
// Our config.outputFile came from --output.
|
|
1828
|
+
const cmdArgs = [watFile, ...config.passthroughArgs, "--output", wasmFile];
|
|
1829
|
+
console.log(`\x1b[0m\x1b[32m Command: ${config.wat2wasmPath} ${cmdArgs.join(" ")}\n`);
|
|
1830
|
+
const result = spawnSync(config.wat2wasmPath, cmdArgs, { stdio: "inherit" });
|
|
1831
|
+
if (result.status === 0) {
|
|
1832
|
+
console.log(`\x1b[0m\x1b[35m🎉 WASM Build Successful: ${wasmFile}`);
|
|
1833
|
+
if (config.unlinkWat4FileIfCompilationSuccess && watFile !== wasmFile) {
|
|
1834
|
+
try { fs.unlinkSync(watFile); } catch (e) { }
|
|
1835
|
+
}
|
|
1836
|
+
if (isJSTarget || isHTMLTarget) {
|
|
1837
|
+
const wasmhex = fs.readFileSync(wasmFile, "hex");
|
|
1838
|
+
let extension = "";
|
|
1839
|
+
let filecontent = "";
|
|
1840
|
+
if (isJSTarget) {
|
|
1841
|
+
let wasmGenerator = ``;
|
|
1842
|
+
if (config.generateWASMFromHEXString && !config.generateWASMFromNumberArray) {
|
|
1843
|
+
wasmGenerator = `Uint8Array.from(/${wasmhex}/.toString().match(/[a-f0-9]{2}/g).map(h => parseInt(h, 16)))`;
|
|
1844
|
+
}
|
|
1845
|
+
else if (!config.generateWASMFromHEXString && config.generateWASMFromNumberArray) {
|
|
1846
|
+
wasmGenerator = `Uint8Array.of(${wasmhex.toString().match(/[a-f0-9]{2}/g).map(h => parseInt(h, 16))})`;
|
|
1847
|
+
}
|
|
1848
|
+
extension = "js";
|
|
1849
|
+
filecontent = `
|
|
1850
|
+
const view = ${wasmGenerator};
|
|
1851
|
+
let wasm = view.buffer;
|
|
1852
|
+
|
|
1853
|
+
wasm.module = null;
|
|
1854
|
+
wasm.instances = new Array;
|
|
1855
|
+
|
|
1856
|
+
const compile = async () => WebAssembly.compile(wasm).then(m => {
|
|
1857
|
+
wasm.module = m;
|
|
1858
|
+
return wasm;
|
|
1859
|
+
});
|
|
1860
|
+
|
|
1861
|
+
const instantiate = async () => WebAssembly.instantiate(wasm.module, self).then(i => {
|
|
1862
|
+
wasm.instances.push(i);
|
|
1863
|
+
return i;
|
|
1864
|
+
});
|
|
1865
|
+
|
|
1866
|
+
wasm.spawn = async imports => {
|
|
1867
|
+
if (wasm.module instanceof WebAssembly.Module === false) {
|
|
1868
|
+
Object.assign(self, Object(imports));
|
|
1869
|
+
return compile().then(() => instantiate());
|
|
1870
|
+
}
|
|
1871
|
+
return instantiate();
|
|
1872
|
+
};
|
|
1873
|
+
|
|
1874
|
+
default wasm;
|
|
1875
|
+
`;
|
|
1876
|
+
}
|
|
1877
|
+
else {
|
|
1878
|
+
extension = "html";
|
|
1879
|
+
const thenCalls = [];
|
|
1880
|
+
if (config.consoleLogInstance) {
|
|
1881
|
+
thenCalls.push(`console.warn(wasm)`);
|
|
1882
|
+
}
|
|
1883
|
+
if (!config.keepHTMLDocument) {
|
|
1884
|
+
thenCalls.push(`Array.from(document.children).forEach(i => i.remove())`);
|
|
1885
|
+
}
|
|
1886
|
+
if (!config.keepChromeGlobal && !config.keepWindowObjects) {
|
|
1887
|
+
thenCalls.push(`self.chrome && (self.chrome = self)`)
|
|
1888
|
+
}
|
|
1889
|
+
const windowTag = config.windowtagName || config.defaultTagName;
|
|
1890
|
+
if ((!config.keepWindowTagName && !config.keepWindowObjects) || config.windowtagName) {
|
|
1891
|
+
thenCalls.push(`Reflect.defineProperty(__proto__, Symbol.toStringTag, {value: String.fromCharCode(${windowTag.split("").map(c => c.charCodeAt())}) })`)
|
|
1892
|
+
}
|
|
1893
|
+
if (!config.keepWindowObjects) {
|
|
1894
|
+
thenCalls.push(`Reflect.ownKeys(self).forEach(Reflect.deleteProperty.bind(Reflect, self))`);
|
|
1895
|
+
}
|
|
1896
|
+
const thenCall = thenCalls.length && String(`.then(wasm => [${thenCalls}])`) || String();
|
|
1897
|
+
let wasmGenerator = ``;
|
|
1898
|
+
if (config.generateWASMFromHEXString && !config.generateWASMFromNumberArray) {
|
|
1899
|
+
wasmGenerator = `Uint8Array.from(/${wasmhex}/.toString().match(/[a-f0-9]{2}/g).map(h => parseInt(h, 16)))`;
|
|
1900
|
+
}
|
|
1901
|
+
else if (!config.generateWASMFromHEXString && config.generateWASMFromNumberArray) {
|
|
1902
|
+
wasmGenerator = `Uint8Array.of(${wasmhex.toString().match(/[a-f0-9]{2}/g).map(h => parseInt(h, 16))})`;
|
|
1903
|
+
}
|
|
1904
|
+
const onload = [
|
|
1905
|
+
`WebAssembly.instantiate(${wasmGenerator}, self)`.concat(thenCall)
|
|
1906
|
+
];
|
|
1907
|
+
if (!config.keepFaviconRequest && config.faviconLinkHref) {
|
|
1908
|
+
const faviconHTML = `<link rel=icon href=${config.faviconLinkHref}>`;
|
|
1909
|
+
onload.push(`Reflect.set(document.head, String.fromCharCode(${'innerHTML'.split("").map(c => c.charCodeAt())}), String.fromCharCode(${faviconHTML.split("").map(c => c.charCodeAt())}))`)
|
|
1910
|
+
}
|
|
1911
|
+
filecontent = `<body onload="[${onload}]"></body>`;
|
|
1912
|
+
}
|
|
1913
|
+
const filename = wasmFile
|
|
1914
|
+
.substring(0, wasmFile.lastIndexOf("."))
|
|
1915
|
+
.concat(".")
|
|
1916
|
+
.concat(extension);
|
|
1917
|
+
fs.writeFileSync(filename, filecontent);
|
|
1918
|
+
if (config.unlinkWat4FileIfCompilationSuccess) {
|
|
1919
|
+
try { fs.unlinkSync(wasmFile); } catch (e) { }
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
} else {
|
|
1923
|
+
console.error(`💥 wat2wasm failed with exit code ${result.status}`);
|
|
1924
|
+
PROCESS.exit(result.status);
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
} catch (err) {
|
|
1928
|
+
console.error(`\n💥 ERROR:`, err);
|
|
1929
|
+
PROCESS.exit(1);
|
|
1930
|
+
} finally {
|
|
1931
|
+
console.log("\x1b[0m")
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
|
|
1936
|
+
const processors = [
|
|
1937
|
+
W4W,
|
|
1938
|
+
TEXT,
|
|
1939
|
+
ASYNC,
|
|
1940
|
+
DATA,
|
|
1941
|
+
IMPORT,
|
|
1942
|
+
INCLUDE,
|
|
1943
|
+
NEW,
|
|
1944
|
+
REF_EXTERN,
|
|
1945
|
+
REF_FUNC,
|
|
1946
|
+
START,
|
|
1947
|
+
STRING,
|
|
1948
|
+
REPLACE_ALL,
|
|
1949
|
+
];
|
|
1950
|
+
processCLI(async wat4 => {
|
|
1951
|
+
let wat2 = wat4, f, i = -1, llen, m = -1, c = 0, ci = 0;
|
|
1952
|
+
while (f = processors[++i]) {
|
|
1953
|
+
wat4 = f(wat2, W4W).toString();
|
|
1954
|
+
const input = wat2;
|
|
1955
|
+
const output = wat4;
|
|
1956
|
+
const inLines = input.split("\n").map(l => l.trim()).filter(Boolean);
|
|
1957
|
+
const outLines = output.split("\n").map(l => l.trim()).filter(Boolean);
|
|
1958
|
+
let startCommon = 0;
|
|
1959
|
+
while (
|
|
1960
|
+
startCommon < inLines.length &&
|
|
1961
|
+
startCommon < outLines.length &&
|
|
1962
|
+
inLines[startCommon] === outLines[startCommon]
|
|
1963
|
+
) { startCommon++; }
|
|
1964
|
+
let endCommon = 0;
|
|
1965
|
+
while (
|
|
1966
|
+
endCommon < inLines.length - startCommon &&
|
|
1967
|
+
endCommon < outLines.length - startCommon &&
|
|
1968
|
+
inLines[inLines.length - 1 - endCommon] === outLines[outLines.length - 1 - endCommon]
|
|
1969
|
+
) { endCommon++; }
|
|
1970
|
+
const commonLines = startCommon + endCommon;
|
|
1971
|
+
const removedLines = inLines.length - commonLines;
|
|
1972
|
+
const addedLines = outLines.length - commonLines;
|
|
1973
|
+
const netChange = outLines.length - inLines.length;
|
|
1974
|
+
const stat = [
|
|
1975
|
+
`\x1b[32m+${addedLines}\x1b[0m`.padStart(15, " "),
|
|
1976
|
+
`\x1b[34m-${removedLines}\x1b[0m`.padStart(15, " "),
|
|
1977
|
+
`\x1b[${netChange && 36 || 33}m\u0394\x1b[0m`.padStart(12, " "),
|
|
1978
|
+
`\x1b[${netChange && 36 || 33}m${netChange}\x1b[0m`.padStart(12, " "),
|
|
1979
|
+
` byte(\u03B4) :`,
|
|
1980
|
+
`\x1b[33m${wat2.length}\x1b[0m`.padStart(14, " "),
|
|
1981
|
+
`-->`,
|
|
1982
|
+
`\x1b[33m${wat4.length}\x1b[0m`,
|
|
1983
|
+
];
|
|
1984
|
+
if (wat2 !== wat4) {
|
|
1985
|
+
c++;
|
|
1986
|
+
wat2 = wat4;
|
|
1987
|
+
console.log(`👀 ƒ( ${f.name.padEnd(12, " ")} )`.padStart(12, " "), ...stat);
|
|
1988
|
+
}
|
|
1989
|
+
if (!processors[i + 1]) {
|
|
1990
|
+
if (c) { i = -1; c = 0; }
|
|
1991
|
+
else { console.log("☘️ untouched raw \x1b[32m-->\x1b[0m finalizing..") }
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
wat2 = clean(wat2);
|
|
1995
|
+
wat2 = wat4beauty(wat2, " ");
|
|
1996
|
+
return wat2;
|
|
1997
|
+
});
|
|
1998
|
+
|