@aeternity/aepp-calldata
Version:
Aeternity data serialization library
181 lines (180 loc) • 5.63 kB
JavaScript
"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;