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