diff --git a/dubbo-common/pom.xml b/dubbo-common/pom.xml
index 98ecc9b87b2..43ebf6209c6 100644
--- a/dubbo-common/pom.xml
+++ b/dubbo-common/pom.xml
@@ -77,5 +77,39 @@
de.ruedigermoeller
fst
+
+ org.checkerframework
+ checker
+ 2.8.1
+
+
+ org.checkerframework
+ jdk8
+ 2.8.1
+
-
\ No newline at end of file
+
+
+
+ maven-compiler-plugin
+ 3.3
+
+
+ 1.8
+
+ 10000
+ 10000
+
+
+ org.checkerframework.checker.index.IndexChecker
+
+
+ -Xbootclasspath/p:$CHECKERFRAMEWORK/checker/dist/jdk8.jar
+ -AprintErrorStack
+ -AonlyDefs=^org\.apache\.dubbo\.common\.io\.
+
+
+
+
+
+
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/io/Bytes.java b/dubbo-common/src/main/java/org/apache/dubbo/common/io/Bytes.java
index 263e7b9f67c..9e928140ab1 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/io/Bytes.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/io/Bytes.java
@@ -30,6 +30,11 @@
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;
+import org.checkerframework.checker.index.qual.IndexOrHigh;
+import org.checkerframework.checker.index.qual.LTLengthOf;
+import org.checkerframework.checker.index.qual.NonNegative;
+import org.checkerframework.common.value.qual.MinLen;
+
/**
* CodecUtils.
*/
@@ -55,7 +60,7 @@ private Bytes() {
* @param length new length.
* @return new byte array.
*/
- public static byte[] copyOf(byte[] src, int length) {
+ public static byte[] copyOf(byte[] src, @NonNegative int length) {
byte[] dest = new byte[length];
System.arraycopy(src, 0, dest, 0, Math.min(src.length, length));
return dest;
@@ -79,7 +84,7 @@ public static byte[] short2bytes(short v) {
* @param v value.
* @param b byte array.
*/
- public static void short2bytes(short v, byte[] b) {
+ public static void short2bytes(short v, byte @MinLen(2) [] b) {
short2bytes(v, b, 0);
}
@@ -89,7 +94,7 @@ public static void short2bytes(short v, byte[] b) {
* @param v value.
* @param b byte array.
*/
- public static void short2bytes(short v, byte[] b, int off) {
+ public static void short2bytes(short v, byte @MinLen(2) [] b, @NonNegative @LTLengthOf(value = "#2", offset = "1") int off) {
b[off + 1] = (byte) v;
b[off + 0] = (byte) (v >>> 8);
}
@@ -112,7 +117,7 @@ public static byte[] int2bytes(int v) {
* @param v value.
* @param b byte array.
*/
- public static void int2bytes(int v, byte[] b) {
+ public static void int2bytes(int v, byte @MinLen(4) [] b) {
int2bytes(v, b, 0);
}
@@ -123,7 +128,7 @@ public static void int2bytes(int v, byte[] b) {
* @param b byte array.
* @param off array offset.
*/
- public static void int2bytes(int v, byte[] b, int off) {
+ public static void int2bytes(int v, byte @MinLen(4) [] b, @NonNegative @LTLengthOf(value = "#2", offset = "3") int off) {
b[off + 3] = (byte) v;
b[off + 2] = (byte) (v >>> 8);
b[off + 1] = (byte) (v >>> 16);
@@ -148,7 +153,7 @@ public static byte[] float2bytes(float v) {
* @param v value.
* @param b byte array.
*/
- public static void float2bytes(float v, byte[] b) {
+ public static void float2bytes(float v, byte @MinLen(4) [] b) {
float2bytes(v, b, 0);
}
@@ -159,7 +164,7 @@ public static void float2bytes(float v, byte[] b) {
* @param b byte array.
* @param off array offset.
*/
- public static void float2bytes(float v, byte[] b, int off) {
+ public static void float2bytes(float v, byte @MinLen(4) [] b, @NonNegative @LTLengthOf(value = "#2", offset = "3") int off) {
int i = Float.floatToIntBits(v);
b[off + 3] = (byte) i;
b[off + 2] = (byte) (i >>> 8);
@@ -185,7 +190,7 @@ public static byte[] long2bytes(long v) {
* @param v value.
* @param b byte array.
*/
- public static void long2bytes(long v, byte[] b) {
+ public static void long2bytes(long v, byte @MinLen(8) [] b) {
long2bytes(v, b, 0);
}
@@ -196,7 +201,7 @@ public static void long2bytes(long v, byte[] b) {
* @param b byte array.
* @param off array offset.
*/
- public static void long2bytes(long v, byte[] b, int off) {
+ public static void long2bytes(long v, byte @MinLen(8) [] b, @NonNegative @LTLengthOf(value = "#2", offset = "7") int off) {
b[off + 7] = (byte) v;
b[off + 6] = (byte) (v >>> 8);
b[off + 5] = (byte) (v >>> 16);
@@ -225,7 +230,7 @@ public static byte[] double2bytes(double v) {
* @param v value.
* @param b byte array.
*/
- public static void double2bytes(double v, byte[] b) {
+ public static void double2bytes(double v, byte @MinLen(8) [] b) {
double2bytes(v, b, 0);
}
@@ -236,7 +241,7 @@ public static void double2bytes(double v, byte[] b) {
* @param b byte array.
* @param off array offset.
*/
- public static void double2bytes(double v, byte[] b, int off) {
+ public static void double2bytes(double v, byte @MinLen(8) [] b, @NonNegative @LTLengthOf(value = "#2", offset = "7") int off) {
long j = Double.doubleToLongBits(v);
b[off + 7] = (byte) j;
b[off + 6] = (byte) (j >>> 8);
@@ -254,7 +259,7 @@ public static void double2bytes(double v, byte[] b, int off) {
* @param b byte array.
* @return short.
*/
- public static short bytes2short(byte[] b) {
+ public static short bytes2short(byte @MinLen(2) [] b) {
return bytes2short(b, 0);
}
@@ -265,7 +270,7 @@ public static short bytes2short(byte[] b) {
* @param off offset.
* @return short.
*/
- public static short bytes2short(byte[] b, int off) {
+ public static short bytes2short(byte @MinLen(2) [] b, @NonNegative @LTLengthOf(value = "#1", offset = "1") int off) {
return (short) (((b[off + 1] & 0xFF) << 0) +
((b[off + 0]) << 8));
}
@@ -276,7 +281,7 @@ public static short bytes2short(byte[] b, int off) {
* @param b byte array.
* @return int.
*/
- public static int bytes2int(byte[] b) {
+ public static int bytes2int(byte @MinLen(4) [] b) {
return bytes2int(b, 0);
}
@@ -287,7 +292,7 @@ public static int bytes2int(byte[] b) {
* @param off offset.
* @return int.
*/
- public static int bytes2int(byte[] b, int off) {
+ public static int bytes2int(byte @MinLen(4) [] b, @NonNegative @LTLengthOf(value = "#1", offset = "3") int off) {
return ((b[off + 3] & 0xFF) << 0) +
((b[off + 2] & 0xFF) << 8) +
((b[off + 1] & 0xFF) << 16) +
@@ -300,7 +305,7 @@ public static int bytes2int(byte[] b, int off) {
* @param b byte array.
* @return int.
*/
- public static float bytes2float(byte[] b) {
+ public static float bytes2float(byte @MinLen(4) [] b) {
return bytes2float(b, 0);
}
@@ -311,7 +316,7 @@ public static float bytes2float(byte[] b) {
* @param off offset.
* @return int.
*/
- public static float bytes2float(byte[] b, int off) {
+ public static float bytes2float(byte @MinLen(4) [] b, @NonNegative @LTLengthOf(value = "#1", offset = "3") int off) {
int i = ((b[off + 3] & 0xFF) << 0) +
((b[off + 2] & 0xFF) << 8) +
((b[off + 1] & 0xFF) << 16) +
@@ -325,7 +330,7 @@ public static float bytes2float(byte[] b, int off) {
* @param b byte array.
* @return long.
*/
- public static long bytes2long(byte[] b) {
+ public static long bytes2long(byte @MinLen(8) [] b) {
return bytes2long(b, 0);
}
@@ -336,7 +341,7 @@ public static long bytes2long(byte[] b) {
* @param off offset.
* @return long.
*/
- public static long bytes2long(byte[] b, int off) {
+ public static long bytes2long(byte @MinLen(8) [] b, @NonNegative @LTLengthOf(value = "#1", offset = "7") int off) {
return ((b[off + 7] & 0xFFL) << 0) +
((b[off + 6] & 0xFFL) << 8) +
((b[off + 5] & 0xFFL) << 16) +
@@ -353,7 +358,7 @@ public static long bytes2long(byte[] b, int off) {
* @param b byte array.
* @return double.
*/
- public static double bytes2double(byte[] b) {
+ public static double bytes2double(byte @MinLen(8) [] b) {
return bytes2double(b, 0);
}
@@ -364,7 +369,7 @@ public static double bytes2double(byte[] b) {
* @param off offset.
* @return double.
*/
- public static double bytes2double(byte[] b, int off) {
+ public static double bytes2double(byte @MinLen(8) [] b, @NonNegative @LTLengthOf(value = "#1", offset = "7") int off) {
long j = ((b[off + 7] & 0xFFL) << 0) +
((b[off + 6] & 0xFFL) << 8) +
((b[off + 5] & 0xFFL) << 16) +
@@ -394,7 +399,8 @@ public static String bytes2hex(byte[] bs) {
* @param len length.
* @return hex string.
*/
- public static String bytes2hex(byte[] bs, int off, int len) {
+ @SuppressWarnings("index:array.access.unsafe") // The for loop is executed len times. off + len has been verified and cs has length of len * 2
+ public static String bytes2hex(byte[] bs, @IndexOrHigh("#1") int off, @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int len) {
if (off < 0) {
throw new IndexOutOfBoundsException("bytes2hex: offset < 0, offset is " + off);
}
@@ -434,7 +440,10 @@ public static byte[] hex2bytes(String str) {
* @param len length.
* @return byte array.
*/
- public static byte[] hex2bytes(final String str, final int off, int len) {
+ @SuppressWarnings({"index:argument.type.incompatible", "index:array.access.unsafe.high"}) /*
+ #1. The loop stops at num steps, which is the length of b. off + len has been previously verified, so accessing str with r is safe.
+ */
+ public static byte[] hex2bytes(final String str, final @IndexOrHigh("#1") int off, @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int len) {
if ((len & 1) == 1) {
throw new IllegalArgumentException("hex2bytes: ( len & 1 ) == 1.");
}
@@ -452,7 +461,7 @@ public static byte[] hex2bytes(final String str, final int off, int len) {
int num = len / 2, r = off, w = 0;
byte[] b = new byte[num];
for (int i = 0; i < num; i++) {
- b[w++] = (byte) (hex(str.charAt(r++)) << 4 | hex(str.charAt(r++)));
+ b[w++] = (byte) (hex(str.charAt(r++)) << 4 | hex(str.charAt(r++))); // #1
}
return b;
}
@@ -473,7 +482,7 @@ public static String bytes2base64(byte[] b) {
* @param b byte array.
* @return base64 string.
*/
- public static String bytes2base64(byte[] b, int offset, int length) {
+ public static String bytes2base64(byte[] b, @IndexOrHigh("#1") int offset, @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int length) {
return bytes2base64(b, offset, length, BASE64);
}
@@ -484,7 +493,7 @@ public static String bytes2base64(byte[] b, int offset, int length) {
* @param code base64 code string(0-63 is base64 char,64 is pad char).
* @return base64 string.
*/
- public static String bytes2base64(byte[] b, String code) {
+ public static String bytes2base64(byte[] b, @MinLen(64) String code) {
return bytes2base64(b, 0, b.length, code);
}
@@ -495,7 +504,7 @@ public static String bytes2base64(byte[] b, String code) {
* @param code base64 code string(0-63 is base64 char,64 is pad char).
* @return base64 string.
*/
- public static String bytes2base64(byte[] b, int offset, int length, String code) {
+ public static String bytes2base64(byte[] b, @IndexOrHigh("#1") int offset, @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int length, @MinLen(64) String code) {
if (code.length() < 64) {
throw new IllegalArgumentException("Base64 code length < 64.");
}
@@ -510,7 +519,7 @@ public static String bytes2base64(byte[] b, int offset, int length, String code)
* @param code base64 code(0-63 is base64 char,64 is pad char).
* @return base64 string.
*/
- public static String bytes2base64(byte[] b, char[] code) {
+ public static String bytes2base64(byte[] b, char @MinLen(64) [] code) {
return bytes2base64(b, 0, b.length, code);
}
@@ -523,7 +532,12 @@ public static String bytes2base64(byte[] b, char[] code) {
* @param code base64 code(0-63 is base64 char,64 is pad char).
* @return base64 string.
*/
- public static String bytes2base64(final byte[] bs, final int off, final int len, final char[] code) {
+ @SuppressWarnings("index:array.access.unsafe") /*
+ #1 - #5. The loop stops at num steps, which is len / 3 and bigger than cs.length / 4. Every index used to access code is smaller than 64.
+ #6 - #15. If rem is 1 or 2, then cs and bs are larger, respectively. Every index used to access code is smaller than 64. If pad is true, then
+ it is safe to access it with an index of 64 and cs is larger as well.
+ */
+ public static String bytes2base64(final byte[] bs, final @IndexOrHigh("#1") int off, final @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int len, final char @MinLen(64) [] code) {
if (off < 0) {
throw new IndexOutOfBoundsException("bytes2base64: offset < 0, offset is " + off);
}
@@ -543,29 +557,29 @@ public static String bytes2base64(final byte[] bs, final int off, final int len,
char[] cs = new char[num * 4 + (rem == 0 ? 0 : pad ? 4 : rem + 1)];
for (int i = 0; i < num; i++) {
- int b1 = bs[r++] & MASK8, b2 = bs[r++] & MASK8, b3 = bs[r++] & MASK8;
+ int b1 = bs[r++] & MASK8, b2 = bs[r++] & MASK8, b3 = bs[r++] & MASK8; // #1
- cs[w++] = code[b1 >> 2];
- cs[w++] = code[(b1 << 4) & MASK6 | (b2 >> 4)];
- cs[w++] = code[(b2 << 2) & MASK6 | (b3 >> 6)];
- cs[w++] = code[b3 & MASK6];
+ cs[w++] = code[b1 >> 2]; // #2
+ cs[w++] = code[(b1 << 4) & MASK6 | (b2 >> 4)]; // #3
+ cs[w++] = code[(b2 << 2) & MASK6 | (b3 >> 6)]; // #4
+ cs[w++] = code[b3 & MASK6]; // #5
}
if (rem == 1) {
- int b1 = bs[r++] & MASK8;
- cs[w++] = code[b1 >> 2];
- cs[w++] = code[(b1 << 4) & MASK6];
+ int b1 = bs[r++] & MASK8; // #6
+ cs[w++] = code[b1 >> 2]; // #7
+ cs[w++] = code[(b1 << 4) & MASK6]; // #8
if (pad) {
- cs[w++] = code[64];
- cs[w++] = code[64];
+ cs[w++] = code[64]; // #9
+ cs[w++] = code[64]; // #10
}
} else if (rem == 2) {
- int b1 = bs[r++] & MASK8, b2 = bs[r++] & MASK8;
- cs[w++] = code[b1 >> 2];
- cs[w++] = code[(b1 << 4) & MASK6 | (b2 >> 4)];
- cs[w++] = code[(b2 << 2) & MASK6];
+ int b1 = bs[r++] & MASK8, b2 = bs[r++] & MASK8; // #11
+ cs[w++] = code[b1 >> 2]; // #12
+ cs[w++] = code[(b1 << 4) & MASK6 | (b2 >> 4)]; // #13
+ cs[w++] = code[(b2 << 2) & MASK6]; // #14
if (pad) {
- cs[w++] = code[64];
+ cs[w++] = code[64]; // #15
}
}
return new String(cs);
@@ -589,7 +603,7 @@ public static byte[] base642bytes(String str) {
* @param length length.
* @return byte array.
*/
- public static byte[] base642bytes(String str, int offset, int length) {
+ public static byte[] base642bytes(String str, @IndexOrHigh("#1") int offset, @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int length) {
return base642bytes(str, offset, length, C64);
}
@@ -600,7 +614,7 @@ public static byte[] base642bytes(String str, int offset, int length) {
* @param code base64 code(0-63 is base64 char,64 is pad char).
* @return byte array.
*/
- public static byte[] base642bytes(String str, String code) {
+ public static byte[] base642bytes(String str, @MinLen(64) String code) {
return base642bytes(str, 0, str.length(), code);
}
@@ -613,7 +627,14 @@ public static byte[] base642bytes(String str, String code) {
* @param code base64 code(0-63 is base64 char,64 is pad char).
* @return byte array.
*/
- public static byte[] base642bytes(final String str, final int off, final int len, final String code) {
+ @SuppressWarnings({"index:array.access.unsafe", "index:argument.type.incompatible", "index:array.length.negative"}) /*
+ #1.
+ #2.
+ #3. size cannot be negative because num is 0 only if len < 4. size gets negative if len is 0, and the code would crash at #1 if so
+ #4 - #8. The loop stops at num steps, which is len / 4 and size / 3.
+ #9 - #13. If rem is 2 or 3, then str has 2 or 3 more spaces, respectively.
+ */
+ public static byte[] base642bytes(final String str, final @IndexOrHigh("#1") int off, final @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int len, final @MinLen(64) String code) {
if (off < 0) {
throw new IndexOutOfBoundsException("base642bytes: offset < 0, offset is " + off);
}
@@ -640,11 +661,11 @@ public static byte[] base642bytes(final String str, final int off, final int len
}
char pc = code.charAt(64);
- if (str.charAt(off + len - 2) == pc) {
+ if (str.charAt(off + len - 2) == pc) { // #1
size -= 2;
--num;
rem = 2;
- } else if (str.charAt(off + len - 1) == pc) {
+ } else if (str.charAt(off + len - 1) == pc) { // #2
size--;
--num;
rem = 3;
@@ -658,25 +679,25 @@ public static byte[] base642bytes(final String str, final int off, final int len
}
int r = off, w = 0;
- byte[] b = new byte[size], t = decodeTable(code);
+ byte[] b = new byte[size], t = decodeTable(code); // #3
for (int i = 0; i < num; i++) {
- int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)];
- int c3 = t[str.charAt(r++)], c4 = t[str.charAt(r++)];
+ int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)]; // #4
+ int c3 = t[str.charAt(r++)], c4 = t[str.charAt(r++)]; // #5
- b[w++] = (byte) ((c1 << 2) | (c2 >> 4));
- b[w++] = (byte) ((c2 << 4) | (c3 >> 2));
- b[w++] = (byte) ((c3 << 6) | c4);
+ b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); // #6
+ b[w++] = (byte) ((c2 << 4) | (c3 >> 2)); // #7
+ b[w++] = (byte) ((c3 << 6) | c4); // #8
}
if (rem == 2) {
- int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)];
+ int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)]; // #9
- b[w++] = (byte) ((c1 << 2) | (c2 >> 4));
+ b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); // #10
} else if (rem == 3) {
- int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)], c3 = t[str.charAt(r++)];
+ int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)], c3 = t[str.charAt(r++)]; // #11
- b[w++] = (byte) ((c1 << 2) | (c2 >> 4));
- b[w++] = (byte) ((c2 << 4) | (c3 >> 2));
+ b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); // #12
+ b[w++] = (byte) ((c2 << 4) | (c3 >> 2)); // #13
}
return b;
}
@@ -688,7 +709,7 @@ public static byte[] base642bytes(final String str, final int off, final int len
* @param code base64 code(0-63 is base64 char,64 is pad char).
* @return byte array.
*/
- public static byte[] base642bytes(String str, char[] code) {
+ public static byte[] base642bytes(String str, char @MinLen(64) [] code) {
return base642bytes(str, 0, str.length(), code);
}
@@ -701,7 +722,14 @@ public static byte[] base642bytes(String str, char[] code) {
* @param code base64 code(0-63 is base64 char,64 is pad char).
* @return byte array.
*/
- public static byte[] base642bytes(final String str, final int off, final int len, final char[] code) {
+ @SuppressWarnings({"index:array.access.unsafe", "index:argument.type.incompatible", "index:array.length.negative"}) /*
+ #1.
+ #2.
+ #3. size cannot be negative because num is 0 only if len < 4. size gets negative if len is 0, and the code would crash at #1 if so
+ #4 - #8. The loop stops at num steps, which is len / 4 and size / 3.
+ #9 - #13. If rem is 2 or 3, then str has 2 or 3 more spaces, respectively.
+ */
+ public static byte[] base642bytes(final String str, final @IndexOrHigh("#1") int off, final @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int len, final char @MinLen(64) [] code) {
if (off < 0) {
throw new IndexOutOfBoundsException("base642bytes: offset < 0, offset is " + off);
}
@@ -728,9 +756,9 @@ public static byte[] base642bytes(final String str, final int off, final int len
}
char pc = code[64];
- if (str.charAt(off + len - 2) == pc) {
+ if (str.charAt(off + len - 2) == pc) { // #1
size -= 2;
- } else if (str.charAt(off + len - 1) == pc) {
+ } else if (str.charAt(off + len - 1) == pc) { // #2
size--;
}
} else {
@@ -742,25 +770,25 @@ public static byte[] base642bytes(final String str, final int off, final int len
}
int r = off, w = 0;
- byte[] b = new byte[size];
+ byte[] b = new byte[size]; // #3
for (int i = 0; i < num; i++) {
- int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++));
- int c3 = indexOf(code, str.charAt(r++)), c4 = indexOf(code, str.charAt(r++));
+ int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++)); // #4
+ int c3 = indexOf(code, str.charAt(r++)), c4 = indexOf(code, str.charAt(r++)); // #5
- b[w++] = (byte) ((c1 << 2) | (c2 >> 4));
- b[w++] = (byte) ((c2 << 4) | (c3 >> 2));
- b[w++] = (byte) ((c3 << 6) | c4);
+ b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); // #6
+ b[w++] = (byte) ((c2 << 4) | (c3 >> 2)); // #7
+ b[w++] = (byte) ((c3 << 6) | c4); // #8
}
if (rem == 2) {
- int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++));
+ int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++)); // #9
- b[w++] = (byte) ((c1 << 2) | (c2 >> 4));
+ b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); // #10
} else if (rem == 3) {
- int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++)), c3 = indexOf(code, str.charAt(r++));
+ int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++)), c3 = indexOf(code, str.charAt(r++)); // #11
- b[w++] = (byte) ((c1 << 2) | (c2 >> 4));
- b[w++] = (byte) ((c2 << 4) | (c3 >> 2));
+ b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); // #12
+ b[w++] = (byte) ((c2 << 4) | (c3 >> 2)); // #13
}
return b;
}
@@ -873,6 +901,10 @@ private static int indexOf(char[] cs, char c) {
return -1;
}
+ @SuppressWarnings({"index:array.access.unsafe", "index:argument.type.incompatible"}) /*
+ #1. ret has length of 128, the loop stops at position 127
+ #2. It was verified that code has at least 64 characters. The ascii value of a char is at most 127, which is a valid index for ret
+ */
private static byte[] decodeTable(String code) {
int hash = code.hashCode();
byte[] ret = DECODE_TABLE_MAP.get(hash);
@@ -884,17 +916,18 @@ private static byte[] decodeTable(String code) {
ret = new byte[128];
for (int i = 0; i < 128; i++) // init table.
{
- ret[i] = -1;
+ ret[i] = -1; // #1
}
for (int i = 0; i < 64; i++) {
- ret[code.charAt(i)] = (byte) i;
+ ret[code.charAt(i)] = (byte) i; // #2
}
DECODE_TABLE_MAP.put(hash, ret);
}
return ret;
}
- private static byte[] getMD5(InputStream is, int bs) throws IOException {
+ @SuppressWarnings("index:argument.type.incompatible") // The call to read is safe because the loop stops before total exceeds bs
+ private static byte[] getMD5(InputStream is, @NonNegative int bs) throws IOException {
MessageDigest md = getMessageDigest();
byte[] buf = new byte[bs];
while (is.available() > 0) {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/io/StreamUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/io/StreamUtils.java
index ab2836ef913..737a339c246 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/io/StreamUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/io/StreamUtils.java
@@ -16,6 +16,8 @@
*/
package org.apache.dubbo.common.io;
+import org.checkerframework.checker.index.qual.*;
+
import java.io.IOException;
import java.io.InputStream;
@@ -31,7 +33,7 @@ public static InputStream limitedInputStream(final InputStream is, final int lim
private int mPosition = 0, mMark = 0, mLimit = Math.min(limit, is.available());
@Override
- public int read() throws IOException {
+ public @GTENegativeOne int read() throws IOException {
if (mPosition < mLimit) {
mPosition++;
return is.read();
@@ -40,7 +42,7 @@ public int read() throws IOException {
}
@Override
- public int read(byte[] b, int off, int len) throws IOException {
+ public @GTENegativeOne @LTEqLengthOf("#1") int read(byte[] b, @IndexOrHigh("#1") int off, @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int len) throws IOException {
if (b == null) {
throw new NullPointerException();
}
@@ -67,7 +69,7 @@ public int read(byte[] b, int off, int len) throws IOException {
}
@Override
- public long skip(long len) throws IOException {
+ public @NonNegative long skip(long len) throws IOException {
if (mPosition + len > mLimit) {
len = mLimit - mPosition;
}
@@ -82,7 +84,7 @@ public long skip(long len) throws IOException {
}
@Override
- public int available() {
+ public @NonNegative int available() {
return mLimit - mPosition;
}
@@ -92,7 +94,7 @@ public boolean markSupported() {
}
@Override
- public void mark(int readlimit) {
+ public void mark(@NonNegative int readlimit) {
is.mark(readlimit);
mMark = mPosition;
}
@@ -110,7 +112,7 @@ public void close() throws IOException {
};
}
- public static InputStream markSupportedInputStream(final InputStream is, final int markBufferSize) {
+ public static InputStream markSupportedInputStream(final InputStream is, final @NonNegative int markBufferSize) {
if (is.markSupported()) {
return is;
}
@@ -125,7 +127,7 @@ public static InputStream markSupportedInputStream(final InputStream is, final i
private int mCount = 0;
@Override
- public int read() throws IOException {
+ public @GTENegativeOne int read() throws IOException {
if (!mInMarked) {
return is.read();
} else {
@@ -202,7 +204,7 @@ public boolean markSupported() {
}
@Override
- public int available() throws IOException {
+ public @NonNegative int available() throws IOException {
int available = is.available();
if (mInMarked && mInReset) {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeByteArrayInputStream.java b/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeByteArrayInputStream.java
index df96a1c1d01..f312ae0d6fd 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeByteArrayInputStream.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeByteArrayInputStream.java
@@ -19,35 +19,48 @@
import java.io.IOException;
import java.io.InputStream;
+import org.checkerframework.checker.index.qual.GTENegativeOne;
+import org.checkerframework.checker.index.qual.IndexFor;
+import org.checkerframework.checker.index.qual.IndexOrHigh;
+import org.checkerframework.checker.index.qual.LTEqLengthOf;
+import org.checkerframework.checker.index.qual.LTLengthOf;
+import org.checkerframework.checker.index.qual.NonNegative;
+
/**
* UnsafeByteArrayInputStream.
*/
public class UnsafeByteArrayInputStream extends InputStream {
protected byte[] mData;
- protected int mPosition, mLimit, mMark = 0;
+ protected @IndexOrHigh("this.mData") int mPosition, mMark, mLimit = 0;
public UnsafeByteArrayInputStream(byte[] buf) {
this(buf, 0, buf.length);
}
- public UnsafeByteArrayInputStream(byte[] buf, int offset) {
+ @SuppressWarnings("index:argument.type.incompatible") // The length of the array is always greater than the index used to access it.
+ public UnsafeByteArrayInputStream(byte[] buf, @IndexOrHigh("#1") int offset) {
this(buf, offset, buf.length - offset);
}
- public UnsafeByteArrayInputStream(byte[] buf, int offset, int length) {
+ public UnsafeByteArrayInputStream(byte[] buf, @IndexOrHigh("#1") int offset, @NonNegative int length) {
mData = buf;
mPosition = mMark = offset;
mLimit = Math.min(offset + length, buf.length);
}
@Override
- public int read() {
+ public @GTENegativeOne int read() {
return (mPosition < mLimit) ? (mData[mPosition++] & 0xff) : -1;
}
@Override
- public int read(byte[] b, int off, int len) {
+ @SuppressWarnings({"index:assignment.type.incompatible", "index:argument.type.incompatible", "index:return.type.incompatible"}) /*
+ #1. mLimit is greater than mPosition and after the assignment the value can only decrease, so it stays valid.
+ #2. The call is safe because len has been verified before.
+ #3. The return type is safe because len has been verified.
+ */
+ public @GTENegativeOne @LTEqLengthOf("#1") int read(byte[] b, @IndexOrHigh("#1") int off, @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int len) {
if (b == null) {
throw new NullPointerException();
}
@@ -58,18 +71,18 @@ public int read(byte[] b, int off, int len) {
return -1;
}
if (mPosition + len > mLimit) {
- len = mLimit - mPosition;
+ len = mLimit - mPosition; // #1
}
if (len <= 0) {
return 0;
}
- System.arraycopy(mData, mPosition, b, off, len);
+ System.arraycopy(mData, mPosition, b, off, len); // #2
mPosition += len;
- return len;
+ return len; // #3
}
@Override
- public long skip(long len) {
+ public @NonNegative long skip(long len) {
if (mPosition + len > mLimit) {
len = mLimit - mPosition;
}
@@ -81,7 +94,8 @@ public long skip(long len) {
}
@Override
- public int available() {
+ @SuppressWarnings("index:return.type.incompatible") // mPosition is always lower than mLimit
+ public @NonNegative int available() {
return mLimit - mPosition;
}
@@ -108,7 +122,7 @@ public int position() {
return mPosition;
}
- public void position(int newPosition) {
+ public void position(@IndexOrHigh("this.mData") int newPosition) {
mPosition = newPosition;
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeByteArrayOutputStream.java b/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeByteArrayOutputStream.java
index 6e75eb83109..f068e501d27 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeByteArrayOutputStream.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeByteArrayOutputStream.java
@@ -21,19 +21,24 @@
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
+import org.checkerframework.checker.index.qual.IndexFor;
+import org.checkerframework.checker.index.qual.IndexOrHigh;
+import org.checkerframework.checker.index.qual.LTLengthOf;
+import org.checkerframework.checker.index.qual.NonNegative;
+
/**
* UnsafeByteArrayOutputStream.
*/
public class UnsafeByteArrayOutputStream extends OutputStream {
protected byte[] mBuffer;
- protected int mCount;
+ protected @IndexOrHigh("this.mBuffer") int mCount;
public UnsafeByteArrayOutputStream() {
this(32);
}
- public UnsafeByteArrayOutputStream(int size) {
+ public UnsafeByteArrayOutputStream(@NonNegative int size) {
if (size < 0) {
throw new IllegalArgumentException("Negative initial size: " + size);
}
@@ -41,6 +46,7 @@ public UnsafeByteArrayOutputStream(int size) {
}
@Override
+ @SuppressWarnings({"index:array.access.unsafe", "index:assignment.type.incompatible"}) // If mCount becomes mBuffer.length, then the array is extended before accessing it
public void write(int b) {
int newcount = mCount + 1;
if (newcount > mBuffer.length) {
@@ -51,7 +57,9 @@ public void write(int b) {
}
@Override
- public void write(byte[] b, int off, int len) {
+ @SuppressWarnings({"index:argument.type.incompatible", "index:assignment.type.incompatible"}) // len is valid because if mCount + len exceeds mBuffer.length, then
+ // the array is extended
+ public void write(byte[] b, @IndexOrHigh("#1") int off, @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int len) {
if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeStringReader.java b/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeStringReader.java
index ad37476bd72..4a322617010 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeStringReader.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeStringReader.java
@@ -19,13 +19,19 @@
import java.io.IOException;
import java.io.Reader;
+import org.checkerframework.checker.index.qual.GTENegativeOne;
+import org.checkerframework.checker.index.qual.IndexOrHigh;
+import org.checkerframework.checker.index.qual.LTEqLengthOf;
+import org.checkerframework.checker.index.qual.LTLengthOf;
+import org.checkerframework.checker.index.qual.NonNegative;
+
/**
* Thread-unsafe StringReader.
*/
public class UnsafeStringReader extends Reader {
private String mString;
- private int mPosition, mLimit, mMark;
+ private @IndexOrHigh("this.mString") int mPosition, mLimit, mMark;
public UnsafeStringReader(String str) {
mString = str;
@@ -34,7 +40,8 @@ public UnsafeStringReader(String str) {
}
@Override
- public int read() throws IOException {
+ @SuppressWarnings("index:return.type.incompatible") // A char is always greater than 0
+ public @GTENegativeOne int read() throws IOException {
ensureOpen();
if (mPosition >= mLimit) {
return -1;
@@ -44,7 +51,11 @@ public int read() throws IOException {
}
@Override
- public int read(char[] cs, int off, int len) throws IOException {
+ @SuppressWarnings({"index:argument.type.incompatible", "index:compound.assignment.type.incompatible", "index:return.type.incompatible"}) /*
+ #1 and #2. mPosition + n is at most mLimit, which is a valid index
+ #3. Both mLimit - mPosition and len have been verified, so the returned variable is correct
+ */
+ public @GTENegativeOne @LTEqLengthOf("#1") int read(char[] cs, @IndexOrHigh("#1") int off, @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int len) throws IOException {
ensureOpen();
if ((off < 0) || (off > cs.length) || (len < 0) ||
((off + len) > cs.length) || ((off + len) < 0)) {
@@ -60,13 +71,14 @@ public int read(char[] cs, int off, int len) throws IOException {
}
int n = Math.min(mLimit - mPosition, len);
- mString.getChars(mPosition, mPosition + n, cs, off);
- mPosition += n;
- return n;
+ mString.getChars(mPosition, mPosition + n, cs, off); // #1
+ mPosition += n; // #2
+ return n; // #3
}
@Override
- public long skip(long ns) throws IOException {
+ @SuppressWarnings("index:compound.assignment.type.incompatible") // n is valid because it was previously verified
+ public @NonNegative long skip(@NonNegative long ns) throws IOException {
ensureOpen();
if (mPosition >= mLimit) {
return 0;
@@ -90,7 +102,7 @@ public boolean markSupported() {
}
@Override
- public void mark(int readAheadLimit) throws IOException {
+ public void mark(@NonNegative int readAheadLimit) throws IOException {
if (readAheadLimit < 0) {
throw new IllegalArgumentException("Read-ahead limit < 0");
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeStringWriter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeStringWriter.java
index 47e9a3a1aa5..7661943e15c 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeStringWriter.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeStringWriter.java
@@ -19,6 +19,10 @@
import java.io.IOException;
import java.io.Writer;
+import org.checkerframework.checker.index.qual.IndexOrHigh;
+import org.checkerframework.checker.index.qual.LTLengthOf;
+import org.checkerframework.checker.index.qual.NonNegative;
+
/**
* Thread-unsafe StringWriter.
*/
@@ -29,7 +33,7 @@ public UnsafeStringWriter() {
lock = mBuffer = new StringBuilder();
}
- public UnsafeStringWriter(int size) {
+ public UnsafeStringWriter(@NonNegative int size) {
if (size < 0) {
throw new IllegalArgumentException("Negative buffer size");
}
@@ -48,7 +52,7 @@ public void write(char[] cs) throws IOException {
}
@Override
- public void write(char[] cs, int off, int len) throws IOException {
+ public void write(char[] cs, @IndexOrHigh("#1") int off, @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int len) throws IOException {
if ((off < 0) || (off > cs.length) || (len < 0) ||
((off + len) > cs.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
@@ -65,7 +69,7 @@ public void write(String str) {
}
@Override
- public void write(String str, int off, int len) {
+ public void write(String str, @IndexOrHigh("#1") int off, @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int len) {
mBuffer.append(str.substring(off, off + len));
}
@@ -80,7 +84,10 @@ public Writer append(CharSequence csq) {
}
@Override
- public Writer append(CharSequence csq, int start, int end) {
+ @SuppressWarnings("index:argument.type.incompatible")
+ // The method can throw an IndexOutOfBoundsException when subsequence(int, int) is called, but this is documented in the overridden method:
+ // https://docs.oracle.com/javase/8/docs/api/java/lang/CharSequence.html#subSequence-int-int-
+ public Writer append(CharSequence csq, @IndexOrHigh("#1") int start, @IndexOrHigh("#1") int end) {
CharSequence cs = (csq == null ? "null" : csq);
write(cs.subSequence(start, end).toString());
return this;
diff --git a/pom.xml b/pom.xml
index 16afc2714ff..572703ac7d7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -194,6 +194,16 @@
${cglib_version}
test
+
+ org.checkerframework
+ checker
+ 2.8.1
+
+
+ org.checkerframework
+ jdk8
+ 2.8.1
+
@@ -443,7 +453,6 @@
maven-compiler-plugin
${maven_compiler_version}
- -proc:none
true
${java_target_version}
@@ -621,6 +630,26 @@
jetty-maven-plugin
${maven_jetty_version}
+
+ maven-compiler-plugin
+ 3.3
+
+
+ 1.8
+
+ 10000
+ 10000
+
+
+ org.checkerframework.checker.index.IndexChecker
+
+
+ -Xbootclasspath/p:$CHECKERFRAMEWORK/checker/dist/jdk8.jar
+ -AprintErrorStack
+ -AonlyDefs=^org\.apache\.dubbo\.common\.io\.
+
+
+