1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package java.lang.reflect; 19 20 import java.io.Serializable; 21 import java.util.ArrayList; 22 import java.util.Arrays; 23 import java.util.Collections; 24 import java.util.Comparator; 25 import java.util.HashSet; 26 import java.util.List; 27 import java.util.Set; 28 import libcore.util.EmptyArray; 29 30 /** 31 * {@code Proxy} defines methods for creating dynamic proxy classes and instances. 32 * A proxy class implements a declared set of interfaces and delegates method 33 * invocations to an {@code InvocationHandler}. 34 * 35 * @see InvocationHandler 36 * @since 1.3 37 */ 38 public class Proxy implements Serializable { 39 40 private static final long serialVersionUID = -2222568056686623797L; 41 42 private static int nextClassNameIndex = 0; 43 44 /** 45 * Orders methods by their name, parameters, return type and inheritance relationship. 46 * 47 * @hide 48 */ 49 private static final Comparator<Method> ORDER_BY_SIGNATURE_AND_SUBTYPE = new Comparator<Method>() { 50 @Override public int compare(Method a, Method b) { 51 int comparison = Method.ORDER_BY_SIGNATURE.compare(a, b); 52 if (comparison != 0) { 53 return comparison; 54 } 55 Class<?> aClass = a.getDeclaringClass(); 56 Class<?> bClass = b.getDeclaringClass(); 57 if (aClass == bClass) { 58 return 0; 59 } else if (aClass.isAssignableFrom(bClass)) { 60 return 1; 61 } else if (bClass.isAssignableFrom(aClass)) { 62 return -1; 63 } else { 64 return 0; 65 } 66 } 67 }; 68 69 /** The invocation handler on which the method calls are dispatched. */ 70 protected InvocationHandler h; 71 72 @SuppressWarnings("unused") Proxy()73 private Proxy() { 74 } 75 76 /** 77 * Constructs a new {@code Proxy} instance with the specified invocation 78 * handler. 79 * 80 * @param h 81 * the invocation handler for the newly created proxy 82 */ Proxy(InvocationHandler h)83 protected Proxy(InvocationHandler h) { 84 this.h = h; 85 } 86 87 /** 88 * Returns the dynamically built {@code Class} for the specified interfaces. 89 * Creates a new {@code Class} when necessary. The order of the interfaces 90 * is relevant. Invocations of this method with the same interfaces but 91 * different order result in different generated classes. The interfaces 92 * must be visible from the supplied class loader; no duplicates are 93 * permitted. All non-public interfaces must be defined in the same package. 94 * 95 * @param loader 96 * the class loader that will define the proxy class 97 * @param interfaces 98 * an array of {@code Class} objects, each one identifying an 99 * interface that will be implemented by the returned proxy 100 * class 101 * @return a proxy class that implements all of the interfaces referred to 102 * in the contents of {@code interfaces} 103 * @throws IllegalArgumentException 104 * if any of the interface restrictions are violated 105 * @throws NullPointerException 106 * if either {@code interfaces} or any of its elements are 107 * {@code null} 108 */ getProxyClass(ClassLoader loader, Class<?>... interfaces)109 public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) 110 throws IllegalArgumentException { 111 if (loader == null) { 112 loader = ClassLoader.getSystemClassLoader(); 113 } 114 115 if (interfaces == null) { 116 throw new NullPointerException("interfaces == null"); 117 } 118 119 // Make a copy of the list early on because we're using the list as a 120 // cache key and we don't want it changing under us. 121 final List<Class<?>> interfaceList = new ArrayList<Class<?>>(interfaces.length); 122 Collections.addAll(interfaceList, interfaces); 123 124 // We use a HashSet *only* for detecting duplicates and null entries. We 125 // can't use it as our cache key because we need to preserve the order in 126 // which these interfaces were specified. (Different orders should define 127 // different proxies.) 128 final Set<Class<?>> interfaceSet = new HashSet<Class<?>>(interfaceList); 129 if (interfaceSet.contains(null)) { 130 throw new NullPointerException("interface list contains null: " + interfaceList); 131 } 132 133 if (interfaceSet.size() != interfaces.length) { 134 throw new IllegalArgumentException("duplicate interface in list: " + interfaceList); 135 } 136 137 synchronized (loader.proxyCache) { 138 Class<?> proxy = loader.proxyCache.get(interfaceList); 139 if (proxy != null) { 140 return proxy; 141 } 142 } 143 144 String commonPackageName = null; 145 for (Class<?> c : interfaces) { 146 if (!c.isInterface()) { 147 throw new IllegalArgumentException(c + " is not an interface"); 148 } 149 if (!isVisibleToClassLoader(loader, c)) { 150 throw new IllegalArgumentException(c + " is not visible from class loader"); 151 } 152 if (!Modifier.isPublic(c.getModifiers())) { 153 String packageName = c.getPackageName$(); 154 if (packageName == null) { 155 packageName = ""; 156 } 157 if (commonPackageName != null && !commonPackageName.equals(packageName)) { 158 throw new IllegalArgumentException( 159 "non-public interfaces must be in the same package"); 160 } 161 commonPackageName = packageName; 162 } 163 } 164 165 List<Method> methods = getMethods(interfaces); 166 Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE); 167 validateReturnTypes(methods); 168 List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods); 169 170 ArtMethod[] methodsArray = new ArtMethod[methods.size()]; 171 for (int i = 0; i < methodsArray.length; i++) { 172 methodsArray[i] = methods.get(i).getArtMethod(); 173 } 174 Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]); 175 176 String baseName = commonPackageName != null && !commonPackageName.isEmpty() 177 ? commonPackageName + ".$Proxy" 178 : "$Proxy"; 179 180 Class<?> result; 181 synchronized (loader.proxyCache) { 182 result = loader.proxyCache.get(interfaceSet); 183 if (result == null) { 184 String name = baseName + nextClassNameIndex++; 185 result = generateProxy(name, interfaces, loader, methodsArray, exceptionsArray); 186 loader.proxyCache.put(interfaceList, result); 187 } 188 } 189 190 return result; 191 } 192 isVisibleToClassLoader(ClassLoader loader, Class<?> c)193 private static boolean isVisibleToClassLoader(ClassLoader loader, Class<?> c) { 194 try { 195 return loader == c.getClassLoader() || c == Class.forName(c.getName(), false, loader); 196 } catch (ClassNotFoundException ex) { 197 return false; 198 } 199 } 200 201 /** 202 * Returns an instance of the dynamically built class for the specified 203 * interfaces. Method invocations on the returned instance are forwarded to 204 * the specified invocation handler. The interfaces must be visible from the 205 * supplied class loader; no duplicates are permitted. All non-public 206 * interfaces must be defined in the same package. 207 * 208 * @param loader 209 * the class loader that will define the proxy class 210 * @param interfaces 211 * an array of {@code Class} objects, each one identifying an 212 * interface that will be implemented by the returned proxy 213 * object 214 * @param invocationHandler 215 * the invocation handler that handles the dispatched method 216 * invocations 217 * @return a new proxy object that delegates to the handler {@code h} 218 * @throws IllegalArgumentException 219 * if any of the interface restrictions are violated 220 * @throws NullPointerException 221 * if the interfaces or any of its elements are null 222 */ newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler invocationHandler)223 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, 224 InvocationHandler invocationHandler) 225 throws IllegalArgumentException { 226 227 if (invocationHandler == null) { 228 throw new NullPointerException("invocationHandler == null"); 229 } 230 Exception cause; 231 try { 232 return getProxyClass(loader, interfaces) 233 .getConstructor(InvocationHandler.class) 234 .newInstance(invocationHandler); 235 } catch (NoSuchMethodException e) { 236 cause = e; 237 } catch (IllegalAccessException e) { 238 cause = e; 239 } catch (InstantiationException e) { 240 cause = e; 241 } catch (InvocationTargetException e) { 242 cause = e; 243 } 244 AssertionError error = new AssertionError(); 245 error.initCause(cause); 246 throw error; 247 } 248 249 /** 250 * Indicates whether or not the specified class is a dynamically generated 251 * proxy class. 252 * 253 * @param cl 254 * the class 255 * @return {@code true} if the class is a proxy class, {@code false} 256 * otherwise 257 * @throws NullPointerException 258 * if the class is {@code null} 259 */ isProxyClass(Class<?> cl)260 public static boolean isProxyClass(Class<?> cl) { 261 return cl.isProxy(); 262 } 263 264 /** 265 * Returns the invocation handler of the specified proxy instance. 266 * 267 * @param proxy 268 * the proxy instance 269 * @return the invocation handler of the specified proxy instance 270 * @throws IllegalArgumentException 271 * if the supplied {@code proxy} is not a proxy object 272 */ getInvocationHandler(Object proxy)273 public static InvocationHandler getInvocationHandler(Object proxy) 274 throws IllegalArgumentException { 275 // TODO: return false for subclasses of Proxy not created by generateProxy() 276 if (!(proxy instanceof Proxy)) { 277 throw new IllegalArgumentException("not a proxy instance"); 278 } 279 return ((Proxy) proxy).h; 280 } 281 getMethods(Class<?>[] interfaces)282 private static List<Method> getMethods(Class<?>[] interfaces) { 283 List<Method> result = new ArrayList<Method>(); 284 try { 285 result.add(Object.class.getMethod("equals", Object.class)); 286 result.add(Object.class.getMethod("hashCode", EmptyArray.CLASS)); 287 result.add(Object.class.getMethod("toString", EmptyArray.CLASS)); 288 } catch (NoSuchMethodException e) { 289 throw new AssertionError(); 290 } 291 292 getMethodsRecursive(interfaces, result); 293 return result; 294 } 295 296 /** 297 * Fills {@code proxiedMethods} with the methods of {@code interfaces} and 298 * the interfaces they extend. May contain duplicates. 299 */ getMethodsRecursive(Class<?>[] interfaces, List<Method> methods)300 private static void getMethodsRecursive(Class<?>[] interfaces, List<Method> methods) { 301 for (Class<?> i : interfaces) { 302 getMethodsRecursive(i.getInterfaces(), methods); 303 Collections.addAll(methods, i.getDeclaredMethods()); 304 } 305 } 306 307 /** 308 * Throws if any two methods in {@code methods} have the same name and 309 * parameters but incompatible return types. 310 * 311 * @param methods the methods to find exceptions for, ordered by name and 312 * signature. 313 */ validateReturnTypes(List<Method> methods)314 private static void validateReturnTypes(List<Method> methods) { 315 Method vs = null; 316 for (Method method : methods) { 317 if (vs == null || !vs.equalNameAndParameters(method)) { 318 vs = method; // this has a different name or parameters 319 continue; 320 } 321 Class<?> returnType = method.getReturnType(); 322 Class<?> vsReturnType = vs.getReturnType(); 323 if (returnType.isInterface() && vsReturnType.isInterface()) { 324 // all interfaces are mutually compatible 325 } else if (vsReturnType.isAssignableFrom(returnType)) { 326 vs = method; // the new return type is a subtype; use it instead 327 } else if (!returnType.isAssignableFrom(vsReturnType)) { 328 throw new IllegalArgumentException("proxied interface methods have incompatible " 329 + "return types:\n " + vs + "\n " + method); 330 } 331 } 332 } 333 334 /** 335 * Remove methods that have the same name, parameters and return type. This 336 * computes the exceptions of each method; this is the intersection of the 337 * exceptions of equivalent methods. 338 * 339 * @param methods the methods to find exceptions for, ordered by name and 340 * signature. 341 */ deduplicateAndGetExceptions(List<Method> methods)342 private static List<Class<?>[]> deduplicateAndGetExceptions(List<Method> methods) { 343 List<Class<?>[]> exceptions = new ArrayList<Class<?>[]>(methods.size()); 344 345 for (int i = 0; i < methods.size(); ) { 346 Method method = methods.get(i); 347 Class<?>[] exceptionTypes = method.getExceptionTypes(); 348 349 if (i > 0 && Method.ORDER_BY_SIGNATURE.compare(method, methods.get(i - 1)) == 0) { 350 exceptions.set(i - 1, intersectExceptions(exceptions.get(i - 1), exceptionTypes)); 351 methods.remove(i); 352 } else { 353 exceptions.add(exceptionTypes); 354 i++; 355 } 356 } 357 return exceptions; 358 } 359 360 /** 361 * Returns the exceptions that are declared in both {@code aExceptions} and 362 * {@code bExceptions}. If an exception type in one array is a subtype of an 363 * exception from the other, the subtype is included in the intersection. 364 */ intersectExceptions(Class<?>[] aExceptions, Class<?>[] bExceptions)365 private static Class<?>[] intersectExceptions(Class<?>[] aExceptions, Class<?>[] bExceptions) { 366 if (aExceptions.length == 0 || bExceptions.length == 0) { 367 return EmptyArray.CLASS; 368 } 369 if (Arrays.equals(aExceptions, bExceptions)) { 370 return aExceptions; 371 } 372 Set<Class<?>> intersection = new HashSet<Class<?>>(); 373 for (Class<?> a : aExceptions) { 374 for (Class<?> b : bExceptions) { 375 if (a.isAssignableFrom(b)) { 376 intersection.add(b); 377 } else if (b.isAssignableFrom(a)) { 378 intersection.add(a); 379 } 380 } 381 } 382 return intersection.toArray(new Class<?>[intersection.size()]); 383 } 384 generateProxy(String name, Class<?>[] interfaces, ClassLoader loader, ArtMethod[] methods, Class<?>[][] exceptions)385 private static native Class<?> generateProxy(String name, Class<?>[] interfaces, 386 ClassLoader loader, ArtMethod[] methods, 387 Class<?>[][] exceptions); 388 389 /* 390 * The VM clones this method's descriptor when generating a proxy class. 391 * There is no implementation. 392 */ constructorPrototype(InvocationHandler h)393 private static native void constructorPrototype(InvocationHandler h); 394 invoke(Proxy proxy, ArtMethod method, Object[] args)395 static Object invoke(Proxy proxy, ArtMethod method, Object[] args) throws Throwable { 396 InvocationHandler h = proxy.h; 397 return h.invoke(proxy, new Method(method), args); 398 } 399 } 400