rt/emul/compact/src/main/java/java/lang/invoke/AbstractValidatingLambdaMetafactory.java
Batch of classes necessary to implement invoke dynamic interfaces. Taken from JDK8 build 132
2 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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.
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).
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.
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
25 package java.lang.invoke;
27 import sun.invoke.util.Wrapper;
29 import static sun.invoke.util.Wrapper.forPrimitiveType;
30 import static sun.invoke.util.Wrapper.forWrapperType;
31 import static sun.invoke.util.Wrapper.isWrapperType;
34 * Abstract implementation of a lambda metafactory which provides parameter
35 * unrolling and input validation.
37 * @see LambdaMetafactory
39 /* package */ abstract class AbstractValidatingLambdaMetafactory {
42 * For context, the comments for the following fields are marked in quotes
43 * with their values, given this program:
44 * interface II<T> { Object foo(T x); }
45 * interface JJ<R extends Number> extends II<R> { }
46 * class CC { String impl(int i) { return "impl:"+i; }}
48 * public static void main(String[] args) {
49 * JJ<Integer> iii = (new CC())::impl;
50 * System.out.printf(">>> %s\n", iii.foo(44));
53 final Class<?> targetClass; // The class calling the meta-factory via invokedynamic "class X"
54 final MethodType invokedType; // The type of the invoked method "(CC)II"
55 final Class<?> samBase; // The type of the returned instance "interface JJ"
56 final String samMethodName; // Name of the SAM method "foo"
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
71 * Meta-factory constructor.
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 samMethodName Name of the method in the functional interface to
83 * which the lambda or method reference is being
84 * converted, represented as a String.
85 * @param samMethodType Type of the method in the functional interface to
86 * which the lambda or method reference is being
87 * converted, represented as a MethodType.
88 * @param implMethod The implementation method which should be called
89 * (with suitable adaptation of argument types, return
90 * types, and adjustment for captured arguments) when
91 * methods of the resulting functional interface instance
93 * @param instantiatedMethodType The signature of the primary functional
94 * interface method after type variables are
95 * substituted with their instantiation from
97 * @param isSerializable Should the lambda be made serializable? If set,
98 * either the target type or one of the additional SAM
99 * types must extend {@code Serializable}.
100 * @param markerInterfaces Additional interfaces which the lambda object
102 * @param additionalBridges Method types for additional signatures to be
103 * bridged to the implementation method
104 * @throws LambdaConversionException If any of the meta-factory protocol
105 * invariants are violated
107 AbstractValidatingLambdaMetafactory(MethodHandles.Lookup caller,
108 MethodType invokedType,
109 String samMethodName,
110 MethodType samMethodType,
111 MethodHandle implMethod,
112 MethodType instantiatedMethodType,
113 boolean isSerializable,
114 Class<?>[] markerInterfaces,
115 MethodType[] additionalBridges)
116 throws LambdaConversionException {
117 if ((caller.lookupModes() & MethodHandles.Lookup.PRIVATE) == 0) {
118 throw new LambdaConversionException(String.format(
119 "Invalid caller: %s",
120 caller.lookupClass().getName()));
122 this.targetClass = caller.lookupClass();
123 this.invokedType = invokedType;
125 this.samBase = invokedType.returnType();
127 this.samMethodName = samMethodName;
128 this.samMethodType = samMethodType;
130 this.implMethod = implMethod;
131 this.implInfo = caller.revealDirect(implMethod);
132 this.implKind = implInfo.getReferenceKind();
133 this.implIsInstanceMethod =
134 implKind == MethodHandleInfo.REF_invokeVirtual ||
135 implKind == MethodHandleInfo.REF_invokeSpecial ||
136 implKind == MethodHandleInfo.REF_invokeInterface;
137 this.implDefiningClass = implInfo.getDeclaringClass();
138 this.implMethodType = implInfo.getMethodType();
139 this.instantiatedMethodType = instantiatedMethodType;
140 this.isSerializable = isSerializable;
141 this.markerInterfaces = markerInterfaces;
142 this.additionalBridges = additionalBridges;
144 if (!samBase.isInterface()) {
145 throw new LambdaConversionException(String.format(
146 "Functional interface %s is not an interface",
150 for (Class<?> c : markerInterfaces) {
151 if (!c.isInterface()) {
152 throw new LambdaConversionException(String.format(
153 "Marker interface %s is not an interface",
160 * Build the CallSite.
162 * @return a CallSite, which, when invoked, will return an instance of the
163 * functional interface
164 * @throws ReflectiveOperationException
166 abstract CallSite buildCallSite()
167 throws LambdaConversionException;
170 * Check the meta-factory arguments for errors
171 * @throws LambdaConversionException if there are improper conversions
173 void validateMetafactoryArgs() throws LambdaConversionException {
175 case MethodHandleInfo.REF_invokeInterface:
176 case MethodHandleInfo.REF_invokeVirtual:
177 case MethodHandleInfo.REF_invokeStatic:
178 case MethodHandleInfo.REF_newInvokeSpecial:
179 case MethodHandleInfo.REF_invokeSpecial:
182 throw new LambdaConversionException(String.format("Unsupported MethodHandle kind: %s", implInfo));
185 // Check arity: optional-receiver + captured + SAM == impl
186 final int implArity = implMethodType.parameterCount();
187 final int receiverArity = implIsInstanceMethod ? 1 : 0;
188 final int capturedArity = invokedType.parameterCount();
189 final int samArity = samMethodType.parameterCount();
190 final int instantiatedArity = instantiatedMethodType.parameterCount();
191 if (implArity + receiverArity != capturedArity + samArity) {
192 throw new LambdaConversionException(
193 String.format("Incorrect number of parameters for %s method %s; %d captured parameters, %d functional interface method parameters, %d implementation parameters",
194 implIsInstanceMethod ? "instance" : "static", implInfo,
195 capturedArity, samArity, implArity));
197 if (instantiatedArity != samArity) {
198 throw new LambdaConversionException(
199 String.format("Incorrect number of parameters for %s method %s; %d instantiated parameters, %d functional interface method parameters",
200 implIsInstanceMethod ? "instance" : "static", implInfo,
201 instantiatedArity, samArity));
203 for (MethodType bridgeMT : additionalBridges) {
204 if (bridgeMT.parameterCount() != samArity) {
205 throw new LambdaConversionException(
206 String.format("Incorrect number of parameters for bridge signature %s; incompatible with %s",
207 bridgeMT, samMethodType));
211 // If instance: first captured arg (receiver) must be subtype of class where impl method is defined
212 final int capturedStart;
214 if (implIsInstanceMethod) {
215 final Class<?> receiverClass;
217 // implementation is an instance method, adjust for receiver in captured variables / SAM arguments
218 if (capturedArity == 0) {
219 // receiver is function parameter
222 receiverClass = instantiatedMethodType.parameterType(0);
224 // receiver is a captured variable
227 receiverClass = invokedType.parameterType(0);
230 // check receiver type
231 if (!implDefiningClass.isAssignableFrom(receiverClass)) {
232 throw new LambdaConversionException(
233 String.format("Invalid receiver type %s; not a subtype of implementation type %s",
234 receiverClass, implDefiningClass));
237 Class<?> implReceiverClass = implMethod.type().parameterType(0);
238 if (implReceiverClass != implDefiningClass && !implReceiverClass.isAssignableFrom(receiverClass)) {
239 throw new LambdaConversionException(
240 String.format("Invalid receiver type %s; not a subtype of implementation receiver type %s",
241 receiverClass, implReceiverClass));
249 // Check for exact match on non-receiver captured arguments
250 final int implFromCaptured = capturedArity - capturedStart;
251 for (int i=0; i<implFromCaptured; i++) {
252 Class<?> implParamType = implMethodType.parameterType(i);
253 Class<?> capturedParamType = invokedType.parameterType(i + capturedStart);
254 if (!capturedParamType.equals(implParamType)) {
255 throw new LambdaConversionException(
256 String.format("Type mismatch in captured lambda parameter %d: expecting %s, found %s",
257 i, capturedParamType, implParamType));
260 // Check for adaptation match on SAM arguments
261 final int samOffset = samStart - implFromCaptured;
262 for (int i=implFromCaptured; i<implArity; i++) {
263 Class<?> implParamType = implMethodType.parameterType(i);
264 Class<?> instantiatedParamType = instantiatedMethodType.parameterType(i + samOffset);
265 if (!isAdaptableTo(instantiatedParamType, implParamType, true)) {
266 throw new LambdaConversionException(
267 String.format("Type mismatch for lambda argument %d: %s is not convertible to %s",
268 i, instantiatedParamType, implParamType));
272 // Adaptation match: return type
273 Class<?> expectedType = instantiatedMethodType.returnType();
274 Class<?> actualReturnType =
275 (implKind == MethodHandleInfo.REF_newInvokeSpecial)
277 : implMethodType.returnType();
278 Class<?> samReturnType = samMethodType.returnType();
279 if (!isAdaptableToAsReturn(actualReturnType, expectedType)) {
280 throw new LambdaConversionException(
281 String.format("Type mismatch for lambda return: %s is not convertible to %s",
282 actualReturnType, expectedType));
284 if (!isAdaptableToAsReturnStrict(expectedType, samReturnType)) {
285 throw new LambdaConversionException(
286 String.format("Type mismatch for lambda expected return: %s is not convertible to %s",
287 expectedType, samReturnType));
289 for (MethodType bridgeMT : additionalBridges) {
290 if (!isAdaptableToAsReturnStrict(expectedType, bridgeMT.returnType())) {
291 throw new LambdaConversionException(
292 String.format("Type mismatch for lambda expected return: %s is not convertible to %s",
293 expectedType, bridgeMT.returnType()));
299 * Check type adaptability for parameter types.
300 * @param fromType Type to convert from
301 * @param toType Type to convert to
302 * @param strict If true, do strict checks, else allow that fromType may be parameterized
303 * @return True if 'fromType' can be passed to an argument of 'toType'
305 private boolean isAdaptableTo(Class<?> fromType, Class<?> toType, boolean strict) {
306 if (fromType.equals(toType)) {
309 if (fromType.isPrimitive()) {
310 Wrapper wfrom = forPrimitiveType(fromType);
311 if (toType.isPrimitive()) {
312 // both are primitive: widening
313 Wrapper wto = forPrimitiveType(toType);
314 return wto.isConvertibleFrom(wfrom);
316 // from primitive to reference: boxing
317 return toType.isAssignableFrom(wfrom.wrapperType());
320 if (toType.isPrimitive()) {
321 // from reference to primitive: unboxing
323 if (isWrapperType(fromType) && (wfrom = forWrapperType(fromType)).primitiveType().isPrimitive()) {
324 // fromType is a primitive wrapper; unbox+widen
325 Wrapper wto = forPrimitiveType(toType);
326 return wto.isConvertibleFrom(wfrom);
328 // must be convertible to primitive
332 // both are reference types: fromType should be a superclass of toType.
333 return !strict || toType.isAssignableFrom(fromType);
339 * Check type adaptability for return types --
340 * special handling of void type) and parameterized fromType
341 * @return True if 'fromType' can be converted to 'toType'
343 private boolean isAdaptableToAsReturn(Class<?> fromType, Class<?> toType) {
344 return toType.equals(void.class)
345 || !fromType.equals(void.class) && isAdaptableTo(fromType, toType, false);
347 private boolean isAdaptableToAsReturnStrict(Class<?> fromType, Class<?> toType) {
348 if (fromType.equals(void.class)) return toType.equals(void.class);
349 return isAdaptableTo(fromType, toType, true);
353 /*********** Logging support -- for debugging only, uncomment as needed
354 static final Executor logPool = Executors.newSingleThreadExecutor();
355 protected static void log(final String s) {
356 MethodHandleProxyLambdaMetafactory.logPool.execute(new Runnable() {
359 System.out.println(s);
364 protected static void log(final String s, final Throwable e) {
365 MethodHandleProxyLambdaMetafactory.logPool.execute(new Runnable() {
368 System.out.println(s);
369 e.printStackTrace(System.out);
373 ***********************/