From a797b6d78787d7a663d53d1046116749cba43749 Mon Sep 17 00:00:00 2001 From: REAndroid Date: Sat, 30 Nov 2024 16:40:18 +0100 Subject: [PATCH] [DEX] Simplify & clean unused code --- .../reandroid/dex/common/AnnotatedItem.java | 74 ++++++ .../dex/common/MethodHandleType.java | 13 + .../reandroid/dex/data/AnnotationElement.java | 2 +- .../reandroid/dex/data/AnnotationItem.java | 21 +- .../com/reandroid/dex/data/AnnotationSet.java | 29 ++- .../dex/data/AnnotationsDirectory.java | 13 + src/main/java/com/reandroid/dex/data/Def.java | 42 +++- .../com/reandroid/dex/data/EncodedArray.java | 15 +- .../java/com/reandroid/dex/data/FieldDef.java | 24 +- .../dex/data/IntegerDataItemList.java | 12 +- .../com/reandroid/dex/data/MethodDef.java | 4 +- .../reandroid/dex/data/MethodParameter.java | 82 ++++--- .../com/reandroid/dex/data/ShortIdList.java | 2 +- .../dex/data/StaticFieldDefArray.java | 10 +- .../java/com/reandroid/dex/id/CallSiteId.java | 4 +- .../java/com/reandroid/dex/id/ClassId.java | 55 ++++- .../dex/key/AnnotationElementKey.java | 3 +- .../reandroid/dex/key/AnnotationItemKey.java | 14 ++ .../reandroid/dex/key/AnnotationSetKey.java | 86 ++++++- .../java/com/reandroid/dex/key/ArrayKey.java | 73 ++++-- .../com/reandroid/dex/key/ArrayKeyHelper.java | 232 ++++++++++++++++++ .../com/reandroid/dex/key/ArrayValueKey.java | 215 ++++++++++++++++ .../com/reandroid/dex/key/CallSiteKey.java | 21 +- .../java/com/reandroid/dex/key/DataKey.java | 6 +- .../java/com/reandroid/dex/key/EnumKey.java | 69 ++++++ .../java/com/reandroid/dex/key/FieldKey.java | 2 +- .../java/com/reandroid/dex/key/IdKey.java | 69 ------ src/main/java/com/reandroid/dex/key/Key.java | 50 ++-- .../java/com/reandroid/dex/key/KeyList.java | 23 ++ .../reandroid/dex/key/MethodHandleKey.java | 8 +- .../java/com/reandroid/dex/key/MethodKey.java | 5 +- .../key/{NullKey.java => NullValueKey.java} | 17 +- .../com/reandroid/dex/key/PrimitiveKey.java | 105 +++----- .../java/com/reandroid/dex/key/ProtoKey.java | 7 +- .../java/com/reandroid/dex/key/StringKey.java | 12 +- .../java/com/reandroid/dex/key/TypeKey.java | 62 +---- .../com/reandroid/dex/key/TypeListKey.java | 4 +- .../reandroid/dex/model/DexAnnotation.java | 2 +- .../dex/model/DexClassRepository.java | 2 +- .../dex/model/DexMethodParameter.java | 2 +- .../dex/smali/model/SmaliAnnotationSet.java | 2 +- .../dex/smali/model/SmaliMethodParameter.java | 14 ++ .../dex/smali/model/SmaliValueArray.java | 8 +- .../dex/smali/model/SmaliValueFactory.java | 4 +- .../dex/smali/model/SmaliValueNull.java | 8 +- .../reandroid/dex/value/AnnotationValue.java | 8 +- .../com/reandroid/dex/value/ArrayValue.java | 14 +- .../com/reandroid/dex/value/BooleanValue.java | 21 +- .../com/reandroid/dex/value/ByteValue.java | 26 +- .../com/reandroid/dex/value/CharValue.java | 43 +--- .../reandroid/dex/value/DexValueBlock.java | 27 +- .../com/reandroid/dex/value/DexValueType.java | 6 +- .../com/reandroid/dex/value/DoubleValue.java | 16 +- .../com/reandroid/dex/value/EnumValue.java | 12 +- .../com/reandroid/dex/value/FloatValue.java | 15 +- .../com/reandroid/dex/value/IntValue.java | 18 +- .../com/reandroid/dex/value/LongValue.java | 17 +- .../com/reandroid/dex/value/NullValue.java | 10 +- .../dex/value/PrimitiveValueBlock.java | 20 +- .../com/reandroid/dex/value/SectionValue.java | 8 - .../com/reandroid/dex/value/ShortValue.java | 25 +- .../com/reandroid/dex/value/StringValue.java | 10 +- 62 files changed, 1199 insertions(+), 624 deletions(-) create mode 100644 src/main/java/com/reandroid/dex/common/AnnotatedItem.java create mode 100644 src/main/java/com/reandroid/dex/key/ArrayKeyHelper.java create mode 100644 src/main/java/com/reandroid/dex/key/ArrayValueKey.java create mode 100644 src/main/java/com/reandroid/dex/key/EnumKey.java delete mode 100644 src/main/java/com/reandroid/dex/key/IdKey.java rename src/main/java/com/reandroid/dex/key/{NullKey.java => NullValueKey.java} (70%) diff --git a/src/main/java/com/reandroid/dex/common/AnnotatedItem.java b/src/main/java/com/reandroid/dex/common/AnnotatedItem.java new file mode 100644 index 000000000..62b0bd92e --- /dev/null +++ b/src/main/java/com/reandroid/dex/common/AnnotatedItem.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2022 github.com/REAndroid + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.reandroid.dex.common; + +import com.reandroid.dex.key.AnnotationItemKey; +import com.reandroid.dex.key.AnnotationSetKey; +import com.reandroid.dex.key.Key; +import com.reandroid.dex.key.TypeKey; + +import java.util.Iterator; +import java.util.function.Predicate; + +public interface AnnotatedItem { + + AnnotationSetKey getAnnotation(); + void setAnnotation(AnnotationSetKey annotationSet); + void clearAnnotations(); + + default void addAnnotation(AnnotationItemKey annotation) { + AnnotationSetKey key = getAnnotation() + .remove(annotation.getType()) + .add(annotation); + setAnnotation(key); + } + default boolean removeAnnotationIf(Predicate predicate) { + AnnotationSetKey key = getAnnotation(); + AnnotationSetKey update = key.removeIf(predicate); + if (key.equals(update)) { + return false; + } + setAnnotation(update); + return true; + } + + default Iterator getAnnotations() { + return getAnnotation().iterator(); + } + default boolean hasAnnotations() { + return !getAnnotation().isEmpty(); + } + default AnnotationItemKey getAnnotation(TypeKey typeKey) { + Iterator iterator = getAnnotations(); + while (iterator.hasNext()) { + AnnotationItemKey key = iterator.next(); + if (typeKey.equals(key.getType())) { + return key; + } + } + return null; + } + default Key getAnnotationValue(TypeKey typeKey, String name) { + AnnotationItemKey annotationItemKey = getAnnotation(typeKey); + if (annotationItemKey != null) { + return annotationItemKey.get(name); + } + return null; + } + default boolean removeAnnotation(TypeKey typeKey) { + return removeAnnotationIf(key -> key.equalsType(typeKey)); + } +} diff --git a/src/main/java/com/reandroid/dex/common/MethodHandleType.java b/src/main/java/com/reandroid/dex/common/MethodHandleType.java index 218b5dc0e..d308b3807 100644 --- a/src/main/java/com/reandroid/dex/common/MethodHandleType.java +++ b/src/main/java/com/reandroid/dex/common/MethodHandleType.java @@ -133,6 +133,19 @@ public static MethodHandleType valueOf(String name) { return nameMap.get(name); } + public static boolean startsWithHandleType(SmaliReader reader) { + return get(reader) != null; + } + public static MethodHandleType get(SmaliReader reader) { + if (reader.available() < 10) { + return null; + } + int position = reader.position(); + MethodHandleType type = read(reader); + reader.position(position); + return type; + } + public static MethodHandleType read(SmaliReader reader) { reader.skipWhitespaces(); int position = reader.position(); diff --git a/src/main/java/com/reandroid/dex/data/AnnotationElement.java b/src/main/java/com/reandroid/dex/data/AnnotationElement.java index ee54388c5..6c2fdf9ee 100644 --- a/src/main/java/com/reandroid/dex/data/AnnotationElement.java +++ b/src/main/java/com/reandroid/dex/data/AnnotationElement.java @@ -174,7 +174,7 @@ public TypeKey getDataTypeKey(){ public TypeKey getParentType(){ AnnotationItem parent = getParentInstance(AnnotationItem.class); if(parent != null){ - return parent.getTypeKey(); + return parent.getType(); } return null; } diff --git a/src/main/java/com/reandroid/dex/data/AnnotationItem.java b/src/main/java/com/reandroid/dex/data/AnnotationItem.java index 8c0e7f253..e6ce70263 100644 --- a/src/main/java/com/reandroid/dex/data/AnnotationItem.java +++ b/src/main/java/com/reandroid/dex/data/AnnotationItem.java @@ -156,7 +156,7 @@ public String[] getNames(){ } @Override public AnnotationItemKey getKey(){ - return checkKey(new AnnotationItemKey(getVisibility(), getTypeKey(), getElements())); + return checkKey(new AnnotationItemKey(getVisibility(), getType(), getElements())); } @Override public void setKey(Key key){ @@ -213,14 +213,7 @@ public int getVisibilityValue(){ } return this.visibility.get(); } - public String getTypeName(){ - TypeId typeId = getTypeId(); - if(typeId != null){ - return typeId.getName(); - } - return null; - } - public TypeKey getTypeKey(){ + public TypeKey getType() { return (TypeKey) typeId.getKey(); } public TypeId getTypeId(){ @@ -239,7 +232,7 @@ public void replaceKeys(Key search, Key replace){ } } public Iterator usedIds(){ - TypeKey typeKey = getTypeKey(); + TypeKey typeKey = getType(); if(typeKey.getTypeName().startsWith("Ldalvik/annotation/")){ return EmptyIterator.of(); } @@ -263,7 +256,7 @@ public void merge(AnnotationItem annotationItem){ return; } setVisibility(annotationItem.getVisibilityValue()); - setType(annotationItem.getTypeKey()); + setType(annotationItem.getType()); annotationElements.ensureCapacity(annotationItem.getElementsCount()); for(AnnotationElement coming : annotationItem){ createNewElement().merge(coming); @@ -320,7 +313,7 @@ public boolean equals(Object obj) { return false; } AnnotationItem item = (AnnotationItem) obj; - if(!ObjectsUtil.equals(this.getTypeName(), item.getTypeName())){ + if(!ObjectsUtil.equals(this.getType(), item.getType())){ return false; } if(this.getVisibilityValue() != item.getVisibilityValue()){ @@ -333,7 +326,7 @@ public boolean equals(Object obj) { public int hashCode() { return ObjectsUtil.hash( getVisibility(), - getTypeName(), + getType(), annotationElements); } @@ -341,7 +334,7 @@ public int hashCode() { public String toString(){ StringBuilder builder = new StringBuilder(); builder.append('@'); - builder.append(getTypeName()); + builder.append(getType()); boolean appendOnce = false; for(AnnotationElement element : this){ if(appendOnce){ diff --git a/src/main/java/com/reandroid/dex/data/AnnotationSet.java b/src/main/java/com/reandroid/dex/data/AnnotationSet.java index 322d9ffbd..d19699fb7 100644 --- a/src/main/java/com/reandroid/dex/data/AnnotationSet.java +++ b/src/main/java/com/reandroid/dex/data/AnnotationSet.java @@ -28,12 +28,14 @@ import com.reandroid.dex.smali.model.SmaliAnnotationItem; import com.reandroid.dex.smali.model.SmaliAnnotationSet; import com.reandroid.dex.value.DexValueBlock; +import com.reandroid.utils.ObjectsUtil; import com.reandroid.utils.collection.CollectionUtil; import com.reandroid.utils.collection.FilterIterator; import com.reandroid.utils.collection.IterableIterator; import java.io.IOException; import java.util.Iterator; +import java.util.function.Predicate; public class AnnotationSet extends IntegerDataItemList implements KeyReference, SmaliFormat, PositionAlignedItem, FullRefresh { @@ -42,6 +44,15 @@ public AnnotationSet(){ super(SectionType.ANNOTATION_ITEM, UsageMarker.USAGE_ANNOTATION, new DexPositionAlign()); } + public boolean remove(TypeKey typeKey) { + return removeIf(item -> ObjectsUtil.equals(typeKey, item.getType())); + } + public boolean remove(AnnotationItemKey itemKey) { + return removeIf(item -> ObjectsUtil.equals(itemKey, item.getKey())); + } + public boolean removeAnnotationIf(Predicate predicate) { + return removeIf(item -> predicate.test(item.getKey())); + } @Override public boolean isBlank() { return isEmpty(); @@ -51,7 +62,7 @@ public boolean isBlank() { public AnnotationSetKey getKey() { AnnotationItemKey[] elements = new AnnotationItemKey[size()]; getItemKeys(elements); - return checkKey(new AnnotationSetKey(elements)); + return checkKey(AnnotationSetKey.create(elements)); } @Override public void setKey(Key key) { @@ -81,26 +92,18 @@ public AnnotationElement getElement(TypeKey typeKey, String name){ } public AnnotationItem get(TypeKey typeKey) { for(AnnotationItem item : this){ - if(typeKey.equals(item.getTypeKey())){ + if(typeKey.equals(item.getType())){ return item; } } return null; } public Iterator getAll(TypeKey typeKey) { - return FilterIterator.of(iterator(), item -> typeKey.equals(item.getTypeKey())); - } - public boolean contains(String typeName) { - for(AnnotationItem item : this){ - if(typeName.equals(item.getTypeName())){ - return true; - } - } - return false; + return FilterIterator.of(iterator(), item -> typeKey.equals(item.getType())); } public boolean contains(TypeKey typeKey) { for(AnnotationItem item : this){ - if(typeKey.equals(item.getTypeKey())){ + if(typeKey.equals(item.getType())){ return true; } } @@ -133,7 +136,7 @@ public AnnotationItem addNew(TypeKey type, String name){ } public AnnotationItem get(TypeKey type, String name){ for (AnnotationItem item : this) { - if (type.equals(item.getTypeKey()) + if (type.equals(item.getType()) && item.containsName(name)) { return item; } diff --git a/src/main/java/com/reandroid/dex/data/AnnotationsDirectory.java b/src/main/java/com/reandroid/dex/data/AnnotationsDirectory.java index b0557cb3d..88d7ea6f1 100644 --- a/src/main/java/com/reandroid/dex/data/AnnotationsDirectory.java +++ b/src/main/java/com/reandroid/dex/data/AnnotationsDirectory.java @@ -149,6 +149,10 @@ public void removeMethod(MethodDef def) { methodsAnnotationMap.remove(def); parametersAnnotationMap.remove(def); } + public void addAnnotation(Def def, AnnotationSetKey key){ + addAnnotation(def, getOrCreateSectionItem( + SectionType.ANNOTATION_SET, key)); + } public void addAnnotation(Def def, AnnotationSet annotationSet){ if(def instanceof FieldDef){ addFieldAnnotation((FieldDef) def, annotationSet); @@ -232,6 +236,15 @@ public AnnotationSet setParameterAnnotation(MethodDef methodDef, int parameterIn AnnotationGroup annotationGroup = getEmptyParameterAnnotationGroup(methodDef, parameterIndex); return annotationGroup.setItemKeyAt(parameterIndex, key); } + public void removeParameterAnnotation(MethodDef methodDef, int parameterIndex) { + Iterator iterator = parametersAnnotationMap.getValues(methodDef); + while (iterator.hasNext()) { + AnnotationGroup group = iterator.next(); + if (group != null) { + group.clearAt(parameterIndex); + } + } + } private AnnotationGroup getEmptyParameterAnnotationGroup(MethodDef methodDef, int parameterIndex){ Iterator iterator = parametersAnnotationMap.getValues(methodDef); while (iterator.hasNext()){ diff --git a/src/main/java/com/reandroid/dex/data/Def.java b/src/main/java/com/reandroid/dex/data/Def.java index ad12cf2ee..0d4879d5d 100644 --- a/src/main/java/com/reandroid/dex/data/Def.java +++ b/src/main/java/com/reandroid/dex/data/Def.java @@ -32,13 +32,15 @@ import com.reandroid.dex.smali.SmaliRegion; import com.reandroid.dex.smali.model.Smali; import com.reandroid.dex.smali.model.SmaliDef; +import com.reandroid.utils.ObjectsUtil; import com.reandroid.utils.collection.*; import java.io.IOException; import java.util.Iterator; public abstract class Def extends FixedDexContainerWithTool implements - IdDefinition, EditableItem, Comparable>, SmaliRegion, DefIndex, IdUsageIterator { + IdDefinition, EditableItem, Comparable>, AnnotatedItem, SmaliRegion, + DefIndex, IdUsageIterator { private final SectionType sectionType; private final Ule128Item relativeId; @@ -56,6 +58,42 @@ public Def(int childesCount, SectionType sectionType) { addChild(0, relativeId); addChild(1, accessFlags); } + @Override + public AnnotationSetKey getAnnotation() { + return AnnotationSetKey.create( + ComputeIterator.of(getAnnotationItemBlocks(), AnnotationItem::getKey)); + } + @Override + public void setAnnotation(AnnotationSetKey annotationSet) { + if (!ObjectsUtil.equals(getAnnotation(), annotationSet)) { + clearAnnotations(); + writeAnnotation(annotationSet); + } + } + @Override + public void clearAnnotations() { + writeAnnotation(AnnotationSetKey.EMPTY); + } + private boolean hasAnnotationSetBlocks() { + return getAnnotationItemBlocks().hasNext(); + } + private Iterator getAnnotationItemBlocks() { + AnnotationsDirectory directory = getAnnotationsDirectory(); + if (directory != null) { + return ExpandIterator.of(directory + .getAnnotations(this)); + } + return EmptyIterator.of(); + } + private void writeAnnotation(AnnotationSetKey key) { + if (key == null || key.isEmpty()) { + if (hasAnnotationSetBlocks()) { + getOrCreateUniqueAnnotationsDirectory().remove(this); + } + } else { + getOrCreateUniqueAnnotationsDirectory().addAnnotation(this, key); + } + } @Override public Iterator getModifiers() { @@ -195,7 +233,7 @@ public Iterator getAnnotationSets(boolean skipEmpty){ } public AnnotationsDirectory getAnnotationsDirectory(){ ClassId classId = getClassId(); - if(classId != null){ + if (classId != null) { return classId.getAnnotationsDirectory(); } return null; diff --git a/src/main/java/com/reandroid/dex/data/EncodedArray.java b/src/main/java/com/reandroid/dex/data/EncodedArray.java index 7ade19bfd..e6cc1f26b 100644 --- a/src/main/java/com/reandroid/dex/data/EncodedArray.java +++ b/src/main/java/com/reandroid/dex/data/EncodedArray.java @@ -20,7 +20,7 @@ import com.reandroid.arsc.io.BlockReader; import com.reandroid.dex.base.Ule128Item; import com.reandroid.dex.id.IdItem; -import com.reandroid.dex.key.ArrayKey; +import com.reandroid.dex.key.ArrayValueKey; import com.reandroid.dex.key.Key; import com.reandroid.dex.key.KeyReference; import com.reandroid.dex.sections.SectionType; @@ -51,20 +51,21 @@ public EncodedArray() { } @Override - public ArrayKey getKey() { + public ArrayValueKey getKey() { int size = size(); Key[] keys = new Key[size]; for (int i = 0; i < size; i++) { keys[i] = get(i).getKey(); } - return checkKey(new ArrayKey(keys)); + return checkKey(ArrayValueKey.create(keys)); } - + @Override public void setKey(Key key) { - ArrayKey arrayKey = (ArrayKey) key; + ArrayValueKey arrayKey = (ArrayValueKey) key; clear(); - for (Key k : arrayKey) { - add(k); + int size = arrayKey.size(); + for (int i = 0; i < size; i++) { + add(arrayKey.get(i)); } } diff --git a/src/main/java/com/reandroid/dex/data/FieldDef.java b/src/main/java/com/reandroid/dex/data/FieldDef.java index 59a4dc159..a9a14e90d 100644 --- a/src/main/java/com/reandroid/dex/data/FieldDef.java +++ b/src/main/java/com/reandroid/dex/data/FieldDef.java @@ -20,11 +20,7 @@ import com.reandroid.dex.common.Modifier; import com.reandroid.dex.id.FieldId; import com.reandroid.dex.id.IdItem; -import com.reandroid.dex.key.FieldKey; -import com.reandroid.dex.key.Key; -import com.reandroid.dex.key.NullKey; -import com.reandroid.dex.key.PrimitiveKey; -import com.reandroid.dex.key.TypeKey; +import com.reandroid.dex.key.*; import com.reandroid.dex.sections.SectionType; import com.reandroid.dex.smali.SmaliDirective; import com.reandroid.dex.smali.model.Smali; @@ -90,13 +86,12 @@ public void append(SmaliWriter writer) throws IOException { appendStaticValue(writer); - Iterator annotations = getAnnotationSets(true); - if(!annotations.hasNext()){ + AnnotationSetKey annotations = getAnnotation(); + if (annotations.isEmpty()) { return; } - writer.indentPlus(); - writer.appendAllWithDoubleNewLine(annotations); - writer.indentMinus(); + writer.newLine(); + annotations.append(writer); getSmaliDirective().appendEnd(writer); } private void appendStaticValue(SmaliWriter writer) throws IOException { @@ -114,7 +109,7 @@ private boolean isNonDefaultValue(Key key) { PrimitiveKey primitiveKey = (PrimitiveKey) key; return primitiveKey.getValueAsLong() != 0; } - return !(key instanceof NullKey); + return !(key instanceof NullValueKey); } public boolean isInitializedInStaticConstructor() { StaticFieldDefArray fieldDefArray = getParentInstance( @@ -153,7 +148,7 @@ public void validateStaticValue() { + SmaliWriter.toStringSafe(staticValue)); } TypeKey typeKey = fieldKey.getType(); - if (typeKey.isPrimitive() != staticValue.isPrimitiveKey()) { + if (typeKey.isPrimitive() != (staticValue instanceof PrimitiveKey)) { throw new DexException("Mismatch in type object vs primitive for value: " + SmaliWriter.toStringSafe(staticValue) + ", in field: " + fieldKey + "\n"); } @@ -186,7 +181,7 @@ public void fromSmali(Smali smali) { setAccessFlagsValue(smaliField.getAccessFlagsValue()); addHiddenApiFlags(smaliField.getHiddenApiFlags()); if(smaliField.hasAnnotation()){ - addAnnotationSet(smaliField.getAnnotationSetKey()); + setAnnotation(smaliField.getAnnotationSetKey()); } SmaliValue smaliValue = smaliField.getValue(); if(smaliValue != null) { @@ -206,6 +201,9 @@ public SmaliField toSmali() { @Override public String toString() { + return SmaliWriter.toStringSafe(this); + } + public String toString1() { FieldId fieldId = getId(); if (fieldId != null) { StringBuilder builder = new StringBuilder(); diff --git a/src/main/java/com/reandroid/dex/data/IntegerDataItemList.java b/src/main/java/com/reandroid/dex/data/IntegerDataItemList.java index d11fe2bb5..900437498 100644 --- a/src/main/java/com/reandroid/dex/data/IntegerDataItemList.java +++ b/src/main/java/com/reandroid/dex/data/IntegerDataItemList.java @@ -55,7 +55,7 @@ public IntegerDataItemList(SectionType sectionType, int usageType, DexPositio public KeyList getKey() { Key[] elements = new Key[size()]; getItemKeys(elements); - return new ArrayKey(elements); + return ArrayKey.create(elements); } public void setKey(Key key) { KeyList keyList = (KeyList) key; @@ -114,6 +114,12 @@ public T setItemKeyAt(int index, Key key) { reference.setKey(key); return reference.getItem(); } + public void clearAt(int index) { + IntegerDataReference reference = getReference(index); + if (reference != null) { + reference.setItem(null); + } + } void getItemKeys(Key[] out) { int length = out.length; for (int i = 0; i < length; i++) { @@ -130,8 +136,8 @@ public void removeSelf() { public void remove(T item) { removeIf(t -> t == item); } - public void removeIf(Predicate filter) { - referenceList.removeIf(reference -> filter.test(reference.getItem())); + public boolean removeIf(Predicate filter) { + return referenceList.removeIf(reference -> filter.test(reference.getItem())); } void removeNulls() { removeIf(item -> item == null); diff --git a/src/main/java/com/reandroid/dex/data/MethodDef.java b/src/main/java/com/reandroid/dex/data/MethodDef.java index cfd311e11..1906f4c6b 100644 --- a/src/main/java/com/reandroid/dex/data/MethodDef.java +++ b/src/main/java/com/reandroid/dex/data/MethodDef.java @@ -135,7 +135,7 @@ public void removeParameter(int index){ if(parameter == null){ return; } - parameter.removeSelf(); + parameter.onRemoved(); MethodKey methodKey = getKey(); if(methodKey == null){ return; @@ -362,7 +362,7 @@ public void fromSmali(Smali smali) { getOrCreateCodeItem().fromSmali(smaliMethod); } if(smaliMethod.hasAnnotation()){ - addAnnotationSet(smaliMethod.getAnnotationSetKey()); + setAnnotation(smaliMethod.getAnnotationSetKey()); } Iterator iterator = smaliMethod.getParameters(); while (iterator.hasNext()){ diff --git a/src/main/java/com/reandroid/dex/data/MethodParameter.java b/src/main/java/com/reandroid/dex/data/MethodParameter.java index 75284928f..92151495a 100644 --- a/src/main/java/com/reandroid/dex/data/MethodParameter.java +++ b/src/main/java/com/reandroid/dex/data/MethodParameter.java @@ -15,25 +15,26 @@ */ package com.reandroid.dex.data; +import com.reandroid.dex.common.AnnotatedItem; import com.reandroid.dex.debug.DebugParameter; import com.reandroid.dex.id.ProtoId; import com.reandroid.dex.id.TypeId; import com.reandroid.dex.key.AnnotationSetKey; import com.reandroid.dex.key.Key; import com.reandroid.dex.key.TypeKey; -import com.reandroid.dex.sections.SectionType; import com.reandroid.dex.smali.SmaliDirective; import com.reandroid.dex.smali.SmaliRegion; import com.reandroid.dex.smali.SmaliWriter; import com.reandroid.dex.smali.model.SmaliMethodParameter; import com.reandroid.utils.StringsUtil; +import com.reandroid.utils.collection.ComputeIterator; import com.reandroid.utils.collection.EmptyIterator; import com.reandroid.utils.collection.ExpandIterator; import java.io.IOException; import java.util.Iterator; -public class MethodParameter implements DefIndex, SmaliRegion { +public class MethodParameter implements DefIndex, AnnotatedItem, SmaliRegion { private final MethodDef methodDef; private final int index; @@ -43,47 +44,42 @@ public MethodParameter(MethodDef methodDef, int index) { this.index = index; } - public void removeSelf() { + @Override + public AnnotationSetKey getAnnotation() { + return AnnotationSetKey.create( + ComputeIterator.of(getAnnotationItemBlocks(), AnnotationItem::getKey)); + } + @Override + public void setAnnotation(AnnotationSetKey annotationSet) { clearAnnotations(); - clearDebugParameter(); + writeAnnotation(annotationSet); } - + @Override public void clearAnnotations() { - MethodDef methodDef = getMethodDef(); - AnnotationsDirectory directory = methodDef.getUniqueAnnotationsDirectory(); - if (directory == null || !hasAnnotationSetBlocks()) { - return; - } - Iterator> iterator = - directory.getParameterEntries(methodDef); - int index = getDefinitionIndex(); - while (iterator.hasNext()) { - DirectoryEntry entry = iterator.next(); - AnnotationGroup group = entry.getValue(); - if (group == null || group.getItem(index) == null) { - continue; - } - AnnotationGroup update = group.getSection(SectionType.ANNOTATION_GROUP) - .createItem(); - entry.setValue(update); - update.setItemKeyAt(index, null); - update.refresh(); - } + writeAnnotation(AnnotationSetKey.EMPTY); } - public boolean hasAnnotationSetBlocks() { - return getAnnotationSetBlocks().hasNext(); + public void onRemoved() { + clearAnnotations(); + clearDebugParameter(); } - public Iterator getAnnotationItemBlocks() { - return ExpandIterator.of(getAnnotationSetBlocks()); + private boolean hasAnnotationSetBlocks() { + MethodDef methodDef = getMethodDef(); + AnnotationsDirectory directory = methodDef.getAnnotationsDirectory(); + if (directory != null) { + return directory.getParameterAnnotation(methodDef, + getDefinitionIndex()).hasNext(); + } + return false; } - public Iterator getAnnotationSetBlocks() { + public Iterator getAnnotationItemBlocks() { MethodDef methodDef = getMethodDef(); AnnotationsDirectory directory = methodDef.getAnnotationsDirectory(); if (directory != null) { - return directory.getParameterAnnotation(methodDef, getDefinitionIndex()); + return ExpandIterator.of(directory + .getParameterAnnotation(methodDef, getDefinitionIndex())); } return EmptyIterator.of(); } @@ -96,16 +92,24 @@ public AnnotationItem getOrCreateAnnotationItemBlock(TypeKey typeKey) { return getOrCreateAnnotationSet().getOrCreate(typeKey); } + public AnnotationSet getOrCreateAnnotationSet() { MethodDef methodDef = getMethodDef(); AnnotationsDirectory directory = methodDef.getOrCreateUniqueAnnotationsDirectory(); return directory.getOrCreateParameterAnnotation(methodDef, getDefinitionIndex()); } - public AnnotationSet setAnnotationSetBlock(AnnotationSetKey key) { + private void writeAnnotation(AnnotationSetKey key) { MethodDef methodDef = getMethodDef(); - AnnotationsDirectory directory = methodDef.getOrCreateUniqueAnnotationsDirectory(); - return directory.setParameterAnnotation(methodDef, getDefinitionIndex(), key); + if (key == null || key.isEmpty()) { + if (hasAnnotationSetBlocks()) { + AnnotationsDirectory directory = methodDef.getOrCreateUniqueAnnotationsDirectory(); + directory.removeParameterAnnotation(methodDef, getDefinitionIndex()); + } + } else { + AnnotationsDirectory directory = methodDef.getOrCreateUniqueAnnotationsDirectory(); + directory.setParameterAnnotation(methodDef, getDefinitionIndex(), key); + } } public TypeKey getType() { @@ -199,7 +203,7 @@ public Key getKey() { public void fromSmali(SmaliMethodParameter smaliMethodParameter) { if (smaliMethodParameter.hasAnnotations()) { - setAnnotationSetBlock(smaliMethodParameter.getAnnotationSet().getKey()); + setAnnotation(smaliMethodParameter.getAnnotationSet().getKey()); } setDebugName(smaliMethodParameter.getName()); } @@ -209,7 +213,7 @@ public boolean isEmpty() { if (debugParameter != null && debugParameter.getNameId() != null) { return false; } - return !getAnnotationItemBlocks().hasNext(); + return !hasAnnotations(); } @Override @@ -217,8 +221,8 @@ public void append(SmaliWriter writer) throws IOException { DebugParameter debugParameter = getDebugParameter(); boolean has_debug = debugParameter != null && debugParameter.getNameId() != null; - Iterator annotations = getAnnotationSetBlocks(); - boolean has_annotation = annotations.hasNext(); + AnnotationSetKey annotation = getAnnotation(); + boolean has_annotation = !annotation.isEmpty(); if (!has_debug && !has_annotation) { return; } @@ -233,7 +237,7 @@ public void append(SmaliWriter writer) throws IOException { return; } writer.indentPlus(); - writer.appendAllWithDoubleNewLine(annotations); + writer.appendAllWithDoubleNewLine(annotation.iterator()); writer.indentMinus(); getSmaliDirective().appendEnd(writer); } diff --git a/src/main/java/com/reandroid/dex/data/ShortIdList.java b/src/main/java/com/reandroid/dex/data/ShortIdList.java index 22ebb3de5..1cd436ff6 100644 --- a/src/main/java/com/reandroid/dex/data/ShortIdList.java +++ b/src/main/java/com/reandroid/dex/data/ShortIdList.java @@ -60,7 +60,7 @@ public ShortIdList(SectionType sectionType, int usageType) { public KeyList getKey() { Key[] elements = new Key[size()]; getItemKeys(elements); - return new ArrayKey(elements); + return ArrayKey.create(elements); } public void setKey(Key key) { KeyList old = getKey(); diff --git a/src/main/java/com/reandroid/dex/data/StaticFieldDefArray.java b/src/main/java/com/reandroid/dex/data/StaticFieldDefArray.java index c2ef57d87..6cd98b41a 100644 --- a/src/main/java/com/reandroid/dex/data/StaticFieldDefArray.java +++ b/src/main/java/com/reandroid/dex/data/StaticFieldDefArray.java @@ -20,7 +20,7 @@ import com.reandroid.dex.id.ClassId; import com.reandroid.dex.ins.Ins; import com.reandroid.dex.ins.SizeXIns; -import com.reandroid.dex.key.ArrayKey; +import com.reandroid.dex.key.ArrayValueKey; import com.reandroid.dex.key.FieldKey; import com.reandroid.dex.key.Key; import com.reandroid.dex.key.MethodKey; @@ -122,14 +122,14 @@ private void releaseStaticValues(Object lock) { if (classId == null) { return; } - ArrayKey cached = buildCachedKey(); - ArrayKey encoded = classId.getStaticValues(); + ArrayValueKey cached = buildCachedKey(); + ArrayValueKey encoded = classId.getStaticValues(); if (!ObjectsUtil.equals(cached, encoded)) { classId.setStaticValues(cached); } mLockedBy = null; } - private ArrayKey buildCachedKey() { + private ArrayValueKey buildCachedKey() { int lastIndex = lastCachedKeyIndex(); if (lastIndex < 0) { return null; @@ -145,7 +145,7 @@ private ArrayKey buildCachedKey() { elements[i] = key; fieldDef.cachedStaticValue(null); } - return new ArrayKey(elements); + return ArrayValueKey.create(elements); } private int lastCachedKeyIndex() { int result = -1; diff --git a/src/main/java/com/reandroid/dex/id/CallSiteId.java b/src/main/java/com/reandroid/dex/id/CallSiteId.java index 39cf14f1b..7164c8e3e 100644 --- a/src/main/java/com/reandroid/dex/id/CallSiteId.java +++ b/src/main/java/com/reandroid/dex/id/CallSiteId.java @@ -103,7 +103,7 @@ public ArrayKey getArguments() { for (int i = 0; i < size; i++) { results[i] = getArgument(i); } - return new ArrayKey(results); + return ArrayKey.create(results); } public Iterator> getArgumentValues() { EncodedArray encodedArray = getEncodedArray(); @@ -138,7 +138,7 @@ public int getArgumentsSize() { } return 0; } - public void setArguments(ArrayKey key) { + public void setArguments(ArrayValueKey key) { if (!key.equals(getArguments())) { setKey(getKey().changeArguments(key)); } diff --git a/src/main/java/com/reandroid/dex/id/ClassId.java b/src/main/java/com/reandroid/dex/id/ClassId.java index f8491615c..0eed2c6b2 100644 --- a/src/main/java/com/reandroid/dex/id/ClassId.java +++ b/src/main/java/com/reandroid/dex/id/ClassId.java @@ -35,7 +35,7 @@ import java.util.Iterator; import java.util.Objects; -public class ClassId extends IdItem implements IdDefinition, Comparable { +public class ClassId extends IdItem implements IdDefinition, AnnotatedItem, Comparable { private final ClassTypeId classTypeId; private final IndirectInteger accessFlagValue; @@ -93,6 +93,45 @@ public void setKey(Key key){ this.classTypeId.setKey(key); keyChanged(old); } + + @Override + public AnnotationSetKey getAnnotation() { + return AnnotationSetKey.create( + ComputeIterator.of(getAnnotationItemBlocks(), AnnotationItem::getKey)); + } + @Override + public void setAnnotation(AnnotationSetKey annotationSet) { + clearAnnotations(); + writeAnnotation(annotationSet); + } + @Override + public void clearAnnotations() { + writeAnnotation(AnnotationSetKey.EMPTY); + } + private boolean hasAnnotationSetBlocks() { + return getAnnotationItemBlocks().hasNext(); + } + private Iterator getAnnotationItemBlocks() { + AnnotationsDirectory directory = getAnnotationsDirectory(); + if (directory != null) { + AnnotationSet annotationSet = directory.getClassAnnotations(); + if (annotationSet != null) { + return annotationSet.iterator(); + } + } + return EmptyIterator.of(); + } + private void writeAnnotation(AnnotationSetKey key) { + if (key == null || key.isEmpty()) { + if (hasAnnotationSetBlocks()) { + AnnotationsDirectory directory = getOrCreateUniqueAnnotationsDirectory(); + directory.setClassAnnotations((AnnotationSetKey )null); + } + } else { + AnnotationsDirectory directory = getOrCreateUniqueAnnotationsDirectory(); + directory.setClassAnnotations(key); + } + } public String getName(){ TypeId typeId = getId(); if(typeId != null){ @@ -384,10 +423,10 @@ public EncodedArray getStaticValuesEncodedArray(){ } return encodedArray; } - public ArrayKey getStaticValues() { - return (ArrayKey) staticValues.getKey(); + public ArrayValueKey getStaticValues() { + return (ArrayValueKey) staticValues.getKey(); } - public void setStaticValues(ArrayKey staticValues){ + public void setStaticValues(ArrayValueKey staticValues){ this.staticValues.setKey(staticValues); this.staticValues.addUniqueUser(this); } @@ -521,7 +560,7 @@ public void fromSmali(SmaliClass smaliClass) throws IOException { getOrCreateClassData().fromSmali(smaliClass); } if(smaliClass.hasAnnotation()) { - setClassAnnotations(smaliClass.getAnnotationSetKey()); + setAnnotation(smaliClass.getAnnotationSetKey()); } } public SmaliClass toSmali() { @@ -549,12 +588,12 @@ public void append(SmaliWriter writer) throws IOException { if(interfaces != null){ interfaces.appendInterfaces(writer); } - AnnotationSet annotationSet = getClassAnnotations(); - if(annotationSet != null){ + AnnotationSetKey annotation = getAnnotation(); + if(!annotation.isEmpty()){ writer.newLine(); writer.newLine(); writer.appendComment("annotations"); - annotationSet.append(writer); + writer.appendAllWithDoubleNewLine(annotation.iterator()); writer.newLine(); } ClassData classData = getClassData(); diff --git a/src/main/java/com/reandroid/dex/key/AnnotationElementKey.java b/src/main/java/com/reandroid/dex/key/AnnotationElementKey.java index 5e4a5d1a2..b357840c6 100644 --- a/src/main/java/com/reandroid/dex/key/AnnotationElementKey.java +++ b/src/main/java/com/reandroid/dex/key/AnnotationElementKey.java @@ -18,6 +18,7 @@ import com.reandroid.dex.smali.SmaliWriter; import com.reandroid.utils.CompareUtil; import com.reandroid.utils.ObjectsUtil; +import com.reandroid.utils.StringsUtil; import com.reandroid.utils.collection.CombiningIterator; import com.reandroid.utils.collection.EmptyIterator; import com.reandroid.utils.collection.SingleIterator; @@ -100,7 +101,7 @@ public int compareTo(Object obj) { return 0; } if (!(obj instanceof AnnotationElementKey)) { - return -1; + return StringsUtil.compareToString(this, obj); } AnnotationElementKey elementKey = (AnnotationElementKey) obj; int i = CompareUtil.compare(getName(), elementKey.getName()); diff --git a/src/main/java/com/reandroid/dex/key/AnnotationItemKey.java b/src/main/java/com/reandroid/dex/key/AnnotationItemKey.java index ff6284e20..e4dd1e6d7 100644 --- a/src/main/java/com/reandroid/dex/key/AnnotationItemKey.java +++ b/src/main/java/com/reandroid/dex/key/AnnotationItemKey.java @@ -17,6 +17,7 @@ import com.reandroid.dex.common.AnnotationVisibility; import com.reandroid.dex.smali.SmaliDirective; +import com.reandroid.dex.smali.SmaliReader; import com.reandroid.dex.smali.SmaliWriter; import com.reandroid.utils.CompareUtil; import com.reandroid.utils.ObjectsUtil; @@ -174,6 +175,9 @@ int computeHash() { return ObjectsUtil.hash(getVisibility(), getType()) * 31 + super.computeHash(); } + public boolean equalsType(TypeKey typeKey) { + return ObjectsUtil.equals(getType(), typeKey); + } @Override public boolean equals(Object obj) { if (this == obj) { @@ -194,6 +198,16 @@ public int hashCode() { return getHashCode(); } + + public static AnnotationItemKey read(SmaliReader reader) throws IOException { + //FIXME + throw new RuntimeException("AnnotationItemKey.read not implemented"); + } + public static AnnotationItemKey parse(String text) { + //FIXME + throw new RuntimeException("AnnotationItemKey.parse not implemented"); + } + private static AnnotationElementKey[] removeNulls(AnnotationElementKey[] elements) { if (elements == null || elements.length == 0) { return EMPTY; diff --git a/src/main/java/com/reandroid/dex/key/AnnotationSetKey.java b/src/main/java/com/reandroid/dex/key/AnnotationSetKey.java index 53f3f281b..936b6e5da 100644 --- a/src/main/java/com/reandroid/dex/key/AnnotationSetKey.java +++ b/src/main/java/com/reandroid/dex/key/AnnotationSetKey.java @@ -15,18 +15,34 @@ */ package com.reandroid.dex.key; +import com.reandroid.dex.smali.SmaliWriter; import com.reandroid.utils.CompareUtil; import com.reandroid.utils.ObjectsUtil; +import com.reandroid.utils.collection.ArrayCollection; import com.reandroid.utils.collection.ArraySort; +import com.reandroid.utils.collection.ComputeIterator; +import com.reandroid.utils.collection.SingleIterator; +import java.io.IOException; +import java.util.Iterator; import java.util.function.Predicate; - public class AnnotationSetKey extends KeyList implements Key { - private static final AnnotationItemKey[] EMPTY = new AnnotationItemKey[0]; + private static final AnnotationItemKey[] EMPTY_ARRAY; + public static final AnnotationSetKey EMPTY; + + static { + AnnotationItemKey[] emptyArray = new AnnotationItemKey[0]; + EMPTY_ARRAY = emptyArray; + EMPTY = new AnnotationSetKey(emptyArray, false); + } + + private AnnotationSetKey(AnnotationItemKey[] elements, boolean unused) { + super(elements); + } - public AnnotationSetKey(AnnotationItemKey[] elements) { + private AnnotationSetKey(AnnotationItemKey[] elements) { super(removeNulls(elements)); } @@ -57,19 +73,26 @@ public AnnotationSetKey remove(int index) { public AnnotationSetKey removeIf(Predicate predicate) { return (AnnotationSetKey) super.removeIf(predicate); } + public AnnotationSetKey remove(TypeKey typeKey) { + return removeIf(item -> item.equalsType(typeKey)); + } @Override public AnnotationSetKey set(int i, AnnotationItemKey item) { return (AnnotationSetKey) super.set(i, item); } + @Override + public AnnotationSetKey sorted() { + return (AnnotationSetKey) super.sorted(); + } @Override AnnotationSetKey newInstance(AnnotationItemKey[] elements) { - return new AnnotationSetKey(elements); + return create(elements); } @Override AnnotationItemKey[] newArray(int length) { if (length == 0) { - return EMPTY; + return EMPTY_ARRAY; } return new AnnotationItemKey[length]; } @@ -101,13 +124,20 @@ public AnnotationSetKey replaceKey(Key search, Key replace) { return (AnnotationSetKey) super.replaceKey(search, replace); } + @Override + public void append(SmaliWriter writer) throws IOException { + writer.indentPlus(); + super.append(writer); + writer.indentMinus(); + } + @Override public int compareTo(Object obj) { if (obj == this) { return 0; } if (!(obj instanceof AnnotationSetKey)) { - return -1; + return 0; } return compareElements((AnnotationSetKey) obj); } @@ -128,10 +158,50 @@ public int hashCode() { return getHashCode(); } - private static AnnotationItemKey[] removeNulls(AnnotationItemKey[] elements) { + public static AnnotationSetKey create(AnnotationItemKey[] elements) { if (elements == null || elements.length == 0) { return EMPTY; } + elements = removeNulls(elements); + if (elements.length == 0) { + return EMPTY; + } + return new AnnotationSetKey(elements); + } + public static AnnotationSetKey create(Iterator iterator) { + ArrayCollection elements = null; + while (iterator.hasNext()) { + AnnotationItemKey key = iterator.next(); + if (elements == null) { + elements = new ArrayCollection<>(); + } + elements.add(key); + } + if (elements == null) { + return create((AnnotationItemKey[]) null); + } + return create(elements.toArrayFill(new AnnotationItemKey[elements.size()])); + } + public static AnnotationSetKey combined(Iterator iterator) { + ArrayCollection elements = null; + while (iterator.hasNext()) { + AnnotationSetKey key = iterator.next(); + if (!key.isEmpty()) { + if (elements == null) { + elements = new ArrayCollection<>(); + } + elements.addAll(key.iterator()); + } + } + if (elements == null) { + return create((AnnotationItemKey[]) null); + } + return create(elements.toArrayFill(new AnnotationItemKey[elements.size()])); + } + private static AnnotationItemKey[] removeNulls(AnnotationItemKey[] elements) { + if (elements == null || elements.length == 0) { + return EMPTY_ARRAY; + } int length = elements.length; int size = 0; for (int i = 0; i < length; i ++) { @@ -144,7 +214,7 @@ private static AnnotationItemKey[] removeNulls(AnnotationItemKey[] elements) { return elements; } if (size == 0) { - return EMPTY; + return EMPTY_ARRAY; } AnnotationItemKey[] results = new AnnotationItemKey[size]; int j = 0; diff --git a/src/main/java/com/reandroid/dex/key/ArrayKey.java b/src/main/java/com/reandroid/dex/key/ArrayKey.java index 9b56f4511..82790ce7d 100644 --- a/src/main/java/com/reandroid/dex/key/ArrayKey.java +++ b/src/main/java/com/reandroid/dex/key/ArrayKey.java @@ -15,24 +15,34 @@ */ package com.reandroid.dex.key; +import com.reandroid.dex.common.MethodHandleType; +import com.reandroid.dex.smali.SmaliDirective; import com.reandroid.dex.smali.SmaliParseException; import com.reandroid.dex.smali.SmaliReader; import com.reandroid.dex.smali.SmaliWriter; +import com.reandroid.utils.StringsUtil; import com.reandroid.utils.collection.ArrayCollection; import java.io.IOException; import java.io.StringWriter; -import java.util.List; import java.util.function.Predicate; public class ArrayKey extends KeyList { - private static final Key[] EMPTY = new Key[0]; + public static final ArrayKey EMPTY; + static final Key[] EMPTY_ARRAY; - public ArrayKey(Key[] elements) { - super(checkNullOrEmpty(elements)); + static { + Key[] emptyArray = new Key[0]; + EMPTY_ARRAY = emptyArray; + EMPTY = new ArrayKey(emptyArray); } + ArrayKey(Key[] elements) { + super(elements); + } + + @Override public ArrayKey add(Key item) { return (ArrayKey) super.add(item); @@ -56,12 +66,12 @@ public ArrayKey set(int i, Key item) { @Override ArrayKey newInstance(Key[] elements) { - return new ArrayKey(elements); + return create(elements); } @Override Key[] newArray(int length) { if (length == 0) { - return EMPTY; + return EMPTY_ARRAY; } return new Key[length]; } @@ -111,7 +121,7 @@ public int compareTo(Object obj) { return 0; } if (!(obj instanceof ArrayKey)) { - return 0; + return StringsUtil.compareToString(this, obj); } return compareElements((ArrayKey) obj); } @@ -132,13 +142,22 @@ public boolean equals(Object obj) { return equalsElements((ArrayKey) obj); } + public static ArrayKey create(Key ... elements) { + if (elements == null || elements.length == 0) { + return EMPTY; + } + return new ArrayKey(elements); + } public static ArrayKey read(SmaliReader reader, char end) throws IOException { + return create(readElements(reader, end)); + } + public static Key[] readElements(SmaliReader reader, char end) throws IOException { reader.skipWhitespacesOrComment(); if (reader.getASCII(reader.position()) == end) { reader.readASCII(); - return new ArrayKey(new Key[0]); + return EMPTY_ARRAY; } - List results = new ArrayCollection<>(); + ArrayCollection results = new ArrayCollection<>(); while (true) { Key key = readNext(reader); results.add(key); @@ -149,7 +168,7 @@ public static ArrayKey read(SmaliReader reader, char end) throws IOException { SmaliParseException.expect(reader, ','); } SmaliParseException.expect(reader, end); - return new ArrayKey(results.toArray(new Key[results.size()])); + return results.toArrayFill(new Key[results.size()]); } private static Key readNext(SmaliReader reader) throws IOException { reader.skipWhitespacesOrComment(); @@ -184,13 +203,35 @@ private static Key readNext(SmaliReader reader) throws IOException { } return FieldKey.read(reader); } - return StringKey.read(reader); - } + if (c == '{') { + return ArrayValueKey.read(reader); + } + if (c == '.') { + SmaliDirective directive = SmaliDirective.parse(reader, false); + if (directive == SmaliDirective.SUB_ANNOTATION) { + return AnnotationItemKey.read(reader); + } + if (directive == SmaliDirective.ENUM) { + return EnumKey.read(reader); + } + throw new SmaliParseException("Unexpected value ", reader); + } + if (c == 'n') { + return NullValueKey.read(reader); + } + if (MethodHandleType.startsWithHandleType(reader)) { + return MethodHandleKey.read(reader); + } - private static Key[] checkNullOrEmpty(Key[] elements) { - if (elements == null || elements.length == 0) { - return EMPTY; + PrimitiveKey primitiveKey = PrimitiveKey.readSafe(reader); + if (primitiveKey != null) { + return primitiveKey; } - return elements; + throw new SmaliParseException("Unexpected value ", reader); + } + + public static ArrayKey parse(String text) { + //FIXME + throw new RuntimeException("ArrayKey.parse not implemented"); } } diff --git a/src/main/java/com/reandroid/dex/key/ArrayKeyHelper.java b/src/main/java/com/reandroid/dex/key/ArrayKeyHelper.java new file mode 100644 index 000000000..acb33430d --- /dev/null +++ b/src/main/java/com/reandroid/dex/key/ArrayKeyHelper.java @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2022 github.com/REAndroid + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.reandroid.dex.key; + +class ArrayKeyHelper { + + public static boolean isAllTypeOf(Class keyClass, ArrayKey arrayKey) { + int size = arrayKey.size(); + if (size == 0) { return false; } + for (int i = 0; i < size; i++) { + if (!keyClass.isInstance(arrayKey.get(i))) { + return false; + } + } + return true; + } + public static Key[] toStringKeys(String[] values) { + if (values == null) { + return null; + } + int length = values.length; + if (length == 0) { + return null; + } + Key[] results = new Key[length]; + for (int i = 0; i < length; i++) { + results[i] = StringKey.create(values[i]); + } + return results; + } + + public static Key[] toPrimitiveKeys(byte[] values) { + if (values == null) { return null; } + int length = values.length; + if (length == 0) { return null; } + Key[] results = new Key[length]; + for (int i = 0; i < length; i++) { + results[i] = PrimitiveKey.of(values[i]); + } + return results; + } + + public static Key[] toPrimitiveKeys(short[] values) { + if (values == null) { return null; } + int length = values.length; + if (length == 0) { return null; } + Key[] results = new Key[length]; + for (int i = 0; i < length; i++) { + results[i] = PrimitiveKey.of(values[i]); + } + return results; + } + + public static Key[] toPrimitiveKeys(int[] values) { + if (values == null) { return null; } + int length = values.length; + if (length == 0) { return null; } + Key[] results = new Key[length]; + for (int i = 0; i < length; i++) { + results[i] = PrimitiveKey.of(values[i]); + } + return results; + } + + public static Key[] toPrimitiveKeys(long[] values) { + if (values == null) { return null; } + int length = values.length; + if (length == 0) { return null; } + Key[] results = new Key[length]; + for (int i = 0; i < length; i++) { + results[i] = PrimitiveKey.of(values[i]); + } + return results; + } + + public static Key[] toPrimitiveKeys(float[] values) { + if (values == null) { return null; } + int length = values.length; + if (length == 0) { return null; } + Key[] results = new Key[length]; + for (int i = 0; i < length; i++) { + results[i] = PrimitiveKey.of(values[i]); + } + return results; + } + + public static Key[] toPrimitiveKeys(double[] values) { + if (values == null) { return null; } + int length = values.length; + if (length == 0) { return null; } + Key[] results = new Key[length]; + for (int i = 0; i < length; i++) { + results[i] = PrimitiveKey.of(values[i]); + } + return results; + } + + public static Key[] toPrimitiveKeys(char[] values) { + if (values == null) { return null; } + int length = values.length; + if (length == 0) { return null; } + Key[] results = new Key[length]; + for (int i = 0; i < length; i++) { + results[i] = PrimitiveKey.of(values[i]); + } + return results; + } + + public static Key[] toPrimitiveKeys(boolean[] values) { + if (values == null) { return null; } + int length = values.length; + if (length == 0) { return null; } + Key[] results = new Key[length]; + for (int i = 0; i < length; i++) { + results[i] = PrimitiveKey.of(values[i]); + } + return results; + } + + public static String[] toStringValues(ArrayKey arrayKey) { + if (!isAllTypeOf(StringKey.class, arrayKey)) { return null; } + int size = arrayKey.size(); + String[] results = new String[size]; + for (int i = 0; i < size; i++) { + results[i] = ((StringKey) arrayKey.get(i)).getString(); + } + return results; + } + + public static byte[] toByteValues(ArrayKey arrayKey) { + if (!isAllTypeOf(PrimitiveKey.ByteKey.class, arrayKey)) { return null; } + int size = arrayKey.size(); + byte[] results = new byte[size]; + for (int i = 0; i < size; i++) { + results[i] = ((PrimitiveKey.ByteKey) arrayKey.get(i)).value(); + } + return results; + } + + public static short[] toShortValues(ArrayKey arrayKey) { + if (!isAllTypeOf(PrimitiveKey.ShortKey.class, arrayKey)) { return null; } + int size = arrayKey.size(); + short[] results = new short[size]; + for (int i = 0; i < size; i++) { + results[i] = ((PrimitiveKey.ShortKey) arrayKey.get(i)).value(); + } + return results; + } + + public static int[] toIntValues(ArrayKey arrayKey) { + if (!isAllTypeOf(PrimitiveKey.IntegerKey.class, arrayKey)) { return null; } + int size = arrayKey.size(); + int[] results = new int[size]; + for (int i = 0; i < size; i++) { + results[i] = ((PrimitiveKey.IntegerKey) arrayKey.get(i)).value(); + } + return results; + } + + public static long[] toLongValues(ArrayKey arrayKey) { + if (!isAllTypeOf(PrimitiveKey.LongKey.class, arrayKey)) { return null; } + int size = arrayKey.size(); + long[] results = new long[size]; + for (int i = 0; i < size; i++) { + results[i] = ((PrimitiveKey.LongKey) arrayKey.get(i)).value(); + } + return results; + } + + public static float[] toFloatValues(ArrayKey arrayKey) { + if (!isAllTypeOf(PrimitiveKey.FloatKey.class, arrayKey)) { return null; } + int size = arrayKey.size(); + float[] results = new float[size]; + for (int i = 0; i < size; i++) { + results[i] = ((PrimitiveKey.FloatKey) arrayKey.get(i)).value(); + } + return results; + } + + public static double[] toDoubleValues(ArrayKey arrayKey) { + if (!isAllTypeOf(PrimitiveKey.DoubleKey.class, arrayKey)) { return null; } + int size = arrayKey.size(); + double[] results = new double[size]; + for (int i = 0; i < size; i++) { + results[i] = ((PrimitiveKey.DoubleKey) arrayKey.get(i)).value(); + } + return results; + } + + public static char[] toCharValues(ArrayKey arrayKey) { + if (!isAllTypeOf(PrimitiveKey.CharKey.class, arrayKey)) { return null; } + int size = arrayKey.size(); + char[] results = new char[size]; + for (int i = 0; i < size; i++) { + results[i] = ((PrimitiveKey.CharKey) arrayKey.get(i)).value(); + } + return results; + } + + public static boolean[] toBooleanValues(ArrayKey arrayKey) { + if (!isAllTypeOf(PrimitiveKey.BooleanKey.class, arrayKey)) { return null; } + int size = arrayKey.size(); + boolean[] results = new boolean[size]; + for (int i = 0; i < size; i++) { + results[i] = ((PrimitiveKey.BooleanKey) arrayKey.get(i)).value(); + } + return results; + } + + public static long[] toNumberValues(ArrayKey arrayKey) { + if (!isAllTypeOf(PrimitiveKey.NumberKey.class, arrayKey)) { return null; } + int size = arrayKey.size(); + long[] results = new long[size]; + for (int i = 0; i < size; i++) { + results[i] = ((PrimitiveKey.NumberKey) arrayKey.get(i)).getValueAsLong(); + } + return results; + } +} diff --git a/src/main/java/com/reandroid/dex/key/ArrayValueKey.java b/src/main/java/com/reandroid/dex/key/ArrayValueKey.java new file mode 100644 index 000000000..6b7b8af5e --- /dev/null +++ b/src/main/java/com/reandroid/dex/key/ArrayValueKey.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2022 github.com/REAndroid + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.reandroid.dex.key; + +import com.reandroid.dex.smali.SmaliParseException; +import com.reandroid.dex.smali.SmaliReader; +import com.reandroid.dex.smali.SmaliWriter; + +import java.io.IOException; +import java.util.function.Predicate; + +public class ArrayValueKey extends ArrayKey { + + public static final ArrayValueKey EMPTY; + + static { + EMPTY = new ArrayValueKey(EMPTY_ARRAY); + } + + private ArrayValueKey(Key[] elements) { + super(elements); + } + + public boolean isStrings() { + return ArrayKeyHelper.isAllTypeOf(StringKey.class, this); + } + public boolean isBytes() { + return ArrayKeyHelper.isAllTypeOf(PrimitiveKey.ByteKey.class, this); + } + public boolean isShorts() { + return ArrayKeyHelper.isAllTypeOf(PrimitiveKey.ShortKey.class, this); + } + public boolean isIntegers() { + return ArrayKeyHelper.isAllTypeOf(PrimitiveKey.IntegerKey.class, this); + } + public boolean isLongs() { + return ArrayKeyHelper.isAllTypeOf(PrimitiveKey.LongKey.class, this); + } + public boolean isFloats() { + return ArrayKeyHelper.isAllTypeOf(PrimitiveKey.FloatKey.class, this); + } + public boolean isDoubles() { + return ArrayKeyHelper.isAllTypeOf(PrimitiveKey.DoubleKey.class, this); + } + public boolean isNumbers() { + return ArrayKeyHelper.isAllTypeOf(PrimitiveKey.NumberKey.class, this); + } + public boolean isBooleans() { + return ArrayKeyHelper.isAllTypeOf(PrimitiveKey.BooleanKey.class, this); + } + public boolean isChars() { + return ArrayKeyHelper.isAllTypeOf(PrimitiveKey.CharKey.class, this); + } + public boolean isValuesTypeOfKey(Class keyClass) { + return ArrayKeyHelper.isAllTypeOf(keyClass, this); + } + + + public String[] getStringValues() { + return ArrayKeyHelper.toStringValues(this); + } + public byte[] getByteValues() { + return ArrayKeyHelper.toByteValues(this); + } + public short[] getShortValues() { + return ArrayKeyHelper.toShortValues(this); + } + public int[] getIntegerValues() { + return ArrayKeyHelper.toIntValues(this); + } + public long[] getLongValues() { + return ArrayKeyHelper.toLongValues(this); + } + public float[] getFloatValues() { + return ArrayKeyHelper.toFloatValues(this); + } + public double[] getDoubleValues() { + return ArrayKeyHelper.toDoubleValues(this); + } + public char[] getCharValues() { + return ArrayKeyHelper.toCharValues(this); + } + public boolean[] getBooleanValues() { + return ArrayKeyHelper.toBooleanValues(this); + } + public long[] getNumberValues() { + return ArrayKeyHelper.toNumberValues(this); + } + + @Override + public ArrayValueKey add(Key item) { + return (ArrayValueKey) super.add(item); + } + @Override + public ArrayValueKey remove(Key itemKey) { + return (ArrayValueKey) super.remove(itemKey); + } + @Override + public ArrayValueKey remove(int index) { + return (ArrayValueKey) super.remove(index); + } + @Override + public ArrayValueKey removeIf(Predicate predicate) { + return (ArrayValueKey) super.removeIf(predicate); + } + @Override + public ArrayValueKey set(int i, Key item) { + return (ArrayValueKey) super.set(i, item); + } + + @Override + ArrayValueKey newInstance(Key[] elements) { + return create(elements); + } + @Override + Key[] newArray(int length) { + if (length == 0) { + return EMPTY_ARRAY; + } + return new Key[length]; + } + + @Override + public void append(SmaliWriter writer) throws IOException { + int size = this.size(); + writer.append('{'); + writer.indentPlus(); + for (int i = 0; i < size; i++) { + Key key = get(i); + if (i != 0) { + writer.append(','); + } + writer.newLine(); + key.append(writer); + } + writer.indentMinus(); + if (size != 0) { + writer.newLine(); + } + writer.append('}'); + } + + @Override + public String toString() { + return SmaliWriter.toStringSafe(this); + } + + + public static ArrayValueKey create(Key ... elements) { + if (elements == null || elements.length == 0) { + return EMPTY; + } + return new ArrayValueKey(elements); + } + public static ArrayValueKey create(ArrayKey arrayKey) { + if (arrayKey instanceof ArrayValueKey) { + return (ArrayValueKey) arrayKey; + } + if (arrayKey.isEmpty()) { + return EMPTY; + } + return create(arrayKey.getElements()); + } + + public static ArrayValueKey read(SmaliReader reader) throws IOException { + SmaliParseException.expect(reader, '{'); + return create(readElements(reader, '}')); + } + + public static ArrayValueKey parse(String text) { + //FIXME + throw new RuntimeException("ArrayValueKey.parse not implemented"); + } + + public static ArrayValueKey of(String[] values) { + return create(ArrayKeyHelper.toStringKeys(values)); + } + public static ArrayValueKey of(byte[] values) { + return create(ArrayKeyHelper.toPrimitiveKeys(values)); + } + public static ArrayValueKey of(short[] values) { + return create(ArrayKeyHelper.toPrimitiveKeys(values)); + } + public static ArrayValueKey of(int[] values) { + return create(ArrayKeyHelper.toPrimitiveKeys(values)); + } + public static ArrayValueKey of(long[] values) { + return create(ArrayKeyHelper.toPrimitiveKeys(values)); + } + public static ArrayValueKey of(float[] values) { + return create(ArrayKeyHelper.toPrimitiveKeys(values)); + } + public static ArrayValueKey of(double[] values) { + return create(ArrayKeyHelper.toPrimitiveKeys(values)); + } + public static ArrayValueKey of(char[] values) { + return create(ArrayKeyHelper.toPrimitiveKeys(values)); + } + public static ArrayValueKey of(boolean[] values) { + return create(ArrayKeyHelper.toPrimitiveKeys(values)); + } +} diff --git a/src/main/java/com/reandroid/dex/key/CallSiteKey.java b/src/main/java/com/reandroid/dex/key/CallSiteKey.java index 07a5bf42a..651b0fefe 100644 --- a/src/main/java/com/reandroid/dex/key/CallSiteKey.java +++ b/src/main/java/com/reandroid/dex/key/CallSiteKey.java @@ -21,6 +21,7 @@ import com.reandroid.dex.smali.SmaliWriter; import com.reandroid.utils.CompareUtil; import com.reandroid.utils.ObjectsUtil; +import com.reandroid.utils.StringsUtil; import java.io.IOException; @@ -60,7 +61,7 @@ public ArrayKey toArrayKey() { for (int i = 0; i < argumentsLength; i++) { elements[i + 3] = arguments.get(i); } - return new ArrayKey(elements); + return ArrayKey.create(elements); } public CallSiteKey changeMethodHandle(MethodHandleKey methodHandle) { if (methodHandle.equals(getMethodHandle())) { @@ -80,7 +81,7 @@ public CallSiteKey changeProto(ProtoKey proto) { } return new CallSiteKey(getMethodHandle(), getName(), proto, getArguments()); } - public CallSiteKey changeArguments(ArrayKey arguments) { + public CallSiteKey changeArguments(ArrayValueKey arguments) { if (arguments.equals(getArguments())) { return this; } @@ -94,9 +95,12 @@ public void append(SmaliWriter writer) throws IOException { writer.append(", "); getProto().append(writer); ArrayKey arguments = getArguments(); - if (!arguments.isEmpty()) { - writer.append(", "); - getArguments().append(writer, ", "); + int size = arguments.size(); + for (int i = 0; i < size; i++) { + if (i != 0) { + writer.append(", "); + } + arguments.get(i).append(writer); } writer.append(')'); getMethodHandle().append(writer, false); @@ -108,7 +112,7 @@ public int compareTo(Object obj) { return 0; } if (!(obj instanceof CallSiteKey)) { - return 0; + return StringsUtil.compareToString(this, obj); } CallSiteKey key = (CallSiteKey) obj; int i = CompareUtil.compare(this.getMethodHandle(), key.getMethodHandle()); @@ -184,6 +188,11 @@ public static CallSiteKey read(SmaliReader reader) throws IOException { MethodHandleKey methodHandleKey = MethodHandleKey.read(MethodHandleType.INVOKE_STATIC, reader); return new CallSiteKey(methodHandleKey, name, protoKey, arguments); } + + public static CallSiteKey parse(String text) { + //FIXME + throw new RuntimeException("CallSiteKey.parse not implemented"); + } private static String readCallSiteLabel(SmaliReader reader) throws IOException { int position = reader.position(); if (reader.getASCII(position) == '(') { diff --git a/src/main/java/com/reandroid/dex/key/DataKey.java b/src/main/java/com/reandroid/dex/key/DataKey.java index ab6648b3f..05b78da92 100644 --- a/src/main/java/com/reandroid/dex/key/DataKey.java +++ b/src/main/java/com/reandroid/dex/key/DataKey.java @@ -17,6 +17,7 @@ import com.reandroid.dex.data.DataItem; import com.reandroid.utils.CompareUtil; +import com.reandroid.utils.StringsUtil; public class DataKey implements Key{ private final T item; @@ -34,8 +35,11 @@ private int getOffset(){ @Override public int compareTo(Object obj) { + if (obj == this) { + return 0; + } if(obj == null || getClass() != obj.getClass()){ - return 1; + return StringsUtil.compareToString(this, obj); } DataKey key = (DataKey) obj; if(getItem().equals(key.getItem())){ diff --git a/src/main/java/com/reandroid/dex/key/EnumKey.java b/src/main/java/com/reandroid/dex/key/EnumKey.java new file mode 100644 index 000000000..cfb7d7cbb --- /dev/null +++ b/src/main/java/com/reandroid/dex/key/EnumKey.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2022 github.com/REAndroid + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.reandroid.dex.key; + +import com.reandroid.dex.smali.SmaliDirective; +import com.reandroid.dex.smali.SmaliParseException; +import com.reandroid.dex.smali.SmaliReader; +import com.reandroid.dex.smali.SmaliWriter; + +import java.io.IOException; + +public class EnumKey extends FieldKey { + + EnumKey(FieldKey fieldKey) { + super(fieldKey.getDeclaring(), fieldKey.getNameKey(), fieldKey.getType()); + } + + @Override + public void append(SmaliWriter writer) throws IOException { + SmaliDirective.ENUM.append(writer); + super.append(writer); + } + + @Override + public String toString() { + return SmaliDirective.ENUM.getName() + " " + super.toString(); + } + + public static EnumKey create(FieldKey fieldKey) { + if (fieldKey == null) { + return null; + } + if (fieldKey instanceof EnumKey) { + return (EnumKey) fieldKey; + } + return new EnumKey(fieldKey); + } + + public static EnumKey parse(String text) { + if (text == null) { + return null; + } + String prefix = ".enum "; + if (!text.startsWith(prefix)) { + return null; + } + text = text.substring(prefix.length()).trim(); + return create(FieldKey.parse(text)); + } + + public static EnumKey read(SmaliReader reader) throws IOException { + SmaliParseException.expect(reader, SmaliDirective.ENUM); + reader.skipWhitespacesOrComment(); + return create(FieldKey.read(reader)); + } +} diff --git a/src/main/java/com/reandroid/dex/key/FieldKey.java b/src/main/java/com/reandroid/dex/key/FieldKey.java index 19a6f7d96..cbceae2b9 100644 --- a/src/main/java/com/reandroid/dex/key/FieldKey.java +++ b/src/main/java/com/reandroid/dex/key/FieldKey.java @@ -33,7 +33,7 @@ public class FieldKey implements Key { private final StringKey name; private final TypeKey type; - private FieldKey(TypeKey declaring, StringKey name, TypeKey type) { + FieldKey(TypeKey declaring, StringKey name, TypeKey type) { this.declaring = declaring; this.name = name; this.type = type; diff --git a/src/main/java/com/reandroid/dex/key/IdKey.java b/src/main/java/com/reandroid/dex/key/IdKey.java deleted file mode 100644 index 61f52abd9..000000000 --- a/src/main/java/com/reandroid/dex/key/IdKey.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2022 github.com/REAndroid - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.reandroid.dex.key; - -import com.reandroid.dex.id.IdItem; - -public class IdKey implements Key{ - - private final T item; - - public IdKey(T item){ - this.item = item; - } - - public T getItem() { - return item; - } - private int getIndex(){ - return getItem().getIndex(); - } - - @Override - public int compareTo(Object obj) { - if(obj == null || getClass() != obj.getClass()){ - return 1; - } - IdKey key = (IdKey) obj; - T item1 = getItem(); - IdItem item2 = key.getItem(); - if(item1.getClass() == item2.getClass()){ - return 0; - } - return 1; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - IdKey key = (IdKey) obj; - return item.equals(key.item); - } - @Override - public int hashCode() { - return item.hashCode(); - } - - @Override - public String toString() { - return getIndex() + ": {" + getItem() +"}"; - } -} diff --git a/src/main/java/com/reandroid/dex/key/Key.java b/src/main/java/com/reandroid/dex/key/Key.java index a4725003e..fce114b77 100644 --- a/src/main/java/com/reandroid/dex/key/Key.java +++ b/src/main/java/com/reandroid/dex/key/Key.java @@ -15,8 +15,6 @@ */ package com.reandroid.dex.key; - -import com.reandroid.dex.common.DexUtils; import com.reandroid.dex.smali.SmaliFormat; import com.reandroid.dex.smali.SmaliWriter; import com.reandroid.utils.ObjectsUtil; @@ -28,13 +26,13 @@ public interface Key extends Comparable, SmaliFormat { - default boolean uses(Key key){ + default boolean uses(Key key) { return this.equals(key) || CollectionUtil.contains(mentionedKeys(), key); } - default TypeKey getDeclaring(){ - return TypeKey.NULL; + default TypeKey getDeclaring() { + return null; } - default Iterator mentionedKeys(){ + default Iterator mentionedKeys() { throw new RuntimeException("Method 'mentionedKeys()' Not implemented for: " + getClass()); } default Key replaceKey(Key search, Key replace){ @@ -43,22 +41,46 @@ default Key replaceKey(Key search, Key replace){ default boolean isPrimitiveKey() { return false; } - default boolean isPlatform(){ - return DexUtils.isPlatform(getDeclaring()); - } + default void append(SmaliWriter writer) throws IOException{ writer.append(toString()); } + @Override + int compareTo(Object o); - static Key parseBasic(String text){ + static Key parseBasic(String text) { if(StringsUtil.isEmpty(text)) { return null; } - int i = text.indexOf('"'); - if(i == 0) { + char first = text.charAt(0); + if (first == '"') { return StringKey.parseQuotedString(text); } - i = text.indexOf('('); + if (first == 'L' || first == '[') { + if (first == 'L' && text.indexOf(':') > 0) { + return FieldKey.parse(text); + } + if (text.indexOf('(') > 0) { + return MethodKey.parse(text); + } + return TypeKey.parse(text); + } + if (first == '(') { + if (text.indexOf(',') > 0) { + return CallSiteKey.parse(text); + } + return ProtoKey.parse(text); + } + if (first == '{') { + return ArrayValueKey.parse(text); + } + if (text.startsWith(".annotation") || text.startsWith(".subannotation")) { + return AnnotationItemKey.parse(text); + } + if (text.startsWith(".enum")) { + return EnumKey.parse(text); + } + int i = text.indexOf('('); if(i > 0) { return MethodKey.parse(text); } @@ -66,7 +88,7 @@ static Key parseBasic(String text){ if(i > 0) { return FieldKey.parse(text); } - return TypeKey.parse(text); + return PrimitiveKey.parse(text); } String DALVIK_accessFlags = ObjectsUtil.of("accessFlags"); String DALVIK_name = ObjectsUtil.of("name"); diff --git a/src/main/java/com/reandroid/dex/key/KeyList.java b/src/main/java/com/reandroid/dex/key/KeyList.java index f1500d500..903f717d7 100644 --- a/src/main/java/com/reandroid/dex/key/KeyList.java +++ b/src/main/java/com/reandroid/dex/key/KeyList.java @@ -135,6 +135,29 @@ public KeyList removeIf(Predicate predicate) { return newInstance(result); } + public T getDuplicate() { + T[] elements = getSortedElements(); + int length = elements.length; + if (length < 2) { + return null; + } + T last = elements[0]; + for (int i = 1; i < length; i++) { + T element = elements[i]; + if (ObjectsUtil.equals(last, element)) { + return element; + } + last = element; + } + return null; + } + public KeyList sorted() { + T[] elements = this.sortedElements; + if (elements == null) { + return this; + } + return newInstance(elements); + } private T[] getSortedElements() { T[] elements = this.sortedElements; if (elements == null) { diff --git a/src/main/java/com/reandroid/dex/key/MethodHandleKey.java b/src/main/java/com/reandroid/dex/key/MethodHandleKey.java index 56d1f9435..686eb96c6 100644 --- a/src/main/java/com/reandroid/dex/key/MethodHandleKey.java +++ b/src/main/java/com/reandroid/dex/key/MethodHandleKey.java @@ -21,6 +21,7 @@ import com.reandroid.dex.smali.SmaliWriter; import com.reandroid.utils.CompareUtil; import com.reandroid.utils.ObjectsUtil; +import com.reandroid.utils.StringsUtil; import java.io.IOException; import java.util.Iterator; @@ -78,8 +79,11 @@ public void append(SmaliWriter writer, boolean appendHandle) throws IOException @Override public int compareTo(Object obj) { - if(obj == null){ - return -1; + if (obj == this) { + return 0; + } + if (!(obj instanceof MethodHandleKey)) { + return StringsUtil.compareToString(this, obj); } MethodHandleKey other = (MethodHandleKey) obj; int i = CompareUtil.compare(getHandleType(), other.getHandleType()); diff --git a/src/main/java/com/reandroid/dex/key/MethodKey.java b/src/main/java/com/reandroid/dex/key/MethodKey.java index be7d1f0d1..d56abd0e2 100644 --- a/src/main/java/com/reandroid/dex/key/MethodKey.java +++ b/src/main/java/com/reandroid/dex/key/MethodKey.java @@ -20,6 +20,7 @@ import com.reandroid.dex.smali.SmaliWriter; import com.reandroid.utils.CompareUtil; import com.reandroid.utils.ObjectsUtil; +import com.reandroid.utils.StringsUtil; import com.reandroid.utils.collection.CombiningIterator; import com.reandroid.utils.collection.SingleIterator; @@ -172,8 +173,8 @@ public int compareTo(Object obj) { if (obj == this) { return 0; } - if (obj == null) { - return -1; + if (!(obj instanceof MethodKey)) { + return StringsUtil.compareToString(this, obj); } MethodKey key = (MethodKey) obj; int i = CompareUtil.compare(getDeclaring(), key.getDeclaring()); diff --git a/src/main/java/com/reandroid/dex/key/NullKey.java b/src/main/java/com/reandroid/dex/key/NullValueKey.java similarity index 70% rename from src/main/java/com/reandroid/dex/key/NullKey.java rename to src/main/java/com/reandroid/dex/key/NullValueKey.java index 0782af982..2dd8ea7f7 100644 --- a/src/main/java/com/reandroid/dex/key/NullKey.java +++ b/src/main/java/com/reandroid/dex/key/NullValueKey.java @@ -15,15 +15,17 @@ */ package com.reandroid.dex.key; +import com.reandroid.dex.smali.SmaliParseException; +import com.reandroid.dex.smali.SmaliReader; import com.reandroid.dex.smali.SmaliWriter; import java.io.IOException; -public class NullKey implements Key { +public class NullValueKey implements Key { - public static NullKey INSTANCE = new NullKey(); + public static NullValueKey INSTANCE = new NullValueKey(); - public NullKey() { + private NullValueKey() { } @Override @@ -57,4 +59,13 @@ public void append(SmaliWriter writer) throws IOException { public String toString() { return "null"; } + + public static NullValueKey read(SmaliReader reader) throws IOException { + reader.skipWhitespacesOrComment(); + SmaliParseException.expect(reader, 'n'); + SmaliParseException.expect(reader, 'u'); + SmaliParseException.expect(reader, 'l'); + SmaliParseException.expect(reader, 'l'); + return INSTANCE; + } } diff --git a/src/main/java/com/reandroid/dex/key/PrimitiveKey.java b/src/main/java/com/reandroid/dex/key/PrimitiveKey.java index 29ebf7007..1258ccc5d 100644 --- a/src/main/java/com/reandroid/dex/key/PrimitiveKey.java +++ b/src/main/java/com/reandroid/dex/key/PrimitiveKey.java @@ -18,9 +18,9 @@ import com.reandroid.dex.common.DexUtils; import com.reandroid.dex.smali.SmaliReader; import com.reandroid.dex.smali.SmaliWriter; -import com.reandroid.utils.CompareUtil; import com.reandroid.utils.HexUtil; import com.reandroid.utils.NumberX; +import com.reandroid.utils.StringsUtil; import java.io.IOException; @@ -29,9 +29,6 @@ public abstract class PrimitiveKey implements Key { public PrimitiveKey() { } - @Override - public boolean isPrimitiveKey() { return true; } - public abstract Object getValue(); public abstract TypeKey valueType(); public abstract int width(); @@ -48,9 +45,17 @@ public PrimitiveKey() { public boolean isShort() { return false; } public boolean isX() { return false; } - @Override - public abstract int compareTo(Object obj); + public int compareTo(Object obj) { + if (obj == this) { + return 0; + } + if (!(obj instanceof PrimitiveKey)) { + return StringsUtil.compareToString(this, obj); + } + PrimitiveKey other = (PrimitiveKey) obj; + return Long.compare(this.getValueAsLong(), other.getValueAsLong()); + } @Override public abstract void append(SmaliWriter writer) throws IOException; @@ -141,13 +146,6 @@ public TypeKey valueType() { return TypeKey.TYPE_Z; } - @Override - public int compareTo(Object obj) { - if (obj == null || obj.getClass() != getClass()) { - return 0; - } - return CompareUtil.compare(value(), ((BooleanKey) obj).value()); - } @Override public int hashCode() { return value() ? 1 : 0; @@ -209,16 +207,6 @@ public boolean isByte() { return true; } - @Override - public int compareTo(Object obj) { - if (obj == this) { - return 0; - } - if (obj == null || obj.getClass() != getClass()) { - return 0; - } - return Byte.compare(value(), ((ByteKey) obj).value()); - } @Override public int hashCode() { return value(); @@ -280,16 +268,6 @@ public boolean isChar() { return true; } - @Override - public int compareTo(Object obj) { - if (obj == this) { - return 0; - } - if (obj == null || obj.getClass() != getClass()) { - return 0; - } - return Character.compare(value(), ((CharKey) obj).value()); - } @Override public int hashCode() { return value(); @@ -355,8 +333,8 @@ public int compareTo(Object obj) { if (obj == this) { return 0; } - if (obj == null || obj.getClass() != getClass()) { - return 0; + if (!(obj instanceof DoubleKey)) { + return StringsUtil.compareToString(this, obj); } return Double.compare(value(), ((DoubleKey) obj).value()); } @@ -426,8 +404,8 @@ public int compareTo(Object obj) { if (obj == this) { return 0; } - if (obj == null || obj.getClass() != getClass()) { - return 0; + if (!(obj instanceof FloatKey)) { + return StringsUtil.compareToString(this, obj); } return Float.compare(value(), ((FloatKey) obj).value()); } @@ -493,13 +471,6 @@ public boolean isInteger() { return true; } - @Override - public int compareTo(Object obj) { - if (obj == null || obj.getClass() != getClass()) { - return 0; - } - return CompareUtil.compare(value(), ((IntegerKey) obj).value()); - } @Override public int hashCode() { return value(); @@ -561,16 +532,6 @@ public boolean isLong() { return true; } - @Override - public int compareTo(Object obj) { - if (obj == this) { - return 0; - } - if (obj == null || obj.getClass() != getClass()) { - return 0; - } - return Long.compare(value(), ((LongKey) obj).value()); - } @Override public int hashCode() { return Long.hashCode(value()); @@ -631,16 +592,6 @@ public boolean isShort() { return true; } - @Override - public int compareTo(Object obj) { - if (obj == this) { - return 0; - } - if (obj == null || obj.getClass() != getClass()) { - return 0; - } - return Short.compare(value(), ((ShortKey) obj).value()); - } @Override public int hashCode() { return value(); @@ -681,7 +632,21 @@ public long value() { return value; } @Override - public NumberX getValue() { + public Number getValue() { + int width = width(); + long value = value(); + if (width == 1) { + return (byte) value; + } + if (width == 2) { + return (short) value; + } + if (width == 4) { + return (int) value; + } + if (width == 8) { + return value; + } return NumberX.valueOf(width(), value()); } @Override @@ -701,16 +666,6 @@ public TypeKey valueType() { return null; } - @Override - public int compareTo(Object obj) { - if (obj == this) { - return 0; - } - if (obj == null || obj.getClass() != getClass()) { - return 0; - } - return Long.compare(value(), ((NumberXKey) obj).value()); - } @Override public int hashCode() { return Long.hashCode(value()); diff --git a/src/main/java/com/reandroid/dex/key/ProtoKey.java b/src/main/java/com/reandroid/dex/key/ProtoKey.java index 129ee6807..227b2ea28 100644 --- a/src/main/java/com/reandroid/dex/key/ProtoKey.java +++ b/src/main/java/com/reandroid/dex/key/ProtoKey.java @@ -4,6 +4,7 @@ import com.reandroid.dex.smali.SmaliWriter; import com.reandroid.utils.CompareUtil; import com.reandroid.utils.ObjectsUtil; +import com.reandroid.utils.StringsUtil; import com.reandroid.utils.collection.*; import java.io.IOException; @@ -137,12 +138,12 @@ public void append(SmaliWriter writer) throws IOException { @Override public int compareTo(Object obj) { - if (obj == null) { - return -1; - } if (obj == this) { return 0; } + if (!(obj instanceof ProtoKey)) { + return StringsUtil.compareToString(this, obj); + } ProtoKey key = (ProtoKey) obj; int i = CompareUtil.compare(getReturnType(), key.getReturnType()); if(i != 0) { diff --git a/src/main/java/com/reandroid/dex/key/StringKey.java b/src/main/java/com/reandroid/dex/key/StringKey.java index 57bb0f412..4a00a1cbd 100644 --- a/src/main/java/com/reandroid/dex/key/StringKey.java +++ b/src/main/java/com/reandroid/dex/key/StringKey.java @@ -51,11 +51,6 @@ public String getAsSimpleName() { return encodeSimpleName(getString()); } - @Override - public boolean isPlatform() { - return false; - } - public boolean isSignature() { return mSignature; } @@ -101,8 +96,11 @@ public void appendSimpleName(SmaliWriter writer) throws IOException { @Override public int compareTo(Object obj) { - if(obj == null){ - return -1; + if (obj == this) { + return 0; + } + if (!(obj instanceof StringKey)) { + return StringsUtil.compareToString(this, obj); } StringKey key = (StringKey) obj; return CompareUtil.compare(getString(), key.getString()); diff --git a/src/main/java/com/reandroid/dex/key/TypeKey.java b/src/main/java/com/reandroid/dex/key/TypeKey.java index 71001577a..deb1e9723 100644 --- a/src/main/java/com/reandroid/dex/key/TypeKey.java +++ b/src/main/java/com/reandroid/dex/key/TypeKey.java @@ -294,12 +294,12 @@ public boolean equalsPackage(TypeKey typeKey) { } @Override public int compareTo(Object obj) { - if(obj == null){ - return -1; - } - if(obj == this){ + if (obj == this) { return 0; } + if (!(obj instanceof TypeKey)) { + return StringsUtil.compareToString(this, obj); + } TypeKey key = (TypeKey) obj; return CompareUtil.compare(getTypeName(), key.getTypeName()); } @@ -513,61 +513,7 @@ public static TypeKey parseSignature(String type){ } return null; } - public static final TypeKey NULL = new TypeKey("00"){ - @Override - public boolean isPlatform() { - return false; - } - @Override - public boolean isPrimitive() { - return false; - } - @Override - public boolean isTypeArray() { - return false; - } - @Override - public boolean isTypeObject() { - return false; - } - @Override - public TypeKey getDeclaring() { - return this; - } - @Override - public String getTypeName() { - return StringsUtil.EMPTY; - } - @Override - public Iterator mentionedKeys() { - return EmptyIterator.of(); - } - @Override - public Key replaceKey(Key search, Key replace) { - return this; - } - @Override - public boolean isPackage(String packageName) { - return false; - } - @Override - public boolean isPackage(String packageName, boolean checkSubPackage) { - return false; - } - @Override - public TypeKey setArrayDimension(int dimension) { - return this; - } - @Override - public TypeKey setPackage(String packageName) { - return this; - } - - @Override - public void append(SmaliWriter writer) { - } - }; static class PrimitiveTypeKey extends TypeKey { private final String sourceName; diff --git a/src/main/java/com/reandroid/dex/key/TypeListKey.java b/src/main/java/com/reandroid/dex/key/TypeListKey.java index c4a3c8ea1..ad9eb8679 100644 --- a/src/main/java/com/reandroid/dex/key/TypeListKey.java +++ b/src/main/java/com/reandroid/dex/key/TypeListKey.java @@ -85,8 +85,8 @@ public int compareTo(Object obj) { if (obj == this) { return 0; } - if (obj == null) { - return -1; + if (!(obj instanceof TypeListKey)) { + return StringsUtil.compareToString(this, obj); } return compareElements((TypeListKey) obj); } diff --git a/src/main/java/com/reandroid/dex/model/DexAnnotation.java b/src/main/java/com/reandroid/dex/model/DexAnnotation.java index c69b5c228..ab0aa717f 100644 --- a/src/main/java/com/reandroid/dex/model/DexAnnotation.java +++ b/src/main/java/com/reandroid/dex/model/DexAnnotation.java @@ -45,7 +45,7 @@ public void setKey(AnnotationItemKey key) { } public TypeKey getType(){ - return getAnnotationItem().getTypeKey(); + return getAnnotationItem().getType(); } public void setType(TypeKey typeKey){ getAnnotationItem().setType(typeKey); diff --git a/src/main/java/com/reandroid/dex/model/DexClassRepository.java b/src/main/java/com/reandroid/dex/model/DexClassRepository.java index 0a0482dc9..d1d2e7c06 100644 --- a/src/main/java/com/reandroid/dex/model/DexClassRepository.java +++ b/src/main/java/com/reandroid/dex/model/DexClassRepository.java @@ -359,7 +359,7 @@ default boolean removeClassesWithKeys(Predicate filter) { } default boolean removeAnnotations(TypeKey typeKey) { return removeEntries(SectionType.ANNOTATION_ITEM, - annotationItem -> typeKey.equals(annotationItem.getTypeKey())); + annotationItem -> typeKey.equals(annotationItem.getType())); } default int removeAnnotationElements(MethodKey methodKey) { int removeCount = 0; diff --git a/src/main/java/com/reandroid/dex/model/DexMethodParameter.java b/src/main/java/com/reandroid/dex/model/DexMethodParameter.java index 339ed78f7..cb5352673 100644 --- a/src/main/java/com/reandroid/dex/model/DexMethodParameter.java +++ b/src/main/java/com/reandroid/dex/model/DexMethodParameter.java @@ -120,7 +120,7 @@ public DexClassRepository getClassRepository() { @Override public void removeSelf() { - getParameter().removeSelf(); + getParameter().onRemoved(); } @Override diff --git a/src/main/java/com/reandroid/dex/smali/model/SmaliAnnotationSet.java b/src/main/java/com/reandroid/dex/smali/model/SmaliAnnotationSet.java index 058e35251..40c859c44 100644 --- a/src/main/java/com/reandroid/dex/smali/model/SmaliAnnotationSet.java +++ b/src/main/java/com/reandroid/dex/smali/model/SmaliAnnotationSet.java @@ -37,7 +37,7 @@ public AnnotationSetKey getKey() { for (int i = 0; i < size; i++) { elements[i] = get(i).getKey(); } - return new AnnotationSetKey(elements); + return AnnotationSetKey.create(elements); } public void setKey(Key key) { clear(); diff --git a/src/main/java/com/reandroid/dex/smali/model/SmaliMethodParameter.java b/src/main/java/com/reandroid/dex/smali/model/SmaliMethodParameter.java index b18d51048..0fc6ab099 100644 --- a/src/main/java/com/reandroid/dex/smali/model/SmaliMethodParameter.java +++ b/src/main/java/com/reandroid/dex/smali/model/SmaliMethodParameter.java @@ -15,6 +15,8 @@ */ package com.reandroid.dex.smali.model; +import com.reandroid.dex.key.AnnotationItemKey; +import com.reandroid.dex.key.AnnotationSetKey; import com.reandroid.dex.key.ProtoKey; import com.reandroid.dex.key.StringKey; import com.reandroid.dex.smali.*; @@ -61,6 +63,13 @@ public boolean hasAnnotations(){ SmaliAnnotationSet annotationSet = getAnnotationSet(); return annotationSet != null && !annotationSet.isEmpty(); } + public AnnotationSetKey getAnnotations() { + SmaliAnnotationSet annotationSet = getAnnotationSet(); + if (annotationSet != null) { + return annotationSet.getKey(); + } + return AnnotationSetKey.EMPTY; + } public SmaliAnnotationSet getAnnotationSet() { return annotationSet; } @@ -126,6 +135,11 @@ public void parse(SmaliReader reader) throws IOException { } parseName(reader); parseAnnotationSet(reader); + AnnotationItemKey duplicate = getAnnotations().getDuplicate(); + if (duplicate != null) { + throw new SmaliParseException("Multiple annotation of type: " + + duplicate.getType() + "\n", reader); + } } private void parseName(SmaliReader reader) throws IOException { reader.skipSpaces(); diff --git a/src/main/java/com/reandroid/dex/smali/model/SmaliValueArray.java b/src/main/java/com/reandroid/dex/smali/model/SmaliValueArray.java index 70a0d4131..5a61f3be1 100644 --- a/src/main/java/com/reandroid/dex/smali/model/SmaliValueArray.java +++ b/src/main/java/com/reandroid/dex/smali/model/SmaliValueArray.java @@ -15,7 +15,7 @@ */ package com.reandroid.dex.smali.model; -import com.reandroid.dex.key.ArrayKey; +import com.reandroid.dex.key.ArrayValueKey; import com.reandroid.dex.key.Key; import com.reandroid.dex.smali.SmaliParseException; import com.reandroid.dex.smali.SmaliReader; @@ -36,17 +36,17 @@ public SmaliValueArray(){ } @Override - public ArrayKey getKey() { + public ArrayValueKey getKey() { int size = size(); Key[] elements = new Key[size]; for (int i = 0; i < size; i++) { elements[i] = get(i).getKey(); } - return new ArrayKey(elements); + return ArrayValueKey.create(elements); } @Override public void setKey(Key key) { - ArrayKey arrayKey = (ArrayKey) key; + ArrayValueKey arrayKey = (ArrayValueKey) key; clear(); for (Key entry : arrayKey) { addValue(entry); diff --git a/src/main/java/com/reandroid/dex/smali/model/SmaliValueFactory.java b/src/main/java/com/reandroid/dex/smali/model/SmaliValueFactory.java index 0856d7f00..9182f8419 100644 --- a/src/main/java/com/reandroid/dex/smali/model/SmaliValueFactory.java +++ b/src/main/java/com/reandroid/dex/smali/model/SmaliValueFactory.java @@ -160,11 +160,11 @@ public static SmaliValue initializeForValue(Key key) { smaliValue = new SmaliValueSectionData(); } else if(key instanceof FieldKey) { smaliValue = new SmaliValueEnum(); - } else if(key instanceof ArrayKey) { + } else if(key instanceof ArrayValueKey) { smaliValue = new SmaliValueArray(); } else if(key instanceof AnnotationItemKey) { smaliValue = new SmaliValueAnnotation(); - } else if(key instanceof NullKey) { + } else if(key instanceof NullValueKey) { smaliValue = new SmaliValueNull(); } else { throw new IllegalArgumentException("Undefined key: " + key.getClass() + ", " + key); diff --git a/src/main/java/com/reandroid/dex/smali/model/SmaliValueNull.java b/src/main/java/com/reandroid/dex/smali/model/SmaliValueNull.java index b0295bcd9..cbfe045c5 100644 --- a/src/main/java/com/reandroid/dex/smali/model/SmaliValueNull.java +++ b/src/main/java/com/reandroid/dex/smali/model/SmaliValueNull.java @@ -16,7 +16,7 @@ package com.reandroid.dex.smali.model; import com.reandroid.dex.key.Key; -import com.reandroid.dex.key.NullKey; +import com.reandroid.dex.key.NullValueKey; import com.reandroid.dex.smali.SmaliParseException; import com.reandroid.dex.smali.SmaliReader; import com.reandroid.dex.smali.SmaliWriter; @@ -36,12 +36,12 @@ public DexValueType getValueType() { } @Override - public NullKey getKey() { - return NullKey.INSTANCE; + public NullValueKey getKey() { + return NullValueKey.INSTANCE; } @Override public void setKey(Key key) { - NullKey nullKey = (NullKey) key; + NullValueKey nullValueKey = (NullValueKey) key; } @Override diff --git a/src/main/java/com/reandroid/dex/value/AnnotationValue.java b/src/main/java/com/reandroid/dex/value/AnnotationValue.java index 71fb5fd3b..a309f3d50 100644 --- a/src/main/java/com/reandroid/dex/value/AnnotationValue.java +++ b/src/main/java/com/reandroid/dex/value/AnnotationValue.java @@ -2,7 +2,6 @@ import com.reandroid.dex.data.AnnotationItem; import com.reandroid.dex.key.AnnotationItemKey; -import com.reandroid.dex.key.DataKey; import com.reandroid.dex.key.Key; import com.reandroid.dex.smali.model.SmaliValue; import com.reandroid.dex.smali.model.SmaliValueAnnotation; @@ -28,17 +27,14 @@ public void setKey(Key key) { public DexValueType getValueType() { return DexValueType.ANNOTATION; } - @Override - public String getAsString() { - return get().toString(); - } + @Override public void merge(DexValueBlock valueBlock){ super.merge(valueBlock); AnnotationValue value = (AnnotationValue) valueBlock; AnnotationItem coming = value.get(); AnnotationItem item = get(); - item.setType(coming.getTypeKey()); + item.setType(coming.getType()); item.merge(coming); } diff --git a/src/main/java/com/reandroid/dex/value/ArrayValue.java b/src/main/java/com/reandroid/dex/value/ArrayValue.java index f929e62aa..335b49144 100644 --- a/src/main/java/com/reandroid/dex/value/ArrayValue.java +++ b/src/main/java/com/reandroid/dex/value/ArrayValue.java @@ -17,7 +17,7 @@ import com.reandroid.dex.data.EncodedArray; import com.reandroid.dex.id.IdItem; -import com.reandroid.dex.key.ArrayKey; +import com.reandroid.dex.key.ArrayValueKey; import com.reandroid.dex.key.Key; import com.reandroid.dex.key.TypeKey; import com.reandroid.dex.smali.SmaliWriter; @@ -26,7 +26,6 @@ import com.reandroid.utils.collection.IterableIterator; import java.io.IOException; -import java.util.Comparator; import java.util.Iterator; import java.util.function.Predicate; @@ -105,7 +104,7 @@ public void replaceKeys(Key search, Key replace) { } @Override - public ArrayKey getKey() { + public ArrayValueKey getKey() { return getValueContainer().getKey(); } @@ -139,15 +138,6 @@ public TypeKey getDataTypeKey() { } return TypeKey.OBJECT.setArrayDimension(1); } - @Override - public Object[] getData() { - int size = size(); - Object[] result = new Object[size]; - for(int i = 0; i < size; i++) { - result[i] = get(i).getData(); - } - return result; - } @Override public void append(SmaliWriter writer) throws IOException { diff --git a/src/main/java/com/reandroid/dex/value/BooleanValue.java b/src/main/java/com/reandroid/dex/value/BooleanValue.java index 1abc06dae..cb352d053 100644 --- a/src/main/java/com/reandroid/dex/value/BooleanValue.java +++ b/src/main/java/com/reandroid/dex/value/BooleanValue.java @@ -26,9 +26,11 @@ import java.io.IOException; public class BooleanValue extends DexValueBlock { + public BooleanValue(){ super(DexValueType.BOOLEAN); } + public boolean get(){ return getValueSize() == 1; } @@ -54,26 +56,11 @@ public void setKey(Key key) { public void append(SmaliWriter writer) throws IOException { writer.append(Boolean.toString(get())); } - @Override - public String getAsString() { - return Boolean.toString(get()); - } @Override public TypeKey getDataTypeKey() { return TypeKey.TYPE_Z; } - @Override - public Boolean getData() { - if(get()) { - return Boolean.TRUE; - } - return Boolean.FALSE; - } - @Override - public void setData(Object data) { - set((Boolean) data); - } @Override public void fromSmali(SmaliValue smaliValue) { @@ -100,7 +87,7 @@ public boolean equals(Object obj) { return get() == value.get(); } @Override - public String toString(){ - return getAsString(); + public String toString() { + return Boolean.toString(get()); } } diff --git a/src/main/java/com/reandroid/dex/value/ByteValue.java b/src/main/java/com/reandroid/dex/value/ByteValue.java index c195e5118..aade55ec2 100644 --- a/src/main/java/com/reandroid/dex/value/ByteValue.java +++ b/src/main/java/com/reandroid/dex/value/ByteValue.java @@ -18,25 +18,19 @@ import com.reandroid.dex.key.Key; import com.reandroid.dex.key.PrimitiveKey; import com.reandroid.dex.key.TypeKey; +import com.reandroid.dex.smali.SmaliWriter; import com.reandroid.dex.smali.model.SmaliValue; import com.reandroid.dex.smali.model.SmaliValueByte; import com.reandroid.utils.HexUtil; +import java.io.IOException; + public class ByteValue extends PrimitiveValueBlock { public ByteValue() { super(DexValueType.BYTE); } - @Override - public Byte getData() { - return get(); - } - @Override - public void setData(Number number) { - this.set((Byte) number); - } - public byte get(){ return (byte) getSignedValue(); } @@ -57,19 +51,25 @@ public PrimitiveKey getKey() { public void setKey(Key key) { set(((PrimitiveKey.ByteKey) key).value()); } - @Override - public String getHex() { - return HexUtil.toSignedHex(get()) + "t"; - } @Override public TypeKey getDataTypeKey() { return TypeKey.TYPE_B; } + @Override + public void append(SmaliWriter writer) throws IOException { + writer.appendHex(get()); + } + @Override public void fromSmali(SmaliValue smaliValue) { SmaliValueByte smaliValueByte = (SmaliValueByte) smaliValue; set(smaliValueByte.getValue()); } + + @Override + public String toString() { + return HexUtil.toSignedHex(get()) + "t"; + } } diff --git a/src/main/java/com/reandroid/dex/value/CharValue.java b/src/main/java/com/reandroid/dex/value/CharValue.java index 2e215d35a..5dd1ced4a 100644 --- a/src/main/java/com/reandroid/dex/value/CharValue.java +++ b/src/main/java/com/reandroid/dex/value/CharValue.java @@ -22,7 +22,6 @@ import com.reandroid.dex.smali.SmaliWriter; import com.reandroid.dex.smali.model.SmaliValue; import com.reandroid.dex.smali.model.SmaliValueChar; -import com.reandroid.utils.HexUtil; import java.io.IOException; @@ -32,38 +31,6 @@ public CharValue(){ super(DexValueType.CHAR); } - @Override - public Number getData() { - int i = (int) getSignedValue(); - if((i & 0xff) == i){ - return (byte) i; - } - if((i & 0xffff) == i){ - return (short) i; - } - return i; - } - @Override - public void setData(Number number) { - if(number == null){ - throw new NullPointerException(); - } - if(number instanceof Integer){ - Integer v = (Integer) number; - set((char) v.intValue()); - }else if(number instanceof Short){ - Short v = (Short) number; - int i = v & 0xffff; - set((char) i); - }else if(number instanceof Byte){ - Byte v = (Byte) number; - int i = v & 0xff; - set((char) i); - } - throw new NumberFormatException("Invalid '" - + number.getClass().getSimpleName() - + "' value for char " + number); - } public char get(){ return (char) (getUnsigned() & 0xffff); } @@ -83,15 +50,7 @@ public PrimitiveKey getKey() { public void setKey(Key key) { set(((PrimitiveKey.CharKey) key).value()); } - @Override - public String getHex() { - return HexUtil.toSignedHex(get()) + "C"; - } - @Override - public String getAsString() { - return DexUtils.quoteChar(get()); - } @Override public TypeKey getDataTypeKey() { return TypeKey.TYPE_C; @@ -109,6 +68,6 @@ public void append(SmaliWriter writer) throws IOException { } @Override public String toString() { - return getAsString(); + return DexUtils.quoteChar(get()); } } diff --git a/src/main/java/com/reandroid/dex/value/DexValueBlock.java b/src/main/java/com/reandroid/dex/value/DexValueBlock.java index 5d1d33d0d..fbfa6cdb9 100644 --- a/src/main/java/com/reandroid/dex/value/DexValueBlock.java +++ b/src/main/java/com/reandroid/dex/value/DexValueBlock.java @@ -25,11 +25,11 @@ import com.reandroid.dex.smali.SmaliFormat; import com.reandroid.dex.smali.SmaliWriter; import com.reandroid.dex.smali.model.SmaliValue; +import com.reandroid.utils.ObjectsUtil; import com.reandroid.utils.collection.EmptyIterator; import java.io.IOException; import java.util.Iterator; -import java.util.Objects; public class DexValueBlock extends FixedBlockContainer implements KeyReference, SmaliFormat { @@ -37,8 +37,6 @@ public class DexValueBlock extends FixedBlockContainer private final ByteItem valueTypeItem; private final T valueContainer; - private boolean mTemporary; - DexValueBlock(T value, DexValueType type){ super(2); valueTypeItem = new ByteItem(); @@ -51,13 +49,6 @@ public class DexValueBlock extends FixedBlockContainer this(null, type); } - public boolean isTemporary() { - return mTemporary; - } - public void setTemporary(boolean temporary) { - this.mTemporary = temporary; - } - T getValueContainer(){ return valueContainer; } @@ -99,13 +90,7 @@ public void fromSmali(SmaliValue smaliValue){ } @Override public void append(SmaliWriter writer) throws IOException { - T value = getValueContainer(); - if(value instanceof SmaliFormat){ - ((SmaliFormat)value).append(writer); - } - } - public String getAsString() { - return String.valueOf(getValueContainer()); + getKey().append(writer); } public boolean is(DexValueType dexValueType){ @@ -114,12 +99,6 @@ public boolean is(DexValueType dexValueType){ public TypeKey getDataTypeKey(){ return TypeKey.OBJECT; } - public Object getData() { - return null; - } - public void setData(Object data) { - throw new RuntimeException("Method not implemented"); - } @Override public int hashCode() { @@ -137,7 +116,7 @@ public boolean equals(Object obj) { return false; } DexValueBlock value = (DexValueBlock) obj; - return Objects.equals(getValueContainer(), value.getValueContainer()); + return ObjectsUtil.equals(getValueContainer(), value.getValueContainer()); } diff --git a/src/main/java/com/reandroid/dex/value/DexValueType.java b/src/main/java/com/reandroid/dex/value/DexValueType.java index d73153b04..c66e6e991 100644 --- a/src/main/java/com/reandroid/dex/value/DexValueType.java +++ b/src/main/java/com/reandroid/dex/value/DexValueType.java @@ -217,10 +217,10 @@ public static DexValueType forKey(Key key) { if(key instanceof StringKey) { return STRING; } - if(key instanceof ArrayKey) { + if(key instanceof ArrayValueKey) { return ARRAY; } - if(key instanceof NullKey) { + if(key instanceof NullValueKey) { return NULL; } if(key instanceof PrimitiveKey) { @@ -266,7 +266,7 @@ public static DexValueType forKey(Key key) { } public static Key createDefaultValue(TypeKey typeKey) { if (!typeKey.isPrimitive()) { - return NullKey.INSTANCE; + return NullValueKey.INSTANCE; } if (TypeKey.TYPE_B.equals(typeKey)) { return PrimitiveKey.of((byte) 0); diff --git a/src/main/java/com/reandroid/dex/value/DoubleValue.java b/src/main/java/com/reandroid/dex/value/DoubleValue.java index e7151d56d..8afca616a 100644 --- a/src/main/java/com/reandroid/dex/value/DoubleValue.java +++ b/src/main/java/com/reandroid/dex/value/DoubleValue.java @@ -21,7 +21,6 @@ import com.reandroid.dex.smali.SmaliWriter; import com.reandroid.dex.smali.model.SmaliValue; import com.reandroid.dex.smali.model.SmaliValueDouble; -import com.reandroid.utils.HexUtil; import java.io.IOException; @@ -31,14 +30,6 @@ public DoubleValue() { super(DexValueType.DOUBLE); } - @Override - public Double getData() { - return get(); - } - @Override - public void setData(Number number) { - this.set((Double) number); - } public double get(){ return Double.longBitsToDouble(getLongBits()); } @@ -70,11 +61,6 @@ public PrimitiveKey getKey() { public void setKey(Key key) { set(((PrimitiveKey.DoubleKey) key).value()); } - @Override - public String getHex() { - int shift = (7 - getValueSize()) * 8; - return HexUtil.toHex(getUnsigned() << shift, 8) + "L"; - } @Override public TypeKey getDataTypeKey() { @@ -94,6 +80,6 @@ public void append(SmaliWriter writer) throws IOException { } @Override public String toString() { - return getHex() + " # " + get(); + return Double.toString(get()); } } diff --git a/src/main/java/com/reandroid/dex/value/EnumValue.java b/src/main/java/com/reandroid/dex/value/EnumValue.java index 7b9eb5063..5e2d4ba2e 100644 --- a/src/main/java/com/reandroid/dex/value/EnumValue.java +++ b/src/main/java/com/reandroid/dex/value/EnumValue.java @@ -16,6 +16,7 @@ package com.reandroid.dex.value; import com.reandroid.dex.id.FieldId; +import com.reandroid.dex.key.EnumKey; import com.reandroid.dex.key.FieldKey; import com.reandroid.dex.sections.SectionType; import com.reandroid.dex.smali.SmaliWriter; @@ -29,8 +30,8 @@ public EnumValue(){ } @Override - public FieldKey getKey() { - return (FieldKey) super.getKey(); + public EnumKey getKey() { + return EnumKey.create((FieldKey) super.getKey()); } @Override @@ -39,11 +40,10 @@ public DexValueType getValueType() { } @Override public void append(SmaliWriter writer) throws IOException { - writer.append(".enum "); - super.append(writer); + getKey().append(writer); } @Override - public String toString(){ - return ".enum " + super.toString(); + public String toString() { + return getKey().toString(); } } diff --git a/src/main/java/com/reandroid/dex/value/FloatValue.java b/src/main/java/com/reandroid/dex/value/FloatValue.java index 0518b2118..3add92a07 100644 --- a/src/main/java/com/reandroid/dex/value/FloatValue.java +++ b/src/main/java/com/reandroid/dex/value/FloatValue.java @@ -21,7 +21,6 @@ import com.reandroid.dex.smali.SmaliWriter; import com.reandroid.dex.smali.model.SmaliValue; import com.reandroid.dex.smali.model.SmaliValueFloat; -import com.reandroid.utils.HexUtil; import java.io.IOException; @@ -31,14 +30,6 @@ public FloatValue() { super(DexValueType.FLOAT); } - @Override - public Float getData() { - return get(); - } - @Override - public void setData(Number number) { - this.set((Float) number); - } public float get(){ return Float.intBitsToFloat(getFloatBits()); } @@ -70,10 +61,6 @@ public PrimitiveKey getKey() { public void setKey(Key key) { set(((PrimitiveKey.FloatKey) key).value()); } - @Override - public String getHex() { - return HexUtil.toHex(getUnsigned(), getValueSize()) + "L"; - } @Override public TypeKey getDataTypeKey() { @@ -92,6 +79,6 @@ public void append(SmaliWriter writer) throws IOException { } @Override public String toString() { - return getHex() + " # " + get(); + return get() + "f"; } } diff --git a/src/main/java/com/reandroid/dex/value/IntValue.java b/src/main/java/com/reandroid/dex/value/IntValue.java index 739c5d891..1a3ce7dd8 100644 --- a/src/main/java/com/reandroid/dex/value/IntValue.java +++ b/src/main/java/com/reandroid/dex/value/IntValue.java @@ -32,14 +32,6 @@ public IntValue() { super(DexValueType.INT); } - @Override - public Integer getData() { - return get(); - } - @Override - public void setData(Number number) { - this.set((Integer) number); - } @Override public int get() { return (int) getSignedValue(); @@ -61,10 +53,7 @@ public PrimitiveKey getKey() { public void setKey(Key key) { set(((PrimitiveKey.IntegerKey) key).value()); } - @Override - public String getHex() { - return HexUtil.toHex(getSignedValue(), getValueSize()); - } + @Override public TypeKey getDataTypeKey() { return TypeKey.TYPE_I; @@ -79,4 +68,9 @@ public void fromSmali(SmaliValue smaliValue) { SmaliValueInteger smaliValueInteger = (SmaliValueInteger) smaliValue; set(smaliValueInteger.getValue()); } + + @Override + public String toString() { + return HexUtil.toSignedHex(get()); + } } diff --git a/src/main/java/com/reandroid/dex/value/LongValue.java b/src/main/java/com/reandroid/dex/value/LongValue.java index c63532779..e14f493f7 100644 --- a/src/main/java/com/reandroid/dex/value/LongValue.java +++ b/src/main/java/com/reandroid/dex/value/LongValue.java @@ -31,14 +31,6 @@ public LongValue() { super(DexValueType.LONG); } - @Override - public Long getData() { - return get(); - } - @Override - public void setData(Number number) { - this.set((Long) number); - } public long get() { return getSignedValue(); } @@ -58,10 +50,6 @@ public PrimitiveKey getKey() { public void setKey(Key key) { set(((PrimitiveKey.LongKey) key).value()); } - @Override - public String getHex() { - return HexUtil.toSignedHex(get()) + "L"; - } @Override public TypeKey getDataTypeKey() { @@ -76,11 +64,10 @@ public void fromSmali(SmaliValue smaliValue) { @Override public void append(SmaliWriter writer) throws IOException { - super.append(writer); - writer.appendComment(Long.toString(get())); + writer.appendHex(get()); } @Override public String toString() { - return getHex() + " # " + get(); + return HexUtil.toSignedHex(get()) + "L"; } } diff --git a/src/main/java/com/reandroid/dex/value/NullValue.java b/src/main/java/com/reandroid/dex/value/NullValue.java index 0e6c82c01..7f2df64c6 100644 --- a/src/main/java/com/reandroid/dex/value/NullValue.java +++ b/src/main/java/com/reandroid/dex/value/NullValue.java @@ -17,7 +17,7 @@ import com.reandroid.arsc.base.Block; import com.reandroid.dex.key.Key; -import com.reandroid.dex.key.NullKey; +import com.reandroid.dex.key.NullValueKey; import com.reandroid.dex.smali.SmaliWriter; import com.reandroid.dex.smali.model.SmaliValue; import com.reandroid.dex.smali.model.SmaliValueNull; @@ -36,8 +36,8 @@ public DexValueType getValueType() { } @Override - public NullKey getKey() { - return NullKey.INSTANCE; + public NullValueKey getKey() { + return NullValueKey.INSTANCE; } @Override @@ -58,10 +58,6 @@ public void fromSmali(SmaliValue smaliValue) { public void append(SmaliWriter writer) throws IOException { writer.append("null"); } - @Override - public String getAsString() { - return "null"; - } @Override public int hashCode() { diff --git a/src/main/java/com/reandroid/dex/value/PrimitiveValueBlock.java b/src/main/java/com/reandroid/dex/value/PrimitiveValueBlock.java index 24ea10fa9..e3b61bb03 100644 --- a/src/main/java/com/reandroid/dex/value/PrimitiveValueBlock.java +++ b/src/main/java/com/reandroid/dex/value/PrimitiveValueBlock.java @@ -29,14 +29,6 @@ public PrimitiveValueBlock(DexValueType type) { super(new NumberValue(), type); } - @Override - public abstract Number getData(); - @Override - public void setData(Object number) { - setData((Number) number); - } - public abstract void setData(Number number); - public long getSignedValue(){ return getValueContainer().getSignedNumber(); } @@ -101,11 +93,6 @@ void setValue(long value, int size){ @Override public abstract void setKey(Key key); - public abstract String getHex(); - @Override - public String getAsString() { - return getHex(); - } @Override public abstract TypeKey getDataTypeKey(); @Override @@ -123,11 +110,10 @@ public void merge(DexValueBlock valueBlock){ getValueContainer().merge(coming.getValueContainer()); } @Override - public void append(SmaliWriter writer) throws IOException { - writer.append(getHex()); - } + public abstract void append(SmaliWriter writer) throws IOException; + @Override public String toString() { - return getHex(); + return SmaliWriter.toStringSafe(this); } } diff --git a/src/main/java/com/reandroid/dex/value/SectionValue.java b/src/main/java/com/reandroid/dex/value/SectionValue.java index 5fb7f3e04..51b12566d 100644 --- a/src/main/java/com/reandroid/dex/value/SectionValue.java +++ b/src/main/java/com/reandroid/dex/value/SectionValue.java @@ -137,14 +137,6 @@ public void pullItem(){ } } - @Override - public String getAsString() { - Key key = getKey(); - if(key != null){ - return key.toString(); - } - return null; - } @Override public void append(SmaliWriter writer) throws IOException { T data = getItem(); diff --git a/src/main/java/com/reandroid/dex/value/ShortValue.java b/src/main/java/com/reandroid/dex/value/ShortValue.java index 6f36412bf..72a8f94f5 100644 --- a/src/main/java/com/reandroid/dex/value/ShortValue.java +++ b/src/main/java/com/reandroid/dex/value/ShortValue.java @@ -18,24 +18,19 @@ import com.reandroid.dex.key.Key; import com.reandroid.dex.key.PrimitiveKey; import com.reandroid.dex.key.TypeKey; +import com.reandroid.dex.smali.SmaliWriter; import com.reandroid.dex.smali.model.SmaliValue; import com.reandroid.dex.smali.model.SmaliValueShort; import com.reandroid.utils.HexUtil; +import java.io.IOException; + public class ShortValue extends PrimitiveValueBlock { public ShortValue() { super(DexValueType.SHORT); } - @Override - public Short getData() { - return get(); - } - @Override - public void setData(Number number) { - this.set((Short) number); - } public short get(){ return (short) getSignedValue(); } @@ -55,19 +50,25 @@ public PrimitiveKey getKey() { public void setKey(Key key) { set(((PrimitiveKey.ShortKey) key).value()); } - @Override - public String getHex() { - return HexUtil.toSignedHex(get()) + "S"; - } @Override public TypeKey getDataTypeKey() { return TypeKey.TYPE_S; } + @Override + public void append(SmaliWriter writer) throws IOException { + writer.appendHex(get()); + } + @Override public void fromSmali(SmaliValue smaliValue) { SmaliValueShort smaliValueShort = (SmaliValueShort) smaliValue; set(smaliValueShort.getValue()); } + + @Override + public String toString() { + return HexUtil.toSignedHex(get()) + "S"; + } } diff --git a/src/main/java/com/reandroid/dex/value/StringValue.java b/src/main/java/com/reandroid/dex/value/StringValue.java index 4c895c0fd..a7b4259f6 100644 --- a/src/main/java/com/reandroid/dex/value/StringValue.java +++ b/src/main/java/com/reandroid/dex/value/StringValue.java @@ -52,7 +52,7 @@ void updateUsageType(StringId stringId) { AnnotationItem annotationItem = getParentInstance(AnnotationItem.class); if(annotationItem != null && - TypeKey.DALVIK_Signature.equals(annotationItem.getTypeKey())){ + TypeKey.DALVIK_Signature.equals(annotationItem.getType())){ stringId.addUsageType(UsageMarker.USAGE_SIGNATURE_TYPE); } } @@ -66,12 +66,4 @@ public DexValueType getValueType() { public TypeKey getDataTypeKey() { return TypeKey.STRING; } - @Override - public String getData() { - return getString(); - } - @Override - public void setData(Object data) { - setString((String) data); - } }