UNPKG

@harmoniclabs/plu-ts-onchain

Version:

An embedded DSL for Cardano smart contracts creation coupled with a library for Cardano transactions, all in Typescript

519 lines (515 loc) 20.6 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getMinVarDbn = exports.getNormalizedLettedArgs = exports.getLettedTerms = exports.default_getLettedTermsOptions = exports.getSortedLettedSet = exports.IRLetted = exports.expandedJsonLettedSetEntry = exports.jsonLettedSetEntry = void 0; var iterTree_1 = require("../toUPLC/_internal/iterTree.js"); var IRVar_1 = require("./IRVar.js"); var positiveIntAsBytes_1 = require("../utils/positiveIntAsBytes.js"); var obj_utils_1 = require("@harmoniclabs/obj-utils"); var BasePlutsError_1 = require("../../utils/BasePlutsError.js"); var concatUint8Arr_1 = require("../utils/concatUint8Arr.js"); var isIRTerm_1 = require("../utils/isIRTerm.js"); var IRApp_1 = require("./IRApp.js"); var IRDelayed_1 = require("./IRDelayed.js"); var IRForced_1 = require("./IRForced.js"); var IRFunc_1 = require("./IRFunc.js"); var IRHoisted_1 = require("./IRHoisted.js"); var utils_1 = require("../utils/index.js"); var isIRParentTerm_1 = require("../utils/isIRParentTerm.js"); var _modifyChildFromTo_1 = require("../toUPLC/_internal/_modifyChildFromTo.js"); var groupByScope_1 = require("../toUPLC/subRoutines/handleLetted/groupByScope.js"); var IRConstr_1 = require("./IRConstr.js"); var IRCase_1 = require("./IRCase.js"); var IRHash_1 = require("../IRHash.js"); var equalIRTerm_1 = require("../utils/equalIRTerm.js"); var IRNodeKind_1 = require("../IRNodeKind.js"); var IRRecursive_1 = require("./IRRecursive.js"); var IRSelfCall_1 = require("./IRSelfCall.js"); function jsonLettedSetEntry(entry) { return { letted: (0, IRHash_1.irHashToHex)(entry.letted.hash), nReferences: entry.nReferences }; } exports.jsonLettedSetEntry = jsonLettedSetEntry; function expandedJsonLettedSetEntry(entry) { return { letted: (0, IRHash_1.irHashToHex)(entry.letted.hash), letted_value: (0, utils_1.prettyIR)(entry.letted.value).text.split("\n"), nReferences: entry.nReferences }; } exports.expandedJsonLettedSetEntry = expandedJsonLettedSetEntry; var defaultLettedMeta = (0, obj_utils_1.freezeAll)({ forceHoist: false, __src__: undefined, isClosed: false }); var n_hash_access = 0; var IRLetted = /** @class */ (function () { function IRLetted(DeBruijn, toLet, metadata, _unsafeHash) { if (metadata === void 0) { metadata = {}; } if (_unsafeHash === void 0) { _unsafeHash = undefined; } DeBruijn = typeof DeBruijn === "bigint" ? Number(DeBruijn) : DeBruijn; if (!(Number.isSafeInteger(DeBruijn) && DeBruijn >= 0)) throw new BasePlutsError_1.BasePlutsError("invalid index for an `IRLetted` instance"); while (toLet instanceof IRLetted || toLet instanceof IRHoisted_1.IRHoisted) { toLet = toLet instanceof IRLetted ? toLet.value : toLet.hoisted; } if (!(0, isIRTerm_1.isIRTerm)(toLet)) throw new BasePlutsError_1.BasePlutsError("letted value was not an IRTerm"); this._dbn = DeBruijn; this._value = toLet; this._value.parent = this; // we need the has before setting dependecies this._hash = (0, IRHash_1.isIRHash)(_unsafeHash) ? _unsafeHash : undefined; this._deps = undefined; this._parent = undefined; this.meta = __assign(__assign({}, defaultLettedMeta), metadata); } Object.defineProperty(IRLetted.prototype, "hash", { get: function () { if (!(0, IRHash_1.isIRHash)(this._hash)) { /* NOTE TO SELF: NEVER NEVER NEVER NEVER GET THE HASH BASED ON THE METADATA NEVER */ var normalized = getNormalizedLettedArgs(this.dbn, this._value); if (normalized === undefined) { // `_value` doesn't includes any `IRVar` // aka. there is nothing to normalize // just use the value regardless of the // `IRLetted` dbn instantiation this._hash = (0, IRHash_1.hashIrData)((0, concatUint8Arr_1.concatUint8Arr)(IRLetted.tag, this._value.hash)); } else { this._hash = (0, IRHash_1.hashIrData)((0, concatUint8Arr_1.concatUint8Arr)(IRLetted.tag, (0, positiveIntAsBytes_1.positiveIntAsBytes)(normalized[0]), normalized[1].hash)); /* const [ normalized_dbn, normalized_value] = normalized; if( toHex( hash ) === "ee84fb036ed2726e01b0415109246927" ) { const original_value = _value.clone(); const minDbn = getMinVarDbn( original_value ); const minUnb = _getMinUnboundDbn( original_value ); console.log( "_ee84fb036ed2726e01b0415109246927_", "\noriginal value:", prettyIRJsonStr( original_value, 2, { hoisted: false } ), "\nmin dbn:", minDbn, "\nmin unbound:", minUnb, "\nnormalized dbn:", normalized_dbn, ); } //*/ } } return this._hash; }, enumerable: false, configurable: true }); IRLetted.prototype.markHashAsInvalid = function () { var _a; this._hash = undefined; this._deps = undefined; (_a = this.parent) === null || _a === void 0 ? void 0 : _a.markHashAsInvalid(); }; IRLetted.prototype.isHashPresent = function () { return (0, IRHash_1.isIRHash)(this._hash); }; Object.defineProperty(IRLetted.prototype, "value", { get: function () { return this._value; }, set: function (newVal) { if (!(0, isIRTerm_1.isIRTerm)(newVal)) throw new BasePlutsError_1.BasePlutsError("letted term was not IRTerm"); if (!(0, equalIRTerm_1.shallowEqualIRTermHash)(this._value, newVal)) this.markHashAsInvalid(); // remove deps even if the value is the same // newValue might be a clone of the current value // and so have different (new) objects this._deps = undefined; // keep the parent reference in the old child, useful for compilation // _value.parent = undefined; this._value = newVal; this._value.parent = this; }, enumerable: false, configurable: true }); Object.defineProperty(IRLetted.prototype, "dbn", { /** * we need to keep track of the debruijn at which the `IRLetted` is instantiated * * this is because stuff like `new IRLetted( new IRVar(0) )` has different meaning * at different DeBruijn levels * * knowing the DeBruijn we can differentiate them */ get: function () { return this._dbn; }, set: function (newDbn) { if (!(Number.isSafeInteger(newDbn) && newDbn >= 0)) throw new BasePlutsError_1.BasePlutsError("invalid index for an `IRLetted` instance"); if (newDbn === this._dbn) { // everything ok // avoid calling `markHashAsInvalid` return; } this.markHashAsInvalid(); this._dbn = newDbn; }, enumerable: false, configurable: true }); IRLetted.prototype.isClosedAtDbn = function (dbn) { if (!Number.isSafeInteger(dbn)) throw new Error("unexpected unsafe dbn integer"); var minUnbound = (0, groupByScope_1._getMinUnboundDbn)(this._value); if (minUnbound === undefined) return true; return minUnbound < dbn; }; Object.defineProperty(IRLetted.prototype, "dependencies", { get: function () { if (this._deps === undefined) this._deps = getSortedLettedSet(getLettedTerms(this._value)); return this._deps; }, enumerable: false, configurable: true }); Object.defineProperty(IRLetted.prototype, "parent", { get: function () { return this._parent; }, set: function (newParent) { if (!( // assert // new parent value is different than current this._parent !== newParent && ( // and the new parent value is valid newParent === undefined || (0, isIRParentTerm_1.isIRParentTerm)(newParent)))) return; this._parent = newParent; }, enumerable: false, configurable: true }); Object.defineProperty(IRLetted, "kind", { get: function () { return IRNodeKind_1.IRNodeKind.Letted; }, enumerable: false, configurable: true }); Object.defineProperty(IRLetted.prototype, "kind", { get: function () { return IRLetted.kind; }, enumerable: false, configurable: true }); Object.defineProperty(IRLetted, "tag", { get: function () { return new Uint8Array([IRLetted.kind]); }, enumerable: false, configurable: true }); IRLetted.prototype.clone = function () { return new IRLetted(this.dbn, this.value.clone(), __assign({}, this.meta), this.isHashPresent() ? this.hash : undefined); }; IRLetted.prototype.toJSON = function () { return this.toJson(); }; IRLetted.prototype.toJson = function () { return { type: "IRLetted", hash: (0, IRHash_1.irHashToHex)(this.hash), value: this.value.toJson() }; }; return IRLetted; }()); exports.IRLetted = IRLetted; /** * basically an insertion sort; * * @param {LettedSetEntry[]} lettedTerms * @returns {LettedSetEntry[]} a **new** array with ```IRLetted```s with no dependencies first, followed by the dependents */ function getSortedLettedSet(lettedTerms) { var set = []; var hashesSet = []; /** * **O((n * m) * d)** * * where * * n = length of set * m = number of terms passed * d = number of unique dependencies among the passed terms */ function addToSet(_terms) { var _loop_1 = function (i) { var thisLettedEntry = _terms[i]; var thisHash = thisLettedEntry.letted.hash; var idxInSet = hashesSet.findIndex(function (hash) { return (0, IRHash_1.equalIrHash)(hash, thisHash); }); // if( !hashesSet.includes( hash ) ) // "includes" uses standard equality (===) if (idxInSet < 0) // not present { // add dependencies first // dependencies don't have references to the current letted // (of course, wouldn't be much of a dependecy otherwhise) // // don't add dependecies of dependecies since `dependecies` proerty // already calls `getSortedLettedSet( getLettedTerms( _value ) )` // so repeating it here would count deps twice (exponentially for deps of deps) addToSet(getLettedTerms(thisLettedEntry.letted.value)); hashesSet.push(thisHash); set.push({ letted: thisLettedEntry.letted, nReferences: thisLettedEntry.nReferences }); } else { var entry = set[idxInSet]; entry.nReferences += thisLettedEntry.nReferences; entry.letted.meta.forceHoist = entry.letted.meta.forceHoist || thisLettedEntry.letted.meta.forceHoist; } }; for (var i = 0; i < _terms.length; i++) { _loop_1(i); } } addToSet(lettedTerms); return set; } exports.getSortedLettedSet = getSortedLettedSet; exports.default_getLettedTermsOptions = { all: false, includeHoisted: false }; /** * * @param {IRTerm} irTerm term to search in * @returns direct letted terms (no possible dependencies) */ function getLettedTerms(irTerm, options) { var _a = __assign(__assign({}, exports.default_getLettedTermsOptions), options), all = _a.all, includeHoisted = _a.includeHoisted; var lettedTerms = []; var stack = [irTerm]; while (stack.length > 0) { var t = stack.pop(); if (t instanceof IRLetted) { lettedTerms.push({ letted: t, nReferences: 1 }); if (all) { stack.push(t.value); } continue; } if (t instanceof IRApp_1.IRApp) { stack.push(t.fn, t.arg); continue; } if (t instanceof IRConstr_1.IRConstr) { stack.push.apply(stack, __spreadArray([], __read(Array.from(t.fields)), false)); continue; } if (t instanceof IRCase_1.IRCase) { stack.push.apply(stack, __spreadArray([t.constrTerm], __read(Array.from(t.continuations)), false)); continue; } if (t instanceof IRFunc_1.IRFunc) { stack.push(t.body); continue; } if (t instanceof IRRecursive_1.IRRecursive) { stack.push(t.body); continue; } if (t instanceof IRForced_1.IRForced) { stack.push(t.forced); continue; } if (t instanceof IRDelayed_1.IRDelayed) { stack.push(t.delayed); continue; } if (includeHoisted) { if (t instanceof IRHoisted_1.IRHoisted) { stack.push(t.hoisted); continue; } } } return lettedTerms; } exports.getLettedTerms = getLettedTerms; function getNormalizedLettedArgs(lettedDbn, value) { var _a; var normalized_value = value.clone(); var minDbn = getMinVarDbn(normalized_value); if (minDbn === undefined) return undefined; if (minDbn !== 0) (0, iterTree_1.iterTree)(normalized_value, function (node, relativeDbn) { if (node instanceof IRVar_1.IRVar) { node.dbn -= minDbn; } else if (node instanceof IRSelfCall_1.IRSelfCall) { node.dbn -= minDbn; } //* else if (node instanceof IRLetted) { var max = getMaxVarDbn(node.value); if ( // no vars typeof max !== "number" || // defined outside of letted max >= relativeDbn) { node.dbn -= minDbn; } else // if depends on vars in this letted { // TODO: fix double checking already inlined values // // this is currently a workaround // // the real problem is that sometimes (when?) // `iterTree` goes back checking some value that was already modified // in the case of `IRApp` as parent, throwing an error // because we don't know which value we should modify if (node.parent instanceof IRApp_1.IRApp) { var parent_1 = node.parent; var currentChild = node; if ( // parent is actually pointing to child currentChild === parent_1.arg || currentChild === parent_1.fn || (0, IRHash_1.equalIrHash)(parent_1.arg.hash, currentChild.hash) || (0, IRHash_1.equalIrHash)(parent_1.fn.hash, currentChild.hash)) { // inline (0, _modifyChildFromTo_1._modifyChildFromTo)(node.parent, node, node.value); return true; // modified parent } else return false; // modified parent } else { if (node.parent) // only modify if not root { // inline (0, _modifyChildFromTo_1._modifyChildFromTo)(node.parent, node, node.value); return true; // modified parent } else return false; } } } //*/ }, // shouldSkipNode ? // hoisted terms are not really here // will be substituted to variables when the time comes // however now are here and we need to skip them // // !!! WARNING !! removing this causes strange uplc compilation BUGS function (node, dbn) { return node instanceof IRHoisted_1.IRHoisted; }); // !!! IMPORTANT !!! _getMinUnboundDbn( value ) MUST be called on the original value; NOT the **modified** `normalized_value` return [lettedDbn - ((_a = (0, groupByScope_1._getMinUnboundDbn)(value)) !== null && _a !== void 0 ? _a : minDbn), normalized_value]; } exports.getNormalizedLettedArgs = getNormalizedLettedArgs; /** * * @param term the ir term to iter to search for vars * @returns {number | undefined} * * @example * ```ts * let minDbn = getMinVarDbn( new IRVar(0) ); // 0 * minDbn = getMinVarDbn( IRConst.unit ); // undefined * minDbn = getMinVarDbn( new IRFunc( 1, new IRVar( 0 ) ) ); // 0 * ``` */ function getMinVarDbn(term) { var min = undefined; var foundAny = false; (0, iterTree_1.iterTree)(term, function (node) { if (node instanceof IRVar_1.IRVar || node instanceof IRSelfCall_1.IRSelfCall) { if (foundAny) { min = Math.min(min, node.dbn); } else { foundAny = true; min = node.dbn; } } }, // shouldSkipNode ? // hoisted terms are not really here // will be substituted to variables when the time comes // however now are here and we need to skip them function (node) { return node instanceof IRHoisted_1.IRHoisted; } // skip if hoisted ); return min; } exports.getMinVarDbn = getMinVarDbn; /** * * @param term the ir term to iter to search for vars * @returns {number | undefined} */ function getMaxVarDbn(term) { var max = undefined; var foundAny = false; (0, iterTree_1.iterTree)(term, function (node) { if (node instanceof IRVar_1.IRVar || node instanceof IRSelfCall_1.IRSelfCall) { if (foundAny) { max = Math.max(max, node.dbn); } else { foundAny = true; max = node.dbn; } } }, // shouldSkipNode ? // hoisted terms are not really here // will be substituted to variables when the time comes // however now are here and we need to skip them function (node) { return node instanceof IRHoisted_1.IRHoisted; } // skip if hoisted ); return max; }