| /******************************************************************************* |
| * Copyright (c) 2005, 2007 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 |
| *******************************************************************************/ |
| package org.eclipse.wst.jsdt.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.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.SortedMap; |
| import java.util.TreeMap; |
| |
| import org.eclipse.wst.jsdt.internal.compiler.flow.FlowInfo; |
| import org.eclipse.wst.jsdt.internal.compiler.flow.NullInfoRegistry; |
| import org.eclipse.wst.jsdt.internal.compiler.flow.UnconditionalFlowInfo; |
| import org.eclipse.wst.jsdt.internal.compiler.flow.UnconditionalFlowInfo.AssertionFailedException; |
| import org.eclipse.wst.jsdt.internal.compiler.impl.Constant; |
| import org.eclipse.wst.jsdt.internal.compiler.lookup.LocalVariableBinding; |
| import org.eclipse.wst.jsdt.internal.compiler.lookup.PackageBinding; |
| import org.eclipse.wst.jsdt.internal.compiler.lookup.TypeBinding; |
| |
| import junit.framework.AssertionFailedError; |
| import junit.framework.Test; |
| import junit.framework.TestSuite; |
| |
| /** |
| * 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. |
| */ |
| 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 |
| 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), // 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 (nextSymbolic == -1) { |
| for (nextSymbolic = 0; nextSymbolic < states.length; nextSymbolic++) { |
| if (states[nextSymbolic].symbolic) { |
| break; |
| } |
| } |
| } else { |
| for (; nextSymbolic < states.length; nextSymbolic++) { |
| if (states[nextSymbolic].symbolic) { |
| break; |
| } |
| } |
| } |
| return nextSymbolic < states.length; |
| } |
| public Object next() { |
| State result = null; |
| if (nextSymbolic < states.length) { |
| result = states[nextSymbolic]; |
| nextSymbolic++; |
| } |
| return result; |
| } |
| public void remove() { |
| throw new RuntimeException("unimplemented"); |
| } |
| }; |
| } |
| public String toString() { |
| return this.name; |
| } |
| } |
| |
| public NullReferenceImplTests(String name) { |
| super(name); |
| } |
| |
| // Tests tuning |
| static final boolean |
| skipHighOrderBits = false; // define to true when tuning encoding |
| static final int |
| combinationTestsloopsNb = 1; // define to 10000s to measure performances |
| |
| 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); |
| } |
| |
| public void test2070_newNullInfoRegistry() { |
| int failures = NullReferenceImplTransformations.newNullInfoRegistry.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 (combinationTestsloopsNb > 1) { |
| start = System.currentTimeMillis(); |
| } |
| String header = "state consistency failures: "; |
| for (int l = 0; l < combinationTestsloopsNb ; 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 (combinationTestsloopsNb > 1) { |
| System.out.println("mergedWith\t\t\t" + combinationTestsloopsNb + "\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 = 39; |
| |
| // PREMATURE reactivate coverage tests |
| // Coverage by state transition tables methods. |
| public void test2998_coverage() { |
| if (UnconditionalFlowInfo.coverageTestFlag) { |
| // 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(); |
| // 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(); |
| } |
| 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.coverageTestFlag) { |
| // 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() { |
| tagBits = 0L; |
| } |
| public char[] constantPoolName() { |
| return null; |
| } |
| public PackageBinding getPackage() { |
| return null; |
| } |
| public boolean isCompatibleWith(TypeBinding right) { |
| 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.wst.jsdt.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( getLocalID(local)); |
| super.markAsDefinitelyNonNull(local); |
| } |
| |
| public void markAsDefinitelyNull(LocalVariableBinding local) { |
| grow( getLocalID(local)); |
| super.markAsDefinitelyNull(local); |
| } |
| |
| public void markAsDefinitelyUnknown(LocalVariableBinding local) { |
| grow( getLocalID(local)); |
| 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.nullBit5 = source.nullBit5; |
| // target.nullBit6 = source.nullBit6; |
| } |
| 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; |
| // zis.nullBit5 = nullBits[4] << position; |
| // zis.nullBit6 = nullBits[5] << 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; |
| } |
| } |
| 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.nullBit5 != other.nullBit5 |
| || zis.nullBit6 != other.nullBit6 */) { |
| 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 /* && |
| ((zis.nullBit5 & mask) ^ |
| (other.nullBit5 & mask)) == 0 && |
| ((zis.nullBit6 & mask) ^ |
| (other.nullBit6 & 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.nullBit5 >> position) //$NON-NLS-1$ |
| // + "," + (zis.nullBit6 >> 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$ |
| } |
| } |
| } |
| /** |
| * A class meant to augment |
| * @link{org.eclipse.wst.jsdt.internal.compiler.flow.NullInfoRegistry} with |
| * capabilities in the test domain. It especially provides factories to build |
| * fake flow info instances for use in state transitions validation. |
| */ |
| /* |
| * The reason why UnconditionalFlowInfoTestHarness and this class were |
| * separated is that NullInfoRegistry redefines part of the markAs* methods, |
| * in effect preventing a harness extending NullInfoRegistry to access |
| * UnconditionalFlowInfo implementations of the said methods. |
| */ |
| class NullInfoRegistryTestHarness extends NullInfoRegistry { |
| private int testPosition; |
| |
| private NullInfoRegistryTestHarness() { |
| super(FlowInfo.DEAD_END); |
| } |
| |
| // Interface |
| /** |
| * Return the state represented by this. |
| * @return the state represented by this |
| */ |
| NullReferenceImplTests.State asState() { |
| return UnconditionalFlowInfoTestHarness.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 UnconditionalFlowInfoTestHarness.asState(this, position); |
| } |
| |
| public FlowInfo copy() { |
| NullInfoRegistryTestHarness copy = |
| new NullInfoRegistryTestHarness(); |
| copy.testPosition = this.testPosition; |
| UnconditionalFlowInfoTestHarness.copy(this, copy); |
| return copy; |
| } |
| |
| /** |
| * Return a fake null info registry derived from an unconditional flow |
| * info. |
| * @param upstream - UnconditionalFlowInfoTestHarness the upstream flow info |
| * @return a fake null info registry derived from upstream |
| */ |
| public static NullInfoRegistryTestHarness testNullInfoRegistry( |
| UnconditionalFlowInfoTestHarness upstream) { |
| NullInfoRegistry nullInfoRegistry = new NullInfoRegistry(upstream); |
| NullInfoRegistryTestHarness result = |
| new NullInfoRegistryTestHarness(); |
| result.testPosition = upstream.testPosition; |
| if (result.testPosition < BitCacheSize) { |
| result.nullBit1 = nullInfoRegistry.nullBit1; |
| result.nullBit2 = nullInfoRegistry.nullBit2; |
| result.nullBit3 = nullInfoRegistry.nullBit3; |
| result.nullBit4 = nullInfoRegistry.nullBit4; |
| // result.nullBit5 = nullInfoRegistry.nullBit5; |
| // result.nullBit6 = nullInfoRegistry.nullBit6; |
| } |
| else if ((nullInfoRegistry.tagBits & NULL_FLAG_MASK) != 0){ |
| int vectorIndex = (result.testPosition / BitCacheSize) - 1, |
| length = vectorIndex + 1; |
| result.extra = new long[extraLength][]; |
| result.extra[0] = new long[length]; |
| result.extra[1] = new long[length]; |
| for (int j = 2; j < extraLength; j++) { |
| result.extra[j] = new long[length]; |
| result.extra[j][vectorIndex] = nullInfoRegistry.extra[j][vectorIndex]; |
| } |
| } |
| if ((nullInfoRegistry.tagBits & NULL_FLAG_MASK) != 0) { |
| result.tagBits |= NULL_FLAG_MASK; |
| } |
| result.maxFieldCount = 0; |
| 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 UnconditionalFlowInfoTestHarness.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 UnconditionalFlowInfoTestHarness.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 UnconditionalFlowInfoTestHarness.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 UnconditionalFlowInfoTestHarness.testString(this, position); |
| } |
| } |
| |
| interface CodeAnalysis { |
| public static final String |
| definitionStartMarker = "DEFINITION START", |
| definitionEndMarker = "DEFINITION END", |
| initializerStartMarker = "INITIALIZER START", |
| initializerEndMarker = "INITIALIZER END"; |
| } |
| 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. |
| */ |
| 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); |
| } |
| 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); |
| } |
| 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); |
| } |
| 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); |
| } |
| 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(); |
| 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 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" |
| ); |
| } |
| } |
| } |
| |