Alias.js 3.3 KB

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