Spaces:
Running
Running
; | |
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; | |
} |