1 /*
2 * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package java.lang.invoke;
26
27 import sun.invoke.util.Wrapper;
28
29 import java.lang.reflect.Modifier;
30
31 import static java.lang.invoke.MethodHandleInfo.*;
32 import static sun.invoke.util.Wrapper.forPrimitiveType;
33 import static sun.invoke.util.Wrapper.forWrapperType;
34 import static sun.invoke.util.Wrapper.isWrapperType;
35
36 /**
37 * Abstract implementation of a lambda metafactory which provides parameter
38 * unrolling and input validation.
39 *
40 * @see LambdaMetafactory
41 */
42 /* package */ abstract class AbstractValidatingLambdaMetafactory {
43
44 /*
45 * For context, the comments for the following fields are marked in quotes
46 * with their values, given this program:
182 this.altMethods = altMethods;
183
184 if (interfaceMethodName.isEmpty() ||
185 interfaceMethodName.indexOf('.') >= 0 ||
186 interfaceMethodName.indexOf(';') >= 0 ||
187 interfaceMethodName.indexOf('[') >= 0 ||
188 interfaceMethodName.indexOf('/') >= 0 ||
189 interfaceMethodName.indexOf('<') >= 0 ||
190 interfaceMethodName.indexOf('>') >= 0) {
191 throw new LambdaConversionException(String.format(
192 "Method name '%s' is not legal",
193 interfaceMethodName));
194 }
195
196 if (!interfaceClass.isInterface()) {
197 throw new LambdaConversionException(String.format(
198 "%s is not an interface",
199 interfaceClass.getName()));
200 }
201
202 for (Class<?> c : altInterfaces) {
203 if (!c.isInterface()) {
204 throw new LambdaConversionException(String.format(
205 "%s is not an interface",
206 c.getName()));
207 }
208 }
209 }
210
211 /**
212 * Build the CallSite.
213 *
214 * @return a CallSite, which, when invoked, will return an instance of the
215 * functional interface
216 * @throws LambdaConversionException
217 */
218 abstract CallSite buildCallSite()
219 throws LambdaConversionException;
220
221 /**
251 // If instance: first captured arg (receiver) must be subtype of class where impl method is defined
252 final int capturedStart; // index of first non-receiver capture parameter in implMethodType
253 final int samStart; // index of first non-receiver sam parameter in implMethodType
254 if (implIsInstanceMethod) {
255 final Class<?> receiverClass;
256
257 // implementation is an instance method, adjust for receiver in captured variables / SAM arguments
258 if (capturedArity == 0) {
259 // receiver is function parameter
260 capturedStart = 0;
261 samStart = 1;
262 receiverClass = dynamicMethodType.parameterType(0);
263 } else {
264 // receiver is a captured variable
265 capturedStart = 1;
266 samStart = capturedArity;
267 receiverClass = factoryType.parameterType(0);
268 }
269
270 // check receiver type
271 if (!implClass.isAssignableFrom(receiverClass)) {
272 throw new LambdaConversionException(
273 String.format("Invalid receiver type %s; not a subtype of implementation type %s",
274 receiverClass, implClass));
275 }
276 } else {
277 // no receiver
278 capturedStart = 0;
279 samStart = capturedArity;
280 }
281
282 // Check for exact match on non-receiver captured arguments
283 for (int i=capturedStart; i<capturedArity; i++) {
284 Class<?> implParamType = implMethodType.parameterType(i);
285 Class<?> capturedParamType = factoryType.parameterType(i);
286 if (!capturedParamType.equals(implParamType)) {
287 throw new LambdaConversionException(
288 String.format("Type mismatch in captured lambda parameter %d: expecting %s, found %s",
289 i, capturedParamType, implParamType));
290 }
291 }
292 // Check for adaptation match on non-receiver SAM arguments
293 for (int i=samStart; i<implArity; i++) {
294 Class<?> implParamType = implMethodType.parameterType(i);
304 Class<?> expectedType = dynamicMethodType.returnType();
305 Class<?> actualReturnType = implMethodType.returnType();
306 if (!isAdaptableToAsReturn(actualReturnType, expectedType)) {
307 throw new LambdaConversionException(
308 String.format("Type mismatch for lambda return: %s is not convertible to %s",
309 actualReturnType, expectedType));
310 }
311
312 // Check descriptors of generated methods
313 checkDescriptor(interfaceMethodType);
314 for (MethodType bridgeMT : altMethods) {
315 checkDescriptor(bridgeMT);
316 }
317 }
318
319 /** Validate that the given descriptor's types are compatible with {@code dynamicMethodType} **/
320 private void checkDescriptor(MethodType descriptor) throws LambdaConversionException {
321 for (int i = 0; i < dynamicMethodType.parameterCount(); i++) {
322 Class<?> dynamicParamType = dynamicMethodType.parameterType(i);
323 Class<?> descriptorParamType = descriptor.parameterType(i);
324 if (!descriptorParamType.isAssignableFrom(dynamicParamType)) {
325 String msg = String.format("Type mismatch for dynamic parameter %d: %s is not a subtype of %s",
326 i, dynamicParamType, descriptorParamType);
327 throw new LambdaConversionException(msg);
328 }
329 }
330
331 Class<?> dynamicReturnType = dynamicMethodType.returnType();
332 Class<?> descriptorReturnType = descriptor.returnType();
333 if (!isAdaptableToAsReturnStrict(dynamicReturnType, descriptorReturnType)) {
334 String msg = String.format("Type mismatch for lambda expected return: %s is not convertible to %s",
335 dynamicReturnType, descriptorReturnType);
336 throw new LambdaConversionException(msg);
337 }
338 }
339
340 /**
341 * Check type adaptability for parameter types.
342 * @param fromType Type to convert from
343 * @param toType Type to convert to
344 * @param strict If true, do strict checks, else allow that fromType may be parameterized
354 // both are primitive: widening
355 Wrapper wto = forPrimitiveType(toType);
356 return wto.isConvertibleFrom(wfrom);
357 } else {
358 // from primitive to reference: boxing
359 return toType.isAssignableFrom(wfrom.wrapperType());
360 }
361 } else {
362 if (toType.isPrimitive()) {
363 // from reference to primitive: unboxing
364 Wrapper wfrom;
365 if (isWrapperType(fromType) && (wfrom = forWrapperType(fromType)).primitiveType().isPrimitive()) {
366 // fromType is a primitive wrapper; unbox+widen
367 Wrapper wto = forPrimitiveType(toType);
368 return wto.isConvertibleFrom(wfrom);
369 } else {
370 // must be convertible to primitive
371 return !strict;
372 }
373 } else {
374 // both are reference types: fromType should be a superclass of toType.
375 return !strict || toType.isAssignableFrom(fromType);
376 }
377 }
378 }
379
380 /**
381 * Check type adaptability for return types --
382 * special handling of void type) and parameterized fromType
383 * @return True if 'fromType' can be converted to 'toType'
384 */
385 private boolean isAdaptableToAsReturn(Class<?> fromType, Class<?> toType) {
386 return toType.equals(void.class)
387 || !fromType.equals(void.class) && isAdaptableTo(fromType, toType, false);
388 }
389 private boolean isAdaptableToAsReturnStrict(Class<?> fromType, Class<?> toType) {
390 if (fromType.equals(void.class) || toType.equals(void.class)) return fromType.equals(toType);
391 else return isAdaptableTo(fromType, toType, true);
392 }
393
394
395 /*********** Logging support -- for debugging only, uncomment as needed
396 static final Executor logPool = Executors.newSingleThreadExecutor();
397 protected static void log(final String s) {
398 MethodHandleProxyLambdaMetafactory.logPool.execute(new Runnable() {
399 @Override
|
1 /*
2 * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package java.lang.invoke;
26
27 import jdk.internal.value.PrimitiveClass;
28 import sun.invoke.util.Wrapper;
29
30 import java.lang.reflect.Modifier;
31
32 import static java.lang.invoke.MethodHandleInfo.*;
33 import static sun.invoke.util.Wrapper.forPrimitiveType;
34 import static sun.invoke.util.Wrapper.forWrapperType;
35 import static sun.invoke.util.Wrapper.isWrapperType;
36
37 /**
38 * Abstract implementation of a lambda metafactory which provides parameter
39 * unrolling and input validation.
40 *
41 * @see LambdaMetafactory
42 */
43 /* package */ abstract class AbstractValidatingLambdaMetafactory {
44
45 /*
46 * For context, the comments for the following fields are marked in quotes
47 * with their values, given this program:
183 this.altMethods = altMethods;
184
185 if (interfaceMethodName.isEmpty() ||
186 interfaceMethodName.indexOf('.') >= 0 ||
187 interfaceMethodName.indexOf(';') >= 0 ||
188 interfaceMethodName.indexOf('[') >= 0 ||
189 interfaceMethodName.indexOf('/') >= 0 ||
190 interfaceMethodName.indexOf('<') >= 0 ||
191 interfaceMethodName.indexOf('>') >= 0) {
192 throw new LambdaConversionException(String.format(
193 "Method name '%s' is not legal",
194 interfaceMethodName));
195 }
196
197 if (!interfaceClass.isInterface()) {
198 throw new LambdaConversionException(String.format(
199 "%s is not an interface",
200 interfaceClass.getName()));
201 }
202
203 if (interfaceClass.isIdentity() || interfaceClass.isValue()) {
204 throw new LambdaConversionException(String.format(
205 "%s is %s interface",
206 interfaceClass.getName(),
207 interfaceClass.isIdentity() ? "an identity" : "a value"));
208 }
209
210 for (Class<?> c : altInterfaces) {
211 if (!c.isInterface()) {
212 throw new LambdaConversionException(String.format(
213 "%s is not an interface",
214 c.getName()));
215 }
216 }
217 }
218
219 /**
220 * Build the CallSite.
221 *
222 * @return a CallSite, which, when invoked, will return an instance of the
223 * functional interface
224 * @throws LambdaConversionException
225 */
226 abstract CallSite buildCallSite()
227 throws LambdaConversionException;
228
229 /**
259 // If instance: first captured arg (receiver) must be subtype of class where impl method is defined
260 final int capturedStart; // index of first non-receiver capture parameter in implMethodType
261 final int samStart; // index of first non-receiver sam parameter in implMethodType
262 if (implIsInstanceMethod) {
263 final Class<?> receiverClass;
264
265 // implementation is an instance method, adjust for receiver in captured variables / SAM arguments
266 if (capturedArity == 0) {
267 // receiver is function parameter
268 capturedStart = 0;
269 samStart = 1;
270 receiverClass = dynamicMethodType.parameterType(0);
271 } else {
272 // receiver is a captured variable
273 capturedStart = 1;
274 samStart = capturedArity;
275 receiverClass = factoryType.parameterType(0);
276 }
277
278 // check receiver type
279 if (!PrimitiveClass.asPrimaryType(implClass).isAssignableFrom(PrimitiveClass.asPrimaryType(receiverClass))) {
280 throw new LambdaConversionException(
281 String.format("Invalid receiver type %s; not a subtype of implementation type %s",
282 receiverClass.descriptorString(), implClass.descriptorString()));
283 }
284 } else {
285 // no receiver
286 capturedStart = 0;
287 samStart = capturedArity;
288 }
289
290 // Check for exact match on non-receiver captured arguments
291 for (int i=capturedStart; i<capturedArity; i++) {
292 Class<?> implParamType = implMethodType.parameterType(i);
293 Class<?> capturedParamType = factoryType.parameterType(i);
294 if (!capturedParamType.equals(implParamType)) {
295 throw new LambdaConversionException(
296 String.format("Type mismatch in captured lambda parameter %d: expecting %s, found %s",
297 i, capturedParamType, implParamType));
298 }
299 }
300 // Check for adaptation match on non-receiver SAM arguments
301 for (int i=samStart; i<implArity; i++) {
302 Class<?> implParamType = implMethodType.parameterType(i);
312 Class<?> expectedType = dynamicMethodType.returnType();
313 Class<?> actualReturnType = implMethodType.returnType();
314 if (!isAdaptableToAsReturn(actualReturnType, expectedType)) {
315 throw new LambdaConversionException(
316 String.format("Type mismatch for lambda return: %s is not convertible to %s",
317 actualReturnType, expectedType));
318 }
319
320 // Check descriptors of generated methods
321 checkDescriptor(interfaceMethodType);
322 for (MethodType bridgeMT : altMethods) {
323 checkDescriptor(bridgeMT);
324 }
325 }
326
327 /** Validate that the given descriptor's types are compatible with {@code dynamicMethodType} **/
328 private void checkDescriptor(MethodType descriptor) throws LambdaConversionException {
329 for (int i = 0; i < dynamicMethodType.parameterCount(); i++) {
330 Class<?> dynamicParamType = dynamicMethodType.parameterType(i);
331 Class<?> descriptorParamType = descriptor.parameterType(i);
332 if (!PrimitiveClass.asPrimaryType(descriptorParamType).isAssignableFrom(PrimitiveClass.asPrimaryType(dynamicParamType))) {
333 String msg = String.format("Type mismatch for dynamic parameter %d: %s is not a subtype of %s",
334 i, dynamicParamType, descriptorParamType);
335 throw new LambdaConversionException(msg);
336 }
337 }
338
339 Class<?> dynamicReturnType = dynamicMethodType.returnType();
340 Class<?> descriptorReturnType = descriptor.returnType();
341 if (!isAdaptableToAsReturnStrict(dynamicReturnType, descriptorReturnType)) {
342 String msg = String.format("Type mismatch for lambda expected return: %s is not convertible to %s",
343 dynamicReturnType, descriptorReturnType);
344 throw new LambdaConversionException(msg);
345 }
346 }
347
348 /**
349 * Check type adaptability for parameter types.
350 * @param fromType Type to convert from
351 * @param toType Type to convert to
352 * @param strict If true, do strict checks, else allow that fromType may be parameterized
362 // both are primitive: widening
363 Wrapper wto = forPrimitiveType(toType);
364 return wto.isConvertibleFrom(wfrom);
365 } else {
366 // from primitive to reference: boxing
367 return toType.isAssignableFrom(wfrom.wrapperType());
368 }
369 } else {
370 if (toType.isPrimitive()) {
371 // from reference to primitive: unboxing
372 Wrapper wfrom;
373 if (isWrapperType(fromType) && (wfrom = forWrapperType(fromType)).primitiveType().isPrimitive()) {
374 // fromType is a primitive wrapper; unbox+widen
375 Wrapper wto = forPrimitiveType(toType);
376 return wto.isConvertibleFrom(wfrom);
377 } else {
378 // must be convertible to primitive
379 return !strict;
380 }
381 } else {
382 // primitive types: fromType and toType are types of the same primitive class
383 // identity types: fromType should be a superclass of toType.
384 return !strict || canConvert(fromType, toType);
385 }
386 }
387 }
388
389 /**
390 * Tests if {@code fromType} can be converted to {@code toType}
391 * via an identity conversion, via a widening reference conversion or
392 * via primitive class narrowing and widening conversions.
393 * <p>
394 * If {@code fromType} represents a class or interface, this method
395 * returns {@code true} if {@code toType} is the same as,
396 * or is a superclass or superinterface of, {@code fromType}.
397 * <p>
398 * If {@code fromType} and {@code toType} is of the same primitive class,
399 * this method returns {@code true}.
400 * <p>
401 * Otherwise, this method returns {@code false}.
402 *
403 * @param fromType the {@code Class} object to be converted from
404 * @param toType the {@code Class} object to be converted to
405 * @return {@code true} if {@code fromType} can be converted to {@code toType}
406 */
407 private boolean canConvert(Class<?> fromType, Class<?> toType) {
408 if (toType.isAssignableFrom(fromType)) {
409 return true;
410 }
411
412 if (fromType.isValue() && toType.isValue()) {
413 // val projection can be converted to ref projection; or vice verse
414 return PrimitiveClass.asPrimaryType(fromType) == PrimitiveClass.asPrimaryType(toType);
415 }
416
417 return false;
418 }
419
420 /**
421 * Check type adaptability for return types --
422 * special handling of void type) and parameterized fromType
423 * @return True if 'fromType' can be converted to 'toType'
424 */
425 private boolean isAdaptableToAsReturn(Class<?> fromType, Class<?> toType) {
426 return toType.equals(void.class)
427 || !fromType.equals(void.class) && isAdaptableTo(fromType, toType, false);
428 }
429 private boolean isAdaptableToAsReturnStrict(Class<?> fromType, Class<?> toType) {
430 if (fromType.equals(void.class) || toType.equals(void.class)) return fromType.equals(toType);
431 else return isAdaptableTo(fromType, toType, true);
432 }
433
434
435 /*********** Logging support -- for debugging only, uncomment as needed
436 static final Executor logPool = Executors.newSingleThreadExecutor();
437 protected static void log(final String s) {
438 MethodHandleProxyLambdaMetafactory.logPool.execute(new Runnable() {
439 @Override
|