blob: abbf1d0f8f8e9318e9d3b122bddec3ebac465aad [file] [log] [blame]
/*
* 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));
}
}
}