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 */
028public abstract class ReferenceType extends Type {
029
030    /**
031     * Class is non-abstract but not instantiable from the outside
032     */
033    ReferenceType() {
034        super(Const.T_OBJECT, "<null object>");
035    }
036
037    protected ReferenceType(final byte t, final String s) {
038        super(t, s);
039    }
040
041    /**
042     * This commutative operation returns the first common superclass (narrowest ReferenceType referencing a class, not an
043     * interface). If one of the types is a superclass of the other, the former is returned. If "this" is Type.NULL, then t
044     * is returned. If t is Type.NULL, then "this" is returned. If "this" equals t ['this.equals(t)'] "this" is returned. If
045     * "this" or t is an ArrayType, then Type.OBJECT is returned. If "this" or t is a ReferenceType referencing an
046     * interface, then Type.OBJECT is returned. If not all of the two classes' superclasses cannot be found, "null" is
047     * returned. See the JVM specification edition 2, "�4.9.2 The Bytecode Verifier".
048     *
049     * @deprecated use getFirstCommonSuperclass(ReferenceType t) which has slightly changed semantics.
050     * @throws ClassNotFoundException on failure to find superclasses of this type, or the type passed as a parameter
051     */
052    @Deprecated
053    public ReferenceType firstCommonSuperclass(final ReferenceType t) throws ClassNotFoundException {
054        if (this.equals(Type.NULL)) {
055            return t;
056        }
057        if (t.equals(Type.NULL) || this.equals(t)) {
058            return this;
059            /*
060             * TODO: Above sounds a little arbitrary. On the other hand, there is no object referenced by Type.NULL so we can also
061             * say all the objects referenced by Type.NULL were derived from java.lang.Object. However, the Java Language's
062             * "instanceof" operator proves us wrong: "null" is not referring to an instance of java.lang.Object :)
063             */
064        }
065        if (this instanceof ArrayType || t instanceof ArrayType) {
066            return Type.OBJECT;
067            // TODO: Is there a proof of OBJECT being the direct ancestor of every ArrayType?
068        }
069        if (this instanceof ObjectType && ((ObjectType) this).referencesInterface() || t instanceof ObjectType && ((ObjectType) t).referencesInterface()) {
070            return Type.OBJECT;
071            // TODO: The above line is correct comparing to the vmspec2. But one could
072            // make class file verification a bit stronger here by using the notion of
073            // superinterfaces or even castability or assignment compatibility.
074        }
075        // this and t are ObjectTypes, see above.
076        final ObjectType thiz = (ObjectType) this;
077        final ObjectType other = (ObjectType) t;
078        final JavaClass[] thiz_sups = Repository.getSuperClasses(thiz.getClassName());
079        final JavaClass[] other_sups = Repository.getSuperClasses(other.getClassName());
080        if (thiz_sups == null || other_sups == null) {
081            return null;
082        }
083        // Waaahh...
084        final JavaClass[] this_sups = new JavaClass[thiz_sups.length + 1];
085        final JavaClass[] t_sups = new JavaClass[other_sups.length + 1];
086        System.arraycopy(thiz_sups, 0, this_sups, 1, thiz_sups.length);
087        System.arraycopy(other_sups, 0, t_sups, 1, other_sups.length);
088        this_sups[0] = Repository.lookupClass(thiz.getClassName());
089        t_sups[0] = Repository.lookupClass(other.getClassName());
090        for (final JavaClass t_sup : t_sups) {
091            for (final JavaClass this_sup : this_sups) {
092                if (this_sup.equals(t_sup)) {
093                    return ObjectType.getInstance(this_sup.getClassName());
094                }
095            }
096        }
097        // Huh? Did you ask for Type.OBJECT's superclass??
098        return null;
099    }
100
101    /**
102     * This commutative operation returns the first common superclass (narrowest ReferenceType referencing a class, not an
103     * interface). If one of the types is a superclass of the other, the former is returned. If "this" is Type.NULL, then t
104     * is returned. If t is Type.NULL, then "this" is returned. If "this" equals t ['this.equals(t)'] "this" is returned. If
105     * "this" or t is an ArrayType, then Type.OBJECT is returned; unless their dimensions match. Then an ArrayType of the
106     * same number of dimensions is returned, with its basic type being the first common super class of the basic types of
107     * "this" and t. If "this" or t is a ReferenceType referencing an interface, then Type.OBJECT is returned. If not all of
108     * the two classes' superclasses cannot be found, "null" is returned. See the JVM specification edition 2, "�4.9.2 The
109     * Bytecode Verifier".
110     *
111     * @throws ClassNotFoundException on failure to find superclasses of this type, or the type passed as a parameter
112     */
113    public ReferenceType getFirstCommonSuperclass(final ReferenceType t) throws ClassNotFoundException {
114        if (this.equals(Type.NULL)) {
115            return t;
116        }
117        if (t.equals(Type.NULL) || this.equals(t)) {
118            return this;
119            /*
120             * TODO: Above sounds a little arbitrary. On the other hand, there is no object referenced by Type.NULL so we can also
121             * say all the objects referenced by Type.NULL were derived from java.lang.Object. However, the Java Language's
122             * "instanceof" operator proves us wrong: "null" is not referring to an instance of java.lang.Object :)
123             */
124        }
125        /* This code is from a bug report by Konstantin Shagin <konst@cs.technion.ac.il> */
126        if (this instanceof ArrayType && t instanceof ArrayType) {
127            final ArrayType arrType1 = (ArrayType) this;
128            final ArrayType arrType2 = (ArrayType) t;
129            if (arrType1.getDimensions() == arrType2.getDimensions() && arrType1.getBasicType() instanceof ObjectType
130                && arrType2.getBasicType() instanceof ObjectType) {
131                return new ArrayType(((ObjectType) arrType1.getBasicType()).getFirstCommonSuperclass((ObjectType) arrType2.getBasicType()),
132                    arrType1.getDimensions());
133            }
134        }
135        if (this instanceof ArrayType || t instanceof ArrayType) {
136            return Type.OBJECT;
137            // TODO: Is there a proof of OBJECT being the direct ancestor of every ArrayType?
138        }
139        if (this instanceof ObjectType && ((ObjectType) this).referencesInterfaceExact()
140            || t instanceof ObjectType && ((ObjectType) t).referencesInterfaceExact()) {
141            return Type.OBJECT;
142            // TODO: The above line is correct comparing to the vmspec2. But one could
143            // make class file verification a bit stronger here by using the notion of
144            // superinterfaces or even castability or assignment compatibility.
145        }
146        // this and t are ObjectTypes, see above.
147        final ObjectType thiz = (ObjectType) this;
148        final ObjectType other = (ObjectType) t;
149        final JavaClass[] thiz_sups = Repository.getSuperClasses(thiz.getClassName());
150        final JavaClass[] other_sups = Repository.getSuperClasses(other.getClassName());
151        if (thiz_sups == null || other_sups == null) {
152            return null;
153        }
154        // Waaahh...
155        final JavaClass[] this_sups = new JavaClass[thiz_sups.length + 1];
156        final JavaClass[] t_sups = new JavaClass[other_sups.length + 1];
157        System.arraycopy(thiz_sups, 0, this_sups, 1, thiz_sups.length);
158        System.arraycopy(other_sups, 0, t_sups, 1, other_sups.length);
159        this_sups[0] = Repository.lookupClass(thiz.getClassName());
160        t_sups[0] = Repository.lookupClass(other.getClassName());
161        for (final JavaClass t_sup : t_sups) {
162            for (final JavaClass this_sup : this_sups) {
163                if (this_sup.equals(t_sup)) {
164                    return ObjectType.getInstance(this_sup.getClassName());
165                }
166            }
167        }
168        // Huh? Did you ask for Type.OBJECT's superclass??
169        return null;
170    }
171
172    /**
173     * Return true iff this is assignment compatible with another type t as defined in the JVM specification; see the
174     * AASTORE definition there.
175     *
176     * @throws ClassNotFoundException if any classes or interfaces required to determine assignment compatibility can't be
177     *         found
178     */
179    public boolean isAssignmentCompatibleWith(final Type t) throws ClassNotFoundException {
180        if (!(t instanceof ReferenceType)) {
181            return false;
182        }
183        final ReferenceType T = (ReferenceType) t;
184        if (this.equals(Type.NULL)) {
185            return true; // This is not explicitely stated, but clear. Isn't it?
186        }
187        /*
188         * If this is a class type then
189         */
190        if (this instanceof ObjectType && ((ObjectType) this).referencesClassExact()) {
191            /*
192             * If T is a class type, then this must be the same class as T, or this must be a subclass of T;
193             */
194            if (T instanceof ObjectType && ((ObjectType) T).referencesClassExact()
195                && (this.equals(T) || Repository.instanceOf(((ObjectType) this).getClassName(), ((ObjectType) T).getClassName()))) {
196                return true;
197            }
198            /*
199             * If T is an interface type, this must implement interface T.
200             */
201            if (T instanceof ObjectType && ((ObjectType) T).referencesInterfaceExact()
202                && Repository.implementationOf(((ObjectType) this).getClassName(), ((ObjectType) T).getClassName())) {
203                return true;
204            }
205        }
206        /*
207         * If this is an interface type, then:
208         */
209        if (this instanceof ObjectType && ((ObjectType) this).referencesInterfaceExact()) {
210            /*
211             * If T is a class type, then T must be Object (�2.4.7).
212             */
213            if (T instanceof ObjectType && ((ObjectType) T).referencesClassExact() && T.equals(Type.OBJECT)) {
214                return true;
215            }
216            /*
217             * If T is an interface type, then T must be the same interface as this or a superinterface of this (�2.13.2).
218             */
219            if (T instanceof ObjectType && ((ObjectType) T).referencesInterfaceExact()
220                && (this.equals(T) || Repository.implementationOf(((ObjectType) this).getClassName(), ((ObjectType) T).getClassName()))) {
221                return true;
222            }
223        }
224        /*
225         * If this is an array type, namely, the type SC[], that is, an array of components of type SC, then:
226         */
227        if (this instanceof ArrayType) {
228            /*
229             * If T is a class type, then T must be Object (�2.4.7).
230             */
231            if (T instanceof ObjectType && ((ObjectType) T).referencesClassExact() && T.equals(Type.OBJECT)) {
232                return true;
233            }
234            /*
235             * If T is an array type TC[], that is, an array of components of type TC, then one of the following must be true:
236             */
237            if (T instanceof ArrayType) {
238                /*
239                 * TC and SC are the same primitive type (�2.4.1).
240                 */
241                final Type sc = ((ArrayType) this).getElementType();
242                final Type tc = ((ArrayType) T).getElementType();
243                if (sc instanceof BasicType && tc instanceof BasicType && sc.equals(tc)) {
244                    return true;
245                }
246                /*
247                 * TC and SC are reference types (�2.4.6), and type SC is assignable to TC by these runtime rules.
248                 */
249                if (tc instanceof ReferenceType && sc instanceof ReferenceType && ((ReferenceType) sc).isAssignmentCompatibleWith(tc)) {
250                    return true;
251                }
252            }
253            /* If T is an interface type, T must be one of the interfaces implemented by arrays (�2.15). */
254            // TODO: Check if this is still valid or find a way to dynamically find out which
255            // interfaces arrays implement. However, as of the JVM specification edition 2, there
256            // are at least two different pages where assignment compatibility is defined and
257            // on one of them "interfaces implemented by arrays" is exchanged with "'Cloneable' or
258            // 'java.io.Serializable'"
259            if (T instanceof ObjectType && ((ObjectType) T).referencesInterfaceExact()) {
260                for (final String element : Const.getInterfacesImplementedByArrays()) {
261                    if (T.equals(ObjectType.getInstance(element))) {
262                        return true;
263                    }
264                }
265            }
266        }
267        return false; // default.
268    }
269
270    /**
271     * Return true iff this type is castable to another type t as defined in the JVM specification. The case where this is
272     * Type.NULL is not defined (see the CHECKCAST definition in the JVM specification). However, because e.g. CHECKCAST
273     * doesn't throw a ClassCastException when casting a null reference to any Object, true is returned in this case.
274     *
275     * @throws ClassNotFoundException if any classes or interfaces required to determine assignment compatibility can't be
276     *         found
277     */
278    public boolean isCastableTo(final Type t) throws ClassNotFoundException {
279        if (this.equals(Type.NULL)) {
280            return t instanceof ReferenceType; // If this is ever changed in isAssignmentCompatible()
281        }
282        return isAssignmentCompatibleWith(t);
283        /*
284         * Yes, it's true: It's the same definition. See vmspec2 AASTORE / CHECKCAST definitions.
285         */
286    }
287}