1/*
2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
3 */
4/*
5 * Licensed to the Apache Software Foundation (ASF) under one or more
6 * contributor license agreements.  See the NOTICE file distributed with
7 * this work for additional information regarding copyright ownership.
8 * The ASF licenses this file to You under the Apache License, Version 2.0
9 * (the "License"); you may not use this file except in compliance with
10 * the License.  You may obtain a copy of the License at
11 *
12 *      http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21package com.sun.org.apache.bcel.internal.generic;
22
23import com.sun.org.apache.bcel.internal.Const;
24import com.sun.org.apache.bcel.internal.Repository;
25import com.sun.org.apache.bcel.internal.classfile.JavaClass;
26
27/**
28 * Super class for object and array types.
29 *
30 * @version $Id: ReferenceType.java 1749603 2016-06-21 20:50:19Z ggregory $
31 */
32public abstract class ReferenceType extends Type {
33
34    protected ReferenceType(final byte t, final String s) {
35        super(t, s);
36    }
37
38
39    /** Class is non-abstract but not instantiable from the outside
40     */
41    ReferenceType() {
42        super(Const.T_OBJECT, "<null object>");
43    }
44
45
46    /**
47     * Return true iff this type is castable to another type t as defined in
48     * the JVM specification.  The case where this is Type.NULL is not
49     * defined (see the CHECKCAST definition in the JVM specification).
50     * However, because e.g. CHECKCAST doesn't throw a
51     * ClassCastException when casting a null reference to any Object,
52     * true is returned in this case.
53     *
54     * @throws ClassNotFoundException if any classes or interfaces required
55     *  to determine assignment compatibility can't be found
56     */
57    public boolean isCastableTo( final Type t ) throws ClassNotFoundException {
58        if (this.equals(Type.NULL)) {
59            return t instanceof ReferenceType; // If this is ever changed in isAssignmentCompatible()
60        }
61        return isAssignmentCompatibleWith(t);
62        /* Yes, it's true: It's the same definition.
63         * See vmspec2 AASTORE / CHECKCAST definitions.
64         */
65    }
66
67
68    /**
69     * Return true iff this is assignment compatible with another type t
70     * as defined in the JVM specification; see the AASTORE definition
71     * there.
72     * @throws ClassNotFoundException if any classes or interfaces required
73     *  to determine assignment compatibility can't be found
74     */
75    public boolean isAssignmentCompatibleWith( final Type t ) throws ClassNotFoundException {
76        if (!(t instanceof ReferenceType)) {
77            return false;
78        }
79        final ReferenceType T = (ReferenceType) t;
80        if (this.equals(Type.NULL)) {
81            return true; // This is not explicitely stated, but clear. Isn't it?
82        }
83        /* If this is a class type then
84         */
85        if ((this instanceof ObjectType) && (((ObjectType) this).referencesClassExact())) {
86            /* If T is a class type, then this must be the same class as T,
87             or this must be a subclass of T;
88             */
89            if ((T instanceof ObjectType) && (((ObjectType) T).referencesClassExact())) {
90                if (this.equals(T)) {
91                    return true;
92                }
93                if (Repository.instanceOf(((ObjectType) this).getClassName(), ((ObjectType) T)
94                        .getClassName())) {
95                    return true;
96                }
97            }
98            /* If T is an interface type, this must implement interface T.
99             */
100            if ((T instanceof ObjectType) && (((ObjectType) T).referencesInterfaceExact())) {
101                if (Repository.implementationOf(((ObjectType) this).getClassName(),
102                        ((ObjectType) T).getClassName())) {
103                    return true;
104                }
105            }
106        }
107        /* If this is an interface type, then:
108         */
109        if ((this instanceof ObjectType) && (((ObjectType) this).referencesInterfaceExact())) {
110            /* If T is a class type, then T must be Object (2.4.7).
111             */
112            if ((T instanceof ObjectType) && (((ObjectType) T).referencesClassExact())) {
113                if (T.equals(Type.OBJECT)) {
114                    return true;
115                }
116            }
117            /* If T is an interface type, then T must be the same interface
118             * as this or a superinterface of this (2.13.2).
119             */
120            if ((T instanceof ObjectType) && (((ObjectType) T).referencesInterfaceExact())) {
121                if (this.equals(T)) {
122                    return true;
123                }
124                if (Repository.implementationOf(((ObjectType) this).getClassName(),
125                        ((ObjectType) T).getClassName())) {
126                    return true;
127                }
128            }
129        }
130        /* If this is an array type, namely, the type SC[], that is, an
131         * array of components of type SC, then:
132         */
133        if (this instanceof ArrayType) {
134            /* If T is a class type, then T must be Object (2.4.7).
135             */
136            if ((T instanceof ObjectType) && (((ObjectType) T).referencesClassExact())) {
137                if (T.equals(Type.OBJECT)) {
138                    return true;
139                }
140            }
141            /* If T is an array type TC[], that is, an array of components
142             * of type TC, then one of the following must be true:
143             */
144            if (T instanceof ArrayType) {
145                /* TC and SC are the same primitive type (2.4.1).
146                 */
147                final Type sc = ((ArrayType) this).getElementType();
148                final Type tc = ((ArrayType) T).getElementType();
149                if (sc instanceof BasicType && tc instanceof BasicType && sc.equals(tc)) {
150                    return true;
151                }
152                /* TC and SC are reference types (2.4.6), and type SC is
153                 * assignable to TC by these runtime rules.
154                 */
155                if (tc instanceof ReferenceType && sc instanceof ReferenceType
156                        && ((ReferenceType) sc).isAssignmentCompatibleWith(tc)) {
157                    return true;
158                }
159            }
160            /* If T is an interface type, T must be one of the interfaces implemented by arrays (2.15). */
161            // TODO: Check if this is still valid or find a way to dynamically find out which
162            // interfaces arrays implement. However, as of the JVM specification edition 2, there
163            // are at least two different pages where assignment compatibility is defined and
164            // on one of them "interfaces implemented by arrays" is exchanged with "'Cloneable' or
165            // 'java.io.Serializable'"
166            if ((T instanceof ObjectType) && (((ObjectType) T).referencesInterfaceExact())) {
167                for (final String element : Const.getInterfacesImplementedByArrays()) {
168                    if (T.equals(ObjectType.getInstance(element))) {
169                        return true;
170                    }
171                }
172            }
173        }
174        return false; // default.
175    }
176
177
178    /**
179     * This commutative operation returns the first common superclass (narrowest ReferenceType
180     * referencing a class, not an interface).
181     * If one of the types is a superclass of the other, the former is returned.
182     * If "this" is Type.NULL, then t is returned.
183     * If t is Type.NULL, then "this" is returned.
184     * If "this" equals t ['this.equals(t)'] "this" is returned.
185     * If "this" or t is an ArrayType, then Type.OBJECT is returned;
186     * unless their dimensions match. Then an ArrayType of the same
187     * number of dimensions is returned, with its basic type being the
188     * first common super class of the basic types of "this" and t.
189     * If "this" or t is a ReferenceType referencing an interface, then Type.OBJECT is returned.
190     * If not all of the two classes' superclasses cannot be found, "null" is returned.
191     * See the JVM specification edition 2, "4.9.2 The Bytecode Verifier".
192     *
193     * @throws ClassNotFoundException on failure to find superclasses of this
194     *  type, or the type passed as a parameter
195     */
196    public ReferenceType getFirstCommonSuperclass( final ReferenceType t ) throws ClassNotFoundException {
197        if (this.equals(Type.NULL)) {
198            return t;
199        }
200        if (t.equals(Type.NULL)) {
201            return this;
202        }
203        if (this.equals(t)) {
204            return this;
205            /*
206             * TODO: Above sounds a little arbitrary. On the other hand, there is
207             * no object referenced by Type.NULL so we can also say all the objects
208             * referenced by Type.NULL were derived from java.lang.Object.
209             * However, the Java Language's "instanceof" operator proves us wrong:
210             * "null" is not referring to an instance of java.lang.Object :)
211             */
212        }
213        /* This code is from a bug report by Konstantin Shagin <konst@cs.technion.ac.il> */
214        if ((this instanceof ArrayType) && (t instanceof ArrayType)) {
215            final ArrayType arrType1 = (ArrayType) this;
216            final ArrayType arrType2 = (ArrayType) t;
217            if ((arrType1.getDimensions() == arrType2.getDimensions())
218                    && arrType1.getBasicType() instanceof ObjectType
219                    && arrType2.getBasicType() instanceof ObjectType) {
220                return new ArrayType(((ObjectType) arrType1.getBasicType())
221                        .getFirstCommonSuperclass((ObjectType) arrType2.getBasicType()), arrType1
222                        .getDimensions());
223            }
224        }
225        if ((this instanceof ArrayType) || (t instanceof ArrayType)) {
226            return Type.OBJECT;
227            // TODO: Is there a proof of OBJECT being the direct ancestor of every ArrayType?
228        }
229        if (((this instanceof ObjectType) && ((ObjectType) this).referencesInterfaceExact())
230                || ((t instanceof ObjectType) && ((ObjectType) t).referencesInterfaceExact())) {
231            return Type.OBJECT;
232            // TODO: The above line is correct comparing to the vmspec2. But one could
233            // make class file verification a bit stronger here by using the notion of
234            // superinterfaces or even castability or assignment compatibility.
235        }
236        // this and t are ObjectTypes, see above.
237        final ObjectType thiz = (ObjectType) this;
238        final ObjectType other = (ObjectType) t;
239        final JavaClass[] thiz_sups = Repository.getSuperClasses(thiz.getClassName());
240        final JavaClass[] other_sups = Repository.getSuperClasses(other.getClassName());
241        if ((thiz_sups == null) || (other_sups == null)) {
242            return null;
243        }
244        // Waaahh...
245        final JavaClass[] this_sups = new JavaClass[thiz_sups.length + 1];
246        final JavaClass[] t_sups = new JavaClass[other_sups.length + 1];
247        System.arraycopy(thiz_sups, 0, this_sups, 1, thiz_sups.length);
248        System.arraycopy(other_sups, 0, t_sups, 1, other_sups.length);
249        this_sups[0] = Repository.lookupClass(thiz.getClassName());
250        t_sups[0] = Repository.lookupClass(other.getClassName());
251        for (final JavaClass t_sup : t_sups) {
252            for (final JavaClass this_sup : this_sups) {
253                if (this_sup.equals(t_sup)) {
254                    return ObjectType.getInstance(this_sup.getClassName());
255                }
256            }
257        }
258        // Huh? Did you ask for Type.OBJECT's superclass??
259        return null;
260    }
261
262    /**
263     * This commutative operation returns the first common superclass (narrowest ReferenceType
264     * referencing a class, not an interface).
265     * If one of the types is a superclass of the other, the former is returned.
266     * If "this" is Type.NULL, then t is returned.
267     * If t is Type.NULL, then "this" is returned.
268     * If "this" equals t ['this.equals(t)'] "this" is returned.
269     * If "this" or t is an ArrayType, then Type.OBJECT is returned.
270     * If "this" or t is a ReferenceType referencing an interface, then Type.OBJECT is returned.
271     * If not all of the two classes' superclasses cannot be found, "null" is returned.
272     * See the JVM specification edition 2, "4.9.2 The Bytecode Verifier".
273     *
274     * @deprecated use getFirstCommonSuperclass(ReferenceType t) which has
275     *             slightly changed semantics.
276     * @throws ClassNotFoundException on failure to find superclasses of this
277     *  type, or the type passed as a parameter
278     */
279    @Deprecated
280    public ReferenceType firstCommonSuperclass( final ReferenceType t ) throws ClassNotFoundException {
281        if (this.equals(Type.NULL)) {
282            return t;
283        }
284        if (t.equals(Type.NULL)) {
285            return this;
286        }
287        if (this.equals(t)) {
288            return this;
289            /*
290             * TODO: Above sounds a little arbitrary. On the other hand, there is
291             * no object referenced by Type.NULL so we can also say all the objects
292             * referenced by Type.NULL were derived from java.lang.Object.
293             * However, the Java Language's "instanceof" operator proves us wrong:
294             * "null" is not referring to an instance of java.lang.Object :)
295             */
296        }
297        if ((this instanceof ArrayType) || (t instanceof ArrayType)) {
298            return Type.OBJECT;
299            // TODO: Is there a proof of OBJECT being the direct ancestor of every ArrayType?
300        }
301        if (((this instanceof ObjectType) && ((ObjectType) this).referencesInterface())
302                || ((t instanceof ObjectType) && ((ObjectType) t).referencesInterface())) {
303            return Type.OBJECT;
304            // TODO: The above line is correct comparing to the vmspec2. But one could
305            // make class file verification a bit stronger here by using the notion of
306            // superinterfaces or even castability or assignment compatibility.
307        }
308        // this and t are ObjectTypes, see above.
309        final ObjectType thiz = (ObjectType) this;
310        final ObjectType other = (ObjectType) t;
311        final JavaClass[] thiz_sups = Repository.getSuperClasses(thiz.getClassName());
312        final JavaClass[] other_sups = Repository.getSuperClasses(other.getClassName());
313        if ((thiz_sups == null) || (other_sups == null)) {
314            return null;
315        }
316        // Waaahh...
317        final JavaClass[] this_sups = new JavaClass[thiz_sups.length + 1];
318        final JavaClass[] t_sups = new JavaClass[other_sups.length + 1];
319        System.arraycopy(thiz_sups, 0, this_sups, 1, thiz_sups.length);
320        System.arraycopy(other_sups, 0, t_sups, 1, other_sups.length);
321        this_sups[0] = Repository.lookupClass(thiz.getClassName());
322        t_sups[0] = Repository.lookupClass(other.getClassName());
323        for (final JavaClass t_sup : t_sups) {
324            for (final JavaClass this_sup : this_sups) {
325                if (this_sup.equals(t_sup)) {
326                    return ObjectType.getInstance(this_sup.getClassName());
327                }
328            }
329        }
330        // Huh? Did you ask for Type.OBJECT's superclass??
331        return null;
332    }
333}
334