2 * Copyright (C) 2016 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 import * as assert from 'assert.js';
27 import * as BuildWebAssembly from 'Builder_WebAssemblyBinary.js';
28 import * as LLB from 'LowLevelBinary.js';
29 import * as WASM from 'WASM.js';
30 import * as util from 'utilities.js';
32 const _isValidValue = (value, type) => {
34 // We allow both signed and unsigned numbers.
35 case "i32": return Math.round(value) === value && LLB.varint32Min <= value && value <= LLB.varuint32Max;
36 case "i64": return true; // FIXME https://bugs.webkit.org/show_bug.cgi?id=163420 64-bit values
37 case "f32": return typeof(value) === "number" && isFinite(value);
38 case "f64": return typeof(value) === "number" && isFinite(value);
39 default: throw new Error(`Implementation problem: unknown type ${type}`);
42 const _unknownSectionId = 0;
44 const _normalizeFunctionSignature = (params, ret) => {
45 assert.isArray(params);
46 for (const p of params)
47 assert.truthy(WASM.isValidValueType(p), `Type parameter ${p} needs a valid value type`);
48 if (typeof(ret) === "undefined")
50 assert.isNotArray(ret, `Multiple return values not supported by WebAssembly yet`);
51 assert.truthy(WASM.isValidBlockType(ret), `Type return ${ret} must be valid block type`);
55 const _errorHandlingProxyFor = builder => builder["__isProxy"] ? builder : new Proxy(builder, {
56 get: (target, property, receiver) => {
57 if (property === "__isProxy")
59 if (target[property] === undefined)
60 throw new Error(`WebAssembly builder received unknown property '${property}'`);
61 return target[property];
65 const _maybeRegisterType = (builder, type) => {
66 const typeSection = builder._getSection("Type");
67 if (typeof(type) === "number") {
68 // Type numbers already refer to the type section, no need to register them.
69 if (builder._checked) {
70 assert.isNotUndef(typeSection, `Can not use type ${type} if a type section is not present`);
71 assert.isNotUndef(typeSection.data[type], `Type ${type} does not exist in type section`);
75 assert.hasObjectProperty(type, "params", `Expected type to be a number or object with 'params' and optionally 'ret' fields`);
76 const [params, ret] = _normalizeFunctionSignature(type.params, type.ret);
77 assert.isNotUndef(typeSection, `Can not add type if a type section is not present`);
78 // Try reusing an equivalent type from the type section.
80 for (let i = 0; i !== typeSection.data.length; ++i) {
81 const t = typeSection.data[i];
82 if (t.ret === ret && params.length === t.params.length) {
83 for (let j = 0; j !== t.params.length; ++j) {
84 if (params[j] !== t.params[j])
91 if (typeof(type) !== "number") {
92 // Couldn't reuse a pre-existing type, register this type in the type section.
93 typeSection.data.push({ params: params, ret: ret });
94 type = typeSection.data.length - 1;
99 const _importFunctionContinuation = (builder, section, nextBuilder) => {
100 return (module, field, type) => {
101 assert.isString(module, `Import function module should be a string, got "${module}"`);
102 assert.isString(field, `Import function field should be a string, got "${field}"`);
103 const typeSection = builder._getSection("Type");
104 type = _maybeRegisterType(builder, type);
105 section.data.push({ field: field, type: type, kind: "Function", module: module });
106 // Imports also count in the function index space. Map them as objects to avoid clashing with Code functions' names.
107 builder._registerFunctionToIndexSpace({ module: module, field: field });
108 return _errorHandlingProxyFor(nextBuilder);
112 const _importMemoryContinuation = (builder, section, nextBuilder) => {
113 return (module, field, {initial, maximum}) => {
114 assert.isString(module, `Import Memory module should be a string, got "${module}"`);
115 assert.isString(field, `Import Memory field should be a string, got "${field}"`);
116 section.data.push({module, field, kind: "Memory", memoryDescription: {initial, maximum}});
117 return _errorHandlingProxyFor(nextBuilder);
121 const _importTableContinuation = (builder, section, nextBuilder) => {
122 return (module, field, {initial, maximum, element}) => {
123 assert.isString(module, `Import Table module should be a string, got "${module}"`);
124 assert.isString(field, `Import Table field should be a string, got "${field}"`);
125 section.data.push({module, field, kind: "Table", tableDescription: {initial, maximum, element}});
126 return _errorHandlingProxyFor(nextBuilder);
130 const _exportFunctionContinuation = (builder, section, nextBuilder) => {
131 return (field, index, type) => {
132 assert.isString(field, `Export function field should be a string, got "${field}"`);
133 const typeSection = builder._getSection("Type");
134 if (typeof(type) !== "undefined") {
135 // Exports can leave the type unspecified, letting the Code builder patch them up later.
136 type = _maybeRegisterType(builder, type);
139 // We can't check much about "index" here because the Code section succeeds the Export section. More work is done at Code().End() time.
140 switch (typeof(index)) {
141 case "string": break; // Assume it's a function name which will be revealed in the Code section.
142 case "number": break; // Assume it's a number in the "function index space".
144 // Re-exporting an import.
145 assert.hasObjectProperty(index, "module", `Re-exporting "${field}" from an import`);
146 assert.hasObjectProperty(index, "field", `Re-exporting "${field}" from an import`);
149 // Assume it's the same as the field (i.e. it's not being renamed).
152 default: throw new Error(`Export section's index must be a string or a number, got ${index}`);
155 const correspondingImport = builder._getFunctionFromIndexSpace(index);
156 const importSection = builder._getSection("Import");
157 if (typeof(index) === "object") {
158 // Re-exporting an import using its module+field name.
159 assert.isNotUndef(correspondingImport, `Re-exporting "${field}" couldn't find import from module "${index.module}" field "${index.field}"`);
160 index = correspondingImport;
161 if (typeof(type) === "undefined")
162 type = importSection.data[index].type;
163 if (builder._checked)
164 assert.eq(type, importSection.data[index].type, `Re-exporting import "${importSection.data[index].field}" as "${field}" has mismatching type`);
165 } else if (typeof(correspondingImport) !== "undefined") {
166 // Re-exporting an import using its index.
168 for (const i of importSection.data) {
169 if (i.module === correspondingImport.module && i.field === correspondingImport.field) {
174 if (typeof(type) === "undefined")
175 type = exportedImport.type;
176 if (builder._checked)
177 assert.eq(type, exportedImport.type, `Re-exporting import "${exportedImport.field}" as "${field}" has mismatching type`);
179 section.data.push({ field: field, type: type, kind: "Function", index: index });
180 return _errorHandlingProxyFor(nextBuilder);
184 const _normalizeMutability = (mutability) => {
185 if (mutability === "mutable")
187 else if (mutability === "immutable")
190 throw new Error(`mutability should be either "mutable" or "immutable", but got ${mutability}`);
193 const _exportGlobalContinuation = (builder, section, nextBuilder) => {
194 return (field, index) => {
195 assert.isNumber(index, `Global exports only support number indices right now`);
196 section.data.push({ field, kind: "Global", index });
197 return _errorHandlingProxyFor(nextBuilder);
201 const _exportMemoryContinuation = (builder, section, nextBuilder) => {
202 return (field, index) => {
203 assert.isNumber(index, `Memory exports only support number indices`);
204 section.data.push({field, kind: "Memory", index});
205 return _errorHandlingProxyFor(nextBuilder);
209 const _exportTableContinuation = (builder, section, nextBuilder) => {
210 return (field, index) => {
211 assert.isNumber(index, `Table exports only support number indices`);
212 section.data.push({field, kind: "Table", index});
213 return _errorHandlingProxyFor(nextBuilder);
217 const _importGlobalContinuation = (builder, section, nextBuilder) => {
219 const globalBuilder = {
220 End: () => nextBuilder
222 for (let op of WASM.description.value_type) {
223 globalBuilder[util.toJavaScriptName(op)] = (module, field, mutability) => {
224 assert.isString(module, `Import global module should be a string, got "${module}"`);
225 assert.isString(field, `Import global field should be a string, got "${field}"`);
226 assert.isString(mutability, `Import global mutability should be a string, got "${mutability}"`);
227 section.data.push({ globalDescription: { type: op, mutability: _normalizeMutability(mutability) }, module, field, kind: "Global" });
228 return _errorHandlingProxyFor(globalBuilder);
231 return _errorHandlingProxyFor(globalBuilder);
235 const _checkStackArgs = (op, param) => {
236 for (let expect of param) {
237 if (WASM.isValidType(expect)) {
238 // FIXME implement stack checks for arguments. https://bugs.webkit.org/show_bug.cgi?id=163421
240 // Handle our own meta-types.
242 case "addr": break; // FIXME implement addr. https://bugs.webkit.org/show_bug.cgi?id=163421
243 case "any": break; // FIXME implement any. https://bugs.webkit.org/show_bug.cgi?id=163421
244 case "bool": break; // FIXME implement bool. https://bugs.webkit.org/show_bug.cgi?id=163421
245 case "call": break; // FIXME implement call stack argument checks based on function signature. https://bugs.webkit.org/show_bug.cgi?id=163421
246 case "global": break; // FIXME implement global. https://bugs.webkit.org/show_bug.cgi?id=163421
247 case "local": break; // FIXME implement local. https://bugs.webkit.org/show_bug.cgi?id=163421
248 case "prev": break; // FIXME implement prev, checking for whetever the previous value was. https://bugs.webkit.org/show_bug.cgi?id=163421
249 case "size": break; // FIXME implement size. https://bugs.webkit.org/show_bug.cgi?id=163421
250 default: throw new Error(`Implementation problem: unhandled meta-type "${expect}" on "${op}"`);
256 const _checkStackReturn = (op, ret) => {
257 for (let expect of ret) {
258 if (WASM.isValidType(expect)) {
259 // FIXME implement stack checks for return. https://bugs.webkit.org/show_bug.cgi?id=163421
261 // Handle our own meta-types.
264 case "bool": break; // FIXME implement bool. https://bugs.webkit.org/show_bug.cgi?id=163421
265 case "call": break; // FIXME implement call stack return check based on function signature. https://bugs.webkit.org/show_bug.cgi?id=163421
266 case "control": break; // FIXME implement control. https://bugs.webkit.org/show_bug.cgi?id=163421
267 case "global": break; // FIXME implement global. https://bugs.webkit.org/show_bug.cgi?id=163421
268 case "local": break; // FIXME implement local. https://bugs.webkit.org/show_bug.cgi?id=163421
269 case "prev": break; // FIXME implement prev, checking for whetever the parameter type was. https://bugs.webkit.org/show_bug.cgi?id=163421
270 case "size": break; // FIXME implement size. https://bugs.webkit.org/show_bug.cgi?id=163421
271 default: throw new Error(`Implementation problem: unhandled meta-type "${expect}" on "${op}"`);
277 const _checkImms = (op, imms, expectedImms, ret) => {
278 const minExpectedImms = expectedImms.filter(i => i.type.slice(-1) !== '*').length;
279 if (minExpectedImms !== expectedImms.length)
280 assert.ge(imms.length, minExpectedImms, `"${op}" expects at least ${minExpectedImms} immediate${minExpectedImms !== 1 ? 's' : ''}, got ${imms.length}`);
282 assert.eq(imms.length, minExpectedImms, `"${op}" expects exactly ${minExpectedImms} immediate${minExpectedImms !== 1 ? 's' : ''}, got ${imms.length}`);
283 for (let idx = 0; idx !== expectedImms.length; ++idx) {
284 const got = imms[idx];
285 const expect = expectedImms[idx];
286 switch (expect.name) {
287 case "function_index":
288 assert.truthy(_isValidValue(got, "i32") && got >= 0, `Invalid value on ${op}: got "${got}", expected non-negative i32`);
289 // FIXME check function indices. https://bugs.webkit.org/show_bug.cgi?id=163421
291 case "local_index": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
292 case "global_index": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
293 case "type_index": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
295 assert.truthy(_isValidValue(got, ret[0]), `Invalid value on ${op}: got "${got}", expected ${ret[0]}`);
297 case "flags": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
298 case "offset": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
300 case "default_target": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
301 case "relative_depth": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
303 assert.truthy(WASM.isValidBlockType(imms[idx]), `Invalid block type on ${op}: "${imms[idx]}"`);
305 case "target_count": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
306 case "target_table": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
307 case "reserved": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
308 default: throw new Error(`Implementation problem: unhandled immediate "${expect.name}" on "${op}"`);
313 const _createFunctionBuilder = (func, builder, previousBuilder) => {
314 let functionBuilder = {};
315 for (const op in WASM.description.opcode) {
316 const name = util.toJavaScriptName(op);
317 const value = WASM.description.opcode[op].value;
318 const ret = WASM.description.opcode[op]["return"];
319 const param = WASM.description.opcode[op].parameter;
320 const imm = WASM.description.opcode[op].immediate;
322 const checkStackArgs = builder._checked ? _checkStackArgs : () => {};
323 const checkStackReturn = builder._checked ? _checkStackReturn : () => {};
324 const checkImms = builder._checked ? _checkImms : () => {};
326 functionBuilder[name] = (...args) => {
330 nextBuilder = functionBuilder;
333 nextBuilder = previousBuilder;
338 nextBuilder = _createFunctionBuilder(func, builder, functionBuilder);
342 // Passing a function as the argument is a way to nest blocks lexically.
343 const continuation = args[args.length - 1];
344 const hasContinuation = typeof(continuation) === "function";
345 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
346 checkImms(op, imms, imm, ret);
347 checkStackArgs(op, param);
348 checkStackReturn(op, ret);
349 const stackArgs = []; // FIXME https://bugs.webkit.org/show_bug.cgi?id=162706
350 func.code.push({ name: op, value: value, arguments: stackArgs, immediates: imms });
352 return _errorHandlingProxyFor(continuation(nextBuilder).End());
353 return _errorHandlingProxyFor(nextBuilder);
357 return _errorHandlingProxyFor(functionBuilder);
360 const _createFunction = (section, builder, previousBuilder) => {
361 return (...args) => {
362 const nameOffset = (typeof(args[0]) === "string") >>> 0;
363 const functionName = nameOffset ? args[0] : undefined;
364 let signature = args[0 + nameOffset];
365 const locals = args[1 + nameOffset] === undefined ? [] : args[1 + nameOffset];
366 for (const local of locals)
367 assert.truthy(WASM.isValidValueType(local), `Type of local: ${local} needs to be a valid value type`);
369 if (typeof(signature) === "undefined")
370 signature = { params: [] };
374 if (typeof signature === "object") {
375 assert.hasObjectProperty(signature, "params", `Expect function signature to be an object with a "params" field, got "${signature}"`);
377 ([params, ret] = _normalizeFunctionSignature(signature.params, signature.ret));
378 signature = {params, ret};
379 type = _maybeRegisterType(builder, signature);
381 assert.truthy(typeof signature === "number");
382 const typeSection = builder._getSection("Type");
383 assert.truthy(!!typeSection);
384 // FIXME: we should allow indices that exceed this to be able to
385 // test JSCs validator is correct. https://bugs.webkit.org/show_bug.cgi?id=165786
386 assert.truthy(signature < typeSection.data.length);
388 signature = typeSection.data[signature];
389 assert.hasObjectProperty(signature, "params", `Expect function signature to be an object with a "params" field, got "${signature}"`);
390 params = signature.params;
396 signature: signature,
397 locals: params.concat(locals), // Parameters are the first locals.
398 parameterCount: params.length,
402 const functionSection = builder._getSection("Function");
404 functionSection.data.push(func.type);
406 section.data.push(func);
407 builder._registerFunctionToIndexSpace(functionName);
409 return _createFunctionBuilder(func, builder, previousBuilder);
413 export default class Builder {
415 this.setChecked(true);
417 for (const p of WASM.description.preamble)
418 preamble[p.name] = p.value;
419 this.setPreamble(preamble);
421 this._functionIndexSpace = new Map();
422 this._functionIndexSpaceCount = 0;
423 this._registerSectionBuilders();
425 setChecked(checked) {
426 this._checked = checked;
430 this._preamble = Object.assign(this._preamble || {}, p);
433 _functionIndexSpaceKeyHash(obj) {
434 // We don't need a full hash, just something that has a defined order for Map. Objects we insert aren't nested.
435 if (typeof(obj) !== 'object')
437 const keys = Object.keys(obj).sort();
440 entries.push([k, obj[k]]);
441 return JSON.stringify(entries);
443 _registerFunctionToIndexSpace(name) {
444 if (typeof(name) === "undefined") {
445 // Registering a nameless function still adds it to the function index space. Register it as something that can't normally be registered.
448 const value = this._functionIndexSpaceCount++;
449 // Collisions are fine: we'll simply count the function and forget the previous one.
450 this._functionIndexSpace.set(this._functionIndexSpaceKeyHash(name), value);
451 // Map it both ways, the number space is distinct from the name space.
452 this._functionIndexSpace.set(this._functionIndexSpaceKeyHash(value), name);
454 _getFunctionFromIndexSpace(name) {
455 return this._functionIndexSpace.get(this._functionIndexSpaceKeyHash(name));
457 _registerSectionBuilders() {
458 for (const section in WASM.description.section) {
461 this[section] = function() {
462 const s = this._addSection(section);
463 const builder = this;
464 const typeBuilder = {
466 Func: (params, ret) => {
467 [params, ret] = _normalizeFunctionSignature(params, ret);
468 s.data.push({ params: params, ret: ret });
469 return _errorHandlingProxyFor(typeBuilder);
472 return _errorHandlingProxyFor(typeBuilder);
477 this[section] = function() {
478 const s = this._addSection(section);
479 const importBuilder = {
482 importBuilder.Global = _importGlobalContinuation(this, s, importBuilder);
483 importBuilder.Function = _importFunctionContinuation(this, s, importBuilder);
484 importBuilder.Memory = _importMemoryContinuation(this, s, importBuilder);
485 importBuilder.Table = _importTableContinuation(this, s, importBuilder);
486 return _errorHandlingProxyFor(importBuilder);
491 this[section] = function() {
492 const s = this._addSection(section);
493 const functionBuilder = {
495 // FIXME: add ability to add this with whatever.
497 return _errorHandlingProxyFor(functionBuilder);
502 this[section] = function() {
503 const s = this._addSection(section);
504 const tableBuilder = {
506 Table: ({initial, maximum, element}) => {
507 s.data.push({tableDescription: {initial, maximum, element}});
508 return _errorHandlingProxyFor(tableBuilder);
511 return _errorHandlingProxyFor(tableBuilder);
516 this[section] = function() {
517 const s = this._addSection(section);
518 const memoryBuilder = {
520 InitialMaxPages: (initial, maximum) => {
521 s.data.push({ initial, maximum });
522 return _errorHandlingProxyFor(memoryBuilder);
525 return _errorHandlingProxyFor(memoryBuilder);
530 this[section] = function() {
531 const s = this._addSection(section);
532 const globalBuilder = {
534 GetGlobal: (type, initValue, mutability) => {
535 s.data.push({ type, op: "get_global", mutability: _normalizeMutability(mutability), initValue });
536 return _errorHandlingProxyFor(globalBuilder);
539 for (let op of WASM.description.value_type) {
540 globalBuilder[util.toJavaScriptName(op)] = (initValue, mutability) => {
541 s.data.push({ type: op, op: op + ".const", mutability: _normalizeMutability(mutability), initValue });
542 return _errorHandlingProxyFor(globalBuilder);
545 return _errorHandlingProxyFor(globalBuilder);
550 this[section] = function() {
551 const s = this._addSection(section);
552 const exportBuilder = {
555 exportBuilder.Global = _exportGlobalContinuation(this, s, exportBuilder);
556 exportBuilder.Function = _exportFunctionContinuation(this, s, exportBuilder);
557 exportBuilder.Memory = _exportMemoryContinuation(this, s, exportBuilder);
558 exportBuilder.Table = _exportTableContinuation(this, s, exportBuilder);
559 return _errorHandlingProxyFor(exportBuilder);
564 this[section] = function(functionIndexOrName) {
565 const s = this._addSection(section);
566 const startBuilder = {
569 if (typeof(functionIndexOrName) !== "number" && typeof(functionIndexOrName) !== "string")
570 throw new Error(`Start section's function index must either be a number or a string`);
571 s.data.push(functionIndexOrName);
572 return _errorHandlingProxyFor(startBuilder);
577 this[section] = function(...args) {
578 if (args.length !== 0 && this._checked)
579 throw new Error("You're doing it wrong. This element does not take arguments. You must chain the call with another Element()");
581 const s = this._addSection(section);
582 const elementBuilder = {
584 Element: ({tableIndex = 0, offset, functionIndices}) => {
585 s.data.push({tableIndex, offset, functionIndices});
586 return _errorHandlingProxyFor(elementBuilder);
590 return _errorHandlingProxyFor(elementBuilder);
595 this[section] = function() {
596 const s = this._addSection(section);
597 const builder = this;
598 const codeBuilder = {
600 // We now have enough information to remap the export section's "type" and "index" according to the Code section we are currently ending.
601 const typeSection = builder._getSection("Type");
602 const importSection = builder._getSection("Import");
603 const exportSection = builder._getSection("Export");
604 const startSection = builder._getSection("Start");
605 const codeSection = s;
607 for (const e of exportSection.data) {
608 if (e.kind !== "Function" || typeof(e.type) !== "undefined")
610 switch (typeof(e.index)) {
611 default: throw new Error(`Unexpected export index "${e.index}"`);
613 const index = builder._getFunctionFromIndexSpace(e.index);
614 assert.isNumber(index, `Export section contains undefined function "${e.index}"`);
618 const index = builder._getFunctionFromIndexSpace(e.index);
619 if (builder._checked)
620 assert.isNotUndef(index, `Export "${e.field}" does not correspond to a defined value in the function index space`);
623 throw new Error(`Unimplemented: Function().End() with undefined export index`); // FIXME
625 if (typeof(e.type) === "undefined") {
626 // This must be a function export from the Code section (re-exports were handled earlier).
627 let functionIndexSpaceOffset = 0;
629 for (const {kind} of importSection.data) {
630 if (kind === "Function")
631 ++functionIndexSpaceOffset;
634 const functionIndex = e.index - functionIndexSpaceOffset;
635 e.type = codeSection.data[functionIndex].type;
640 const start = startSection.data[0];
641 let mapped = builder._getFunctionFromIndexSpace(start);
642 if (!builder._checked) {
643 if (typeof(mapped) === "undefined")
644 mapped = start; // In unchecked mode, simply use what was provided if it's nonsensical.
645 assert.isA(start, "number"); // It can't be too nonsensical, otherwise we can't create a binary.
646 startSection.data[0] = start;
648 if (typeof(mapped) === "undefined")
649 throw new Error(`Start section refers to non-existant function '${start}'`);
650 if (typeof(start) === "string" || typeof(start) === "object")
651 startSection.data[0] = mapped;
652 // 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
655 return _errorHandlingProxyFor(builder);
659 codeBuilder.Function = _createFunction(s, builder, codeBuilder);
660 return _errorHandlingProxyFor(codeBuilder);
665 this[section] = function() {
666 const s = this._addSection(section);
667 const dataBuilder = {
670 assert.isArray(data);
671 for (const datum of data) {
672 assert.isNumber(datum);
674 assert.le(datum, 0xff);
676 s.data.push({ data: data, index: 0, offset: 0 });
677 let thisSegment = s.data[s.data.length - 1];
678 const segmentBuilder = {
679 End: () => dataBuilder,
681 assert.eq(index, 0); // Linear memory index must be zero in MVP.
682 thisSegment.index = index;
683 return _errorHandlingProxyFor(segmentBuilder);
686 // FIXME allow complex init_expr here. https://bugs.webkit.org/show_bug.cgi?id=165700
687 assert.isNumber(offset);
688 thisSegment.offset = offset;
689 return _errorHandlingProxyFor(segmentBuilder);
692 return _errorHandlingProxyFor(segmentBuilder);
695 return _errorHandlingProxyFor(dataBuilder);
700 this[section] = () => { throw new Error(`Unknown section type "${section}"`); };
705 this.Unknown = function(name) {
706 const s = this._addSection(name);
707 const builder = this;
708 const unknownBuilder = {
711 assert.eq(b & 0xFF, b, `Unknown section expected byte, got: "${b}"`);
713 return _errorHandlingProxyFor(unknownBuilder);
716 return _errorHandlingProxyFor(unknownBuilder);
719 _addSection(nameOrNumber, extraObject) {
720 const name = typeof(nameOrNumber) === "string" ? nameOrNumber : "";
721 const number = typeof(nameOrNumber) === "number" ? nameOrNumber : (WASM.description.section[name] ? WASM.description.section[name].value : _unknownSectionId);
724 for (const s of this._sections)
725 if (number !== _unknownSectionId)
726 assert.falsy(s.name === name && s.id === number, `Cannot have two sections with the same name "${name}" and ID ${number}`);
728 if ((number !== _unknownSectionId) && (this._sections.length !== 0)) {
729 for (let i = this._sections.length - 1; i >= 0; --i) {
730 if (this._sections[i].id === _unknownSectionId)
732 assert.le(this._sections[i].id, number, `Bad section ordering: "${this._sections[i].name}" cannot precede "${name}"`);
737 const s = Object.assign({ name: name, id: number, data: [] }, extraObject || {});
738 this._sections.push(s);
741 _getSection(nameOrNumber) {
742 switch (typeof(nameOrNumber)) {
743 default: throw new Error(`Implementation problem: can not get section "${nameOrNumber}"`);
745 for (const s of this._sections)
746 if (s.name === nameOrNumber)
750 for (const s of this._sections)
751 if (s.id === nameOrNumber)
757 // FIXME Add more optimizations. https://bugs.webkit.org/show_bug.cgi?id=163424
762 preamble: this._preamble,
763 section: this._sections
765 // JSON.stringify serializes -0.0 as 0.0.
766 const replacer = (key, value) => {
767 if (value === 0.0 && 1.0 / value === -Infinity)
768 return "NEGATIVE_ZERO";
771 return JSON.stringify(obj, replacer);
774 "use asm"; // For speed.
775 // FIXME Create an asm.js equivalent string which can be eval'd. https://bugs.webkit.org/show_bug.cgi?id=163425
776 throw new Error("asm.js not implemented yet");
778 WebAssembly() { return BuildWebAssembly.Binary(this._preamble, this._sections); }