blob: bb24cfa954adc0b4360c0270aa38582cc9f6cbf0 [file] [log] [blame]
/*
* Copyright 2009 Google Inc.
*
* 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.gwt.dev.js;
import com.google.gwt.dev.cfg.ConfigurationProperties;
import com.google.gwt.dev.jjs.JsOutputOption;
import com.google.gwt.dev.jjs.SourceOrigin;
import com.google.gwt.dev.js.JsIncrementalNamer.JsIncrementalNamerState;
import com.google.gwt.dev.js.JsNamer.IllegalNameException;
import com.google.gwt.dev.js.ast.JsBlock;
import com.google.gwt.dev.js.ast.JsExprStmt;
import com.google.gwt.dev.js.ast.JsFunction;
import com.google.gwt.dev.js.ast.JsInvocation;
import com.google.gwt.dev.js.ast.JsName;
import com.google.gwt.dev.js.ast.JsNameRef;
import com.google.gwt.dev.js.ast.JsProgram;
import com.google.gwt.dev.js.ast.JsScope;
import com.google.gwt.dev.js.ast.JsStatement;
import com.google.gwt.dev.js.ast.JsVisitor;
import com.google.gwt.dev.util.DefaultTextOutput;
import com.google.gwt.dev.util.TextOutput;
import com.google.gwt.thirdparty.guava.common.collect.Maps;
import junit.framework.TestCase;
import java.io.IOException;
import java.io.StringReader;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* Tests the JsStaticEval optimizer.
*/
public class JsNamerTest extends TestCase {
private BlacklistProps props;
private JsIncrementalNamerState jsIncrementalNamerState;
@Override
protected void setUp() throws Exception {
super.setUp();
props = new BlacklistProps();
props.blacklist = Arrays.asList("foo, bar", "baz");
props.blacklistSuffixes = Arrays.asList("logger");
jsIncrementalNamerState = new JsIncrementalNamerState();
}
public void testBannedIdent() throws Exception {
assertEquals("function foo_0(){return 42}\n",
rename("function foo() { return 42; }"));
assertEquals("function bar_0(){return 42}\n",
rename("function bar() { return 42; }"));
assertEquals("function baz_0(){return 42}\n",
rename("function baz() { return 42; }"));
}
public void testBannedSuffix() throws Exception {
assertEquals("function fooLogger_0(){return 42}\n",
rename("function fooLogger() { return 42; }"));
assertEquals("function foologger_0(){return 42}\n",
rename("function foologger() { return 42; }"));
assertEquals("function fooLOGGER_0(){return 42}\n",
rename("function fooLOGGER() { return 42; }"));
}
public void testNoBlacklist() throws Exception {
props.blacklist = null;
props.blacklistSuffixes = null;
assertEquals("function fooLogger(){return 42}\n",
rename("function fooLogger() { return 42; }"));
}
public void testAvoidDuplicatesSameScope() throws Exception {
JsProgram program = parseJs(
"function f1(){ return 1 }\n" +
"function f2(){ return 2 }\n");
program.getScope().findExistingName("f1").setShortIdent("thing");
program.getScope().findExistingName("f2").setShortIdent("thing");
assertEquals(
"function thing(){return 1}\n" +
"function thing_0(){return 2}\n",
rename(program));
}
public void testAvoidDuplicatesChildScope() throws Exception {
JsProgram program = parseJs(
"function f1(){\n" +
" function f2(){ return 2 }\n" +
" return 1\n" +
"}\n");
program.getScope().findExistingName("f1").setShortIdent("thing");
program.getScope().getChildren().get(0).findExistingName("f2").setShortIdent("thing");
assertEquals("function thing_0(){function thing(){return 2}\nreturn 1}\n",
rename(program));
}
public void testPackageInfo() throws Exception {
// Synthesize a function definition with an illegal name, "package-info" like can result from
// JDT compilation of package-info.java files.
JsProgram jsProgram = new JsProgram();
JsScope scope = jsProgram.getScope();
// Function declaration statement.
JsName name = scope.declareName("package-info", "package-info");
List<JsStatement> statements = jsProgram.getFragment(0).getGlobalBlock().getStatements();
final SourceOrigin sourceInfo = SourceOrigin.UNKNOWN;
JsFunction function = new JsFunction(sourceInfo, scope, name);
function.setBody(new JsBlock(sourceInfo));
statements.add(new JsExprStmt(sourceInfo, function));
// Function invocation statement.
statements.add(new JsInvocation(sourceInfo, new JsNameRef(sourceInfo, name)).makeStmt());
// Verify that the illegal "-" character is translated.
assertEquals("function package_info(){}\npackage_info();",
rename(jsProgram, JsOutputOption.PRETTY, false));
assertEquals("function package_info(){}\npackage_info();",
rename(jsProgram, JsOutputOption.DETAILED, false));
}
public void testRejectsReservedSuffix() throws Exception {
// Regular renaming runs fine.
assertEquals("function foo_0_g$(){return 42}\n",
rename(parseJs("function foo() { return 42; }"), JsOutputOption.PRETTY, true));
// Renaming with the reserved suffix is rejected.
try {
String functionName = "foo" + JsIncrementalNamer.RESERVED_IDENT_SUFFIX;
JsProgram jsProgram = parseJs(
"function " + functionName + "() { return 42; }");
jsProgram.getScope().findExistingName(functionName).setUnobfuscatable();
rename(jsProgram, JsOutputOption.OBFUSCATED, true);
fail("Naming an unobfuscatable identifier containing the reserved suffix should have "
+ "thrown an exception in JsIncrementalNamer.");
} catch (IllegalNameException e) {
// Expected path.
}
}
private JsProgram parseJs(String js) throws IOException, JsParserException {
JsProgram program = new JsProgram();
List<JsStatement> expected = JsParser.parse(SourceOrigin.UNKNOWN,
program.getScope(), new StringReader(js));
program.getGlobalBlock().getStatements().addAll(expected);
return program;
}
private String rename(JsProgram program) throws IllegalNameException {
return rename(program, JsOutputOption.PRETTY, false);
}
private String rename(JsProgram program, JsOutputOption outputOption, boolean persistent)
throws IllegalNameException {
JsSymbolResolver.exec(program);
ConfigurationProperties config = props.makeConfig();
switch (outputOption) {
case PRETTY:
if (persistent) {
JsIncrementalNamer.exec(program, config, jsIncrementalNamerState, null, false);
} else {
JsPrettyNamer.exec(program, config);
}
break;
case OBFUSCATED:
if (persistent) {
JsIncrementalNamer.exec(program, config, jsIncrementalNamerState, null, true);
} else {
JsObfuscateNamer.exec(program, config);
}
break;
case DETAILED:
JsVerboseNamer.exec(program, config);
break;
}
TextOutput text = new DefaultTextOutput(true);
JsVisitor generator = new JsSourceGenerationVisitor(text);
generator.accept(program);
return text.toString();
}
private String rename(String js) throws Exception {
return rename(parseJs(js));
}
private static class BlacklistProps {
List<String> blacklist;
List<String> blacklistSuffixes;
private ConfigurationProperties makeConfig() {
Map<String, List<String>> props = Maps.newHashMap();
if (blacklist != null) {
props.put(ReservedNames.BLACKLIST, blacklist);
}
if (blacklistSuffixes != null) {
props.put(ReservedNames.BLACKLIST_SUFFIXES, blacklistSuffixes);
}
return new ConfigurationProperties(props);
}
}
}