Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle ClassDeclaration binding #383

Merged
merged 3 commits into from Feb 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -1129,4 +1129,24 @@ describe("mangle-names", () => {
`);
expect(transform(source)).toBe(expected);
});

it("should fix issue#365 - classDeclaration with unsafe parent scope", () => {
const source = unpad(`
function foo() {
eval("");
class A {}
class B {}
}
`);
expect(transform(source)).toBe(source);
});

it("should fix classDeclaration with unsafe program scope", () => {
const source = unpad(`
class A {}
class B {}
eval("");
`);
expect(transform(source, { topLevel: true })).toBe(source);
});
});
159 changes: 75 additions & 84 deletions packages/babel-plugin-minify-mangle-names/src/index.js
Expand Up @@ -86,100 +86,91 @@ module.exports = ({ types: t, traverse }) => {
}
}

mangle() {
mangleScope(scope) {
const mangler = this;

this.program.traverse({
Scopable(path) {
const {scope} = path;
if (!mangler.eval && hasEval(scope)) return;

if (!mangler.eval && hasEval(scope)) return;
if (mangler.visitedScopes.has(scope)) return;
mangler.visitedScopes.add(scope);

if (mangler.visitedScopes.has(scope)) return;
mangler.visitedScopes.add(scope);
let i = 0;
function getNext() {
return mangler.charset.getIdentifier(i++);
}

function hasOwnBinding(name) {
if (scope.parent !== mangler.program.scope) {
return scope.hasOwnBinding(name);
}
return mangler.program.scope.hasOwnBinding(name) || scope.hasOwnBinding(name);
}
// This is useful when we have vars of single character
// => var a, ...z, A, ...Z, $, _;
// to
// => var aa, a, b ,c;
// instead of
// => var aa, ab, ...;
// TODO:
// Re-enable after enabling this feature
// This doesn't work right now as we are concentrating
// on performance improvements
// function resetNext() {
// i = 0;
// }

const bindings = scope.getAllBindings();
const names = Object.keys(bindings);

for (let i = 0; i < names.length; i++) {
const oldName = names[i];
const binding = bindings[oldName];

if (
// arguments
oldName === "arguments"
// other scope bindings
|| !scope.hasOwnBinding(oldName)
// labels
|| binding.path.isLabeledStatement()
// ClassDeclaration has binding in two scopes
// 1. The scope in which it is declared
// 2. The class's own scope
// - https://github.com/babel/babel/issues/5156
|| (binding.path.isClassDeclaration() && binding.path === scope.path)
// blacklisted
|| mangler.isBlacklist(oldName)
// function names
|| (mangler.keepFnName ? isFunction(binding.path) : false)
// class names
|| (mangler.keepClassName ? isClass(binding.path) : false)
) {
continue;
}

let i = 0;
function getNext() {
return mangler.charset.getIdentifier(i++);
}
let next;
do {
next = getNext();
} while (
!t.isValidIdentifier(next)
|| hop.call(bindings, next)
|| scope.hasGlobal(next)
|| scope.hasReference(next)
);

// TODO:
// re-enable this - check above
// resetNext();
mangler.rename(scope, oldName, next);
}
}

// This is useful when we have vars of single character
// => var a, ...z, A, ...Z, $, _;
// to
// => var aa, a, b ,c;
// instead of
// => var aa, ab, ...;
// TODO:
// Re-enable after enabling this feature
// This doesn't work right now as we are concentrating
// on performance improvements
// function resetNext() {
// i = 0;
// }

const bindings = scope.getAllBindings();
const names = Object.keys(bindings);

for (let i = 0; i < names.length; i++) {
const oldName = names[i];
const binding = bindings[oldName];
const isTopLevel = mangler.program.scope.bindings[oldName] === binding;

if (
// already renamed bindings
binding.renamed
// arguments
|| oldName === "arguments"
// globals
|| (mangler.topLevel ? false : isTopLevel)
// other scope bindings
|| !hasOwnBinding(oldName)
// labels
|| binding.path.isLabeledStatement()
// blacklisted
|| mangler.isBlacklist(oldName)
// function names
|| (mangler.keepFnName ? isFunction(binding.path) : false)
// class names
|| (mangler.keepClassName ? isClass(binding.path) : false)
) {
continue;
}
mangle() {
const mangler = this;

let next;
do {
next = getNext();
} while (
!t.isValidIdentifier(next)
|| hop.call(bindings, next)
|| scope.hasGlobal(next)
|| scope.hasReference(next)
);

// TODO:
// re-enable this - check above
// resetNext();
mangler.rename(scope, oldName, next);
if (isTopLevel) {
mangler.rename(mangler.program.scope, oldName, next);
}
// mark the binding as renamed
binding.renamed = true;
}
if (mangler.topLevel) {
mangler.mangleScope(mangler.program.scope);
}

this.program.traverse({
Scopable(path) {
mangler.mangleScope(path.scope);
}
});

// TODO:
// re-enable
// check above
// this.updateReferences();
}

rename(scope, oldName, newName) {
Expand Down