Changeset 209651 in webkit


Ignore:
Timestamp:
Dec 9, 2016 11:08:31 PM (7 years ago)
Author:
jfbastien@apple.com
Message:

WebAssembly: implement data section
https://bugs.webkit.org/show_bug.cgi?id=165696

Reviewed by Keith Miller.

As specified in https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md#data-section
Note that some of the interesting corner cases are ill-defined by the spec: https://github.com/WebAssembly/design/issues/897

JSTests:

  • wasm/Builder.js: create a data section from JavaScript
  • wasm/Builder_WebAssemblyBinary.js: assemble the data section into the proper binary encoding

(const.emitters.Data):

  • wasm/js-api/test_Data.js: Added.

(DataSection):
(DataSectionOffTheEnd):
(DataSectionPartlyOffTheEnd):
(DataSectionEmptyOffTheEnd):
(DataSectionSeenByStart):

  • wasm/self-test/test_BuilderJSON.js: make sure the JSON structure is fine (this sanity checks before going to binary)

Source/JavaScriptCore:

  • wasm/WasmFormat.h: segments are what represent sections of memory to initialize (similar to ELF's non-zero intializer data / rodata)

(JSC::Wasm::Segment::make):
(JSC::Wasm::Segment::destroy):
(JSC::Wasm::Segment::byte):
(JSC::Wasm::Segment::makePtr):

  • wasm/WasmModuleParser.cpp: parse the data section, and prevent a few overflows if a user passes in UINT_MAX (the loops would overflow)

(JSC::Wasm::ModuleParser::parseType):
(JSC::Wasm::ModuleParser::parseImport):
(JSC::Wasm::ModuleParser::parseFunction):
(JSC::Wasm::ModuleParser::parseExport):
(JSC::Wasm::ModuleParser::parseCode):
(JSC::Wasm::ModuleParser::parseData):

  • wasm/js/WebAssemblyModuleRecord.cpp:

(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

Location:
trunk
Files:
1 added
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r209642 r209651  
     12016-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
    1222016-12-09  JF Bastien  <jfbastien@apple.com>
    223
  • trunk/JSTests/wasm/Builder.js

    r209642 r209651  
    524524
    525525            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                };
    528558                break;
    529559
  • trunk/JSTests/wasm/Builder_WebAssemblyBinary.js

    r209642 r209651  
    157157    },
    158158
    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    },
    160173};
    161174
  • trunk/JSTests/wasm/self-test/test_BuilderJSON.js

    r209642 r209651  
    620620    assert.eq(j.section[1].data[0].code[3].name, "select");
    621621})();
     622// FIXME test type mismatch with select. https://bugs.webkit.org/show_bug.cgi?id=163267
    622623
    623624(function MemoryImport() {
     
    640641})();
    641642
    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  
     12016-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
    1262016-12-09  Karim H  <karim@karhm.com>
    227
  • trunk/Source/JavaScriptCore/wasm/WasmFormat.h

    r209642 r209651  
    3636#include "WasmOps.h"
    3737#include "WasmPageCount.h"
     38#include <memory>
     39#include <wtf/FastMalloc.h>
    3840#include <wtf/Optional.h>
    3941#include <wtf/Vector.h>
     
    115117};
    116118
     119struct 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};
    117148
    118149struct ModuleInformation {
     
    126157    Vector<Export> exports;
    127158    std::optional<uint32_t> startFunctionIndexSpace;
     159    Vector<Segment::Ptr> data;
    128160
    129161    ~ModuleInformation();
  • trunk/Source/JavaScriptCore/wasm/WasmModuleParser.cpp

    r209642 r209651  
    158158{
    159159    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))
    161163        return false;
    162164    if (verbose)
    163         dataLogLn("count: ", count);
    164     if (!m_module->signatures.tryReserveCapacity(count))
    165         return false;
     165        dataLogLn("  count: ", count);
    166166
    167167    for (uint32_t i = 0; i < count; ++i) {
     
    176176
    177177        uint32_t argumentCount;
    178         if (!parseVarUInt32(argumentCount))
    179             return false;
    180 
    181         if (verbose)
    182             dataLogLn("argumentCount: ", argumentCount);
    183 
    184178        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) {
    189187            Type argumentType;
    190188            if (!parseResultType(argumentType))
     
    217215{
    218216    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.
    222220        || !m_module->importFunctions.tryReserveCapacity(importCount) // FIXME this over-allocates when we fix the FIXMEs below.
    223221        || !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.
    224222        return false;
    225223
    226     for (uint32_t importNumber = 0; importNumber != importCount; ++importNumber) {
     224    for (uint32_t importNumber = 0; importNumber < importCount; ++importNumber) {
    227225        Import imp;
    228226        uint32_t moduleLen;
     
    279277    uint32_t count;
    280278    if (!parseVarUInt32(count)
     279        || count == std::numeric_limits<uint32_t>::max()
    281280        || !m_module->internalFunctionSignatures.tryReserveCapacity(count)
    282281        || !m_functionLocationInBinary.tryReserveCapacity(count)
     
    284283        return false;
    285284
    286     for (uint32_t i = 0; i != count; ++i) {
     285    for (uint32_t i = 0; i < count; ++i) {
    287286        uint32_t typeNumber;
    288287        if (!parseVarUInt32(typeNumber)
     
    375374    uint32_t exportCount;
    376375    if (!parseVarUInt32(exportCount)
     376        || exportCount == std::numeric_limits<uint32_t>::max()
    377377        || !m_module->exports.tryReserveCapacity(exportCount))
    378378        return false;
    379379
    380     for (uint32_t exportNumber = 0; exportNumber != exportCount; ++exportNumber) {
     380    for (uint32_t exportNumber = 0; exportNumber < exportCount; ++exportNumber) {
    381381        Export exp;
    382382        uint32_t fieldLen;
     
    386386            return false;
    387387        exp.field = Identifier::fromString(m_vm, fieldString);
     388
    388389        if (!parseExternalKind(exp.kind))
    389390            return false;
     391
    390392        switch (exp.kind) {
    391393        case External::Function: {
     
    441443    uint32_t count;
    442444    if (!parseVarUInt32(count)
     445        || count == std::numeric_limits<uint32_t>::max()
    443446        || count != m_functionLocationInBinary.size())
    444447        return false;
    445448
    446     for (uint32_t i = 0; i != count; ++i) {
     449    for (uint32_t i = 0; i < count; ++i) {
    447450        uint32_t functionSize;
    448451        if (!parseVarUInt32(functionSize)
     
    461464bool ModuleParser::parseData()
    462465{
    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    }
    465516    return true;
    466517}
  • trunk/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp

    r209642 r209651  
    188188JSValue WebAssemblyModuleRecord::evaluate(ExecState* state)
    189189{
     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
    190210    if (WebAssemblyFunction* startFunction = m_startFunction.get()) {
    191         VM& vm = state->vm();
    192         auto scope = DECLARE_THROW_SCOPE(vm);
    193211        ProtoCallFrame protoCallFrame;
    194212        protoCallFrame.init(nullptr, startFunction, JSValue(), 1, nullptr);
Note: See TracChangeset for help on using the changeset viewer.