74 pullModeBSM = pullMode ? bootstrapMethod : |
75 pullModeBSM = pullMode ? bootstrapMethod : |
75 pushMePullYou(bootstrapMethod, false); |
76 pushMePullYou(bootstrapMethod, false); |
76 bootstrapMethod = null; |
77 bootstrapMethod = null; |
77 } |
78 } |
78 try { |
79 try { |
|
80 // As an optimization we special case various known BSMs, |
|
81 // such as LambdaMetafactory::metafactory and |
|
82 // StringConcatFactory::makeConcatWithConstants. |
|
83 // |
|
84 // By providing static type information or even invoking |
|
85 // exactly, we avoid emitting code to perform runtime |
|
86 // checking. |
79 info = maybeReBox(info); |
87 info = maybeReBox(info); |
80 if (info == null) { |
88 if (info == null) { |
81 // VM is allowed to pass up a null meaning no BSM args |
89 // VM is allowed to pass up a null meaning no BSM args |
82 result = bootstrapMethod.invoke(caller, name, type); |
90 result = invoke(bootstrapMethod, caller, name, type); |
83 } |
91 } |
84 else if (!info.getClass().isArray()) { |
92 else if (!info.getClass().isArray()) { |
85 // VM is allowed to pass up a single BSM arg directly |
93 // VM is allowed to pass up a single BSM arg directly |
86 result = bootstrapMethod.invoke(caller, name, type, info); |
94 |
|
95 // Call to StringConcatFactory::makeConcatWithConstants |
|
96 // with empty constant arguments? |
|
97 if (isStringConcatFactoryBSM(bootstrapMethod.type())) { |
|
98 result = (CallSite)bootstrapMethod |
|
99 .invokeExact(caller, name, (MethodType)type, |
|
100 (String)info, new Object[0]); |
|
101 } else { |
|
102 result = invoke(bootstrapMethod, caller, name, type, info); |
|
103 } |
87 } |
104 } |
88 else if (info.getClass() == int[].class) { |
105 else if (info.getClass() == int[].class) { |
89 // VM is allowed to pass up a pair {argc, index} |
106 // VM is allowed to pass up a pair {argc, index} |
90 // referring to 'argc' BSM args at some place 'index' |
107 // referring to 'argc' BSM args at some place 'index' |
91 // in the guts of the VM (associated with callerClass). |
108 // in the guts of the VM (associated with callerClass). |
101 } |
118 } |
102 else { |
119 else { |
103 // VM is allowed to pass up a full array of resolved BSM args |
120 // VM is allowed to pass up a full array of resolved BSM args |
104 Object[] argv = (Object[]) info; |
121 Object[] argv = (Object[]) info; |
105 maybeReBoxElements(argv); |
122 maybeReBoxElements(argv); |
106 switch (argv.length) { |
123 |
107 case 0: |
124 MethodType bsmType = bootstrapMethod.type(); |
108 result = bootstrapMethod.invoke(caller, name, type); |
125 if (isLambdaMetafactoryIndyBSM(bsmType) && argv.length == 3) { |
109 break; |
126 result = (CallSite)bootstrapMethod |
110 case 1: |
127 .invokeExact(caller, name, (MethodType)type, (MethodType)argv[0], |
111 result = bootstrapMethod.invoke(caller, name, type, |
128 (MethodHandle)argv[1], (MethodType)argv[2]); |
112 argv[0]); |
129 } else if (isLambdaMetafactoryCondyBSM(bsmType) && argv.length == 3) { |
113 break; |
130 result = bootstrapMethod |
114 case 2: |
131 .invokeExact(caller, name, (Class<?>)type, (MethodType)argv[0], |
115 result = bootstrapMethod.invoke(caller, name, type, |
132 (MethodHandle)argv[1], (MethodType)argv[2]); |
116 argv[0], argv[1]); |
133 } else if (isStringConcatFactoryBSM(bsmType) && argv.length >= 1) { |
117 break; |
134 String recipe = (String)argv[0]; |
118 case 3: |
135 Object[] shiftedArgs = Arrays.copyOfRange(argv, 1, argv.length); |
119 // Special case the LambdaMetafactory::metafactory BSM |
136 result = (CallSite)bootstrapMethod.invokeExact(caller, name, (MethodType)type, recipe, shiftedArgs); |
120 // |
137 } else { |
121 // By invoking exactly, we can avoid generating a number of |
138 switch (argv.length) { |
122 // classes on first (and subsequent) lambda initialization, |
139 case 0: |
123 // most of which won't be shared with other invoke uses. |
140 result = invoke(bootstrapMethod, caller, name, type); |
124 MethodType bsmType = bootstrapMethod.type(); |
141 break; |
125 if (isLambdaMetafactoryIndyBSM(bsmType)) { |
142 case 1: |
126 result = (CallSite)bootstrapMethod |
143 result = invoke(bootstrapMethod, caller, name, type, |
127 .invokeExact(caller, name, (MethodType)type, (MethodType)argv[0], |
144 argv[0]); |
128 (MethodHandle)argv[1], (MethodType)argv[2]); |
145 break; |
129 } else if (isLambdaMetafactoryCondyBSM(bsmType)) { |
146 case 2: |
130 result = bootstrapMethod |
147 result = invoke(bootstrapMethod, caller, name, type, |
131 .invokeExact(caller, name, (Class<?>)type, (MethodType)argv[0], |
148 argv[0], argv[1]); |
132 (MethodHandle)argv[1], (MethodType)argv[2]); |
149 break; |
133 } else { |
150 case 3: |
134 result = bootstrapMethod.invoke(caller, name, type, |
151 result = invoke(bootstrapMethod, caller, name, type, |
135 argv[0], argv[1], argv[2]); |
152 argv[0], argv[1], argv[2]); |
136 } |
153 break; |
137 break; |
154 case 4: |
138 case 4: |
155 result = invoke(bootstrapMethod, caller, name, type, |
139 result = bootstrapMethod.invoke(caller, name, type, |
156 argv[0], argv[1], argv[2], argv[3]); |
140 argv[0], argv[1], argv[2], argv[3]); |
157 break; |
141 break; |
158 case 5: |
142 case 5: |
159 result = invoke(bootstrapMethod, caller, name, type, |
143 result = bootstrapMethod.invoke(caller, name, type, |
160 argv[0], argv[1], argv[2], argv[3], argv[4]); |
144 argv[0], argv[1], argv[2], argv[3], argv[4]); |
161 break; |
145 break; |
162 case 6: |
146 case 6: |
163 result = invoke(bootstrapMethod, caller, name, type, |
147 result = bootstrapMethod.invoke(caller, name, type, |
164 argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); |
148 argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); |
165 break; |
149 break; |
166 default: |
150 default: |
167 result = invokeWithManyArguments(bootstrapMethod, caller, name, type, argv); |
151 final int NON_SPREAD_ARG_COUNT = 3; // (caller, name, type) |
168 } |
152 final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT; |
|
153 if (argv.length >= MAX_SAFE_SIZE) { |
|
154 // to be on the safe side, use invokeWithArguments which handles jumbo lists |
|
155 Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argv.length]; |
|
156 newargv[0] = caller; |
|
157 newargv[1] = name; |
|
158 newargv[2] = type; |
|
159 System.arraycopy(argv, 0, newargv, NON_SPREAD_ARG_COUNT, argv.length); |
|
160 result = bootstrapMethod.invokeWithArguments(newargv); |
|
161 break; |
|
162 } |
|
163 MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length); |
|
164 MethodHandle typedBSM = bootstrapMethod.asType(invocationType); |
|
165 MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT); |
|
166 result = spreader.invokeExact(typedBSM, (Object) caller, (Object) name, type, argv); |
|
167 } |
169 } |
168 } |
170 } |
169 if (resultType.isPrimitive()) { |
171 if (resultType.isPrimitive()) { |
170 // Non-reference conversions are more than just plain casts. |
172 // Non-reference conversions are more than just plain casts. |
171 // By pushing the value through a funnel of the form (T x)->x, |
173 // By pushing the value through a funnel of the form (T x)->x, |
189 // Wrap anything else in BootstrapMethodError |
191 // Wrap anything else in BootstrapMethodError |
190 throw new BootstrapMethodError("bootstrap method initialization exception", ex); |
192 throw new BootstrapMethodError("bootstrap method initialization exception", ex); |
191 } |
193 } |
192 } |
194 } |
193 |
195 |
|
196 // If we don't provide static type information for type, we'll generate runtime |
|
197 // checks. Let's try not to... |
|
198 |
|
199 private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, |
|
200 String name, Object type) throws Throwable { |
|
201 if (type instanceof Class) { |
|
202 return bootstrapMethod.invoke(caller, name, (Class<?>)type); |
|
203 } else { |
|
204 return bootstrapMethod.invoke(caller, name, (MethodType)type); |
|
205 } |
|
206 } |
|
207 |
|
208 private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, |
|
209 String name, Object type, Object arg0) throws Throwable { |
|
210 if (type instanceof Class) { |
|
211 return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0); |
|
212 } else { |
|
213 return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0); |
|
214 } |
|
215 } |
|
216 |
|
217 private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name, |
|
218 Object type, Object arg0, Object arg1) throws Throwable { |
|
219 if (type instanceof Class) { |
|
220 return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1); |
|
221 } else { |
|
222 return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1); |
|
223 } |
|
224 } |
|
225 |
|
226 private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name, |
|
227 Object type, Object arg0, Object arg1, |
|
228 Object arg2) throws Throwable { |
|
229 if (type instanceof Class) { |
|
230 return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2); |
|
231 } else { |
|
232 return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2); |
|
233 } |
|
234 } |
|
235 |
|
236 private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name, |
|
237 Object type, Object arg0, Object arg1, |
|
238 Object arg2, Object arg3) throws Throwable { |
|
239 if (type instanceof Class) { |
|
240 return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3); |
|
241 } else { |
|
242 return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3); |
|
243 } |
|
244 } |
|
245 |
|
246 private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, |
|
247 String name, Object type, Object arg0, Object arg1, |
|
248 Object arg2, Object arg3, Object arg4) throws Throwable { |
|
249 if (type instanceof Class) { |
|
250 return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3, arg4); |
|
251 } else { |
|
252 return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3, arg4); |
|
253 } |
|
254 } |
|
255 |
|
256 private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, |
|
257 String name, Object type, Object arg0, Object arg1, |
|
258 Object arg2, Object arg3, Object arg4, Object arg5) throws Throwable { |
|
259 if (type instanceof Class) { |
|
260 return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3, arg4, arg5); |
|
261 } else { |
|
262 return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3, arg4, arg5); |
|
263 } |
|
264 } |
|
265 |
|
266 private static Object invokeWithManyArguments(MethodHandle bootstrapMethod, Lookup caller, |
|
267 String name, Object type, Object[] argv) throws Throwable { |
|
268 final int NON_SPREAD_ARG_COUNT = 3; // (caller, name, type) |
|
269 final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT; |
|
270 if (argv.length >= MAX_SAFE_SIZE) { |
|
271 // to be on the safe side, use invokeWithArguments which handles jumbo lists |
|
272 Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argv.length]; |
|
273 newargv[0] = caller; |
|
274 newargv[1] = name; |
|
275 newargv[2] = type; |
|
276 System.arraycopy(argv, 0, newargv, NON_SPREAD_ARG_COUNT, argv.length); |
|
277 return bootstrapMethod.invokeWithArguments(newargv); |
|
278 } else { |
|
279 MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length); |
|
280 MethodHandle typedBSM = bootstrapMethod.asType(invocationType); |
|
281 MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT); |
|
282 return spreader.invokeExact(typedBSM, (Object) caller, (Object) name, type, argv); |
|
283 } |
|
284 } |
|
285 |
194 private static final MethodType LMF_INDY_MT = MethodType.methodType(CallSite.class, |
286 private static final MethodType LMF_INDY_MT = MethodType.methodType(CallSite.class, |
195 Lookup.class, String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class); |
287 Lookup.class, String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class); |
196 |
288 |
197 private static final MethodType LMF_CONDY_MT = MethodType.methodType(Object.class, |
289 private static final MethodType LMF_CONDY_MT = MethodType.methodType(Object.class, |
198 Lookup.class, String.class, Class.class, MethodType.class, MethodHandle.class, MethodType.class); |
290 Lookup.class, String.class, Class.class, MethodType.class, MethodHandle.class, MethodType.class); |
|
291 |
|
292 private static final MethodType SCF_MT = MethodType.methodType(CallSite.class, |
|
293 Lookup.class, String.class, MethodType.class, String.class, Object[].class); |
|
294 |
|
295 /** |
|
296 * @return true iff the BSM method type exactly matches |
|
297 * {@see java.lang.invoke.StringConcatFactory#makeConcatWithConstants(MethodHandles.Lookup, |
|
298 * String,MethodType,String,Object...))} |
|
299 */ |
|
300 private static boolean isStringConcatFactoryBSM(MethodType bsmType) { |
|
301 return bsmType == SCF_MT; |
|
302 } |
199 |
303 |
200 /** |
304 /** |
201 * @return true iff the BSM method type exactly matches |
305 * @return true iff the BSM method type exactly matches |
202 * {@see java.lang.invoke.LambdaMetafactory#metafactory( |
306 * {@see java.lang.invoke.LambdaMetafactory#metafactory( |
203 * MethodHandles.Lookup,String,Class,MethodType,MethodHandle,MethodType)} |
307 * MethodHandles.Lookup,String,Class,MethodType,MethodHandle,MethodType)} |