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
This commit is contained in:
Huáng Jùnliàng 2019-11-05 05:11:58 -05:00 committed by Nicolò Ribaudo
parent e9c1bce50f
commit b114486bc1
4 changed files with 77 additions and 21 deletions

View File

@ -31,6 +31,10 @@ export default function traverse(
}
}
if (!t.VISITOR_KEYS[parent.type]) {
return;
}
visitors.explode(opts);
traverse.node(parent, opts, scope, state, parentPath);

View File

@ -1,6 +1,7 @@
// This file contains methods responsible for maintaining a TraversalContext.
import traverse from "../index";
import { SHOULD_SKIP, SHOULD_STOP } from "./index";
export function call(key): boolean {
const opts = this.opts;
@ -43,7 +44,8 @@ export function _call(fns?: Array<Function>): boolean {
// node has been replaced, it will have been requeued
if (this.node !== node) return true;
if (this.shouldStop || this.shouldSkip || this.removed) return true;
// this.shouldSkip || this.shouldStop || this.removed
if (this._traverseFlags > 0) return true;
}
return false;
@ -97,12 +99,15 @@ export function skip() {
}
export function skipKey(key) {
if (this.skipKeys == null) {
this.skipKeys = {};
}
this.skipKeys[key] = true;
}
export function stop() {
this.shouldStop = true;
this.shouldSkip = true;
// this.shouldSkip = true; this.shouldStop = true;
this._traverseFlags |= SHOULD_SKIP | SHOULD_STOP;
}
export function setScope() {
@ -122,10 +127,11 @@ export function setScope() {
}
export function setContext(context) {
this.shouldSkip = false;
this.shouldStop = false;
this.removed = false;
this.skipKeys = {};
if (this.skipKeys != null) {
this.skipKeys = {};
}
// this.shouldSkip = false; this.shouldStop = false; this.removed = false;
this._traverseFlags = 0;
if (context) {
this.context = context;
@ -220,9 +226,7 @@ export function pushContext(context) {
}
export function setup(parentPath, container, listKey, key) {
this.inList = !!listKey;
this.listKey = listKey;
this.parentKey = listKey || key;
this.container = container;
this.parentPath = parentPath || this.parentPath;

View File

@ -23,15 +23,18 @@ import * as NodePath_comments from "./comments";
const debug = buildDebug("babel");
export const REMOVED = 1 << 0;
export const SHOULD_STOP = 1 << 1;
export const SHOULD_SKIP = 1 << 2;
export default class NodePath {
constructor(hub: HubInterface, parent: Object) {
this.parent = parent;
this.hub = hub;
this.contexts = [];
this.data = Object.create(null);
this.shouldSkip = false;
this.shouldStop = false;
this.removed = false;
this.data = null;
// this.shouldSkip = false; this.shouldStop = false; this.removed = false;
this._traverseFlags = 0;
this.state = null;
this.opts = null;
this.skipKeys = null;
@ -39,13 +42,10 @@ export default class NodePath {
this.context = null;
this.container = null;
this.listKey = null;
this.inList = false;
this.parentKey = null;
this.key = null;
this.node = null;
this.scope = null;
this.type = null;
this.typeAnnotation = null;
}
parent: Object;
@ -57,18 +57,16 @@ export default class NodePath {
removed: boolean;
state: any;
opts: ?Object;
_traverseFlags: number;
skipKeys: ?Object;
parentPath: ?NodePath;
context: TraversalContext;
container: ?Object | Array<Object>;
listKey: ?string;
inList: boolean;
parentKey: ?string;
key: ?string;
node: ?Object;
scope: Scope;
type: ?string;
typeAnnotation: ?Object;
static get({ hub, parentPath, parent, container, listKey, key }): NodePath {
if (!hub && parentPath) {
@ -111,10 +109,16 @@ export default class NodePath {
}
setData(key: string, val: any): any {
if (this.data == null) {
this.data = Object.create(null);
}
return (this.data[key] = val);
}
getData(key: string, def?: any): any {
if (this.data == null) {
this.data = Object.create(null);
}
let val = this.data[key];
if (val === undefined && def !== undefined) val = this.data[key] = def;
return val;
@ -152,6 +156,49 @@ export default class NodePath {
toString() {
return generator(this.node).code;
}
get inList() {
return !!this.listKey;
}
get parentKey() {
return this.listKey || this.key;
}
get shouldSkip() {
return !!(this._traverseFlags & SHOULD_SKIP);
}
set shouldSkip(v) {
if (v) {
this._traverseFlags |= SHOULD_SKIP;
} else {
this._traverseFlags &= ~SHOULD_SKIP;
}
}
get shouldStop() {
return !!(this._traverseFlags & SHOULD_STOP);
}
set shouldStop(v) {
if (v) {
this._traverseFlags |= SHOULD_STOP;
} else {
this._traverseFlags &= ~SHOULD_STOP;
}
}
get removed() {
return !!(this._traverseFlags & REMOVED);
}
set removed(v) {
if (v) {
this._traverseFlags |= REMOVED;
} else {
this._traverseFlags &= ~REMOVED;
}
}
}
Object.assign(

View File

@ -1,6 +1,7 @@
// This file contains methods responsible for removing a node.
import { hooks } from "./lib/removal-hooks";
import { REMOVED, SHOULD_SKIP } from "./index";
export function remove() {
this._assertUnremoved();
@ -39,8 +40,8 @@ export function _remove() {
}
export function _markRemoved() {
this.shouldSkip = true;
this.removed = true;
// this.shouldSkip = true; this.removed = true;
this._traverseFlags |= SHOULD_SKIP | REMOVED;
this.node = null;
}