'use strict'; var anchors = require('../doc/anchors.js'); var visit = require('../visit.js'); var Node = require('./Node.js'); class Alias extends Node.NodeBase { constructor(source) { super(Node.ALIAS); this.source = source; Object.defineProperty(this, 'tag', { set() { throw new Error('Alias nodes cannot have tags'); } }); } /** * Resolve the value of this alias within `doc`, finding the last * instance of the `source` anchor before this node. */ resolve(doc) { let found = undefined; visit.visit(doc, { Node: (_key, node) => { if (node === this) return visit.visit.BREAK; if (node.anchor === this.source) found = node; } }); return found; } toJSON(_arg, ctx) { if (!ctx) return { source: this.source }; const { anchors, doc, maxAliasCount } = ctx; const source = this.resolve(doc); if (!source) { const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`; throw new ReferenceError(msg); } const data = anchors.get(source); /* istanbul ignore if */ if (!data || data.res === undefined) { const msg = 'This should not happen: Alias anchor was not resolved?'; throw new ReferenceError(msg); } if (maxAliasCount >= 0) { data.count += 1; if (data.aliasCount === 0) data.aliasCount = getAliasCount(doc, source, anchors); if (data.count * data.aliasCount > maxAliasCount) { const msg = 'Excessive alias count indicates a resource exhaustion attack'; throw new ReferenceError(msg); } } return data.res; } toString(ctx, _onComment, _onChompKeep) { const src = `*${this.source}`; if (ctx) { anchors.anchorIsValid(this.source); if (ctx.options.verifyAliasOrder && !ctx.anchors.has(this.source)) { const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`; throw new Error(msg); } if (ctx.implicitKey) return `${src} `; } return src; } } function getAliasCount(doc, node, anchors) { if (Node.isAlias(node)) { const source = node.resolve(doc); const anchor = anchors && source && anchors.get(source); return anchor ? anchor.count * anchor.aliasCount : 0; } else if (Node.isCollection(node)) { let count = 0; for (const item of node.items) { const c = getAliasCount(doc, item, anchors); if (c > count) count = c; } return count; } else if (Node.isPair(node)) { const kc = getAliasCount(doc, node.key, anchors); const vc = getAliasCount(doc, node.value, anchors); return Math.max(kc, vc); } return 1; } exports.Alias = Alias;