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    Redistribution and use in source and binary forms, with or without
37    modification, are permitted provided that the following conditions are
38    met:
39    * Redistributions of source code must retain the above copyright
40      notice, this list of conditions and the following disclaimer.
41    * Redistributions in binary form must reproduce the above copyright
42      notice, this list of conditions and the following disclaimer in the
43      documentation and/or other materials provided with the distribution.
44    * Neither the name of the copyright holder nor the names of
45      contributors may be used to endorse or promote products derived from
46      this software without specific prior written permission.
47 
48    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
49    IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
50    TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
51    PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
52    BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
53    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
54    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
55    BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
56    WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
57    OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
58    ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59 */
60 
61 package jdk.dynalink.linker.support;
62 
63 import java.lang.invoke.MethodHandle;
64 import java.lang.invoke.MethodHandles;
65 import java.lang.invoke.MethodType;
66 import java.lang.reflect.Constructor;
67 import java.lang.reflect.Field;
68 import java.lang.reflect.Method;
69 
70 /**
71  * A wrapper around {@link java.lang.invoke.MethodHandles.Lookup} that masks
72  * checked exceptions. It is useful in those cases when you're looking up
73  * methods within your own codebase (therefore it is an error if they are not
74  * present).
75  */
76 public final class Lookup {
77     private final MethodHandles.Lookup lookup;
78 
79     /**
80      * Creates a new instance, bound to an instance of
81      * {@link java.lang.invoke.MethodHandles.Lookup}.
82      *
83      * @param lookup the {@link java.lang.invoke.MethodHandles.Lookup} it delegates to.
84      */
Lookup(final MethodHandles.Lookup lookup)85     public Lookup(final MethodHandles.Lookup lookup) {
86         this.lookup = lookup;
87     }
88 
89     /**
90      * A canonical Lookup object that wraps {@link MethodHandles#publicLookup()}.
91      */
92     public static final Lookup PUBLIC = new Lookup(MethodHandles.publicLookup());
93 
94     /**
95      * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflect(Method)},
96      * converting any encountered {@link IllegalAccessException} into an
97      * {@link IllegalAccessError}.
98      *
99      * @param m the method to unreflect
100      * @return the unreflected method handle.
101      * @throws IllegalAccessError if the method is inaccessible.
102      */
unreflect(final Method m)103     public MethodHandle unreflect(final Method m) {
104         return unreflect(lookup, m);
105     }
106 
107     /**
108      * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflect(Method)},
109      * converting any encountered {@link IllegalAccessException} into an
110      * {@link IllegalAccessError}.
111      *
112      * @param lookup the lookup used to unreflect
113      * @param m the method to unreflect
114      * @return the unreflected method handle.
115      * @throws IllegalAccessError if the method is inaccessible.
116      */
unreflect(final MethodHandles.Lookup lookup, final Method m)117     public static MethodHandle unreflect(final MethodHandles.Lookup lookup, final Method m) {
118         try {
119             return lookup.unreflect(m);
120         } catch(final IllegalAccessException e) {
121             final IllegalAccessError ee = new IllegalAccessError("Failed to unreflect method " + m);
122             ee.initCause(e);
123             throw ee;
124         }
125     }
126 
127     /**
128      * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflectGetter(Field)},
129      * converting any encountered {@link IllegalAccessException} into an {@link IllegalAccessError}.
130      *
131      * @param f the field for which a getter is unreflected
132      * @return the unreflected field getter handle.
133      * @throws IllegalAccessError if the getter is inaccessible.
134      */
unreflectGetter(final Field f)135     public MethodHandle unreflectGetter(final Field f) {
136         try {
137             return lookup.unreflectGetter(f);
138         } catch(final IllegalAccessException e) {
139             final IllegalAccessError ee = new IllegalAccessError("Failed to unreflect getter for field " + f);
140             ee.initCause(e);
141             throw ee;
142         }
143     }
144 
145     /**
146      * Performs a {@link java.lang.invoke.MethodHandles.Lookup#findGetter(Class, String, Class)},
147      * converting any encountered {@link IllegalAccessException} into an
148      * {@link IllegalAccessError} and {@link NoSuchFieldException} into a
149      * {@link NoSuchFieldError}.
150      *
151      * @param refc the class declaring the field
152      * @param name the name of the field
153      * @param type the type of the field
154      * @return the unreflected field getter handle.
155      * @throws IllegalAccessError if the field is inaccessible.
156      * @throws NoSuchFieldError if the field does not exist.
157      */
findGetter(final Class<?>refc, final String name, final Class<?> type)158     public MethodHandle findGetter(final Class<?>refc, final String name, final Class<?> type) {
159         try {
160             return lookup.findGetter(refc, name, type);
161         } catch(final IllegalAccessException e) {
162             final IllegalAccessError ee = new IllegalAccessError("Failed to access getter for field " + refc.getName() +
163                     "." + name + " of type " + type.getName());
164             ee.initCause(e);
165             throw ee;
166         } catch(final NoSuchFieldException e) {
167             final NoSuchFieldError ee = new NoSuchFieldError("Failed to find getter for field " + refc.getName() +
168                     "." + name + " of type " + type.getName());
169             ee.initCause(e);
170             throw ee;
171         }
172     }
173 
174     /**
175      * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflectSetter(Field)},
176      * converting any encountered {@link IllegalAccessException} into an
177      * {@link IllegalAccessError}.
178      *
179      * @param f the field for which a setter is unreflected
180      * @return the unreflected field setter handle.
181      * @throws IllegalAccessError if the field is inaccessible.
182      * @throws NoSuchFieldError if the field does not exist.
183      */
unreflectSetter(final Field f)184     public MethodHandle unreflectSetter(final Field f) {
185         try {
186             return lookup.unreflectSetter(f);
187         } catch(final IllegalAccessException e) {
188             final IllegalAccessError ee = new IllegalAccessError("Failed to unreflect setter for field " + f);
189             ee.initCause(e);
190             throw ee;
191         }
192     }
193 
194     /**
195      * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflectConstructor(Constructor)},
196      * converting any encountered {@link IllegalAccessException} into an
197      * {@link IllegalAccessError}.
198      *
199      * @param c the constructor to unreflect
200      * @return the unreflected constructor handle.
201      * @throws IllegalAccessError if the constructor is inaccessible.
202      */
unreflectConstructor(final Constructor<?> c)203     public MethodHandle unreflectConstructor(final Constructor<?> c) {
204         return unreflectConstructor(lookup, c);
205     }
206 
207     /**
208      * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflectConstructor(Constructor)},
209      * converting any encountered {@link IllegalAccessException} into an
210      * {@link IllegalAccessError}.
211      *
212      * @param lookup the lookup used to unreflect
213      * @param c the constructor to unreflect
214      * @return the unreflected constructor handle.
215      * @throws IllegalAccessError if the constructor is inaccessible.
216      */
unreflectConstructor(final MethodHandles.Lookup lookup, final Constructor<?> c)217     public static MethodHandle unreflectConstructor(final MethodHandles.Lookup lookup, final Constructor<?> c) {
218         try {
219             return lookup.unreflectConstructor(c);
220         } catch(final IllegalAccessException e) {
221             final IllegalAccessError ee = new IllegalAccessError("Failed to unreflect constructor " + c);
222             ee.initCause(e);
223             throw ee;
224         }
225     }
226 
227     /**
228      * Performs a {@link java.lang.invoke.MethodHandles.Lookup#findSpecial(Class, String, MethodType, Class)}
229      * on the underlying lookup. Converts any encountered
230      * {@link IllegalAccessException} into an {@link IllegalAccessError} and
231      * {@link NoSuchMethodException} into a {@link NoSuchMethodError}.
232      *
233      * @param declaringClass class declaring the method
234      * @param name the name of the method
235      * @param type the type of the method
236      * @return a method handle for the method
237      * @throws IllegalAccessError if the method is inaccessible.
238      * @throws NoSuchMethodError if the method does not exist.
239      */
findSpecial(final Class<?> declaringClass, final String name, final MethodType type)240     public MethodHandle findSpecial(final Class<?> declaringClass, final String name, final MethodType type) {
241         try {
242             return lookup.findSpecial(declaringClass, name, type, declaringClass);
243         } catch(final IllegalAccessException e) {
244             final IllegalAccessError ee = new IllegalAccessError("Failed to access special method " + methodDescription(
245                     declaringClass, name, type));
246             ee.initCause(e);
247             throw ee;
248         } catch(final NoSuchMethodException e) {
249             final NoSuchMethodError ee = new NoSuchMethodError("Failed to find special method " + methodDescription(
250                     declaringClass, name, type));
251             ee.initCause(e);
252             throw ee;
253         }
254     }
255 
methodDescription(final Class<?> declaringClass, final String name, final MethodType type)256     private static String methodDescription(final Class<?> declaringClass, final String name, final MethodType type) {
257         return declaringClass.getName() + "#" + name + type;
258     }
259 
260     /**
261      * Performs a {@link java.lang.invoke.MethodHandles.Lookup#findStatic(Class, String, MethodType)}
262      * on the underlying lookup. Converts any encountered
263      * {@link IllegalAccessException} into an {@link IllegalAccessError} and
264      * {@link NoSuchMethodException} into a {@link NoSuchMethodError}.
265      *
266      * @param declaringClass class declaring the method
267      * @param name the name of the method
268      * @param type the type of the method
269      * @return a method handle for the method
270      * @throws IllegalAccessError if the method is inaccessible.
271      * @throws NoSuchMethodError if the method does not exist.
272      */
findStatic(final Class<?> declaringClass, final String name, final MethodType type)273     public MethodHandle findStatic(final Class<?> declaringClass, final String name, final MethodType type) {
274         try {
275             return lookup.findStatic(declaringClass, name, type);
276         } catch(final IllegalAccessException e) {
277             final IllegalAccessError ee = new IllegalAccessError("Failed to access static method " + methodDescription(
278                     declaringClass, name, type));
279             ee.initCause(e);
280             throw ee;
281         } catch(final NoSuchMethodException e) {
282             final NoSuchMethodError ee = new NoSuchMethodError("Failed to find static method " + methodDescription(
283                     declaringClass, name, type));
284             ee.initCause(e);
285             throw ee;
286         }
287     }
288 
289     /**
290      * Performs a {@link java.lang.invoke.MethodHandles.Lookup#findVirtual(Class, String, MethodType)}
291      * on the underlying lookup. Converts any encountered
292      * {@link IllegalAccessException} into an {@link IllegalAccessError} and
293      * {@link NoSuchMethodException} into a {@link NoSuchMethodError}.
294      *
295      * @param declaringClass class declaring the method
296      * @param name the name of the method
297      * @param type the type of the method
298      * @return a method handle for the method
299      * @throws IllegalAccessError if the method is inaccessible.
300      * @throws NoSuchMethodError if the method does not exist.
301      */
findVirtual(final Class<?> declaringClass, final String name, final MethodType type)302     public MethodHandle findVirtual(final Class<?> declaringClass, final String name, final MethodType type) {
303         try {
304             return lookup.findVirtual(declaringClass, name, type);
305         } catch(final IllegalAccessException e) {
306             final IllegalAccessError ee = new IllegalAccessError("Failed to access virtual method " + methodDescription(
307                     declaringClass, name, type));
308             ee.initCause(e);
309             throw ee;
310         } catch(final NoSuchMethodException e) {
311             final NoSuchMethodError ee = new NoSuchMethodError("Failed to find virtual method " + methodDescription(
312                     declaringClass, name, type));
313             ee.initCause(e);
314             throw ee;
315         }
316     }
317 
318     /**
319      * Given a lookup, finds using {@link #findSpecial(Class, String, MethodType)}
320      * a method on that lookup's class. Useful in classes' code for convenient
321      * linking to their own privates.
322      * @param lookup the lookup for the class
323      * @param name the name of the method
324      * @param rtype the return type of the method
325      * @param ptypes the parameter types of the method
326      * @return the method handle for the method
327      */
findOwnSpecial(final MethodHandles.Lookup lookup, final String name, final Class<?> rtype, final Class<?>... ptypes)328     public static MethodHandle findOwnSpecial(final MethodHandles.Lookup lookup, final String name, final Class<?> rtype, final Class<?>... ptypes) {
329         return new Lookup(lookup).findOwnSpecial(name, rtype, ptypes);
330     }
331 
332 
333     /**
334      * Finds using {@link #findSpecial(Class, String, MethodType)} a method on
335      * that lookup's class. Useful in classes' code for convenient linking to
336      * their own privates. It's also more convenient than {@code findSpecial}
337      * in that you can just list the parameter types, and don't have to specify
338      * lookup class.
339      * @param name the name of the method
340      * @param rtype the return type of the method
341      * @param ptypes the parameter types of the method
342      * @return the method handle for the method
343      */
findOwnSpecial(final String name, final Class<?> rtype, final Class<?>... ptypes)344     public MethodHandle findOwnSpecial(final String name, final Class<?> rtype, final Class<?>... ptypes) {
345         return findSpecial(lookup.lookupClass(), name, MethodType.methodType(rtype, ptypes));
346     }
347 
348     /**
349      * Given a lookup, finds using {@link #findStatic(Class, String, MethodType)}
350      * a method on that lookup's class. Useful in classes' code for convenient
351      * linking to their own privates. It's easier to use than {@code findStatic}
352      * in that you can just list the parameter types, and don't have to specify
353      * lookup class.
354      * @param lookup the lookup for the class
355      * @param name the name of the method
356      * @param rtype the return type of the method
357      * @param ptypes the parameter types of the method
358      * @return the method handle for the method
359      */
findOwnStatic(final MethodHandles.Lookup lookup, final String name, final Class<?> rtype, final Class<?>... ptypes)360     public static MethodHandle findOwnStatic(final MethodHandles.Lookup lookup, final String name, final Class<?> rtype, final Class<?>... ptypes) {
361         return new Lookup(lookup).findOwnStatic(name, rtype, ptypes);
362     }
363 
364     /**
365      * Finds using {@link #findStatic(Class, String, MethodType)} a method on
366      * that lookup's class. Useful in classes' code for convenient linking to
367      * their own privates. It's easier to use than {@code findStatic}
368      * in that you can just list the parameter types, and don't have to specify
369      * lookup class.
370      * @param name the name of the method
371      * @param rtype the return type of the method
372      * @param ptypes the parameter types of the method
373      * @return the method handle for the method
374      */
findOwnStatic(final String name, final Class<?> rtype, final Class<?>... ptypes)375     public MethodHandle findOwnStatic(final String name, final Class<?> rtype, final Class<?>... ptypes) {
376         return findStatic(lookup.lookupClass(), name, MethodType.methodType(rtype, ptypes));
377     }
378 }
379