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 3// found in the LICENSE file. 4 5class Binary extends Array { 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 75class 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 } 101} 102 103class 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 } 115 116 addStart(start_index) { 117 this.start_index = start_index; 118 } 119 120 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 }); 193 } 194 195 // Add imports section 196 if (wasm.imports.length > 0) { 197 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 }); 206 } 207 208 // Add functions declarations 209 let has_names = false; 210 let names = false; 211 let exports = 0; 212 if (wasm.functions.length > 0) { 213 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 }); 223 } 224 225 // 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 }); 234 } 235 236 // Add memory section 237 if (wasm.memory != undefined) { 238 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 }); 244 } 245 246 247 // Add export table. 248 if (exports > 0) { 249 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 }); 259 } 260 261 // Add start function section. 262 if (wasm.start_index != undefined) { 263 if (debug) print("emitting start function @ " + binary.length); 264 binary.emit_section(kDeclStart, section => { 265 section.emit_varint(wasm.start_index); 266 }); 267 } 268 269 // Add function bodies. 270 if (wasm.functions.length > 0) { 271 // 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}); 283 } 284 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 }); 307 } 308 309 // Add data segments. 310 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 }); 320 } 321 322 // Add any explicitly added sections 323 for (let exp of wasm.explicit) { 324 if (debug) print("emitting explicit @ " + binary.length); 325 binary.emit_bytes(exp); 326 } 327 328 // Add function names. 329 if (has_names) { 330 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 }); 348 } 349 350 // End the module. 351 if (debug) print("emitting end @ " + binary.length); 352 binary.emit_section(kDeclEnd, section => {}); 353 354 return binary; 355 } 356 357 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; 365 } 366 return buffer; 367 } 368 369 instantiate(...args) { 370 let module = new WebAssembly.Module(this.toBuffer()); 371 let instance = new WebAssembly.Instance(module, ...args); 372 return instance; 373 } 374} 375