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