Reduce dependency on lodash functions: values, extends (#11798)

* Replace lodash 'values' usage with Object.keys => .map(obj[key])

* Block scoping: refactor letReferences, outsideLetReferences as objects of type Map

* Remove lodash dependency from babel-plugin-transform-block-scoping

* Fixup: Add missing Object.keys call

* Fixup: Update remaining property accessors

* Coerce Map.values() iterator results into an array via spread operator

* Fixup: Map.put -> Map.set

* Fixup: undo incorrect variable de-duplication

* Replace array-spread-plus-map combination with Array.from call

* Extract an extendMap function as an attempt to create an optimization boundary

* Experiment: cast objects to string (eliminates one Map/object difference per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map )

* Fixup: perform String cast on map keys, not values

* Revert "Fixup: perform String cast on map keys, not values"

This reverts commit abdd147438fa74f51ac50ef1f96bb462810cd3f2.

* Revert "Experiment: cast objects to string (eliminates one Map/object difference per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map )"

This reverts commit a4035c885b37bfd6e926a0362bda9dcf5b5a52c2.

* Experiment: filter keys via Object.prototype.hasOwnProperty.call

* Revert "Experiment: filter keys via Object.prototype.hasOwnProperty.call"

This reverts commit 491c093f213c6229815b2e6dc9243245376265b0.

* Migrate back from Map-based reference storage to Object-based storage; access performance appears much improved for Object property access

* Revert "Migrate back from Map-based reference storage to Object-based storage; access performance appears much improved for Object property access"

This reverts commit 2119acc7f0d78ced3b9ad77820b4b72e5ad67475.

* Iterate over a clone of outsideRefs keys

* Revert "Extract an extendMap function as an attempt to create an optimization boundary"

This reverts commit 85689f2bfc180d0b5c0e674e5de7954470c7ec69.

* Fixup: migrate remaining Object property access to Map.get in tdz module
This commit is contained in:
James Addison 2020-07-08 12:17:04 +01:00 committed by GitHub
parent 58cfaf20ee
commit bff6298578
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 27 additions and 24 deletions

View File

@ -13,8 +13,7 @@
}, },
"main": "lib/index.js", "main": "lib/index.js",
"dependencies": { "dependencies": {
"@babel/helper-plugin-utils": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4"
"lodash": "^4.17.13"
}, },
"keywords": [ "keywords": [
"babel-plugin" "babel-plugin"

View File

@ -2,8 +2,6 @@ import { declare } from "@babel/helper-plugin-utils";
import type NodePath from "@babel/traverse"; import type NodePath from "@babel/traverse";
import type Scope from "@babel/traverse"; import type Scope from "@babel/traverse";
import { visitor as tdzVisitor } from "./tdz"; import { visitor as tdzVisitor } from "./tdz";
import values from "lodash/values";
import extend from "lodash/extend";
import { traverse, template, types as t } from "@babel/core"; import { traverse, template, types as t } from "@babel/core";
const DONE = new WeakSet(); const DONE = new WeakSet();
@ -195,7 +193,7 @@ const letReferenceBlockVisitor = traverse.visitors.merge([
const letReferenceFunctionVisitor = traverse.visitors.merge([ const letReferenceFunctionVisitor = traverse.visitors.merge([
{ {
ReferencedIdentifier(path, state) { ReferencedIdentifier(path, state) {
const ref = state.letReferences[path.node.name]; const ref = state.letReferences.get(path.node.name);
// not a part of our scope // not a part of our scope
if (!ref) return; if (!ref) return;
@ -250,7 +248,7 @@ const continuationVisitor = {
if (path.isAssignmentExpression() || path.isUpdateExpression()) { if (path.isAssignmentExpression() || path.isUpdateExpression()) {
for (const name of Object.keys(path.getBindingIdentifiers())) { for (const name of Object.keys(path.getBindingIdentifiers())) {
if ( if (
state.outsideReferences[name] !== state.outsideReferences.get(name) !==
path.scope.getBindingIdentifier(name) path.scope.getBindingIdentifier(name)
) { ) {
continue; continue;
@ -359,9 +357,9 @@ class BlockScoping {
this.blockPath = blockPath; this.blockPath = blockPath;
this.block = blockPath.node; this.block = blockPath.node;
this.outsideLetReferences = Object.create(null); this.outsideLetReferences = new Map();
this.hasLetReferences = false; this.hasLetReferences = false;
this.letReferences = Object.create(null); this.letReferences = new Map();
this.body = []; this.body = [];
if (loopPath) { if (loopPath) {
@ -447,8 +445,8 @@ class BlockScoping {
blockScope.getFunctionParent() || blockScope.getProgramParent(); blockScope.getFunctionParent() || blockScope.getProgramParent();
const letRefs = this.letReferences; const letRefs = this.letReferences;
for (const key of Object.keys(letRefs)) { for (const key of letRefs.keys()) {
const ref = letRefs[key]; const ref = letRefs.get(key);
const binding = blockScope.getBinding(ref.name); const binding = blockScope.getBinding(ref.name);
if (!binding) continue; if (!binding) continue;
if (binding.kind === "let" || binding.kind === "const") { if (binding.kind === "let" || binding.kind === "const") {
@ -476,10 +474,10 @@ class BlockScoping {
// those in upper scopes and then if they do, generate a uid // those in upper scopes and then if they do, generate a uid
// for them and replace all references with it // for them and replace all references with it
for (const key of Object.keys(letRefs)) { for (const key of letRefs.keys()) {
// just an Identifier node we collected in `getLetReferences` // just an Identifier node we collected in `getLetReferences`
// this is the defining identifier of a declaration // this is the defining identifier of a declaration
const ref = letRefs[key]; const ref = letRefs.get(key);
// todo: could skip this if the colliding binding is in another function // todo: could skip this if the colliding binding is in another function
if (scope.parentHasBinding(key) || scope.hasGlobal(key)) { if (scope.parentHasBinding(key) || scope.hasGlobal(key)) {
@ -496,8 +494,8 @@ class BlockScoping {
} }
} }
for (const key of Object.keys(outsideLetRefs)) { for (const key of outsideLetRefs.keys()) {
const ref = letRefs[key]; const ref = letRefs.get(key);
// check for collisions with a for loop's init variable and the enclosing scope's bindings // check for collisions with a for loop's init variable and the enclosing scope's bindings
// https://github.com/babel/babel/issues/8498 // https://github.com/babel/babel/issues/8498
if (isInLoop(this.blockPath) && blockPathScope.hasOwnBinding(key)) { if (isInLoop(this.blockPath) && blockPathScope.hasOwnBinding(key)) {
@ -519,20 +517,21 @@ class BlockScoping {
// remap loop heads with colliding variables // remap loop heads with colliding variables
if (this.loop) { if (this.loop) {
for (const name of Object.keys(outsideRefs)) { // nb: clone outsideRefs keys since the map is modified within the loop
const id = outsideRefs[name]; for (const name of [...outsideRefs.keys()]) {
const id = outsideRefs.get(name);
if ( if (
this.scope.hasGlobal(id.name) || this.scope.hasGlobal(id.name) ||
this.scope.parentHasBinding(id.name) this.scope.parentHasBinding(id.name)
) { ) {
delete outsideRefs[id.name]; outsideRefs.delete(id.name);
delete this.letReferences[id.name]; this.letReferences.delete(id.name);
this.scope.rename(id.name); this.scope.rename(id.name);
this.letReferences[id.name] = id; this.letReferences.set(id.name, id);
outsideRefs[id.name] = id; outsideRefs.set(id.name, id);
} }
} }
} }
@ -545,7 +544,7 @@ class BlockScoping {
this.hoistVarDeclarations(); this.hoistVarDeclarations();
// turn outsideLetReferences into an array // turn outsideLetReferences into an array
const args = values(outsideRefs).map(id => t.cloneNode(id)); const args = Array.from(outsideRefs.values(), node => t.cloneNode(node));
const params = args.map(id => t.cloneNode(id)); const params = args.map(id => t.cloneNode(id));
const isSwitch = this.blockPath.isSwitchStatement(); const isSwitch = this.blockPath.isSwitchStatement();
@ -702,7 +701,10 @@ class BlockScoping {
const init = this.loop.left || this.loop.init; const init = this.loop.left || this.loop.init;
if (isBlockScoped(init)) { if (isBlockScoped(init)) {
declarators.push(init); declarators.push(init);
extend(this.outsideLetReferences, t.getBindingIdentifiers(init)); const names = t.getBindingIdentifiers(init);
for (const name of Object.keys(names)) {
this.outsideLetReferences.set(name, names[name]);
}
} }
} }
@ -751,7 +753,9 @@ class BlockScoping {
// declaration, rather than (for example) mistakenly including the // declaration, rather than (for example) mistakenly including the
// parameters of a function declaration. Fixes #4880. // parameters of a function declaration. Fixes #4880.
const keys = t.getBindingIdentifiers(declar, false, true); const keys = t.getBindingIdentifiers(declar, false, true);
extend(this.letReferences, keys); for (const key of Object.keys(keys)) {
this.letReferences.set(key, keys[key]);
}
this.hasLetReferences = true; this.hasLetReferences = true;
} }

View File

@ -20,7 +20,7 @@ function buildTDZAssert(node, state) {
} }
function isReference(node, scope, state) { function isReference(node, scope, state) {
const declared = state.letReferences[node.name]; const declared = state.letReferences.get(node.name);
if (!declared) return false; if (!declared) return false;
// declared node is different in this scope // declared node is different in this scope