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 static sun.invoke.util.Wrapper.forPrimitiveType; 30 import static sun.invoke.util.Wrapper.forWrapperType; 31 import static sun.invoke.util.Wrapper.isWrapperType; 32 33 /** 34 * Abstract implementation of a lambda metafactory which provides parameter unrolling and input validation. 35 * 36 * @see LambdaMetafactory 37 */ 38 /* package */ abstract class AbstractValidatingLambdaMetafactory { 39 40 /* 41 * For context, the comments for the following fields are marked in quotes with their values, given this program: 42 * interface II<T> { Object foo(T x); } 43 * interface JJ<R extends Number> extends II<R> { } 44 * class CC { String impl(int i) { return "impl:"+i; }} 45 * class X { 46 * public static void main(String[] args) { 47 * JJ<Integer> iii = (new CC())::impl; 48 * System.out.printf(">>> %s\n", iii.foo(44)); 49 * }} 50 */ 51 final Class<?> targetClass; // The class calling the meta-factory via invokedynamic "class X" 52 final MethodType invokedType; // The type of the invoked method "(CC)II" 53 final Class<?> samBase; // The type of the returned instance "interface JJ" 54 final MethodHandle samMethod; // Raw method handle for the functional interface method 55 final MethodHandleInfo samInfo; // Info about the SAM method handle "MethodHandleInfo[9 II.foo(Object)Object]" 56 final Class<?> samClass; // Interface containing the SAM method "interface II" 57 final MethodType samMethodType; // Type of the SAM method "(Object)Object" 58 final MethodHandle implMethod; // Raw method handle for the implementation method 59 final MethodHandleInfo implInfo; // Info about the implementation method handle "MethodHandleInfo[5 CC.impl(int)String]" 60 final int implKind; // Invocation kind for implementation "5"=invokevirtual 61 final boolean implIsInstanceMethod; // Is the implementation an instance method "true" 62 final Class<?> implDefiningClass; // Type defining the implementation "class CC" 63 final MethodType implMethodType; // Type of the implementation method "(int)String" 64 final MethodType instantiatedMethodType; // Instantiated erased functional interface method type "(Integer)Object" 65 final boolean isSerializable; // Should the returned instance be serializable 66 final Class<?>[] markerInterfaces; // Additional marker interfaces to be implemented 67 final MethodType[] additionalBridges; // Signatures of additional methods to bridge 68 69 70 /** 71 * Meta-factory constructor. 72 * 73 * @param caller Stacked automatically by VM; represents a lookup context 74 * with the accessibility privileges of the caller. 75 * @param invokedType Stacked automatically by VM; the signature of the 76 * invoked method, which includes the expected static 77 * type of the returned lambda object, and the static 78 * types of the captured arguments for the lambda. In 79 * the event that the implementation method is an 80 * instance method, the first argument in the invocation 81 * signature will correspond to the receiver. 82 * @param samMethod The primary method in the functional interface to which 83 * the lambda or method reference is being converted, 84 * represented as a method handle. 85 * @param implMethod The implementation method which should be called 86 * (with suitable adaptation of argument types, return 87 * types, and adjustment for captured arguments) when 88 * methods of the resulting functional interface instance 89 * are invoked. 90 * @param instantiatedMethodType The signature of the primary functional 91 * interface method after type variables are 92 * substituted with their instantiation from 93 * the capture site 94 * @param isSerializable Should the lambda be made serializable? If set, 95 * either the target type or one of the additional SAM 96 * types must extend {@code Serializable}. 97 * @param markerInterfaces Additional interfaces which the lambda object 98 * should implement. 99 * @param additionalBridges Method types for additional signatures to be 100 * bridged to the implementation method 101 * @throws ReflectiveOperationException 102 * @throws LambdaConversionException If any of the meta-factory protocol 103 * invariants are violated 104 */ 105 AbstractValidatingLambdaMetafactory(MethodHandles.Lookup caller, 106 MethodType invokedType, 107 MethodHandle samMethod, 108 MethodHandle implMethod, 109 MethodType instantiatedMethodType, 110 boolean isSerializable, 111 Class<?>[] markerInterfaces, 112 MethodType[] additionalBridges) 113 throws ReflectiveOperationException, LambdaConversionException { 114 this.targetClass = caller.lookupClass(); 115 this.invokedType = invokedType; 116 117 this.samBase = invokedType.returnType(); 118 119 this.samMethod = samMethod; 120 this.samInfo = new MethodHandleInfo(samMethod); 121 this.samClass = samInfo.getDeclaringClass(); 122 this.samMethodType = samInfo.getMethodType(); 123 124 this.implMethod = implMethod; 125 this.implInfo = new MethodHandleInfo(implMethod); 126 // @@@ Temporary work-around pending resolution of 8005119 127 this.implKind = (implInfo.getReferenceKind() == MethodHandleInfo.REF_invokeSpecial) 128 ? MethodHandleInfo.REF_invokeVirtual 129 : implInfo.getReferenceKind(); 130 this.implIsInstanceMethod = 131 implKind == MethodHandleInfo.REF_invokeVirtual || 132 implKind == MethodHandleInfo.REF_invokeSpecial || 133 implKind == MethodHandleInfo.REF_invokeInterface; 134 this.implDefiningClass = implInfo.getDeclaringClass(); 135 this.implMethodType = implInfo.getMethodType(); 136 this.instantiatedMethodType = instantiatedMethodType; 137 this.isSerializable = isSerializable; 138 this.markerInterfaces = markerInterfaces; 139 this.additionalBridges = additionalBridges; 140 141 if (!samClass.isInterface()) { 142 throw new LambdaConversionException(String.format( 143 "Functional interface %s is not an interface", samClass.getName())); 144 } 145 146 for (Class<?> c : markerInterfaces) { 147 if (!c.isInterface()) { 148 throw new LambdaConversionException(String.format( 149 "Marker interface %s is not an interface", c.getName())); 150 } 151 } 152 } 153 154 /** 155 * Build the CallSite. 156 * 157 * @return a CallSite, which, when invoked, will return an instance of the 158 * functional interface 159 * @throws ReflectiveOperationException 160 */ 161 abstract CallSite buildCallSite() throws ReflectiveOperationException, LambdaConversionException; 162 163 /** 164 * Check the meta-factory arguments for errors 165 * @throws LambdaConversionException if there are improper conversions 166 */ 167 void validateMetafactoryArgs() throws LambdaConversionException { 168 // Check target type is a subtype of class where SAM method is defined 169 if (!samClass.isAssignableFrom(samBase)) { 170 throw new LambdaConversionException( 171 String.format("Invalid target type %s for lambda conversion; not a subtype of functional interface %s", 172 samBase.getName(), samClass.getName())); 253 throw new LambdaConversionException( 254 String.format("Type mismatch for lambda argument %d: %s is not convertible to %s", 255 i, instantiatedParamType, implParamType)); 256 } 257 } 258 259 // Adaptation match: return type 260 Class<?> expectedType = instantiatedMethodType.returnType(); 261 Class<?> actualReturnType = 262 (implKind == MethodHandleInfo.REF_newInvokeSpecial) 263 ? implDefiningClass 264 : implMethodType.returnType(); 265 if (!isAdaptableToAsReturn(actualReturnType, expectedType)) { 266 throw new LambdaConversionException( 267 String.format("Type mismatch for lambda return: %s is not convertible to %s", 268 actualReturnType, expectedType)); 269 } 270 } 271 272 /** 273 * Check type adaptability for parameter types. 274 * @param fromType Type to convert from 275 * @param toType Type to convert to 276 * @param strict If true, do strict checks, else allow that fromType may be parameterized 277 * @return True if 'fromType' can be passed to an argument of 'toType' 278 */ 279 private boolean isAdaptableTo(Class<?> fromType, Class<?> toType, boolean strict) { 280 if (fromType.equals(toType)) { 281 return true; 282 } 283 if (fromType.isPrimitive()) { 284 Wrapper wfrom = forPrimitiveType(fromType); 285 if (toType.isPrimitive()) { 286 // both are primitive: widening 287 Wrapper wto = forPrimitiveType(toType); 288 return wto.isConvertibleFrom(wfrom); 289 } else { 290 // from primitive to reference: boxing 291 return toType.isAssignableFrom(wfrom.wrapperType()); 292 } 293 } else { 294 if (toType.isPrimitive()) { 295 // from reference to primitive: unboxing 296 Wrapper wfrom; 297 if (isWrapperType(fromType) && (wfrom = forWrapperType(fromType)).primitiveType().isPrimitive()) { 298 // fromType is a primitive wrapper; unbox+widen 299 Wrapper wto = forPrimitiveType(toType); 300 return wto.isConvertibleFrom(wfrom); 301 } else { 302 // must be convertible to primitive 303 return !strict; 304 } 305 } else { 306 // both are reference types: fromType should be a superclass of toType. 307 return !strict || toType.isAssignableFrom(fromType); 308 } 309 } 310 } 311 312 /** 313 * Check type adaptability for return types -- special handling of void type) 314 * and parameterized fromType 315 * @return True if 'fromType' can be converted to 'toType' 316 */ 317 private boolean isAdaptableToAsReturn(Class<?> fromType, Class<?> toType) { 318 return toType.equals(void.class) 319 || !fromType.equals(void.class) && isAdaptableTo(fromType, toType, false); 320 } 321 322 323 /*********** Logging support -- for debugging only, uncomment as needed 324 static final Executor logPool = Executors.newSingleThreadExecutor(); 325 protected static void log(final String s) { 326 MethodHandleProxyLambdaMetafactory.logPool.execute(new Runnable() { 327 @Override 328 public void run() { 329 System.out.println(s); 330 } 331 }); 332 } 333 334 protected static void log(final String s, final Throwable e) { 335 MethodHandleProxyLambdaMetafactory.logPool.execute(new Runnable() { 336 @Override 337 public void run() { 338 System.out.println(s); 339 e.printStackTrace(System.out); 340 } 341 }); 342 } 343 ***********************/ 344 345 } | 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 java.io.Serializable; 28 import java.lang.reflect.Method; 29 import java.lang.reflect.Modifier; 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.List; 33 import sun.invoke.util.Wrapper; 34 import static sun.invoke.util.Wrapper.*; 35 36 /** 37 * Abstract implementation of a lambda metafactory which provides parameter unrolling and input validation. 38 * 39 * @see LambdaMetafactory 40 */ 41 /* package */ abstract class AbstractValidatingLambdaMetafactory { 42 43 /* 44 * For context, the comments for the following fields are marked in quotes with their values, given this program: 45 * interface II<T> { Object foo(T x); } 46 * interface JJ<R extends Number> extends II<R> { } 47 * class CC { String impl(int i) { return "impl:"+i; }} 48 * class X { 49 * public static void main(String[] args) { 50 * JJ<Integer> iii = (new CC())::impl; 51 * System.out.printf(">>> %s\n", iii.foo(44)); 52 * }} 53 */ 54 final Class<?> targetClass; // The class calling the meta-factory via invokedynamic "class X" 55 final MethodType invokedType; // The type of the invoked method "(CC)II" 56 final Class<?> samBase; // The type of the returned instance "interface JJ" 57 final MethodHandle samMethod; // Raw method handle for the functional interface method 58 final MethodHandleInfo samInfo; // Info about the SAM method handle "MethodHandleInfo[9 II.foo(Object)Object]" 59 final Class<?> samClass; // Interface containing the SAM method "interface II" 60 final MethodType samMethodType; // Type of the SAM method "(Object)Object" 61 final MethodHandle implMethod; // Raw method handle for the implementation method 62 final MethodHandleInfo implInfo; // Info about the implementation method handle "MethodHandleInfo[5 CC.impl(int)String]" 63 final int implKind; // Invocation kind for implementation "5"=invokevirtual 64 final boolean implIsInstanceMethod; // Is the implementation an instance method "true" 65 final Class<?> implDefiningClass; // Type defining the implementation "class CC" 66 final MethodType implMethodType; // Type of the implementation method "(int)String" 67 final MethodType instantiatedMethodType; // Instantiated erased functional interface method type "(Integer)Object" 68 final boolean isSerializable; // Should the returned instance be serializable 69 final Class<?>[] markerInterfaces; // Additional marker interfaces to be implemented 70 71 72 /** 73 * Meta-factory constructor. 74 * 75 * @param caller Stacked automatically by VM; represents a lookup context with the accessibility privileges 76 * of the caller. 77 * @param invokedType Stacked automatically by VM; the signature of the invoked method, which includes the 78 * expected static type of the returned lambda object, and the static types of the captured 79 * arguments for the lambda. In the event that the implementation method is an instance method, 80 * the first argument in the invocation signature will correspond to the receiver. 81 * @param samMethod The primary method in the functional interface to which the lambda or method reference is 82 * being converted, represented as a method handle. 83 * @param implMethod The implementation method which should be called (with suitable adaptation of argument 84 * types, return types, and adjustment for captured arguments) when methods of the resulting 85 * functional interface instance are invoked. 86 * @param instantiatedMethodType The signature of the primary functional interface method after type variables 87 * are substituted with their instantiation from the capture site 88 * @throws ReflectiveOperationException 89 * @throws LambdaConversionException If any of the meta-factory protocol invariants are violated 90 */ 91 AbstractValidatingLambdaMetafactory(MethodHandles.Lookup caller, 92 MethodType invokedType, 93 MethodHandle samMethod, 94 MethodHandle implMethod, 95 MethodType instantiatedMethodType, 96 int flags, 97 Class<?>[] markerInterfaces) 98 throws ReflectiveOperationException, LambdaConversionException { 99 this.targetClass = caller.lookupClass(); 100 this.invokedType = invokedType; 101 102 this.samBase = invokedType.returnType(); 103 104 this.samMethod = samMethod; 105 this.samInfo = new MethodHandleInfo(samMethod); 106 this.samClass = samInfo.getDeclaringClass(); 107 this.samMethodType = samInfo.getMethodType(); 108 109 this.implMethod = implMethod; 110 this.implInfo = new MethodHandleInfo(implMethod); 111 // @@@ Temporary work-around pending resolution of 8005119 112 this.implKind = (implInfo.getReferenceKind() == MethodHandleInfo.REF_invokeSpecial) 113 ? MethodHandleInfo.REF_invokeVirtual 114 : implInfo.getReferenceKind(); 115 this.implIsInstanceMethod = 116 implKind == MethodHandleInfo.REF_invokeVirtual || 117 implKind == MethodHandleInfo.REF_invokeSpecial || 118 implKind == MethodHandleInfo.REF_invokeInterface; 119 this.implDefiningClass = implInfo.getDeclaringClass(); 120 this.implMethodType = implInfo.getMethodType(); 121 122 this.instantiatedMethodType = instantiatedMethodType; 123 124 if (!samClass.isInterface()) { 125 throw new LambdaConversionException(String.format( 126 "Functional interface %s is not an interface", 127 samClass.getName())); 128 } 129 130 boolean foundSerializableSupertype = Serializable.class.isAssignableFrom(samBase); 131 for (Class<?> c : markerInterfaces) { 132 if (!c.isInterface()) { 133 throw new LambdaConversionException(String.format( 134 "Marker interface %s is not an interface", 135 c.getName())); 136 } 137 foundSerializableSupertype |= Serializable.class.isAssignableFrom(c); 138 } 139 this.isSerializable = ((flags & LambdaMetafactory.FLAG_SERIALIZABLE) != 0) 140 || foundSerializableSupertype; 141 142 if (isSerializable && !foundSerializableSupertype) { 143 markerInterfaces = Arrays.copyOf(markerInterfaces, markerInterfaces.length + 1); 144 markerInterfaces[markerInterfaces.length-1] = Serializable.class; 145 } 146 this.markerInterfaces = markerInterfaces; 147 } 148 149 /** 150 * Build the CallSite. 151 * 152 * @return a CallSite, which, when invoked, will return an instance of the 153 * functional interface 154 * @throws ReflectiveOperationException 155 */ 156 abstract CallSite buildCallSite() throws ReflectiveOperationException, LambdaConversionException; 157 158 /** 159 * Check the meta-factory arguments for errors 160 * @throws LambdaConversionException if there are improper conversions 161 */ 162 void validateMetafactoryArgs() throws LambdaConversionException { 163 // Check target type is a subtype of class where SAM method is defined 164 if (!samClass.isAssignableFrom(samBase)) { 165 throw new LambdaConversionException( 166 String.format("Invalid target type %s for lambda conversion; not a subtype of functional interface %s", 167 samBase.getName(), samClass.getName())); 248 throw new LambdaConversionException( 249 String.format("Type mismatch for lambda argument %d: %s is not convertible to %s", 250 i, instantiatedParamType, implParamType)); 251 } 252 } 253 254 // Adaptation match: return type 255 Class<?> expectedType = instantiatedMethodType.returnType(); 256 Class<?> actualReturnType = 257 (implKind == MethodHandleInfo.REF_newInvokeSpecial) 258 ? implDefiningClass 259 : implMethodType.returnType(); 260 if (!isAdaptableToAsReturn(actualReturnType, expectedType)) { 261 throw new LambdaConversionException( 262 String.format("Type mismatch for lambda return: %s is not convertible to %s", 263 actualReturnType, expectedType)); 264 } 265 } 266 267 /** 268 * Check type adaptability 269 * @param fromType 270 * @param toType 271 * @param strict If true, do strict checks, else allow that fromType may be parameterized 272 * @return True if 'fromType' can be passed to an argument of 'toType' 273 */ 274 private boolean isAdaptableTo(Class<?> fromType, Class<?> toType, boolean strict) { 275 if (fromType.equals(toType)) { 276 return true; 277 } 278 if (fromType.isPrimitive()) { 279 Wrapper wfrom = forPrimitiveType(fromType); 280 if (toType.isPrimitive()) { 281 // both are primitive: widening 282 Wrapper wto = forPrimitiveType(toType); 283 return wto.isConvertibleFrom(wfrom); 284 } else { 285 // from primitive to reference: boxing 286 return toType.isAssignableFrom(wfrom.wrapperType()); 287 } 288 } else { 289 if (toType.isPrimitive()) { 290 // from reference to primitive: unboxing 291 Wrapper wfrom; 292 if (isWrapperType(fromType) && (wfrom = forWrapperType(fromType)).primitiveType().isPrimitive()) { 293 // fromType is a primitive wrapper; unbox+widen 294 Wrapper wto = forPrimitiveType(toType); 295 return wto.isConvertibleFrom(wfrom); 296 } else { 297 // must be convertible to primitive 298 return !strict; 299 } 300 } else { 301 // both are reference types: fromType should be a superclass of toType. 302 return strict? toType.isAssignableFrom(fromType) : true; 303 } 304 } 305 } 306 307 /** 308 * Check type adaptability for return types -- special handling of void type) and parameterized fromType 309 * @param fromType 310 * @param toType 311 * @return True if 'fromType' can be converted to 'toType' 312 */ 313 private boolean isAdaptableToAsReturn(Class<?> fromType, Class<?> toType) { 314 return toType.equals(void.class) 315 || !fromType.equals(void.class) && isAdaptableTo(fromType, toType, false); 316 } 317 318 319 /*********** Logging support -- for debugging only, uncomment as needed 320 static final Executor logPool = Executors.newSingleThreadExecutor(); 321 protected static void log(final String s) { 322 MethodHandleProxyLambdaMetafactory.logPool.execute(new Runnable() { 323 @Override 324 public void run() { 325 System.out.println(s); 326 } 327 }); 328 } 329 330 protected static void log(final String s, final Throwable e) { 331 MethodHandleProxyLambdaMetafactory.logPool.execute(new Runnable() { 332 @Override 333 public void run() { 334 System.out.println(s); 335 e.printStackTrace(System.out); 336 } 337 }); 338 } 339 ***********************/ 340 341 /** 342 * Find the functional interface method and corresponding abstract methods 343 * which should be bridged. The functional interface method and those to be 344 * bridged will have the same name and number of parameters. Check for 345 * matching default methods (non-abstract), the VM will create bridges for 346 * default methods; We don't have enough readily available type information 347 * to distinguish between where the functional interface method should be 348 * bridged and where the default method should be bridged; This situation is 349 * flagged. 350 */ 351 class MethodAnalyzer { 352 private final Method[] methods = samBase.getMethods(); 353 354 private Method samMethod = null; 355 private final List<Method> methodsToBridge = new ArrayList<>(methods.length); 356 private boolean conflictFoundBetweenDefaultAndBridge = false; 357 358 MethodAnalyzer() { 359 String samMethodName = samInfo.getName(); 360 Class<?>[] samParamTypes = samMethodType.parameterArray(); 361 int samParamLength = samParamTypes.length; 362 Class<?> samReturnType = samMethodType.returnType(); 363 Class<?> objectClass = Object.class; 364 List<Method> defaultMethods = new ArrayList<>(methods.length); 365 366 for (Method m : methods) { 367 if (m.getName().equals(samMethodName) && m.getDeclaringClass() != objectClass) { 368 Class<?>[] mParamTypes = m.getParameterTypes(); 369 if (mParamTypes.length == samParamLength) { 370 // Method matches name and parameter length -- and is not Object 371 if (Modifier.isAbstract(m.getModifiers())) { 372 // Method is abstract 373 if (m.getReturnType().equals(samReturnType) 374 && Arrays.equals(mParamTypes, samParamTypes)) { 375 // Exact match, this is the SAM method signature 376 samMethod = m; 377 } else if (!hasMatchingBridgeSignature(m)) { 378 // Record bridges, exclude methods with duplicate signatures 379 methodsToBridge.add(m); 380 } 381 } else { 382 // Record default methods for conflict testing 383 defaultMethods.add(m); 384 } 385 } 386 } 387 } 388 for (Method dm : defaultMethods) { 389 if (hasMatchingBridgeSignature(dm)) { 390 conflictFoundBetweenDefaultAndBridge = true; 391 break; 392 } 393 } 394 } 395 396 Method getSamMethod() { 397 return samMethod; 398 } 399 400 List<Method> getMethodsToBridge() { 401 return methodsToBridge; 402 } 403 404 boolean conflictFoundBetweenDefaultAndBridge() { 405 return conflictFoundBetweenDefaultAndBridge; 406 } 407 408 /** 409 * Search the list of previously found bridge methods to determine if there is a method with the same signature 410 * (return and parameter types) as the specified method. 411 * 412 * @param m The method to match 413 * @return True if the method was found, False otherwise 414 */ 415 private boolean hasMatchingBridgeSignature(Method m) { 416 Class<?>[] ptypes = m.getParameterTypes(); 417 Class<?> rtype = m.getReturnType(); 418 for (Method md : methodsToBridge) { 419 if (md.getReturnType().equals(rtype) && Arrays.equals(ptypes, md.getParameterTypes())) { 420 return true; 421 } 422 } 423 return false; 424 } 425 } 426 } |