Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 the V8 project authors. All rights reserved. | |
|
bradnelson
2017/02/18 22:16:22
I see this is lifted from v8.
Don't imagine we hav
Mircea Trofin
2017/02/19 00:18:33
We had a similar problem with the wasm structured
| |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 // Used for encoding f32 and double constants to bits. | |
| 6 let __buffer = new ArrayBuffer(8); | |
| 7 let byte_view = new Int8Array(__buffer); | |
| 8 let f32_view = new Float32Array(__buffer); | |
| 9 let f64_view = new Float64Array(__buffer); | |
| 10 | |
| 11 class Binary extends Array { | |
| 12 emit_u8(val) { | |
| 13 this.push(val); | |
| 14 } | |
| 15 | |
| 16 emit_u16(val) { | |
| 17 this.push(val & 0xff); | |
| 18 this.push((val >> 8) & 0xff); | |
| 19 } | |
| 20 | |
| 21 emit_u32(val) { | |
| 22 this.push(val & 0xff); | |
| 23 this.push((val >> 8) & 0xff); | |
| 24 this.push((val >> 16) & 0xff); | |
| 25 this.push((val >> 24) & 0xff); | |
| 26 } | |
| 27 | |
| 28 emit_u32v(val) { | |
| 29 while (true) { | |
| 30 let v = val & 0xff; | |
| 31 val = val >>> 7; | |
| 32 if (val == 0) { | |
| 33 this.push(v); | |
| 34 break; | |
| 35 } | |
| 36 this.push(v | 0x80); | |
| 37 } | |
| 38 } | |
| 39 | |
| 40 emit_bytes(data) { | |
| 41 for (let i = 0; i < data.length; i++) { | |
| 42 this.push(data[i] & 0xff); | |
| 43 } | |
| 44 } | |
| 45 | |
| 46 emit_string(string) { | |
| 47 // When testing illegal names, we pass a byte array directly. | |
| 48 if (string instanceof Array) { | |
| 49 this.emit_u32v(string.length); | |
| 50 this.emit_bytes(string); | |
| 51 return; | |
| 52 } | |
| 53 | |
| 54 // This is the hacky way to convert a JavaScript string to a UTF8 encoded | |
| 55 // string only containing single-byte characters. | |
| 56 let string_utf8 = unescape(encodeURIComponent(string)); | |
| 57 this.emit_u32v(string_utf8.length); | |
| 58 for (let i = 0; i < string_utf8.length; i++) { | |
| 59 this.emit_u8(string_utf8.charCodeAt(i)); | |
| 60 } | |
| 61 } | |
| 62 | |
| 63 emit_header() { | |
| 64 this.push(kWasmH0, kWasmH1, kWasmH2, kWasmH3, | |
| 65 kWasmV0, kWasmV1, kWasmV2, kWasmV3); | |
| 66 } | |
| 67 | |
| 68 emit_section(section_code, content_generator) { | |
| 69 // Emit section name. | |
| 70 this.emit_u8(section_code); | |
| 71 // Emit the section to a temporary buffer: its full length isn't know yet. | |
| 72 let section = new Binary; | |
| 73 content_generator(section); | |
| 74 // Emit section length. | |
| 75 this.emit_u32v(section.length); | |
| 76 // Copy the temporary buffer. | |
| 77 this.push(...section); | |
| 78 } | |
| 79 } | |
| 80 | |
| 81 class WasmFunctionBuilder { | |
| 82 constructor(module, name, type_index) { | |
| 83 this.module = module; | |
| 84 this.name = name; | |
| 85 this.type_index = type_index; | |
| 86 this.body = []; | |
| 87 } | |
| 88 | |
| 89 exportAs(name) { | |
| 90 this.module.addExport(name, this.index); | |
| 91 return this; | |
| 92 } | |
| 93 | |
| 94 exportFunc() { | |
| 95 this.exportAs(this.name); | |
| 96 return this; | |
| 97 } | |
| 98 | |
| 99 addBody(body) { | |
| 100 for (let b of body) { | |
| 101 if (typeof b != 'number') throw new Error("invalid body"); | |
| 102 } | |
| 103 this.body = body; | |
| 104 // Automatically add the end for the function block to the body. | |
| 105 body.push(kExprEnd); | |
| 106 return this; | |
| 107 } | |
| 108 | |
| 109 addBodyWithEnd(body) { | |
| 110 this.body = body; | |
| 111 return this; | |
| 112 } | |
| 113 | |
| 114 addLocals(locals) { | |
| 115 this.locals = locals; | |
| 116 return this; | |
| 117 } | |
| 118 | |
| 119 end() { | |
| 120 return this.module; | |
| 121 } | |
| 122 } | |
| 123 | |
| 124 class WasmGlobalBuilder { | |
| 125 constructor(module, type, mutable) { | |
| 126 this.module = module; | |
| 127 this.type = type; | |
| 128 this.mutable = mutable; | |
| 129 this.init = 0; | |
| 130 } | |
| 131 | |
| 132 exportAs(name) { | |
| 133 this.module.exports.push({name: name, kind: kExternalGlobal, | |
| 134 index: this.index}); | |
| 135 return this; | |
| 136 } | |
| 137 } | |
| 138 | |
| 139 class WasmModuleBuilder { | |
| 140 constructor() { | |
| 141 this.types = []; | |
| 142 this.imports = []; | |
| 143 this.exports = []; | |
| 144 this.globals = []; | |
| 145 this.functions = []; | |
| 146 this.function_table = []; | |
| 147 this.function_table_length = 0; | |
| 148 this.function_table_inits = []; | |
| 149 this.segments = []; | |
| 150 this.explicit = []; | |
| 151 this.num_imported_funcs = 0; | |
| 152 this.num_imported_globals = 0; | |
| 153 return this; | |
| 154 } | |
| 155 | |
| 156 addStart(start_index) { | |
| 157 this.start_index = start_index; | |
| 158 return this; | |
| 159 } | |
| 160 | |
| 161 addMemory(min, max, exp) { | |
| 162 this.memory = {min: min, max: max, exp: exp}; | |
| 163 return this; | |
| 164 } | |
| 165 | |
| 166 addExplicitSection(bytes) { | |
| 167 this.explicit.push(bytes); | |
| 168 return this; | |
| 169 } | |
| 170 | |
| 171 stringToBytes(name) { | |
| 172 var result = new Binary(); | |
| 173 result.emit_u32v(name.length); | |
| 174 for (var i = 0; i < name.length; i++) { | |
| 175 result.emit_u8(name.charCodeAt(i)); | |
| 176 } | |
| 177 return result; | |
| 178 } | |
| 179 | |
| 180 addCustomSection(name, bytes) { | |
| 181 name = this.stringToBytes(name); | |
| 182 var length = new Binary(); | |
| 183 length.emit_u32v(name.length + bytes.length); | |
| 184 this.explicit.push([0, ...length, ...name, ...bytes]); | |
| 185 } | |
| 186 | |
| 187 addType(type) { | |
| 188 // TODO: canonicalize types? | |
| 189 this.types.push(type); | |
| 190 return this.types.length - 1; | |
| 191 } | |
| 192 | |
| 193 addGlobal(local_type, mutable) { | |
| 194 let glob = new WasmGlobalBuilder(this, local_type, mutable); | |
| 195 glob.index = this.globals.length + this.num_imported_globals; | |
| 196 this.globals.push(glob); | |
| 197 return glob; | |
| 198 } | |
| 199 | |
| 200 addFunction(name, type) { | |
| 201 let type_index = (typeof type) == "number" ? type : this.addType(type); | |
| 202 let func = new WasmFunctionBuilder(this, name, type_index); | |
| 203 func.index = this.functions.length + this.num_imported_funcs; | |
| 204 this.functions.push(func); | |
| 205 return func; | |
| 206 } | |
| 207 | |
| 208 addImport(module = "", name, type) { | |
| 209 let type_index = (typeof type) == "number" ? type : this.addType(type); | |
| 210 this.imports.push({module: module, name: name, kind: kExternalFunction, | |
| 211 type: type_index}); | |
| 212 return this.num_imported_funcs++; | |
| 213 } | |
| 214 | |
| 215 addImportedGlobal(module = "", name, type) { | |
| 216 let o = {module: module, name: name, kind: kExternalGlobal, type: type, | |
| 217 mutable: false} | |
| 218 this.imports.push(o); | |
| 219 return this.num_imported_globals++; | |
| 220 } | |
| 221 | |
| 222 addImportedMemory(module = "", name, initial = 0, maximum) { | |
| 223 let o = {module: module, name: name, kind: kExternalMemory, | |
| 224 initial: initial, maximum: maximum}; | |
| 225 this.imports.push(o); | |
| 226 return this; | |
| 227 } | |
| 228 | |
| 229 addImportedTable(module = "", name, initial, maximum) { | |
| 230 let o = {module: module, name: name, kind: kExternalTable, initial: initial, | |
| 231 maximum: maximum}; | |
| 232 this.imports.push(o); | |
| 233 } | |
| 234 | |
| 235 addExport(name, index) { | |
| 236 this.exports.push({name: name, kind: kExternalFunction, index: index}); | |
| 237 return this; | |
| 238 } | |
| 239 | |
| 240 addExportOfKind(name, kind, index) { | |
| 241 this.exports.push({name: name, kind: kind, index: index}); | |
| 242 return this; | |
| 243 } | |
| 244 | |
| 245 addDataSegment(addr, data, is_global = false) { | |
| 246 this.segments.push({addr: addr, data: data, is_global: is_global}); | |
| 247 return this.segments.length - 1; | |
| 248 } | |
| 249 | |
| 250 exportMemoryAs(name) { | |
| 251 this.exports.push({name: name, kind: kExternalMemory, index: 0}); | |
| 252 } | |
| 253 | |
| 254 addFunctionTableInit(base, is_global, array, is_import = false) { | |
| 255 this.function_table_inits.push({base: base, is_global: is_global, | |
| 256 array: array}); | |
| 257 if (!is_global) { | |
| 258 var length = base + array.length; | |
| 259 if (length > this.function_table_length && !is_import) { | |
| 260 this.function_table_length = length; | |
| 261 } | |
| 262 } | |
| 263 return this; | |
| 264 } | |
| 265 | |
| 266 appendToTable(array) { | |
| 267 return this.addFunctionTableInit(this.function_table.length, false, array); | |
| 268 } | |
| 269 | |
| 270 setFunctionTableLength(length) { | |
| 271 this.function_table_length = length; | |
| 272 return this; | |
| 273 } | |
| 274 | |
| 275 toArray(debug = false) { | |
| 276 let binary = new Binary; | |
| 277 let wasm = this; | |
| 278 | |
| 279 // Add header | |
| 280 binary.emit_header(); | |
| 281 | |
| 282 // Add type section | |
| 283 if (wasm.types.length > 0) { | |
| 284 if (debug) print("emitting types @ " + binary.length); | |
| 285 binary.emit_section(kTypeSectionCode, section => { | |
| 286 section.emit_u32v(wasm.types.length); | |
| 287 for (let type of wasm.types) { | |
| 288 section.emit_u8(kWasmFunctionTypeForm); | |
| 289 section.emit_u32v(type.params.length); | |
| 290 for (let param of type.params) { | |
| 291 section.emit_u8(param); | |
| 292 } | |
| 293 section.emit_u32v(type.results.length); | |
| 294 for (let result of type.results) { | |
| 295 section.emit_u8(result); | |
| 296 } | |
| 297 } | |
| 298 }); | |
| 299 } | |
| 300 | |
| 301 // Add imports section | |
| 302 if (wasm.imports.length > 0) { | |
| 303 if (debug) print("emitting imports @ " + binary.length); | |
| 304 binary.emit_section(kImportSectionCode, section => { | |
| 305 section.emit_u32v(wasm.imports.length); | |
| 306 for (let imp of wasm.imports) { | |
| 307 section.emit_string(imp.module); | |
| 308 section.emit_string(imp.name || ''); | |
| 309 section.emit_u8(imp.kind); | |
| 310 if (imp.kind == kExternalFunction) { | |
| 311 section.emit_u32v(imp.type); | |
| 312 } else if (imp.kind == kExternalGlobal) { | |
| 313 section.emit_u32v(imp.type); | |
| 314 section.emit_u8(imp.mutable); | |
| 315 } else if (imp.kind == kExternalMemory) { | |
| 316 var has_max = (typeof imp.maximum) != "undefined"; | |
| 317 section.emit_u8(has_max ? 1 : 0); // flags | |
| 318 section.emit_u32v(imp.initial); // initial | |
| 319 if (has_max) section.emit_u32v(imp.maximum); // maximum | |
| 320 } else if (imp.kind == kExternalTable) { | |
| 321 section.emit_u8(kWasmAnyFunctionTypeForm); | |
| 322 var has_max = (typeof imp.maximum) != "undefined"; | |
| 323 section.emit_u8(has_max ? 1 : 0); // flags | |
| 324 section.emit_u32v(imp.initial); // initial | |
| 325 if (has_max) section.emit_u32v(imp.maximum); // maximum | |
| 326 } else { | |
| 327 throw new Error("unknown/unsupported import kind " + imp.kind); | |
| 328 } | |
| 329 } | |
| 330 }); | |
| 331 } | |
| 332 | |
| 333 // Add functions declarations | |
| 334 let has_names = false; | |
| 335 let names = false; | |
| 336 if (wasm.functions.length > 0) { | |
| 337 if (debug) print("emitting function decls @ " + binary.length); | |
| 338 binary.emit_section(kFunctionSectionCode, section => { | |
| 339 section.emit_u32v(wasm.functions.length); | |
| 340 for (let func of wasm.functions) { | |
| 341 has_names = has_names || (func.name != undefined && | |
| 342 func.name.length > 0); | |
| 343 section.emit_u32v(func.type_index); | |
| 344 } | |
| 345 }); | |
| 346 } | |
| 347 | |
| 348 // Add function_table. | |
| 349 if (wasm.function_table_length > 0) { | |
| 350 if (debug) print("emitting table @ " + binary.length); | |
| 351 binary.emit_section(kTableSectionCode, section => { | |
| 352 section.emit_u8(1); // one table entry | |
| 353 section.emit_u8(kWasmAnyFunctionTypeForm); | |
| 354 section.emit_u8(1); | |
| 355 section.emit_u32v(wasm.function_table_length); | |
| 356 section.emit_u32v(wasm.function_table_length); | |
| 357 }); | |
| 358 } | |
| 359 | |
| 360 // Add memory section | |
| 361 if (wasm.memory != undefined) { | |
| 362 if (debug) print("emitting memory @ " + binary.length); | |
| 363 binary.emit_section(kMemorySectionCode, section => { | |
| 364 section.emit_u8(1); // one memory entry | |
| 365 section.emit_u32v(kResizableMaximumFlag); | |
| 366 section.emit_u32v(wasm.memory.min); | |
| 367 section.emit_u32v(wasm.memory.max); | |
| 368 }); | |
| 369 } | |
| 370 | |
| 371 // Add global section. | |
| 372 if (wasm.globals.length > 0) { | |
| 373 if (debug) print ("emitting globals @ " + binary.length); | |
| 374 binary.emit_section(kGlobalSectionCode, section => { | |
| 375 section.emit_u32v(wasm.globals.length); | |
| 376 for (let global of wasm.globals) { | |
| 377 section.emit_u8(global.type); | |
| 378 section.emit_u8(global.mutable); | |
| 379 if ((typeof global.init_index) == "undefined") { | |
| 380 // Emit a constant initializer. | |
| 381 switch (global.type) { | |
| 382 case kWasmI32: | |
| 383 section.emit_u8(kExprI32Const); | |
| 384 section.emit_u32v(global.init); | |
| 385 break; | |
| 386 case kWasmI64: | |
| 387 section.emit_u8(kExprI64Const); | |
| 388 section.emit_u32v(global.init); | |
| 389 break; | |
| 390 case kWasmF32: | |
| 391 section.emit_u8(kExprF32Const); | |
| 392 f32_view[0] = global.init; | |
| 393 section.emit_u8(byte_view[0]); | |
| 394 section.emit_u8(byte_view[1]); | |
| 395 section.emit_u8(byte_view[2]); | |
| 396 section.emit_u8(byte_view[3]); | |
| 397 break; | |
| 398 case kWasmF64: | |
| 399 section.emit_u8(kExprF64Const); | |
| 400 f64_view[0] = global.init; | |
| 401 section.emit_u8(byte_view[0]); | |
| 402 section.emit_u8(byte_view[1]); | |
| 403 section.emit_u8(byte_view[2]); | |
| 404 section.emit_u8(byte_view[3]); | |
| 405 section.emit_u8(byte_view[4]); | |
| 406 section.emit_u8(byte_view[5]); | |
| 407 section.emit_u8(byte_view[6]); | |
| 408 section.emit_u8(byte_view[7]); | |
| 409 break; | |
| 410 } | |
| 411 } else { | |
| 412 // Emit a global-index initializer. | |
| 413 section.emit_u8(kExprGetGlobal); | |
| 414 section.emit_u32v(global.init_index); | |
| 415 } | |
| 416 section.emit_u8(kExprEnd); // end of init expression | |
| 417 } | |
| 418 }); | |
| 419 } | |
| 420 | |
| 421 // Add export table. | |
| 422 var mem_export = (wasm.memory != undefined && wasm.memory.exp); | |
| 423 var exports_count = wasm.exports.length + (mem_export ? 1 : 0); | |
| 424 if (exports_count > 0) { | |
| 425 if (debug) print("emitting exports @ " + binary.length); | |
| 426 binary.emit_section(kExportSectionCode, section => { | |
| 427 section.emit_u32v(exports_count); | |
| 428 for (let exp of wasm.exports) { | |
| 429 section.emit_string(exp.name); | |
| 430 section.emit_u8(exp.kind); | |
| 431 section.emit_u32v(exp.index); | |
| 432 } | |
| 433 if (mem_export) { | |
| 434 section.emit_string("memory"); | |
| 435 section.emit_u8(kExternalMemory); | |
| 436 section.emit_u8(0); | |
| 437 } | |
| 438 }); | |
| 439 } | |
| 440 | |
| 441 // Add start function section. | |
| 442 if (wasm.start_index != undefined) { | |
| 443 if (debug) print("emitting start function @ " + binary.length); | |
| 444 binary.emit_section(kStartSectionCode, section => { | |
| 445 section.emit_u32v(wasm.start_index); | |
| 446 }); | |
| 447 } | |
| 448 | |
| 449 // Add table elements. | |
| 450 if (wasm.function_table_inits.length > 0) { | |
| 451 if (debug) print("emitting table @ " + binary.length); | |
| 452 binary.emit_section(kElementSectionCode, section => { | |
| 453 var inits = wasm.function_table_inits; | |
| 454 section.emit_u32v(inits.length); | |
| 455 section.emit_u8(0); // table index | |
| 456 | |
| 457 for (let init of inits) { | |
| 458 if (init.is_global) { | |
| 459 section.emit_u8(kExprGetGlobal); | |
| 460 } else { | |
| 461 section.emit_u8(kExprI32Const); | |
| 462 } | |
| 463 section.emit_u32v(init.base); | |
| 464 section.emit_u8(kExprEnd); | |
| 465 section.emit_u32v(init.array.length); | |
| 466 for (let index of init.array) { | |
| 467 section.emit_u32v(index); | |
| 468 } | |
| 469 } | |
| 470 }); | |
| 471 } | |
| 472 | |
| 473 // Add function bodies. | |
| 474 if (wasm.functions.length > 0) { | |
| 475 // emit function bodies | |
| 476 if (debug) print("emitting code @ " + binary.length); | |
| 477 binary.emit_section(kCodeSectionCode, section => { | |
| 478 section.emit_u32v(wasm.functions.length); | |
| 479 for (let func of wasm.functions) { | |
| 480 // Function body length will be patched later. | |
| 481 let local_decls = []; | |
| 482 let l = func.locals; | |
| 483 if (l != undefined) { | |
| 484 let local_decls_count = 0; | |
| 485 if (l.i32_count > 0) { | |
| 486 local_decls.push({count: l.i32_count, type: kWasmI32}); | |
| 487 } | |
| 488 if (l.i64_count > 0) { | |
| 489 local_decls.push({count: l.i64_count, type: kWasmI64}); | |
| 490 } | |
| 491 if (l.f32_count > 0) { | |
| 492 local_decls.push({count: l.f32_count, type: kWasmF32}); | |
| 493 } | |
| 494 if (l.f64_count > 0) { | |
| 495 local_decls.push({count: l.f64_count, type: kWasmF64}); | |
| 496 } | |
| 497 } | |
| 498 | |
| 499 let header = new Binary; | |
| 500 header.emit_u32v(local_decls.length); | |
| 501 for (let decl of local_decls) { | |
| 502 header.emit_u32v(decl.count); | |
| 503 header.emit_u8(decl.type); | |
| 504 } | |
| 505 | |
| 506 section.emit_u32v(header.length + func.body.length); | |
| 507 section.emit_bytes(header); | |
| 508 section.emit_bytes(func.body); | |
| 509 } | |
| 510 }); | |
| 511 } | |
| 512 | |
| 513 // Add data segments. | |
| 514 if (wasm.segments.length > 0) { | |
| 515 if (debug) print("emitting data segments @ " + binary.length); | |
| 516 binary.emit_section(kDataSectionCode, section => { | |
| 517 section.emit_u32v(wasm.segments.length); | |
| 518 for (let seg of wasm.segments) { | |
| 519 section.emit_u8(0); // linear memory index 0 | |
| 520 if (seg.is_global) { | |
| 521 // initializer is a global variable | |
| 522 section.emit_u8(kExprGetGlobal); | |
| 523 section.emit_u32v(seg.addr); | |
| 524 } else { | |
| 525 // initializer is a constant | |
| 526 section.emit_u8(kExprI32Const); | |
| 527 section.emit_u32v(seg.addr); | |
| 528 } | |
| 529 section.emit_u8(kExprEnd); | |
| 530 section.emit_u32v(seg.data.length); | |
| 531 section.emit_bytes(seg.data); | |
| 532 } | |
| 533 }); | |
| 534 } | |
| 535 | |
| 536 // Add any explicitly added sections | |
| 537 for (let exp of wasm.explicit) { | |
| 538 if (debug) print("emitting explicit @ " + binary.length); | |
| 539 binary.emit_bytes(exp); | |
| 540 } | |
| 541 | |
| 542 // Add function names. | |
| 543 if (has_names) { | |
| 544 if (debug) print("emitting names @ " + binary.length); | |
| 545 binary.emit_section(kUnknownSectionCode, section => { | |
| 546 section.emit_string("name"); | |
| 547 var count = wasm.functions.length + wasm.num_imported_funcs; | |
| 548 section.emit_u32v(count); | |
| 549 for (var i = 0; i < wasm.num_imported_funcs; i++) { | |
| 550 section.emit_u8(0); // empty string | |
| 551 section.emit_u8(0); // local names count == 0 | |
| 552 } | |
| 553 for (let func of wasm.functions) { | |
| 554 var name = func.name == undefined ? "" : func.name; | |
| 555 section.emit_string(name); | |
| 556 section.emit_u8(0); // local names count == 0 | |
| 557 } | |
| 558 }); | |
| 559 } | |
| 560 | |
| 561 return binary; | |
| 562 } | |
| 563 | |
| 564 toBuffer(debug = false) { | |
| 565 let bytes = this.toArray(debug); | |
| 566 let buffer = new ArrayBuffer(bytes.length); | |
| 567 let view = new Uint8Array(buffer); | |
| 568 for (let i = 0; i < bytes.length; i++) { | |
| 569 let val = bytes[i]; | |
| 570 if ((typeof val) == "string") val = val.charCodeAt(0); | |
| 571 view[i] = val | 0; | |
| 572 } | |
| 573 return buffer; | |
| 574 } | |
| 575 | |
| 576 instantiate(ffi) { | |
| 577 let module = new WebAssembly.Module(this.toBuffer()); | |
| 578 let instance = new WebAssembly.Instance(module, ffi); | |
| 579 return instance; | |
| 580 } | |
| 581 } | |
| OLD | NEW |