You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
96 lines
3.1 KiB
96 lines
3.1 KiB
'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;
|
|
|