2 * Copyright (C) 2017 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
26 const _fail = (msg, extra) => {
27 throw new Error(msg + (extra ? ": " + extra : ""));
32 const isNotA = assert.isNotA = (v, t, msg) => {
34 _fail(`Shouldn't be ${t}`, msg);
37 const isA = assert.isA = (v, t, msg) => {
39 _fail(`Should be ${t}, got ${typeof(v)}`, msg);
42 const isNotUndef = assert.isNotUndef = (v, msg) => isNotA(v, "undefined", msg);
43 assert.isUndef = (v, msg) => isA(v, "undefined", msg);
44 assert.notObject = (v, msg) => isNotA(v, "object", msg);
45 const isObject = assert.isObject = (v, msg) => isA(v, "object", msg);
46 assert.notString = (v, msg) => isNotA(v, "string", msg);
47 assert.isString = (v, msg) => isA(v, "string", msg);
48 assert.notNumber = (v, msg) => isNotA(v, "number", msg);
49 assert.isNumber = (v, msg) => isA(v, "number", msg);
50 assert.notFunction = (v, msg) => isNotA(v, "function", msg);
51 assert.isFunction = (v, msg) => isA(v, "function", msg);
53 assert.hasObjectProperty = (o, p, msg) => {
55 isNotUndef(o[p], msg, `expected object to have property ${p}`);
58 assert.isArray = (v, msg) => {
59 if (!Array.isArray(v))
60 _fail(`Expected an array, got ${typeof(v)}`, msg);
63 assert.isNotArray = (v, msg) => {
65 _fail(`Expected to not be an array`, msg);
68 assert.truthy = (v, msg) => {
70 _fail(`Expected truthy`, msg);
73 assert.falsy = (v, msg) => {
75 _fail(`Expected falsy`, msg);
78 assert.eq = (lhs, rhs, msg) => {
79 if (typeof lhs !== typeof rhs)
80 _fail(`Not the same: "${lhs}" and "${rhs}"`, msg);
81 if (Array.isArray(lhs) && Array.isArray(rhs) && (lhs.length === rhs.length)) {
82 for (let i = 0; i !== lhs.length; ++i)
83 eq(lhs[i], rhs[i], msg);
84 } else if (lhs !== rhs) {
85 if (typeof lhs === "number" && isNaN(lhs) && isNaN(rhs))
87 _fail(`Not the same: "${lhs}" and "${rhs}"`, msg);
89 if (typeof lhs === "number" && (1.0 / lhs !== 1.0 / rhs)) // Distinguish -0.0 from 0.0.
90 _fail(`Not the same: "${lhs}" and "${rhs}"`, msg);
94 const canonicalizeI32 = (number) => {
95 if (Math.round(number) === number && number >= 2 ** 31)
96 number = number - 2 ** 32;
100 assert.eqI32 = (lhs, rhs, msg) => {
101 return eq(canonicalizeI32(lhs), canonicalizeI32(rhs), msg);
104 assert.ge = (lhs, rhs, msg) => {
108 _fail(`Expected: "${lhs}" < "${rhs}"`, msg);
111 assert.le = (lhs, rhs, msg) => {
115 _fail(`Expected: "${lhs}" > "${rhs}"`, msg);
118 const _throws = (func, type, message, ...args) => {
122 if (e instanceof type) {
123 if (e.message === message)
125 // Ignore source information at the end of the error message if the
126 // expected message didn't specify that information. Sometimes it
127 // changes, or it's tricky to get just right.
128 const evaluatingIndex = e.message.indexOf(" (evaluating '");
129 if (evaluatingIndex !== -1) {
130 const cleanMessage = e.message.substring(0, evaluatingIndex);
131 if (cleanMessage === message)
135 _fail(`Expected to throw a ${type.name} with message "${message}", got ${e.name} with message "${e.message}"`);
137 _fail(`Expected to throw a ${type.name} with message "${message}"`);
140 const _instanceof = (obj, type, msg) => {
141 if (!(obj instanceof type))
142 _fail(`Expected a ${typeof(type)}, got ${typeof obj}`);
145 // Use underscore names to avoid clashing with builtin names.
146 assert.throws = _throws;
147 assert.instanceof = _instanceof;
149 const asyncTestImpl = (promise, thenFunc, catchFunc) => {
151 promise.then(thenFunc).catch(catchFunc);
154 const printExn = (e) => {
155 print("Failed: ", e);
159 assert.asyncTest = (promise) => asyncTestImpl(promise, asyncTestPassed, printExn);
160 assert.asyncTestEq = (promise, expected) => {
161 const thenCheck = (value) => {
162 if (value === expected)
163 return asyncTestPassed();
164 print("Failed: got ", value, " but expected ", expected);
167 asyncTestImpl(promise, thenCheck, printExn);
172 "comments": ["This file describes the WebAssembly ISA.",
173 "Scripts in this folder auto-generate C++ code for JavaScriptCore as well as the testing DSL which WebKit's WebAssembly tests use."
176 { "name": "magic number", "type": "uint32", "value": 1836278016, "description": "NULL character followed by 'asm'" },
177 { "name": "version", "type": "uint32", "value": 1, "description": "Version number" }
180 "i32": { "type": "varint7", "value": -1, "b3type": "B3::Int32" },
181 "i64": { "type": "varint7", "value": -2, "b3type": "B3::Int64" },
182 "f32": { "type": "varint7", "value": -3, "b3type": "B3::Float" },
183 "f64": { "type": "varint7", "value": -4, "b3type": "B3::Double" },
184 "anyfunc": { "type": "varint7", "value": -16, "b3type": "B3::Void" },
185 "func": { "type": "varint7", "value": -32, "b3type": "B3::Void" },
186 "void": { "type": "varint7", "value": -64, "b3type": "B3::Void" }
188 "value_type": ["i32", "i64", "f32", "f64"],
189 "block_type": ["i32", "i64", "f32", "f64", "void"],
190 "elem_type": ["anyfunc"],
192 "Function": { "type": "uint8", "value": 0 },
193 "Table": { "type": "uint8", "value": 1 },
194 "Memory": { "type": "uint8", "value": 2 },
195 "Global": { "type": "uint8", "value": 3 }
198 "Type": { "type": "varuint7", "value": 1, "description": "Function signature declarations" },
199 "Import": { "type": "varuint7", "value": 2, "description": "Import declarations" },
200 "Function": { "type": "varuint7", "value": 3, "description": "Function declarations" },
201 "Table": { "type": "varuint7", "value": 4, "description": "Indirect function table and other tables" },
202 "Memory": { "type": "varuint7", "value": 5, "description": "Memory attributes" },
203 "Global": { "type": "varuint7", "value": 6, "description": "Global declarations" },
204 "Export": { "type": "varuint7", "value": 7, "description": "Exports" },
205 "Start": { "type": "varuint7", "value": 8, "description": "Start function declaration" },
206 "Element": { "type": "varuint7", "value": 9, "description": "Elements section" },
207 "Code": { "type": "varuint7", "value": 10, "description": "Function bodies (code)" },
208 "Data": { "type": "varuint7", "value": 11, "description": "Data segments" }
211 "unreachable": { "category": "control", "value": 0, "return": [], "parameter": [], "immediate": [], "description": "trap immediately" },
212 "block": { "category": "control", "value": 2, "return": ["control"], "parameter": [], "immediate": [{"name": "sig", "type": "block_type"}], "description": "begin a sequence of expressions, yielding 0 or 1 values" },
213 "loop": { "category": "control", "value": 3, "return": ["control"], "parameter": [], "immediate": [{"name": "sig", "type": "block_type"}], "description": "begin a block which can also form control flow loops" },
214 "if": { "category": "control", "value": 4, "return": ["control"], "parameter": ["bool"], "immediate": [{"name": "sig", "type": "block_type"}], "description": "begin if expression" },
215 "else": { "category": "control", "value": 5, "return": ["control"], "parameter": [], "immediate": [], "description": "begin else expression of if" },
216 "select": { "category": "control", "value": 27, "return": ["prev"], "parameter": ["any", "prev", "bool"], "immediate": [], "description": "select one of two values based on condition" },
217 "br": { "category": "control", "value": 12, "return": [], "parameter": [], "immediate": [{"name": "relative_depth", "type": "varuint32"}], "description": "break that targets an outer nested block" },
218 "br_if": { "category": "control", "value": 13, "return": [], "parameter": [], "immediate": [{"name": "relative_depth", "type": "varuint32"}], "description": "conditional break that targets an outer nested block" },
219 "br_table": { "category": "control", "value": 14, "return": [], "parameter": [], "immediate": [{"name": "target_count", "type": "varuint32", "description": "number of entries in the target_table"},
220 {"name": "target_table", "type": "varuint32*", "description": "target entries that indicate an outer block or loop to which to break"},
221 {"name": "default_target", "type": "varuint32", "description": "an outer block or loop to which to break in the default case"}],
222 "description": "branch table control flow construct" },
223 "return": { "category": "control", "value": 15, "return": [], "parameter": [], "immediate": [], "description": "return zero or one value from this function" },
224 "drop": { "category": "control", "value": 26, "return": [], "parameter": ["any"], "immediate": [], "description": "ignore value" },
225 "nop": { "category": "control", "value": 1, "return": [], "parameter": [], "immediate": [], "description": "no operation" },
226 "end": { "category": "control", "value": 11, "return": [], "parameter": [], "immediate": [], "description": "end a block, loop, or if" },
227 "i32.const": { "category": "special", "value": 65, "return": ["i32"], "parameter": [], "immediate": [{"name": "value", "type": "varint32"}], "description": "a constant value interpreted as i32" },
228 "i64.const": { "category": "special", "value": 66, "return": ["i64"], "parameter": [], "immediate": [{"name": "value", "type": "varint64"}], "description": "a constant value interpreted as i64" },
229 "f64.const": { "category": "special", "value": 68, "return": ["f64"], "parameter": [], "immediate": [{"name": "value", "type": "double"}], "description": "a constant value interpreted as f64" },
230 "f32.const": { "category": "special", "value": 67, "return": ["f32"], "parameter": [], "immediate": [{"name": "value", "type": "float"}], "description": "a constant value interpreted as f32" },
231 "get_local": { "category": "special", "value": 32, "return": ["any"], "parameter": [], "immediate": [{"name": "local_index", "type": "varuint32"}], "description": "read a local variable or parameter" },
232 "set_local": { "category": "special", "value": 33, "return": [], "parameter": ["any"], "immediate": [{"name": "local_index", "type": "varuint32"}], "description": "write a local variable or parameter" },
233 "tee_local": { "category": "special", "value": 34, "return": ["any"], "parameter": ["any"], "immediate": [{"name": "local_index", "type": "varuint32"}], "description": "write a local variable or parameter and return the same value" },
234 "get_global": { "category": "special", "value": 35, "return": ["any"], "parameter": [], "immediate": [{"name": "global_index", "type": "varuint32"}], "description": "read a global variable" },
235 "set_global": { "category": "special", "value": 36, "return": [], "parameter": ["any"], "immediate": [{"name": "global_index", "type": "varuint32"}], "description": "write a global variable" },
236 "call": { "category": "call", "value": 16, "return": ["call"], "parameter": ["call"], "immediate": [{"name": "function_index", "type": "varuint32"}], "description": "call a function by its index" },
237 "call_indirect": { "category": "call", "value": 17, "return": ["call"], "parameter": ["call"], "immediate": [{"name": "type_index", "type": "varuint32"}, {"name": "reserved", "type": "varuint1"}], "description": "call a function indirect with an expected signature" },
238 "i32.load8_s": { "category": "memory", "value": 44, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" },
239 "i32.load8_u": { "category": "memory", "value": 45, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" },
240 "i32.load16_s": { "category": "memory", "value": 46, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" },
241 "i32.load16_u": { "category": "memory", "value": 47, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" },
242 "i64.load8_s": { "category": "memory", "value": 48, "return": ["i64"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" },
243 "i64.load8_u": { "category": "memory", "value": 49, "return": ["i64"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" },
244 "i64.load16_s": { "category": "memory", "value": 50, "return": ["i64"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" },
245 "i64.load16_u": { "category": "memory", "value": 51, "return": ["i64"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" },
246 "i64.load32_s": { "category": "memory", "value": 52, "return": ["i64"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" },
247 "i64.load32_u": { "category": "memory", "value": 53, "return": ["i64"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" },
248 "i32.load": { "category": "memory", "value": 40, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" },
249 "i64.load": { "category": "memory", "value": 41, "return": ["i64"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" },
250 "f32.load": { "category": "memory", "value": 42, "return": ["f32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" },
251 "f64.load": { "category": "memory", "value": 43, "return": ["f64"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" },
252 "i32.store8": { "category": "memory", "value": 58, "return": [], "parameter": ["addr", "i32"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" },
253 "i32.store16": { "category": "memory", "value": 59, "return": [], "parameter": ["addr", "i32"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" },
254 "i64.store8": { "category": "memory", "value": 60, "return": [], "parameter": ["addr", "i64"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" },
255 "i64.store16": { "category": "memory", "value": 61, "return": [], "parameter": ["addr", "i64"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" },
256 "i64.store32": { "category": "memory", "value": 62, "return": [], "parameter": ["addr", "i64"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" },
257 "i32.store": { "category": "memory", "value": 54, "return": [], "parameter": ["addr", "i32"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" },
258 "i64.store": { "category": "memory", "value": 55, "return": [], "parameter": ["addr", "i64"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" },
259 "f32.store": { "category": "memory", "value": 56, "return": [], "parameter": ["addr", "f32"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" },
260 "f64.store": { "category": "memory", "value": 57, "return": [], "parameter": ["addr", "f64"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" },
261 "current_memory": { "category": "operation", "value": 63, "return": ["size"], "parameter": [], "immediate": [{"name": "flags", "type": "varuint32"}], "description": "query the size of memory" },
262 "grow_memory": { "category": "operation", "value": 64, "return": ["size"], "parameter": ["size"], "immediate": [{"name": "flags", "type": "varuint32"}], "description": "grow the size of memory" },
263 "i32.add": { "category": "arithmetic", "value": 106, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "Add" },
264 "i32.sub": { "category": "arithmetic", "value": 107, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "Sub" },
265 "i32.mul": { "category": "arithmetic", "value": 108, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "Mul" },
266 "i32.div_s": { "category": "arithmetic", "value": 109, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [] },
267 "i32.div_u": { "category": "arithmetic", "value": 110, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [] },
268 "i32.rem_s": { "category": "arithmetic", "value": 111, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [] },
269 "i32.rem_u": { "category": "arithmetic", "value": 112, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [] },
270 "i32.and": { "category": "arithmetic", "value": 113, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "BitAnd" },
271 "i32.or": { "category": "arithmetic", "value": 114, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "BitOr" },
272 "i32.xor": { "category": "arithmetic", "value": 115, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "BitXor" },
273 "i32.shl": { "category": "arithmetic", "value": 116, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "Shl" },
274 "i32.shr_u": { "category": "arithmetic", "value": 118, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "ZShr" },
275 "i32.shr_s": { "category": "arithmetic", "value": 117, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "SShr" },
276 "i32.rotr": { "category": "arithmetic", "value": 120, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "RotR" },
277 "i32.rotl": { "category": "arithmetic", "value": 119, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "RotL" },
278 "i32.eq": { "category": "comparison", "value": 70, "return": ["bool"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "Equal" },
279 "i32.ne": { "category": "comparison", "value": 71, "return": ["bool"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "NotEqual" },
280 "i32.lt_s": { "category": "comparison", "value": 72, "return": ["bool"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "LessThan" },
281 "i32.le_s": { "category": "comparison", "value": 76, "return": ["bool"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "LessEqual" },
282 "i32.lt_u": { "category": "comparison", "value": 73, "return": ["bool"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "Below" },
283 "i32.le_u": { "category": "comparison", "value": 77, "return": ["bool"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "BelowEqual" },
284 "i32.gt_s": { "category": "comparison", "value": 74, "return": ["bool"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "GreaterThan" },
285 "i32.ge_s": { "category": "comparison", "value": 78, "return": ["bool"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "GreaterEqual" },
286 "i32.gt_u": { "category": "comparison", "value": 75, "return": ["bool"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "Above" },
287 "i32.ge_u": { "category": "comparison", "value": 79, "return": ["bool"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "AboveEqual" },
288 "i32.clz": { "category": "arithmetic", "value": 103, "return": ["i32"], "parameter": ["i32"], "immediate": [], "b3op": "Clz" },
289 "i32.ctz": { "category": "arithmetic", "value": 104, "return": ["i32"], "parameter": ["i32"], "immediate": [] },
290 "i32.popcnt": { "category": "arithmetic", "value": 105, "return": ["i32"], "parameter": ["i32"], "immediate": [] },
291 "i32.eqz": { "category": "comparison", "value": 69, "return": ["bool"], "parameter": ["i32"], "immediate": [], "b3op": "Equal(i32(0), @0)" },
292 "i64.add": { "category": "arithmetic", "value": 124, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "Add" },
293 "i64.sub": { "category": "arithmetic", "value": 125, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "Sub" },
294 "i64.mul": { "category": "arithmetic", "value": 126, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "Mul" },
295 "i64.div_s": { "category": "arithmetic", "value": 127, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [] },
296 "i64.div_u": { "category": "arithmetic", "value": 128, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [] },
297 "i64.rem_s": { "category": "arithmetic", "value": 129, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [] },
298 "i64.rem_u": { "category": "arithmetic", "value": 130, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [] },
299 "i64.and": { "category": "arithmetic", "value": 131, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "BitAnd" },
300 "i64.or": { "category": "arithmetic", "value": 132, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "BitOr" },
301 "i64.xor": { "category": "arithmetic", "value": 133, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "BitXor" },
302 "i64.shl": { "category": "arithmetic", "value": 134, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "Shl(@0, Trunc(@1))" },
303 "i64.shr_u": { "category": "arithmetic", "value": 136, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "ZShr(@0, Trunc(@1))" },
304 "i64.shr_s": { "category": "arithmetic", "value": 135, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "SShr(@0, Trunc(@1))" },
305 "i64.rotr": { "category": "arithmetic", "value": 138, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "RotR(@0, Trunc(@1))" },
306 "i64.rotl": { "category": "arithmetic", "value": 137, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "RotL(@0, Trunc(@1))" },
307 "i64.eq": { "category": "comparison", "value": 81, "return": ["bool"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "Equal" },
308 "i64.ne": { "category": "comparison", "value": 82, "return": ["bool"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "NotEqual" },
309 "i64.lt_s": { "category": "comparison", "value": 83, "return": ["bool"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "LessThan" },
310 "i64.le_s": { "category": "comparison", "value": 87, "return": ["bool"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "LessEqual" },
311 "i64.lt_u": { "category": "comparison", "value": 84, "return": ["bool"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "Below" },
312 "i64.le_u": { "category": "comparison", "value": 88, "return": ["bool"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "BelowEqual" },
313 "i64.gt_s": { "category": "comparison", "value": 85, "return": ["bool"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "GreaterThan" },
314 "i64.ge_s": { "category": "comparison", "value": 89, "return": ["bool"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "GreaterEqual" },
315 "i64.gt_u": { "category": "comparison", "value": 86, "return": ["bool"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "Above" },
316 "i64.ge_u": { "category": "comparison", "value": 90, "return": ["bool"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "AboveEqual" },
317 "i64.clz": { "category": "arithmetic", "value": 121, "return": ["i64"], "parameter": ["i64"], "immediate": [], "b3op": "Clz" },
318 "i64.ctz": { "category": "arithmetic", "value": 122, "return": ["i64"], "parameter": ["i64"], "immediate": [] },
319 "i64.popcnt": { "category": "arithmetic", "value": 123, "return": ["i64"], "parameter": ["i64"], "immediate": [] },
320 "i64.eqz": { "category": "comparison", "value": 80, "return": ["bool"], "parameter": ["i64"], "immediate": [], "b3op": "Equal(i64(0), @0)" },
321 "f32.add": { "category": "arithmetic", "value": 146, "return": ["f32"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "Add" },
322 "f32.sub": { "category": "arithmetic", "value": 147, "return": ["f32"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "Sub" },
323 "f32.mul": { "category": "arithmetic", "value": 148, "return": ["f32"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "Mul" },
324 "f32.div": { "category": "arithmetic", "value": 149, "return": ["f32"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "Div" },
325 "f32.min": { "category": "arithmetic", "value": 150, "return": ["f32"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "Select(Equal(@0, @1), BitOr(@0, @1), Select(LessThan(@0, @1), @0, @1))" },
326 "f32.max": { "category": "arithmetic", "value": 151, "return": ["f32"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "Select(Equal(@0, @1), BitAnd(@0, @1), Select(LessThan(@0, @1), @1, @0))" },
327 "f32.abs": { "category": "arithmetic", "value": 139, "return": ["f32"], "parameter": ["f32"], "immediate": [], "b3op": "Abs" },
328 "f32.neg": { "category": "arithmetic", "value": 140, "return": ["f32"], "parameter": ["f32"], "immediate": [], "b3op": "Neg" },
329 "f32.copysign": { "category": "arithmetic", "value": 152, "return": ["f32"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "BitwiseCast(BitOr(BitAnd(BitwiseCast(@1), i32(0x80000000)), BitAnd(BitwiseCast(@0), i32(0x7fffffff))))" },
330 "f32.ceil": { "category": "arithmetic", "value": 141, "return": ["f32"], "parameter": ["f32"], "immediate": [], "b3op": "Ceil" },
331 "f32.floor": { "category": "arithmetic", "value": 142, "return": ["f32"], "parameter": ["f32"], "immediate": [], "b3op": "Floor" },
332 "f32.trunc": { "category": "arithmetic", "value": 143, "return": ["f32"], "parameter": ["f32"], "immediate": [] },
333 "f32.nearest": { "category": "arithmetic", "value": 144, "return": ["f32"], "parameter": ["f32"], "immediate": [] },
334 "f32.sqrt": { "category": "arithmetic", "value": 145, "return": ["f32"], "parameter": ["f32"], "immediate": [], "b3op": "Sqrt" },
335 "f32.eq": { "category": "comparison", "value": 91, "return": ["bool"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "Equal" },
336 "f32.ne": { "category": "comparison", "value": 92, "return": ["bool"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "NotEqual" },
337 "f32.lt": { "category": "comparison", "value": 93, "return": ["bool"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "LessThan" },
338 "f32.le": { "category": "comparison", "value": 95, "return": ["bool"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "LessEqual" },
339 "f32.gt": { "category": "comparison", "value": 94, "return": ["bool"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "GreaterThan" },
340 "f32.ge": { "category": "comparison", "value": 96, "return": ["bool"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "GreaterEqual" },
341 "f64.add": { "category": "arithmetic", "value": 160, "return": ["f64"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "Add" },
342 "f64.sub": { "category": "arithmetic", "value": 161, "return": ["f64"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "Sub" },
343 "f64.mul": { "category": "arithmetic", "value": 162, "return": ["f64"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "Mul" },
344 "f64.div": { "category": "arithmetic", "value": 163, "return": ["f64"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "Div" },
345 "f64.min": { "category": "arithmetic", "value": 164, "return": ["f64"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "Select(Equal(@0, @1), BitOr(@0, @1), Select(LessThan(@0, @1), @0, @1))" },
346 "f64.max": { "category": "arithmetic", "value": 165, "return": ["f64"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "Select(Equal(@0, @1), BitAnd(@0, @1), Select(LessThan(@0, @1), @1, @0))" },
347 "f64.abs": { "category": "arithmetic", "value": 153, "return": ["f64"], "parameter": ["f64"], "immediate": [], "b3op": "Abs" },
348 "f64.neg": { "category": "arithmetic", "value": 154, "return": ["f64"], "parameter": ["f64"], "immediate": [], "b3op": "Neg" },
349 "f64.copysign": { "category": "arithmetic", "value": 166, "return": ["f64"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "BitwiseCast(BitOr(BitAnd(BitwiseCast(@1), i64(0x8000000000000000)), BitAnd(BitwiseCast(@0), i64(0x7fffffffffffffff))))" },
350 "f64.ceil": { "category": "arithmetic", "value": 155, "return": ["f64"], "parameter": ["f64"], "immediate": [], "b3op": "Ceil" },
351 "f64.floor": { "category": "arithmetic", "value": 156, "return": ["f64"], "parameter": ["f64"], "immediate": [], "b3op": "Floor" },
352 "f64.trunc": { "category": "arithmetic", "value": 157, "return": ["f64"], "parameter": ["f64"], "immediate": [] },
353 "f64.nearest": { "category": "arithmetic", "value": 158, "return": ["f64"], "parameter": ["f64"], "immediate": [] },
354 "f64.sqrt": { "category": "arithmetic", "value": 159, "return": ["f64"], "parameter": ["f64"], "immediate": [], "b3op": "Sqrt" },
355 "f64.eq": { "category": "comparison", "value": 97, "return": ["bool"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "Equal" },
356 "f64.ne": { "category": "comparison", "value": 98, "return": ["bool"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "NotEqual" },
357 "f64.lt": { "category": "comparison", "value": 99, "return": ["bool"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "LessThan" },
358 "f64.le": { "category": "comparison", "value": 101, "return": ["bool"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "LessEqual" },
359 "f64.gt": { "category": "comparison", "value": 100, "return": ["bool"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "GreaterThan" },
360 "f64.ge": { "category": "comparison", "value": 102, "return": ["bool"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "GreaterEqual" },
361 "i32.trunc_s/f32": { "category": "conversion", "value": 168, "return": ["i32"], "parameter": ["f32"], "immediate": [] },
362 "i32.trunc_s/f64": { "category": "conversion", "value": 170, "return": ["i32"], "parameter": ["f64"], "immediate": [] },
363 "i32.trunc_u/f32": { "category": "conversion", "value": 169, "return": ["i32"], "parameter": ["f32"], "immediate": [] },
364 "i32.trunc_u/f64": { "category": "conversion", "value": 171, "return": ["i32"], "parameter": ["f64"], "immediate": [] },
365 "i32.wrap/i64": { "category": "conversion", "value": 167, "return": ["i32"], "parameter": ["i64"], "immediate": [], "b3op": "Trunc" },
366 "i64.trunc_s/f32": { "category": "conversion", "value": 174, "return": ["i64"], "parameter": ["f32"], "immediate": [] },
367 "i64.trunc_s/f64": { "category": "conversion", "value": 176, "return": ["i64"], "parameter": ["f64"], "immediate": [] },
368 "i64.trunc_u/f32": { "category": "conversion", "value": 175, "return": ["i64"], "parameter": ["f32"], "immediate": [] },
369 "i64.trunc_u/f64": { "category": "conversion", "value": 177, "return": ["i64"], "parameter": ["f64"], "immediate": [] },
370 "i64.extend_s/i32": { "category": "conversion", "value": 172, "return": ["i64"], "parameter": ["i32"], "immediate": [], "b3op": "SExt32" },
371 "i64.extend_u/i32": { "category": "conversion", "value": 173, "return": ["i64"], "parameter": ["i32"], "immediate": [], "b3op": "ZExt32" },
372 "f32.convert_s/i32": { "category": "conversion", "value": 178, "return": ["f32"], "parameter": ["i32"], "immediate": [], "b3op": "IToF" },
373 "f32.convert_u/i32": { "category": "conversion", "value": 179, "return": ["f32"], "parameter": ["i32"], "immediate": [], "b3op": "IToF(ZExt32(@0))" },
374 "f32.convert_s/i64": { "category": "conversion", "value": 180, "return": ["f32"], "parameter": ["i64"], "immediate": [], "b3op": "IToF" },
375 "f32.convert_u/i64": { "category": "conversion", "value": 181, "return": ["f32"], "parameter": ["i64"], "immediate": [] },
376 "f32.demote/f64": { "category": "conversion", "value": 182, "return": ["f32"], "parameter": ["f64"], "immediate": [], "b3op": "DoubleToFloat"},
377 "f32.reinterpret/i32": { "category": "conversion", "value": 190, "return": ["f32"], "parameter": ["i32"], "immediate": [], "b3op": "BitwiseCast" },
378 "f64.convert_s/i32": { "category": "conversion", "value": 183, "return": ["f64"], "parameter": ["i32"], "immediate": [], "b3op": "IToD" },
379 "f64.convert_u/i32": { "category": "conversion", "value": 184, "return": ["f64"], "parameter": ["i32"], "immediate": [], "b3op": "IToD(ZExt32(@0))" },
380 "f64.convert_s/i64": { "category": "conversion", "value": 185, "return": ["f64"], "parameter": ["i64"], "immediate": [], "b3op": "IToD" },
381 "f64.convert_u/i64": { "category": "conversion", "value": 186, "return": ["f64"], "parameter": ["i64"], "immediate": [] },
382 "f64.promote/f32": { "category": "conversion", "value": 187, "return": ["f64"], "parameter": ["f32"], "immediate": [], "b3op": "FloatToDouble"},
383 "f64.reinterpret/i64": { "category": "conversion", "value": 191, "return": ["f64"], "parameter": ["i64"], "immediate": [], "b3op": "BitwiseCast" },
384 "i32.reinterpret/f32": { "category": "conversion", "value": 188, "return": ["i32"], "parameter": ["f32"], "immediate": [], "b3op": "BitwiseCast" },
385 "i64.reinterpret/f64": { "category": "conversion", "value": 189, "return": ["i64"], "parameter": ["f64"], "immediate": [], "b3op": "BitwiseCast" }
392 ((typeof process === "object" && typeof require === "function") ? "node"
393 : (typeof window === "object" ? "web"
394 : (typeof importScripts === "function" ? "worker"
397 let _global = (typeof global !== 'object' || !global || global.Math !== Math || global.Array !== Array)
398 ? ((typeof self !== 'undefined') ? self
399 : (typeof window !== 'undefined') ? window
400 : (typeof global !== 'undefined') ? global
401 : Function('return this')())
404 const _eval = x => eval.call(null, x);
406 const _read = filename => {
407 switch (_environment) {
408 case "node": return read(filename);
409 case "web": // fallthrough
410 case "worker": let xhr = new XMLHttpRequest(); xhr.open("GET", filename, /*async=*/false); return xhr.responseText;
411 case "shell": return read(filename);
415 const _load = filename => {
416 switch (_environment) {
417 case "node": // fallthrough
418 case "web": // fallthrough
419 case "shell": return _eval(_read(filename));
420 case "worker": return importScripts(filename);
424 const _json = filename => {
425 assert.eq(filename, "wasm.json");
426 return JSON.parse(WASM_JSON);
428 switch (_environment) {
429 case "node": // fallthrough
430 case "shell": return JSON.parse(_read(filename));
431 case "web": // fallthrough
432 case "worker": let xhr = new XMLHttpRequest(); xhr.overrideMimeType("application/json"); xhr.open("GET", filename, /*async=*/false); return xhr.response;
436 const _dump = (what, name, pad = ' ') => {
438 try { return `"${v}"`; }
439 catch (e) { return `Error: "${e.message}"`; }
441 let s = `${pad}${name} ${typeof what}: ${value(what)}`;
442 for (let p in what) {
443 s += `\n${pad}${pad}${p}: ${value(what[p])} ${typeof v}`;
444 s += '\n' + _dump(what[p], p, pad + pad);
449 // Use underscore names to avoid clashing with builtin names.
460 const _mapValues = from => {
462 for (const key in from)
463 values[key] = from[key].value;
470 const description = WASM.description = utilities.json("wasm.json");
471 const type = WASM.type = Object.keys(description.type);
472 const _typeSet = new Set(type);
473 WASM.isValidType = v => _typeSet.has(v);
474 WASM.typeValue = _mapValues(description.type);
475 const _valueTypeSet = new Set(description.value_type);
476 WASM.isValidValueType = v => _valueTypeSet.has(v);
477 const _blockTypeSet = new Set(description.block_type);
478 WASM.isValidBlockType = v => _blockTypeSet.has(v);
479 WASM.externalKindValue = _mapValues(description.external_kind);
480 const sections = WASM.sections = Object.keys(description.section);
481 WASM.sectionEncodingType = description.section[sections[0]].type;
486 const _initialAllocationSize = 1024;
487 const _growAllocationSize = allocated => allocated * 2;
490 const varuint32Min = 0;
491 const varint7Min = -0b1000000;
492 const varint7Max = 0b111111;
493 const varuint7Max = 0b1111111;
494 const varuint32Max = ((((1 << 31) >>> 0) - 1) * 2) + 1;
495 const varint32Min = -((1 << 31) >>> 0);
496 const varint32Max = ((1 << 31) - 1) >>> 0;
497 const varBitsMax = 5;
499 const _getterRangeCheck = (llb, at, size) => {
500 if (0 > at || at + size > llb._used)
501 throw new RangeError(`[${at}, ${at + size}) is out of buffer range [0, ${llb._used})`);
504 const _hexdump = (buf, size) => {
508 for (let row = 0; row * width < size; ++row) {
509 const address = (row * width).toString(base);
510 s += "0".repeat(8 - address.length) + address;
512 for (let col = 0; col !== width; ++col) {
513 const idx = row * width + col;
515 const byte = buf[idx];
516 const bytestr = byte.toString(base);
517 s += " " + (bytestr.length === 1 ? "0" + bytestr : bytestr);
518 chars += 0x20 <= byte && byte < 0x7F ? String.fromCharCode(byte) : "·";
524 s+= " |" + chars + "|\n";
529 class LowLevelBinary {
531 this._buf = new Uint8Array(_initialAllocationSize);
535 newPatchable(type) { return new PatchableLowLevelBinary(type, this); }
538 get() { return this._buf; }
539 trim() { this._buf = this._buf.slice(0, this._used); }
541 hexdump() { return _hexdump(this._buf, this._used); }
543 const allocated = this._buf.length;
544 if (allocated - this._used < bytes) {
545 let buf = new Uint8Array(_growAllocationSize(allocated));
550 _push8(v) { this._buf[this._used] = v & 0xFF; this._used += 1; }
554 if ((v & 0xFF) >>> 0 !== v)
555 throw new RangeError(`Invalid uint8 ${v}`);
560 if ((v & 0xFFFF) >>> 0 !== v)
561 throw new RangeError(`Invalid uint16 ${v}`);
564 this._push8(v >>> 8);
567 if ((v & 0xFFFFFF) >>> 0 !== v)
568 throw new RangeError(`Invalid uint24 ${v}`);
571 this._push8(v >>> 8);
572 this._push8(v >>> 16);
575 if ((v & 0xFFFFFFFF) >>> 0 !== v)
576 throw new RangeError(`Invalid uint32 ${v}`);
579 this._push8(v >>> 8);
580 this._push8(v >>> 16);
581 this._push8(v >>> 24);
585 throw new RangeError("unimplemented, NaNs");
586 // Unfortunately, we cannot just view the actual buffer as a Float32Array since it needs to be 4 byte aligned
587 let buffer = new ArrayBuffer(4);
588 let floatView = new Float32Array(buffer);
589 let int8View = new Uint8Array(buffer);
591 for (let byte of int8View)
597 throw new RangeError("unimplemented, NaNs");
598 // Unfortunately, we cannot just view the actual buffer as a Float64Array since it needs to be 4 byte aligned
599 let buffer = new ArrayBuffer(8);
600 let floatView = new Float64Array(buffer);
601 let int8View = new Uint8Array(buffer);
603 for (let byte of int8View)
609 if (v < varuint32Min || varuint32Max < v)
610 throw new RangeError(`Invalid varuint32 ${v} range is [${varuint32Min}, ${varuint32Max}]`);
612 this.uint8(0x80 | (v & 0x7F));
619 if (v < varint32Min || varint32Max < v)
620 throw new RangeError(`Invalid varint32 ${v} range is [${varint32Min}, ${varint32Max}]`);
624 if ((v === 0 && ((b & 0x40) === 0)) || (v === -1 && ((b & 0x40) === 0x40))) {
625 this.uint8(b & 0x7F);
628 this.uint8(0x80 | b);
633 if (v < varuint32Min || varuint32Max < v)
634 throw new RangeError(`unimplemented: varuint64 larger than 32-bit`);
635 this.varuint32(v); // FIXME implement 64-bit var{u}int https://bugs.webkit.org/show_bug.cgi?id=163420
639 if (v < varint32Min || varint32Max < v)
640 throw new RangeError(`unimplemented: varint64 larger than 32-bit`);
641 this.varint32(v); // FIXME implement 64-bit var{u}int https://bugs.webkit.org/show_bug.cgi?id=163420
644 if (v !== 0 && v !== 1)
645 throw new RangeError(`Invalid varuint1 ${v} range is [0, 1]`);
649 if (v < varint7Min || varint7Max < v)
650 throw new RangeError(`Invalid varint7 ${v} range is [${varint7Min}, ${varint7Max}]`);
654 if (v < varuint32Min || varuint7Max < v)
655 throw new RangeError(`Invalid varuint7 ${v} range is [${varuint32Min}, ${varuint7Max}]`);
659 if (!WASM.isValidBlockType(v))
660 throw new Error(`Invalid block type ${v}`);
661 this.varint7(WASM.typeValue[v]);
664 let patch = this.newPatchable("varuint32");
665 for (const char of str) {
666 // Encode UTF-8 2003 code points.
667 const code = char.codePointAt();
668 if (code <= 0x007F) {
671 } else if (code <= 0x07FF) {
672 const utf8 = 0x80C0 | ((code & 0x7C0) >> 6) | ((code & 0x3F) << 8);
674 } else if (code <= 0xFFFF) {
675 const utf8 = 0x8080E0 | ((code & 0xF000) >> 12) | ((code & 0xFC0) << 2) | ((code & 0x3F) << 16);
677 } else if (code <= 0x10FFFF) {
678 const utf8 = (0x808080F0 | ((code & 0x1C0000) >> 18) | ((code & 0x3F000) >> 4) | ((code & 0xFC0) << 10) | ((code & 0x3F) << 24)) >>> 0;
681 throw new Error(`Unexpectedly large UTF-8 character code point '${char}' 0x${code.toString(16)}`);
687 getSize() { return this._used; }
689 _getterRangeCheck(this, at, 1);
690 return this._buf[at];
693 _getterRangeCheck(this, at, 2);
694 return this._buf[at] | (this._buf[at + 1] << 8);
697 _getterRangeCheck(this, at, 3);
698 return this._buf[at] | (this._buf[at + 1] << 8) | (this._buf[at + 2] << 16);
701 _getterRangeCheck(this, at, 4);
702 return (this._buf[at] | (this._buf[at + 1] << 8) | (this._buf[at + 2] << 16) | (this._buf[at + 3] << 24)) >>> 0;
709 byte = this.getUint8(at++);
711 v = (v | ((byte & 0x7F) << shift) >>> 0) >>> 0;
713 } while ((byte & 0x80) !== 0);
714 if (shift - 7 > 32) throw new RangeError(`Shifting too much at ${at}`);
715 if ((shift == 35) && ((byte & 0xF0) != 0)) throw new Error(`Unexpected non-significant varuint32 bits in last byte 0x${byte.toString(16)}`);
716 return { value: v, next: at };
723 byte = this.getUint8(at++);
724 v = (v | ((byte & 0x7F) << shift) >>> 0) >>> 0;
726 } while ((byte & 0x80) !== 0);
727 if (shift - 7 > 32) throw new RangeError(`Shifting too much at ${at}`);
728 if ((shift == 35) && (((byte << 26) >> 30) != ((byte << 25) >> 31))) throw new Error(`Unexpected non-significant varint32 bits in last byte 0x${byte.toString(16)}`);
729 if ((byte & 0x40) === 0x40) {
730 const sext = shift < 32 ? 32 - shift : 0;
731 v = (v << sext) >> sext;
733 return { value: v, next: at };
736 const res = this.getVaruint32(at);
737 if (res.value !== 0 && res.value !== 1) throw new Error(`Expected a varuint1, got value ${res.value}`);
741 const res = this.getVaruint32(at);
742 if (res.value > varuint7Max) throw new Error(`Expected a varuint7, got value ${res.value}`);
746 const size = this.getVaruint32(at);
747 const last = size.next + size.value;
751 // Decode UTF-8 2003 code points.
752 const peek = this.getUint8(i);
754 if ((peek & 0x80) === 0x0) {
755 const utf8 = this.getUint8(i);
756 assert.eq(utf8 & 0x80, 0x00);
759 } else if ((peek & 0xE0) === 0xC0) {
760 const utf8 = this.getUint16(i);
761 assert.eq(utf8 & 0xC0E0, 0x80C0);
763 code = ((utf8 & 0x1F) << 6) | ((utf8 & 0x3F00) >> 8);
764 } else if ((peek & 0xF0) === 0xE0) {
765 const utf8 = this.getUint24(i);
766 assert.eq(utf8 & 0xC0C0F0, 0x8080E0);
768 code = ((utf8 & 0xF) << 12) | ((utf8 & 0x3F00) >> 2) | ((utf8 & 0x3F0000) >> 16);
769 } else if ((peek & 0xF8) === 0xF0) {
770 const utf8 = this.getUint32(i);
771 assert.eq((utf8 & 0xC0C0C0F8) | 0, 0x808080F0 | 0);
773 code = ((utf8 & 0x7) << 18) | ((utf8 & 0x3F00) << 4) | ((utf8 & 0x3F0000) >> 10) | ((utf8 & 0x3F000000) >> 24);
775 throw new Error(`Unexpectedly large UTF-8 initial byte 0x${peek.toString(16)}`);
776 str += String.fromCodePoint(code);
779 throw new Error(`String decoding read up to ${i}, expected ${last}, UTF-8 decoding was too greedy`);
784 class PatchableLowLevelBinary extends LowLevelBinary {
785 constructor(type, lowLevelBinary) {
788 this.target = lowLevelBinary;
789 this._buffered_bytes = 0;
791 _push8(v) { ++this._buffered_bytes; super._push8(v); }
793 this.target[this.type](this._buffered_bytes);
794 for (let i = 0; i < this._buffered_bytes; ++i)
795 this.target.uint8(this._buf[i]);
799 LowLevelBinary.varuint32Min = 0;
800 LowLevelBinary.varint7Min = -0b1000000;
801 LowLevelBinary.varint7Max = 0b111111;
802 LowLevelBinary.varuint7Max = 0b1111111;
803 LowLevelBinary.varuint32Max = ((((1 << 31) >>> 0) - 1) * 2) + 1;
804 LowLevelBinary.varint32Min = -((1 << 31) >>> 0);
805 LowLevelBinary.varint32Max = ((1 << 31) - 1) >>> 0;
806 LowLevelBinary.varBitsMax = 5;
809 // Builder_WebAssemblyBinary.js
810 const BuildWebAssembly = {};
812 const put = (bin, type, value) => bin[type](value);
814 const putResizableLimits = (bin, initial, maximum) => {
815 assert.truthy(typeof initial === "number", "We expect 'initial' to be an integer");
817 if (typeof maximum === "number") {
820 assert.truthy(typeof maximum === "undefined", "We expect 'maximum' to be an integer if it's defined");
823 put(bin, "varuint1", hasMaximum);
824 put(bin, "varuint32", initial);
826 put(bin, "varuint32", maximum);
829 const putTable = (bin, {initial, maximum, element}) => {
830 assert.truthy(WASM.isValidType(element), "We expect 'element' to be a valid type. It was: " + element);
831 put(bin, "varint7", WASM.typeValue[element]);
833 putResizableLimits(bin, initial, maximum);
836 const valueType = WASM.description.type.i32.type
838 const putGlobalType = (bin, global) => {
839 put(bin, valueType, WASM.typeValue[global.type]);
840 put(bin, "varuint1", global.mutability);
843 const putOp = (bin, op) => {
844 put(bin, "uint8", op.value);
845 if (op.arguments.length !== 0)
846 throw new Error(`Unimplemented: arguments`); // FIXME https://bugs.webkit.org/show_bug.cgi?id=162706
850 for (let i = 0; i < op.immediates.length; ++i) {
851 const type = WASM.description.opcode[op.name].immediate[i].type
853 throw new TypeError(`Unknown type: ${type} in op: ${op.name}`);
854 put(bin, type, op.immediates[i]);
858 assert.eq(op.immediates.length, 1);
859 let imm = op.immediates[0];
860 // Do a static cast to make large int32s signed.
862 imm = imm - (2 ** 32);
863 put(bin, "varint32", imm);
867 put(bin, "varuint32", op.immediates.length - 1);
868 for (let imm of op.immediates)
869 put(bin, "varuint32", imm);
874 const putInitExpr = (bin, expr) => {
875 putOp(bin, { value: WASM.description.opcode[expr.op].value, name: expr.op, immediates: [expr.initValue], arguments: [] });
876 putOp(bin, { value: WASM.description.opcode.end.value, name: "end", immediates: [], arguments: [] });
880 Type: (section, bin) => {
881 put(bin, "varuint32", section.data.length);
882 for (const entry of section.data) {
883 put(bin, "varint7", WASM.typeValue["func"]);
884 put(bin, "varuint32", entry.params.length);
885 for (const param of entry.params)
886 put(bin, "varint7", WASM.typeValue[param]);
887 if (entry.ret === "void")
888 put(bin, "varuint1", 0);
890 put(bin, "varuint1", 1);
891 put(bin, "varint7", WASM.typeValue[entry.ret]);
895 Import: (section, bin) => {
896 put(bin, "varuint32", section.data.length);
897 for (const entry of section.data) {
898 put(bin, "string", entry.module);
899 put(bin, "string", entry.field);
900 put(bin, "uint8", WASM.externalKindValue[entry.kind]);
901 switch (entry.kind) {
902 default: throw new Error(`Implementation problem: unexpected kind ${entry.kind}`);
904 put(bin, "varuint32", entry.type);
908 putTable(bin, entry.tableDescription);
912 let {initial, maximum} = entry.memoryDescription;
913 putResizableLimits(bin, initial, maximum);
917 putGlobalType(bin, entry.globalDescription);
923 Function: (section, bin) => {
924 put(bin, "varuint32", section.data.length);
925 for (const signature of section.data)
926 put(bin, "varuint32", signature);
929 Table: (section, bin) => {
930 put(bin, "varuint32", section.data.length);
931 for (const {tableDescription} of section.data) {
932 putTable(bin, tableDescription);
936 Memory: (section, bin) => {
937 // Flags, currently can only be [0,1]
938 put(bin, "varuint1", section.data.length);
939 for (const memory of section.data) {
940 put(bin, "varuint32", memory.max ? 1 : 0);
941 put(bin, "varuint32", memory.initial);
943 put(bin, "varuint32", memory.max);
947 Global: (section, bin) => {
948 put(bin, "varuint32", section.data.length);
949 for (const global of section.data) {
950 putGlobalType(bin, global);
951 putInitExpr(bin, global)
955 Export: (section, bin) => {
956 put(bin, "varuint32", section.data.length);
957 for (const entry of section.data) {
958 put(bin, "string", entry.field);
959 put(bin, "uint8", WASM.externalKindValue[entry.kind]);
960 switch (entry.kind) {
965 put(bin, "varuint32", entry.index);
967 default: throw new Error(`Implementation problem: unexpected kind ${entry.kind}`);
971 Start: (section, bin) => {
972 put(bin, "varuint32", section.data[0]);
974 Element: (section, bin) => {
975 const data = section.data;
976 put(bin, "varuint32", data.length);
977 for (const {tableIndex, offset, functionIndices} of data) {
978 put(bin, "varuint32", tableIndex);
981 if (typeof offset === "number")
982 initExpr = {op: "i32.const", initValue: offset};
985 putInitExpr(bin, initExpr);
987 put(bin, "varuint32", functionIndices.length);
988 for (const functionIndex of functionIndices)
989 put(bin, "varuint32", functionIndex);
993 Code: (section, bin) => {
994 put(bin, "varuint32", section.data.length);
995 for (const func of section.data) {
996 let funcBin = bin.newPatchable("varuint32");
997 const localCount = func.locals.length - func.parameterCount;
998 put(funcBin, "varuint32", localCount);
999 for (let i = func.parameterCount; i < func.locals.length; ++i) {
1000 put(funcBin, "varuint32", 1);
1001 put(funcBin, "varint7", WASM.typeValue[func.locals[i]]);
1004 for (const op of func.code)
1011 Data: (section, bin) => {
1012 put(bin, "varuint32", section.data.length);
1013 for (const datum of section.data) {
1014 put(bin, "varuint32", datum.index);
1015 // FIXME allow complex init_expr here. https://bugs.webkit.org/show_bug.cgi?id=165700
1016 // For now we only handle i32.const as offset.
1017 put(bin, "uint8", WASM.description.opcode["i32.const"].value);
1018 put(bin, WASM.description.opcode["i32.const"].immediate[0].type, datum.offset);
1019 put(bin, "uint8", WASM.description.opcode["end"].value);
1020 put(bin, "varuint32", datum.data.length);
1021 for (const byte of datum.data)
1022 put(bin, "uint8", byte);
1027 BuildWebAssembly.Binary = (preamble, sections) => {
1028 let wasmBin = new LowLevelBinary();
1029 for (const p of WASM.description.preamble)
1030 put(wasmBin, p.type, preamble[p.name]);
1031 for (const section of sections) {
1032 put(wasmBin, WASM.sectionEncodingType, section.id);
1033 let sectionBin = wasmBin.newPatchable("varuint32");
1034 const emitter = emitters[section.name];
1036 emitter(section, sectionBin);
1039 put(sectionBin, "string", section.name);
1040 for (const byte of section.data)
1041 put(sectionBin, "uint8", byte);
1051 const LLB = LowLevelBinary;
1053 const _toJavaScriptName = name => {
1054 const camelCase = name.replace(/([^a-z0-9].)/g, c => c[1].toUpperCase());
1055 const CamelCase = camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
1059 const _isValidValue = (value, type) => {
1061 // We allow both signed and unsigned numbers.
1062 case "i32": return Math.round(value) === value && LLB.varint32Min <= value && value <= LLB.varuint32Max;
1063 case "i64": return true; // FIXME https://bugs.webkit.org/show_bug.cgi?id=163420 64-bit values
1064 case "f32": return typeof(value) === "number" && isFinite(value);
1065 case "f64": return typeof(value) === "number" && isFinite(value);
1066 default: throw new Error(`Implementation problem: unknown type ${type}`);
1069 const _unknownSectionId = 0;
1071 const _normalizeFunctionSignature = (params, ret) => {
1072 assert.isArray(params);
1073 for (const p of params)
1074 assert.truthy(WASM.isValidValueType(p), `Type parameter ${p} needs a valid value type`);
1075 if (typeof(ret) === "undefined")
1077 assert.isNotArray(ret, `Multiple return values not supported by WebAssembly yet`);
1078 assert.truthy(WASM.isValidBlockType(ret), `Type return ${ret} must be valid block type`);
1079 return [params, ret];
1082 const _errorHandlingProxyFor = builder => builder["__isProxy"] ? builder : new Proxy(builder, {
1083 get: (target, property, receiver) => {
1084 if (property === "__isProxy")
1086 if (target[property] === undefined)
1087 throw new Error(`WebAssembly builder received unknown property '${property}'`);
1088 return target[property];
1092 const _maybeRegisterType = (builder, type) => {
1093 const typeSection = builder._getSection("Type");
1094 if (typeof(type) === "number") {
1095 // Type numbers already refer to the type section, no need to register them.
1096 if (builder._checked) {
1097 assert.isNotUndef(typeSection, `Can not use type ${type} if a type section is not present`);
1098 assert.isNotUndef(typeSection.data[type], `Type ${type} does not exist in type section`);
1102 assert.hasObjectProperty(type, "params", `Expected type to be a number or object with 'params' and optionally 'ret' fields`);
1103 const [params, ret] = _normalizeFunctionSignature(type.params, type.ret);
1104 assert.isNotUndef(typeSection, `Can not add type if a type section is not present`);
1105 // Try reusing an equivalent type from the type section.
1107 for (let i = 0; i !== typeSection.data.length; ++i) {
1108 const t = typeSection.data[i];
1109 if (t.ret === ret && params.length === t.params.length) {
1110 for (let j = 0; j !== t.params.length; ++j) {
1111 if (params[j] !== t.params[j])
1118 if (typeof(type) !== "number") {
1119 // Couldn't reuse a pre-existing type, register this type in the type section.
1120 typeSection.data.push({ params: params, ret: ret });
1121 type = typeSection.data.length - 1;
1126 const _importFunctionContinuation = (builder, section, nextBuilder) => {
1127 return (module, field, type) => {
1128 assert.isString(module, `Import function module should be a string, got "${module}"`);
1129 assert.isString(field, `Import function field should be a string, got "${field}"`);
1130 const typeSection = builder._getSection("Type");
1131 type = _maybeRegisterType(builder, type);
1132 section.data.push({ field: field, type: type, kind: "Function", module: module });
1133 // Imports also count in the function index space. Map them as objects to avoid clashing with Code functions' names.
1134 builder._registerFunctionToIndexSpace({ module: module, field: field });
1135 return _errorHandlingProxyFor(nextBuilder);
1139 const _importMemoryContinuation = (builder, section, nextBuilder) => {
1140 return (module, field, {initial, maximum}) => {
1141 assert.isString(module, `Import Memory module should be a string, got "${module}"`);
1142 assert.isString(field, `Import Memory field should be a string, got "${field}"`);
1143 section.data.push({module, field, kind: "Memory", memoryDescription: {initial, maximum}});
1144 return _errorHandlingProxyFor(nextBuilder);
1148 const _importTableContinuation = (builder, section, nextBuilder) => {
1149 return (module, field, {initial, maximum, element}) => {
1150 assert.isString(module, `Import Table module should be a string, got "${module}"`);
1151 assert.isString(field, `Import Table field should be a string, got "${field}"`);
1152 section.data.push({module, field, kind: "Table", tableDescription: {initial, maximum, element}});
1153 return _errorHandlingProxyFor(nextBuilder);
1157 const _exportFunctionContinuation = (builder, section, nextBuilder) => {
1158 return (field, index, type) => {
1159 assert.isString(field, `Export function field should be a string, got "${field}"`);
1160 const typeSection = builder._getSection("Type");
1161 if (typeof(type) !== "undefined") {
1162 // Exports can leave the type unspecified, letting the Code builder patch them up later.
1163 type = _maybeRegisterType(builder, type);
1166 // We can't check much about "index" here because the Code section succeeds the Export section. More work is done at Code().End() time.
1167 switch (typeof(index)) {
1168 case "string": break; // Assume it's a function name which will be revealed in the Code section.
1169 case "number": break; // Assume it's a number in the "function index space".
1171 // Re-exporting an import.
1172 assert.hasObjectProperty(index, "module", `Re-exporting "${field}" from an import`);
1173 assert.hasObjectProperty(index, "field", `Re-exporting "${field}" from an import`);
1176 // Assume it's the same as the field (i.e. it's not being renamed).
1179 default: throw new Error(`Export section's index must be a string or a number, got ${index}`);
1182 const correspondingImport = builder._getFunctionFromIndexSpace(index);
1183 const importSection = builder._getSection("Import");
1184 if (typeof(index) === "object") {
1185 // Re-exporting an import using its module+field name.
1186 assert.isNotUndef(correspondingImport, `Re-exporting "${field}" couldn't find import from module "${index.module}" field "${index.field}"`);
1187 index = correspondingImport;
1188 if (typeof(type) === "undefined")
1189 type = importSection.data[index].type;
1190 if (builder._checked)
1191 assert.eq(type, importSection.data[index].type, `Re-exporting import "${importSection.data[index].field}" as "${field}" has mismatching type`);
1192 } else if (typeof(correspondingImport) !== "undefined") {
1193 // Re-exporting an import using its index.
1195 for (const i of importSection.data) {
1196 if (i.module === correspondingImport.module && i.field === correspondingImport.field) {
1201 if (typeof(type) === "undefined")
1202 type = exportedImport.type;
1203 if (builder._checked)
1204 assert.eq(type, exportedImport.type, `Re-exporting import "${exportedImport.field}" as "${field}" has mismatching type`);
1206 section.data.push({ field: field, type: type, kind: "Function", index: index });
1207 return _errorHandlingProxyFor(nextBuilder);
1211 const _normalizeMutability = (mutability) => {
1212 if (mutability === "mutable")
1214 else if (mutability === "immutable")
1217 throw new Error(`mutability should be either "mutable" or "immutable", but got ${mutability}`);
1220 const _exportGlobalContinuation = (builder, section, nextBuilder) => {
1221 return (field, index) => {
1222 assert.isNumber(index, `Global exports only support number indices right now`);
1223 section.data.push({ field, kind: "Global", index });
1224 return _errorHandlingProxyFor(nextBuilder);
1228 const _exportMemoryContinuation = (builder, section, nextBuilder) => {
1229 return (field, index) => {
1230 assert.isNumber(index, `Memory exports only support number indices`);
1231 section.data.push({field, kind: "Memory", index});
1232 return _errorHandlingProxyFor(nextBuilder);
1236 const _exportTableContinuation = (builder, section, nextBuilder) => {
1237 return (field, index) => {
1238 assert.isNumber(index, `Table exports only support number indices`);
1239 section.data.push({field, kind: "Table", index});
1240 return _errorHandlingProxyFor(nextBuilder);
1244 const _importGlobalContinuation = (builder, section, nextBuilder) => {
1246 const globalBuilder = {
1247 End: () => nextBuilder
1249 for (let op of WASM.description.value_type) {
1250 globalBuilder[_toJavaScriptName(op)] = (module, field, mutability) => {
1251 assert.isString(module, `Import global module should be a string, got "${module}"`);
1252 assert.isString(field, `Import global field should be a string, got "${field}"`);
1253 assert.isString(mutability, `Import global mutability should be a string, got "${mutability}"`);
1254 section.data.push({ globalDescription: { type: op, mutability: _normalizeMutability(mutability) }, module, field, kind: "Global" });
1255 return _errorHandlingProxyFor(globalBuilder);
1258 return _errorHandlingProxyFor(globalBuilder);
1262 const _checkStackArgs = (op, param) => {
1263 for (let expect of param) {
1264 if (WASM.isValidType(expect)) {
1265 // FIXME implement stack checks for arguments. https://bugs.webkit.org/show_bug.cgi?id=163421
1267 // Handle our own meta-types.
1269 case "addr": break; // FIXME implement addr. https://bugs.webkit.org/show_bug.cgi?id=163421
1270 case "any": break; // FIXME implement any. https://bugs.webkit.org/show_bug.cgi?id=163421
1271 case "bool": break; // FIXME implement bool. https://bugs.webkit.org/show_bug.cgi?id=163421
1272 case "call": break; // FIXME implement call stack argument checks based on function signature. https://bugs.webkit.org/show_bug.cgi?id=163421
1273 case "global": break; // FIXME implement global. https://bugs.webkit.org/show_bug.cgi?id=163421
1274 case "local": break; // FIXME implement local. https://bugs.webkit.org/show_bug.cgi?id=163421
1275 case "prev": break; // FIXME implement prev, checking for whetever the previous value was. https://bugs.webkit.org/show_bug.cgi?id=163421
1276 case "size": break; // FIXME implement size. https://bugs.webkit.org/show_bug.cgi?id=163421
1277 default: throw new Error(`Implementation problem: unhandled meta-type "${expect}" on "${op}"`);
1283 const _checkStackReturn = (op, ret) => {
1284 for (let expect of ret) {
1285 if (WASM.isValidType(expect)) {
1286 // FIXME implement stack checks for return. https://bugs.webkit.org/show_bug.cgi?id=163421
1288 // Handle our own meta-types.
1291 case "bool": break; // FIXME implement bool. https://bugs.webkit.org/show_bug.cgi?id=163421
1292 case "call": break; // FIXME implement call stack return check based on function signature. https://bugs.webkit.org/show_bug.cgi?id=163421
1293 case "control": break; // FIXME implement control. https://bugs.webkit.org/show_bug.cgi?id=163421
1294 case "global": break; // FIXME implement global. https://bugs.webkit.org/show_bug.cgi?id=163421
1295 case "local": break; // FIXME implement local. https://bugs.webkit.org/show_bug.cgi?id=163421
1296 case "prev": break; // FIXME implement prev, checking for whetever the parameter type was. https://bugs.webkit.org/show_bug.cgi?id=163421
1297 case "size": break; // FIXME implement size. https://bugs.webkit.org/show_bug.cgi?id=163421
1298 default: throw new Error(`Implementation problem: unhandled meta-type "${expect}" on "${op}"`);
1304 const _checkImms = (op, imms, expectedImms, ret) => {
1305 const minExpectedImms = expectedImms.filter(i => i.type.slice(-1) !== '*').length;
1306 if (minExpectedImms !== expectedImms.length)
1307 assert.ge(imms.length, minExpectedImms, `"${op}" expects at least ${minExpectedImms} immediate${minExpectedImms !== 1 ? 's' : ''}, got ${imms.length}`);
1309 assert.eq(imms.length, minExpectedImms, `"${op}" expects exactly ${minExpectedImms} immediate${minExpectedImms !== 1 ? 's' : ''}, got ${imms.length}`);
1310 for (let idx = 0; idx !== expectedImms.length; ++idx) {
1311 const got = imms[idx];
1312 const expect = expectedImms[idx];
1313 switch (expect.name) {
1314 case "function_index":
1315 assert.truthy(_isValidValue(got, "i32") && got >= 0, `Invalid value on ${op}: got "${got}", expected non-negative i32`);
1316 // FIXME check function indices. https://bugs.webkit.org/show_bug.cgi?id=163421
1318 case "local_index": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
1319 case "global_index": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
1320 case "type_index": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
1322 assert.truthy(_isValidValue(got, ret[0]), `Invalid value on ${op}: got "${got}", expected ${ret[0]}`);
1324 case "flags": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
1325 case "offset": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
1327 case "default_target": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
1328 case "relative_depth": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
1330 assert.truthy(WASM.isValidBlockType(imms[idx]), `Invalid block type on ${op}: "${imms[idx]}"`);
1332 case "target_count": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
1333 case "target_table": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
1334 case "reserved": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
1335 default: throw new Error(`Implementation problem: unhandled immediate "${expect.name}" on "${op}"`);
1340 const _createFunctionBuilder = (func, builder, previousBuilder) => {
1341 let functionBuilder = {};
1342 for (const op in WASM.description.opcode) {
1343 const name = _toJavaScriptName(op);
1344 const value = WASM.description.opcode[op].value;
1345 const ret = WASM.description.opcode[op]["return"];
1346 const param = WASM.description.opcode[op].parameter;
1347 const imm = WASM.description.opcode[op].immediate;
1349 const checkStackArgs = builder._checked ? _checkStackArgs : () => {};
1350 const checkStackReturn = builder._checked ? _checkStackReturn : () => {};
1351 const checkImms = builder._checked ? _checkImms : () => {};
1353 functionBuilder[name] = (...args) => {
1357 nextBuilder = functionBuilder;
1360 nextBuilder = previousBuilder;
1365 nextBuilder = _createFunctionBuilder(func, builder, functionBuilder);
1369 // Passing a function as the argument is a way to nest blocks lexically.
1370 const continuation = args[args.length - 1];
1371 const hasContinuation = typeof(continuation) === "function";
1372 const imms = hasContinuation ? args.slice(0, -1) : args; // FIXME: allow passing in stack values, as-if code were a stack machine. Just check for a builder to this function, and drop. https://bugs.webkit.org/show_bug.cgi?id=163422
1373 checkImms(op, imms, imm, ret);
1374 checkStackArgs(op, param);
1375 checkStackReturn(op, ret);
1376 const stackArgs = []; // FIXME https://bugs.webkit.org/show_bug.cgi?id=162706
1377 func.code.push({ name: op, value: value, arguments: stackArgs, immediates: imms });
1378 if (hasContinuation)
1379 return _errorHandlingProxyFor(continuation(nextBuilder).End());
1380 return _errorHandlingProxyFor(nextBuilder);
1384 return _errorHandlingProxyFor(functionBuilder);
1387 const _createFunction = (section, builder, previousBuilder) => {
1388 return (...args) => {
1389 const nameOffset = (typeof(args[0]) === "string") >>> 0;
1390 const functionName = nameOffset ? args[0] : undefined;
1391 let signature = args[0 + nameOffset];
1392 const locals = args[1 + nameOffset] === undefined ? [] : args[1 + nameOffset];
1393 for (const local of locals)
1394 assert.truthy(WASM.isValidValueType(local), `Type of local: ${local} needs to be a valid value type`);
1396 if (typeof(signature) === "undefined")
1397 signature = { params: [] };
1401 if (typeof signature === "object") {
1402 assert.hasObjectProperty(signature, "params", `Expect function signature to be an object with a "params" field, got "${signature}"`);
1404 ([params, ret] = _normalizeFunctionSignature(signature.params, signature.ret));
1405 signature = {params, ret};
1406 type = _maybeRegisterType(builder, signature);
1408 assert.truthy(typeof signature === "number");
1409 const typeSection = builder._getSection("Type");
1410 assert.truthy(!!typeSection);
1411 // FIXME: we should allow indices that exceed this to be able to
1412 // test JSCs validator is correct. https://bugs.webkit.org/show_bug.cgi?id=165786
1413 assert.truthy(signature < typeSection.data.length);
1415 signature = typeSection.data[signature];
1416 assert.hasObjectProperty(signature, "params", `Expect function signature to be an object with a "params" field, got "${signature}"`);
1417 params = signature.params;
1423 signature: signature,
1424 locals: params.concat(locals), // Parameters are the first locals.
1425 parameterCount: params.length,
1429 const functionSection = builder._getSection("Function");
1430 if (functionSection)
1431 functionSection.data.push(func.type);
1433 section.data.push(func);
1434 builder._registerFunctionToIndexSpace(functionName);
1436 return _createFunctionBuilder(func, builder, previousBuilder);
1442 this.setChecked(true);
1444 for (const p of WASM.description.preamble)
1445 preamble[p.name] = p.value;
1446 this.setPreamble(preamble);
1447 this._sections = [];
1448 this._functionIndexSpace = new Map();
1449 this._functionIndexSpaceCount = 0;
1450 this._registerSectionBuilders();
1452 setChecked(checked) {
1453 this._checked = checked;
1457 this._preamble = Object.assign(this._preamble || {}, p);
1460 _functionIndexSpaceKeyHash(obj) {
1461 // We don't need a full hash, just something that has a defined order for Map. Objects we insert aren't nested.
1462 if (typeof(obj) !== 'object')
1464 const keys = Object.keys(obj).sort();
1467 entries.push([k, obj[k]]);
1468 return JSON.stringify(entries);
1470 _registerFunctionToIndexSpace(name) {
1471 if (typeof(name) === "undefined") {
1472 // Registering a nameless function still adds it to the function index space. Register it as something that can't normally be registered.
1475 const value = this._functionIndexSpaceCount++;
1476 // Collisions are fine: we'll simply count the function and forget the previous one.
1477 this._functionIndexSpace.set(this._functionIndexSpaceKeyHash(name), value);
1478 // Map it both ways, the number space is distinct from the name space.
1479 this._functionIndexSpace.set(this._functionIndexSpaceKeyHash(value), name);
1481 _getFunctionFromIndexSpace(name) {
1482 return this._functionIndexSpace.get(this._functionIndexSpaceKeyHash(name));
1484 _registerSectionBuilders() {
1485 for (const section in WASM.description.section) {
1488 this[section] = function() {
1489 const s = this._addSection(section);
1490 const builder = this;
1491 const typeBuilder = {
1493 Func: (params, ret) => {
1494 [params, ret] = _normalizeFunctionSignature(params, ret);
1495 s.data.push({ params: params, ret: ret });
1496 return _errorHandlingProxyFor(typeBuilder);
1499 return _errorHandlingProxyFor(typeBuilder);
1504 this[section] = function() {
1505 const s = this._addSection(section);
1506 const importBuilder = {
1509 importBuilder.Global = _importGlobalContinuation(this, s, importBuilder);
1510 importBuilder.Function = _importFunctionContinuation(this, s, importBuilder);
1511 importBuilder.Memory = _importMemoryContinuation(this, s, importBuilder);
1512 importBuilder.Table = _importTableContinuation(this, s, importBuilder);
1513 return _errorHandlingProxyFor(importBuilder);
1518 this[section] = function() {
1519 const s = this._addSection(section);
1520 const functionBuilder = {
1522 // FIXME: add ability to add this with whatever.
1524 return _errorHandlingProxyFor(functionBuilder);
1529 this[section] = function() {
1530 const s = this._addSection(section);
1531 const tableBuilder = {
1533 Table: ({initial, maximum, element}) => {
1534 s.data.push({tableDescription: {initial, maximum, element}});
1535 return _errorHandlingProxyFor(tableBuilder);
1538 return _errorHandlingProxyFor(tableBuilder);
1543 this[section] = function() {
1544 const s = this._addSection(section);
1545 const memoryBuilder = {
1547 InitialMaxPages: (initial, max) => {
1548 s.data.push({ initial, max });
1549 return _errorHandlingProxyFor(memoryBuilder);
1552 return _errorHandlingProxyFor(memoryBuilder);
1557 this[section] = function() {
1558 const s = this._addSection(section);
1559 const globalBuilder = {
1561 GetGlobal: (type, initValue, mutability) => {
1562 s.data.push({ type, op: "get_global", mutability: _normalizeMutability(mutability), initValue });
1563 return _errorHandlingProxyFor(globalBuilder);
1566 for (let op of WASM.description.value_type) {
1567 globalBuilder[_toJavaScriptName(op)] = (initValue, mutability) => {
1568 s.data.push({ type: op, op: op + ".const", mutability: _normalizeMutability(mutability), initValue });
1569 return _errorHandlingProxyFor(globalBuilder);
1572 return _errorHandlingProxyFor(globalBuilder);
1577 this[section] = function() {
1578 const s = this._addSection(section);
1579 const exportBuilder = {
1582 exportBuilder.Global = _exportGlobalContinuation(this, s, exportBuilder);
1583 exportBuilder.Function = _exportFunctionContinuation(this, s, exportBuilder);
1584 exportBuilder.Memory = _exportMemoryContinuation(this, s, exportBuilder);
1585 exportBuilder.Table = _exportTableContinuation(this, s, exportBuilder);
1586 return _errorHandlingProxyFor(exportBuilder);
1591 this[section] = function(functionIndexOrName) {
1592 const s = this._addSection(section);
1593 const startBuilder = {
1596 if (typeof(functionIndexOrName) !== "number" && typeof(functionIndexOrName) !== "string")
1597 throw new Error(`Start section's function index must either be a number or a string`);
1598 s.data.push(functionIndexOrName);
1599 return _errorHandlingProxyFor(startBuilder);
1604 this[section] = function(...args) {
1605 if (args.length !== 0)
1606 throw new Error("You're doing it wrong. This element does not take arguments. You must chain the call with another Element()");
1608 const s = this._addSection(section);
1609 const elementBuilder = {
1611 Element: ({tableIndex = 0, offset, functionIndices}) => {
1612 s.data.push({tableIndex, offset, functionIndices});
1613 return _errorHandlingProxyFor(elementBuilder);
1617 return _errorHandlingProxyFor(elementBuilder);
1622 this[section] = function() {
1623 const s = this._addSection(section);
1624 const builder = this;
1625 const codeBuilder = {
1627 // We now have enough information to remap the export section's "type" and "index" according to the Code section we are currently ending.
1628 const typeSection = builder._getSection("Type");
1629 const importSection = builder._getSection("Import");
1630 const exportSection = builder._getSection("Export");
1631 const startSection = builder._getSection("Start");
1632 const codeSection = s;
1633 if (exportSection) {
1634 for (const e of exportSection.data) {
1635 if (e.kind !== "Function" || typeof(e.type) !== "undefined")
1637 switch (typeof(e.index)) {
1638 default: throw new Error(`Unexpected export index "${e.index}"`);
1640 const index = builder._getFunctionFromIndexSpace(e.index);
1641 assert.isNumber(index, `Export section contains undefined function "${e.index}"`);
1645 const index = builder._getFunctionFromIndexSpace(e.index);
1646 if (builder._checked)
1647 assert.isNotUndef(index, `Export "${e.field}" does not correspond to a defined value in the function index space`);
1650 throw new Error(`Unimplemented: Function().End() with undefined export index`); // FIXME
1652 if (typeof(e.type) === "undefined") {
1653 // This must be a function export from the Code section (re-exports were handled earlier).
1654 let functionIndexSpaceOffset = 0;
1655 if (importSection) {
1656 for (const {kind} of importSection.data) {
1657 if (kind === "Function")
1658 ++functionIndexSpaceOffset;
1661 const functionIndex = e.index - functionIndexSpaceOffset;
1662 e.type = codeSection.data[functionIndex].type;
1667 const start = startSection.data[0];
1668 let mapped = builder._getFunctionFromIndexSpace(start);
1669 if (!builder._checked) {
1670 if (typeof(mapped) === "undefined")
1671 mapped = start; // In unchecked mode, simply use what was provided if it's nonsensical.
1672 assert.isA(start, "number"); // It can't be too nonsensical, otherwise we can't create a binary.
1673 startSection.data[0] = start;
1675 if (typeof(mapped) === "undefined")
1676 throw new Error(`Start section refers to non-existant function '${start}'`);
1677 if (typeof(start) === "string" || typeof(start) === "object")
1678 startSection.data[0] = mapped;
1679 // FIXME in checked mode, test that the type is acceptable for start function. We probably want _registerFunctionToIndexSpace to also register types per index. https://bugs.webkit.org/show_bug.cgi?id=165658
1682 return _errorHandlingProxyFor(builder);
1686 codeBuilder.Function = _createFunction(s, builder, codeBuilder);
1687 return _errorHandlingProxyFor(codeBuilder);
1692 this[section] = function() {
1693 const s = this._addSection(section);
1694 const dataBuilder = {
1697 assert.isArray(data);
1698 for (const datum of data) {
1699 assert.isNumber(datum);
1700 assert.ge(datum, 0);
1701 assert.le(datum, 0xff);
1703 s.data.push({ data: data, index: 0, offset: 0 });
1704 let thisSegment = s.data[s.data.length - 1];
1705 const segmentBuilder = {
1706 End: () => dataBuilder,
1708 assert.eq(index, 0); // Linear memory index must be zero in MVP.
1709 thisSegment.index = index;
1710 return _errorHandlingProxyFor(segmentBuilder);
1713 // FIXME allow complex init_expr here. https://bugs.webkit.org/show_bug.cgi?id=165700
1714 assert.isNumber(offset);
1715 thisSegment.offset = offset;
1716 return _errorHandlingProxyFor(segmentBuilder);
1719 return _errorHandlingProxyFor(segmentBuilder);
1722 return _errorHandlingProxyFor(dataBuilder);
1727 this[section] = () => { throw new Error(`Unknown section type "${section}"`); };
1732 this.Unknown = function(name) {
1733 const s = this._addSection(name);
1734 const builder = this;
1735 const unknownBuilder = {
1738 assert.eq(b & 0xFF, b, `Unknown section expected byte, got: "${b}"`);
1740 return _errorHandlingProxyFor(unknownBuilder);
1743 return _errorHandlingProxyFor(unknownBuilder);
1746 _addSection(nameOrNumber, extraObject) {
1747 const name = typeof(nameOrNumber) === "string" ? nameOrNumber : "";
1748 const number = typeof(nameOrNumber) === "number" ? nameOrNumber : (WASM.description.section[name] ? WASM.description.section[name].value : _unknownSectionId);
1749 if (this._checked) {
1750 // Check uniqueness.
1751 for (const s of this._sections)
1752 if (number !== _unknownSectionId)
1753 assert.falsy(s.name === name && s.id === number, `Cannot have two sections with the same name "${name}" and ID ${number}`);
1755 if ((number !== _unknownSectionId) && (this._sections.length !== 0)) {
1756 for (let i = this._sections.length - 1; i >= 0; --i) {
1757 if (this._sections[i].id === _unknownSectionId)
1759 assert.le(this._sections[i].id, number, `Bad section ordering: "${this._sections[i].name}" cannot precede "${name}"`);
1764 const s = Object.assign({ name: name, id: number, data: [] }, extraObject || {});
1765 this._sections.push(s);
1768 _getSection(nameOrNumber) {
1769 switch (typeof(nameOrNumber)) {
1770 default: throw new Error(`Implementation problem: can not get section "${nameOrNumber}"`);
1772 for (const s of this._sections)
1773 if (s.name === nameOrNumber)
1777 for (const s of this._sections)
1778 if (s.id === nameOrNumber)
1784 // FIXME Add more optimizations. https://bugs.webkit.org/show_bug.cgi?id=163424
1789 preamble: this._preamble,
1790 section: this._sections
1792 // JSON.stringify serializes -0.0 as 0.0.
1793 const replacer = (key, value) => {
1794 if (value === 0.0 && 1.0 / value === -Infinity)
1795 return "NEGATIVE_ZERO";
1798 return JSON.stringify(obj, replacer);
1801 "use asm"; // For speed.
1802 // FIXME Create an asm.js equivalent string which can be eval'd. https://bugs.webkit.org/show_bug.cgi?id=163425
1803 throw new Error("asm.js not implemented yet");
1805 WebAssembly() { return BuildWebAssembly.Binary(this._preamble, this._sections); }