/** * Looks up the <code>ProtoIdItem</code> from the given <code>DexFile</code> for the given * values * @param dexFile the <code>Dexfile</code> to find the type in * @param returnType the return type * @param parameters a <code>TypeListItem</code> containing a list of the parameter types * @return a <code>ProtoIdItem</code> from the given <code>DexFile</code> for the given * values, or null if it doesn't exist */ public static ProtoIdItem lookupProtoIdItem(DexFile dexFile, TypeIdItem returnType, TypeListItem parameters) { ProtoIdItem protoIdItem = new ProtoIdItem(dexFile, returnType, parameters); return dexFile.ProtoIdsSection.getInternedItem(protoIdItem); }
@Override public int hashCode() { //there's a small possibility that the actual hash code will be 0. If so, we'll //just end up recalculating it each time if (hashCode == 0) calcHashCode(); return hashCode; }
/** {@inheritDoc} */ public int compareTo(MethodIdItem o) { int result = classType.compareTo(o.classType); if (result != 0) { return result; } result = methodName.compareTo(o.methodName); if (result != 0) { return result; } return methodPrototype.compareTo(o.methodPrototype); }
/** {@inheritDoc} */ protected void writeItem(AnnotatedOutput out) { if (out.annotates()) { out.annotate(2, "class_type: " + classType.getTypeDescriptor()); out.annotate(2, "method_prototype: " + methodPrototype.getPrototypeString()); out.annotate(4, "method_name: " + methodName.getStringValue()); } int classIndex = classType.getIndex(); if (classIndex > 0xffff) { throw new RuntimeException(String.format("Error writing method_id_item for %s. The type index of " + "defining class %s is too large", getMethodString(), classType.getTypeDescriptor())); } out.writeShort(classIndex); int prototypeIndex = methodPrototype.getIndex(); if (prototypeIndex > 0xffff) { throw new RuntimeException(String.format("Error writing method_id_item for %0. The prototype index of " + "method prototype %s is too large", getMethodString(), methodPrototype.getPrototypeString())); } out.writeShort(prototypeIndex); out.writeInt(methodName.getIndex()); }
int parameterRegisters = methodIdItem.getPrototype().getParameterRegisterCount(); TypeListItem parameters = methodIdItem.getPrototype().getParameters(); if (parameters != null) { RegisterType[] parameterTypes = getParameterTypes(parameters, parameterRegisters);
private static void checkItem(Opcode opcode, Item item, int regCount) { if (opcode == FILLED_NEW_ARRAY_RANGE) { //check data for filled-new-array/range opcode String type = ((TypeIdItem) item).getTypeDescriptor(); if (type.charAt(0) != '[') { throw new RuntimeException("The type must be an array type"); } if (type.charAt(1) == 'J' || type.charAt(1) == 'D') { throw new RuntimeException("The type cannot be an array of longs or doubles"); } } else if (opcode.value >= INVOKE_VIRTUAL_RANGE.value && opcode.value <= INVOKE_INTERFACE_RANGE.value || opcode == INVOKE_OBJECT_INIT_RANGE) { //check data for invoke-*/range opcodes MethodIdItem methodIdItem = (MethodIdItem) item; int parameterRegisterCount = methodIdItem.getPrototype().getParameterRegisterCount(); if (opcode != INVOKE_STATIC_RANGE) { parameterRegisterCount++; } if (parameterRegisterCount != regCount) { throw new RuntimeException("regCount does not match the number of arguments of the method"); } } }
/** {@inheritDoc} */ public String getConciseIdentity() { return "proto_id_item: " + getPrototypeString(); }
/** * calculate and cache the hashcode */ private void calcHashCode() { hashCode = classType.hashCode(); hashCode = 31 * hashCode + methodPrototype.hashCode(); hashCode = 31 * hashCode + methodName.hashCode(); }
ProtoIdItem protoItem = ProtoIdItem.lookupProtoIdItem(dexFile, retType, paramListItem); if (protoItem == null) { return null;
TypeListItem typeListItem = methodIdItem.getPrototype().getParameters(); int methodParameterRegisterCount; if (typeListItem == null) {
private void verifyReturnVoid(AnalyzedInstruction analyzedInstruction) { TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType(); if (returnType.getTypeDescriptor().charAt(0) != 'V') { //TODO: could add which return-* variation should be used instead throw new ValidationException("Cannot use return-void with a non-void return type (" + returnType.getTypeDescriptor() + ")"); } }
/** {@inheritDoc} */ protected void writeItem(AnnotatedOutput out) { if (out.annotates()) { out.annotate(2, "class_type: " + classType.getTypeDescriptor()); out.annotate(2, "method_prototype: " + methodPrototype.getPrototypeString()); out.annotate(4, "method_name: " + methodName.getStringValue()); } int classIndex = classType.getIndex(); if (classIndex > 0xffff) { throw new RuntimeException(String.format("Error writing method_id_item for %s. The type index of " + "defining class %s is too large", getMethodString(), classType.getTypeDescriptor())); } out.writeShort(classIndex); int prototypeIndex = methodPrototype.getIndex(); if (prototypeIndex > 0xffff) { throw new RuntimeException(String.format("Error writing method_id_item for %0. The prototype index of " + "method prototype %s is too large", getMethodString(), methodPrototype.getPrototypeString())); } out.writeShort(prototypeIndex); out.writeInt(methodName.getIndex()); }
int parameterRegisters = methodIdItem.getPrototype().getParameterRegisterCount(); TypeListItem parameters = methodIdItem.getPrototype().getParameters(); if (parameters != null) { RegisterType[] parameterTypes = getParameterTypes(parameters, parameterRegisters);
private static void checkItem(Opcode opcode, Item item, int regCount) { if (opcode == FILLED_NEW_ARRAY_JUMBO) { //check data for filled-new-array/jumbo opcode String type = ((TypeIdItem) item).getTypeDescriptor(); if (type.charAt(0) != '[') { throw new RuntimeException("The type must be an array type"); } if (type.charAt(1) == 'J' || type.charAt(1) == 'D') { throw new RuntimeException("The type cannot be an array of longs or doubles"); } } else if (opcode.value >= INVOKE_VIRTUAL_JUMBO.value && opcode.value <= INVOKE_INTERFACE_JUMBO.value || opcode == INVOKE_OBJECT_INIT_JUMBO) { //check data for invoke-*/range opcodes MethodIdItem methodIdItem = (MethodIdItem) item; int parameterRegisterCount = methodIdItem.getPrototype().getParameterRegisterCount(); if (opcode != INVOKE_STATIC_JUMBO) { parameterRegisterCount++; } if (parameterRegisterCount != regCount) { throw new RuntimeException("regCount does not match the number of arguments of the method"); } } }
/** * @return a string formatted like methodName(TTTT..)R */ public String getVirtualMethodString() { if (cachedVirtualMethodString == null) { String methodName = this.methodName.getStringValue(); String prototypeString = methodPrototype.getPrototypeString(); StringBuilder sb = new StringBuilder(methodName.length() + prototypeString.length()); sb.append(methodName); sb.append(prototypeString); cachedVirtualMethodString = sb.toString(); } return cachedVirtualMethodString; }
/** * calculate and cache the hashcode */ private void calcHashCode() { hashCode = classType.hashCode(); hashCode = 31 * hashCode + methodPrototype.hashCode(); hashCode = 31 * hashCode + methodName.hashCode(); }
ProtoIdItem protoItem = ProtoIdItem.lookupProtoIdItem(dexFile, retType, paramListItem); if (protoItem == null) { return null;
private void verifyMoveResult(AnalyzedInstruction analyzedInstruction, EnumSet<RegisterType.Category> allowedCategories) { if (analyzedInstruction.instructionIndex == 0) { throw new ValidationException(analyzedInstruction.instruction.opcode.name + " cannot be the first " + "instruction in a method. It must occur after an invoke-*/fill-new-array instruction"); } AnalyzedInstruction previousInstruction = instructions.valueAt(analyzedInstruction.instructionIndex-1); if (!previousInstruction.instruction.opcode.setsResult()) { throw new ValidationException(analyzedInstruction.instruction.opcode.name + " must occur after an " + "invoke-*/fill-new-array instruction"); } //TODO: does dalvik allow a move-result after an invoke with a void return type? RegisterType resultRegisterType; InstructionWithReference invokeInstruction = (InstructionWithReference)previousInstruction.getInstruction(); Item item = invokeInstruction.getReferencedItem(); if (item instanceof MethodIdItem) { resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem( ((MethodIdItem)item).getPrototype().getReturnType()); } else { assert item instanceof TypeIdItem; resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); } if (!allowedCategories.contains(resultRegisterType.category)) { throw new ValidationException(String.format("Wrong move-result* instruction for return value %s", resultRegisterType.toString())); } }
private static void checkItem(Opcode opcode, Item item, int regCount) { if (opcode == FILLED_NEW_ARRAY) { //check data for filled-new-array opcode String type = ((TypeIdItem) item).getTypeDescriptor(); if (type.charAt(0) != '[') { throw new RuntimeException("The type must be an array type"); } if (type.charAt(1) == 'J' || type.charAt(1) == 'D') { throw new RuntimeException("The type cannot be an array of longs or doubles"); } } else if (opcode.value >= INVOKE_VIRTUAL.value && opcode.value <= INVOKE_INTERFACE.value || opcode == INVOKE_DIRECT_EMPTY) { //check data for invoke-* opcodes MethodIdItem methodIdItem = (MethodIdItem) item; int parameterRegisterCount = methodIdItem.getPrototype().getParameterRegisterCount(); if (opcode != INVOKE_STATIC) { parameterRegisterCount++; } if (parameterRegisterCount != regCount) { throw new RuntimeException("regCount does not match the number of arguments of the method"); } } }
/** * Returns a <code>ProtoIdItem</code> for the given values, and that has been interned into * the given <code>DexFile</code> * @param dexFile The <code>DexFile</code> that this item belongs to * @param returnType the return type * @param parameters a <code>TypeListItem</code> containing a list of the parameter types * @return a <code>ProtoIdItem</code> for the given values, and that has been interned into * the given <code>DexFile</code> */ public static ProtoIdItem internProtoIdItem(DexFile dexFile, TypeIdItem returnType, TypeListItem parameters) { ProtoIdItem protoIdItem = new ProtoIdItem(dexFile, returnType, parameters); return dexFile.ProtoIdsSection.intern(protoIdItem); }