UNPKG

@aeternity/aepp-sdk

Version:

SDK for the æternity blockchain

173 lines (172 loc) 7.01 kB
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