/* * Copyright 2008 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.gflow.constants; import com.google.gwt.dev.jjs.impl.gflow.Analysis; import com.google.gwt.dev.jjs.impl.gflow.CfgAnalysisTestBase; import com.google.gwt.dev.jjs.impl.gflow.cfg.Cfg; import com.google.gwt.dev.jjs.impl.gflow.cfg.CfgEdge; import com.google.gwt.dev.jjs.impl.gflow.cfg.CfgNode; public class ConstantsAnalysisTest extends CfgAnalysisTestBase<ConstantsAssumption> { @Override protected void setUp() throws Exception { super.setUp(); addSnippetClassDecl("static int i;"); addSnippetClassDecl("static int j;"); addSnippetClassDecl("static int k;"); addSnippetClassDecl("static int l;"); addSnippetClassDecl("static int m;"); addSnippetClassDecl("static int foo() { return 0; };"); addSnippetClassDecl("static void bar(Object o) { };"); addSnippetClassDecl("static int baz(Object o) { return 0; };"); } public void testDeclWithConstInit() throws Exception { analyze("void", "int i = 1;").into( "BLOCK -> [* T]", "STMT -> [* T]", "WRITE(i, 1) -> [* {i = 1}]", "END"); } public void testDeclWithConstOps() throws Exception { analyze("void", "int i = 1 + 1;").into( "BLOCK -> [* T]", "STMT -> [* T]", "WRITE(i, 2) -> [* {i = 2}]", "END"); } public void testDeclWithNonconstInit() throws Exception { analyze("void", "int i = foo();").into( "BLOCK -> [* T]", "STMT -> [* T]", "OPTTHROW(foo()) -> [NOTHROW=* T, RE=1 T, E=1 T]", "CALL(foo) -> [* T]", "WRITE(i, EntryPoint.foo()) -> [* T]", "1: END"); } public void testReassign() throws Exception { analyze("void", "int i = 1; i = 2;").into( "BLOCK -> [* T]", "STMT -> [* T]", "WRITE(i, 1) -> [* {i = 1}]", "STMT -> [* {i = 1}]", "WRITE(i, 2) -> [* {i = 2}]", "END"); analyze("void", "int i; i = 3;").into( "BLOCK -> [* T]", "STMT -> [* T]", "STMT -> [* T]", "WRITE(i, 3) -> [* {i = 3}]", "END"); } public void test2Vars() throws Exception { analyze("void", "int i = 1; int j = 2;").into( "BLOCK -> [* T]", "STMT -> [* T]", "WRITE(i, 1) -> [* {i = 1}]", "STMT -> [* {i = 1}]", "WRITE(j, 2) -> [* {i = 1, j = 2}]", "END"); } public void testSequence() throws Exception { analyze("void", "int i = 1; int j = i; int k = j; int l = k;").into( "BLOCK -> [* T]", "STMT -> [* T]", "WRITE(i, 1) -> [* {i = 1}]", "STMT -> [* {i = 1}]", "READ(i) -> [* {i = 1}]", "WRITE(j, i) -> [* {i = 1, j = 1}]", "STMT -> [* {i = 1, j = 1}]", "READ(j) -> [* {i = 1, j = 1}]", "WRITE(k, j) -> [* {i = 1, j = 1, k = 1}]", "STMT -> [* {i = 1, j = 1, k = 1}]", "READ(k) -> [* {i = 1, j = 1, k = 1}]", "WRITE(l, k) -> [* {i = 1, j = 1, k = 1, l = 1}]", "END"); } public void testIfStatement() throws Exception { analyze("void", "int i = k; if (i == 1) { int j = i; } else { int j = i; } ").into( "BLOCK -> [* T]", "STMT -> [* T]", "READ(k) -> [* T]", "WRITE(i, EntryPoint.k) -> [* T]", "STMT -> [* T]", "READ(i) -> [* T]", "COND (i == 1) -> [THEN=* {i = 1}, ELSE=1 T]", "BLOCK -> [* {i = 1}]", "STMT -> [* {i = 1}]", "READ(i) -> [* {i = 1}]", "WRITE(j, i) -> [2 {i = 1, j = 1}]", "1: BLOCK -> [* T]", "STMT -> [* T]", "READ(i) -> [* T]", "WRITE(j, i) -> [* T]", "2: END"); analyze("int", "int j = 0; if (foo() == 1) j = 1; return j;").into( "BLOCK -> [* T]", "STMT -> [* T]", "WRITE(j, 0) -> [* {j = 0}]", "STMT -> [* {j = 0}]", "OPTTHROW(foo()) -> [NOTHROW=* {j = 0}, RE=2 {j = 0}, E=2 {j = 0}]", "CALL(foo) -> [* {j = 0}]", "COND (EntryPoint.foo() == 1) -> [THEN=* {j = 0}, ELSE=1 {j = 0}]", "STMT -> [* {j = 0}]", "WRITE(j, 1) -> [* {j = 1}]", "1: STMT -> [* T]", "READ(j) -> [* T]", "GOTO -> [* T]", "2: END"); analyze("int", "int j = 0; if (foo() == 1) j = foo(); return j;").into( "BLOCK -> [* T]", "STMT -> [* T]", "WRITE(j, 0) -> [* {j = 0}]", "STMT -> [* {j = 0}]", "OPTTHROW(foo()) -> [NOTHROW=* {j = 0}, RE=2 {j = 0}, E=2 {j = 0}]", "CALL(foo) -> [* {j = 0}]", "COND (EntryPoint.foo() == 1) -> [THEN=* {j = 0}, ELSE=1 {j = 0}]", "STMT -> [* {j = 0}]", "OPTTHROW(foo()) -> [NOTHROW=* {j = 0}, RE=2 {j = 0}, E=2 {j = 0}]", "CALL(foo) -> [* {j = 0}]", "WRITE(j, EntryPoint.foo()) -> [* T]", "1: STMT -> [* T]", "READ(j) -> [* T]", "GOTO -> [* T]", "2: END"); } public void testWhileLoop1() throws Exception { analyze("void", "int j = 1; while (j > 0) ++j;").into( "BLOCK -> [* T]", "STMT -> [* T]", "WRITE(j, 1) -> [* {j = 1}]", "STMT -> [* {j = 1}]", "1: READ(j) -> [* T]", "COND (j > 0) -> [THEN=* T, ELSE=2 T]", "STMT -> [* T]", "READWRITE(j, null) -> [1 T]", "2: END"); } public void testWhileLoop2() throws Exception { analyze("void", "int j = 0; while (j > 0) {};").into( "BLOCK -> [* T]", "STMT -> [* T]", "WRITE(j, 0) -> [* {j = 0}]", "STMT -> [* {j = 0}]", "1: READ(j) -> [* {j = 0}]", "COND (j > 0) -> [THEN=* {j = 0}, ELSE=2 {j = 0}]", "BLOCK -> [1 {j = 0}]", "2: END"); } public void testConditionalExpressions() throws Exception { analyze("void", "boolean b1 = false; boolean b2 = false; if (b1 && (b2 = true)) b1 = true;").into( "BLOCK -> [* T]", "STMT -> [* T]", "WRITE(b1, false) -> [* {b1 = false}]", "STMT -> [* {b1 = false}]", "WRITE(b2, false) -> [* {b1 = false, b2 = false}]", "STMT -> [* {b1 = false, b2 = false}]", "READ(b1) -> [* {b1 = false, b2 = false}]", "COND (b1) -> [THEN=* {b1 = false, b2 = false}, ELSE=1 {b1 = false, b2 = false}]", "WRITE(b2, true) -> [* {b1 = false, b2 = true}]", "1: COND (b1 && (b2 = true)) -> [THEN=* {b1 = false}, ELSE=2 {b1 = false}]", "STMT -> [* {b1 = false}]", "WRITE(b1, true) -> [* {b1 = true}]", "2: END"); } // Various real-world stuff public void testVariousStuff() throws Exception { addSnippetClassDecl("static Object f = null;"); analyze("void", "Object e = null;" + "if (f != null) if (e == null)" + " return;" + "boolean b = e == null;").into( "BLOCK -> [* T]", "STMT -> [* T]", "WRITE(e, null) -> [* {e = null}]", "STMT -> [* {e = null}]", "READ(f) -> [* {e = null}]", "COND (EntryPoint.f != null) -> [THEN=* {e = null}, ELSE=1 {e = null}]", "STMT -> [* {e = null}]", "READ(e) -> [* {e = null}]", "COND (e == null) -> [THEN=* {e = null}, ELSE=1 {e = null}]", "STMT -> [* {e = null}]", "GOTO -> [2 {e = null}]", "1: STMT -> [* {e = null}]", "READ(e) -> [* {e = null}]", "WRITE(b, e == null) -> [* {b = true, e = null}]", "2: END" ); } /** * Parameters should have an initial assumption of non-constant. */ public void testParamNonConstant() throws Exception { analyzeWithParams("void", "int i, int j", "if (j == 0) { i = 0; } j=i; j=0;").into( "BLOCK -> [* T]", "STMT -> [* T]", "READ(j) -> [* T]", "COND (j == 0) -> [THEN=* {j = 0}, ELSE=1 T]", "BLOCK -> [* {j = 0}]", "STMT -> [* {j = 0}]", "WRITE(i, 0) -> [* {i = 0, j = 0}]", "1: STMT -> [* T]", "READ(i) -> [* T]", "WRITE(j, i) -> [* T]", "STMT -> [* T]", "WRITE(j, 0) -> [* {j = 0}]", "END" ); } @Override protected Analysis<CfgNode<?>, CfgEdge, Cfg, ConstantsAssumption> createAnalysis() { return new ConstantsAnalysis(); } }