1111import java .lang .reflect .Modifier ;
1212import java .util .ArrayList ;
1313import java .util .Map ;
14+ import java .util .Optional ;
1415import java .util .concurrent .Callable ;
1516import java .util .regex .Matcher ;
1617import 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