UNPKG

@aeternity/aepp-calldata

Version:
181 lines (180 loc) 5.63 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _rlp = _interopRequireDefault(require("rlp")); var _BaseSerializer = _interopRequireDefault(require("./BaseSerializer.cjs")); var _TypeSerializer = _interopRequireDefault(require("./TypeSerializer.cjs")); var _int2ByteArray = require("../utils/int2ByteArray.cjs"); var _FateOpcodes = _interopRequireDefault(require("../FateOpcodes.cjs")); var _FateTypes = require("../FateTypes.cjs"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } const MODIFIERS = { 0b11: 'immediate', 0b10: 'var', 0b01: 'arg', 0b00: 'stack' }; const splitArgs = (data, n = 1) => { const args = []; let bits = Number((0, _int2ByteArray.byteArray2Int)(data)); for (let i = 0; i < n * 2; i += 2) { args.push(bits & 0b11); bits >>= 2; } if (bits !== 0) { throw new Error(`Invalid argument modifier data. Unexpected padding: 0b${bits.toString(2)}`); } return args; }; class BytecodeSerializer extends _BaseSerializer.default { constructor(globalSerializer) { super(globalSerializer); this._typeSerializer = new _TypeSerializer.default(); } deserialize(data) { const codeBin = _rlp.default.decode(data, true); const symbolsBin = _rlp.default.decode(codeBin.remainder, true); const annotationsBin = _rlp.default.decode(symbolsBin.remainder); const symbols = this.deserializeSymbols(symbolsBin.data); const functions = this.deserializeFunctions(codeBin.data, symbols); const annotations = this.deserializeAnnotations(annotationsBin); return { functions, symbols, annotations }; } deserializeFunctions(data, symbols) { let fun = {}; let rest = data; const functions = []; while (rest.length) { [fun, rest] = this.deserializeFunction(rest, symbols); functions.push(fun); } return functions; } deserializeFunction(data, symbols) { const prefix = data[0]; const id = (0, _int2ByteArray.byteArray2Hex)(data.slice(1, 5)); if (prefix !== 0xfe) { throw new Error(`Wrong function prefix, expected 0xfe got 0x${data[0].toString(16)}`); } const name = symbols[id]; const [attributes, rest2] = this.deserializeAttributes(data.slice(5)); const [args, returnType, rest3] = this.deserializeSignature(rest2); const [instructions, rest4] = this.deserializeInstructions(rest3); return [{ id, name, attributes, args, returnType, instructions }, rest4]; } deserializeInstructions(data) { let instruction = {}; let rest = data; const instructions = []; let block = 0; let end = false; while (rest.length && rest[0] !== 0xfe) { if (instructions[block] === undefined) { instructions[block] = []; } [instruction, rest, end] = this.deserializeInstruction(rest); instructions[block].push(instruction); if (end) { block++; } } return [instructions, rest]; } deserializeInstruction(data) { const opcode = data[0]; const rest = data.slice(1); if (!_FateOpcodes.default.hasOwnProperty(opcode)) { throw new Error(`Unsupported opcode: 0x${opcode.toString(16)}`); } const instr = _FateOpcodes.default[opcode]; const { mnemonic, end } = instr; if (instr.args === 0) { return [{ mnemonic, args: [] }, rest, end]; } const [args, rest2] = this.deserializeArguments(rest, instr.args); return [{ mnemonic, args }, rest2, end]; } deserializeArguments(data, n) { const numBytes = n <= 4 ? 1 : 2; const modBytes = data.subarray(0, numBytes); const rest = data.slice(numBytes); const modifiers = splitArgs(modBytes, n); const args = []; let rest2 = rest; let arg; modifiers.forEach(mod => { [arg, rest2] = this.deserializeArgument(mod, rest2); args.push(arg); }); return [args, rest2]; } deserializeArgument(bits, stream) { const mod = MODIFIERS[bits]; if (mod === 'stack') { return [{ mod, arg: 0 }, stream]; } const [arg, rest] = this.globalSerializer.deserializeStream(stream); return [{ mod, arg: arg.valueOf() }, rest]; } deserializeSignature(data) { const [args, rest] = this._typeSerializer.deserializeStream(data); const [returnType, rest2] = this._typeSerializer.deserializeStream(rest); return [args, returnType, rest2]; } deserializeAttributes(data) { const [attributesInt, rest] = this.globalSerializer.deserializeStream(data); const attributesByte = Number(attributesInt.valueOf()); const attributes = []; if (attributesByte & 0b0001) { attributes.push('private'); } if (attributesByte & 0b0010) { attributes.push('payable'); } return [attributes, rest]; } deserializeSymbols(data) { // Needs typehint with ByteArray otherwise deserializes to string const type = (0, _FateTypes.FateTypeMap)((0, _FateTypes.FateTypeByteArray)(), (0, _FateTypes.FateTypeString)()); const symbols = this.globalSerializer.deserializeWithType(data, type).valueOf(); const symbolsMap = {}; symbols.forEach((val, key) => { const hex = (0, _int2ByteArray.byteArray2Hex)(key); symbolsMap[hex] = val; }); return symbolsMap; } deserializeAnnotations(data) { const annotations = this.globalSerializer.deserialize(data).valueOf(); return annotations; } } var _default = exports.default = BytecodeSerializer;