123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- import { anchorIsValid } from '../doc/anchors.js';
- import { visit } from '../visit.js';
- import { ALIAS, isAlias, isCollection, isPair } from './identity.js';
- import { NodeBase } from './Node.js';
- import { toJS } from './toJS.js';
- class Alias extends NodeBase {
- constructor(source) {
- super(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(doc, {
- Node: (_key, node) => {
- if (node === this)
- return 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);
- }
- let data = anchors.get(source);
- if (!data) {
- // Resolve anchors for Node.prototype.toJS()
- toJS(source, null, ctx);
- 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) {
- 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 (isAlias(node)) {
- const source = node.resolve(doc);
- const anchor = anchors && source && anchors.get(source);
- return anchor ? anchor.count * anchor.aliasCount : 0;
- }
- else if (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 (isPair(node)) {
- const kc = getAliasCount(doc, node.key, anchors);
- const vc = getAliasCount(doc, node.value, anchors);
- return Math.max(kc, vc);
- }
- return 1;
- }
- export { Alias };
|