/* * Copyright 2011 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; /** * @author johnlenz@google.com (John Lenz) */ public class RemoveUnusedClassPropertiesTest extends CompilerTestCase { private static final String EXTERNS = "var window;\n" + "function alert(a) {}\n" + "var EXT = {};" + "EXT.ext;"; public RemoveUnusedClassPropertiesTest() { super(EXTERNS); } @Override protected CompilerPass getProcessor(Compiler compiler) { return new RemoveUnusedClassProperties(compiler); } public void testSimple1() { // A property defined on "this" can be removed test("this.a = 2", "2"); test("x = (this.a = 2)", "x = 2"); testSame("this.a = 2; x = this.a;"); } public void testSimple2() { // A property defined on "this" can be removed, even when defined // as part of an expression test("this.a = 2, f()", "2, f()"); test("x = (this.a = 2, f())", "x = (2, f())"); test("x = (f(), this.a = 2)", "x = (f(), 2)"); } public void testSimple3() { // A property defined on an object other than "this" can not be removed. testSame("y.a = 2"); // but doesn't prevent the removal of the definition on 'this'. test("y.a = 2; this.a = 2", "y.a = 2; 2"); // Some use of the property "a" prevents the removal. testSame("y.a = 2; this.a = 1; alert(x.a)"); } public void testObjLit() { // A property defined on an object other than "this" can not be removed. testSame("({a:2})"); // but doesn't prevent the removal of the definition on 'this'. test("({a:0}); this.a = 1;", "({a:0});1"); // Some use of the property "a" prevents the removal. testSame("x = ({a:0}); this.a = 1; alert(x.a)"); } public void testExtern() { // A property defined in the externs is can not be removed. testSame("this.ext = 2"); } public void testExport() { // An exported property can not be removed. testSame("this.ext = 2; window['export'] = this.ext;"); testSame("function f() { this.ext = 2; } window['export'] = this.ext;"); } public void testAssignOp1() { // Properties defined using a compound assignment can be removed if the // result of the assignment expression is not immediately used. test("this.x += 2", "2"); testSame("x = (this.x += 2)"); testSame("this.x += 2; x = this.x;"); // But, of course, a later use prevents its removal. testSame("this.x += 2; x.x;"); } public void testAssignOp2() { // Properties defined using a compound assignment can be removed if the // result of the assignment expression is not immediately used. test("this.a += 2, f()", "2, f()"); test("x = (this.a += 2, f())", "x = (2, f())"); testSame("x = (f(), this.a += 2)"); } public void testInc1() { // Increments and Decrements are handled similarly to compound assignments // but need a placeholder value when replaced. test("this.x++", "0"); testSame("x = (this.x++)"); testSame("this.x++; x = this.x;"); test("--this.x", "0"); testSame("x = (--this.x)"); testSame("--this.x; x = this.x;"); } public void testInc2() { // Increments and Decrements are handled similarly to compound assignments // but need a placeholder value when replaced. test("this.a++, f()", "0, f()"); test("x = (this.a++, f())", "x = (0, f())"); testSame("x = (f(), this.a++)"); test("--this.a, f()", "0, f()"); test("x = (--this.a, f())", "x = (0, f())"); testSame("x = (f(), --this.a)"); } public void testJSCompiler_renameProperty() { // JSCompiler_renameProperty introduces a use of the property testSame("this.a = 2; x[JSCompiler_renameProperty('a')]"); testSame("this.a = 2; JSCompiler_renameProperty('a')"); } public void testForIn() { // This is the basic assumption that this pass makes: // it can remove properties even when the object is used in a FOR-IN loop test("this.y = 1;for (var a in x) { alert(x[a]) }", "1;for (var a in x) { alert(x[a]) }"); } public void testObjectKeys() { // This is the basic assumption that this pass makes: // it can remove properties even when the object are referenced test("this.y = 1;alert(Object.keys(this))", "1;alert(Object.keys(this))"); } public void testIssue730() { // Partial removal of properties can causes problems if the object is // sealed. // TODO(johnlenz): should we not allow partial removals? test( "function A() {this.foo = 0;}\n" + "function B() {this.a = new A();}\n" + "B.prototype.dostuff = function() {this.a.foo++;alert('hi');}\n" + "new B().dostuff();\n", "function A(){0}" + "function B(){this.a=new A}" + "B.prototype.dostuff=function(){this.a.foo++;alert(\"hi\")};" + "new B().dostuff();"); } }