@aeternity/aepp-sdk
Version:
SDK for the æternity blockchain
173 lines (172 loc) • 7.01 kB
JavaScript
function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); }
function _classPrivateFieldInitSpec(e, t, a) { _checkPrivateRedeclaration(e, t), t.set(e, a); }
function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); }
function _classPrivateFieldGet(s, a) { return s.get(_assertClassBrand(s, a)); }
function _classPrivateFieldSet(s, a, r) { return s.set(_assertClassBrand(s, a), r), r; }
function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); }
import { execFile } from 'child_process';
import { tmpdir } from 'os';
import { resolve, dirname, basename, relative, isAbsolute } from 'path';
import { mkdir, writeFile, rm } from 'fs/promises';
import { fileURLToPath } from 'url';
import CompilerBase from './Base.js';
import { CompilerError, IllegalArgumentError, InternalError, UnsupportedVersionError } from '../../utils/errors.js';
import semverSatisfies from '../../utils/semver-satisfies.js';
import { ensureError } from '../../utils/other.js';
export const getPackagePath = () => {
const path = dirname(fileURLToPath(import.meta.url));
if (basename(path) === 'dist') return resolve(path, '..');
if (basename(path) === 'compiler') return resolve(path, '../../..');
throw new InternalError("Can't get package path");
};
/**
* A wrapper around aesophia_cli, available only in Node.js.
* Requires Erlang installed, assumes that `escript` is available in PATH.
* @category contract
*/
var _path = /*#__PURE__*/new WeakMap();
var _ensureCompatibleVersion = /*#__PURE__*/new WeakMap();
var _CompilerCli_brand = /*#__PURE__*/new WeakSet();
export default class CompilerCli extends CompilerBase {
/**
* @param compilerPath - A path to aesophia_cli binary, by default uses the integrated one
* @param options - Options
* @param options.ignoreVersion - Print warning instead of throwing exception if compiler version
* is not supported, use with caution
*/
constructor(compilerPath = resolve(getPackagePath(), './bin/aesophia_cli'), {
ignoreVersion = false
} = {}) {
super();
_classPrivateMethodInitSpec(this, _CompilerCli_brand);
_classPrivateFieldInitSpec(this, _path, void 0);
_classPrivateFieldInitSpec(this, _ensureCompatibleVersion, void 0);
_classPrivateFieldSet(_path, this, compilerPath);
_classPrivateFieldSet(_ensureCompatibleVersion, this, this.version().then(version => {
const versions = [version, '8.0.0', '9.0.0'];
if (!semverSatisfies(...versions)) {
const error = new UnsupportedVersionError('compiler', ...versions);
if (ignoreVersion) console.warn(error.message);else throw error;
}
}));
}
async compile(path) {
await _classPrivateFieldGet(_ensureCompatibleVersion, this);
try {
const [compileRes, aci] = await Promise.all([_assertClassBrand(_CompilerCli_brand, this, _runWithStderr).call(this, path), this.generateAci(path)]);
return {
bytecode: compileRes.stdout.trimEnd(),
aci,
warnings: compileRes.stderr.split('Warning in ').slice(1).map(warning => {
const reg = /^'(.+)' at line (\d+), col (\d+):\n(.+)$/s;
const match = warning.match(reg);
if (match == null) throw new InternalError(`Can't parse compiler output: "${warning}"`);
return {
message: match[4].trimEnd(),
pos: {
...(match[1] !== path && {
file: match[1]
}),
line: +match[2],
col: +match[3]
}
};
})
};
} catch (error) {
ensureError(error);
throw new CompilerError(error.message);
}
}
async compileBySourceCode(sourceCode, fileSystem) {
const tmp = await _saveContractToTmpDir.call(CompilerCli, sourceCode, fileSystem);
try {
return await this.compile(tmp);
} finally {
await rm(dirname(tmp), {
recursive: true
});
}
}
async generateAci(path) {
await _classPrivateFieldGet(_ensureCompatibleVersion, this);
try {
return JSON.parse(await _assertClassBrand(_CompilerCli_brand, this, _run).call(this, '--no_code', '--create_json_aci', path));
} catch (error) {
ensureError(error);
throw new CompilerError(error.message);
}
}
async generateAciBySourceCode(sourceCode, fileSystem) {
const tmp = await _saveContractToTmpDir.call(CompilerCli, sourceCode, fileSystem);
try {
return await this.generateAci(tmp);
} finally {
await rm(dirname(tmp), {
recursive: true
});
}
}
async validate(bytecode, path) {
await _classPrivateFieldGet(_ensureCompatibleVersion, this);
try {
return (await _assertClassBrand(_CompilerCli_brand, this, _run).call(this, path, '--validate', bytecode)).includes('Validation successful.');
} catch (error) {
return false;
}
}
async validateBySourceCode(bytecode, sourceCode, fileSystem) {
const tmp = await _saveContractToTmpDir.call(CompilerCli, sourceCode, fileSystem);
try {
return await this.validate(bytecode, tmp);
} finally {
await rm(dirname(tmp), {
recursive: true
});
}
}
async version() {
const verMessage = await _assertClassBrand(_CompilerCli_brand, this, _run).call(this, '--version');
const ver = verMessage.match(/Sophia compiler version ([\d.]+.*)\n/)?.[1];
if (ver == null) throw new CompilerError("Can't get compiler version");
return ver;
}
}
async function _runWithStderr(...parameters) {
return new Promise((pResolve, pReject) => {
execFile('escript', [_classPrivateFieldGet(_path, this), ...parameters], (error, stdout, stderr) => {
if (error != null) pReject(error);else pResolve({
stdout,
stderr
});
});
});
}
async function _run(...parameters) {
const {
stderr,
stdout
} = await _assertClassBrand(_CompilerCli_brand, this, _runWithStderr).call(this, ...parameters);
if (stderr !== '') throw new CompilerError(stderr);
return stdout;
}
async function _saveContractToTmpDir(sourceCode, fileSystem = {}) {
const randomName = () => Math.random().toString(36).slice(2);
const path = resolve(tmpdir(), `aepp-sdk-js-${randomName()}`);
await mkdir(path);
const sourceCodePath = resolve(path, `${randomName()}.aes`);
await writeFile(sourceCodePath, sourceCode);
await Promise.all(Object.entries(fileSystem).map(async ([name, src]) => {
const p = resolve(path, name);
const rel = relative(path, p);
if (isAbsolute(rel) || rel.startsWith('..')) {
throw new IllegalArgumentError(`Path "${name}" is outside of the temp directory`);
}
await mkdir(dirname(p), {
recursive: true
});
return writeFile(p, src);
}));
return sourceCodePath;
}
//# sourceMappingURL=Cli.js.map