| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267 |
21x
21x
21x
21x
21x
21x
21x
21x
21x
21x
21x
21x
21x
21x
21x
21x
21x
21x
159x
159x
159x
159x
159x
159x
104x
159x
158x
76x
76x
76x
10x
76x
76x
8x
76x
159x
159x
241x
35x
241x
241x
76x
4x
76x
76x
78x
50x
50x
27x
27x
50x
50x
50x
22x
90x
90x
3x
87x
2x
2x
76x
50x
128x
128x
12x
12x
12x
9x
9x
3x
3x
131x
131x
27x
22x
159x
159x
159x
106x
106x
53x
159x
4x
4x
4x
4x
3x
3x
4x
159x
159x
159x
159x
158x
2x
158x
90x
90x
90x
50x
21x
76x
75x
75x
| // @flow
import invariant from 'invariant';
import Syntax from 'walt-syntax';
import walkNode from 'walt-parser-tools/walk-node';
import { mapNode } from 'walt-parser-tools/map-node';
import mapSyntax from './map-syntax';
import mergeBlock from './merge-block';
import generateElement from './element';
import generateExport from './export';
import generateMemory from './memory';
import generateTable from './table';
import generateInitializer from '../generator/initializer';
import generateImport from './import';
import generateType from './type';
import generateData from './generate-data';
import { generateValueType } from './utils';
import { generateImplicitFunctionType } from './type';
import {
GLOBAL_INDEX,
FUNCTION_INDEX,
FUNCTION_METADATA,
TYPE_INDEX,
AST_METADATA,
} from '../semantics/metadata';
import type { NodeType, GeneratorOptions } from '../flow/types';
import type {
ProgramType,
IntermediateOpcodeType,
IntermediateVariableType,
} from './flow/types';
const DATA_SECTION_HEADER_SIZE = 4;
export const generateCode = (
func: NodeType
): { code: IntermediateOpcodeType[], locals: IntermediateVariableType[] } => {
// eslint-disable-next-line
const [argsNode, resultNode, ...body] = func.params;
const metadata = func.meta[FUNCTION_METADATA];
invariant(body, 'Cannot generate code for function without body');
invariant(metadata, 'Cannot generate code for function without metadata');
const { locals, argumentsCount } = metadata;
const block = {
code: [],
// On this Episode of ECMAScript Spec: Object own keys traversal!
// Sometimes it pays to know the spec. Keys are traversed in the order
// they are added to the object. This includes Object.keys. Because the AST is traversed
// depth-first we can guarantee that arguments will also be added first
// to the locals object. We can depend on the spec providing the keys,
// such that we can slice away the number of arguments and get DECLARED locals _only_.
locals: Object.keys(locals)
.slice(argumentsCount)
.map(key => generateValueType(locals[key])),
debug: `Function ${func.value}`,
};
block.code = body.map(mapSyntax(block)).reduce(mergeBlock, []);
return block;
};
function generator(ast: NodeType, config: GeneratorOptions): ProgramType {
const program: ProgramType = {
Version: config.version,
Types: [],
Start: [],
Element: [],
Code: [],
Exports: [],
Imports: [],
Globals: [],
Functions: [],
Memory: [],
Table: [],
Artifacts: [],
Data: [],
Name: {
module: config.filename,
functions: [],
locals: [],
},
};
let { statics } = ast.meta[AST_METADATA];
if (config.linker != null) {
statics = {
...config.linker.statics,
...statics,
};
}
const { map: staticsMap, data } = generateData(
statics,
DATA_SECTION_HEADER_SIZE
);
if (Object.keys(statics).length > 0) {
program.Data = data;
}
const findTypeIndex = (functionNode: NodeType): number => {
const search = generateImplicitFunctionType(functionNode);
return program.Types.findIndex(t => {
const paramsMatch =
t.params.length === search.params.length &&
t.params.reduce((a, v, i) => a && v === search.params[i], true);
const resultMatch = t.result === search.result;
return paramsMatch && resultMatch;
});
};
const findTableIndex = functionIndex =>
program.Element.findIndex(n => n.functionIndex === functionIndex);
const typeMap = {};
const astWithTypes = mapNode({
[Syntax.Typedef]: (node, _ignore) => {
let typeIndex = program.Types.findIndex(({ id }) => id === node.value);
let typeNode = program.Types[typeIndex];
if (typeNode == null) {
typeIndex = program.Types.length;
program.Types.push(generateType(node));
}
typeNode = {
...node,
meta: { ...node.meta, [TYPE_INDEX]: typeIndex },
};
typeMap[node.value] = { typeIndex, typeNode };
return typeNode;
},
})(
mapNode({
[Syntax.Import]: (node, _) => node,
[Syntax.StringLiteral]: (node, _ignore) => {
const { value } = node;
// Don't replace any statics which are not mapped. For example table
// definitions have StringLiterals, but these literals do not get converted.
if (staticsMap[value] == null) {
return node;
}
return {
...node,
value: String(staticsMap[value]),
Type: Syntax.Constant,
};
},
[Syntax.StaticValueList]: node => {
const { value } = node;
return {
...node,
value: String(staticsMap[value]),
Type: Syntax.Constant,
};
},
})(ast)
);
const nodeMap = {
[Syntax.Typedef]: (_, __) => _,
[Syntax.Export]: node => {
const [nodeToExport] = node.params;
program.Exports.push(generateExport(nodeToExport));
},
[Syntax.ImmutableDeclaration]: node => {
const globalMeta = node.meta[GLOBAL_INDEX];
Eif (globalMeta != null) {
switch (node.type) {
case 'Memory':
program.Memory.push(generateMemory(node));
break;
case 'Table':
program.Table.push(generateTable(node));
break;
}
}
},
[Syntax.Declaration]: node => {
const globalMeta = node.meta[GLOBAL_INDEX];
if (globalMeta != null) {
program.Globals.push(generateInitializer(node));
}
},
[Syntax.Import]: node => {
program.Imports.push(...generateImport(node));
},
[Syntax.FunctionDeclaration]: node => {
const typeIndex = (() => {
const index = findTypeIndex(node);
if (index === -1) {
// attach to a type index
program.Types.push(generateImplicitFunctionType(node));
return program.Types.length - 1;
}
return index;
})();
const patched = mapNode({
FunctionPointer(pointer) {
const metaFunctionIndex = pointer.meta[FUNCTION_INDEX];
const functionIndex = metaFunctionIndex;
let tableIndex = findTableIndex(functionIndex);
if (tableIndex < 0) {
tableIndex = program.Element.length;
program.Element.push(generateElement(functionIndex));
}
return pointer;
},
})(node);
// Quick fix for shifting around function indices. These don't necessarily
// get written in the order they appear in the source code.
const index = node.meta[FUNCTION_INDEX];
invariant(index != null, 'Function index must be set');
program.Functions[index] = typeIndex;
// We will need to filter out the empty slots later
program.Code[index] = generateCode(patched);
if (patched.value === 'start') {
program.Start.push(index);
}
if (config.encodeNames) {
program.Name.functions.push({
index,
name: node.value,
});
const functionMetadata = node.meta[FUNCTION_METADATA];
if (
functionMetadata != null &&
Object.keys(functionMetadata.locals).length
) {
program.Name.locals[index] = {
index,
locals: Object.entries(functionMetadata.locals).map(
([name, local]: [string, any]) => {
return {
name,
index: Number(local.meta['local/index']),
};
}
),
};
}
}
},
};
walkNode(nodeMap)(astWithTypes);
// Unlike function indexes we need function bodies to be exact
program.Code = program.Code.filter(Boolean);
return program;
}
export default generator;
|