Java Code Examples for com.android.dx.rop.type.Type#isArray()

The following examples show how to use com.android.dx.rop.type.Type#isArray() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: Simulator.java    From Box with Apache License 2.0 4 votes vote down vote up
/**
 * Returns the required array type for an array load or store
 * instruction, based on a given implied type and an observed
 * actual array type.
 *
 * <p>The interesting cases here have to do with object arrays,
 * <code>byte[]</code>s, <code>boolean[]</code>s, and
 * known-nulls.</p>
 *
 * <p>In the case of arrays of objects, we want to narrow the type
 * to the actual array present on the stack, as long as what is
 * present is an object type. Similarly, due to a quirk of the
 * original bytecode representation, the instructions for dealing
 * with <code>byte[]</code> and <code>boolean[]</code> are
 * undifferentiated, and we aim here to return whichever one was
 * actually present on the stack.</p>
 *
 * <p>In the case where there is a known-null on the stack where
 * an array is expected, our behavior depends on the implied type
 * of the instruction. When the implied type is a reference, we
 * don't attempt to infer anything, as we don't know the dimension
 * of the null constant and thus any explicit inferred type could
 * be wrong. When the implied type is a primitive, we fall back to
 * the implied type of the instruction. Due to the quirk described
 * above, this means that source code that uses
 * <code>boolean[]</code> might get translated surprisingly -- but
 * correctly -- into an instruction that specifies a
 * <code>byte[]</code>. It will be correct, because should the
 * code actually execute, it will necessarily throw a
 * <code>NullPointerException</code>, and it won't matter what
 * opcode variant is used to achieve that result.</p>
 *
 * @param impliedType {@code non-null;} type implied by the
 * instruction; is <i>not</i> an array type
 * @param foundArrayType {@code non-null;} type found on the
 * stack; is either an array type or a known-null
 * @return {@code non-null;} the array type that should be
 * required in this context
 */
private static Type requiredArrayTypeFor(Type impliedType,
        Type foundArrayType) {
    if (foundArrayType == Type.KNOWN_NULL) {
        return impliedType.isReference()
            ? Type.KNOWN_NULL
            : impliedType.getArrayType();
    }

    if ((impliedType == Type.OBJECT)
            && foundArrayType.isArray()
            && foundArrayType.getComponentType().isReference()) {
        return foundArrayType;
    }

    if ((impliedType == Type.BYTE)
            && (foundArrayType == Type.BOOLEAN_ARRAY)) {
        /*
         * Per above, an instruction with implied byte[] is also
         * allowed to be used on boolean[].
         */
        return Type.BOOLEAN_ARRAY;
    }

    return impliedType.getArrayType();
}
 
Example 2
Source File: Merger.java    From Box with Apache License 2.0 4 votes vote down vote up
/**
 * Merges two frame types.
 *
 * @param ft1 {@code non-null;} a frame type
 * @param ft2 {@code non-null;} another frame type
 * @return {@code non-null;} the result of merging the two types
 */
public static TypeBearer mergeType(TypeBearer ft1, TypeBearer ft2) {
    if ((ft1 == null) || ft1.equals(ft2)) {
        return ft1;
    } else if (ft2 == null) {
        return null;
    } else {
        Type type1 = ft1.getType();
        Type type2 = ft2.getType();

        if (type1 == type2) {
            return type1;
        } else if (type1.isReference() && type2.isReference()) {
            if (type1 == Type.KNOWN_NULL) {
                /*
                 * A known-null merges with any other reference type to
                 * be that reference type.
                 */
                return type2;
            } else if (type2 == Type.KNOWN_NULL) {
                /*
                 * The same as above, but this time it's type2 that's
                 * the known-null.
                 */
                return type1;
            } else if (type1.isArray() && type2.isArray()) {
                TypeBearer componentUnion =
                    mergeType(type1.getComponentType(),
                            type2.getComponentType());
                if (componentUnion == null) {
                    /*
                     * At least one of the types is a primitive type,
                     * so the merged result is just Object.
                     */
                    return Type.OBJECT;
                }
                return ((Type) componentUnion).getArrayType();
            } else {
                /*
                 * All other unequal reference types get merged to be
                 * Object in this phase. This is fine here, but it
                 * won't be the right thing to do in the verifier.
                 */
                return Type.OBJECT;
            }
        } else if (type1.isIntlike() && type2.isIntlike()) {
            /*
             * Merging two non-identical int-like types results in
             * the type int.
             */
            return Type.INT;
        } else {
            return null;
        }
    }
}
 
Example 3
Source File: Merger.java    From Box with Apache License 2.0 4 votes vote down vote up
/**
 * Returns whether the given supertype is possibly assignable from
 * the given subtype. This takes into account primitiveness,
 * int-likeness, known-nullness, and array dimensions, but does
 * not assume anything about class hierarchy other than that the
 * type {@code Object} is the supertype of all reference
 * types and all arrays are assignable to
 * {@code Serializable} and {@code Cloneable}.
 *
 * @param supertypeBearer {@code non-null;} the supertype
 * @param subtypeBearer {@code non-null;} the subtype
 */
public static boolean isPossiblyAssignableFrom(TypeBearer supertypeBearer,
        TypeBearer subtypeBearer) {
    Type supertype = supertypeBearer.getType();
    Type subtype = subtypeBearer.getType();

    if (supertype.equals(subtype)) {
        // Easy out.
        return true;
    }

    int superBt = supertype.getBasicType();
    int subBt = subtype.getBasicType();

    // Treat return types as Object for the purposes of this method.

    if (superBt == Type.BT_ADDR) {
        supertype = Type.OBJECT;
        superBt = Type.BT_OBJECT;
    }

    if (subBt == Type.BT_ADDR) {
        subtype = Type.OBJECT;
        subBt = Type.BT_OBJECT;
    }

    if ((superBt != Type.BT_OBJECT) || (subBt != Type.BT_OBJECT)) {
        /*
         * No two distinct primitive types are assignable in this sense,
         * unless they are both int-like.
         */
        return supertype.isIntlike() && subtype.isIntlike();
    }

    // At this point, we know both types are reference types.

    if (supertype == Type.KNOWN_NULL) {
        /*
         * A known-null supertype is only assignable from another
         * known-null (handled in the easy out at the top of the
         * method).
         */
        return false;
    } else if (subtype == Type.KNOWN_NULL) {
        /*
         * A known-null subtype is in fact assignable to any
         * reference type.
         */
        return true;
    } else if (supertype == Type.OBJECT) {
        /*
         * Object is assignable from any reference type.
         */
        return true;
    } else if (supertype.isArray()) {
        // The supertype is an array type.
        if (! subtype.isArray()) {
            // The subtype isn't an array, and so can't be assignable.
            return false;
        }

        /*
         * Strip off as many matched component types from both
         * types as possible, and check the assignability of the
         * results.
         */
        do {
            supertype = supertype.getComponentType();
            subtype = subtype.getComponentType();
        } while (supertype.isArray() && subtype.isArray());

        return isPossiblyAssignableFrom(supertype, subtype);
    } else if (subtype.isArray()) {
        /*
         * Other than Object (handled above), array types are
         * assignable only to Serializable and Cloneable.
         */
        return (supertype == Type.SERIALIZABLE) ||
            (supertype == Type.CLONEABLE);
    } else {
        /*
         * All other unequal reference types are considered at
         * least possibly assignable.
         */
        return true;
    }
}
 
Example 4
Source File: Simulator.java    From Box with Apache License 2.0 4 votes vote down vote up
/**
 * Returns the required array type for an array load or store
 * instruction, based on a given implied type and an observed
 * actual array type.
 *
 * <p>The interesting cases here have to do with object arrays,
 * <code>byte[]</code>s, <code>boolean[]</code>s, and
 * known-nulls.</p>
 *
 * <p>In the case of arrays of objects, we want to narrow the type
 * to the actual array present on the stack, as long as what is
 * present is an object type. Similarly, due to a quirk of the
 * original bytecode representation, the instructions for dealing
 * with <code>byte[]</code> and <code>boolean[]</code> are
 * undifferentiated, and we aim here to return whichever one was
 * actually present on the stack.</p>
 *
 * <p>In the case where there is a known-null on the stack where
 * an array is expected, our behavior depends on the implied type
 * of the instruction. When the implied type is a reference, we
 * don't attempt to infer anything, as we don't know the dimension
 * of the null constant and thus any explicit inferred type could
 * be wrong. When the implied type is a primitive, we fall back to
 * the implied type of the instruction. Due to the quirk described
 * above, this means that source code that uses
 * <code>boolean[]</code> might get translated surprisingly -- but
 * correctly -- into an instruction that specifies a
 * <code>byte[]</code>. It will be correct, because should the
 * code actually execute, it will necessarily throw a
 * <code>NullPointerException</code>, and it won't matter what
 * opcode variant is used to achieve that result.</p>
 *
 * @param impliedType {@code non-null;} type implied by the
 * instruction; is <i>not</i> an array type
 * @param foundArrayType {@code non-null;} type found on the
 * stack; is either an array type or a known-null
 * @return {@code non-null;} the array type that should be
 * required in this context
 */
private static Type requiredArrayTypeFor(Type impliedType,
        Type foundArrayType) {
    if (foundArrayType == Type.KNOWN_NULL) {
        return impliedType.isReference()
            ? Type.KNOWN_NULL
            : impliedType.getArrayType();
    }

    if ((impliedType == Type.OBJECT)
            && foundArrayType.isArray()
            && foundArrayType.getComponentType().isReference()) {
        return foundArrayType;
    }

    if ((impliedType == Type.BYTE)
            && (foundArrayType == Type.BOOLEAN_ARRAY)) {
        /*
         * Per above, an instruction with implied byte[] is also
         * allowed to be used on boolean[].
         */
        return Type.BOOLEAN_ARRAY;
    }

    return impliedType.getArrayType();
}
 
Example 5
Source File: Merger.java    From Box with Apache License 2.0 4 votes vote down vote up
/**
 * Merges two frame types.
 *
 * @param ft1 {@code non-null;} a frame type
 * @param ft2 {@code non-null;} another frame type
 * @return {@code non-null;} the result of merging the two types
 */
public static TypeBearer mergeType(TypeBearer ft1, TypeBearer ft2) {
    if ((ft1 == null) || ft1.equals(ft2)) {
        return ft1;
    } else if (ft2 == null) {
        return null;
    } else {
        Type type1 = ft1.getType();
        Type type2 = ft2.getType();

        if (type1 == type2) {
            return type1;
        } else if (type1.isReference() && type2.isReference()) {
            if (type1 == Type.KNOWN_NULL) {
                /*
                 * A known-null merges with any other reference type to
                 * be that reference type.
                 */
                return type2;
            } else if (type2 == Type.KNOWN_NULL) {
                /*
                 * The same as above, but this time it's type2 that's
                 * the known-null.
                 */
                return type1;
            } else if (type1.isArray() && type2.isArray()) {
                TypeBearer componentUnion =
                    mergeType(type1.getComponentType(),
                            type2.getComponentType());
                if (componentUnion == null) {
                    /*
                     * At least one of the types is a primitive type,
                     * so the merged result is just Object.
                     */
                    return Type.OBJECT;
                }
                return ((Type) componentUnion).getArrayType();
            } else {
                /*
                 * All other unequal reference types get merged to be
                 * Object in this phase. This is fine here, but it
                 * won't be the right thing to do in the verifier.
                 */
                return Type.OBJECT;
            }
        } else if (type1.isIntlike() && type2.isIntlike()) {
            /*
             * Merging two non-identical int-like types results in
             * the type int.
             */
            return Type.INT;
        } else {
            return null;
        }
    }
}
 
Example 6
Source File: Merger.java    From Box with Apache License 2.0 4 votes vote down vote up
/**
 * Returns whether the given supertype is possibly assignable from
 * the given subtype. This takes into account primitiveness,
 * int-likeness, known-nullness, and array dimensions, but does
 * not assume anything about class hierarchy other than that the
 * type {@code Object} is the supertype of all reference
 * types and all arrays are assignable to
 * {@code Serializable} and {@code Cloneable}.
 *
 * @param supertypeBearer {@code non-null;} the supertype
 * @param subtypeBearer {@code non-null;} the subtype
 */
public static boolean isPossiblyAssignableFrom(TypeBearer supertypeBearer,
        TypeBearer subtypeBearer) {
    Type supertype = supertypeBearer.getType();
    Type subtype = subtypeBearer.getType();

    if (supertype.equals(subtype)) {
        // Easy out.
        return true;
    }

    int superBt = supertype.getBasicType();
    int subBt = subtype.getBasicType();

    // Treat return types as Object for the purposes of this method.

    if (superBt == Type.BT_ADDR) {
        supertype = Type.OBJECT;
        superBt = Type.BT_OBJECT;
    }

    if (subBt == Type.BT_ADDR) {
        subtype = Type.OBJECT;
        subBt = Type.BT_OBJECT;
    }

    if ((superBt != Type.BT_OBJECT) || (subBt != Type.BT_OBJECT)) {
        /*
         * No two distinct primitive types are assignable in this sense,
         * unless they are both int-like.
         */
        return supertype.isIntlike() && subtype.isIntlike();
    }

    // At this point, we know both types are reference types.

    if (supertype == Type.KNOWN_NULL) {
        /*
         * A known-null supertype is only assignable from another
         * known-null (handled in the easy out at the top of the
         * method).
         */
        return false;
    } else if (subtype == Type.KNOWN_NULL) {
        /*
         * A known-null subtype is in fact assignable to any
         * reference type.
         */
        return true;
    } else if (supertype == Type.OBJECT) {
        /*
         * Object is assignable from any reference type.
         */
        return true;
    } else if (supertype.isArray()) {
        // The supertype is an array type.
        if (! subtype.isArray()) {
            // The subtype isn't an array, and so can't be assignable.
            return false;
        }

        /*
         * Strip off as many matched component types from both
         * types as possible, and check the assignability of the
         * results.
         */
        do {
            supertype = supertype.getComponentType();
            subtype = subtype.getComponentType();
        } while (supertype.isArray() && subtype.isArray());

        return isPossiblyAssignableFrom(supertype, subtype);
    } else if (subtype.isArray()) {
        /*
         * Other than Object (handled above), array types are
         * assignable only to Serializable and Cloneable.
         */
        return (supertype == Type.SERIALIZABLE) ||
            (supertype == Type.CLONEABLE);
    } else {
        /*
         * All other unequal reference types are considered at
         * least possibly assignable.
         */
        return true;
    }
}
 
Example 7
Source File: Simulator.java    From J2ME-Loader with Apache License 2.0 4 votes vote down vote up
/**
 * Returns the required array type for an array load or store
 * instruction, based on a given implied type and an observed
 * actual array type.
 *
 * <p>The interesting cases here have to do with object arrays,
 * <code>byte[]</code>s, <code>boolean[]</code>s, and
 * known-nulls.</p>
 *
 * <p>In the case of arrays of objects, we want to narrow the type
 * to the actual array present on the stack, as long as what is
 * present is an object type. Similarly, due to a quirk of the
 * original bytecode representation, the instructions for dealing
 * with <code>byte[]</code> and <code>boolean[]</code> are
 * undifferentiated, and we aim here to return whichever one was
 * actually present on the stack.</p>
 *
 * <p>In the case where there is a known-null on the stack where
 * an array is expected, our behavior depends on the implied type
 * of the instruction. When the implied type is a reference, we
 * don't attempt to infer anything, as we don't know the dimension
 * of the null constant and thus any explicit inferred type could
 * be wrong. When the implied type is a primitive, we fall back to
 * the implied type of the instruction. Due to the quirk described
 * above, this means that source code that uses
 * <code>boolean[]</code> might get translated surprisingly -- but
 * correctly -- into an instruction that specifies a
 * <code>byte[]</code>. It will be correct, because should the
 * code actually execute, it will necessarily throw a
 * <code>NullPointerException</code>, and it won't matter what
 * opcode variant is used to achieve that result.</p>
 *
 * @param impliedType {@code non-null;} type implied by the
 * instruction; is <i>not</i> an array type
 * @param foundArrayType {@code non-null;} type found on the
 * stack; is either an array type or a known-null
 * @return {@code non-null;} the array type that should be
 * required in this context
 */
private static Type requiredArrayTypeFor(Type impliedType,
        Type foundArrayType) {
    if (foundArrayType == Type.KNOWN_NULL) {
        return impliedType.isReference()
            ? Type.KNOWN_NULL
            : impliedType.getArrayType();
    }

    if ((impliedType == Type.OBJECT)
            && foundArrayType.isArray()
            && foundArrayType.getComponentType().isReference()) {
        return foundArrayType;
    }

    if ((impliedType == Type.BYTE)
            && (foundArrayType == Type.BOOLEAN_ARRAY)) {
        /*
         * Per above, an instruction with implied byte[] is also
         * allowed to be used on boolean[].
         */
        return Type.BOOLEAN_ARRAY;
    }

    return impliedType.getArrayType();
}
 
Example 8
Source File: Merger.java    From J2ME-Loader with Apache License 2.0 4 votes vote down vote up
/**
 * Merges two frame types.
 *
 * @param ft1 {@code non-null;} a frame type
 * @param ft2 {@code non-null;} another frame type
 * @return {@code non-null;} the result of merging the two types
 */
public static TypeBearer mergeType(TypeBearer ft1, TypeBearer ft2) {
    if ((ft1 == null) || ft1.equals(ft2)) {
        return ft1;
    } else if (ft2 == null) {
        return null;
    } else {
        Type type1 = ft1.getType();
        Type type2 = ft2.getType();

        if (type1 == type2) {
            return type1;
        } else if (type1.isReference() && type2.isReference()) {
            if (type1 == Type.KNOWN_NULL) {
                /*
                 * A known-null merges with any other reference type to
                 * be that reference type.
                 */
                return type2;
            } else if (type2 == Type.KNOWN_NULL) {
                /*
                 * The same as above, but this time it's type2 that's
                 * the known-null.
                 */
                return type1;
            } else if (type1.isArray() && type2.isArray()) {
                TypeBearer componentUnion =
                    mergeType(type1.getComponentType(),
                            type2.getComponentType());
                if (componentUnion == null) {
                    /*
                     * At least one of the types is a primitive type,
                     * so the merged result is just Object.
                     */
                    return Type.OBJECT;
                }
                return ((Type) componentUnion).getArrayType();
            } else {
                /*
                 * All other unequal reference types get merged to be
                 * Object in this phase. This is fine here, but it
                 * won't be the right thing to do in the verifier.
                 */
                return Type.OBJECT;
            }
        } else if (type1.isIntlike() && type2.isIntlike()) {
            /*
             * Merging two non-identical int-like types results in
             * the type int.
             */
            return Type.INT;
        } else {
            return null;
        }
    }
}
 
Example 9
Source File: Merger.java    From J2ME-Loader with Apache License 2.0 4 votes vote down vote up
/**
 * Returns whether the given supertype is possibly assignable from
 * the given subtype. This takes into account primitiveness,
 * int-likeness, known-nullness, and array dimensions, but does
 * not assume anything about class hierarchy other than that the
 * type {@code Object} is the supertype of all reference
 * types and all arrays are assignable to
 * {@code Serializable} and {@code Cloneable}.
 *
 * @param supertypeBearer {@code non-null;} the supertype
 * @param subtypeBearer {@code non-null;} the subtype
 */
public static boolean isPossiblyAssignableFrom(TypeBearer supertypeBearer,
        TypeBearer subtypeBearer) {
    Type supertype = supertypeBearer.getType();
    Type subtype = subtypeBearer.getType();

    if (supertype.equals(subtype)) {
        // Easy out.
        return true;
    }

    int superBt = supertype.getBasicType();
    int subBt = subtype.getBasicType();

    // Treat return types as Object for the purposes of this method.

    if (superBt == Type.BT_ADDR) {
        supertype = Type.OBJECT;
        superBt = Type.BT_OBJECT;
    }

    if (subBt == Type.BT_ADDR) {
        subtype = Type.OBJECT;
        subBt = Type.BT_OBJECT;
    }

    if ((superBt != Type.BT_OBJECT) || (subBt != Type.BT_OBJECT)) {
        /*
         * No two distinct primitive types are assignable in this sense,
         * unless they are both int-like.
         */
        return supertype.isIntlike() && subtype.isIntlike();
    }

    // At this point, we know both types are reference types.

    if (supertype == Type.KNOWN_NULL) {
        /*
         * A known-null supertype is only assignable from another
         * known-null (handled in the easy out at the top of the
         * method).
         */
        return false;
    } else if (subtype == Type.KNOWN_NULL) {
        /*
         * A known-null subtype is in fact assignable to any
         * reference type.
         */
        return true;
    } else if (supertype == Type.OBJECT) {
        /*
         * Object is assignable from any reference type.
         */
        return true;
    } else if (supertype.isArray()) {
        // The supertype is an array type.
        if (! subtype.isArray()) {
            // The subtype isn't an array, and so can't be assignable.
            return false;
        }

        /*
         * Strip off as many matched component types from both
         * types as possible, and check the assignability of the
         * results.
         */
        do {
            supertype = supertype.getComponentType();
            subtype = subtype.getComponentType();
        } while (supertype.isArray() && subtype.isArray());

        return isPossiblyAssignableFrom(supertype, subtype);
    } else if (subtype.isArray()) {
        /*
         * Other than Object (handled above), array types are
         * assignable only to Serializable and Cloneable.
         */
        return (supertype == Type.SERIALIZABLE) ||
            (supertype == Type.CLONEABLE);
    } else {
        /*
         * All other unequal reference types are considered at
         * least possibly assignable.
         */
        return true;
    }
}
 
Example 10
Source File: Simulator.java    From buck with Apache License 2.0 4 votes vote down vote up
/**
 * Returns the required array type for an array load or store
 * instruction, based on a given implied type and an observed
 * actual array type.
 *
 * <p>The interesting cases here have to do with object arrays,
 * <code>byte[]</code>s, <code>boolean[]</code>s, and
 * known-nulls.</p>
 *
 * <p>In the case of arrays of objects, we want to narrow the type
 * to the actual array present on the stack, as long as what is
 * present is an object type. Similarly, due to a quirk of the
 * original bytecode representation, the instructions for dealing
 * with <code>byte[]</code> and <code>boolean[]</code> are
 * undifferentiated, and we aim here to return whichever one was
 * actually present on the stack.</p>
 *
 * <p>In the case where there is a known-null on the stack where
 * an array is expected, our behavior depends on the implied type
 * of the instruction. When the implied type is a reference, we
 * don't attempt to infer anything, as we don't know the dimension
 * of the null constant and thus any explicit inferred type could
 * be wrong. When the implied type is a primitive, we fall back to
 * the implied type of the instruction. Due to the quirk described
 * above, this means that source code that uses
 * <code>boolean[]</code> might get translated surprisingly -- but
 * correctly -- into an instruction that specifies a
 * <code>byte[]</code>. It will be correct, because should the
 * code actually execute, it will necessarily throw a
 * <code>NullPointerException</code>, and it won't matter what
 * opcode variant is used to achieve that result.</p>
 *
 * @param impliedType {@code non-null;} type implied by the
 * instruction; is <i>not</i> an array type
 * @param foundArrayType {@code non-null;} type found on the
 * stack; is either an array type or a known-null
 * @return {@code non-null;} the array type that should be
 * required in this context
 */
private static Type requiredArrayTypeFor(Type impliedType,
        Type foundArrayType) {
    if (foundArrayType == Type.KNOWN_NULL) {
        return impliedType.isReference()
            ? Type.KNOWN_NULL
            : impliedType.getArrayType();
    }

    if ((impliedType == Type.OBJECT)
            && foundArrayType.isArray()
            && foundArrayType.getComponentType().isReference()) {
        return foundArrayType;
    }

    if ((impliedType == Type.BYTE)
            && (foundArrayType == Type.BOOLEAN_ARRAY)) {
        /*
         * Per above, an instruction with implied byte[] is also
         * allowed to be used on boolean[].
         */
        return Type.BOOLEAN_ARRAY;
    }

    return impliedType.getArrayType();
}
 
Example 11
Source File: Merger.java    From buck with Apache License 2.0 4 votes vote down vote up
/**
 * Merges two frame types.
 *
 * @param ft1 {@code non-null;} a frame type
 * @param ft2 {@code non-null;} another frame type
 * @return {@code non-null;} the result of merging the two types
 */
public static TypeBearer mergeType(TypeBearer ft1, TypeBearer ft2) {
    if ((ft1 == null) || ft1.equals(ft2)) {
        return ft1;
    } else if (ft2 == null) {
        return null;
    } else {
        Type type1 = ft1.getType();
        Type type2 = ft2.getType();

        if (type1 == type2) {
            return type1;
        } else if (type1.isReference() && type2.isReference()) {
            if (type1 == Type.KNOWN_NULL) {
                /*
                 * A known-null merges with any other reference type to
                 * be that reference type.
                 */
                return type2;
            } else if (type2 == Type.KNOWN_NULL) {
                /*
                 * The same as above, but this time it's type2 that's
                 * the known-null.
                 */
                return type1;
            } else if (type1.isArray() && type2.isArray()) {
                TypeBearer componentUnion =
                    mergeType(type1.getComponentType(),
                            type2.getComponentType());
                if (componentUnion == null) {
                    /*
                     * At least one of the types is a primitive type,
                     * so the merged result is just Object.
                     */
                    return Type.OBJECT;
                }
                return ((Type) componentUnion).getArrayType();
            } else {
                /*
                 * All other unequal reference types get merged to be
                 * Object in this phase. This is fine here, but it
                 * won't be the right thing to do in the verifier.
                 */
                return Type.OBJECT;
            }
        } else if (type1.isIntlike() && type2.isIntlike()) {
            /*
             * Merging two non-identical int-like types results in
             * the type int.
             */
            return Type.INT;
        } else {
            return null;
        }
    }
}
 
Example 12
Source File: Merger.java    From buck with Apache License 2.0 4 votes vote down vote up
/**
 * Returns whether the given supertype is possibly assignable from
 * the given subtype. This takes into account primitiveness,
 * int-likeness, known-nullness, and array dimensions, but does
 * not assume anything about class hierarchy other than that the
 * type {@code Object} is the supertype of all reference
 * types and all arrays are assignable to
 * {@code Serializable} and {@code Cloneable}.
 *
 * @param supertypeBearer {@code non-null;} the supertype
 * @param subtypeBearer {@code non-null;} the subtype
 */
public static boolean isPossiblyAssignableFrom(TypeBearer supertypeBearer,
        TypeBearer subtypeBearer) {
    Type supertype = supertypeBearer.getType();
    Type subtype = subtypeBearer.getType();

    if (supertype.equals(subtype)) {
        // Easy out.
        return true;
    }

    int superBt = supertype.getBasicType();
    int subBt = subtype.getBasicType();

    // Treat return types as Object for the purposes of this method.

    if (superBt == Type.BT_ADDR) {
        supertype = Type.OBJECT;
        superBt = Type.BT_OBJECT;
    }

    if (subBt == Type.BT_ADDR) {
        subtype = Type.OBJECT;
        subBt = Type.BT_OBJECT;
    }

    if ((superBt != Type.BT_OBJECT) || (subBt != Type.BT_OBJECT)) {
        /*
         * No two distinct primitive types are assignable in this sense,
         * unless they are both int-like.
         */
        return supertype.isIntlike() && subtype.isIntlike();
    }

    // At this point, we know both types are reference types.

    if (supertype == Type.KNOWN_NULL) {
        /*
         * A known-null supertype is only assignable from another
         * known-null (handled in the easy out at the top of the
         * method).
         */
        return false;
    } else if (subtype == Type.KNOWN_NULL) {
        /*
         * A known-null subtype is in fact assignable to any
         * reference type.
         */
        return true;
    } else if (supertype == Type.OBJECT) {
        /*
         * Object is assignable from any reference type.
         */
        return true;
    } else if (supertype.isArray()) {
        // The supertype is an array type.
        if (! subtype.isArray()) {
            // The subtype isn't an array, and so can't be assignable.
            return false;
        }

        /*
         * Strip off as many matched component types from both
         * types as possible, and check the assignability of the
         * results.
         */
        do {
            supertype = supertype.getComponentType();
            subtype = subtype.getComponentType();
        } while (supertype.isArray() && subtype.isArray());

        return isPossiblyAssignableFrom(supertype, subtype);
    } else if (subtype.isArray()) {
        /*
         * Other than Object (handled above), array types are
         * assignable only to Serializable and Cloneable.
         */
        return (supertype == Type.SERIALIZABLE) ||
            (supertype == Type.CLONEABLE);
    } else {
        /*
         * All other unequal reference types are considered at
         * least possibly assignable.
         */
        return true;
    }
}