Skip to content

Commit

Permalink
Reuse removed vars in mangler (#395)
Browse files Browse the repository at this point in the history
* Reuse removed vars in mangler

ResetNext identifier only when reuse is true

Fix tests - add keepClassName

Reuse vars as default

I dont know why it works - #326

Extract tracker to a separate file, Add topLevel Option

* BFS traverse

* Fix scope tracking

* Fix tests

* Fix mangle blacklist tests

* Fix formatting

* Remove unnecessary preset tests

* Add JSDoc Comments and update Error messages

* Remove duplicate tests

* Fix Error message
  • Loading branch information
boopathi committed May 22, 2017
1 parent e2710cf commit 51bbb07
Show file tree
Hide file tree
Showing 9 changed files with 1,116 additions and 604 deletions.
797 changes: 356 additions & 441 deletions packages/babel-plugin-minify-mangle-names/__tests__/mangle-names-test.js

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions packages/babel-plugin-minify-mangle-names/src/bfs-traverse.js
@@ -0,0 +1,48 @@
"use strict";

module.exports = function bfsTraverseCreator({ types: t, traverse }) {
function getFields(path) {
return t.VISITOR_KEYS[path.type];
}

return function bfsTraverse(path, _visitor) {
if (!path.node) {
throw new Error("Not a valid path");
}
const visitor = traverse.explode(_visitor);

const queue = [path];
let current;

while (queue.length > 0) {
current = queue.shift();

// call
if (
visitor &&
visitor[current.type] &&
Array.isArray(visitor[current.type].enter)
) {
const fns = visitor[current.type].enter;
for (const fn of fns) {
if (typeof fn === "function") fn(current);
}
}

const fields = getFields(current);

for (const field of fields) {
const child = current.get(field);

if (Array.isArray(child)) {
// visit container left to right
for (const c of child) {
if (c.node) queue.push(c);
}
} else {
if (child.node) queue.push(child);
}
}
}
};
};
53 changes: 53 additions & 0 deletions packages/babel-plugin-minify-mangle-names/src/charset.js
@@ -0,0 +1,53 @@
"use strict";

const CHARSET = ("abcdefghijklmnopqrstuvwxyz" +
"ABCDEFGHIJKLMNOPQRSTUVWXYZ$_").split("");

module.exports = class Charset {
constructor(shouldConsider) {
this.shouldConsider = shouldConsider;
this.chars = CHARSET.slice();
this.frequency = {};
this.chars.forEach(c => {
this.frequency[c] = 0;
});
this.finalized = false;
}

consider(str) {
if (!this.shouldConsider) {
return;
}

str.split("").forEach(c => {
if (this.frequency[c] != null) {
this.frequency[c]++;
}
});
}

sort() {
if (this.shouldConsider) {
this.chars = this.chars.sort(
(a, b) => this.frequency[b] - this.frequency[a]
);
}

this.finalized = true;
}

getIdentifier(num) {
if (!this.finalized) {
throw new Error("Should sort first");
}

let ret = "";
num++;
do {
num--;
ret += this.chars[num % this.chars.length];
num = Math.floor(num / this.chars.length);
} while (num > 0);
return ret;
}
};
28 changes: 28 additions & 0 deletions packages/babel-plugin-minify-mangle-names/src/counted-set.js
@@ -0,0 +1,28 @@
// Set that counts
module.exports = class CountedSet {
constructor() {
// because you can't simply extend Builtins yet
this.map = new Map();
}
keys() {
return [...this.map.keys()];
}
has(value) {
return this.map.has(value);
}
add(value) {
if (!this.has(value)) {
this.map.set(value, 0);
}
this.map.set(value, this.map.get(value) + 1);
}
delete(value) {
if (!this.has(value)) return;
const count = this.map.get(value);
if (count <= 1) {
this.map.delete(value);
} else {
this.map.set(value, count - 1);
}
}
};
55 changes: 55 additions & 0 deletions packages/babel-plugin-minify-mangle-names/src/fixup-var-scoping.js
@@ -0,0 +1,55 @@
// this fixes a bug where converting let to var
// doesn't change the binding's scope to function scope
// https://github.com/babel/babel/issues/4818
module.exports = function(mangler) {
mangler.program.traverse({
VariableDeclaration(path) {
if (path.node.kind !== "var") {
return;
}
const fnScope = path.scope.getFunctionParent();
const bindingIds = path.getOuterBindingIdentifierPaths();

for (const name in bindingIds) {
const binding = path.scope.getBinding(name);

// var isn't hoisted to fnScope
if (binding.scope !== fnScope) {
const existingBinding = fnScope.bindings[name];
// make sure we are clear that the fnScope doesn't already have
// an existing binding
if (!existingBinding) {
// move binding to the function scope

// update our scopeTracker first before
// we mutate the scope
mangler.scopeTracker.moveBinding(binding, fnScope);

fnScope.bindings[name] = binding;
binding.scope = fnScope;
delete binding.scope.bindings[name];
} else {
// we need a new binding that's valid in both the scopes
// binding.scope and fnScope
const newName = fnScope.generateUid(
binding.scope.generateUid(name)
);

// rename binding in the original scope
mangler.rename(binding.scope, binding, name, newName);

// move binding to fnScope as newName

// update our scopeTracker first before
// we mutate the scope
mangler.scopeTracker.moveBinding(binding, fnScope);

fnScope.bindings[newName] = binding;
binding.scope = fnScope;
delete binding.scope.bindings[newName];
}
}
}
}
});
};

0 comments on commit 51bbb07

Please sign in to comment.