hardhat-scilla-plugin
Version:
Hardhat TypeScript plugin for scilla testing
311 lines • 10.7 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseScilla = exports.parseScillaLibrary = exports.isNumeric = void 0;
exports.generateTypeConstructors = generateTypeConstructors;
const child_process_1 = require("child_process");
const fs_1 = __importDefault(require("fs"));
const plugins_1 = require("hardhat/plugins");
const path_1 = __importDefault(require("path"));
const readline_1 = __importDefault(require("readline"));
const ZilliqaUtils = __importStar(require("../ZilliqaUtils"));
// eslint-disable-next-line @typescript-eslint/no-var-requires
const parse = require("s-expression");
const isNumeric = (type) => {
if (typeof type === "string") {
switch (type) {
case "Int64":
case "Int128":
case "Int256":
case "Uint32":
case "Uint64":
case "Uint128":
case "Uint256":
return true;
default:
return false;
}
}
else {
return false;
}
};
exports.isNumeric = isNumeric;
const parseScillaLibrary = async (filename) => {
if (!fs_1.default.existsSync(filename)) {
throw new Error(`${filename} doesn't exist.`);
}
const fileStream = fs_1.default.createReadStream(filename);
const rl = readline_1.default.createInterface({
input: fileStream,
crlfDelay: Infinity,
});
let libraryName;
for await (const line of rl) {
if (line.trim().startsWith("library")) {
libraryName = line.trim().split(" ")[1];
break;
}
}
return {
name: libraryName || "",
transitions: [],
fields: [],
constructorParams: [],
ctors: [],
};
};
exports.parseScillaLibrary = parseScillaLibrary;
const parseScilla = (filename) => {
const resolvedFilename = path_1.default.resolve(filename);
if (!fs_1.default.existsSync(resolvedFilename)) {
throw new Error(`${resolvedFilename} doesn't exist.`);
}
let sexp;
if (ZilliqaUtils.useNativeScilla()) {
sexp = (0, child_process_1.execSync)(`scilla-fmt --sexp --human-readable ${filename}`);
}
else {
sexp = (0, child_process_1.execSync)(`docker run --rm -v ${resolvedFilename}:/tmp/input.scilla -i zilliqa/scilla:v0.13.3 /scilla/0/bin/scilla-fmt --sexp --human-readable /tmp/input.scilla`);
}
const result = parse(sexp.toString());
const libr = result.filter((row) => row[0] === "libs")[0][1];
const contr = result.filter((row) => row[0] === "contr")[0][1];
const ctors = extractTypes(libr);
const contractName = extractContractName(contr);
const contractParams = extractContractParams(contr);
const cfields = contr.filter((row) => row[0] === "cfields")[0][1];
const fields = extractContractFields(cfields);
const ccomps = contr.filter((row) => row[0] === "ccomps")[0][1];
const transitions = extractTransitions(ccomps);
return {
name: contractName,
transitions,
fields,
constructorParams: contractParams,
ctors,
};
};
exports.parseScilla = parseScilla;
const extractTypes = (lib) => {
const ctors = [];
if (lib.length > 0) {
const lentries = lib[0][1][1];
for (const lentry of lentries) {
switch (lentry[0]) {
case "LibVar":
break;
case "LibTyp":
for (const typector of lentry[2]) {
const typename = lentry[1][1][1];
const typectorname = typector[0][1][1][1];
const typectorargtypes = typector[1][1].map(parseField);
const userADT = {
typename,
ctorname: typectorname,
argtypes: typectorargtypes,
};
ctors.push(userADT);
}
break;
}
}
}
return ctors;
};
const extractContractName = (contrElem) => {
return contrElem
.filter((row) => row[0] === "cname")[0][1]
.filter((row) => row[0] === "SimpleLocal")[0][1];
};
const extractContractParams = (contrElem) => {
if (contrElem[1][0] !== "cparams") {
throw new Error(`Index 0 is not cparams: ${contrElem}`);
}
if (contrElem[1][1].length === 0) {
return null;
}
return extractContractFields(contrElem[1][1]);
};
const extractContractFields = (cfieldsElem) => {
return cfieldsElem.map((row) => {
const identData = row[0];
if (identData[0] !== "Ident") {
throw new Error(`Index 0 is not Ident: ${identData}`);
}
const fieldNameData = identData[1];
if (fieldNameData[0] !== "SimpleLocal") {
throw new Error(`Index 0 is not SimpleLocal: ${fieldNameData}`);
}
const fieldTypeData = row[1];
// Currently we just parse PrimType, for the rest we don't parse it completely.
if (fieldTypeData[0] === "PrimType") {
return {
type: fieldTypeData[1],
name: fieldNameData[1],
};
}
else if (fieldTypeData[0] === "ADT") {
const adt = parseAdt(fieldTypeData);
return {
typeJSON: adt,
type: adt.ctor + adt.argtypes.map((arg) => " " + arg.type).join(" "),
name: fieldNameData[1],
};
}
else if (fieldTypeData[0] === "MapType") {
return {
type: "Map",
name: fieldNameData[1],
};
}
else if (fieldTypeData[0] === "Address") {
return {
type: "ByStr20",
name: fieldNameData[1],
};
}
else {
throw new Error(`Data type is unknown: ${fieldTypeData}`);
}
});
};
const extractTransitions = (ccompsElem) => {
return ccompsElem.map((row) => {
const compTypeData = row[0];
if (compTypeData[0] !== "comp_type") {
throw new Error(`Index 0 is not comp_type ${compTypeData}`);
}
const compType = compTypeData[1];
const compNameData = row[1];
if (compNameData[0] !== "comp_name") {
throw new Error(`Index 0 is not comp_name ${compNameData}`);
}
const compName = compNameData[1][1];
if (compName[0] !== "SimpleLocal") {
throw new Error(`Index 0 is not SimpleLocal: ${compName}`);
}
const compParamsData = row[2];
if (compParamsData[0] !== "comp_params") {
throw new Error(`Index 0 is not comp_params: ${compParamsData}`);
}
const compParams = compParamsData[1].map((r) => {
const param = parseField(r[1]);
param.name = r[0][1][1];
return param;
});
return {
type: compType,
name: compName[1],
params: compParams,
};
});
};
function parseAdt(row) {
const ctor = row[1][1][1];
const argtypes = row[2].map(parseField);
return {
ctor,
argtypes,
};
}
function generateAdtType(field) {
if (field.argtypes.length === 0) {
return field.ctor;
}
const type = `${field.ctor} ${field.argtypes
.map((arg) => {
// Here we're sure that type is ADTField
const typeJson = arg.typeJSON;
if (["Pair", "List"].includes(typeJson.ctor))
return `(${arg.type})`;
else
return arg.type;
})
.reduce((prev, current) => `${prev} ${current}`)}`;
return type;
}
function parseField(row) {
const field_type = row[0];
if (field_type === "PrimType") {
const type = row[1];
return {
name: "",
typeJSON: type,
type,
};
}
else if (field_type === "ADT") {
const adt = parseAdt(row);
const name = row[0][1][1];
return {
typeJSON: adt,
type: generateAdtType(adt),
name,
};
}
else if (field_type === "Address") {
const type = "ByStr20";
return {
name: "",
typeJSON: type,
type,
};
}
else {
throw new plugins_1.HardhatPluginError("hardhat-scilla-plugin", `Encountered unexpected field type ${row}`);
}
}
function generateTypeConstructors(parsedCtors) {
const functions = {};
for (const parsedCtor of parsedCtors) {
// We need to copy parsedCtor as it is placed in the closure of the function we are declaring so we do
// not want it to be modified by the floor loop.
const ctorForClosure = Object.create(parsedCtor);
functions[ctorForClosure.ctorname] = (args) => {
// TODO: Add dynamic type checking.
return {
constructor: ctorForClosure.ctorname,
argtypes: ctorForClosure.argtypes,
args,
};
};
}
return functions;
}
//# sourceMappingURL=ScillaParser.js.map