Java Code Examples for org.objectweb.asm.Type#getElementType()

The following examples show how to use org.objectweb.asm.Type#getElementType() . 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: DefaultClassDependenciesAnalyzer.java    From pushfish-android with BSD 2-Clause "Simplified" License 6 votes vote down vote up
private Set<String> getClassDependencies(ClassRelevancyFilter filter, ClassReader reader) {
    Set<String> out = new HashSet<String>();
    char[] charBuffer = new char[reader.getMaxStringLength()];
    for (int i = 1; i < reader.getItemCount(); i++) {
        int itemOffset = reader.getItem(i);
        if (itemOffset > 0 && reader.readByte(itemOffset - 1) == 7) {
            // A CONSTANT_Class entry, read the class descriptor
            String classDescriptor = reader.readUTF8(itemOffset, charBuffer);
            Type type = Type.getObjectType(classDescriptor);
            while (type.getSort() == Type.ARRAY) {
                type = type.getElementType();
            }
            if (type.getSort() != Type.OBJECT) {
                // A primitive type
                continue;
            }
            String name = type.getClassName();
            if (filter.isRelevant(name)) {
                out.add(name);
            }
        }
    }
    return out;
}
 
Example 2
Source File: ClassDependenciesAnalyzer.java    From pushfish-android with BSD 2-Clause "Simplified" License 6 votes vote down vote up
private List<String> getClassDependencies(ClassRelevancyFilter filter, ClassReader reader) {
    List<String> out = new LinkedList<String>();
    char[] charBuffer = new char[reader.getMaxStringLength()];
    for (int i = 1; i < reader.getItemCount(); i++) {
        int itemOffset = reader.getItem(i);
        if (itemOffset > 0 && reader.readByte(itemOffset - 1) == 7) {
            // A CONSTANT_Class entry, read the class descriptor
            String classDescriptor = reader.readUTF8(itemOffset, charBuffer);
            Type type = Type.getObjectType(classDescriptor);
            while (type.getSort() == Type.ARRAY) {
                type = type.getElementType();
            }
            if (type.getSort() != Type.OBJECT) {
                // A primitive type
                continue;
            }
            String name = type.getClassName();
            if (filter.isRelevant(name)) {
                out.add(name);
            }
        }
    }
    return out;
}
 
Example 3
Source File: DescParser.java    From Recaf with MIT License 6 votes vote down vote up
private static boolean validate(String token) {
	// Void check
	if(token.equals("V"))
		return true;
	// Ensure type is not an array
	Type type = Type.getType(token);
	while(type.getSort() == Type.ARRAY)
		type = type.getElementType();
	// Check for primitives
	if(type.getSort() < Type.ARRAY)
		return true;
	// Verify L...; pattern
	// - getDescriptor doesn't modify the original element type (vs getInternalName)
	String desc = type.getDescriptor();
	return desc.startsWith("L") && desc.endsWith(";");
}
 
Example 4
Source File: ClassScanner.java    From forbidden-apis with Apache License 2.0 6 votes vote down vote up
String checkClassUse(Type type, String what, boolean deep, String origInternalName) {
  while (type.getSort() == Type.ARRAY) {
    type = type.getElementType(); // unwrap array
  }
  if (type.getSort() != Type.OBJECT) {
    return null; // we don't know this type, just pass!
  }
  final String printout = forbiddenSignatures.checkType(type);
  if (printout != null) {
    return String.format(Locale.ENGLISH, "Forbidden %s use: %s", what, printout);
  }
  if (deep && forbidNonPortableRuntime) {
    final String binaryClassName = type.getClassName();
    final ClassSignature c = lookup.lookupRelatedClass(type.getInternalName(), origInternalName);
    if (c != null && c.isRuntimeClass && !AsmUtils.isPortableRuntimeClass(binaryClassName)) {
      return String.format(Locale.ENGLISH,
        "Forbidden %s use: %s [non-portable or internal runtime class]",
        what, binaryClassName
      );
    }
  }
  return null;
}
 
Example 5
Source File: DefaultClassDependenciesAnalyzer.java    From Pushjet-Android with BSD 2-Clause "Simplified" License 6 votes vote down vote up
private Set<String> getClassDependencies(ClassRelevancyFilter filter, ClassReader reader) {
    Set<String> out = new HashSet<String>();
    char[] charBuffer = new char[reader.getMaxStringLength()];
    for (int i = 1; i < reader.getItemCount(); i++) {
        int itemOffset = reader.getItem(i);
        if (itemOffset > 0 && reader.readByte(itemOffset - 1) == 7) {
            // A CONSTANT_Class entry, read the class descriptor
            String classDescriptor = reader.readUTF8(itemOffset, charBuffer);
            Type type = Type.getObjectType(classDescriptor);
            while (type.getSort() == Type.ARRAY) {
                type = type.getElementType();
            }
            if (type.getSort() != Type.OBJECT) {
                // A primitive type
                continue;
            }
            String name = type.getClassName();
            if (filter.isRelevant(name)) {
                out.add(name);
            }
        }
    }
    return out;
}
 
Example 6
Source File: FastClassVerifier.java    From tascalate-javaflow with Apache License 2.0 5 votes vote down vote up
@Override
protected boolean isAssignableFrom(Type t, Type u) {
    if (t.equals(u)) {
        return true;
    }
    // Null is assignable to any reference type
    if ("Lnull;".equals(u.getDescriptor()) && t.getSort() >= Type.ARRAY) {
        return true;
    }
    Type et, eu;
    if (t.getSort() == Type.ARRAY) {
        if (u.getSort() != Type.ARRAY ) {
            return false;
        }
        et = t.getElementType();
        eu = u.getElementType();
        int dt = t.getDimensions();
        int du = u.getDimensions();
        boolean isObject = et.equals(BasicValue.REFERENCE_VALUE.getType());
        // u must be an array of equals dimension or bigger dimension if t is Object
        if (dt == du || dt < du && isObject) {
            // Ok
        } else {
            return false;
        }
    } else {
        et = t; 
        eu = u;
    }
    /*
    Type commonType = classHierarchy.getCommonSuperType(et, eu);
    */
    // isAssignableFrom(Number, Integer) => getCommonSuperType(Integer, Number) == Number        
    // Use ClassHierarchy.isSubclass biased behavior (for performance)
    Type commonType = classHierarchy.getCommonSuperType(eu, et);
    return commonType.equals(et);

}
 
Example 7
Source File: HierarchyMethods.java    From maple-ir with GNU General Public License v3.0 5 votes vote down vote up
/**
 * Two types are congruent if they are primitive and the same, or if one is a subclass of another.
 * @param a type A
 * @param b type B
 * @return true if type A and B are congruent
 */
private boolean areTypesCongruent(Type a, Type b) {
	if (a.equals(b)) {
		return true;
	}
	
	boolean eArr = a.getSort() == Type.ARRAY;
	boolean aArr = b.getSort() == Type.ARRAY;
	if(eArr != aArr) {
		return false;
	}
	
	if(eArr) {
		a = a.getElementType();
		b = b.getElementType();
	}
	
	if(TypeUtils.isPrimitive(a) || TypeUtils.isPrimitive(b)) {
		return false;
	}
	if(a == Type.VOID_TYPE || b == Type.VOID_TYPE) {
		return false;
	}
	
	ClassNode cnA = app.findClassNode(a.getInternalName());
	ClassNode cnB = app.findClassNode(b.getInternalName());
	
	ClassTree tree = app.getClassTree();
	return tree.getAllParents(cnB).contains(cnA) ||
              tree.getAllParents(cnA).contains(cnB);
}
 
Example 8
Source File: FastClassVerifier.java    From tascalate-javaflow with Apache License 2.0 5 votes vote down vote up
@Override
protected boolean isAssignableFrom(Type t, Type u) {
    if (t.equals(u)) {
        return true;
    }
    // Null is assignable to any reference type
    if ("Lnull;".equals(u.getDescriptor()) && t.getSort() >= Type.ARRAY) {
        return true;
    }
    Type et, eu;
    if (t.getSort() == Type.ARRAY) {
        if (u.getSort() != Type.ARRAY ) {
            return false;
        }
        et = t.getElementType();
        eu = u.getElementType();
        int dt = t.getDimensions();
        int du = u.getDimensions();
        boolean isObject = et.equals(((BasicValue)BasicValue.REFERENCE_VALUE).getType());
        // u must be an array of equals dimension or bigger dimension if t is Object
        if (dt == du || dt < du && isObject) {
            // Ok
        } else {
            return false;
        }
    } else {
        et = t; 
        eu = u;
    }
    /*
    Type commonType = classHierarchy.getCommonSuperType(et, eu);
    */
    // isAssignableFrom(Number, Integer) => getCommonSuperType(Integer, Number) == Number        
    // Use ClassHierarchy.isSubclass biased behavior (for performance)
    Type commonType = classHierarchy.getCommonSuperType(eu, et);
    return commonType.equals(et);

}
 
Example 9
Source File: BuildStackInfoAdapter.java    From copper-engine with Apache License 2.0 5 votes vote down vote up
private static Type getArrayElementType(Type arrayType) {
    int dim = arrayType.getDimensions();
    if(dim < 1) throw new IllegalArgumentException("Not an array type: " + arrayType);
    if(dim > 1) {
        String descr = arrayType.getDescriptor();
        return Type.getType(descr.substring(1));
    }
    return arrayType.getElementType();
}
 
Example 10
Source File: Dependencies.java    From twill with Apache License 2.0 5 votes vote down vote up
private void addType(Type type) {
  if (type.getSort() == Type.ARRAY) {
    type = type.getElementType();
  }
  if (type.getSort() == Type.OBJECT) {
    addClass(type.getInternalName());
  }
}
 
Example 11
Source File: FastClassVerifier.java    From tascalate-javaflow with Apache License 2.0 5 votes vote down vote up
@Override
protected boolean isAssignableFrom(Type t, Type u) {
    if (t.equals(u)) {
        return true;
    }
    // Null is assignable to any reference type
    if ("Lnull;".equals(u.getDescriptor()) && t.getSort() >= Type.ARRAY) {
        return true;
    }
    Type et, eu;
    if (t.getSort() == Type.ARRAY) {
        if (u.getSort() != Type.ARRAY ) {
            return false;
        }
        et = t.getElementType();
        eu = u.getElementType();
        int dt = t.getDimensions();
        int du = u.getDimensions();
        boolean isObject = et.equals(BasicValue.REFERENCE_VALUE.getType());
        // u must be an array of equals dimension or bigger dimension if t is Object
        if (dt == du || dt < du && isObject) {
            // Ok
        } else {
            return false;
        }
    } else {
        et = t; 
        eu = u;
    }
    /*
    Type commonType = classHierarchy.getCommonSuperType(et, eu);
    */
    // isAssignableFrom(Number, Integer) => getCommonSuperType(Integer, Number) == Number        
    // Use ClassHierarchy.isSubclass biased behavior (for performance)
    Type commonType = classHierarchy.getCommonSuperType(eu, et);
    return commonType.equals(et);

}
 
Example 12
Source File: ReflectiveProvider.java    From deobfuscator with Apache License 2.0 5 votes vote down vote up
@Override
public boolean canCheckcast(JavaValue target, Type type, Context context) {
    Type finalType = type;
    while (finalType.getSort() == Type.ARRAY) {
        finalType = finalType.getElementType();
    }
    if (finalType.getSort() != Type.OBJECT) {
        throw new ExecutionException("Expected instanceof Object, but got " + finalType.getSort());
    }
    return dictionary.containsKey(finalType.getInternalName());
}
 
Example 13
Source File: JavaClass.java    From deobfuscator with Apache License 2.0 5 votes vote down vote up
public JavaClass(String name, Context context) {
    if (name.contains("/")) {
        name = name.replace('/', '.');
    }
    this.name = name;
    this.context = context;
    String internalName = this.name.replace('.', '/');
    Class<?> primitive = PrimitiveUtils.getPrimitiveByName(internalName);
    if (primitive == null) {
        this.type = Type.getObjectType(internalName);
        Type elementType = this.type;
        if (elementType.getSort() == Type.ARRAY) {
            elementType = elementType.getElementType();
        }
        primitive = PrimitiveUtils.getPrimitiveByName(elementType.getClassName());
        if (primitive == null) {
            this.classNode = context.dictionary.get(elementType.getInternalName());
            if (this.classNode == null) {
                System.out.println("Could not find classnode " + this.name);
                throw new NoClassInPathException(this.name);
            }
            this.isPrimitive = false;
        } else {
            this.classNode = context.dictionary.get("java/lang/Object");
            this.isPrimitive = false;
        }
    } else {
        this.type = Type.getType(primitive);
        this.classNode = null;
        this.isPrimitive = true;
    }
}
 
Example 14
Source File: NameTranslator.java    From CodenameOne with GNU General Public License v2.0 5 votes vote down vote up
private Type getMirrorType(final Type type) {
	int numDimensions = 0;
	final Type basicType;

	if (type.getSort() == Type.ARRAY) {
		numDimensions = type.getDimensions();
		basicType = type.getElementType();
	} else {
		basicType = type;
	}

	if (basicType.getSort() != Type.OBJECT) {
		return type;
	}

	final Mirror mirror = getMirror(basicType.getInternalName());

	if (mirror.isClassMirror()) {
		final StringBuilder name = new StringBuilder();

		for (int i = 0; i < numDimensions; ++i) {
			name.append('[');
		}
		name.append('L').append(mirror.getTranslatedName()).append(';');

		return Type.getType(name.toString());
	}

	return type;
}
 
Example 15
Source File: TypeUtils.java    From coroutines with GNU Lesser General Public License v3.0 4 votes vote down vote up
/**
 * Checks to see if one type is assignable from another type. This method should be similar to
 * {@link Class#isAssignableFrom(java.lang.Class) } (with some caveats, explained in next paragraph), but uses a
 * {@link ClassInformationRepository} object rather than requiring classes to be loaded up in to the JVM.
 * <p>
 * Note that this code tries to mimic what ASM's original {@link org.objectweb.asm.tree.analysis.SimpleVerifier} does to find out
 * if two types are equal. The main difference between SimpleVerifier's code and {@link Class#isAssignableFrom(java.lang.Class) } is
 * that SimpleVerifier will treat any interface instance as if it were an {@link Object} instance. That means that, for example, an
 * {@link Object} is assignable to a {@link Comparable} (Comparable = Object) in the eyes of ASM's SimpleVerifier. Why this is the case
 * has never been explained.
 * @param repo repository to use for deriving class details
 * @param t type being assigned from
 * @param u type being assigned to
 * @return {@code true} if u is assignable to t ({@code t = u}), {@code false} otherwise
 */
public static boolean isAssignableFrom(ClassInformationRepository repo, Type t, Type u) {
    Validate.notNull(repo);
    Validate.notNull(t);
    Validate.notNull(u);
    
    if (t.equals(u)) {
        return true;
    }

    if (t.getSort() == Type.OBJECT && u.getSort() == Type.OBJECT) {
        // Both are objects, check hierarchy for both to see if assignable
        // e.g. you're allowed to do Number = Integer
        // e.g. you're allowed to do Serializable = Object, this seems counter-intuative but it is what ASM does
        return isObjectTypeAssignableFrom(repo, t, u);
    } else if (t.getSort() == Type.ARRAY && u.getSort() == Type.ARRAY) {
        // Both are arrays
        if (t.getDimensions() == u.getDimensions()) {
            // If dimensions are equal...
            Type tElem = t.getElementType();
            Type uElem = u.getElementType();
            if (tElem.getSort() == Type.OBJECT && uElem.getSort() == Type.OBJECT) {
                // If dimensions are equal and both element types are objects, check hierarchy for both to see if assignable
                // e.g. you're allowed to do Number[][] = Integer[][]
                // e.g. you're allowed to do Object[][] = Integer[][]
                // e.g. you're not allowed to do Serializable[][] = Object[][] because of false being passed in to method below...
                //      we only want to resolve interfaces to object if we aren't dealing with arrays (see first if block at top of this
                //      method)
                return isArrayElementTypeAssignableFrom(repo, tElem, uElem);
            } else if (tElem.getSort() != Type.OBJECT && uElem.getSort() != Type.OBJECT) {
                // If dimensions are equal and both element types are primitives, check that both are equal to see if assignable
                // e.g. you're allowed to do int[][] = int[][]
                // e.g. you're not allowed to do int[][] = byte[][]
                // e.g. you're not allowed to do byte[][] = int[][]
                return tElem.equals(uElem);
            } else {
                // If dimensions are equal but you're dealing with one element type being an object and the other a primitive, always
                // return false
                // e.g. you're not allowed to do int[][] = Object[][]
                // e.g. you're not allowed to do Object[][] = int[][]
                return false;
            }
        } else if (t.getDimensions() > u.getDimensions()) {
            // If t has MORE dimensions than u, it is not assignable
            // e.g. you're not allowed to do Number[][] = Integer[]
            // e.g. you're not allowed to do Object[][] = Integer[]
            return false;
        } else if (t.getDimensions() < u.getDimensions()) {
            // If t has LESS dimensions than u, it is not assignable UNLESS t has an element type of Object
            // e.g. you're allowed to do Object[][] = Number[][][]
            // e.g. you're not allowed to do Number[][] = Integer[][][]
            // e.g. you're not allowed to do Object[][] = Integer[][][]
            return Type.getType(Object.class).equals(t.getElementType());
        }
    } else if (t.getSort() == Type.OBJECT && u.getSort() == Type.ARRAY) {
        // Assigning an array to an object, only valid if the object is of type Object
        // e.g. you're allowed to do Object = Integer[]
        return Type.getType(Object.class).equals(t);
    } else if (t.getSort() == Type.ARRAY && u.getSort() == Type.OBJECT) {
        // Assigning an array to an object, never valid
        // e.g. it doesn't make sense to do Integer[] = Object
        return false;
    }
    
    // What about primitives?

    return false;
}
 
Example 16
Source File: SimpleVerifier.java    From JReFrameworker with MIT License 4 votes vote down vote up
@Override
public BasicValue merge(final BasicValue value1, final BasicValue value2) {
  if (!value1.equals(value2)) {
    Type type1 = value1.getType();
    Type type2 = value2.getType();
    if (type1 != null
        && (type1.getSort() == Type.OBJECT || type1.getSort() == Type.ARRAY)
        && type2 != null
        && (type2.getSort() == Type.OBJECT || type2.getSort() == Type.ARRAY)) {
      if (type1.equals(NULL_TYPE)) {
        return value2;
      }
      if (type2.equals(NULL_TYPE)) {
        return value1;
      }
      if (isAssignableFrom(type1, type2)) {
        return value1;
      }
      if (isAssignableFrom(type2, type1)) {
        return value2;
      }
      int numDimensions = 0;
      if (type1.getSort() == Type.ARRAY
          && type2.getSort() == Type.ARRAY
          && type1.getDimensions() == type2.getDimensions()
          && type1.getElementType().getSort() == Type.OBJECT
          && type2.getElementType().getSort() == Type.OBJECT) {
        numDimensions = type1.getDimensions();
        type1 = type1.getElementType();
        type2 = type2.getElementType();
      }
      do {
        if (type1 == null || isInterface(type1)) {
          return newArrayValue(Type.getObjectType("java/lang/Object"), numDimensions);
        }
        type1 = getSuperClass(type1);
        if (isAssignableFrom(type1, type2)) {
          return newArrayValue(type1, numDimensions);
        }
      } while (true);
    }
    return BasicValue.UNINITIALIZED_VALUE;
  }
  return value1;
}
 
Example 17
Source File: ClassScanner.java    From forbidden-apis with Apache License 2.0 4 votes vote down vote up
String checkType(Type type) {
  while (type != null) {
    String violation;
    switch (type.getSort()) {
      case Type.OBJECT:
        final String internalName = type.getInternalName();
        violation = checkClassUse(type, "class/interface", true, internalName);
        if (violation != null) {
          return violation;
        }
        final ClassSignature c = lookup.lookupRelatedClass(internalName, internalName);
        if (c == null) return null;
        return checkClassDefinition(internalName, c.superName, c.interfaces);
      case Type.ARRAY:
        type = type.getElementType();
        break;
      case Type.METHOD:
        final ArrayList<String> violations = new ArrayList<>();
        violation = checkType(type.getReturnType());
        if (violation != null) {
          violations.add(violation);
        }
        for (final Type t : type.getArgumentTypes()) {
          violation = checkType(t);
          if (violation != null) {
            violations.add(violation);
          }
        }
        if (violations.isEmpty()) {
          return null;
        } else if (violations.size() == 1) {
          return violations.get(0);
        } else {
          final StringBuilder sb = new StringBuilder();
          boolean nl = false;
          for (final String v : violations) {
            if (nl) sb.append(ForbiddenViolation.SEPARATOR);
            sb.append(v);
            nl = true;
          }
          return sb.toString();
        }
      default:
        return null;
    }
  }
  return null;
}
 
Example 18
Source File: Variables.java    From Recaf with MIT License 4 votes vote down vote up
/**
 * Fills missing index-to-descriptor mappings.
 *
 * @param labels
 * 		Map of label names to instances.
 * @param frames
 * 		Stack-frame analysis data.
 *
 * @throws AssemblerException
 * 		When multiple types for a single variable have conflicting array-levels.
 */
void visitWithFrames(Frame<AbstractValue>[] frames, Map<String, LabelNode> labels) throws AssemblerException {
	// TODO: Reuse variable slots of the same sort if the scope of the variables do not collide
	for(Map.Entry<String, Integer> entry : nameToIndex.entrySet()) {
		// Skip already visitied
		String name = entry.getKey();
		String startName = nameToStart.get(name);
		String endName = nameToEnd.get(name);
		LabelNode start = labels.get(startName);
		LabelNode end = labels.get(endName);
		if (start == null || end == null)
			continue;
		// Collect the types stored in this index
		Set<Type> types = new HashSet<>();
		int index = entry.getValue();
		for(Frame<AbstractValue> frame : frames) {
			if(frame == null)
				continue;
			AbstractValue value = frame.getLocal(index);
			if(value != null && value.getType() != null)
				types.add(value.getType());
		}
		Iterator<Type> it = types.iterator();
		// If we don't have type information, abort for this index
		if (!it.hasNext())
			continue;
		Type lastElementType = it.next();
		int arrayLevel = TypeUtil.getArrayDepth(lastElementType);
		while (it.hasNext()) {
			int lastArrayLevel = TypeUtil.getArrayDepth(lastElementType);
			Type type1 = it.next();
			if (lastArrayLevel != TypeUtil.getArrayDepth(type1)) {
				// TODO: See above TODO about variable index re-use
				//  - The problem here is this logic assumes no index re-use...
				//  - This should throw an exception later, but for now
				//    we just pretend the variable is an object (since everything is)
				lastElementType = TypeUtil.OBJECT_TYPE;
				break;
				//throw new VerifierException("Stored multiple array sizes in same variable slot: " + index);
			}
			if (lastElementType.equals(type1))
				continue;
			if(Recaf.getCurrentWorkspace() != null) {
				Type lastType = lastElementType;
				Type otherType = type1;
				if (lastType.getSort() == Type.ARRAY)
					lastType = lastType.getElementType();
				if (otherType.getSort() == Type.ARRAY)
					otherType = otherType.getElementType();
				lastElementType = Type.getObjectType(Recaf.getCurrentWorkspace().getHierarchyGraph()
						.getCommon(lastType.getInternalName(), otherType.getInternalName()));
			}
			else break;
		}
		while (lastElementType.getSort() == Type.ARRAY) {
			lastElementType = lastElementType.getElementType();
		}
		// Save type
		StringBuilder arr = new StringBuilder();
		for(int i = 0; i < arrayLevel; i++)
			arr.append('[');
		// TODO: Boolean is saved as int, which is technically correct but not expected by most users
		//  - Sort is saved as INTEGER because we don't analyze difference between int/bool cases
		if (lastElementType.getSort() < Type.ARRAY)
			nameToDesc.put(name, arr.toString() + lastElementType.getDescriptor());
		else
			nameToDesc.put(name, arr.toString() + "L" + lastElementType.getInternalName() + ";");
	}
}
 
Example 19
Source File: ABICompilerClassVisitor.java    From AVM with MIT License 4 votes vote down vote up
private boolean isArrayOfTypeAndDimensions(Type arrayType, Type expectedElementType, int expectedDimensions) {
    return arrayType.getSort() == Type.ARRAY && arrayType.getDimensions() == expectedDimensions && arrayType.getElementType() == expectedElementType;
}
 
Example 20
Source File: SimpleVerifier.java    From Concurnas with MIT License 4 votes vote down vote up
@Override
public BasicValue merge(final BasicValue value1, final BasicValue value2) {
  if (!value1.equals(value2)) {
    Type type1 = value1.getType();
    Type type2 = value2.getType();
    if (type1 != null
        && (type1.getSort() == Type.OBJECT || type1.getSort() == Type.ARRAY)
        && type2 != null
        && (type2.getSort() == Type.OBJECT || type2.getSort() == Type.ARRAY)) {
      if (type1.equals(NULL_TYPE)) {
        return value2;
      }
      if (type2.equals(NULL_TYPE)) {
        return value1;
      }
      if (isAssignableFrom(type1, type2)) {
        return value1;
      }
      if (isAssignableFrom(type2, type1)) {
        return value2;
      }
      int numDimensions = 0;
      if (type1.getSort() == Type.ARRAY
          && type2.getSort() == Type.ARRAY
          && type1.getDimensions() == type2.getDimensions()
          && type1.getElementType().getSort() == Type.OBJECT
          && type2.getElementType().getSort() == Type.OBJECT) {
        numDimensions = type1.getDimensions();
        type1 = type1.getElementType();
        type2 = type2.getElementType();
      }
      while (true) {
        if (type1 == null || isInterface(type1)) {
          return newArrayValue(Type.getObjectType("java/lang/Object"), numDimensions);
        }
        type1 = getSuperClass(type1);
        if (isAssignableFrom(type1, type2)) {
          return newArrayValue(type1, numDimensions);
        }
      }
    }
    return BasicValue.UNINITIALIZED_VALUE;
  }
  return value1;
}