16
16
package com .headius .invokebinder .transform ;
17
17
18
18
import com .headius .invokebinder .Binder ;
19
+ import com .headius .invokebinder .Util ;
19
20
20
21
import java .lang .invoke .MethodHandle ;
21
22
import java .lang .invoke .MethodHandles ;
22
23
import java .lang .invoke .MethodType ;
23
24
24
25
/**
25
- * An argument-boxing transform with a fixed incoming size .
26
+ * A filter that takes multiple arguments and replaces them with zero or one argument of a new type .
26
27
*
27
28
* Equivalent call: MethodHandle.asCollector(Class, int) or MethodHandles.collectArguments
28
29
*/
@@ -31,61 +32,68 @@ public class Collect extends Transform {
31
32
private final MethodType source ;
32
33
private final int index ;
33
34
private final int count ;
34
- private final Class <?> arrayType ;
35
+ private final Class <?> resultType ;
35
36
private final MethodHandle collector ;
36
37
37
- public Collect (MethodType source , int index , Class <?> arrayType ) {
38
+ public Collect (MethodType source , int index , Class <?> resultType ) {
38
39
this .source = source ;
39
40
this .index = index ;
40
41
this .count = source .parameterCount () - index ;
41
- this .arrayType = arrayType ;
42
+ this .resultType = resultType ;
42
43
this .collector = null ;
43
44
}
44
45
45
- public Collect (MethodType source , int index , Class <?> arrayType , MethodHandle collector ) {
46
+ public Collect (MethodType source , int index , Class <?> resultType , MethodHandle collector ) {
46
47
this .source = source ;
47
48
this .index = index ;
48
49
this .count = source .parameterCount () - index ;
49
- this .arrayType = arrayType ;
50
+ this .resultType = resultType ;
50
51
this .collector = collector ;
51
52
}
52
53
53
- public Collect (MethodType source , int index , int count , Class <?> arrayType ) {
54
+ public Collect (MethodType source , int index , int count , Class <?> resultType ) {
54
55
this .source = source ;
55
56
this .index = index ;
56
57
this .count = count ;
57
- this .arrayType = arrayType ;
58
+ this .resultType = resultType ;
58
59
this .collector = null ;
59
60
}
60
61
61
- public Collect (MethodType source , int index , int count , Class <?> arrayType , MethodHandle collector ) {
62
+ public Collect (MethodType source , int index , int count , Class <?> resultType , MethodHandle collector ) {
62
63
this .source = source ;
63
64
this .index = index ;
64
65
this .count = count ;
65
- this .arrayType = arrayType ;
66
+ this .resultType = resultType ;
66
67
this .collector = collector ;
67
68
}
68
69
69
70
public MethodHandle up (MethodHandle target ) {
70
- if (onlyTail ()) {
71
- // fast path for tail args
72
- if (collector == null ) {
73
- return target .asCollector (arrayType , count );
71
+ if (collector == null ) {
72
+ if (Util .isJava9 ()) {
73
+ // Java 9 can collect a subset of non-tail arguments
74
+ return target .asCollector (index , resultType , count );
75
+ } else {
76
+ if (onlyTail ()) {
77
+ // tail arguments can be array-collected on all Java versions
78
+ return target .asCollector (resultType , count );
79
+ } else {
80
+ // non-tail arguments must be permuted prior to Java 9
81
+ Permutes permutes = buildPermutes (source , target .type ());
82
+
83
+ Binder binder = preparePermuteBinder (permutes );
84
+ return binder .invoke (target );
85
+ }
74
86
}
75
-
76
- return MethodHandles .collectArguments (target , index , collector );
77
87
} else {
78
- Permutes permutes = buildPermutes (source , target .type ());
79
-
80
- Binder binder = preparePermuteBinder (permutes );
81
- return binder .invoke (target );
88
+ // custom collector always collects only as many args as it accepts
89
+ return MethodHandles .collectArguments (target , index , collector );
82
90
}
83
91
}
84
92
85
93
private Binder preparePermuteBinder (Permutes permutes ) {
86
94
return Binder .from (source )
87
95
.permute (permutes .movePermute )
88
- .collect (source .parameterCount () - count , arrayType , collector )
96
+ .collect (source .parameterCount () - count , resultType )
89
97
.permute (permutes .moveBackPermute );
90
98
}
91
99
@@ -94,28 +102,39 @@ public MethodType down(MethodType type) {
94
102
95
103
return type
96
104
.dropParameterTypes (index , index + count )
97
- .insertParameterTypes (index , arrayType );
105
+ .insertParameterTypes (index , resultType );
98
106
}
99
107
100
108
private void assertTypesAreCompatible () {
101
- Class <?> componentType = arrayType .getComponentType ();
102
- for (int i = index ; i < index + count ; i ++) {
103
- Class <?> in = source .parameterType (i );
104
- assert in .isAssignableFrom (componentType )
105
- : "incoming type " + in .getName () + " not compatible with " + componentType .getName () + "[]" ;
109
+ if (collector == null ) {
110
+ // default array collector
111
+ assert resultType .isArray () : "no collector provided but target type is not array" ;
112
+ Class <?> componentType = resultType .getComponentType ();
113
+ for (int i = index ; i < index + count ; i ++) {
114
+ Class <?> in = source .parameterType (i );
115
+ assert in .isAssignableFrom (componentType )
116
+ : "incoming type " + in .getName () + " not compatible with " + componentType .getName () + "[]" ;
117
+ }
118
+ } else {
119
+ for (int i = 0 ; i < count ; i ++) {
120
+ Class <?> in = source .parameterType (index + i );
121
+ Class <?> out = collector .type ().parameterType (i );
122
+ assert in .isAssignableFrom (out ) : "incoming type " + in .getName () + " not compatible with " + out ;
123
+ }
124
+ assert collector .type ().returnType ().isAssignableFrom (resultType );
106
125
}
107
126
}
108
127
109
128
public String toString () {
110
- return "collect at " + index + " into " + arrayType .getName ();
129
+ return "collect at " + index + " into " + resultType .getName ();
111
130
}
112
131
113
132
public String toJava (MethodType incoming ) {
114
133
StringBuilder builder = new StringBuilder ();
115
134
if (onlyTail ()) {
116
135
if (collector == null ) {
117
136
builder .append ("handle = handle.asCollector(" );
118
- buildClassArgument (builder , arrayType );
137
+ buildClassArgument (builder , resultType );
119
138
builder
120
139
.append (", " )
121
140
.append (count )
@@ -128,7 +147,7 @@ public String toJava(MethodType incoming) {
128
147
.append (count )
129
148
.append (", " );
130
149
131
- buildClassArgument (builder , arrayType );
150
+ buildClassArgument (builder , resultType );
132
151
133
152
builder .append (");" );
134
153
}
0 commit comments