OLD | NEW |
---|---|
1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 function WasmFunctionBuilder(name, sig_index) { | 5 class Binary extends Array { |
bradnelson
2016/06/21 19:05:00
Nice pithy name :-)
| |
6 emit_u8(val) { | |
7 this.push(val); | |
8 } | |
9 | |
10 emit_u16(val) { | |
11 this.push(val & 0xff); | |
12 this.push((val >> 8) & 0xff); | |
13 } | |
14 | |
15 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 } | |
21 | |
22 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 } | |
33 | |
34 emit_bytes(data) { | |
35 for (let i = 0; i < data.length; i++) { | |
36 this.push(data[i] & 0xff); | |
37 } | |
38 } | |
39 | |
40 emit_string(string) { | |
41 // When testing illegal names, we pass a byte array directly. | |
42 if (string instanceof Array) { | |
43 this.emit_varint(string.length); | |
44 this.emit_bytes(string); | |
45 return; | |
46 } | |
47 | |
48 // This is the hacky way to convert a JavaScript string to a UTF8 encoded | |
49 // string only containing single-byte characters. | |
50 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)); | |
54 } | |
55 } | |
56 | |
57 emit_header() { | |
58 this.push(kWasmH0, kWasmH1, kWasmH2, kWasmH3, | |
59 kWasmV0, kWasmV1, kWasmV2, kWasmV3); | |
60 } | |
61 | |
62 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); | |
72 } | |
73 } | |
74 | |
75 class WasmFunctionBuilder { | |
76 constructor(name, type_index) { | |
6 this.name = name; | 77 this.name = name; |
7 this.sig_index = sig_index; | 78 this.type_index = type_index; |
8 this.exports = []; | 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 } | |
9 } | 101 } |
10 | 102 |
11 WasmFunctionBuilder.prototype.exportAs = function(name) { | 103 class WasmModuleBuilder { |
12 this.exports.push(name); | 104 constructor() { |
13 return this; | 105 this.types = []; |
14 } | |
15 | |
16 WasmFunctionBuilder.prototype.exportFunc = function() { | |
17 this.exports.push(this.name); | |
18 return this; | |
19 } | |
20 | |
21 WasmFunctionBuilder.prototype.addBody = function(body) { | |
22 this.body = body; | |
23 return this; | |
24 } | |
25 | |
26 WasmFunctionBuilder.prototype.addLocals = function(locals) { | |
27 this.locals = locals; | |
28 return this; | |
29 } | |
30 | |
31 function WasmModuleBuilder() { | |
32 this.signatures = []; | |
33 this.imports = []; | 106 this.imports = []; |
34 this.functions = []; | 107 this.functions = []; |
35 this.exports = []; | 108 this.exports = []; |
36 this.function_table = []; | 109 this.table = []; |
37 this.data_segments = []; | 110 this.segments = []; |
38 this.explicit = []; | 111 this.explicit = []; |
39 return this; | 112 } |
40 } | 113 |
41 | 114 addStart(start_index) { |
42 WasmModuleBuilder.prototype.addStart = function(start_index) { | |
43 this.start_index = start_index; | 115 this.start_index = start_index; |
44 } | 116 } |
45 | 117 |
46 WasmModuleBuilder.prototype.addMemory = function(min, max, exp) { | 118 addMemory(min, max, exp) { |
47 this.memory = {min: min, max: max, exp: exp}; | 119 this.memory = {min: min, max: max, exp: exp}; |
48 return this; | 120 return this; |
49 } | 121 } |
50 | 122 |
51 WasmModuleBuilder.prototype.addExplicitSection = function(bytes) { | 123 addExplicitSection(bytes) { |
52 this.explicit.push(bytes); | 124 this.explicit.push(bytes); |
53 return this; | 125 return this; |
54 } | 126 } |
55 | 127 |
56 // Add a signature; format is [param_count, param0, param1, ..., retcount, ret0] | 128 addType(type) { |
57 WasmModuleBuilder.prototype.addSignature = function(sig) { | 129 // TODO: canonicalize types? |
58 // TODO: canonicalize signatures? | 130 this.types.push(type); |
59 this.signatures.push(sig); | 131 return this.types.length - 1; |
60 return this.signatures.length - 1; | 132 } |
61 } | 133 |
62 | 134 addFunction(name, type) { |
63 WasmModuleBuilder.prototype.addFunction = function(name, sig) { | 135 let type_index = (typeof type) == "number" ? type : this.addType(type); |
64 var sig_index = (typeof sig) == "number" ? sig : this.addSignature(sig); | 136 let func = new WasmFunctionBuilder(name, type_index); |
65 var func = new WasmFunctionBuilder(name, sig_index); | |
66 func.index = this.functions.length; | 137 func.index = this.functions.length; |
67 this.functions.push(func); | 138 this.functions.push(func); |
68 return func; | 139 return func; |
69 } | 140 } |
70 | 141 |
71 WasmModuleBuilder.prototype.addImportWithModule = function(module, name, sig) { | 142 addImportWithModule(module, name, type) { |
72 var sig_index = (typeof sig) == "number" ? sig : this.addSignature(sig); | 143 let type_index = (typeof type) == "number" ? type : this.addType(type); |
73 this.imports.push({module: module, name: name, sig_index: sig_index}); | 144 this.imports.push({module: module, name: name, type: type_index}); |
74 return this.imports.length - 1; | 145 return this.imports.length - 1; |
75 } | 146 } |
76 | 147 |
77 WasmModuleBuilder.prototype.addImport = function(name, sig) { | 148 addImport(name, type) { |
78 this.addImportWithModule(name, undefined, sig); | 149 return this.addImportWithModule(name, undefined, type); |
79 } | 150 } |
80 | 151 |
81 WasmModuleBuilder.prototype.addDataSegment = function(addr, data, init) { | 152 addDataSegment(addr, data, init) { |
82 this.data_segments.push({addr: addr, data: data, init: init}); | 153 this.segments.push({addr: addr, data: data, init: init}); |
83 return this.data_segments.length - 1; | 154 return this.segments.length - 1; |
84 } | 155 } |
85 | 156 |
86 WasmModuleBuilder.prototype.appendToFunctionTable = function(array) { | 157 appendToTable(array) { |
87 this.function_table = this.function_table.concat(array); | 158 this.table.push(...array); |
88 return this; | 159 return this; |
89 } | 160 } |
90 | 161 |
91 function emit_u8(bytes, val) { | 162 toArray(debug) { |
92 bytes.push(val & 0xff); | 163 let binary = new Binary; |
93 } | 164 let wasm = this; |
94 | 165 |
95 function emit_u16(bytes, val) { | 166 // Add header |
96 bytes.push(val & 0xff); | 167 binary.emit_header(); |
97 bytes.push((val >> 8) & 0xff); | 168 |
98 } | 169 // Add type section |
99 | 170 if (wasm.types.length > 0) { |
100 function emit_u32(bytes, val) { | 171 if (debug) print("emitting types @ " + binary.length); |
101 bytes.push(val & 0xff); | 172 binary.emit_section(kDeclTypes, section => { |
102 bytes.push((val >> 8) & 0xff); | 173 section.emit_varint(wasm.types.length); |
103 bytes.push((val >> 16) & 0xff); | 174 for (let type of wasm.types) { |
104 bytes.push((val >> 24) & 0xff); | 175 section.emit_u8(kWasmFunctionTypeForm); |
105 } | 176 section.emit_varint(type.params.length); |
106 | 177 for (let param of type.params) { |
107 function emit_string(bytes, string) { | 178 section.emit_u8(param); |
108 // When testing illegal names, we pass a byte array directly. | 179 } |
109 if (string instanceof Array) { | 180 section.emit_varint(type.results.length); |
110 emit_varint(bytes, string.length); | 181 for (let result of type.results) { |
111 emit_bytes(bytes, string); | 182 section.emit_u8(result); |
112 return; | 183 } |
113 } | 184 } |
114 | 185 }); |
115 // This is the hacky way to convert a JavaScript scring to a UTF8 encoded | |
116 // string only containing single-byte characters. | |
117 var string_utf8 = unescape(encodeURIComponent(string)); | |
118 emit_varint(bytes, string_utf8.length); | |
119 for (var i = 0; i < string_utf8.length; i++) { | |
120 emit_u8(bytes, string_utf8.charCodeAt(i)); | |
121 } | |
122 } | |
123 | |
124 function emit_varint(bytes, val) { | |
125 while (true) { | |
126 var v = val & 0xff; | |
127 val = val >>> 7; | |
128 if (val == 0) { | |
129 bytes.push(v); | |
130 break; | |
131 } | |
132 bytes.push(v | 0x80); | |
133 } | |
134 } | |
135 | |
136 function emit_bytes(bytes, data) { | |
137 for (var i = 0; i < data.length; i++) { | |
138 bytes.push(data[i] & 0xff); | |
139 } | |
140 } | |
141 | |
142 function emit_section(bytes, section_code, content_generator) { | |
143 // Emit section name. | |
144 emit_string(bytes, section_names[section_code]); | |
145 // Emit the section to a temporary buffer: its full length isn't know yet. | |
146 var tmp_bytes = []; | |
147 content_generator(tmp_bytes); | |
148 // Emit section length. | |
149 emit_varint(bytes, tmp_bytes.length); | |
150 // Copy the temporary buffer. | |
151 Array.prototype.push.apply(bytes, tmp_bytes); | |
152 } | |
153 | |
154 WasmModuleBuilder.prototype.toArray = function(debug) { | |
155 // Add header bytes | |
156 var bytes = []; | |
157 bytes = bytes.concat([kWasmH0, kWasmH1, kWasmH2, kWasmH3, | |
158 kWasmV0, kWasmV1, kWasmV2, kWasmV3]); | |
159 | |
160 var wasm = this; | |
161 | |
162 // Add signatures section | |
163 if (wasm.signatures.length > 0) { | |
164 if (debug) print("emitting signatures @ " + bytes.length); | |
165 emit_section(bytes, kDeclSignatures, function(bytes) { | |
166 emit_varint(bytes, wasm.signatures.length); | |
167 for (sig of wasm.signatures) { | |
168 emit_u8(bytes, kWasmFunctionTypeForm); | |
169 for (var j = 0; j < sig.length; j++) { | |
170 emit_u8(bytes, sig[j]); | |
171 } | |
172 } | |
173 }); | |
174 } | 186 } |
175 | 187 |
176 // Add imports section | 188 // Add imports section |
177 if (wasm.imports.length > 0) { | 189 if (wasm.imports.length > 0) { |
178 if (debug) print("emitting imports @ " + bytes.length); | 190 if (debug) print("emitting imports @ " + binary.length); |
179 emit_section(bytes, kDeclImportTable, function(bytes) { | 191 binary.emit_section(kDeclImports, section => { |
180 emit_varint(bytes, wasm.imports.length); | 192 section.emit_varint(wasm.imports.length); |
181 for (imp of wasm.imports) { | 193 for (let imp of wasm.imports) { |
182 emit_varint(bytes, imp.sig_index); | 194 section.emit_varint(imp.type); |
183 emit_string(bytes, imp.module); | 195 section.emit_string(imp.module); |
184 emit_string(bytes, imp.name || ''); | 196 section.emit_string(imp.name || ''); |
185 } | 197 } |
186 }); | 198 }); |
187 } | 199 } |
188 | 200 |
189 // Add functions declarations | 201 // Add functions declarations |
190 var names = false; | 202 let has_names = false; |
191 var exports = 0; | 203 let names = false; |
204 let exports = 0; | |
192 if (wasm.functions.length > 0) { | 205 if (wasm.functions.length > 0) { |
193 var has_names = false; | 206 if (debug) print("emitting function decls @ " + binary.length); |
194 | 207 binary.emit_section(kDeclFunctions, section => { |
195 // emit function signatures | 208 section.emit_varint(wasm.functions.length); |
196 if (debug) print("emitting function sigs @ " + bytes.length); | 209 for (let func of wasm.functions) { |
197 emit_section(bytes, kDeclFunctionSignatures, function(bytes) { | 210 has_names = has_names || (func.name != undefined && |
198 emit_varint(bytes, wasm.functions.length); | 211 func.name.length > 0); |
199 for (func of wasm.functions) { | 212 exports += func.exports.length; |
200 has_names = has_names || (func.name != undefined && | 213 section.emit_varint(func.type_index); |
201 func.name.length > 0); | 214 } |
202 exports += func.exports.length; | 215 }); |
203 | 216 } |
204 emit_varint(bytes, func.sig_index); | 217 |
205 } | 218 // Add table. |
206 }); | 219 if (wasm.table.length > 0) { |
207 | 220 if (debug) print("emitting table @ " + binary.length); |
208 } | 221 binary.emit_section(kDeclTable, section => { |
209 | 222 section.emit_varint(wasm.table.length); |
210 // Add function table. | 223 for (let index of wasm.table) { |
211 if (wasm.function_table.length > 0) { | 224 section.emit_varint(index); |
212 if (debug) print("emitting function table @ " + bytes.length); | 225 } |
213 emit_section(bytes, kDeclFunctionTable, function(bytes) { | 226 }); |
214 emit_varint(bytes, wasm.function_table.length); | |
215 for (index of wasm.function_table) { | |
216 emit_varint(bytes, index); | |
217 } | |
218 }); | |
219 } | 227 } |
220 | 228 |
221 // Add memory section | 229 // Add memory section |
222 if (wasm.memory != undefined) { | 230 if (wasm.memory != undefined) { |
223 if (debug) print("emitting memory @ " + bytes.length); | 231 if (debug) print("emitting memory @ " + binary.length); |
224 emit_section(bytes, kDeclMemory, function(bytes) { | 232 binary.emit_section(kDeclMemory, section => { |
225 emit_varint(bytes, wasm.memory.min); | 233 section.emit_varint(wasm.memory.min); |
226 emit_varint(bytes, wasm.memory.max); | 234 section.emit_varint(wasm.memory.max); |
227 emit_u8(bytes, wasm.memory.exp ? 1 : 0); | 235 section.emit_u8(wasm.memory.exp ? 1 : 0); |
228 }); | 236 }); |
229 } | 237 } |
230 | 238 |
231 | 239 |
232 // Add export table. | 240 // Add export table. |
233 if (exports > 0) { | 241 if (exports > 0) { |
234 if (debug) print("emitting exports @ " + bytes.length); | 242 if (debug) print("emitting exports @ " + binary.length); |
235 emit_section(bytes, kDeclExportTable, function(bytes) { | 243 binary.emit_section(kDeclExports, section => { |
236 emit_varint(bytes, exports); | 244 section.emit_varint(exports); |
237 for (func of wasm.functions) { | 245 for (let func of wasm.functions) { |
238 for (exp of func.exports) { | 246 for (let exp of func.exports) { |
239 emit_varint(bytes, func.index); | 247 section.emit_varint(func.index); |
240 emit_string(bytes, exp); | 248 section.emit_string(exp); |
241 } | 249 } |
242 } | 250 } |
243 }); | 251 }); |
244 } | 252 } |
245 | 253 |
246 // Add start function section. | 254 // Add start function section. |
247 if (wasm.start_index != undefined) { | 255 if (wasm.start_index != undefined) { |
248 if (debug) print("emitting start function @ " + bytes.length); | 256 if (debug) print("emitting start function @ " + binary.length); |
249 emit_section(bytes, kDeclStartFunction, function(bytes) { | 257 binary.emit_section(kDeclStart, section => { |
250 emit_varint(bytes, wasm.start_index); | 258 section.emit_varint(wasm.start_index); |
251 }); | 259 }); |
252 } | 260 } |
253 | 261 |
254 // Add function bodies. | 262 // Add function bodies. |
255 if (wasm.functions.length > 0) { | 263 if (wasm.functions.length > 0) { |
256 // emit function bodies | 264 // emit function bodies |
257 if (debug) print("emitting function bodies @ " + bytes.length); | 265 if (debug) print("emitting code @ " + binary.length); |
258 emit_section(bytes, kDeclFunctionBodies, function(bytes) { | 266 binary.emit_section(kDeclCode, section => { |
259 emit_varint(bytes, wasm.functions.length); | 267 section.emit_varint(wasm.functions.length); |
260 for (func of wasm.functions) { | 268 for (let func of wasm.functions) { |
261 // Function body length will be patched later. | 269 // Function body length will be patched later. |
262 var local_decls = []; | 270 let local_decls = []; |
263 var l = func.locals; | 271 let l = func.locals; |
264 if (l != undefined) { | 272 if (l != undefined) { |
265 var local_decls_count = 0; | 273 let local_decls_count = 0; |
266 if (l.i32_count > 0) { | 274 if (l.i32_count > 0) { |
267 local_decls.push({count: l.i32_count, type: kAstI32}); | 275 local_decls.push({count: l.i32_count, type: kAstI32}); |
268 } | |
269 if (l.i64_count > 0) { | |
270 local_decls.push({count: l.i64_count, type: kAstI64}); | |
271 } | |
272 if (l.f32_count > 0) { | |
273 local_decls.push({count: l.f32_count, type: kAstF32}); | |
274 } | |
275 if (l.f64_count > 0) { | |
276 local_decls.push({count: l.f64_count, type: kAstF64}); | |
277 } | |
278 } | |
279 var header = new Array(); | |
280 | |
281 emit_varint(header, local_decls.length); | |
282 for (decl of local_decls) { | |
283 emit_varint(header, decl.count); | |
284 emit_u8(header, decl.type); | |
285 } | |
286 | |
287 emit_varint(bytes, header.length + func.body.length); | |
288 emit_bytes(bytes, header); | |
289 emit_bytes(bytes, func.body); | |
290 } | 276 } |
291 }); | 277 if (l.i64_count > 0) { |
278 local_decls.push({count: l.i64_count, type: kAstI64}); | |
279 } | |
280 if (l.f32_count > 0) { | |
281 local_decls.push({count: l.f32_count, type: kAstF32}); | |
282 } | |
283 if (l.f64_count > 0) { | |
284 local_decls.push({count: l.f64_count, type: kAstF64}); | |
285 } | |
286 } | |
287 | |
288 let header = new Binary; | |
289 header.emit_varint(local_decls.length); | |
290 for (let decl of local_decls) { | |
291 header.emit_varint(decl.count); | |
292 header.emit_u8(decl.type); | |
293 } | |
294 | |
295 section.emit_varint(header.length + func.body.length); | |
296 section.emit_bytes(header); | |
297 section.emit_bytes(func.body); | |
298 } | |
299 }); | |
292 } | 300 } |
293 | 301 |
294 // Add data segments. | 302 // Add data segments. |
295 if (wasm.data_segments.length > 0) { | 303 if (wasm.segments.length > 0) { |
296 if (debug) print("emitting data segments @ " + bytes.length); | 304 if (debug) print("emitting data segments @ " + binary.length); |
297 emit_section(bytes, kDeclDataSegments, function(bytes) { | 305 binary.emit_section(kDeclData, section => { |
298 emit_varint(bytes, wasm.data_segments.length); | 306 section.emit_varint(wasm.segments.length); |
299 for (seg of wasm.data_segments) { | 307 for (let seg of wasm.segments) { |
300 emit_varint(bytes, seg.addr); | 308 section.emit_varint(seg.addr); |
301 emit_varint(bytes, seg.data.length); | 309 section.emit_varint(seg.data.length); |
302 emit_bytes(bytes, seg.data); | 310 section.emit_bytes(seg.data); |
303 } | 311 } |
304 }); | 312 }); |
305 } | 313 } |
306 | 314 |
307 // Add any explicitly added sections | 315 // Add any explicitly added sections |
308 for (exp of wasm.explicit) { | 316 for (let exp of wasm.explicit) { |
309 if (debug) print("emitting explicit @ " + bytes.length); | 317 if (debug) print("emitting explicit @ " + binary.length); |
310 emit_bytes(bytes, exp); | 318 binary.emit_bytes(exp); |
311 } | 319 } |
312 | 320 |
313 // Add function names. | 321 // Add function names. |
314 if (has_names) { | 322 if (has_names) { |
315 if (debug) print("emitting names @ " + bytes.length); | 323 if (debug) print("emitting names @ " + binary.length); |
316 emit_section(bytes, kDeclNames, function(bytes) { | 324 binary.emit_section(kDeclNames, section => { |
317 emit_varint(bytes, wasm.functions.length); | 325 section.emit_varint(wasm.functions.length); |
318 for (func of wasm.functions) { | 326 for (let func of wasm.functions) { |
319 var name = func.name == undefined ? "" : func.name; | 327 var name = func.name == undefined ? "" : func.name; |
320 emit_string(bytes, name); | 328 section.emit_string(name); |
321 emit_u8(bytes, 0); // local names count == 0 | 329 section.emit_u8(0); // local names count == 0 |
322 } | 330 } |
323 }); | 331 }); |
324 } | 332 } |
325 | 333 |
326 // End the module. | 334 return binary; |
327 if (debug) print("emitting end @ " + bytes.length); | 335 } |
328 emit_section(bytes, kDeclEnd, function(bytes) {}); | 336 |
329 | 337 toBuffer(debug) { |
330 return bytes; | 338 let bytes = this.toArray(debug); |
339 let buffer = new ArrayBuffer(bytes.length); | |
340 let view = new Uint8Array(buffer); | |
341 for (let i = 0; i < bytes.length; i++) { | |
342 let val = bytes[i]; | |
343 if ((typeof val) == "string") val = val.charCodeAt(0); | |
344 view[i] = val | 0; | |
345 } | |
346 return buffer; | |
347 } | |
348 | |
349 instantiate(...args) { | |
350 let module = new WebAssembly.Module(this.toBuffer()); | |
351 let instance = new WebAssembly.Instance(module, ...args); | |
352 return instance; | |
353 } | |
331 } | 354 } |
332 | |
333 WasmModuleBuilder.prototype.toBuffer = function(debug) { | |
334 var bytes = this.toArray(debug); | |
335 var buffer = new ArrayBuffer(bytes.length); | |
336 var view = new Uint8Array(buffer); | |
337 for (var i = 0; i < bytes.length; i++) { | |
338 var val = bytes[i]; | |
339 if ((typeof val) == "string") val = val.charCodeAt(0); | |
340 view[i] = val | 0; | |
341 } | |
342 return buffer; | |
343 } | |
344 | |
345 WasmModuleBuilder.prototype.instantiate = function(...args) { | |
346 var module = new WebAssembly.Module(this.toBuffer()); | |
347 var instance = new WebAssembly.Instance(module, ...args); | |
348 return instance; | |
349 } | |
OLD | NEW |