1
|
// TODO(sven): add flow in here
|
2
|
|
3
|
import { isSignature, isNumberLiteral } from "@webassemblyjs/ast";
|
4
|
import { assert } from "mamacro";
|
5
|
|
6
|
export function moduleContextFromModuleAST(m) {
|
7
|
const moduleContext = new ModuleContext();
|
8
|
|
9
|
assert(m.type === "Module");
|
10
|
|
11
|
m.fields.forEach(field => {
|
12
|
switch (field.type) {
|
13
|
case "Start": {
|
14
|
moduleContext.setStart(field.index);
|
15
|
break;
|
16
|
}
|
17
|
case "TypeInstruction": {
|
18
|
moduleContext.addType(field);
|
19
|
break;
|
20
|
}
|
21
|
case "Func": {
|
22
|
moduleContext.addFunction(field);
|
23
|
break;
|
24
|
}
|
25
|
case "Global": {
|
26
|
moduleContext.defineGlobal(field);
|
27
|
break;
|
28
|
}
|
29
|
case "ModuleImport": {
|
30
|
switch (field.descr.type) {
|
31
|
case "GlobalType": {
|
32
|
moduleContext.importGlobal(
|
33
|
field.descr.valtype,
|
34
|
field.descr.mutability
|
35
|
);
|
36
|
break;
|
37
|
}
|
38
|
case "Memory": {
|
39
|
moduleContext.addMemory(
|
40
|
field.descr.limits.min,
|
41
|
field.descr.limits.max
|
42
|
);
|
43
|
break;
|
44
|
}
|
45
|
case "FuncImportDescr": {
|
46
|
moduleContext.importFunction(field.descr);
|
47
|
break;
|
48
|
}
|
49
|
|
50
|
case "Table": {
|
51
|
// FIXME(sven): not implemented yet
|
52
|
break;
|
53
|
}
|
54
|
|
55
|
default:
|
56
|
throw new Error(
|
57
|
"Unsupported ModuleImport of type " +
|
58
|
JSON.stringify(field.descr.type)
|
59
|
);
|
60
|
}
|
61
|
break;
|
62
|
}
|
63
|
case "Memory": {
|
64
|
moduleContext.addMemory(field.limits.min, field.limits.max);
|
65
|
break;
|
66
|
}
|
67
|
}
|
68
|
});
|
69
|
|
70
|
return moduleContext;
|
71
|
}
|
72
|
|
73
|
/**
|
74
|
* Module context for type checking
|
75
|
*/
|
76
|
export class ModuleContext {
|
77
|
constructor() {
|
78
|
this.funcs = [];
|
79
|
this.funcsOffsetByIdentifier = [];
|
80
|
|
81
|
this.types = [];
|
82
|
|
83
|
this.globals = [];
|
84
|
this.globalsOffsetByIdentifier = [];
|
85
|
|
86
|
this.mems = [];
|
87
|
|
88
|
// Current stack frame
|
89
|
this.locals = [];
|
90
|
this.labels = [];
|
91
|
this.return = [];
|
92
|
|
93
|
this.debugName = "unknown";
|
94
|
|
95
|
this.start = null;
|
96
|
}
|
97
|
|
98
|
/**
|
99
|
* Set start segment
|
100
|
*/
|
101
|
setStart(index) {
|
102
|
this.start = index.value;
|
103
|
}
|
104
|
|
105
|
/**
|
106
|
* Get start function
|
107
|
*/
|
108
|
getStart() {
|
109
|
return this.start;
|
110
|
}
|
111
|
|
112
|
/**
|
113
|
* Reset the active stack frame
|
114
|
*/
|
115
|
newContext(debugName, expectedResult) {
|
116
|
this.locals = [];
|
117
|
this.labels = [expectedResult];
|
118
|
this.return = expectedResult;
|
119
|
this.debugName = debugName;
|
120
|
}
|
121
|
|
122
|
/**
|
123
|
* Functions
|
124
|
*/
|
125
|
addFunction(func /*: Func*/) {
|
126
|
// eslint-disable-next-line prefer-const
|
127
|
let { params: args = [], results: result = [] } = func.signature || {};
|
128
|
|
129
|
args = args.map(arg => arg.valtype);
|
130
|
|
131
|
this.funcs.push({ args, result });
|
132
|
|
133
|
if (typeof func.name !== "undefined") {
|
134
|
this.funcsOffsetByIdentifier[func.name.value] = this.funcs.length - 1;
|
135
|
}
|
136
|
}
|
137
|
|
138
|
importFunction(funcimport) {
|
139
|
if (isSignature(funcimport.signature)) {
|
140
|
// eslint-disable-next-line prefer-const
|
141
|
let { params: args, results: result } = funcimport.signature;
|
142
|
args = args.map(arg => arg.valtype);
|
143
|
|
144
|
this.funcs.push({ args, result });
|
145
|
} else {
|
146
|
assert(isNumberLiteral(funcimport.signature));
|
147
|
|
148
|
const typeId = funcimport.signature.value;
|
149
|
assert(this.hasType(typeId));
|
150
|
|
151
|
const signature = this.getType(typeId);
|
152
|
this.funcs.push({
|
153
|
args: signature.params.map(arg => arg.valtype),
|
154
|
result: signature.results
|
155
|
});
|
156
|
}
|
157
|
|
158
|
if (typeof funcimport.id !== "undefined") {
|
159
|
// imports are first, we can assume their index in the array
|
160
|
this.funcsOffsetByIdentifier[funcimport.id.value] = this.funcs.length - 1;
|
161
|
}
|
162
|
}
|
163
|
|
164
|
hasFunction(index) {
|
165
|
return typeof this.getFunction(index) !== "undefined";
|
166
|
}
|
167
|
|
168
|
getFunction(index) {
|
169
|
if (typeof index !== "number") {
|
170
|
throw new Error("getFunction only supported for number index");
|
171
|
}
|
172
|
|
173
|
return this.funcs[index];
|
174
|
}
|
175
|
|
176
|
getFunctionOffsetByIdentifier(name) {
|
177
|
assert(typeof name === "string");
|
178
|
|
179
|
return this.funcsOffsetByIdentifier[name];
|
180
|
}
|
181
|
|
182
|
/**
|
183
|
* Labels
|
184
|
*/
|
185
|
addLabel(result) {
|
186
|
this.labels.unshift(result);
|
187
|
}
|
188
|
|
189
|
hasLabel(index) {
|
190
|
return this.labels.length > index && index >= 0;
|
191
|
}
|
192
|
|
193
|
getLabel(index) {
|
194
|
return this.labels[index];
|
195
|
}
|
196
|
|
197
|
popLabel() {
|
198
|
this.labels.shift();
|
199
|
}
|
200
|
|
201
|
/**
|
202
|
* Locals
|
203
|
*/
|
204
|
hasLocal(index) {
|
205
|
return typeof this.getLocal(index) !== "undefined";
|
206
|
}
|
207
|
|
208
|
getLocal(index) {
|
209
|
return this.locals[index];
|
210
|
}
|
211
|
|
212
|
addLocal(type) {
|
213
|
this.locals.push(type);
|
214
|
}
|
215
|
|
216
|
/**
|
217
|
* Types
|
218
|
*/
|
219
|
addType(type) {
|
220
|
assert(type.functype.type === "Signature");
|
221
|
this.types.push(type.functype);
|
222
|
}
|
223
|
|
224
|
hasType(index) {
|
225
|
return this.types[index] !== undefined;
|
226
|
}
|
227
|
|
228
|
getType(index) {
|
229
|
return this.types[index];
|
230
|
}
|
231
|
|
232
|
/**
|
233
|
* Globals
|
234
|
*/
|
235
|
hasGlobal(index) {
|
236
|
return this.globals.length > index && index >= 0;
|
237
|
}
|
238
|
|
239
|
getGlobal(index) {
|
240
|
return this.globals[index].type;
|
241
|
}
|
242
|
|
243
|
getGlobalOffsetByIdentifier(name) {
|
244
|
assert(typeof name === "string");
|
245
|
|
246
|
return this.globalsOffsetByIdentifier[name];
|
247
|
}
|
248
|
|
249
|
defineGlobal(global /*: Global*/) {
|
250
|
const type = global.globalType.valtype;
|
251
|
const mutability = global.globalType.mutability;
|
252
|
|
253
|
this.globals.push({ type, mutability });
|
254
|
|
255
|
if (typeof global.name !== "undefined") {
|
256
|
this.globalsOffsetByIdentifier[global.name.value] =
|
257
|
this.globals.length - 1;
|
258
|
}
|
259
|
}
|
260
|
|
261
|
importGlobal(type, mutability) {
|
262
|
this.globals.push({ type, mutability });
|
263
|
}
|
264
|
|
265
|
isMutableGlobal(index) {
|
266
|
return this.globals[index].mutability === "var";
|
267
|
}
|
268
|
|
269
|
isImmutableGlobal(index) {
|
270
|
return this.globals[index].mutability === "const";
|
271
|
}
|
272
|
|
273
|
/**
|
274
|
* Memories
|
275
|
*/
|
276
|
hasMemory(index) {
|
277
|
return this.mems.length > index && index >= 0;
|
278
|
}
|
279
|
|
280
|
addMemory(min, max) {
|
281
|
this.mems.push({ min, max });
|
282
|
}
|
283
|
|
284
|
getMemory(index) {
|
285
|
return this.mems[index];
|
286
|
}
|
287
|
}
|