Move helper files for iOS touch gestures into LayoutTests/resources
[WebKit-https.git] / LayoutTests / resources / wasm-builder.js
1 /*
2  * Copyright (C) 2017 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 const _fail = (msg, extra) => {
27     throw new Error(msg + (extra ? ": " + extra : ""));
28 };
29
30 const assert = {};
31
32 const isNotA = assert.isNotA = (v, t, msg) => {
33     if (typeof v === t)
34         _fail(`Shouldn't be ${t}`, msg);
35 };
36
37 const isA = assert.isA = (v, t, msg) => {
38     if (typeof v !== t)
39         _fail(`Should be ${t}, got ${typeof(v)}`, msg);
40 };
41
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);
52
53 assert.hasObjectProperty = (o, p, msg) => {
54     isObject(o, msg);
55     isNotUndef(o[p], msg, `expected object to have property ${p}`);
56 };
57
58 assert.isArray = (v, msg) => {
59     if (!Array.isArray(v))
60         _fail(`Expected an array, got ${typeof(v)}`, msg);
61 };
62
63 assert.isNotArray = (v, msg) => {
64     if (Array.isArray(v))
65         _fail(`Expected to not be an array`, msg);
66 };
67
68 assert.truthy = (v, msg) => {
69     if (!v)
70         _fail(`Expected truthy`, msg);
71 };
72
73 assert.falsy = (v, msg) => {
74     if (v)
75         _fail(`Expected falsy`, msg);
76 };
77
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))
86             return;
87         _fail(`Not the same: "${lhs}" and "${rhs}"`, msg);
88     } else {
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);
91     }
92 };
93
94 const canonicalizeI32 = (number) => {
95     if (Math.round(number) === number && number >= 2 ** 31)
96         number = number - 2 ** 32;
97     return number;
98 }
99
100 assert.eqI32 = (lhs, rhs, msg) => {
101     return eq(canonicalizeI32(lhs), canonicalizeI32(rhs), msg);
102 };
103
104 assert.ge = (lhs, rhs, msg) => {
105     isNotUndef(lhs);
106     isNotUndef(rhs);
107     if (!(lhs >= rhs))
108         _fail(`Expected: "${lhs}" < "${rhs}"`, msg);
109 };
110
111 assert.le = (lhs, rhs, msg) => {
112     isNotUndef(lhs);
113     isNotUndef(rhs);
114     if (!(lhs <= rhs))
115         _fail(`Expected: "${lhs}" > "${rhs}"`, msg);
116 };
117
118 const _throws = (func, type, message, ...args) => {
119     try {
120         func(...args);
121     } catch (e) {
122         if (e instanceof type) {
123             if (e.message === message)
124                 return e;
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)
132                     return e;
133             }
134         }
135         _fail(`Expected to throw a ${type.name} with message "${message}", got ${e.name} with message "${e.message}"`);
136     }
137     _fail(`Expected to throw a ${type.name} with message "${message}"`);
138 };
139
140 const _instanceof = (obj, type, msg) => {
141     if (!(obj instanceof type))
142         _fail(`Expected a ${typeof(type)}, got ${typeof obj}`);
143 };
144
145 // Use underscore names to avoid clashing with builtin names.
146 assert.throws = _throws;
147 assert.instanceof = _instanceof;
148
149 const asyncTestImpl = (promise, thenFunc, catchFunc) => {
150     asyncTestStart(1);
151     promise.then(thenFunc).catch(catchFunc);
152 };
153
154 const printExn = (e) => {
155     print("Failed: ", e);
156     print(e.stack);
157 };
158
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);
165
166     }
167     asyncTestImpl(promise, thenCheck, printExn);
168 };
169
170 const WASM_JSON = `
171 {
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."
174                 ],
175     "preamble": [
176         { "name": "magic number", "type": "uint32", "value": 1836278016, "description": "NULL character followed by 'asm'" },
177         { "name": "version",      "type": "uint32", "value":          1, "description": "Version number" }
178     ],
179     "type" : {
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" }
187     },
188     "value_type": ["i32", "i64", "f32", "f64"],
189     "block_type": ["i32", "i64", "f32", "f64", "void"],
190     "elem_type": ["anyfunc"],
191     "external_kind": {
192         "Function": { "type": "uint8", "value": 0 },
193         "Table":    { "type": "uint8", "value": 1 },
194         "Memory":   { "type": "uint8", "value": 2 },
195         "Global":   { "type": "uint8", "value": 3 }
196     },
197     "section" : {
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" }
209     },
210     "opcode": {
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"  }
386     }
387 }
388 `;
389
390
391 const _environment =
392     ((typeof process === "object" && typeof require === "function") ? "node"
393      : (typeof window === "object" ? "web"
394         : (typeof importScripts === "function" ? "worker"
395            : "shell")));
396
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')())
402     : global;
403
404 const _eval = x => eval.call(null, x);
405
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);
412     }
413 }
414
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);
421     }
422 }
423
424 const _json = filename => {
425     assert.eq(filename, "wasm.json");
426     return JSON.parse(WASM_JSON);
427
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;
433     }
434 }
435
436 const _dump = (what, name, pad = '    ') => {
437     const value = v => {
438         try { return `"${v}"`; }
439         catch (e) { return `Error: "${e.message}"`; }
440     };
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);
445     }
446     return s;
447 };
448
449 // Use underscore names to avoid clashing with builtin names.
450 const utilities = {
451     dump : _dump,
452     eval : _eval,
453     read : _read,
454     load : _load,
455     json : _json,
456     global : _global
457 };
458
459
460 const _mapValues = from => {
461     let values = {};
462     for (const key in from)
463         values[key] = from[key].value;
464     return values;
465 };
466
467
468 const WASM = {};
469 {
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;
482 }
483
484
485 // LowLevelBinary.js
486 const _initialAllocationSize = 1024;
487 const _growAllocationSize = allocated => allocated * 2;
488
489
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;
498
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})`);
502 };
503
504 const _hexdump = (buf, size) => {
505     let s = "";
506     const width = 16;
507     const base = 16;
508     for (let row = 0; row * width < size; ++row) {
509         const address = (row * width).toString(base);
510         s += "0".repeat(8 - address.length) + address;
511         let chars = "";
512         for (let col = 0; col !== width; ++col) {
513             const idx = row * width + col;
514             if (idx < size) {
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) : "ยท";
519             } else {
520                 s += "   ";
521                 chars += " ";
522             }
523         }
524         s+= "  |" + chars + "|\n";
525     }
526     return s;
527 };
528
529 class LowLevelBinary {
530     constructor() {
531         this._buf = new Uint8Array(_initialAllocationSize);
532         this._used = 0;
533     }
534
535     newPatchable(type) { return new PatchableLowLevelBinary(type, this); }
536
537     // Utilities.
538     get() { return this._buf; }
539     trim() { this._buf = this._buf.slice(0, this._used); }
540
541     hexdump() { return _hexdump(this._buf, this._used); }
542     _maybeGrow(bytes) {
543         const allocated = this._buf.length;
544         if (allocated - this._used < bytes) {
545             let buf = new Uint8Array(_growAllocationSize(allocated));
546             buf.set(this._buf);
547             this._buf = buf;
548         }
549     }
550     _push8(v) { this._buf[this._used] = v & 0xFF; this._used += 1; }
551
552     // Data types.
553     uint8(v) {
554         if ((v & 0xFF) >>> 0 !== v)
555             throw new RangeError(`Invalid uint8 ${v}`);
556         this._maybeGrow(1);
557         this._push8(v);
558     }
559     uint16(v) {
560         if ((v & 0xFFFF) >>> 0 !== v)
561             throw new RangeError(`Invalid uint16 ${v}`);
562         this._maybeGrow(2);
563         this._push8(v);
564         this._push8(v >>> 8);
565     }
566     uint24(v) {
567         if ((v & 0xFFFFFF) >>> 0 !== v)
568             throw new RangeError(`Invalid uint24 ${v}`);
569         this._maybeGrow(3);
570         this._push8(v);
571         this._push8(v >>> 8);
572         this._push8(v >>> 16);
573     }
574     uint32(v) {
575         if ((v & 0xFFFFFFFF) >>> 0 !== v)
576             throw new RangeError(`Invalid uint32 ${v}`);
577         this._maybeGrow(4);
578         this._push8(v);
579         this._push8(v >>> 8);
580         this._push8(v >>> 16);
581         this._push8(v >>> 24);
582     }
583     float(v) {
584         if (isNaN(v))
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);
590         floatView[0] = v;
591         for (let byte of int8View)
592             this._push8(byte);
593     }
594
595     double(v) {
596         if (isNaN(v))
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);
602         floatView[0] = v;
603         for (let byte of int8View)
604             this._push8(byte);
605     }
606
607     varuint32(v) {
608         assert.isNumber(v);
609         if (v < varuint32Min || varuint32Max < v)
610             throw new RangeError(`Invalid varuint32 ${v} range is [${varuint32Min}, ${varuint32Max}]`);
611         while (v >= 0x80) {
612             this.uint8(0x80 | (v & 0x7F));
613             v >>>= 7;
614         }
615         this.uint8(v);
616     }
617     varint32(v) {
618         assert.isNumber(v);
619         if (v < varint32Min || varint32Max < v)
620             throw new RangeError(`Invalid varint32 ${v} range is [${varint32Min}, ${varint32Max}]`);
621         do {
622             const b = v & 0x7F;
623             v >>= 7;
624             if ((v === 0 && ((b & 0x40) === 0)) || (v === -1 && ((b & 0x40) === 0x40))) {
625                 this.uint8(b & 0x7F);
626                 break;
627             }
628             this.uint8(0x80 | b);
629         } while (true);
630     }
631     varuint64(v) {
632         assert.isNumber(v);
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
636     }
637     varint64(v) {
638         assert.isNumber(v);
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
642     }
643     varuint1(v) {
644         if (v !== 0 && v !== 1)
645             throw new RangeError(`Invalid varuint1 ${v} range is [0, 1]`);
646         this.varuint32(v);
647     }
648     varint7(v) {
649         if (v < varint7Min || varint7Max < v)
650             throw new RangeError(`Invalid varint7 ${v} range is [${varint7Min}, ${varint7Max}]`);
651         this.varint32(v);
652     }
653     varuint7(v) {
654         if (v < varuint32Min || varuint7Max < v)
655             throw new RangeError(`Invalid varuint7 ${v} range is [${varuint32Min}, ${varuint7Max}]`);
656         this.varuint32(v);
657     }
658     block_type(v) {
659         if (!WASM.isValidBlockType(v))
660             throw new Error(`Invalid block type ${v}`);
661         this.varint7(WASM.typeValue[v]);
662     }
663     string(str) {
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) {
669                 const utf8 = code;
670                 patch.uint8(utf8);
671             } else if (code <= 0x07FF) {
672                 const utf8 = 0x80C0 | ((code & 0x7C0) >> 6) | ((code & 0x3F) << 8);
673                 patch.uint16(utf8);
674             } else if (code <= 0xFFFF) {
675                 const utf8 = 0x8080E0 | ((code & 0xF000) >> 12) | ((code & 0xFC0) << 2) | ((code & 0x3F) << 16);
676                 patch.uint24(utf8);
677             } else if (code <= 0x10FFFF) {
678                 const utf8 = (0x808080F0 | ((code & 0x1C0000) >> 18) | ((code & 0x3F000) >> 4) | ((code & 0xFC0) << 10) | ((code & 0x3F) << 24)) >>> 0;
679                 patch.uint32(utf8);
680             } else
681                 throw new Error(`Unexpectedly large UTF-8 character code point '${char}' 0x${code.toString(16)}`);
682         }
683         patch.apply();
684     }
685
686     // Getters.
687     getSize() { return this._used; }
688     getUint8(at) {
689         _getterRangeCheck(this, at, 1);
690         return this._buf[at];
691     }
692     getUint16(at) {
693         _getterRangeCheck(this, at, 2);
694         return this._buf[at] | (this._buf[at + 1] << 8);
695     }
696     getUint24(at) {
697         _getterRangeCheck(this, at, 3);
698         return this._buf[at] | (this._buf[at + 1] << 8) | (this._buf[at + 2] << 16);
699     }
700     getUint32(at) {
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;
703     }
704     getVaruint32(at) {
705         let v = 0;
706         let shift = 0;
707         let byte = 0;
708         do {
709             byte = this.getUint8(at++);
710             ++read;
711             v = (v | ((byte & 0x7F) << shift) >>> 0) >>> 0;
712             shift += 7;
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 };
717     }
718     getVarint32(at) {
719         let v = 0;
720         let shift = 0;
721         let byte = 0;
722         do {
723             byte = this.getUint8(at++);
724             v = (v | ((byte & 0x7F) << shift) >>> 0) >>> 0;
725             shift += 7;
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;
732         }
733         return { value: v, next: at };
734     }
735     getVaruint1(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}`);
738         return res;
739     }
740     getVaruint7(at) {
741         const res = this.getVaruint32(at);
742         if (res.value > varuint7Max) throw new Error(`Expected a varuint7, got value ${res.value}`);
743         return res;
744     }
745     getString(at) {
746         const size = this.getVaruint32(at);
747         const last = size.next + size.value;
748         let i = size.next;
749         let str = "";
750         while (i < last) {
751             // Decode UTF-8 2003 code points.
752             const peek = this.getUint8(i);
753             let code;
754             if ((peek & 0x80) === 0x0) {
755                 const utf8 = this.getUint8(i);
756                 assert.eq(utf8 & 0x80, 0x00);
757                 i += 1;
758                 code = utf8;
759             } else if ((peek & 0xE0) === 0xC0) {
760                 const utf8 = this.getUint16(i);
761                 assert.eq(utf8 & 0xC0E0, 0x80C0);
762                 i += 2;
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);
767                 i += 3;
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);
772                 i += 4;
773                 code = ((utf8 & 0x7) << 18) | ((utf8 & 0x3F00) << 4) | ((utf8 & 0x3F0000) >> 10) | ((utf8 & 0x3F000000) >> 24);
774             } else
775                 throw new Error(`Unexpectedly large UTF-8 initial byte 0x${peek.toString(16)}`);
776             str += String.fromCodePoint(code);
777         }
778         if (i !== last)
779             throw new Error(`String decoding read up to ${i}, expected ${last}, UTF-8 decoding was too greedy`);
780         return str;
781     }
782 };
783
784 class PatchableLowLevelBinary extends LowLevelBinary {
785     constructor(type, lowLevelBinary) {
786         super();
787         this.type = type;
788         this.target = lowLevelBinary;
789         this._buffered_bytes = 0;
790     }
791     _push8(v) { ++this._buffered_bytes; super._push8(v); }
792     apply() {
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]);
796     }
797 };
798
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;
807
808
809 // Builder_WebAssemblyBinary.js
810 const BuildWebAssembly = {};
811 {
812     const put = (bin, type, value) => bin[type](value);
813
814     const putResizableLimits = (bin, initial, maximum) => {
815         assert.truthy(typeof initial === "number", "We expect 'initial' to be an integer");
816         let hasMaximum = 0;
817         if (typeof maximum === "number") {
818             hasMaximum = 1;
819         } else {
820             assert.truthy(typeof maximum === "undefined", "We expect 'maximum' to be an integer if it's defined");
821         }
822
823         put(bin, "varuint1", hasMaximum);
824         put(bin, "varuint32", initial);
825         if (hasMaximum)
826             put(bin, "varuint32", maximum);
827     };
828
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]);
832
833         putResizableLimits(bin, initial, maximum);
834     };
835
836     const valueType = WASM.description.type.i32.type
837
838     const putGlobalType = (bin, global) => {
839         put(bin, valueType, WASM.typeValue[global.type]);
840         put(bin, "varuint1", global.mutability);
841     };
842
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
847
848         switch (op.name) {
849         default:
850             for (let i = 0; i < op.immediates.length; ++i) {
851                 const type = WASM.description.opcode[op.name].immediate[i].type
852                 if (!bin[type])
853                     throw new TypeError(`Unknown type: ${type} in op: ${op.name}`);
854                 put(bin, type, op.immediates[i]);
855             }
856             break;
857         case "i32.const": {
858             assert.eq(op.immediates.length, 1);
859             let imm = op.immediates[0];
860             // Do a static cast to make large int32s signed.
861             if (imm >= 2 ** 31)
862                 imm = imm - (2 ** 32);
863             put(bin, "varint32", imm);
864             break;
865         }
866         case "br_table":
867             put(bin, "varuint32", op.immediates.length - 1);
868             for (let imm of op.immediates)
869                 put(bin, "varuint32", imm);
870             break;
871         }
872     };
873
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: [] });
877     };
878
879     const emitters = {
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);
889                 else {
890                     put(bin, "varuint1", 1);
891                     put(bin, "varint7", WASM.typeValue[entry.ret]);
892                 }
893             }
894         },
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}`);
903                 case "Function": {
904                     put(bin, "varuint32", entry.type);
905                     break;
906                 }
907                 case "Table": {
908                     putTable(bin, entry.tableDescription);
909                     break;
910                 }
911                 case "Memory": {
912                     let {initial, maximum} = entry.memoryDescription;
913                     putResizableLimits(bin, initial, maximum);
914                     break;
915                 };
916                 case "Global":
917                     putGlobalType(bin, entry.globalDescription);
918                     break;
919                 }
920             }
921         },
922
923         Function: (section, bin) => {
924             put(bin, "varuint32", section.data.length);
925             for (const signature of section.data)
926                 put(bin, "varuint32", signature);
927         },
928
929         Table: (section, bin) => {
930             put(bin, "varuint32", section.data.length);
931             for (const {tableDescription} of section.data) {
932                 putTable(bin, tableDescription);
933             }
934         },
935
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);
942                 if (memory.max)
943                     put(bin, "varuint32", memory.max);
944             }
945         },
946
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)
952             }
953         },
954
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) {
961                 case "Global":
962                 case "Function":
963                 case "Memory":
964                 case "Table":
965                     put(bin, "varuint32", entry.index);
966                     break;
967                 default: throw new Error(`Implementation problem: unexpected kind ${entry.kind}`);
968                 }
969             }
970         },
971         Start: (section, bin) => {
972             put(bin, "varuint32", section.data[0]);
973         },
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);
979
980                 let initExpr;
981                 if (typeof offset === "number")
982                     initExpr = {op: "i32.const", initValue: offset};
983                 else
984                     initExpr = offset;
985                 putInitExpr(bin, initExpr);
986
987                 put(bin, "varuint32", functionIndices.length);
988                 for (const functionIndex of functionIndices)
989                     put(bin, "varuint32", functionIndex);
990             }
991         },
992
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]]);
1002                 }
1003
1004                 for (const op of func.code)
1005                     putOp(funcBin, op);
1006
1007                 funcBin.apply();
1008             }
1009         },
1010
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);
1023             }
1024         },
1025     };
1026
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];
1035             if (emitter)
1036                 emitter(section, sectionBin);
1037             else {
1038                 // Unknown section.
1039                 put(sectionBin, "string", section.name);
1040                 for (const byte of section.data)
1041                     put(sectionBin, "uint8", byte);
1042             }
1043             sectionBin.apply();
1044         }
1045         wasmBin.trim();
1046         return wasmBin;
1047     };
1048 }
1049
1050
1051 const LLB = LowLevelBinary;
1052
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);
1056     return CamelCase;
1057 };
1058
1059 const _isValidValue = (value, type) => {
1060     switch (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}`);
1067     }
1068 };
1069 const _unknownSectionId = 0;
1070
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")
1076         ret = "void";
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];
1080 };
1081
1082 const _errorHandlingProxyFor = builder => builder["__isProxy"] ? builder : new Proxy(builder, {
1083     get: (target, property, receiver) => {
1084         if (property === "__isProxy")
1085             return true;
1086         if (target[property] === undefined)
1087             throw new Error(`WebAssembly builder received unknown property '${property}'`);
1088         return target[property];
1089     }
1090 });
1091
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`);
1099         }
1100         return type;
1101     }
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.
1106     types:
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])
1112                     continue types;
1113             }
1114             type = i;
1115             break;
1116         }
1117     }
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;
1122     }
1123     return type;
1124 };
1125
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);
1136     };
1137 };
1138
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);
1145     };
1146 };
1147
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);
1154     };
1155 };
1156
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);
1164         }
1165
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".
1170         case "object":
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`);
1174             break;
1175         case "undefined":
1176             // Assume it's the same as the field (i.e. it's not being renamed).
1177             index = field;
1178             break;
1179         default: throw new Error(`Export section's index must be a string or a number, got ${index}`);
1180         }
1181
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.
1194             let exportedImport;
1195             for (const i of importSection.data) {
1196                 if (i.module === correspondingImport.module && i.field === correspondingImport.field) {
1197                     exportedImport = i;
1198                     break;
1199                 }
1200             }
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`);
1205         }
1206         section.data.push({ field: field, type: type, kind: "Function", index: index });
1207         return _errorHandlingProxyFor(nextBuilder);
1208     };
1209 };
1210
1211 const _normalizeMutability = (mutability) => {
1212     if (mutability === "mutable")
1213         return 1;
1214     else if (mutability === "immutable")
1215         return 0;
1216     else
1217         throw new Error(`mutability should be either "mutable" or "immutable", but got ${mutability}`);
1218 };
1219
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);
1225     }
1226 };
1227
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);
1233     }
1234 };
1235
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);
1241     }
1242 };
1243
1244 const _importGlobalContinuation = (builder, section, nextBuilder) => {
1245     return () => {
1246         const globalBuilder = {
1247             End: () => nextBuilder
1248         };
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);
1256             };
1257         }
1258         return _errorHandlingProxyFor(globalBuilder);
1259     };
1260 };
1261
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
1266         } else {
1267             // Handle our own meta-types.
1268             switch (expect) {
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}"`);
1278             }
1279         }
1280     }
1281 }
1282
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
1287         } else {
1288             // Handle our own meta-types.
1289             switch (expect) {
1290             case "any": break;
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}"`);
1299             }
1300         }
1301     }
1302 };
1303
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}`);
1308     else
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
1317             break;
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
1321         case "value":
1322             assert.truthy(_isValidValue(got, ret[0]), `Invalid value on ${op}: got "${got}", expected ${ret[0]}`);
1323             break;
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
1326             // Control:
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
1329         case "sig":
1330             assert.truthy(WASM.isValidBlockType(imms[idx]), `Invalid block type on ${op}: "${imms[idx]}"`);
1331             break;
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}"`);
1336         }
1337     }
1338 };
1339
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;
1348
1349         const checkStackArgs = builder._checked ? _checkStackArgs : () => {};
1350         const checkStackReturn = builder._checked ? _checkStackReturn : () => {};
1351         const checkImms = builder._checked ? _checkImms : () => {};
1352
1353         functionBuilder[name] = (...args) => {
1354             let nextBuilder;
1355             switch (name) {
1356             default:
1357                 nextBuilder = functionBuilder;
1358                 break;
1359             case "End":
1360                 nextBuilder = previousBuilder;
1361                 break;
1362             case "Block":
1363             case "Loop":
1364             case "If":
1365                 nextBuilder = _createFunctionBuilder(func, builder, functionBuilder);
1366                 break;
1367             }
1368
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);
1381         };
1382     };
1383
1384     return _errorHandlingProxyFor(functionBuilder);
1385 }
1386
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`);
1395
1396         if (typeof(signature) === "undefined")
1397             signature = { params: [] };
1398
1399         let type;
1400         let params;
1401         if (typeof signature === "object") {
1402             assert.hasObjectProperty(signature, "params", `Expect function signature to be an object with a "params" field, got "${signature}"`);
1403             let ret;
1404             ([params, ret] = _normalizeFunctionSignature(signature.params, signature.ret));
1405             signature = {params, ret};
1406             type = _maybeRegisterType(builder, signature);
1407         } else {
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);
1414             type = signature;
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;
1418         }
1419
1420         const func = {
1421             name: functionName,
1422             type,
1423             signature: signature,
1424             locals: params.concat(locals), // Parameters are the first locals.
1425             parameterCount: params.length,
1426             code: []
1427         };
1428
1429         const functionSection = builder._getSection("Function");
1430         if (functionSection)
1431             functionSection.data.push(func.type);
1432
1433         section.data.push(func);
1434         builder._registerFunctionToIndexSpace(functionName);
1435
1436         return _createFunctionBuilder(func, builder, previousBuilder);
1437     }
1438 };
1439
1440 class Builder {
1441     constructor() {
1442         this.setChecked(true);
1443         let preamble = {};
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();
1451     }
1452     setChecked(checked) {
1453         this._checked = checked;
1454         return this;
1455     }
1456     setPreamble(p) {
1457         this._preamble = Object.assign(this._preamble || {}, p);
1458         return this;
1459     }
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')
1463             return obj;
1464         const keys = Object.keys(obj).sort();
1465         let entries = [];
1466         for (let k in keys)
1467             entries.push([k, obj[k]]);
1468         return JSON.stringify(entries);
1469     }
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.
1473             name = {};
1474         }
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);
1480     }
1481     _getFunctionFromIndexSpace(name) {
1482         return this._functionIndexSpace.get(this._functionIndexSpaceKeyHash(name));
1483     }
1484     _registerSectionBuilders() {
1485         for (const section in WASM.description.section) {
1486             switch (section) {
1487             case "Type":
1488                 this[section] = function() {
1489                     const s = this._addSection(section);
1490                     const builder = this;
1491                     const typeBuilder = {
1492                         End: () => builder,
1493                         Func: (params, ret) => {
1494                             [params, ret] = _normalizeFunctionSignature(params, ret);
1495                             s.data.push({ params: params, ret: ret });
1496                             return _errorHandlingProxyFor(typeBuilder);
1497                         },
1498                     };
1499                     return _errorHandlingProxyFor(typeBuilder);
1500                 };
1501                 break;
1502
1503             case "Import":
1504                 this[section] = function() {
1505                     const s = this._addSection(section);
1506                     const importBuilder = {
1507                         End: () => this,
1508                     };
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);
1514                 };
1515                 break;
1516
1517             case "Function":
1518                 this[section] = function() {
1519                     const s = this._addSection(section);
1520                     const functionBuilder = {
1521                         End: () => this
1522                         // FIXME: add ability to add this with whatever.
1523                     };
1524                     return _errorHandlingProxyFor(functionBuilder);
1525                 };
1526                 break;
1527
1528             case "Table":
1529                 this[section] = function() {
1530                     const s = this._addSection(section);
1531                     const tableBuilder = {
1532                         End: () => this,
1533                         Table: ({initial, maximum, element}) => {
1534                             s.data.push({tableDescription: {initial, maximum, element}});
1535                             return _errorHandlingProxyFor(tableBuilder);
1536                         }
1537                     };
1538                     return _errorHandlingProxyFor(tableBuilder);
1539                 };
1540                 break;
1541
1542             case "Memory":
1543                 this[section] = function() {
1544                     const s = this._addSection(section);
1545                     const memoryBuilder = {
1546                         End: () => this,
1547                         InitialMaxPages: (initial, max) => {
1548                             s.data.push({ initial, max });
1549                             return _errorHandlingProxyFor(memoryBuilder);
1550                         }
1551                     };
1552                     return _errorHandlingProxyFor(memoryBuilder);
1553                 };
1554                 break;
1555
1556             case "Global":
1557                 this[section] = function() {
1558                     const s = this._addSection(section);
1559                     const globalBuilder = {
1560                         End: () => this,
1561                         GetGlobal: (type, initValue, mutability) => {
1562                             s.data.push({ type, op: "get_global", mutability: _normalizeMutability(mutability), initValue });
1563                             return _errorHandlingProxyFor(globalBuilder);
1564                         }
1565                     };
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);
1570                         };
1571                     }
1572                     return _errorHandlingProxyFor(globalBuilder);
1573                 };
1574                 break;
1575
1576             case "Export":
1577                 this[section] = function() {
1578                     const s = this._addSection(section);
1579                     const exportBuilder = {
1580                         End: () => this,
1581                     };
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);
1587                 };
1588                 break;
1589
1590             case "Start":
1591                 this[section] = function(functionIndexOrName) {
1592                     const s = this._addSection(section);
1593                     const startBuilder = {
1594                         End: () => this,
1595                     };
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);
1600                 };
1601                 break;
1602
1603             case "Element":
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()");
1607
1608                     const s = this._addSection(section);
1609                     const elementBuilder = {
1610                         End: () => this,
1611                         Element: ({tableIndex = 0, offset, functionIndices}) => {
1612                             s.data.push({tableIndex, offset, functionIndices});
1613                             return _errorHandlingProxyFor(elementBuilder);
1614                         }
1615                     };
1616
1617                     return _errorHandlingProxyFor(elementBuilder);
1618                 };
1619                 break;
1620
1621             case "Code":
1622                 this[section] = function() {
1623                     const s = this._addSection(section);
1624                     const builder = this;
1625                     const codeBuilder =  {
1626                         End: () => {
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")
1636                                         continue;
1637                                     switch (typeof(e.index)) {
1638                                     default: throw new Error(`Unexpected export index "${e.index}"`);
1639                                     case "string": {
1640                                         const index = builder._getFunctionFromIndexSpace(e.index);
1641                                         assert.isNumber(index, `Export section contains undefined function "${e.index}"`);
1642                                         e.index = index;
1643                                     } // Fallthrough.
1644                                     case "number": {
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`);
1648                                     } break;
1649                                     case "undefined":
1650                                         throw new Error(`Unimplemented: Function().End() with undefined export index`); // FIXME
1651                                     }
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;
1659                                             }
1660                                         }
1661                                         const functionIndex = e.index - functionIndexSpaceOffset;
1662                                         e.type = codeSection.data[functionIndex].type;
1663                                     }
1664                                 }
1665                             }
1666                             if (startSection) {
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;
1674                                 } else {
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
1680                                 }
1681                             }
1682                             return _errorHandlingProxyFor(builder);
1683                         },
1684
1685                     };
1686                     codeBuilder.Function = _createFunction(s, builder, codeBuilder);
1687                     return _errorHandlingProxyFor(codeBuilder);
1688                 };
1689                 break;
1690
1691             case "Data":
1692                 this[section] = function() {
1693                     const s = this._addSection(section);
1694                     const dataBuilder = {
1695                         End: () => this,
1696                         Segment: data => {
1697                             assert.isArray(data);
1698                             for (const datum of data) {
1699                                 assert.isNumber(datum);
1700                                 assert.ge(datum, 0);
1701                                 assert.le(datum, 0xff);
1702                             }
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,
1707                                 Index: index => {
1708                                     assert.eq(index, 0); // Linear memory index must be zero in MVP.
1709                                     thisSegment.index = index;
1710                                     return _errorHandlingProxyFor(segmentBuilder);
1711                                 },
1712                                 Offset: offset => {
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);
1717                                 },
1718                             };
1719                             return _errorHandlingProxyFor(segmentBuilder);
1720                         },
1721                     };
1722                     return _errorHandlingProxyFor(dataBuilder);
1723                 };
1724                 break;
1725
1726             default:
1727                 this[section] = () => { throw new Error(`Unknown section type "${section}"`); };
1728                 break;
1729             }
1730         }
1731
1732         this.Unknown = function(name) {
1733             const s = this._addSection(name);
1734             const builder = this;
1735             const unknownBuilder =  {
1736                 End: () => builder,
1737                 Byte: b => {
1738                     assert.eq(b & 0xFF, b, `Unknown section expected byte, got: "${b}"`);
1739                     s.data.push(b);
1740                     return _errorHandlingProxyFor(unknownBuilder);
1741                 }
1742             };
1743             return _errorHandlingProxyFor(unknownBuilder);
1744         };
1745     }
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}`);
1754             // Check ordering.
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)
1758                         continue;
1759                     assert.le(this._sections[i].id, number, `Bad section ordering: "${this._sections[i].name}" cannot precede "${name}"`);
1760                     break;
1761                 }
1762             }
1763         }
1764         const s = Object.assign({ name: name, id: number, data: [] }, extraObject || {});
1765         this._sections.push(s);
1766         return s;
1767     }
1768     _getSection(nameOrNumber) {
1769         switch (typeof(nameOrNumber)) {
1770         default: throw new Error(`Implementation problem: can not get section "${nameOrNumber}"`);
1771         case "string":
1772             for (const s of this._sections)
1773                 if (s.name === nameOrNumber)
1774                     return s;
1775             return undefined;
1776         case "number":
1777             for (const s of this._sections)
1778                 if (s.id === nameOrNumber)
1779                     return s;
1780             return undefined;
1781         }
1782     }
1783     optimize() {
1784         // FIXME Add more optimizations. https://bugs.webkit.org/show_bug.cgi?id=163424
1785         return this;
1786     }
1787     json() {
1788         const obj = {
1789             preamble: this._preamble,
1790             section: this._sections
1791         };
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";
1796             return value;
1797         };
1798         return JSON.stringify(obj, replacer);
1799     }
1800     AsmJS() {
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");
1804     }
1805     WebAssembly() { return BuildWebAssembly.Binary(this._preamble, this._sections); }
1806 };