blob: 13c9feed87a93855ce8dca2a03b1f7080805fd30 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2016 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Stephan Herrmann <stephan@cs.tu-berlin.de> - Contributions for
* bug 320170
* bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
* bug 386181 - [compiler][null] wrong transition in UnconditionalFlowInfo.mergedWith()
* bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation.
* Bug 453635 - [compiler][null] Update NullReferenceImplTests and friends
*******************************************************************************/
package org.eclipse.jdt.core.tests.compiler.regression;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import junit.framework.AssertionFailedError;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.eclipse.jdt.core.tests.compiler.regression.NullReferenceImplTests.State;
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
import org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo;
import org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo.AssertionFailedException;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
/**
* A tests series especially meant to validate the internals of our null
* reference analysis. See NullReferenceTest for tests targetted at
* the source code compiler behavior level.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public class NullReferenceImplTests extends NullReferenceTest {
// Static initializer to specify tests subset using TESTS_* static variables
// All specified tests which does not belong to the class are skipped...
// Only the highest compliance level is run; add the VM argument
// -Dcompliance=1.4 (for example) to lower it if needed
static {
// TESTS_NAMES = new String[] { "test2050" };
// TESTS_NUMBERS = new int[] { 2061 };
// TESTS_NUMBERS = new int[] { 2999 };
// TESTS_RANGE = new int[] { 2050, -1 };
}
/**
* A class to hold states as seen by the low level validation tests and machinery.
* State provides:
* - singletons for all possible states given the number of bits for the said
* states;
* - semantic names for known states;
* - printable representation of states as bit fields;
* - coordination with other classes to perform transitive closure analysis, etc.
*/
/*
This is a tabular definition for states. It can be completed/leveraged by
the Generator class so as to smoothen the transition between differing encodings
of the states.
// STATES DEFINITION START
000000 start
000001
000010
000011
000100 pot. unknown
000101
000110
000111
001000 pot. non null
001001
001010
001011
001100 pot. nn & pot. un
001101
001110
001111
010000 pot. null
010001
010010
010011
010100 pot. n & pot. un
010101
010110
010111
011000 pot. n & pot. nn
011001
011010
011011
011100 pot. n & pot. nn & pot. un
011101
011110
011111
100000
100001
100010
100011
100100 def. unknown
100101
100110
100111
101000 def. non null
101001
101010
101011
101100 pot. nn & prot. nn
101101
101110
101111
110000 def. null
110001
110010
110011
110100 pot. n & prot. n
110101
110110
110111
111000 prot. null
111001
111010
111011
111100 prot. non null
111101
111110
111111
// STATES DEFINITION END
*/
public static class State implements Comparable {
// PREMATURE consider moving initialization to test setup/dispose
public final static State[] states = {
// STATES INITIALIZER START
new State(0, "start"), // 000000
new State(1), // 000001
new State(2), // 000010
new State(3), // 000011
new State(4, "pot. unknown"), // 000100
new State(5), // 000101
new State(6), // 000110
new State(7), // 000111
new State(8, "pot. non null"), // 001000
new State(9), // 001001
new State(10), // 001010
new State(11), // 001011
new State(12, "pot. nn & pot. un"), // 001100
new State(13), // 001101
new State(14), // 001110
new State(15), // 001111
new State(16, "pot. null"), // 010000
new State(17), // 010001
new State(18), // 010010
new State(19), // 010011
new State(20, "pot. n & pot. un"), // 010100
new State(21), // 010101
new State(22), // 010110
new State(23), // 010111
new State(24, "pot. n & pot. nn"), // 011000
new State(25), // 011001
new State(26), // 011010
new State(27), // 011011
new State(28, "pot. n & pot. nn & pot. un"), // 011100
new State(29), // 011101
new State(30), // 011110
new State(31), // 011111
new State(32), // 100000
new State(33), // 100001
new State(34), // 100010
new State(35), // 100011
new State(36, "def. unknown"), // 100100
new State(37), // 100101
new State(38), // 100110
new State(39), // 100111
new State(40, "def. non null"), // 101000
new State(41), // 101001
new State(42), // 101010
new State(43), // 101011
new State(44, "pot. nn & prot. nn"), // 101100
new State(45), // 101101
new State(46), // 101110
new State(47), // 101111
new State(48, "def. null"), // 110000
new State(49), // 110001
new State(50), // 110010
new State(51), // 110011
new State(52, "pot. n & prot. n"), // 110100
new State(53), // 110101
new State(54), // 110110
new State(55), // 110111
new State(56, "prot. null"), // 111000
new State(57), // 111001
new State(58), // 111010
new State(59), // 111011
new State(60, "prot. non null"), // 111100
new State(61), // 111101
new State(62), // 111110
new State(63), // 111111
// STATES INITIALIZER END
};
public final static State start = states[0];
public static final int
stateMaxValue = 0x3F,
stateWidth = 6,
statesNb = stateMaxValue + 1;
String name, printableBitsField, hexString;
public byte value;
boolean symbolic;
private State() {
}
private State(int numericValue) {
this(numericValue, null);
}
private State(int numericValue, String publicName) {
if (numericValue > stateMaxValue) {
throw new IllegalArgumentException("state value overflow");
}
this.value = (byte) numericValue;
StringBuffer printableValue = new StringBuffer(6);
for (int i = stateWidth - 1; i >= 0; i--) {
printableValue.append((numericValue >>> i & 1) != 0 ? '1' : '0');
}
this.printableBitsField = printableValue.toString();
if (this.value > 0xF) {
this.hexString = "0x" + Integer.toHexString(this.value).toUpperCase();
}
else {
this.hexString = "0x0" + Integer.toHexString(this.value).toUpperCase();
}
if (publicName != null) {
this.name = publicName;
this.symbolic = true;
}
else {
this.name = this.printableBitsField;
}
}
private State(String commentLine) {
char current = ' '; // keep the initialization status quiet
int cursor, length;
for (cursor = 0, length = commentLine.length();
cursor < length;
cursor++) {
if ((current = commentLine.charAt(cursor)) == '0' ||
current == '1') {
break;
}
}
if (cursor == length) {
throw new RuntimeException("bad state definition format (missing bits field): " + commentLine);
// PREMATURE adopt consistent error policy
}
int valueDigits;
for (valueDigits = 1; cursor < (length - 1) && valueDigits < stateWidth; valueDigits++) {
this.value = (byte) ((this.value << 1) + (current - '0'));
if ((current = commentLine.charAt(++cursor)) != '0' &&
current != '1') {
throw new RuntimeException("bad state definition format (inappropriate character in bits field): " + commentLine);
// PREMATURE adopt consistent error policy
}
}
if (valueDigits < stateWidth) {
throw new RuntimeException("bad state definition format (bits field is too short): " + commentLine);
// PREMATURE adopt consistent error policy
}
this.value = (byte) ((this.value << 1) + (current - '0'));
this.printableBitsField = commentLine.substring(cursor - stateWidth + 1, cursor + 1);
if (this.value > 0xF) {
this.hexString = "0x" + Integer.toHexString(this.value).toUpperCase();
}
else {
this.hexString = "0x0" + Integer.toHexString(this.value).toUpperCase();
}
while (++cursor < length && Character.isWhitespace(current = commentLine.charAt(++cursor)) && current != '\n') {
// loop
}
if (cursor < length && current != '\n') {
this.name = commentLine.substring(cursor, length);
}
if (this.name == null) {
this.name = this.printableBitsField;
} else {
this.symbolic = true;
}
}
private String asInitializer() {
StringBuffer result = new StringBuffer(70);
result.append(" new State(");
result.append(this.value);
char first;
boolean nameIsSymbolic = (first = this.name.charAt(0)) != '0'
&& first != '1';
if (nameIsSymbolic) {
result.append(", \"");
result.append(this.name);
result.append('"');
}
result.append("), // ");
result.append(this.printableBitsField);
return result.toString();
}
long [] asLongArray() {
long[] result = new long[stateWidth];
for (int i = 0; i < stateWidth; i++) {
result[i] = ((this.value >> (stateWidth - i - 1)) & 1) == 0 ? 0 : 1;
}
return result;
}
private String asSourceComment() {
StringBuffer result = new StringBuffer(70);
result.append("\t\t");
result.append(this.printableBitsField);
char first;
boolean nameIsSymbolic = (first = this.name.charAt(0)) != '0'
&& first != '1';
if (nameIsSymbolic) {
result.append('\t');
result.append(this.name);
}
return result.toString();
}
public int compareTo(Object o) {
return this.value - ((State) o).value;
}
static State fromLongValues(long bit1, long bit2, long bit3, long bit4, long bit5, long bit6) {
// PREMATURE consider taking an UnconditionalFlowInfo in parameter
return states[(int)(
(bit6 & 1) +
2 * ((bit5 & 1) +
2 * ((bit4 & 1) +
2 * ((bit3 & 1) +
2 * ((bit2 & 1) +
2 * (bit1 & 1))))))];
}
private static Map namesIndex;
static State fromSymbolicName (String name) {
if (namesIndex == null) {
namesIndex = new HashMap(states.length);
for (int i = 0; i < states.length; i++) {
if (states[i].name != null) {
namesIndex.put(states[i].name, states[i]);
}
}
}
return (State) namesIndex.get(name);
}
private static void grabDefinitionFromComment(BufferedReader input) {
String line;
State current;
// use when the initializer is incomplete, hence needs to be reinitialized
// states = new State[stateMaxValue + 1];
// use when the states field is final, with the appropriate size:
for (int i = 0; i <= stateMaxValue; i++) {
states[i] = null;
}
try {
while ((line = input.readLine()) != null && line.indexOf(definitionEndMarker) == -1) {
current = new State(line);
if (states[current.value] != null) {
throw new RuntimeException("duplicate state for index: " + current.value);
}
else {
states[current.value] = current;
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
for (int i = 0; i < stateMaxValue; i++) {
if (states[i] == null) {
states[i] = new State(i);
}
}
}
// PREMATURE may decide to remove
//private static void printAsInitializer() {
// int i, length;
// System.out.println(initializerStartMarker);
// for (i = 0, length = states.length; i < length; i++) {
// System.out.println(states[i].asInitializer());
// }
// for (/* continue */; i <= stateMaxValue; i++) {
// System.out.println((new State(i)).asInitializer() + " CHECK");
// }
// System.out.println(initializerEndMarker);
//}
// PREMATURE may decide to remove
//private static void printAsSourceComment() {
// int i, length;
// System.out.println("/*");
// System.out.println(definitionStartMarker);
// for (i = 0, length = states.length; i < length; i++) {
// System.out.println(states[i].asSourceComment());
// }
// for (/* continue */; i <= stateMaxValue; i++) {
// System.out.println((new State(i)).asSourceComment());
// }
// System.out.println(definitionEndMarker);
// System.out.println("*/");
//}
private final static String
definitionStartMarker = "// STATES " + CodeAnalysis.definitionStartMarker,
definitionEndMarker = "// STATES " + CodeAnalysis.definitionEndMarker,
initializerStartMarker = "// STATES " + CodeAnalysis.initializerStartMarker,
initializerEndMarker = "// STATES " + CodeAnalysis.initializerEndMarker;
static void reinitializeFromComment(BufferedReader input, BufferedWriter output) {
String line, tab = "";
int cursor;
char c;
try {
while ((line = input.readLine()) != null) {
output.write(line);
output.write('\n');
if ((cursor = line.indexOf(definitionStartMarker)) != -1) {
// check the line format
boolean reachedStart = true;
for (int i = 0; i < cursor; i++) {
if (!Character.isWhitespace(c = line.charAt(i))) {
reachedStart = false;
break;
}
else {
tab += c;
}
}
if (reachedStart) {
grabDefinitionFromComment(input); // consumes up to the END line
int i, length;
for (i = 0, length = states.length; i < length; i++) {
output.write(states[i].asSourceComment());
output.write('\n');
}
output.write(tab + definitionEndMarker + "\n");
}
}
if ((cursor = line.indexOf(initializerStartMarker)) != -1) {
// check the line format
boolean reachedStart = true;
tab = "";
for (int i = 0; i < cursor; i++) {
if (!Character.isWhitespace(c = line.charAt(i))) {
reachedStart = false;
break;
}
else {
tab += c;
}
}
if (reachedStart) {
while ((line = input.readLine()) != null &&
line.indexOf(initializerEndMarker) == -1) {
// loop
}
int i, length;
for (i = 0, length = states.length; i < length; i++) {
output.write(states[i].asInitializer());
output.write('\n');
}
output.write(tab + initializerEndMarker + "\n");
}
}
}
output.flush();
namesIndex = null;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
static Iterator symbolicStates() {
return new Iterator() {
int nextSymbolic = -1;
public boolean hasNext() {
if (this.nextSymbolic == -1) {
for (this.nextSymbolic = 0; this.nextSymbolic < states.length; this.nextSymbolic++) {
if (states[this.nextSymbolic].symbolic) {
break;
}
}
} else {
for (; this.nextSymbolic < states.length; this.nextSymbolic++) {
if (states[this.nextSymbolic].symbolic) {
break;
}
}
}
return this.nextSymbolic < states.length;
}
public Object next() {
State result = null;
if (this.nextSymbolic < states.length) {
result = states[this.nextSymbolic];
this.nextSymbolic++;
}
return result;
}
public void remove() {
throw new RuntimeException("unimplemented");
}
};
}
public String toString() {
return this.name;
}
public boolean equals(Object other) {
return (other instanceof State) && ((State)other).value == this.value;
}
public int hashCode() {
return this.value;
}
}
public NullReferenceImplTests(String name) {
super(name);
}
// Tests tuning
// private static final boolean skipHighOrderBits = false; // define to true when tuning encoding
private static final int COMBINATION_TESTS_LOOP_NB = 1; // define to 10000s to measure performances
private static final boolean MEASURE_PERFORMANCES = COMBINATION_TESTS_LOOP_NB > 1;
public static Test suite() {
// we do not want to run for 1.3, 1.4, 1.5 but once only
Class clazz = testClass();
TestSuite all = new TestSuite(clazz.getName());
List tests = buildTestsList(testClass());
for (int i = 0, length = tests.size(); i < length; i++) {
all.addTest((Test) tests.get(i));
}
return all;
}
public static Class testClass() {
return NullReferenceImplTests.class;
}
public void test2050_markAsComparedEqualToNonNull() {
int failures = NullReferenceImplTransformations.markAsComparedEqualToNonNull.test();
assertTrue("nb of failures: " + failures, failures == 0);
}
public void test2051_markAsComparedEqualToNull() {
int failures = NullReferenceImplTransformations.markAsComparedEqualToNull.test();
assertTrue("nb of failures: " + failures, failures == 0);
}
public void test2055_markAsDefinitelyNonNull() {
int failures = NullReferenceImplTransformations.markAsDefinitelyNonNull.test();
assertTrue("nb of failures: " + failures, failures == 0);
}
public void test2056_markAsDefinitelyNull() {
int failures = NullReferenceImplTransformations.markAsDefinitelyNull.test();
assertTrue("nb of failures: " + failures, failures == 0);
}
public void test2057_markAsDefinitelyUnknown() {
int failures = NullReferenceImplTransformations.markAsDefinitelyUnknown.test();
assertTrue("nb of failures: " + failures, failures == 0);
}
public void test2060_addInitializationsFrom() {
int failures = NullReferenceImplTransformations.addInitializationsFrom.test();
assertTrue("nb of failures: " + failures, failures == 0);
}
public void test2061_addPotentialInitializationsFrom() {
int failures = NullReferenceImplTransformations.addPotentialInitializationsFrom.test();
assertTrue("nb of failures: " + failures, failures == 0);
}
public void test2062_mergedWith() {
int failures = NullReferenceImplTransformations.mergedWith.test();
assertTrue("nb of failures: " + failures, failures == 0);
}
// PREMATURE rewrite from scratch
//public void _test2058_recode() {
// long [][][] testData = transitionsTablesData[recode];
// int failures = 0;
// long start;
// if (combinationTestsloopsNb > 1) {
// start = System.currentTimeMillis();
// }
// String header = "recode failures: ";
// for (int l = 0; l < combinationTestsloopsNb ; l++) {
// for (int i = 0; i < testData.length; i++) {
// UnconditionalFlowInfoTestHarness result;
// result = UnconditionalFlowInfoTestHarness.
// testUnconditionalFlowInfo(testData[i][0]);
// result.encode();
// result.decode();
//
// if (!result.testEquals(UnconditionalFlowInfoTestHarness.
// testUnconditionalFlowInfo(testData[i][0]))) {
// if (failures == 0) {
// System.out.println(header);
// }
// failures++;
// System.out.println("\t\t{" + result.testString() +
// "}, // instead of: " + testStringValueOf(testData[i][0]));
// }
// }
// }
// if (combinationTestsloopsNb > 1) {
// System.out.println("mergedWith\t\t\t" + combinationTestsloopsNb + "\t" +
// (System.currentTimeMillis() - start));
// }
// for (int i = 0; i < testData.length; i++) {
// UnconditionalFlowInfoTestHarness result;
// result = UnconditionalFlowInfoTestHarness.
// testUnconditionalFlowInfo(testData[i][0], 64);
// result.encode();
// result.decode();
//
// if (!result.testEquals(UnconditionalFlowInfoTestHarness.
// testUnconditionalFlowInfo(testData[i][0], 64))) {
// if (failures == 0) {
// System.out.println(header);
// }
// failures++;
// System.out.println("\t\t{" + result.testString() +
// "}, // (64) - instead of: " + testStringValueOf(testData[i][0]));
// }
// }
// assertTrue("nb of failures: " + failures, failures == 0);
//}
public void test2400_state_consistency() {
int failures = 0;
long start;
if (MEASURE_PERFORMANCES) {
start = System.currentTimeMillis();
}
String header = "state consistency failures: ";
for (int l = 0; l < COMBINATION_TESTS_LOOP_NB ; l++) {
for (int i = 0; i < State.states.length; i++) {
if (State.states[i].symbolic) {
UnconditionalFlowInfoTestHarness
state = UnconditionalFlowInfoTestHarness.
testUnconditionalFlowInfo(State.states[i]);
boolean
isDefinitelyNonNull = state.isDefinitelyNonNull(TestLocalVariableBinding.local0),
isDefinitelyNull = state.isDefinitelyNull(TestLocalVariableBinding.local0),
isDefinitelyUnknown = state.isDefinitelyUnknown(TestLocalVariableBinding.local0),
isPotentiallyNonNull = state.isPotentiallyNonNull(TestLocalVariableBinding.local0),
isPotentiallyNull = state.isPotentiallyNull(TestLocalVariableBinding.local0),
isPotentiallyUnknown = state.isPotentiallyUnknown(TestLocalVariableBinding.local0),
isProtectedNonNull = state.isProtectedNonNull(TestLocalVariableBinding.local0),
isProtectedNull = state.isProtectedNull(TestLocalVariableBinding.local0),
cannotBeDefinitelyNullOrNonNull = state.cannotBeDefinitelyNullOrNonNull(TestLocalVariableBinding.local0),
cannotBeNull = state.cannotBeNull(TestLocalVariableBinding.local0),
canOnlyBeNull = state.canOnlyBeNull(TestLocalVariableBinding.local0);
if (isDefinitelyNonNull
&& (isDefinitelyNull || isDefinitelyUnknown
|| isPotentiallyNull
|| isProtectedNull)) {
if (failures == 0) {
System.out.println(header);
}
failures++;
System.out.println("\t\tconsistency breakage for definitely non null state " + State.states[i].name);
}
if (isDefinitelyNull
&& (isDefinitelyNonNull || isDefinitelyUnknown
|| isPotentiallyUnknown || isProtectedNonNull)) {
if (failures == 0) {
System.out.println(header);
}
failures++;
System.out.println("\t\tconsistency breakage for definitely null state " + State.states[i].name);
}
if (isDefinitelyUnknown
&& (isDefinitelyNonNull || isDefinitelyNull
|| isPotentiallyNull || isProtectedNonNull
|| isProtectedNull)) {
if (failures == 0) {
System.out.println(header);
}
failures++;
System.out.println("\t\tconsistency breakage for definitely unknown state " + State.states[i].name);
}
if (isProtectedNonNull && !isDefinitelyNonNull
|| isProtectedNull && !isDefinitelyNull
|| i > 0 // not start
&& !State.states[i].name.equals("pot. non null")
&& !(isDefinitelyNonNull || isDefinitelyNull
|| isDefinitelyUnknown || isPotentiallyNull
|| isPotentiallyUnknown || isProtectedNonNull
|| isProtectedNull)
|| cannotBeDefinitelyNullOrNonNull !=
(isPotentiallyUnknown ||
isPotentiallyNull && isPotentiallyNonNull)
|| cannotBeNull != (isProtectedNonNull ||
isDefinitelyNonNull)
|| canOnlyBeNull != (isProtectedNull ||
isDefinitelyNull)) {
if (failures == 0) {
System.out.println(header);
}
failures++;
System.out.println("\t\tconsistency breakage for " + State.states[i].name);
}
}
}
}
if (MEASURE_PERFORMANCES) {
System.out.println("mergedWith\t\t\t" + COMBINATION_TESTS_LOOP_NB + "\t" +
(System.currentTimeMillis() - start));
}
for (int i = 0; i < State.states.length; i++) {
if (State.states[i].symbolic) {
UnconditionalFlowInfoTestHarness state;
state = UnconditionalFlowInfoTestHarness.
testUnconditionalFlowInfo(State.states[i], 64);
boolean
isDefinitelyNonNull = state.isDefinitelyNonNull(TestLocalVariableBinding.local64),
isDefinitelyNull = state.isDefinitelyNull(TestLocalVariableBinding.local64),
isDefinitelyUnknown = state.isDefinitelyUnknown(TestLocalVariableBinding.local64),
isPotentiallyNonNull = state.isPotentiallyNonNull(TestLocalVariableBinding.local64),
isPotentiallyNull = state.isPotentiallyNull(TestLocalVariableBinding.local64),
isPotentiallyUnknown = state.isPotentiallyUnknown(TestLocalVariableBinding.local64),
isProtectedNonNull = state.isProtectedNonNull(TestLocalVariableBinding.local64),
isProtectedNull = state.isProtectedNull(TestLocalVariableBinding.local64),
cannotBeDefinitelyNullOrNonNull = state.cannotBeDefinitelyNullOrNonNull(TestLocalVariableBinding.local64),
cannotBeNull = state.cannotBeNull(TestLocalVariableBinding.local64),
canOnlyBeNull = state.canOnlyBeNull(TestLocalVariableBinding.local64);
if (isDefinitelyNonNull
&& (isDefinitelyNull || isDefinitelyUnknown
|| isPotentiallyNull
|| isProtectedNull)) {
if (failures == 0) {
System.out.println(header);
}
failures++;
System.out.println("\t\tconsistency breakage (64) for definitely non null state " + State.states[i].name);
}
if (isDefinitelyNull
&& (isDefinitelyNonNull || isDefinitelyUnknown
|| isPotentiallyUnknown || isProtectedNonNull)) {
if (failures == 0) {
System.out.println(header);
}
failures++;
System.out.println("\t\tconsistency breakage (64) for definitely null state " + State.states[i].name);
}
if (isDefinitelyUnknown
&& (isDefinitelyNonNull || isDefinitelyNull
|| isPotentiallyNull || isProtectedNonNull
|| isProtectedNull)) {
if (failures == 0) {
System.out.println(header);
}
failures++;
System.out.println("\t\tconsistency breakage (64) for definitely unknown state " + State.states[i].name);
}
if (isProtectedNonNull && !isDefinitelyNonNull
|| isProtectedNull && !isDefinitelyNull
|| i > 0 // not start
&& !State.states[i].name.equals("pot. non null")
&& !(isDefinitelyNonNull || isDefinitelyNull
|| isDefinitelyUnknown || isPotentiallyNull
|| isPotentiallyUnknown || isProtectedNonNull
|| isProtectedNull)
|| cannotBeDefinitelyNullOrNonNull !=
(isPotentiallyUnknown ||
isPotentiallyNull &&
isPotentiallyNonNull)
|| cannotBeNull != (isProtectedNonNull ||
isDefinitelyNonNull)
|| canOnlyBeNull != (isProtectedNull ||
isDefinitelyNull)) {
if (failures == 0) {
System.out.println(header);
}
failures++;
System.out.println("\t\tconsistency breakage (64) for " + State.states[i].name);
}
}
}
assertTrue("nb of failures: " + failures, failures == 0);
}
public void test2500_addInitializationsFrom_for_definites() {
// when an added initialization is a def. something, it should
// affect the left hand term as the markAsDefinite* method would
// do
int failures = 0;
for (int i = 0; i < State.states.length; i++) {
if (State.states[i].symbolic) {
UnconditionalFlowInfoTestHarness source1, source2, result1, result2;
source1 = UnconditionalFlowInfoTestHarness.
testUnconditionalFlowInfo(State.states[i]);
for (int j = 0; j < State.states.length; j++) {
if (State.states[j].symbolic) {
source2 = UnconditionalFlowInfoTestHarness.
testUnconditionalFlowInfo(State.states[j]);
result1 = (UnconditionalFlowInfoTestHarness) source1.copy();
result2 = (UnconditionalFlowInfoTestHarness) source1.copy();
if (source2.isDefinitelyNonNull(TestLocalVariableBinding.local0)) {
if (! source2.isProtectedNonNull(TestLocalVariableBinding.local0)) {
result1.markAsDefinitelyNonNull(TestLocalVariableBinding.local0);
} else {
continue;
}
}
else if (source2.isDefinitelyNull(TestLocalVariableBinding.local0)) {
if (! source2.isProtectedNull(TestLocalVariableBinding.local0)) {
result1.markAsDefinitelyNull(TestLocalVariableBinding.local0);
} else {
continue;
}
}
else if (source2.isDefinitelyUnknown(TestLocalVariableBinding.local0)) {
result1.markAsDefinitelyUnknown(TestLocalVariableBinding.local0);
}
else if (source2.nullBit1 != 0) {
if (failures == 0) {
System.out.println("addInitializationsFrom_for_definites failures: "); //$NON-NLS-1$
}
failures++;
System.out.println("\t\t" + State.states[j].name +
" should answer true to at least one isDefinite* query");
// PREMATURE move to specific queries test case
}
else {
continue;
}
result2.addInitializationsFrom(source2);
if (!result1.testEquals(result2)) {
if (failures == 0) {
System.out.println("addInitializationsFrom_for_definites failures: "); //$NON-NLS-1$
}
failures++;
System.out.println("\t\t" + State.states[i].name +
" + " + State.states[j].name +
" => " + result2.asState().name +
" instead of: " + result1.asState().name);
}
}
}
}
}
assertTrue("nb of failures: " + failures, failures == 0);
}
// Use for coverage tests only. Needs specific instrumentation of code,
// that is controled by UnconditionalFlowInfo#coverageTestFlag.
// Note: coverage tests tend to fill the console with messages, and the
// instrumented code is slower, so never release code with active
// coverage tests.
private static int coveragePointsNb = 45;
// PREMATURE reactivate coverage tests
// Coverage by state transition tables methods.
public void test2998_coverage() {
if (UnconditionalFlowInfo.COVERAGE_TEST_FLAG) {
// sanity check: need to be sure that the tests execute properly when not
// trying to check coverage
UnconditionalFlowInfo.CoverageTestId = 0;
test0053_array();
test0070_type_reference();
test2050_markAsComparedEqualToNonNull();
test2051_markAsComparedEqualToNull();
test2055_markAsDefinitelyNonNull();
test2056_markAsDefinitelyNull();
test2057_markAsDefinitelyUnknown();
test2060_addInitializationsFrom();
test2061_addPotentialInitializationsFrom();
test2062_mergedWith();
testBug292478();
testBug292478c();
test0331_if_else_nested();
testBug325755b();
testBug292478g();
// coverage check
int failuresNb = 0;
for (int i = 1; i <= coveragePointsNb; i++) {
if (i == 11 || i == 12 || i == 14) {
continue;
// these can only be reached via a direct call to addPotentialNullInfoFrom,
// which is not implemented in low level tests - all those go through
// addPotentialInitsFrom
}
try {
UnconditionalFlowInfo.CoverageTestId = i;
test0053_array();
test0070_type_reference();
test2050_markAsComparedEqualToNonNull();
test2051_markAsComparedEqualToNull();
test2055_markAsDefinitelyNonNull();
test2056_markAsDefinitelyNull();
test2057_markAsDefinitelyUnknown();
test2060_addInitializationsFrom();
test2061_addPotentialInitializationsFrom();
test2062_mergedWith();
testBug292478();
testBug292478c();
test0331_if_else_nested();
testBug325755b();
testBug292478g();
}
catch (AssertionFailedError e) {
continue;
}
catch (AssertionFailedException e) {
continue;
}
failuresNb++;
System.out.println("Missing coverage point: " + i);
}
UnconditionalFlowInfo.CoverageTestId = 0; // reset for other tests
assertEquals(failuresNb + " missing coverage point(s)", failuresNb, 0);
}
}
// Coverage by code samples.
public void test2999_coverage() {
if (UnconditionalFlowInfo.COVERAGE_TEST_FLAG) {
// sanity check: need to be sure that the tests execute properly when not
// trying to check coverage
UnconditionalFlowInfo.CoverageTestId = 0;
test0001_simple_local();
test0053_array();
test0070_type_reference();
test0327_if_else();
test0401_while();
test0420_while();
test0509_try_finally_embedded();
test2000_flow_info();
test2004_flow_info();
test2008_flow_info();
test2011_flow_info();
test2013_flow_info();
test2018_flow_info();
test2019_flow_info();
test2020_flow_info();
// coverage check
int failuresNb = 0;
for (int i = 1; i <= coveragePointsNb; i++) {
if (i < 3
|| 4 < i && i < 11
|| 11 < i && i < 16
|| 16 < i && i < 20
|| i == 21
|| 23 < i && i < 27
|| 29 < i && i < 33
|| 36 < i) { // TODO (maxime) complete coverage tests
continue;
}
try {
UnconditionalFlowInfo.CoverageTestId = i;
test0001_simple_local();
test0053_array();
test0070_type_reference();
test0327_if_else();
test0401_while();
test0420_while();
test0509_try_finally_embedded();
test2000_flow_info();
test2004_flow_info();
test2008_flow_info();
test2011_flow_info();
test2013_flow_info();
test2018_flow_info();
test2019_flow_info();
test2020_flow_info();
}
catch (AssertionFailedError e) {
continue;
}
catch (AssertionFailedException e) {
continue;
}
failuresNb++;
System.out.println("Missing coverage point: " + i);
}
UnconditionalFlowInfo.CoverageTestId = 0; // reset for other tests
assertEquals(failuresNb + " missing coverage point(s)", failuresNb, 0);
}
}
// only works for info coded on bit 0 - least significant
String testCodedValueOf(long[] data) {
int length;
StringBuffer result = new StringBuffer(length = data.length);
for (int i = 0; i < length; i++) {
result.append(data[i] == 0 ? '0' : '1');
}
return result.toString();
}
static String testStringValueOf(long[] data) {
int length;
StringBuffer result = new StringBuffer((length = data.length) * 2 + 1);
result.append('{');
for (int i = 0; i < length; i++) {
if (i > 0) {
result.append(',');
}
result.append(data[i]);
}
result.append('}');
return result.toString();
}
}
/**
* A specific extension of LocalVariableBinding suitable for flow info
* manipulation at an implementation level.
*/
class TestLocalVariableBinding extends LocalVariableBinding {
static class TestTypeBinding extends TypeBinding {
public TestTypeBinding() {
this.tagBits = 0L;
}
public char[] constantPoolName() {
return null;
}
public PackageBinding getPackage() {
return null;
}
public boolean isCompatibleWith(TypeBinding right, Scope captureScope) {
return false;
}
public char[] qualifiedSourceName() {
return null;
}
public char[] sourceName() {
return null;
}
public char[] readableName() {
return null;
}
}
final static TypeBinding testTypeBinding = new TestTypeBinding();
final static char [] testName = {'t', 'e', 's', 't'};
TestLocalVariableBinding(int id) {
super(testName, testTypeBinding, 0, false);
this.id = id;
}
public Constant constant() {
return Constant.NotAConstant;
}
static final TestLocalVariableBinding
local0 = new TestLocalVariableBinding(0),
local64 = new TestLocalVariableBinding(64),
local128 = new TestLocalVariableBinding(128);
}
/**
* A class meant to augment
* @link{org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo} with
* capabilities in the test domain. It especially provides factories to build
* fake flow info instances for use in state transitions validation.
*/
/*
* Moreover, this class defines the implementation of key operations for the
* benefit of itself and NullInfoRegistryTestHarness. Given the fact that the
* latter could not extend UnconditionalFlowInfoTestHarness and
* NullInfoRegistry, the code is factorized into static methods.
*/
class UnconditionalFlowInfoTestHarness extends UnconditionalFlowInfo {
int testPosition;
// Interface
/**
* Return the state represented by this.
* @return the state represented by this
*/
NullReferenceImplTests.State asState() {
return asState(this, 0);
}
/**
* Return the state represented by this for a variable encoded at a given position.
* @param position - int the position of the considered variable
* @return the state represented by this for a variable encoded at a given position
*/
NullReferenceImplTests.State asState(int position) {
return asState(this, position);
}
public FlowInfo copy() {
UnconditionalFlowInfoTestHarness copy =
new UnconditionalFlowInfoTestHarness();
copy.testPosition = this.testPosition;
copy(this, copy);
return copy;
}
public void markAsDefinitelyNonNull(LocalVariableBinding local) {
grow(local.id + this.maxFieldCount);
super.markAsDefinitelyNonNull(local);
}
public void markAsDefinitelyNull(LocalVariableBinding local) {
grow(local.id + this.maxFieldCount);
super.markAsDefinitelyNull(local);
}
public void markAsDefinitelyUnknown(LocalVariableBinding local) {
grow(local.id + this.maxFieldCount);
super.markAsDefinitelyUnknown(local);
}
/**
* Return a fake unconditional flow info which bit fields represent the given
* null bits for a local variable of id 0 within a class that would have no
* field.
* @param nullBits the bits that must be set, given in the same order as the
* nullAssignment* fields in UnconditionalFlowInfo definition; use 0
* for a bit that is not set, 1 else
* @return a fake unconditional flow info which bit fields represent the
* null bits given in parameter
*/
public static UnconditionalFlowInfoTestHarness testUnconditionalFlowInfo(
long [] nullBits) {
return testUnconditionalFlowInfo(nullBits, 0);
}
/**
* Return a fake unconditional flow info which bit fields represent the given
* null bits for a local variable of id position within a class that would have
* no field.
* @param nullBits the bits that must be set, given in the same order as the
* nullAssignment* fields in UnconditionalFlowInfo definition; use 0
* for a bit that is not set, 1 else
* @param position the position of the variable within the bit fields; use
* various values to test different parts of the bit fields, within
* or beyond BitCacheSize
* @return a fake unconditional flow info which bit fields represent the
* null bits given in parameter
*/
public static UnconditionalFlowInfoTestHarness testUnconditionalFlowInfo(
long [] nullBits, int position) {
UnconditionalFlowInfoTestHarness result =
new UnconditionalFlowInfoTestHarness();
result.testPosition = position;
init(result, nullBits, position);
return result;
}
/**
* Return a fake unconditional flow info which bit fields represent the given
* state for a local variable of id 0 within a class that would have
* no field.
* @param state - State the desired state for the variable
* @return a fake unconditional flow info which bit fields represent the
* state given in parameter
*/
public static UnconditionalFlowInfoTestHarness testUnconditionalFlowInfo(NullReferenceImplTests.State state) {
return testUnconditionalFlowInfo(state, 0);
}
/**
* Return a fake unconditional flow info which bit fields represent the given
* state for a local variable of id position within a class that would have
* no field.
* @param state - State the desired state for the variable
* @param position the position of the variable within the bit fields; use
* various values to test different parts of the bit fields, within
* or beyond BitCacheSize
* @return a fake unconditional flow info which bit fields represent the
* state given in parameter
*/
public static UnconditionalFlowInfoTestHarness testUnconditionalFlowInfo(
NullReferenceImplTests.State state, int position) {
UnconditionalFlowInfoTestHarness result =
new UnconditionalFlowInfoTestHarness();
long[] nullBits = state.asLongArray();
result.testPosition = position;
init(result, nullBits, position);
return result;
}
/**
* Return true iff this flow info can be considered as equal to the one passed
* in parameter.
* @param other the flow info to compare to
* @return true iff this flow info compares equal to other
*/
public boolean testEquals(UnconditionalFlowInfo other) {
return testEquals(this, other);
}
/**
* Return true iff this flow info can be considered as equal to the one passed
* in parameter in respect with a single local variable which id would be
* position in a class with no field.
* @param other the flow info to compare to
* @param position the position of the local to consider
* @return true iff this flow info compares equal to other for a given local
*/
public boolean testEquals(UnconditionalFlowInfo other, int position) {
return testEquals(this, other, position);
}
/**
* Return a string suitable for use as a representation of this flow info
* within test series.
* @return a string suitable for use as a representation of this flow info
*/
public String testString() {
if (this == DEAD_END) {
return "FlowInfo.DEAD_END"; //$NON-NLS-1$
}
return testString(this, this.testPosition);
}
/**
* Return a string suitable for use as a representation of this flow info
* within test series.
* @param position a position to consider instead of this flow info default
* test position
* @return a string suitable for use as a representation of this flow info
*/
public String testString(int position) {
return testString(this, position);
}
// Factorized implementation
static NullReferenceImplTests.State asState(UnconditionalFlowInfo zis, int position) {
if ((zis.tagBits & NULL_FLAG_MASK) == 0) {
return NullReferenceImplTests.State.start;
}
if (position < BitCacheSize) {
return NullReferenceImplTests.State.fromLongValues(
(zis.nullBit1 >> position) & 1,
(zis.nullBit2 >> position) & 1,
(zis.nullBit3 >> position) & 1,
(zis.nullBit4 >> position) & 1,
0,
0);
}
else {
int vectorIndex = (position / BitCacheSize) - 1;
position %= BitCacheSize;
if (vectorIndex >= zis.extra[2].length) {
return NullReferenceImplTests.State.start;
}
return NullReferenceImplTests.State.fromLongValues(
(zis.extra[2][vectorIndex] >> position) & 1,
(zis.extra[3][vectorIndex] >> position) & 1,
(zis.extra[4][vectorIndex] >> position) & 1,
(zis.extra[5][vectorIndex] >> position) & 1,
0 //(zis.extra[6][vectorIndex] >> position) & 1,
, 0 //(zis.extra[7][vectorIndex] >> position) & 1
);
}
}
static void copy(UnconditionalFlowInfo source, UnconditionalFlowInfo target) {
target.definiteInits = source.definiteInits;
target.potentialInits = source.potentialInits;
boolean hasNullInfo = (source.tagBits & NULL_FLAG_MASK) != 0;
if (hasNullInfo) {
target.nullBit1 = source.nullBit1;
target.nullBit2 = source.nullBit2;
target.nullBit3 = source.nullBit3;
target.nullBit4 = source.nullBit4;
}
target.iNBit = source.iNBit;
target.iNNBit = source.iNNBit;
target.tagBits = source.tagBits;
target.maxFieldCount = source.maxFieldCount;
if (source.extra != null) {
int length;
target.extra = new long[extraLength][];
System.arraycopy(source.extra[0], 0,
(target.extra[0] = new long[length = source.extra[0].length]), 0, length);
System.arraycopy(source.extra[1], 0,
(target.extra[1] = new long[length]), 0, length);
if (hasNullInfo) {
for (int j = 0; j < extraLength; j++) {
System.arraycopy(source.extra[j], 0,
(target.extra[j] = new long[length]), 0, length);
}
}
else {
for (int j = 0; j < extraLength; j++) {
target.extra[j] = new long[length];
}
}
}
}
public void grow(int position) {
int vectorIndex = ((position) / BitCacheSize) - 1;
int length = vectorIndex + 1, oldLength;
if (this.extra == null) {
this.extra = new long[extraLength][];
for (int j = 0; j < extraLength; j++) {
this.extra[j] = new long[length];
}
} else if (length > (oldLength = this.extra[2].length)) {
for (int j = 0; j < extraLength; j++) {
System.arraycopy(this.extra[j], 0,
this.extra[j] = new long[length], 0, oldLength);
}
}
}
static void init(UnconditionalFlowInfo zis, long [] nullBits, int position) {
if (position < BitCacheSize) {
zis.nullBit1 = nullBits[0] << position;
zis.nullBit2 = nullBits[1] << position;
zis.nullBit3 = nullBits[2] << position;
zis.nullBit4 = nullBits[3] << position;
}
else {
int vectorIndex = (position / BitCacheSize) - 1,
length = vectorIndex + 1;
position %= BitCacheSize;
zis.extra = new long[extraLength][];
zis.extra[0] = new long[length];
zis.extra[1] = new long[length];
for (int j = 2; j < extraLength; j++) {
zis.extra[j] = new long[length];
zis.extra[j][vectorIndex] = nullBits[j - 2] << position;
}
// FIXME: while IN,INN are not included in nullBits:
Arrays.fill(zis.extra[UnconditionalFlowInfo.IN], -1L);
Arrays.fill(zis.extra[UnconditionalFlowInfo.INN], -1L);
}
zis.iNBit = -1L; // FIXME: nullBits[4] << position;
zis.iNNBit = -1L; // FIXME: nullBits[5] << position;
if (nullBits[0] != 0 || nullBits[1] != 0
|| nullBits[2] != 0 || nullBits[3] != 0
|| nullBits[4] != 0 || nullBits[5] != 0) {
// cascade better than nullBits[0] | nullBits[1] | nullBits[2] | nullBits[3]
// by 10%+
// TODO (maxime) run stats to determine which is the better order
zis.tagBits |= NULL_FLAG_MASK;
}
zis.maxFieldCount = 0;
}
static boolean testEquals(UnconditionalFlowInfo zis, UnconditionalFlowInfo other) {
if (zis.tagBits != other.tagBits) {
return false;
}
if (zis.nullBit1 != other.nullBit1
|| zis.nullBit2 != other.nullBit2
|| zis.nullBit3 != other.nullBit3
|| zis.nullBit4 != other.nullBit4
/*|| zis.iNBit != other.iNBit // FIXME: include these bits in comparison?
|| zis.iNNBit != other.iNNBit */) {
return false;
}
int left = zis.extra == null ? 0 : zis.extra[2].length,
right = other.extra == null ? 0 : other.extra[2].length,
both = 0, i;
if (left > right) {
both = right;
}
else {
both = left;
}
for (i = 0; i < both ; i++) {
for (int j = 2; j < extraLength; j++) {
if (zis.extra[j][i] !=
other.extra[j][i]) {
return false;
}
}
}
for (; i < left; i++) {
for (int j = 2; j < extraLength; j++) {
if (zis.extra[j][i] != 0) {
return false;
}
}
}
for (; i < right; i++) {
for (int j = 2; j < extraLength; j++) {
if (other.extra[j][i] != 0) {
return false;
}
}
}
return true;
}
static boolean testEquals(UnconditionalFlowInfo zis, UnconditionalFlowInfo other,
int position) {
int vectorIndex = position / BitCacheSize - 1;
if ((zis.tagBits & other.tagBits & NULL_FLAG_MASK) == 0) {
return true;
}
long mask;
if (vectorIndex < 0) {
return ((zis.nullBit1 & (mask = (1L << position))) ^
(other.nullBit1 & mask)) == 0 &&
((zis.nullBit2 & mask) ^
(other.nullBit2 & mask)) == 0 &&
((zis.nullBit3 & mask) ^
(other.nullBit3 & mask)) == 0 &&
((zis.nullBit4 & mask) ^
(other.nullBit4 & mask)) == 0 /* && // FIXME: include these bits in comparison?
((zis.iNBit & mask) ^
(other.iNBit & mask)) == 0 &&
((zis.iNNBit & mask) ^
(other.iNNBit & mask)) == 0 */;
}
else {
int left = zis.extra == null ?
0 :
zis.extra[0].length;
int right = other.extra == null ?
0 :
other.extra[0].length;
int both = left < right ? left : right;
if (vectorIndex < both) {
mask = (1L << (position % BitCacheSize));
for (int j = 2; j < extraLength; j++) {
if (((zis.extra[j][vectorIndex] & mask)
^ (other.extra[j][vectorIndex] & mask)) != 0) {
return false;
}
}
return true;
}
if (vectorIndex < left) {
return ((zis.extra[2][vectorIndex] |
zis.extra[3][vectorIndex] |
zis.extra[4][vectorIndex] |
zis.extra[5][vectorIndex] |
zis.extra[6][vectorIndex] |
zis.extra[7][vectorIndex]) &
(1L << (position % BitCacheSize))) == 0;
}
return ((other.extra[2][vectorIndex] |
other.extra[3][vectorIndex] |
other.extra[4][vectorIndex] |
other.extra[5][vectorIndex] |
other.extra[6][vectorIndex] |
other.extra[7][vectorIndex]) &
(1L << (position % BitCacheSize))) == 0;
}
}
static String testString(UnconditionalFlowInfo zis, int position) {
if (zis == DEAD_END) {
return "FlowInfo.DEAD_END"; //$NON-NLS-1$
}
if (position < BitCacheSize) {
return "{" + (zis.nullBit1 >> position) //$NON-NLS-1$
+ "," + (zis.nullBit2 >> position) //$NON-NLS-1$
+ "," + (zis.nullBit3 >> position) //$NON-NLS-1$
+ "," + (zis.nullBit4 >> position) //$NON-NLS-1$
// + "," + (zis.iNBit >> position) //$NON-NLS-1$
// + "," + (zis.iNNBit >> position) //$NON-NLS-1$
+ "}"; //$NON-NLS-1$
}
else {
int vectorIndex = position / BitCacheSize - 1,
shift = position % BitCacheSize;
return "{" + (zis.extra[2][vectorIndex] //$NON-NLS-1$
>> shift)
+ "," + (zis.extra[3][vectorIndex] //$NON-NLS-1$
>> shift)
+ "," + (zis.extra[4][vectorIndex] //$NON-NLS-1$
>> shift)
+ "," + (zis.extra[5][vectorIndex] //$NON-NLS-1$
>> shift)
// + "," + (zis.extra[6][vectorIndex] //$NON-NLS-1$
// >> shift)
// + "," + (zis.extra[7][vectorIndex] //$NON-NLS-1$
// >> shift)
+ "}"; //$NON-NLS-1$
}
}
}
interface CodeAnalysis {
public static final String
definitionStartMarker = "DEFINITION START",
definitionEndMarker = "DEFINITION END",
initializerStartMarker = "INITIALIZER START",
initializerEndMarker = "INITIALIZER END";
}
@SuppressWarnings({ "unchecked", "rawtypes" })
class TransitiveClosureHolder {
static class Element {
NullReferenceImplTests.State value;
boolean alreadyKnown;
Element(NullReferenceImplTests.State value) {
if (value == null) {
throw new IllegalArgumentException("not a valid element");
}
this.value = value;
}
}
Map elements = new TreeMap();
public TransitiveClosureHolder() {
Element start = new Element(NullReferenceImplTests.State.start);
this.elements.put(start.value, start);
}
void add(NullReferenceImplTests.State value) {
if (value == null) {
throw new IllegalArgumentException("not a valid state");
}
if (! this.elements.containsKey(value)) {
this.elements.put(value, new Element(value));
}
}
void add(NullReferenceImplTests.State[] values) {
if (values == null) {
throw new IllegalArgumentException("not a valid states set");
}
for (int i = 0, length = values.length; i < length; i++) {
add(values[i]);
}
}
NullReferenceImplTests.State[] asArray() {
int length;
NullReferenceImplTests.State[] result = new NullReferenceImplTests.State[length = this.elements.size()];
Iterator elementsIterator = this.elements.keySet().iterator();
for (int j = 0; j < length; j++) {
result[j] = (NullReferenceImplTests.State) elementsIterator.next();
}
return result;
}
NullReferenceImplTests.State[] notAlreadyKnowns() {
List resultAccumulator = new ArrayList(this.elements.size());
Iterator i = this.elements.values().iterator();
Element current;
while (i.hasNext()) {
if (! (current = (Element) i.next()).alreadyKnown) {
resultAccumulator.add(current.value);
}
}
int length;
NullReferenceImplTests.State[] result = new NullReferenceImplTests.State[length = resultAccumulator.size()];
for (int j = 0; j < length; j++) {
result[j] = (NullReferenceImplTests.State) resultAccumulator.get(j);
}
return result;
}
void markAllAsAlreadyKnown() {
Iterator i = this.elements.values().iterator();
while (i.hasNext()) {
((Element) i.next()).alreadyKnown = true;
}
}
public String toString() {
StringBuffer output = new StringBuffer();
output.append("Transitive closure:\n");
SortedMap sorted = new TreeMap(this.elements);
Iterator i = sorted.keySet().iterator();
while (i.hasNext()) {
output.append(i.next().toString());
output.append('\n');
}
return output.toString();
}
}
// PREMATURE segregate pure tooling into a separate project, keep tests only here
/**
* The Generator class is meant to generate the tabular data needed by the
* flow information implementation level tests. While the tests should ensure
* non regression by leveraging their initialization tables only, any change
* into the flow information logic or encoding is due to yield considerable
* changes into the literal values sets of the initializers themselves.
* Tooling the production of those literals buys us flexibility.
* {@link #printHelp printHelp} for details.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
class Generator {
static NullReferenceImplTests.State[] computeTransitiveClosure() {
TransitiveClosureHolder transitiveClosure = new TransitiveClosureHolder();
NullReferenceImplTests.State[] unknowns;
unknowns = transitiveClosure.notAlreadyKnowns();
while (unknowns.length != 0) {
transitiveClosure.markAllAsAlreadyKnown();
for (int i = 0, length = NullReferenceImplTransformations.transformations.length; i < length; i ++) {
transitiveClosure.add(
NullReferenceImplTransformations.transformations[i].
computeOutputs(transitiveClosure.asArray()));
}
unknowns = transitiveClosure.notAlreadyKnowns();
}
return transitiveClosure.asArray();
}
public static void main(String[] args) {
if (args.length == 0) {
printHelp(false);
System.exit(1);
}
switch (args.length) {
case 1:
if (args[0].equals("--help")) {
printHelp(true);
System.exit(0);
}
else {
printHelp(false);
System.exit(1);
}
break;
case 2:
if (args[0].equals("--printTruthTables")) {
File outputDir = new File(args[1]);
if (outputDir.isDirectory()) {
for (int i = 0, length = NullReferenceImplTransformations.transformations.length; i < length; i++) {
NullReferenceImplTransformations.transformations[i].printTruthTables(outputDir);
}
}
else {
// PREMATURE error handling
}
System.exit(0);
}
else {
printHelp(false);
System.exit(1);
}
break;
case 3:
if (args[0].equals("--reinitializeFromComputedValues")) {
reinitializeFromComputedValues(args[1], args[2]);
System.out.println("Generator generated new file into " + args[2]);
System.exit(0);
}
//$FALL-THROUGH$
case 5:
if (args[0].equals("--reinitializeFromComments")) {
reinitializeFromComments(args[1], args[2], args[3], args[4]);
System.out.println("Generator generated new files into " + args[2]
+ " and " + args[4]);
System.exit(0);
}
//$FALL-THROUGH$
default:
printHelp(false);
System.exit(1);
}
}
private static void reinitializeFromComments(
String statesSource, String statesTarget,
String transformationsSource, String transformationsTarget) {
if (statesSource.equals(transformationsSource) ||
statesTarget.equals(transformationsTarget)) {
throw new RuntimeException();
}
try {
BufferedReader in;
BufferedWriter out;
NullReferenceImplTests.State.reinitializeFromComment(
in = new BufferedReader(
new FileReader(statesSource)),
out = new BufferedWriter(new FileWriter(statesTarget)));
in.close();
out.close();
File[] tempFiles = new File[2];
tempFiles[0] = File.createTempFile("generator", "java");
tempFiles[1] = File.createTempFile("generator", "java");
NullReferenceImplTransformations.transformations[0].reinitializeFromComments(
in = new BufferedReader(
new FileReader(transformationsSource)),
out = new BufferedWriter(new FileWriter(tempFiles[0])));
in.close();
out.close();
int i, length;
for (i = 1, length = NullReferenceImplTransformations.transformations.length - 1; i < length; i++) {
NullReferenceImplTransformations.transformations[i].reinitializeFromComments(
in = new BufferedReader(
new FileReader(tempFiles[(i + 1) % 2])),
out = new BufferedWriter(new FileWriter(tempFiles[i % 2])));
in.close();
out.close();
}
NullReferenceImplTransformations.transformations[i].reinitializeFromComments(
in = new BufferedReader(
new FileReader(tempFiles[(i + 1) % 2])),
out = new BufferedWriter(new FileWriter(transformationsTarget)));
in.close();
out.close();
} catch (Throwable t) {
System.err.println("Generator error:");
t.printStackTrace(System.err);
System.exit(2);
}
}
private static void reinitializeFromComputedValues(String source, String target) {
for (int i = 0, length = NullReferenceImplTransformations.transformations.length;
i < length; i++) {
NullReferenceImplTransformations.transformations[i].hydrate();
}
NullReferenceImplTests.State[] transitiveClosure = computeTransitiveClosure(); // need for initialization?
transitiveClosure = addSymbolicStates(transitiveClosure); // don't rely on reachibility alone, since we don't cover all operations in these tests.
Arrays.sort(transitiveClosure, new Comparator() {
public int compare(Object o1, Object o2) {
return Integer.valueOf(((State)o1).value).compareTo(Integer.valueOf(((State)o2).value));
}
});
try {
BufferedReader in;
BufferedWriter out;
File[] tempFiles = new File[2];
tempFiles[0] = File.createTempFile("generator", "java");
tempFiles[1] = File.createTempFile("generator", "java");
NullReferenceImplTransformations.transformations[0].reinitializeFromComputedValues(
in = new BufferedReader(
new FileReader(source)),
out = new BufferedWriter(new FileWriter(tempFiles[0])),
transitiveClosure);
in.close();
out.close();
int i, length;
for (i = 1, length = NullReferenceImplTransformations.transformations.length - 1; i < length; i++) {
NullReferenceImplTransformations.transformations[i].reinitializeFromComputedValues(
in = new BufferedReader(
new FileReader(tempFiles[(i + 1) % 2])),
out = new BufferedWriter(new FileWriter(tempFiles[i % 2])),
transitiveClosure);
in.close();
out.close();
}
NullReferenceImplTransformations.transformations[i].reinitializeFromComputedValues(
in = new BufferedReader(
new FileReader(tempFiles[(i + 1) % 2])),
out = new BufferedWriter(new FileWriter(target)),
transitiveClosure);
in.close();
out.close();
} catch (Throwable t) {
System.err.println("Generator error:");
t.printStackTrace(System.err);
System.exit(2);
}
}
private static State[] addSymbolicStates(State[] transitiveClosure) {
Set allStates = new HashSet();
for (int i = 0; i < transitiveClosure.length; i++)
allStates.add(transitiveClosure[i]);
for (int i=0; i < State.statesNb; i++)
if (State.states[i].symbolic)
allStates.add(State.states[i]);
return (State[]) allStates.toArray(new State[allStates.size()]);
}
private static void printHelp(boolean longText) {
if (longText) {
System.out.println(
"Generator use cases\n" +
" - when a brand new logic is experimented for the transitions, the best\n" +
" way to go is to write explicit (inefficient) transformation code within\n" +
" UnconditionalFlowInfo, then generate the literal initializers from\n" +
" there; use the command\n" +
" --reinitializeFromComputedValues <source file> <target file>\n" +
" to this effect; in case of inconsistencies or errors, messages are\n" +
" printed to the error output stream and the result should be considered as non reliable;\n" +
" - when only a few changes are made to state names or a specific\n" +
" transitions, it should be possible to get the test initializers fixed\n" +
" before UnconditionalFlowInfo implements those changes; use the command\n" +
" --reinitializeFromComments <states source file> <states target file> <transformations source file> <transformations target file>\n" +
" to this effect;\n" +
" - the same command can be used when, while the semantics of the system\n" +
" are unchanged, the encoding is modified; it should then produce the\n" +
" initializers according to the new encoding, as defined by the comment\n" +
" for State.states, and the transformations as defined by their\n" +
" respective comment;\n" +
" - when a given encoding is retained, its optimization may leverage truth\n" +
" tables; use the --printTruthTables command to this effect.\n" +
" \n\n");
printHelp(false);
}
else {
System.out.println(
"Usage:\n" +
"Generator --help\n" +
" prints a more detailed help message\n" +
"Generator --printTruthTables\n" +
" prints the truth tables of the transformations\n" +
"Generator --reinitializeFromComments <source file> <target file>\n" +
" generates into target file a copy of source file into which\n" +
" transformations initializers have been reset from their definitions\n" +
"Generator --reinitializeFromComputedValues <source file> <target file>\n" +
" generates into target file a copy of source file into which\n" +
" transformations definitions and initializers have been reset\n" +
" according to the behavior of the current UnconditionalFlowInfo\n"
);
}
}
}