Changeset 209651 in webkit
- Timestamp:
- Dec 9, 2016 11:08:31 PM (7 years ago)
- Location:
- trunk
- Files:
-
- 1 added
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/JSTests/ChangeLog
r209642 r209651 1 2016-12-09 JF Bastien <jfbastien@apple.com> 2 3 WebAssembly: implement data section 4 https://bugs.webkit.org/show_bug.cgi?id=165696 5 6 Reviewed by Keith Miller. 7 8 As specified in https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md#data-section 9 Note that some of the interesting corner cases are ill-defined by the spec: https://github.com/WebAssembly/design/issues/897 10 11 * wasm/Builder.js: create a data section from JavaScript 12 * wasm/Builder_WebAssemblyBinary.js: assemble the data section into the proper binary encoding 13 (const.emitters.Data): 14 * wasm/js-api/test_Data.js: Added. 15 (DataSection): 16 (DataSectionOffTheEnd): 17 (DataSectionPartlyOffTheEnd): 18 (DataSectionEmptyOffTheEnd): 19 (DataSectionSeenByStart): 20 * wasm/self-test/test_BuilderJSON.js: make sure the JSON structure is fine (this sanity checks before going to binary) 21 1 22 2016-12-09 JF Bastien <jfbastien@apple.com> 2 23 -
trunk/JSTests/wasm/Builder.js
r209642 r209651 524 524 525 525 case "Data": 526 // FIXME implement data https://bugs.webkit.org/show_bug.cgi?id=161709 527 this[section] = () => { throw new Error(`Unimplemented: section type "${section}"`); }; 526 this[section] = function() { 527 const s = this._addSection(section); 528 const dataBuilder = { 529 End: () => this, 530 Segment: data => { 531 assert.isArray(data); 532 for (const datum of data) { 533 assert.isNumber(datum); 534 assert.ge(datum, 0); 535 assert.le(datum, 0xff); 536 } 537 s.data.push({ data: data, index: 0, offset: 0 }); 538 let thisSegment = s.data[s.data.length - 1]; 539 const segmentBuilder = { 540 End: () => dataBuilder, 541 Index: index => { 542 assert.eq(index, 0); // Linear memory index must be zero in MVP. 543 thisSegment.index = index; 544 return segmentBuilder; 545 }, 546 Offset: offset => { 547 // FIXME allow complex init_expr here. https://bugs.webkit.org/show_bug.cgi?id=165700 548 assert.isNumber(offset); 549 thisSegment.offset = offset; 550 return segmentBuilder; 551 }, 552 }; 553 return segmentBuilder; 554 }, 555 }; 556 return dataBuilder; 557 }; 528 558 break; 529 559 -
trunk/JSTests/wasm/Builder_WebAssemblyBinary.js
r209642 r209651 157 157 }, 158 158 159 Data: (section, bin) => { throw new Error(`Not yet implemented`); }, 159 Data: (section, bin) => { 160 put(bin, "varuint32", section.data.length); 161 for (const datum of section.data) { 162 put(bin, "varuint32", datum.index); 163 // FIXME allow complex init_expr here. https://bugs.webkit.org/show_bug.cgi?id=165700 164 // For now we only handle i32.const as offset. 165 put(bin, "uint8", WASM.description.opcode["i32.const"].value); 166 put(bin, WASM.description.opcode["i32.const"].immediate[0].type, datum.offset); 167 put(bin, "uint8", WASM.description.opcode["end"].value); 168 put(bin, "varuint32", datum.data.length); 169 for (const byte of datum.data) 170 put(bin, "uint8", byte); 171 } 172 }, 160 173 }; 161 174 -
trunk/JSTests/wasm/self-test/test_BuilderJSON.js
r209642 r209651 620 620 assert.eq(j.section[1].data[0].code[3].name, "select"); 621 621 })(); 622 // FIXME test type mismatch with select. https://bugs.webkit.org/show_bug.cgi?id=163267 622 623 623 624 (function MemoryImport() { … … 640 641 })(); 641 642 642 // FIXME test type mismatch with select. https://bugs.webkit.org/show_bug.cgi?id=163267 643 (function DataSection() { 644 const builder = (new Builder()) 645 .Memory().InitialMaxPages(64, 64).End() 646 .Data() 647 .Segment([0xff, 0x2a]).Offset(4).End() 648 .Segment([0xde, 0xad, 0xbe, 0xef]).Index(0).Offset(24).End() 649 .End(); 650 const json = JSON.parse(builder.json()); 651 assert.eq(json.section.length, 2); 652 assert.eq(json.section[1].name, "Data"); 653 assert.eq(json.section[1].data.length, 2); 654 assert.eq(json.section[1].data[0].index, 0); 655 assert.eq(json.section[1].data[0].offset, 4); 656 assert.eq(json.section[1].data[0].data.length, 2); 657 assert.eq(json.section[1].data[0].data[0], 0xff); 658 assert.eq(json.section[1].data[0].data[1], 0x2a); 659 assert.eq(json.section[1].data[1].index, 0); 660 assert.eq(json.section[1].data[1].offset, 24); 661 assert.eq(json.section[1].data[1].data.length, 4); 662 assert.eq(json.section[1].data[1].data[0], 0xde); 663 assert.eq(json.section[1].data[1].data[1], 0xad); 664 assert.eq(json.section[1].data[1].data[2], 0xbe); 665 assert.eq(json.section[1].data[1].data[3], 0xef); 666 })(); -
trunk/Source/JavaScriptCore/ChangeLog
r209650 r209651 1 2016-12-09 JF Bastien <jfbastien@apple.com> 2 3 WebAssembly: implement data section 4 https://bugs.webkit.org/show_bug.cgi?id=165696 5 6 Reviewed by Keith Miller. 7 8 As specified in https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md#data-section 9 Note that some of the interesting corner cases are ill-defined by the spec: https://github.com/WebAssembly/design/issues/897 10 11 * wasm/WasmFormat.h: segments are what represent sections of memory to initialize (similar to ELF's non-zero intializer data / rodata) 12 (JSC::Wasm::Segment::make): 13 (JSC::Wasm::Segment::destroy): 14 (JSC::Wasm::Segment::byte): 15 (JSC::Wasm::Segment::makePtr): 16 * wasm/WasmModuleParser.cpp: parse the data section, and prevent a few overflows if a user passes in UINT_MAX (the loops would overflow) 17 (JSC::Wasm::ModuleParser::parseType): 18 (JSC::Wasm::ModuleParser::parseImport): 19 (JSC::Wasm::ModuleParser::parseFunction): 20 (JSC::Wasm::ModuleParser::parseExport): 21 (JSC::Wasm::ModuleParser::parseCode): 22 (JSC::Wasm::ModuleParser::parseData): 23 * wasm/js/WebAssemblyModuleRecord.cpp: 24 (JSC::WebAssemblyModuleRecord::evaluate): the only sensible time to initialize the data section is after linking, but before calling start, I test for this but the spec isn't clear it's correct yet 25 1 26 2016-12-09 Karim H <karim@karhm.com> 2 27 -
trunk/Source/JavaScriptCore/wasm/WasmFormat.h
r209642 r209651 36 36 #include "WasmOps.h" 37 37 #include "WasmPageCount.h" 38 #include <memory> 39 #include <wtf/FastMalloc.h> 38 40 #include <wtf/Optional.h> 39 41 #include <wtf/Vector.h> … … 115 117 }; 116 118 119 struct Segment { 120 uint32_t offset; 121 uint32_t sizeInBytes; 122 // Bytes are allocated at the end. 123 static Segment* make(uint32_t offset, uint32_t sizeInBytes) 124 { 125 auto allocated = tryFastCalloc(sizeof(Segment) + sizeInBytes, 1); 126 Segment* segment; 127 if (!allocated.getValue(segment)) 128 return nullptr; 129 segment->offset = offset; 130 segment->sizeInBytes = sizeInBytes; 131 return segment; 132 } 133 static void destroy(Segment *segment) 134 { 135 fastFree(segment); 136 } 137 uint8_t& byte(uint32_t pos) 138 { 139 ASSERT(pos < sizeInBytes); 140 return *reinterpret_cast<uint8_t*>(reinterpret_cast<char*>(this) + sizeof(offset) + sizeof(sizeInBytes) + pos); 141 } 142 typedef std::unique_ptr<Segment, decltype(&Segment::destroy)> Ptr; 143 static Ptr makePtr(Segment* segment) 144 { 145 return Ptr(segment, &Segment::destroy); 146 } 147 }; 117 148 118 149 struct ModuleInformation { … … 126 157 Vector<Export> exports; 127 158 std::optional<uint32_t> startFunctionIndexSpace; 159 Vector<Segment::Ptr> data; 128 160 129 161 ~ModuleInformation(); -
trunk/Source/JavaScriptCore/wasm/WasmModuleParser.cpp
r209642 r209651 158 158 { 159 159 uint32_t count; 160 if (!parseVarUInt32(count)) 160 if (!parseVarUInt32(count) 161 || count == std::numeric_limits<uint32_t>::max() 162 || !m_module->signatures.tryReserveCapacity(count)) 161 163 return false; 162 164 if (verbose) 163 dataLogLn("count: ", count); 164 if (!m_module->signatures.tryReserveCapacity(count)) 165 return false; 165 dataLogLn(" count: ", count); 166 166 167 167 for (uint32_t i = 0; i < count; ++i) { … … 176 176 177 177 uint32_t argumentCount; 178 if (!parseVarUInt32(argumentCount))179 return false;180 181 if (verbose)182 dataLogLn("argumentCount: ", argumentCount);183 184 178 Vector<Type> argumentTypes; 185 if (!argumentTypes.tryReserveCapacity(argumentCount)) 186 return false; 187 188 for (unsigned i = 0; i != argumentCount; ++i) { 179 if (!parseVarUInt32(argumentCount) 180 || argumentCount == std::numeric_limits<uint32_t>::max() 181 || !argumentTypes.tryReserveCapacity(argumentCount)) 182 return false; 183 if (verbose) 184 dataLogLn(" argument count: ", argumentCount); 185 186 for (unsigned i = 0; i < argumentCount; ++i) { 189 187 Type argumentType; 190 188 if (!parseResultType(argumentType)) … … 217 215 { 218 216 uint32_t importCount; 219 if (!parseVarUInt32(importCount) )220 return false;221 if (!m_module->imports.tryReserveCapacity(importCount) // FIXME this over-allocates when we fix the FIXMEs below.217 if (!parseVarUInt32(importCount) 218 || importCount == std::numeric_limits<uint32_t>::max() 219 || !m_module->imports.tryReserveCapacity(importCount) // FIXME this over-allocates when we fix the FIXMEs below. 222 220 || !m_module->importFunctions.tryReserveCapacity(importCount) // FIXME this over-allocates when we fix the FIXMEs below. 223 221 || !m_functionIndexSpace.tryReserveCapacity(importCount)) // FIXME this over-allocates when we fix the FIXMEs below. We'll allocate some more here when we know how many functions to expect. 224 222 return false; 225 223 226 for (uint32_t importNumber = 0; importNumber !=importCount; ++importNumber) {224 for (uint32_t importNumber = 0; importNumber < importCount; ++importNumber) { 227 225 Import imp; 228 226 uint32_t moduleLen; … … 279 277 uint32_t count; 280 278 if (!parseVarUInt32(count) 279 || count == std::numeric_limits<uint32_t>::max() 281 280 || !m_module->internalFunctionSignatures.tryReserveCapacity(count) 282 281 || !m_functionLocationInBinary.tryReserveCapacity(count) … … 284 283 return false; 285 284 286 for (uint32_t i = 0; i !=count; ++i) {285 for (uint32_t i = 0; i < count; ++i) { 287 286 uint32_t typeNumber; 288 287 if (!parseVarUInt32(typeNumber) … … 375 374 uint32_t exportCount; 376 375 if (!parseVarUInt32(exportCount) 376 || exportCount == std::numeric_limits<uint32_t>::max() 377 377 || !m_module->exports.tryReserveCapacity(exportCount)) 378 378 return false; 379 379 380 for (uint32_t exportNumber = 0; exportNumber !=exportCount; ++exportNumber) {380 for (uint32_t exportNumber = 0; exportNumber < exportCount; ++exportNumber) { 381 381 Export exp; 382 382 uint32_t fieldLen; … … 386 386 return false; 387 387 exp.field = Identifier::fromString(m_vm, fieldString); 388 388 389 if (!parseExternalKind(exp.kind)) 389 390 return false; 391 390 392 switch (exp.kind) { 391 393 case External::Function: { … … 441 443 uint32_t count; 442 444 if (!parseVarUInt32(count) 445 || count == std::numeric_limits<uint32_t>::max() 443 446 || count != m_functionLocationInBinary.size()) 444 447 return false; 445 448 446 for (uint32_t i = 0; i !=count; ++i) {449 for (uint32_t i = 0; i < count; ++i) { 447 450 uint32_t functionSize; 448 451 if (!parseVarUInt32(functionSize) … … 461 464 bool ModuleParser::parseData() 462 465 { 463 // FIXME https://bugs.webkit.org/show_bug.cgi?id=161709 464 RELEASE_ASSERT_NOT_REACHED(); 466 uint32_t segmentCount; 467 if (!parseVarUInt32(segmentCount) 468 || segmentCount == std::numeric_limits<uint32_t>::max() 469 || !m_module->data.tryReserveCapacity(segmentCount)) 470 return false; 471 if (verbose) 472 dataLogLn(" segments: ", segmentCount); 473 474 for (uint32_t segmentNumber = 0; segmentNumber < segmentCount; ++segmentNumber) { 475 if (verbose) 476 dataLogLn(" segment #", segmentNumber); 477 uint32_t index; 478 uint8_t opcode; 479 uint32_t offset; 480 uint8_t endOpcode; 481 uint32_t dataByteLength; 482 if (!parseVarUInt32(index) 483 || index) 484 return false; 485 486 // FIXME allow complex init_expr here. https://bugs.webkit.org/show_bug.cgi?id=165700 487 // For now we only handle i32.const as offset. 488 if (!parseUInt8(opcode) 489 || opcode != Wasm::I32Const 490 || !parseVarUInt32(offset) 491 || !parseUInt8(endOpcode) 492 || endOpcode != Wasm::End) 493 return false; 494 if (verbose) 495 dataLogLn(" offset: ", offset); 496 497 if (!parseVarUInt32(dataByteLength) 498 || dataByteLength == std::numeric_limits<uint32_t>::max()) 499 return false; 500 if (verbose) 501 dataLogLn(" data bytes: ", dataByteLength); 502 503 Segment* segment = Segment::make(offset, dataByteLength); 504 if (!segment) 505 return false; 506 m_module->data.uncheckedAppend(Segment::makePtr(segment)); 507 for (uint32_t dataByte = 0; dataByte < dataByteLength; ++dataByte) { 508 uint8_t byte; 509 if (!parseUInt8(byte)) 510 return false; 511 segment->byte(dataByte) = byte; 512 if (verbose) 513 dataLogLn(" [", dataByte, "] = ", segment->byte(dataByte)); 514 } 515 } 465 516 return true; 466 517 } -
trunk/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp
r209642 r209651 188 188 JSValue WebAssemblyModuleRecord::evaluate(ExecState* state) 189 189 { 190 VM& vm = state->vm(); 191 auto scope = DECLARE_THROW_SCOPE(vm); 192 193 if (JSWebAssemblyMemory* jsMemory = m_instance->memory()) { 194 uint8_t* memory = reinterpret_cast<uint8_t*>(jsMemory->memory()->memory()); 195 auto sizeInBytes = jsMemory->memory()->size(); 196 if (memory) { 197 const Vector<Wasm::Segment::Ptr>& data = m_instance->module()->moduleInformation().data; 198 for (auto& segment : data) { 199 if (segment->sizeInBytes) { 200 if (sizeInBytes < segment->sizeInBytes 201 || segment->offset > sizeInBytes 202 || segment->offset > sizeInBytes - segment->sizeInBytes) 203 return throwException(state, scope, createRangeError(state, ASCIILiteral("Data segment initializes memory out of range"))); 204 memcpy(memory + segment->offset, &segment->byte(0), segment->sizeInBytes); 205 } 206 } 207 } 208 } 209 190 210 if (WebAssemblyFunction* startFunction = m_startFunction.get()) { 191 VM& vm = state->vm();192 auto scope = DECLARE_THROW_SCOPE(vm);193 211 ProtoCallFrame protoCallFrame; 194 212 protoCallFrame.init(nullptr, startFunction, JSValue(), 1, nullptr);
Note: See TracChangeset
for help on using the changeset viewer.