Skip to content

Commit a6510af

Browse files
committed
Mock: Add multidimensional type class
While mocking native interface, it will fail with error such as: ``` Caused by: java.lang.ClassNotFoundException: Invalid name: [Lbyte[]; at java.lang.Class.classForName(Native Method) at java.lang.Class.forName(Class.java:597) at java.lang.Class.forName(Class.java:502) at com.android.dx.mockito.inline.MockMethodAdvice.getOrigin(MockMethodAdvice.java:166) ``` Indeed "[Lbyte[]" is not correct as the class is a "[[B". Adding entries in MockMethodAdvice to do the conversion with a non specified number of dimension for primitive and qualified type
1 parent 51aea60 commit a6510af

File tree

1 file changed

+60
-27
lines changed

1 file changed

+60
-27
lines changed

dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/MockMethodAdvice.java

Lines changed: 60 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.lang.reflect.Modifier;
1212
import java.util.ArrayList;
1313
import java.util.Map;
14+
import java.util.Optional;
1415
import java.util.concurrent.Callable;
1516
import java.util.regex.Matcher;
1617
import java.util.regex.Pattern;
@@ -25,6 +26,9 @@ class MockMethodAdvice {
2526
/** Pattern to decompose a instrumentedMethodWithTypeAndSignature */
2627
private final Pattern methodPattern = Pattern.compile("(.*)#(.*)\\((.*)\\)");
2728

29+
/** Pattern to verifies types description is ending only with array type */
30+
private static final Pattern ARRAY_PATTERN = Pattern.compile("(\\[\\])+");
31+
2832
@SuppressWarnings("ThreadLocalUsage")
2933
private final SelfCallInfo selfCallInfo = new SelfCallInfo();
3034

@@ -86,6 +90,59 @@ private static Throwable hideRecursiveCall(Throwable throwable, int current,
8690
}
8791
}
8892

93+
private static final Map<String, String> PRIMITIVE_CLASS_TO_SIGNATURE =
94+
Map.of(
95+
"byte", "B",
96+
"short", "S",
97+
"int", "I",
98+
"long", "J",
99+
"char", "C",
100+
"float", "F",
101+
"double", "D",
102+
"boolean", "Z");
103+
104+
/**
105+
* Convert a type signature of an array to the corresponding class
106+
*
107+
* <p>It parse "foo[][][][]" into a jni signature "[[[[Lfoo;" and rely on Class.forName to do
108+
* the conversion. Primivite type are converted using {@code PRIMITIVE_CLASS_TO_SIGNATURE}.
109+
*
110+
* @param argTypeName the type description (e.g: byte[])
111+
* @return the class wrapped in Optional on success, or empty if the conversion failed
112+
*/
113+
private static Optional<Class<?>> parseTypeName(String argTypeName) {
114+
int index = argTypeName.indexOf("[");
115+
if (index == -1) {
116+
// not an Array type
117+
return Optional.empty();
118+
}
119+
120+
String typeName = argTypeName.substring(0, index);
121+
String rest = argTypeName.substring(index, argTypeName.length());
122+
123+
if (!ARRAY_PATTERN.matcher(rest).matches()) {
124+
return Optional.empty();
125+
}
126+
int dimensionCount = (int) argTypeName.chars().filter(ch -> ch == '[').count();
127+
128+
String classSignature =
129+
PRIMITIVE_CLASS_TO_SIGNATURE.getOrDefault(typeName, "L" + typeName + ";");
130+
131+
StringBuilder sb = new StringBuilder();
132+
// "[".repeat(dimensionCount) would be a shorter alternative but this is not available on
133+
// all android test target. See b/396768441
134+
for (int i = 0; i < dimensionCount; i++) {
135+
sb.append("[");
136+
}
137+
sb.append(classSignature);
138+
String fullTypeSignature = sb.toString();
139+
try {
140+
return Optional.of(Class.forName(fullTypeSignature));
141+
} catch (ClassNotFoundException e) {
142+
return Optional.empty();
143+
}
144+
}
145+
89146
/**
90147
* Get the method of {@code instance} specified by {@code methodWithTypeAndSignature}.
91148
*
@@ -135,34 +192,10 @@ public Method getOrigin(Object instance, String methodWithTypeAndSignature) thro
135192
case "boolean":
136193
argTypes.add(Boolean.TYPE);
137194
break;
138-
case "byte[]":
139-
argTypes.add(byte[].class);
140-
break;
141-
case "short[]":
142-
argTypes.add(short[].class);
143-
break;
144-
case "int[]":
145-
argTypes.add(int[].class);
146-
break;
147-
case "long[]":
148-
argTypes.add(long[].class);
149-
break;
150-
case "char[]":
151-
argTypes.add(char[].class);
152-
break;
153-
case "float[]":
154-
argTypes.add(float[].class);
155-
break;
156-
case "double[]":
157-
argTypes.add(double[].class);
158-
break;
159-
case "boolean[]":
160-
argTypes.add(boolean[].class);
161-
break;
162195
default:
163-
if (argTypeName.endsWith("[]")) {
164-
argTypes.add(Class.forName("[L" + argTypeName.substring(0,
165-
argTypeName.length() - 2) + ";"));
196+
Optional<Class<?>> arrayClass = parseTypeName(argTypeName);
197+
if (arrayClass.isPresent()) {
198+
argTypes.add(arrayClass.get());
166199
} else {
167200
argTypes.add(Class.forName(argTypeName));
168201
}

0 commit comments

Comments
 (0)