blob: 5c5df798fbc9126c780a50a2a26eb9f37a82f4f6 [file] [log] [blame]
Ben Murdochda12d292016-06-02 14:46:10 +01001// Copyright 2016 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Ben Murdoch61f157c2016-09-16 13:49:30 +01005class Binary extends Array {
6 emit_u8(val) {
7 this.push(val);
8 }
Ben Murdochda12d292016-06-02 14:46:10 +01009
Ben Murdoch61f157c2016-09-16 13:49:30 +010010 emit_u16(val) {
11 this.push(val & 0xff);
12 this.push((val >> 8) & 0xff);
13 }
Ben Murdochda12d292016-06-02 14:46:10 +010014
Ben Murdoch61f157c2016-09-16 13:49:30 +010015 emit_u32(val) {
16 this.push(val & 0xff);
17 this.push((val >> 8) & 0xff);
18 this.push((val >> 16) & 0xff);
19 this.push((val >> 24) & 0xff);
20 }
Ben Murdochda12d292016-06-02 14:46:10 +010021
Ben Murdoch61f157c2016-09-16 13:49:30 +010022 emit_varint(val) {
23 while (true) {
24 let v = val & 0xff;
25 val = val >>> 7;
26 if (val == 0) {
27 this.push(v);
28 break;
29 }
30 this.push(v | 0x80);
31 }
32 }
Ben Murdochda12d292016-06-02 14:46:10 +010033
Ben Murdoch61f157c2016-09-16 13:49:30 +010034 emit_bytes(data) {
35 for (let i = 0; i < data.length; i++) {
36 this.push(data[i] & 0xff);
37 }
38 }
Ben Murdochda12d292016-06-02 14:46:10 +010039
Ben Murdoch61f157c2016-09-16 13:49:30 +010040 emit_string(string) {
Ben Murdochc5610432016-08-08 18:44:38 +010041 // When testing illegal names, we pass a byte array directly.
42 if (string instanceof Array) {
Ben Murdoch61f157c2016-09-16 13:49:30 +010043 this.emit_varint(string.length);
44 this.emit_bytes(string);
Ben Murdochc5610432016-08-08 18:44:38 +010045 return;
46 }
47
Ben Murdoch61f157c2016-09-16 13:49:30 +010048 // This is the hacky way to convert a JavaScript string to a UTF8 encoded
Ben Murdochc5610432016-08-08 18:44:38 +010049 // string only containing single-byte characters.
Ben Murdoch61f157c2016-09-16 13:49:30 +010050 let string_utf8 = unescape(encodeURIComponent(string));
51 this.emit_varint(string_utf8.length);
52 for (let i = 0; i < string_utf8.length; i++) {
53 this.emit_u8(string_utf8.charCodeAt(i));
Ben Murdochda12d292016-06-02 14:46:10 +010054 }
Ben Murdoch61f157c2016-09-16 13:49:30 +010055 }
Ben Murdochda12d292016-06-02 14:46:10 +010056
Ben Murdoch61f157c2016-09-16 13:49:30 +010057 emit_header() {
58 this.push(kWasmH0, kWasmH1, kWasmH2, kWasmH3,
59 kWasmV0, kWasmV1, kWasmV2, kWasmV3);
60 }
Ben Murdochda12d292016-06-02 14:46:10 +010061
Ben Murdoch61f157c2016-09-16 13:49:30 +010062 emit_section(section_code, content_generator) {
63 // Emit section name.
64 this.emit_string(section_names[section_code]);
65 // Emit the section to a temporary buffer: its full length isn't know yet.
66 let section = new Binary;
67 content_generator(section);
68 // Emit section length.
69 this.emit_varint(section.length);
70 // Copy the temporary buffer.
71 this.push(...section);
Ben Murdochda12d292016-06-02 14:46:10 +010072 }
73}
74
Ben Murdoch61f157c2016-09-16 13:49:30 +010075class WasmFunctionBuilder {
76 constructor(name, type_index) {
77 this.name = name;
78 this.type_index = type_index;
79 this.exports = [];
80 }
81
82 exportAs(name) {
83 this.exports.push(name);
84 return this;
85 }
86
87 exportFunc() {
88 this.exports.push(this.name);
89 return this;
90 }
91
92 addBody(body) {
93 this.body = body;
94 return this;
95 }
96
97 addLocals(locals) {
98 this.locals = locals;
99 return this;
100 }
Ben Murdochda12d292016-06-02 14:46:10 +0100101}
102
Ben Murdoch61f157c2016-09-16 13:49:30 +0100103class WasmModuleBuilder {
104 constructor() {
105 this.types = [];
106 this.imports = [];
107 this.functions = [];
108 this.exports = [];
109 this.table = [];
110 this.segments = [];
111 this.explicit = [];
112 this.pad = null;
113 return this;
114 }
Ben Murdochda12d292016-06-02 14:46:10 +0100115
Ben Murdoch61f157c2016-09-16 13:49:30 +0100116 addStart(start_index) {
117 this.start_index = start_index;
118 }
Ben Murdochda12d292016-06-02 14:46:10 +0100119
Ben Murdoch61f157c2016-09-16 13:49:30 +0100120 addMemory(min, max, exp) {
121 this.memory = {min: min, max: max, exp: exp};
122 return this;
123 }
124
125 addPadFunctionTable(size) {
126 this.pad = size;
127 return this;
128 }
129
130 addExplicitSection(bytes) {
131 this.explicit.push(bytes);
132 return this;
133 }
134
135 addType(type) {
136 // TODO: canonicalize types?
137 this.types.push(type);
138 return this.types.length - 1;
139 }
140
141 addFunction(name, type) {
142 let type_index = (typeof type) == "number" ? type : this.addType(type);
143 let func = new WasmFunctionBuilder(name, type_index);
144 func.index = this.functions.length;
145 this.functions.push(func);
146 return func;
147 }
148
149 addImportWithModule(module, name, type) {
150 let type_index = (typeof type) == "number" ? type : this.addType(type);
151 this.imports.push({module: module, name: name, type: type_index});
152 return this.imports.length - 1;
153 }
154
155 addImport(name, type) {
156 return this.addImportWithModule(name, undefined, type);
157 }
158
159 addDataSegment(addr, data, init) {
160 this.segments.push({addr: addr, data: data, init: init});
161 return this.segments.length - 1;
162 }
163
164 appendToTable(array) {
165 this.table.push(...array);
166 return this;
167 }
168
169 toArray(debug) {
170 let binary = new Binary;
171 let wasm = this;
172
173 // Add header
174 binary.emit_header();
175
176 // Add type section
177 if (wasm.types.length > 0) {
178 if (debug) print("emitting types @ " + binary.length);
179 binary.emit_section(kDeclTypes, section => {
180 section.emit_varint(wasm.types.length);
181 for (let type of wasm.types) {
182 section.emit_u8(kWasmFunctionTypeForm);
183 section.emit_varint(type.params.length);
184 for (let param of type.params) {
185 section.emit_u8(param);
186 }
187 section.emit_varint(type.results.length);
188 for (let result of type.results) {
189 section.emit_u8(result);
190 }
191 }
192 });
Ben Murdochda12d292016-06-02 14:46:10 +0100193 }
194
195 // Add imports section
196 if (wasm.imports.length > 0) {
Ben Murdoch61f157c2016-09-16 13:49:30 +0100197 if (debug) print("emitting imports @ " + binary.length);
198 binary.emit_section(kDeclImports, section => {
199 section.emit_varint(wasm.imports.length);
200 for (let imp of wasm.imports) {
201 section.emit_varint(imp.type);
202 section.emit_string(imp.module);
203 section.emit_string(imp.name || '');
204 }
205 });
Ben Murdochda12d292016-06-02 14:46:10 +0100206 }
207
Ben Murdochc5610432016-08-08 18:44:38 +0100208 // Add functions declarations
Ben Murdoch61f157c2016-09-16 13:49:30 +0100209 let has_names = false;
210 let names = false;
211 let exports = 0;
Ben Murdochda12d292016-06-02 14:46:10 +0100212 if (wasm.functions.length > 0) {
Ben Murdoch61f157c2016-09-16 13:49:30 +0100213 if (debug) print("emitting function decls @ " + binary.length);
214 binary.emit_section(kDeclFunctions, section => {
215 section.emit_varint(wasm.functions.length);
216 for (let func of wasm.functions) {
217 has_names = has_names || (func.name != undefined &&
218 func.name.length > 0);
219 exports += func.exports.length;
220 section.emit_varint(func.type_index);
221 }
222 });
Ben Murdochc5610432016-08-08 18:44:38 +0100223 }
224
Ben Murdoch61f157c2016-09-16 13:49:30 +0100225 // Add table.
226 if (wasm.table.length > 0) {
227 if (debug) print("emitting table @ " + binary.length);
228 binary.emit_section(kDeclTable, section => {
229 section.emit_varint(wasm.table.length);
230 for (let index of wasm.table) {
231 section.emit_varint(index);
232 }
233 });
Ben Murdochc5610432016-08-08 18:44:38 +0100234 }
235
236 // Add memory section
237 if (wasm.memory != undefined) {
Ben Murdoch61f157c2016-09-16 13:49:30 +0100238 if (debug) print("emitting memory @ " + binary.length);
239 binary.emit_section(kDeclMemory, section => {
240 section.emit_varint(wasm.memory.min);
241 section.emit_varint(wasm.memory.max);
242 section.emit_u8(wasm.memory.exp ? 1 : 0);
243 });
Ben Murdochc5610432016-08-08 18:44:38 +0100244 }
245
246
247 // Add export table.
248 if (exports > 0) {
Ben Murdoch61f157c2016-09-16 13:49:30 +0100249 if (debug) print("emitting exports @ " + binary.length);
250 binary.emit_section(kDeclExports, section => {
251 section.emit_varint(exports);
252 for (let func of wasm.functions) {
253 for (let exp of func.exports) {
254 section.emit_varint(func.index);
255 section.emit_string(exp);
256 }
257 }
258 });
Ben Murdochc5610432016-08-08 18:44:38 +0100259 }
260
261 // Add start function section.
262 if (wasm.start_index != undefined) {
Ben Murdoch61f157c2016-09-16 13:49:30 +0100263 if (debug) print("emitting start function @ " + binary.length);
264 binary.emit_section(kDeclStart, section => {
265 section.emit_varint(wasm.start_index);
266 });
Ben Murdochc5610432016-08-08 18:44:38 +0100267 }
268
269 // Add function bodies.
270 if (wasm.functions.length > 0) {
Ben Murdoch61f157c2016-09-16 13:49:30 +0100271 // emit function bodies
272 if (debug) print("emitting code @ " + binary.length);
273 binary.emit_section(kDeclCode, section => {
274 section.emit_varint(wasm.functions.length);
275 for (let func of wasm.functions) {
276 // Function body length will be patched later.
277 let local_decls = [];
278 let l = func.locals;
279 if (l != undefined) {
280 let local_decls_count = 0;
281 if (l.i32_count > 0) {
282 local_decls.push({count: l.i32_count, type: kAstI32});
Ben Murdochda12d292016-06-02 14:46:10 +0100283 }
Ben Murdoch61f157c2016-09-16 13:49:30 +0100284 if (l.i64_count > 0) {
285 local_decls.push({count: l.i64_count, type: kAstI64});
286 }
287 if (l.f32_count > 0) {
288 local_decls.push({count: l.f32_count, type: kAstF32});
289 }
290 if (l.f64_count > 0) {
291 local_decls.push({count: l.f64_count, type: kAstF64});
292 }
293 }
294
295 let header = new Binary;
296 header.emit_varint(local_decls.length);
297 for (let decl of local_decls) {
298 header.emit_varint(decl.count);
299 header.emit_u8(decl.type);
300 }
301
302 section.emit_varint(header.length + func.body.length);
303 section.emit_bytes(header);
304 section.emit_bytes(func.body);
305 }
306 });
Ben Murdochda12d292016-06-02 14:46:10 +0100307 }
308
Ben Murdochc5610432016-08-08 18:44:38 +0100309 // Add data segments.
Ben Murdoch61f157c2016-09-16 13:49:30 +0100310 if (wasm.segments.length > 0) {
311 if (debug) print("emitting data segments @ " + binary.length);
312 binary.emit_section(kDeclData, section => {
313 section.emit_varint(wasm.segments.length);
314 for (let seg of wasm.segments) {
315 section.emit_varint(seg.addr);
316 section.emit_varint(seg.data.length);
317 section.emit_bytes(seg.data);
318 }
319 });
Ben Murdochda12d292016-06-02 14:46:10 +0100320 }
321
Ben Murdochc5610432016-08-08 18:44:38 +0100322 // Add any explicitly added sections
Ben Murdoch61f157c2016-09-16 13:49:30 +0100323 for (let exp of wasm.explicit) {
324 if (debug) print("emitting explicit @ " + binary.length);
325 binary.emit_bytes(exp);
Ben Murdochda12d292016-06-02 14:46:10 +0100326 }
327
Ben Murdochc5610432016-08-08 18:44:38 +0100328 // Add function names.
329 if (has_names) {
Ben Murdoch61f157c2016-09-16 13:49:30 +0100330 if (debug) print("emitting names @ " + binary.length);
331 binary.emit_section(kDeclNames, section => {
332 section.emit_varint(wasm.functions.length);
333 for (let func of wasm.functions) {
334 var name = func.name == undefined ? "" : func.name;
335 section.emit_string(name);
336 section.emit_u8(0); // local names count == 0
337 }
338 });
339 }
340
341 // Add an indirect function table pad section.
342 if (wasm.pad !== null) {
343 if (debug)
344 print("emitting indirect function table pad @ " + binary.length);
345 binary.emit_section(kDeclFunctionTablePad, section => {
346 section.emit_varint(wasm.pad);
347 });
Ben Murdochc5610432016-08-08 18:44:38 +0100348 }
349
Ben Murdochda12d292016-06-02 14:46:10 +0100350 // End the module.
Ben Murdoch61f157c2016-09-16 13:49:30 +0100351 if (debug) print("emitting end @ " + binary.length);
352 binary.emit_section(kDeclEnd, section => {});
Ben Murdochda12d292016-06-02 14:46:10 +0100353
Ben Murdoch61f157c2016-09-16 13:49:30 +0100354 return binary;
355 }
Ben Murdochda12d292016-06-02 14:46:10 +0100356
Ben Murdoch61f157c2016-09-16 13:49:30 +0100357 toBuffer(debug) {
358 let bytes = this.toArray(debug);
359 let buffer = new ArrayBuffer(bytes.length);
360 let view = new Uint8Array(buffer);
361 for (let i = 0; i < bytes.length; i++) {
362 let val = bytes[i];
363 if ((typeof val) == "string") val = val.charCodeAt(0);
364 view[i] = val | 0;
Ben Murdochda12d292016-06-02 14:46:10 +0100365 }
366 return buffer;
Ben Murdoch61f157c2016-09-16 13:49:30 +0100367 }
Ben Murdochda12d292016-06-02 14:46:10 +0100368
Ben Murdoch61f157c2016-09-16 13:49:30 +0100369 instantiate(...args) {
370 let module = new WebAssembly.Module(this.toBuffer());
371 let instance = new WebAssembly.Instance(module, ...args);
372 return instance;
373 }
Ben Murdochda12d292016-06-02 14:46:10 +0100374}