mirror of
https://github.com/revanced/smali.git
synced 2025-05-28 11:50:12 +02:00
Annotate CodeItems
This commit is contained in:
parent
cd12f13ffc
commit
6d607ebe1d
@ -32,12 +32,14 @@
|
||||
package org.jf.dexlib2.dexbacked.raw;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.Maps;
|
||||
import org.jf.dexlib2.AccessFlags;
|
||||
import org.jf.dexlib2.dexbacked.DexReader;
|
||||
import org.jf.dexlib2.util.AnnotatedBytes;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ClassDataItem {
|
||||
@ -159,4 +161,59 @@ public class ClassDataItem {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable public static Map<Integer, String> getCodeItemMethodMap(@Nonnull RawDexFile dexFile) {
|
||||
MapItem classDataMap = dexFile.getMapItemForSection(ItemType.CLASS_DATA_ITEM);
|
||||
if (classDataMap != null) {
|
||||
HashMap<Integer, String> codeItemMethodMap = Maps.newHashMap();
|
||||
|
||||
int classDataOffset = classDataMap.getOffset();
|
||||
|
||||
DexReader reader = dexFile.readerAt(classDataOffset);
|
||||
|
||||
for (int i=0; i<classDataMap.getItemCount(); i++) {
|
||||
int staticFieldsSize = reader.readSmallUleb128();
|
||||
int instanceFieldsSize = reader.readSmallUleb128();
|
||||
int directMethodsSize = reader.readSmallUleb128();
|
||||
int virtualMethodsSize = reader.readSmallUleb128();
|
||||
|
||||
for (int j=0; j<staticFieldsSize; j++) {
|
||||
reader.readSmallUleb128();
|
||||
reader.readSmallUleb128();
|
||||
}
|
||||
|
||||
for (int j=0; j<instanceFieldsSize; j++) {
|
||||
reader.readSmallUleb128();
|
||||
reader.readSmallUleb128();
|
||||
}
|
||||
|
||||
int previousIndex = 0;
|
||||
for (int j=0; j<directMethodsSize; j++) {
|
||||
previousIndex = handleMethod(reader, previousIndex, codeItemMethodMap);
|
||||
}
|
||||
|
||||
previousIndex = 0;
|
||||
for (int j=0; j<virtualMethodsSize; j++) {
|
||||
previousIndex = handleMethod(reader, previousIndex, codeItemMethodMap);
|
||||
}
|
||||
}
|
||||
|
||||
return codeItemMethodMap;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int handleMethod(@Nonnull DexReader reader, int previousIndex, Map<Integer, String> map) {
|
||||
int methodIndexDelta = reader.readSmallUleb128();
|
||||
int methodIndex = previousIndex + methodIndexDelta;
|
||||
String methodString = MethodIdItem.asString(reader.dexBuf, methodIndex);
|
||||
|
||||
reader.readSmallUleb128();
|
||||
|
||||
int codeOffset = reader.readSmallUleb128();
|
||||
if (codeOffset != 0) {
|
||||
map.put(codeOffset, methodString);
|
||||
}
|
||||
return methodIndex;
|
||||
}
|
||||
}
|
||||
|
380
dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/CodeItem.java
Normal file
380
dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/CodeItem.java
Normal file
@ -0,0 +1,380 @@
|
||||
/*
|
||||
* Copyright 2013, Google Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.dexlib2.dexbacked.raw;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.jf.dexlib2.dexbacked.DexReader;
|
||||
import org.jf.dexlib2.dexbacked.instruction.DexBackedInstruction;
|
||||
import org.jf.dexlib2.iface.instruction.*;
|
||||
import org.jf.dexlib2.iface.instruction.formats.*;
|
||||
import org.jf.dexlib2.util.AnnotatedBytes;
|
||||
import org.jf.dexlib2.util.ReferenceUtil;
|
||||
import org.jf.util.NumberUtils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class CodeItem {
|
||||
public static final int REGISTERS_OFFSET = 0;
|
||||
public static final int INS_OFFSET = 2;
|
||||
public static final int OUTS_OFFSET = 4;
|
||||
public static final int TRIES_OFFSET = 6;
|
||||
public static final int DEBUG_INFO_OFFSET = 8;
|
||||
public static final int INSTRUCTION_COUNT_OFFSET = 12;
|
||||
|
||||
private abstract static class CodeItemAnnotator extends SectionAnnotator {
|
||||
public CodeItemAnnotator() {
|
||||
}
|
||||
|
||||
@Nonnull @Override public String getItemName() {
|
||||
return "code_item";
|
||||
}
|
||||
|
||||
@Override public int getItemAlignment() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
@Override protected void annotateItem(@Nonnull AnnotatedBytes out, @Nonnull RawDexFile dexFile, int itemIndex) {
|
||||
DexReader reader = dexFile.readerAt(out.getCursor());
|
||||
|
||||
int registers = reader.readUshort();
|
||||
out.annotate(2, "registers_size = %d", registers);
|
||||
|
||||
int inSize = reader.readUshort();
|
||||
out.annotate(2, "ins_size = %d", inSize);
|
||||
|
||||
int outSize = reader.readUshort();
|
||||
out.annotate(2, "outs_size = %d", outSize);
|
||||
|
||||
int triesCount = reader.readUshort();
|
||||
out.annotate(2, "tries_size = %d", triesCount);
|
||||
|
||||
int debugInfoOffset = reader.readSmallUint();
|
||||
out.annotate(4, "debug_info_off = 0x%x", debugInfoOffset);
|
||||
|
||||
int instructionSize = reader.readSmallUint();
|
||||
out.annotate(4, "insns_size = 0x%x", instructionSize);
|
||||
|
||||
out.annotate(0, "instructions:");
|
||||
out.indent();
|
||||
|
||||
int end = reader.getOffset() + instructionSize*2;
|
||||
while (reader.getOffset() < end) {
|
||||
Instruction instruction = DexBackedInstruction.readFrom(reader);
|
||||
|
||||
switch (instruction.getOpcode().format) {
|
||||
case Format10x:
|
||||
annotateInstruction10x(out, instruction);
|
||||
break;
|
||||
case Format35c:
|
||||
annotateInstruction35c(out, (Instruction35c)instruction);
|
||||
break;
|
||||
case Format3rc:
|
||||
annotateInstruction3rc(out, (Instruction3rc)instruction);
|
||||
break;
|
||||
case ArrayPayload:
|
||||
annotateArrayPayload(out, (ArrayPayload)instruction);
|
||||
break;
|
||||
case PackedSwitchPayload:
|
||||
annotatePackedSwitchPayload(out, (PackedSwitchPayload)instruction);
|
||||
break;
|
||||
case SparseSwitchPayload:
|
||||
annotateSparseSwitchPayload(out, (SparseSwitchPayload)instruction);
|
||||
break;
|
||||
default:
|
||||
annotateDefaultInstruction(out, instruction);
|
||||
break;
|
||||
}
|
||||
|
||||
assert reader.getOffset() == out.getCursor();
|
||||
}
|
||||
out.deindent();
|
||||
|
||||
if (triesCount > 0) {
|
||||
if ((reader.getOffset() % 4) != 0) {
|
||||
reader.readUshort();
|
||||
out.annotate(2, "padding");
|
||||
}
|
||||
|
||||
out.annotate(0, "try_items:");
|
||||
out.indent();
|
||||
for (int i=0; i<triesCount; i++) {
|
||||
out.annotate(0, "try_item[%d]:", i);
|
||||
out.indent();
|
||||
int startAddr = reader.readSmallUint();
|
||||
out.annotate(4, "start_addr = 0x%x", startAddr);
|
||||
|
||||
int instructionCount = reader.readUshort();
|
||||
out.annotate(2, "insn_count = 0x%x", instructionCount);
|
||||
|
||||
int handlerOffset = reader.readUshort();
|
||||
out.annotate(2, "handler_off = 0x%x", handlerOffset);
|
||||
out.deindent();
|
||||
}
|
||||
out.deindent();
|
||||
|
||||
int mark = reader.getOffset();
|
||||
int handlerListCount = reader.readSmallUleb128();
|
||||
out.annotate(0, "encoded_catch_handler_list:");
|
||||
out.annotate(reader.getOffset() - mark, "size = %d", handlerListCount);
|
||||
out.indent();
|
||||
for (int i=0; i<handlerListCount; i++) {
|
||||
out.annotate(0, "encoded_catch_handler[%d]", i);
|
||||
out.indent();
|
||||
mark = reader.getOffset();
|
||||
int handlerCount = reader.readSleb128();
|
||||
out.annotate(reader.getOffset() - mark, "size = %d", handlerCount);
|
||||
boolean hasCatchAll = handlerCount <= 0;
|
||||
handlerCount = Math.abs(handlerCount);
|
||||
if (handlerCount != 0) {
|
||||
out.annotate(0, "handlers:");
|
||||
out.indent();
|
||||
for (int j=0; j<handlerCount; j++) {
|
||||
out.annotate(0, "encoded_type_addr_pair[%d]", i);
|
||||
out.indent();
|
||||
mark = reader.getOffset();
|
||||
int typeIndex = reader.readSmallUleb128();
|
||||
out.annotate(reader.getOffset() - mark,
|
||||
TypeIdItem.getReferenceAnnotation(dexFile, typeIndex));
|
||||
|
||||
mark = reader.getOffset();
|
||||
int handlerAddress = reader.readSmallUleb128();
|
||||
out.annotate(reader.getOffset() - mark, "addr = 0x%x", handlerAddress);
|
||||
out.deindent();
|
||||
}
|
||||
out.deindent();
|
||||
}
|
||||
if (hasCatchAll) {
|
||||
mark = reader.getOffset();
|
||||
int catchAllAddress = reader.readSmallUleb128();
|
||||
out.annotate(reader.getOffset() - mark, "catch_all_addr = 0x%x", catchAllAddress);
|
||||
}
|
||||
out.deindent();
|
||||
}
|
||||
out.deindent();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static void annotateInstruction10x(@Nonnull AnnotatedBytes out, @Nonnull Instruction instruction) {
|
||||
out.annotate(2, instruction.getOpcode().name);
|
||||
}
|
||||
|
||||
private static void annotateInstruction35c(@Nonnull AnnotatedBytes out, @Nonnull Instruction35c instruction) {
|
||||
List<String> args = Lists.newArrayList();
|
||||
|
||||
int registerCount = instruction.getRegisterCount();
|
||||
if (registerCount == 1) {
|
||||
args.add(formatRegister(instruction.getRegisterC()));
|
||||
} else if (registerCount == 2) {
|
||||
args.add(formatRegister(instruction.getRegisterC()));
|
||||
args.add(formatRegister(instruction.getRegisterD()));
|
||||
} else if (registerCount == 3) {
|
||||
args.add(formatRegister(instruction.getRegisterC()));
|
||||
args.add(formatRegister(instruction.getRegisterD()));
|
||||
args.add(formatRegister(instruction.getRegisterE()));
|
||||
} else if (registerCount == 4) {
|
||||
args.add(formatRegister(instruction.getRegisterC()));
|
||||
args.add(formatRegister(instruction.getRegisterD()));
|
||||
args.add(formatRegister(instruction.getRegisterE()));
|
||||
args.add(formatRegister(instruction.getRegisterF()));
|
||||
} else if (registerCount == 5) {
|
||||
args.add(formatRegister(instruction.getRegisterC()));
|
||||
args.add(formatRegister(instruction.getRegisterD()));
|
||||
args.add(formatRegister(instruction.getRegisterE()));
|
||||
args.add(formatRegister(instruction.getRegisterF()));
|
||||
args.add(formatRegister(instruction.getRegisterG()));
|
||||
}
|
||||
|
||||
String reference = ReferenceUtil.getReferenceString(instruction.getReference());
|
||||
|
||||
out.annotate(6, String.format("%s {%s}, %s",
|
||||
instruction.getOpcode().name, Joiner.on(", ").join(args), reference));
|
||||
}
|
||||
|
||||
private static void annotateInstruction3rc(@Nonnull AnnotatedBytes out, @Nonnull Instruction3rc instruction) {
|
||||
int startRegister = instruction.getStartRegister();
|
||||
int endRegister = startRegister + instruction.getRegisterCount() - 1;
|
||||
String reference = ReferenceUtil.getReferenceString(instruction.getReference());
|
||||
out.annotate(6, String.format("%s {%s .. %s}, %s",
|
||||
instruction.getOpcode().name, formatRegister(startRegister), formatRegister(endRegister),
|
||||
reference));
|
||||
}
|
||||
|
||||
private static void annotateDefaultInstruction(@Nonnull AnnotatedBytes out, @Nonnull Instruction instruction) {
|
||||
List<String> args = Lists.newArrayList();
|
||||
|
||||
if (instruction instanceof OneRegisterInstruction) {
|
||||
args.add(formatRegister(((OneRegisterInstruction)instruction).getRegisterA()));
|
||||
if (instruction instanceof TwoRegisterInstruction) {
|
||||
args.add(formatRegister(((TwoRegisterInstruction)instruction).getRegisterB()));
|
||||
if (instruction instanceof ThreeRegisterInstruction) {
|
||||
args.add(formatRegister(((ThreeRegisterInstruction)instruction).getRegisterC()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (instruction instanceof ReferenceInstruction) {
|
||||
args.add(ReferenceUtil.getReferenceString(
|
||||
((ReferenceInstruction)instruction).getReference()));
|
||||
}
|
||||
|
||||
if (instruction instanceof OffsetInstruction) {
|
||||
int offset = ((OffsetInstruction)instruction).getCodeOffset();
|
||||
String sign = offset>=0?"+":"-";
|
||||
args.add(String.format("%s0x%x", sign, offset));
|
||||
}
|
||||
|
||||
if (instruction instanceof NarrowLiteralInstruction) {
|
||||
int value = ((NarrowLiteralInstruction)instruction).getNarrowLiteral();
|
||||
if (NumberUtils.isLikelyFloat(value)) {
|
||||
args.add(String.format("%d # %f", value, Float.intBitsToFloat(value)));
|
||||
} else {
|
||||
args.add(String.format("%d", value));
|
||||
}
|
||||
} else if (instruction instanceof WideLiteralInstruction) {
|
||||
long value = ((WideLiteralInstruction)instruction).getWideLiteral();
|
||||
if (NumberUtils.isLikelyDouble(value)) {
|
||||
args.add(String.format("%d # %f", value, Double.longBitsToDouble(value)));
|
||||
} else {
|
||||
args.add(String.format("%d", value));
|
||||
}
|
||||
}
|
||||
|
||||
out.annotate(instruction.getCodeUnits()*2, "%s %s",
|
||||
instruction.getOpcode().name, Joiner.on(", ").join(args));
|
||||
}
|
||||
|
||||
private void annotateArrayPayload(@Nonnull AnnotatedBytes out, @Nonnull ArrayPayload instruction) {
|
||||
List<Number> elements = instruction.getArrayElements();
|
||||
int elementWidth = instruction.getElementWidth();
|
||||
|
||||
out.annotate(2, instruction.getOpcode().name);
|
||||
out.indent();
|
||||
out.annotate(2, "element_width = %d", elementWidth);
|
||||
out.annotate(4, "size = %d", elements.size());
|
||||
out.annotate(0, "elements:");
|
||||
out.indent();
|
||||
for (int i=0; i<elements.size(); i++) {
|
||||
if (elementWidth == 8) {
|
||||
long value = elements.get(i).longValue();
|
||||
if (NumberUtils.isLikelyDouble(value)) {
|
||||
out.annotate(elementWidth, "element[%d] = %d # %f", i, value, Double.longBitsToDouble(value));
|
||||
} else {
|
||||
out.annotate(elementWidth, "element[%d] = %d", i, value);
|
||||
}
|
||||
} else {
|
||||
int value = elements.get(i).intValue();
|
||||
if (NumberUtils.isLikelyFloat(value)) {
|
||||
out.annotate(elementWidth, "element[%d] = %d # %f", i, value, Float.intBitsToFloat(value));
|
||||
} else {
|
||||
out.annotate(elementWidth, "element[%d] = %d", i, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (out.getCursor() % 2 != 0) {
|
||||
out.annotate(1, "padding");
|
||||
}
|
||||
out.deindent();
|
||||
out.deindent();
|
||||
}
|
||||
|
||||
private void annotatePackedSwitchPayload(@Nonnull AnnotatedBytes out,
|
||||
@Nonnull PackedSwitchPayload instruction) {
|
||||
List<? extends SwitchElement> elements = instruction.getSwitchElements();
|
||||
|
||||
out.annotate(2, instruction.getOpcode().name);
|
||||
out.indent();
|
||||
|
||||
out.annotate(2, "size = %d", elements.size());
|
||||
out.annotate(4, "first_key = %d", elements.get(0).getKey());
|
||||
out.annotate(0, "targets:");
|
||||
out.indent();
|
||||
for (int i=0; i<elements.size(); i++) {
|
||||
out.annotate(4, "target[%d] = %d", i, elements.get(i).getOffset());
|
||||
}
|
||||
out.deindent();
|
||||
out.deindent();
|
||||
}
|
||||
|
||||
private void annotateSparseSwitchPayload(@Nonnull AnnotatedBytes out,
|
||||
@Nonnull SparseSwitchPayload instruction) {
|
||||
List<? extends SwitchElement> elements = instruction.getSwitchElements();
|
||||
|
||||
out.annotate(2, instruction.getOpcode().name);
|
||||
out.indent();
|
||||
out.annotate(2, "size = %d", elements.size());
|
||||
out.annotate(0, "keys:");
|
||||
out.indent();
|
||||
for (int i=0; i<elements.size(); i++) {
|
||||
out.annotate(4, "key[%d] = %d", i, elements.get(i).getKey());
|
||||
}
|
||||
out.deindent();
|
||||
out.annotate(0, "targets:");
|
||||
out.indent();
|
||||
for (int i=0; i<elements.size(); i++) {
|
||||
out.annotate(4, "target[%d] = %d", i, elements.get(i).getOffset());
|
||||
}
|
||||
out.deindent();
|
||||
out.deindent();
|
||||
}
|
||||
}
|
||||
|
||||
private static String formatRegister(int registerNum) {
|
||||
return String.format("v%d", registerNum);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static SectionAnnotator getAnnotator() {
|
||||
return new CodeItemAnnotator() {
|
||||
@Override
|
||||
public void annotateSection(@Nonnull AnnotatedBytes out, @Nonnull RawDexFile dexFile, int itemCount) {
|
||||
final Map<Integer, String> methodMap = ClassDataItem.getCodeItemMethodMap(dexFile);
|
||||
|
||||
SectionAnnotator annotator = new CodeItemAnnotator() {
|
||||
@Nullable @Override
|
||||
public String getItemIdentity(@Nonnull RawDexFile dexFile, int itemIndex, int itemOffset) {
|
||||
if (methodMap != null) {
|
||||
return methodMap.get(itemOffset);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
annotator.annotateSection(out, dexFile, itemCount);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -97,6 +97,7 @@ public class RawDexFile extends DexBackedDexFile.Impl {
|
||||
builder.put(ItemType.ANNOTATION_SET_ITEM, AnnotationSetItem.getAnnotator());
|
||||
builder.put(ItemType.ANNOTATION_ITEM, AnnotationItem.getAnnotator());
|
||||
builder.put(ItemType.CLASS_DATA_ITEM, ClassDataItem.getAnnotator());
|
||||
builder.put(ItemType.CODE_ITEM, CodeItem.getAnnotator());
|
||||
annotators = builder.build();
|
||||
}
|
||||
|
||||
|
@ -31,9 +31,10 @@
|
||||
|
||||
package org.jf.dexlib2.util;
|
||||
|
||||
import org.jf.dexlib2.iface.reference.FieldReference;
|
||||
import org.jf.dexlib2.iface.reference.MethodReference;
|
||||
import org.jf.dexlib2.iface.reference.*;
|
||||
import org.jf.util.StringUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
@ -93,5 +94,22 @@ public final class ReferenceUtil {
|
||||
writer.write(fieldReference.getType());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getReferenceString(Reference reference) {
|
||||
if (reference instanceof StringReference) {
|
||||
return String.format("\"%s\"", StringUtils.escapeString(((StringReference)reference).getString()));
|
||||
}
|
||||
if (reference instanceof TypeReference) {
|
||||
return ((TypeReference)reference).getType();
|
||||
}
|
||||
if (reference instanceof FieldReference) {
|
||||
return getFieldDescriptor((FieldReference)reference);
|
||||
}
|
||||
if (reference instanceof MethodReference) {
|
||||
return getMethodDescriptor((MethodReference)reference);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ReferenceUtil() {}
|
||||
}
|
||||
|
141
util/src/main/java/org/jf/util/NumberUtils.java
Normal file
141
util/src/main/java/org/jf/util/NumberUtils.java
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright 2013, Google Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.util;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
public class NumberUtils {
|
||||
private static final int canonicalFloatNaN = Float.floatToRawIntBits(Float.NaN);
|
||||
private static final int maxFloat = Float.floatToRawIntBits(Float.MAX_VALUE);
|
||||
private static final int piFloat = Float.floatToRawIntBits((float)Math.PI);
|
||||
private static final int eFloat = Float.floatToRawIntBits((float)Math.E);
|
||||
|
||||
private static final long canonicalDoubleNaN = Double.doubleToRawLongBits(Double.NaN);
|
||||
private static final long maxDouble = Double.doubleToLongBits(Double.MAX_VALUE);
|
||||
private static final long piDouble = Double.doubleToLongBits(Math.PI);
|
||||
private static final long eDouble = Double.doubleToLongBits(Math.E);
|
||||
|
||||
private static final DecimalFormat format = new DecimalFormat("0.####################E0");
|
||||
|
||||
public static boolean isLikelyFloat(int value) {
|
||||
// Check for some common named float values
|
||||
// We don't check for Float.MIN_VALUE, which has an integer representation of 1
|
||||
if (value == canonicalFloatNaN ||
|
||||
value == maxFloat ||
|
||||
value == piFloat ||
|
||||
value == eFloat) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for some named integer values
|
||||
if (value == Integer.MAX_VALUE || value == Integer.MIN_VALUE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Check for likely resource id
|
||||
int packageId = value >> 24;
|
||||
int resourceType = value >> 16 & 0xff;
|
||||
int resourceId = value & 0xffff;
|
||||
if ((packageId == 0x7f || packageId == 1) && resourceType < 0x1f && resourceId < 0xfff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// a non-canocical NaN is more likely to be an integer
|
||||
float floatValue = Float.intBitsToFloat(value);
|
||||
if (Float.isNaN(floatValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, whichever has a shorter scientific notation representation is more likely.
|
||||
// Integer wins the tie
|
||||
String asInt = format.format(value);
|
||||
String asFloat = format.format(floatValue);
|
||||
|
||||
// try to strip off any small imprecision near the end of the mantissa
|
||||
int decimalPoint = asFloat.indexOf('.');
|
||||
int exponent = asFloat.indexOf("E");
|
||||
int zeros = asFloat.indexOf("000");
|
||||
if (zeros > decimalPoint && zeros < exponent) {
|
||||
asFloat = asFloat.substring(0, zeros) + asFloat.substring(exponent);
|
||||
} else {
|
||||
int nines = asFloat.indexOf("999");
|
||||
if (nines > decimalPoint && nines < exponent) {
|
||||
asFloat = asFloat.substring(0, nines) + asFloat.substring(exponent);
|
||||
}
|
||||
}
|
||||
|
||||
return asFloat.length() < asInt.length();
|
||||
}
|
||||
|
||||
public static boolean isLikelyDouble(long value) {
|
||||
// Check for some common named double values
|
||||
// We don't check for Double.MIN_VALUE, which has a long representation of 1
|
||||
if (value == canonicalDoubleNaN ||
|
||||
value == maxDouble ||
|
||||
value == piDouble ||
|
||||
value == eDouble) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for some named long values
|
||||
if (value == Long.MAX_VALUE || value == Long.MIN_VALUE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// a non-canocical NaN is more likely to be an long
|
||||
double doubleValue = Double.longBitsToDouble(value);
|
||||
if (Double.isNaN(doubleValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, whichever has a shorter scientific notation representation is more likely.
|
||||
// Long wins the tie
|
||||
String asLong = format.format(value);
|
||||
String asDouble = format.format(doubleValue);
|
||||
|
||||
// try to strip off any small imprecision near the end of the mantissa
|
||||
int decimalPoint = asDouble.indexOf('.');
|
||||
int exponent = asDouble.indexOf("E");
|
||||
int zeros = asDouble.indexOf("000");
|
||||
if (zeros > decimalPoint && zeros < exponent) {
|
||||
asDouble = asDouble.substring(0, zeros) + asDouble.substring(exponent);
|
||||
} else {
|
||||
int nines = asDouble.indexOf("999");
|
||||
if (nines > decimalPoint && nines < exponent) {
|
||||
asDouble = asDouble.substring(0, nines) + asDouble.substring(exponent);
|
||||
}
|
||||
}
|
||||
|
||||
return asDouble.length() < asLong.length();
|
||||
}
|
||||
}
|
128
util/src/test/java/org/jf/util/NumberUtilsTest.java
Normal file
128
util/src/test/java/org/jf/util/NumberUtilsTest.java
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2013, Google Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.util;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class NumberUtilsTest {
|
||||
@Test
|
||||
public void isLikelyFloatTest() {
|
||||
Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(1.23f)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(1.0f)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(Float.NaN)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(Float.NEGATIVE_INFINITY)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(Float.POSITIVE_INFINITY)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(1e-30f)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(1000f)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(1f)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(-1f)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(-5f)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(1.3333f)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(4.5f)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(.1f)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(50000f)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(Float.MAX_VALUE)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits((float)Math.PI)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits((float)Math.E)));
|
||||
|
||||
Assert.assertTrue(NumberUtils.isLikelyFloat(2139095039));
|
||||
|
||||
|
||||
// Float.MIN_VALUE is equivalent to integer value 1 - this should be detected as an integer
|
||||
//Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(Float.MIN_VALUE)));
|
||||
|
||||
// This one doesn't quite work. It has a series of 2 0's, but that is probably not enough to strip off normally
|
||||
//Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(1.33333f)));
|
||||
|
||||
Assert.assertFalse(NumberUtils.isLikelyFloat(0));
|
||||
Assert.assertFalse(NumberUtils.isLikelyFloat(1));
|
||||
Assert.assertFalse(NumberUtils.isLikelyFloat(10));
|
||||
Assert.assertFalse(NumberUtils.isLikelyFloat(100));
|
||||
Assert.assertFalse(NumberUtils.isLikelyFloat(1000));
|
||||
Assert.assertFalse(NumberUtils.isLikelyFloat(1024));
|
||||
Assert.assertFalse(NumberUtils.isLikelyFloat(1234));
|
||||
Assert.assertFalse(NumberUtils.isLikelyFloat(-5));
|
||||
Assert.assertFalse(NumberUtils.isLikelyFloat(-13));
|
||||
Assert.assertFalse(NumberUtils.isLikelyFloat(-123));
|
||||
Assert.assertFalse(NumberUtils.isLikelyFloat(20000000));
|
||||
Assert.assertFalse(NumberUtils.isLikelyFloat(2000000000));
|
||||
Assert.assertFalse(NumberUtils.isLikelyFloat(-2000000000));
|
||||
Assert.assertFalse(NumberUtils.isLikelyFloat(Integer.MAX_VALUE));
|
||||
Assert.assertFalse(NumberUtils.isLikelyFloat(Integer.MIN_VALUE));
|
||||
Assert.assertFalse(NumberUtils.isLikelyFloat(Short.MIN_VALUE));
|
||||
Assert.assertFalse(NumberUtils.isLikelyFloat(Short.MAX_VALUE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isLikelyDoubleTest() {
|
||||
Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(1.23f)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(1.0f)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(Double.NaN)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(Double.NEGATIVE_INFINITY)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(Double.POSITIVE_INFINITY)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(1e-30f)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(1000f)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(1f)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(-1f)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(-5f)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(1.3333f)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(1.33333f)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(4.5f)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(.1f)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(50000f)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(Double.MAX_VALUE)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(Math.PI)));
|
||||
Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(Math.E)));
|
||||
|
||||
// Double.MIN_VALUE is equivalent to integer value 1 - this should be detected as an integer
|
||||
//Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(Double.MIN_VALUE)));
|
||||
|
||||
Assert.assertFalse(NumberUtils.isLikelyDouble(0));
|
||||
Assert.assertFalse(NumberUtils.isLikelyDouble(1));
|
||||
Assert.assertFalse(NumberUtils.isLikelyDouble(10));
|
||||
Assert.assertFalse(NumberUtils.isLikelyDouble(100));
|
||||
Assert.assertFalse(NumberUtils.isLikelyDouble(1000));
|
||||
Assert.assertFalse(NumberUtils.isLikelyDouble(1024));
|
||||
Assert.assertFalse(NumberUtils.isLikelyDouble(1234));
|
||||
Assert.assertFalse(NumberUtils.isLikelyDouble(-5));
|
||||
Assert.assertFalse(NumberUtils.isLikelyDouble(-13));
|
||||
Assert.assertFalse(NumberUtils.isLikelyDouble(-123));
|
||||
Assert.assertFalse(NumberUtils.isLikelyDouble(20000000));
|
||||
Assert.assertFalse(NumberUtils.isLikelyDouble(2000000000));
|
||||
Assert.assertFalse(NumberUtils.isLikelyDouble(-2000000000));
|
||||
Assert.assertFalse(NumberUtils.isLikelyDouble(Integer.MAX_VALUE));
|
||||
Assert.assertFalse(NumberUtils.isLikelyDouble(Integer.MIN_VALUE));
|
||||
Assert.assertFalse(NumberUtils.isLikelyDouble(Short.MIN_VALUE));
|
||||
Assert.assertFalse(NumberUtils.isLikelyDouble(Short.MAX_VALUE));
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user