/* * Copyright 2014 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.jjs.impl; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.dev.jjs.ast.JField; import com.google.gwt.dev.jjs.ast.JMethod; import com.google.gwt.dev.jjs.ast.JProgram; import com.google.gwt.thirdparty.guava.common.base.Predicate; import com.google.gwt.thirdparty.guava.common.collect.ImmutableList; /** * Tests {@link ComputePotentiallyObservableUninitializedValues}. */ public class ComputePotentiallyObservableUninitializedValuesTest extends OptimizerTestBase { private boolean runMethodInliner; private boolean runSpecializer; @Override public void setUp() throws Exception { runMethodInliner = false; runSpecializer = false; } public void testSimpleClass() throws Exception { addSnippetClassDecl( "static class A { ", " int i1 = 1;", " Integer i2 = new Integer(1);", " final int fi1 = 1;", " final Integer fi2 = new Integer(1);", "}"); JProgram program = compileSnippet("void", "return;"); assertAnalysisCorrect(program, ImmutableList.<String>of(), ImmutableList .of("EntryPoint$A.i1", "EntryPoint$A.i2", "EntryPoint$A.fi1", "EntryPoint$A.fi2")); } public void testOnlyFinalLiteralUnobservable() throws Exception { addSnippetClassDecl( "static class A { ", " A() { m(); }", " void m() { }", "}"); addSnippetClassDecl( "static class B extends A { ", " int i1 = 1;", " Integer i2 = new Integer(1);", " final int fi1 = 1;", " final Integer fi2 = new Integer(1);", "}"); JProgram program = compileSnippet("void", "return;"); assertAnalysisCorrect(program, ImmutableList.of("EntryPoint$B.i1", "EntryPoint$B.i2", "EntryPoint$B.fi2"), ImmutableList.of("EntryPoint$B.fi1")); } public void testSafeCustomInitializer() throws Exception { addSnippetClassDecl( "static class A { ", " { m(); }", " static void m() { }", "}"); addSnippetClassDecl( "static class B extends A { ", " int i1 = 1;", " Integer i2 = new Integer(1);", " final int fi1 = 1;", " final Integer fi2 = new Integer(1);", "}"); JProgram program = compileSnippet("void", "return;"); assertAnalysisCorrect(program, ImmutableList.<String>of(), ImmutableList .of("EntryPoint$B.fi1", "EntryPoint$B.i1", "EntryPoint$B.i2", "EntryPoint$B.fi2")); MakeCallsStatic.exec(program, false); assertAnalysisCorrect(program, ImmutableList.<String>of(), ImmutableList .of("EntryPoint$B.fi1", "EntryPoint$B.i1", "EntryPoint$B.i2", "EntryPoint$B.fi2")); } public void testSafeStaticCall() throws Exception { addSnippetClassDecl( "static class A { ", " A() { m(new A(), 2); }", " static void m(A a, int i) { }", "}"); addSnippetClassDecl( "static class B extends A { ", " int i1 = 1;", " Integer i2 = new Integer(1);", " final int fi1 = 1;", " final Integer fi2 = new Integer(1);", "}"); JProgram program = compileSnippet("void", "return;"); assertAnalysisCorrect(program, ImmutableList.<String>of(), ImmutableList .of("EntryPoint$B.fi1", "EntryPoint$B.i1", "EntryPoint$B.i2", "EntryPoint$B.fi2")); } public void testSafePolyCall() throws Exception { addSnippetClassDecl( "static class A { ", " A() { String s = new Integer(0).toString(); }", "}"); addSnippetClassDecl( "static class B extends A { ", " int i1 = 1;", " Integer i2 = new Integer(1);", " final int fi1 = 1;", " final Integer fi2 = new Integer(1);", "}"); JProgram program = compileSnippet("void", "return;"); assertAnalysisCorrect(program, ImmutableList.<String>of(), ImmutableList .of("EntryPoint$B.fi1", "EntryPoint$B.i1", "EntryPoint$B.i2", "EntryPoint$B.fi2")); } public void testUnSafeCustomInitializer_polymorphicDispatch() throws Exception { addSnippetClassDecl( "static class A { ", " void m() { }", " { m(); }", "}"); addSnippetClassDecl( "static class B extends A { ", " int i1 = 1;", " Integer i2 = new Integer(1);", " final int fi1 = 1;", " final Integer fi2 = new Integer(1);", "}"); JProgram program = compileSnippet("void", "return;"); assertAnalysisCorrect(program, ImmutableList.of("EntryPoint$B.i1", "EntryPoint$B.i2", "EntryPoint$B.fi2"), ImmutableList.of("EntryPoint$B.fi1")); } public void testUnSafeCustomInitializer_escapingThis() throws Exception { addSnippetClassDecl( "static class A { ", " static void m(A a) { }", " { m(this); }", "}"); addSnippetClassDecl( "static class B extends A { ", " int i1 = 1;", " Integer i2 = new Integer(1);", " final int fi1 = 1;", " final Integer fi2 = new Integer(1);", "}"); JProgram program = compileSnippet("void", "return;"); assertAnalysisCorrect(program, ImmutableList.of("EntryPoint$B.i1", "EntryPoint$B.i2", "EntryPoint$B.fi2"), ImmutableList.of("EntryPoint$B.fi1")); } public void testUnSafeCustomInitializer_escapingThisThroughArrayInitializer() throws Exception { addSnippetClassDecl( "static class A { ", " { A[] a = new A[] {this}; }", "}"); addSnippetClassDecl( "static class B extends A { ", " int i1 = 1;", " Integer i2 = new Integer(1);", " final int fi1 = 1;", " final Integer fi2 = new Integer(1);", "}"); JProgram program = compileSnippet("void", "return;"); assertAnalysisCorrect(program, ImmutableList.of("EntryPoint$B.i1", "EntryPoint$B.i2", "EntryPoint$B.fi2"), ImmutableList.of("EntryPoint$B.fi1")); } public void testUnSafeCustomInitializer_escapingDeepReference() throws Exception { addSnippetClassDecl( "static class A { ", " A() {String s = (new Integer(0).toString() + this.toString()).substring(0, 1); }", "}"); addSnippetClassDecl( "static class B extends A { ", " int i1 = 1;", " Integer i2 = new Integer(1);", " final int fi1 = 1;", " final Integer fi2 = new Integer(1);", "}"); JProgram program = compileSnippet("void", "return;"); assertAnalysisCorrect(program, ImmutableList.of("EntryPoint$B.i1", "EntryPoint$B.i2", "EntryPoint$B.fi2"), ImmutableList.of("EntryPoint$B.fi1")); MakeCallsStatic.exec(program, false); // required so that method is static assertAnalysisCorrect(program, ImmutableList.of("EntryPoint$B.i1", "EntryPoint$B.i2", "EntryPoint$B.fi2"), ImmutableList.of("EntryPoint$B.fi1")); } @Override protected boolean doOptimizeMethod(TreeLogger logger, JProgram program, JMethod method) { if (runMethodInliner) { MethodInliner.exec(program); } if (runSpecializer) { Finalizer.exec(program); // required so that method is marked final MakeCallsStatic.exec(program, false); // required so that method is static TypeTightener.exec(program); // required so that the parameter types are tightened MethodCallSpecializer.exec(program); } OptimizerStats result = DeadCodeElimination.exec(program, method); if (result.didChange()) { // Make sure we converge in one pass. // // TODO(rluble): It does not appear to be true in general unless we iterate until a // fixpoint in exec(). // // Example: // // Constructor( ) { deadcode } // m( new Constructor(); } // // If m is processed first, it will see the constructor as having side effects. // Then the constructor will become empty enabling m() become empty in the next round. // assertFalse(DeadCodeElimination.exec(program, method).didChange()); } return result.didChange(); } private void assertAnalysisCorrect(JProgram program, Iterable<String> fieldsThatCanBeObservedUninitialized, Iterable<String> fieldsThatCannotBeObservedUninitialized) { Predicate<JField> uninitializedValuesCanBeObserved = ComputePotentiallyObservableUninitializedValues.analyze(program); for (String fieldName : fieldsThatCanBeObservedUninitialized) { JField field = findField(program, fieldName); assertNotNull(field); assertTrue("Field " + fieldName + " was erroneously determined to uninitialized unobservable", uninitializedValuesCanBeObserved.apply(field)); } for (String fieldName : fieldsThatCannotBeObservedUninitialized) { JField field = findField(program, fieldName); assertNotNull(field); assertFalse("Field " + fieldName + " was erroneously determined to uninitialized observable", uninitializedValuesCanBeObserved.apply(field)); } } }