Skip to content

Commit b114486

Browse files
JLHwungnicolo-ribaudo
authored andcommitted
Traverse performance (#10480)
* perf: remove this.inList assignment * perf: convert NodePath.parentKey into accessor function * perf: compress shouldSkip/shouldStop/removed traverse flags * perf: lazy initialize this.skipKeys * perf: lazily initialize NodePath.data * add code comments before bit operations * remove unused typeAnnotation property # Conflicts: # packages/babel-traverse/src/path/index.js * early return when visitor keys are empty
1 parent e9c1bce commit b114486

4 files changed

Lines changed: 77 additions & 21 deletions

File tree

packages/babel-traverse/src/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ export default function traverse(
3131
}
3232
}
3333

34+
if (!t.VISITOR_KEYS[parent.type]) {
35+
return;
36+
}
37+
3438
visitors.explode(opts);
3539

3640
traverse.node(parent, opts, scope, state, parentPath);

packages/babel-traverse/src/path/context.js

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// This file contains methods responsible for maintaining a TraversalContext.
22

33
import traverse from "../index";
4+
import { SHOULD_SKIP, SHOULD_STOP } from "./index";
45

56
export function call(key): boolean {
67
const opts = this.opts;
@@ -43,7 +44,8 @@ export function _call(fns?: Array<Function>): boolean {
4344
// node has been replaced, it will have been requeued
4445
if (this.node !== node) return true;
4546

46-
if (this.shouldStop || this.shouldSkip || this.removed) return true;
47+
// this.shouldSkip || this.shouldStop || this.removed
48+
if (this._traverseFlags > 0) return true;
4749
}
4850

4951
return false;
@@ -97,12 +99,15 @@ export function skip() {
9799
}
98100

99101
export function skipKey(key) {
102+
if (this.skipKeys == null) {
103+
this.skipKeys = {};
104+
}
100105
this.skipKeys[key] = true;
101106
}
102107

103108
export function stop() {
104-
this.shouldStop = true;
105-
this.shouldSkip = true;
109+
// this.shouldSkip = true; this.shouldStop = true;
110+
this._traverseFlags |= SHOULD_SKIP | SHOULD_STOP;
106111
}
107112

108113
export function setScope() {
@@ -122,10 +127,11 @@ export function setScope() {
122127
}
123128

124129
export function setContext(context) {
125-
this.shouldSkip = false;
126-
this.shouldStop = false;
127-
this.removed = false;
128-
this.skipKeys = {};
130+
if (this.skipKeys != null) {
131+
this.skipKeys = {};
132+
}
133+
// this.shouldSkip = false; this.shouldStop = false; this.removed = false;
134+
this._traverseFlags = 0;
129135

130136
if (context) {
131137
this.context = context;
@@ -220,9 +226,7 @@ export function pushContext(context) {
220226
}
221227

222228
export function setup(parentPath, container, listKey, key) {
223-
this.inList = !!listKey;
224229
this.listKey = listKey;
225-
this.parentKey = listKey || key;
226230
this.container = container;
227231

228232
this.parentPath = parentPath || this.parentPath;

packages/babel-traverse/src/path/index.js

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,29 +23,29 @@ import * as NodePath_comments from "./comments";
2323

2424
const debug = buildDebug("babel");
2525

26+
export const REMOVED = 1 << 0;
27+
export const SHOULD_STOP = 1 << 1;
28+
export const SHOULD_SKIP = 1 << 2;
29+
2630
export default class NodePath {
2731
constructor(hub: HubInterface, parent: Object) {
2832
this.parent = parent;
2933
this.hub = hub;
3034
this.contexts = [];
31-
this.data = Object.create(null);
32-
this.shouldSkip = false;
33-
this.shouldStop = false;
34-
this.removed = false;
35+
this.data = null;
36+
// this.shouldSkip = false; this.shouldStop = false; this.removed = false;
37+
this._traverseFlags = 0;
3538
this.state = null;
3639
this.opts = null;
3740
this.skipKeys = null;
3841
this.parentPath = null;
3942
this.context = null;
4043
this.container = null;
4144
this.listKey = null;
42-
this.inList = false;
43-
this.parentKey = null;
4445
this.key = null;
4546
this.node = null;
4647
this.scope = null;
4748
this.type = null;
48-
this.typeAnnotation = null;
4949
}
5050

5151
parent: Object;
@@ -57,18 +57,16 @@ export default class NodePath {
5757
removed: boolean;
5858
state: any;
5959
opts: ?Object;
60+
_traverseFlags: number;
6061
skipKeys: ?Object;
6162
parentPath: ?NodePath;
6263
context: TraversalContext;
6364
container: ?Object | Array<Object>;
6465
listKey: ?string;
65-
inList: boolean;
66-
parentKey: ?string;
6766
key: ?string;
6867
node: ?Object;
6968
scope: Scope;
7069
type: ?string;
71-
typeAnnotation: ?Object;
7270

7371
static get({ hub, parentPath, parent, container, listKey, key }): NodePath {
7472
if (!hub && parentPath) {
@@ -111,10 +109,16 @@ export default class NodePath {
111109
}
112110

113111
setData(key: string, val: any): any {
112+
if (this.data == null) {
113+
this.data = Object.create(null);
114+
}
114115
return (this.data[key] = val);
115116
}
116117

117118
getData(key: string, def?: any): any {
119+
if (this.data == null) {
120+
this.data = Object.create(null);
121+
}
118122
let val = this.data[key];
119123
if (val === undefined && def !== undefined) val = this.data[key] = def;
120124
return val;
@@ -152,6 +156,49 @@ export default class NodePath {
152156
toString() {
153157
return generator(this.node).code;
154158
}
159+
160+
get inList() {
161+
return !!this.listKey;
162+
}
163+
164+
get parentKey() {
165+
return this.listKey || this.key;
166+
}
167+
168+
get shouldSkip() {
169+
return !!(this._traverseFlags & SHOULD_SKIP);
170+
}
171+
172+
set shouldSkip(v) {
173+
if (v) {
174+
this._traverseFlags |= SHOULD_SKIP;
175+
} else {
176+
this._traverseFlags &= ~SHOULD_SKIP;
177+
}
178+
}
179+
180+
get shouldStop() {
181+
return !!(this._traverseFlags & SHOULD_STOP);
182+
}
183+
184+
set shouldStop(v) {
185+
if (v) {
186+
this._traverseFlags |= SHOULD_STOP;
187+
} else {
188+
this._traverseFlags &= ~SHOULD_STOP;
189+
}
190+
}
191+
192+
get removed() {
193+
return !!(this._traverseFlags & REMOVED);
194+
}
195+
set removed(v) {
196+
if (v) {
197+
this._traverseFlags |= REMOVED;
198+
} else {
199+
this._traverseFlags &= ~REMOVED;
200+
}
201+
}
155202
}
156203

157204
Object.assign(

packages/babel-traverse/src/path/removal.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// This file contains methods responsible for removing a node.
22

33
import { hooks } from "./lib/removal-hooks";
4+
import { REMOVED, SHOULD_SKIP } from "./index";
45

56
export function remove() {
67
this._assertUnremoved();
@@ -39,8 +40,8 @@ export function _remove() {
3940
}
4041

4142
export function _markRemoved() {
42-
this.shouldSkip = true;
43-
this.removed = true;
43+
// this.shouldSkip = true; this.removed = true;
44+
this._traverseFlags |= SHOULD_SKIP | REMOVED;
4445
this.node = null;
4546
}
4647

0 commit comments

Comments
 (0)