AccessibleMembersLookup.java revision 1612:0da44ab8c417
1121986Sjhb/*
2121986Sjhb * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
3121986Sjhb * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4121986Sjhb *
5121986Sjhb * This code is free software; you can redistribute it and/or modify it
6121986Sjhb * under the terms of the GNU General Public License version 2 only, as
7121986Sjhb * published by the Free Software Foundation.  Oracle designates this
8121986Sjhb * particular file as subject to the "Classpath" exception as provided
9121986Sjhb * by Oracle in the LICENSE file that accompanied this code.
10121986Sjhb *
11121986Sjhb * This code is distributed in the hope that it will be useful, but WITHOUT
12121986Sjhb * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13121986Sjhb * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14121986Sjhb * version 2 for more details (a copy is included in the LICENSE file that
15121986Sjhb * accompanied this code).
16121986Sjhb *
17121986Sjhb * You should have received a copy of the GNU General Public License version
18121986Sjhb * 2 along with this work; if not, write to the Free Software Foundation,
19121986Sjhb * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20121986Sjhb *
21121986Sjhb * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22121986Sjhb * or visit www.oracle.com if you need additional information or have any
23121986Sjhb * questions.
24121986Sjhb */
25121986Sjhb
26121986Sjhb/*
27121986Sjhb * This file is available under and governed by the GNU General Public
28121986Sjhb * License version 2 only, as published by the Free Software Foundation.
29121986Sjhb * However, the following notice accompanied the original version of this
30121986Sjhb * file, and Oracle licenses the original version of this file under the BSD
31121986Sjhb * license:
32121986Sjhb */
33121986Sjhb/*
34121986Sjhb   Copyright 2009-2013 Attila Szegedi
35121986Sjhb
36121986Sjhb   Licensed under both the Apache License, Version 2.0 (the "Apache License")
37121986Sjhb   and the BSD License (the "BSD License"), with licensee being free to
38121986Sjhb   choose either of the two at their discretion.
39121986Sjhb
40121986Sjhb   You may not use this file except in compliance with either the Apache
41121986Sjhb   License or the BSD License.
42121986Sjhb
43121986Sjhb   If you choose to use this file in compliance with the Apache License, the
44121986Sjhb   following notice applies to you:
45121986Sjhb
46121986Sjhb       You may obtain a copy of the Apache License at
47121986Sjhb
48121986Sjhb           http://www.apache.org/licenses/LICENSE-2.0
49121986Sjhb
50121986Sjhb       Unless required by applicable law or agreed to in writing, software
51121986Sjhb       distributed under the License is distributed on an "AS IS" BASIS,
52121986Sjhb       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
53121986Sjhb       implied. See the License for the specific language governing
54121986Sjhb       permissions and limitations under the License.
55121986Sjhb
56121986Sjhb   If you choose to use this file in compliance with the BSD License, the
57121986Sjhb   following notice applies to you:
58122124Sjhb
59122124Sjhb       Redistribution and use in source and binary forms, with or without
60122124Sjhb       modification, are permitted provided that the following conditions are
61122124Sjhb       met:
62121986Sjhb       * Redistributions of source code must retain the above copyright
63121986Sjhb         notice, this list of conditions and the following disclaimer.
64121986Sjhb       * Redistributions in binary form must reproduce the above copyright
65121986Sjhb         notice, this list of conditions and the following disclaimer in the
66121986Sjhb         documentation and/or other materials provided with the distribution.
67121986Sjhb       * Neither the name of the copyright holder nor the names of
68121986Sjhb         contributors may be used to endorse or promote products derived from
69121986Sjhb         this software without specific prior written permission.
70121986Sjhb
71121986Sjhb       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
72121986Sjhb       IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
73121986Sjhb       TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
74121986Sjhb       PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
75121986Sjhb       BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
76121986Sjhb       CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
77121986Sjhb       SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
78121986Sjhb       BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
79121986Sjhb       WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
80121986Sjhb       OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
81121986Sjhb       ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
82121986Sjhb*/
83121986Sjhb
84121986Sjhbpackage jdk.dynalink.beans;
85121986Sjhb
86121986Sjhbimport java.lang.reflect.Method;
87121986Sjhbimport java.lang.reflect.Modifier;
88121986Sjhbimport java.util.Arrays;
89121986Sjhbimport java.util.Collection;
90121986Sjhbimport java.util.HashMap;
91121986Sjhbimport java.util.LinkedHashSet;
92121986Sjhbimport java.util.Map;
93121986Sjhbimport java.util.Set;
94121986Sjhb
95121986Sjhb/**
96122124Sjhb * Utility class for discovering accessible methods and inner classes. Normally, a public member declared on a class is
97122124Sjhb * accessible (that is, it can be invoked from anywhere). However, this is not the case if the class itself is not
98122124Sjhb * public, or belongs to a restricted-access package. In that case, it is required to lookup a member in a publicly
99122124Sjhb * accessible superclass or implemented interface of the class, and use it instead of the member discovered on the
100122124Sjhb * class.
101121986Sjhb */
102130980Sjhbclass AccessibleMembersLookup {
103121986Sjhb    private final Map<MethodSignature, Method> methods;
104121986Sjhb    private final Set<Class<?>> innerClasses;
105121986Sjhb    private final boolean instance;
106121986Sjhb
107121986Sjhb    /**
108121986Sjhb     * Creates a mapping for all accessible methods and inner classes on a class.
109121986Sjhb     *
110121986Sjhb     * @param clazz the inspected class
111121986Sjhb     * @param instance true to inspect instance methods, false to inspect static methods.
112121986Sjhb     */
113121986Sjhb    AccessibleMembersLookup(final Class<?> clazz, final boolean instance) {
114121986Sjhb        this.methods = new HashMap<>();
115121986Sjhb        this.innerClasses = new LinkedHashSet<>();
116121986Sjhb        this.instance = instance;
117121986Sjhb        lookupAccessibleMembers(clazz);
118130980Sjhb    }
119130980Sjhb
120121986Sjhb    /**
121133017Sscottl     * Returns an accessible method equivalent of a method.
122121986Sjhb     *
123121986Sjhb     * @param m the method whose accessible equivalent is requested.
124121986Sjhb     * @return the accessible equivalent for the method (can be the same as the passed in method), or null if there is
125121986Sjhb     * no accessible method equivalent.
126128931Sjhb     */
127128931Sjhb    Method getAccessibleMethod(final Method m) {
128121986Sjhb        return m == null ? null : methods.get(new MethodSignature(m));
129121986Sjhb    }
130121986Sjhb
131129964Sjhb    Collection<Method> getMethods() {
132121986Sjhb        return methods.values();
133121986Sjhb    }
134129097Sjhb
135121986Sjhb    Class<?>[] getInnerClasses() {
136121986Sjhb        return innerClasses.toArray(new Class<?>[0]);
137121986Sjhb    }
138128931Sjhb
139128931Sjhb    /**
140121986Sjhb     * A helper class that represents a method signature - name and argument types.
141129964Sjhb     */
142129097Sjhb    static final class MethodSignature {
143129097Sjhb        private final String name;
144129097Sjhb        private final Class<?>[] args;
145129097Sjhb
146129097Sjhb        /**
147129097Sjhb         * Creates a new method signature from arbitrary data.
148129097Sjhb         *
149121986Sjhb         * @param name the name of the method this signature represents.
150133017Sscottl         * @param args the argument types of the method.
151133017Sscottl         */
152133017Sscottl        MethodSignature(final String name, final Class<?>[] args) {
153133017Sscottl            this.name = name;
154133017Sscottl            this.args = args;
155133017Sscottl        }
156121986Sjhb
157121986Sjhb        /**
158121986Sjhb         * Creates a signature for the given method.
159121986Sjhb         *
160121986Sjhb         * @param method the method for which a signature is created.
161121986Sjhb         */
162121986Sjhb        MethodSignature(final Method method) {
163121986Sjhb            this(method.getName(), method.getParameterTypes());
164121986Sjhb        }
165121986Sjhb
166121986Sjhb        /**
167121986Sjhb         * Compares this object to another object
168121986Sjhb         *
169121986Sjhb         * @param o the other object
170121986Sjhb         * @return true if the other object is also a method signature with the same name, same number of arguments, and
171121986Sjhb         * same types of arguments.
172121986Sjhb         */
173121986Sjhb        @Override
174130980Sjhb        public boolean equals(final Object o) {
175130980Sjhb            if(o instanceof MethodSignature) {
176130980Sjhb                final MethodSignature ms = (MethodSignature)o;
177130980Sjhb                return ms.name.equals(name) && Arrays.equals(args, ms.args);
178130980Sjhb            }
179130980Sjhb            return false;
180130980Sjhb        }
181130980Sjhb
182130980Sjhb        /**
183130980Sjhb         * Returns a hash code, consistent with the overridden {@link #equals(Object)}.
184130980Sjhb         */
185130980Sjhb        @Override
186130980Sjhb        public int hashCode() {
187130980Sjhb            return name.hashCode() ^ Arrays.hashCode(args);
188130980Sjhb        }
189130980Sjhb
190121986Sjhb        @Override
191130980Sjhb        public String toString() {
192130980Sjhb            final StringBuilder b = new StringBuilder();
193130980Sjhb            b.append("[MethodSignature ").append(name).append('(');
194130980Sjhb            if(args.length > 0) {
195130980Sjhb                b.append(args[0].getCanonicalName());
196130980Sjhb                for(int i = 1; i < args.length; ++i) {
197130980Sjhb                    b.append(", ").append(args[i].getCanonicalName());
198130980Sjhb                }
199130980Sjhb            }
200130980Sjhb            return b.append(")]").toString();
201130980Sjhb        }
202130980Sjhb    }
203130980Sjhb
204130980Sjhb    private void lookupAccessibleMembers(final Class<?> clazz) {
205130980Sjhb        boolean searchSuperTypes;
206130980Sjhb
207130980Sjhb        if(!CheckRestrictedPackage.isRestrictedClass(clazz)) {
208130980Sjhb            searchSuperTypes = false;
209130980Sjhb            for(final Method method: clazz.getMethods()) {
210130980Sjhb                final boolean isStatic = Modifier.isStatic(method.getModifiers());
211130980Sjhb                if(instance != isStatic) {
212130980Sjhb                    final MethodSignature sig = new MethodSignature(method);
213130980Sjhb                    if(!methods.containsKey(sig)) {
214121986Sjhb                        final Class<?> declaringClass = method.getDeclaringClass();
215121986Sjhb                        if(declaringClass != clazz && CheckRestrictedPackage.isRestrictedClass(declaringClass)) {
216121986Sjhb                            //Sometimes, the declaring class of a method (Method.getDeclaringClass())
217121986Sjhb                            //retrieved through Class.getMethods() for a public class will be a
218121986Sjhb                            //non-public superclass. For such a method, we need to find a method with
219121986Sjhb                            //the same name and signature in a public superclass or implemented
220121986Sjhb                            //interface.
221121986Sjhb                            //This typically doesn't happen with classes emitted by a reasonably modern
222121986Sjhb                            //javac, as it'll create synthetic delegator methods in all public
223121986Sjhb                            //immediate subclasses of the non-public class. We have, however, observed
224121986Sjhb                            //this in the wild with class files compiled with older javac that doesn't
225121986Sjhb                            //generate the said synthetic delegators.
226121986Sjhb                            searchSuperTypes = true;
227121986Sjhb                        } else {
228121986Sjhb                            // don't allow inherited static
229121986Sjhb                            if (!isStatic || clazz == declaringClass) {
230121986Sjhb                                methods.put(sig, method);
231121986Sjhb                            }
232121986Sjhb                        }
233133017Sscottl                    }
234121986Sjhb                }
235121986Sjhb            }
236121986Sjhb            for(final Class<?> innerClass: clazz.getClasses()) {
237121986Sjhb                // Add both static and non-static classes, regardless of instance flag. StaticClassLinker will just
238121986Sjhb                // expose non-static classes with explicit constructor outer class argument.
239121986Sjhb                // NOTE: getting inner class objects through getClasses() does not resolve them, so if those classes
240121986Sjhb                // were not yet loaded, they'll only get loaded in a non-resolved state; no static initializers for
241121986Sjhb                // them will trigger just by doing this.
242121986Sjhb                innerClasses.add(innerClass);
243121986Sjhb            }
244121986Sjhb        } else {
245121986Sjhb            searchSuperTypes = true;
246121986Sjhb        }
247121986Sjhb
248133017Sscottl        // don't need to search super types for static methods
249133017Sscottl        if(instance && searchSuperTypes) {
250133017Sscottl            // If we reach here, the class is either not public, or it is in a restricted package. Alternatively, it is
251133017Sscottl            // public, but some of its methods claim that their declaring class is non-public. We'll try superclasses
252121986Sjhb            // and implemented interfaces then looking for public ones.
253121986Sjhb            final Class<?>[] interfaces = clazz.getInterfaces();
254121986Sjhb            for(int i = 0; i < interfaces.length; i++) {
255121986Sjhb                lookupAccessibleMembers(interfaces[i]);
256121986Sjhb            }
257121986Sjhb            final Class<?> superclass = clazz.getSuperclass();
258122148Sjhb            if(superclass != null) {
259133017Sscottl                lookupAccessibleMembers(superclass);
260121986Sjhb            }
261121986Sjhb        }
262121986Sjhb    }
263129964Sjhb}
264129964Sjhb