WebAssembly: implement data section
[WebKit-https.git] / JSTests / wasm / Builder_WebAssemblyBinary.js
1 /*
2  * Copyright (C) 2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 import * as assert from 'assert.js';
27 import LowLevelBinary from 'LowLevelBinary.js';
28 import * as WASM from 'WASM.js';
29
30 const put = (bin, type, value) => bin[type](value);
31
32 const emitters = {
33     Type: (section, bin) => {
34         put(bin, "varuint32", section.data.length);
35         for (const entry of section.data) {
36             put(bin, "varint7", WASM.typeValue["func"]);
37             put(bin, "varuint32", entry.params.length);
38             for (const param of entry.params)
39                 put(bin, "varint7", WASM.typeValue[param]);
40             if (entry.ret === "void")
41                 put(bin, "varuint1", 0);
42             else {
43                 put(bin, "varuint1", 1);
44                 put(bin, "varint7", WASM.typeValue[entry.ret]);
45             }
46         }
47     },
48     Import: (section, bin) => {
49         put(bin, "varuint32", section.data.length);
50         for (const entry of section.data) {
51             put(bin, "string", entry.module);
52             put(bin, "string", entry.field);
53             put(bin, "uint8", WASM.externalKindValue[entry.kind]);
54             switch (entry.kind) {
55             default: throw new Error(`Implementation problem: unexpected kind ${entry.kind}`);
56             case "Function": {
57                 put(bin, "varuint32", entry.type);
58                 break;
59             }
60             case "Table": throw new Error(`Not yet implemented`);
61             case "Memory": {
62                 let {initial, maximum} = entry.memoryDescription;
63                 assert.truthy(typeof initial === "number", "We expect 'initial' to be a number");
64                 initial |= 0;
65                 let hasMaximum = 0;
66                 if (typeof maximum === "number") {
67                     maximum |= 0;
68                     hasMaximum = 1;
69                 } else {
70                     assert.truthy(typeof maximum === "undefined", "We expect 'maximum' to be a number if it's defined");
71                 }
72
73                 put(bin, "varuint1", hasMaximum);
74                 put(bin, "varuint32", initial);
75                 if (hasMaximum)
76                     put(bin, "varuint32", maximum);
77                 break;
78             };
79             case "Global": throw new Error(`Not yet implemented`);
80             }
81         }
82     },
83
84     Function: (section, bin) => {
85         put(bin, "varuint32", section.data.length);
86         for (const signature of section.data)
87             put(bin, "varuint32", signature);
88     },
89
90     Table: (section, bin) => { throw new Error(`Not yet implemented`); },
91
92     Memory: (section, bin) => {
93         // Flags, currently can only be [0,1]
94         put(bin, "varuint1", section.data.length);
95         for (const memory of section.data) {
96             put(bin, "varuint32", memory.max ? 1 : 0);
97             put(bin, "varuint32", memory.initial);
98             if (memory.max)
99                 put(bin, "varuint32", memory.max);
100         }
101     },
102
103     Global: (section, bin) => { throw new Error(`Not yet implemented`); },
104     Export: (section, bin) => {
105         put(bin, "varuint32", section.data.length);
106         for (const entry of section.data) {
107             put(bin, "string", entry.field);
108             put(bin, "uint8", WASM.externalKindValue[entry.kind]);
109             switch (entry.kind) {
110             default: throw new Error(`Implementation problem: unexpected kind ${entry.kind}`);
111             case "Function": put(bin, "varuint32", entry.index); break;
112             case "Table": throw new Error(`Not yet implemented`);
113             case "Memory": throw new Error(`Not yet implemented`);
114             case "Global": throw new Error(`Not yet implemented`);
115             }
116         }
117     },
118     Start: (section, bin) => {
119         put(bin, "varuint32", section.data[0]);
120     },
121     Element: (section, bin) => { throw new Error(`Not yet implemented`); },
122
123     Code: (section, bin) => {
124         put(bin, "varuint32", section.data.length);
125         for (const func of section.data) {
126             let funcBin = bin.newPatchable("varuint32");
127             const localCount = func.locals.length - func.parameterCount;
128             put(funcBin, "varuint32", localCount);
129             for (let i = func.parameterCount; i < func.locals.length; ++i) {
130                 put(funcBin, "varuint32", 1);
131                 put(funcBin, "varint7", WASM.typeValue[func.locals[i]]);
132             }
133
134             for (const op of func.code) {
135                 put(funcBin, "uint8", op.value);
136                 if (op.arguments.length !== 0)
137                     throw new Error(`Unimplemented: arguments`); // FIXME https://bugs.webkit.org/show_bug.cgi?id=162706
138
139                 switch (op.name) {
140                 default:
141                     for (let i = 0; i < op.immediates.length; ++i) {
142                         const type = WASM.description.opcode[op.name].immediate[i].type
143                         if (!funcBin[type])
144                             throw new TypeError(`Unknown type: ${type} in op: ${op.name}`);
145                         put(funcBin, type, op.immediates[i]);
146                     }
147                     break;
148                 case "br_table":
149                     put(funcBin, "varuint32", op.immediates.length - 1);
150                     for (let imm of op.immediates)
151                         put(funcBin, "varuint32", imm);
152                     break;
153                 }
154             }
155             funcBin.apply();
156         }
157     },
158
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     },
173 };
174
175 export const Binary = (preamble, sections) => {
176     let wasmBin = new LowLevelBinary();
177     for (const p of WASM.description.preamble)
178         put(wasmBin, p.type, preamble[p.name]);
179     for (const section of sections) {
180         put(wasmBin, WASM.sectionEncodingType, section.id);
181         let sectionBin = wasmBin.newPatchable("varuint32");
182         const emitter = emitters[section.name];
183         if (emitter)
184             emitter(section, sectionBin);
185         else {
186             // Unknown section.
187             put(sectionBin, "string", section.name);
188             for (const byte of section.data)
189                 put(sectionBin, "uint8", byte);
190         }
191         sectionBin.apply();
192     }
193     wasmBin.trim();
194     return wasmBin;
195 };