[d24f17c] | 1 | 'use strict';
|
---|
| 2 |
|
---|
| 3 | var anchors = require('../doc/anchors.js');
|
---|
| 4 | var visit = require('../visit.js');
|
---|
| 5 | var identity = require('./identity.js');
|
---|
| 6 | var Node = require('./Node.js');
|
---|
| 7 | var toJS = require('./toJS.js');
|
---|
| 8 |
|
---|
| 9 | class Alias extends Node.NodeBase {
|
---|
| 10 | constructor(source) {
|
---|
| 11 | super(identity.ALIAS);
|
---|
| 12 | this.source = source;
|
---|
| 13 | Object.defineProperty(this, 'tag', {
|
---|
| 14 | set() {
|
---|
| 15 | throw new Error('Alias nodes cannot have tags');
|
---|
| 16 | }
|
---|
| 17 | });
|
---|
| 18 | }
|
---|
| 19 | /**
|
---|
| 20 | * Resolve the value of this alias within `doc`, finding the last
|
---|
| 21 | * instance of the `source` anchor before this node.
|
---|
| 22 | */
|
---|
| 23 | resolve(doc) {
|
---|
| 24 | let found = undefined;
|
---|
| 25 | visit.visit(doc, {
|
---|
| 26 | Node: (_key, node) => {
|
---|
| 27 | if (node === this)
|
---|
| 28 | return visit.visit.BREAK;
|
---|
| 29 | if (node.anchor === this.source)
|
---|
| 30 | found = node;
|
---|
| 31 | }
|
---|
| 32 | });
|
---|
| 33 | return found;
|
---|
| 34 | }
|
---|
| 35 | toJSON(_arg, ctx) {
|
---|
| 36 | if (!ctx)
|
---|
| 37 | return { source: this.source };
|
---|
| 38 | const { anchors, doc, maxAliasCount } = ctx;
|
---|
| 39 | const source = this.resolve(doc);
|
---|
| 40 | if (!source) {
|
---|
| 41 | const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`;
|
---|
| 42 | throw new ReferenceError(msg);
|
---|
| 43 | }
|
---|
| 44 | let data = anchors.get(source);
|
---|
| 45 | if (!data) {
|
---|
| 46 | // Resolve anchors for Node.prototype.toJS()
|
---|
| 47 | toJS.toJS(source, null, ctx);
|
---|
| 48 | data = anchors.get(source);
|
---|
| 49 | }
|
---|
| 50 | /* istanbul ignore if */
|
---|
| 51 | if (!data || data.res === undefined) {
|
---|
| 52 | const msg = 'This should not happen: Alias anchor was not resolved?';
|
---|
| 53 | throw new ReferenceError(msg);
|
---|
| 54 | }
|
---|
| 55 | if (maxAliasCount >= 0) {
|
---|
| 56 | data.count += 1;
|
---|
| 57 | if (data.aliasCount === 0)
|
---|
| 58 | data.aliasCount = getAliasCount(doc, source, anchors);
|
---|
| 59 | if (data.count * data.aliasCount > maxAliasCount) {
|
---|
| 60 | const msg = 'Excessive alias count indicates a resource exhaustion attack';
|
---|
| 61 | throw new ReferenceError(msg);
|
---|
| 62 | }
|
---|
| 63 | }
|
---|
| 64 | return data.res;
|
---|
| 65 | }
|
---|
| 66 | toString(ctx, _onComment, _onChompKeep) {
|
---|
| 67 | const src = `*${this.source}`;
|
---|
| 68 | if (ctx) {
|
---|
| 69 | anchors.anchorIsValid(this.source);
|
---|
| 70 | if (ctx.options.verifyAliasOrder && !ctx.anchors.has(this.source)) {
|
---|
| 71 | const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`;
|
---|
| 72 | throw new Error(msg);
|
---|
| 73 | }
|
---|
| 74 | if (ctx.implicitKey)
|
---|
| 75 | return `${src} `;
|
---|
| 76 | }
|
---|
| 77 | return src;
|
---|
| 78 | }
|
---|
| 79 | }
|
---|
| 80 | function getAliasCount(doc, node, anchors) {
|
---|
| 81 | if (identity.isAlias(node)) {
|
---|
| 82 | const source = node.resolve(doc);
|
---|
| 83 | const anchor = anchors && source && anchors.get(source);
|
---|
| 84 | return anchor ? anchor.count * anchor.aliasCount : 0;
|
---|
| 85 | }
|
---|
| 86 | else if (identity.isCollection(node)) {
|
---|
| 87 | let count = 0;
|
---|
| 88 | for (const item of node.items) {
|
---|
| 89 | const c = getAliasCount(doc, item, anchors);
|
---|
| 90 | if (c > count)
|
---|
| 91 | count = c;
|
---|
| 92 | }
|
---|
| 93 | return count;
|
---|
| 94 | }
|
---|
| 95 | else if (identity.isPair(node)) {
|
---|
| 96 | const kc = getAliasCount(doc, node.key, anchors);
|
---|
| 97 | const vc = getAliasCount(doc, node.value, anchors);
|
---|
| 98 | return Math.max(kc, vc);
|
---|
| 99 | }
|
---|
| 100 | return 1;
|
---|
| 101 | }
|
---|
| 102 |
|
---|
| 103 | exports.Alias = Alias;
|
---|