Skip to content

Commit de5b4f3

Browse files
authored
Further enhancements to parseForTypeSignatures (#1478)
We generalize the method so it is useful for parsing either method argument types or generic type arguments. (The latter required handling wildcards.) Also factor out some common logic, and make the method public so it can be used from other projects.
1 parent ac47859 commit de5b4f3

File tree

2 files changed

+121
-38
lines changed

2 files changed

+121
-38
lines changed

core/src/main/java/com/ibm/wala/types/generics/TypeSignature.java

Lines changed: 47 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import com.ibm.wala.types.TypeReference;
1414
import com.ibm.wala.util.debug.Assertions;
1515
import java.util.ArrayList;
16-
import java.util.Iterator;
1716

1817
/**
1918
* UNDER CONSTRUCTION.
@@ -81,13 +80,21 @@ public static TypeSignature make(String s) throws IllegalArgumentException {
8180
public abstract boolean isBaseType();
8281

8382
/**
84-
* @param typeSigs TypeSignature*
85-
* @return tokenize it
83+
* Split a string of consecutive type signatures (TypeSignature*) into its top-level type
84+
* signatures. The string should start with either {@code (} or {@code <} and have a respective
85+
* matching {@code )} or {@code >}.
86+
*
87+
* @param typeSigs a string of consecutive type signatures
88+
* @return an array of top-level type signatures
8689
*/
87-
static String[] parseForTypeSignatures(String typeSigs) throws IllegalArgumentException {
90+
public static String[] parseForTypeSignatures(String typeSigs) throws IllegalArgumentException {
8891
ArrayList<String> sigs = new ArrayList<>(10);
92+
char start = typeSigs.charAt(0);
93+
if (start != '(' && start != '<') {
94+
throw new IllegalArgumentException(
95+
"illegal start of TypeSignature " + typeSigs + ", must be '(' or '<'");
96+
}
8997
if (typeSigs.length() < 2) {
90-
// TODO: check this?
9198
throw new IllegalArgumentException("illegal string of TypeSignature " + typeSigs);
9299
}
93100

@@ -124,20 +131,16 @@ static String[] parseForTypeSignatures(String typeSigs) throws IllegalArgumentEx
124131
case TypeReference.ClassTypeCode:
125132
{
126133
int off = i - 1;
127-
int depth = 0;
128-
while (typeSigs.charAt(i++) != ';' || depth > 0) {
129-
if (typeSigs.charAt(i - 1) == '<') {
130-
depth++;
131-
}
132-
if (typeSigs.charAt(i - 1) == '>') {
133-
depth--;
134-
}
135-
}
134+
i = getEndIndexOfClassType(typeSigs, i);
136135
sigs.add(typeSigs.substring(off, i));
137136
continue;
138137
}
139138
case TypeReference.ArrayTypeCode:
140139
{
140+
int arrayStart = i - 1;
141+
while (typeSigs.charAt(i) == TypeReference.ArrayTypeCode) {
142+
i++;
143+
}
141144
switch (typeSigs.charAt(i)) {
142145
case TypeReference.BooleanTypeCode:
143146
case TypeReference.ByteTypeCode:
@@ -147,23 +150,14 @@ static String[] parseForTypeSignatures(String typeSigs) throws IllegalArgumentEx
147150
case TypeReference.FloatTypeCode:
148151
case TypeReference.DoubleTypeCode:
149152
case TypeReference.CharTypeCode:
150-
sigs.add(typeSigs.substring(i - 1, i + 1));
153+
sigs.add(typeSigs.substring(arrayStart, i + 1));
151154
i++;
152155
break;
153156
case 'T':
154157
case TypeReference.ClassTypeCode:
155-
int off = i - 1;
156-
i++;
157-
int depth = 0;
158-
while (typeSigs.charAt(i++) != ';' || depth > 0) {
159-
if (typeSigs.charAt(i - 1) == '<') {
160-
depth++;
161-
}
162-
if (typeSigs.charAt(i - 1) == '>') {
163-
depth--;
164-
}
165-
}
166-
sigs.add(typeSigs.substring(off, i));
158+
i++; // to skip 'L' or 'T'
159+
i = getEndIndexOfClassType(typeSigs, i);
160+
sigs.add(typeSigs.substring(arrayStart, i));
167161
break;
168162
default:
169163
Assertions.UNREACHABLE("BANG " + typeSigs.charAt(i));
@@ -178,20 +172,35 @@ static String[] parseForTypeSignatures(String typeSigs) throws IllegalArgumentEx
178172
sigs.add(typeSigs.substring(off, i));
179173
continue;
180174
}
175+
case (byte) '*': // unbounded wildcard
176+
sigs.add("*");
177+
break;
178+
case (byte) '-': // bounded wildcard
179+
case (byte) '+': // bounded wildcard
180+
int boundedStart = i - 1;
181+
i++; // to skip 'L'
182+
i = getEndIndexOfClassType(typeSigs, i);
183+
sigs.add(typeSigs.substring(boundedStart, i));
184+
break;
181185
case (byte) ')': // end of parameter list
182-
int size = sigs.size();
183-
if (size == 0) {
184-
return null;
185-
}
186-
Iterator<String> it = sigs.iterator();
187-
String[] result = new String[size];
188-
for (int j = 0; j < size; j++) {
189-
result[j] = it.next();
190-
}
191-
return result;
186+
case (byte) '>': // end of type argument list
187+
return sigs.toArray(new String[sigs.size()]);
192188
default:
193-
assert false : "bad type signature list " + typeSigs;
189+
throw new IllegalArgumentException("bad type signature list " + typeSigs);
190+
}
191+
}
192+
}
193+
194+
private static int getEndIndexOfClassType(String typeSigs, int i) {
195+
int depth = 0;
196+
while (typeSigs.charAt(i++) != ';' || depth > 0) {
197+
if (typeSigs.charAt(i - 1) == '<') {
198+
depth++;
199+
}
200+
if (typeSigs.charAt(i - 1) == '>') {
201+
depth--;
194202
}
195203
}
204+
return i;
196205
}
197206
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package com.ibm.wala.types.generics;
2+
3+
import static org.hamcrest.MatcherAssert.assertThat;
4+
import static org.hamcrest.Matchers.arrayContaining;
5+
import static org.hamcrest.Matchers.is;
6+
7+
import com.ibm.wala.classLoader.IClass;
8+
import com.ibm.wala.classLoader.IMethod;
9+
import com.ibm.wala.classLoader.ShrikeCTMethod;
10+
import com.ibm.wala.core.tests.callGraph.CallGraphTestUtil;
11+
import com.ibm.wala.core.tests.util.TestConstants;
12+
import com.ibm.wala.ipa.callgraph.AnalysisScope;
13+
import com.ibm.wala.ipa.cha.ClassHierarchy;
14+
import com.ibm.wala.ipa.cha.ClassHierarchyException;
15+
import com.ibm.wala.ipa.cha.ClassHierarchyFactory;
16+
import com.ibm.wala.shrike.shrikeCT.InvalidClassFileException;
17+
import java.io.IOException;
18+
import org.junit.jupiter.api.Test;
19+
20+
public class TypeSignatureTest {
21+
22+
@Test
23+
void basicTypeArguments() {
24+
assertThat(
25+
TypeSignature.parseForTypeSignatures("<Ljava/lang/String;ILjava/lang/Object;>"),
26+
arrayContaining(is("Ljava/lang/String;"), is("I"), is("Ljava/lang/Object;")));
27+
}
28+
29+
@Test
30+
void multiDimArray() {
31+
assertThat(
32+
TypeSignature.parseForTypeSignatures("<[[Ljava/lang/String;[[[J>"),
33+
arrayContaining(is("[[Ljava/lang/String;"), is("[[[J")));
34+
}
35+
36+
@Test
37+
void wildcards() {
38+
assertThat(
39+
TypeSignature.parseForTypeSignatures("<B*J>"), arrayContaining(is("B"), is("*"), is("J")));
40+
assertThat(
41+
TypeSignature.parseForTypeSignatures("<+Ljava/lang/Object;>"),
42+
arrayContaining(is("+Ljava/lang/Object;")));
43+
assertThat(
44+
TypeSignature.parseForTypeSignatures("<-Ljava/lang/Double;BB>"),
45+
arrayContaining(is("-Ljava/lang/Double;"), is("B"), is("B")));
46+
}
47+
48+
@Test
49+
void testAllGenericMethodSigs()
50+
throws IOException, ClassHierarchyException, InvalidClassFileException {
51+
AnalysisScope scope =
52+
CallGraphTestUtil.makeJ2SEAnalysisScope(
53+
TestConstants.WALA_TESTDATA, "J2SEClassHierarchyExclusions.txt");
54+
ClassHierarchy cha = ClassHierarchyFactory.make(scope);
55+
for (IClass klass : cha) {
56+
for (IMethod m : klass.getDeclaredMethods()) {
57+
if (m instanceof ShrikeCTMethod) {
58+
ShrikeCTMethod method = (ShrikeCTMethod) m;
59+
MethodTypeSignature methodTypeSignature = method.getMethodTypeSignature();
60+
if (methodTypeSignature != null) {
61+
String typeSigStr = methodTypeSignature.toString();
62+
for (int i = 0; i < typeSigStr.length(); i++) {
63+
if ((typeSigStr.charAt(i) == '<' && i != 0) || typeSigStr.charAt(i) == '(') {
64+
// parsing will automatically end at the matching '>' or ')'
65+
// this is just testing for crashes
66+
TypeSignature.parseForTypeSignatures(typeSigStr.substring(i));
67+
}
68+
}
69+
}
70+
}
71+
}
72+
}
73+
}
74+
}

0 commit comments

Comments
 (0)