| "use strict"; |
| |
| Object.defineProperty(exports, "__esModule", { |
| value: true |
| }); |
| exports.applyOperations = applyOperations; |
| |
| var _wasmGen = require("@webassemblyjs/wasm-gen"); |
| |
| var _encoder = require("@webassemblyjs/wasm-gen/lib/encoder"); |
| |
| var _ast = require("@webassemblyjs/ast"); |
| |
| var _helperWasmSection = require("@webassemblyjs/helper-wasm-section"); |
| |
| var _helperBuffer = require("@webassemblyjs/helper-buffer"); |
| |
| var _helperWasmBytecode = require("@webassemblyjs/helper-wasm-bytecode"); |
| |
| function _sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } |
| |
| function _slicedToArray(arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return _sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } } |
| |
| function shiftLocNodeByDelta(node, delta) { |
| (0, _ast.assertHasLoc)(node); // $FlowIgnore: assertHasLoc ensures that |
| |
| node.loc.start.column += delta; // $FlowIgnore: assertHasLoc ensures that |
| |
| node.loc.end.column += delta; |
| } |
| |
| function applyUpdate(ast, uint8Buffer, _ref) { |
| var _ref2 = _slicedToArray(_ref, 2), |
| oldNode = _ref2[0], |
| newNode = _ref2[1]; |
| |
| var deltaElements = 0; |
| (0, _ast.assertHasLoc)(oldNode); |
| var sectionName = (0, _helperWasmBytecode.getSectionForNode)(newNode); |
| var replacementByteArray = (0, _wasmGen.encodeNode)(newNode); |
| /** |
| * Replace new node as bytes |
| */ |
| |
| uint8Buffer = (0, _helperBuffer.overrideBytesInBuffer)(uint8Buffer, // $FlowIgnore: assertHasLoc ensures that |
| oldNode.loc.start.column, // $FlowIgnore: assertHasLoc ensures that |
| oldNode.loc.end.column, replacementByteArray); |
| /** |
| * Update function body size if needed |
| */ |
| |
| if (sectionName === "code") { |
| // Find the parent func |
| (0, _ast.traverse)(ast, { |
| Func: function Func(_ref3) { |
| var node = _ref3.node; |
| var funcHasThisIntr = node.body.find(function (n) { |
| return n === newNode; |
| }) !== undefined; // Update func's body size if needed |
| |
| if (funcHasThisIntr === true) { |
| // These are the old functions locations informations |
| (0, _ast.assertHasLoc)(node); |
| var oldNodeSize = (0, _wasmGen.encodeNode)(oldNode).length; |
| var bodySizeDeltaBytes = replacementByteArray.length - oldNodeSize; |
| |
| if (bodySizeDeltaBytes !== 0) { |
| var newValue = node.metadata.bodySize + bodySizeDeltaBytes; |
| var newByteArray = (0, _encoder.encodeU32)(newValue); // function body size byte |
| // FIXME(sven): only handles one byte u32 |
| |
| var start = node.loc.start.column; |
| var end = start + 1; |
| uint8Buffer = (0, _helperBuffer.overrideBytesInBuffer)(uint8Buffer, start, end, newByteArray); |
| } |
| } |
| } |
| }); |
| } |
| /** |
| * Update section size |
| */ |
| |
| |
| var deltaBytes = replacementByteArray.length - ( // $FlowIgnore: assertHasLoc ensures that |
| oldNode.loc.end.column - oldNode.loc.start.column); // Init location informations |
| |
| newNode.loc = { |
| start: { |
| line: -1, |
| column: -1 |
| }, |
| end: { |
| line: -1, |
| column: -1 |
| } |
| }; // Update new node end position |
| // $FlowIgnore: assertHasLoc ensures that |
| |
| newNode.loc.start.column = oldNode.loc.start.column; // $FlowIgnore: assertHasLoc ensures that |
| |
| newNode.loc.end.column = // $FlowIgnore: assertHasLoc ensures that |
| oldNode.loc.start.column + replacementByteArray.length; |
| return { |
| uint8Buffer: uint8Buffer, |
| deltaBytes: deltaBytes, |
| deltaElements: deltaElements |
| }; |
| } |
| |
| function applyDelete(ast, uint8Buffer, node) { |
| var deltaElements = -1; // since we removed an element |
| |
| (0, _ast.assertHasLoc)(node); |
| var sectionName = (0, _helperWasmBytecode.getSectionForNode)(node); |
| |
| if (sectionName === "start") { |
| var sectionMetadata = (0, _ast.getSectionMetadata)(ast, "start"); |
| /** |
| * The start section only contains one element, |
| * we need to remove the whole section |
| */ |
| |
| uint8Buffer = (0, _helperWasmSection.removeSections)(ast, uint8Buffer, "start"); |
| |
| var _deltaBytes = -(sectionMetadata.size.value + 1) |
| /* section id */ |
| ; |
| |
| return { |
| uint8Buffer: uint8Buffer, |
| deltaBytes: _deltaBytes, |
| deltaElements: deltaElements |
| }; |
| } // replacement is nothing |
| |
| |
| var replacement = []; |
| uint8Buffer = (0, _helperBuffer.overrideBytesInBuffer)(uint8Buffer, // $FlowIgnore: assertHasLoc ensures that |
| node.loc.start.column, // $FlowIgnore: assertHasLoc ensures that |
| node.loc.end.column, replacement); |
| /** |
| * Update section |
| */ |
| // $FlowIgnore: assertHasLoc ensures that |
| |
| var deltaBytes = -(node.loc.end.column - node.loc.start.column); |
| return { |
| uint8Buffer: uint8Buffer, |
| deltaBytes: deltaBytes, |
| deltaElements: deltaElements |
| }; |
| } |
| |
| function applyAdd(ast, uint8Buffer, node) { |
| var deltaElements = +1; // since we added an element |
| |
| var sectionName = (0, _helperWasmBytecode.getSectionForNode)(node); |
| var sectionMetadata = (0, _ast.getSectionMetadata)(ast, sectionName); // Section doesn't exists, we create an empty one |
| |
| if (typeof sectionMetadata === "undefined") { |
| var res = (0, _helperWasmSection.createEmptySection)(ast, uint8Buffer, sectionName); |
| uint8Buffer = res.uint8Buffer; |
| sectionMetadata = res.sectionMetadata; |
| } |
| /** |
| * check that the expressions were ended |
| */ |
| |
| |
| if ((0, _ast.isFunc)(node)) { |
| // $FlowIgnore |
| var body = node.body; |
| |
| if (body.length === 0 || body[body.length - 1].id !== "end") { |
| throw new Error("expressions must be ended"); |
| } |
| } |
| |
| if ((0, _ast.isGlobal)(node)) { |
| // $FlowIgnore |
| var body = node.init; |
| |
| if (body.length === 0 || body[body.length - 1].id !== "end") { |
| throw new Error("expressions must be ended"); |
| } |
| } |
| /** |
| * Add nodes |
| */ |
| |
| |
| var newByteArray = (0, _wasmGen.encodeNode)(node); // The size of the section doesn't include the storage of the size itself |
| // we need to manually add it here |
| |
| var start = (0, _ast.getEndOfSection)(sectionMetadata); |
| var end = start; |
| /** |
| * Update section |
| */ |
| |
| var deltaBytes = newByteArray.length; |
| uint8Buffer = (0, _helperBuffer.overrideBytesInBuffer)(uint8Buffer, start, end, newByteArray); |
| node.loc = { |
| start: { |
| line: -1, |
| column: start |
| }, |
| end: { |
| line: -1, |
| column: start + deltaBytes |
| } |
| }; // for func add the additional metadata in the AST |
| |
| if (node.type === "Func") { |
| // the size is the first byte |
| // FIXME(sven): handle LEB128 correctly here |
| var bodySize = newByteArray[0]; |
| node.metadata = { |
| bodySize: bodySize |
| }; |
| } |
| |
| if (node.type !== "IndexInFuncSection") { |
| (0, _ast.orderedInsertNode)(ast.body[0], node); |
| } |
| |
| return { |
| uint8Buffer: uint8Buffer, |
| deltaBytes: deltaBytes, |
| deltaElements: deltaElements |
| }; |
| } |
| |
| function applyOperations(ast, uint8Buffer, ops) { |
| ops.forEach(function (op) { |
| var state; |
| var sectionName; |
| |
| switch (op.kind) { |
| case "update": |
| state = applyUpdate(ast, uint8Buffer, [op.oldNode, op.node]); |
| sectionName = (0, _helperWasmBytecode.getSectionForNode)(op.node); |
| break; |
| |
| case "delete": |
| state = applyDelete(ast, uint8Buffer, op.node); |
| sectionName = (0, _helperWasmBytecode.getSectionForNode)(op.node); |
| break; |
| |
| case "add": |
| state = applyAdd(ast, uint8Buffer, op.node); |
| sectionName = (0, _helperWasmBytecode.getSectionForNode)(op.node); |
| break; |
| |
| default: |
| throw new Error("Unknown operation"); |
| } |
| /** |
| * Resize section vec size. |
| * If the length of the LEB-encoded size changes, this can change |
| * the byte length of the section and the offset for nodes in the |
| * section. So we do this first before resizing section byte size |
| * or shifting following operations' nodes. |
| */ |
| |
| |
| if (state.deltaElements !== 0 && sectionName !== "start") { |
| var oldBufferLength = state.uint8Buffer.length; |
| state.uint8Buffer = (0, _helperWasmSection.resizeSectionVecSize)(ast, state.uint8Buffer, sectionName, state.deltaElements); // Infer bytes added/removed by comparing buffer lengths |
| |
| state.deltaBytes += state.uint8Buffer.length - oldBufferLength; |
| } |
| /** |
| * Resize section byte size. |
| * If the length of the LEB-encoded size changes, this can change |
| * the offset for nodes in the section. So we do this before |
| * shifting following operations' nodes. |
| */ |
| |
| |
| if (state.deltaBytes !== 0 && sectionName !== "start") { |
| var _oldBufferLength = state.uint8Buffer.length; |
| state.uint8Buffer = (0, _helperWasmSection.resizeSectionByteSize)(ast, state.uint8Buffer, sectionName, state.deltaBytes); // Infer bytes added/removed by comparing buffer lengths |
| |
| state.deltaBytes += state.uint8Buffer.length - _oldBufferLength; |
| } |
| /** |
| * Shift following operation's nodes |
| */ |
| |
| |
| if (state.deltaBytes !== 0) { |
| ops.forEach(function (op) { |
| // We don't need to handle add ops, they are positioning independent |
| switch (op.kind) { |
| case "update": |
| shiftLocNodeByDelta(op.oldNode, state.deltaBytes); |
| break; |
| |
| case "delete": |
| shiftLocNodeByDelta(op.node, state.deltaBytes); |
| break; |
| } |
| }); |
| } |
| |
| uint8Buffer = state.uint8Buffer; |
| }); |
| return uint8Buffer; |
| } |