This repository has been archived on 2023-06-18. You can view files and clone it, but cannot push or open issues or pull requests.
ima02/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/RenamePropertiesTest.java

417 lines
15 KiB
Java
Raw Permalink Normal View History

2023-04-25 11:33:41 +00:00
/*
* Copyright 2005 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.ImmutableList;
import com.google.common.collect.Lists;
/**
* {@link RenameProperties} tests.
*
*/
public class RenamePropertiesTest extends CompilerTestCase {
private static final String EXTERNS =
"var window;" +
"prop.toString;" +
"var google = { gears: { factory: {}, workerPool: {} } };";
private RenameProperties renameProperties;
private static boolean generatePseudoNames = false;
private static boolean useAffinity = false;
private VariableMap prevUsedPropertyMap = null;
public RenamePropertiesTest() {
super(EXTERNS);
enableNormalize();
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
prevUsedPropertyMap = null;
useAffinity = false;
}
@Override protected int getNumRepetitions() {
// The RenameProperties pass should only be run once over a parse tree.
return 1;
}
public void testPrototypeProperties() {
test("Bar.prototype.getA = function(){}; bar.getA();" +
"Bar.prototype.getB = function(){};",
"Bar.prototype.a = function(){}; bar.a();" +
"Bar.prototype.b = function(){}");
}
public void testPrototypePropertiesAsObjLitKeys1() {
test("Bar.prototype = {2: function(){}, getA: function(){}}; bar[2]();",
"Bar.prototype = {2: function(){}, a: function(){}}; bar[2]();");
}
public void testPrototypePropertiesAsObjLitKeys2() {
testSame("Bar.prototype = {get 2(){}}; bar[2];");
testSame("Bar.prototype = {get 'a'(){}}; bar['a'];");
test("Bar.prototype = {get getA(){}}; bar.getA;",
"Bar.prototype = {get a(){}}; bar.a;");
}
public void testPrototypePropertiesAsObjLitKeys3() {
testSame("Bar.prototype = {set 2(x){}}; bar[2];");
testSame("Bar.prototype = {set 'a'(x){}}; bar['a'];");
test("Bar.prototype = {set getA(x){}}; bar.getA;",
"Bar.prototype = {set a(x){}}; bar.a;");
}
public void testMixedQuotedAndUnquotedObjLitKeys1() {
test("Bar = {getA: function(){}, 'getB': function(){}}; bar.getA();",
"Bar = {a: function(){}, 'getB': function(){}}; bar.a();");
}
public void testMixedQuotedAndUnquotedObjLitKeys2() {
test("Bar = {getA: function(){}, 'getB': function(){}}; bar.getA();",
"Bar = {a: function(){}, 'getB': function(){}}; bar.a();");
}
public void testQuotedPrototypeProperty() {
testSame("Bar.prototype['getA'] = function(){}; bar['getA']();");
}
public void testOverlappingOriginalAndGeneratedNames() {
test("Bar.prototype = {b: function(){}, a: function(){}}; bar.b();",
"Bar.prototype = {a: function(){}, b: function(){}}; bar.a();");
}
public void testRenamePropertiesWithLeadingUnderscores() {
test("Bar.prototype = {_getA: function(){}, _b: 0}; bar._getA();",
"Bar.prototype = {a: function(){}, b: 0}; bar.a();");
}
public void testPropertyAddedToObject() {
test("var foo = {}; foo.prop = '';",
"var foo = {}; foo.a = '';");
}
public void testPropertyAddedToFunction() {
test("var foo = function(){}; foo.prop = '';",
"var foo = function(){}; foo.a = '';");
}
public void testPropertyOfObjectOfUnknownType() {
test("var foo = x(); foo.prop = '';",
"var foo = x(); foo.a = '';");
}
public void testSetPropertyOfThis() {
test("this.prop = 'bar'",
"this.a = 'bar'");
}
public void testReadPropertyOfThis() {
test("f(this.prop);",
"f(this.a);");
}
public void testObjectLiteralInLocalScope() {
test("function x() { var foo = {prop1: 'bar', prop2: 'baz'}; }",
"function x() { var foo = {a: 'bar', b: 'baz'}; }");
}
public void testIncorrectAttemptToAccessQuotedProperty() {
// The correct way to call the quoted 'getFoo' method is: bar['getFoo']().
test("Bar.prototype = {'B': 0, 'getFoo': function(){}}; bar.getFoo();",
"Bar.prototype = {'B': 0, 'getFoo': function(){}}; bar.a();");
}
public void testSetQuotedPropertyOfThis() {
testSame("this['prop'] = 'bar';");
}
public void testExternedPropertyName() {
test("Bar.prototype = {toString: function(){}, foo: 0}; bar.toString();",
"Bar.prototype = {toString: function(){}, a: 0}; bar.toString();");
}
public void testExternedPropertyNameDefinedByObjectLiteral() {
test("function x() { var foo = google.gears.factory; }",
"function x() { var foo = google.gears.factory; }");
}
public void testAvoidingConflictsBetweenQuotedAndUnquotedPropertyNames() {
test("Bar.prototype.foo = function(){}; Bar.prototype['a'] = 0; bar.foo();",
"Bar.prototype.b = function(){}; Bar.prototype['a'] = 0; bar.b();");
}
public void testSamePropertyNameQuotedAndUnquoted() {
test("Bar.prototype.prop = function(){}; y = {'prop': 0};",
"Bar.prototype.a = function(){}; y = {'prop': 0};");
}
public void testStaticAndInstanceMethodWithSameName() {
test("Bar = function(){}; Bar.getA = function(){}; " +
"Bar.prototype.getA = function(){}; Bar.getA(); bar.getA();",
"Bar = function(){}; Bar.a = function(){}; " +
"Bar.prototype.a = function(){}; Bar.a(); bar.a();");
}
public void testRenamePropertiesFunctionCall1() {
test("var foo = {myProp: 0}; f(foo[JSCompiler_renameProperty('myProp')]);",
"var foo = {a: 0}; f(foo['a']);");
}
public void testRenamePropertiesFunctionCall2() {
test("var foo = {myProp: 0}; " +
"f(JSCompiler_renameProperty('otherProp.myProp.someProp')); " +
"foo.myProp = 1; foo.theirProp = 2; foo.yourProp = 3;",
"var foo = {a: 0}; f('b.a.c'); " +
"foo.a = 1; foo.d = 2; foo.e = 3;");
}
public void testRemoveRenameFunctionStubs1() {
test("function JSCompiler_renameProperty(x) { return x; }",
"");
}
public void testRemoveRenameFunctionStubs2() {
test("function JSCompiler_renameProperty(x) { return x; }" +
"var foo = {myProp: 0}; f(foo[JSCompiler_renameProperty('myProp')]);",
"var foo = {a: 0}; f(foo['a']);");
}
public void testGeneratePseudoNames() {
generatePseudoNames = true;
test("var foo={}; foo.bar=1; foo['abc']=2",
"var foo={}; foo.$bar$=1; foo['abc']=2");
generatePseudoNames = false;
}
public void testModules() {
String module1Js = "function Bar(){} Bar.prototype.getA=function(x){};" +
"var foo;foo.getA(foo);foo.doo=foo;foo.bloo=foo;";
String module2Js = "function Far(){} Far.prototype.getB=function(x){};" +
"var too;too.getB(too);too.woo=too;too.bloo=too;";
String module3Js = "function Car(){} Car.prototype.getC=function(x){};" +
"var noo;noo.getC(noo);noo.zoo=noo;noo.cloo=noo;";
JSModule module1 = new JSModule("m1");
module1.add(SourceFile.fromCode("input1", module1Js));
JSModule module2 = new JSModule("m2");
module2.add(SourceFile.fromCode("input2", module2Js));
JSModule module3 = new JSModule("m3");
module3.add(SourceFile.fromCode("input3", module3Js));
JSModule[] modules = new JSModule[] { module1, module2, module3 };
Compiler compiler = compileModules("", modules);
Result result = compiler.getResult();
assertTrue(result.success);
assertEquals("function Bar(){}Bar.prototype.b=function(x){};" +
"var foo;foo.b(foo);foo.f=foo;foo.a=foo;",
compiler.toSource(module1));
assertEquals("function Far(){}Far.prototype.c=function(x){};" +
"var too;too.c(too);too.g=too;too.a=too;",
compiler.toSource(module2));
// Note that properties that occur most often globally get the earliest
// names. The "getC" property, which doesn't occur until module 3, is
// renamed to an earlier name in the alphabet than "woo", which appears
// in module 2, because "getC" occurs more total times across all modules.
// Might be better to give early modules the shortest names, but this is
// how the pass currently works.
assertEquals("function Car(){}Car.prototype.d=function(x){};" +
"var noo;noo.d(noo);noo.h=noo;noo.e=noo;",
compiler.toSource(module3));
}
public void testPropertyAffinity() {
// 'y' gets to be 'b' because it appears with z often.
// Other wise, 'x' gets to be 'b' because of alphabetical ordering.
useAffinity = true;
test("var foo={};foo.x=1;foo.y=2;foo.z=3;" +
"function f1() { foo.z; foo.z; foo.z; foo.y}" +
"function f2() { foo.x}",
"var foo={};foo.c=1;foo.b=2;foo.a=3;" +
"function f1() { foo.a; foo.a; foo.a; foo.b}" +
"function f2() { foo.c}");
test("var foo={};foo.x=1;foo.y=2;foo.z=3;" +
"function f1() { foo.z; foo.z; foo.z; foo.y}" +
"function f2() { foo.z; foo.z; foo.z; foo.x}",
"var foo={};foo.b=1;foo.c=2;foo.a=3;" +
"function f1() { foo.a; foo.a; foo.a; foo.c}" +
"function f2() { foo.a; foo.a; foo.a; foo.b}");
}
public void testPropertyAffinityOff() {
useAffinity = false;
test("var foo={};foo.x=1;foo.y=2;foo.z=3;" +
"function f1() { foo.z; foo.z; foo.z; foo.y}" +
"function f2() { foo.x}",
"var foo={};foo.b=1;foo.c=2;foo.a=3;" +
"function f1() { foo.a; foo.a; foo.a; foo.c}" +
"function f2() { foo.b}");
test("var foo={};foo.x=1;foo.y=2;foo.z=3;" +
"function f1() { foo.z; foo.z; foo.z; foo.y}" +
"function f2() { foo.z; foo.z; foo.z; foo.x}",
"var foo={};foo.b=1;foo.c=2;foo.a=3;" +
"function f1() { foo.a; foo.a; foo.a; foo.c}" +
"function f2() { foo.a; foo.a; foo.a; foo.b}");
}
public void testPrototypePropertiesStable() {
testStableRenaming(
"Bar.prototype.getA = function(){}; bar.getA();" +
"Bar.prototype.getB = function(){};",
"Bar.prototype.a = function(){}; bar.a();" +
"Bar.prototype.b = function(){}",
"Bar.prototype.get = function(){}; bar.get();" +
"Bar.prototype.getA = function(){}; bar.getA();" +
"Bar.prototype.getB = function(){};",
"Bar.prototype.c = function(){}; bar.c();" +
"Bar.prototype.a = function(){}; bar.a();" +
"Bar.prototype.b = function(){}");
}
public void testPrototypePropertiesAsObjLitKeysStable() {
testStableRenaming(
"Bar.prototype = {2: function(){}, getA: function(){}}; bar[2]();",
"Bar.prototype = {2: function(){}, a: function(){}}; bar[2]();",
"Bar.prototype = {getB: function(){},getA: function(){}}; bar.getB();",
"Bar.prototype = {b: function(){},a: function(){}}; bar.b();");
}
public void testMixedQuotedAndUnquotedObjLitKeysStable() {
testStableRenaming(
"Bar = {getA: function(){}, 'getB': function(){}}; bar.getA();",
"Bar = {a: function(){}, 'getB': function(){}}; bar.a();",
"Bar = {get: function(){}, getA: function(){}, 'getB': function(){}};" +
"bar.getA();bar.get();",
"Bar = {b: function(){}, a: function(){}, 'getB': function(){}};" +
"bar.a();bar.b();");
}
public void testOverlappingOriginalAndGeneratedNamesStable() {
testStableRenaming(
"Bar.prototype = {b: function(){}, a: function(){}}; bar.b();",
"Bar.prototype = {a: function(){}, b: function(){}}; bar.a();",
"Bar.prototype = {c: function(){}, b: function(){}, a: function(){}};" +
"bar.b();",
"Bar.prototype = {c: function(){}, a: function(){}, b: function(){}};" +
"bar.a();");
}
public void testStableWithTrickyExternsChanges() {
test("Bar.prototype = {b: function(){}, a: function(){}}; bar.b();",
"Bar.prototype = {a: function(){}, b: function(){}}; bar.a();");
prevUsedPropertyMap = renameProperties.getPropertyMap();
String externs = EXTERNS + "prop.b;";
test(externs,
"Bar.prototype = {new_f: function(){}, b: function(){}, " +
"a: function(){}};bar.b();",
"Bar.prototype = {c:function(){}, b:function(){}, a:function(){}};" +
"bar.b();", null, null);
}
public void testRenamePropertiesWithLeadingUnderscoresStable() {
testStableRenaming(
"Bar.prototype = {_getA: function(){}, _b: 0}; bar._getA();",
"Bar.prototype = {a: function(){}, b: 0}; bar.a();",
"Bar.prototype = {_getA: function(){}, _c: 1, _b: 0}; bar._getA();",
"Bar.prototype = {a: function(){}, c: 1, b: 0}; bar.a();");
}
public void testPropertyAddedToObjectStable() {
testStableRenaming("var foo = {}; foo.prop = '';",
"var foo = {}; foo.a = '';",
"var foo = {}; foo.prop = ''; foo.a='';",
"var foo = {}; foo.a = ''; foo.b='';");
}
public void testAvoidingConflictsBetQuotedAndUnquotedPropertyNamesStable() {
testStableRenaming(
"Bar.prototype.foo = function(){}; Bar.prototype['b'] = 0; bar.foo();",
"Bar.prototype.a = function(){}; Bar.prototype['b'] = 0; bar.a();",
"Bar.prototype.foo = function(){}; Bar.prototype['a'] = 0; bar.foo();",
"Bar.prototype.b = function(){}; Bar.prototype['a'] = 0; bar.b();");
}
public void testRenamePropertiesFunctionCallStable() {
testStableRenaming(
"var foo = {myProp: 0}; " +
"f(JSCompiler_renameProperty('otherProp.myProp.someProp')); " +
"foo.myProp = 1; foo.theirProp = 2; foo.yourProp = 3;",
"var foo = {a: 0}; f('b.a.c'); " +
"foo.a = 1; foo.d = 2; foo.e = 3;",
"var bar = {newProp: 0}; var foo = {myProp: 0}; " +
"f(JSCompiler_renameProperty('otherProp.myProp.someProp')); " +
"foo.myProp = 1; foo.theirProp = 2; foo.yourProp = 3;",
"var bar = {f: 0}; var foo = {a: 0}; f('b.a.c'); " +
"foo.a = 1; foo.d = 2; foo.e = 3;");
}
private void testStableRenaming(String input1, String expected1,
String input2, String expected2) {
test(input1, expected1);
prevUsedPropertyMap = renameProperties.getPropertyMap();
test(input2, expected2);
}
private Compiler compileModules(String externs, JSModule[] modules) {
SourceFile externsInput = SourceFile.fromCode("externs", externs);
CompilerOptions options = new CompilerOptions();
options.propertyRenaming = PropertyRenamingPolicy.ALL_UNQUOTED;
Compiler compiler = new Compiler();
compiler.compileModules(
ImmutableList.of(externsInput), Lists.newArrayList(modules), options);
return compiler;
}
@Override
public CompilerPass getProcessor(Compiler compiler) {
return renameProperties =
new RenameProperties(compiler, useAffinity, generatePseudoNames,
prevUsedPropertyMap);
}
}