1
1
package cucumber .runtime .java8 ;
2
2
3
- import cucumber .runtime .CucumberException ;
3
+ import static java .lang .Class .forName ;
4
+ import static java .lang .System .arraycopy ;
5
+ import static jdk .internal .org .objectweb .asm .Type .getObjectType ;
6
+
4
7
import cucumber .api .java8 .StepdefBody ;
8
+ import cucumber .runtime .CucumberException ;
5
9
import cucumber .runtime .java .TypeIntrospector ;
6
10
import sun .reflect .ConstantPool ;
7
11
8
12
import java .lang .reflect .Method ;
9
13
import java .lang .reflect .Type ;
10
- import java .util .Arrays ;
11
- import java .util .List ;
12
14
13
15
public class ConstantPoolTypeIntrospector implements TypeIntrospector {
14
16
private static final Method Class_getConstantPool ;
17
+ private static final int REFERENCE_CLASS = 0 ;
18
+ private static final int REFERENCE_METHOD = 1 ;
19
+ private static final int REFERENCE_ARGUMENT_TYPES = 2 ;
15
20
16
21
static {
17
22
try {
@@ -26,37 +31,63 @@ public class ConstantPoolTypeIntrospector implements TypeIntrospector {
26
31
27
32
@ Override
28
33
public Type [] getGenericTypes (Class <? extends StepdefBody > clazz , Class <? extends StepdefBody > interfac3 ) throws Exception {
29
- ConstantPool constantPool = (ConstantPool ) Class_getConstantPool .invoke (clazz );
30
- String typeString = getLambdaTypeString (constantPool );
31
- int typeParameterCount = interfac3 .getTypeParameters ().length ;
32
- jdk .internal .org .objectweb .asm .Type [] argumentTypes = jdk .internal .org .objectweb .asm .Type .getArgumentTypes (typeString );
34
+ final ConstantPool constantPool = (ConstantPool ) Class_getConstantPool .invoke (clazz );
35
+ final String [] member = getMemberReference (constantPool );
36
+ final int parameterCount = interfac3 .getTypeParameters ().length ;
37
+
38
+ final jdk .internal .org .objectweb .asm .Type [] argumentTypes = jdk .internal .org .objectweb .asm .Type .getArgumentTypes (member [REFERENCE_ARGUMENT_TYPES ]);
39
+
40
+ // If we are one parameter short, this is a
41
+ // - Reference to an instance method of an arbitrary object of a particular type
42
+ if (parameterCount - 1 == argumentTypes .length ) {
43
+ return handleMethodReferenceToObjectOfType (member [REFERENCE_CLASS ], handleLambda (argumentTypes , parameterCount - 1 ));
44
+ }
45
+ // If we are not short on parameters this either
46
+ // - Reference to a static method
47
+ // - Reference to an instance method of a particular object
48
+ // - Reference to a constructor
49
+ // - A lambda expression
50
+ // We can all treat these as lambda's for figuring out the types.
51
+ return handleLambda (argumentTypes , parameterCount );
52
+ }
53
+
54
+ private static Type [] handleMethodReferenceToObjectOfType (String containingType , Type [] methodArgumentTypes ) throws ClassNotFoundException {
55
+ Type [] containingTypeAndMethodArgumentTypes = new Type [methodArgumentTypes .length + 1 ];
56
+ containingTypeAndMethodArgumentTypes [0 ] = forName (getObjectType (containingType ).getClassName ());
57
+ arraycopy (methodArgumentTypes , 0 , containingTypeAndMethodArgumentTypes , 1 , methodArgumentTypes .length );
58
+ return containingTypeAndMethodArgumentTypes ;
59
+ }
60
+
61
+ private static Type [] handleLambda (jdk .internal .org .objectweb .asm .Type [] argumentTypes , int typeParameterCount ) throws ClassNotFoundException {
62
+ if (argumentTypes .length < typeParameterCount ) {
63
+ throw new CucumberException (String .format ("Expected at least %s arguments but found only %s" , typeParameterCount , argumentTypes .length ));
64
+ }
65
+
33
66
// Only look at the N last arguments to the lambda static method, since the first ones might be variables
34
67
// who only pass in the states of closed variables
35
- List < jdk .internal .org .objectweb .asm .Type > interestingArgumentTypes = Arrays . asList ( argumentTypes )
36
- . subList (argumentTypes .length - typeParameterCount , argumentTypes . length );
68
+ jdk .internal .org .objectweb .asm .Type [] interestingArgumentTypes = new jdk . internal . org . objectweb . asm . Type [ typeParameterCount ];
69
+ arraycopy (argumentTypes , argumentTypes .length - typeParameterCount , interestingArgumentTypes , 0 , typeParameterCount );
37
70
38
71
Type [] typeArguments = new Type [typeParameterCount ];
39
72
for (int i = 0 ; i < typeParameterCount ; i ++) {
40
- typeArguments [i ] = Class . forName (interestingArgumentTypes . get ( i ) .getClassName ());
73
+ typeArguments [i ] = forName (interestingArgumentTypes [ i ] .getClassName ());
41
74
}
42
75
return typeArguments ;
43
76
}
44
77
45
- private String getLambdaTypeString (ConstantPool constantPool ) {
78
+ private static String [] getMemberReference (ConstantPool constantPool ) {
46
79
int size = constantPool .getSize ();
47
- String [] memberRef = null ;
48
80
49
81
// find last element in constantPool with valid memberRef
50
82
// - previously always at size-2 index but changed with JDK 1.8.0_60
51
83
for (int i = size - 1 ; i > -1 ; i --) {
52
84
try {
53
- memberRef = constantPool .getMemberRefInfoAt (i );
54
- return memberRef [2 ];
85
+ return constantPool .getMemberRefInfoAt (i );
55
86
} catch (IllegalArgumentException e ) {
56
87
// eat error; null entry at ConstantPool index?
57
88
}
58
89
}
59
- throw new CucumberException ("Couldn't find memberRef." );
90
+ throw new CucumberException ("Couldn't find memberRef." );
60
91
}
61
92
62
93
}
0 commit comments