/** * Test for one of the bugs causing issue 8284. JsObfuscateNamer was used to assign obfuscated * names to deduped functions and as a result it might have modified the other names that had been * assigned invalidating the irrevocable decision made by the deduper. */ public void testRerunNamerError() throws Exception { JsProgram program = new JsProgram(); // Reference to a in _.b is to the top level scope a function where the one in _.c is to the // local a definition. // // After deduping _.b and _.c the identifier a in the deduped function points to the top level // scope a function and runing a namer afterwards makes the deduping invalid. // CAVEAT: The two functions that have {return a;} as their bodies are not actually duplicates // but we use them to model functions that refer to names at different scopes. Because this // optimization only runs on JsFunctions that come from Java source this situation does not // happen. String js = "var c; function a(){return f1;}; function f1() {_.b = function() {return a;} }; " + "function f2() { var a = null; _.c = function() {return a;} };f1();f2();_.b();_.c();"; List<JsStatement> input = JsParser.parse(SourceOrigin.UNKNOWN, program.getScope(), new StringReader(js)); program.getGlobalBlock().getStatements().addAll(input); // Mark all functions as if they were translated from Java sources. setAllFromJava(program); // Get the JsNames for the top level a and the f2() scoped a. JsName topScope_a = program.getScope().findExistingName("a"); JsName f2_a = null; for (JsScope scope : program.getScope().getChildren()) { if (scope.toString().startsWith("function f2->")) { f2_a = scope.findExistingName("a"); } } assertTrue(topScope_a != f2_a); optimize(program, JsSymbolResolver.class, JsDuplicateFunctionRemoverProxy.class); // collect values assigned to some identifiers. final Map<String, JsName> assignments = AssignmentGatherer.exec(program); // If the function have been dedupped then there is a constraint that the different JsNames // they referred to are obfuscated to the same id. // Hence if _.c and _.b are collapsed the top scope name "a" and the one in f2() need to remain // the same. assertTrue( assignments.get("_.b") != assignments.get("_.c") || topScope_a.getShortIdent().equals(f2_a.getShortIdent())); }
@Override protected void visit(JsScope scope) { // Visit children. for (JsScope child : scope.getChildren()) { visit(child); } // Visit all my idents. for (JsName name : scope.getAllNames()) { if (!referenced.contains(name)) { // Don't allocate idents for non-referenced names. continue; } if (!name.isObfuscatable()) { // Unobfuscatable names become themselves. name.setShortIdent(name.getIdent()); continue; } String fullIdent = name.getIdent(); // Fixes package-info.java classes. fullIdent = fullIdent.replace("-", "_"); if (!isLegal(fullIdent)) { String checkIdent; for (int i = 0; true; ++i) { checkIdent = fullIdent + "_" + i; if (isLegal(checkIdent)) { break; } } name.setShortIdent(checkIdent); } else { // set each name's short ident to its full ident name.setShortIdent(fullIdent); } } }