InternalTypeUtilities.java revision 1551:f3b883bec2d0
1166065Spjd/*
2166065Spjd * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
3166065Spjd * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4166065Spjd *
5166065Spjd * This code is free software; you can redistribute it and/or modify it
6166065Spjd * under the terms of the GNU General Public License version 2 only, as
7166065Spjd * published by the Free Software Foundation.  Oracle designates this
8166065Spjd * particular file as subject to the "Classpath" exception as provided
9210984Spjd * by Oracle in the LICENSE file that accompanied this code.
10166065Spjd *
11210984Spjd * This code is distributed in the hope that it will be useful, but WITHOUT
12166065Spjd * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13210984Spjd * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14210984Spjd * version 2 for more details (a copy is included in the LICENSE file that
15210984Spjd * accompanied this code).
16210984Spjd *
17210984Spjd * You should have received a copy of the GNU General Public License version
18210984Spjd * 2 along with this work; if not, write to the Free Software Foundation,
19210984Spjd * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20210984Spjd *
21210984Spjd * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22210984Spjd * or visit www.oracle.com if you need additional information or have any
23210984Spjd * questions.
24210984Spjd */
25210984Spjdpackage jdk.dynalink.internal;
26210984Spjd
27210984Spjdimport java.util.ArrayList;
28210984Spjdimport java.util.HashSet;
29210984Spjdimport java.util.Iterator;
30210984Spjdimport java.util.List;
31210984Spjdimport java.util.Set;
32210984Spjdimport jdk.dynalink.linker.support.TypeUtilities;
33
34/**
35 * Various static utility methods for testing type relationships; internal to Dynalink.
36 */
37public class InternalTypeUtilities {
38    private InternalTypeUtilities() {
39    }
40
41    /**
42     * Returns true if either of the types is assignable from the other.
43     * @param c1 one type
44     * @param c2 another type
45     * @return true if either c1 is assignable from c2 or c2 is assignable from c1.
46     */
47    public static boolean areAssignable(final Class<?> c1, final Class<?> c2) {
48        return c1.isAssignableFrom(c2) || c2.isAssignableFrom(c1);
49    }
50
51    /**
52     * Return true if it is safe to strongly reference a class from the referred
53     * class loader from a class associated with the referring class loader
54     * without risking a class loader memory leak. Generally, it is only safe
55     * to reference classes from the same or ancestor class loader. {@code null}
56     * indicates the system class loader; classes from it can always be
57     * directly referenced, and it can only directly reference classes from
58     * itself. This method can be used by language runtimes to ensure they are
59     * using weak references in their linkages when they need to link to methods
60     * in unrelated class loaders.
61     *
62     * @param referrerLoader the referrer class loader.
63     * @param referredLoader the referred class loader
64     * @return true if it is safe to strongly reference the class from referred
65     * in referred.
66     * @throws SecurityException if the caller does not have the
67     * {@code RuntimePermission("getClassLoader")} permission and the method
68     * needs to traverse the parent class loader chain.
69     */
70    public static boolean canReferenceDirectly(final ClassLoader referrerLoader, final ClassLoader referredLoader) {
71        if(referredLoader == null) {
72            // Can always refer directly to a system class
73            return true;
74        }
75        if(referrerLoader == null) {
76            // System classes can't refer directly to any non-system class
77            return false;
78        }
79        // Otherwise, can only refer directly to classes residing in same or
80        // parent class loader.
81
82        ClassLoader referrer = referrerLoader;
83        do {
84            if(referrer == referredLoader) {
85                return true;
86            }
87            referrer = referrer.getParent();
88        } while(referrer != null);
89        return false;
90    }
91
92    /**
93     * Given two types represented by c1 and c2, returns a type that is their
94     * most specific common supertype for purposes of lossless conversions.
95     *
96     * @param c1 one type
97     * @param c2 another type
98     * @return their most common superclass or superinterface for purposes of
99     * lossless conversions. If they have several unrelated superinterfaces as
100     * their most specific common type, or the types themselves are completely
101     * unrelated interfaces, {@link java.lang.Object} is returned.
102     */
103    public static Class<?> getCommonLosslessConversionType(final Class<?> c1, final Class<?> c2) {
104        if(c1 == c2) {
105            return c1;
106        } else if (c1 == void.class || c2 == void.class) {
107            return Object.class;
108        } else if(TypeUtilities.isConvertibleWithoutLoss(c2, c1)) {
109            return c1;
110        } else if(TypeUtilities.isConvertibleWithoutLoss(c1, c2)) {
111            return c2;
112        } else if(c1.isPrimitive() && c2.isPrimitive()) {
113            if((c1 == byte.class && c2 == char.class) || (c1 == char.class && c2 == byte.class)) {
114                // byte + char = int
115                return int.class;
116            } else if((c1 == short.class && c2 == char.class) || (c1 == char.class && c2 == short.class)) {
117                // short + char = int
118                return int.class;
119            } else if((c1 == int.class && c2 == float.class) || (c1 == float.class && c2 == int.class)) {
120                // int + float = double
121                return double.class;
122            }
123        }
124        // For all other cases. This will handle long + (float|double) = Number case as well as boolean + anything = Object case too.
125        return getMostSpecificCommonTypeUnequalNonprimitives(c1, c2);
126    }
127
128    private static Class<?> getMostSpecificCommonTypeUnequalNonprimitives(final Class<?> c1, final Class<?> c2) {
129        final Class<?> npc1 = c1.isPrimitive() ? TypeUtilities.getWrapperType(c1) : c1;
130        final Class<?> npc2 = c2.isPrimitive() ? TypeUtilities.getWrapperType(c2) : c2;
131        final Set<Class<?>> a1 = getAssignables(npc1, npc2);
132        final Set<Class<?>> a2 = getAssignables(npc2, npc1);
133        a1.retainAll(a2);
134        if(a1.isEmpty()) {
135            // Can happen when at least one of the arguments is an interface,
136            // as they don't have Object at the root of their hierarchy.
137            return Object.class;
138        }
139        // Gather maximally specific elements. Yes, there can be more than one
140        // thank to interfaces. I.e., if you call this method for String.class
141        // and Number.class, you'll have Comparable, Serializable, and Object
142        // as maximal elements.
143        final List<Class<?>> max = new ArrayList<>();
144        outer: for(final Class<?> clazz: a1) {
145            for(final Iterator<Class<?>> maxiter = max.iterator(); maxiter.hasNext();) {
146                final Class<?> maxClazz = maxiter.next();
147                if(TypeUtilities.isSubtype(maxClazz, clazz)) {
148                    // It can't be maximal, if there's already a more specific
149                    // maximal than it.
150                    continue outer;
151                }
152                if(TypeUtilities.isSubtype(clazz, maxClazz)) {
153                    // If it's more specific than a currently maximal element,
154                    // that currently maximal is no longer a maximal.
155                    maxiter.remove();
156                }
157            }
158            // If we get here, no current maximal is more specific than the
159            // current class, so it is considered maximal as well
160            max.add(clazz);
161        }
162        if(max.size() > 1) {
163            return Object.class;
164        }
165        return max.get(0);
166    }
167
168    private static Set<Class<?>> getAssignables(final Class<?> c1, final Class<?> c2) {
169        final Set<Class<?>> s = new HashSet<>();
170        collectAssignables(c1, c2, s);
171        return s;
172    }
173
174    private static void collectAssignables(final Class<?> c1, final Class<?> c2, final Set<Class<?>> s) {
175        if(c1.isAssignableFrom(c2)) {
176            s.add(c1);
177        }
178        final Class<?> sc = c1.getSuperclass();
179        if(sc != null) {
180            collectAssignables(sc, c2, s);
181        }
182        final Class<?>[] itf = c1.getInterfaces();
183        for(int i = 0; i < itf.length; ++i) {
184            collectAssignables(itf[i], c2, s);
185        }
186    }
187}
188