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.Executable;
68 import java.lang.reflect.Field;
69 import java.lang.reflect.Method;
70 
71 /**
72  * A wrapper around {@link java.lang.invoke.MethodHandles.Lookup} that masks
73  * checked exceptions. It is useful in those cases when you're looking up
74  * methods within your own codebase (therefore it is an error if they are not
75  * present).
76  */
77 public final class Lookup {
78     private final MethodHandles.Lookup lookup;
79 
80     /**
81      * Creates a new instance, bound to an instance of
82      * {@link java.lang.invoke.MethodHandles.Lookup}.
83      *
84      * @param lookup the {@link java.lang.invoke.MethodHandles.Lookup} it delegates to.
85      */
Lookup(final MethodHandles.Lookup lookup)86     public Lookup(final MethodHandles.Lookup lookup) {
87         this.lookup = lookup;
88     }
89 
90     /**
91      * A canonical Lookup object that wraps {@link MethodHandles#publicLookup()}.
92      */
93     public static final Lookup PUBLIC = new Lookup(MethodHandles.publicLookup());
94 
95     /**
96      * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflect(Method)},
97      * converting any encountered {@link IllegalAccessException} into an
98      * {@link IllegalAccessError}.
99      *
100      * @param m the method to unreflect
101      * @return the unreflected method handle.
102      * @throws IllegalAccessError if the method is inaccessible.
103      */
unreflect(final Method m)104     public MethodHandle unreflect(final Method m) {
105         return unreflect(lookup, m);
106     }
107 
108     /**
109      * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflect(Method)},
110      * converting any encountered {@link IllegalAccessException} into an
111      * {@link IllegalAccessError}.
112      *
113      * @param lookup the lookup used to unreflect
114      * @param m the method to unreflect
115      * @return the unreflected method handle.
116      * @throws IllegalAccessError if the method is inaccessible.
117      */
unreflect(final MethodHandles.Lookup lookup, final Method m)118     public static MethodHandle unreflect(final MethodHandles.Lookup lookup, final Method m) {
119         try {
120             return lookup.unreflect(m);
121         } catch(final IllegalAccessException e) {
122             final IllegalAccessError ee = new IllegalAccessError("Failed to unreflect method " + m);
123             ee.initCause(e);
124             throw ee;
125         }
126     }
127 
128     /**
129      * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflectGetter(Field)},
130      * converting any encountered {@link IllegalAccessException} into an {@link IllegalAccessError}.
131      *
132      * @param f the field for which a getter is unreflected
133      * @return the unreflected field getter handle.
134      * @throws IllegalAccessError if the getter is inaccessible.
135      */
unreflectGetter(final Field f)136     public MethodHandle unreflectGetter(final Field f) {
137         try {
138             return lookup.unreflectGetter(f);
139         } catch(final IllegalAccessException e) {
140             final IllegalAccessError ee = new IllegalAccessError("Failed to unreflect getter for field " + f);
141             ee.initCause(e);
142             throw ee;
143         }
144     }
145 
146     /**
147      * Performs a {@link java.lang.invoke.MethodHandles.Lookup#findGetter(Class, String, Class)},
148      * converting any encountered {@link IllegalAccessException} into an
149      * {@link IllegalAccessError} and {@link NoSuchFieldException} into a
150      * {@link NoSuchFieldError}.
151      *
152      * @param refc the class declaring the field
153      * @param name the name of the field
154      * @param type the type of the field
155      * @return the unreflected field getter handle.
156      * @throws IllegalAccessError if the field is inaccessible.
157      * @throws NoSuchFieldError if the field does not exist.
158      */
findGetter(final Class<?>refc, final String name, final Class<?> type)159     public MethodHandle findGetter(final Class<?>refc, final String name, final Class<?> type) {
160         try {
161             return lookup.findGetter(refc, name, type);
162         } catch(final IllegalAccessException e) {
163             final IllegalAccessError ee = new IllegalAccessError("Failed to access getter for field " + refc.getName() +
164                     "." + name + " of type " + type.getName());
165             ee.initCause(e);
166             throw ee;
167         } catch(final NoSuchFieldException e) {
168             final NoSuchFieldError ee = new NoSuchFieldError("Failed to find getter for field " + refc.getName() +
169                     "." + name + " of type " + type.getName());
170             ee.initCause(e);
171             throw ee;
172         }
173     }
174 
175     /**
176      * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflectSetter(Field)},
177      * converting any encountered {@link IllegalAccessException} into an
178      * {@link IllegalAccessError}.
179      *
180      * @param f the field for which a setter is unreflected
181      * @return the unreflected field setter handle.
182      * @throws IllegalAccessError if the field is inaccessible.
183      * @throws NoSuchFieldError if the field does not exist.
184      */
unreflectSetter(final Field f)185     public MethodHandle unreflectSetter(final Field f) {
186         try {
187             return lookup.unreflectSetter(f);
188         } catch(final IllegalAccessException e) {
189             final IllegalAccessError ee = new IllegalAccessError("Failed to unreflect setter for field " + f);
190             ee.initCause(e);
191             throw ee;
192         }
193     }
194 
195     /**
196      * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflectConstructor(Constructor)},
197      * converting any encountered {@link IllegalAccessException} into an
198      * {@link IllegalAccessError}.
199      *
200      * @param c the constructor to unreflect
201      * @return the unreflected constructor handle.
202      * @throws IllegalAccessError if the constructor is inaccessible.
203      */
unreflectConstructor(final Constructor<?> c)204     public MethodHandle unreflectConstructor(final Constructor<?> c) {
205         return unreflectConstructor(lookup, c);
206     }
207 
208     /**
209      * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflectConstructor(Constructor)},
210      * converting any encountered {@link IllegalAccessException} into an
211      * {@link IllegalAccessError}.
212      *
213      * @param lookup the lookup used to unreflect
214      * @param c the constructor to unreflect
215      * @return the unreflected constructor handle.
216      * @throws IllegalAccessError if the constructor is inaccessible.
217      */
unreflectConstructor(final MethodHandles.Lookup lookup, final Constructor<?> c)218     public static MethodHandle unreflectConstructor(final MethodHandles.Lookup lookup, final Constructor<?> c) {
219         try {
220             return lookup.unreflectConstructor(c);
221         } catch(final IllegalAccessException e) {
222             final IllegalAccessError ee = new IllegalAccessError("Failed to unreflect constructor " + c);
223             ee.initCause(e);
224             throw ee;
225         }
226     }
227 
228     /**
229      * Performs a {@link java.lang.invoke.MethodHandles.Lookup#findSpecial(Class, String, MethodType, Class)}
230      * on the underlying lookup. Converts any encountered
231      * {@link IllegalAccessException} into an {@link IllegalAccessError} and
232      * {@link NoSuchMethodException} into a {@link NoSuchMethodError}.
233      *
234      * @param declaringClass class declaring the method
235      * @param name the name of the method
236      * @param type the type of the method
237      * @return a method handle for the method
238      * @throws IllegalAccessError if the method is inaccessible.
239      * @throws NoSuchMethodError if the method does not exist.
240      */
findSpecial(final Class<?> declaringClass, final String name, final MethodType type)241     public MethodHandle findSpecial(final Class<?> declaringClass, final String name, final MethodType type) {
242         try {
243             return lookup.findSpecial(declaringClass, name, type, declaringClass);
244         } catch(final IllegalAccessException e) {
245             final IllegalAccessError ee = new IllegalAccessError("Failed to access special method " + methodDescription(
246                     declaringClass, name, type));
247             ee.initCause(e);
248             throw ee;
249         } catch(final NoSuchMethodException e) {
250             final NoSuchMethodError ee = new NoSuchMethodError("Failed to find special method " + methodDescription(
251                     declaringClass, name, type));
252             ee.initCause(e);
253             throw ee;
254         }
255     }
256 
methodDescription(final Class<?> declaringClass, final String name, final MethodType type)257     private static String methodDescription(final Class<?> declaringClass, final String name, final MethodType type) {
258         return declaringClass.getName() + "#" + name + type;
259     }
260 
261     /**
262      * Performs a {@link java.lang.invoke.MethodHandles.Lookup#findStatic(Class, String, MethodType)}
263      * on the underlying lookup. Converts any encountered
264      * {@link IllegalAccessException} into an {@link IllegalAccessError} and
265      * {@link NoSuchMethodException} into a {@link NoSuchMethodError}.
266      *
267      * @param declaringClass class declaring the method
268      * @param name the name of the method
269      * @param type the type of the method
270      * @return a method handle for the method
271      * @throws IllegalAccessError if the method is inaccessible.
272      * @throws NoSuchMethodError if the method does not exist.
273      */
findStatic(final Class<?> declaringClass, final String name, final MethodType type)274     public MethodHandle findStatic(final Class<?> declaringClass, final String name, final MethodType type) {
275         try {
276             return lookup.findStatic(declaringClass, name, type);
277         } catch(final IllegalAccessException e) {
278             final IllegalAccessError ee = new IllegalAccessError("Failed to access static method " + methodDescription(
279                     declaringClass, name, type));
280             ee.initCause(e);
281             throw ee;
282         } catch(final NoSuchMethodException e) {
283             final NoSuchMethodError ee = new NoSuchMethodError("Failed to find static method " + methodDescription(
284                     declaringClass, name, type));
285             ee.initCause(e);
286             throw ee;
287         }
288     }
289 
290     /**
291      * Performs a {@link java.lang.invoke.MethodHandles.Lookup#findVirtual(Class, String, MethodType)}
292      * on the underlying lookup. Converts any encountered
293      * {@link IllegalAccessException} into an {@link IllegalAccessError} and
294      * {@link NoSuchMethodException} into a {@link NoSuchMethodError}.
295      *
296      * @param declaringClass class declaring the method
297      * @param name the name of the method
298      * @param type the type of the method
299      * @return a method handle for the method
300      * @throws IllegalAccessError if the method is inaccessible.
301      * @throws NoSuchMethodError if the method does not exist.
302      */
findVirtual(final Class<?> declaringClass, final String name, final MethodType type)303     public MethodHandle findVirtual(final Class<?> declaringClass, final String name, final MethodType type) {
304         try {
305             return lookup.findVirtual(declaringClass, name, type);
306         } catch(final IllegalAccessException e) {
307             final IllegalAccessError ee = new IllegalAccessError("Failed to access virtual method " + methodDescription(
308                     declaringClass, name, type));
309             ee.initCause(e);
310             throw ee;
311         } catch(final NoSuchMethodException e) {
312             final NoSuchMethodError ee = new NoSuchMethodError("Failed to find virtual method " + methodDescription(
313                     declaringClass, name, type));
314             ee.initCause(e);
315             throw ee;
316         }
317     }
318 
319     /**
320      * Given a lookup, finds using {@link #findSpecial(Class, String, MethodType)}
321      * a method on that lookup's class. Useful in classes' code for convenient
322      * linking to their own privates.
323      * @param lookup the lookup for the class
324      * @param name the name of the method
325      * @param rtype the return type of the method
326      * @param ptypes the parameter types of the method
327      * @return the method handle for the method
328      */
findOwnSpecial(final MethodHandles.Lookup lookup, final String name, final Class<?> rtype, final Class<?>... ptypes)329     public static MethodHandle findOwnSpecial(final MethodHandles.Lookup lookup, final String name, final Class<?> rtype, final Class<?>... ptypes) {
330         return new Lookup(lookup).findOwnSpecial(name, rtype, ptypes);
331     }
332 
333 
334     /**
335      * Finds using {@link #findSpecial(Class, String, MethodType)} a method on
336      * that lookup's class. Useful in classes' code for convenient linking to
337      * their own privates. It's also more convenient than {@code findSpecial}
338      * in that you can just list the parameter types, and don't have to specify
339      * lookup class.
340      * @param name the name of the method
341      * @param rtype the return type of the method
342      * @param ptypes the parameter types of the method
343      * @return the method handle for the method
344      */
findOwnSpecial(final String name, final Class<?> rtype, final Class<?>... ptypes)345     public MethodHandle findOwnSpecial(final String name, final Class<?> rtype, final Class<?>... ptypes) {
346         return findSpecial(lookup.lookupClass(), name, MethodType.methodType(rtype, ptypes));
347     }
348 
349     /**
350      * Given a lookup, finds using {@link #findStatic(Class, String, MethodType)}
351      * a method on that lookup's class. Useful in classes' code for convenient
352      * linking to their own privates. It's easier to use than {@code findStatic}
353      * in that you can just list the parameter types, and don't have to specify
354      * lookup class.
355      * @param lookup the lookup for the class
356      * @param name the name of the method
357      * @param rtype the return type of the method
358      * @param ptypes the parameter types of the method
359      * @return the method handle for the method
360      */
findOwnStatic(final MethodHandles.Lookup lookup, final String name, final Class<?> rtype, final Class<?>... ptypes)361     public static MethodHandle findOwnStatic(final MethodHandles.Lookup lookup, final String name, final Class<?> rtype, final Class<?>... ptypes) {
362         return new Lookup(lookup).findOwnStatic(name, rtype, ptypes);
363     }
364 
365     /**
366      * Finds using {@link #findStatic(Class, String, MethodType)} a method on
367      * that lookup's class. Useful in classes' code for convenient linking to
368      * their own privates. It's easier to use than {@code findStatic}
369      * in that you can just list the parameter types, and don't have to specify
370      * lookup class.
371      * @param name the name of the method
372      * @param rtype the return type of the method
373      * @param ptypes the parameter types of the method
374      * @return the method handle for the method
375      */
findOwnStatic(final String name, final Class<?> rtype, final Class<?>... ptypes)376     public MethodHandle findOwnStatic(final String name, final Class<?> rtype, final Class<?>... ptypes) {
377         return findStatic(lookup.lookupClass(), name, MethodType.methodType(rtype, ptypes));
378     }
379 }
380