Nav apraksta

Alias.js 3.1KB

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