/* * Copyright 2007 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; import com.google.common.collect.ImmutableSet; import java.util.Set; /** * Tests for {@link AliasStrings}. * */ public final class AliasStringsTest extends CompilerTestCase { private static final String EXTERNS = "alert"; private static final Set<String> ALL_STRINGS = null; private Set<String> strings = ALL_STRINGS; private boolean hashReduction = false; public AliasStringsTest() { super(EXTERNS); } @Override protected CompilerPass getProcessor(Compiler compiler) { AliasStrings pass = new AliasStrings(compiler, compiler.getModuleGraph(), strings, "(?i)secret", false); if (hashReduction) { pass.unitTestHashReductionMask = 0; } return pass; } public void testAssignment() { strings = ImmutableSet.of("none", "width", "overimaginative"); // Strings not in alias list testSame("var foo='foo'"); testSame("if(true) {myStr='width'}"); testSame("a='titanium',b='titanium',c='titanium',d='titanium'"); // Not worth aliasing: testSame("myStr='width'"); testSame("Bar.prototype.start='none'"); // Worth aliasing: test("a='overimaginative';b='overimaginative'", "var $$S_overimaginative='overimaginative';" + "a=$$S_overimaginative;b=$$S_overimaginative"); test("if(true) {a='overimaginative';b='overimaginative'}", "var $$S_overimaginative='overimaginative';" + "if(true) {a=$$S_overimaginative;b=$$S_overimaginative }"); testSame("var width=1234"); testSame("width=1234;width=10000;width=9900;width=17;"); } public void testSeveral() { strings = ImmutableSet.of("", "px", "none", "width"); // 'display' is not in the allowed string set and only 'none' and 'width' are large and common // enough to be worth interning. test( "function f() {" + " var styles1 = [" + " 'width', 100, 'px', 'display', 'none'" + " ].join('');" + " var styles2 = [" + " 'width', 100, 'px', 'display', 'none'" + " ].join('');" + " var styles3 = [" + " 'width', 100, 'px', 'display', 'none'" + " ].join('');" + " var styles4 = [" + " 'width', 100, 'px', 'display', 'none'" + " ].join('');" + " var styles5 = [" + " 'width', 100, 'px', 'display', 'none'" + " ].join('');" + " var styles6 = [" + " 'width', 100, 'px', 'display', 'none'" + " ].join('');" + "}", "var $$S_none = 'none';" + "var $$S_width = 'width';" + "function f() {" + " var styles1 = [" + " $$S_width, 100, 'px', 'display', $$S_none" + " ].join('');" + " var styles2 = [" + " $$S_width, 100, 'px', 'display', $$S_none" + " ].join('');" + " var styles3 = [" + " $$S_width, 100, 'px', 'display', $$S_none" + " ].join('');" + " var styles4 = [" + " $$S_width, 100, 'px', 'display', $$S_none" + " ].join('');" + " var styles5 = [" + " $$S_width, 100, 'px', 'display', $$S_none" + " ].join('');" + " var styles6 = [" + " $$S_width, 100, 'px', 'display', $$S_none" + " ].join('')" + "}"); } public void testSortedOutput() { strings = ImmutableSet.of( "abababababababababab", "aaaaaaaaaaaaaaaaaaaa", "acacacacacacacacacac", "bcabcabcabcabcabcabc", "bbabbabbabbabbabbabb"); test( "function f() {return ['abababababababababab', 'abababababababababab', " + " 'aaaaaaaaaaaaaaaaaaaa', 'aaaaaaaaaaaaaaaaaaaa', " + " 'acacacacacacacacacac', 'acacacacacacacacacac', " + " 'bcabcabcabcabcabcabc', 'bcabcabcabcabcabcabc', " + " 'bbabbabbabbabbabbabb', 'bbabbabbabbabbabbabb']}", "var $$S_aaaaaaaaaaaaaaaaaaaa='aaaaaaaaaaaaaaaaaaaa';" + "var $$S_abababababababababab='abababababababababab';" + "var $$S_acacacacacacacacacac='acacacacacacacacacac';" + "var $$S_bbabbabbabbabbabbabb='bbabbabbabbabbabbabb';" + "var $$S_bcabcabcabcabcabcabc='bcabcabcabcabcabcabc';" + "function f() {" + " return [$$S_abababababababababab, $$S_abababababababababab, " + " $$S_aaaaaaaaaaaaaaaaaaaa, $$S_aaaaaaaaaaaaaaaaaaaa, " + " $$S_acacacacacacacacacac, $$S_acacacacacacacacacac, " + " $$S_bcabcabcabcabcabcabc, $$S_bcabcabcabcabcabcabc, " + " $$S_bbabbabbabbabbabbabb, $$S_bbabbabbabbabbabbabb]}"); } public void testObjectLiterals() { strings = ImmutableSet.of("pxpxpxpxpxpxpxpxpxpx", "abcdefghijabcdefghij"); testSame("var foo={px:435}"); // string as key testSame("var foo={'pxpxpxpxpxpxpxpxpxpx':435}"); testSame("bar=function f(){return {'pxpxpxpxpxpxpxpxpxpx':435}}"); test( "function f() {var foo={bar:'abcdefghijabcdefghij'+'abcdefghijabcdefghij'}}", "var $$S_abcdefghijabcdefghij='abcdefghijabcdefghij';" + "function f() {var foo={bar:$$S_abcdefghijabcdefghij+$$S_abcdefghijabcdefghij}}"); test( "function f() {" + " var foo = {" + " px: 435," + " foo1: 'pxpxpxpxpxpxpxpxpxpx'," + " foo2: 'pxpxpxpxpxpxpxpxpxpx'," + " bar: 'baz'" + " }" + "}", "var $$S_pxpxpxpxpxpxpxpxpxpx = 'pxpxpxpxpxpxpxpxpxpx';" + "function f() {" + " var foo = {" + " px: 435," + " foo1: $$S_pxpxpxpxpxpxpxpxpxpx," + " foo2: $$S_pxpxpxpxpxpxpxpxpxpx," + " bar: 'baz'" + " }" + "}"); } public void testGetProp() { strings = ImmutableSet.of("pxpxpxpxpxpxpxpxpxpx", "widthwidthwidthwidth"); testSame("function f(){element.style.px=1234}"); test( "function f() {" + " shape.width.units='pxpxpxpxpxpxpxpxpxpx';" + " shape.width.units='pxpxpxpxpxpxpxpxpxpx';" + "}", "var $$S_pxpxpxpxpxpxpxpxpxpx='pxpxpxpxpxpxpxpxpxpx';" + "function f() {" + " shape.width.units=$$S_pxpxpxpxpxpxpxpxpxpx;" + " shape.width.units=$$S_pxpxpxpxpxpxpxpxpxpx;" + "}"); test( "function f() {" + " shape['widthwidthwidthwidth'].units='pt';" + " shape['widthwidthwidthwidth'].units='pt';" + "}", "var $$S_widthwidthwidthwidth='widthwidthwidthwidth';" + "function f() {" + " shape[$$S_widthwidthwidthwidth].units='pt';" + " shape[$$S_widthwidthwidthwidth].units='pt';" + "}"); } public void testFunctionCalls() { strings = ImmutableSet.of("", ",", "overimaginative"); // Not worth aliasing testSame("alert('')"); testSame("var a=[1,2,3];a.join(',')"); // worth aliasing test("f('overimaginative', 'overimaginative')", "var $$S_overimaginative='overimaginative';" + "f($$S_overimaginative,$$S_overimaginative)"); } public void testRegularExpressions() { strings = ImmutableSet.of("px"); testSame("/px/.match('10px')"); } public void testBlackList() { // The 'TOPseCreT' string is configured to be ignored even though it fits the aliasing // conditions. test( "(function() {" + " var f = 'sec ret sec ret sec ' + 'sec ret sec ret sec ';" + " g = 'TOPseCreT TOPseCreT ' + 'TOPseCreT TOPseCreT '" + "})" + "", "var $$S_sec$20ret$20sec$20ret$20sec$20 = 'sec ret sec ret sec ';" + "(function() {" + " var f = $$S_sec$20ret$20sec$20ret$20sec$20 + $$S_sec$20ret$20sec$20ret$20sec$20;" + " g = 'TOPseCreT TOPseCreT ' + 'TOPseCreT TOPseCreT '" + "})"); } public void testLongStableAlias() { strings = ALL_STRINGS; // Check long strings get a hash code test("a='Antidisestablishmentarianism';" + "b='Antidisestablishmentarianism';", "var $$S_Antidisestablishment_e428eaa9=" + " 'Antidisestablishmentarianism';" + "a=$$S_Antidisestablishment_e428eaa9;" + "b=$$S_Antidisestablishment_e428eaa9"); // Check that small changes give different hash codes test("a='AntidisestablishmentarianIsm';" + "b='AntidisestablishmentarianIsm';", "var $$S_Antidisestablishment_e4287289=" + " 'AntidisestablishmentarianIsm';" + "a=$$S_Antidisestablishment_e4287289;" + "b=$$S_Antidisestablishment_e4287289"); // TODO(user): check that hash code collisions are handled. } public void testLongStableAliasHashCollision() { strings = ALL_STRINGS; hashReduction = true; // Check that hash code collisions generate different alias // variable names test("f('Antidisestablishmentarianism');" + "f('Antidisestablishmentarianism');" + "f('Antidisestablishmentarianismo');" + "f('Antidisestablishmentarianismo');", "var $$S_Antidisestablishment_0=" + " 'Antidisestablishmentarianism';" + "var $$S_Antidisestablishment_0_1=" + " 'Antidisestablishmentarianismo';" + "f($$S_Antidisestablishment_0);" + "f($$S_Antidisestablishment_0);" + "f($$S_Antidisestablishment_0_1);" + "f($$S_Antidisestablishment_0_1);"); } public void testStringsThatAreGlobalVarValues() { strings = ALL_STRINGS; testSame("var foo='foo'; var bar='';"); // Regular array testSame("var foo=['foo','bar'];"); // Nested array testSame("var foo=['foo',['bar']];"); // Same string is in a global array and a local in a function testSame("var foo=['foo', 'bar'];function bar() {return 'foo';}"); // Regular object literal testSame("var foo={'foo': 'bar'};"); // Nested object literal testSame("var foo={'foo': {'bar': 'baz'}};"); // Same string is in a global object literal (as key) and local in a // function testSame("var foo={'foo': 'bar'};function bar() {return 'foo';}"); // Same string is in a global object literal (as value) and local in a // function testSame("var foo={'foo': 'foo'};function bar() {return 'foo';}"); } public void testStringsInModules() { strings = ALL_STRINGS; // Aliases must be placed in the correct module. The alias for // '------adios------' must be lifted from m2 and m3 and go in the // common parent module m1 JSModule[] modules = createModuleBush( // m0 "function f(a) { alert('ffffffffffffffffffff' + 'ffffffffffffffffffff' + a); }" + "function g() { alert('ciaociaociaociaociao'); }", // m1 "f('---------hi---------');" + "f('bye');" + "function h(a) { alert('hhhhhhhhhhhhhhhhhhhh' + 'hhhhhhhhhhhhhhhhhhhh' + a); }", // m2 "f('---------hi---------');" + "h('ciaociaociaociaociao' + '--------adios-------');" + "(function() { alert('zzzzzzzzzzzzzzzzzzzz' + 'zzzzzzzzzzzzzzzzzzzz'); })();", // m3 "f('---------hi---------'); alert('--------adios-------');" + "h('-------peaches------'); h('-------peaches------');"); test( modules, new String[] { // m1 "var $$S_ciaociaociaociaociao = 'ciaociaociaociaociao';" + "var $$S_ffffffffffffffffffff = 'ffffffffffffffffffff';" + "function f(a) { alert($$S_ffffffffffffffffffff + $$S_ffffffffffffffffffff + a); }" + "function g() { alert($$S_ciaociaociaociaociao); }", // m2 "var $$S_$2d$2d$2d$2d$2d$2d$2d$2d$2dhi$2d$2d$2d$2d$2d$2d$2d$2d$2d" + " = '---------hi---------';" + "var $$S_$2d$2d$2d$2d$2d$2d$2d$2d_adios$2d$2d$2d$2d$2d$2d$2d" + " = '--------adios-------'; " + "var $$S_hhhhhhhhhhhhhhhhhhhh = 'hhhhhhhhhhhhhhhhhhhh';" + "f($$S_$2d$2d$2d$2d$2d$2d$2d$2d$2dhi$2d$2d$2d$2d$2d$2d$2d$2d$2d);" + "f('bye');" + "function h(a) { alert($$S_hhhhhhhhhhhhhhhhhhhh + $$S_hhhhhhhhhhhhhhhhhhhh + a); }", // m3 "var $$S_zzzzzzzzzzzzzzzzzzzz = 'zzzzzzzzzzzzzzzzzzzz';" + "f($$S_$2d$2d$2d$2d$2d$2d$2d$2d$2dhi$2d$2d$2d$2d$2d$2d$2d$2d$2d);" + "h($$S_ciaociaociaociaociao + " + "$$S_$2d$2d$2d$2d$2d$2d$2d$2d_adios$2d$2d$2d$2d$2d$2d$2d);" + "(function() { alert($$S_zzzzzzzzzzzzzzzzzzzz + $$S_zzzzzzzzzzzzzzzzzzzz) })();", // m4 "var $$S_$2d$2d$2d$2d$2d$2d$2dpeaches$2d$2d$2d$2d$2d$2d" + " = '-------peaches------';" + "f($$S_$2d$2d$2d$2d$2d$2d$2d$2d$2dhi$2d$2d$2d$2d$2d$2d$2d$2d$2d);" + "alert($$S_$2d$2d$2d$2d$2d$2d$2d$2d_adios$2d$2d$2d$2d$2d$2d$2d);" + "h($$S_$2d$2d$2d$2d$2d$2d$2dpeaches$2d$2d$2d$2d$2d$2d);" + "h($$S_$2d$2d$2d$2d$2d$2d$2dpeaches$2d$2d$2d$2d$2d$2d);", }); } public void testStringsInModules2() { strings = ALL_STRINGS; // Aliases must be placed in the correct module. The alias for // '------adios------' must be lifted from m2 and m3 and go in the // common parent module m1 JSModule[] modules = createModuleBush( // m0 "function g() { alert('ciaociaociaociaociao'); }", // m1 "function h(a) {" + " alert('hhhhhhhhhhhhhhhhhhh:' + a);" + " alert('hhhhhhhhhhhhhhhhhhh:' + a);" + "}", // m2 "h('ciaociaociaociaociao' + 'adios');", // m3 "g();"); test( modules, new String[] { // m1 LINE_JOINER.join( "var $$S_ciaociaociaociaociao = 'ciaociaociaociaociao';", "function g() { alert($$S_ciaociaociaociaociao); }"), // m2 LINE_JOINER.join( "var $$S_hhhhhhhhhhhhhhhhhhh$3a = 'hhhhhhhhhhhhhhhhhhh:';", "function h(a) {" + " alert($$S_hhhhhhhhhhhhhhhhhhh$3a + a);" + " alert($$S_hhhhhhhhhhhhhhhhhhh$3a + a);" + "}"), // m3 "h($$S_ciaociaociaociaociao + 'adios');", // m4 "g();", }); } public void testAliasInCommonModuleInclusive() { strings = ALL_STRINGS; JSModule[] modules = createModuleBush( // m0 "", // m1 "function g() { alert('ciaociaociaociaociao'); }", // m2 "h('ciaociaociaociaociao' + 'adios');", // m3 "g();"); // The "ciao" string is used in m1 and m2. // Since m2 depends on m1, we should create the module there and not force it into m0. test( modules, new String[] { // m0 "", // m1 LINE_JOINER.join( "var $$S_ciaociaociaociaociao = 'ciaociaociaociaociao';", "function g() { alert($$S_ciaociaociaociaociao); }"), // m2 "h($$S_ciaociaociaociaociao + 'adios');", // m3 "g();", }); } public void testEmptyModules() { JSModule[] modules = createModuleStar( // m0 "", // m1 "function foo() { f('goodgoodgoodgoodgood') }", // m2 "function foo() { f('goodgoodgoodgoodgood') }"); test( modules, new String[] { // m0 "var $$S_goodgoodgoodgoodgood='goodgoodgoodgoodgood'", // m1 "function foo() {f($$S_goodgoodgoodgoodgood)}", // m2 "function foo() {f($$S_goodgoodgoodgoodgood)}", }); } }