Lookup.java revision 1643:133ea8746b37
1/* 2 * Copyright (c) 2010, 2013, 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 26/* 27 * This file is available under and governed by the GNU General Public 28 * License version 2 only, as published by the Free Software Foundation. 29 * However, the following notice accompanied the original version of this 30 * file, and Oracle licenses the original version of this file under the BSD 31 * license: 32 */ 33/* 34 Copyright 2009-2013 Attila Szegedi 35 36 Licensed under both the Apache License, Version 2.0 (the "Apache License") 37 and the BSD License (the "BSD License"), with licensee being free to 38 choose either of the two at their discretion. 39 40 You may not use this file except in compliance with either the Apache 41 License or the BSD License. 42 43 If you choose to use this file in compliance with the Apache License, the 44 following notice applies to you: 45 46 You may obtain a copy of the Apache License at 47 48 http://www.apache.org/licenses/LICENSE-2.0 49 50 Unless required by applicable law or agreed to in writing, software 51 distributed under the License is distributed on an "AS IS" BASIS, 52 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 53 implied. See the License for the specific language governing 54 permissions and limitations under the License. 55 56 If you choose to use this file in compliance with the BSD License, the 57 following notice applies to you: 58 59 Redistribution and use in source and binary forms, with or without 60 modification, are permitted provided that the following conditions are 61 met: 62 * Redistributions of source code must retain the above copyright 63 notice, this list of conditions and the following disclaimer. 64 * Redistributions in binary form must reproduce the above copyright 65 notice, this list of conditions and the following disclaimer in the 66 documentation and/or other materials provided with the distribution. 67 * Neither the name of the copyright holder nor the names of 68 contributors may be used to endorse or promote products derived from 69 this software without specific prior written permission. 70 71 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 72 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 73 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 74 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER 75 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 76 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 77 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 78 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 79 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 80 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 81 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 82*/ 83 84package jdk.dynalink.linker.support; 85 86import java.lang.invoke.MethodHandle; 87import java.lang.invoke.MethodHandles; 88import java.lang.invoke.MethodType; 89import java.lang.reflect.Constructor; 90import java.lang.reflect.Executable; 91import java.lang.reflect.Field; 92import java.lang.reflect.Module; 93import java.lang.reflect.Method; 94import jdk.internal.module.Modules; 95 96/** 97 * A wrapper around {@link java.lang.invoke.MethodHandles.Lookup} that masks 98 * checked exceptions. It is useful in those cases when you're looking up 99 * methods within your own codebase (therefore it is an error if they are not 100 * present). 101 */ 102public final class Lookup { 103 private final MethodHandles.Lookup lookup; 104 105 /** 106 * Creates a new instance, bound to an instance of 107 * {@link java.lang.invoke.MethodHandles.Lookup}. 108 * 109 * @param lookup the {@link java.lang.invoke.MethodHandles.Lookup} it delegates to. 110 */ 111 public Lookup(final MethodHandles.Lookup lookup) { 112 this.lookup = lookup; 113 } 114 115 /** 116 * A canonical Lookup object that wraps {@link MethodHandles#publicLookup()}. 117 */ 118 public static final Lookup PUBLIC = new Lookup(MethodHandles.publicLookup()); 119 120 /** 121 * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflect(Method)}, 122 * converting any encountered {@link IllegalAccessException} into an 123 * {@link IllegalAccessError}. 124 * 125 * @param m the method to unreflect 126 * @return the unreflected method handle. 127 * @throws IllegalAccessError if the method is inaccessible. 128 */ 129 public MethodHandle unreflect(final Method m) { 130 return unreflect(lookup, m); 131 } 132 133 private static boolean addModuleRead(final MethodHandles.Lookup lookup, final Executable e) { 134 // may be module read missing from a script class! 135 final Class<?> declClass = e.getDeclaringClass(); 136 final Module from = lookup.lookupClass().getModule(); 137 final Module to = declClass.getModule(); 138 if (from != null && to != null) { 139 Modules.addReads(from, to); 140 return true; 141 } 142 143 return false; 144 } 145 146 /** 147 * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflect(Method)}, of a caller sensitive method 148 * converting any encountered {@link IllegalAccessException} into an {@link IllegalAccessError}. 149 * 150 * @param lookup the lookup used to unreflect 151 * @param m the method to unreflect 152 * @return the unreflected method handle. 153 */ 154 public static MethodHandle unreflectCallerSensitive(final MethodHandles.Lookup lookup, final Method m) { 155 try { 156 return unreflect(lookup, m); 157 } catch (final IllegalAccessError iae) { 158 if (addModuleRead(lookup, m)) { 159 try { 160 return unreflect(lookup, m); 161 } catch (final IllegalAccessError e2) { 162 // fall through and throw original error as cause 163 } 164 } 165 throw iae; 166 } 167 } 168 169 /** 170 * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflect(Method)}, 171 * converting any encountered {@link IllegalAccessException} into an 172 * {@link IllegalAccessError}. 173 * 174 * @param lookup the lookup used to unreflect 175 * @param m the method to unreflect 176 * @return the unreflected method handle. 177 * @throws IllegalAccessError if the method is inaccessible. 178 */ 179 public static MethodHandle unreflect(final MethodHandles.Lookup lookup, final Method m) { 180 try { 181 return lookup.unreflect(m); 182 } catch(final IllegalAccessException e) { 183 final IllegalAccessError ee = new IllegalAccessError("Failed to unreflect method " + m); 184 ee.initCause(e); 185 throw ee; 186 } 187 } 188 189 /** 190 * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflectGetter(Field)}, 191 * converting any encountered {@link IllegalAccessException} into an {@link IllegalAccessError}. 192 * 193 * @param f the field for which a getter is unreflected 194 * @return the unreflected field getter handle. 195 * @throws IllegalAccessError if the getter is inaccessible. 196 */ 197 public MethodHandle unreflectGetter(final Field f) { 198 try { 199 return lookup.unreflectGetter(f); 200 } catch(final IllegalAccessException e) { 201 final IllegalAccessError ee = new IllegalAccessError("Failed to unreflect getter for field " + f); 202 ee.initCause(e); 203 throw ee; 204 } 205 } 206 207 /** 208 * Performs a {@link java.lang.invoke.MethodHandles.Lookup#findGetter(Class, String, Class)}, 209 * converting any encountered {@link IllegalAccessException} into an 210 * {@link IllegalAccessError} and {@link NoSuchFieldException} into a 211 * {@link NoSuchFieldError}. 212 * 213 * @param refc the class declaring the field 214 * @param name the name of the field 215 * @param type the type of the field 216 * @return the unreflected field getter handle. 217 * @throws IllegalAccessError if the field is inaccessible. 218 * @throws NoSuchFieldError if the field does not exist. 219 */ 220 public MethodHandle findGetter(final Class<?>refc, final String name, final Class<?> type) { 221 try { 222 return lookup.findGetter(refc, name, type); 223 } catch(final IllegalAccessException e) { 224 final IllegalAccessError ee = new IllegalAccessError("Failed to access getter for field " + refc.getName() + 225 "." + name + " of type " + type.getName()); 226 ee.initCause(e); 227 throw ee; 228 } catch(final NoSuchFieldException e) { 229 final NoSuchFieldError ee = new NoSuchFieldError("Failed to find getter for field " + refc.getName() + 230 "." + name + " of type " + type.getName()); 231 ee.initCause(e); 232 throw ee; 233 } 234 } 235 236 /** 237 * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflectSetter(Field)}, 238 * converting any encountered {@link IllegalAccessException} into an 239 * {@link IllegalAccessError}. 240 * 241 * @param f the field for which a setter is unreflected 242 * @return the unreflected field setter handle. 243 * @throws IllegalAccessError if the field is inaccessible. 244 * @throws NoSuchFieldError if the field does not exist. 245 */ 246 public MethodHandle unreflectSetter(final Field f) { 247 try { 248 return lookup.unreflectSetter(f); 249 } catch(final IllegalAccessException e) { 250 final IllegalAccessError ee = new IllegalAccessError("Failed to unreflect setter for field " + f); 251 ee.initCause(e); 252 throw ee; 253 } 254 } 255 256 /** 257 * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflectConstructor(Constructor)}, 258 * converting any encountered {@link IllegalAccessException} into an 259 * {@link IllegalAccessError}. 260 * 261 * @param c the constructor to unreflect 262 * @return the unreflected constructor handle. 263 * @throws IllegalAccessError if the constructor is inaccessible. 264 */ 265 public MethodHandle unreflectConstructor(final Constructor<?> c) { 266 return unreflectConstructor(lookup, c); 267 } 268 269 /** 270 * Performs a caller sensitive {@link java.lang.invoke.MethodHandles.Lookup#unreflectConstructor(Constructor)}, converting any 271 * encountered {@link IllegalAccessException} into an {@link IllegalAccessError}. 272 * 273 * @param lookup the lookup used to unreflect 274 * @param c the constructor to unreflect 275 * @return the unreflected constructor handle. 276 */ 277 public static MethodHandle unreflectConstructorCallerSensitive(final MethodHandles.Lookup lookup, final Constructor<?> c) { 278 try { 279 return unreflectConstructor(lookup, c); 280 } catch (final IllegalAccessError iae) { 281 if (addModuleRead(lookup, c)) { 282 try { 283 return unreflectConstructor(lookup, c); 284 } catch (final IllegalAccessError e2) { 285 // fall through and throw original error as cause 286 } 287 } 288 throw iae; 289 } 290 } 291 292 /** 293 * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflectConstructor(Constructor)}, 294 * converting any encountered {@link IllegalAccessException} into an 295 * {@link IllegalAccessError}. 296 * 297 * @param lookup the lookup used to unreflect 298 * @param c the constructor to unreflect 299 * @return the unreflected constructor handle. 300 * @throws IllegalAccessError if the constructor is inaccessible. 301 */ 302 public static MethodHandle unreflectConstructor(final MethodHandles.Lookup lookup, final Constructor<?> c) { 303 try { 304 return lookup.unreflectConstructor(c); 305 } catch(final IllegalAccessException e) { 306 final IllegalAccessError ee = new IllegalAccessError("Failed to unreflect constructor " + c); 307 ee.initCause(e); 308 throw ee; 309 } 310 } 311 312 /** 313 * Performs a {@link java.lang.invoke.MethodHandles.Lookup#findSpecial(Class, String, MethodType, Class)} 314 * on the underlying lookup. Converts any encountered 315 * {@link IllegalAccessException} into an {@link IllegalAccessError} and 316 * {@link NoSuchMethodException} into a {@link NoSuchMethodError}. 317 * 318 * @param declaringClass class declaring the method 319 * @param name the name of the method 320 * @param type the type of the method 321 * @return a method handle for the method 322 * @throws IllegalAccessError if the method is inaccessible. 323 * @throws NoSuchMethodError if the method does not exist. 324 */ 325 public MethodHandle findSpecial(final Class<?> declaringClass, final String name, final MethodType type) { 326 try { 327 return lookup.findSpecial(declaringClass, name, type, declaringClass); 328 } catch(final IllegalAccessException e) { 329 final IllegalAccessError ee = new IllegalAccessError("Failed to access special method " + methodDescription( 330 declaringClass, name, type)); 331 ee.initCause(e); 332 throw ee; 333 } catch(final NoSuchMethodException e) { 334 final NoSuchMethodError ee = new NoSuchMethodError("Failed to find special method " + methodDescription( 335 declaringClass, name, type)); 336 ee.initCause(e); 337 throw ee; 338 } 339 } 340 341 private static String methodDescription(final Class<?> declaringClass, final String name, final MethodType type) { 342 return declaringClass.getName() + "#" + name + type; 343 } 344 345 /** 346 * Performs a {@link java.lang.invoke.MethodHandles.Lookup#findStatic(Class, String, MethodType)} 347 * on the underlying lookup. Converts any encountered 348 * {@link IllegalAccessException} into an {@link IllegalAccessError} and 349 * {@link NoSuchMethodException} into a {@link NoSuchMethodError}. 350 * 351 * @param declaringClass class declaring the method 352 * @param name the name of the method 353 * @param type the type of the method 354 * @return a method handle for the method 355 * @throws IllegalAccessError if the method is inaccessible. 356 * @throws NoSuchMethodError if the method does not exist. 357 */ 358 public MethodHandle findStatic(final Class<?> declaringClass, final String name, final MethodType type) { 359 try { 360 return lookup.findStatic(declaringClass, name, type); 361 } catch(final IllegalAccessException e) { 362 final IllegalAccessError ee = new IllegalAccessError("Failed to access static method " + methodDescription( 363 declaringClass, name, type)); 364 ee.initCause(e); 365 throw ee; 366 } catch(final NoSuchMethodException e) { 367 final NoSuchMethodError ee = new NoSuchMethodError("Failed to find static method " + methodDescription( 368 declaringClass, name, type)); 369 ee.initCause(e); 370 throw ee; 371 } 372 } 373 374 /** 375 * Performs a {@link java.lang.invoke.MethodHandles.Lookup#findVirtual(Class, String, MethodType)} 376 * on the underlying lookup. Converts any encountered 377 * {@link IllegalAccessException} into an {@link IllegalAccessError} and 378 * {@link NoSuchMethodException} into a {@link NoSuchMethodError}. 379 * 380 * @param declaringClass class declaring the method 381 * @param name the name of the method 382 * @param type the type of the method 383 * @return a method handle for the method 384 * @throws IllegalAccessError if the method is inaccessible. 385 * @throws NoSuchMethodError if the method does not exist. 386 */ 387 public MethodHandle findVirtual(final Class<?> declaringClass, final String name, final MethodType type) { 388 try { 389 return lookup.findVirtual(declaringClass, name, type); 390 } catch(final IllegalAccessException e) { 391 final IllegalAccessError ee = new IllegalAccessError("Failed to access virtual method " + methodDescription( 392 declaringClass, name, type)); 393 ee.initCause(e); 394 throw ee; 395 } catch(final NoSuchMethodException e) { 396 final NoSuchMethodError ee = new NoSuchMethodError("Failed to find virtual method " + methodDescription( 397 declaringClass, name, type)); 398 ee.initCause(e); 399 throw ee; 400 } 401 } 402 403 /** 404 * Given a lookup, finds using {@link #findSpecial(Class, String, MethodType)} 405 * a method on that lookup's class. Useful in classes' code for convenient 406 * linking to their own privates. 407 * @param lookup the lookup for the class 408 * @param name the name of the method 409 * @param rtype the return type of the method 410 * @param ptypes the parameter types of the method 411 * @return the method handle for the method 412 */ 413 public static MethodHandle findOwnSpecial(final MethodHandles.Lookup lookup, final String name, final Class<?> rtype, final Class<?>... ptypes) { 414 return new Lookup(lookup).findOwnSpecial(name, rtype, ptypes); 415 } 416 417 418 /** 419 * Finds using {@link #findSpecial(Class, String, MethodType)} a method on 420 * that lookup's class. Useful in classes' code for convenient linking to 421 * their own privates. It's also more convenient than {@code findSpecial} 422 * in that you can just list the parameter types, and don't have to specify 423 * lookup class. 424 * @param name the name of the method 425 * @param rtype the return type of the method 426 * @param ptypes the parameter types of the method 427 * @return the method handle for the method 428 */ 429 public MethodHandle findOwnSpecial(final String name, final Class<?> rtype, final Class<?>... ptypes) { 430 return findSpecial(lookup.lookupClass(), name, MethodType.methodType(rtype, ptypes)); 431 } 432 433 /** 434 * Given a lookup, finds using {@link #findStatic(Class, String, MethodType)} 435 * a method on that lookup's class. Useful in classes' code for convenient 436 * linking to their own privates. It's easier to use than {@code findStatic} 437 * in that you can just list the parameter types, and don't have to specify 438 * lookup class. 439 * @param lookup the lookup for the class 440 * @param name the name of the method 441 * @param rtype the return type of the method 442 * @param ptypes the parameter types of the method 443 * @return the method handle for the method 444 */ 445 public static MethodHandle findOwnStatic(final MethodHandles.Lookup lookup, final String name, final Class<?> rtype, final Class<?>... ptypes) { 446 return new Lookup(lookup).findOwnStatic(name, rtype, ptypes); 447 } 448 449 /** 450 * Finds using {@link #findStatic(Class, String, MethodType)} a method on 451 * that lookup's class. Useful in classes' code for convenient linking to 452 * their own privates. It's easier to use than {@code findStatic} 453 * in that you can just list the parameter types, and don't have to specify 454 * lookup class. 455 * @param name the name of the method 456 * @param rtype the return type of the method 457 * @param ptypes the parameter types of the method 458 * @return the method handle for the method 459 */ 460 public MethodHandle findOwnStatic(final String name, final Class<?> rtype, final Class<?>... ptypes) { 461 return findStatic(lookup.lookupClass(), name, MethodType.methodType(rtype, ptypes)); 462 } 463} 464