001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 *
017 */
018package org.apache.bcel.generic;
019
020import org.apache.bcel.Const;
021import org.apache.bcel.Repository;
022import org.apache.bcel.classfile.JavaClass;
023
024/**
025 * Super class for object and array types.
026 *
027 * @version $Id: ReferenceType.java 1749603 2016-06-21 20:50:19Z ggregory $
028 */
029public abstract class ReferenceType extends Type {
030
031    protected ReferenceType(final byte t, final String s) {
032        super(t, s);
033    }
034
035
036    /** Class is non-abstract but not instantiable from the outside
037     */
038    ReferenceType() {
039        super(Const.T_OBJECT, "<null object>");
040    }
041
042
043    /**
044     * Return true iff this type is castable to another type t as defined in
045     * the JVM specification.  The case where this is Type.NULL is not
046     * defined (see the CHECKCAST definition in the JVM specification).
047     * However, because e.g. CHECKCAST doesn't throw a
048     * ClassCastException when casting a null reference to any Object,
049     * true is returned in this case.
050     *
051     * @throws ClassNotFoundException if any classes or interfaces required
052     *  to determine assignment compatibility can't be found
053     */
054    public boolean isCastableTo( final Type t ) throws ClassNotFoundException {
055        if (this.equals(Type.NULL)) {
056            return t instanceof ReferenceType; // If this is ever changed in isAssignmentCompatible()
057        }
058        return isAssignmentCompatibleWith(t);
059        /* Yes, it's true: It's the same definition.
060         * See vmspec2 AASTORE / CHECKCAST definitions.
061         */
062    }
063
064
065    /**
066     * Return true iff this is assignment compatible with another type t
067     * as defined in the JVM specification; see the AASTORE definition
068     * there.
069     * @throws ClassNotFoundException if any classes or interfaces required
070     *  to determine assignment compatibility can't be found
071     */
072    public boolean isAssignmentCompatibleWith( final Type t ) throws ClassNotFoundException {
073        if (!(t instanceof ReferenceType)) {
074            return false;
075        }
076        final ReferenceType T = (ReferenceType) t;
077        if (this.equals(Type.NULL)) {
078            return true; // This is not explicitely stated, but clear. Isn't it?
079        }
080        /* If this is a class type then
081         */
082        if ((this instanceof ObjectType) && (((ObjectType) this).referencesClassExact())) {
083            /* If T is a class type, then this must be the same class as T,
084             or this must be a subclass of T;
085             */
086            if ((T instanceof ObjectType) && (((ObjectType) T).referencesClassExact())) {
087                if (this.equals(T)) {
088                    return true;
089                }
090                if (Repository.instanceOf(((ObjectType) this).getClassName(), ((ObjectType) T)
091                        .getClassName())) {
092                    return true;
093                }
094            }
095            /* If T is an interface type, this must implement interface T.
096             */
097            if ((T instanceof ObjectType) && (((ObjectType) T).referencesInterfaceExact())) {
098                if (Repository.implementationOf(((ObjectType) this).getClassName(),
099                        ((ObjectType) T).getClassName())) {
100                    return true;
101                }
102            }
103        }
104        /* If this is an interface type, then:
105         */
106        if ((this instanceof ObjectType) && (((ObjectType) this).referencesInterfaceExact())) {
107            /* If T is a class type, then T must be Object (�2.4.7).
108             */
109            if ((T instanceof ObjectType) && (((ObjectType) T).referencesClassExact())) {
110                if (T.equals(Type.OBJECT)) {
111                    return true;
112                }
113            }
114            /* If T is an interface type, then T must be the same interface
115             * as this or a superinterface of this (�2.13.2).
116             */
117            if ((T instanceof ObjectType) && (((ObjectType) T).referencesInterfaceExact())) {
118                if (this.equals(T)) {
119                    return true;
120                }
121                if (Repository.implementationOf(((ObjectType) this).getClassName(),
122                        ((ObjectType) T).getClassName())) {
123                    return true;
124                }
125            }
126        }
127        /* If this is an array type, namely, the type SC[], that is, an
128         * array of components of type SC, then:
129         */
130        if (this instanceof ArrayType) {
131            /* If T is a class type, then T must be Object (�2.4.7).
132             */
133            if ((T instanceof ObjectType) && (((ObjectType) T).referencesClassExact())) {
134                if (T.equals(Type.OBJECT)) {
135                    return true;
136                }
137            }
138            /* If T is an array type TC[], that is, an array of components
139             * of type TC, then one of the following must be true:
140             */
141            if (T instanceof ArrayType) {
142                /* TC and SC are the same primitive type (�2.4.1).
143                 */
144                final Type sc = ((ArrayType) this).getElementType();
145                final Type tc = ((ArrayType) T).getElementType();
146                if (sc instanceof BasicType && tc instanceof BasicType && sc.equals(tc)) {
147                    return true;
148                }
149                /* TC and SC are reference types (�2.4.6), and type SC is
150                 * assignable to TC by these runtime rules.
151                 */
152                if (tc instanceof ReferenceType && sc instanceof ReferenceType
153                        && ((ReferenceType) sc).isAssignmentCompatibleWith(tc)) {
154                    return true;
155                }
156            }
157            /* If T is an interface type, T must be one of the interfaces implemented by arrays (�2.15). */
158            // TODO: Check if this is still valid or find a way to dynamically find out which
159            // interfaces arrays implement. However, as of the JVM specification edition 2, there
160            // are at least two different pages where assignment compatibility is defined and
161            // on one of them "interfaces implemented by arrays" is exchanged with "'Cloneable' or
162            // 'java.io.Serializable'"
163            if ((T instanceof ObjectType) && (((ObjectType) T).referencesInterfaceExact())) {
164                for (final String element : Const.getInterfacesImplementedByArrays()) {
165                    if (T.equals(ObjectType.getInstance(element))) {
166                        return true;
167                    }
168                }
169            }
170        }
171        return false; // default.
172    }
173
174
175    /**
176     * This commutative operation returns the first common superclass (narrowest ReferenceType
177     * referencing a class, not an interface).
178     * If one of the types is a superclass of the other, the former is returned.
179     * If "this" is Type.NULL, then t is returned.
180     * If t is Type.NULL, then "this" is returned.
181     * If "this" equals t ['this.equals(t)'] "this" is returned.
182     * If "this" or t is an ArrayType, then Type.OBJECT is returned;
183     * unless their dimensions match. Then an ArrayType of the same
184     * number of dimensions is returned, with its basic type being the
185     * first common super class of the basic types of "this" and t.
186     * If "this" or t is a ReferenceType referencing an interface, then Type.OBJECT is returned.
187     * If not all of the two classes' superclasses cannot be found, "null" is returned.
188     * See the JVM specification edition 2, "�4.9.2 The Bytecode Verifier".
189     *
190     * @throws ClassNotFoundException on failure to find superclasses of this
191     *  type, or the type passed as a parameter
192     */
193    public ReferenceType getFirstCommonSuperclass( final ReferenceType t ) throws ClassNotFoundException {
194        if (this.equals(Type.NULL)) {
195            return t;
196        }
197        if (t.equals(Type.NULL)) {
198            return this;
199        }
200        if (this.equals(t)) {
201            return this;
202            /*
203             * TODO: Above sounds a little arbitrary. On the other hand, there is
204             * no object referenced by Type.NULL so we can also say all the objects
205             * referenced by Type.NULL were derived from java.lang.Object.
206             * However, the Java Language's "instanceof" operator proves us wrong:
207             * "null" is not referring to an instance of java.lang.Object :)
208             */
209        }
210        /* This code is from a bug report by Konstantin Shagin <konst@cs.technion.ac.il> */
211        if ((this instanceof ArrayType) && (t instanceof ArrayType)) {
212            final ArrayType arrType1 = (ArrayType) this;
213            final ArrayType arrType2 = (ArrayType) t;
214            if ((arrType1.getDimensions() == arrType2.getDimensions())
215                    && arrType1.getBasicType() instanceof ObjectType
216                    && arrType2.getBasicType() instanceof ObjectType) {
217                return new ArrayType(((ObjectType) arrType1.getBasicType())
218                        .getFirstCommonSuperclass((ObjectType) arrType2.getBasicType()), arrType1
219                        .getDimensions());
220            }
221        }
222        if ((this instanceof ArrayType) || (t instanceof ArrayType)) {
223            return Type.OBJECT;
224            // TODO: Is there a proof of OBJECT being the direct ancestor of every ArrayType?
225        }
226        if (((this instanceof ObjectType) && ((ObjectType) this).referencesInterfaceExact())
227                || ((t instanceof ObjectType) && ((ObjectType) t).referencesInterfaceExact())) {
228            return Type.OBJECT;
229            // TODO: The above line is correct comparing to the vmspec2. But one could
230            // make class file verification a bit stronger here by using the notion of
231            // superinterfaces or even castability or assignment compatibility.
232        }
233        // this and t are ObjectTypes, see above.
234        final ObjectType thiz = (ObjectType) this;
235        final ObjectType other = (ObjectType) t;
236        final JavaClass[] thiz_sups = Repository.getSuperClasses(thiz.getClassName());
237        final JavaClass[] other_sups = Repository.getSuperClasses(other.getClassName());
238        if ((thiz_sups == null) || (other_sups == null)) {
239            return null;
240        }
241        // Waaahh...
242        final JavaClass[] this_sups = new JavaClass[thiz_sups.length + 1];
243        final JavaClass[] t_sups = new JavaClass[other_sups.length + 1];
244        System.arraycopy(thiz_sups, 0, this_sups, 1, thiz_sups.length);
245        System.arraycopy(other_sups, 0, t_sups, 1, other_sups.length);
246        this_sups[0] = Repository.lookupClass(thiz.getClassName());
247        t_sups[0] = Repository.lookupClass(other.getClassName());
248        for (final JavaClass t_sup : t_sups) {
249            for (final JavaClass this_sup : this_sups) {
250                if (this_sup.equals(t_sup)) {
251                    return ObjectType.getInstance(this_sup.getClassName());
252                }
253            }
254        }
255        // Huh? Did you ask for Type.OBJECT's superclass??
256        return null;
257    }
258
259    /**
260     * This commutative operation returns the first common superclass (narrowest ReferenceType
261     * referencing a class, not an interface).
262     * If one of the types is a superclass of the other, the former is returned.
263     * If "this" is Type.NULL, then t is returned.
264     * If t is Type.NULL, then "this" is returned.
265     * If "this" equals t ['this.equals(t)'] "this" is returned.
266     * If "this" or t is an ArrayType, then Type.OBJECT is returned.
267     * If "this" or t is a ReferenceType referencing an interface, then Type.OBJECT is returned.
268     * If not all of the two classes' superclasses cannot be found, "null" is returned.
269     * See the JVM specification edition 2, "�4.9.2 The Bytecode Verifier".
270     *
271     * @deprecated use getFirstCommonSuperclass(ReferenceType t) which has
272     *             slightly changed semantics.
273     * @throws ClassNotFoundException on failure to find superclasses of this
274     *  type, or the type passed as a parameter
275     */
276    @Deprecated
277    public ReferenceType firstCommonSuperclass( final ReferenceType t ) throws ClassNotFoundException {
278        if (this.equals(Type.NULL)) {
279            return t;
280        }
281        if (t.equals(Type.NULL)) {
282            return this;
283        }
284        if (this.equals(t)) {
285            return this;
286            /*
287             * TODO: Above sounds a little arbitrary. On the other hand, there is
288             * no object referenced by Type.NULL so we can also say all the objects
289             * referenced by Type.NULL were derived from java.lang.Object.
290             * However, the Java Language's "instanceof" operator proves us wrong:
291             * "null" is not referring to an instance of java.lang.Object :)
292             */
293        }
294        if ((this instanceof ArrayType) || (t instanceof ArrayType)) {
295            return Type.OBJECT;
296            // TODO: Is there a proof of OBJECT being the direct ancestor of every ArrayType?
297        }
298        if (((this instanceof ObjectType) && ((ObjectType) this).referencesInterface())
299                || ((t instanceof ObjectType) && ((ObjectType) t).referencesInterface())) {
300            return Type.OBJECT;
301            // TODO: The above line is correct comparing to the vmspec2. But one could
302            // make class file verification a bit stronger here by using the notion of
303            // superinterfaces or even castability or assignment compatibility.
304        }
305        // this and t are ObjectTypes, see above.
306        final ObjectType thiz = (ObjectType) this;
307        final ObjectType other = (ObjectType) t;
308        final JavaClass[] thiz_sups = Repository.getSuperClasses(thiz.getClassName());
309        final JavaClass[] other_sups = Repository.getSuperClasses(other.getClassName());
310        if ((thiz_sups == null) || (other_sups == null)) {
311            return null;
312        }
313        // Waaahh...
314        final JavaClass[] this_sups = new JavaClass[thiz_sups.length + 1];
315        final JavaClass[] t_sups = new JavaClass[other_sups.length + 1];
316        System.arraycopy(thiz_sups, 0, this_sups, 1, thiz_sups.length);
317        System.arraycopy(other_sups, 0, t_sups, 1, other_sups.length);
318        this_sups[0] = Repository.lookupClass(thiz.getClassName());
319        t_sups[0] = Repository.lookupClass(other.getClassName());
320        for (final JavaClass t_sup : t_sups) {
321            for (final JavaClass this_sup : this_sups) {
322                if (this_sup.equals(t_sup)) {
323                    return ObjectType.getInstance(this_sup.getClassName());
324                }
325            }
326        }
327        // Huh? Did you ask for Type.OBJECT's superclass??
328        return null;
329    }
330}