All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.shapesecurity.bandolier.es2017.transformations.DeadCodeElimination Maven / Gradle / Ivy

package com.shapesecurity.bandolier.es2017.transformations;

import com.shapesecurity.functional.Pair;
import com.shapesecurity.functional.data.ImmutableList;
import com.shapesecurity.functional.data.ImmutableSet;
import com.shapesecurity.functional.data.Maybe;
import com.shapesecurity.shift.es2017.ast.ArrowExpression;
import com.shapesecurity.shift.es2017.ast.BindingIdentifier;
import com.shapesecurity.shift.es2017.ast.CallExpression;
import com.shapesecurity.shift.es2017.ast.ClassDeclaration;
import com.shapesecurity.shift.es2017.ast.ClassExpression;
import com.shapesecurity.shift.es2017.ast.Directive;
import com.shapesecurity.shift.es2017.ast.ExpressionStatement;
import com.shapesecurity.shift.es2017.ast.FormalParameters;
import com.shapesecurity.shift.es2017.ast.FunctionBody;
import com.shapesecurity.shift.es2017.ast.FunctionDeclaration;
import com.shapesecurity.shift.es2017.ast.FunctionExpression;
import com.shapesecurity.shift.es2017.ast.ImportDeclarationExportDeclarationStatement;
import com.shapesecurity.shift.es2017.ast.LiteralBooleanExpression;
import com.shapesecurity.shift.es2017.ast.LiteralInfinityExpression;
import com.shapesecurity.shift.es2017.ast.LiteralNullExpression;
import com.shapesecurity.shift.es2017.ast.LiteralNumericExpression;
import com.shapesecurity.shift.es2017.ast.LiteralRegExpExpression;
import com.shapesecurity.shift.es2017.ast.LiteralStringExpression;
import com.shapesecurity.shift.es2017.ast.Script;
import com.shapesecurity.shift.es2017.ast.Statement;
import com.shapesecurity.shift.es2017.ast.VariableDeclaration;
import com.shapesecurity.shift.es2017.ast.VariableDeclarationStatement;
import com.shapesecurity.shift.es2017.ast.VariableDeclarator;
import com.shapesecurity.shift.es2017.scope.Reference;
import com.shapesecurity.shift.es2017.scope.ScopeAnalyzer;
import com.shapesecurity.shift.es2017.scope.ScopeLookup;
import com.shapesecurity.shift.es2017.scope.Variable;

import javax.annotation.Nonnull;

public final class DeadCodeElimination {

	@Nonnull
	private final ScopeLookup lookup;

	private DeadCodeElimination(@Nonnull ScopeLookup lookup) {
		this.lookup = lookup;
	}

	private void checkName(@Nonnull String name) {
		if (name.equals("arguments") || name.equals("eval")) {
			throw new RuntimeException("Cannot declare as arguments or eval");
		}
	}

	private Pair>, ImmutableList> fixDeclarations(@Nonnull Statement item) {
		ImmutableSet> removedImportSet = ImmutableSet.emptyUsingEquality();
		if (item instanceof FunctionDeclaration) {
			FunctionDeclaration declaration = (FunctionDeclaration) item;
			checkName(declaration.name.name);
			Variable variable = lookup.findVariableDeclaredBy(declaration.name).fromJust();
			if (variable.references.length == 0) {
				return Pair.of(removedImportSet, ImmutableList.empty());
			}
		} else if (item instanceof ClassDeclaration) {
			ClassDeclaration declaration = (ClassDeclaration) item;
			checkName(declaration.name.name);
			Variable variable = lookup.findVariablesForClassDecl(declaration).right;
			if (variable.references.length == 0) {
				return Pair.of(removedImportSet, ImmutableList.empty());
			}
		} else if (item instanceof VariableDeclarationStatement) {
			ImmutableList unremovableDeclarators = ((VariableDeclarationStatement) item).declaration.declarators.filter(declarator -> {
				if (declarator.binding instanceof BindingIdentifier) {
					if (declarator.init.isNothing() ||
							declarator.init.fromJust() instanceof FunctionExpression ||
							declarator.init.fromJust() instanceof ClassExpression ||
							declarator.init.fromJust() instanceof ArrowExpression ||
							declarator.init.fromJust() instanceof LiteralStringExpression ||
							declarator.init.fromJust() instanceof LiteralNumericExpression ||
							declarator.init.fromJust() instanceof LiteralNullExpression ||
							declarator.init.fromJust() instanceof LiteralBooleanExpression ||
							declarator.init.fromJust() instanceof LiteralInfinityExpression ||
							declarator.init.fromJust() instanceof LiteralRegExpExpression) {
						checkName(((BindingIdentifier) declarator.binding).name);
						Variable variable = lookup.findVariableDeclaredBy((BindingIdentifier) declarator.binding).fromJust();
						ImmutableSet references = variable.references.filter(reference -> reference.node != declarator.binding).uniqByIdentity();
						return references.length() > 0;
					}
				}
				// TODO: array/object bindings?
				return true;
			});
			if (unremovableDeclarators.length == 0) {
				return Pair.of(removedImportSet, ImmutableList.empty());
			}
			return Pair.of(removedImportSet, ImmutableList.of(new VariableDeclarationStatement(new VariableDeclaration(((VariableDeclarationStatement) item).declaration.kind, unremovableDeclarators))));
		}
		return Pair.of(removedImportSet, ImmutableList.of(item));
	}

	public static Script removeAllUnusedDeclarations(@Nonnull Script script) {
		DeadCodeElimination deadCodeElimination = new DeadCodeElimination(new ScopeLookup(ScopeAnalyzer.analyze(script)));
		ImmutableList directives = script.directives;
		ImmutableList statements = script.statements;
		boolean iife = false;
		if (statements.length == 1) { // IIFE
			ImportDeclarationExportDeclarationStatement item = statements.maybeHead().fromJust();
			if (item instanceof  ExpressionStatement && ((ExpressionStatement) item).expression instanceof CallExpression) {
				CallExpression callExpression = (CallExpression) ((ExpressionStatement) item).expression;
				if (callExpression.arguments.length == 0 && callExpression.callee instanceof FunctionExpression) {
					FunctionExpression functionExpression = (FunctionExpression) callExpression.callee;
					if (functionExpression.params.items.length == 0 && functionExpression.params.rest.isNothing()) {
						directives = directives.uniqByEquality().putAll(functionExpression.body.directives).toList();
						statements = functionExpression.body.statements.map(statement -> statement);
						iife = true;
					}
				}
			}
		}
		ImmutableList finalStatements = statements.flatMap(item -> {
			Pair>, ImmutableList> pair = deadCodeElimination.fixDeclarations(item);
			return pair.right;
		});
		Script finalScript = new Script(directives, iife ?
				ImmutableList.of(new ExpressionStatement(new CallExpression(new FunctionExpression(false, false, Maybe.empty(), new FormalParameters(ImmutableList.empty(), Maybe.empty()), new FunctionBody(directives, finalStatements)), ImmutableList.empty())))
				: finalStatements);
		return finalScript;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy