1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | const assert = require("assert");
|
7 | const utils = require("./utils.js");
|
8 | const brandKey = "reify-fast-path";
|
9 | const brand = typeof Symbol === "function"
|
10 | ? Symbol.for(brandKey)
|
11 | : brandKey;
|
12 |
|
13 | class FastPath {
|
14 | constructor(ast) {
|
15 | assert.notStrictEqual(ast, null);
|
16 | assert.strictEqual(typeof ast, "object");
|
17 | this.stack = [ast];
|
18 | }
|
19 |
|
20 | static isInstance(value) {
|
21 | return value !== null &&
|
22 | typeof value === "object" &&
|
23 | value[brand] === true;
|
24 | }
|
25 |
|
26 | static from(value) {
|
27 | return this.isInstance(value) ? value : new this(value);
|
28 | }
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 | call(callback) {
|
36 | const s = this.stack;
|
37 | const origLen = s.length;
|
38 | const argCount = arguments.length;
|
39 | let value = s[origLen - 1];
|
40 |
|
41 | for (let i = 1; i < argCount; ++i) {
|
42 | const name = arguments[i];
|
43 | value = value[name];
|
44 | s.push(name, value);
|
45 | }
|
46 | const result = callback(this);
|
47 | s.length = origLen;
|
48 |
|
49 | return result;
|
50 | }
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 | each(callback) {
|
57 | const s = this.stack;
|
58 | const origLen = s.length;
|
59 | const argCount = arguments.length;
|
60 | let value = s[origLen - 1];
|
61 |
|
62 | for (let i = 1; i < argCount; ++i) {
|
63 | const name = arguments[i];
|
64 | value = value[name];
|
65 | s.push(name, value);
|
66 | }
|
67 |
|
68 | for (let i = 0; i < value.length; ++i) {
|
69 | s.push(i, value[i]);
|
70 |
|
71 |
|
72 | callback(this);
|
73 | s.length -= 2;
|
74 | }
|
75 |
|
76 | s.length = origLen;
|
77 | }
|
78 |
|
79 | getContainer() {
|
80 | return getStackAt(this, 3);
|
81 | }
|
82 |
|
83 | getName() {
|
84 |
|
85 |
|
86 |
|
87 |
|
88 | return getStackAt(this, 2);
|
89 | }
|
90 |
|
91 | getNode() {
|
92 | return getNodeAt(this, 0);
|
93 | }
|
94 |
|
95 | getParentNode() {
|
96 | return getNodeAt(this, 1);
|
97 | }
|
98 |
|
99 | getValue() {
|
100 |
|
101 |
|
102 | return getStackAt(this, 1);
|
103 | }
|
104 |
|
105 | replace(newValue) {
|
106 | const s = this.stack;
|
107 | const len = s.length;
|
108 | const oldValue = this.getValue();
|
109 |
|
110 | if (len > 2) {
|
111 | const parent = s[len - 3];
|
112 | const name = this.getName();
|
113 | parent[name] = s[len - 1] = newValue;
|
114 | }
|
115 | return oldValue;
|
116 | }
|
117 |
|
118 | valueIsNode() {
|
119 | return utils.isNodeLike(this.getValue());
|
120 | }
|
121 | };
|
122 |
|
123 | Object.setPrototypeOf(FastPath.prototype, null);
|
124 |
|
125 | Object.defineProperty(FastPath.prototype, brand, {
|
126 | value: true,
|
127 | enumerable: false,
|
128 | writable: false,
|
129 | configurable: false
|
130 | });
|
131 |
|
132 | function getNodeAt(path, pos) {
|
133 | const s = path.stack;
|
134 |
|
135 | for (let i = s.length - 1; i >= 0; i -= 2) {
|
136 | const value = s[i];
|
137 | if (utils.isNodeLike(value) && --pos < 0) {
|
138 | return value;
|
139 | }
|
140 | }
|
141 | return null;
|
142 | }
|
143 |
|
144 | function getStackAt(path, pos) {
|
145 | const s = path.stack;
|
146 | const len = s.length;
|
147 | return len < pos ? null : s[len - pos];
|
148 | }
|
149 |
|
150 | module.exports = FastPath;
|